From 066b395a0c5aff31b2ccc05387205a7aa72e0148 Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Sun, 28 Jun 2020 18:14:14 -0400 Subject: [PATCH] Fixes for 4.14 Signed-off-by: Sasha Levin --- ...clean-up-mixer-element-list-traverse.patch | 159 ++++++++++++++++++ ...fix-oob-access-of-mixer-element-list.patch | 125 ++++++++++++++ ...dio-uac1-invalidate-ctl-on-interrupt.patch | 44 +++++ ...ix-data-inconsistent-when-punch-hole.patch | 57 +++++++ ...ta-inconsistent-when-zero-file-range.patch | 49 ++++++ queue-4.14/series | 6 + ...poll-for-u0-after-disabling-usb2-lpm.patch | 64 +++++++ 7 files changed, 504 insertions(+) create mode 100644 queue-4.14/alsa-usb-audio-clean-up-mixer-element-list-traverse.patch create mode 100644 queue-4.14/alsa-usb-audio-fix-oob-access-of-mixer-element-list.patch create mode 100644 queue-4.14/alsa-usb-audio-uac1-invalidate-ctl-on-interrupt.patch create mode 100644 queue-4.14/cifs-smb3-fix-data-inconsistent-when-punch-hole.patch create mode 100644 queue-4.14/cifs-smb3-fix-data-inconsistent-when-zero-file-range.patch create mode 100644 queue-4.14/xhci-poll-for-u0-after-disabling-usb2-lpm.patch diff --git a/queue-4.14/alsa-usb-audio-clean-up-mixer-element-list-traverse.patch b/queue-4.14/alsa-usb-audio-clean-up-mixer-element-list-traverse.patch new file mode 100644 index 00000000000..609e7f294f4 --- /dev/null +++ b/queue-4.14/alsa-usb-audio-clean-up-mixer-element-list-traverse.patch @@ -0,0 +1,159 @@ +From 7235fe0ab22d32e7800a3651c63974be4f19a4cd Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 3 May 2018 12:33:32 +0200 +Subject: ALSA: usb-audio: Clean up mixer element list traverse + +From: Takashi Iwai + +[ Upstream commit 8c558076c740e8009a96c6fdc3d4245dde62be77 ] + +Introduce a new macro for iterating over mixer element list for +avoiding the open codes in many places. Also the open-coded +container_of() and the forced cast to struct usb_mixer_elem_info are +replaced with another simple macro, too. + +No functional changes but just readability improvement. + +Signed-off-by: Takashi Iwai +Signed-off-by: Sasha Levin +--- + sound/usb/mixer.c | 20 +++++++++----------- + sound/usb/mixer.h | 6 ++++++ + sound/usb/mixer_quirks.c | 2 +- + sound/usb/mixer_scarlett.c | 6 ++---- + 4 files changed, 18 insertions(+), 16 deletions(-) + +diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c +index 33deb5ec8b7a1..1a8d706491e65 100644 +--- a/sound/usb/mixer.c ++++ b/sound/usb/mixer.c +@@ -2403,9 +2403,9 @@ void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, int unitid) + { + struct usb_mixer_elem_list *list; + +- for (list = mixer->id_elems[unitid]; list; list = list->next_id_elem) { ++ for_each_mixer_elem(list, mixer, unitid) { + struct usb_mixer_elem_info *info = +- (struct usb_mixer_elem_info *)list; ++ mixer_elem_list_to_info(list); + /* invalidate cache, so the value is read from the device */ + info->cached = 0; + snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE, +@@ -2416,7 +2416,7 @@ void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, int unitid) + static void snd_usb_mixer_dump_cval(struct snd_info_buffer *buffer, + struct usb_mixer_elem_list *list) + { +- struct usb_mixer_elem_info *cval = (struct usb_mixer_elem_info *)list; ++ struct usb_mixer_elem_info *cval = mixer_elem_list_to_info(list); + static char *val_types[] = {"BOOLEAN", "INV_BOOLEAN", + "S8", "U8", "S16", "U16"}; + snd_iprintf(buffer, " Info: id=%i, control=%i, cmask=0x%x, " +@@ -2442,8 +2442,7 @@ static void snd_usb_mixer_proc_read(struct snd_info_entry *entry, + mixer->ignore_ctl_error); + snd_iprintf(buffer, "Card: %s\n", chip->card->longname); + for (unitid = 0; unitid < MAX_ID_ELEMS; unitid++) { +- for (list = mixer->id_elems[unitid]; list; +- list = list->next_id_elem) { ++ for_each_mixer_elem(list, mixer, unitid) { + snd_iprintf(buffer, " Unit: %i\n", list->id); + if (list->kctl) + snd_iprintf(buffer, +@@ -2473,19 +2472,19 @@ static void snd_usb_mixer_interrupt_v2(struct usb_mixer_interface *mixer, + return; + } + +- for (list = mixer->id_elems[unitid]; list; list = list->next_id_elem) ++ for_each_mixer_elem(list, mixer, unitid) + count++; + + if (count == 0) + return; + +- for (list = mixer->id_elems[unitid]; list; list = list->next_id_elem) { ++ for_each_mixer_elem(list, mixer, unitid) { + struct usb_mixer_elem_info *info; + + if (!list->kctl) + continue; + +- info = (struct usb_mixer_elem_info *)list; ++ info = mixer_elem_list_to_info(list); + if (count > 1 && info->control != control) + continue; + +@@ -2705,7 +2704,7 @@ int snd_usb_mixer_suspend(struct usb_mixer_interface *mixer) + + static int restore_mixer_value(struct usb_mixer_elem_list *list) + { +- struct usb_mixer_elem_info *cval = (struct usb_mixer_elem_info *)list; ++ struct usb_mixer_elem_info *cval = mixer_elem_list_to_info(list); + int c, err, idx; + + if (cval->cmask) { +@@ -2741,8 +2740,7 @@ int snd_usb_mixer_resume(struct usb_mixer_interface *mixer, bool reset_resume) + if (reset_resume) { + /* restore cached mixer values */ + for (id = 0; id < MAX_ID_ELEMS; id++) { +- for (list = mixer->id_elems[id]; list; +- list = list->next_id_elem) { ++ for_each_mixer_elem(list, mixer, id) { + if (list->resume) { + err = list->resume(list); + if (err < 0) +diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h +index ba27f7ade670e..e02653465e292 100644 +--- a/sound/usb/mixer.h ++++ b/sound/usb/mixer.h +@@ -53,6 +53,12 @@ struct usb_mixer_elem_list { + usb_mixer_elem_resume_func_t resume; + }; + ++/* iterate over mixer element list of the given unit id */ ++#define for_each_mixer_elem(list, mixer, id) \ ++ for ((list) = (mixer)->id_elems[id]; (list); (list) = (list)->next_id_elem) ++#define mixer_elem_list_to_info(list) \ ++ container_of(list, struct usb_mixer_elem_info, head) ++ + struct usb_mixer_elem_info { + struct usb_mixer_elem_list head; + unsigned int control; /* CS or ICN (high byte) */ +diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c +index b9ea4a42aee4e..ead0456a747c2 100644 +--- a/sound/usb/mixer_quirks.c ++++ b/sound/usb/mixer_quirks.c +@@ -1171,7 +1171,7 @@ void snd_emuusb_set_samplerate(struct snd_usb_audio *chip, + int unitid = 12; /* SamleRate ExtensionUnit ID */ + + list_for_each_entry(mixer, &chip->mixer_list, list) { +- cval = (struct usb_mixer_elem_info *)mixer->id_elems[unitid]; ++ cval = mixer_elem_list_to_info(mixer->id_elems[unitid]); + if (cval) { + snd_usb_mixer_set_ctl_value(cval, UAC_SET_CUR, + cval->control << 8, +diff --git a/sound/usb/mixer_scarlett.c b/sound/usb/mixer_scarlett.c +index c33e2378089d5..4aeb9488a0c99 100644 +--- a/sound/usb/mixer_scarlett.c ++++ b/sound/usb/mixer_scarlett.c +@@ -287,8 +287,7 @@ static int scarlett_ctl_switch_put(struct snd_kcontrol *kctl, + + static int scarlett_ctl_resume(struct usb_mixer_elem_list *list) + { +- struct usb_mixer_elem_info *elem = +- container_of(list, struct usb_mixer_elem_info, head); ++ struct usb_mixer_elem_info *elem = mixer_elem_list_to_info(list); + int i; + + for (i = 0; i < elem->channels; i++) +@@ -447,8 +446,7 @@ static int scarlett_ctl_enum_put(struct snd_kcontrol *kctl, + + static int scarlett_ctl_enum_resume(struct usb_mixer_elem_list *list) + { +- struct usb_mixer_elem_info *elem = +- container_of(list, struct usb_mixer_elem_info, head); ++ struct usb_mixer_elem_info *elem = mixer_elem_list_to_info(list); + + if (elem->cached) + snd_usb_set_cur_mix_value(elem, 0, 0, *elem->cache_val); +-- +2.25.1 + diff --git a/queue-4.14/alsa-usb-audio-fix-oob-access-of-mixer-element-list.patch b/queue-4.14/alsa-usb-audio-fix-oob-access-of-mixer-element-list.patch new file mode 100644 index 00000000000..dc4752fac56 --- /dev/null +++ b/queue-4.14/alsa-usb-audio-fix-oob-access-of-mixer-element-list.patch @@ -0,0 +1,125 @@ +From 399b8d1ccab2d3084f198ac8873381a5e8ac32e3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 24 Jun 2020 14:23:40 +0200 +Subject: ALSA: usb-audio: Fix OOB access of mixer element list + +From: Takashi Iwai + +[ Upstream commit 220345e98f1cdc768eeb6e3364a0fa7ab9647fe7 ] + +The USB-audio mixer code holds a linked list of usb_mixer_elem_list, +and several operations are performed for each mixer element. A few of +them (snd_usb_mixer_notify_id() and snd_usb_mixer_interrupt_v2()) +assume each mixer element being a usb_mixer_elem_info object that is a +subclass of usb_mixer_elem_list, cast via container_of() and access it +members. This may result in an out-of-bound access when a +non-standard list element has been added, as spotted by syzkaller +recently. + +This patch adds a new field, is_std_info, in usb_mixer_elem_list to +indicate that the element is the usb_mixer_elem_info type or not, and +skip the access to such an element if needed. + +Reported-by: syzbot+fb14314433463ad51625@syzkaller.appspotmail.com +Reported-by: syzbot+2405ca3401e943c538b5@syzkaller.appspotmail.com +Cc: +Link: https://lore.kernel.org/r/20200624122340.9615-1-tiwai@suse.de +Signed-off-by: Takashi Iwai +Signed-off-by: Sasha Levin +--- + sound/usb/mixer.c | 15 +++++++++++---- + sound/usb/mixer.h | 9 +++++++-- + sound/usb/mixer_quirks.c | 3 ++- + 3 files changed, 20 insertions(+), 7 deletions(-) + +diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c +index 1a8d706491e65..b29a3546ab6af 100644 +--- a/sound/usb/mixer.c ++++ b/sound/usb/mixer.c +@@ -591,8 +591,9 @@ static int check_matrix_bitmap(unsigned char *bmap, + * if failed, give up and free the control instance. + */ + +-int snd_usb_mixer_add_control(struct usb_mixer_elem_list *list, +- struct snd_kcontrol *kctl) ++int snd_usb_mixer_add_list(struct usb_mixer_elem_list *list, ++ struct snd_kcontrol *kctl, ++ bool is_std_info) + { + struct usb_mixer_interface *mixer = list->mixer; + int err; +@@ -605,6 +606,7 @@ int snd_usb_mixer_add_control(struct usb_mixer_elem_list *list, + return err; + } + list->kctl = kctl; ++ list->is_std_info = is_std_info; + list->next_id_elem = mixer->id_elems[list->id]; + mixer->id_elems[list->id] = list; + return 0; +@@ -2404,8 +2406,11 @@ void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, int unitid) + struct usb_mixer_elem_list *list; + + for_each_mixer_elem(list, mixer, unitid) { +- struct usb_mixer_elem_info *info = +- mixer_elem_list_to_info(list); ++ struct usb_mixer_elem_info *info; ++ ++ if (!list->is_std_info) ++ continue; ++ info = mixer_elem_list_to_info(list); + /* invalidate cache, so the value is read from the device */ + info->cached = 0; + snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE, +@@ -2483,6 +2488,8 @@ static void snd_usb_mixer_interrupt_v2(struct usb_mixer_interface *mixer, + + if (!list->kctl) + continue; ++ if (!list->is_std_info) ++ continue; + + info = mixer_elem_list_to_info(list); + if (count > 1 && info->control != control) +diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h +index e02653465e292..7c824a44589b0 100644 +--- a/sound/usb/mixer.h ++++ b/sound/usb/mixer.h +@@ -49,6 +49,7 @@ struct usb_mixer_elem_list { + struct usb_mixer_elem_list *next_id_elem; /* list of controls with same id */ + struct snd_kcontrol *kctl; + unsigned int id; ++ bool is_std_info; + usb_mixer_elem_dump_func_t dump; + usb_mixer_elem_resume_func_t resume; + }; +@@ -86,8 +87,12 @@ void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, int unitid); + int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval, + int request, int validx, int value_set); + +-int snd_usb_mixer_add_control(struct usb_mixer_elem_list *list, +- struct snd_kcontrol *kctl); ++int snd_usb_mixer_add_list(struct usb_mixer_elem_list *list, ++ struct snd_kcontrol *kctl, ++ bool is_std_info); ++ ++#define snd_usb_mixer_add_control(list, kctl) \ ++ snd_usb_mixer_add_list(list, kctl, true) + + void snd_usb_mixer_elem_init_std(struct usb_mixer_elem_list *list, + struct usb_mixer_interface *mixer, +diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c +index ead0456a747c2..5604cce30a582 100644 +--- a/sound/usb/mixer_quirks.c ++++ b/sound/usb/mixer_quirks.c +@@ -169,7 +169,8 @@ static int add_single_ctl_with_resume(struct usb_mixer_interface *mixer, + return -ENOMEM; + } + kctl->private_free = snd_usb_mixer_elem_free; +- return snd_usb_mixer_add_control(list, kctl); ++ /* don't use snd_usb_mixer_add_control() here, this is a special list element */ ++ return snd_usb_mixer_add_list(list, kctl, false); + } + + /* +-- +2.25.1 + diff --git a/queue-4.14/alsa-usb-audio-uac1-invalidate-ctl-on-interrupt.patch b/queue-4.14/alsa-usb-audio-uac1-invalidate-ctl-on-interrupt.patch new file mode 100644 index 00000000000..5fa31a54453 --- /dev/null +++ b/queue-4.14/alsa-usb-audio-uac1-invalidate-ctl-on-interrupt.patch @@ -0,0 +1,44 @@ +From 84a08162da3de7ac2f682a77bee58a8b9e4bf036 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 16 Nov 2017 17:35:17 +0100 +Subject: ALSA: usb-audio: uac1: Invalidate ctl on interrupt + +From: Julian Scheel + +[ Upstream commit b2500b584cfd228d67e1e43daf27c8af865b499e ] + +When an interrupt occurs, the value of at least one of the belonging +controls should have changed. To make sure they get re-read from device +on the next read, invalidate the cache. This was correctly implemented +for uac2 already, but missing for uac1. + +Signed-off-by: Julian Scheel +Signed-off-by: Takashi Iwai +Signed-off-by: Sasha Levin +--- + sound/usb/mixer.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c +index dbbc5609b453b..33deb5ec8b7a1 100644 +--- a/sound/usb/mixer.c ++++ b/sound/usb/mixer.c +@@ -2403,9 +2403,14 @@ void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, int unitid) + { + struct usb_mixer_elem_list *list; + +- for (list = mixer->id_elems[unitid]; list; list = list->next_id_elem) ++ for (list = mixer->id_elems[unitid]; list; list = list->next_id_elem) { ++ struct usb_mixer_elem_info *info = ++ (struct usb_mixer_elem_info *)list; ++ /* invalidate cache, so the value is read from the device */ ++ info->cached = 0; + snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE, + &list->kctl->id); ++ } + } + + static void snd_usb_mixer_dump_cval(struct snd_info_buffer *buffer, +-- +2.25.1 + diff --git a/queue-4.14/cifs-smb3-fix-data-inconsistent-when-punch-hole.patch b/queue-4.14/cifs-smb3-fix-data-inconsistent-when-punch-hole.patch new file mode 100644 index 00000000000..5d70777f3e3 --- /dev/null +++ b/queue-4.14/cifs-smb3-fix-data-inconsistent-when-punch-hole.patch @@ -0,0 +1,57 @@ +From 86dfa1cf0b1bae628c603bdb0bb0985cfb56a2c7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 23 Jun 2020 07:31:53 -0400 +Subject: cifs/smb3: Fix data inconsistent when punch hole + +From: Zhang Xiaoxu + +[ Upstream commit acc91c2d8de4ef46ed751c5f9df99ed9a109b100 ] + +When punch hole success, we also can read old data from file: + # strace -e trace=pread64,fallocate xfs_io -f -c "pread 20 40" \ + -c "fpunch 20 40" -c"pread 20 40" file + pread64(3, " version 5.8.0-rc1+"..., 40, 20) = 40 + fallocate(3, FALLOC_FL_KEEP_SIZE|FALLOC_FL_PUNCH_HOLE, 20, 40) = 0 + pread64(3, " version 5.8.0-rc1+"..., 40, 20) = 40 + +CIFS implements the fallocate(FALLOCATE_FL_PUNCH_HOLE) with send SMB +ioctl(FSCTL_SET_ZERO_DATA) to server. It just set the range of the +remote file to zero, but local page caches not updated, then the +local page caches inconsistent with server. + +Also can be found by xfstests generic/316. + +So, we need to remove the page caches before send the SMB +ioctl(FSCTL_SET_ZERO_DATA) to server. + +Fixes: 31742c5a33176 ("enable fallocate punch hole ("fallocate -p") for SMB3") +Suggested-by: Pavel Shilovsky +Reviewed-by: Pavel Shilovsky +Signed-off-by: Zhang Xiaoxu +Cc: stable@vger.kernel.org # v3.17 +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/cifs/smb2ops.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c +index 951c444d83e7b..61ea429e1210b 100644 +--- a/fs/cifs/smb2ops.c ++++ b/fs/cifs/smb2ops.c +@@ -1824,6 +1824,12 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon, + return rc; + } + ++ /* ++ * We implement the punch hole through ioctl, so we need remove the page ++ * caches first, otherwise the data may be inconsistent with the server. ++ */ ++ truncate_pagecache_range(inode, offset, offset + len - 1); ++ + cifs_dbg(FYI, "offset %lld len %lld", offset, len); + + fsctl_buf.FileOffset = cpu_to_le64(offset); +-- +2.25.1 + diff --git a/queue-4.14/cifs-smb3-fix-data-inconsistent-when-zero-file-range.patch b/queue-4.14/cifs-smb3-fix-data-inconsistent-when-zero-file-range.patch new file mode 100644 index 00000000000..c5d58fdab4d --- /dev/null +++ b/queue-4.14/cifs-smb3-fix-data-inconsistent-when-zero-file-range.patch @@ -0,0 +1,49 @@ +From a94c42f513bb2098c480ac5aeb7ea72cc2832a21 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 23 Jun 2020 07:31:54 -0400 +Subject: cifs/smb3: Fix data inconsistent when zero file range + +From: Zhang Xiaoxu + +[ Upstream commit 6b69040247e14b43419a520f841f2b3052833df9 ] + +CIFS implements the fallocate(FALLOC_FL_ZERO_RANGE) with send SMB +ioctl(FSCTL_SET_ZERO_DATA) to server. It just set the range of the +remote file to zero, but local page cache not update, then the data +inconsistent with server, which leads the xfstest generic/008 failed. + +So we need to remove the local page caches before send SMB +ioctl(FSCTL_SET_ZERO_DATA) to server. After next read, it will +re-cache it. + +Fixes: 30175628bf7f5 ("[SMB3] Enable fallocate -z support for SMB3 mounts") +Reported-by: Hulk Robot +Signed-off-by: Zhang Xiaoxu +Reviewed-by: Pavel Shilovsky +Cc: stable@vger.kernel.org # v3.17 +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/cifs/smb2ops.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c +index 61ea429e1210b..b46fdb2b8d349 100644 +--- a/fs/cifs/smb2ops.c ++++ b/fs/cifs/smb2ops.c +@@ -1755,6 +1755,12 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon, + inode = d_inode(cfile->dentry); + cifsi = CIFS_I(inode); + ++ /* ++ * We zero the range through ioctl, so we need remove the page caches ++ * first, otherwise the data may be inconsistent with the server. ++ */ ++ truncate_pagecache_range(inode, offset, offset + len - 1); ++ + /* if file not oplocked can't be sure whether asking to extend size */ + if (!CIFS_CACHE_READ(cifsi)) + if (keep_size == false) { +-- +2.25.1 + diff --git a/queue-4.14/series b/queue-4.14/series index 7ee20e9640e..255ed484014 100644 --- a/queue-4.14/series +++ b/queue-4.14/series @@ -30,3 +30,9 @@ alsa-usb-audio-add-quirk-for-denon-dcd-1500re.patch xhci-fix-incorrect-ep_state_mask.patch xhci-fix-enumeration-issue-when-setting-max-packet-size-for-fs-devices.patch cdc-acm-add-disable_echo-quirk-for-microchip-smsc-chip.patch +alsa-usb-audio-uac1-invalidate-ctl-on-interrupt.patch +alsa-usb-audio-clean-up-mixer-element-list-traverse.patch +alsa-usb-audio-fix-oob-access-of-mixer-element-list.patch +xhci-poll-for-u0-after-disabling-usb2-lpm.patch +cifs-smb3-fix-data-inconsistent-when-punch-hole.patch +cifs-smb3-fix-data-inconsistent-when-zero-file-range.patch diff --git a/queue-4.14/xhci-poll-for-u0-after-disabling-usb2-lpm.patch b/queue-4.14/xhci-poll-for-u0-after-disabling-usb2-lpm.patch new file mode 100644 index 00000000000..56cf6edf41a --- /dev/null +++ b/queue-4.14/xhci-poll-for-u0-after-disabling-usb2-lpm.patch @@ -0,0 +1,64 @@ +From 34ce3bf9cc3665aa79988b9c6be0589b7d310acc Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 24 Jun 2020 16:59:49 +0300 +Subject: xhci: Poll for U0 after disabling USB2 LPM + +From: Kai-Heng Feng + +[ Upstream commit b3d71abd135e6919ca0b6cab463738472653ddfb ] + +USB2 devices with LPM enabled may interrupt the system suspend: +[ 932.510475] usb 1-7: usb suspend, wakeup 0 +[ 932.510549] hub 1-0:1.0: hub_suspend +[ 932.510581] usb usb1: bus suspend, wakeup 0 +[ 932.510590] xhci_hcd 0000:00:14.0: port 9 not suspended +[ 932.510593] xhci_hcd 0000:00:14.0: port 8 not suspended +.. +[ 932.520323] xhci_hcd 0000:00:14.0: Port change event, 1-7, id 7, portsc: 0x400e03 +.. +[ 932.591405] PM: pci_pm_suspend(): hcd_pci_suspend+0x0/0x30 returns -16 +[ 932.591414] PM: dpm_run_callback(): pci_pm_suspend+0x0/0x160 returns -16 +[ 932.591418] PM: Device 0000:00:14.0 failed to suspend async: error -16 + +During system suspend, USB core will let HC suspends the device if it +doesn't have remote wakeup enabled and doesn't have any children. +However, from the log above we can see that the usb 1-7 doesn't get bus +suspended due to not in U0. After a while the port finished U2 -> U0 +transition, interrupts the suspend process. + +The observation is that after disabling LPM, port doesn't transit to U0 +immediately and can linger in U2. xHCI spec 4.23.5.2 states that the +maximum exit latency for USB2 LPM should be BESL + 10us. The BESL for +the affected device is advertised as 400us, which is still not enough +based on my testing result. + +So let's use the maximum permitted latency, 10000, to poll for U0 +status to solve the issue. + +Cc: stable@vger.kernel.org +Signed-off-by: Kai-Heng Feng +Signed-off-by: Mathias Nyman +Link: https://lore.kernel.org/r/20200624135949.22611-6-mathias.nyman@linux.intel.com +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Sasha Levin +--- + drivers/usb/host/xhci.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c +index d727cbbad44a2..cdf1c91554035 100644 +--- a/drivers/usb/host/xhci.c ++++ b/drivers/usb/host/xhci.c +@@ -4226,6 +4226,9 @@ static int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd, + mutex_lock(hcd->bandwidth_mutex); + xhci_change_max_exit_latency(xhci, udev, 0); + mutex_unlock(hcd->bandwidth_mutex); ++ readl_poll_timeout(port_array[port_num], pm_val, ++ (pm_val & PORT_PLS_MASK) == XDEV_U0, ++ 100, 10000); + return 0; + } + } +-- +2.25.1 + -- 2.47.3