From: Sasha Levin Date: Wed, 8 Jul 2026 16:17:09 +0000 (-0400) Subject: Fixes for all trees X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=3fab3c512e7ef7951a3e49ed124782b1df1d77fe;p=thirdparty%2Fkernel%2Fstable-queue.git Fixes for all trees Signed-off-by: Sasha Levin --- diff --git a/queue-5.10/mmc-renesas_sdhi-add-quirk-entry-for-rz-g2h-soc.patch b/queue-5.10/mmc-renesas_sdhi-add-quirk-entry-for-rz-g2h-soc.patch new file mode 100644 index 0000000000..d88a5d070a --- /dev/null +++ b/queue-5.10/mmc-renesas_sdhi-add-quirk-entry-for-rz-g2h-soc.patch @@ -0,0 +1,39 @@ +From f9de342881552d3ddc59318fbc85cec3899781f8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 7 Jul 2026 11:44:17 +0100 +Subject: mmc: renesas_sdhi: Add quirk entry for RZ/G2H SoC + +From: Lad Prabhakar + +The RZ/G2H (R8A774E1) SoC uses the same SDHI IP as the R-Car H3-N +(R8A77951) and requires the sdhi_quirks_bad_taps2367 quirk. + +The 5.10 stable backport of commit 77fa80a1b732 ("mmc: renesas_sdhi: +Add OF entry for RZ/G2H SoC") adds an OF match entry for RZ/G2H. +However, unlike upstream, the 5.10 driver applies SoC-specific quirks +through the sdhi_quirks_match table. As a result, the required quirk is +not applied for RZ/G2H. + +Add the missing sdhi_quirks_match entry for RZ/G2H ES3.x. + +Signed-off-by: Lad Prabhakar +Signed-off-by: Sasha Levin +--- + drivers/mmc/host/renesas_sdhi_core.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c +index c0acb309e3243b..f02f9ef5cfbc80 100644 +--- a/drivers/mmc/host/renesas_sdhi_core.c ++++ b/drivers/mmc/host/renesas_sdhi_core.c +@@ -903,6 +903,7 @@ static const struct renesas_sdhi_quirks sdhi_quirks_r8a77990 = { + */ + static const struct soc_device_attribute sdhi_quirks_match[] = { + { .soc_id = "r8a774a1", .revision = "ES1.[012]", .data = &sdhi_quirks_4tap_nohs400 }, ++ { .soc_id = "r8a774e1", .revision = "ES3.*", .data = &sdhi_quirks_bad_taps2367 }, + { .soc_id = "r8a7795", .revision = "ES1.*", .data = &sdhi_quirks_4tap_nohs400 }, + { .soc_id = "r8a7795", .revision = "ES2.0", .data = &sdhi_quirks_4tap }, + { .soc_id = "r8a7795", .revision = "ES3.*", .data = &sdhi_quirks_bad_taps2367 }, +-- +2.53.0 + diff --git a/queue-5.10/series b/queue-5.10/series index 990bb0dcb2..3f554b8ae2 100644 --- a/queue-5.10/series +++ b/queue-5.10/series @@ -5,3 +5,4 @@ userfaultfd-gate-must_wait-writability-check-on-pte_.patch clk-imx-add-check-for-kcalloc.patch nfsd-move-name-lookup-out-of-nfsd4_list_rec_dir.patch nfsd-change-nfs4_client_to_reclaim-to-allocate-data.patch +mmc-renesas_sdhi-add-quirk-entry-for-rz-g2h-soc.patch diff --git a/queue-6.1/mm-vmscan-flush-deferred-tlb-before-freeing-large-fo.patch b/queue-6.1/mm-vmscan-flush-deferred-tlb-before-freeing-large-fo.patch new file mode 100644 index 0000000000..f3a07ed7c7 --- /dev/null +++ b/queue-6.1/mm-vmscan-flush-deferred-tlb-before-freeing-large-fo.patch @@ -0,0 +1,60 @@ +From 705fe2dde3194444671a6b00ab0122083ea5d27e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 8 Jul 2026 12:12:36 +0800 +Subject: mm/vmscan: flush deferred TLB before freeing large folios + +From: Jiayuan Chen + +In reclaim, shrink_folio_list() unmaps PTEs with a deferred, batched TLB +flush. The batch is only flushed by try_to_unmap_flush() near the end of +the function, just before the order-0 folios collected in @free_folios are +handed back to the allocator. + +Large folios don't go through @free_folios -- they're freed inline at the +free_it label via destroy_large_folio(), which runs before that flush. So +a large folio's pages can be returned to the buddy allocator and reused +while another CPU still holds a stale TLB entry for them, and that CPU then +reads or executes through the stale translation into the reused page. For +file-backed large folios (e.g. executable text) this shows up as random +SIGSEGV/SIGILL in user space, with fault addresses that don't match the +code being run. + +Flush the deferred batch before freeing a large folio inline, the same way +the order-0 path already waits for the flush. + +Upstream this is fixed as a side effect of commit bc2ff4cbc329 ("mm: free +folios in a batch in shrink_folio_list()"), which is a larger change; this +is the minimal fix for -stable. + +Reported-by: Yingfu Zhou +Fixes: bd4c82c22c36 ("mm, THP, swap: delay splitting THP after swapped out") +Cc: Jiayuan Chen +Signed-off-by: Jiayuan Chen +Reviewed-by: Matthew Wilcox +Signed-off-by: Sasha Levin +--- + mm/vmscan.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/mm/vmscan.c b/mm/vmscan.c +index f6f8c18dc45f57..964802dad63803 100644 +--- a/mm/vmscan.c ++++ b/mm/vmscan.c +@@ -2050,10 +2050,12 @@ static unsigned int shrink_folio_list(struct list_head *folio_list, + * Is there need to periodically free_folio_list? It would + * appear not as the counts should be low + */ +- if (unlikely(folio_test_large(folio))) ++ if (unlikely(folio_test_large(folio))) { ++ try_to_unmap_flush(); + destroy_large_folio(folio); +- else ++ } else { + list_add(&folio->lru, &free_folios); ++ } + continue; + + activate_locked_split: +-- +2.53.0 + diff --git a/queue-6.1/series b/queue-6.1/series index 99bd83c088..b6324033a6 100644 --- a/queue-6.1/series +++ b/queue-6.1/series @@ -12,3 +12,4 @@ slimbus-convert-to-platform-remove-callback-returnin.patch slimbus-qcom-ngd-ctrl-register-callbacks-after-creat.patch nfsd-move-name-lookup-out-of-nfsd4_list_rec_dir.patch nfsd-change-nfs4_client_to_reclaim-to-allocate-data.patch +mm-vmscan-flush-deferred-tlb-before-freeing-large-fo.patch diff --git a/queue-6.12/block-add-a-store_limit-operations-for-sysfs-entries.patch b/queue-6.12/block-add-a-store_limit-operations-for-sysfs-entries.patch new file mode 100644 index 0000000000..1313bcbbc3 --- /dev/null +++ b/queue-6.12/block-add-a-store_limit-operations-for-sysfs-entries.patch @@ -0,0 +1,281 @@ +From 247a6d7dbcaf6aced51d2d2627f21994418f739e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 7 Jul 2026 18:37:27 +0300 +Subject: block: add a store_limit operations for sysfs entries + +From: Christoph Hellwig + +[ Upstream commit a16230649ce27f8ac7dd8a5b079d9657aa96de16 ] + +De-duplicate the code for updating queue limits by adding a store_limit +method that allows having common code handle the actual queue limits +update. + +Note that this is a pure refactoring patch and does not address the +existing freeze vs limits lock order problem in the refactored code, +which will be addressed next. + +[6.12.y-backport note: dropped "iostats_passthrough" +queue limit parameter from the upstream commit, +as this parameter does not exist in the 6.12 kernel branch] + +Signed-off-by: Christoph Hellwig +Reviewed-by: Ming Lei +Reviewed-by: Damien Le Moal +Reviewed-by: Martin K. Petersen +Reviewed-by: Nilay Shroff +Reviewed-by: Johannes Thumshirn +Reviewed-by: John Garry +Link: https://lore.kernel.org/r/20250110054726.1499538-6-hch@lst.de +Signed-off-by: Jens Axboe +Stable-dep-of: c99f66e4084a ("block: fix queue freeze vs limits lock order in sysfs store methods") +Signed-off-by: Konstantin Andreev +Signed-off-by: Sasha Levin +--- + block/blk-sysfs.c | 108 +++++++++++++++++++++++----------------------- + 1 file changed, 54 insertions(+), 54 deletions(-) + +diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c +index 6a38f312e385c3..c9e572ae0b9c63 100644 +--- a/block/blk-sysfs.c ++++ b/block/blk-sysfs.c +@@ -25,6 +25,8 @@ struct queue_sysfs_entry { + ssize_t (*show)(struct gendisk *disk, char *page); + int (*load_module)(struct gendisk *disk, const char *page, size_t count); + ssize_t (*store)(struct gendisk *disk, const char *page, size_t count); ++ int (*store_limit)(struct gendisk *disk, const char *page, ++ size_t count, struct queue_limits *lim); + }; + + static ssize_t +@@ -152,13 +154,11 @@ QUEUE_SYSFS_SHOW_CONST(discard_zeroes_data, 0) + QUEUE_SYSFS_SHOW_CONST(write_same_max, 0) + QUEUE_SYSFS_SHOW_CONST(poll_delay, -1) + +-static ssize_t queue_max_discard_sectors_store(struct gendisk *disk, +- const char *page, size_t count) ++static int queue_max_discard_sectors_store(struct gendisk *disk, ++ const char *page, size_t count, struct queue_limits *lim) + { + unsigned long max_discard_bytes; +- struct queue_limits lim; + ssize_t ret; +- int err; + + ret = queue_var_store(&max_discard_bytes, page, count); + if (ret < 0) +@@ -170,12 +170,8 @@ static ssize_t queue_max_discard_sectors_store(struct gendisk *disk, + if ((max_discard_bytes >> SECTOR_SHIFT) > UINT_MAX) + return -EINVAL; + +- lim = queue_limits_start_update(disk->queue); +- lim.max_user_discard_sectors = max_discard_bytes >> SECTOR_SHIFT; +- err = queue_limits_commit_update(disk->queue, &lim); +- if (err) +- return err; +- return ret; ++ lim->max_user_discard_sectors = max_discard_bytes >> SECTOR_SHIFT; ++ return 0; + } + + /* +@@ -190,30 +186,24 @@ static ssize_t queue_zone_append_max_show(struct gendisk *disk, char *page) + SECTOR_SHIFT); + } + +-static ssize_t +-queue_max_sectors_store(struct gendisk *disk, const char *page, size_t count) ++static int ++queue_max_sectors_store(struct gendisk *disk, const char *page, size_t count, ++ struct queue_limits *lim) + { + unsigned long max_sectors_kb; +- struct queue_limits lim; + ssize_t ret; +- int err; + + ret = queue_var_store(&max_sectors_kb, page, count); + if (ret < 0) + return ret; + +- lim = queue_limits_start_update(disk->queue); +- lim.max_user_sectors = max_sectors_kb << 1; +- err = queue_limits_commit_update(disk->queue, &lim); +- if (err) +- return err; +- return ret; ++ lim->max_user_sectors = max_sectors_kb << 1; ++ return 0; + } + + static ssize_t queue_feature_store(struct gendisk *disk, const char *page, +- size_t count, blk_features_t feature) ++ size_t count, struct queue_limits *lim, blk_features_t feature) + { +- struct queue_limits lim; + unsigned long val; + ssize_t ret; + +@@ -221,15 +211,11 @@ static ssize_t queue_feature_store(struct gendisk *disk, const char *page, + if (ret < 0) + return ret; + +- lim = queue_limits_start_update(disk->queue); + if (val) +- lim.features |= feature; ++ lim->features |= feature; + else +- lim.features &= ~feature; +- ret = queue_limits_commit_update(disk->queue, &lim); +- if (ret) +- return ret; +- return count; ++ lim->features &= ~feature; ++ return 0; + } + + #define QUEUE_SYSFS_FEATURE(_name, _feature) \ +@@ -238,10 +224,10 @@ static ssize_t queue_##_name##_show(struct gendisk *disk, char *page) \ + return sprintf(page, "%u\n", \ + !!(disk->queue->limits.features & _feature)); \ + } \ +-static ssize_t queue_##_name##_store(struct gendisk *disk, \ +- const char *page, size_t count) \ ++static int queue_##_name##_store(struct gendisk *disk, \ ++ const char *page, size_t count, struct queue_limits *lim) \ + { \ +- return queue_feature_store(disk, page, count, _feature); \ ++ return queue_feature_store(disk, page, count, lim, _feature); \ + } + + QUEUE_SYSFS_FEATURE(rotational, BLK_FEAT_ROTATIONAL) +@@ -381,12 +367,10 @@ static ssize_t queue_wc_show(struct gendisk *disk, char *page) + return sprintf(page, "write through\n"); + } + +-static ssize_t queue_wc_store(struct gendisk *disk, const char *page, +- size_t count) ++static int queue_wc_store(struct gendisk *disk, const char *page, ++ size_t count, struct queue_limits *lim) + { +- struct queue_limits lim; + bool disable; +- int err; + + if (!strncmp(page, "write back", 10)) { + disable = false; +@@ -397,15 +381,11 @@ static ssize_t queue_wc_store(struct gendisk *disk, const char *page, + return -EINVAL; + } + +- lim = queue_limits_start_update(disk->queue); + if (disable) +- lim.flags |= BLK_FLAG_WRITE_CACHE_DISABLED; ++ lim->flags |= BLK_FLAG_WRITE_CACHE_DISABLED; + else +- lim.flags &= ~BLK_FLAG_WRITE_CACHE_DISABLED; +- err = queue_limits_commit_update(disk->queue, &lim); +- if (err) +- return err; +- return count; ++ lim->flags &= ~BLK_FLAG_WRITE_CACHE_DISABLED; ++ return 0; + } + + #define QUEUE_RO_ENTRY(_prefix, _name) \ +@@ -421,6 +401,13 @@ static struct queue_sysfs_entry _prefix##_entry = { \ + .store = _prefix##_store, \ + }; + ++#define QUEUE_LIM_RW_ENTRY(_prefix, _name) \ ++static struct queue_sysfs_entry _prefix##_entry = { \ ++ .attr = { .name = _name, .mode = 0644 }, \ ++ .show = _prefix##_show, \ ++ .store_limit = _prefix##_store, \ ++} ++ + #define QUEUE_RW_LOAD_MODULE_ENTRY(_prefix, _name) \ + static struct queue_sysfs_entry _prefix##_entry = { \ + .attr = { .name = _name, .mode = 0644 }, \ +@@ -431,7 +418,7 @@ static struct queue_sysfs_entry _prefix##_entry = { \ + + QUEUE_RW_ENTRY(queue_requests, "nr_requests"); + QUEUE_RW_ENTRY(queue_ra, "read_ahead_kb"); +-QUEUE_RW_ENTRY(queue_max_sectors, "max_sectors_kb"); ++QUEUE_LIM_RW_ENTRY(queue_max_sectors, "max_sectors_kb"); + QUEUE_RO_ENTRY(queue_max_hw_sectors, "max_hw_sectors_kb"); + QUEUE_RO_ENTRY(queue_max_segments, "max_segments"); + QUEUE_RO_ENTRY(queue_max_integrity_segments, "max_integrity_segments"); +@@ -447,7 +434,7 @@ QUEUE_RO_ENTRY(queue_io_opt, "optimal_io_size"); + QUEUE_RO_ENTRY(queue_max_discard_segments, "max_discard_segments"); + QUEUE_RO_ENTRY(queue_discard_granularity, "discard_granularity"); + QUEUE_RO_ENTRY(queue_max_hw_discard_sectors, "discard_max_hw_bytes"); +-QUEUE_RW_ENTRY(queue_max_discard_sectors, "discard_max_bytes"); ++QUEUE_LIM_RW_ENTRY(queue_max_discard_sectors, "discard_max_bytes"); + QUEUE_RO_ENTRY(queue_discard_zeroes_data, "discard_zeroes_data"); + + QUEUE_RO_ENTRY(queue_atomic_write_max_sectors, "atomic_write_max_bytes"); +@@ -470,7 +457,7 @@ QUEUE_RW_ENTRY(queue_nomerges, "nomerges"); + QUEUE_RW_ENTRY(queue_rq_affinity, "rq_affinity"); + QUEUE_RW_ENTRY(queue_poll, "io_poll"); + QUEUE_RW_ENTRY(queue_poll_delay, "io_poll_delay"); +-QUEUE_RW_ENTRY(queue_wc, "write_cache"); ++QUEUE_LIM_RW_ENTRY(queue_wc, "write_cache"); + QUEUE_RO_ENTRY(queue_fua, "fua"); + QUEUE_RO_ENTRY(queue_dax, "dax"); + QUEUE_RW_ENTRY(queue_io_timeout, "io_timeout"); +@@ -483,10 +470,10 @@ static struct queue_sysfs_entry queue_hw_sector_size_entry = { + .show = queue_logical_block_size_show, + }; + +-QUEUE_RW_ENTRY(queue_rotational, "rotational"); +-QUEUE_RW_ENTRY(queue_iostats, "iostats"); +-QUEUE_RW_ENTRY(queue_add_random, "add_random"); +-QUEUE_RW_ENTRY(queue_stable_writes, "stable_writes"); ++QUEUE_LIM_RW_ENTRY(queue_rotational, "rotational"); ++QUEUE_LIM_RW_ENTRY(queue_iostats, "iostats"); ++QUEUE_LIM_RW_ENTRY(queue_add_random, "add_random"); ++QUEUE_LIM_RW_ENTRY(queue_stable_writes, "stable_writes"); + + #ifdef CONFIG_BLK_WBT + static ssize_t queue_var_store64(s64 *var, const char *page) +@@ -683,7 +670,7 @@ queue_attr_store(struct kobject *kobj, struct attribute *attr, + struct request_queue *q = disk->queue; + ssize_t res; + +- if (!entry->store) ++ if (!entry->store_limit && !entry->store) + return -EIO; + + /* +@@ -697,11 +684,24 @@ queue_attr_store(struct kobject *kobj, struct attribute *attr, + return res; + } + +- blk_mq_freeze_queue(q); + mutex_lock(&q->sysfs_lock); +- res = entry->store(disk, page, length); +- mutex_unlock(&q->sysfs_lock); ++ blk_mq_freeze_queue(q); ++ if (entry->store_limit) { ++ struct queue_limits lim = queue_limits_start_update(q); ++ ++ res = entry->store_limit(disk, page, length, &lim); ++ if (res < 0) { ++ queue_limits_cancel_update(q); ++ } else { ++ res = queue_limits_commit_update(q, &lim); ++ if (!res) ++ res = length; ++ } ++ } else { ++ res = entry->store(disk, page, length); ++ } + blk_mq_unfreeze_queue(q); ++ mutex_unlock(&q->sysfs_lock); + return res; + } + +-- +2.53.0 + diff --git a/queue-6.12/block-fix-queue-freeze-vs-limits-lock-order-in-sysfs.patch b/queue-6.12/block-fix-queue-freeze-vs-limits-lock-order-in-sysfs.patch new file mode 100644 index 0000000000..1524409e50 --- /dev/null +++ b/queue-6.12/block-fix-queue-freeze-vs-limits-lock-order-in-sysfs.patch @@ -0,0 +1,86 @@ +From 91407d252535f722304ca9915e17f79fdfb91d1f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 7 Jul 2026 18:37:28 +0300 +Subject: block: fix queue freeze vs limits lock order in sysfs store methods + +From: Christoph Hellwig + +[ Upstream commit c99f66e4084a62a2cc401c4704a84328aeddc9ec ] + +queue_attr_store() always freezes a device queue before calling the +attribute store operation. For attributes that control queue limits, the +store operation will also lock the queue limits with a call to +queue_limits_start_update(). However, some drivers (e.g. SCSI sd) may +need to issue commands to a device to obtain limit values from the +hardware with the queue limits locked. This creates a potential ABBA +deadlock situation if a user attempts to modify a limit (thus freezing +the device queue) while the device driver starts a revalidation of the +device queue limits. + +Avoid such deadlock by not freezing the queue before calling the +->store_limit() method in struct queue_sysfs_entry and instead use the +queue_limits_commit_update_frozen helper to freeze the queue after taking +the limits lock. + +This also removes taking the sysfs lock for the store_limit method as +it doesn't protect anything here, but creates even more nesting. +Hopefully it will go away from the actual sysfs methods entirely soon. + +(commit log adapted from a similar patch from Damien Le Moal) + +Fixes: ff956a3be95b ("block: use queue_limits_commit_update in queue_discard_max_store") +Fixes: 0327ca9d53bf ("block: use queue_limits_commit_update in queue_max_sectors_store") +Signed-off-by: Christoph Hellwig +Reviewed-by: Ming Lei +Reviewed-by: Damien Le Moal +Reviewed-by: Martin K. Petersen +Reviewed-by: Nilay Shroff +Reviewed-by: Johannes Thumshirn +Link: https://lore.kernel.org/r/20250110054726.1499538-7-hch@lst.de +Signed-off-by: Jens Axboe +Signed-off-by: Konstantin Andreev +Signed-off-by: Sasha Levin +--- + block/blk-sysfs.c | 18 ++++++++++-------- + 1 file changed, 10 insertions(+), 8 deletions(-) + +diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c +index c9e572ae0b9c63..a5b6bcdfb9aa0a 100644 +--- a/block/blk-sysfs.c ++++ b/block/blk-sysfs.c +@@ -684,22 +684,24 @@ queue_attr_store(struct kobject *kobj, struct attribute *attr, + return res; + } + +- mutex_lock(&q->sysfs_lock); +- blk_mq_freeze_queue(q); + if (entry->store_limit) { + struct queue_limits lim = queue_limits_start_update(q); + + res = entry->store_limit(disk, page, length, &lim); + if (res < 0) { + queue_limits_cancel_update(q); +- } else { +- res = queue_limits_commit_update(q, &lim); +- if (!res) +- res = length; ++ return res; + } +- } else { +- res = entry->store(disk, page, length); ++ ++ res = queue_limits_commit_update_frozen(q, &lim); ++ if (res) ++ return res; ++ return length; + } ++ ++ mutex_lock(&q->sysfs_lock); ++ blk_mq_freeze_queue(q); ++ res = entry->store(disk, page, length); + blk_mq_unfreeze_queue(q); + mutex_unlock(&q->sysfs_lock); + return res; +-- +2.53.0 + diff --git a/queue-6.12/bonding-fix-xfrm-offload-feature-setup-on-active-bac.patch b/queue-6.12/bonding-fix-xfrm-offload-feature-setup-on-active-bac.patch new file mode 100644 index 0000000000..5dce36723a --- /dev/null +++ b/queue-6.12/bonding-fix-xfrm-offload-feature-setup-on-active-bac.patch @@ -0,0 +1,93 @@ +From 395ed4519a34b3f17a35c4a0aadfaeba99694de6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 25 Sep 2025 02:33:03 +0000 +Subject: bonding: fix xfrm offload feature setup on active-backup mode + +From: Hangbin Liu + +[ Upstream commit 5b66169f6be4847008c0aea50885ff0632151479 ] + +The active-backup bonding mode supports XFRM ESP offload. However, when +a bond is added using command like `ip link add bond0 type bond mode 1 +miimon 100`, the `ethtool -k` command shows that the XFRM ESP offload is +disabled. This occurs because, in bond_newlink(), we change bond link +first and register bond device later. So the XFRM feature update in +bond_option_mode_set() is not called as the bond device is not yet +registered, leading to the offload feature not being set successfully. + +To resolve this issue, we can modify the code order in bond_newlink() to +ensure that the bond device is registered first before changing the bond +link parameters. This change will allow the XFRM ESP offload feature to be +correctly enabled. + +Fixes: 007ab5345545 ("bonding: fix feature flag setting at init time") +Signed-off-by: Hangbin Liu +Link: https://patch.msgid.link/20250925023304.472186-1-liuhangbin@gmail.com +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/bonding/bond_main.c | 2 +- + drivers/net/bonding/bond_netlink.c | 16 +++++++++------- + include/net/bonding.h | 1 + + 3 files changed, 11 insertions(+), 8 deletions(-) + +diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c +index 69e79b22e31909..7d71cfc2c93741 100644 +--- a/drivers/net/bonding/bond_main.c ++++ b/drivers/net/bonding/bond_main.c +@@ -4388,7 +4388,7 @@ void bond_work_init_all(struct bonding *bond) + INIT_DELAYED_WORK(&bond->slave_arr_work, bond_slave_arr_handler); + } + +-static void bond_work_cancel_all(struct bonding *bond) ++void bond_work_cancel_all(struct bonding *bond) + { + cancel_delayed_work_sync(&bond->mii_work); + cancel_delayed_work_sync(&bond->arp_work); +diff --git a/drivers/net/bonding/bond_netlink.c b/drivers/net/bonding/bond_netlink.c +index d7fc781b642ca2..a754bad4c5d668 100644 +--- a/drivers/net/bonding/bond_netlink.c ++++ b/drivers/net/bonding/bond_netlink.c +@@ -597,18 +597,20 @@ static int bond_newlink(struct net *src_net, struct net_device *bond_dev, + struct nlattr *tb[], struct nlattr *data[], + struct netlink_ext_ack *extack) + { ++ struct bonding *bond = netdev_priv(bond_dev); + int err; + +- err = bond_changelink(bond_dev, tb, data, extack); +- if (err < 0) ++ err = register_netdevice(bond_dev); ++ if (err) + return err; + +- err = register_netdevice(bond_dev); +- if (!err) { +- struct bonding *bond = netdev_priv(bond_dev); ++ netif_carrier_off(bond_dev); ++ bond_work_init_all(bond); + +- netif_carrier_off(bond_dev); +- bond_work_init_all(bond); ++ err = bond_changelink(bond_dev, tb, data, extack); ++ if (err) { ++ bond_work_cancel_all(bond); ++ unregister_netdevice(bond_dev); + } + + return err; +diff --git a/include/net/bonding.h b/include/net/bonding.h +index 0d9c1eb40d12b6..8f5507ef5c0dac 100644 +--- a/include/net/bonding.h ++++ b/include/net/bonding.h +@@ -713,6 +713,7 @@ struct bond_vlan_tag *bond_verify_device_path(struct net_device *start_dev, + int bond_update_slave_arr(struct bonding *bond, struct slave *skipslave); + void bond_slave_arr_work_rearm(struct bonding *bond, unsigned long delay); + void bond_work_init_all(struct bonding *bond); ++void bond_work_cancel_all(struct bonding *bond); + + #ifdef CONFIG_PROC_FS + void bond_create_proc_entry(struct bonding *bond); +-- +2.53.0 + diff --git a/queue-6.12/series b/queue-6.12/series index 9ee4dba2cf..95940c0b46 100644 --- a/queue-6.12/series +++ b/queue-6.12/series @@ -26,3 +26,6 @@ locking-rtmutex-make-sure-we-wake-anything-on-the-wake_q-when-we-release-the-loc apparmor-advertise-the-tcp-fast-open-fix-is-applied.patch nfsd-move-name-lookup-out-of-nfsd4_list_rec_dir.patch nfsd-change-nfs4_client_to_reclaim-to-allocate-data.patch +bonding-fix-xfrm-offload-feature-setup-on-active-bac.patch +block-add-a-store_limit-operations-for-sysfs-entries.patch +block-fix-queue-freeze-vs-limits-lock-order-in-sysfs.patch diff --git a/queue-6.6/bonding-add-esp-offload-features-when-slaves-support.patch b/queue-6.6/bonding-add-esp-offload-features-when-slaves-support.patch new file mode 100644 index 0000000000..2548321a7f --- /dev/null +++ b/queue-6.6/bonding-add-esp-offload-features-when-slaves-support.patch @@ -0,0 +1,61 @@ +From 2216dfc5e120b0b7f4b2fa5e741f09c5dcfc6b23 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 8 Jul 2026 17:47:08 +0800 +Subject: bonding: add ESP offload features when slaves support + +From: Jianbo Liu + +[ Upstream commit 4861333b42178fa3d8fd1bb4e2cfb2fedc968dba ] + +Add NETIF_F_GSO_ESP bit to bond's gso_partial_features if all slaves +support it, such that ESP segmentation is handled by hardware if possible. + +Signed-off-by: Jianbo Liu +Reviewed-by: Boris Pismenny +Signed-off-by: Tariq Toukan +Link: https://patch.msgid.link/20241105192721.584822-1-tariqt@nvidia.com +Signed-off-by: Jakub Kicinski +Stable-dep-of: 950803f72547 ("bonding: fix type confusion in bond_setup_by_slave()") +Signed-off-by: Wentao Guan +Signed-off-by: Sasha Levin +--- + drivers/net/bonding/bond_main.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c +index d200909a75cfe9..ec21958babe7d8 100644 +--- a/drivers/net/bonding/bond_main.c ++++ b/drivers/net/bonding/bond_main.c +@@ -1484,6 +1484,7 @@ static void bond_compute_features(struct bonding *bond) + { + unsigned int dst_release_flag = IFF_XMIT_DST_RELEASE | + IFF_XMIT_DST_RELEASE_PERM; ++ netdev_features_t gso_partial_features = NETIF_F_GSO_ESP; + netdev_features_t vlan_features = BOND_VLAN_FEATURES; + netdev_features_t enc_features = BOND_ENC_FEATURES; + #ifdef CONFIG_XFRM_OFFLOAD +@@ -1517,6 +1518,9 @@ static void bond_compute_features(struct bonding *bond) + BOND_XFRM_FEATURES); + #endif /* CONFIG_XFRM_OFFLOAD */ + ++ if (slave->dev->hw_enc_features & NETIF_F_GSO_PARTIAL) ++ gso_partial_features &= slave->dev->gso_partial_features; ++ + mpls_features = netdev_increment_features(mpls_features, + slave->dev->mpls_features, + BOND_MPLS_FEATURES); +@@ -1530,6 +1534,11 @@ static void bond_compute_features(struct bonding *bond) + } + bond_dev->hard_header_len = max_hard_header_len; + ++ if (gso_partial_features & NETIF_F_GSO_ESP) ++ bond_dev->gso_partial_features |= NETIF_F_GSO_ESP; ++ else ++ bond_dev->gso_partial_features &= ~NETIF_F_GSO_ESP; ++ + done: + bond_dev->vlan_features = vlan_features; + bond_dev->hw_enc_features = enc_features | NETIF_F_GSO_ENCAP_ALL | +-- +2.53.0 + diff --git a/queue-6.6/bonding-correctly-support-gso-esp-offload.patch b/queue-6.6/bonding-correctly-support-gso-esp-offload.patch new file mode 100644 index 0000000000..aeea41b976 --- /dev/null +++ b/queue-6.6/bonding-correctly-support-gso-esp-offload.patch @@ -0,0 +1,109 @@ +From eefea913c577312e33cf13d62e25a82615a9a1df Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 8 Jul 2026 17:47:10 +0800 +Subject: bonding: Correctly support GSO ESP offload + +From: Cosmin Ratiu + +[ Upstream commit 9e6c4e6b605c1fa3e24f74ee0b641e95f090188a ] + +The referenced fix is incomplete. It correctly computes +bond_dev->gso_partial_features across slaves, but unfortunately +netdev_fix_features discards gso_partial_features from the feature set +if NETIF_F_GSO_PARTIAL isn't set in bond_dev->features. + +This is visible with ethtool -k bond0 | grep esp: +tx-esp-segmentation: off [requested on] +esp-hw-offload: on +esp-tx-csum-hw-offload: on + +This patch reworks the bonding GSO offload support by: +- making aggregating gso_partial_features across slaves similar to the + other feature sets (this part is a no-op). +- advertising the default partial gso features on empty bond devs, same + as with other feature sets (also a no-op). +- adding NETIF_F_GSO_PARTIAL to hw_enc_features filtered across slaves. +- adding NETIF_F_GSO_PARTIAL to features in bond_setup() + +With all of these, 'ethtool -k bond0 | grep esp' now reports: +tx-esp-segmentation: on +esp-hw-offload: on +esp-tx-csum-hw-offload: on + +Fixes: 4861333b4217 ("bonding: add ESP offload features when slaves support") +Signed-off-by: Hangbin Liu +Signed-off-by: Cosmin Ratiu +Acked-by: Jay Vosburgh +Link: https://patch.msgid.link/20250127104147.759658-1-cratiu@nvidia.com +Signed-off-by: Paolo Abeni +Stable-dep-of: 950803f72547 ("bonding: fix type confusion in bond_setup_by_slave()") +Signed-off-by: Wentao Guan +Signed-off-by: Sasha Levin +--- + drivers/net/bonding/bond_main.c | 19 ++++++++++--------- + 1 file changed, 10 insertions(+), 9 deletions(-) + +diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c +index ec21958babe7d8..9ba63cf77cf086 100644 +--- a/drivers/net/bonding/bond_main.c ++++ b/drivers/net/bonding/bond_main.c +@@ -1474,17 +1474,20 @@ static netdev_features_t bond_fix_features(struct net_device *dev, + NETIF_F_HIGHDMA | NETIF_F_LRO) + + #define BOND_ENC_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \ +- NETIF_F_RXCSUM | NETIF_F_GSO_SOFTWARE) ++ NETIF_F_RXCSUM | NETIF_F_GSO_SOFTWARE | \ ++ NETIF_F_GSO_PARTIAL) + + #define BOND_MPLS_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \ + NETIF_F_GSO_SOFTWARE) + ++#define BOND_GSO_PARTIAL_FEATURES (NETIF_F_GSO_ESP) ++ + + static void bond_compute_features(struct bonding *bond) + { ++ netdev_features_t gso_partial_features = BOND_GSO_PARTIAL_FEATURES; + unsigned int dst_release_flag = IFF_XMIT_DST_RELEASE | + IFF_XMIT_DST_RELEASE_PERM; +- netdev_features_t gso_partial_features = NETIF_F_GSO_ESP; + netdev_features_t vlan_features = BOND_VLAN_FEATURES; + netdev_features_t enc_features = BOND_ENC_FEATURES; + #ifdef CONFIG_XFRM_OFFLOAD +@@ -1518,8 +1521,9 @@ static void bond_compute_features(struct bonding *bond) + BOND_XFRM_FEATURES); + #endif /* CONFIG_XFRM_OFFLOAD */ + +- if (slave->dev->hw_enc_features & NETIF_F_GSO_PARTIAL) +- gso_partial_features &= slave->dev->gso_partial_features; ++ gso_partial_features = netdev_increment_features(gso_partial_features, ++ slave->dev->gso_partial_features, ++ BOND_GSO_PARTIAL_FEATURES); + + mpls_features = netdev_increment_features(mpls_features, + slave->dev->mpls_features, +@@ -1534,12 +1538,8 @@ static void bond_compute_features(struct bonding *bond) + } + bond_dev->hard_header_len = max_hard_header_len; + +- if (gso_partial_features & NETIF_F_GSO_ESP) +- bond_dev->gso_partial_features |= NETIF_F_GSO_ESP; +- else +- bond_dev->gso_partial_features &= ~NETIF_F_GSO_ESP; +- + done: ++ bond_dev->gso_partial_features = gso_partial_features; + bond_dev->vlan_features = vlan_features; + bond_dev->hw_enc_features = enc_features | NETIF_F_GSO_ENCAP_ALL | + NETIF_F_HW_VLAN_CTAG_TX | +@@ -6090,6 +6090,7 @@ void bond_setup(struct net_device *bond_dev) + bond_dev->hw_features |= NETIF_F_GSO_ENCAP_ALL; + bond_dev->features |= bond_dev->hw_features; + bond_dev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_STAG_TX; ++ bond_dev->features |= NETIF_F_GSO_PARTIAL; + #ifdef CONFIG_XFRM_OFFLOAD + bond_dev->hw_features |= BOND_XFRM_FEATURES; + /* Only enable XFRM features if this is an active-backup config */ +-- +2.53.0 + diff --git a/queue-6.6/bonding-fix-initial-vlan-mpls-_feature-set-in-bond_c.patch b/queue-6.6/bonding-fix-initial-vlan-mpls-_feature-set-in-bond_c.patch new file mode 100644 index 0000000000..fe7c7338ca --- /dev/null +++ b/queue-6.6/bonding-fix-initial-vlan-mpls-_feature-set-in-bond_c.patch @@ -0,0 +1,54 @@ +From e5a61acb96184788c8c34c97709073db1dfb69fc Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 8 Jul 2026 17:47:06 +0800 +Subject: bonding: Fix initial {vlan,mpls}_feature set in bond_compute_features + +From: Daniel Borkmann + +[ Upstream commit d064ea7fe2a24938997b5e88e6b61cbb0a4bb906 ] + +If a bonding device has slave devices, then the current logic to derive +the feature set for the master bond device is limited in that flags which +are fully supported by the underlying slave devices cannot be propagated +up to vlan devices which sit on top of bond devices. Instead, these get +blindly masked out via current NETIF_F_ALL_FOR_ALL logic. + +vlan_features and mpls_features should reuse netdev_base_features() in +order derive the set in the same way as ndo_fix_features before iterating +through the slave devices to refine the feature set. + +Fixes: a9b3ace44c7d ("bonding: fix vlan_features computing") +Fixes: 2e770b507ccd ("net: bonding: Inherit MPLS features from slave devices") +Signed-off-by: Daniel Borkmann +Cc: Nikolay Aleksandrov +Cc: Ido Schimmel +Cc: Jiri Pirko +Reviewed-by: Nikolay Aleksandrov +Reviewed-by: Hangbin Liu +Link: https://patch.msgid.link/20241210141245.327886-2-daniel@iogearbox.net +Signed-off-by: Paolo Abeni +Signed-off-by: Wentao Guan +Signed-off-by: Sasha Levin +--- + drivers/net/bonding/bond_main.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c +index 36277bcefe2908..d200909a75cfe9 100644 +--- a/drivers/net/bonding/bond_main.c ++++ b/drivers/net/bonding/bond_main.c +@@ -1499,8 +1499,9 @@ static void bond_compute_features(struct bonding *bond) + + if (!bond_has_slaves(bond)) + goto done; +- vlan_features &= NETIF_F_ALL_FOR_ALL; +- mpls_features &= NETIF_F_ALL_FOR_ALL; ++ ++ vlan_features = netdev_base_features(vlan_features); ++ mpls_features = netdev_base_features(mpls_features); + + bond_for_each_slave(bond, slave, iter) { + vlan_features = netdev_increment_features(vlan_features, +-- +2.53.0 + diff --git a/queue-6.6/bonding-fix-type-confusion-in-bond_setup_by_slave.patch b/queue-6.6/bonding-fix-type-confusion-in-bond_setup_by_slave.patch new file mode 100644 index 0000000000..4a67b8323e --- /dev/null +++ b/queue-6.6/bonding-fix-type-confusion-in-bond_setup_by_slave.patch @@ -0,0 +1,152 @@ +From f4d1907efe2a511e1f9b65987c5037d12594ce80 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 8 Jul 2026 17:47:19 +0800 +Subject: bonding: fix type confusion in bond_setup_by_slave() + +From: Jiayuan Chen + +[ Upstream commit 950803f7254721c1c15858fbbfae3deaaeeecb11 ] + +kernel BUG at net/core/skbuff.c:2306! +Oops: invalid opcode: 0000 [#1] SMP KASAN NOPTI +RIP: 0010:pskb_expand_head+0xa08/0xfe0 net/core/skbuff.c:2306 +RSP: 0018:ffffc90004aff760 EFLAGS: 00010293 +RAX: 0000000000000000 RBX: ffff88807e3c8780 RCX: ffffffff89593e0e +RDX: ffff88807b7c4900 RSI: ffffffff89594747 RDI: ffff88807b7c4900 +RBP: 0000000000000820 R08: 0000000000000005 R09: 0000000000000000 +R10: 00000000961a63e0 R11: 0000000000000000 R12: ffff88807e3c8780 +R13: 00000000961a6560 R14: dffffc0000000000 R15: 00000000961a63e0 +CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 +CR2: 00007fe1a0ed8df0 CR3: 000000002d816000 CR4: 00000000003526f0 +Call Trace: + + ipgre_header+0xdd/0x540 net/ipv4/ip_gre.c:900 + dev_hard_header include/linux/netdevice.h:3439 [inline] + packet_snd net/packet/af_packet.c:3028 [inline] + packet_sendmsg+0x3ae5/0x53c0 net/packet/af_packet.c:3108 + sock_sendmsg_nosec net/socket.c:727 [inline] + __sock_sendmsg net/socket.c:742 [inline] + ____sys_sendmsg+0xa54/0xc30 net/socket.c:2592 + ___sys_sendmsg+0x190/0x1e0 net/socket.c:2646 + __sys_sendmsg+0x170/0x220 net/socket.c:2678 + do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] + do_syscall_64+0x106/0xf80 arch/x86/entry/syscall_64.c:94 + entry_SYSCALL_64_after_hwframe+0x77/0x7f +RIP: 0033:0x7fe1a0e6c1a9 + +When a non-Ethernet device (e.g. GRE tunnel) is enslaved to a bond, +bond_setup_by_slave() directly copies the slave's header_ops to the +bond device: + + bond_dev->header_ops = slave_dev->header_ops; + +This causes a type confusion when dev_hard_header() is later called +on the bond device. Functions like ipgre_header(), ip6gre_header(),all use +netdev_priv(dev) to access their device-specific private data. When +called with the bond device, netdev_priv() returns the bond's private +data (struct bonding) instead of the expected type (e.g. struct +ip_tunnel), leading to garbage values being read and kernel crashes. + +Fix this by introducing bond_header_ops with wrapper functions that +delegate to the active slave's header_ops using the slave's own +device. This ensures netdev_priv() in the slave's header functions +always receives the correct device. + +The fix is placed in the bonding driver rather than individual device +drivers, as the root cause is bond blindly inheriting header_ops from +the slave without considering that these callbacks expect a specific +netdev_priv() layout. + +The type confusion can be observed by adding a printk in +ipgre_header() and running the following commands: + + ip link add dummy0 type dummy + ip addr add 10.0.0.1/24 dev dummy0 + ip link set dummy0 up + ip link add gre1 type gre local 10.0.0.1 + ip link add bond1 type bond mode active-backup + ip link set gre1 master bond1 + ip link set gre1 up + ip link set bond1 up + ip addr add fe80::1/64 dev bond1 + +Fixes: 1284cd3a2b74 ("bonding: two small fixes for IPoIB support") +Suggested-by: Jay Vosburgh +Reviewed-by: Eric Dumazet +Signed-off-by: Jiayuan Chen +Link: https://patch.msgid.link/20260306021508.222062-1-jiayuan.chen@linux.dev +Signed-off-by: Paolo Abeni +Signed-off-by: Wentao Guan +Signed-off-by: Sasha Levin +--- + drivers/net/bonding/bond_main.c | 47 ++++++++++++++++++++++++++++++++- + 1 file changed, 46 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c +index 8c156f71c70667..af8cdc8d26c91d 100644 +--- a/drivers/net/bonding/bond_main.c ++++ b/drivers/net/bonding/bond_main.c +@@ -1468,6 +1468,50 @@ static netdev_features_t bond_fix_features(struct net_device *dev, + return features; + } + ++static int bond_header_create(struct sk_buff *skb, struct net_device *bond_dev, ++ unsigned short type, const void *daddr, ++ const void *saddr, unsigned int len) ++{ ++ struct bonding *bond = netdev_priv(bond_dev); ++ const struct header_ops *slave_ops; ++ struct slave *slave; ++ int ret = 0; ++ ++ rcu_read_lock(); ++ slave = rcu_dereference(bond->curr_active_slave); ++ if (slave) { ++ slave_ops = READ_ONCE(slave->dev->header_ops); ++ if (slave_ops && slave_ops->create) ++ ret = slave_ops->create(skb, slave->dev, ++ type, daddr, saddr, len); ++ } ++ rcu_read_unlock(); ++ return ret; ++} ++ ++static int bond_header_parse(const struct sk_buff *skb, unsigned char *haddr) ++{ ++ struct bonding *bond = netdev_priv(skb->dev); ++ const struct header_ops *slave_ops; ++ struct slave *slave; ++ int ret = 0; ++ ++ rcu_read_lock(); ++ slave = rcu_dereference(bond->curr_active_slave); ++ if (slave) { ++ slave_ops = READ_ONCE(slave->dev->header_ops); ++ if (slave_ops && slave_ops->parse) ++ ret = slave_ops->parse(skb, haddr); ++ } ++ rcu_read_unlock(); ++ return ret; ++} ++ ++static const struct header_ops bond_header_ops = { ++ .create = bond_header_create, ++ .parse = bond_header_parse, ++}; ++ + static void bond_setup_by_slave(struct net_device *bond_dev, + struct net_device *slave_dev) + { +@@ -1475,7 +1519,8 @@ static void bond_setup_by_slave(struct net_device *bond_dev, + + dev_close(bond_dev); + +- bond_dev->header_ops = slave_dev->header_ops; ++ bond_dev->header_ops = slave_dev->header_ops ? ++ &bond_header_ops : NULL; + + bond_dev->type = slave_dev->type; + bond_dev->hard_header_len = slave_dev->hard_header_len; +-- +2.53.0 + diff --git a/queue-6.6/bonding-fix-xfrm-offload-feature-setup-on-active-bac.patch b/queue-6.6/bonding-fix-xfrm-offload-feature-setup-on-active-bac.patch new file mode 100644 index 0000000000..aeb3fffd44 --- /dev/null +++ b/queue-6.6/bonding-fix-xfrm-offload-feature-setup-on-active-bac.patch @@ -0,0 +1,94 @@ +From 24c4923bd46d77df84b5d67da010e135fdd33bf0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 8 Jul 2026 17:47:17 +0800 +Subject: bonding: fix xfrm offload feature setup on active-backup mode + +From: Hangbin Liu + +[ Upstream commit 5b66169f6be4847008c0aea50885ff0632151479 ] + +The active-backup bonding mode supports XFRM ESP offload. However, when +a bond is added using command like `ip link add bond0 type bond mode 1 +miimon 100`, the `ethtool -k` command shows that the XFRM ESP offload is +disabled. This occurs because, in bond_newlink(), we change bond link +first and register bond device later. So the XFRM feature update in +bond_option_mode_set() is not called as the bond device is not yet +registered, leading to the offload feature not being set successfully. + +To resolve this issue, we can modify the code order in bond_newlink() to +ensure that the bond device is registered first before changing the bond +link parameters. This change will allow the XFRM ESP offload feature to be +correctly enabled. + +Fixes: 007ab5345545 ("bonding: fix feature flag setting at init time") +Signed-off-by: Hangbin Liu +Link: https://patch.msgid.link/20250925023304.472186-1-liuhangbin@gmail.com +Signed-off-by: Paolo Abeni +Signed-off-by: Wentao Guan +Signed-off-by: Sasha Levin +--- + drivers/net/bonding/bond_main.c | 2 +- + drivers/net/bonding/bond_netlink.c | 16 +++++++++------- + include/net/bonding.h | 1 + + 3 files changed, 11 insertions(+), 8 deletions(-) + +diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c +index 4f2c03f52a310b..8c156f71c70667 100644 +--- a/drivers/net/bonding/bond_main.c ++++ b/drivers/net/bonding/bond_main.c +@@ -4260,7 +4260,7 @@ void bond_work_init_all(struct bonding *bond) + INIT_DELAYED_WORK(&bond->slave_arr_work, bond_slave_arr_handler); + } + +-static void bond_work_cancel_all(struct bonding *bond) ++void bond_work_cancel_all(struct bonding *bond) + { + cancel_delayed_work_sync(&bond->mii_work); + cancel_delayed_work_sync(&bond->arp_work); +diff --git a/drivers/net/bonding/bond_netlink.c b/drivers/net/bonding/bond_netlink.c +index 0eaf4b0e06ffb2..f771d569524f3e 100644 +--- a/drivers/net/bonding/bond_netlink.c ++++ b/drivers/net/bonding/bond_netlink.c +@@ -596,18 +596,20 @@ static int bond_newlink(struct net *src_net, struct net_device *bond_dev, + struct nlattr *tb[], struct nlattr *data[], + struct netlink_ext_ack *extack) + { ++ struct bonding *bond = netdev_priv(bond_dev); + int err; + +- err = bond_changelink(bond_dev, tb, data, extack); +- if (err < 0) ++ err = register_netdevice(bond_dev); ++ if (err) + return err; + +- err = register_netdevice(bond_dev); +- if (!err) { +- struct bonding *bond = netdev_priv(bond_dev); ++ netif_carrier_off(bond_dev); ++ bond_work_init_all(bond); + +- netif_carrier_off(bond_dev); +- bond_work_init_all(bond); ++ err = bond_changelink(bond_dev, tb, data, extack); ++ if (err) { ++ bond_work_cancel_all(bond); ++ unregister_netdevice(bond_dev); + } + + return err; +diff --git a/include/net/bonding.h b/include/net/bonding.h +index 0d9c1eb40d12b6..8f5507ef5c0dac 100644 +--- a/include/net/bonding.h ++++ b/include/net/bonding.h +@@ -713,6 +713,7 @@ struct bond_vlan_tag *bond_verify_device_path(struct net_device *start_dev, + int bond_update_slave_arr(struct bonding *bond, struct slave *skipslave); + void bond_slave_arr_work_rearm(struct bonding *bond, unsigned long delay); + void bond_work_init_all(struct bonding *bond); ++void bond_work_cancel_all(struct bonding *bond); + + #ifdef CONFIG_PROC_FS + void bond_create_proc_entry(struct bonding *bond); +-- +2.53.0 + diff --git a/queue-6.6/bonding-prevent-potential-infinite-loop-in-bond_head.patch b/queue-6.6/bonding-prevent-potential-infinite-loop-in-bond_head.patch new file mode 100644 index 0000000000..c380caed38 --- /dev/null +++ b/queue-6.6/bonding-prevent-potential-infinite-loop-in-bond_head.patch @@ -0,0 +1,236 @@ +From 685145376618de2a2f7f47cb66119d55e8b39a53 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 8 Jul 2026 17:47:21 +0800 +Subject: bonding: prevent potential infinite loop in bond_header_parse() + +From: Eric Dumazet + +[ Upstream commit b7405dcf7385445e10821777143f18c3ce20fa04 ] + +bond_header_parse() can loop if a stack of two bonding devices is setup, +because skb->dev always points to the hierarchy top. + +Add new "const struct net_device *dev" parameter to +(struct header_ops)->parse() method to make sure the recursion +is bounded, and that the final leaf parse method is called. + +Fixes: 950803f72547 ("bonding: fix type confusion in bond_setup_by_slave()") +Signed-off-by: Eric Dumazet +Reviewed-by: Jiayuan Chen +Tested-by: Jiayuan Chen +Cc: Jay Vosburgh +Cc: Andrew Lunn +Link: https://patch.msgid.link/20260315104152.1436867-1-edumazet@google.com +Signed-off-by: Jakub Kicinski +[backport to 6.6, also fix function for wireless/{cisco/hostap}] +Signed-off-by: Wentao Guan +Signed-off-by: Sasha Levin +--- + drivers/firewire/net.c | 5 +++-- + drivers/net/bonding/bond_main.c | 8 +++++--- + drivers/net/wireless/cisco/airo.c | 4 +++- + drivers/net/wireless/intersil/hostap/hostap_main.c | 1 + + include/linux/etherdevice.h | 3 ++- + include/linux/if_ether.h | 3 ++- + include/linux/netdevice.h | 6 ++++-- + net/ethernet/eth.c | 9 +++------ + net/ipv4/ip_gre.c | 3 ++- + net/mac802154/iface.c | 4 +++- + net/phonet/af_phonet.c | 5 ++++- + 11 files changed, 32 insertions(+), 19 deletions(-) + +diff --git a/drivers/firewire/net.c b/drivers/firewire/net.c +index 7a4d1a478e33e2..21f3a9dae072a9 100644 +--- a/drivers/firewire/net.c ++++ b/drivers/firewire/net.c +@@ -257,9 +257,10 @@ static void fwnet_header_cache_update(struct hh_cache *hh, + memcpy((u8 *)hh->hh_data + HH_DATA_OFF(FWNET_HLEN), haddr, net->addr_len); + } + +-static int fwnet_header_parse(const struct sk_buff *skb, unsigned char *haddr) ++static int fwnet_header_parse(const struct sk_buff *skb, const struct net_device *dev, ++ unsigned char *haddr) + { +- memcpy(haddr, skb->dev->dev_addr, FWNET_ALEN); ++ memcpy(haddr, dev->dev_addr, FWNET_ALEN); + + return FWNET_ALEN; + } +diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c +index af8cdc8d26c91d..6b558aa98c6d2f 100644 +--- a/drivers/net/bonding/bond_main.c ++++ b/drivers/net/bonding/bond_main.c +@@ -1489,9 +1489,11 @@ static int bond_header_create(struct sk_buff *skb, struct net_device *bond_dev, + return ret; + } + +-static int bond_header_parse(const struct sk_buff *skb, unsigned char *haddr) ++static int bond_header_parse(const struct sk_buff *skb, ++ const struct net_device *dev, ++ unsigned char *haddr) + { +- struct bonding *bond = netdev_priv(skb->dev); ++ struct bonding *bond = netdev_priv(dev); + const struct header_ops *slave_ops; + struct slave *slave; + int ret = 0; +@@ -1501,7 +1503,7 @@ static int bond_header_parse(const struct sk_buff *skb, unsigned char *haddr) + if (slave) { + slave_ops = READ_ONCE(slave->dev->header_ops); + if (slave_ops && slave_ops->parse) +- ret = slave_ops->parse(skb, haddr); ++ ret = slave_ops->parse(skb, slave->dev, haddr); + } + rcu_read_unlock(); + return ret; +diff --git a/drivers/net/wireless/cisco/airo.c b/drivers/net/wireless/cisco/airo.c +index dbd13f7aa3e6ea..bd269fdaa1d014 100644 +--- a/drivers/net/wireless/cisco/airo.c ++++ b/drivers/net/wireless/cisco/airo.c +@@ -2437,7 +2437,9 @@ void stop_airo_card(struct net_device *dev, int freeres) + + EXPORT_SYMBOL(stop_airo_card); + +-static int wll_header_parse(const struct sk_buff *skb, unsigned char *haddr) ++static int wll_header_parse(const struct sk_buff *skb, ++ const struct net_device *dev, ++ unsigned char *haddr) + { + memcpy(haddr, skb_mac_header(skb) + 10, ETH_ALEN); + return ETH_ALEN; +diff --git a/drivers/net/wireless/intersil/hostap/hostap_main.c b/drivers/net/wireless/intersil/hostap/hostap_main.c +index 787f685e70b492..8ba1a709fe47de 100644 +--- a/drivers/net/wireless/intersil/hostap/hostap_main.c ++++ b/drivers/net/wireless/intersil/hostap/hostap_main.c +@@ -575,6 +575,7 @@ void hostap_dump_tx_header(const char *name, const struct hfa384x_tx_frame *tx) + + + static int hostap_80211_header_parse(const struct sk_buff *skb, ++ const struct net_device *dev, + unsigned char *haddr) + { + memcpy(haddr, skb_mac_header(skb) + 10, ETH_ALEN); /* addr2 */ +diff --git a/include/linux/etherdevice.h b/include/linux/etherdevice.h +index 297231854ada51..6f96d1c6d10fd1 100644 +--- a/include/linux/etherdevice.h ++++ b/include/linux/etherdevice.h +@@ -42,7 +42,8 @@ extern const struct header_ops eth_header_ops; + + int eth_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, + const void *daddr, const void *saddr, unsigned len); +-int eth_header_parse(const struct sk_buff *skb, unsigned char *haddr); ++int eth_header_parse(const struct sk_buff *skb, const struct net_device *dev, ++ unsigned char *haddr); + int eth_header_cache(const struct neighbour *neigh, struct hh_cache *hh, + __be16 type); + void eth_header_cache_update(struct hh_cache *hh, const struct net_device *dev, +diff --git a/include/linux/if_ether.h b/include/linux/if_ether.h +index 8a9792a6427ad9..47a0feffc12155 100644 +--- a/include/linux/if_ether.h ++++ b/include/linux/if_ether.h +@@ -37,7 +37,8 @@ static inline struct ethhdr *inner_eth_hdr(const struct sk_buff *skb) + return (struct ethhdr *)skb_inner_mac_header(skb); + } + +-int eth_header_parse(const struct sk_buff *skb, unsigned char *haddr); ++int eth_header_parse(const struct sk_buff *skb, const struct net_device *dev, ++ unsigned char *haddr); + + extern ssize_t sysfs_format_mac(char *buf, const unsigned char *addr, int len); + +diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h +index a81dab6c2f5f24..74474139fb9ac8 100644 +--- a/include/linux/netdevice.h ++++ b/include/linux/netdevice.h +@@ -312,7 +312,9 @@ struct header_ops { + int (*create) (struct sk_buff *skb, struct net_device *dev, + unsigned short type, const void *daddr, + const void *saddr, unsigned int len); +- int (*parse)(const struct sk_buff *skb, unsigned char *haddr); ++ int (*parse)(const struct sk_buff *skb, ++ const struct net_device *dev, ++ unsigned char *haddr); + int (*cache)(const struct neighbour *neigh, struct hh_cache *hh, __be16 type); + void (*cache_update)(struct hh_cache *hh, + const struct net_device *dev, +@@ -3174,7 +3176,7 @@ static inline int dev_parse_header(const struct sk_buff *skb, + + if (!dev->header_ops || !dev->header_ops->parse) + return 0; +- return dev->header_ops->parse(skb, haddr); ++ return dev->header_ops->parse(skb, dev, haddr); + } + + static inline __be16 dev_parse_header_protocol(const struct sk_buff *skb) +diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c +index b4a6e26ec2871b..a90d2470e090bb 100644 +--- a/net/ethernet/eth.c ++++ b/net/ethernet/eth.c +@@ -195,14 +195,11 @@ __be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev) + } + EXPORT_SYMBOL(eth_type_trans); + +-/** +- * eth_header_parse - extract hardware address from packet +- * @skb: packet to extract header from +- * @haddr: destination buffer +- */ +-int eth_header_parse(const struct sk_buff *skb, unsigned char *haddr) ++int eth_header_parse(const struct sk_buff *skb, const struct net_device *dev, ++ unsigned char *haddr) + { + const struct ethhdr *eth = eth_hdr(skb); ++ + memcpy(haddr, eth->h_source, ETH_ALEN); + return ETH_ALEN; + } +diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c +index e58965ce8263d2..b5ebfee9a8faf6 100644 +--- a/net/ipv4/ip_gre.c ++++ b/net/ipv4/ip_gre.c +@@ -888,7 +888,8 @@ static int ipgre_header(struct sk_buff *skb, struct net_device *dev, + return -(t->hlen + sizeof(*iph)); + } + +-static int ipgre_header_parse(const struct sk_buff *skb, unsigned char *haddr) ++static int ipgre_header_parse(const struct sk_buff *skb, const struct net_device *dev, ++ unsigned char *haddr) + { + const struct iphdr *iph = (const struct iphdr *) skb_mac_header(skb); + memcpy(haddr, &iph->saddr, 4); +diff --git a/net/mac802154/iface.c b/net/mac802154/iface.c +index 9e4631fade90c9..000be60d958034 100644 +--- a/net/mac802154/iface.c ++++ b/net/mac802154/iface.c +@@ -469,7 +469,9 @@ static int mac802154_header_create(struct sk_buff *skb, + } + + static int +-mac802154_header_parse(const struct sk_buff *skb, unsigned char *haddr) ++mac802154_header_parse(const struct sk_buff *skb, ++ const struct net_device *dev, ++ unsigned char *haddr) + { + struct ieee802154_hdr hdr; + +diff --git a/net/phonet/af_phonet.c b/net/phonet/af_phonet.c +index 2b582da1e88c05..e38fad4144e489 100644 +--- a/net/phonet/af_phonet.c ++++ b/net/phonet/af_phonet.c +@@ -129,9 +129,12 @@ static int pn_header_create(struct sk_buff *skb, struct net_device *dev, + return 1; + } + +-static int pn_header_parse(const struct sk_buff *skb, unsigned char *haddr) ++static int pn_header_parse(const struct sk_buff *skb, ++ const struct net_device *dev, ++ unsigned char *haddr) + { + const u8 *media = skb_mac_header(skb); ++ + *haddr = *media; + return 1; + } +-- +2.53.0 + diff --git a/queue-6.6/bonding-use-common-function-to-compute-the-features.patch b/queue-6.6/bonding-use-common-function-to-compute-the-features.patch new file mode 100644 index 0000000000..f5a13e3b49 --- /dev/null +++ b/queue-6.6/bonding-use-common-function-to-compute-the-features.patch @@ -0,0 +1,173 @@ +From da6240a620a80cc06bbc924263952938ce8355eb Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 8 Jul 2026 17:47:15 +0800 +Subject: bonding: use common function to compute the features + +From: Hangbin Liu + +[ Upstream commit d4fde269a970666a30dd3abd0413273a06dd972d ] + +Use the new functon netdev_compute_master_upper_features() to compute the bonding +features. + +Note that bond_compute_features() currently uses bond_for_each_slave() +to traverse the lower devices list, and that is just a macro wrapper of +netdev_for_each_lower_private(). We use similar helper +netdev_for_each_lower_dev() in netdev_compute_master_upper_features() to +iterate the slave device, as there is not need to get the private data. + +No functional change intended. + +Signed-off-by: Hangbin Liu +Reviewed-by: Sabrina Dubroca +Reviewed-by: Jiri Pirko +Link: https://patch.msgid.link/20251017034155.61990-3-liuhangbin@gmail.com +Signed-off-by: Jakub Kicinski +Stable-dep-of: 950803f72547 ("bonding: fix type confusion in bond_setup_by_slave()") +Signed-off-by: Wentao Guan +Signed-off-by: Sasha Levin +--- + drivers/net/bonding/bond_main.c | 99 ++------------------------------- + 1 file changed, 4 insertions(+), 95 deletions(-) + +diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c +index 9ba63cf77cf086..4f2c03f52a310b 100644 +--- a/drivers/net/bonding/bond_main.c ++++ b/drivers/net/bonding/bond_main.c +@@ -1468,97 +1468,6 @@ static netdev_features_t bond_fix_features(struct net_device *dev, + return features; + } + +-#define BOND_VLAN_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \ +- NETIF_F_FRAGLIST | NETIF_F_GSO_SOFTWARE | \ +- NETIF_F_GSO_ENCAP_ALL | \ +- NETIF_F_HIGHDMA | NETIF_F_LRO) +- +-#define BOND_ENC_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \ +- NETIF_F_RXCSUM | NETIF_F_GSO_SOFTWARE | \ +- NETIF_F_GSO_PARTIAL) +- +-#define BOND_MPLS_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \ +- NETIF_F_GSO_SOFTWARE) +- +-#define BOND_GSO_PARTIAL_FEATURES (NETIF_F_GSO_ESP) +- +- +-static void bond_compute_features(struct bonding *bond) +-{ +- netdev_features_t gso_partial_features = BOND_GSO_PARTIAL_FEATURES; +- unsigned int dst_release_flag = IFF_XMIT_DST_RELEASE | +- IFF_XMIT_DST_RELEASE_PERM; +- netdev_features_t vlan_features = BOND_VLAN_FEATURES; +- netdev_features_t enc_features = BOND_ENC_FEATURES; +-#ifdef CONFIG_XFRM_OFFLOAD +- netdev_features_t xfrm_features = BOND_XFRM_FEATURES; +-#endif /* CONFIG_XFRM_OFFLOAD */ +- netdev_features_t mpls_features = BOND_MPLS_FEATURES; +- struct net_device *bond_dev = bond->dev; +- struct list_head *iter; +- struct slave *slave; +- unsigned short max_hard_header_len = ETH_HLEN; +- unsigned int tso_max_size = TSO_MAX_SIZE; +- u16 tso_max_segs = TSO_MAX_SEGS; +- +- if (!bond_has_slaves(bond)) +- goto done; +- +- vlan_features = netdev_base_features(vlan_features); +- mpls_features = netdev_base_features(mpls_features); +- +- bond_for_each_slave(bond, slave, iter) { +- vlan_features = netdev_increment_features(vlan_features, +- slave->dev->vlan_features, BOND_VLAN_FEATURES); +- +- enc_features = netdev_increment_features(enc_features, +- slave->dev->hw_enc_features, +- BOND_ENC_FEATURES); +- +-#ifdef CONFIG_XFRM_OFFLOAD +- xfrm_features = netdev_increment_features(xfrm_features, +- slave->dev->hw_enc_features, +- BOND_XFRM_FEATURES); +-#endif /* CONFIG_XFRM_OFFLOAD */ +- +- gso_partial_features = netdev_increment_features(gso_partial_features, +- slave->dev->gso_partial_features, +- BOND_GSO_PARTIAL_FEATURES); +- +- mpls_features = netdev_increment_features(mpls_features, +- slave->dev->mpls_features, +- BOND_MPLS_FEATURES); +- +- dst_release_flag &= slave->dev->priv_flags; +- if (slave->dev->hard_header_len > max_hard_header_len) +- max_hard_header_len = slave->dev->hard_header_len; +- +- tso_max_size = min(tso_max_size, slave->dev->tso_max_size); +- tso_max_segs = min(tso_max_segs, slave->dev->tso_max_segs); +- } +- bond_dev->hard_header_len = max_hard_header_len; +- +-done: +- bond_dev->gso_partial_features = gso_partial_features; +- bond_dev->vlan_features = vlan_features; +- bond_dev->hw_enc_features = enc_features | NETIF_F_GSO_ENCAP_ALL | +- NETIF_F_HW_VLAN_CTAG_TX | +- NETIF_F_HW_VLAN_STAG_TX; +-#ifdef CONFIG_XFRM_OFFLOAD +- bond_dev->hw_enc_features |= xfrm_features; +-#endif /* CONFIG_XFRM_OFFLOAD */ +- bond_dev->mpls_features = mpls_features; +- netif_set_tso_max_segs(bond_dev, tso_max_segs); +- netif_set_tso_max_size(bond_dev, tso_max_size); +- +- bond_dev->priv_flags &= ~IFF_XMIT_DST_RELEASE; +- if ((bond_dev->priv_flags & IFF_XMIT_DST_RELEASE_PERM) && +- dst_release_flag == (IFF_XMIT_DST_RELEASE | IFF_XMIT_DST_RELEASE_PERM)) +- bond_dev->priv_flags |= IFF_XMIT_DST_RELEASE; +- +- netdev_change_features(bond_dev); +-} +- + static void bond_setup_by_slave(struct net_device *bond_dev, + struct net_device *slave_dev) + { +@@ -2311,7 +2220,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev, + } + + bond->slave_cnt++; +- bond_compute_features(bond); ++ netdev_compute_master_upper_features(bond->dev, true); + bond_set_carrier(bond); + + /* Needs to be called before bond_select_active_slave(), which will +@@ -2563,7 +2472,7 @@ static int __bond_release_one(struct net_device *bond_dev, + call_netdevice_notifiers(NETDEV_RELEASE, bond->dev); + } + +- bond_compute_features(bond); ++ netdev_compute_master_upper_features(bond->dev, true); + if (!(bond_dev->features & NETIF_F_VLAN_CHALLENGED) && + (old_features & NETIF_F_VLAN_CHALLENGED)) + slave_info(bond_dev, slave_dev, "last VLAN challenged slave left bond - VLAN blocking is removed\n"); +@@ -4065,7 +3974,7 @@ static int bond_slave_netdev_event(unsigned long event, + case NETDEV_FEAT_CHANGE: + if (!bond->notifier_ctx) { + bond->notifier_ctx = true; +- bond_compute_features(bond); ++ netdev_compute_master_upper_features(bond->dev, true); + bond->notifier_ctx = false; + } + break; +@@ -6081,7 +5990,7 @@ void bond_setup(struct net_device *bond_dev) + /* Don't allow bond devices to change network namespaces. */ + bond_dev->features |= NETIF_F_NETNS_LOCAL; + +- bond_dev->hw_features = BOND_VLAN_FEATURES | ++ bond_dev->hw_features = MASTER_UPPER_DEV_VLAN_FEATURES | + NETIF_F_HW_VLAN_CTAG_RX | + NETIF_F_HW_VLAN_CTAG_FILTER | + NETIF_F_HW_VLAN_STAG_RX | +-- +2.53.0 + diff --git a/queue-6.6/mm-vmscan-flush-deferred-tlb-before-freeing-large-fo.patch b/queue-6.6/mm-vmscan-flush-deferred-tlb-before-freeing-large-fo.patch new file mode 100644 index 0000000000..720dcd22d2 --- /dev/null +++ b/queue-6.6/mm-vmscan-flush-deferred-tlb-before-freeing-large-fo.patch @@ -0,0 +1,60 @@ +From e43878f5e8210161803bef7a85475f554a84d771 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 8 Jul 2026 12:12:36 +0800 +Subject: mm/vmscan: flush deferred TLB before freeing large folios + +From: Jiayuan Chen + +In reclaim, shrink_folio_list() unmaps PTEs with a deferred, batched TLB +flush. The batch is only flushed by try_to_unmap_flush() near the end of +the function, just before the order-0 folios collected in @free_folios are +handed back to the allocator. + +Large folios don't go through @free_folios -- they're freed inline at the +free_it label via destroy_large_folio(), which runs before that flush. So +a large folio's pages can be returned to the buddy allocator and reused +while another CPU still holds a stale TLB entry for them, and that CPU then +reads or executes through the stale translation into the reused page. For +file-backed large folios (e.g. executable text) this shows up as random +SIGSEGV/SIGILL in user space, with fault addresses that don't match the +code being run. + +Flush the deferred batch before freeing a large folio inline, the same way +the order-0 path already waits for the flush. + +Upstream this is fixed as a side effect of commit bc2ff4cbc329 ("mm: free +folios in a batch in shrink_folio_list()"), which is a larger change; this +is the minimal fix for -stable. + +Reported-by: Yingfu Zhou +Fixes: bd4c82c22c36 ("mm, THP, swap: delay splitting THP after swapped out") +Cc: Jiayuan Chen +Signed-off-by: Jiayuan Chen +Reviewed-by: Matthew Wilcox +Signed-off-by: Sasha Levin +--- + mm/vmscan.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/mm/vmscan.c b/mm/vmscan.c +index aba757e5c59784..8eb498351d9b30 100644 +--- a/mm/vmscan.c ++++ b/mm/vmscan.c +@@ -2123,10 +2123,12 @@ static unsigned int shrink_folio_list(struct list_head *folio_list, + * Is there need to periodically free_folio_list? It would + * appear not as the counts should be low + */ +- if (unlikely(folio_test_large(folio))) ++ if (unlikely(folio_test_large(folio))) { ++ try_to_unmap_flush(); + destroy_large_folio(folio); +- else ++ } else { + list_add(&folio->lru, &free_folios); ++ } + continue; + + activate_locked_split: +-- +2.53.0 + diff --git a/queue-6.6/net-add-a-common-function-to-compute-features-for-up.patch b/queue-6.6/net-add-a-common-function-to-compute-features-for-up.patch new file mode 100644 index 0000000000..58cee49a45 --- /dev/null +++ b/queue-6.6/net-add-a-common-function-to-compute-features-for-up.patch @@ -0,0 +1,176 @@ +From e55b7c4ebfcdbc5ce81f09961a1e15787bbc2c14 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 8 Jul 2026 17:47:12 +0800 +Subject: net: add a common function to compute features for upper devices + +From: Hangbin Liu + +[ Upstream commit 28098defc79fe7d29e6bfe4eb6312991f6bdc3d3 ] + +Some high level software drivers need to compute features from lower +devices. But each has their own implementations and may lost some +feature compute. Let's use one common function to compute features +for kinds of these devices. + +The new helper uses the current bond implementation as the reference +one, as the latter already handles all the relevant aspects: netdev +features, TSO limits and dst retention. + +Suggested-by: Paolo Abeni +Signed-off-by: Hangbin Liu +Reviewed-by: Sabrina Dubroca +Reviewed-by: Jiri Pirko +Link: https://patch.msgid.link/20251017034155.61990-2-liuhangbin@gmail.com +Signed-off-by: Jakub Kicinski +Stable-dep-of: 950803f72547 ("bonding: fix type confusion in bond_setup_by_slave()") +Signed-off-by: Wentao Guan +Signed-off-by: Sasha Levin +--- + include/linux/netdev_features.h | 18 +++++++ + include/linux/netdevice.h | 1 + + net/core/dev.c | 88 +++++++++++++++++++++++++++++++++ + 3 files changed, 107 insertions(+) + +diff --git a/include/linux/netdev_features.h b/include/linux/netdev_features.h +index 1b5ad57e7cbf5a..988e62cafd316a 100644 +--- a/include/linux/netdev_features.h ++++ b/include/linux/netdev_features.h +@@ -261,6 +261,24 @@ static inline int find_next_netdev_feature(u64 feature, unsigned long start) + NETIF_F_GSO_UDP_TUNNEL | \ + NETIF_F_GSO_UDP_TUNNEL_CSUM) + ++/* virtual device features */ ++#define MASTER_UPPER_DEV_VLAN_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \ ++ NETIF_F_FRAGLIST | NETIF_F_GSO_SOFTWARE | \ ++ NETIF_F_GSO_ENCAP_ALL | \ ++ NETIF_F_HIGHDMA | NETIF_F_LRO) ++ ++#define MASTER_UPPER_DEV_ENC_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \ ++ NETIF_F_RXCSUM | NETIF_F_GSO_SOFTWARE | \ ++ NETIF_F_GSO_PARTIAL) ++ ++#define MASTER_UPPER_DEV_MPLS_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \ ++ NETIF_F_GSO_SOFTWARE) ++ ++#define MASTER_UPPER_DEV_XFRM_FEATURES (NETIF_F_HW_ESP | NETIF_F_HW_ESP_TX_CSUM | \ ++ NETIF_F_GSO_ESP) ++ ++#define MASTER_UPPER_DEV_GSO_PARTIAL_FEATURES (NETIF_F_GSO_ESP) ++ + static inline netdev_features_t netdev_base_features(netdev_features_t features) + { + features &= ~NETIF_F_ONE_FOR_ALL; +diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h +index 202e557496fb41..a81dab6c2f5f24 100644 +--- a/include/linux/netdevice.h ++++ b/include/linux/netdevice.h +@@ -5005,6 +5005,7 @@ static inline netdev_features_t netdev_add_tso_features(netdev_features_t featur + int __netdev_update_features(struct net_device *dev); + void netdev_update_features(struct net_device *dev); + void netdev_change_features(struct net_device *dev); ++void netdev_compute_master_upper_features(struct net_device *dev, bool update_header); + + void netif_stacked_transfer_operstate(const struct net_device *rootdev, + struct net_device *dev); +diff --git a/net/core/dev.c b/net/core/dev.c +index 405b448bd91fef..0cc03e6c6fb144 100644 +--- a/net/core/dev.c ++++ b/net/core/dev.c +@@ -11450,6 +11450,94 @@ netdev_features_t netdev_increment_features(netdev_features_t all, + } + EXPORT_SYMBOL(netdev_increment_features); + ++/** ++ * netdev_compute_master_upper_features - compute feature from lowers ++ * @dev: the upper device ++ * @update_header: whether to update upper device's header_len/headroom/tailroom ++ * ++ * Recompute the upper device's feature based on all lower devices. ++ */ ++void netdev_compute_master_upper_features(struct net_device *dev, bool update_header) ++{ ++ unsigned int dst_release_flag = IFF_XMIT_DST_RELEASE | IFF_XMIT_DST_RELEASE_PERM; ++ netdev_features_t gso_partial_features = MASTER_UPPER_DEV_GSO_PARTIAL_FEATURES; ++ netdev_features_t xfrm_features = MASTER_UPPER_DEV_XFRM_FEATURES; ++ netdev_features_t mpls_features = MASTER_UPPER_DEV_MPLS_FEATURES; ++ netdev_features_t vlan_features = MASTER_UPPER_DEV_VLAN_FEATURES; ++ netdev_features_t enc_features = MASTER_UPPER_DEV_ENC_FEATURES; ++ unsigned short max_header_len = ETH_HLEN; ++ unsigned int tso_max_size = TSO_MAX_SIZE; ++ unsigned short max_headroom = 0; ++ unsigned short max_tailroom = 0; ++ u16 tso_max_segs = TSO_MAX_SEGS; ++ struct net_device *lower_dev; ++ struct list_head *iter; ++ ++ mpls_features = netdev_base_features(mpls_features); ++ vlan_features = netdev_base_features(vlan_features); ++ enc_features = netdev_base_features(enc_features); ++ ++ netdev_for_each_lower_dev(dev, lower_dev, iter) { ++ gso_partial_features = netdev_increment_features(gso_partial_features, ++ lower_dev->gso_partial_features, ++ MASTER_UPPER_DEV_GSO_PARTIAL_FEATURES); ++ ++ vlan_features = netdev_increment_features(vlan_features, ++ lower_dev->vlan_features, ++ MASTER_UPPER_DEV_VLAN_FEATURES); ++ ++ enc_features = netdev_increment_features(enc_features, ++ lower_dev->hw_enc_features, ++ MASTER_UPPER_DEV_ENC_FEATURES); ++ ++ if (IS_ENABLED(CONFIG_XFRM_OFFLOAD)) ++ xfrm_features = netdev_increment_features(xfrm_features, ++ lower_dev->hw_enc_features, ++ MASTER_UPPER_DEV_XFRM_FEATURES); ++ ++ mpls_features = netdev_increment_features(mpls_features, ++ lower_dev->mpls_features, ++ MASTER_UPPER_DEV_MPLS_FEATURES); ++ ++ dst_release_flag &= lower_dev->priv_flags; ++ ++ if (update_header) { ++ max_header_len = max(max_header_len, lower_dev->hard_header_len); ++ max_headroom = max(max_headroom, lower_dev->needed_headroom); ++ max_tailroom = max(max_tailroom, lower_dev->needed_tailroom); ++ } ++ ++ tso_max_size = min(tso_max_size, lower_dev->tso_max_size); ++ tso_max_segs = min(tso_max_segs, lower_dev->tso_max_segs); ++ } ++ ++ dev->gso_partial_features = gso_partial_features; ++ dev->vlan_features = vlan_features; ++ dev->hw_enc_features = enc_features | NETIF_F_GSO_ENCAP_ALL | ++ NETIF_F_HW_VLAN_CTAG_TX | ++ NETIF_F_HW_VLAN_STAG_TX; ++ if (IS_ENABLED(CONFIG_XFRM_OFFLOAD)) ++ dev->hw_enc_features |= xfrm_features; ++ dev->mpls_features = mpls_features; ++ ++ dev->priv_flags &= ~IFF_XMIT_DST_RELEASE; ++ if ((dev->priv_flags & IFF_XMIT_DST_RELEASE_PERM) && ++ dst_release_flag == (IFF_XMIT_DST_RELEASE | IFF_XMIT_DST_RELEASE_PERM)) ++ dev->priv_flags |= IFF_XMIT_DST_RELEASE; ++ ++ if (update_header) { ++ dev->hard_header_len = max_header_len; ++ dev->needed_headroom = max_headroom; ++ dev->needed_tailroom = max_tailroom; ++ } ++ ++ netif_set_tso_max_segs(dev, tso_max_segs); ++ netif_set_tso_max_size(dev, tso_max_size); ++ ++ netdev_change_features(dev); ++} ++EXPORT_SYMBOL(netdev_compute_master_upper_features); ++ + static struct hlist_head * __net_init netdev_create_hash(void) + { + int i; +-- +2.53.0 + diff --git a/queue-6.6/net-team-bonding-add-netdev_base_features-helper.patch b/queue-6.6/net-team-bonding-add-netdev_base_features-helper.patch new file mode 100644 index 0000000000..a33e89c020 --- /dev/null +++ b/queue-6.6/net-team-bonding-add-netdev_base_features-helper.patch @@ -0,0 +1,81 @@ +From 7fab5b63b63afe030227cc9843d5b32eb6c0d520 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 8 Jul 2026 17:47:04 +0800 +Subject: net, team, bonding: Add netdev_base_features helper + +From: Daniel Borkmann + +[ Upstream commit d2516c3a53705f783bb6868df0f4a2b977898a71 ] + +Both bonding and team driver have logic to derive the base feature +flags before iterating over their slave devices to refine the set +via netdev_increment_features(). + +Add a small helper netdev_base_features() so this can be reused +instead of having it open-coded multiple times. + +Signed-off-by: Daniel Borkmann +Cc: Nikolay Aleksandrov +Cc: Ido Schimmel +Cc: Jiri Pirko +Reviewed-by: Hangbin Liu +Reviewed-by: Nikolay Aleksandrov +Link: https://patch.msgid.link/20241210141245.327886-1-daniel@iogearbox.net +Signed-off-by: Paolo Abeni +Signed-off-by: Wentao Guan +Stable-dep-of: d064ea7fe2a2 ("bonding: Fix initial {vlan,mpls}_feature set in bond_compute_features") +Signed-off-by: Sasha Levin +--- + drivers/net/bonding/bond_main.c | 4 +--- + drivers/net/team/team_core.c | 3 +-- + include/linux/netdev_features.h | 7 +++++++ + 3 files changed, 9 insertions(+), 5 deletions(-) + +diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c +index e57e1296da3740..36277bcefe2908 100644 +--- a/drivers/net/bonding/bond_main.c ++++ b/drivers/net/bonding/bond_main.c +@@ -1456,9 +1456,7 @@ static netdev_features_t bond_fix_features(struct net_device *dev, + struct slave *slave; + + mask = features; +- +- features &= ~NETIF_F_ONE_FOR_ALL; +- features |= NETIF_F_ALL_FOR_ALL; ++ features = netdev_base_features(features); + + bond_for_each_slave(bond, slave, iter) { + features = netdev_increment_features(features, +diff --git a/drivers/net/team/team_core.c b/drivers/net/team/team_core.c +index deb6eb3a240a03..5cd1807e11f797 100644 +--- a/drivers/net/team/team_core.c ++++ b/drivers/net/team/team_core.c +@@ -2026,8 +2026,7 @@ static netdev_features_t team_fix_features(struct net_device *dev, + netdev_features_t mask; + + mask = features; +- features &= ~NETIF_F_ONE_FOR_ALL; +- features |= NETIF_F_ALL_FOR_ALL; ++ features = netdev_base_features(features); + + rcu_read_lock(); + list_for_each_entry_rcu(port, &team->port_list, list) { +diff --git a/include/linux/netdev_features.h b/include/linux/netdev_features.h +index 7c2d77d75a888c..1b5ad57e7cbf5a 100644 +--- a/include/linux/netdev_features.h ++++ b/include/linux/netdev_features.h +@@ -261,4 +261,11 @@ static inline int find_next_netdev_feature(u64 feature, unsigned long start) + NETIF_F_GSO_UDP_TUNNEL | \ + NETIF_F_GSO_UDP_TUNNEL_CSUM) + ++static inline netdev_features_t netdev_base_features(netdev_features_t features) ++{ ++ features &= ~NETIF_F_ONE_FOR_ALL; ++ features |= NETIF_F_ALL_FOR_ALL; ++ return features; ++} ++ + #endif /* _LINUX_NETDEV_FEATURES_H */ +-- +2.53.0 + diff --git a/queue-6.6/net-team-rename-team-to-team_core-for-linking.patch b/queue-6.6/net-team-rename-team-to-team_core-for-linking.patch new file mode 100644 index 0000000000..99a3661f11 --- /dev/null +++ b/queue-6.6/net-team-rename-team-to-team_core-for-linking.patch @@ -0,0 +1,46 @@ +From cc0b38eb2673f47bff9723b126f40b88dd040ccc Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 8 Jul 2026 17:47:01 +0800 +Subject: net: team: rename team to team_core for linking + +From: Hangbin Liu + +[ Upstream commit a0393e3e3ddbb177b25f0a978c72f5efe942fe7d ] + +Similar with commit 08d323234d10 ("net: fou: rename the source for linking"), +We'll need to link two objects together to form the team module. +This means the source can't be called team, the build system expects +team.o to be the combined object. + +Signed-off-by: Hangbin Liu +Reviewed-by: Jiri Pirko +Link: https://lore.kernel.org/r/20240401031004.1159713-3-liuhangbin@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Wentao Guan +Stable-dep-of: 425000dbf173 ("team: fix header_ops type confusion with non-Ethernet ports") +Signed-off-by: Sasha Levin +--- + drivers/net/team/Makefile | 1 + + drivers/net/team/{team.c => team_core.c} | 0 + 2 files changed, 1 insertion(+) + rename drivers/net/team/{team.c => team_core.c} (100%) + +diff --git a/drivers/net/team/Makefile b/drivers/net/team/Makefile +index f582d81a509119..244db32c1060e1 100644 +--- a/drivers/net/team/Makefile ++++ b/drivers/net/team/Makefile +@@ -3,6 +3,7 @@ + # Makefile for the network team driver + # + ++team-y:= team_core.o + obj-$(CONFIG_NET_TEAM) += team.o + obj-$(CONFIG_NET_TEAM_MODE_BROADCAST) += team_mode_broadcast.o + obj-$(CONFIG_NET_TEAM_MODE_ROUNDROBIN) += team_mode_roundrobin.o +diff --git a/drivers/net/team/team.c b/drivers/net/team/team_core.c +similarity index 100% +rename from drivers/net/team/team.c +rename to drivers/net/team/team_core.c +-- +2.53.0 + diff --git a/queue-6.6/series b/queue-6.6/series index 6dd435d12a..e9942e61d2 100644 --- a/queue-6.6/series +++ b/queue-6.6/series @@ -40,3 +40,19 @@ nfsd-don-t-reset-the-write-verifier-on-a-commit-eagain.patch apparmor-advertise-the-tcp-fast-open-fix-is-applied.patch nfsd-move-name-lookup-out-of-nfsd4_list_rec_dir.patch nfsd-change-nfs4_client_to_reclaim-to-allocate-data.patch +net-team-rename-team-to-team_core-for-linking.patch +net-team-bonding-add-netdev_base_features-helper.patch +bonding-fix-initial-vlan-mpls-_feature-set-in-bond_c.patch +bonding-add-esp-offload-features-when-slaves-support.patch +bonding-correctly-support-gso-esp-offload.patch +net-add-a-common-function-to-compute-features-for-up.patch +bonding-use-common-function-to-compute-the-features.patch +bonding-fix-xfrm-offload-feature-setup-on-active-bac.patch +bonding-fix-type-confusion-in-bond_setup_by_slave.patch +bonding-prevent-potential-infinite-loop-in-bond_head.patch +team-fix-header_ops-type-confusion-with-non-ethernet.patch +mm-vmscan-flush-deferred-tlb-before-freeing-large-fo.patch +xfs-recreate-work-items-when-recovering-intent-items.patch +xfs-dump-the-recovered-xattri-log-item-if-corruption.patch +xfs-use-xfs_defer_finish_one-to-finish-recovered-wor.patch +xfs-move-iop_recover-to-xfs_defer_op_type.patch diff --git a/queue-6.6/team-fix-header_ops-type-confusion-with-non-ethernet.patch b/queue-6.6/team-fix-header_ops-type-confusion-with-non-ethernet.patch new file mode 100644 index 0000000000..c7b2422393 --- /dev/null +++ b/queue-6.6/team-fix-header_ops-type-confusion-with-non-ethernet.patch @@ -0,0 +1,133 @@ +From c443cc640515cca0d1851781bf1d357d0548f754 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 8 Jul 2026 17:47:23 +0800 +Subject: team: fix header_ops type confusion with non-Ethernet ports + +From: Jiayuan Chen + +[ Upstream commit 425000dbf17373a4ab8be9428f5dc055ef870a56 ] + +Similar to commit 950803f72547 ("bonding: fix type confusion in +bond_setup_by_slave()") team has the same class of header_ops type +confusion. + +For non-Ethernet ports, team_setup_by_port() copies port_dev->header_ops +directly. When the team device later calls dev_hard_header() or +dev_parse_header(), these callbacks can run with the team net_device +instead of the real lower device, so netdev_priv(dev) is interpreted as +the wrong private type and can crash. + +The syzbot report shows a crash in bond_header_create(), but the root +cause is in team: the topology is gre -> bond -> team, and team calls +the inherited header_ops with its own net_device instead of the lower +device, so bond_header_create() receives a team device and interprets +netdev_priv() as bonding private data, causing a type confusion crash. + +Fix this by introducing team header_ops wrappers for create/parse, +selecting a team port under RCU, and calling the lower device callbacks +with port->dev, so each callback always sees the correct net_device +context. + +Also pass the selected lower device to the lower parse callback, so +recursion is bounded in stacked non-Ethernet topologies and parse +callbacks always run with the correct device context. + +Fixes: 1d76efe1577b ("team: add support for non-ethernet devices") +Reported-by: syzbot+3d8bc31c45e11450f24c@syzkaller.appspotmail.com +Closes: https://lore.kernel.org/all/69b46af7.050a0220.36eb34.000e.GAE@google.com/T/ +Cc: Jiayuan Chen +Signed-off-by: Jiayuan Chen +Link: https://patch.msgid.link/20260320072139.134249-2-jiayuan.chen@linux.dev +Signed-off-by: Paolo Abeni +Signed-off-by: Wentao Guan +Signed-off-by: Sasha Levin +--- + drivers/net/team/team_core.c | 65 +++++++++++++++++++++++++++++++++++- + 1 file changed, 64 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/team/team_core.c b/drivers/net/team/team_core.c +index 5cd1807e11f797..a4bf020cf8e7cb 100644 +--- a/drivers/net/team/team_core.c ++++ b/drivers/net/team/team_core.c +@@ -2128,6 +2128,68 @@ static const struct ethtool_ops team_ethtool_ops = { + * rt netlink interface + ***********************/ + ++/* For tx path we need a linkup && enabled port and for parse any port ++ * suffices. ++ */ ++static struct team_port *team_header_port_get_rcu(struct team *team, ++ bool txable) ++{ ++ struct team_port *port; ++ ++ list_for_each_entry_rcu(port, &team->port_list, list) { ++ if (!txable || team_port_txable(port)) ++ return port; ++ } ++ ++ return NULL; ++} ++ ++static int team_header_create(struct sk_buff *skb, struct net_device *team_dev, ++ unsigned short type, const void *daddr, ++ const void *saddr, unsigned int len) ++{ ++ struct team *team = netdev_priv(team_dev); ++ const struct header_ops *port_ops; ++ struct team_port *port; ++ int ret = 0; ++ ++ rcu_read_lock(); ++ port = team_header_port_get_rcu(team, true); ++ if (port) { ++ port_ops = READ_ONCE(port->dev->header_ops); ++ if (port_ops && port_ops->create) ++ ret = port_ops->create(skb, port->dev, ++ type, daddr, saddr, len); ++ } ++ rcu_read_unlock(); ++ return ret; ++} ++ ++static int team_header_parse(const struct sk_buff *skb, ++ const struct net_device *team_dev, ++ unsigned char *haddr) ++{ ++ struct team *team = netdev_priv(team_dev); ++ const struct header_ops *port_ops; ++ struct team_port *port; ++ int ret = 0; ++ ++ rcu_read_lock(); ++ port = team_header_port_get_rcu(team, false); ++ if (port) { ++ port_ops = READ_ONCE(port->dev->header_ops); ++ if (port_ops && port_ops->parse) ++ ret = port_ops->parse(skb, port->dev, haddr); ++ } ++ rcu_read_unlock(); ++ return ret; ++} ++ ++static const struct header_ops team_header_ops = { ++ .create = team_header_create, ++ .parse = team_header_parse, ++}; ++ + static void team_setup_by_port(struct net_device *dev, + struct net_device *port_dev) + { +@@ -2136,7 +2198,8 @@ static void team_setup_by_port(struct net_device *dev, + if (port_dev->type == ARPHRD_ETHER) + dev->header_ops = team->header_ops_cache; + else +- dev->header_ops = port_dev->header_ops; ++ dev->header_ops = port_dev->header_ops ? ++ &team_header_ops : NULL; + dev->type = port_dev->type; + dev->hard_header_len = port_dev->hard_header_len; + dev->needed_headroom = port_dev->needed_headroom; +-- +2.53.0 + diff --git a/queue-6.6/xfs-dump-the-recovered-xattri-log-item-if-corruption.patch b/queue-6.6/xfs-dump-the-recovered-xattri-log-item-if-corruption.patch new file mode 100644 index 0000000000..ecef2c9d87 --- /dev/null +++ b/queue-6.6/xfs-dump-the-recovered-xattri-log-item-if-corruption.patch @@ -0,0 +1,39 @@ +From e80a583c0fd7442b733acf6aed9aeb9425579a7b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 22 Mar 2025 17:34:13 +0300 +Subject: xfs: dump the recovered xattri log item if corruption happens + +From: Darrick J. Wong + +commit a51489e140d302c7afae763eacf882a23513f7e4 upstream. + +If xfs_attri_item_recover receives a corruption error when it tries to +finish a recovered log intent item, it should dump the log item for +debugging, just like all the other log intent items. + +Signed-off-by: Darrick J. Wong +Reviewed-by: Christoph Hellwig +Signed-off-by: Fedor Pchelkin +Signed-off-by: Sasha Levin +--- + fs/xfs/xfs_attr_item.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/fs/xfs/xfs_attr_item.c b/fs/xfs/xfs_attr_item.c +index 929a53db22462f..94e16f51a5ac52 100644 +--- a/fs/xfs/xfs_attr_item.c ++++ b/fs/xfs/xfs_attr_item.c +@@ -672,6 +672,10 @@ xfs_attri_item_recover( + xfs_irele(ip); + return 0; + } ++ if (error == -EFSCORRUPTED) ++ XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, ++ &attrip->attri_format, ++ sizeof(attrip->attri_format)); + if (error) { + xfs_trans_cancel(tp); + goto out_unlock; +-- +2.53.0 + diff --git a/queue-6.6/xfs-move-iop_recover-to-xfs_defer_op_type.patch b/queue-6.6/xfs-move-iop_recover-to-xfs_defer_op_type.patch new file mode 100644 index 0000000000..cb190cdaaa --- /dev/null +++ b/queue-6.6/xfs-move-iop_recover-to-xfs_defer_op_type.patch @@ -0,0 +1,531 @@ +From f7e8d778bd40dda8a6e93b2c9bb5101ea37213ff Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 22 Mar 2025 17:34:15 +0300 +Subject: xfs: move ->iop_recover to xfs_defer_op_type + +From: Darrick J. Wong + +commit db7ccc0bac2add5a41b66578e376b49328fc99d0 upstream. + +Finish off the series by moving the intent item recovery function +pointer to the xfs_defer_op_type struct, since this is really a deferred +work function now. + +Signed-off-by: Darrick J. Wong +Reviewed-by: Christoph Hellwig +Signed-off-by: Fedor Pchelkin +Signed-off-by: Sasha Levin +--- + fs/xfs/libxfs/xfs_defer.c | 17 +++++++++++++ + fs/xfs/libxfs/xfs_defer.h | 4 +++ + fs/xfs/libxfs/xfs_log_recover.h | 2 ++ + fs/xfs/xfs_attr_item.c | 21 +++++++++------- + fs/xfs/xfs_bmap_item.c | 39 ++++++++++++++++-------------- + fs/xfs/xfs_extfree_item.c | 43 +++++++++++++++++---------------- + fs/xfs/xfs_log_recover.c | 19 ++++++--------- + fs/xfs/xfs_refcount_item.c | 24 +++++++++--------- + fs/xfs/xfs_rmap_item.c | 24 +++++++++--------- + fs/xfs/xfs_trans.h | 4 --- + 10 files changed, 109 insertions(+), 88 deletions(-) + +diff --git a/fs/xfs/libxfs/xfs_defer.c b/fs/xfs/libxfs/xfs_defer.c +index eb262ea061226b..dd565e4e3daf06 100644 +--- a/fs/xfs/libxfs/xfs_defer.c ++++ b/fs/xfs/libxfs/xfs_defer.c +@@ -713,6 +713,23 @@ xfs_defer_cancel_recovery( + xfs_defer_pending_cancel_work(mp, dfp); + } + ++/* Replay the deferred work item created from a recovered log intent item. */ ++int ++xfs_defer_finish_recovery( ++ struct xfs_mount *mp, ++ struct xfs_defer_pending *dfp, ++ struct list_head *capture_list) ++{ ++ const struct xfs_defer_op_type *ops = defer_op_types[dfp->dfp_type]; ++ int error; ++ ++ error = ops->recover_work(dfp, capture_list); ++ if (error) ++ trace_xlog_intent_recovery_failed(mp, error, ++ ops->recover_work); ++ return error; ++} ++ + /* + * Move deferred ops from one transaction to another and reset the source to + * initial state. This is primarily used to carry state forward across +diff --git a/fs/xfs/libxfs/xfs_defer.h b/fs/xfs/libxfs/xfs_defer.h +index c1a648e9917489..ef86a7f9b05950 100644 +--- a/fs/xfs/libxfs/xfs_defer.h ++++ b/fs/xfs/libxfs/xfs_defer.h +@@ -57,6 +57,8 @@ struct xfs_defer_op_type { + void (*finish_cleanup)(struct xfs_trans *tp, + struct xfs_btree_cur *state, int error); + void (*cancel_item)(struct list_head *item); ++ int (*recover_work)(struct xfs_defer_pending *dfp, ++ struct list_head *capture_list); + unsigned int max_items; + }; + +@@ -130,6 +132,8 @@ void xfs_defer_start_recovery(struct xfs_log_item *lip, + enum xfs_defer_ops_type dfp_type, struct list_head *r_dfops); + void xfs_defer_cancel_recovery(struct xfs_mount *mp, + struct xfs_defer_pending *dfp); ++int xfs_defer_finish_recovery(struct xfs_mount *mp, ++ struct xfs_defer_pending *dfp, struct list_head *capture_list); + + static inline void + xfs_defer_add_item( +diff --git a/fs/xfs/libxfs/xfs_log_recover.h b/fs/xfs/libxfs/xfs_log_recover.h +index 52162a17fc5e3a..c8e5d912895bcd 100644 +--- a/fs/xfs/libxfs/xfs_log_recover.h ++++ b/fs/xfs/libxfs/xfs_log_recover.h +@@ -153,6 +153,8 @@ xlog_recover_resv(const struct xfs_trans_res *r) + return ret; + } + ++struct xfs_defer_pending; ++ + void xlog_recover_intent_item(struct xlog *log, struct xfs_log_item *lip, + xfs_lsn_t lsn, unsigned int dfp_type); + int xlog_recover_finish_intent(struct xfs_trans *tp, +diff --git a/fs/xfs/xfs_attr_item.c b/fs/xfs/xfs_attr_item.c +index b7bf99d8c4739e..1628e797a6f123 100644 +--- a/fs/xfs/xfs_attr_item.c ++++ b/fs/xfs/xfs_attr_item.c +@@ -551,12 +551,17 @@ xfs_attri_recover_work( + struct xfs_mount *mp, + struct xfs_defer_pending *dfp, + struct xfs_attri_log_format *attrp, +- struct xfs_inode *ip, ++ struct xfs_inode **ipp, + struct xfs_attri_log_nameval *nv) + { + struct xfs_attr_intent *attr; + struct xfs_da_args *args; + int local; ++ int error; ++ ++ error = xlog_recover_iget(mp, attrp->alfi_ino, ipp); ++ if (error) ++ return ERR_PTR(error); + + attr = kmem_zalloc(sizeof(struct xfs_attr_intent) + + sizeof(struct xfs_da_args), KM_NOFS); +@@ -574,7 +579,7 @@ xfs_attri_recover_work( + attr->xattri_nameval = xfs_attri_log_nameval_get(nv); + ASSERT(attr->xattri_nameval); + +- args->dp = ip; ++ args->dp = *ipp; + args->geo = mp->m_attr_geo; + args->whichfork = XFS_ATTR_FORK; + args->name = nv->name.i_addr; +@@ -609,7 +614,7 @@ xfs_attri_recover_work( + * delete the attr that it describes. + */ + STATIC int +-xfs_attri_item_recover( ++xfs_attr_recover_work( + struct xfs_defer_pending *dfp, + struct list_head *capture_list) + { +@@ -636,11 +641,9 @@ xfs_attri_item_recover( + nv->name.i_len)) + return -EFSCORRUPTED; + +- error = xlog_recover_iget(mp, attrp->alfi_ino, &ip); +- if (error) +- return error; +- +- attr = xfs_attri_recover_work(mp, dfp, attrp, ip, nv); ++ attr = xfs_attri_recover_work(mp, dfp, attrp, &ip, nv); ++ if (IS_ERR(attr)) ++ return PTR_ERR(attr); + args = attr->xattri_da_args; + + xfs_init_attr_trans(args, &resv, &total); +@@ -890,6 +893,7 @@ const struct xfs_defer_op_type xfs_attr_defer_type = { + .create_done = xfs_attr_create_done, + .finish_item = xfs_attr_finish_item, + .cancel_item = xfs_attr_cancel_item, ++ .recover_work = xfs_attr_recover_work, + }; + + /* +@@ -926,7 +930,6 @@ static const struct xfs_item_ops xfs_attri_item_ops = { + .iop_format = xfs_attri_item_format, + .iop_unpin = xfs_attri_item_unpin, + .iop_release = xfs_attri_item_release, +- .iop_recover = xfs_attri_item_recover, + .iop_match = xfs_attri_item_match, + .iop_relog = xfs_attri_item_relog, + }; +diff --git a/fs/xfs/xfs_bmap_item.c b/fs/xfs/xfs_bmap_item.c +index 2d91ade24b689a..59b438ac37a254 100644 +--- a/fs/xfs/xfs_bmap_item.c ++++ b/fs/xfs/xfs_bmap_item.c +@@ -438,15 +438,6 @@ xfs_bmap_update_cancel_item( + kmem_cache_free(xfs_bmap_intent_cache, bi); + } + +-const struct xfs_defer_op_type xfs_bmap_update_defer_type = { +- .max_items = XFS_BUI_MAX_FAST_EXTENTS, +- .create_intent = xfs_bmap_update_create_intent, +- .abort_intent = xfs_bmap_update_abort_intent, +- .create_done = xfs_bmap_update_create_done, +- .finish_item = xfs_bmap_update_finish_item, +- .cancel_item = xfs_bmap_update_cancel_item, +-}; +- + /* Is this recovered BUI ok? */ + static inline bool + xfs_bui_validate( +@@ -485,9 +476,15 @@ static inline struct xfs_bmap_intent * + xfs_bui_recover_work( + struct xfs_mount *mp, + struct xfs_defer_pending *dfp, ++ struct xfs_inode **ipp, + struct xfs_map_extent *map) + { + struct xfs_bmap_intent *bi; ++ int error; ++ ++ error = xlog_recover_iget(mp, map->me_owner, ipp); ++ if (error) ++ return ERR_PTR(error); + + bi = kmem_cache_zalloc(xfs_bmap_intent_cache, GFP_NOFS | __GFP_NOFAIL); + bi->bi_whichfork = (map->me_flags & XFS_BMAP_EXTENT_ATTR_FORK) ? +@@ -498,6 +495,7 @@ xfs_bui_recover_work( + bi->bi_bmap.br_blockcount = map->me_len; + bi->bi_bmap.br_state = (map->me_flags & XFS_BMAP_EXTENT_UNWRITTEN) ? + XFS_EXT_UNWRITTEN : XFS_EXT_NORM; ++ bi->bi_owner = *ipp; + xfs_bmap_update_get_group(mp, bi); + + xfs_defer_add_item(dfp, &bi->bi_list); +@@ -509,7 +507,7 @@ xfs_bui_recover_work( + * We need to update some inode's bmbt. + */ + STATIC int +-xfs_bui_item_recover( ++xfs_bmap_recover_work( + struct xfs_defer_pending *dfp, + struct list_head *capture_list) + { +@@ -531,11 +529,9 @@ xfs_bui_item_recover( + } + + map = &buip->bui_format.bui_extents[0]; +- work = xfs_bui_recover_work(mp, dfp, map); +- +- error = xlog_recover_iget(mp, map->me_owner, &ip); +- if (error) +- return error; ++ work = xfs_bui_recover_work(mp, dfp, &ip, map); ++ if (IS_ERR(work)) ++ return PTR_ERR(work); + + /* Allocate transaction and do the work. */ + resv = xlog_recover_resv(&M_RES(mp)->tr_itruncate); +@@ -558,8 +554,6 @@ xfs_bui_item_recover( + if (error) + goto err_cancel; + +- work->bi_owner = ip; +- + error = xlog_recover_finish_intent(tp, dfp); + if (error == -EFSCORRUPTED) + XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, +@@ -588,6 +582,16 @@ xfs_bui_item_recover( + return error; + } + ++const struct xfs_defer_op_type xfs_bmap_update_defer_type = { ++ .max_items = XFS_BUI_MAX_FAST_EXTENTS, ++ .create_intent = xfs_bmap_update_create_intent, ++ .abort_intent = xfs_bmap_update_abort_intent, ++ .create_done = xfs_bmap_update_create_done, ++ .finish_item = xfs_bmap_update_finish_item, ++ .cancel_item = xfs_bmap_update_cancel_item, ++ .recover_work = xfs_bmap_recover_work, ++}; ++ + STATIC bool + xfs_bui_item_match( + struct xfs_log_item *lip, +@@ -628,7 +632,6 @@ static const struct xfs_item_ops xfs_bui_item_ops = { + .iop_format = xfs_bui_item_format, + .iop_unpin = xfs_bui_item_unpin, + .iop_release = xfs_bui_item_release, +- .iop_recover = xfs_bui_item_recover, + .iop_match = xfs_bui_item_match, + .iop_relog = xfs_bui_item_relog, + }; +diff --git a/fs/xfs/xfs_extfree_item.c b/fs/xfs/xfs_extfree_item.c +index 6a434ade486c93..49e96ffd64e0c1 100644 +--- a/fs/xfs/xfs_extfree_item.c ++++ b/fs/xfs/xfs_extfree_item.c +@@ -567,15 +567,6 @@ xfs_extent_free_cancel_item( + kmem_cache_free(xfs_extfree_item_cache, xefi); + } + +-const struct xfs_defer_op_type xfs_extent_free_defer_type = { +- .max_items = XFS_EFI_MAX_FAST_EXTENTS, +- .create_intent = xfs_extent_free_create_intent, +- .abort_intent = xfs_extent_free_abort_intent, +- .create_done = xfs_extent_free_create_done, +- .finish_item = xfs_extent_free_finish_item, +- .cancel_item = xfs_extent_free_cancel_item, +-}; +- + /* + * AGFL blocks are accounted differently in the reserve pools and are not + * inserted into the busy extent list. +@@ -632,16 +623,6 @@ xfs_agfl_free_finish_item( + return error; + } + +-/* sub-type with special handling for AGFL deferred frees */ +-const struct xfs_defer_op_type xfs_agfl_free_defer_type = { +- .max_items = XFS_EFI_MAX_FAST_EXTENTS, +- .create_intent = xfs_extent_free_create_intent, +- .abort_intent = xfs_extent_free_abort_intent, +- .create_done = xfs_extent_free_create_done, +- .finish_item = xfs_agfl_free_finish_item, +- .cancel_item = xfs_extent_free_cancel_item, +-}; +- + /* Is this recovered EFI ok? */ + static inline bool + xfs_efi_validate_ext( +@@ -675,7 +656,7 @@ xfs_efi_recover_work( + * the log. We need to free the extents that it describes. + */ + STATIC int +-xfs_efi_item_recover( ++xfs_extent_free_recover_work( + struct xfs_defer_pending *dfp, + struct list_head *capture_list) + { +@@ -724,6 +705,27 @@ xfs_efi_item_recover( + return error; + } + ++const struct xfs_defer_op_type xfs_extent_free_defer_type = { ++ .max_items = XFS_EFI_MAX_FAST_EXTENTS, ++ .create_intent = xfs_extent_free_create_intent, ++ .abort_intent = xfs_extent_free_abort_intent, ++ .create_done = xfs_extent_free_create_done, ++ .finish_item = xfs_extent_free_finish_item, ++ .cancel_item = xfs_extent_free_cancel_item, ++ .recover_work = xfs_extent_free_recover_work, ++}; ++ ++/* sub-type with special handling for AGFL deferred frees */ ++const struct xfs_defer_op_type xfs_agfl_free_defer_type = { ++ .max_items = XFS_EFI_MAX_FAST_EXTENTS, ++ .create_intent = xfs_extent_free_create_intent, ++ .abort_intent = xfs_extent_free_abort_intent, ++ .create_done = xfs_extent_free_create_done, ++ .finish_item = xfs_agfl_free_finish_item, ++ .cancel_item = xfs_extent_free_cancel_item, ++ .recover_work = xfs_extent_free_recover_work, ++}; ++ + STATIC bool + xfs_efi_item_match( + struct xfs_log_item *lip, +@@ -766,7 +768,6 @@ static const struct xfs_item_ops xfs_efi_item_ops = { + .iop_format = xfs_efi_item_format, + .iop_unpin = xfs_efi_item_unpin, + .iop_release = xfs_efi_item_release, +- .iop_recover = xfs_efi_item_recover, + .iop_match = xfs_efi_item_match, + .iop_relog = xfs_efi_item_relog, + }; +diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c +index c668164defdc13..4a372e54880c0b 100644 +--- a/fs/xfs/xfs_log_recover.c ++++ b/fs/xfs/xfs_log_recover.c +@@ -2565,17 +2565,14 @@ xlog_recover_process_intents( + #endif + + list_for_each_entry_safe(dfp, n, &log->r_dfops, dfp_list) { +- struct xfs_log_item *lip = dfp->dfp_intent; +- const struct xfs_item_ops *ops = lip->li_ops; +- +- ASSERT(xlog_item_is_intent(lip)); ++ ASSERT(xlog_item_is_intent(dfp->dfp_intent)); + + /* + * We should never see a redo item with a LSN higher than + * the last transaction we found in the log at the start + * of recovery. + */ +- ASSERT(XFS_LSN_CMP(last_lsn, lip->li_lsn) >= 0); ++ ASSERT(XFS_LSN_CMP(last_lsn, dfp->dfp_intent->li_lsn) >= 0); + + /* + * NOTE: If your intent processing routine can create more +@@ -2584,15 +2581,13 @@ xlog_recover_process_intents( + * replayed in the wrong order! + * + * The recovery function can free the log item, so we must not +- * access lip after it returns. It must dispose of @dfp if it +- * returns 0. ++ * access dfp->dfp_intent after it returns. It must dispose of ++ * @dfp if it returns 0. + */ +- error = ops->iop_recover(dfp, &capture_list); +- if (error) { +- trace_xlog_intent_recovery_failed(log->l_mp, error, +- ops->iop_recover); ++ error = xfs_defer_finish_recovery(log->l_mp, dfp, ++ &capture_list); ++ if (error) + break; +- } + } + if (error) + goto err; +diff --git a/fs/xfs/xfs_refcount_item.c b/fs/xfs/xfs_refcount_item.c +index f561ca73c784ff..48f1a38b272e1d 100644 +--- a/fs/xfs/xfs_refcount_item.c ++++ b/fs/xfs/xfs_refcount_item.c +@@ -433,16 +433,6 @@ xfs_refcount_update_cancel_item( + kmem_cache_free(xfs_refcount_intent_cache, ri); + } + +-const struct xfs_defer_op_type xfs_refcount_update_defer_type = { +- .max_items = XFS_CUI_MAX_FAST_EXTENTS, +- .create_intent = xfs_refcount_update_create_intent, +- .abort_intent = xfs_refcount_update_abort_intent, +- .create_done = xfs_refcount_update_create_done, +- .finish_item = xfs_refcount_update_finish_item, +- .finish_cleanup = xfs_refcount_finish_one_cleanup, +- .cancel_item = xfs_refcount_update_cancel_item, +-}; +- + /* Is this recovered CUI ok? */ + static inline bool + xfs_cui_validate_phys( +@@ -491,7 +481,7 @@ xfs_cui_recover_work( + * We need to update the refcountbt. + */ + STATIC int +-xfs_cui_item_recover( ++xfs_refcount_recover_work( + struct xfs_defer_pending *dfp, + struct list_head *capture_list) + { +@@ -553,6 +543,17 @@ xfs_cui_item_recover( + return error; + } + ++const struct xfs_defer_op_type xfs_refcount_update_defer_type = { ++ .max_items = XFS_CUI_MAX_FAST_EXTENTS, ++ .create_intent = xfs_refcount_update_create_intent, ++ .abort_intent = xfs_refcount_update_abort_intent, ++ .create_done = xfs_refcount_update_create_done, ++ .finish_item = xfs_refcount_update_finish_item, ++ .finish_cleanup = xfs_refcount_finish_one_cleanup, ++ .cancel_item = xfs_refcount_update_cancel_item, ++ .recover_work = xfs_refcount_recover_work, ++}; ++ + STATIC bool + xfs_cui_item_match( + struct xfs_log_item *lip, +@@ -593,7 +594,6 @@ static const struct xfs_item_ops xfs_cui_item_ops = { + .iop_format = xfs_cui_item_format, + .iop_unpin = xfs_cui_item_unpin, + .iop_release = xfs_cui_item_release, +- .iop_recover = xfs_cui_item_recover, + .iop_match = xfs_cui_item_match, + .iop_relog = xfs_cui_item_relog, + }; +diff --git a/fs/xfs/xfs_rmap_item.c b/fs/xfs/xfs_rmap_item.c +index 23e736179894b3..23684bc2ab85a2 100644 +--- a/fs/xfs/xfs_rmap_item.c ++++ b/fs/xfs/xfs_rmap_item.c +@@ -452,16 +452,6 @@ xfs_rmap_update_cancel_item( + kmem_cache_free(xfs_rmap_intent_cache, ri); + } + +-const struct xfs_defer_op_type xfs_rmap_update_defer_type = { +- .max_items = XFS_RUI_MAX_FAST_EXTENTS, +- .create_intent = xfs_rmap_update_create_intent, +- .abort_intent = xfs_rmap_update_abort_intent, +- .create_done = xfs_rmap_update_create_done, +- .finish_item = xfs_rmap_update_finish_item, +- .finish_cleanup = xfs_rmap_finish_one_cleanup, +- .cancel_item = xfs_rmap_update_cancel_item, +-}; +- + /* Is this recovered RUI ok? */ + static inline bool + xfs_rui_validate_map( +@@ -556,7 +546,7 @@ xfs_rui_recover_work( + * We need to update the rmapbt. + */ + STATIC int +-xfs_rui_item_recover( ++xfs_rmap_recover_work( + struct xfs_defer_pending *dfp, + struct list_head *capture_list) + { +@@ -606,6 +596,17 @@ xfs_rui_item_recover( + return error; + } + ++const struct xfs_defer_op_type xfs_rmap_update_defer_type = { ++ .max_items = XFS_RUI_MAX_FAST_EXTENTS, ++ .create_intent = xfs_rmap_update_create_intent, ++ .abort_intent = xfs_rmap_update_abort_intent, ++ .create_done = xfs_rmap_update_create_done, ++ .finish_item = xfs_rmap_update_finish_item, ++ .finish_cleanup = xfs_rmap_finish_one_cleanup, ++ .cancel_item = xfs_rmap_update_cancel_item, ++ .recover_work = xfs_rmap_recover_work, ++}; ++ + STATIC bool + xfs_rui_item_match( + struct xfs_log_item *lip, +@@ -646,7 +647,6 @@ static const struct xfs_item_ops xfs_rui_item_ops = { + .iop_format = xfs_rui_item_format, + .iop_unpin = xfs_rui_item_unpin, + .iop_release = xfs_rui_item_release, +- .iop_recover = xfs_rui_item_recover, + .iop_match = xfs_rui_item_match, + .iop_relog = xfs_rui_item_relog, + }; +diff --git a/fs/xfs/xfs_trans.h b/fs/xfs/xfs_trans.h +index ead65f5f8dc329..ae32ffe9a1b15c 100644 +--- a/fs/xfs/xfs_trans.h ++++ b/fs/xfs/xfs_trans.h +@@ -66,8 +66,6 @@ struct xfs_log_item { + { (1u << XFS_LI_DIRTY), "DIRTY" }, \ + { (1u << XFS_LI_WHITEOUT), "WHITEOUT" } + +-struct xfs_defer_pending; +- + struct xfs_item_ops { + unsigned flags; + void (*iop_size)(struct xfs_log_item *, int *, int *); +@@ -80,8 +78,6 @@ struct xfs_item_ops { + xfs_lsn_t (*iop_committed)(struct xfs_log_item *, xfs_lsn_t); + uint (*iop_push)(struct xfs_log_item *, struct list_head *); + void (*iop_release)(struct xfs_log_item *); +- int (*iop_recover)(struct xfs_defer_pending *dfp, +- struct list_head *capture_list); + bool (*iop_match)(struct xfs_log_item *item, uint64_t id); + struct xfs_log_item *(*iop_relog)(struct xfs_log_item *intent, + struct xfs_trans *tp); +-- +2.53.0 + diff --git a/queue-6.6/xfs-recreate-work-items-when-recovering-intent-items.patch b/queue-6.6/xfs-recreate-work-items-when-recovering-intent-items.patch new file mode 100644 index 0000000000..c9a9eaa0bd --- /dev/null +++ b/queue-6.6/xfs-recreate-work-items-when-recovering-intent-items.patch @@ -0,0 +1,652 @@ +From 27e97ff9539f126816c99bde38abcc4bf04e6a1b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 22 Mar 2025 17:34:12 +0300 +Subject: xfs: recreate work items when recovering intent items + +From: Darrick J. Wong + +commit e70fb328d5277297ea2d9169a3a046de6412d777 upstream. + +Recreate work items for each xfs_defer_pending object when we are +recovering intent items. + +Signed-off-by: Darrick J. Wong +Reviewed-by: Christoph Hellwig +[ fp: fix conflict in calling xfs_attr_namecheck() with 2 args instead + of 3 due to backported ea0b3e814741 ("xfs: enforce one namespace per + attribute") ] +Signed-off-by: Fedor Pchelkin +Signed-off-by: Sasha Levin +--- + fs/xfs/libxfs/xfs_defer.c | 3 +- + fs/xfs/libxfs/xfs_defer.h | 9 +++ + fs/xfs/xfs_attr_item.c | 92 +++++++++++++++++------------- + fs/xfs/xfs_bmap_item.c | 55 +++++++++++------- + fs/xfs/xfs_extfree_item.c | 49 +++++++++------- + fs/xfs/xfs_refcount_item.c | 60 ++++++++++---------- + fs/xfs/xfs_rmap_item.c | 112 ++++++++++++++++++++----------------- + 7 files changed, 216 insertions(+), 164 deletions(-) + +diff --git a/fs/xfs/libxfs/xfs_defer.c b/fs/xfs/libxfs/xfs_defer.c +index 363da37a8e7fbe..8fb523e4f66931 100644 +--- a/fs/xfs/libxfs/xfs_defer.c ++++ b/fs/xfs/libxfs/xfs_defer.c +@@ -676,9 +676,8 @@ xfs_defer_add( + list_add_tail(&dfp->dfp_list, &tp->t_dfops); + } + +- list_add_tail(li, &dfp->dfp_work); ++ xfs_defer_add_item(dfp, li); + trace_xfs_defer_add_item(tp->t_mountp, dfp, li); +- dfp->dfp_count++; + } + + /* +diff --git a/fs/xfs/libxfs/xfs_defer.h b/fs/xfs/libxfs/xfs_defer.h +index 5dce938ba3d594..bef5823f61fb82 100644 +--- a/fs/xfs/libxfs/xfs_defer.h ++++ b/fs/xfs/libxfs/xfs_defer.h +@@ -130,6 +130,15 @@ void xfs_defer_start_recovery(struct xfs_log_item *lip, + void xfs_defer_cancel_recovery(struct xfs_mount *mp, + struct xfs_defer_pending *dfp); + ++static inline void ++xfs_defer_add_item( ++ struct xfs_defer_pending *dfp, ++ struct list_head *work) ++{ ++ list_add_tail(work, &dfp->dfp_work); ++ dfp->dfp_count++; ++} ++ + int __init xfs_defer_init_item_caches(void); + void xfs_defer_destroy_item_caches(void); + +diff --git a/fs/xfs/xfs_attr_item.c b/fs/xfs/xfs_attr_item.c +index 889fca3f8de1ce..929a53db22462f 100644 +--- a/fs/xfs/xfs_attr_item.c ++++ b/fs/xfs/xfs_attr_item.c +@@ -546,43 +546,17 @@ xfs_attri_validate( + return xfs_verify_ino(mp, attrp->alfi_ino); + } + +-/* +- * Process an attr intent item that was recovered from the log. We need to +- * delete the attr that it describes. +- */ +-STATIC int +-xfs_attri_item_recover( ++static inline struct xfs_attr_intent * ++xfs_attri_recover_work( ++ struct xfs_mount *mp, + struct xfs_defer_pending *dfp, +- struct list_head *capture_list) ++ struct xfs_attri_log_format *attrp, ++ struct xfs_inode *ip, ++ struct xfs_attri_log_nameval *nv) + { +- struct xfs_log_item *lip = dfp->dfp_intent; +- struct xfs_attri_log_item *attrip = ATTRI_ITEM(lip); + struct xfs_attr_intent *attr; +- struct xfs_mount *mp = lip->li_log->l_mp; +- struct xfs_inode *ip; + struct xfs_da_args *args; +- struct xfs_trans *tp; +- struct xfs_trans_res resv; +- struct xfs_attri_log_format *attrp; +- struct xfs_attri_log_nameval *nv = attrip->attri_nameval; +- int error; +- int total; + int local; +- struct xfs_attrd_log_item *done_item = NULL; +- +- /* +- * First check the validity of the attr described by the ATTRI. If any +- * are bad, then assume that all are bad and just toss the ATTRI. +- */ +- attrp = &attrip->attri_format; +- if (!xfs_attri_validate(mp, attrp) || +- !xfs_attr_namecheck(attrp->alfi_attr_filter, nv->name.i_addr, +- nv->name.i_len)) +- return -EFSCORRUPTED; +- +- error = xlog_recover_iget(mp, attrp->alfi_ino, &ip); +- if (error) +- return error; + + attr = kmem_zalloc(sizeof(struct xfs_attr_intent) + + sizeof(struct xfs_da_args), KM_NOFS); +@@ -624,19 +598,59 @@ xfs_attri_item_recover( + case XFS_ATTRI_OP_FLAGS_REMOVE: + attr->xattri_dela_state = xfs_attr_init_remove_state(args); + break; +- default: +- ASSERT(0); +- error = -EFSCORRUPTED; +- goto out; + } + ++ xfs_defer_add_item(dfp, &attr->xattri_list); ++ return attr; ++} ++ ++/* ++ * Process an attr intent item that was recovered from the log. We need to ++ * delete the attr that it describes. ++ */ ++STATIC int ++xfs_attri_item_recover( ++ struct xfs_defer_pending *dfp, ++ struct list_head *capture_list) ++{ ++ struct xfs_log_item *lip = dfp->dfp_intent; ++ struct xfs_attri_log_item *attrip = ATTRI_ITEM(lip); ++ struct xfs_attr_intent *attr; ++ struct xfs_mount *mp = lip->li_log->l_mp; ++ struct xfs_inode *ip; ++ struct xfs_da_args *args; ++ struct xfs_trans *tp; ++ struct xfs_trans_res resv; ++ struct xfs_attri_log_format *attrp; ++ struct xfs_attri_log_nameval *nv = attrip->attri_nameval; ++ int error; ++ int total; ++ struct xfs_attrd_log_item *done_item = NULL; ++ ++ /* ++ * First check the validity of the attr described by the ATTRI. If any ++ * are bad, then assume that all are bad and just toss the ATTRI. ++ */ ++ attrp = &attrip->attri_format; ++ if (!xfs_attri_validate(mp, attrp) || ++ !xfs_attr_namecheck(attrp->alfi_attr_filter, nv->name.i_addr, ++ nv->name.i_len)) ++ return -EFSCORRUPTED; ++ ++ error = xlog_recover_iget(mp, attrp->alfi_ino, &ip); ++ if (error) ++ return error; ++ ++ attr = xfs_attri_recover_work(mp, dfp, attrp, ip, nv); ++ args = attr->xattri_da_args; ++ + xfs_init_attr_trans(args, &resv, &total); + resv = xlog_recover_resv(&resv); + error = xfs_trans_alloc(mp, &resv, total, 0, XFS_TRANS_RESERVE, &tp); + if (error) +- goto out; +- ++ return error; + args->trans = tp; ++ + done_item = xfs_trans_get_attrd(tp, attrip); + xlog_recover_transfer_intent(tp, dfp); + +@@ -667,8 +681,6 @@ xfs_attri_item_recover( + out_unlock: + xfs_iunlock(ip, XFS_ILOCK_EXCL); + xfs_irele(ip); +-out: +- xfs_attr_free_item(attr); + return error; + } + +diff --git a/fs/xfs/xfs_bmap_item.c b/fs/xfs/xfs_bmap_item.c +index 969cad575c3029..1be29b3db3e710 100644 +--- a/fs/xfs/xfs_bmap_item.c ++++ b/fs/xfs/xfs_bmap_item.c +@@ -481,6 +481,28 @@ xfs_bui_validate( + return xfs_verify_fsbext(mp, map->me_startblock, map->me_len); + } + ++static inline struct xfs_bmap_intent * ++xfs_bui_recover_work( ++ struct xfs_mount *mp, ++ struct xfs_defer_pending *dfp, ++ struct xfs_map_extent *map) ++{ ++ struct xfs_bmap_intent *bi; ++ ++ bi = kmem_cache_zalloc(xfs_bmap_intent_cache, GFP_NOFS | __GFP_NOFAIL); ++ bi->bi_whichfork = (map->me_flags & XFS_BMAP_EXTENT_ATTR_FORK) ? ++ XFS_ATTR_FORK : XFS_DATA_FORK; ++ bi->bi_type = map->me_flags & XFS_BMAP_EXTENT_TYPE_MASK; ++ bi->bi_bmap.br_startblock = map->me_startblock; ++ bi->bi_bmap.br_startoff = map->me_startoff; ++ bi->bi_bmap.br_blockcount = map->me_len; ++ bi->bi_bmap.br_state = (map->me_flags & XFS_BMAP_EXTENT_UNWRITTEN) ? ++ XFS_EXT_UNWRITTEN : XFS_EXT_NORM; ++ ++ xfs_defer_add_item(dfp, &bi->bi_list); ++ return bi; ++} ++ + /* + * Process a bmap update intent item that was recovered from the log. + * We need to update some inode's bmbt. +@@ -490,7 +512,6 @@ xfs_bui_item_recover( + struct xfs_defer_pending *dfp, + struct list_head *capture_list) + { +- struct xfs_bmap_intent fake = { }; + struct xfs_trans_res resv; + struct xfs_log_item *lip = dfp->dfp_intent; + struct xfs_bui_log_item *buip = BUI_ITEM(lip); +@@ -499,6 +520,7 @@ xfs_bui_item_recover( + struct xfs_mount *mp = lip->li_log->l_mp; + struct xfs_map_extent *map; + struct xfs_bud_log_item *budp; ++ struct xfs_bmap_intent *fake; + int iext_delta; + int error = 0; + +@@ -509,9 +531,7 @@ xfs_bui_item_recover( + } + + map = &buip->bui_format.bui_extents[0]; +- fake.bi_whichfork = (map->me_flags & XFS_BMAP_EXTENT_ATTR_FORK) ? +- XFS_ATTR_FORK : XFS_DATA_FORK; +- fake.bi_type = map->me_flags & XFS_BMAP_EXTENT_TYPE_MASK; ++ fake = xfs_bui_recover_work(mp, dfp, map); + + error = xlog_recover_iget(mp, map->me_owner, &ip); + if (error) +@@ -530,36 +550,31 @@ xfs_bui_item_recover( + xfs_ilock(ip, XFS_ILOCK_EXCL); + xfs_trans_ijoin(tp, ip, 0); + +- if (fake.bi_type == XFS_BMAP_MAP) ++ if (fake->bi_type == XFS_BMAP_MAP) + iext_delta = XFS_IEXT_ADD_NOSPLIT_CNT; + else + iext_delta = XFS_IEXT_PUNCH_HOLE_CNT; + +- error = xfs_iext_count_may_overflow(ip, fake.bi_whichfork, iext_delta); ++ error = xfs_iext_count_may_overflow(ip, fake->bi_whichfork, iext_delta); + if (error == -EFBIG) + error = xfs_iext_count_upgrade(tp, ip, iext_delta); + if (error) + goto err_cancel; + +- fake.bi_owner = ip; +- fake.bi_bmap.br_startblock = map->me_startblock; +- fake.bi_bmap.br_startoff = map->me_startoff; +- fake.bi_bmap.br_blockcount = map->me_len; +- fake.bi_bmap.br_state = (map->me_flags & XFS_BMAP_EXTENT_UNWRITTEN) ? +- XFS_EXT_UNWRITTEN : XFS_EXT_NORM; ++ fake->bi_owner = ip; + +- xfs_bmap_update_get_group(mp, &fake); +- error = xfs_trans_log_finish_bmap_update(tp, budp, &fake); ++ xfs_bmap_update_get_group(mp, fake); ++ error = xfs_trans_log_finish_bmap_update(tp, budp, fake); + if (error == -EFSCORRUPTED) +- XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, map, +- sizeof(*map)); +- xfs_bmap_update_put_group(&fake); ++ XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, ++ &buip->bui_format, sizeof(buip->bui_format)); ++ xfs_bmap_update_put_group(fake); + if (error) + goto err_cancel; + +- if (fake.bi_bmap.br_blockcount > 0) { +- ASSERT(fake.bi_type == XFS_BMAP_UNMAP); +- xfs_bmap_unmap_extent(tp, ip, &fake.bi_bmap); ++ if (fake->bi_bmap.br_blockcount > 0) { ++ ASSERT(fake->bi_type == XFS_BMAP_UNMAP); ++ xfs_bmap_unmap_extent(tp, ip, &fake->bi_bmap); + } + + /* +diff --git a/fs/xfs/xfs_extfree_item.c b/fs/xfs/xfs_extfree_item.c +index c9908fb337657a..41108a0b60c91a 100644 +--- a/fs/xfs/xfs_extfree_item.c ++++ b/fs/xfs/xfs_extfree_item.c +@@ -651,6 +651,24 @@ xfs_efi_validate_ext( + return xfs_verify_fsbext(mp, extp->ext_start, extp->ext_len); + } + ++static inline void ++xfs_efi_recover_work( ++ struct xfs_mount *mp, ++ struct xfs_defer_pending *dfp, ++ struct xfs_extent *extp) ++{ ++ struct xfs_extent_free_item *xefi; ++ ++ xefi = kmem_cache_zalloc(xfs_extfree_item_cache, ++ GFP_KERNEL | __GFP_NOFAIL); ++ xefi->xefi_startblock = extp->ext_start; ++ xefi->xefi_blockcount = extp->ext_len; ++ xefi->xefi_agresv = XFS_AG_RESV_NONE; ++ xefi->xefi_owner = XFS_RMAP_OWN_UNKNOWN; ++ ++ xfs_defer_add_item(dfp, &xefi->xefi_list); ++} ++ + /* + * Process an extent free intent item that was recovered from + * the log. We need to free the extents that it describes. +@@ -666,6 +684,7 @@ xfs_efi_item_recover( + struct xfs_mount *mp = lip->li_log->l_mp; + struct xfs_efd_log_item *efdp; + struct xfs_trans *tp; ++ struct xfs_extent_free_item *fake; + int i; + int error = 0; + bool requeue_only = false; +@@ -683,6 +702,8 @@ xfs_efi_item_recover( + sizeof(efip->efi_format)); + return -EFSCORRUPTED; + } ++ ++ xfs_efi_recover_work(mp, dfp, &efip->efi_format.efi_extents[i]); + } + + resv = xlog_recover_resv(&M_RES(mp)->tr_itruncate); +@@ -693,22 +714,11 @@ xfs_efi_item_recover( + efdp = xfs_trans_get_efd(tp, efip, efip->efi_format.efi_nextents); + xlog_recover_transfer_intent(tp, dfp); + +- for (i = 0; i < efip->efi_format.efi_nextents; i++) { +- struct xfs_extent_free_item fake = { +- .xefi_owner = XFS_RMAP_OWN_UNKNOWN, +- .xefi_agresv = XFS_AG_RESV_NONE, +- }; +- struct xfs_extent *extp; +- +- extp = &efip->efi_format.efi_extents[i]; +- +- fake.xefi_startblock = extp->ext_start; +- fake.xefi_blockcount = extp->ext_len; +- ++ list_for_each_entry(fake, &dfp->dfp_work, xefi_list) { + if (!requeue_only) { +- xfs_extent_free_get_group(mp, &fake); +- error = xfs_trans_free_extent(tp, efdp, &fake); +- xfs_extent_free_put_group(&fake); ++ xfs_extent_free_get_group(mp, fake); ++ error = xfs_trans_free_extent(tp, efdp, fake); ++ xfs_extent_free_put_group(fake); + } + + /* +@@ -717,10 +727,10 @@ xfs_efi_item_recover( + * run again later with a new transaction context. + */ + if (error == -EAGAIN || requeue_only) { +- error = xfs_free_extent_later(tp, fake.xefi_startblock, +- fake.xefi_blockcount, ++ error = xfs_free_extent_later(tp, fake->xefi_startblock, ++ fake->xefi_blockcount, + &XFS_RMAP_OINFO_ANY_OWNER, +- fake.xefi_agresv); ++ fake->xefi_agresv); + if (!error) { + requeue_only = true; + continue; +@@ -729,7 +739,8 @@ xfs_efi_item_recover( + + if (error == -EFSCORRUPTED) + XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, +- extp, sizeof(*extp)); ++ &efip->efi_format, ++ sizeof(efip->efi_format)); + if (error) + goto abort_error; + +diff --git a/fs/xfs/xfs_refcount_item.c b/fs/xfs/xfs_refcount_item.c +index f1b2592238022e..4ffc34e6f0a079 100644 +--- a/fs/xfs/xfs_refcount_item.c ++++ b/fs/xfs/xfs_refcount_item.c +@@ -468,6 +468,23 @@ xfs_cui_validate_phys( + return xfs_verify_fsbext(mp, pmap->pe_startblock, pmap->pe_len); + } + ++static inline void ++xfs_cui_recover_work( ++ struct xfs_mount *mp, ++ struct xfs_defer_pending *dfp, ++ struct xfs_phys_extent *pmap) ++{ ++ struct xfs_refcount_intent *ri; ++ ++ ri = kmem_cache_alloc(xfs_refcount_intent_cache, ++ GFP_NOFS | __GFP_NOFAIL); ++ ri->ri_type = pmap->pe_flags & XFS_REFCOUNT_EXTENT_TYPE_MASK; ++ ri->ri_startblock = pmap->pe_startblock; ++ ri->ri_blockcount = pmap->pe_len; ++ ++ xfs_defer_add_item(dfp, &ri->ri_list); ++} ++ + /* + * Process a refcount update intent item that was recovered from the log. + * We need to update the refcountbt. +@@ -484,7 +501,7 @@ xfs_cui_item_recover( + struct xfs_trans *tp; + struct xfs_btree_cur *rcur = NULL; + struct xfs_mount *mp = lip->li_log->l_mp; +- unsigned int refc_type; ++ struct xfs_refcount_intent *fake; + bool requeue_only = false; + int i; + int error = 0; +@@ -502,6 +519,8 @@ xfs_cui_item_recover( + sizeof(cuip->cui_format)); + return -EFSCORRUPTED; + } ++ ++ xfs_cui_recover_work(mp, dfp, &cuip->cui_format.cui_extents[i]); + } + + /* +@@ -525,35 +544,12 @@ xfs_cui_item_recover( + cudp = xfs_trans_get_cud(tp, cuip); + xlog_recover_transfer_intent(tp, dfp); + +- for (i = 0; i < cuip->cui_format.cui_nextents; i++) { +- struct xfs_refcount_intent fake = { }; +- struct xfs_phys_extent *pmap; +- +- pmap = &cuip->cui_format.cui_extents[i]; +- refc_type = pmap->pe_flags & XFS_REFCOUNT_EXTENT_TYPE_MASK; +- switch (refc_type) { +- case XFS_REFCOUNT_INCREASE: +- case XFS_REFCOUNT_DECREASE: +- case XFS_REFCOUNT_ALLOC_COW: +- case XFS_REFCOUNT_FREE_COW: +- fake.ri_type = refc_type; +- break; +- default: +- XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, +- &cuip->cui_format, +- sizeof(cuip->cui_format)); +- error = -EFSCORRUPTED; +- goto abort_error; +- } +- +- fake.ri_startblock = pmap->pe_startblock; +- fake.ri_blockcount = pmap->pe_len; +- ++ list_for_each_entry(fake, &dfp->dfp_work, ri_list) { + if (!requeue_only) { +- xfs_refcount_update_get_group(mp, &fake); ++ xfs_refcount_update_get_group(mp, fake); + error = xfs_trans_log_finish_refcount_update(tp, cudp, +- &fake, &rcur); +- xfs_refcount_update_put_group(&fake); ++ fake, &rcur); ++ xfs_refcount_update_put_group(fake); + } + if (error == -EFSCORRUPTED) + XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, +@@ -563,13 +559,13 @@ xfs_cui_item_recover( + goto abort_error; + + /* Requeue what we didn't finish. */ +- if (fake.ri_blockcount > 0) { ++ if (fake->ri_blockcount > 0) { + struct xfs_bmbt_irec irec = { +- .br_startblock = fake.ri_startblock, +- .br_blockcount = fake.ri_blockcount, ++ .br_startblock = fake->ri_startblock, ++ .br_blockcount = fake->ri_blockcount, + }; + +- switch (fake.ri_type) { ++ switch (fake->ri_type) { + case XFS_REFCOUNT_INCREASE: + xfs_refcount_increase_extent(tp, &irec); + break; +diff --git a/fs/xfs/xfs_rmap_item.c b/fs/xfs/xfs_rmap_item.c +index 5e8a02d2b045de..9fb3ae4bfd5915 100644 +--- a/fs/xfs/xfs_rmap_item.c ++++ b/fs/xfs/xfs_rmap_item.c +@@ -498,6 +498,58 @@ xfs_rui_validate_map( + return xfs_verify_fsbext(mp, map->me_startblock, map->me_len); + } + ++static inline void ++xfs_rui_recover_work( ++ struct xfs_mount *mp, ++ struct xfs_defer_pending *dfp, ++ const struct xfs_map_extent *map) ++{ ++ struct xfs_rmap_intent *ri; ++ ++ ri = kmem_cache_alloc(xfs_rmap_intent_cache, GFP_NOFS | __GFP_NOFAIL); ++ ++ switch (map->me_flags & XFS_RMAP_EXTENT_TYPE_MASK) { ++ case XFS_RMAP_EXTENT_MAP: ++ ri->ri_type = XFS_RMAP_MAP; ++ break; ++ case XFS_RMAP_EXTENT_MAP_SHARED: ++ ri->ri_type = XFS_RMAP_MAP_SHARED; ++ break; ++ case XFS_RMAP_EXTENT_UNMAP: ++ ri->ri_type = XFS_RMAP_UNMAP; ++ break; ++ case XFS_RMAP_EXTENT_UNMAP_SHARED: ++ ri->ri_type = XFS_RMAP_UNMAP_SHARED; ++ break; ++ case XFS_RMAP_EXTENT_CONVERT: ++ ri->ri_type = XFS_RMAP_CONVERT; ++ break; ++ case XFS_RMAP_EXTENT_CONVERT_SHARED: ++ ri->ri_type = XFS_RMAP_CONVERT_SHARED; ++ break; ++ case XFS_RMAP_EXTENT_ALLOC: ++ ri->ri_type = XFS_RMAP_ALLOC; ++ break; ++ case XFS_RMAP_EXTENT_FREE: ++ ri->ri_type = XFS_RMAP_FREE; ++ break; ++ default: ++ ASSERT(0); ++ return; ++ } ++ ++ ri->ri_owner = map->me_owner; ++ ri->ri_whichfork = (map->me_flags & XFS_RMAP_EXTENT_ATTR_FORK) ? ++ XFS_ATTR_FORK : XFS_DATA_FORK; ++ ri->ri_bmap.br_startblock = map->me_startblock; ++ ri->ri_bmap.br_startoff = map->me_startoff; ++ ri->ri_bmap.br_blockcount = map->me_len; ++ ri->ri_bmap.br_state = (map->me_flags & XFS_RMAP_EXTENT_UNWRITTEN) ? ++ XFS_EXT_UNWRITTEN : XFS_EXT_NORM; ++ ++ xfs_defer_add_item(dfp, &ri->ri_list); ++} ++ + /* + * Process an rmap update intent item that was recovered from the log. + * We need to update the rmapbt. +@@ -514,6 +566,7 @@ xfs_rui_item_recover( + struct xfs_trans *tp; + struct xfs_btree_cur *rcur = NULL; + struct xfs_mount *mp = lip->li_log->l_mp; ++ struct xfs_rmap_intent *fake; + int i; + int error = 0; + +@@ -530,6 +583,8 @@ xfs_rui_item_recover( + sizeof(ruip->rui_format)); + return -EFSCORRUPTED; + } ++ ++ xfs_rui_recover_work(mp, dfp, &ruip->rui_format.rui_extents[i]); + } + + resv = xlog_recover_resv(&M_RES(mp)->tr_itruncate); +@@ -541,60 +596,15 @@ xfs_rui_item_recover( + rudp = xfs_trans_get_rud(tp, ruip); + xlog_recover_transfer_intent(tp, dfp); + +- for (i = 0; i < ruip->rui_format.rui_nextents; i++) { +- struct xfs_rmap_intent fake = { }; +- struct xfs_map_extent *map; +- +- map = &ruip->rui_format.rui_extents[i]; +- switch (map->me_flags & XFS_RMAP_EXTENT_TYPE_MASK) { +- case XFS_RMAP_EXTENT_MAP: +- fake.ri_type = XFS_RMAP_MAP; +- break; +- case XFS_RMAP_EXTENT_MAP_SHARED: +- fake.ri_type = XFS_RMAP_MAP_SHARED; +- break; +- case XFS_RMAP_EXTENT_UNMAP: +- fake.ri_type = XFS_RMAP_UNMAP; +- break; +- case XFS_RMAP_EXTENT_UNMAP_SHARED: +- fake.ri_type = XFS_RMAP_UNMAP_SHARED; +- break; +- case XFS_RMAP_EXTENT_CONVERT: +- fake.ri_type = XFS_RMAP_CONVERT; +- break; +- case XFS_RMAP_EXTENT_CONVERT_SHARED: +- fake.ri_type = XFS_RMAP_CONVERT_SHARED; +- break; +- case XFS_RMAP_EXTENT_ALLOC: +- fake.ri_type = XFS_RMAP_ALLOC; +- break; +- case XFS_RMAP_EXTENT_FREE: +- fake.ri_type = XFS_RMAP_FREE; +- break; +- default: +- XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, +- &ruip->rui_format, +- sizeof(ruip->rui_format)); +- error = -EFSCORRUPTED; +- goto abort_error; +- } +- +- fake.ri_owner = map->me_owner; +- fake.ri_whichfork = (map->me_flags & XFS_RMAP_EXTENT_ATTR_FORK) ? +- XFS_ATTR_FORK : XFS_DATA_FORK; +- fake.ri_bmap.br_startblock = map->me_startblock; +- fake.ri_bmap.br_startoff = map->me_startoff; +- fake.ri_bmap.br_blockcount = map->me_len; +- fake.ri_bmap.br_state = (map->me_flags & XFS_RMAP_EXTENT_UNWRITTEN) ? +- XFS_EXT_UNWRITTEN : XFS_EXT_NORM; +- +- xfs_rmap_update_get_group(mp, &fake); +- error = xfs_trans_log_finish_rmap_update(tp, rudp, &fake, ++ list_for_each_entry(fake, &dfp->dfp_work, ri_list) { ++ xfs_rmap_update_get_group(mp, fake); ++ error = xfs_trans_log_finish_rmap_update(tp, rudp, fake, + &rcur); + if (error == -EFSCORRUPTED) + XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, +- map, sizeof(*map)); +- xfs_rmap_update_put_group(&fake); ++ &ruip->rui_format, ++ sizeof(ruip->rui_format)); ++ xfs_rmap_update_put_group(fake); + if (error) + goto abort_error; + +-- +2.53.0 + diff --git a/queue-6.6/xfs-use-xfs_defer_finish_one-to-finish-recovered-wor.patch b/queue-6.6/xfs-use-xfs_defer_finish_one-to-finish-recovered-wor.patch new file mode 100644 index 0000000000..3cb28ed975 --- /dev/null +++ b/queue-6.6/xfs-use-xfs_defer_finish_one-to-finish-recovered-wor.patch @@ -0,0 +1,462 @@ +From f2d03bffca2aa7121db80dad9c916dba9a9672e9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 22 Mar 2025 17:34:14 +0300 +Subject: xfs: use xfs_defer_finish_one to finish recovered work items + +From: Darrick J. Wong + +commit e5f1a5146ec35f3ed5d7f5ac7807a10c0062b6b8 upstream. + +Get rid of the open-coded calls to xfs_defer_finish_one. This also +means that the recovery transaction takes care of cleaning up the dfp, +and we have solved (I hope) all the ownership issues in recovery. + +Signed-off-by: Darrick J. Wong +Reviewed-by: Christoph Hellwig +Signed-off-by: Fedor Pchelkin +Signed-off-by: Sasha Levin +--- + fs/xfs/libxfs/xfs_defer.c | 2 +- + fs/xfs/libxfs/xfs_defer.h | 1 + + fs/xfs/libxfs/xfs_log_recover.h | 2 +- + fs/xfs/xfs_attr_item.c | 20 +---------- + fs/xfs/xfs_bmap_item.c | 24 ++++--------- + fs/xfs/xfs_extfree_item.c | 45 +++++------------------- + fs/xfs/xfs_log_recover.c | 22 +++++++----- + fs/xfs/xfs_refcount_item.c | 61 +++++---------------------------- + fs/xfs/xfs_rmap_item.c | 29 +++++----------- + 9 files changed, 49 insertions(+), 157 deletions(-) + +diff --git a/fs/xfs/libxfs/xfs_defer.c b/fs/xfs/libxfs/xfs_defer.c +index 8fb523e4f66931..eb262ea061226b 100644 +--- a/fs/xfs/libxfs/xfs_defer.c ++++ b/fs/xfs/libxfs/xfs_defer.c +@@ -484,7 +484,7 @@ xfs_defer_relog( + * Log an intent-done item for the first pending intent, and finish the work + * items. + */ +-static int ++int + xfs_defer_finish_one( + struct xfs_trans *tp, + struct xfs_defer_pending *dfp) +diff --git a/fs/xfs/libxfs/xfs_defer.h b/fs/xfs/libxfs/xfs_defer.h +index bef5823f61fb82..c1a648e9917489 100644 +--- a/fs/xfs/libxfs/xfs_defer.h ++++ b/fs/xfs/libxfs/xfs_defer.h +@@ -41,6 +41,7 @@ void xfs_defer_add(struct xfs_trans *tp, enum xfs_defer_ops_type type, + struct list_head *h); + int xfs_defer_finish_noroll(struct xfs_trans **tp); + int xfs_defer_finish(struct xfs_trans **tp); ++int xfs_defer_finish_one(struct xfs_trans *tp, struct xfs_defer_pending *dfp); + void xfs_defer_cancel(struct xfs_trans *); + void xfs_defer_move(struct xfs_trans *dtp, struct xfs_trans *stp); + +diff --git a/fs/xfs/libxfs/xfs_log_recover.h b/fs/xfs/libxfs/xfs_log_recover.h +index 13583df9f2397f..52162a17fc5e3a 100644 +--- a/fs/xfs/libxfs/xfs_log_recover.h ++++ b/fs/xfs/libxfs/xfs_log_recover.h +@@ -155,7 +155,7 @@ xlog_recover_resv(const struct xfs_trans_res *r) + + void xlog_recover_intent_item(struct xlog *log, struct xfs_log_item *lip, + xfs_lsn_t lsn, unsigned int dfp_type); +-void xlog_recover_transfer_intent(struct xfs_trans *tp, ++int xlog_recover_finish_intent(struct xfs_trans *tp, + struct xfs_defer_pending *dfp); + + #endif /* __XFS_LOG_RECOVER_H__ */ +diff --git a/fs/xfs/xfs_attr_item.c b/fs/xfs/xfs_attr_item.c +index 94e16f51a5ac52..b7bf99d8c4739e 100644 +--- a/fs/xfs/xfs_attr_item.c ++++ b/fs/xfs/xfs_attr_item.c +@@ -625,7 +625,6 @@ xfs_attri_item_recover( + struct xfs_attri_log_nameval *nv = attrip->attri_nameval; + int error; + int total; +- struct xfs_attrd_log_item *done_item = NULL; + + /* + * First check the validity of the attr described by the ATTRI. If any +@@ -651,27 +650,10 @@ xfs_attri_item_recover( + return error; + args->trans = tp; + +- done_item = xfs_trans_get_attrd(tp, attrip); +- xlog_recover_transfer_intent(tp, dfp); +- + xfs_ilock(ip, XFS_ILOCK_EXCL); + xfs_trans_ijoin(tp, ip, 0); + +- error = xfs_xattri_finish_update(attr, done_item); +- if (error == -EAGAIN) { +- /* +- * There's more work to do, so add the intent item to this +- * transaction so that we can continue it later. +- */ +- xfs_defer_add(tp, XFS_DEFER_OPS_TYPE_ATTR, &attr->xattri_list); +- error = xfs_defer_ops_capture_and_commit(tp, capture_list); +- if (error) +- goto out_unlock; +- +- xfs_iunlock(ip, XFS_ILOCK_EXCL); +- xfs_irele(ip); +- return 0; +- } ++ error = xlog_recover_finish_intent(tp, dfp); + if (error == -EFSCORRUPTED) + XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, + &attrip->attri_format, +diff --git a/fs/xfs/xfs_bmap_item.c b/fs/xfs/xfs_bmap_item.c +index 1be29b3db3e710..2d91ade24b689a 100644 +--- a/fs/xfs/xfs_bmap_item.c ++++ b/fs/xfs/xfs_bmap_item.c +@@ -498,6 +498,7 @@ xfs_bui_recover_work( + bi->bi_bmap.br_blockcount = map->me_len; + bi->bi_bmap.br_state = (map->me_flags & XFS_BMAP_EXTENT_UNWRITTEN) ? + XFS_EXT_UNWRITTEN : XFS_EXT_NORM; ++ xfs_bmap_update_get_group(mp, bi); + + xfs_defer_add_item(dfp, &bi->bi_list); + return bi; +@@ -519,8 +520,7 @@ xfs_bui_item_recover( + struct xfs_inode *ip = NULL; + struct xfs_mount *mp = lip->li_log->l_mp; + struct xfs_map_extent *map; +- struct xfs_bud_log_item *budp; +- struct xfs_bmap_intent *fake; ++ struct xfs_bmap_intent *work; + int iext_delta; + int error = 0; + +@@ -531,7 +531,7 @@ xfs_bui_item_recover( + } + + map = &buip->bui_format.bui_extents[0]; +- fake = xfs_bui_recover_work(mp, dfp, map); ++ work = xfs_bui_recover_work(mp, dfp, map); + + error = xlog_recover_iget(mp, map->me_owner, &ip); + if (error) +@@ -544,39 +544,29 @@ xfs_bui_item_recover( + if (error) + goto err_rele; + +- budp = xfs_trans_get_bud(tp, buip); +- xlog_recover_transfer_intent(tp, dfp); +- + xfs_ilock(ip, XFS_ILOCK_EXCL); + xfs_trans_ijoin(tp, ip, 0); + +- if (fake->bi_type == XFS_BMAP_MAP) ++ if (work->bi_type == XFS_BMAP_MAP) + iext_delta = XFS_IEXT_ADD_NOSPLIT_CNT; + else + iext_delta = XFS_IEXT_PUNCH_HOLE_CNT; + +- error = xfs_iext_count_may_overflow(ip, fake->bi_whichfork, iext_delta); ++ error = xfs_iext_count_may_overflow(ip, work->bi_whichfork, iext_delta); + if (error == -EFBIG) + error = xfs_iext_count_upgrade(tp, ip, iext_delta); + if (error) + goto err_cancel; + +- fake->bi_owner = ip; ++ work->bi_owner = ip; + +- xfs_bmap_update_get_group(mp, fake); +- error = xfs_trans_log_finish_bmap_update(tp, budp, fake); ++ error = xlog_recover_finish_intent(tp, dfp); + if (error == -EFSCORRUPTED) + XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, + &buip->bui_format, sizeof(buip->bui_format)); +- xfs_bmap_update_put_group(fake); + if (error) + goto err_cancel; + +- if (fake->bi_bmap.br_blockcount > 0) { +- ASSERT(fake->bi_type == XFS_BMAP_UNMAP); +- xfs_bmap_unmap_extent(tp, ip, &fake->bi_bmap); +- } +- + /* + * Commit transaction, which frees the transaction and saves the inode + * for later replay activities. +diff --git a/fs/xfs/xfs_extfree_item.c b/fs/xfs/xfs_extfree_item.c +index 41108a0b60c91a..6a434ade486c93 100644 +--- a/fs/xfs/xfs_extfree_item.c ++++ b/fs/xfs/xfs_extfree_item.c +@@ -665,6 +665,7 @@ xfs_efi_recover_work( + xefi->xefi_blockcount = extp->ext_len; + xefi->xefi_agresv = XFS_AG_RESV_NONE; + xefi->xefi_owner = XFS_RMAP_OWN_UNKNOWN; ++ xfs_extent_free_get_group(mp, xefi); + + xfs_defer_add_item(dfp, &xefi->xefi_list); + } +@@ -682,12 +683,9 @@ xfs_efi_item_recover( + struct xfs_log_item *lip = dfp->dfp_intent; + struct xfs_efi_log_item *efip = EFI_ITEM(lip); + struct xfs_mount *mp = lip->li_log->l_mp; +- struct xfs_efd_log_item *efdp; + struct xfs_trans *tp; +- struct xfs_extent_free_item *fake; + int i; + int error = 0; +- bool requeue_only = false; + + /* + * First check the validity of the extents described by the +@@ -711,40 +709,13 @@ xfs_efi_item_recover( + if (error) + return error; + +- efdp = xfs_trans_get_efd(tp, efip, efip->efi_format.efi_nextents); +- xlog_recover_transfer_intent(tp, dfp); +- +- list_for_each_entry(fake, &dfp->dfp_work, xefi_list) { +- if (!requeue_only) { +- xfs_extent_free_get_group(mp, fake); +- error = xfs_trans_free_extent(tp, efdp, fake); +- xfs_extent_free_put_group(fake); +- } +- +- /* +- * If we can't free the extent without potentially deadlocking, +- * requeue the rest of the extents to a new so that they get +- * run again later with a new transaction context. +- */ +- if (error == -EAGAIN || requeue_only) { +- error = xfs_free_extent_later(tp, fake->xefi_startblock, +- fake->xefi_blockcount, +- &XFS_RMAP_OINFO_ANY_OWNER, +- fake->xefi_agresv); +- if (!error) { +- requeue_only = true; +- continue; +- } +- } +- +- if (error == -EFSCORRUPTED) +- XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, +- &efip->efi_format, +- sizeof(efip->efi_format)); +- if (error) +- goto abort_error; +- +- } ++ error = xlog_recover_finish_intent(tp, dfp); ++ if (error == -EFSCORRUPTED) ++ XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, ++ &efip->efi_format, ++ sizeof(efip->efi_format)); ++ if (error) ++ goto abort_error; + + return xfs_defer_ops_capture_and_commit(tp, capture_list); + +diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c +index f708bf4104b1a3..c668164defdc13 100644 +--- a/fs/xfs/xfs_log_recover.c ++++ b/fs/xfs/xfs_log_recover.c +@@ -2584,7 +2584,8 @@ xlog_recover_process_intents( + * replayed in the wrong order! + * + * The recovery function can free the log item, so we must not +- * access lip after it returns. ++ * access lip after it returns. It must dispose of @dfp if it ++ * returns 0. + */ + error = ops->iop_recover(dfp, &capture_list); + if (error) { +@@ -2592,8 +2593,6 @@ xlog_recover_process_intents( + ops->iop_recover); + break; + } +- +- xfs_defer_cancel_recovery(log->l_mp, dfp); + } + if (error) + goto err; +@@ -2627,15 +2626,22 @@ xlog_recover_cancel_intents( + } + + /* +- * Transfer ownership of the recovered log intent item to the recovery +- * transaction. ++ * Transfer ownership of the recovered pending work to the recovery transaction ++ * and try to finish the work. If there is more work to be done, the dfp will ++ * remain attached to the transaction. If not, the dfp is freed. + */ +-void +-xlog_recover_transfer_intent( ++int ++xlog_recover_finish_intent( + struct xfs_trans *tp, + struct xfs_defer_pending *dfp) + { +- dfp->dfp_intent = NULL; ++ int error; ++ ++ list_move(&dfp->dfp_list, &tp->t_dfops); ++ error = xfs_defer_finish_one(tp, dfp); ++ if (error == -EAGAIN) ++ return 0; ++ return error; + } + + /* +diff --git a/fs/xfs/xfs_refcount_item.c b/fs/xfs/xfs_refcount_item.c +index 4ffc34e6f0a079..f561ca73c784ff 100644 +--- a/fs/xfs/xfs_refcount_item.c ++++ b/fs/xfs/xfs_refcount_item.c +@@ -481,6 +481,7 @@ xfs_cui_recover_work( + ri->ri_type = pmap->pe_flags & XFS_REFCOUNT_EXTENT_TYPE_MASK; + ri->ri_startblock = pmap->pe_startblock; + ri->ri_blockcount = pmap->pe_len; ++ xfs_refcount_update_get_group(mp, ri); + + xfs_defer_add_item(dfp, &ri->ri_list); + } +@@ -497,12 +498,8 @@ xfs_cui_item_recover( + struct xfs_trans_res resv; + struct xfs_log_item *lip = dfp->dfp_intent; + struct xfs_cui_log_item *cuip = CUI_ITEM(lip); +- struct xfs_cud_log_item *cudp; + struct xfs_trans *tp; +- struct xfs_btree_cur *rcur = NULL; + struct xfs_mount *mp = lip->li_log->l_mp; +- struct xfs_refcount_intent *fake; +- bool requeue_only = false; + int i; + int error = 0; + +@@ -541,59 +538,17 @@ xfs_cui_item_recover( + if (error) + return error; + +- cudp = xfs_trans_get_cud(tp, cuip); +- xlog_recover_transfer_intent(tp, dfp); +- +- list_for_each_entry(fake, &dfp->dfp_work, ri_list) { +- if (!requeue_only) { +- xfs_refcount_update_get_group(mp, fake); +- error = xfs_trans_log_finish_refcount_update(tp, cudp, +- fake, &rcur); +- xfs_refcount_update_put_group(fake); +- } +- if (error == -EFSCORRUPTED) +- XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, +- &cuip->cui_format, +- sizeof(cuip->cui_format)); +- if (error) +- goto abort_error; +- +- /* Requeue what we didn't finish. */ +- if (fake->ri_blockcount > 0) { +- struct xfs_bmbt_irec irec = { +- .br_startblock = fake->ri_startblock, +- .br_blockcount = fake->ri_blockcount, +- }; +- +- switch (fake->ri_type) { +- case XFS_REFCOUNT_INCREASE: +- xfs_refcount_increase_extent(tp, &irec); +- break; +- case XFS_REFCOUNT_DECREASE: +- xfs_refcount_decrease_extent(tp, &irec); +- break; +- case XFS_REFCOUNT_ALLOC_COW: +- xfs_refcount_alloc_cow_extent(tp, +- irec.br_startblock, +- irec.br_blockcount); +- break; +- case XFS_REFCOUNT_FREE_COW: +- xfs_refcount_free_cow_extent(tp, +- irec.br_startblock, +- irec.br_blockcount); +- break; +- default: +- ASSERT(0); +- } +- requeue_only = true; +- } +- } ++ error = xlog_recover_finish_intent(tp, dfp); ++ if (error == -EFSCORRUPTED) ++ XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, ++ &cuip->cui_format, ++ sizeof(cuip->cui_format)); ++ if (error) ++ goto abort_error; + +- xfs_refcount_finish_one_cleanup(tp, rcur, error); + return xfs_defer_ops_capture_and_commit(tp, capture_list); + + abort_error: +- xfs_refcount_finish_one_cleanup(tp, rcur, error); + xfs_trans_cancel(tp); + return error; + } +diff --git a/fs/xfs/xfs_rmap_item.c b/fs/xfs/xfs_rmap_item.c +index 9fb3ae4bfd5915..23e736179894b3 100644 +--- a/fs/xfs/xfs_rmap_item.c ++++ b/fs/xfs/xfs_rmap_item.c +@@ -546,6 +546,7 @@ xfs_rui_recover_work( + ri->ri_bmap.br_blockcount = map->me_len; + ri->ri_bmap.br_state = (map->me_flags & XFS_RMAP_EXTENT_UNWRITTEN) ? + XFS_EXT_UNWRITTEN : XFS_EXT_NORM; ++ xfs_rmap_update_get_group(mp, ri); + + xfs_defer_add_item(dfp, &ri->ri_list); + } +@@ -562,11 +563,8 @@ xfs_rui_item_recover( + struct xfs_trans_res resv; + struct xfs_log_item *lip = dfp->dfp_intent; + struct xfs_rui_log_item *ruip = RUI_ITEM(lip); +- struct xfs_rud_log_item *rudp; + struct xfs_trans *tp; +- struct xfs_btree_cur *rcur = NULL; + struct xfs_mount *mp = lip->li_log->l_mp; +- struct xfs_rmap_intent *fake; + int i; + int error = 0; + +@@ -593,28 +591,17 @@ xfs_rui_item_recover( + if (error) + return error; + +- rudp = xfs_trans_get_rud(tp, ruip); +- xlog_recover_transfer_intent(tp, dfp); +- +- list_for_each_entry(fake, &dfp->dfp_work, ri_list) { +- xfs_rmap_update_get_group(mp, fake); +- error = xfs_trans_log_finish_rmap_update(tp, rudp, fake, +- &rcur); +- if (error == -EFSCORRUPTED) +- XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, +- &ruip->rui_format, +- sizeof(ruip->rui_format)); +- xfs_rmap_update_put_group(fake); +- if (error) +- goto abort_error; +- +- } ++ error = xlog_recover_finish_intent(tp, dfp); ++ if (error == -EFSCORRUPTED) ++ XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, ++ &ruip->rui_format, ++ sizeof(ruip->rui_format)); ++ if (error) ++ goto abort_error; + +- xfs_rmap_finish_one_cleanup(tp, rcur, error); + return xfs_defer_ops_capture_and_commit(tp, capture_list); + + abort_error: +- xfs_rmap_finish_one_cleanup(tp, rcur, error); + xfs_trans_cancel(tp); + return error; + } +-- +2.53.0 +