]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
4.9-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 31 Aug 2020 10:31:54 +0000 (12:31 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 31 Aug 2020 10:31:54 +0000 (12:31 +0200)
added patches:
btrfs-fix-space-cache-memory-leak-after-transaction-abort.patch
fbcon-prevent-user-font-height-or-width-change-from-causing-potential-out-of-bounds-access.patch
hid-i2c-hid-always-sleep-60ms-after-i2c_hid_pwr_on-commands.patch
serial-8250-change-lock-order-in-serial8250_do_startup.patch
serial-pl011-don-t-leak-amba_ports-entry-on-driver-register-error.patch
serial-pl011-fix-oops-on-eprobe_defer.patch
serial-samsung-removes-the-irq-not-found-warning.patch
usb-lvtest-return-proper-error-code-in-probe.patch
vt-defer-kfree-of-vc_screenbuf-in-vc_do_resize.patch
vt_ioctl-change-vt_resizex-ioctl-to-check-for-error-return-from-vc_resize.patch
writeback-avoid-skipping-inode-writeback.patch
writeback-fix-sync-livelock-due-to-b_dirty_time-processing.patch
writeback-protect-inode-i_io_list-with-inode-i_lock.patch
xen-uses-irqdesc-irq_data_common-handler_data-to-store-a-per-interrupt-xen-data-pointer-which-contains-xen-specific-information.patch
xhci-do-warm-reset-when-both-cas-and-xdev_resume-are-set.patch

16 files changed:
queue-4.9/btrfs-fix-space-cache-memory-leak-after-transaction-abort.patch [new file with mode: 0644]
queue-4.9/fbcon-prevent-user-font-height-or-width-change-from-causing-potential-out-of-bounds-access.patch [new file with mode: 0644]
queue-4.9/hid-i2c-hid-always-sleep-60ms-after-i2c_hid_pwr_on-commands.patch [new file with mode: 0644]
queue-4.9/serial-8250-change-lock-order-in-serial8250_do_startup.patch [new file with mode: 0644]
queue-4.9/serial-pl011-don-t-leak-amba_ports-entry-on-driver-register-error.patch [new file with mode: 0644]
queue-4.9/serial-pl011-fix-oops-on-eprobe_defer.patch [new file with mode: 0644]
queue-4.9/serial-samsung-removes-the-irq-not-found-warning.patch [new file with mode: 0644]
queue-4.9/series
queue-4.9/usb-lvtest-return-proper-error-code-in-probe.patch [new file with mode: 0644]
queue-4.9/vt-defer-kfree-of-vc_screenbuf-in-vc_do_resize.patch [new file with mode: 0644]
queue-4.9/vt_ioctl-change-vt_resizex-ioctl-to-check-for-error-return-from-vc_resize.patch [new file with mode: 0644]
queue-4.9/writeback-avoid-skipping-inode-writeback.patch [new file with mode: 0644]
queue-4.9/writeback-fix-sync-livelock-due-to-b_dirty_time-processing.patch [new file with mode: 0644]
queue-4.9/writeback-protect-inode-i_io_list-with-inode-i_lock.patch [new file with mode: 0644]
queue-4.9/xen-uses-irqdesc-irq_data_common-handler_data-to-store-a-per-interrupt-xen-data-pointer-which-contains-xen-specific-information.patch [new file with mode: 0644]
queue-4.9/xhci-do-warm-reset-when-both-cas-and-xdev_resume-are-set.patch [new file with mode: 0644]

diff --git a/queue-4.9/btrfs-fix-space-cache-memory-leak-after-transaction-abort.patch b/queue-4.9/btrfs-fix-space-cache-memory-leak-after-transaction-abort.patch
new file mode 100644 (file)
index 0000000..d0d78ad
--- /dev/null
@@ -0,0 +1,125 @@
+From bbc37d6e475eee8ffa2156ec813efc6bbb43c06d Mon Sep 17 00:00:00 2001
+From: Filipe Manana <fdmanana@suse.com>
+Date: Fri, 14 Aug 2020 11:04:09 +0100
+Subject: btrfs: fix space cache memory leak after transaction abort
+
+From: Filipe Manana <fdmanana@suse.com>
+
+commit bbc37d6e475eee8ffa2156ec813efc6bbb43c06d upstream.
+
+If a transaction aborts it can cause a memory leak of the pages array of
+a block group's io_ctl structure. The following steps explain how that can
+happen:
+
+1) Transaction N is committing, currently in state TRANS_STATE_UNBLOCKED
+   and it's about to start writing out dirty extent buffers;
+
+2) Transaction N + 1 already started and another task, task A, just called
+   btrfs_commit_transaction() on it;
+
+3) Block group B was dirtied (extents allocated from it) by transaction
+   N + 1, so when task A calls btrfs_start_dirty_block_groups(), at the
+   very beginning of the transaction commit, it starts writeback for the
+   block group's space cache by calling btrfs_write_out_cache(), which
+   allocates the pages array for the block group's io_ctl with a call to
+   io_ctl_init(). Block group A is added to the io_list of transaction
+   N + 1 by btrfs_start_dirty_block_groups();
+
+4) While transaction N's commit is writing out the extent buffers, it gets
+   an IO error and aborts transaction N, also setting the file system to
+   RO mode;
+
+5) Task A has already returned from btrfs_start_dirty_block_groups(), is at
+   btrfs_commit_transaction() and has set transaction N + 1 state to
+   TRANS_STATE_COMMIT_START. Immediately after that it checks that the
+   filesystem was turned to RO mode, due to transaction N's abort, and
+   jumps to the "cleanup_transaction" label. After that we end up at
+   btrfs_cleanup_one_transaction() which calls btrfs_cleanup_dirty_bgs().
+   That helper finds block group B in the transaction's io_list but it
+   never releases the pages array of the block group's io_ctl, resulting in
+   a memory leak.
+
+In fact at the point when we are at btrfs_cleanup_dirty_bgs(), the pages
+array points to pages that were already released by us at
+__btrfs_write_out_cache() through the call to io_ctl_drop_pages(). We end
+up freeing the pages array only after waiting for the ordered extent to
+complete through btrfs_wait_cache_io(), which calls io_ctl_free() to do
+that. But in the transaction abort case we don't wait for the space cache's
+ordered extent to complete through a call to btrfs_wait_cache_io(), so
+that's why we end up with a memory leak - we wait for the ordered extent
+to complete indirectly by shutting down the work queues and waiting for
+any jobs in them to complete before returning from close_ctree().
+
+We can solve the leak simply by freeing the pages array right after
+releasing the pages (with the call to io_ctl_drop_pages()) at
+__btrfs_write_out_cache(), since we will never use it anymore after that
+and the pages array points to already released pages at that point, which
+is currently not a problem since no one will use it after that, but not a
+good practice anyway since it can easily lead to use-after-free issues.
+
+So fix this by freeing the pages array right after releasing the pages at
+__btrfs_write_out_cache().
+
+This issue can often be reproduced with test case generic/475 from fstests
+and kmemleak can detect it and reports it with the following trace:
+
+unreferenced object 0xffff9bbf009fa600 (size 512):
+  comm "fsstress", pid 38807, jiffies 4298504428 (age 22.028s)
+  hex dump (first 32 bytes):
+    00 a0 7c 4d 3d ed ff ff 40 a0 7c 4d 3d ed ff ff  ..|M=...@.|M=...
+    80 a0 7c 4d 3d ed ff ff c0 a0 7c 4d 3d ed ff ff  ..|M=.....|M=...
+  backtrace:
+    [<00000000f4b5cfe2>] __kmalloc+0x1a8/0x3e0
+    [<0000000028665e7f>] io_ctl_init+0xa7/0x120 [btrfs]
+    [<00000000a1f95b2d>] __btrfs_write_out_cache+0x86/0x4a0 [btrfs]
+    [<00000000207ea1b0>] btrfs_write_out_cache+0x7f/0xf0 [btrfs]
+    [<00000000af21f534>] btrfs_start_dirty_block_groups+0x27b/0x580 [btrfs]
+    [<00000000c3c23d44>] btrfs_commit_transaction+0xa6f/0xe70 [btrfs]
+    [<000000009588930c>] create_subvol+0x581/0x9a0 [btrfs]
+    [<000000009ef2fd7f>] btrfs_mksubvol+0x3fb/0x4a0 [btrfs]
+    [<00000000474e5187>] __btrfs_ioctl_snap_create+0x119/0x1a0 [btrfs]
+    [<00000000708ee349>] btrfs_ioctl_snap_create_v2+0xb0/0xf0 [btrfs]
+    [<00000000ea60106f>] btrfs_ioctl+0x12c/0x3130 [btrfs]
+    [<000000005c923d6d>] __x64_sys_ioctl+0x83/0xb0
+    [<0000000043ace2c9>] do_syscall_64+0x33/0x80
+    [<00000000904efbce>] entry_SYSCALL_64_after_hwframe+0x44/0xa9
+
+CC: stable@vger.kernel.org # 4.9+
+Reviewed-by: Josef Bacik <josef@toxicpanda.com>
+Signed-off-by: Filipe Manana <fdmanana@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ fs/btrfs/disk-io.c          |    1 +
+ fs/btrfs/free-space-cache.c |    2 +-
+ 2 files changed, 2 insertions(+), 1 deletion(-)
+
+--- a/fs/btrfs/disk-io.c
++++ b/fs/btrfs/disk-io.c
+@@ -4432,6 +4432,7 @@ static void btrfs_cleanup_bg_io(struct b
+               cache->io_ctl.inode = NULL;
+               iput(inode);
+       }
++      ASSERT(cache->io_ctl.pages == NULL);
+       btrfs_put_block_group(cache);
+ }
+--- a/fs/btrfs/free-space-cache.c
++++ b/fs/btrfs/free-space-cache.c
+@@ -1165,7 +1165,6 @@ int btrfs_wait_cache_io(struct btrfs_roo
+       ret = update_cache_item(trans, root, inode, path, offset,
+                               io_ctl->entries, io_ctl->bitmaps);
+ out:
+-      io_ctl_free(io_ctl);
+       if (ret) {
+               invalidate_inode_pages2(inode->i_mapping);
+               BTRFS_I(inode)->generation = 0;
+@@ -1314,6 +1313,7 @@ static int __btrfs_write_out_cache(struc
+        * them out later
+        */
+       io_ctl_drop_pages(io_ctl);
++      io_ctl_free(io_ctl);
+       unlock_extent_cached(&BTRFS_I(inode)->io_tree, 0,
+                            i_size_read(inode) - 1, &cached_state, GFP_NOFS);
diff --git a/queue-4.9/fbcon-prevent-user-font-height-or-width-change-from-causing-potential-out-of-bounds-access.patch b/queue-4.9/fbcon-prevent-user-font-height-or-width-change-from-causing-potential-out-of-bounds-access.patch
new file mode 100644 (file)
index 0000000..4c7897c
--- /dev/null
@@ -0,0 +1,79 @@
+From 39b3cffb8cf3111738ea993e2757ab382253d86a Mon Sep 17 00:00:00 2001
+From: George Kennedy <george.kennedy@oracle.com>
+Date: Fri, 31 Jul 2020 12:33:11 -0400
+Subject: fbcon: prevent user font height or width change from causing potential out-of-bounds access
+
+From: George Kennedy <george.kennedy@oracle.com>
+
+commit 39b3cffb8cf3111738ea993e2757ab382253d86a upstream.
+
+Add a check to fbcon_resize() to ensure that a possible change to user font
+height or user font width will not allow a font data out-of-bounds access.
+NOTE: must use original charcount in calculation as font charcount can
+change and cannot be used to determine the font data allocated size.
+
+Signed-off-by: George Kennedy <george.kennedy@oracle.com>
+Cc: stable <stable@vger.kernel.org>
+Reported-by: syzbot+38a3699c7eaf165b97a6@syzkaller.appspotmail.com
+Link: https://lore.kernel.org/r/1596213192-6635-1-git-send-email-george.kennedy@oracle.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/video/console/fbcon.c |   25 +++++++++++++++++++++++--
+ 1 file changed, 23 insertions(+), 2 deletions(-)
+
+--- a/drivers/video/console/fbcon.c
++++ b/drivers/video/console/fbcon.c
+@@ -2116,6 +2116,9 @@ static void updatescrollmode(struct disp
+       }
+ }
++#define PITCH(w) (((w) + 7) >> 3)
++#define CALC_FONTSZ(h, p, c) ((h) * (p) * (c)) /* size = height * pitch * charcount */
++
+ static int fbcon_resize(struct vc_data *vc, unsigned int width, 
+                       unsigned int height, unsigned int user)
+ {
+@@ -2125,6 +2128,24 @@ static int fbcon_resize(struct vc_data *
+       struct fb_var_screeninfo var = info->var;
+       int x_diff, y_diff, virt_w, virt_h, virt_fw, virt_fh;
++      if (ops->p && ops->p->userfont && FNTSIZE(vc->vc_font.data)) {
++              int size;
++              int pitch = PITCH(vc->vc_font.width);
++
++              /*
++               * If user font, ensure that a possible change to user font
++               * height or width will not allow a font data out-of-bounds access.
++               * NOTE: must use original charcount in calculation as font
++               * charcount can change and cannot be used to determine the
++               * font data allocated size.
++               */
++              if (pitch <= 0)
++                      return -EINVAL;
++              size = CALC_FONTSZ(vc->vc_font.height, pitch, FNTCHARCNT(vc->vc_font.data));
++              if (size > FNTSIZE(vc->vc_font.data))
++                      return -EINVAL;
++      }
++
+       virt_w = FBCON_SWAP(ops->rotate, width, height);
+       virt_h = FBCON_SWAP(ops->rotate, height, width);
+       virt_fw = FBCON_SWAP(ops->rotate, vc->vc_font.width,
+@@ -2586,7 +2607,7 @@ static int fbcon_set_font(struct vc_data
+       int size;
+       int i, csum;
+       u8 *new_data, *data = font->data;
+-      int pitch = (font->width+7) >> 3;
++      int pitch = PITCH(font->width);
+       /* Is there a reason why fbconsole couldn't handle any charcount >256?
+        * If not this check should be changed to charcount < 256 */
+@@ -2602,7 +2623,7 @@ static int fbcon_set_font(struct vc_data
+       if (fbcon_invalid_charcount(info, charcount))
+               return -EINVAL;
+-      size = h * pitch * charcount;
++      size = CALC_FONTSZ(h, pitch, charcount);
+       new_data = kmalloc(FONT_EXTRA_WORDS * sizeof(int) + size, GFP_USER);
diff --git a/queue-4.9/hid-i2c-hid-always-sleep-60ms-after-i2c_hid_pwr_on-commands.patch b/queue-4.9/hid-i2c-hid-always-sleep-60ms-after-i2c_hid_pwr_on-commands.patch
new file mode 100644 (file)
index 0000000..83861bd
--- /dev/null
@@ -0,0 +1,93 @@
+From eef4016243e94c438f177ca8226876eb873b9c75 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Tue, 11 Aug 2020 15:39:58 +0200
+Subject: HID: i2c-hid: Always sleep 60ms after I2C_HID_PWR_ON commands
+
+From: Hans de Goede <hdegoede@redhat.com>
+
+commit eef4016243e94c438f177ca8226876eb873b9c75 upstream.
+
+Before this commit i2c_hid_parse() consists of the following steps:
+
+1. Send power on cmd
+2. usleep_range(1000, 5000)
+3. Send reset cmd
+4. Wait for reset to complete (device interrupt, or msleep(100))
+5. Send power on cmd
+6. Try to read HID descriptor
+
+Notice how there is an usleep_range(1000, 5000) after the first power-on
+command, but not after the second power-on command.
+
+Testing has shown that at least on the BMAX Y13 laptop's i2c-hid touchpad,
+not having a delay after the second power-on command causes the HID
+descriptor to read as all zeros.
+
+In case we hit this on other devices too, the descriptor being all zeros
+can be recognized by the following message being logged many, many times:
+
+hid-generic 0018:0911:5288.0002: unknown main item tag 0x0
+
+At the same time as the BMAX Y13's touchpad issue was debugged,
+Kai-Heng was working on debugging some issues with Goodix i2c-hid
+touchpads. It turns out that these need a delay after a PWR_ON command
+too, otherwise they stop working after a suspend/resume cycle.
+According to Goodix a delay of minimal 60ms is needed.
+
+Having multiple cases where we need a delay after sending the power-on
+command, seems to indicate that we should always sleep after the power-on
+command.
+
+This commit fixes the mentioned issues by moving the existing 1ms sleep to
+the i2c_hid_set_power() function and changing it to a 60ms sleep.
+
+Cc: stable@vger.kernel.org
+BugLink: https://bugzilla.kernel.org/show_bug.cgi?id=208247
+Reported-by: Kai-Heng Feng <kai.heng.feng@canonical.com>
+Reported-and-tested-by: Andrea Borgia <andrea@borgia.bo.it>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Jiri Kosina <jkosina@suse.cz>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/hid/i2c-hid/i2c-hid-core.c |   22 +++++++++++++---------
+ 1 file changed, 13 insertions(+), 9 deletions(-)
+
+--- a/drivers/hid/i2c-hid/i2c-hid-core.c
++++ b/drivers/hid/i2c-hid/i2c-hid-core.c
+@@ -407,6 +407,19 @@ static int i2c_hid_set_power(struct i2c_
+               dev_err(&client->dev, "failed to change power setting.\n");
+ set_pwr_exit:
++
++      /*
++       * The HID over I2C specification states that if a DEVICE needs time
++       * after the PWR_ON request, it should utilise CLOCK stretching.
++       * However, it has been observered that the Windows driver provides a
++       * 1ms sleep between the PWR_ON and RESET requests.
++       * According to Goodix Windows even waits 60 ms after (other?)
++       * PWR_ON requests. Testing has confirmed that several devices
++       * will not work properly without a delay after a PWR_ON request.
++       */
++      if (!ret && power_state == I2C_HID_PWR_ON)
++              msleep(60);
++
+       return ret;
+ }
+@@ -428,15 +441,6 @@ static int i2c_hid_hwreset(struct i2c_cl
+       if (ret)
+               goto out_unlock;
+-      /*
+-       * The HID over I2C specification states that if a DEVICE needs time
+-       * after the PWR_ON request, it should utilise CLOCK stretching.
+-       * However, it has been observered that the Windows driver provides a
+-       * 1ms sleep between the PWR_ON and RESET requests and that some devices
+-       * rely on this.
+-       */
+-      usleep_range(1000, 5000);
+-
+       i2c_hid_dbg(ihid, "resetting...\n");
+       ret = i2c_hid_command(client, &hid_reset_cmd, NULL, 0);
diff --git a/queue-4.9/serial-8250-change-lock-order-in-serial8250_do_startup.patch b/queue-4.9/serial-8250-change-lock-order-in-serial8250_do_startup.patch
new file mode 100644 (file)
index 0000000..5585864
--- /dev/null
@@ -0,0 +1,215 @@
+From 205d300aea75623e1ae4aa43e0d265ab9cf195fd Mon Sep 17 00:00:00 2001
+From: Sergey Senozhatsky <sergey.senozhatsky@gmail.com>
+Date: Mon, 17 Aug 2020 11:26:46 +0900
+Subject: serial: 8250: change lock order in serial8250_do_startup()
+
+From: Sergey Senozhatsky <sergey.senozhatsky@gmail.com>
+
+commit 205d300aea75623e1ae4aa43e0d265ab9cf195fd upstream.
+
+We have a number of "uart.port->desc.lock vs desc.lock->uart.port"
+lockdep reports coming from 8250 driver; this causes a bit of trouble
+to people, so let's fix it.
+
+The problem is reverse lock order in two different call paths:
+
+chain #1:
+
+ serial8250_do_startup()
+  spin_lock_irqsave(&port->lock);
+   disable_irq_nosync(port->irq);
+    raw_spin_lock_irqsave(&desc->lock)
+
+chain #2:
+
+  __report_bad_irq()
+   raw_spin_lock_irqsave(&desc->lock)
+    for_each_action_of_desc()
+     printk()
+      spin_lock_irqsave(&port->lock);
+
+Fix this by changing the order of locks in serial8250_do_startup():
+ do disable_irq_nosync() first, which grabs desc->lock, and grab
+ uart->port after that, so that chain #1 and chain #2 have same lock
+ order.
+
+Full lockdep splat:
+
+ ======================================================
+ WARNING: possible circular locking dependency detected
+ 5.4.39 #55 Not tainted
+ ======================================================
+
+ swapper/0/0 is trying to acquire lock:
+ ffffffffab65b6c0 (console_owner){-...}, at: console_lock_spinning_enable+0x31/0x57
+
+ but task is already holding lock:
+ ffff88810a8e34c0 (&irq_desc_lock_class){-.-.}, at: __report_bad_irq+0x5b/0xba
+
+ which lock already depends on the new lock.
+
+ the existing dependency chain (in reverse order) is:
+
+ -> #2 (&irq_desc_lock_class){-.-.}:
+        _raw_spin_lock_irqsave+0x61/0x8d
+        __irq_get_desc_lock+0x65/0x89
+        __disable_irq_nosync+0x3b/0x93
+        serial8250_do_startup+0x451/0x75c
+        uart_startup+0x1b4/0x2ff
+        uart_port_activate+0x73/0xa0
+        tty_port_open+0xae/0x10a
+        uart_open+0x1b/0x26
+        tty_open+0x24d/0x3a0
+        chrdev_open+0xd5/0x1cc
+        do_dentry_open+0x299/0x3c8
+        path_openat+0x434/0x1100
+        do_filp_open+0x9b/0x10a
+        do_sys_open+0x15f/0x3d7
+        kernel_init_freeable+0x157/0x1dd
+        kernel_init+0xe/0x105
+        ret_from_fork+0x27/0x50
+
+ -> #1 (&port_lock_key){-.-.}:
+        _raw_spin_lock_irqsave+0x61/0x8d
+        serial8250_console_write+0xa7/0x2a0
+        console_unlock+0x3b7/0x528
+        vprintk_emit+0x111/0x17f
+        printk+0x59/0x73
+        register_console+0x336/0x3a4
+        uart_add_one_port+0x51b/0x5be
+        serial8250_register_8250_port+0x454/0x55e
+        dw8250_probe+0x4dc/0x5b9
+        platform_drv_probe+0x67/0x8b
+        really_probe+0x14a/0x422
+        driver_probe_device+0x66/0x130
+        device_driver_attach+0x42/0x5b
+        __driver_attach+0xca/0x139
+        bus_for_each_dev+0x97/0xc9
+        bus_add_driver+0x12b/0x228
+        driver_register+0x64/0xed
+        do_one_initcall+0x20c/0x4a6
+        do_initcall_level+0xb5/0xc5
+        do_basic_setup+0x4c/0x58
+        kernel_init_freeable+0x13f/0x1dd
+        kernel_init+0xe/0x105
+        ret_from_fork+0x27/0x50
+
+ -> #0 (console_owner){-...}:
+        __lock_acquire+0x118d/0x2714
+        lock_acquire+0x203/0x258
+        console_lock_spinning_enable+0x51/0x57
+        console_unlock+0x25d/0x528
+        vprintk_emit+0x111/0x17f
+        printk+0x59/0x73
+        __report_bad_irq+0xa3/0xba
+        note_interrupt+0x19a/0x1d6
+        handle_irq_event_percpu+0x57/0x79
+        handle_irq_event+0x36/0x55
+        handle_fasteoi_irq+0xc2/0x18a
+        do_IRQ+0xb3/0x157
+        ret_from_intr+0x0/0x1d
+        cpuidle_enter_state+0x12f/0x1fd
+        cpuidle_enter+0x2e/0x3d
+        do_idle+0x1ce/0x2ce
+        cpu_startup_entry+0x1d/0x1f
+        start_kernel+0x406/0x46a
+        secondary_startup_64+0xa4/0xb0
+
+ other info that might help us debug this:
+
+ Chain exists of:
+   console_owner --> &port_lock_key --> &irq_desc_lock_class
+
+  Possible unsafe locking scenario:
+
+        CPU0                    CPU1
+        ----                    ----
+   lock(&irq_desc_lock_class);
+                                lock(&port_lock_key);
+                                lock(&irq_desc_lock_class);
+   lock(console_owner);
+
+  *** DEADLOCK ***
+
+ 2 locks held by swapper/0/0:
+  #0: ffff88810a8e34c0 (&irq_desc_lock_class){-.-.}, at: __report_bad_irq+0x5b/0xba
+  #1: ffffffffab65b5c0 (console_lock){+.+.}, at: console_trylock_spinning+0x20/0x181
+
+ stack backtrace:
+ CPU: 0 PID: 0 Comm: swapper/0 Not tainted 5.4.39 #55
+ Hardware name: XXXXXX
+ Call Trace:
+  <IRQ>
+  dump_stack+0xbf/0x133
+  ? print_circular_bug+0xd6/0xe9
+  check_noncircular+0x1b9/0x1c3
+  __lock_acquire+0x118d/0x2714
+  lock_acquire+0x203/0x258
+  ? console_lock_spinning_enable+0x31/0x57
+  console_lock_spinning_enable+0x51/0x57
+  ? console_lock_spinning_enable+0x31/0x57
+  console_unlock+0x25d/0x528
+  ? console_trylock+0x18/0x4e
+  vprintk_emit+0x111/0x17f
+  ? lock_acquire+0x203/0x258
+  printk+0x59/0x73
+  __report_bad_irq+0xa3/0xba
+  note_interrupt+0x19a/0x1d6
+  handle_irq_event_percpu+0x57/0x79
+  handle_irq_event+0x36/0x55
+  handle_fasteoi_irq+0xc2/0x18a
+  do_IRQ+0xb3/0x157
+  common_interrupt+0xf/0xf
+  </IRQ>
+
+Signed-off-by: Sergey Senozhatsky <sergey.senozhatsky@gmail.com>
+Fixes: 768aec0b5bcc ("serial: 8250: fix shared interrupts issues with SMP and RT kernels")
+Reported-by: Guenter Roeck <linux@roeck-us.net>
+Reported-by: Raul Rangel <rrangel@google.com>
+BugLink: https://bugs.chromium.org/p/chromium/issues/detail?id=1114800
+Link: https://lore.kernel.org/lkml/CAHQZ30BnfX+gxjPm1DUd5psOTqbyDh4EJE=2=VAMW_VDafctkA@mail.gmail.com/T/#u
+Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Reviewed-by: Guenter Roeck <linux@roeck-us.net>
+Tested-by: Guenter Roeck <linux@roeck-us.net>
+Cc: stable <stable@vger.kernel.org>
+Link: https://lore.kernel.org/r/20200817022646.1484638-1-sergey.senozhatsky@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/tty/serial/8250/8250_port.c |    9 ++++++---
+ 1 file changed, 6 insertions(+), 3 deletions(-)
+
+--- a/drivers/tty/serial/8250/8250_port.c
++++ b/drivers/tty/serial/8250/8250_port.c
+@@ -2205,6 +2205,10 @@ int serial8250_do_startup(struct uart_po
+       if (port->irq) {
+               unsigned char iir1;
++
++              if (port->irqflags & IRQF_SHARED)
++                      disable_irq_nosync(port->irq);
++
+               /*
+                * Test for UARTs that do not reassert THRE when the
+                * transmitter is idle and the interrupt has already
+@@ -2214,8 +2218,6 @@ int serial8250_do_startup(struct uart_po
+                * allow register changes to become visible.
+                */
+               spin_lock_irqsave(&port->lock, flags);
+-              if (up->port.irqflags & IRQF_SHARED)
+-                      disable_irq_nosync(port->irq);
+               wait_for_xmitr(up, UART_LSR_THRE);
+               serial_port_out_sync(port, UART_IER, UART_IER_THRI);
+@@ -2227,9 +2229,10 @@ int serial8250_do_startup(struct uart_po
+               iir = serial_port_in(port, UART_IIR);
+               serial_port_out(port, UART_IER, 0);
++              spin_unlock_irqrestore(&port->lock, flags);
++
+               if (port->irqflags & IRQF_SHARED)
+                       enable_irq(port->irq);
+-              spin_unlock_irqrestore(&port->lock, flags);
+               /*
+                * If the interrupt is not reasserted, or we otherwise
diff --git a/queue-4.9/serial-pl011-don-t-leak-amba_ports-entry-on-driver-register-error.patch b/queue-4.9/serial-pl011-don-t-leak-amba_ports-entry-on-driver-register-error.patch
new file mode 100644 (file)
index 0000000..e4b8576
--- /dev/null
@@ -0,0 +1,52 @@
+From 89efbe70b27dd325d8a8c177743a26b885f7faec Mon Sep 17 00:00:00 2001
+From: Lukas Wunner <lukas@wunner.de>
+Date: Thu, 13 Aug 2020 12:59:54 +0200
+Subject: serial: pl011: Don't leak amba_ports entry on driver register error
+
+From: Lukas Wunner <lukas@wunner.de>
+
+commit 89efbe70b27dd325d8a8c177743a26b885f7faec upstream.
+
+pl011_probe() calls pl011_setup_port() to reserve an amba_ports[] entry,
+then calls pl011_register_port() to register the uart driver with the
+tty layer.
+
+If registration of the uart driver fails, the amba_ports[] entry is not
+released.  If this happens 14 times (value of UART_NR macro), then all
+amba_ports[] entries will have been leaked and driver probing is no
+longer possible.  (To be fair, that can only happen if the DeviceTree
+doesn't contain alias IDs since they cause the same entry to be used for
+a given port.)   Fix it.
+
+Fixes: ef2889f7ffee ("serial: pl011: Move uart_register_driver call to device")
+Signed-off-by: Lukas Wunner <lukas@wunner.de>
+Cc: stable@vger.kernel.org # v3.15+
+Cc: Tushar Behera <tushar.behera@linaro.org>
+Link: https://lore.kernel.org/r/138f8c15afb2f184d8102583f8301575566064a6.1597316167.git.lukas@wunner.de
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/tty/serial/amba-pl011.c |    5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+--- a/drivers/tty/serial/amba-pl011.c
++++ b/drivers/tty/serial/amba-pl011.c
+@@ -2532,7 +2532,7 @@ static int pl011_setup_port(struct devic
+ static int pl011_register_port(struct uart_amba_port *uap)
+ {
+-      int ret;
++      int ret, i;
+       /* Ensure interrupts from this UART are masked and cleared */
+       pl011_write(0, uap, REG_IMSC);
+@@ -2543,6 +2543,9 @@ static int pl011_register_port(struct ua
+               if (ret < 0) {
+                       dev_err(uap->port.dev,
+                               "Failed to register AMBA-PL011 driver\n");
++                      for (i = 0; i < ARRAY_SIZE(amba_ports); i++)
++                              if (amba_ports[i] == uap)
++                                      amba_ports[i] = NULL;
+                       return ret;
+               }
+       }
diff --git a/queue-4.9/serial-pl011-fix-oops-on-eprobe_defer.patch b/queue-4.9/serial-pl011-fix-oops-on-eprobe_defer.patch
new file mode 100644 (file)
index 0000000..749f181
--- /dev/null
@@ -0,0 +1,93 @@
+From 27afac93e3bd7fa89749cf11da5d86ac9cde4dba Mon Sep 17 00:00:00 2001
+From: Lukas Wunner <lukas@wunner.de>
+Date: Thu, 13 Aug 2020 12:52:40 +0200
+Subject: serial: pl011: Fix oops on -EPROBE_DEFER
+
+From: Lukas Wunner <lukas@wunner.de>
+
+commit 27afac93e3bd7fa89749cf11da5d86ac9cde4dba upstream.
+
+If probing of a pl011 gets deferred until after free_initmem(), an oops
+ensues because pl011_console_match() is called which has been freed.
+
+Fix by removing the __init attribute from the function and those it
+calls.
+
+Commit 10879ae5f12e ("serial: pl011: add console matching function")
+introduced pl011_console_match() not just for early consoles but
+regular preferred consoles, such as those added by acpi_parse_spcr().
+Regular consoles may be registered after free_initmem() for various
+reasons, one being deferred probing, another being dynamic enablement
+of serial ports using a DeviceTree overlay.
+
+Thus, pl011_console_match() must not be declared __init and the
+functions it calls mustn't either.
+
+Stack trace for posterity:
+
+Unable to handle kernel paging request at virtual address 80c38b58
+Internal error: Oops: 8000000d [#1] PREEMPT SMP ARM
+PC is at pl011_console_match+0x0/0xfc
+LR is at register_console+0x150/0x468
+[<80187004>] (register_console)
+[<805a8184>] (uart_add_one_port)
+[<805b2b68>] (pl011_register_port)
+[<805b3ce4>] (pl011_probe)
+[<80569214>] (amba_probe)
+[<805ca088>] (really_probe)
+[<805ca2ec>] (driver_probe_device)
+[<805ca5b0>] (__device_attach_driver)
+[<805c8060>] (bus_for_each_drv)
+[<805c9dfc>] (__device_attach)
+[<805ca630>] (device_initial_probe)
+[<805c90a8>] (bus_probe_device)
+[<805c95a8>] (deferred_probe_work_func)
+
+Fixes: 10879ae5f12e ("serial: pl011: add console matching function")
+Signed-off-by: Lukas Wunner <lukas@wunner.de>
+Cc: stable@vger.kernel.org # v4.10+
+Cc: Aleksey Makarov <amakarov@marvell.com>
+Cc: Peter Hurley <peter@hurleysoftware.com>
+Cc: Russell King <linux@armlinux.org.uk>
+Cc: Christopher Covington <cov@codeaurora.org>
+Link: https://lore.kernel.org/r/f827ff09da55b8c57d316a1b008a137677b58921.1597315557.git.lukas@wunner.de
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/tty/serial/amba-pl011.c |   11 +++++------
+ 1 file changed, 5 insertions(+), 6 deletions(-)
+
+--- a/drivers/tty/serial/amba-pl011.c
++++ b/drivers/tty/serial/amba-pl011.c
+@@ -2249,9 +2249,8 @@ pl011_console_write(struct console *co,
+       clk_disable(uap->clk);
+ }
+-static void __init
+-pl011_console_get_options(struct uart_amba_port *uap, int *baud,
+-                           int *parity, int *bits)
++static void pl011_console_get_options(struct uart_amba_port *uap, int *baud,
++                                    int *parity, int *bits)
+ {
+       if (pl011_read(uap, REG_CR) & UART01x_CR_UARTEN) {
+               unsigned int lcr_h, ibrd, fbrd;
+@@ -2284,7 +2283,7 @@ pl011_console_get_options(struct uart_am
+       }
+ }
+-static int __init pl011_console_setup(struct console *co, char *options)
++static int pl011_console_setup(struct console *co, char *options)
+ {
+       struct uart_amba_port *uap;
+       int baud = 38400;
+@@ -2352,8 +2351,8 @@ static int __init pl011_console_setup(st
+  *
+  *    Returns 0 if console matches; otherwise non-zero to use default matching
+  */
+-static int __init pl011_console_match(struct console *co, char *name, int idx,
+-                                    char *options)
++static int pl011_console_match(struct console *co, char *name, int idx,
++                             char *options)
+ {
+       unsigned char iotype;
+       resource_size_t addr;
diff --git a/queue-4.9/serial-samsung-removes-the-irq-not-found-warning.patch b/queue-4.9/serial-samsung-removes-the-irq-not-found-warning.patch
new file mode 100644 (file)
index 0000000..e6047db
--- /dev/null
@@ -0,0 +1,50 @@
+From 8c6c378b0cbe0c9f1390986b5f8ffb5f6ff7593b Mon Sep 17 00:00:00 2001
+From: Tamseel Shams <m.shams@samsung.com>
+Date: Mon, 10 Aug 2020 08:30:21 +0530
+Subject: serial: samsung: Removes the IRQ not found warning
+
+From: Tamseel Shams <m.shams@samsung.com>
+
+commit 8c6c378b0cbe0c9f1390986b5f8ffb5f6ff7593b upstream.
+
+In few older Samsung SoCs like s3c2410, s3c2412
+and s3c2440, UART IP is having 2 interrupt lines.
+However, in other SoCs like s3c6400, s5pv210,
+exynos5433, and exynos4210 UART is having only 1
+interrupt line. Due to this, "platform_get_irq(platdev, 1)"
+call in the driver gives the following false-positive error:
+"IRQ index 1 not found" on newer SoC's.
+
+This patch adds the condition to check for Tx interrupt
+only for the those SoC's which have 2 interrupt lines.
+
+Tested-by: Alim Akhtar <alim.akhtar@samsung.com>
+Tested-by: Marek Szyprowski <m.szyprowski@samsung.com>
+Reviewed-by: Krzysztof Kozlowski <krzk@kernel.org>
+Reviewed-by: Alim Akhtar <alim.akhtar@samsung.com>
+Signed-off-by: Tamseel Shams <m.shams@samsung.com>
+Cc: stable <stable@vger.kernel.org>
+Link: https://lore.kernel.org/r/20200810030021.45348-1-m.shams@samsung.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/tty/serial/samsung.c |    8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+--- a/drivers/tty/serial/samsung.c
++++ b/drivers/tty/serial/samsung.c
+@@ -1725,9 +1725,11 @@ static int s3c24xx_serial_init_port(stru
+               ourport->tx_irq = ret + 1;
+       }
+-      ret = platform_get_irq(platdev, 1);
+-      if (ret > 0)
+-              ourport->tx_irq = ret;
++      if (!s3c24xx_serial_has_interrupt_mask(port)) {
++              ret = platform_get_irq(platdev, 1);
++              if (ret > 0)
++                      ourport->tx_irq = ret;
++      }
+       /*
+        * DMA is currently supported only on DT platforms, if DMA properties
+        * are specified.
index e29d51421359304d1a62de459b993ec860415ad6..26589982737abe359545c30156d63a83efee0633 100644 (file)
@@ -46,3 +46,18 @@ scsi-ufs-fix-possible-infinite-loop-in-ufshcd_hold.patch
 scsi-ufs-improve-interrupt-handling-for-shared-inter.patch
 net-gianfar-add-of_node_put-before-goto-statement.patch
 powerpc-perf-fix-soft-lockups-due-to-missed-interrup.patch
+hid-i2c-hid-always-sleep-60ms-after-i2c_hid_pwr_on-commands.patch
+btrfs-fix-space-cache-memory-leak-after-transaction-abort.patch
+fbcon-prevent-user-font-height-or-width-change-from-causing-potential-out-of-bounds-access.patch
+usb-lvtest-return-proper-error-code-in-probe.patch
+vt-defer-kfree-of-vc_screenbuf-in-vc_do_resize.patch
+vt_ioctl-change-vt_resizex-ioctl-to-check-for-error-return-from-vc_resize.patch
+serial-samsung-removes-the-irq-not-found-warning.patch
+serial-pl011-fix-oops-on-eprobe_defer.patch
+serial-pl011-don-t-leak-amba_ports-entry-on-driver-register-error.patch
+serial-8250-change-lock-order-in-serial8250_do_startup.patch
+writeback-protect-inode-i_io_list-with-inode-i_lock.patch
+writeback-avoid-skipping-inode-writeback.patch
+writeback-fix-sync-livelock-due-to-b_dirty_time-processing.patch
+xen-uses-irqdesc-irq_data_common-handler_data-to-store-a-per-interrupt-xen-data-pointer-which-contains-xen-specific-information.patch
+xhci-do-warm-reset-when-both-cas-and-xdev_resume-are-set.patch
diff --git a/queue-4.9/usb-lvtest-return-proper-error-code-in-probe.patch b/queue-4.9/usb-lvtest-return-proper-error-code-in-probe.patch
new file mode 100644 (file)
index 0000000..b98c590
--- /dev/null
@@ -0,0 +1,35 @@
+From 531412492ce93ea29b9ca3b4eb5e3ed771f851dd Mon Sep 17 00:00:00 2001
+From: Evgeny Novikov <novikov@ispras.ru>
+Date: Wed, 5 Aug 2020 12:06:43 +0300
+Subject: USB: lvtest: return proper error code in probe
+
+From: Evgeny Novikov <novikov@ispras.ru>
+
+commit 531412492ce93ea29b9ca3b4eb5e3ed771f851dd upstream.
+
+lvs_rh_probe() can return some nonnegative value from usb_control_msg()
+when it is less than "USB_DT_HUB_NONVAR_SIZE + 2" that is considered as
+a failure. Make lvs_rh_probe() return -EINVAL in this case.
+
+Found by Linux Driver Verification project (linuxtesting.org).
+
+Signed-off-by: Evgeny Novikov <novikov@ispras.ru>
+Cc: stable <stable@vger.kernel.org>
+Link: https://lore.kernel.org/r/20200805090643.3432-1-novikov@ispras.ru
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/usb/misc/lvstest.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/usb/misc/lvstest.c
++++ b/drivers/usb/misc/lvstest.c
+@@ -392,7 +392,7 @@ static int lvs_rh_probe(struct usb_inter
+                       USB_DT_SS_HUB_SIZE, USB_CTRL_GET_TIMEOUT);
+       if (ret < (USB_DT_HUB_NONVAR_SIZE + 2)) {
+               dev_err(&hdev->dev, "wrong root hub descriptor read %d\n", ret);
+-              return ret;
++              return ret < 0 ? ret : -EINVAL;
+       }
+       /* submit urb to poll interrupt endpoint */
diff --git a/queue-4.9/vt-defer-kfree-of-vc_screenbuf-in-vc_do_resize.patch b/queue-4.9/vt-defer-kfree-of-vc_screenbuf-in-vc_do_resize.patch
new file mode 100644 (file)
index 0000000..5719ba1
--- /dev/null
@@ -0,0 +1,57 @@
+From f8d1653daec02315e06d30246cff4af72e76e54e Mon Sep 17 00:00:00 2001
+From: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
+Date: Wed, 29 Jul 2020 23:57:01 +0900
+Subject: vt: defer kfree() of vc_screenbuf in vc_do_resize()
+
+From: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
+
+commit f8d1653daec02315e06d30246cff4af72e76e54e upstream.
+
+syzbot is reporting UAF bug in set_origin() from vc_do_resize() [1], for
+vc_do_resize() calls kfree(vc->vc_screenbuf) before calling set_origin().
+
+Unfortunately, in set_origin(), vc->vc_sw->con_set_origin() might access
+vc->vc_pos when scroll is involved in order to manipulate cursor, but
+vc->vc_pos refers already released vc->vc_screenbuf until vc->vc_pos gets
+updated based on the result of vc->vc_sw->con_set_origin().
+
+Preserving old buffer and tolerating outdated vc members until set_origin()
+completes would be easier than preventing vc->vc_sw->con_set_origin() from
+accessing outdated vc members.
+
+[1] https://syzkaller.appspot.com/bug?id=6649da2081e2ebdc65c0642c214b27fe91099db3
+
+Reported-by: syzbot <syzbot+9116ecc1978ca3a12f43@syzkaller.appspotmail.com>
+Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
+Cc: stable <stable@vger.kernel.org>
+Link: https://lore.kernel.org/r/1596034621-4714-1-git-send-email-penguin-kernel@I-love.SAKURA.ne.jp
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/tty/vt/vt.c |    5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+--- a/drivers/tty/vt/vt.c
++++ b/drivers/tty/vt/vt.c
+@@ -868,7 +868,7 @@ static int vc_do_resize(struct tty_struc
+       unsigned int old_rows, old_row_size;
+       unsigned int new_cols, new_rows, new_row_size, new_screen_size;
+       unsigned int user;
+-      unsigned short *newscreen;
++      unsigned short *oldscreen, *newscreen;
+       WARN_CONSOLE_UNLOCKED();
+@@ -950,10 +950,11 @@ static int vc_do_resize(struct tty_struc
+       if (new_scr_end > new_origin)
+               scr_memsetw((void *)new_origin, vc->vc_video_erase_char,
+                           new_scr_end - new_origin);
+-      kfree(vc->vc_screenbuf);
++      oldscreen = vc->vc_screenbuf;
+       vc->vc_screenbuf = newscreen;
+       vc->vc_screenbuf_size = new_screen_size;
+       set_origin(vc);
++      kfree(oldscreen);
+       /* do part of a reset_terminal() */
+       vc->vc_top = 0;
diff --git a/queue-4.9/vt_ioctl-change-vt_resizex-ioctl-to-check-for-error-return-from-vc_resize.patch b/queue-4.9/vt_ioctl-change-vt_resizex-ioctl-to-check-for-error-return-from-vc_resize.patch
new file mode 100644 (file)
index 0000000..8fc6bc1
--- /dev/null
@@ -0,0 +1,49 @@
+From bc5269ca765057a1b762e79a1cfd267cd7bf1c46 Mon Sep 17 00:00:00 2001
+From: George Kennedy <george.kennedy@oracle.com>
+Date: Fri, 31 Jul 2020 12:33:12 -0400
+Subject: vt_ioctl: change VT_RESIZEX ioctl to check for error return from vc_resize()
+
+From: George Kennedy <george.kennedy@oracle.com>
+
+commit bc5269ca765057a1b762e79a1cfd267cd7bf1c46 upstream.
+
+vc_resize() can return with an error after failure. Change VT_RESIZEX ioctl
+to save struct vc_data values that are modified and restore the original
+values in case of error.
+
+Signed-off-by: George Kennedy <george.kennedy@oracle.com>
+Cc: stable <stable@vger.kernel.org>
+Reported-by: syzbot+38a3699c7eaf165b97a6@syzkaller.appspotmail.com
+Link: https://lore.kernel.org/r/1596213192-6635-2-git-send-email-george.kennedy@oracle.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/tty/vt/vt_ioctl.c |   12 +++++++++++-
+ 1 file changed, 11 insertions(+), 1 deletion(-)
+
+--- a/drivers/tty/vt/vt_ioctl.c
++++ b/drivers/tty/vt/vt_ioctl.c
+@@ -896,12 +896,22 @@ int vt_ioctl(struct tty_struct *tty,
+                       console_lock();
+                       vcp = vc_cons[i].d;
+                       if (vcp) {
++                              int ret;
++                              int save_scan_lines = vcp->vc_scan_lines;
++                              int save_font_height = vcp->vc_font.height;
++
+                               if (v.v_vlin)
+                                       vcp->vc_scan_lines = v.v_vlin;
+                               if (v.v_clin)
+                                       vcp->vc_font.height = v.v_clin;
+                               vcp->vc_resize_user = 1;
+-                              vc_resize(vcp, v.v_cols, v.v_rows);
++                              ret = vc_resize(vcp, v.v_cols, v.v_rows);
++                              if (ret) {
++                                      vcp->vc_scan_lines = save_scan_lines;
++                                      vcp->vc_font.height = save_font_height;
++                                      console_unlock();
++                                      return ret;
++                              }
+                       }
+                       console_unlock();
+               }
diff --git a/queue-4.9/writeback-avoid-skipping-inode-writeback.patch b/queue-4.9/writeback-avoid-skipping-inode-writeback.patch
new file mode 100644 (file)
index 0000000..10f1185
--- /dev/null
@@ -0,0 +1,146 @@
+From 5afced3bf28100d81fb2fe7e98918632a08feaf5 Mon Sep 17 00:00:00 2001
+From: Jan Kara <jack@suse.cz>
+Date: Fri, 29 May 2020 15:05:22 +0200
+Subject: writeback: Avoid skipping inode writeback
+
+From: Jan Kara <jack@suse.cz>
+
+commit 5afced3bf28100d81fb2fe7e98918632a08feaf5 upstream.
+
+Inode's i_io_list list head is used to attach inode to several different
+lists - wb->{b_dirty, b_dirty_time, b_io, b_more_io}. When flush worker
+prepares a list of inodes to writeback e.g. for sync(2), it moves inodes
+to b_io list. Thus it is critical for sync(2) data integrity guarantees
+that inode is not requeued to any other writeback list when inode is
+queued for processing by flush worker. That's the reason why
+writeback_single_inode() does not touch i_io_list (unless the inode is
+completely clean) and why __mark_inode_dirty() does not touch i_io_list
+if I_SYNC flag is set.
+
+However there are two flaws in the current logic:
+
+1) When inode has only I_DIRTY_TIME set but it is already queued in b_io
+list due to sync(2), concurrent __mark_inode_dirty(inode, I_DIRTY_SYNC)
+can still move inode back to b_dirty list resulting in skipping
+writeback of inode time stamps during sync(2).
+
+2) When inode is on b_dirty_time list and writeback_single_inode() races
+with __mark_inode_dirty() like:
+
+writeback_single_inode()               __mark_inode_dirty(inode, I_DIRTY_PAGES)
+  inode->i_state |= I_SYNC
+  __writeback_single_inode()
+                                         inode->i_state |= I_DIRTY_PAGES;
+                                         if (inode->i_state & I_SYNC)
+                                           bail
+  if (!(inode->i_state & I_DIRTY_ALL))
+  - not true so nothing done
+
+We end up with I_DIRTY_PAGES inode on b_dirty_time list and thus
+standard background writeback will not writeback this inode leading to
+possible dirty throttling stalls etc. (thanks to Martijn Coenen for this
+analysis).
+
+Fix these problems by tracking whether inode is queued in b_io or
+b_more_io lists in a new I_SYNC_QUEUED flag. When this flag is set, we
+know flush worker has queued inode and we should not touch i_io_list.
+On the other hand we also know that once flush worker is done with the
+inode it will requeue the inode to appropriate dirty list. When
+I_SYNC_QUEUED is not set, __mark_inode_dirty() can (and must) move inode
+to appropriate dirty list.
+
+Reported-by: Martijn Coenen <maco@android.com>
+Reviewed-by: Martijn Coenen <maco@android.com>
+Tested-by: Martijn Coenen <maco@android.com>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Fixes: 0ae45f63d4ef ("vfs: add support for a lazytime mount option")
+CC: stable@vger.kernel.org
+Signed-off-by: Jan Kara <jack@suse.cz>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ fs/fs-writeback.c  |   17 ++++++++++++-----
+ include/linux/fs.h |    8 ++++++--
+ 2 files changed, 18 insertions(+), 7 deletions(-)
+
+--- a/fs/fs-writeback.c
++++ b/fs/fs-writeback.c
+@@ -162,6 +162,7 @@ static void inode_io_list_del_locked(str
+       assert_spin_locked(&wb->list_lock);
+       assert_spin_locked(&inode->i_lock);
++      inode->i_state &= ~I_SYNC_QUEUED;
+       list_del_init(&inode->i_io_list);
+       wb_io_lists_depopulated(wb);
+ }
+@@ -1103,6 +1104,7 @@ static void redirty_tail_locked(struct i
+                       inode->dirtied_when = jiffies;
+       }
+       inode_io_list_move_locked(inode, wb, &wb->b_dirty);
++      inode->i_state &= ~I_SYNC_QUEUED;
+ }
+ static void redirty_tail(struct inode *inode, struct bdi_writeback *wb)
+@@ -1178,8 +1180,11 @@ static int move_expired_inodes(struct li
+                       break;
+               list_move(&inode->i_io_list, &tmp);
+               moved++;
++              spin_lock(&inode->i_lock);
+               if (flags & EXPIRE_DIRTY_ATIME)
+-                      set_bit(__I_DIRTY_TIME_EXPIRED, &inode->i_state);
++                      inode->i_state |= I_DIRTY_TIME_EXPIRED;
++              inode->i_state |= I_SYNC_QUEUED;
++              spin_unlock(&inode->i_lock);
+               if (sb_is_blkdev_sb(inode->i_sb))
+                       continue;
+               if (sb && sb != inode->i_sb)
+@@ -1354,6 +1359,7 @@ static void requeue_inode(struct inode *
+       } else if (inode->i_state & I_DIRTY_TIME) {
+               inode->dirtied_when = jiffies;
+               inode_io_list_move_locked(inode, wb, &wb->b_dirty_time);
++              inode->i_state &= ~I_SYNC_QUEUED;
+       } else {
+               /* The inode is clean. Remove from writeback lists. */
+               inode_io_list_del_locked(inode, wb);
+@@ -2188,11 +2194,12 @@ void __mark_inode_dirty(struct inode *in
+               inode->i_state |= flags;
+               /*
+-               * If the inode is being synced, just update its dirty state.
+-               * The unlocker will place the inode on the appropriate
+-               * superblock list, based upon its state.
++               * If the inode is queued for writeback by flush worker, just
++               * update its dirty state. Once the flush worker is done with
++               * the inode it will place it on the appropriate superblock
++               * list, based upon its state.
+                */
+-              if (inode->i_state & I_SYNC)
++              if (inode->i_state & I_SYNC_QUEUED)
+                       goto out_unlock_inode;
+               /*
+--- a/include/linux/fs.h
++++ b/include/linux/fs.h
+@@ -1954,6 +1954,10 @@ static inline bool HAS_UNMAPPED_ID(struc
+  *                    wb stat updates to grab mapping->tree_lock.  See
+  *                    inode_switch_wb_work_fn() for details.
+  *
++ * I_SYNC_QUEUED      Inode is queued in b_io or b_more_io writeback lists.
++ *                    Used to detect that mark_inode_dirty() should not move
++ *                    inode between dirty lists.
++ *
+  * Q: What is the difference between I_WILL_FREE and I_FREEING?
+  */
+ #define I_DIRTY_SYNC          (1 << 0)
+@@ -1971,9 +1975,9 @@ static inline bool HAS_UNMAPPED_ID(struc
+ #define I_DIO_WAKEUP          (1 << __I_DIO_WAKEUP)
+ #define I_LINKABLE            (1 << 10)
+ #define I_DIRTY_TIME          (1 << 11)
+-#define __I_DIRTY_TIME_EXPIRED        12
+-#define I_DIRTY_TIME_EXPIRED  (1 << __I_DIRTY_TIME_EXPIRED)
++#define I_DIRTY_TIME_EXPIRED  (1 << 12)
+ #define I_WB_SWITCH           (1 << 13)
++#define I_SYNC_QUEUED         (1 << 17)
+ #define I_DIRTY (I_DIRTY_SYNC | I_DIRTY_DATASYNC | I_DIRTY_PAGES)
+ #define I_DIRTY_ALL (I_DIRTY | I_DIRTY_TIME)
diff --git a/queue-4.9/writeback-fix-sync-livelock-due-to-b_dirty_time-processing.patch b/queue-4.9/writeback-fix-sync-livelock-due-to-b_dirty_time-processing.patch
new file mode 100644 (file)
index 0000000..e423fbc
--- /dev/null
@@ -0,0 +1,193 @@
+From f9cae926f35e8230330f28c7b743ad088611a8de Mon Sep 17 00:00:00 2001
+From: Jan Kara <jack@suse.cz>
+Date: Fri, 29 May 2020 16:08:58 +0200
+Subject: writeback: Fix sync livelock due to b_dirty_time processing
+
+From: Jan Kara <jack@suse.cz>
+
+commit f9cae926f35e8230330f28c7b743ad088611a8de upstream.
+
+When we are processing writeback for sync(2), move_expired_inodes()
+didn't set any inode expiry value (older_than_this). This can result in
+writeback never completing if there's steady stream of inodes added to
+b_dirty_time list as writeback rechecks dirty lists after each writeback
+round whether there's more work to be done. Fix the problem by using
+sync(2) start time is inode expiry value when processing b_dirty_time
+list similarly as for ordinarily dirtied inodes. This requires some
+refactoring of older_than_this handling which simplifies the code
+noticeably as a bonus.
+
+Fixes: 0ae45f63d4ef ("vfs: add support for a lazytime mount option")
+CC: stable@vger.kernel.org
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Signed-off-by: Jan Kara <jack@suse.cz>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ fs/fs-writeback.c                |   44 +++++++++++++++------------------------
+ include/trace/events/writeback.h |   13 +++++------
+ 2 files changed, 23 insertions(+), 34 deletions(-)
+
+--- a/fs/fs-writeback.c
++++ b/fs/fs-writeback.c
+@@ -45,7 +45,6 @@ struct wb_completion {
+ struct wb_writeback_work {
+       long nr_pages;
+       struct super_block *sb;
+-      unsigned long *older_than_this;
+       enum writeback_sync_modes sync_mode;
+       unsigned int tagged_writepages:1;
+       unsigned int for_kupdate:1;
+@@ -1150,16 +1149,13 @@ static bool inode_dirtied_after(struct i
+ #define EXPIRE_DIRTY_ATIME 0x0001
+ /*
+- * Move expired (dirtied before work->older_than_this) dirty inodes from
++ * Move expired (dirtied before dirtied_before) dirty inodes from
+  * @delaying_queue to @dispatch_queue.
+  */
+ static int move_expired_inodes(struct list_head *delaying_queue,
+                              struct list_head *dispatch_queue,
+-                             int flags,
+-                             struct wb_writeback_work *work)
++                             int flags, unsigned long dirtied_before)
+ {
+-      unsigned long *older_than_this = NULL;
+-      unsigned long expire_time;
+       LIST_HEAD(tmp);
+       struct list_head *pos, *node;
+       struct super_block *sb = NULL;
+@@ -1167,16 +1163,9 @@ static int move_expired_inodes(struct li
+       int do_sb_sort = 0;
+       int moved = 0;
+-      if ((flags & EXPIRE_DIRTY_ATIME) == 0)
+-              older_than_this = work->older_than_this;
+-      else if (!work->for_sync) {
+-              expire_time = jiffies - (dirtytime_expire_interval * HZ);
+-              older_than_this = &expire_time;
+-      }
+       while (!list_empty(delaying_queue)) {
+               inode = wb_inode(delaying_queue->prev);
+-              if (older_than_this &&
+-                  inode_dirtied_after(inode, *older_than_this))
++              if (inode_dirtied_after(inode, dirtied_before))
+                       break;
+               list_move(&inode->i_io_list, &tmp);
+               moved++;
+@@ -1222,18 +1211,22 @@ out:
+  *                                           |
+  *                                           +--> dequeue for IO
+  */
+-static void queue_io(struct bdi_writeback *wb, struct wb_writeback_work *work)
++static void queue_io(struct bdi_writeback *wb, struct wb_writeback_work *work,
++                   unsigned long dirtied_before)
+ {
+       int moved;
++      unsigned long time_expire_jif = dirtied_before;
+       assert_spin_locked(&wb->list_lock);
+       list_splice_init(&wb->b_more_io, &wb->b_io);
+-      moved = move_expired_inodes(&wb->b_dirty, &wb->b_io, 0, work);
++      moved = move_expired_inodes(&wb->b_dirty, &wb->b_io, 0, dirtied_before);
++      if (!work->for_sync)
++              time_expire_jif = jiffies - dirtytime_expire_interval * HZ;
+       moved += move_expired_inodes(&wb->b_dirty_time, &wb->b_io,
+-                                   EXPIRE_DIRTY_ATIME, work);
++                                   EXPIRE_DIRTY_ATIME, time_expire_jif);
+       if (moved)
+               wb_io_lists_populated(wb);
+-      trace_writeback_queue_io(wb, work, moved);
++      trace_writeback_queue_io(wb, work, dirtied_before, moved);
+ }
+ static int write_inode(struct inode *inode, struct writeback_control *wbc)
+@@ -1745,7 +1738,7 @@ static long writeback_inodes_wb(struct b
+       blk_start_plug(&plug);
+       spin_lock(&wb->list_lock);
+       if (list_empty(&wb->b_io))
+-              queue_io(wb, &work);
++              queue_io(wb, &work, jiffies);
+       __writeback_inodes_wb(wb, &work);
+       spin_unlock(&wb->list_lock);
+       blk_finish_plug(&plug);
+@@ -1765,7 +1758,7 @@ static long writeback_inodes_wb(struct b
+  * takes longer than a dirty_writeback_interval interval, then leave a
+  * one-second gap.
+  *
+- * older_than_this takes precedence over nr_to_write.  So we'll only write back
++ * dirtied_before takes precedence over nr_to_write.  So we'll only write back
+  * all dirty pages if they are all attached to "old" mappings.
+  */
+ static long wb_writeback(struct bdi_writeback *wb,
+@@ -1773,14 +1766,11 @@ static long wb_writeback(struct bdi_writ
+ {
+       unsigned long wb_start = jiffies;
+       long nr_pages = work->nr_pages;
+-      unsigned long oldest_jif;
++      unsigned long dirtied_before = jiffies;
+       struct inode *inode;
+       long progress;
+       struct blk_plug plug;
+-      oldest_jif = jiffies;
+-      work->older_than_this = &oldest_jif;
+-
+       blk_start_plug(&plug);
+       spin_lock(&wb->list_lock);
+       for (;;) {
+@@ -1814,14 +1804,14 @@ static long wb_writeback(struct bdi_writ
+                * safe.
+                */
+               if (work->for_kupdate) {
+-                      oldest_jif = jiffies -
++                      dirtied_before = jiffies -
+                               msecs_to_jiffies(dirty_expire_interval * 10);
+               } else if (work->for_background)
+-                      oldest_jif = jiffies;
++                      dirtied_before = jiffies;
+               trace_writeback_start(wb, work);
+               if (list_empty(&wb->b_io))
+-                      queue_io(wb, work);
++                      queue_io(wb, work, dirtied_before);
+               if (work->sb)
+                       progress = writeback_sb_inodes(work->sb, wb, work);
+               else
+--- a/include/trace/events/writeback.h
++++ b/include/trace/events/writeback.h
+@@ -360,8 +360,9 @@ DEFINE_WBC_EVENT(wbc_writepage);
+ TRACE_EVENT(writeback_queue_io,
+       TP_PROTO(struct bdi_writeback *wb,
+                struct wb_writeback_work *work,
++               unsigned long dirtied_before,
+                int moved),
+-      TP_ARGS(wb, work, moved),
++      TP_ARGS(wb, work, dirtied_before, moved),
+       TP_STRUCT__entry(
+               __array(char,           name, 32)
+               __field(unsigned long,  older)
+@@ -371,19 +372,17 @@ TRACE_EVENT(writeback_queue_io,
+               __field(unsigned int,   cgroup_ino)
+       ),
+       TP_fast_assign(
+-              unsigned long *older_than_this = work->older_than_this;
+               strncpy(__entry->name, dev_name(wb->bdi->dev), 32);
+-              __entry->older  = older_than_this ?  *older_than_this : 0;
+-              __entry->age    = older_than_this ?
+-                                (jiffies - *older_than_this) * 1000 / HZ : -1;
++              __entry->older  = dirtied_before;
++              __entry->age    = (jiffies - dirtied_before) * 1000 / HZ;
+               __entry->moved  = moved;
+               __entry->reason = work->reason;
+               __entry->cgroup_ino     = __trace_wb_assign_cgroup(wb);
+       ),
+       TP_printk("bdi %s: older=%lu age=%ld enqueue=%d reason=%s cgroup_ino=%u",
+               __entry->name,
+-              __entry->older, /* older_than_this in jiffies */
+-              __entry->age,   /* older_than_this in relative milliseconds */
++              __entry->older, /* dirtied_before in jiffies */
++              __entry->age,   /* dirtied_before in relative milliseconds */
+               __entry->moved,
+               __print_symbolic(__entry->reason, WB_WORK_REASON),
+               __entry->cgroup_ino
diff --git a/queue-4.9/writeback-protect-inode-i_io_list-with-inode-i_lock.patch b/queue-4.9/writeback-protect-inode-i_io_list-with-inode-i_lock.patch
new file mode 100644 (file)
index 0000000..2f5f0bb
--- /dev/null
@@ -0,0 +1,107 @@
+From b35250c0816c7cf7d0a8de92f5fafb6a7508a708 Mon Sep 17 00:00:00 2001
+From: Jan Kara <jack@suse.cz>
+Date: Wed, 10 Jun 2020 17:36:03 +0200
+Subject: writeback: Protect inode->i_io_list with inode->i_lock
+
+From: Jan Kara <jack@suse.cz>
+
+commit b35250c0816c7cf7d0a8de92f5fafb6a7508a708 upstream.
+
+Currently, operations on inode->i_io_list are protected by
+wb->list_lock. In the following patches we'll need to maintain
+consistency between inode->i_state and inode->i_io_list so change the
+code so that inode->i_lock protects also all inode's i_io_list handling.
+
+Reviewed-by: Martijn Coenen <maco@android.com>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+CC: stable@vger.kernel.org # Prerequisite for "writeback: Avoid skipping inode writeback"
+Signed-off-by: Jan Kara <jack@suse.cz>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ fs/fs-writeback.c |   22 +++++++++++++++++-----
+ 1 file changed, 17 insertions(+), 5 deletions(-)
+
+--- a/fs/fs-writeback.c
++++ b/fs/fs-writeback.c
+@@ -160,6 +160,7 @@ static void inode_io_list_del_locked(str
+                                    struct bdi_writeback *wb)
+ {
+       assert_spin_locked(&wb->list_lock);
++      assert_spin_locked(&inode->i_lock);
+       list_del_init(&inode->i_io_list);
+       wb_io_lists_depopulated(wb);
+@@ -1039,7 +1040,9 @@ void inode_io_list_del(struct inode *ino
+       struct bdi_writeback *wb;
+       wb = inode_to_wb_and_lock_list(inode);
++      spin_lock(&inode->i_lock);
+       inode_io_list_del_locked(inode, wb);
++      spin_unlock(&inode->i_lock);
+       spin_unlock(&wb->list_lock);
+ }
+@@ -1088,8 +1091,10 @@ void sb_clear_inode_writeback(struct ino
+  * the case then the inode must have been redirtied while it was being written
+  * out and we don't reset its dirtied_when.
+  */
+-static void redirty_tail(struct inode *inode, struct bdi_writeback *wb)
++static void redirty_tail_locked(struct inode *inode, struct bdi_writeback *wb)
+ {
++      assert_spin_locked(&inode->i_lock);
++
+       if (!list_empty(&wb->b_dirty)) {
+               struct inode *tail;
+@@ -1100,6 +1105,13 @@ static void redirty_tail(struct inode *i
+       inode_io_list_move_locked(inode, wb, &wb->b_dirty);
+ }
++static void redirty_tail(struct inode *inode, struct bdi_writeback *wb)
++{
++      spin_lock(&inode->i_lock);
++      redirty_tail_locked(inode, wb);
++      spin_unlock(&inode->i_lock);
++}
++
+ /*
+  * requeue inode for re-scanning after bdi->b_io list is exhausted.
+  */
+@@ -1310,7 +1322,7 @@ static void requeue_inode(struct inode *
+                * writeback is not making progress due to locked
+                * buffers. Skip this inode for now.
+                */
+-              redirty_tail(inode, wb);
++              redirty_tail_locked(inode, wb);
+               return;
+       }
+@@ -1330,7 +1342,7 @@ static void requeue_inode(struct inode *
+                        * retrying writeback of the dirty page/inode
+                        * that cannot be performed immediately.
+                        */
+-                      redirty_tail(inode, wb);
++                      redirty_tail_locked(inode, wb);
+               }
+       } else if (inode->i_state & I_DIRTY) {
+               /*
+@@ -1338,7 +1350,7 @@ static void requeue_inode(struct inode *
+                * such as delayed allocation during submission or metadata
+                * updates after data IO completion.
+                */
+-              redirty_tail(inode, wb);
++              redirty_tail_locked(inode, wb);
+       } else if (inode->i_state & I_DIRTY_TIME) {
+               inode->dirtied_when = jiffies;
+               inode_io_list_move_locked(inode, wb, &wb->b_dirty_time);
+@@ -1585,8 +1597,8 @@ static long writeback_sb_inodes(struct s
+                */
+               spin_lock(&inode->i_lock);
+               if (inode->i_state & (I_NEW | I_FREEING | I_WILL_FREE)) {
++                      redirty_tail_locked(inode, wb);
+                       spin_unlock(&inode->i_lock);
+-                      redirty_tail(inode, wb);
+                       continue;
+               }
+               if ((inode->i_state & I_SYNC) && wbc.sync_mode != WB_SYNC_ALL) {
diff --git a/queue-4.9/xen-uses-irqdesc-irq_data_common-handler_data-to-store-a-per-interrupt-xen-data-pointer-which-contains-xen-specific-information.patch b/queue-4.9/xen-uses-irqdesc-irq_data_common-handler_data-to-store-a-per-interrupt-xen-data-pointer-which-contains-xen-specific-information.patch
new file mode 100644 (file)
index 0000000..f91da2a
--- /dev/null
@@ -0,0 +1,107 @@
+From c330fb1ddc0a922f044989492b7fcca77ee1db46 Mon Sep 17 00:00:00 2001
+From: Thomas Gleixner <tglx@linutronix.de>
+Date: Tue, 25 Aug 2020 17:22:58 +0200
+Subject: XEN uses irqdesc::irq_data_common::handler_data to store a per interrupt XEN data pointer which contains XEN specific information.
+
+From: Thomas Gleixner <tglx@linutronix.de>
+
+commit c330fb1ddc0a922f044989492b7fcca77ee1db46 upstream.
+
+handler data is meant for interrupt handlers and not for storing irq chip
+specific information as some devices require handler data to store internal
+per interrupt information, e.g. pinctrl/GPIO chained interrupt handlers.
+
+This obviously creates a conflict of interests and crashes the machine
+because the XEN pointer is overwritten by the driver pointer.
+
+As the XEN data is not handler specific it should be stored in
+irqdesc::irq_data::chip_data instead.
+
+A simple sed s/irq_[sg]et_handler_data/irq_[sg]et_chip_data/ cures that.
+
+Cc: stable@vger.kernel.org
+Reported-by: Roman Shaposhnik <roman@zededa.com>
+Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
+Tested-by: Roman Shaposhnik <roman@zededa.com>
+Reviewed-by: Juergen Gross <jgross@suse.com>
+Link: https://lore.kernel.org/r/87lfi2yckt.fsf@nanos.tec.linutronix.de
+Signed-off-by: Juergen Gross <jgross@suse.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/xen/events/events_base.c |   16 ++++++++--------
+ 1 file changed, 8 insertions(+), 8 deletions(-)
+
+--- a/drivers/xen/events/events_base.c
++++ b/drivers/xen/events/events_base.c
+@@ -155,7 +155,7 @@ int get_evtchn_to_irq(unsigned evtchn)
+ /* Get info for IRQ */
+ struct irq_info *info_for_irq(unsigned irq)
+ {
+-      return irq_get_handler_data(irq);
++      return irq_get_chip_data(irq);
+ }
+ /* Constructors for packed IRQ information. */
+@@ -384,7 +384,7 @@ static void xen_irq_init(unsigned irq)
+       info->type = IRQT_UNBOUND;
+       info->refcnt = -1;
+-      irq_set_handler_data(irq, info);
++      irq_set_chip_data(irq, info);
+       list_add_tail(&info->list, &xen_irq_list_head);
+ }
+@@ -433,14 +433,14 @@ static int __must_check xen_allocate_irq
+ static void xen_free_irq(unsigned irq)
+ {
+-      struct irq_info *info = irq_get_handler_data(irq);
++      struct irq_info *info = irq_get_chip_data(irq);
+       if (WARN_ON(!info))
+               return;
+       list_del(&info->list);
+-      irq_set_handler_data(irq, NULL);
++      irq_set_chip_data(irq, NULL);
+       WARN_ON(info->refcnt > 0);
+@@ -610,7 +610,7 @@ EXPORT_SYMBOL_GPL(xen_irq_from_gsi);
+ static void __unbind_from_irq(unsigned int irq)
+ {
+       int evtchn = evtchn_from_irq(irq);
+-      struct irq_info *info = irq_get_handler_data(irq);
++      struct irq_info *info = irq_get_chip_data(irq);
+       if (info->refcnt > 0) {
+               info->refcnt--;
+@@ -1114,7 +1114,7 @@ int bind_ipi_to_irqhandler(enum ipi_vect
+ void unbind_from_irqhandler(unsigned int irq, void *dev_id)
+ {
+-      struct irq_info *info = irq_get_handler_data(irq);
++      struct irq_info *info = irq_get_chip_data(irq);
+       if (WARN_ON(!info))
+               return;
+@@ -1148,7 +1148,7 @@ int evtchn_make_refcounted(unsigned int
+       if (irq == -1)
+               return -ENOENT;
+-      info = irq_get_handler_data(irq);
++      info = irq_get_chip_data(irq);
+       if (!info)
+               return -ENOENT;
+@@ -1176,7 +1176,7 @@ int evtchn_get(unsigned int evtchn)
+       if (irq == -1)
+               goto done;
+-      info = irq_get_handler_data(irq);
++      info = irq_get_chip_data(irq);
+       if (!info)
+               goto done;
diff --git a/queue-4.9/xhci-do-warm-reset-when-both-cas-and-xdev_resume-are-set.patch b/queue-4.9/xhci-do-warm-reset-when-both-cas-and-xdev_resume-are-set.patch
new file mode 100644 (file)
index 0000000..6ac2633
--- /dev/null
@@ -0,0 +1,69 @@
+From 904df64a5f4d5ebd670801d869ca0a6d6a6e8df6 Mon Sep 17 00:00:00 2001
+From: Kai-Heng Feng <kai.heng.feng@canonical.com>
+Date: Fri, 21 Aug 2020 12:15:48 +0300
+Subject: xhci: Do warm-reset when both CAS and XDEV_RESUME are set
+
+From: Kai-Heng Feng <kai.heng.feng@canonical.com>
+
+commit 904df64a5f4d5ebd670801d869ca0a6d6a6e8df6 upstream.
+
+Sometimes re-plugging a USB device during system sleep renders the device
+useless:
+[  173.418345] xhci_hcd 0000:00:14.0: Get port status 2-4 read: 0x14203e2, return 0x10262
+...
+[  176.496485] usb 2-4: Waited 2000ms for CONNECT
+[  176.496781] usb usb2-port4: status 0000.0262 after resume, -19
+[  176.497103] usb 2-4: can't resume, status -19
+[  176.497438] usb usb2-port4: logical disconnect
+
+Because PLS equals to XDEV_RESUME, xHCI driver reports U3 to usbcore,
+despite of CAS bit is flagged.
+
+So proritize CAS over XDEV_RESUME to let usbcore handle warm-reset for
+the port.
+
+Cc: stable <stable@vger.kernel.org>
+Signed-off-by: Kai-Heng Feng <kai.heng.feng@canonical.com>
+Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
+Link: https://lore.kernel.org/r/20200821091549.20556-3-mathias.nyman@linux.intel.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/usb/host/xhci-hub.c |   19 ++++++++++---------
+ 1 file changed, 10 insertions(+), 9 deletions(-)
+
+--- a/drivers/usb/host/xhci-hub.c
++++ b/drivers/usb/host/xhci-hub.c
+@@ -623,15 +623,6 @@ static void xhci_hub_report_usb3_link_st
+ {
+       u32 pls = status_reg & PORT_PLS_MASK;
+-      /* resume state is a xHCI internal state.
+-       * Do not report it to usb core, instead, pretend to be U3,
+-       * thus usb core knows it's not ready for transfer
+-       */
+-      if (pls == XDEV_RESUME) {
+-              *status |= USB_SS_PORT_LS_U3;
+-              return;
+-      }
+-
+       /* When the CAS bit is set then warm reset
+        * should be performed on port
+        */
+@@ -654,6 +645,16 @@ static void xhci_hub_report_usb3_link_st
+               pls |= USB_PORT_STAT_CONNECTION;
+       } else {
+               /*
++               * Resume state is an xHCI internal state.  Do not report it to
++               * usb core, instead, pretend to be U3, thus usb core knows
++               * it's not ready for transfer.
++               */
++              if (pls == XDEV_RESUME) {
++                      *status |= USB_SS_PORT_LS_U3;
++                      return;
++              }
++
++              /*
+                * If CAS bit isn't set but the Port is already at
+                * Compliance Mode, fake a connection so the USB core
+                * notices the Compliance state and resets the port.