--- /dev/null
+From f9de342881552d3ddc59318fbc85cec3899781f8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 7 Jul 2026 11:44:17 +0100
+Subject: mmc: renesas_sdhi: Add quirk entry for RZ/G2H SoC
+
+From: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
+
+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 <prabhakar.mahadev-lad.rj@bp.renesas.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
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
--- /dev/null
+From 705fe2dde3194444671a6b00ab0122083ea5d27e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 8 Jul 2026 12:12:36 +0800
+Subject: mm/vmscan: flush deferred TLB before freeing large folios
+
+From: Jiayuan Chen <jiayuan.chen@shopee.com>
+
+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 <yingfu.zhou@shopee.com>
+Fixes: bd4c82c22c36 ("mm, THP, swap: delay splitting THP after swapped out")
+Cc: Jiayuan Chen <jiayuan.chen@linux.dev>
+Signed-off-by: Jiayuan Chen <jiayuan.chen@shopee.com>
+Reviewed-by: Matthew Wilcox <willy@infradead.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
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
--- /dev/null
+From 247a6d7dbcaf6aced51d2d2627f21994418f739e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 7 Jul 2026 18:37:27 +0300
+Subject: block: add a store_limit operations for sysfs entries
+
+From: Christoph Hellwig <hch@lst.de>
+
+[ 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 <hch@lst.de>
+Reviewed-by: Ming Lei <ming.lei@redhat.com>
+Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
+Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com>
+Reviewed-by: Nilay Shroff <nilay@linux.ibm.com>
+Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
+Reviewed-by: John Garry <john.g.garry@oracle.com>
+Link: https://lore.kernel.org/r/20250110054726.1499538-6-hch@lst.de
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+Stable-dep-of: c99f66e4084a ("block: fix queue freeze vs limits lock order in sysfs store methods")
+Signed-off-by: Konstantin Andreev <andreev@swemel.ru>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 91407d252535f722304ca9915e17f79fdfb91d1f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <hch@lst.de>
+
+[ 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 <hch@lst.de>
+Reviewed-by: Ming Lei <ming.lei@redhat.com>
+Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
+Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com>
+Reviewed-by: Nilay Shroff <nilay@linux.ibm.com>
+Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
+Link: https://lore.kernel.org/r/20250110054726.1499538-7-hch@lst.de
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+Signed-off-by: Konstantin Andreev <andreev@swemel.ru>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 395ed4519a34b3f17a35c4a0aadfaeba99694de6 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 25 Sep 2025 02:33:03 +0000
+Subject: bonding: fix xfrm offload feature setup on active-backup mode
+
+From: Hangbin Liu <liuhangbin@gmail.com>
+
+[ 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 <liuhangbin@gmail.com>
+Link: https://patch.msgid.link/20250925023304.472186-1-liuhangbin@gmail.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
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
--- /dev/null
+From 2216dfc5e120b0b7f4b2fa5e741f09c5dcfc6b23 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 8 Jul 2026 17:47:08 +0800
+Subject: bonding: add ESP offload features when slaves support
+
+From: Jianbo Liu <jianbol@nvidia.com>
+
+[ 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 <jianbol@nvidia.com>
+Reviewed-by: Boris Pismenny <borisp@nvidia.com>
+Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
+Link: https://patch.msgid.link/20241105192721.584822-1-tariqt@nvidia.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Stable-dep-of: 950803f72547 ("bonding: fix type confusion in bond_setup_by_slave()")
+Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From eefea913c577312e33cf13d62e25a82615a9a1df Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 8 Jul 2026 17:47:10 +0800
+Subject: bonding: Correctly support GSO ESP offload
+
+From: Cosmin Ratiu <cratiu@nvidia.com>
+
+[ 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 <liuhangbin@gmail.com>
+Signed-off-by: Cosmin Ratiu <cratiu@nvidia.com>
+Acked-by: Jay Vosburgh <jv@jvosburgh.net>
+Link: https://patch.msgid.link/20250127104147.759658-1-cratiu@nvidia.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Stable-dep-of: 950803f72547 ("bonding: fix type confusion in bond_setup_by_slave()")
+Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From e5a61acb96184788c8c34c97709073db1dfb69fc Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 8 Jul 2026 17:47:06 +0800
+Subject: bonding: Fix initial {vlan,mpls}_feature set in bond_compute_features
+
+From: Daniel Borkmann <daniel@iogearbox.net>
+
+[ 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 <daniel@iogearbox.net>
+Cc: Nikolay Aleksandrov <razor@blackwall.org>
+Cc: Ido Schimmel <idosch@idosch.org>
+Cc: Jiri Pirko <jiri@nvidia.com>
+Reviewed-by: Nikolay Aleksandrov <razor@blackwall.org>
+Reviewed-by: Hangbin Liu <liuhangbin@gmail.com>
+Link: https://patch.msgid.link/20241210141245.327886-2-daniel@iogearbox.net
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From f4d1907efe2a511e1f9b65987c5037d12594ce80 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 8 Jul 2026 17:47:19 +0800
+Subject: bonding: fix type confusion in bond_setup_by_slave()
+
+From: Jiayuan Chen <jiayuan.chen@shopee.com>
+
+[ 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:
+ <TASK>
+ 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 <jv@jvosburgh.net>
+Reviewed-by: Eric Dumazet <edumazet@google.com>
+Signed-off-by: Jiayuan Chen <jiayuan.chen@shopee.com>
+Link: https://patch.msgid.link/20260306021508.222062-1-jiayuan.chen@linux.dev
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 24c4923bd46d77df84b5d67da010e135fdd33bf0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 8 Jul 2026 17:47:17 +0800
+Subject: bonding: fix xfrm offload feature setup on active-backup mode
+
+From: Hangbin Liu <liuhangbin@gmail.com>
+
+[ 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 <liuhangbin@gmail.com>
+Link: https://patch.msgid.link/20250925023304.472186-1-liuhangbin@gmail.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 685145376618de2a2f7f47cb66119d55e8b39a53 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 8 Jul 2026 17:47:21 +0800
+Subject: bonding: prevent potential infinite loop in bond_header_parse()
+
+From: Eric Dumazet <edumazet@google.com>
+
+[ 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 <edumazet@google.com>
+Reviewed-by: Jiayuan Chen <jiayuan.chen@shopee.com>
+Tested-by: Jiayuan Chen <jiayuan.chen@shopee.com>
+Cc: Jay Vosburgh <jv@jvosburgh.net>
+Cc: Andrew Lunn <andrew+netdev@lunn.ch>
+Link: https://patch.msgid.link/20260315104152.1436867-1-edumazet@google.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+[backport to 6.6, also fix function for wireless/{cisco/hostap}]
+Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From da6240a620a80cc06bbc924263952938ce8355eb Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 8 Jul 2026 17:47:15 +0800
+Subject: bonding: use common function to compute the features
+
+From: Hangbin Liu <liuhangbin@gmail.com>
+
+[ 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 <liuhangbin@gmail.com>
+Reviewed-by: Sabrina Dubroca <sd@queasysnail.net>
+Reviewed-by: Jiri Pirko <jiri@nvidia.com>
+Link: https://patch.msgid.link/20251017034155.61990-3-liuhangbin@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Stable-dep-of: 950803f72547 ("bonding: fix type confusion in bond_setup_by_slave()")
+Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From e43878f5e8210161803bef7a85475f554a84d771 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 8 Jul 2026 12:12:36 +0800
+Subject: mm/vmscan: flush deferred TLB before freeing large folios
+
+From: Jiayuan Chen <jiayuan.chen@shopee.com>
+
+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 <yingfu.zhou@shopee.com>
+Fixes: bd4c82c22c36 ("mm, THP, swap: delay splitting THP after swapped out")
+Cc: Jiayuan Chen <jiayuan.chen@linux.dev>
+Signed-off-by: Jiayuan Chen <jiayuan.chen@shopee.com>
+Reviewed-by: Matthew Wilcox <willy@infradead.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From e55b7c4ebfcdbc5ce81f09961a1e15787bbc2c14 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 8 Jul 2026 17:47:12 +0800
+Subject: net: add a common function to compute features for upper devices
+
+From: Hangbin Liu <liuhangbin@gmail.com>
+
+[ 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 <pabeni@redhat.com>
+Signed-off-by: Hangbin Liu <liuhangbin@gmail.com>
+Reviewed-by: Sabrina Dubroca <sd@queasysnail.net>
+Reviewed-by: Jiri Pirko <jiri@nvidia.com>
+Link: https://patch.msgid.link/20251017034155.61990-2-liuhangbin@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Stable-dep-of: 950803f72547 ("bonding: fix type confusion in bond_setup_by_slave()")
+Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 7fab5b63b63afe030227cc9843d5b32eb6c0d520 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 8 Jul 2026 17:47:04 +0800
+Subject: net, team, bonding: Add netdev_base_features helper
+
+From: Daniel Borkmann <daniel@iogearbox.net>
+
+[ 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 <daniel@iogearbox.net>
+Cc: Nikolay Aleksandrov <razor@blackwall.org>
+Cc: Ido Schimmel <idosch@idosch.org>
+Cc: Jiri Pirko <jiri@nvidia.com>
+Reviewed-by: Hangbin Liu <liuhangbin@gmail.com>
+Reviewed-by: Nikolay Aleksandrov <razor@blackwall.org>
+Link: https://patch.msgid.link/20241210141245.327886-1-daniel@iogearbox.net
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
+Stable-dep-of: d064ea7fe2a2 ("bonding: Fix initial {vlan,mpls}_feature set in bond_compute_features")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From cc0b38eb2673f47bff9723b126f40b88dd040ccc Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 8 Jul 2026 17:47:01 +0800
+Subject: net: team: rename team to team_core for linking
+
+From: Hangbin Liu <liuhangbin@gmail.com>
+
+[ 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 <liuhangbin@gmail.com>
+Reviewed-by: Jiri Pirko <jiri@nvidia.com>
+Link: https://lore.kernel.org/r/20240401031004.1159713-3-liuhangbin@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
+Stable-dep-of: 425000dbf173 ("team: fix header_ops type confusion with non-Ethernet ports")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
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
--- /dev/null
+From c443cc640515cca0d1851781bf1d357d0548f754 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 8 Jul 2026 17:47:23 +0800
+Subject: team: fix header_ops type confusion with non-Ethernet ports
+
+From: Jiayuan Chen <jiayuan.chen@shopee.com>
+
+[ 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 <jiayuan.chen@linux.dev>
+Signed-off-by: Jiayuan Chen <jiayuan.chen@shopee.com>
+Link: https://patch.msgid.link/20260320072139.134249-2-jiayuan.chen@linux.dev
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From e80a583c0fd7442b733acf6aed9aeb9425579a7b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 22 Mar 2025 17:34:13 +0300
+Subject: xfs: dump the recovered xattri log item if corruption happens
+
+From: Darrick J. Wong <djwong@kernel.org>
+
+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 <djwong@kernel.org>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Signed-off-by: Fedor Pchelkin <pchelkin@ispras.ru>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From f7e8d778bd40dda8a6e93b2c9bb5101ea37213ff Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 22 Mar 2025 17:34:15 +0300
+Subject: xfs: move ->iop_recover to xfs_defer_op_type
+
+From: Darrick J. Wong <djwong@kernel.org>
+
+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 <djwong@kernel.org>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Signed-off-by: Fedor Pchelkin <pchelkin@ispras.ru>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 27e97ff9539f126816c99bde38abcc4bf04e6a1b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 22 Mar 2025 17:34:12 +0300
+Subject: xfs: recreate work items when recovering intent items
+
+From: Darrick J. Wong <djwong@kernel.org>
+
+commit e70fb328d5277297ea2d9169a3a046de6412d777 upstream.
+
+Recreate work items for each xfs_defer_pending object when we are
+recovering intent items.
+
+Signed-off-by: Darrick J. Wong <djwong@kernel.org>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+[ 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 <pchelkin@ispras.ru>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From f2d03bffca2aa7121db80dad9c916dba9a9672e9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <djwong@kernel.org>
+
+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 <djwong@kernel.org>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Signed-off-by: Fedor Pchelkin <pchelkin@ispras.ru>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+