From: Greg Kroah-Hartman Date: Tue, 7 Jul 2026 14:36:22 +0000 (+0200) Subject: 6.12-stable patches X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=040ee55251bd76357db17d18d54a543b662bde43;p=thirdparty%2Fkernel%2Fstable-queue.git 6.12-stable patches added patches: apparmor-advertise-the-tcp-fast-open-fix-is-applied.patch device-property-initialize-the-remaining-fields-of-fwnode_handle-in-fwnode_init.patch f2fs-atomic-fix-uaf-issue-on-f2fs_inode_info.atomic_inode.patch f2fs-bound-i_inline_xattr_size-for-non-inline-xattr-inodes.patch f2fs-fix-listxattr-handling-of-corrupted-xattr-entries.patch f2fs-fix-potential-deadlock-in-f2fs_balance_fs.patch f2fs-fix-potential-deadlock-in-gc_merge-path-of-f2fs_balance_fs.patch f2fs-validate-orphan-inode-entry-count.patch fbdev-fbcon-fix-out-of-bounds-read-in-err_out-of-fbcon_do_set_font.patch locking-rtmutex-make-sure-we-wake-anything-on-the-wake_q-when-we-release-the-lock-wait_lock.patch nfs_common-rename-functions-that-invalidate-localio-nfs_clients.patch nfsd-add-nfsd_file_-get-put-to-nfs_to-nfsd_localio_operations.patch nfsv4-flexfiles-add-data-structure-support-for-striped-layouts.patch nfsv4-flexfiles-reject-zero-filehandle-version-count.patch nfsv4-flexfiles-remove-cred-local-variable-dependency.patch --- diff --git a/queue-6.12/apparmor-advertise-the-tcp-fast-open-fix-is-applied.patch b/queue-6.12/apparmor-advertise-the-tcp-fast-open-fix-is-applied.patch new file mode 100644 index 0000000000..adfe0dc188 --- /dev/null +++ b/queue-6.12/apparmor-advertise-the-tcp-fast-open-fix-is-applied.patch @@ -0,0 +1,36 @@ +From stable+bounces-272222-greg=kroah.com@vger.kernel.org Mon Jul 6 16:57:59 2026 +From: Sasha Levin +Date: Mon, 6 Jul 2026 09:17:08 -0400 +Subject: apparmor: advertise the tcp fast open fix is applied +To: stable@vger.kernel.org +Cc: John Johansen , Sasha Levin +Message-ID: <20260706131708.2289810-1-sashal@kernel.org> + +From: John Johansen + +[ Upstream commit 2f6701a5ce6257ae7a64ddc6d89d0a08d2a034f8 ] + +The fix for tcp-fast-open ensures that the connect permission is being +mediated correctly but it didn't add an artifact to the feature set to +advertise the fix is available. Add an artifact so that the test suite +can identify if the fix has not been properly applied or a new +unexpected regression has occurred. + +Fixes: 4d587cd8a7215 ("apparmor: mediate the implicit connect of TCP fast open sendmsg") +Signed-off-by: John Johansen +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + security/apparmor/net.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/security/apparmor/net.c ++++ b/security/apparmor/net.c +@@ -21,6 +21,7 @@ + + struct aa_sfs_entry aa_sfs_entry_network[] = { + AA_SFS_FILE_STRING("af_mask", AA_SFS_AF_MASK), ++ AA_SFS_FILE_BOOLEAN("tcp-fast-open", 1), + { } + }; + diff --git a/queue-6.12/device-property-initialize-the-remaining-fields-of-fwnode_handle-in-fwnode_init.patch b/queue-6.12/device-property-initialize-the-remaining-fields-of-fwnode_handle-in-fwnode_init.patch new file mode 100644 index 0000000000..29ee4d9747 --- /dev/null +++ b/queue-6.12/device-property-initialize-the-remaining-fields-of-fwnode_handle-in-fwnode_init.patch @@ -0,0 +1,49 @@ +From stable+bounces-271576-greg=kroah.com@vger.kernel.org Fri Jul 3 00:23:04 2026 +From: Sasha Levin +Date: Thu, 2 Jul 2026 18:22:52 -0400 +Subject: device property: initialize the remaining fields of fwnode_handle in fwnode_init() +To: stable@vger.kernel.org +Cc: Bartosz Golaszewski , Sakari Ailus , "Rafael J. Wysocki (Intel)" , Andy Shevchenko , Danilo Krummrich , Sasha Levin +Message-ID: <20260702222252.3701173-1-sashal@kernel.org> + +From: Bartosz Golaszewski + +[ Upstream commit 7eba000621fff223dd7bab484d48918c7c77a307 ] + +If a firmware node is allocated on the stack (for instance: temporary +software node whose life-time we control) or on the heap - but using a +non-zeroing allocation function - and initialized using fwnode_init(), +its secondary pointer will contain uninitialized memory which likely +will be neither NULL nor IS_ERR() and so may end up being dereferenced +(for example: in dev_to_swnode()). Set fwnode->secondary to NULL on +initialization. While at it: initialize the remaining fields of struct +fwnode_handle too just to be sure. + +Cc: stable@vger.kernel.org +Fixes: 01bb86b380a3 ("driver core: Add fwnode_init()") +Reviewed-by: Sakari Ailus +Reviewed-by: Rafael J. Wysocki (Intel) +Reviewed-by: Andy Shevchenko +Signed-off-by: Bartosz Golaszewski +Link: https://patch.msgid.link/20260511074927.9473-1-bartosz.golaszewski@oss.qualcomm.com +[ Fix typo in commit message. - Danilo ] +Signed-off-by: Danilo Krummrich +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + include/linux/fwnode.h | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/include/linux/fwnode.h ++++ b/include/linux/fwnode.h +@@ -202,8 +202,10 @@ static inline void fwnode_init(struct fw + { + fwnode->secondary = NULL; + fwnode->ops = ops; ++ fwnode->dev = NULL; + INIT_LIST_HEAD(&fwnode->consumers); + INIT_LIST_HEAD(&fwnode->suppliers); ++ fwnode->flags = 0; + } + + static inline void fwnode_set_flag(struct fwnode_handle *fwnode, diff --git a/queue-6.12/f2fs-atomic-fix-uaf-issue-on-f2fs_inode_info.atomic_inode.patch b/queue-6.12/f2fs-atomic-fix-uaf-issue-on-f2fs_inode_info.atomic_inode.patch new file mode 100644 index 0000000000..0569454780 --- /dev/null +++ b/queue-6.12/f2fs-atomic-fix-uaf-issue-on-f2fs_inode_info.atomic_inode.patch @@ -0,0 +1,168 @@ +From stable+bounces-271775-greg=kroah.com@vger.kernel.org Fri Jul 3 15:33:23 2026 +From: Sasha Levin +Date: Fri, 3 Jul 2026 09:23:42 -0400 +Subject: f2fs: atomic: fix UAF issue on f2fs_inode_info.atomic_inode +To: stable@vger.kernel.org +Cc: Chao Yu , stable@kernel.org, Daeho Jeong , Sunmin Jeong , Jaegeuk Kim , Sasha Levin +Message-ID: <20260703132342.4101156-1-sashal@kernel.org> + +From: Chao Yu + +[ Upstream commit e0288584baa5dc41df4a829a023c4c1b33fe53d7 ] + +- ioctl(F2FS_IOC_GARBAGE_COLLECT_RANGE) - shrink + - f2fs_gc + - gc_data_segment + - ra_data_block(cow_inode) + - mapping = F2FS_I(inode)->atomic_inode->i_mapping + : f2fs_is_cow_file(cow_inode) is true + - f2fs_evict_inode(atomic_inode) + - clear_inode_flag(fi->cow_inode, FI_COW_FILE) + - F2FS_I(fi->cow_inode)->atomic_inode = NULL + ... + - truncate_inode_pages_final(atomic_inode) + - f2fs_grab_cache_folio(mapping) + : create folio in atomic_inode->mapping + - clear_inode(atomic_inode) + - BUG_ON(atomic_inode->i_data.nrpages) + +We need to add a reference on fi->atomic_inode before using its mapping +field during garbage collection, otherwise, it will cause UAF issue. + +Cc: stable@kernel.org +Cc: Daeho Jeong +Cc: Sunmin Jeong +Fixes: 3db1de0e582c ("f2fs: change the current atomic write way") +Fixes: f18d00769336 ("f2fs: use meta inode for GC of COW file") +Signed-off-by: Chao Yu +Signed-off-by: Jaegeuk Kim +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/f2fs/gc.c | 50 ++++++++++++++++++++++++++++++++++++++++++-------- + fs/f2fs/inode.c | 11 ++++++++--- + 2 files changed, 50 insertions(+), 11 deletions(-) + +--- a/fs/f2fs/gc.c ++++ b/fs/f2fs/gc.c +@@ -1213,8 +1213,8 @@ static bool is_alive(struct f2fs_sb_info + static int ra_data_block(struct inode *inode, pgoff_t index) + { + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); +- struct address_space *mapping = f2fs_is_cow_file(inode) ? +- F2FS_I(inode)->atomic_inode->i_mapping : inode->i_mapping; ++ struct address_space *mapping = inode->i_mapping; ++ struct inode *atomic_inode = NULL; + struct dnode_of_data dn; + struct page *page; + struct f2fs_io_info fio = { +@@ -1229,9 +1229,22 @@ static int ra_data_block(struct inode *i + }; + int err; + ++ f2fs_down_read(&F2FS_I(inode)->i_sem); ++ if (f2fs_is_cow_file(inode)) { ++ atomic_inode = igrab(F2FS_I(inode)->atomic_inode); ++ if (!atomic_inode) { ++ f2fs_up_read(&F2FS_I(inode)->i_sem); ++ return -EBUSY; ++ } ++ mapping = atomic_inode->i_mapping; ++ } ++ f2fs_up_read(&F2FS_I(inode)->i_sem); ++ + page = f2fs_grab_cache_page(mapping, index, true); +- if (!page) +- return -ENOMEM; ++ if (!page) { ++ err = -ENOMEM; ++ goto out_iput; ++ } + + if (f2fs_lookup_read_extent_cache_block(inode, index, + &dn.data_blkaddr)) { +@@ -1288,11 +1301,16 @@ got_it: + f2fs_update_iostat(sbi, inode, FS_DATA_READ_IO, F2FS_BLKSIZE); + f2fs_update_iostat(sbi, NULL, FS_GDATA_READ_IO, F2FS_BLKSIZE); + ++ if (atomic_inode) ++ iput(atomic_inode); + return 0; + put_encrypted_page: + f2fs_put_page(fio.encrypted_page, 1); + put_page: + f2fs_put_page(page, 1); ++out_iput: ++ if (atomic_inode) ++ iput(atomic_inode); + return err; + } + +@@ -1303,8 +1321,8 @@ put_page: + static int move_data_block(struct inode *inode, block_t bidx, + int gc_type, unsigned int segno, int off) + { +- struct address_space *mapping = f2fs_is_cow_file(inode) ? +- F2FS_I(inode)->atomic_inode->i_mapping : inode->i_mapping; ++ struct address_space *mapping = inode->i_mapping; ++ struct inode *atomic_inode = NULL; + struct f2fs_io_info fio = { + .sbi = F2FS_I_SB(inode), + .ino = inode->i_ino, +@@ -1326,10 +1344,23 @@ static int move_data_block(struct inode + (fio.sbi->gc_mode != GC_URGENT_HIGH) ? + CURSEG_ALL_DATA_ATGC : CURSEG_COLD_DATA; + ++ f2fs_down_read(&F2FS_I(inode)->i_sem); ++ if (f2fs_is_cow_file(inode)) { ++ atomic_inode = igrab(F2FS_I(inode)->atomic_inode); ++ if (!atomic_inode) { ++ f2fs_up_read(&F2FS_I(inode)->i_sem); ++ return -EBUSY; ++ } ++ mapping = atomic_inode->i_mapping; ++ } ++ f2fs_up_read(&F2FS_I(inode)->i_sem); ++ + /* do not read out */ + page = f2fs_grab_cache_page(mapping, bidx, false); +- if (!page) +- return -ENOMEM; ++ if (!page) { ++ err = -ENOMEM; ++ goto out_iput; ++ } + + if (!check_valid_map(F2FS_I_SB(inode), segno, off)) { + err = -ENOENT; +@@ -1456,6 +1487,9 @@ put_out: + f2fs_put_dnode(&dn); + out: + f2fs_put_page(page, 1); ++out_iput: ++ if (atomic_inode) ++ iput(atomic_inode); + return err; + } + +--- a/fs/f2fs/inode.c ++++ b/fs/f2fs/inode.c +@@ -839,10 +839,15 @@ void f2fs_evict_inode(struct inode *inod + f2fs_abort_atomic_write(inode, true); + + if (fi->cow_inode && f2fs_is_cow_file(fi->cow_inode)) { +- clear_inode_flag(fi->cow_inode, FI_COW_FILE); +- F2FS_I(fi->cow_inode)->atomic_inode = NULL; +- iput(fi->cow_inode); ++ struct inode *cow_inode = fi->cow_inode; ++ ++ f2fs_down_write(&F2FS_I(cow_inode)->i_sem); ++ clear_inode_flag(cow_inode, FI_COW_FILE); ++ F2FS_I(cow_inode)->atomic_inode = NULL; + fi->cow_inode = NULL; ++ f2fs_up_write(&F2FS_I(cow_inode)->i_sem); ++ ++ iput(cow_inode); + } + + trace_f2fs_evict_inode(inode); diff --git a/queue-6.12/f2fs-bound-i_inline_xattr_size-for-non-inline-xattr-inodes.patch b/queue-6.12/f2fs-bound-i_inline_xattr_size-for-non-inline-xattr-inodes.patch new file mode 100644 index 0000000000..b57c7e2acd --- /dev/null +++ b/queue-6.12/f2fs-bound-i_inline_xattr_size-for-non-inline-xattr-inodes.patch @@ -0,0 +1,96 @@ +From stable+bounces-271805-greg=kroah.com@vger.kernel.org Fri Jul 3 16:35:31 2026 +From: Sasha Levin +Date: Fri, 3 Jul 2026 10:26:35 -0400 +Subject: f2fs: bound i_inline_xattr_size for non-inline-xattr inodes +To: stable@vger.kernel.org +Cc: Bryam Vargas , Chao Yu , Jaegeuk Kim , Sasha Levin +Message-ID: <20260703142635.55675-1-sashal@kernel.org> + +From: Bryam Vargas + +[ Upstream commit 378acf3cf19b6af6cba55e8dd1154c4e1504bae8 ] + +When the flexible_inline_xattr feature is enabled, do_read_inode() loads +the on-disk i_inline_xattr_size unconditionally: + + if (f2fs_sb_has_flexible_inline_xattr(sbi)) + fi->i_inline_xattr_size = le16_to_cpu(ri->i_inline_xattr_size); + +but sanity_check_inode() only range-checks it when the inode also has the +FI_INLINE_XATTR flag set. An inode that carries an inline dentry or inline +data but not FI_INLINE_XATTR -- the normal layout for an inline +directory -- therefore keeps a fully attacker-controlled +i_inline_xattr_size from a crafted image. + +get_inline_xattr_addrs() returns that value with no flag gating, so it +feeds the inode geometry: + + MAX_INLINE_DATA() = 4 * (CUR_ADDRS_PER_INODE - i_inline_xattr_size - 1) + NR_INLINE_DENTRY() = MAX_INLINE_DATA() * BITS_PER_BYTE / (...) + addrs_per_page() = CUR_ADDRS_PER_INODE - i_inline_xattr_size + +A large i_inline_xattr_size drives MAX_INLINE_DATA() and NR_INLINE_DENTRY() +negative, so make_dentry_ptr_inline() sets d->max (int) to a negative +value. The inline directory walk then compares an unsigned long bit_pos +against that negative d->max, which is promoted to a huge unsigned bound, +and reads far past the inline area: + + while (bit_pos < d->max) /* fs/f2fs/dir.c */ + ... test_bit_le(bit_pos, d->bitmap) / d->dentry[bit_pos] ... + +Mounting a crafted image and reading such a directory triggers an +out-of-bounds read in f2fs_fill_dentries(); the same underflow also +corrupts ADDRS_PER_INODE for regular files. + +Validate i_inline_xattr_size against MAX_INLINE_XATTR_SIZE whenever the +flexible_inline_xattr feature is enabled -- i.e. whenever the value is +loaded from disk and consumed -- and keep the lower MIN_INLINE_XATTR_SIZE +bound gated on inodes that actually carry an inline xattr, so legitimate +inodes with i_inline_xattr_size == 0 are still accepted. + +Cc: stable@vger.kernel.org +Fixes: 6afc662e68b5 ("f2fs: support flexible inline xattr size") +Signed-off-by: Bryam Vargas +Reviewed-by: Chao Yu +Signed-off-by: Jaegeuk Kim +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/f2fs/inode.c | 19 ++++++++++--------- + 1 file changed, 10 insertions(+), 9 deletions(-) + +--- a/fs/f2fs/inode.c ++++ b/fs/f2fs/inode.c +@@ -308,15 +308,6 @@ static bool sanity_check_inode(struct in + F2FS_TOTAL_EXTRA_ATTR_SIZE); + return false; + } +- if (f2fs_sb_has_flexible_inline_xattr(sbi) && +- f2fs_has_inline_xattr(inode) && +- (!fi->i_inline_xattr_size || +- fi->i_inline_xattr_size > MAX_INLINE_XATTR_SIZE)) { +- f2fs_warn(sbi, "%s: inode (ino=%lx) has corrupted i_inline_xattr_size: %d, max: %lu", +- __func__, inode->i_ino, fi->i_inline_xattr_size, +- MAX_INLINE_XATTR_SIZE); +- return false; +- } + if (f2fs_sb_has_compression(sbi) && + fi->i_flags & F2FS_COMPR_FL && + F2FS_FITS_IN_INODE(ri, fi->i_extra_isize, +@@ -326,6 +317,16 @@ static bool sanity_check_inode(struct in + } + } + ++ if (f2fs_sb_has_flexible_inline_xattr(sbi) && ++ (fi->i_inline_xattr_size > MAX_INLINE_XATTR_SIZE || ++ (f2fs_has_inline_xattr(inode) && ++ fi->i_inline_xattr_size < MIN_INLINE_XATTR_SIZE))) { ++ f2fs_warn(sbi, "%s: inode (ino=%lx) has corrupted i_inline_xattr_size: %d, min: %zu, max: %lu", ++ __func__, inode->i_ino, fi->i_inline_xattr_size, ++ MIN_INLINE_XATTR_SIZE, MAX_INLINE_XATTR_SIZE); ++ return false; ++ } ++ + if (!f2fs_sb_has_extra_attr(sbi)) { + if (f2fs_sb_has_project_quota(sbi)) { + f2fs_warn(sbi, "%s: corrupted inode ino=%lx, wrong feature flag: %u, run fsck to fix.", diff --git a/queue-6.12/f2fs-fix-listxattr-handling-of-corrupted-xattr-entries.patch b/queue-6.12/f2fs-fix-listxattr-handling-of-corrupted-xattr-entries.patch new file mode 100644 index 0000000000..71fe3fc0e1 --- /dev/null +++ b/queue-6.12/f2fs-fix-listxattr-handling-of-corrupted-xattr-entries.patch @@ -0,0 +1,51 @@ +From stable+bounces-271856-greg=kroah.com@vger.kernel.org Fri Jul 3 20:50:22 2026 +From: Sasha Levin +Date: Fri, 3 Jul 2026 14:50:15 -0400 +Subject: f2fs: fix listxattr handling of corrupted xattr entries +To: stable@vger.kernel.org +Cc: Keshav Verma , stable@kernel.org, Chao Yu , Jaegeuk Kim , Sasha Levin +Message-ID: <20260703185015.285028-1-sashal@kernel.org> + +From: Keshav Verma + +[ Upstream commit 5ef5bc304f23c3fe255d4936472378dcb74d0e94 ] + +Validate the xattr entry before reading its fields in f2fs_listxattr(). +Return -EFSCORRUPTED when the entry is outside the valid xattr storage +area instead of returning a successful partial result. + +Fixes: 688078e7f36c ("f2fs: fix to avoid memory leakage in f2fs_listxattr") +Cc: stable@kernel.org +Reviewed-by: Chao Yu +Signed-off-by: Keshav Verma +Signed-off-by: Jaegeuk Kim +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/f2fs/xattr.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +--- a/fs/f2fs/xattr.c ++++ b/fs/f2fs/xattr.c +@@ -581,8 +581,6 @@ ssize_t f2fs_listxattr(struct dentry *de + size_t prefix_len; + size_t size; + +- prefix = f2fs_xattr_prefix(entry->e_name_index, dentry); +- + if ((void *)(entry) + sizeof(__u32) > last_base_addr || + (void *)XATTR_NEXT_ENTRY(entry) > last_base_addr) { + f2fs_err(F2FS_I_SB(inode), "list inode (%lu) has corrupted xattr", +@@ -590,9 +588,11 @@ ssize_t f2fs_listxattr(struct dentry *de + set_sbi_flag(F2FS_I_SB(inode), SBI_NEED_FSCK); + f2fs_handle_error(F2FS_I_SB(inode), + ERROR_CORRUPTED_XATTR); +- break; ++ error = -EFSCORRUPTED; ++ goto cleanup; + } + ++ prefix = f2fs_xattr_prefix(entry->e_name_index, dentry); + if (!prefix) + continue; + diff --git a/queue-6.12/f2fs-fix-potential-deadlock-in-f2fs_balance_fs.patch b/queue-6.12/f2fs-fix-potential-deadlock-in-f2fs_balance_fs.patch new file mode 100644 index 0000000000..512930ee52 --- /dev/null +++ b/queue-6.12/f2fs-fix-potential-deadlock-in-f2fs_balance_fs.patch @@ -0,0 +1,253 @@ +From stable+bounces-271846-greg=kroah.com@vger.kernel.org Fri Jul 3 20:32:05 2026 +From: Sasha Levin +Date: Fri, 3 Jul 2026 14:24:12 -0400 +Subject: f2fs: fix potential deadlock in f2fs_balance_fs() +To: stable@vger.kernel.org +Cc: Ruipeng Qi , Chao Yu , Jaegeuk Kim , Sasha Levin +Message-ID: <20260703182413.254727-1-sashal@kernel.org> + +From: Ruipeng Qi + +[ Upstream commit dd3114870771562036fdcf5abe813956f36d224d ] + +When the f2fs filesystem space is nearly exhausted, we encounter deadlock +issues as below: + +INFO: task A:1890 blocked for more than 120 seconds. + Tainted: G O 6.12.41-g3fe07ddf05ab #1 +"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. +task:A state:D stack:0 pid:1890 tgid:1626 ppid:1153 flags:0x00000204 +Call trace: + __switch_to+0xf4/0x158 + __schedule+0x27c/0x908 + schedule+0x3c/0x118 + io_schedule+0x44/0x68 + folio_wait_bit_common+0x174/0x370 + folio_wait_bit+0x20/0x38 + folio_wait_writeback+0x54/0xc8 + truncate_inode_partial_folio+0x70/0x1e0 + truncate_inode_pages_range+0x1b0/0x450 + truncate_pagecache+0x54/0x88 + f2fs_file_write_iter+0x3e8/0xb80 + do_iter_readv_writev+0xf0/0x1e0 + vfs_writev+0x138/0x2c8 + do_writev+0x88/0x130 + __arm64_sys_writev+0x28/0x40 + invoke_syscall+0x50/0x120 + el0_svc_common.constprop.0+0xc8/0xf0 + do_el0_svc+0x24/0x38 + el0_svc+0x30/0xf8 + el0t_64_sync_handler+0x120/0x130 + el0t_64_sync+0x190/0x198 + +INFO: task kworker/u8:11:2680853 blocked for more than 120 seconds. + Tainted: G O 6.12.41-g3fe07ddf05ab #1 +"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. +task:kworker/u8:11 state:D stack:0 pid:2680853 tgid:2680853 ppid:2 flags:0x00000208 +Workqueue: writeback wb_workfn (flush-254:0) +Call trace: + __switch_to+0xf4/0x158 + __schedule+0x27c/0x908 + schedule+0x3c/0x118 + io_schedule+0x44/0x68 + folio_wait_bit_common+0x174/0x370 + __filemap_get_folio+0x214/0x348 + pagecache_get_page+0x20/0x70 + f2fs_get_read_data_page+0x150/0x3e8 + f2fs_get_lock_data_page+0x2c/0x160 + move_data_page+0x50/0x478 + do_garbage_collect+0xd38/0x1528 + f2fs_gc+0x240/0x7e0 + f2fs_balance_fs+0x1a0/0x208 + f2fs_write_single_data_page+0x6e4/0x730 + f2fs_write_cache_pages+0x378/0x9b0 + f2fs_write_data_pages+0x2e4/0x388 + do_writepages+0x8c/0x2c8 + __writeback_single_inode+0x4c/0x498 + writeback_sb_inodes+0x234/0x4a8 + __writeback_inodes_wb+0x58/0x118 + wb_writeback+0x2f8/0x3c0 + wb_workfn+0x2c4/0x508 + process_one_work+0x180/0x408 + worker_thread+0x258/0x368 + kthread+0x118/0x128 + ret_from_fork+0x10/0x200 + +INFO: task kworker/u8:8:2641297 blocked for more than 120 seconds. + Tainted: G O 6.12.41-g3fe07ddf05ab #1 +"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. +task:kworker/u8:8 state:D stack:0 pid:2641297 tgid:2641297 ppid:2 flags:0x00000208 +Workqueue: writeback wb_workfn (flush-254:0) +Call trace: + __switch_to+0xf4/0x158 + __schedule+0x27c/0x908 + rt_mutex_schedule+0x30/0x60 + __rt_mutex_slowlock_locked.constprop.0+0x460/0x8a8 + rwbase_write_lock+0x24c/0x378 + down_write+0x1c/0x30 + f2fs_balance_fs+0x184/0x208 + f2fs_write_inode+0xf4/0x328 + __writeback_single_inode+0x370/0x498 + writeback_sb_inodes+0x234/0x4a8 + __writeback_inodes_wb+0x58/0x118 + wb_writeback+0x2f8/0x3c0 + wb_workfn+0x2c4/0x508 + process_one_work+0x180/0x408 + worker_thread+0x258/0x368 + kthread+0x118/0x128 + ret_from_fork+0x10/0x20 + +INFO: task B:1902 blocked for more than 120 seconds. + Tainted: G O 6.12.41-g3fe07ddf05ab #1 +"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. +task:B state:D stack:0 pid:1902 tgid:1626 ppid:1153 flags:0x0000020c +Call trace: + __switch_to+0xf4/0x158 + __schedule+0x27c/0x908 + rt_mutex_schedule+0x30/0x60 + __rt_mutex_slowlock_locked.constprop.0+0x460/0x8a8 + rwbase_write_lock+0x24c/0x378 + down_write+0x1c/0x30 + f2fs_balance_fs+0x184/0x208 + f2fs_map_blocks+0x94c/0x1110 + f2fs_file_write_iter+0x228/0xb80 + do_iter_readv_writev+0xf0/0x1e0 + vfs_writev+0x138/0x2c8 + do_writev+0x88/0x130 + __arm64_sys_writev+0x28/0x40 + invoke_syscall+0x50/0x120 + el0_svc_common.constprop.0+0xc8/0xf0 + do_el0_svc+0x24/0x38 + el0_svc+0x30/0xf8 + el0t_64_sync_handler+0x120/0x130 + el0t_64_sync+0x190/0x198 + +INFO: task sync:2769849 blocked for more than 120 seconds. + Tainted: G O 6.12.41-g3fe07ddf05ab #1 +"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. +task:sync state:D stack:0 pid:2769849 tgid:2769849 ppid:736 flags:0x0000020c +Call trace: + __switch_to+0xf4/0x158 + __schedule+0x27c/0x908 + schedule+0x3c/0x118 + wb_wait_for_completion+0xb0/0xe8 + sync_inodes_sb+0xc8/0x2b0 + sync_inodes_one_sb+0x24/0x38 + iterate_supers+0xa8/0x138 + ksys_sync+0x54/0xc8 + __arm64_sys_sync+0x18/0x30 + invoke_syscall+0x50/0x120 + el0_svc_common.constprop.0+0xc8/0xf0 + do_el0_svc+0x24/0x38 + el0_svc+0x30/0xf8 + el0t_64_sync_handler+0x120/0x130 + el0t_64_sync+0x190/0x198 + +The root cause is a potential deadlock between the following tasks: + +kworker/u8:11 Thread A +- f2fs_write_single_data_page + - f2fs_do_write_data_page + - folio_start_writeback(X) + - f2fs_outplace_write_data + - bio_add_folio(X) + - folio_unlock(X) + - truncate_inode_pages_range + - __filemap_get_folio(X, FGP_LOCK) + - truncate_inode_partial_folio(X) + - folio_wait_writeback(X) + - f2fs_balance_fs + - f2fs_gc + - do_garbage_collect + - move_data_page + - f2fs_get_lock_data_page + - __filemap_get_folio(X, FGP_LOCK) + +Both threads try to access folio X. Thread A holds the lock but waits +for writeback, while kworker waits for the lock. This causes a deadlock. + +Other threads also enter D state, waiting for locks such as gc_lock and +writepages. + +OPU/IPU DATA folio are all affected by this issue. To avoid such +potential deadlocks, always commit these cached folios before +triggering f2fs_gc() in f2fs_balance_fs(). + +Suggested-by: Chao Yu +Reviewed-by: Chao Yu +Signed-off-by: Ruipeng Qi +Signed-off-by: Jaegeuk Kim +Stable-dep-of: 8b4468ec023d ("f2fs: fix potential deadlock in gc_merge path of f2fs_balance_fs()") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/f2fs/data.c | 29 +++++++++++++++++++++++++++++ + fs/f2fs/f2fs.h | 1 + + fs/f2fs/segment.c | 8 ++++++++ + 3 files changed, 38 insertions(+) + +--- a/fs/f2fs/data.c ++++ b/fs/f2fs/data.c +@@ -890,6 +890,35 @@ void f2fs_submit_merged_ipu_write(struct + } + } + ++void f2fs_submit_all_merged_ipu_writes(struct f2fs_sb_info *sbi) ++{ ++ struct bio_entry *be, *tmp; ++ struct f2fs_bio_info *io; ++ enum temp_type temp; ++ ++ for (temp = HOT; temp < NR_TEMP_TYPE; temp++) { ++ LIST_HEAD(list); ++ ++ io = sbi->write_io[DATA] + temp; ++ ++ /* A lockless list_empty() check is safe here: any bios from ++ * other kworkers that we miss will be submitted by those ++ * kworkers accordingly. ++ */ ++ if (list_empty(&io->bio_list)) ++ continue; ++ ++ f2fs_down_write(&io->bio_list_lock); ++ list_splice_init(&io->bio_list, &list); ++ f2fs_up_write(&io->bio_list_lock); ++ ++ list_for_each_entry_safe(be, tmp, &list, list) { ++ f2fs_submit_write_bio(sbi, be->bio, DATA); ++ del_bio_entry(be); ++ } ++ } ++} ++ + int f2fs_merge_page_bio(struct f2fs_io_info *fio) + { + struct bio *bio = *fio->bio; +--- a/fs/f2fs/f2fs.h ++++ b/fs/f2fs/f2fs.h +@@ -3929,6 +3929,7 @@ void f2fs_submit_merged_write_cond(struc + nid_t ino, enum page_type type); + void f2fs_submit_merged_ipu_write(struct f2fs_sb_info *sbi, + struct bio **bio, struct page *page); ++void f2fs_submit_all_merged_ipu_writes(struct f2fs_sb_info *sbi); + void f2fs_flush_merged_writes(struct f2fs_sb_info *sbi); + int f2fs_submit_page_bio(struct f2fs_io_info *fio); + int f2fs_merge_page_bio(struct f2fs_io_info *fio); +--- a/fs/f2fs/segment.c ++++ b/fs/f2fs/segment.c +@@ -451,6 +451,14 @@ void f2fs_balance_fs(struct f2fs_sb_info + .should_migrate_blocks = false, + .err_gc_skipped = false, + .nr_free_secs = 1 }; ++ ++ /* ++ * Submit all cached OPU/IPU DATA bios before triggering ++ * foreground GC to avoid potential deadlocks. ++ */ ++ f2fs_submit_merged_write(sbi, DATA); ++ f2fs_submit_all_merged_ipu_writes(sbi); ++ + f2fs_down_write(&sbi->gc_lock); + stat_inc_gc_call_count(sbi, FOREGROUND); + f2fs_gc(sbi, &gc_control); diff --git a/queue-6.12/f2fs-fix-potential-deadlock-in-gc_merge-path-of-f2fs_balance_fs.patch b/queue-6.12/f2fs-fix-potential-deadlock-in-gc_merge-path-of-f2fs_balance_fs.patch new file mode 100644 index 0000000000..98e6fb2c60 --- /dev/null +++ b/queue-6.12/f2fs-fix-potential-deadlock-in-gc_merge-path-of-f2fs_balance_fs.patch @@ -0,0 +1,85 @@ +From stable+bounces-271847-greg=kroah.com@vger.kernel.org Fri Jul 3 20:27:31 2026 +From: Sasha Levin +Date: Fri, 3 Jul 2026 14:24:13 -0400 +Subject: f2fs: fix potential deadlock in gc_merge path of f2fs_balance_fs() +To: stable@vger.kernel.org +Cc: Chao Yu , stable@kernel.org, Ruipeng Qi , Chao Yu , Jaegeuk Kim , Sasha Levin +Message-ID: <20260703182413.254727-2-sashal@kernel.org> + +From: Chao Yu + +[ Upstream commit 8b4468ec023d0d1b4669dfb867588997cc03a06b ] + +When we mount device w/ gc_merge mount option, we may suffer below +potential deadlock: + +Kworker GC trehad Truncator +- f2fs_write_cache_pages + - f2fs_write_single_data_page + - f2fs_do_write_data_page + - folio_start_writeback --- set writeback flag on folio + - f2fs_outplace_write_data + : cached folio in internal bio cache + - f2fs_balance_fs + - wake_up(gc_thread) + : wake up gc thread to run foreground GC + - finish_wait(fggc_wq) + : wait on the waitqueue --- wait on GC thread to finish the work + - truncate_inode_pages_range + - __filemap_get_folio(, FGP_LOCK) --- lock folio + - truncate_inode_partial_folio + - folio_wait_writeback --- wait on writeback being cleared + - do_garbage_collect + - move_data_page + - f2fs_get_lock_data_folio + - lock on folio --- blocked on folio's lock + +In order to avoid such deadlock, let's call below functions to commit +cached bios in GC_MERGE path of f2fs_balance_fs() as the same as we did +in NOGC_MERGE path. +- f2fs_submit_merged_write(sbi, DATA); +- f2fs_submit_all_merged_ipu_writes(sbi); + +Cc: stable@kernel.org +Fixes: 351df4b20115 ("f2fs: add segment operations") +Cc: Ruipeng Qi +Reported: Sandeep Dhavale +Signed-off-by: Chao Yu +Signed-off-by: Chao Yu +Signed-off-by: Jaegeuk Kim +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/f2fs/segment.c | 14 +++++++------- + 1 file changed, 7 insertions(+), 7 deletions(-) + +--- a/fs/f2fs/segment.c ++++ b/fs/f2fs/segment.c +@@ -434,6 +434,13 @@ void f2fs_balance_fs(struct f2fs_sb_info + if (has_enough_free_secs(sbi, 0, 0)) + return; + ++ /* ++ * Submit all cached OPU/IPU DATA bios before triggering ++ * foreground GC to avoid potential deadlocks. ++ */ ++ f2fs_submit_merged_write(sbi, DATA); ++ f2fs_submit_all_merged_ipu_writes(sbi); ++ + if (test_opt(sbi, GC_MERGE) && sbi->gc_thread && + sbi->gc_thread->f2fs_gc_task) { + DEFINE_WAIT(wait); +@@ -452,13 +459,6 @@ void f2fs_balance_fs(struct f2fs_sb_info + .err_gc_skipped = false, + .nr_free_secs = 1 }; + +- /* +- * Submit all cached OPU/IPU DATA bios before triggering +- * foreground GC to avoid potential deadlocks. +- */ +- f2fs_submit_merged_write(sbi, DATA); +- f2fs_submit_all_merged_ipu_writes(sbi); +- + f2fs_down_write(&sbi->gc_lock); + stat_inc_gc_call_count(sbi, FOREGROUND); + f2fs_gc(sbi, &gc_control); diff --git a/queue-6.12/f2fs-validate-orphan-inode-entry-count.patch b/queue-6.12/f2fs-validate-orphan-inode-entry-count.patch new file mode 100644 index 0000000000..54a2e8f589 --- /dev/null +++ b/queue-6.12/f2fs-validate-orphan-inode-entry-count.patch @@ -0,0 +1,79 @@ +From stable+bounces-271761-greg=kroah.com@vger.kernel.org Fri Jul 3 14:42:14 2026 +From: Sasha Levin +Date: Fri, 3 Jul 2026 08:34:00 -0400 +Subject: f2fs: validate orphan inode entry count +To: stable@vger.kernel.org +Cc: Wenjie Qi , stable@kernel.org, Wenjie Qi , Chao Yu , Jaegeuk Kim , Sasha Levin +Message-ID: <20260703123400.3946185-1-sashal@kernel.org> + +From: Wenjie Qi + +[ Upstream commit 846c499a65816d13f1186e3090e825e8bb8bcb8b ] + +f2fs_recover_orphan_inodes() trusts the orphan block entry_count when +replaying orphan inodes from the checkpoint pack. A corrupted entry_count +larger than F2FS_ORPHANS_PER_BLOCK makes the recovery loop read past the +ino[] array and interpret footer or following data as inode numbers. + +On a crafted image, mounting an unpatched kernel can drive orphan recovery +into f2fs_bug_on() and panic the kernel. Validate entry_count before +consuming entries so corrupted checkpoint data fails the mount with +-EFSCORRUPTED and requests fsck instead. + +Set ERROR_INCONSISTENT_ORPHAN as well, so the corruption reason can be +recorded in the superblock s_errors[] field. This gives fsck a persistent +hint even though mount-time orphan recovery failure may leave no chance to +persist SBI_NEED_FSCK through a checkpoint. + +Cc: stable@kernel.org +Fixes: 127e670abfa7 ("f2fs: add checkpoint operations") +Signed-off-by: Wenjie Qi +Reviewed-by: Chao Yu +Signed-off-by: Jaegeuk Kim +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/f2fs/checkpoint.c | 14 +++++++++++++- + include/linux/f2fs_fs.h | 1 + + 2 files changed, 14 insertions(+), 1 deletion(-) + +--- a/fs/f2fs/checkpoint.c ++++ b/fs/f2fs/checkpoint.c +@@ -759,6 +759,7 @@ int f2fs_recover_orphan_inodes(struct f2 + for (i = 0; i < orphan_blocks; i++) { + struct page *page; + struct f2fs_orphan_block *orphan_blk; ++ unsigned int entry_count; + + page = f2fs_get_meta_page(sbi, start_blk + i); + if (IS_ERR(page)) { +@@ -767,7 +768,18 @@ int f2fs_recover_orphan_inodes(struct f2 + } + + orphan_blk = (struct f2fs_orphan_block *)page_address(page); +- for (j = 0; j < le32_to_cpu(orphan_blk->entry_count); j++) { ++ entry_count = le32_to_cpu(orphan_blk->entry_count); ++ if (entry_count > F2FS_ORPHANS_PER_BLOCK) { ++ f2fs_err(sbi, "invalid orphan inode entry count %u", ++ entry_count); ++ set_sbi_flag(sbi, SBI_NEED_FSCK); ++ f2fs_handle_error(sbi, ERROR_INCONSISTENT_ORPHAN); ++ err = -EFSCORRUPTED; ++ f2fs_put_page(page, 1); ++ goto out; ++ } ++ ++ for (j = 0; j < entry_count; j++) { + nid_t ino = le32_to_cpu(orphan_blk->ino[j]); + + err = recover_orphan_inode(sbi, ino); +--- a/include/linux/f2fs_fs.h ++++ b/include/linux/f2fs_fs.h +@@ -104,6 +104,7 @@ enum f2fs_error { + ERROR_CORRUPTED_XATTR, + ERROR_INVALID_NODE_REFERENCE, + ERROR_INCONSISTENT_NAT, ++ ERROR_INCONSISTENT_ORPHAN, + ERROR_MAX, + }; + diff --git a/queue-6.12/fbdev-fbcon-fix-out-of-bounds-read-in-err_out-of-fbcon_do_set_font.patch b/queue-6.12/fbdev-fbcon-fix-out-of-bounds-read-in-err_out-of-fbcon_do_set_font.patch new file mode 100644 index 0000000000..0a036e5d47 --- /dev/null +++ b/queue-6.12/fbdev-fbcon-fix-out-of-bounds-read-in-err_out-of-fbcon_do_set_font.patch @@ -0,0 +1,70 @@ +From stable+bounces-271995-greg=kroah.com@vger.kernel.org Sun Jul 5 05:11:47 2026 +From: Sasha Levin +Date: Sat, 4 Jul 2026 23:11:39 -0400 +Subject: fbdev: fbcon: fix out-of-bounds read in err_out of fbcon_do_set_font() +To: stable@vger.kernel.org +Cc: Mingyu Wang <25181214217@stu.xidian.edu.cn>, Thomas Zimmermann , Helge Deller , Sasha Levin +Message-ID: <20260705031139.1600541-1-sashal@kernel.org> + +From: Mingyu Wang <25181214217@stu.xidian.edu.cn> + +[ Upstream commit 8fdc8c2057eea08d40ce2c8eed41ff9e451c65c2 ] + +When fbcon_do_set_font() fails (e.g., due to a memory allocation failure +inside vc_resize() under heavy memory pressure), it jumps to the `err_out` +label to roll back the console state. However, the current rollback logic +forgets to restore the `hi_font` state, leading to a severe state machine +corruption. + +Earlier in the function, `set_vc_hi_font()` might be called to change +`vc->vc_hi_font_mask` and mutate the screen buffer. If `vc_resize()` +subsequently fails, the `err_out` path restores `vc_font.charcount` +but entirely skips rolling back the `vc_hi_font_mask` and the screen +buffer. + +This mismatch leaves the terminal in a desynchronized state. Because +`vc_hi_font_mask` remains set, the VT subsystem will still accept +character indices greater than 255 from userspace and write them to the +screen buffer. Subsequent rendering calls (e.g., `fbcon_putcs()`) will +then use these inflated indices to access the reverted, 256-character +font array, leading to a deterministic out-of-bounds read and potential +kernel memory disclosure. + +Fix this by adding the missing rollback logic for the `hi_font` mask +and screen buffer in the error path. + +Fixes: a5a923038d70 ("fbdev: fbcon: Properly revert changes when vc_resize() failed") +Cc: stable@vger.kernel.org +Signed-off-by: Mingyu Wang <25181214217@stu.xidian.edu.cn> +Reviewed-by: Thomas Zimmermann +Signed-off-by: Helge Deller +[ Adjust context ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/video/fbdev/core/fbcon.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +--- a/drivers/video/fbdev/core/fbcon.c ++++ b/drivers/video/fbdev/core/fbcon.c +@@ -2427,6 +2427,7 @@ static int fbcon_do_set_font(struct vc_d + struct fbcon_display *p = &fb_display[vc->vc_num]; + int resize, ret, old_userfont, old_width, old_height, old_charcount; + u8 *old_data = vc->vc_font.data; ++ unsigned short old_hi_font_mask = vc->vc_hi_font_mask; + + resize = (w != vc->vc_font.width) || (h != vc->vc_font.height); + vc->vc_font.data = (void *)(p->fontdata = data); +@@ -2480,6 +2481,12 @@ err_out: + vc->vc_font.height = old_height; + vc->vc_font.charcount = old_charcount; + ++ /* Restore the hi_font state and screen buffer */ ++ if (old_hi_font_mask && !vc->vc_hi_font_mask) ++ set_vc_hi_font(vc, true); ++ else if (!old_hi_font_mask && vc->vc_hi_font_mask) ++ set_vc_hi_font(vc, false); ++ + return ret; + } + diff --git a/queue-6.12/locking-rtmutex-make-sure-we-wake-anything-on-the-wake_q-when-we-release-the-lock-wait_lock.patch b/queue-6.12/locking-rtmutex-make-sure-we-wake-anything-on-the-wake_q-when-we-release-the-lock-wait_lock.patch new file mode 100644 index 0000000000..02e17e1429 --- /dev/null +++ b/queue-6.12/locking-rtmutex-make-sure-we-wake-anything-on-the-wake_q-when-we-release-the-lock-wait_lock.patch @@ -0,0 +1,105 @@ +From stable+bounces-272218-greg=kroah.com@vger.kernel.org Mon Jul 6 15:03:44 2026 +From: Sasha Levin +Date: Mon, 6 Jul 2026 08:50:05 -0400 +Subject: locking/rtmutex: Make sure we wake anything on the wake_q when we release the lock->wait_lock +To: stable@vger.kernel.org +Cc: John Stultz , Bert Karwatzki , "Peter Zijlstra (Intel)" , Sasha Levin +Message-ID: <20260706125005.2225064-1-sashal@kernel.org> + +From: John Stultz + +[ Upstream commit 4a077914578183ec397ad09f7156a357e00e5d72 ] + +Bert reported seeing occasional boot hangs when running with +PREEPT_RT and bisected it down to commit 894d1b3db41c +("locking/mutex: Remove wakeups from under mutex::wait_lock"). + +It looks like I missed a few spots where we drop the wait_lock and +potentially call into schedule without waking up the tasks on the +wake_q structure. Since the tasks being woken are ww_mutex tasks +they need to be able to run to release the mutex and unblock the +task that currently is planning to wake them. Thus we can deadlock. + +So make sure we wake the wake_q tasks when we unlock the wait_lock. + +Closes: https://lore.kernel.org/lkml/20241211182502.2915-1-spasswolf@web.de +Fixes: 894d1b3db41c ("locking/mutex: Remove wakeups from under mutex::wait_lock") +Reported-by: Bert Karwatzki +Signed-off-by: John Stultz +Signed-off-by: Peter Zijlstra (Intel) +Link: https://lkml.kernel.org/r/20241212222138.2400498-1-jstultz@google.com +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + kernel/locking/rtmutex.c | 18 ++++++++++++++++-- + kernel/locking/rtmutex_api.c | 2 +- + 2 files changed, 17 insertions(+), 3 deletions(-) + +--- a/kernel/locking/rtmutex.c ++++ b/kernel/locking/rtmutex.c +@@ -1292,7 +1292,13 @@ static int __sched task_blocks_on_rt_mut + */ + get_task_struct(owner); + ++ preempt_disable(); + raw_spin_unlock_irq(&lock->wait_lock); ++ /* wake up any tasks on the wake_q before calling rt_mutex_adjust_prio_chain */ ++ wake_up_q(wake_q); ++ wake_q_init(wake_q); ++ preempt_enable(); ++ + + res = rt_mutex_adjust_prio_chain(owner, chwalk, lock, + next_lock, waiter, task); +@@ -1602,6 +1608,7 @@ static void __sched remove_waiter(struct + * or TASK_UNINTERRUPTIBLE) + * @timeout: the pre-initialized and started timer, or NULL for none + * @waiter: the pre-initialized rt_mutex_waiter ++ * @wake_q: wake_q of tasks to wake when we drop the lock->wait_lock + * + * Must be called with lock->wait_lock held and interrupts disabled + */ +@@ -1609,7 +1616,8 @@ static int __sched rt_mutex_slowlock_blo + struct ww_acquire_ctx *ww_ctx, + unsigned int state, + struct hrtimer_sleeper *timeout, +- struct rt_mutex_waiter *waiter) ++ struct rt_mutex_waiter *waiter, ++ struct wake_q_head *wake_q) + { + struct rt_mutex *rtm = container_of(lock, struct rt_mutex, rtmutex); + struct task_struct *owner; +@@ -1639,7 +1647,13 @@ static int __sched rt_mutex_slowlock_blo + owner = rt_mutex_owner(lock); + else + owner = NULL; ++ preempt_disable(); + raw_spin_unlock_irq(&lock->wait_lock); ++ if (wake_q) { ++ wake_up_q(wake_q); ++ wake_q_init(wake_q); ++ } ++ preempt_enable(); + + if (!owner || !rtmutex_spin_on_owner(lock, waiter, owner)) + rt_mutex_schedule(); +@@ -1713,7 +1727,7 @@ static int __sched __rt_mutex_slowlock(s + + ret = task_blocks_on_rt_mutex(lock, waiter, current, ww_ctx, chwalk, wake_q); + if (likely(!ret)) +- ret = rt_mutex_slowlock_block(lock, ww_ctx, state, NULL, waiter); ++ ret = rt_mutex_slowlock_block(lock, ww_ctx, state, NULL, waiter, wake_q); + + if (likely(!ret)) { + /* acquired the lock */ +--- a/kernel/locking/rtmutex_api.c ++++ b/kernel/locking/rtmutex_api.c +@@ -383,7 +383,7 @@ int __sched rt_mutex_wait_proxy_lock(str + raw_spin_lock_irq(&lock->wait_lock); + /* sleep on the mutex */ + set_current_state(TASK_INTERRUPTIBLE); +- ret = rt_mutex_slowlock_block(lock, NULL, TASK_INTERRUPTIBLE, to, waiter); ++ ret = rt_mutex_slowlock_block(lock, NULL, TASK_INTERRUPTIBLE, to, waiter, NULL); + /* + * try_to_take_rt_mutex() sets the waiter bit unconditionally. We might + * have to fix that up. diff --git a/queue-6.12/nfs_common-rename-functions-that-invalidate-localio-nfs_clients.patch b/queue-6.12/nfs_common-rename-functions-that-invalidate-localio-nfs_clients.patch new file mode 100644 index 0000000000..0def40732b --- /dev/null +++ b/queue-6.12/nfs_common-rename-functions-that-invalidate-localio-nfs_clients.patch @@ -0,0 +1,105 @@ +From stable+bounces-271997-greg=kroah.com@vger.kernel.org Sun Jul 5 05:11:59 2026 +From: Sasha Levin +Date: Sat, 4 Jul 2026 23:11:47 -0400 +Subject: nfs_common: rename functions that invalidate LOCALIO nfs_clients +To: stable@vger.kernel.org +Cc: Mike Snitzer , NeilBrown , Jeff Layton , Anna Schumaker , Sasha Levin +Message-ID: <20260705031150.1600970-2-sashal@kernel.org> + +From: Mike Snitzer + +[ Upstream commit b49f049a22227df701bfbca083d6cc859496e615 ] + +Rename nfs_uuid_invalidate_one_client to nfs_localio_disable_client. +Rename nfs_uuid_invalidate_clients to nfs_localio_invalidate_clients. + +Signed-off-by: Mike Snitzer +Reviewed-by: NeilBrown +Reviewed-by: Jeff Layton +Signed-off-by: Anna Schumaker +Stable-dep-of: 2c6bb3c40bc2 ("NFSv4/flexfiles: reject zero filehandle version count") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/nfs/localio.c | 2 +- + fs/nfs_common/nfslocalio.c | 8 ++++---- + fs/nfsd/nfsctl.c | 4 ++-- + include/linux/nfslocalio.h | 5 +++-- + 4 files changed, 10 insertions(+), 9 deletions(-) + +--- a/fs/nfs/localio.c ++++ b/fs/nfs/localio.c +@@ -141,7 +141,7 @@ void nfs_local_disable(struct nfs_client + spin_lock(&clp->cl_localio_lock); + if (test_and_clear_bit(NFS_CS_LOCAL_IO, &clp->cl_flags)) { + trace_nfs_local_disable(clp); +- nfs_uuid_invalidate_one_client(&clp->cl_uuid); ++ nfs_localio_disable_client(&clp->cl_uuid); + } + spin_unlock(&clp->cl_localio_lock); + } +--- a/fs/nfs_common/nfslocalio.c ++++ b/fs/nfs_common/nfslocalio.c +@@ -107,7 +107,7 @@ static void nfs_uuid_put_locked(nfs_uuid + list_del_init(&nfs_uuid->list); + } + +-void nfs_uuid_invalidate_clients(struct list_head *list) ++void nfs_localio_invalidate_clients(struct list_head *list) + { + nfs_uuid_t *nfs_uuid, *tmp; + +@@ -116,9 +116,9 @@ void nfs_uuid_invalidate_clients(struct + nfs_uuid_put_locked(nfs_uuid); + spin_unlock(&nfs_uuid_lock); + } +-EXPORT_SYMBOL_GPL(nfs_uuid_invalidate_clients); ++EXPORT_SYMBOL_GPL(nfs_localio_invalidate_clients); + +-void nfs_uuid_invalidate_one_client(nfs_uuid_t *nfs_uuid) ++void nfs_localio_disable_client(nfs_uuid_t *nfs_uuid) + { + if (nfs_uuid->net) { + spin_lock(&nfs_uuid_lock); +@@ -126,7 +126,7 @@ void nfs_uuid_invalidate_one_client(nfs_ + spin_unlock(&nfs_uuid_lock); + } + } +-EXPORT_SYMBOL_GPL(nfs_uuid_invalidate_one_client); ++EXPORT_SYMBOL_GPL(nfs_localio_disable_client); + + /* + * Caller is responsible for calling nfsd_net_put and +--- a/fs/nfsd/nfsctl.c ++++ b/fs/nfsd/nfsctl.c +@@ -2296,14 +2296,14 @@ out_export_error: + * nfsd_net_pre_exit - Disconnect localio clients from net namespace + * @net: a network namespace that is about to be destroyed + * +- * This invalidated ->net pointers held by localio clients ++ * This invalidates ->net pointers held by localio clients + * while they can still safely access nn->counter. + */ + static __net_exit void nfsd_net_pre_exit(struct net *net) + { + struct nfsd_net *nn = net_generic(net, nfsd_net_id); + +- nfs_uuid_invalidate_clients(&nn->local_clients); ++ nfs_localio_invalidate_clients(&nn->local_clients); + } + #endif + +--- a/include/linux/nfslocalio.h ++++ b/include/linux/nfslocalio.h +@@ -37,8 +37,9 @@ bool nfs_uuid_begin(nfs_uuid_t *); + void nfs_uuid_end(nfs_uuid_t *); + void nfs_uuid_is_local(const uuid_t *, struct list_head *, + struct net *, struct auth_domain *, struct module *); +-void nfs_uuid_invalidate_clients(struct list_head *list); +-void nfs_uuid_invalidate_one_client(nfs_uuid_t *nfs_uuid); ++ ++void nfs_localio_disable_client(nfs_uuid_t *nfs_uuid); ++void nfs_localio_invalidate_clients(struct list_head *list); + + /* localio needs to map filehandle -> struct nfsd_file */ + extern struct nfsd_file * diff --git a/queue-6.12/nfsd-add-nfsd_file_-get-put-to-nfs_to-nfsd_localio_operations.patch b/queue-6.12/nfsd-add-nfsd_file_-get-put-to-nfs_to-nfsd_localio_operations.patch new file mode 100644 index 0000000000..9d81bd306a --- /dev/null +++ b/queue-6.12/nfsd-add-nfsd_file_-get-put-to-nfs_to-nfsd_localio_operations.patch @@ -0,0 +1,49 @@ +From stable+bounces-271996-greg=kroah.com@vger.kernel.org Sun Jul 5 05:11:57 2026 +From: Sasha Levin +Date: Sat, 4 Jul 2026 23:11:46 -0400 +Subject: nfsd: add nfsd_file_{get,put} to 'nfs_to' nfsd_localio_operations +To: stable@vger.kernel.org +Cc: Mike Snitzer , Jeff Layton , Chuck Lever , Anna Schumaker , Sasha Levin +Message-ID: <20260705031150.1600970-1-sashal@kernel.org> + +From: Mike Snitzer + +[ Upstream commit a61466315d7afca032342183a57e62d5e3a3157c ] + +In later a commit LOCALIO must call both nfsd_file_get and +nfsd_file_put to manage extra nfsd_file references. + +Signed-off-by: Mike Snitzer +Reviewed-by: Jeff Layton +Acked-by: Chuck Lever +Signed-off-by: Anna Schumaker +Stable-dep-of: 2c6bb3c40bc2 ("NFSv4/flexfiles: reject zero filehandle version count") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/nfsd/localio.c | 2 ++ + include/linux/nfslocalio.h | 2 ++ + 2 files changed, 4 insertions(+) + +--- a/fs/nfsd/localio.c ++++ b/fs/nfsd/localio.c +@@ -29,6 +29,8 @@ static const struct nfsd_localio_operati + .nfsd_net_put = nfsd_net_put, + .nfsd_open_local_fh = nfsd_open_local_fh, + .nfsd_file_put_local = nfsd_file_put_local, ++ .nfsd_file_get = nfsd_file_get, ++ .nfsd_file_put = nfsd_file_put, + .nfsd_file_file = nfsd_file_file, + }; + +--- a/include/linux/nfslocalio.h ++++ b/include/linux/nfslocalio.h +@@ -56,6 +56,8 @@ struct nfsd_localio_operations { + const struct nfs_fh *, + const fmode_t); + struct net *(*nfsd_file_put_local)(struct nfsd_file *); ++ struct nfsd_file *(*nfsd_file_get)(struct nfsd_file *); ++ void (*nfsd_file_put)(struct nfsd_file *); + struct file *(*nfsd_file_file)(struct nfsd_file *); + } ____cacheline_aligned; + diff --git a/queue-6.12/nfsv4-flexfiles-add-data-structure-support-for-striped-layouts.patch b/queue-6.12/nfsv4-flexfiles-add-data-structure-support-for-striped-layouts.patch new file mode 100644 index 0000000000..c7865a9fbf --- /dev/null +++ b/queue-6.12/nfsv4-flexfiles-add-data-structure-support-for-striped-layouts.patch @@ -0,0 +1,625 @@ +From stable+bounces-271999-greg=kroah.com@vger.kernel.org Sun Jul 5 05:12:08 2026 +From: Sasha Levin +Date: Sat, 4 Jul 2026 23:11:49 -0400 +Subject: NFSv4/flexfiles: Add data structure support for striped layouts +To: stable@vger.kernel.org +Cc: Jonathan Curley , Anna Schumaker , Sasha Levin +Message-ID: <20260705031150.1600970-4-sashal@kernel.org> + +From: Jonathan Curley + +[ Upstream commit d442670c0f63c46b7f348f68fb2002af597708f2 ] + +Adds a new struct nfs4_ff_layout_ds_stripe that represents a data +server stripe within a layout. A new dynamically allocated array of +this type has been added to nfs4_ff_layout_mirror and per stripe +configuration information has been moved from the mirror type to the +stripe based on the RFC. + +Signed-off-by: Jonathan Curley +Signed-off-by: Anna Schumaker +Stable-dep-of: 2c6bb3c40bc2 ("NFSv4/flexfiles: reject zero filehandle version count") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/nfs/flexfilelayout/flexfilelayout.c | 149 ++++++++++++++++-------------- + fs/nfs/flexfilelayout/flexfilelayout.h | 27 +++-- + fs/nfs/flexfilelayout/flexfilelayoutdev.c | 54 +++++----- + 3 files changed, 128 insertions(+), 102 deletions(-) + +--- a/fs/nfs/flexfilelayout/flexfilelayout.c ++++ b/fs/nfs/flexfilelayout/flexfilelayout.c +@@ -183,13 +183,13 @@ static bool ff_mirror_match_fh(const str + { + int i, j; + +- if (m1->fh_versions_cnt != m2->fh_versions_cnt) ++ if (m1->dss[0].fh_versions_cnt != m2->dss[0].fh_versions_cnt) + return false; +- for (i = 0; i < m1->fh_versions_cnt; i++) { ++ for (i = 0; i < m1->dss[0].fh_versions_cnt; i++) { + bool found_fh = false; +- for (j = 0; j < m2->fh_versions_cnt; j++) { +- if (nfs_compare_fh(&m1->fh_versions[i], +- &m2->fh_versions[j]) == 0) { ++ for (j = 0; j < m2->dss[0].fh_versions_cnt; j++) { ++ if (nfs_compare_fh(&m1->dss[0].fh_versions[i], ++ &m2->dss[0].fh_versions[j]) == 0) { + found_fh = true; + break; + } +@@ -210,7 +210,8 @@ ff_layout_add_mirror(struct pnfs_layout_ + + spin_lock(&inode->i_lock); + list_for_each_entry(pos, &ff_layout->mirrors, mirrors) { +- if (memcmp(&mirror->devid, &pos->devid, sizeof(pos->devid)) != 0) ++ if (memcmp(&mirror->dss[0].devid, &pos->dss[0].devid, ++ sizeof(pos->dss[0].devid)) != 0) + continue; + if (!ff_mirror_match_fh(mirror, pos)) + continue; +@@ -238,30 +239,46 @@ ff_layout_remove_mirror(struct nfs4_ff_l + mirror->layout = NULL; + } + +-static struct nfs4_ff_layout_mirror *ff_layout_alloc_mirror(gfp_t gfp_flags) ++static struct nfs4_ff_layout_mirror *ff_layout_alloc_mirror(u32 dss_count, ++ gfp_t gfp_flags) + { + struct nfs4_ff_layout_mirror *mirror; + + mirror = kzalloc(sizeof(*mirror), gfp_flags); +- if (mirror != NULL) { +- spin_lock_init(&mirror->lock); +- refcount_set(&mirror->ref, 1); +- INIT_LIST_HEAD(&mirror->mirrors); ++ if (mirror == NULL) ++ return NULL; ++ ++ spin_lock_init(&mirror->lock); ++ refcount_set(&mirror->ref, 1); ++ INIT_LIST_HEAD(&mirror->mirrors); ++ ++ mirror->dss_count = dss_count; ++ mirror->dss = ++ kcalloc(dss_count, sizeof(struct nfs4_ff_layout_ds_stripe), ++ gfp_flags); ++ if (mirror->dss == NULL) { ++ kfree(mirror); ++ return NULL; + } ++ + return mirror; + } + + static void ff_layout_free_mirror(struct nfs4_ff_layout_mirror *mirror) + { +- const struct cred *cred; ++ const struct cred *cred; ++ int dss_id = 0; + + ff_layout_remove_mirror(mirror); +- kfree(mirror->fh_versions); +- cred = rcu_access_pointer(mirror->ro_cred); ++ ++ kfree(mirror->dss[dss_id].fh_versions); ++ cred = rcu_access_pointer(mirror->dss[dss_id].ro_cred); + put_cred(cred); +- cred = rcu_access_pointer(mirror->rw_cred); ++ cred = rcu_access_pointer(mirror->dss[dss_id].rw_cred); + put_cred(cred); +- nfs4_ff_layout_put_deviceid(mirror->mirror_ds); ++ nfs4_ff_layout_put_deviceid(mirror->dss[dss_id].mirror_ds); ++ ++ kfree(mirror->dss); + kfree(mirror); + } + +@@ -371,8 +388,8 @@ static void ff_layout_sort_mirrors(struc + + for (i = 0; i < fls->mirror_array_cnt - 1; i++) { + for (j = i + 1; j < fls->mirror_array_cnt; j++) +- if (fls->mirror_array[i]->efficiency < +- fls->mirror_array[j]->efficiency) ++ if (fls->mirror_array[i]->dss[0].efficiency < ++ fls->mirror_array[j]->dss[0].efficiency) + swap(fls->mirror_array[i], + fls->mirror_array[j]); + } +@@ -426,35 +443,35 @@ ff_layout_alloc_lseg(struct pnfs_layout_ + fls->mirror_array_cnt = mirror_array_cnt; + fls->stripe_unit = stripe_unit; + ++ u32 dss_count = 0; + for (i = 0; i < fls->mirror_array_cnt; i++) { + struct nfs4_ff_layout_mirror *mirror; + struct cred *kcred; + const struct cred __rcu *cred; + kuid_t uid; + kgid_t gid; +- u32 ds_count, fh_count, id; +- int j; ++ u32 fh_count, id; ++ int j, dss_id = 0; + + rc = -EIO; + p = xdr_inline_decode(&stream, 4); + if (!p) + goto out_err_free; +- ds_count = be32_to_cpup(p); ++ ++ dss_count = be32_to_cpup(p); + + /* FIXME: allow for striping? */ +- if (ds_count != 1) ++ if (dss_count != 1) + goto out_err_free; + +- fls->mirror_array[i] = ff_layout_alloc_mirror(gfp_flags); ++ fls->mirror_array[i] = ff_layout_alloc_mirror(dss_count, gfp_flags); + if (fls->mirror_array[i] == NULL) { + rc = -ENOMEM; + goto out_err_free; + } + +- fls->mirror_array[i]->ds_count = ds_count; +- + /* deviceid */ +- rc = decode_deviceid(&stream, &fls->mirror_array[i]->devid); ++ rc = decode_deviceid(&stream, &fls->mirror_array[i]->dss[dss_id].devid); + if (rc) + goto out_err_free; + +@@ -463,10 +480,10 @@ ff_layout_alloc_lseg(struct pnfs_layout_ + p = xdr_inline_decode(&stream, 4); + if (!p) + goto out_err_free; +- fls->mirror_array[i]->efficiency = be32_to_cpup(p); ++ fls->mirror_array[i]->dss[dss_id].efficiency = be32_to_cpup(p); + + /* stateid */ +- rc = decode_pnfs_stateid(&stream, &fls->mirror_array[i]->stateid); ++ rc = decode_pnfs_stateid(&stream, &fls->mirror_array[i]->dss[dss_id].stateid); + if (rc) + goto out_err_free; + +@@ -477,22 +494,22 @@ ff_layout_alloc_lseg(struct pnfs_layout_ + goto out_err_free; + fh_count = be32_to_cpup(p); + +- fls->mirror_array[i]->fh_versions = +- kcalloc(fh_count, sizeof(struct nfs_fh), +- gfp_flags); +- if (fls->mirror_array[i]->fh_versions == NULL) { ++ fls->mirror_array[i]->dss[dss_id].fh_versions = ++ kcalloc(fh_count, sizeof(struct nfs_fh), ++ gfp_flags); ++ if (fls->mirror_array[i]->dss[dss_id].fh_versions == NULL) { + rc = -ENOMEM; + goto out_err_free; + } + + for (j = 0; j < fh_count; j++) { + rc = decode_nfs_fh(&stream, +- &fls->mirror_array[i]->fh_versions[j]); ++ &fls->mirror_array[i]->dss[dss_id].fh_versions[j]); + if (rc) + goto out_err_free; + } + +- fls->mirror_array[i]->fh_versions_cnt = fh_count; ++ fls->mirror_array[i]->dss[dss_id].fh_versions_cnt = fh_count; + + /* user */ + rc = decode_name(&stream, &id); +@@ -523,19 +540,21 @@ ff_layout_alloc_lseg(struct pnfs_layout_ + cred = RCU_INITIALIZER(kcred); + + if (lgr->range.iomode == IOMODE_READ) +- rcu_assign_pointer(fls->mirror_array[i]->ro_cred, cred); ++ rcu_assign_pointer(fls->mirror_array[i]->dss[dss_id].ro_cred, cred); + else +- rcu_assign_pointer(fls->mirror_array[i]->rw_cred, cred); ++ rcu_assign_pointer(fls->mirror_array[i]->dss[dss_id].rw_cred, cred); + + mirror = ff_layout_add_mirror(lh, fls->mirror_array[i]); + if (mirror != fls->mirror_array[i]) { + /* swap cred ptrs so free_mirror will clean up old */ + if (lgr->range.iomode == IOMODE_READ) { +- cred = xchg(&mirror->ro_cred, fls->mirror_array[i]->ro_cred); +- rcu_assign_pointer(fls->mirror_array[i]->ro_cred, cred); ++ cred = xchg(&mirror->dss[dss_id].ro_cred, ++ fls->mirror_array[i]->dss[dss_id].ro_cred); ++ rcu_assign_pointer(fls->mirror_array[i]->dss[dss_id].ro_cred, cred); + } else { +- cred = xchg(&mirror->rw_cred, fls->mirror_array[i]->rw_cred); +- rcu_assign_pointer(fls->mirror_array[i]->rw_cred, cred); ++ cred = xchg(&mirror->dss[dss_id].rw_cred, ++ fls->mirror_array[i]->dss[dss_id].rw_cred); ++ rcu_assign_pointer(fls->mirror_array[i]->dss[dss_id].rw_cred, cred); + } + ff_layout_free_mirror(fls->mirror_array[i]); + fls->mirror_array[i] = mirror; +@@ -623,8 +642,8 @@ nfs4_ff_layoutstat_start_io(struct nfs4_ + struct nfs4_flexfile_layout *ffl = FF_LAYOUT_FROM_HDR(mirror->layout); + + nfs4_ff_start_busy_timer(&layoutstat->busy_timer, now); +- if (!mirror->start_time) +- mirror->start_time = now; ++ if (!mirror->dss[0].start_time) ++ mirror->dss[0].start_time = now; + if (mirror->report_interval != 0) + report_interval = (s64)mirror->report_interval * 1000LL; + else if (layoutstats_timer != 0) +@@ -679,8 +698,8 @@ nfs4_ff_layout_stat_io_start_read(struct + bool report; + + spin_lock(&mirror->lock); +- report = nfs4_ff_layoutstat_start_io(mirror, &mirror->read_stat, now); +- nfs4_ff_layout_stat_io_update_requested(&mirror->read_stat, requested); ++ report = nfs4_ff_layoutstat_start_io(mirror, &mirror->dss[0].read_stat, now); ++ nfs4_ff_layout_stat_io_update_requested(&mirror->dss[0].read_stat, requested); + set_bit(NFS4_FF_MIRROR_STAT_AVAIL, &mirror->flags); + spin_unlock(&mirror->lock); + +@@ -695,7 +714,7 @@ nfs4_ff_layout_stat_io_end_read(struct r + __u64 completed) + { + spin_lock(&mirror->lock); +- nfs4_ff_layout_stat_io_update_completed(&mirror->read_stat, ++ nfs4_ff_layout_stat_io_update_completed(&mirror->dss[0].read_stat, + requested, completed, + ktime_get(), task->tk_start); + set_bit(NFS4_FF_MIRROR_STAT_AVAIL, &mirror->flags); +@@ -710,8 +729,8 @@ nfs4_ff_layout_stat_io_start_write(struc + bool report; + + spin_lock(&mirror->lock); +- report = nfs4_ff_layoutstat_start_io(mirror , &mirror->write_stat, now); +- nfs4_ff_layout_stat_io_update_requested(&mirror->write_stat, requested); ++ report = nfs4_ff_layoutstat_start_io(mirror, &mirror->dss[0].write_stat, now); ++ nfs4_ff_layout_stat_io_update_requested(&mirror->dss[0].write_stat, requested); + set_bit(NFS4_FF_MIRROR_STAT_AVAIL, &mirror->flags); + spin_unlock(&mirror->lock); + +@@ -730,7 +749,7 @@ nfs4_ff_layout_stat_io_end_write(struct + requested = completed = 0; + + spin_lock(&mirror->lock); +- nfs4_ff_layout_stat_io_update_completed(&mirror->write_stat, ++ nfs4_ff_layout_stat_io_update_completed(&mirror->dss[0].write_stat, + requested, completed, ktime_get(), task->tk_start); + set_bit(NFS4_FF_MIRROR_STAT_AVAIL, &mirror->flags); + spin_unlock(&mirror->lock); +@@ -772,7 +791,7 @@ ff_layout_choose_ds_for_read(struct pnfs + continue; + + if (check_device && +- nfs4_test_deviceid_unavailable(&mirror->mirror_ds->id_node)) { ++ nfs4_test_deviceid_unavailable(&mirror->dss[0].mirror_ds->id_node)) { + // reinitialize the error state in case if this is the last iteration + ds = ERR_PTR(-EINVAL); + continue; +@@ -881,7 +900,7 @@ retry: + + mirror = FF_LAYOUT_COMP(pgio->pg_lseg, ds_idx); + pgm = &pgio->pg_mirrors[0]; +- pgm->pg_bsize = mirror->mirror_ds->ds_versions[0].rsize; ++ pgm->pg_bsize = mirror->dss[0].mirror_ds->ds_versions[0].rsize; + + pgio->pg_mirror_idx = ds_idx; + return; +@@ -953,7 +972,7 @@ retry: + goto retry; + } + pgm = &pgio->pg_mirrors[i]; +- pgm->pg_bsize = mirror->mirror_ds->ds_versions[0].wsize; ++ pgm->pg_bsize = mirror->dss[0].mirror_ds->ds_versions[0].wsize; + } + + if (NFS_SERVER(pgio->pg_inode)->flags & +@@ -2013,7 +2032,7 @@ select_ds_fh_from_commit(struct pnfs_lay + /* FIXME: Assume that there is only one NFS version available + * for the DS. + */ +- return &flseg->mirror_array[i]->fh_versions[0]; ++ return &flseg->mirror_array[i]->dss[0].fh_versions[0]; + } + + static int ff_layout_initiate_commit(struct nfs_commit_data *data, int how) +@@ -2129,10 +2148,10 @@ static void ff_layout_cancel_io(struct p + + for (idx = 0; idx < flseg->mirror_array_cnt; idx++) { + mirror = flseg->mirror_array[idx]; +- mirror_ds = mirror->mirror_ds; ++ mirror_ds = mirror->dss[0].mirror_ds; + if (IS_ERR_OR_NULL(mirror_ds)) + continue; +- ds = mirror->mirror_ds->ds; ++ ds = mirror->dss[0].mirror_ds->ds; + if (!ds) + continue; + ds_clp = ds->ds_clp; +@@ -2533,8 +2552,8 @@ ff_layout_encode_ff_layoutupdate(struct + struct nfs4_ff_layout_mirror *mirror) + { + struct nfs4_pnfs_ds_addr *da; +- struct nfs4_pnfs_ds *ds = mirror->mirror_ds->ds; +- struct nfs_fh *fh = &mirror->fh_versions[0]; ++ struct nfs4_pnfs_ds *ds = mirror->dss[0].mirror_ds->ds; ++ struct nfs_fh *fh = &mirror->dss[0].fh_versions[0]; + __be32 *p; + + da = list_first_entry(&ds->ds_addrs, struct nfs4_pnfs_ds_addr, da_node); +@@ -2547,12 +2566,12 @@ ff_layout_encode_ff_layoutupdate(struct + xdr_encode_opaque(p, fh->data, fh->size); + /* ff_io_latency4 read */ + spin_lock(&mirror->lock); +- ff_layout_encode_io_latency(xdr, &mirror->read_stat.io_stat); ++ ff_layout_encode_io_latency(xdr, &mirror->dss[0].read_stat.io_stat); + /* ff_io_latency4 write */ +- ff_layout_encode_io_latency(xdr, &mirror->write_stat.io_stat); ++ ff_layout_encode_io_latency(xdr, &mirror->dss[0].write_stat.io_stat); + spin_unlock(&mirror->lock); + /* nfstime4 */ +- ff_layout_encode_nfstime(xdr, ktime_sub(ktime_get(), mirror->start_time)); ++ ff_layout_encode_nfstime(xdr, ktime_sub(ktime_get(), mirror->dss[0].start_time)); + /* bool */ + p = xdr_reserve_space(xdr, 4); + *p = cpu_to_be32(false); +@@ -2599,7 +2618,7 @@ ff_layout_mirror_prepare_stats(struct pn + list_for_each_entry(mirror, &ff_layout->mirrors, mirrors) { + if (i >= dev_limit) + break; +- if (IS_ERR_OR_NULL(mirror->mirror_ds)) ++ if (IS_ERR_OR_NULL(mirror->dss[0].mirror_ds)) + continue; + if (!test_and_clear_bit(NFS4_FF_MIRROR_STAT_AVAIL, + &mirror->flags) && +@@ -2608,15 +2627,15 @@ ff_layout_mirror_prepare_stats(struct pn + /* mirror refcount put in cleanup_layoutstats */ + if (!refcount_inc_not_zero(&mirror->ref)) + continue; +- dev = &mirror->mirror_ds->id_node; ++ dev = &mirror->dss[0].mirror_ds->id_node; + memcpy(&devinfo->dev_id, &dev->deviceid, NFS4_DEVICEID4_SIZE); + devinfo->offset = 0; + devinfo->length = NFS4_MAX_UINT64; + spin_lock(&mirror->lock); +- devinfo->read_count = mirror->read_stat.io_stat.ops_completed; +- devinfo->read_bytes = mirror->read_stat.io_stat.bytes_completed; +- devinfo->write_count = mirror->write_stat.io_stat.ops_completed; +- devinfo->write_bytes = mirror->write_stat.io_stat.bytes_completed; ++ devinfo->read_count = mirror->dss[0].read_stat.io_stat.ops_completed; ++ devinfo->read_bytes = mirror->dss[0].read_stat.io_stat.bytes_completed; ++ devinfo->write_count = mirror->dss[0].write_stat.io_stat.ops_completed; ++ devinfo->write_bytes = mirror->dss[0].write_stat.io_stat.bytes_completed; + spin_unlock(&mirror->lock); + devinfo->layout_type = LAYOUT_FLEX_FILES; + devinfo->ld_private.ops = &layoutstat_ops; +--- a/fs/nfs/flexfilelayout/flexfilelayout.h ++++ b/fs/nfs/flexfilelayout/flexfilelayout.h +@@ -71,24 +71,31 @@ struct nfs4_ff_layoutstat { + struct nfs4_ff_busy_timer busy_timer; + }; + +-struct nfs4_ff_layout_mirror { +- struct pnfs_layout_hdr *layout; +- struct list_head mirrors; +- u32 ds_count; +- u32 efficiency; ++struct nfs4_ff_layout_mirror; ++ ++struct nfs4_ff_layout_ds_stripe { ++ struct nfs4_ff_layout_mirror *mirror; + struct nfs4_deviceid devid; ++ u32 efficiency; + struct nfs4_ff_layout_ds *mirror_ds; + u32 fh_versions_cnt; + struct nfs_fh *fh_versions; + nfs4_stateid stateid; + const struct cred __rcu *ro_cred; + const struct cred __rcu *rw_cred; +- refcount_t ref; +- spinlock_t lock; +- unsigned long flags; + struct nfs4_ff_layoutstat read_stat; + struct nfs4_ff_layoutstat write_stat; + ktime_t start_time; ++}; ++ ++struct nfs4_ff_layout_mirror { ++ struct pnfs_layout_hdr *layout; ++ struct list_head mirrors; ++ u32 dss_count; ++ struct nfs4_ff_layout_ds_stripe *dss; ++ refcount_t ref; ++ spinlock_t lock; ++ unsigned long flags; + u32 report_interval; + }; + +@@ -154,7 +161,7 @@ FF_LAYOUT_DEVID_NODE(struct pnfs_layout_ + struct nfs4_ff_layout_mirror *mirror = FF_LAYOUT_COMP(lseg, idx); + + if (mirror != NULL) { +- struct nfs4_ff_layout_ds *mirror_ds = mirror->mirror_ds; ++ struct nfs4_ff_layout_ds *mirror_ds = mirror->dss[0].mirror_ds; + + if (!IS_ERR_OR_NULL(mirror_ds)) + return &mirror_ds->id_node; +@@ -183,7 +190,7 @@ ff_layout_no_read_on_rw(struct pnfs_layo + static inline int + nfs4_ff_layout_ds_version(const struct nfs4_ff_layout_mirror *mirror) + { +- return mirror->mirror_ds->ds_versions[0].version; ++ return mirror->dss[0].mirror_ds->ds_versions[0].version; + } + + struct nfs4_ff_layout_ds * +--- a/fs/nfs/flexfilelayout/flexfilelayoutdev.c ++++ b/fs/nfs/flexfilelayout/flexfilelayoutdev.c +@@ -259,7 +259,7 @@ int ff_layout_track_ds_error(struct nfs4 + if (status == 0) + return 0; + +- if (IS_ERR_OR_NULL(mirror->mirror_ds)) ++ if (IS_ERR_OR_NULL(mirror->dss[0].mirror_ds)) + return -EINVAL; + + dserr = kmalloc(sizeof(*dserr), gfp_flags); +@@ -271,8 +271,8 @@ int ff_layout_track_ds_error(struct nfs4 + dserr->length = length; + dserr->status = status; + dserr->opnum = opnum; +- nfs4_stateid_copy(&dserr->stateid, &mirror->stateid); +- memcpy(&dserr->deviceid, &mirror->mirror_ds->id_node.deviceid, ++ nfs4_stateid_copy(&dserr->stateid, &mirror->dss[0].stateid); ++ memcpy(&dserr->deviceid, &mirror->dss[0].mirror_ds->id_node.deviceid, + NFS4_DEVICEID4_SIZE); + + spin_lock(&flo->generic_hdr.plh_inode->i_lock); +@@ -287,9 +287,9 @@ ff_layout_get_mirror_cred(struct nfs4_ff + const struct cred *cred, __rcu **pcred; + + if (iomode == IOMODE_READ) +- pcred = &mirror->ro_cred; ++ pcred = &mirror->dss[0].ro_cred; + else +- pcred = &mirror->rw_cred; ++ pcred = &mirror->dss[0].rw_cred; + + rcu_read_lock(); + do { +@@ -307,7 +307,7 @@ struct nfs_fh * + nfs4_ff_layout_select_ds_fh(struct nfs4_ff_layout_mirror *mirror) + { + /* FIXME: For now assume there is only 1 version available for the DS */ +- return &mirror->fh_versions[0]; ++ return &mirror->dss[0].fh_versions[0]; + } + + void +@@ -315,7 +315,7 @@ nfs4_ff_layout_select_ds_stateid(const s + nfs4_stateid *stateid) + { + if (nfs4_ff_layout_ds_version(mirror) == 4) +- nfs4_stateid_copy(stateid, &mirror->stateid); ++ nfs4_stateid_copy(stateid, &mirror->dss[0].stateid); + } + + static bool +@@ -324,23 +324,23 @@ ff_layout_init_mirror_ds(struct pnfs_lay + { + if (mirror == NULL) + goto outerr; +- if (mirror->mirror_ds == NULL) { ++ if (mirror->dss[0].mirror_ds == NULL) { + struct nfs4_deviceid_node *node; + struct nfs4_ff_layout_ds *mirror_ds = ERR_PTR(-ENODEV); + + node = nfs4_find_get_deviceid(NFS_SERVER(lo->plh_inode), +- &mirror->devid, lo->plh_lc_cred, ++ &mirror->dss[0].devid, lo->plh_lc_cred, + GFP_KERNEL); + if (node) + mirror_ds = FF_LAYOUT_MIRROR_DS(node); + + /* check for race with another call to this function */ +- if (cmpxchg(&mirror->mirror_ds, NULL, mirror_ds) && ++ if (cmpxchg(&mirror->dss[0].mirror_ds, NULL, mirror_ds) && + mirror_ds != ERR_PTR(-ENODEV)) + nfs4_put_deviceid_node(node); + } + +- if (IS_ERR(mirror->mirror_ds)) ++ if (IS_ERR(mirror->dss[0].mirror_ds)) + goto outerr; + + return true; +@@ -379,7 +379,7 @@ nfs4_ff_layout_prepare_ds(struct pnfs_la + if (!ff_layout_init_mirror_ds(lseg->pls_layout, mirror)) + goto noconnect; + +- ds = mirror->mirror_ds->ds; ++ ds = mirror->dss[0].mirror_ds->ds; + if (READ_ONCE(ds->ds_clp)) + goto out; + /* matching smp_wmb() in _nfs4_pnfs_v3/4_ds_connect */ +@@ -388,10 +388,10 @@ nfs4_ff_layout_prepare_ds(struct pnfs_la + /* FIXME: For now we assume the server sent only one version of NFS + * to use for the DS. + */ +- status = nfs4_pnfs_ds_connect(s, ds, &mirror->mirror_ds->id_node, ++ status = nfs4_pnfs_ds_connect(s, ds, &mirror->dss[0].mirror_ds->id_node, + dataserver_timeo, dataserver_retrans, +- mirror->mirror_ds->ds_versions[0].version, +- mirror->mirror_ds->ds_versions[0].minor_version); ++ mirror->dss[0].mirror_ds->ds_versions[0].version, ++ mirror->dss[0].mirror_ds->ds_versions[0].minor_version); + + /* connect success, check rsize/wsize limit */ + if (!status) { +@@ -404,10 +404,10 @@ nfs4_ff_layout_prepare_ds(struct pnfs_la + max_payload = + nfs_block_size(rpc_max_payload(ds->ds_clp->cl_rpcclient), + NULL); +- if (mirror->mirror_ds->ds_versions[0].rsize > max_payload) +- mirror->mirror_ds->ds_versions[0].rsize = max_payload; +- if (mirror->mirror_ds->ds_versions[0].wsize > max_payload) +- mirror->mirror_ds->ds_versions[0].wsize = max_payload; ++ if (mirror->dss[0].mirror_ds->ds_versions[0].rsize > max_payload) ++ mirror->dss[0].mirror_ds->ds_versions[0].rsize = max_payload; ++ if (mirror->dss[0].mirror_ds->ds_versions[0].wsize > max_payload) ++ mirror->dss[0].mirror_ds->ds_versions[0].wsize = max_payload; + goto out; + } + noconnect: +@@ -430,7 +430,7 @@ ff_layout_get_ds_cred(struct nfs4_ff_lay + { + const struct cred *cred; + +- if (mirror && !mirror->mirror_ds->ds_versions[0].tightly_coupled) { ++ if (mirror && !mirror->dss[0].mirror_ds->ds_versions[0].tightly_coupled) { + cred = ff_layout_get_mirror_cred(mirror, range->iomode); + if (!cred) + cred = get_cred(mdscred); +@@ -453,7 +453,7 @@ struct rpc_clnt * + nfs4_ff_find_or_create_ds_client(struct nfs4_ff_layout_mirror *mirror, + struct nfs_client *ds_clp, struct inode *inode) + { +- switch (mirror->mirror_ds->ds_versions[0].version) { ++ switch (mirror->dss[0].mirror_ds->ds_versions[0].version) { + case 3: + /* For NFSv3 DS, flavor is set when creating DS connections */ + return ds_clp->cl_rpcclient; +@@ -564,11 +564,11 @@ static bool ff_read_layout_has_available + for (idx = 0; idx < FF_LAYOUT_MIRROR_COUNT(lseg); idx++) { + mirror = FF_LAYOUT_COMP(lseg, idx); + if (mirror) { +- if (!mirror->mirror_ds) ++ if (!mirror->dss[0].mirror_ds) + return true; +- if (IS_ERR(mirror->mirror_ds)) ++ if (IS_ERR(mirror->dss[0].mirror_ds)) + continue; +- devid = &mirror->mirror_ds->id_node; ++ devid = &mirror->dss[0].mirror_ds->id_node; + if (!nfs4_test_deviceid_unavailable(devid)) + return true; + } +@@ -585,11 +585,11 @@ static bool ff_rw_layout_has_available_d + + for (idx = 0; idx < FF_LAYOUT_MIRROR_COUNT(lseg); idx++) { + mirror = FF_LAYOUT_COMP(lseg, idx); +- if (!mirror || IS_ERR(mirror->mirror_ds)) ++ if (!mirror || IS_ERR(mirror->dss[0].mirror_ds)) + return false; +- if (!mirror->mirror_ds) ++ if (!mirror->dss[0].mirror_ds) + continue; +- devid = &mirror->mirror_ds->id_node; ++ devid = &mirror->dss[0].mirror_ds->id_node; + if (nfs4_test_deviceid_unavailable(devid)) + return false; + } diff --git a/queue-6.12/nfsv4-flexfiles-reject-zero-filehandle-version-count.patch b/queue-6.12/nfsv4-flexfiles-reject-zero-filehandle-version-count.patch new file mode 100644 index 0000000000..5b3531a74f --- /dev/null +++ b/queue-6.12/nfsv4-flexfiles-reject-zero-filehandle-version-count.patch @@ -0,0 +1,58 @@ +From stable+bounces-272000-greg=kroah.com@vger.kernel.org Sun Jul 5 05:12:09 2026 +From: Sasha Levin +Date: Sat, 4 Jul 2026 23:11:50 -0400 +Subject: NFSv4/flexfiles: reject zero filehandle version count +To: stable@vger.kernel.org +Cc: Michael Bommarito , Anna Schumaker , Sasha Levin +Message-ID: <20260705031150.1600970-5-sashal@kernel.org> + +From: Michael Bommarito + +[ Upstream commit 2c6bb3c40bc24f6aa8dfbe6fe98c3ad6389203f2 ] + +ff_layout_alloc_lseg() decodes the filehandle-version array count +from the flexfiles layout body. The value is used as the count for +kzalloc_objs(), and the current code only rejects NULL. + +A zero count yields ZERO_SIZE_PTR, which can be stored in +dss_info->fh_versions even though later flexfiles paths assume that at +least one filehandle version exists. + +Reject fh_count == 0 before the allocation, matching the existing zero +version_count validation in the flexfiles GETDEVICEINFO parser. + +A QEMU/KASAN run with a malformed flexfiles layout hit: + + KASAN: null-ptr-deref in range [0x0000000000000010-0x0000000000000017] + RIP: 0010:ff_layout_encode_ff_layoutupdate.isra.0+0x15f/0x750 + ff_layout_encode_layoutreturn+0x683/0x970 + nfs4_xdr_enc_layoutreturn+0x278/0x3a0 + Kernel panic - not syncing: Fatal exception + +The patched kernel rejects the malformed layout without KASAN/oops/panic, +and a valid fh_count=1 regression still opens, reads, and unmounts cleanly. + +Cc: stable@vger.kernel.org +Fixes: d67ae825a59d ("pnfs/flexfiles: Add the FlexFile Layout Driver") +Assisted-by: Claude:claude-opus-4-7 +Signed-off-by: Michael Bommarito +Signed-off-by: Anna Schumaker +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/nfs/flexfilelayout/flexfilelayout.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/fs/nfs/flexfilelayout/flexfilelayout.c ++++ b/fs/nfs/flexfilelayout/flexfilelayout.c +@@ -493,6 +493,10 @@ ff_layout_alloc_lseg(struct pnfs_layout_ + if (!p) + goto out_err_free; + fh_count = be32_to_cpup(p); ++ if (fh_count == 0) { ++ rc = -EINVAL; ++ goto out_err_free; ++ } + + fls->mirror_array[i]->dss[dss_id].fh_versions = + kcalloc(fh_count, sizeof(struct nfs_fh), diff --git a/queue-6.12/nfsv4-flexfiles-remove-cred-local-variable-dependency.patch b/queue-6.12/nfsv4-flexfiles-remove-cred-local-variable-dependency.patch new file mode 100644 index 0000000000..4dc499788a --- /dev/null +++ b/queue-6.12/nfsv4-flexfiles-remove-cred-local-variable-dependency.patch @@ -0,0 +1,40 @@ +From stable+bounces-271998-greg=kroah.com@vger.kernel.org Sun Jul 5 05:12:02 2026 +From: Sasha Levin +Date: Sat, 4 Jul 2026 23:11:48 -0400 +Subject: NFSv4/flexfiles: Remove cred local variable dependency +To: stable@vger.kernel.org +Cc: Jonathan Curley , Anna Schumaker , Sasha Levin +Message-ID: <20260705031150.1600970-3-sashal@kernel.org> + +From: Jonathan Curley + +[ Upstream commit fec80afc41afa3016421115427df8d00dc491ee5 ] + +No-op preparation change to remove dependency on cred local +variable. Subsequent striping diff has a cred per stripe so this local +variable can't be trusted to be the same. + +Signed-off-by: Jonathan Curley +Signed-off-by: Anna Schumaker +Stable-dep-of: 2c6bb3c40bc2 ("NFSv4/flexfiles: reject zero filehandle version count") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/nfs/flexfilelayout/flexfilelayout.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/fs/nfs/flexfilelayout/flexfilelayout.c ++++ b/fs/nfs/flexfilelayout/flexfilelayout.c +@@ -531,10 +531,10 @@ ff_layout_alloc_lseg(struct pnfs_layout_ + if (mirror != fls->mirror_array[i]) { + /* swap cred ptrs so free_mirror will clean up old */ + if (lgr->range.iomode == IOMODE_READ) { +- cred = xchg(&mirror->ro_cred, cred); ++ cred = xchg(&mirror->ro_cred, fls->mirror_array[i]->ro_cred); + rcu_assign_pointer(fls->mirror_array[i]->ro_cred, cred); + } else { +- cred = xchg(&mirror->rw_cred, cred); ++ cred = xchg(&mirror->rw_cred, fls->mirror_array[i]->rw_cred); + rcu_assign_pointer(fls->mirror_array[i]->rw_cred, cred); + } + ff_layout_free_mirror(fls->mirror_array[i]); diff --git a/queue-6.12/series b/queue-6.12/series index a914a68356..2ce5a72f9b 100644 --- a/queue-6.12/series +++ b/queue-6.12/series @@ -9,3 +9,18 @@ gpio-rockchip-change-the-gpio-version-judgment-logic.patch gpio-rockchip-teardown-bugs-and-resource-leaks.patch gpio-rockchip-fix-generic-irq-chip-leak-on-remove.patch mm-vmalloc-take-vmap_purge_lock-in-shrinker.patch +device-property-initialize-the-remaining-fields-of-fwnode_handle-in-fwnode_init.patch +f2fs-validate-orphan-inode-entry-count.patch +f2fs-atomic-fix-uaf-issue-on-f2fs_inode_info.atomic_inode.patch +f2fs-bound-i_inline_xattr_size-for-non-inline-xattr-inodes.patch +f2fs-fix-potential-deadlock-in-f2fs_balance_fs.patch +f2fs-fix-potential-deadlock-in-gc_merge-path-of-f2fs_balance_fs.patch +f2fs-fix-listxattr-handling-of-corrupted-xattr-entries.patch +fbdev-fbcon-fix-out-of-bounds-read-in-err_out-of-fbcon_do_set_font.patch +nfsd-add-nfsd_file_-get-put-to-nfs_to-nfsd_localio_operations.patch +nfs_common-rename-functions-that-invalidate-localio-nfs_clients.patch +nfsv4-flexfiles-remove-cred-local-variable-dependency.patch +nfsv4-flexfiles-add-data-structure-support-for-striped-layouts.patch +nfsv4-flexfiles-reject-zero-filehandle-version-count.patch +locking-rtmutex-make-sure-we-wake-anything-on-the-wake_q-when-we-release-the-lock-wait_lock.patch +apparmor-advertise-the-tcp-fast-open-fix-is-applied.patch