From: Greg Kroah-Hartman Date: Mon, 13 Jun 2022 07:31:34 +0000 (+0200) Subject: 5.15-stable patches X-Git-Tag: v4.9.318~51 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=1f8a1b924787e1833284821c5d4ff41847fdb017;p=thirdparty%2Fkernel%2Fstable-queue.git 5.15-stable patches added patches: ata-libata-transport-fix-dma-pio-xfer-_mode-sysfs-files.patch keys-trusted-tpm2-fix-migratable-logic.patch mmc-block-fix-cqe-recovery-reset-success.patch net-openvswitch-fix-misuse-of-the-cached-connection-on-tuple-changes.patch net-phy-dp83867-retrigger-sgmii-an-when-link-change.patch writeback-fix-inode-i_io_list-not-be-protected-by-inode-i_lock-error.patch --- diff --git a/queue-5.15/ata-libata-transport-fix-dma-pio-xfer-_mode-sysfs-files.patch b/queue-5.15/ata-libata-transport-fix-dma-pio-xfer-_mode-sysfs-files.patch new file mode 100644 index 00000000000..57b7f0c7165 --- /dev/null +++ b/queue-5.15/ata-libata-transport-fix-dma-pio-xfer-_mode-sysfs-files.patch @@ -0,0 +1,74 @@ +From 72aad489f992871e908ff6d9055b26c6366fb864 Mon Sep 17 00:00:00 2001 +From: Sergey Shtylyov +Date: Wed, 8 Jun 2022 22:51:07 +0300 +Subject: ata: libata-transport: fix {dma|pio|xfer}_mode sysfs files + +From: Sergey Shtylyov + +commit 72aad489f992871e908ff6d9055b26c6366fb864 upstream. + +The {dma|pio}_mode sysfs files are incorrectly documented as having a +list of the supported DMA/PIO transfer modes, while the corresponding +fields of the *struct* ata_device hold the transfer mode IDs, not masks. + +To match these docs, the {dma|pio}_mode (and even xfer_mode!) sysfs +files are handled by the ata_bitfield_name_match() macro which leads to +reading such kind of nonsense from them: + +$ cat /sys/class/ata_device/dev3.0/pio_mode +XFER_UDMA_7, XFER_UDMA_6, XFER_UDMA_5, XFER_UDMA_4, XFER_MW_DMA_4, +XFER_PIO_6, XFER_PIO_5, XFER_PIO_4, XFER_PIO_3, XFER_PIO_2, XFER_PIO_1, +XFER_PIO_0 + +Using the correct ata_bitfield_name_search() macro fixes that: + +$ cat /sys/class/ata_device/dev3.0/pio_mode +XFER_PIO_4 + +While fixing the file documentation, somewhat reword the {dma|pio}_mode +file doc and add a note about being mostly useful for PATA devices to +the xfer_mode file doc... + +Fixes: d9027470b886 ("[libata] Add ATA transport class") +Signed-off-by: Sergey Shtylyov +Cc: stable@vger.kernel.org +Signed-off-by: Damien Le Moal +Signed-off-by: Greg Kroah-Hartman +--- + Documentation/ABI/testing/sysfs-ata | 11 ++++++----- + drivers/ata/libata-transport.c | 2 +- + 2 files changed, 7 insertions(+), 6 deletions(-) + +--- a/Documentation/ABI/testing/sysfs-ata ++++ b/Documentation/ABI/testing/sysfs-ata +@@ -107,13 +107,14 @@ Description: + described in ATA8 7.16 and 7.17. Only valid if + the device is not a PM. + +- pio_mode: (RO) Transfer modes supported by the device when +- in PIO mode. Mostly used by PATA device. ++ pio_mode: (RO) PIO transfer mode used by the device. ++ Mostly used by PATA devices. + +- xfer_mode: (RO) Current transfer mode ++ xfer_mode: (RO) Current transfer mode. Mostly used by ++ PATA devices. + +- dma_mode: (RO) Transfer modes supported by the device when +- in DMA mode. Mostly used by PATA device. ++ dma_mode: (RO) DMA transfer mode used by the device. ++ Mostly used by PATA devices. + + class: (RO) Device class. Can be "ata" for disk, + "atapi" for packet device, "pmp" for PM, or +--- a/drivers/ata/libata-transport.c ++++ b/drivers/ata/libata-transport.c +@@ -196,7 +196,7 @@ static struct { + { XFER_PIO_0, "XFER_PIO_0" }, + { XFER_PIO_SLOW, "XFER_PIO_SLOW" } + }; +-ata_bitfield_name_match(xfer,ata_xfer_names) ++ata_bitfield_name_search(xfer, ata_xfer_names) + + /* + * ATA Port attributes diff --git a/queue-5.15/keys-trusted-tpm2-fix-migratable-logic.patch b/queue-5.15/keys-trusted-tpm2-fix-migratable-logic.patch new file mode 100644 index 00000000000..c04acb5ed1c --- /dev/null +++ b/queue-5.15/keys-trusted-tpm2-fix-migratable-logic.patch @@ -0,0 +1,47 @@ +From dda5384313a40ecbaafd8a9a80f47483255e4c4d Mon Sep 17 00:00:00 2001 +From: David Safford +Date: Tue, 7 Jun 2022 14:07:57 -0400 +Subject: KEYS: trusted: tpm2: Fix migratable logic + +From: David Safford + +commit dda5384313a40ecbaafd8a9a80f47483255e4c4d upstream. + +When creating (sealing) a new trusted key, migratable +trusted keys have the FIXED_TPM and FIXED_PARENT attributes +set, and non-migratable keys don't. This is backwards, and +also causes creation to fail when creating a migratable key +under a migratable parent. (The TPM thinks you are trying to +seal a non-migratable blob under a migratable parent.) + +The following simple patch fixes the logic, and has been +tested for all four combinations of migratable and non-migratable +trusted keys and parent storage keys. With this logic, you will +get a proper failure if you try to create a non-migratable +trusted key under a migratable parent storage key, and all other +combinations work correctly. + +Cc: stable@vger.kernel.org # v5.13+ +Fixes: e5fb5d2c5a03 ("security: keys: trusted: Make sealed key properly interoperable") +Signed-off-by: David Safford +Reviewed-by: Ahmad Fatoum +Reviewed-by: Jarkko Sakkinen +Signed-off-by: Jarkko Sakkinen +Signed-off-by: Greg Kroah-Hartman +--- + security/keys/trusted-keys/trusted_tpm2.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/security/keys/trusted-keys/trusted_tpm2.c ++++ b/security/keys/trusted-keys/trusted_tpm2.c +@@ -283,8 +283,8 @@ int tpm2_seal_trusted(struct tpm_chip *c + /* key properties */ + flags = 0; + flags |= options->policydigest_len ? 0 : TPM2_OA_USER_WITH_AUTH; +- flags |= payload->migratable ? (TPM2_OA_FIXED_TPM | +- TPM2_OA_FIXED_PARENT) : 0; ++ flags |= payload->migratable ? 0 : (TPM2_OA_FIXED_TPM | ++ TPM2_OA_FIXED_PARENT); + tpm_buf_append_u32(&buf, flags); + + /* policy */ diff --git a/queue-5.15/mmc-block-fix-cqe-recovery-reset-success.patch b/queue-5.15/mmc-block-fix-cqe-recovery-reset-success.patch new file mode 100644 index 00000000000..de4c2afc2b8 --- /dev/null +++ b/queue-5.15/mmc-block-fix-cqe-recovery-reset-success.patch @@ -0,0 +1,41 @@ +From a051246b786af7e4a9d9219cc7038a6e8a411531 Mon Sep 17 00:00:00 2001 +From: Adrian Hunter +Date: Tue, 31 May 2022 20:19:22 +0300 +Subject: mmc: block: Fix CQE recovery reset success + +From: Adrian Hunter + +commit a051246b786af7e4a9d9219cc7038a6e8a411531 upstream. + +The intention of the use of mmc_blk_reset_success() in +mmc_blk_cqe_recovery() was to prevent repeated resets when retrying and +getting the same error. However, that may not be the case - any amount +of time and I/O may pass before another recovery is needed, in which +case there would be no reason to deny it the opportunity to recover via +a reset if necessary. CQE recovery is expected seldom and failure to +recover (if the clear tasks command fails), even more seldom, so it is +better to allow the reset always, which can be done by calling +mmc_blk_reset_success() always. + +Fixes: 1e8e55b67030c6 ("mmc: block: Add CQE support") +Cc: stable@vger.kernel.org +Signed-off-by: Adrian Hunter +Link: https://lore.kernel.org/r/20220531171922.76080-1-adrian.hunter@intel.com +Signed-off-by: Ulf Hansson +Signed-off-by: Greg Kroah-Hartman +--- + drivers/mmc/core/block.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +--- a/drivers/mmc/core/block.c ++++ b/drivers/mmc/core/block.c +@@ -1482,8 +1482,7 @@ void mmc_blk_cqe_recovery(struct mmc_que + err = mmc_cqe_recovery(host); + if (err) + mmc_blk_reset(mq->blkdata, host, MMC_BLK_CQE_RECOVERY); +- else +- mmc_blk_reset_success(mq->blkdata, MMC_BLK_CQE_RECOVERY); ++ mmc_blk_reset_success(mq->blkdata, MMC_BLK_CQE_RECOVERY); + + pr_debug("%s: CQE recovery done\n", mmc_hostname(host)); + } diff --git a/queue-5.15/net-openvswitch-fix-misuse-of-the-cached-connection-on-tuple-changes.patch b/queue-5.15/net-openvswitch-fix-misuse-of-the-cached-connection-on-tuple-changes.patch new file mode 100644 index 00000000000..b36ecf0d5c7 --- /dev/null +++ b/queue-5.15/net-openvswitch-fix-misuse-of-the-cached-connection-on-tuple-changes.patch @@ -0,0 +1,106 @@ +From 2061ecfdf2350994e5b61c43e50e98a7a70e95ee Mon Sep 17 00:00:00 2001 +From: Ilya Maximets +Date: Tue, 7 Jun 2022 00:11:40 +0200 +Subject: net: openvswitch: fix misuse of the cached connection on tuple changes + +From: Ilya Maximets + +commit 2061ecfdf2350994e5b61c43e50e98a7a70e95ee upstream. + +If packet headers changed, the cached nfct is no longer relevant +for the packet and attempt to re-use it leads to the incorrect packet +classification. + +This issue is causing broken connectivity in OpenStack deployments +with OVS/OVN due to hairpin traffic being unexpectedly dropped. + +The setup has datapath flows with several conntrack actions and tuple +changes between them: + + actions:ct(commit,zone=8,mark=0/0x1,nat(src)), + set(eth(src=00:00:00:00:00:01,dst=00:00:00:00:00:06)), + set(ipv4(src=172.18.2.10,dst=192.168.100.6,ttl=62)), + ct(zone=8),recirc(0x4) + +After the first ct() action the packet headers are almost fully +re-written. The next ct() tries to re-use the existing nfct entry +and marks the packet as invalid, so it gets dropped later in the +pipeline. + +Clearing the cached conntrack entry whenever packet tuple is changed +to avoid the issue. + +The flow key should not be cleared though, because we should still +be able to match on the ct_state if the recirculation happens after +the tuple change but before the next ct() action. + +Cc: stable@vger.kernel.org +Fixes: 7f8a436eaa2c ("openvswitch: Add conntrack action") +Reported-by: Frode Nordahl +Link: https://mail.openvswitch.org/pipermail/ovs-discuss/2022-May/051829.html +Link: https://bugs.launchpad.net/ubuntu/+source/ovn/+bug/1967856 +Signed-off-by: Ilya Maximets +Link: https://lore.kernel.org/r/20220606221140.488984-1-i.maximets@ovn.org +Signed-off-by: Jakub Kicinski +Signed-off-by: Greg Kroah-Hartman +--- + net/openvswitch/actions.c | 6 ++++++ + net/openvswitch/conntrack.c | 4 +++- + 2 files changed, 9 insertions(+), 1 deletion(-) + +--- a/net/openvswitch/actions.c ++++ b/net/openvswitch/actions.c +@@ -373,6 +373,7 @@ static void set_ip_addr(struct sk_buff * + update_ip_l4_checksum(skb, nh, *addr, new_addr); + csum_replace4(&nh->check, *addr, new_addr); + skb_clear_hash(skb); ++ ovs_ct_clear(skb, NULL); + *addr = new_addr; + } + +@@ -420,6 +421,7 @@ static void set_ipv6_addr(struct sk_buff + update_ipv6_checksum(skb, l4_proto, addr, new_addr); + + skb_clear_hash(skb); ++ ovs_ct_clear(skb, NULL); + memcpy(addr, new_addr, sizeof(__be32[4])); + } + +@@ -660,6 +662,7 @@ static int set_nsh(struct sk_buff *skb, + static void set_tp_port(struct sk_buff *skb, __be16 *port, + __be16 new_port, __sum16 *check) + { ++ ovs_ct_clear(skb, NULL); + inet_proto_csum_replace2(check, skb, *port, new_port, false); + *port = new_port; + } +@@ -699,6 +702,7 @@ static int set_udp(struct sk_buff *skb, + uh->dest = dst; + flow_key->tp.src = src; + flow_key->tp.dst = dst; ++ ovs_ct_clear(skb, NULL); + } + + skb_clear_hash(skb); +@@ -761,6 +765,8 @@ static int set_sctp(struct sk_buff *skb, + sh->checksum = old_csum ^ old_correct_csum ^ new_csum; + + skb_clear_hash(skb); ++ ovs_ct_clear(skb, NULL); ++ + flow_key->tp.src = sh->source; + flow_key->tp.dst = sh->dest; + +--- a/net/openvswitch/conntrack.c ++++ b/net/openvswitch/conntrack.c +@@ -1336,7 +1336,9 @@ int ovs_ct_clear(struct sk_buff *skb, st + + nf_ct_put(ct); + nf_ct_set(skb, NULL, IP_CT_UNTRACKED); +- ovs_ct_fill_key(skb, key, false); ++ ++ if (key) ++ ovs_ct_fill_key(skb, key, false); + + return 0; + } diff --git a/queue-5.15/net-phy-dp83867-retrigger-sgmii-an-when-link-change.patch b/queue-5.15/net-phy-dp83867-retrigger-sgmii-an-when-link-change.patch new file mode 100644 index 00000000000..1f9151527a8 --- /dev/null +++ b/queue-5.15/net-phy-dp83867-retrigger-sgmii-an-when-link-change.patch @@ -0,0 +1,89 @@ +From c76acfb7e19dcc3a0964e0563770b1d11b8d4540 Mon Sep 17 00:00:00 2001 +From: Tan Tee Min +Date: Thu, 26 May 2022 17:03:47 +0800 +Subject: net: phy: dp83867: retrigger SGMII AN when link change + +From: Tan Tee Min + +commit c76acfb7e19dcc3a0964e0563770b1d11b8d4540 upstream. + +There is a limitation in TI DP83867 PHY device where SGMII AN is only +triggered once after the device is booted up. Even after the PHY TPI is +down and up again, SGMII AN is not triggered and hence no new in-band +message from PHY to MAC side SGMII. + +This could cause an issue during power up, when PHY is up prior to MAC. +At this condition, once MAC side SGMII is up, MAC side SGMII wouldn`t +receive new in-band message from TI PHY with correct link status, speed +and duplex info. + +As suggested by TI, implemented a SW solution here to retrigger SGMII +Auto-Neg whenever there is a link change. + +v2: Add Fixes tag in commit message. + +Fixes: 2a10154abcb7 ("net: phy: dp83867: Add TI dp83867 phy") +Cc: # 5.4.x +Signed-off-by: Sit, Michael Wei Hong +Reviewed-by: Voon Weifeng +Signed-off-by: Tan Tee Min +Reviewed-by: Andrew Lunn +Link: https://lore.kernel.org/r/20220526090347.128742-1-tee.min.tan@linux.intel.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Greg Kroah-Hartman +--- + drivers/net/phy/dp83867.c | 29 +++++++++++++++++++++++++++++ + 1 file changed, 29 insertions(+) + +--- a/drivers/net/phy/dp83867.c ++++ b/drivers/net/phy/dp83867.c +@@ -137,6 +137,7 @@ + #define DP83867_DOWNSHIFT_2_COUNT 2 + #define DP83867_DOWNSHIFT_4_COUNT 4 + #define DP83867_DOWNSHIFT_8_COUNT 8 ++#define DP83867_SGMII_AUTONEG_EN BIT(7) + + /* CFG3 bits */ + #define DP83867_CFG3_INT_OE BIT(7) +@@ -836,6 +837,32 @@ static int dp83867_phy_reset(struct phy_ + DP83867_PHYCR_FORCE_LINK_GOOD, 0); + } + ++static void dp83867_link_change_notify(struct phy_device *phydev) ++{ ++ /* There is a limitation in DP83867 PHY device where SGMII AN is ++ * only triggered once after the device is booted up. Even after the ++ * PHY TPI is down and up again, SGMII AN is not triggered and ++ * hence no new in-band message from PHY to MAC side SGMII. ++ * This could cause an issue during power up, when PHY is up prior ++ * to MAC. At this condition, once MAC side SGMII is up, MAC side ++ * SGMII wouldn`t receive new in-band message from TI PHY with ++ * correct link status, speed and duplex info. ++ * Thus, implemented a SW solution here to retrigger SGMII Auto-Neg ++ * whenever there is a link change. ++ */ ++ if (phydev->interface == PHY_INTERFACE_MODE_SGMII) { ++ int val = 0; ++ ++ val = phy_clear_bits(phydev, DP83867_CFG2, ++ DP83867_SGMII_AUTONEG_EN); ++ if (val < 0) ++ return; ++ ++ phy_set_bits(phydev, DP83867_CFG2, ++ DP83867_SGMII_AUTONEG_EN); ++ } ++} ++ + static struct phy_driver dp83867_driver[] = { + { + .phy_id = DP83867_PHY_ID, +@@ -860,6 +887,8 @@ static struct phy_driver dp83867_driver[ + + .suspend = genphy_suspend, + .resume = genphy_resume, ++ ++ .link_change_notify = dp83867_link_change_notify, + }, + }; + module_phy_driver(dp83867_driver); diff --git a/queue-5.15/series b/queue-5.15/series index 61b2fa2a882..6ef9e7ab773 100644 --- a/queue-5.15/series +++ b/queue-5.15/series @@ -219,3 +219,9 @@ alsa-hda-realtek-fix-for-quirk-to-enable-speaker-output-on-the-lenovo-yoga-dueti alsa-hda-realtek-add-quirk-for-hp-dev-one.patch cifs-return-errors-during-session-setup-during-reconnects.patch cifs-fix-reconnect-on-smb3-mount-types.patch +keys-trusted-tpm2-fix-migratable-logic.patch +ata-libata-transport-fix-dma-pio-xfer-_mode-sysfs-files.patch +mmc-block-fix-cqe-recovery-reset-success.patch +net-phy-dp83867-retrigger-sgmii-an-when-link-change.patch +net-openvswitch-fix-misuse-of-the-cached-connection-on-tuple-changes.patch +writeback-fix-inode-i_io_list-not-be-protected-by-inode-i_lock-error.patch diff --git a/queue-5.15/writeback-fix-inode-i_io_list-not-be-protected-by-inode-i_lock-error.patch b/queue-5.15/writeback-fix-inode-i_io_list-not-be-protected-by-inode-i_lock-error.patch new file mode 100644 index 00000000000..46c6ea768cb --- /dev/null +++ b/queue-5.15/writeback-fix-inode-i_io_list-not-be-protected-by-inode-i_lock-error.patch @@ -0,0 +1,161 @@ +From 10e14073107dd0b6d97d9516a02845a8e501c2c9 Mon Sep 17 00:00:00 2001 +From: Jchao Sun +Date: Tue, 24 May 2022 08:05:40 -0700 +Subject: writeback: Fix inode->i_io_list not be protected by inode->i_lock error + +From: Jchao Sun + +commit 10e14073107dd0b6d97d9516a02845a8e501c2c9 upstream. + +Commit b35250c0816c ("writeback: Protect inode->i_io_list with +inode->i_lock") made inode->i_io_list not only protected by +wb->list_lock but also inode->i_lock, but inode_io_list_move_locked() +was missed. Add lock there and also update comment describing +things protected by inode->i_lock. This also fixes a race where +__mark_inode_dirty() could move inode under flush worker's hands +and thus sync(2) could miss writing some inodes. + +Fixes: b35250c0816c ("writeback: Protect inode->i_io_list with inode->i_lock") +Link: https://lore.kernel.org/r/20220524150540.12552-1-sunjunchao2870@gmail.com +CC: stable@vger.kernel.org +Signed-off-by: Jchao Sun +Signed-off-by: Jan Kara +Signed-off-by: Greg Kroah-Hartman +--- + fs/fs-writeback.c | 37 ++++++++++++++++++++++++++++--------- + fs/inode.c | 2 +- + 2 files changed, 29 insertions(+), 10 deletions(-) + +--- a/fs/fs-writeback.c ++++ b/fs/fs-writeback.c +@@ -120,6 +120,7 @@ static bool inode_io_list_move_locked(st + struct list_head *head) + { + assert_spin_locked(&wb->list_lock); ++ assert_spin_locked(&inode->i_lock); + + list_move(&inode->i_io_list, head); + +@@ -1400,9 +1401,9 @@ static int move_expired_inodes(struct li + inode = wb_inode(delaying_queue->prev); + if (inode_dirtied_after(inode, dirtied_before)) + break; ++ spin_lock(&inode->i_lock); + list_move(&inode->i_io_list, &tmp); + moved++; +- spin_lock(&inode->i_lock); + inode->i_state |= I_SYNC_QUEUED; + spin_unlock(&inode->i_lock); + if (sb_is_blkdev_sb(inode->i_sb)) +@@ -1418,7 +1419,12 @@ static int move_expired_inodes(struct li + goto out; + } + +- /* Move inodes from one superblock together */ ++ /* ++ * Although inode's i_io_list is moved from 'tmp' to 'dispatch_queue', ++ * we don't take inode->i_lock here because it is just a pointless overhead. ++ * Inode is already marked as I_SYNC_QUEUED so writeback list handling is ++ * fully under our control. ++ */ + while (!list_empty(&tmp)) { + sb = wb_inode(tmp.prev)->i_sb; + list_for_each_prev_safe(pos, node, &tmp) { +@@ -1853,8 +1859,8 @@ static long writeback_sb_inodes(struct s + * We'll have another go at writing back this inode + * when we completed a full scan of b_io. + */ +- spin_unlock(&inode->i_lock); + requeue_io(inode, wb); ++ spin_unlock(&inode->i_lock); + trace_writeback_sb_inodes_requeue(inode); + continue; + } +@@ -2389,6 +2395,7 @@ void __mark_inode_dirty(struct inode *in + { + struct super_block *sb = inode->i_sb; + int dirtytime = 0; ++ struct bdi_writeback *wb = NULL; + + trace_writeback_mark_inode_dirty(inode, flags); + +@@ -2441,13 +2448,24 @@ void __mark_inode_dirty(struct inode *in + inode->i_state |= flags; + + /* ++ * Grab inode's wb early because it requires dropping i_lock and we ++ * need to make sure following checks happen atomically with dirty ++ * list handling so that we don't move inodes under flush worker's ++ * hands. ++ */ ++ if (!was_dirty) { ++ wb = locked_inode_to_wb_and_lock_list(inode); ++ spin_lock(&inode->i_lock); ++ } ++ ++ /* + * 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_QUEUED) +- goto out_unlock_inode; ++ goto out_unlock; + + /* + * Only add valid (hashed) inodes to the superblock's +@@ -2455,22 +2473,19 @@ void __mark_inode_dirty(struct inode *in + */ + if (!S_ISBLK(inode->i_mode)) { + if (inode_unhashed(inode)) +- goto out_unlock_inode; ++ goto out_unlock; + } + if (inode->i_state & I_FREEING) +- goto out_unlock_inode; ++ goto out_unlock; + + /* + * If the inode was already on b_dirty/b_io/b_more_io, don't + * reposition it (that would break b_dirty time-ordering). + */ + if (!was_dirty) { +- struct bdi_writeback *wb; + struct list_head *dirty_list; + bool wakeup_bdi = false; + +- wb = locked_inode_to_wb_and_lock_list(inode); +- + inode->dirtied_when = jiffies; + if (dirtytime) + inode->dirtied_time_when = jiffies; +@@ -2484,6 +2499,7 @@ void __mark_inode_dirty(struct inode *in + dirty_list); + + spin_unlock(&wb->list_lock); ++ spin_unlock(&inode->i_lock); + trace_writeback_dirty_inode_enqueue(inode); + + /* +@@ -2498,6 +2514,9 @@ void __mark_inode_dirty(struct inode *in + return; + } + } ++out_unlock: ++ if (wb) ++ spin_unlock(&wb->list_lock); + out_unlock_inode: + spin_unlock(&inode->i_lock); + } +--- a/fs/inode.c ++++ b/fs/inode.c +@@ -27,7 +27,7 @@ + * Inode locking rules: + * + * inode->i_lock protects: +- * inode->i_state, inode->i_hash, __iget() ++ * inode->i_state, inode->i_hash, __iget(), inode->i_io_list + * Inode LRU list locks protect: + * inode->i_sb->s_inode_lru, inode->i_lru + * inode->i_sb->s_inode_list_lock protects: