From 1a6e4692decaa72638fb40163ff77bd44367a689 Mon Sep 17 00:00:00 2001 From: Agatha Isabelle Moreira Date: Wed, 20 May 2026 16:58:16 -0300 Subject: fs: buffer: use clear_and_wake_up_bit() in unlock_buffer() Use `clear_and_wake_up_bit()` in `unlock_buffer()`, since the helper was introduced in 'commit 8236b0ae31c83 ("bdi: wake up concurrent wb_shutdown() callers.")' as a generic way of doing the same sequence of operations: clear_bit_unlock(); smp_mb__after_atomic(); wake_up_bit(); The helper was implemented to avoid bugs caused by forgetting to call `wake_up_bit()` after `clear_bit_unlock()`. Since `unlock_buffer()` predates git and was last modified in 'commit 4e857c58efeb9 ("arch: Mass conversion of smp_mb__*()")', years before `clear_and_wake_up_bit()`, it still uses the open-coded sequence. Replace the open-coded sequence with the helper to avoid duplicate code and reduce code paths to maintain. Suggested-by: shuo chen <1289151713@qq.com> Link: https://lore.kernel.org/kernelnewbies/agzoqV835-co4kAN@guidai/T/#t Signed-off-by: Agatha Isabelle Moreira Link: https://patch.msgid.link/ag4SD-mkmn5IbuN7@guidai Reviewed-by: Jan Kara Signed-off-by: Christian Brauner (Amutable) --- fs/buffer.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/fs/buffer.c b/fs/buffer.c index b0b3792b1496..4348b240bd97 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -74,9 +74,7 @@ EXPORT_SYMBOL(__lock_buffer); void unlock_buffer(struct buffer_head *bh) { - clear_bit_unlock(BH_Lock, &bh->b_state); - smp_mb__after_atomic(); - wake_up_bit(&bh->b_state, BH_Lock); + clear_and_wake_up_bit(BH_Lock, &bh->b_state); } EXPORT_SYMBOL(unlock_buffer); -- cgit v1.2.3 From 8efd38683c81ef5f83ef14664f117c9338f6deef Mon Sep 17 00:00:00 2001 From: Agatha Isabelle Moreira Date: Wed, 20 May 2026 17:05:46 -0300 Subject: fs: jbd2: use clear_and_wake_up_bit() in journal_end_buffer_io_sync() Use `clear_and_wake_up_bit()` in `journal_end_buffer_io_sync()`, since the helper was introduced in 'commit 8236b0ae31c83 ("bdi: wake up concurrent wb_shutdown() callers.")' as a generic way of doing the same sequence of operations: clear_bit_unlock(); smp_mb__after_atomic(); wake_up_bit(); The helper was first implemented to avoid bugs caused by forgetting to call `wake_up_bit()` after `clear_bit_unlock()`. Since `journal_end_buffer_io_sync()` was first introduced by 'commit 470decc613ab2 ("jbd2: initial copy of files from jbd")' and last modified in this operation by 'commit 4e857c58efeb9 ("arch: Mass conversion of smp_mb__*()")', years before `clear_and_wake_up_bit()`, it still uses the open-coded sequence. Replace the open-coded sequence with the helper to avoid duplicate code and reduce code paths to maintain. Suggested-by: shuo chen <1289151713@qq.com> Link: https://lore.kernel.org/kernelnewbies/agzoqV835-co4kAN@guidai/T/#t Signed-off-by: Agatha Isabelle Moreira Link: https://patch.msgid.link/ag4SrrOl7R2DcLLi@guidai Reviewed-by: Jan Kara Signed-off-by: Christian Brauner (Amutable) --- fs/jbd2/commit.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c index 8cf61e7185c4..b647fde76e49 100644 --- a/fs/jbd2/commit.c +++ b/fs/jbd2/commit.c @@ -39,9 +39,7 @@ static void journal_end_buffer_io_sync(struct buffer_head *bh, int uptodate) else clear_buffer_uptodate(bh); if (orig_bh) { - clear_bit_unlock(BH_Shadow, &orig_bh->b_state); - smp_mb__after_atomic(); - wake_up_bit(&orig_bh->b_state, BH_Shadow); + clear_and_wake_up_bit(BH_Shadow, &orig_bh->b_state); } unlock_buffer(bh); } -- cgit v1.2.3 From 57d0ef995d313435686390119cb01d057bf1a65d Mon Sep 17 00:00:00 2001 From: Brian Foster Date: Wed, 17 Jun 2026 07:42:53 -0400 Subject: iomap: always return status from iomap_write_iter iomap_write_iter() returns either an error code or 0 if partial progress has been made. The error sanitization was required in the past because the return value was used by iomap_iter() to determine how much progress was made, and thus what to pass to ->iomap_end() and how much to advance the iter. Now that iter handlers advance the iter incrementally and progress is separate from return status, this is no longer needed. iomap_iter() infers partial progress directly from the iter state and similarly, iomap_file_buffered_write() uses iter.pos to determine whether to return a short write or an error code. This also eliminates a minor quirk in the write iteration where if an error interrupts a partial write, we'd have to loop back into iomap_write_iter() once more and run into the error a second time before iomap terminates the operation and returns the error. With the error code returned directly and separate from write progress, we can complete the operation and return from iomap_iter() immediately. Signed-off-by: Brian Foster Link: https://patch.msgid.link/20260617114253.635751-1-bfoster@redhat.com Reviewed-by: Christoph Hellwig Signed-off-by: Christian Brauner (Amutable) --- fs/iomap/buffered-io.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c index 8d4806dc46d4..fb1f60130cd0 100644 --- a/fs/iomap/buffered-io.c +++ b/fs/iomap/buffered-io.c @@ -1156,7 +1156,6 @@ static bool iomap_write_end(struct iomap_iter *iter, size_t len, size_t copied, static int iomap_write_iter(struct iomap_iter *iter, struct iov_iter *i, const struct iomap_write_ops *write_ops) { - ssize_t total_written = 0; int status = 0; struct address_space *mapping = iter->inode->i_mapping; size_t chunk = mapping_max_folio_size(mapping); @@ -1252,12 +1251,11 @@ retry: goto retry; } } else { - total_written += written; iomap_iter_advance(iter, written); } } while (iov_iter_count(i) && iomap_length(iter)); - return total_written ? 0 : status; + return status; } ssize_t -- cgit v1.2.3 From f64d945fa137e419065f67f74e3e4875f1467826 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Wed, 17 Jun 2026 11:47:21 +0100 Subject: fs: nullfs should include mount.h The nullfs_fs_type is declared in mount.h but when declared in nullfs.c there is a warning as mount.h is not being included. Add include of "mount.h" to remove the following sparse warning: fs/nullfs.c:66:25: warning: symbol 'nullfs_fs_type' was not declared. Should it be static? Signed-off-by: Ben Dooks Link: https://patch.msgid.link/20260617104721.900914-1-ben.dooks@codethink.co.uk Signed-off-by: Christian Brauner (Amutable) --- fs/nullfs.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/nullfs.c b/fs/nullfs.c index fdbd3e5d3d71..95079202fd48 100644 --- a/fs/nullfs.c +++ b/fs/nullfs.c @@ -4,6 +4,8 @@ #include #include +#include "mount.h" + static const struct super_operations nullfs_super_operations = { .statfs = simple_statfs, }; -- cgit v1.2.3 From 879b3353d04d043a9e01525c520d9b81339421b2 Mon Sep 17 00:00:00 2001 From: Amin Vakil Date: Thu, 18 Jun 2026 18:44:44 +0330 Subject: selftests: proc: include fcntl.h in proc-pidns proc-pidns.c uses open() and O_* flags, but does not include . This breaks the proc selftests build with errors such as: error: implicit declaration of function 'open' error: 'O_WRONLY' undeclared error: 'O_CREAT' undeclared error: 'O_RDONLY' undeclared Include to provide the declaration and flag definitions. Fixes: 5554d820f71c ("selftests/proc: add tests for new pidns APIs") Tested with: make -C tools/testing/selftests TARGETS=proc Signed-off-by: Amin Vakil Link: https://patch.msgid.link/20260618151444.124739-1-info@aminvakil.com Signed-off-by: Christian Brauner (Amutable) --- tools/testing/selftests/proc/proc-pidns.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/testing/selftests/proc/proc-pidns.c b/tools/testing/selftests/proc/proc-pidns.c index 25b9a2933c45..6f7c10fe97b3 100644 --- a/tools/testing/selftests/proc/proc-pidns.c +++ b/tools/testing/selftests/proc/proc-pidns.c @@ -6,6 +6,7 @@ #include #include +#include #include #include #include -- cgit v1.2.3 From 0baad6f9b9970c6e3f1d33dbfd17d1a77702771d Mon Sep 17 00:00:00 2001 From: Usama Arif Date: Tue, 9 Jun 2026 05:30:47 -0700 Subject: fs/super: skip non-memcg-aware nr_cached_objects in memcg slab shrink The super_block shrinker is registered with SHRINKER_MEMCG_AWARE because its dentry and inode LRUs are memcg-aware (via list_lru). But the optional ->nr_cached_objects() hooks that the shrinker also drives are not memcg-aware: btrfs extent maps and xfs inode reclaim operate on filesystem-global state, and shmem's unused-huge shrinker walks a per-superblock shrinklist. None of them filter by sc->memcg. The mismatch shows up under memcg-heavy slab reclaim. shrink_slab_memcg() calls do_shrink_slab() once per (memcg, NUMA node) pair for every memcg whose bit is set in the per-superblock shrinker bitmap, which on a busy host means hundreds of calls per reclaim pass. Each scan queues the same global shrinker work item that's already kicked from the root path. Because btrfs/xfs global count is typically non-zero on any in-use filesystem, the returned total stays positive even if a memcg's own dentry/inode LRUs are empty. shrink_slab_memcg() therefore never clears the SB shrinker bit in the memcg bitmap, so subsequent reclaim passes from the same memcg re-enter super_cache_count() and pay for the global counter walk again. Restrict ->nr_cached_objects() to the global shrink path (sc->memcg NULL or root). The memcg-aware dentry/inode LRUs keep being counted and scanned per memcg as before; only the global fs-specific hooks are skipped. The root/global shrink path still drives those hooks; only their invocation from non-root memcg slab reclaim is removed. Signed-off-by: Usama Arif Link: https://patch.msgid.link/20260609123047.1948242-1-usama.arif@linux.dev Signed-off-by: Christian Brauner (Amutable) --- fs/super.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/fs/super.c b/fs/super.c index a8fd61136aaf..d2d04a6f4f84 100644 --- a/fs/super.c +++ b/fs/super.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include /* for the emergency remount stuff */ @@ -169,6 +170,19 @@ static void super_wake(struct super_block *sb, unsigned int flag) wake_up_var(&sb->s_flags); } +/* + * The s_op->nr_cached_objects hooks (used for example by btrfs and xfs) + * operate on filesystem-global state and ignore sc->memcg. Driving them + * from per-memcg shrink_slab_memcg() invocations only burns CPU walking + * per-cpu counters and queueing duplicate work: the actual reclaim happens on + * the global path (kswapd or root direct reclaim) regardless. Restrict them + * to that path. + */ +static inline bool super_fs_objects_eligible(struct shrink_control *sc) +{ + return !sc->memcg || mem_cgroup_is_root(sc->memcg); +} + /* * One thing we have to be careful of with a per-sb shrinker is that we don't * drop the last active reference to the superblock from within the shrinker. @@ -198,7 +212,7 @@ static unsigned long super_cache_scan(struct shrinker *shrink, if (!super_trylock_shared(sb)) return SHRINK_STOP; - if (sb->s_op->nr_cached_objects) + if (sb->s_op->nr_cached_objects && super_fs_objects_eligible(sc)) fs_objects = sb->s_op->nr_cached_objects(sb, sc); inodes = list_lru_shrink_count(&sb->s_inode_lru, sc); @@ -259,7 +273,8 @@ static unsigned long super_cache_count(struct shrinker *shrink, return 0; smp_rmb(); - if (sb->s_op && sb->s_op->nr_cached_objects) + if (sb->s_op && sb->s_op->nr_cached_objects && + super_fs_objects_eligible(sc)) total_objects = sb->s_op->nr_cached_objects(sb, sc); total_objects += list_lru_shrink_count(&sb->s_dentry_lru, sc); -- cgit v1.2.3 From ee3f011250104129893d8e9599147e457d4d7280 Mon Sep 17 00:00:00 2001 From: "Matthew Wilcox (Oracle)" Date: Tue, 23 Jun 2026 20:28:48 +0100 Subject: fs: Free any excess xarray nodes in clear_inode() For many years we've had a hard to hit leak of xarray nodes. Hugh documented it well in commit 786b31121a2c. Recently people and syzbot have found ways to force it to happen with madvise. Rather than fix the leaks where they happen, just call xa_destroy() which has the side-effect of cycling the i_pages lock. Cc: Rik van Riel Cc: Zi Yan Cc: Jinjiang Tu Cc: Dave Jones Link: https://lore.kernel.org/all/20260121062243.1893129-1-tujinjiang@huawei.com/ Signed-off-by: Matthew Wilcox (Oracle) Link: https://patch.msgid.link/20260623192850.1595958-1-willy@infradead.org Reviewed-by: Rik van Riel Signed-off-by: Christian Brauner (Amutable) --- fs/inode.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/fs/inode.c b/fs/inode.c index 31c5b9ee3a81..a31aa7cb47f6 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -763,21 +763,18 @@ void clear_inode(struct inode *inode) fsverity_cleanup_inode(inode); /* - * We have to cycle the i_pages lock here because reclaim can be in the - * process of removing the last page (in __filemap_remove_folio()) - * and we must not free the mapping under it. + * We have to cycle the i_pages lock here because reclaim + * can be in the process of removing the last page (in + * __filemap_remove_folio()) and we must not free the mapping + * under it. We also remove nodes which are empty; these + * can occur in two different ways. The first is that radix + * tree expansion can fail partway and the second is that THP + * collapse_file() can allocate some temporary nodes and not + * clean them up. */ - xa_lock_irq(&inode->i_data.i_pages); + xa_destroy(&inode->i_data.i_pages); + BUG_ON(inode->i_data.nrpages); - /* - * Almost always, mapping_empty(&inode->i_data) here; but there are - * two known and long-standing ways in which nodes may get left behind - * (when deep radix-tree node allocation failed partway; or when THP - * collapse_file() failed). Until those two known cases are cleaned up, - * or a cleanup function is called here, do not BUG_ON(!mapping_empty), - * nor even WARN_ON(!mapping_empty). - */ - xa_unlock_irq(&inode->i_data.i_pages); BUG_ON(!(inode_state_read_once(inode) & I_FREEING)); BUG_ON(inode_state_read_once(inode) & I_CLEAR); BUG_ON(!list_empty(&inode->i_wb_list)); -- cgit v1.2.3 From af695109e83085441d95e65d5e7795681b303500 Mon Sep 17 00:00:00 2001 From: Luis Henriques Date: Mon, 29 Jun 2026 16:45:54 +0100 Subject: posix_acl: remove useless code This is just a trivial clean-up: it removes an unnecessary return branch. Signed-off-by: Luis Henriques Link: https://patch.msgid.link/20260629154554.29093-1-luis@igalia.com Signed-off-by: Christian Brauner (Amutable) --- fs/posix_acl.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/fs/posix_acl.c b/fs/posix_acl.c index b4bfe4ddf64e..6df5de23aac4 100644 --- a/fs/posix_acl.c +++ b/fs/posix_acl.c @@ -740,8 +740,6 @@ static int posix_acl_fix_xattr_common(const void *value, size_t size) count = posix_acl_xattr_count(size); if (count < 0) return -EINVAL; - if (count == 0) - return 0; return count; } -- cgit v1.2.3 From 2f3a7a488cf289d0af85a3ba62c1393c52d7b83d Mon Sep 17 00:00:00 2001 From: Jori Koolstra Date: Tue, 30 Jun 2026 12:53:30 +0200 Subject: vfs: pass S_IFDIR mode to vfs_prepare_mode() There is a comment in vfs_prepare_mode() that says: Note that it's currently valid for @type to be 0 if a directory is created. Filesystems raise that flag individually and we need to check whether each filesystem can deal with receiving S_IFDIR from the vfs before we enforce a non-zero type. It is safe to do this clean-up except that three filesystems (fuse, cifs, and coda) forward the mkdir @mode unchanged to something outside the kernel. Mask S_IFDIR back out in coda_mkdir(), fuse_mkdir() and cifs_mkdir() so that what is sent outside the kernel is unchanged. Their maintainers can drop the mask once they have confirmed it is safe. Assisted-by: LLM Signed-off-by: Jori Koolstra Link: https://patch.msgid.link/20260630105400.68459-2-jkoolstra@xs4all.nl Reviewed-by: NeilBrown Reviewed-by: Jan Kara Signed-off-by: Christian Brauner (Amutable) --- fs/coda/dir.c | 7 ++++++- fs/fuse/dir.c | 8 ++++++++ fs/namei.c | 7 +------ fs/smb/client/inode.c | 7 +++++++ 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/fs/coda/dir.c b/fs/coda/dir.c index 835eb7fdfdad..9ad4d217c8b6 100644 --- a/fs/coda/dir.c +++ b/fs/coda/dir.c @@ -179,7 +179,12 @@ static struct dentry *coda_mkdir(struct mnt_idmap *idmap, struct inode *dir, if (is_root_inode(dir) && coda_iscontrol(name, len)) return ERR_PTR(-EPERM); - attrs.va_mode = mode; + /* + * vfs_mkdir() now passes S_IFDIR in @mode, but @mode is forwarded + * verbatim to userspace, which has only ever been given the permission + * bits. Strip the type bit until venus is known to cope with it. + */ + attrs.va_mode = mode & ~S_IFDIR; error = venus_mkdir(dir->i_sb, coda_i2f(dir), name, len, &newfid, &attrs); if (error) diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 0e2a1039fa43..7decbe4ea48a 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -1117,6 +1117,14 @@ static struct dentry *fuse_mkdir(struct mnt_idmap *idmap, struct inode *dir, if (!fm->fc->dont_mask) mode &= ~current_umask(); + /* + * vfs_mkdir() now passes S_IFDIR in @mode, but @mode is forwarded + * verbatim to the userspace server which has only ever been given the + * permission bits. Strip the type bit until the protocol is known to + * cope with it. + */ + mode &= ~S_IFDIR; + memset(&inarg, 0, sizeof(inarg)); inarg.mode = mode; inarg.umask = current_umask(); diff --git a/fs/namei.c b/fs/namei.c index 5cc9f0f466b8..6554803d6903 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -4140,11 +4140,6 @@ EXPORT_SYMBOL(end_renaming); * after setgid stripping allows the same ordering for both non-POSIX ACL and * POSIX ACL supporting filesystems. * - * Note that it's currently valid for @type to be 0 if a directory is created. - * Filesystems raise that flag individually and we need to check whether each - * filesystem can deal with receiving S_IFDIR from the vfs before we enforce a - * non-zero type. - * * Returns: mode to be passed to the filesystem */ static inline umode_t vfs_prepare_mode(struct mnt_idmap *idmap, @@ -5256,7 +5251,7 @@ struct dentry *vfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, if (!dir->i_op->mkdir) goto err; - mode = vfs_prepare_mode(idmap, dir, mode, S_IRWXUGO | S_ISVTX, 0); + mode = vfs_prepare_mode(idmap, dir, mode, S_IRWXUGO | S_ISVTX, S_IFDIR); error = security_inode_mkdir(dir, dentry, mode); if (error) goto err; diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c index 1dbcfd163ff0..369dfd56c1e6 100644 --- a/fs/smb/client/inode.c +++ b/fs/smb/client/inode.c @@ -2282,6 +2282,13 @@ struct dentry *cifs_mkdir(struct mnt_idmap *idmap, struct inode *inode, const char *full_path; void *page; + /* + * vfs_mkdir() now passes S_IFDIR in @mode, but @mode is forwarded + * verbatim to the server and in the past only contained permission + * bits. Strip the type bit until SMB is verified to deal with it. + */ + mode &= ~S_IFDIR; + cifs_dbg(FYI, "In cifs_mkdir, mode = %04ho inode = 0x%p\n", mode, inode); -- cgit v1.2.3 From 015a1f57507932b6bbc0a1453e4962fda441f9ff Mon Sep 17 00:00:00 2001 From: Jori Koolstra Date: Tue, 30 Jun 2026 12:53:31 +0200 Subject: 9p: drop redundant S_IFDIR from mkdir vfs_mkdir() now sets the S_IFDIR type bit in the mode it passes to ->mkdir(), so OR-ing S_IFDIR into the mode again in v9fs_vfs_mkdir() is redundant. Drop it. Assisted-by: LLM Signed-off-by: Jori Koolstra Link: https://patch.msgid.link/20260630105400.68459-3-jkoolstra@xs4all.nl Reviewed-by: NeilBrown Signed-off-by: Christian Brauner (Amutable) --- fs/9p/vfs_inode.c | 2 +- fs/9p/vfs_inode_dotl.c | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index 5783d0336f96..8abc88a20f00 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -689,7 +689,7 @@ static struct dentry *v9fs_vfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, p9_debug(P9_DEBUG_VFS, "name %pd\n", dentry); v9ses = v9fs_inode2v9ses(dir); - perm = unixmode2p9mode(v9ses, mode | S_IFDIR); + perm = unixmode2p9mode(v9ses, mode); fid = v9fs_create(v9ses, dir, dentry, NULL, perm, P9_OREAD); if (IS_ERR(fid)) return ERR_CAST(fid); diff --git a/fs/9p/vfs_inode_dotl.c b/fs/9p/vfs_inode_dotl.c index f7396d20cb6c..92d065609a8d 100644 --- a/fs/9p/vfs_inode_dotl.c +++ b/fs/9p/vfs_inode_dotl.c @@ -362,7 +362,6 @@ static struct dentry *v9fs_vfs_mkdir_dotl(struct mnt_idmap *idmap, p9_debug(P9_DEBUG_VFS, "name %pd\n", dentry); v9ses = v9fs_inode2v9ses(dir); - omode |= S_IFDIR; if (dir->i_mode & S_ISGID) omode |= S_ISGID; -- cgit v1.2.3 From c8ba66ee6028e7f45b9b2161276e1f76bd304c93 Mon Sep 17 00:00:00 2001 From: Jori Koolstra Date: Tue, 30 Jun 2026 12:53:32 +0200 Subject: affs: drop redundant S_IFDIR from mkdir vfs_mkdir() now sets the S_IFDIR type bit in the mode it passes to ->mkdir(), so OR-ing S_IFDIR into the mode again in affs_mkdir() is redundant. Drop it. Assisted-by: LLM Signed-off-by: Jori Koolstra Link: https://patch.msgid.link/20260630105400.68459-4-jkoolstra@xs4all.nl Reviewed-by: NeilBrown Signed-off-by: Christian Brauner (Amutable) --- fs/affs/namei.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/affs/namei.c b/fs/affs/namei.c index c3c6532da4b0..1a6b2492ab03 100644 --- a/fs/affs/namei.c +++ b/fs/affs/namei.c @@ -287,7 +287,7 @@ affs_mkdir(struct mnt_idmap *idmap, struct inode *dir, if (!inode) return ERR_PTR(-ENOSPC); - inode->i_mode = S_IFDIR | mode; + inode->i_mode = mode; affs_mode_to_prot(inode); inode->i_op = &affs_dir_inode_operations; -- cgit v1.2.3 From b16f5529c63735ee29e9982c37b26b18d48f0696 Mon Sep 17 00:00:00 2001 From: Jori Koolstra Date: Tue, 30 Jun 2026 12:53:33 +0200 Subject: afs: drop redundant S_IFDIR from mkdir vfs_mkdir() now sets the S_IFDIR type bit in the mode it passes to ->mkdir(), so OR-ing S_IFDIR into the mode again in afs_mkdir() is redundant. Drop it. Assisted-by: LLM Signed-off-by: Jori Koolstra Link: https://patch.msgid.link/20260630105400.68459-5-jkoolstra@xs4all.nl Reviewed-by: NeilBrown Signed-off-by: Christian Brauner (Amutable) --- fs/afs/dir.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/afs/dir.c b/fs/afs/dir.c index 498b99ccdf0e..3bff8731e67a 100644 --- a/fs/afs/dir.c +++ b/fs/afs/dir.c @@ -1323,7 +1323,7 @@ static struct dentry *afs_mkdir(struct mnt_idmap *idmap, struct inode *dir, op->file[0].modification = true; op->file[0].update_ctime = true; op->dentry = dentry; - op->create.mode = S_IFDIR | mode; + op->create.mode = mode; op->create.reason = afs_edit_dir_for_mkdir; op->mtime = current_time(dir); op->ops = &afs_mkdir_operation; -- cgit v1.2.3 From 2c04cc9c4958cf016a5993c6990ddff59d737ec0 Mon Sep 17 00:00:00 2001 From: Jori Koolstra Date: Tue, 30 Jun 2026 12:53:34 +0200 Subject: autofs: drop redundant S_IFDIR from mkdir vfs_mkdir() now sets the S_IFDIR type bit in the mode it passes to ->mkdir(), so OR-ing S_IFDIR into the mode again in autofs_dir_mkdir() is redundant. Drop it. Assisted-by: LLM Signed-off-by: Jori Koolstra Link: https://patch.msgid.link/20260630105400.68459-6-jkoolstra@xs4all.nl Reviewed-by: NeilBrown Signed-off-by: Christian Brauner (Amutable) --- fs/autofs/root.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/autofs/root.c b/fs/autofs/root.c index 186e960f1e23..b36439f4521e 100644 --- a/fs/autofs/root.c +++ b/fs/autofs/root.c @@ -741,7 +741,7 @@ static struct dentry *autofs_dir_mkdir(struct mnt_idmap *idmap, autofs_del_active(dentry); - inode = autofs_get_inode(dir->i_sb, S_IFDIR | mode); + inode = autofs_get_inode(dir->i_sb, mode); if (!inode) return ERR_PTR(-ENOMEM); -- cgit v1.2.3 From e6e3cc72f46aa328a806603f1ed56c99c9faa654 Mon Sep 17 00:00:00 2001 From: Jori Koolstra Date: Tue, 30 Jun 2026 12:53:35 +0200 Subject: btrfs: drop redundant S_IFDIR from mkdir vfs_mkdir() now sets the S_IFDIR type bit in the mode it passes to ->mkdir(), so OR-ing S_IFDIR into the mode again in btrfs_mkdir() is redundant. Drop it. Assisted-by: LLM Signed-off-by: Jori Koolstra Link: https://patch.msgid.link/20260630105400.68459-7-jkoolstra@xs4all.nl Reviewed-by: NeilBrown Signed-off-by: Christian Brauner (Amutable) --- fs/btrfs/inode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 272598f6ae77..c19f75cc3e7c 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -6936,7 +6936,7 @@ static struct dentry *btrfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, inode = new_inode(dir->i_sb); if (!inode) return ERR_PTR(-ENOMEM); - inode_init_owner(idmap, inode, dir, S_IFDIR | mode); + inode_init_owner(idmap, inode, dir, mode); inode->i_op = &btrfs_dir_inode_operations; inode->i_fop = &btrfs_dir_file_operations; return ERR_PTR(btrfs_create_common(dir, dentry, inode)); -- cgit v1.2.3 From 5c39d53bf5c3967b1b64ea310ddd1d42a8cc365e Mon Sep 17 00:00:00 2001 From: Jori Koolstra Date: Tue, 30 Jun 2026 12:53:36 +0200 Subject: ceph: drop redundant S_IFDIR from mkdir vfs_mkdir() now sets the S_IFDIR type bit in the mode it passes to ->mkdir(), so OR-ing S_IFDIR into the mode again in ceph_mkdir() is redundant. Drop it. Assisted-by: LLM Signed-off-by: Jori Koolstra Link: https://patch.msgid.link/20260630105400.68459-8-jkoolstra@xs4all.nl Reviewed-by: NeilBrown Signed-off-by: Christian Brauner (Amutable) --- fs/ceph/dir.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c index 27ce9e55e947..32a48550eacf 100644 --- a/fs/ceph/dir.c +++ b/fs/ceph/dir.c @@ -1142,7 +1142,6 @@ static struct dentry *ceph_mkdir(struct mnt_idmap *idmap, struct inode *dir, goto out; } - mode |= S_IFDIR; req->r_new_inode = ceph_new_inode(dir, dentry, &mode, &as_ctx); if (IS_ERR(req->r_new_inode)) { ret = ERR_CAST(req->r_new_inode); -- cgit v1.2.3 From 3a48f5f81af24fbf2fb5c540cb0cd1826445408a Mon Sep 17 00:00:00 2001 From: Jori Koolstra Date: Tue, 30 Jun 2026 12:53:37 +0200 Subject: ext2: drop redundant S_IFDIR from mkdir vfs_mkdir() now sets the S_IFDIR type bit in the mode it passes to ->mkdir(), so OR-ing S_IFDIR into the mode again in ext2_mkdir() is redundant. Drop it. Assisted-by: LLM Signed-off-by: Jori Koolstra Link: https://patch.msgid.link/20260630105400.68459-9-jkoolstra@xs4all.nl Reviewed-by: NeilBrown Reviewed-by: Jan Kara Signed-off-by: Christian Brauner (Amutable) --- fs/ext2/namei.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c index 0d09d22fe708..411fd09c3c7e 100644 --- a/fs/ext2/namei.c +++ b/fs/ext2/namei.c @@ -236,7 +236,7 @@ static struct dentry *ext2_mkdir(struct mnt_idmap * idmap, inode_inc_link_count(dir); - inode = ext2_new_inode(dir, S_IFDIR | mode, &dentry->d_name); + inode = ext2_new_inode(dir, mode, &dentry->d_name); err = PTR_ERR(inode); if (IS_ERR(inode)) goto out_dir; -- cgit v1.2.3 From dc5419ffdb80a16b782da8ab208df2da7bd15691 Mon Sep 17 00:00:00 2001 From: Jori Koolstra Date: Tue, 30 Jun 2026 12:53:38 +0200 Subject: ext4: drop redundant S_IFDIR from mkdir vfs_mkdir() now sets the S_IFDIR type bit in the mode it passes to ->mkdir(), so OR-ing S_IFDIR into the mode again in ext4_mkdir() is redundant. Drop it. Assisted-by: LLM Signed-off-by: Jori Koolstra Link: https://patch.msgid.link/20260630105400.68459-10-jkoolstra@xs4all.nl Reviewed-by: NeilBrown Reviewed-by: Jan Kara Signed-off-by: Christian Brauner (Amutable) --- fs/ext4/namei.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index cc49ae04a6f6..0992fe21b261 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -3009,7 +3009,7 @@ static struct dentry *ext4_mkdir(struct mnt_idmap *idmap, struct inode *dir, credits = (EXT4_DATA_TRANS_BLOCKS(dir->i_sb) + EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3); retry: - inode = ext4_new_inode_start_handle(idmap, dir, S_IFDIR | mode, + inode = ext4_new_inode_start_handle(idmap, dir, mode, &dentry->d_name, 0, NULL, EXT4_HT_DIR, credits); handle = ext4_journal_current_handle(); -- cgit v1.2.3 From e88c34c35bc18f1c9a74233b8b77e6cbd6ee7167 Mon Sep 17 00:00:00 2001 From: Jori Koolstra Date: Tue, 30 Jun 2026 12:53:39 +0200 Subject: f2fs: drop redundant S_IFDIR from mkdir vfs_mkdir() now sets the S_IFDIR type bit in the mode it passes to ->mkdir(), so OR-ing S_IFDIR into the mode again in f2fs_mkdir() is redundant. Drop it. Assisted-by: LLM Signed-off-by: Jori Koolstra Link: https://patch.msgid.link/20260630105400.68459-11-jkoolstra@xs4all.nl Reviewed-by: NeilBrown Signed-off-by: Christian Brauner (Amutable) --- fs/f2fs/namei.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index cac03b8e91a1..592ef4ae59b0 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -742,7 +742,7 @@ static struct dentry *f2fs_mkdir(struct mnt_idmap *idmap, struct inode *dir, if (err) return ERR_PTR(err); - inode = f2fs_new_inode(idmap, dir, S_IFDIR | mode, NULL); + inode = f2fs_new_inode(idmap, dir, mode, NULL); if (IS_ERR(inode)) return ERR_CAST(inode); -- cgit v1.2.3 From 3d4e1570fff50c19d7b75584813589769c58642d Mon Sep 17 00:00:00 2001 From: Jori Koolstra Date: Tue, 30 Jun 2026 12:53:40 +0200 Subject: gfs2: drop redundant S_IFDIR from mkdir vfs_mkdir() now sets the S_IFDIR type bit in the mode it passes to ->mkdir(), so OR-ing S_IFDIR into the mode again in gfs2_mkdir() is redundant. Drop it. Assisted-by: LLM Signed-off-by: Jori Koolstra Link: https://patch.msgid.link/20260630105400.68459-12-jkoolstra@xs4all.nl Reviewed-by: NeilBrown Signed-off-by: Christian Brauner (Amutable) --- fs/gfs2/inode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index 8a77794bbd4a..62926d8e997a 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c @@ -1351,7 +1351,7 @@ static struct dentry *gfs2_mkdir(struct mnt_idmap *idmap, struct inode *dir, { unsigned dsize = gfs2_max_stuffed_size(GFS2_I(dir)); - return ERR_PTR(gfs2_create_inode(dir, dentry, NULL, S_IFDIR | mode, 0, NULL, dsize, 0)); + return ERR_PTR(gfs2_create_inode(dir, dentry, NULL, mode, 0, NULL, dsize, 0)); } /** -- cgit v1.2.3 From b93efaa9aa9020349615f0791bf873997bd2e65d Mon Sep 17 00:00:00 2001 From: Jori Koolstra Date: Tue, 30 Jun 2026 12:53:41 +0200 Subject: hfs: drop redundant S_IFDIR from mkdir vfs_mkdir() now sets the S_IFDIR type bit in the mode it passes to ->mkdir(), so OR-ing S_IFDIR into the mode again in hfs_mkdir() is redundant. Drop it. Assisted-by: LLM Signed-off-by: Jori Koolstra Link: https://patch.msgid.link/20260630105400.68459-13-jkoolstra@xs4all.nl Reviewed-by: NeilBrown Signed-off-by: Christian Brauner (Amutable) --- fs/hfs/dir.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/hfs/dir.c b/fs/hfs/dir.c index e13450bb933e..a7bb9009c5ee 100644 --- a/fs/hfs/dir.c +++ b/fs/hfs/dir.c @@ -219,7 +219,7 @@ static struct dentry *hfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, struct inode *inode; int res; - inode = hfs_new_inode(dir, &dentry->d_name, S_IFDIR | mode); + inode = hfs_new_inode(dir, &dentry->d_name, mode); if (IS_ERR(inode)) return ERR_CAST(inode); -- cgit v1.2.3 From b27e20b4475af6f0d839badec9648b5587c8dffd Mon Sep 17 00:00:00 2001 From: Jori Koolstra Date: Tue, 30 Jun 2026 12:53:42 +0200 Subject: hfsplus: drop redundant S_IFDIR from mkdir vfs_mkdir() now sets the S_IFDIR type bit in the mode it passes to ->mkdir(), so OR-ing S_IFDIR into the mode again in hfsplus_mkdir() is redundant. Drop it. Assisted-by: LLM Signed-off-by: Jori Koolstra Link: https://patch.msgid.link/20260630105400.68459-14-jkoolstra@xs4all.nl Reviewed-by: NeilBrown Signed-off-by: Christian Brauner (Amutable) --- fs/hfsplus/dir.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/hfsplus/dir.c b/fs/hfsplus/dir.c index 8bf6c7cdd9a8..ec74de68b35f 100644 --- a/fs/hfsplus/dir.c +++ b/fs/hfsplus/dir.c @@ -570,7 +570,7 @@ static int hfsplus_create(struct mnt_idmap *idmap, struct inode *dir, static struct dentry *hfsplus_mkdir(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, umode_t mode) { - return ERR_PTR(hfsplus_mknod(&nop_mnt_idmap, dir, dentry, mode | S_IFDIR, 0)); + return ERR_PTR(hfsplus_mknod(&nop_mnt_idmap, dir, dentry, mode, 0)); } static int hfsplus_rename(struct mnt_idmap *idmap, -- cgit v1.2.3 From 9c8ef28c0ccac3749ac4669309b6af26099098c3 Mon Sep 17 00:00:00 2001 From: Jori Koolstra Date: Tue, 30 Jun 2026 12:53:43 +0200 Subject: hpfs: drop redundant S_IFDIR from mkdir vfs_mkdir() now sets the S_IFDIR type bit in the mode it passes to ->mkdir(), so OR-ing S_IFDIR into the mode again in hpfs_mkdir() is redundant. Drop it. Assisted-by: LLM Signed-off-by: Jori Koolstra Link: https://patch.msgid.link/20260630105400.68459-15-jkoolstra@xs4all.nl Reviewed-by: NeilBrown Signed-off-by: Christian Brauner (Amutable) --- fs/hpfs/namei.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/hpfs/namei.c b/fs/hpfs/namei.c index 353e13a615f5..b57dee5a0660 100644 --- a/fs/hpfs/namei.c +++ b/fs/hpfs/namei.c @@ -105,10 +105,10 @@ static struct dentry *hpfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, if (!uid_eq(result->i_uid, current_fsuid()) || !gid_eq(result->i_gid, current_fsgid()) || - result->i_mode != (mode | S_IFDIR)) { + result->i_mode != mode) { result->i_uid = current_fsuid(); result->i_gid = current_fsgid(); - result->i_mode = mode | S_IFDIR; + result->i_mode = mode; hpfs_write_inode_nolock(result); } hpfs_update_directory_times(dir); -- cgit v1.2.3 From 73c6af95575933388ecd2149816dfaa0185a5f59 Mon Sep 17 00:00:00 2001 From: Jori Koolstra Date: Tue, 30 Jun 2026 12:53:44 +0200 Subject: hugetlbfs: drop redundant S_IFDIR from mkdir vfs_mkdir() now sets the S_IFDIR type bit in the mode it passes to ->mkdir(), so OR-ing S_IFDIR into the mode again in hugetlbfs_mkdir() is redundant. Drop it. Assisted-by: LLM Signed-off-by: Jori Koolstra Link: https://patch.msgid.link/20260630105400.68459-16-jkoolstra@xs4all.nl Reviewed-by: NeilBrown Signed-off-by: Christian Brauner (Amutable) --- fs/hugetlbfs/inode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c index 216e1a0dd0b2..154d9fa8ccd1 100644 --- a/fs/hugetlbfs/inode.c +++ b/fs/hugetlbfs/inode.c @@ -971,7 +971,7 @@ static struct dentry *hugetlbfs_mkdir(struct mnt_idmap *idmap, struct inode *dir struct dentry *dentry, umode_t mode) { int retval = hugetlbfs_mknod(idmap, dir, dentry, - mode | S_IFDIR, 0); + mode, 0); if (!retval) inc_nlink(dir); return ERR_PTR(retval); -- cgit v1.2.3 From 950c8f79547c9331f56dfb7fe06f70ee861e49bc Mon Sep 17 00:00:00 2001 From: Jori Koolstra Date: Tue, 30 Jun 2026 12:53:45 +0200 Subject: jffs2: drop redundant S_IFDIR from mkdir vfs_mkdir() now sets the S_IFDIR type bit in the mode it passes to ->mkdir(), so OR-ing S_IFDIR into the mode again in jffs2_mkdir() is redundant. Drop it. Assisted-by: LLM Signed-off-by: Jori Koolstra Link: https://patch.msgid.link/20260630105400.68459-17-jkoolstra@xs4all.nl Reviewed-by: NeilBrown Signed-off-by: Christian Brauner (Amutable) --- fs/jffs2/dir.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c index c4088c3b4ac0..2b86adcbd8f4 100644 --- a/fs/jffs2/dir.c +++ b/fs/jffs2/dir.c @@ -462,8 +462,6 @@ static struct dentry *jffs2_mkdir (struct mnt_idmap *idmap, struct inode *dir_i, uint32_t alloclen; int ret; - mode |= S_IFDIR; - ri = jffs2_alloc_raw_inode(); if (!ri) return ERR_PTR(-ENOMEM); -- cgit v1.2.3 From 557a11939f2838996082763e5ae8c959bfe94128 Mon Sep 17 00:00:00 2001 From: Jori Koolstra Date: Tue, 30 Jun 2026 12:53:46 +0200 Subject: jfs: drop redundant S_IFDIR from mkdir vfs_mkdir() now sets the S_IFDIR type bit in the mode it passes to ->mkdir(), so OR-ing S_IFDIR into the mode again in jfs_mkdir() is redundant. Drop it. Assisted-by: LLM Signed-off-by: Jori Koolstra Link: https://patch.msgid.link/20260630105400.68459-18-jkoolstra@xs4all.nl Reviewed-by: NeilBrown Signed-off-by: Christian Brauner (Amutable) --- fs/jfs/namei.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/jfs/namei.c b/fs/jfs/namei.c index 442d62679262..9ce5b8ff91bf 100644 --- a/fs/jfs/namei.c +++ b/fs/jfs/namei.c @@ -223,7 +223,7 @@ static struct dentry *jfs_mkdir(struct mnt_idmap *idmap, struct inode *dip, * block there while holding dtree page, so we allocate the inode & * begin the transaction before we search the directory. */ - ip = ialloc(dip, S_IFDIR | mode); + ip = ialloc(dip, mode); if (IS_ERR(ip)) { rc = PTR_ERR(ip); goto out2; -- cgit v1.2.3 From 1ab6211652b42f1a06ddd2beb55cd346cb353268 Mon Sep 17 00:00:00 2001 From: Jori Koolstra Date: Tue, 30 Jun 2026 12:53:47 +0200 Subject: minix: drop redundant S_IFDIR from mkdir vfs_mkdir() now sets the S_IFDIR type bit in the mode it passes to ->mkdir(), so OR-ing S_IFDIR into the mode again in minix_mkdir() is redundant. Drop it. Assisted-by: LLM Signed-off-by: Jori Koolstra Link: https://patch.msgid.link/20260630105400.68459-19-jkoolstra@xs4all.nl Reviewed-by: NeilBrown Reviewed-by: Jan Kara Signed-off-by: Christian Brauner (Amutable) --- fs/minix/namei.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/minix/namei.c b/fs/minix/namei.c index 263e4ba8b1c8..19b03ee15c28 100644 --- a/fs/minix/namei.c +++ b/fs/minix/namei.c @@ -110,7 +110,7 @@ static struct dentry *minix_mkdir(struct mnt_idmap *idmap, struct inode *dir, struct inode * inode; int err; - inode = minix_new_inode(dir, S_IFDIR | mode); + inode = minix_new_inode(dir, mode); if (IS_ERR(inode)) return ERR_CAST(inode); -- cgit v1.2.3 From 428475b82a4da0dbd5c0091718e059ff85b322a7 Mon Sep 17 00:00:00 2001 From: Jori Koolstra Date: Tue, 30 Jun 2026 12:53:48 +0200 Subject: nilfs2: drop redundant S_IFDIR from mkdir vfs_mkdir() now sets the S_IFDIR type bit in the mode it passes to ->mkdir(), so OR-ing S_IFDIR into the mode again in nilfs_mkdir() is redundant. Drop it. Assisted-by: LLM Signed-off-by: Jori Koolstra Link: https://patch.msgid.link/20260630105400.68459-20-jkoolstra@xs4all.nl Reviewed-by: NeilBrown Signed-off-by: Christian Brauner (Amutable) --- fs/nilfs2/namei.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/nilfs2/namei.c b/fs/nilfs2/namei.c index e2fe95de3d71..d7d4f4d9a4e8 100644 --- a/fs/nilfs2/namei.c +++ b/fs/nilfs2/namei.c @@ -231,7 +231,7 @@ static struct dentry *nilfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, inc_nlink(dir); - inode = nilfs_new_inode(dir, S_IFDIR | mode); + inode = nilfs_new_inode(dir, mode); err = PTR_ERR(inode); if (IS_ERR(inode)) goto out_dir; -- cgit v1.2.3 From bcf69800f9a5599b85b2fc6c3eecfe75bd185597 Mon Sep 17 00:00:00 2001 From: Jori Koolstra Date: Tue, 30 Jun 2026 12:53:49 +0200 Subject: ntfs3: drop redundant S_IFDIR from mkdir vfs_mkdir() now sets the S_IFDIR type bit in the mode it passes to ->mkdir(), so OR-ing S_IFDIR into the mode again in ntfs_mkdir() is redundant. Drop it. Assisted-by: LLM Signed-off-by: Jori Koolstra Link: https://patch.msgid.link/20260630105400.68459-21-jkoolstra@xs4all.nl Reviewed-by: NeilBrown Signed-off-by: Christian Brauner (Amutable) --- fs/ntfs3/namei.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ntfs3/namei.c b/fs/ntfs3/namei.c index c59de5f2fa97..c6efb488e48e 100644 --- a/fs/ntfs3/namei.c +++ b/fs/ntfs3/namei.c @@ -213,7 +213,7 @@ static struct dentry *ntfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, umode_t mode) { return ERR_PTR(ntfs_create_inode(idmap, dir, dentry, NULL, - S_IFDIR | mode, 0, NULL, 0, NULL)); + mode, 0, NULL, 0, NULL)); } /* -- cgit v1.2.3 From 6caf971bc5e59276679d8fec5d791ab4ff0db702 Mon Sep 17 00:00:00 2001 From: Jori Koolstra Date: Tue, 30 Jun 2026 12:53:50 +0200 Subject: ocfs2: drop redundant S_IFDIR from mkdir vfs_mkdir() now sets the S_IFDIR type bit in the mode it passes to ->mkdir(), so OR-ing S_IFDIR into the mode again in ocfs2_mkdir() is redundant. Drop it. Assisted-by: LLM Signed-off-by: Jori Koolstra Link: https://patch.msgid.link/20260630105400.68459-22-jkoolstra@xs4all.nl Reviewed-by: NeilBrown Reviewed-by: Jan Kara Signed-off-by: Christian Brauner (Amutable) --- fs/ocfs2/namei.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ocfs2/namei.c b/fs/ocfs2/namei.c index 1277666c77cd..b87eb6a2fa38 100644 --- a/fs/ocfs2/namei.c +++ b/fs/ocfs2/namei.c @@ -657,7 +657,7 @@ static struct dentry *ocfs2_mkdir(struct mnt_idmap *idmap, trace_ocfs2_mkdir(dir, dentry, dentry->d_name.len, dentry->d_name.name, OCFS2_I(dir)->ip_blkno, mode); - ret = ocfs2_mknod(&nop_mnt_idmap, dir, dentry, mode | S_IFDIR, 0); + ret = ocfs2_mknod(&nop_mnt_idmap, dir, dentry, mode, 0); if (ret) mlog_errno(ret); -- cgit v1.2.3 From 8f1b4d14e9fc0110b4a22c7dfbc9d3dde6cb60df Mon Sep 17 00:00:00 2001 From: Jori Koolstra Date: Tue, 30 Jun 2026 12:53:51 +0200 Subject: ocfs2: dlmfs: drop redundant S_IFDIR from mkdir vfs_mkdir() now sets the S_IFDIR type bit in the mode it passes to ->mkdir(), so OR-ing S_IFDIR into the mode again in dlmfs_mkdir() is redundant. Drop it. Assisted-by: LLM Signed-off-by: Jori Koolstra Link: https://patch.msgid.link/20260630105400.68459-23-jkoolstra@xs4all.nl Reviewed-by: NeilBrown Reviewed-by: Jan Kara Signed-off-by: Christian Brauner (Amutable) --- fs/ocfs2/dlmfs/dlmfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ocfs2/dlmfs/dlmfs.c b/fs/ocfs2/dlmfs/dlmfs.c index 5821e33df78f..dc538fd8d9f8 100644 --- a/fs/ocfs2/dlmfs/dlmfs.c +++ b/fs/ocfs2/dlmfs/dlmfs.c @@ -422,7 +422,7 @@ static struct dentry *dlmfs_mkdir(struct mnt_idmap * idmap, goto bail; } - inode = dlmfs_get_inode(dir, dentry, mode | S_IFDIR); + inode = dlmfs_get_inode(dir, dentry, mode); if (!inode) { status = -ENOMEM; mlog_errno(status); -- cgit v1.2.3 From 38d8af9d314da7952f6876a2f5e07fa79bb1c459 Mon Sep 17 00:00:00 2001 From: Jori Koolstra Date: Tue, 30 Jun 2026 12:53:52 +0200 Subject: omfs: drop redundant S_IFDIR from mkdir vfs_mkdir() now sets the S_IFDIR type bit in the mode it passes to ->mkdir(), so OR-ing S_IFDIR into the mode again in omfs_mkdir() is redundant. Drop it. Assisted-by: LLM Signed-off-by: Jori Koolstra Link: https://patch.msgid.link/20260630105400.68459-24-jkoolstra@xs4all.nl Reviewed-by: NeilBrown Signed-off-by: Christian Brauner (Amutable) --- fs/omfs/dir.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/omfs/dir.c b/fs/omfs/dir.c index 2ed541fccf33..418906614f89 100644 --- a/fs/omfs/dir.c +++ b/fs/omfs/dir.c @@ -282,7 +282,7 @@ out_free_inode: static struct dentry *omfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, umode_t mode) { - return ERR_PTR(omfs_add_node(dir, dentry, mode | S_IFDIR)); + return ERR_PTR(omfs_add_node(dir, dentry, mode)); } static int omfs_create(struct mnt_idmap *idmap, struct inode *dir, -- cgit v1.2.3 From 2eab03836e641cd47b8691b25664f8195ae74538 Mon Sep 17 00:00:00 2001 From: Jori Koolstra Date: Tue, 30 Jun 2026 12:53:53 +0200 Subject: orangefs: drop redundant S_IFDIR from mkdir vfs_mkdir() now sets the S_IFDIR type bit in the mode it passes to ->mkdir(), so OR-ing S_IFDIR into the mode again in orangefs_mkdir() is redundant. Drop it. Assisted-by: LLM Signed-off-by: Jori Koolstra Link: https://patch.msgid.link/20260630105400.68459-25-jkoolstra@xs4all.nl Reviewed-by: NeilBrown Signed-off-by: Christian Brauner (Amutable) --- fs/orangefs/namei.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/orangefs/namei.c b/fs/orangefs/namei.c index 75e65e72c2d6..22b4107b8dc0 100644 --- a/fs/orangefs/namei.c +++ b/fs/orangefs/namei.c @@ -333,7 +333,7 @@ static struct dentry *orangefs_mkdir(struct mnt_idmap *idmap, struct inode *dir, ref = new_op->downcall.resp.mkdir.refn; - inode = orangefs_new_inode(dir->i_sb, dir, S_IFDIR | mode, 0, &ref); + inode = orangefs_new_inode(dir->i_sb, dir, mode, 0, &ref); if (IS_ERR(inode)) { gossip_err("*** Failed to allocate orangefs dir inode\n"); ret = PTR_ERR(inode); -- cgit v1.2.3 From 0ffe991d6ccc6787f68c5ddecc4c13eb4bd0fe4a Mon Sep 17 00:00:00 2001 From: Jori Koolstra Date: Tue, 30 Jun 2026 12:53:54 +0200 Subject: ramfs: drop redundant S_IFDIR from mkdir vfs_mkdir() now sets the S_IFDIR type bit in the mode it passes to ->mkdir(), so OR-ing S_IFDIR into the mode again in ramfs_mkdir() is redundant. Drop it. Assisted-by: LLM Signed-off-by: Jori Koolstra Link: https://patch.msgid.link/20260630105400.68459-26-jkoolstra@xs4all.nl Reviewed-by: NeilBrown Reviewed-by: Jan Kara Signed-off-by: Christian Brauner (Amutable) --- fs/ramfs/inode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ramfs/inode.c b/fs/ramfs/inode.c index 3987639ed132..e884ebc58a33 100644 --- a/fs/ramfs/inode.c +++ b/fs/ramfs/inode.c @@ -121,7 +121,7 @@ out: static struct dentry *ramfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, umode_t mode) { - int retval = ramfs_mknod(&nop_mnt_idmap, dir, dentry, mode | S_IFDIR, 0); + int retval = ramfs_mknod(&nop_mnt_idmap, dir, dentry, mode, 0); if (!retval) inc_nlink(dir); return ERR_PTR(retval); -- cgit v1.2.3 From 30638fe73a3aad4e5545e2c843f20bcfa2be9272 Mon Sep 17 00:00:00 2001 From: Jori Koolstra Date: Tue, 30 Jun 2026 12:53:55 +0200 Subject: udf: drop redundant S_IFDIR from mkdir vfs_mkdir() now sets the S_IFDIR type bit in the mode it passes to ->mkdir(), so OR-ing S_IFDIR into the mode again in udf_mkdir() is redundant. Drop it. Assisted-by: LLM Signed-off-by: Jori Koolstra Link: https://patch.msgid.link/20260630105400.68459-27-jkoolstra@xs4all.nl Reviewed-by: NeilBrown Reviewed-by: Jan Kara Signed-off-by: Christian Brauner (Amutable) --- fs/udf/namei.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/udf/namei.c b/fs/udf/namei.c index 9a3b7cef3606..849762a7d14e 100644 --- a/fs/udf/namei.c +++ b/fs/udf/namei.c @@ -428,7 +428,7 @@ static struct dentry *udf_mkdir(struct mnt_idmap *idmap, struct inode *dir, struct udf_inode_info *dinfo = UDF_I(dir); struct udf_inode_info *iinfo; - inode = udf_new_inode(dir, S_IFDIR | mode); + inode = udf_new_inode(dir, mode); if (IS_ERR(inode)) return ERR_CAST(inode); -- cgit v1.2.3 From 384de989eba9ff12925fcc8d0bbf316a0c272a8b Mon Sep 17 00:00:00 2001 From: Jori Koolstra Date: Tue, 30 Jun 2026 12:53:56 +0200 Subject: ufs: drop redundant S_IFDIR from mkdir vfs_mkdir() now sets the S_IFDIR type bit in the mode it passes to ->mkdir(), so OR-ing S_IFDIR into the mode again in ufs_mkdir() is redundant. Drop it. Assisted-by: LLM Signed-off-by: Jori Koolstra Link: https://patch.msgid.link/20260630105400.68459-28-jkoolstra@xs4all.nl Reviewed-by: NeilBrown Signed-off-by: Christian Brauner (Amutable) --- fs/ufs/namei.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ufs/namei.c b/fs/ufs/namei.c index 5b3c85c93242..718e96506532 100644 --- a/fs/ufs/namei.c +++ b/fs/ufs/namei.c @@ -174,7 +174,7 @@ static struct dentry *ufs_mkdir(struct mnt_idmap * idmap, struct inode * dir, inode_inc_link_count(dir); - inode = ufs_new_inode(dir, S_IFDIR|mode); + inode = ufs_new_inode(dir, mode); err = PTR_ERR(inode); if (IS_ERR(inode)) goto out_dir; -- cgit v1.2.3 From 0b83c6b36075cc6d157a073a31738a945c0f629f Mon Sep 17 00:00:00 2001 From: Jori Koolstra Date: Tue, 30 Jun 2026 12:53:57 +0200 Subject: nfs: drop redundant S_IFDIR from mkdir vfs_mkdir() now sets the S_IFDIR type bit in the mode it passes to ->mkdir(), so OR-ing S_IFDIR into the mode again in nfs_mkdir() is redundant. Drop it. Assisted-by: LLM Signed-off-by: Jori Koolstra Link: https://patch.msgid.link/20260630105400.68459-29-jkoolstra@xs4all.nl Reviewed-by: NeilBrown Signed-off-by: Christian Brauner (Amutable) --- fs/nfs/dir.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index c7b723c18620..630718739d59 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -2474,7 +2474,7 @@ struct dentry *nfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, dir->i_sb->s_id, dir->i_ino, dentry); attr.ia_valid = ATTR_MODE; - attr.ia_mode = mode | S_IFDIR; + attr.ia_mode = mode; trace_nfs_mkdir_enter(dir, dentry); ret = NFS_PROTO(dir)->mkdir(dir, dentry, &attr); -- cgit v1.2.3 From 2a58d0e0f070c05955dc21f49962ae449c272b0c Mon Sep 17 00:00:00 2001 From: Jori Koolstra Date: Tue, 30 Jun 2026 12:53:58 +0200 Subject: ubifs: drop redundant S_IFDIR from mkdir vfs_mkdir() now sets the S_IFDIR type bit in the mode it passes to ->mkdir(), so OR-ing S_IFDIR into the mode again in ubifs_mkdir() is redundant. Drop it. Assisted-by: LLM Signed-off-by: Jori Koolstra Link: https://patch.msgid.link/20260630105400.68459-30-jkoolstra@xs4all.nl Reviewed-by: NeilBrown Signed-off-by: Christian Brauner (Amutable) --- fs/ubifs/dir.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ubifs/dir.c b/fs/ubifs/dir.c index 86d41e077e4d..b200f3d682b6 100644 --- a/fs/ubifs/dir.c +++ b/fs/ubifs/dir.c @@ -1031,7 +1031,7 @@ static struct dentry *ubifs_mkdir(struct mnt_idmap *idmap, struct inode *dir, sz_change = CALC_DENT_SIZE(fname_len(&nm)); - inode = ubifs_new_inode(c, dir, S_IFDIR | mode, false); + inode = ubifs_new_inode(c, dir, mode, false); if (IS_ERR(inode)) { err = PTR_ERR(inode); goto out_fname; -- cgit v1.2.3 From 0ddd31b242644973514966d26fe07313aa7e83c8 Mon Sep 17 00:00:00 2001 From: Jori Koolstra Date: Tue, 30 Jun 2026 12:53:59 +0200 Subject: xfs: drop redundant S_IFDIR from mkdir vfs_mkdir() now sets the S_IFDIR type bit in the mode it passes to ->mkdir(), so OR-ing S_IFDIR into the mode again in xfs_vn_mkdir() is redundant. Drop it. Assisted-by: LLM Signed-off-by: Jori Koolstra Link: https://patch.msgid.link/20260630105400.68459-31-jkoolstra@xs4all.nl Reviewed-by: NeilBrown Signed-off-by: Christian Brauner (Amutable) --- fs/xfs/xfs_iops.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c index 6339f4956ecb..a3c02101ff3f 100644 --- a/fs/xfs/xfs_iops.c +++ b/fs/xfs/xfs_iops.c @@ -306,7 +306,7 @@ xfs_vn_mkdir( struct dentry *dentry, umode_t mode) { - return ERR_PTR(xfs_generic_create(idmap, dir, dentry, mode | S_IFDIR, 0, NULL)); + return ERR_PTR(xfs_generic_create(idmap, dir, dentry, mode, 0, NULL)); } STATIC struct dentry * -- cgit v1.2.3 From a380b9693c7a005806a7bf901f4a2be8b4395249 Mon Sep 17 00:00:00 2001 From: Jori Koolstra Date: Tue, 30 Jun 2026 12:54:00 +0200 Subject: ntfs: drop redundant S_IFDIR from mkdir vfs_mkdir() now sets the S_IFDIR type bit in the mode it passes to ->mkdir(), so OR-ing S_IFDIR into the mode again in ntfs_mkdir() is redundant. Drop it. Assisted-by: LLM Signed-off-by: Jori Koolstra Link: https://patch.msgid.link/20260630105400.68459-32-jkoolstra@xs4all.nl Reviewed-by: NeilBrown Signed-off-by: Christian Brauner (Amutable) --- fs/ntfs/namei.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ntfs/namei.c b/fs/ntfs/namei.c index a19626a135bd..ef4c52b2b7b9 100644 --- a/fs/ntfs/namei.c +++ b/fs/ntfs/namei.c @@ -1082,7 +1082,7 @@ static struct dentry *ntfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, if (!(vol->vol_flags & VOLUME_IS_DIRTY)) ntfs_set_volume_flags(vol, VOLUME_IS_DIRTY); - ni = __ntfs_create(idmap, dir, uname, uname_len, S_IFDIR | mode, 0, NULL, 0); + ni = __ntfs_create(idmap, dir, uname, uname_len, mode, 0, NULL, 0); kmem_cache_free(ntfs_name_cache, uname); if (IS_ERR(ni)) { err = PTR_ERR(ni); -- cgit v1.2.3 From d30b5a954e0a4dcc16bba9c310c13709a6169a52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=83=81=ED=98=B8?= Date: Thu, 2 Jul 2026 07:07:29 +0900 Subject: romfs: detect hard link cycles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit romfs_iget() follows on-disk hard link entries until it reaches a non-hard link inode: pos = be32_to_cpu(ri.spec) & ROMFH_MASK; The target position is image-controlled, and the loop does not detect cycles. A crafted romfs image can make the root inode a hard link. The hard link can point back to itself and leave mount(2) spinning in the kernel. Reject excessive hard link indirection with -ELOOP. Normal romfs images do not need long hard link chains. This bounds corrupted-image traversal. Propagate romfs_iget() errors from lookup because hard link traversal can now fail with -ELOOP. Signed-off-by: 이상호 Link: https://patch.msgid.link/20260701220729.822112-1-kudo3228@gmail.com Signed-off-by: Christian Brauner (Amutable) --- fs/romfs/super.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/fs/romfs/super.c b/fs/romfs/super.c index ac55193bf398..3a836af3ca7e 100644 --- a/fs/romfs/super.c +++ b/fs/romfs/super.c @@ -240,6 +240,8 @@ static struct dentry *romfs_lookup(struct inode *dir, struct dentry *dentry, if ((be32_to_cpu(ri.next) & ROMFH_TYPE) == ROMFH_HRD) offset = be32_to_cpu(ri.spec) & ROMFH_MASK; inode = romfs_iget(dir->i_sb, offset); + if (IS_ERR(inode)) + return ERR_CAST(inode); break; } @@ -262,6 +264,8 @@ static const struct inode_operations romfs_dir_inode_operations = { .lookup = romfs_lookup, }; +#define ROMFS_MAX_HARDLINK_DEPTH 64 + /* * get a romfs inode based on its position in the image (which doubles as the * inode number) @@ -273,6 +277,7 @@ static struct inode *romfs_iget(struct super_block *sb, unsigned long pos) struct inode *i; unsigned long nlen; unsigned nextfh; + unsigned int depth = 0; int ret; umode_t mode; @@ -289,6 +294,9 @@ static struct inode *romfs_iget(struct super_block *sb, unsigned long pos) if ((nextfh & ROMFH_TYPE) != ROMFH_HRD) break; + if (++depth > ROMFS_MAX_HARDLINK_DEPTH) + return ERR_PTR(-ELOOP); + pos = be32_to_cpu(ri.spec) & ROMFH_MASK; } -- cgit v1.2.3 From 50bb761eb9baa63b7fd81fae259aaae07000ade1 Mon Sep 17 00:00:00 2001 From: Wang Yan Date: Thu, 2 Jul 2026 09:54:28 +0800 Subject: selftests/filesystems: fix spelling error in statmount test comment Fix typo "didnt't" -> "didn't" in statmount_test.c comment. Signed-off-by: Wang Yan Link: https://patch.msgid.link/20260702015428.363642-1-wangyan01@kylinos.cn Signed-off-by: Christian Brauner (Amutable) --- tools/testing/selftests/filesystems/statmount/statmount_test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/filesystems/statmount/statmount_test.c b/tools/testing/selftests/filesystems/statmount/statmount_test.c index 8dc018d47a93..9d963e09e510 100644 --- a/tools/testing/selftests/filesystems/statmount/statmount_test.c +++ b/tools/testing/selftests/filesystems/statmount/statmount_test.c @@ -515,7 +515,7 @@ static void test_statmount_mnt_opts(void) return; } - ksft_test_result_fail("didnt't find mount entry\n"); + ksft_test_result_fail("didn't find mount entry\n"); free(sm); free(line); } -- cgit v1.2.3 From 38b4ee06d15a90e53969016c8042c12a0a4b4813 Mon Sep 17 00:00:00 2001 From: Yuhong Cheng Date: Sun, 5 Jul 2026 15:26:09 +0800 Subject: docs: filesystems: porting: fix spelling of returned and instead Fix the spelling of 'rreturned' and 'instread' in the LOOKUP_EXCL section. Signed-off-by: Yuhong Cheng Link: https://patch.msgid.link/20260705072609.1692-1-ceohunk@gmail.com Acked-by: Randy Dunlap Signed-off-by: Christian Brauner (Amutable) --- Documentation/filesystems/porting.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/filesystems/porting.rst b/Documentation/filesystems/porting.rst index d13f0a23c882..e040b0ff4798 100644 --- a/Documentation/filesystems/porting.rst +++ b/Documentation/filesystems/porting.rst @@ -1173,7 +1173,7 @@ these conditions don't require explicit checks: - if LOOKUP_CREATE is NOT given, then the dentry won't be negative, ERR_PTR(-ENOENT) is returned instead - if LOOKUP_EXCL IS given, then the dentry won't be positive, - ERR_PTR(-EEXIST) is rreturned instread + ERR_PTR(-EEXIST) is returned instead LOOKUP_EXCL now means "target must not exist". It can be combined with LOOK_CREATE or LOOKUP_RENAME_TARGET. -- cgit v1.2.3 From 7689b7221333bf82ceb09ab19642b848a5f02a6d Mon Sep 17 00:00:00 2001 From: Marco Crivellari Date: Mon, 6 Jul 2026 12:54:33 +0200 Subject: ufs: Move long delayed work on system_dfl_long_wq MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently the code enqueue work items using {queue|mod}_delayed_work(), using system_long_wq. This workqueue should be used when long works are expected and it is a per-cpu workqueue. The function(s) end up calling __queue_delayed_work(), which set a global timer that could fire anywhere, enqueuing the work where the timer fired. Unbound works could benefit from scheduler task placement, to optimize performance and power consumption. Long work shouldn't stick to a single CPU. Recently, a new unbound workqueue specific for long running work has been added:     c116737e972e ("workqueue: Add system_dfl_long_wq for long unbound works") Since the workqueue work doesn't rely on per-cpu variables, there is no obvious reason that justify the use of a per-cpu workqueue. So change system_long_wq with system_dfl_long_wq so that the work may benefit from scheduler task placement. Cc: Al Viro Cc: Kees Cook Cc: Eric Sandeen Signed-off-by: Marco Crivellari Link: https://patch.msgid.link/20260706105443.173697-2-marco.crivellari@suse.com Signed-off-by: Christian Brauner (Amutable) --- fs/ufs/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ufs/super.c b/fs/ufs/super.c index c4831a8b9b3f..6dcf6d048cce 100644 --- a/fs/ufs/super.c +++ b/fs/ufs/super.c @@ -672,7 +672,7 @@ void ufs_mark_sb_dirty(struct super_block *sb) spin_lock(&sbi->work_lock); if (!sbi->work_queued) { delay = msecs_to_jiffies(dirty_writeback_interval * 10); - queue_delayed_work(system_long_wq, &sbi->sync_work, delay); + queue_delayed_work(system_dfl_long_wq, &sbi->sync_work, delay); sbi->work_queued = 1; } spin_unlock(&sbi->work_lock); -- cgit v1.2.3 From 8c8fe5c77b604ceb9ce7be1cd5932031f500dda2 Mon Sep 17 00:00:00 2001 From: Malaya Kumar Rout Date: Sat, 4 Jul 2026 17:34:36 +0530 Subject: selftests/statmount: Fix file descriptor leak in setup_namespace In setup_namespace(), f_mountinfo is opened with fopen() at line 115 but is never closed. Multiple ksft_exit_fail_msg() calls exit the program without closing this file descriptor, and the cleanup_namespace() function registered with atexit() also doesn't close it. Add fclose(f_mountinfo) in cleanup_namespace() to ensure the file descriptor is properly closed on both normal and error exit paths, since cleanup_namespace() is already registered as an atexit handler. Signed-off-by: Malaya Kumar Rout Link: https://patch.msgid.link/20260704120437.99851-1-malayarout91@gmail.com Signed-off-by: Christian Brauner (Amutable) --- tools/testing/selftests/filesystems/statmount/statmount_test.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/testing/selftests/filesystems/statmount/statmount_test.c b/tools/testing/selftests/filesystems/statmount/statmount_test.c index 9d963e09e510..60c2c544db6a 100644 --- a/tools/testing/selftests/filesystems/statmount/statmount_test.c +++ b/tools/testing/selftests/filesystems/statmount/statmount_test.c @@ -82,6 +82,9 @@ static void cleanup_namespace(void) { int ret; + if (f_mountinfo) + fclose(f_mountinfo); + ret = fchdir(orig_root); if (ret == -1) ksft_perror("fchdir to original root"); -- cgit v1.2.3 From cb0ceb9fa03fa4a4f104762f71f151b70f5e9a01 Mon Sep 17 00:00:00 2001 From: Marco Crivellari Date: Mon, 6 Jul 2026 12:54:34 +0200 Subject: fs/jffs2: Move long delayed work on system_dfl_long_wq MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently the code enqueue work items using {queue|mod}_delayed_work(), using system_long_wq. This workqueue should be used when long works are expected and it is a per-cpu workqueue. The function(s) end up calling __queue_delayed_work(), which set a global timer that could fire anywhere, enqueuing the work where the timer fired. Unbound works could benefit from scheduler task placement, to optimize performance and power consumption. Long work shouldn't stick to a single CPU. Recently, a new unbound workqueue specific for long running work has been added:     c116737e972e ("workqueue: Add system_dfl_long_wq for long unbound works") Since the workqueue work doesn't rely on per-cpu variables, there is no obvious reason that justify the use of a per-cpu workqueue. So change system_long_wq with system_dfl_long_wq so that the work may benefit from scheduler task placement. Cc: David Woodhouse Cc: Richard Weinberger Cc: linux-mtd@lists.infradead.org Signed-off-by: Marco Crivellari Link: https://patch.msgid.link/20260706105443.173697-3-marco.crivellari@suse.com Signed-off-by: Christian Brauner (Amutable) --- fs/jffs2/wbuf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c index 8ff7a0b6add2..3b7803c75d58 100644 --- a/fs/jffs2/wbuf.c +++ b/fs/jffs2/wbuf.c @@ -1177,7 +1177,7 @@ void jffs2_dirty_trigger(struct jffs2_sb_info *c) return; delay = msecs_to_jiffies(dirty_writeback_interval * 10); - if (queue_delayed_work(system_long_wq, &c->wbuf_dwork, delay)) + if (queue_delayed_work(system_dfl_long_wq, &c->wbuf_dwork, delay)) jffs2_dbg(1, "%s()\n", __func__); } -- cgit v1.2.3 From f159da4398352302948c4f328515dca0b6f336b7 Mon Sep 17 00:00:00 2001 From: Marco Crivellari Date: Mon, 6 Jul 2026 12:54:35 +0200 Subject: hfsplus: Move long delayed work on system_dfl_long_wq MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently the code enqueue work items using {queue|mod}_delayed_work(), using system_long_wq. This workqueue should be used when long works are expected and it is a per-cpu workqueue. The function(s) end up calling __queue_delayed_work(), which set a global timer that could fire anywhere, enqueuing the work where the timer fired. Unbound works could benefit from scheduler task placement, to optimize performance and power consumption. Long work shouldn't stick to a single CPU. Recently, a new unbound workqueue specific for long running work has been added:     c116737e972e ("workqueue: Add system_dfl_long_wq for long unbound works") Since the workqueue work doesn't rely on per-cpu variables, there is no obvious reason that justify the use of a per-cpu workqueue. So change system_long_wq with system_dfl_long_wq so that the work may benefit from scheduler task placement. Cc: Viacheslav Dubeyko Cc: John Paul Adrian Glaubitz Cc: Yangtao Li Cc: linux-fsdevel@vger.kernel.org Signed-off-by: Marco Crivellari Link: https://patch.msgid.link/20260706105443.173697-4-marco.crivellari@suse.com Reviewed-by: Viacheslav Dubeyko Signed-off-by: Christian Brauner (Amutable) --- fs/hfsplus/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c index 5777e31de45a..ff7d6b3336a6 100644 --- a/fs/hfsplus/super.c +++ b/fs/hfsplus/super.c @@ -312,7 +312,7 @@ void hfsplus_mark_mdb_dirty(struct super_block *sb) spin_lock(&sbi->work_lock); if (!sbi->work_queued) { delay = msecs_to_jiffies(dirty_writeback_interval * 10); - queue_delayed_work(system_long_wq, &sbi->sync_work, delay); + queue_delayed_work(system_dfl_long_wq, &sbi->sync_work, delay); sbi->work_queued = 1; } spin_unlock(&sbi->work_lock); -- cgit v1.2.3 From c7443c7bfa55b99c80097041f8fb6a4040f69630 Mon Sep 17 00:00:00 2001 From: Marco Crivellari Date: Mon, 6 Jul 2026 12:54:36 +0200 Subject: hfs: Move long delayed work on system_dfl_long_wq MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently the code enqueue work items using {queue|mod}_delayed_work(), using system_long_wq. This workqueue should be used when long works are expected and it is a per-cpu workqueue. The function(s) end up calling __queue_delayed_work(), which set a global timer that could fire anywhere, enqueuing the work where the timer fired. Unbound works could benefit from scheduler task placement, to optimize performance and power consumption. Long work shouldn't stick to a single CPU. Recently, a new unbound workqueue specific for long running work has been added:     c116737e972e ("workqueue: Add system_dfl_long_wq for long unbound works") Since the workqueue work doesn't rely on per-cpu variables, there is no obvious reason that justify the use of a per-cpu workqueue. So change system_long_wq with system_dfl_long_wq so that the work may benefit from scheduler task placement. Cc: Viacheslav Dubeyko Cc: John Paul Adrian Glaubitz Cc: Yangtao Li Cc: linux-fsdevel@vger.kernel.org Signed-off-by: Marco Crivellari Link: https://patch.msgid.link/20260706105443.173697-5-marco.crivellari@suse.com Reviewed-by: Viacheslav Dubeyko Signed-off-by: Christian Brauner (Amutable) --- fs/hfs/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/hfs/super.c b/fs/hfs/super.c index a466c401f6bb..ecdafc658928 100644 --- a/fs/hfs/super.c +++ b/fs/hfs/super.c @@ -82,7 +82,7 @@ void hfs_mark_mdb_dirty(struct super_block *sb) spin_lock(&sbi->work_lock); if (!sbi->work_queued) { delay = msecs_to_jiffies(dirty_writeback_interval * 10); - queue_delayed_work(system_long_wq, &sbi->mdb_work, delay); + queue_delayed_work(system_dfl_long_wq, &sbi->mdb_work, delay); sbi->work_queued = 1; } spin_unlock(&sbi->work_lock); -- cgit v1.2.3 From 34361f3452f75c3a5c03e597cd71a24461d275cd Mon Sep 17 00:00:00 2001 From: Marco Crivellari Date: Mon, 6 Jul 2026 12:54:37 +0200 Subject: affs: Move long delayed work on system_dfl_long_wq MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently the code enqueue work items using {queue|mod}_delayed_work(), using system_long_wq. This workqueue should be used when long works are expected and it is a per-cpu workqueue. The function(s) end up calling __queue_delayed_work(), which set a global timer that could fire anywhere, enqueuing the work where the timer fired. Unbound works could benefit from scheduler task placement, to optimize performance and power consumption. Long work shouldn't stick to a single CPU. Recently, a new unbound workqueue specific for long running work has been added:     c116737e972e ("workqueue: Add system_dfl_long_wq for long unbound works") Since the workqueue work doesn't rely on per-cpu variables, there is no obvious reason that justify the use of a per-cpu workqueue. So change system_long_wq with system_dfl_long_wq so that the work may benefit from scheduler task placement. Cc: David Sterba Cc: linux-fsdevel@vger.kernel.org Signed-off-by: Marco Crivellari Link: https://patch.msgid.link/20260706105443.173697-6-marco.crivellari@suse.com Acked-by: David Sterba Signed-off-by: Christian Brauner (Amutable) --- fs/affs/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/affs/super.c b/fs/affs/super.c index b232251aa7bb..4f331f784db2 100644 --- a/fs/affs/super.c +++ b/fs/affs/super.c @@ -88,7 +88,7 @@ void affs_mark_sb_dirty(struct super_block *sb) spin_lock(&sbi->work_lock); if (!sbi->work_queued) { delay = msecs_to_jiffies(dirty_writeback_interval * 10); - queue_delayed_work(system_long_wq, &sbi->sb_work, delay); + queue_delayed_work(system_dfl_long_wq, &sbi->sb_work, delay); sbi->work_queued = 1; } spin_unlock(&sbi->work_lock); -- cgit v1.2.3 From 0342482a4d15358fe6931606caf58968de5d1d38 Mon Sep 17 00:00:00 2001 From: Noah Orlando Date: Mon, 6 Jul 2026 14:25:59 -0400 Subject: put_mnt_ns(): leave mounts connected When a mount namespace is destroyed, put_mnt_ns() disconnects its mounts from their mount points. A file descriptor still open on the parent of a mount point can then be used to look under the mount point. Locked mounts are kept connected to prevent this. However, a mount is only locked when its tree is copied across a user namespace boundary. A mount namespace set up by a privileged component has no locked mounts, so its mounts are disconnected. Pass UMOUNT_CONNECTED so every mount is kept connected, as locked mounts already are. Signed-off-by: Noah Orlando Link: https://patch.msgid.link/20260706182559.2496448-2-Noah.Orlando@deshaw.com Signed-off-by: Christian Brauner (Amutable) --- fs/namespace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/namespace.c b/fs/namespace.c index 3d5cd5bf3b05..a58c9d4ea25c 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -6283,7 +6283,7 @@ void put_mnt_ns(struct mnt_namespace *ns) guard(namespace_excl)(); emptied_ns = ns; guard(mount_writer)(); - umount_tree(ns->root, 0); + umount_tree(ns->root, UMOUNT_CONNECTED); } struct vfsmount *kern_mount(struct file_system_type *type) -- cgit v1.2.3 From 3452eecbcc954ef859b7d19309c121a78d6d0e31 Mon Sep 17 00:00:00 2001 From: Noah Orlando Date: Mon, 6 Jul 2026 14:26:01 -0400 Subject: selftests/filesystems: add mntns cleanup test Verify that destroying a mount namespace keeps its mounts connected. Signed-off-by: Noah Orlando Link: https://patch.msgid.link/20260706182559.2496448-4-Noah.Orlando@deshaw.com Signed-off-by: Christian Brauner (Amutable) --- tools/testing/selftests/Makefile | 1 + .../selftests/filesystems/mntns_cleanup/.gitignore | 2 + .../selftests/filesystems/mntns_cleanup/Makefile | 6 +++ .../filesystems/mntns_cleanup/mntns_cleanup_test.c | 58 ++++++++++++++++++++++ 4 files changed, 67 insertions(+) create mode 100644 tools/testing/selftests/filesystems/mntns_cleanup/.gitignore create mode 100644 tools/testing/selftests/filesystems/mntns_cleanup/Makefile create mode 100644 tools/testing/selftests/filesystems/mntns_cleanup/mntns_cleanup_test.c diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 8d4db2241cc2..023699f05e13 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -42,6 +42,7 @@ TARGETS += filesystems/fuse TARGETS += filesystems/move_mount TARGETS += filesystems/empty_mntns TARGETS += filesystems/fsmount_ns +TARGETS += filesystems/mntns_cleanup TARGETS += firmware TARGETS += fpu TARGETS += ftrace diff --git a/tools/testing/selftests/filesystems/mntns_cleanup/.gitignore b/tools/testing/selftests/filesystems/mntns_cleanup/.gitignore new file mode 100644 index 000000000000..493fbcf8d9ec --- /dev/null +++ b/tools/testing/selftests/filesystems/mntns_cleanup/.gitignore @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +mntns_cleanup_test diff --git a/tools/testing/selftests/filesystems/mntns_cleanup/Makefile b/tools/testing/selftests/filesystems/mntns_cleanup/Makefile new file mode 100644 index 000000000000..0e09e7030a5c --- /dev/null +++ b/tools/testing/selftests/filesystems/mntns_cleanup/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 +TEST_GEN_PROGS := mntns_cleanup_test + +CFLAGS += -Wall -O2 -g $(KHDR_INCLUDES) + +include ../../lib.mk diff --git a/tools/testing/selftests/filesystems/mntns_cleanup/mntns_cleanup_test.c b/tools/testing/selftests/filesystems/mntns_cleanup/mntns_cleanup_test.c new file mode 100644 index 000000000000..5209712568b1 --- /dev/null +++ b/tools/testing/selftests/filesystems/mntns_cleanup/mntns_cleanup_test.c @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0 + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include + +#include "../../kselftest_harness.h" + +FIXTURE(mntns_cleanup) { +}; + +FIXTURE_SETUP(mntns_cleanup) +{ + if (geteuid() != 0) + SKIP(return, "test requires CAP_SYS_ADMIN"); + + ASSERT_EQ(unshare(CLONE_NEWNS), 0); + ASSERT_EQ(mount("", "/", NULL, MS_REC | MS_PRIVATE, NULL), 0); + + rmdir("/mnt_dir"); + ASSERT_EQ(mkdir("/mnt_dir", 0755), 0); + ASSERT_EQ(mount("tmpfs", "/mnt_dir", "tmpfs", 0, NULL), 0); + ASSERT_EQ(mkdir("/mnt_dir/hidden", 0755), 0); + ASSERT_EQ(mkdir("/mnt_dir/hidden/secret", 0755), 0); + ASSERT_EQ(mount("tmpfs", "/mnt_dir/hidden", "tmpfs", 0, NULL), 0); +} + +FIXTURE_TEARDOWN(mntns_cleanup) +{ +} + +/* Mounts must stay connected when a mount namespace is cleaned up. */ +TEST_F(mntns_cleanup, keeps_mounts_connected) +{ + int fd, sfd, err; + + fd = open("/mnt_dir", O_PATH | O_DIRECTORY | O_CLOEXEC); + ASSERT_GE(fd, 0); + + /* Destroy the namespace; the fd keeps /mnt_dir alive. */ + ASSERT_EQ(unshare(CLONE_NEWNS), 0); + + sfd = openat(fd, "hidden/secret", O_RDONLY); + err = errno; + if (sfd >= 0) + close(sfd); + close(fd); + + ASSERT_LT(sfd, 0) + TH_LOG("mount namespace teardown revealed what the overmount covered"); + ASSERT_EQ(err, ENOENT); +} + +TEST_HARNESS_MAIN -- cgit v1.2.3 From f797d7b64eb46bb16b51cdfdb72f915f31d4c11b Mon Sep 17 00:00:00 2001 From: Usama Arif Date: Tue, 7 Jul 2026 12:02:38 -0700 Subject: eventpoll: compute timer slack lazily in ep_poll() ep_poll() computes the timer slack via select_estimate_accuracy() up front, before checking whether events are already available. select_estimate_accuracy() reads the clock (ktime_get_ts64()), and the resulting slack is only consumed by the schedule_hrtimeout_range() call on the blocking path. A busy poller such as an L7 proxy event loop calls epoll_wait() at a very high rate and often finds events already pending, returning via ep_try_send_events() without ever blocking. In that case the up-front slack estimation - including its clock read - is pure overhead. read_tsc() attributable to select_estimate_accuracy() sometimes shows up in perf profiles of such a workload via the epoll_wait() path. Move the slack estimation to the point where the thread is actually about to sleep. The timeout passed to ep_poll() is already an absolute deadline (ep_timeout_to_timespec()), so deferring the estimate does not change the wakeup time; taken closer to the sleep it is, if anything, marginally more accurate. On the common non-blocking path the clock read is skipped entirely. Measured on a host running a Meta production workload with the following bpftrace script: #!/usr/bin/bpftrace fentry:__x64_sys_epoll_wait, fentry:__x64_sys_epoll_pwait { @in[tid] = 1; } fexit:__x64_sys_epoll_wait, fexit:__x64_sys_epoll_pwait { delete(@in, tid); } fentry:select_estimate_accuracy /@in[tid]/ { @sea++; } fentry:schedule_hrtimeout_range /@in[tid]/ { @shr++; } interval:s:30 { printf("sea=%lld shr=%lld wasted=%lld (%d%%)\n", @sea, @shr, @sea - @shr, (@sea - @shr) * 100 / @sea); exit(); } Over a 30s window: sea=3,587,704 shr=3,003,920 wasted=583,784 (16%) So ~16% of ep_poll invocations of select_estimate_accuracy have no consumer. Signed-off-by: Usama Arif Link: https://patch.msgid.link/20260707190238.3478608-1-usama.arif@linux.dev Signed-off-by: Christian Brauner (Amutable) --- fs/eventpoll.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/eventpoll.c b/fs/eventpoll.c index 0e65c7431dfc..128d7fd3d0ea 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -2248,7 +2248,6 @@ static int ep_poll(struct eventpoll *ep, struct epoll_event __user *events, lockdep_assert_irqs_enabled(); if (timeout && (timeout->tv_sec | timeout->tv_nsec)) { - slack = select_estimate_accuracy(timeout); to = &expires; *to = timespec64_to_ktime(*timeout); } else if (timeout) { @@ -2327,10 +2326,13 @@ static int ep_poll(struct eventpoll *ep, struct epoll_event __user *events, spin_unlock_irq(&ep->lock); - if (!eavail) + if (!eavail) { + if (to) + slack = select_estimate_accuracy(timeout); timed_out = !ep_schedule_timeout(to) || !schedule_hrtimeout_range(to, slack, HRTIMER_MODE_ABS); + } __set_current_state(TASK_RUNNING); /* -- cgit v1.2.3 From c610d2d0787961cdd6fc1de69d9be1ff3687e1a6 Mon Sep 17 00:00:00 2001 From: Yu Peng Date: Wed, 8 Jul 2026 16:02:32 +0800 Subject: fs: annotate inode timestamp accessors syzbot reported a KCSAN race between fill_mg_cmtime() and inode_set_ctime_to_ts() on inode->i_ctime_{sec,nsec}. stat/getattr can sample inode timestamps while update paths store new values concurrently, so KCSAN can report benign races on these fields. Annotate the timestamp accessors with READ_ONCE()/WRITE_ONCE(), and use the ctime accessor for the remaining ctime loads. This avoids the KCSAN reports without changing timestamp semantics. Fixes: 4e40eff0b573 ("fs: add infrastructure for multigrain timestamps") Reported-by: syzbot+8b3bd9f8a06658479d4a@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=8b3bd9f8a06658479d4a Signed-off-by: Yu Peng Link: https://patch.msgid.link/20260708080232.2564807-1-pengyu@kylinos.cn Reviewed-by: Jeff Layton Signed-off-by: Christian Brauner (Amutable) --- fs/inode.c | 18 +++++++++--------- fs/stat.c | 2 +- include/linux/fs.h | 20 ++++++++++---------- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/fs/inode.c b/fs/inode.c index a31aa7cb47f6..238fcd1cad6e 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -2830,8 +2830,8 @@ struct timespec64 inode_set_ctime_to_ts(struct inode *inode, struct timespec64 t { trace_inode_set_ctime_to_ts(inode, &ts); set_normalized_timespec64(&ts, ts.tv_sec, ts.tv_nsec); - inode->i_ctime_sec = ts.tv_sec; - inode->i_ctime_nsec = ts.tv_nsec; + WRITE_ONCE(inode->i_ctime_sec, ts.tv_sec); + WRITE_ONCE(inode->i_ctime_nsec, ts.tv_nsec); return ts; } EXPORT_SYMBOL(inode_set_ctime_to_ts); @@ -2905,7 +2905,7 @@ struct timespec64 inode_set_ctime_current(struct inode *inode) */ cns = smp_load_acquire(&inode->i_ctime_nsec); if (cns & I_CTIME_QUERIED) { - struct timespec64 ctime = { .tv_sec = inode->i_ctime_sec, + struct timespec64 ctime = { .tv_sec = inode_get_ctime_sec(inode), .tv_nsec = cns & ~I_CTIME_QUERIED }; if (timespec64_compare(&now, &ctime) <= 0) { @@ -2917,7 +2917,7 @@ struct timespec64 inode_set_ctime_current(struct inode *inode) mgtime_counter_inc(mg_ctime_updates); /* No need to cmpxchg if it's exactly the same */ - if (cns == now.tv_nsec && inode->i_ctime_sec == now.tv_sec) { + if (cns == now.tv_nsec && inode_get_ctime_sec(inode) == now.tv_sec) { trace_ctime_xchg_skip(inode, &now); goto out; } @@ -2926,7 +2926,7 @@ retry: /* Try to swap the nsec value into place. */ if (try_cmpxchg(&inode->i_ctime_nsec, &cur, now.tv_nsec)) { /* If swap occurred, then we're (mostly) done */ - inode->i_ctime_sec = now.tv_sec; + WRITE_ONCE(inode->i_ctime_sec, now.tv_sec); trace_ctime_ns_xchg(inode, cns, now.tv_nsec, cur); mgtime_counter_inc(mg_ctime_swaps); } else { @@ -2941,7 +2941,7 @@ retry: goto retry; } /* Otherwise, keep the existing ctime */ - now.tv_sec = inode->i_ctime_sec; + now.tv_sec = inode_get_ctime_sec(inode); now.tv_nsec = cur & ~I_CTIME_QUERIED; } out: @@ -2974,7 +2974,7 @@ struct timespec64 inode_set_ctime_deleg(struct inode *inode, struct timespec64 u /* pairs with try_cmpxchg below */ cur = smp_load_acquire(&inode->i_ctime_nsec); cur_ts.tv_nsec = cur & ~I_CTIME_QUERIED; - cur_ts.tv_sec = inode->i_ctime_sec; + cur_ts.tv_sec = inode_get_ctime_sec(inode); /* If the update is older than the existing value, skip it. */ if (timespec64_compare(&update, &cur_ts) <= 0) @@ -3000,7 +3000,7 @@ struct timespec64 inode_set_ctime_deleg(struct inode *inode, struct timespec64 u retry: old = cur; if (try_cmpxchg(&inode->i_ctime_nsec, &cur, update.tv_nsec)) { - inode->i_ctime_sec = update.tv_sec; + WRITE_ONCE(inode->i_ctime_sec, update.tv_sec); mgtime_counter_inc(mg_ctime_swaps); return update; } @@ -3016,7 +3016,7 @@ retry: goto retry; /* Otherwise, it was a new timestamp. */ - cur_ts.tv_sec = inode->i_ctime_sec; + cur_ts.tv_sec = inode_get_ctime_sec(inode); cur_ts.tv_nsec = cur & ~I_CTIME_QUERIED; return cur_ts; } diff --git a/fs/stat.c b/fs/stat.c index 89909746bed1..c461c3054234 100644 --- a/fs/stat.c +++ b/fs/stat.c @@ -53,7 +53,7 @@ void fill_mg_cmtime(struct kstat *stat, u32 request_mask, struct inode *inode) } stat->mtime = inode_get_mtime(inode); - stat->ctime.tv_sec = inode->i_ctime_sec; + stat->ctime.tv_sec = inode_get_ctime_sec(inode); stat->ctime.tv_nsec = (u32)atomic_read(pcn); if (!(stat->ctime.tv_nsec & I_CTIME_QUERIED)) stat->ctime.tv_nsec = ((u32)atomic_fetch_or(I_CTIME_QUERIED, pcn)); diff --git a/include/linux/fs.h b/include/linux/fs.h index d10897b3a1e3..e5b97e324db1 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1598,12 +1598,12 @@ struct timespec64 inode_set_ctime_deleg(struct inode *inode, static inline time64_t inode_get_atime_sec(const struct inode *inode) { - return inode->i_atime_sec; + return READ_ONCE(inode->i_atime_sec); } static inline long inode_get_atime_nsec(const struct inode *inode) { - return inode->i_atime_nsec; + return READ_ONCE(inode->i_atime_nsec); } static inline struct timespec64 inode_get_atime(const struct inode *inode) @@ -1617,8 +1617,8 @@ static inline struct timespec64 inode_get_atime(const struct inode *inode) static inline struct timespec64 inode_set_atime_to_ts(struct inode *inode, struct timespec64 ts) { - inode->i_atime_sec = ts.tv_sec; - inode->i_atime_nsec = ts.tv_nsec; + WRITE_ONCE(inode->i_atime_sec, ts.tv_sec); + WRITE_ONCE(inode->i_atime_nsec, ts.tv_nsec); return ts; } @@ -1633,12 +1633,12 @@ static inline struct timespec64 inode_set_atime(struct inode *inode, static inline time64_t inode_get_mtime_sec(const struct inode *inode) { - return inode->i_mtime_sec; + return READ_ONCE(inode->i_mtime_sec); } static inline long inode_get_mtime_nsec(const struct inode *inode) { - return inode->i_mtime_nsec; + return READ_ONCE(inode->i_mtime_nsec); } static inline struct timespec64 inode_get_mtime(const struct inode *inode) @@ -1651,8 +1651,8 @@ static inline struct timespec64 inode_get_mtime(const struct inode *inode) static inline struct timespec64 inode_set_mtime_to_ts(struct inode *inode, struct timespec64 ts) { - inode->i_mtime_sec = ts.tv_sec; - inode->i_mtime_nsec = ts.tv_nsec; + WRITE_ONCE(inode->i_mtime_sec, ts.tv_sec); + WRITE_ONCE(inode->i_mtime_nsec, ts.tv_nsec); return ts; } @@ -1677,12 +1677,12 @@ static inline struct timespec64 inode_set_mtime(struct inode *inode, static inline time64_t inode_get_ctime_sec(const struct inode *inode) { - return inode->i_ctime_sec; + return READ_ONCE(inode->i_ctime_sec); } static inline long inode_get_ctime_nsec(const struct inode *inode) { - return inode->i_ctime_nsec & ~I_CTIME_QUERIED; + return READ_ONCE(inode->i_ctime_nsec) & ~I_CTIME_QUERIED; } static inline struct timespec64 inode_get_ctime(const struct inode *inode) -- cgit v1.2.3 From f7f4665dc520fd8bbc1db20e59945773e03744a0 Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Mon, 20 Jul 2026 03:39:33 -0700 Subject: fs/pipe: unify the page pools into a single per-pipe pool Pipes keep two separate page caches: a) The per-pipe, lock-protected tmp_page[2] b) An on-stack anon_pipe_prealloc burst pool of up to eight pages filled before the lock Converge them into a single per-pipe pool (struct anon_pipe_prealloc embedded in pipe_inode_info) with the same budget as before: up to PIPE_PREALLOC_MAX (8) pages, trimmed back to PIPE_PREALLOC_KEEP (2) after each operation. tmp_page[2] is removed. Pages are still allocated and freed outside pipe->mutex; only the assignment into the pool is done under it. The pool count is also read locklessly in the prefill path, so it is annotated __data_racy. anon_pipe_prefill_and_lock() tops the pool up to the write's page count -- and returns with pipe->mutex held, so a write acquires the lock only once. anon_pipe_trim_and_unlock() trims the pool under that same lock before dropping it, then frees the excess. Signed-off-by: Breno Leitao Link: https://patch.msgid.link/20260720-b4-pipe-unification-v5-1-9002a3fe5e6d@debian.org Reviewed-by: Mateusz Guzik Reviewed-by: Oleg Nesterov Signed-off-by: Christian Brauner (Amutable) --- fs/pipe.c | 171 ++++++++++++++++++++-------------------------- include/linux/pipe_fs_i.h | 22 +++++- 2 files changed, 94 insertions(+), 99 deletions(-) diff --git a/fs/pipe.c b/fs/pipe.c index 429b0714ec57..3c6061cefe79 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -111,75 +111,95 @@ void pipe_double_lock(struct pipe_inode_info *pipe1, pipe_lock(pipe2); } -#define PIPE_PREALLOC_MAX 8 +static struct page *anon_pipe_prealloc_pop(struct anon_pipe_prealloc *prealloc) +{ + if (!prealloc->count) + return NULL; -struct anon_pipe_prealloc { - struct page *pages[PIPE_PREALLOC_MAX]; - unsigned int count; -}; + prealloc->count--; + + return prealloc->pages[prealloc->count]; +} + +/* Push a page to the prealloc pool. Returns true if added, false if full. */ +static bool anon_pipe_prealloc_push(struct anon_pipe_prealloc *prealloc, + struct page *page) +{ + if (prealloc->count >= PIPE_PREALLOC_MAX) + return false; + prealloc->pages[prealloc->count++] = page; + return true; +} /* - * Pre-allocate pages outside pipe->mutex for multi-page writes. - * alloc_page() with GFP_HIGHUSER can sleep in reclaim and runs memcg - * charging; doing it under the mutex stalls a concurrent reader. - * - * Loop alloc_page() instead of alloc_pages_bulk_*(): the bulk path refuses - * __GFP_ACCOUNT under memcg (see commit 8dcb3060d81d "memcg: page_alloc: - * skip bulk allocator for __GFP_ACCOUNT") and silently degrades to a single - * page. A per-page loop keeps memcg accounting and the task NUMA mempolicy - * honoured for every page; the per-call overhead is small compared to the - * pipe->mutex hold-time being shrunk. Any shortfall is covered by the - * in-lock alloc_page() fallback in anon_pipe_get_page(). + * Top up the pipe's own pool, then take pipe->mutex and return with it held. + * The shortfall is allocated outside the lock; the push and the caller's write + * then run under a single lock acquisition, avoiding a separate prefill + * lock/unlock cycle. anon_pipe_get_page() drains the pool instead of allocating + * under the lock. */ -static void anon_pipe_get_page_prealloc(struct anon_pipe_prealloc *prealloc, - size_t total_len) +static void anon_pipe_prefill_and_lock(struct pipe_inode_info *pipe, size_t total_len) { - unsigned int want, i; - struct page *page; - - prealloc->count = 0; - if (total_len <= PAGE_SIZE) - return; + struct page *pages[PIPE_PREALLOC_MAX]; + unsigned int want, have, need, n = 0; want = min_t(unsigned int, DIV_ROUND_UP(total_len, PAGE_SIZE), PIPE_PREALLOC_MAX); + /* Unlocked read; the pool is refilled under the lock below. */ + have = min_t(unsigned int, READ_ONCE(pipe->prealloc.count), want); + need = want - have; + + if (!need) { + mutex_lock(&pipe->mutex); + return; + } + + while (n < need) { + struct page *page = alloc_page(GFP_HIGHUSER | __GFP_ACCOUNT); - for (i = 0; i < want; i++) { - page = alloc_page(GFP_HIGHUSER | __GFP_ACCOUNT); if (!page) break; - prealloc->pages[prealloc->count++] = page; + pages[n++] = page; } + + mutex_lock(&pipe->mutex); + while (n && anon_pipe_prealloc_push(&pipe->prealloc, pages[n - 1])) + n--; + + /* + * Just flush any extra page that got affected by the TOCTOU + * effect + */ + while (n) + put_page(pages[--n]); } -static struct page *anon_pipe_prealloc_pop(struct anon_pipe_prealloc *prealloc) +/* + * Called with pipe->mutex held. Trim the pool down to PIPE_PREALLOC_KEEP under + * the lock, drop it, then free the excess outside the critical section. + */ +static void anon_pipe_trim_and_unlock(struct pipe_inode_info *pipe) { - if (!prealloc->count) - return NULL; + struct page *excess[PIPE_PREALLOC_MAX]; + unsigned int nexcess = 0; - prealloc->count--; + while (pipe->prealloc.count > PIPE_PREALLOC_KEEP) + excess[nexcess++] = anon_pipe_prealloc_pop(&pipe->prealloc); + mutex_unlock(&pipe->mutex); - return prealloc->pages[prealloc->count]; + while (nexcess) + put_page(excess[--nexcess]); } -static struct page *anon_pipe_get_page(struct pipe_inode_info *pipe, - struct anon_pipe_prealloc *prealloc) +static struct page *anon_pipe_get_page(struct pipe_inode_info *pipe) { struct page *page; - /* Drain prealloc first to keep tmp_page[] hot for later small writes. */ - page = anon_pipe_prealloc_pop(prealloc); + /* Drain the prealloc pool before allocating. Called with mutex held. */ + page = anon_pipe_prealloc_pop(&pipe->prealloc); if (page) return page; - for (int i = 0; i < ARRAY_SIZE(pipe->tmp_page); i++) { - if (pipe->tmp_page[i]) { - page = pipe->tmp_page[i]; - pipe->tmp_page[i] = NULL; - return page; - } - } - /* FWIW: This is called with pipe->mutex held */ return alloc_page(GFP_HIGHUSER | __GFP_ACCOUNT); } @@ -187,48 +207,11 @@ static struct page *anon_pipe_get_page(struct pipe_inode_info *pipe, static void anon_pipe_put_page(struct pipe_inode_info *pipe, struct page *page) { - if (page_count(page) == 1) { - for (int i = 0; i < ARRAY_SIZE(pipe->tmp_page); i++) { - if (!pipe->tmp_page[i]) { - pipe->tmp_page[i] = page; - return; - } - } - } - - put_page(page); -} - -/* - * Stash leftover prealloc pages in tmp_page[] so the next write to this - * pipe gets a hot page without entering the allocator. - */ -static void anon_pipe_refill_tmp_pages(struct pipe_inode_info *pipe, - struct anon_pipe_prealloc *prealloc) -{ - int i, idx; - - if (!prealloc->count) + if (page_count(page) == 1 && + anon_pipe_prealloc_push(&pipe->prealloc, page)) return; - for (i = 0; i < ARRAY_SIZE(pipe->tmp_page); i++) { - if (pipe->tmp_page[i]) - continue; - if (!prealloc->count) - return; - idx = --prealloc->count; - pipe->tmp_page[i] = prealloc->pages[idx]; - prealloc->pages[idx] = NULL; - } -} - -/* Runs after mutex_unlock() to keep put_page() out of the critical section. */ -static void anon_pipe_free_pages(struct anon_pipe_prealloc *prealloc) -{ - while (prealloc->count) { - prealloc->count--; - put_page(prealloc->pages[prealloc->count]); - } + put_page(page); } static void anon_pipe_buf_release(struct pipe_inode_info *pipe, @@ -485,7 +468,8 @@ anon_pipe_read(struct kiocb *iocb, struct iov_iter *to) } if (pipe_is_empty(pipe)) wake_next_reader = false; - mutex_unlock(&pipe->mutex); + /* Consumed buffers may have refilled the pool; trim it and unlock. */ + anon_pipe_trim_and_unlock(pipe); if (wake_writer) wake_up_interruptible_sync_poll(&pipe->wr_wait, EPOLLOUT | EPOLLWRNORM); @@ -524,7 +508,6 @@ anon_pipe_write(struct kiocb *iocb, struct iov_iter *from) { struct file *filp = iocb->ki_filp; struct pipe_inode_info *pipe = filp->private_data; - struct anon_pipe_prealloc prealloc; unsigned int head; ssize_t ret = 0; size_t total_len = iov_iter_count(from); @@ -548,9 +531,7 @@ anon_pipe_write(struct kiocb *iocb, struct iov_iter *from) if (unlikely(total_len == 0)) return 0; - anon_pipe_get_page_prealloc(&prealloc, total_len); - - mutex_lock(&pipe->mutex); + anon_pipe_prefill_and_lock(pipe, total_len); if (!pipe->readers) { if ((iocb->ki_flags & IOCB_NOSIGNAL) == 0) @@ -607,7 +588,7 @@ anon_pipe_write(struct kiocb *iocb, struct iov_iter *from) struct page *page; int copied; - page = anon_pipe_get_page(pipe, &prealloc); + page = anon_pipe_get_page(pipe); if (unlikely(!page)) { if (!ret) ret = -ENOMEM; @@ -671,11 +652,9 @@ anon_pipe_write(struct kiocb *iocb, struct iov_iter *from) wake_next_writer = true; } out: - anon_pipe_refill_tmp_pages(pipe, &prealloc); if (pipe_is_full(pipe)) wake_next_writer = false; - mutex_unlock(&pipe->mutex); - anon_pipe_free_pages(&prealloc); + anon_pipe_trim_and_unlock(pipe); /* * If we do do a wakeup event, we do a 'sync' wakeup, because we @@ -956,10 +935,8 @@ void free_pipe_info(struct pipe_inode_info *pipe) if (pipe->watch_queue) put_watch_queue(pipe->watch_queue); #endif - for (i = 0; i < ARRAY_SIZE(pipe->tmp_page); i++) { - if (pipe->tmp_page[i]) - __free_page(pipe->tmp_page[i]); - } + for (i = 0; i < pipe->prealloc.count; i++) + __free_page(pipe->prealloc.pages[i]); kfree(pipe->bufs); kfree(pipe); } diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h index 7f6a92ac9704..9e8b60ad806a 100644 --- a/include/linux/pipe_fs_i.h +++ b/include/linux/pipe_fs_i.h @@ -14,6 +14,9 @@ #define PIPE_BUF_FLAG_LOSS 0x40 /* Message loss happened after this buffer */ #endif +#define PIPE_PREALLOC_MAX 8 /* max pages in prealloc pool */ +#define PIPE_PREALLOC_KEEP 2 /* keep at least this many after trim */ + /** * struct pipe_buffer - a linux kernel pipe buffer * @page: the page containing the data for the pipe buffer @@ -58,6 +61,21 @@ union pipe_index { }; }; +/** + * struct anon_pipe_prealloc - per-pipe page preallocation pool + * @pages: array of cached pages (pool) + * @count: number of pages currently in the pool + * + * Each pipe keeps a small bounded pool of preallocated pages to reduce + * allocation overhead during writes. The pool is bounded at PIPE_PREALLOC_MAX + * and trimmed down to PIPE_PREALLOC_KEEP after a write completes. + */ +struct anon_pipe_prealloc { + struct page *pages[PIPE_PREALLOC_MAX]; + + unsigned int __data_racy count; +}; + /** * struct pipe_inode_info - a linux kernel pipe * @mutex: mutex protecting the whole thing @@ -68,7 +86,7 @@ union pipe_index { * @max_usage: The maximum number of slots that may be used in the ring * @ring_size: total number of buffers (should be a power of 2) * @nr_accounted: The amount this pipe accounts for in user->pipe_bufs - * @tmp_page: cached released page + * @prealloc: per-pipe page preallocation pool * @readers: number of current readers of this pipe * @writers: number of current writers of this pipe * @files: number of struct file referring this pipe (protected by ->i_lock) @@ -99,7 +117,7 @@ struct pipe_inode_info { #ifdef CONFIG_WATCH_QUEUE bool note_loss; #endif - struct page *tmp_page[2]; + struct anon_pipe_prealloc prealloc; struct fasync_struct *fasync_readers; struct fasync_struct *fasync_writers; struct pipe_buffer *bufs; -- cgit v1.2.3 From cb6a7cc2bd7bb361c313a785eb76edc60de7bdce Mon Sep 17 00:00:00 2001 From: Shivank Sharma Date: Thu, 16 Jul 2026 21:39:44 +0530 Subject: initramfs: fix typo in reserve_initrd_mem comment Fix a minor typo in the comment inside reserve_initrd_mem. Change "virtul" to "virtual". Signed-off-by: Shivank Sharma Link: https://patch.msgid.link/20260716160944.1331096-1-shivanksharma2376543@gmail.com Signed-off-by: Christian Brauner (Amutable) --- init/initramfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/init/initramfs.c b/init/initramfs.c index 20a18fcda48e..55c17c8f3991 100644 --- a/init/initramfs.c +++ b/init/initramfs.c @@ -618,7 +618,7 @@ void __init reserve_initrd_mem(void) phys_addr_t start; unsigned long size; - /* Ignore the virtul address computed during device tree parsing */ + /* Ignore the virtual address computed during device tree parsing */ initrd_start = initrd_end = 0; if (!phys_initrd_size) -- cgit v1.2.3 From 91e27ed8a387c156f72175748de48db9ede74237 Mon Sep 17 00:00:00 2001 From: Mateusz Guzik Date: Fri, 24 Jul 2026 19:14:21 +0200 Subject: lockref: tidy up dead count handling 1. put the dead val into a macro so that it can be used in other places 2. __lockref_is_dead(): - drop the __ suffix, this is not an internal routine - drop the spurious cast, the value is already a signed int - use READ_ONCE to prevent any compile shenanigans 3. provide lockref_is_dead_or_zero() Signed-off-by: Mateusz Guzik Link: https://patch.msgid.link/20260724171422.429284-2-mjguzik@gmail.com Signed-off-by: Christian Brauner (Amutable) --- fs/ceph/dir.c | 2 +- fs/erofs/zdata.c | 4 ++-- fs/gfs2/glock.c | 6 +++--- fs/gfs2/lock_dlm.c | 6 +++--- fs/gfs2/quota.c | 4 ++-- fs/xfs/xfs_buf.c | 4 ++-- fs/xfs/xfs_qm.c | 4 ++-- include/linux/lockref.h | 12 ++++++++++-- lib/lockref.c | 2 +- 9 files changed, 26 insertions(+), 18 deletions(-) diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c index 32a48550eacf..ab4806ea790a 100644 --- a/fs/ceph/dir.c +++ b/fs/ceph/dir.c @@ -1667,7 +1667,7 @@ __dentry_leases_walk(struct ceph_mds_client *mdsc, if (!spin_trylock(&dentry->d_lock)) continue; - if (__lockref_is_dead(&dentry->d_lockref)) { + if (lockref_is_dead(&dentry->d_lockref)) { list_del_init(&di->lease_list); goto next; } diff --git a/fs/erofs/zdata.c b/fs/erofs/zdata.c index 74520e910259..d022d1dff5a1 100644 --- a/fs/erofs/zdata.c +++ b/fs/erofs/zdata.c @@ -725,7 +725,7 @@ static bool z_erofs_get_pcluster(struct z_erofs_pcluster *pcl) return true; spin_lock(&pcl->lockref.lock); - if (__lockref_is_dead(&pcl->lockref)) { + if (lockref_is_dead(&pcl->lockref)) { spin_unlock(&pcl->lockref.lock); return false; } @@ -945,7 +945,7 @@ static void z_erofs_put_pcluster(struct erofs_sb_info *sbi, if (lockref_put_or_lock(&pcl->lockref)) return; - DBG_BUGON(__lockref_is_dead(&pcl->lockref)); + DBG_BUGON(lockref_is_dead(&pcl->lockref)); if (!--pcl->lockref.count) { if (try_free && xa_trylock(&sbi->managed_pslots)) { free = __erofs_try_to_release_pcluster(sbi, pcl); diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index b8a144d3a73b..eaa2980051ed 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -2080,7 +2080,7 @@ static void clear_glock(struct gfs2_glock *gl) gfs2_glock_remove_from_lru(gl); spin_lock(&gl->gl_lockref.lock); - if (!__lockref_is_dead(&gl->gl_lockref)) { + if (!lockref_is_dead(&gl->gl_lockref)) { gl->gl_lockref.count++; if (gl->gl_state != LM_ST_UNLOCKED) request_demote(gl, LM_ST_UNLOCKED, 0, false); @@ -2115,7 +2115,7 @@ static void dump_glock_func(struct gfs2_glock *gl) static void withdraw_glock(struct gfs2_glock *gl) { spin_lock(&gl->gl_lockref.lock); - if (!__lockref_is_dead(&gl->gl_lockref)) { + if (!lockref_is_dead(&gl->gl_lockref)) { /* * We don't want to write back any more dirty data. Unlock the * remaining inode and resource group glocks; this will cause @@ -2483,7 +2483,7 @@ static void gfs2_glock_iter_next(struct gfs2_glock_iter *gi, loff_t n) continue; break; } else { - if (__lockref_is_dead(&gl->gl_lockref)) + if (lockref_is_dead(&gl->gl_lockref)) continue; n--; } diff --git a/fs/gfs2/lock_dlm.c b/fs/gfs2/lock_dlm.c index 7828ad0b6f5a..cc901fb97da0 100644 --- a/fs/gfs2/lock_dlm.c +++ b/fs/gfs2/lock_dlm.c @@ -126,7 +126,7 @@ static void gdlm_ast(void *arg) clear_bit(GLF_BLOCKING, &gl->gl_flags); /* If the glock is dead, we only react to a dlm_unlock() reply. */ - if (__lockref_is_dead(&gl->gl_lockref) && + if (lockref_is_dead(&gl->gl_lockref) && gl->gl_lksb.sb_status != -DLM_EUNLOCK) return; @@ -182,7 +182,7 @@ static void gdlm_bast(void *arg, int mode) { struct gfs2_glock *gl = arg; - if (__lockref_is_dead(&gl->gl_lockref)) + if (lockref_is_dead(&gl->gl_lockref)) return; switch (mode) { @@ -329,7 +329,7 @@ static void gdlm_put_lock(struct gfs2_glock *gl) uint32_t flags = 0; int error; - BUG_ON(!__lockref_is_dead(&gl->gl_lockref)); + BUG_ON(!lockref_is_dead(&gl->gl_lockref)); if (test_bit(GLF_INITIAL, &gl->gl_flags)) { gfs2_glock_free(gl); diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 91e9975d25e8..001c8b39ca55 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -342,7 +342,7 @@ static void qd_put(struct gfs2_quota_data *qd) if (lockref_put_or_lock(&qd->qd_lockref)) return; - BUG_ON(__lockref_is_dead(&qd->qd_lockref)); + BUG_ON(lockref_is_dead(&qd->qd_lockref)); sdp = qd->qd_sbd; if (unlikely(!test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags))) { lockref_mark_dead(&qd->qd_lockref); @@ -486,7 +486,7 @@ static bool qd_grab_sync(struct gfs2_sbd *sdp, struct gfs2_quota_data *qd, qd->qd_sync_gen >= sync_gen) goto out; - if (__lockref_is_dead(&qd->qd_lockref)) + if (lockref_is_dead(&qd->qd_lockref)) goto out; qd->qd_lockref.count++; diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c index 3ce12fe1c307..be1577f51c91 100644 --- a/fs/xfs/xfs_buf.c +++ b/fs/xfs/xfs_buf.c @@ -80,7 +80,7 @@ xfs_buf_stale( spin_lock(&bp->b_lockref.lock); atomic_set(&bp->b_lru_ref, 0); - if (!__lockref_is_dead(&bp->b_lockref)) + if (!lockref_is_dead(&bp->b_lockref)) list_lru_del_obj(&bp->b_target->bt_lru, &bp->b_lru); spin_unlock(&bp->b_lockref.lock); } @@ -826,7 +826,7 @@ static void xfs_buf_destroy( struct xfs_buf *bp) { - ASSERT(__lockref_is_dead(&bp->b_lockref)); + ASSERT(lockref_is_dead(&bp->b_lockref)); ASSERT(!(bp->b_flags & _XBF_DELWRI_Q)); if (bp->b_pag) diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c index aa0d2976f1c3..960f86b02c28 100644 --- a/fs/xfs/xfs_qm.c +++ b/fs/xfs/xfs_qm.c @@ -128,7 +128,7 @@ xfs_qm_dqpurge( struct xfs_quotainfo *qi = dqp->q_mount->m_quotainfo; spin_lock(&dqp->q_lockref.lock); - if (dqp->q_lockref.count > 0 || __lockref_is_dead(&dqp->q_lockref)) { + if (dqp->q_lockref.count > 0 || lockref_is_dead(&dqp->q_lockref)) { spin_unlock(&dqp->q_lockref.lock); return -EAGAIN; } @@ -430,7 +430,7 @@ xfs_qm_dquot_isolate( * from the LRU, leave it for the freeing task to complete the freeing * process rather than risk it being free from under us here. */ - if (__lockref_is_dead(&dqp->q_lockref)) + if (lockref_is_dead(&dqp->q_lockref)) goto out_miss_unlock; /* diff --git a/include/linux/lockref.h b/include/linux/lockref.h index 6ded24cdb4a8..ddfb7d3b8cec 100644 --- a/include/linux/lockref.h +++ b/include/linux/lockref.h @@ -34,6 +34,8 @@ struct lockref { }; }; +#define __LOCKREF_DEAD_VAL -128 + /** * lockref_init - Initialize a lockref * @lockref: pointer to lockref structure @@ -55,9 +57,15 @@ void lockref_mark_dead(struct lockref *lockref); bool lockref_get_not_dead(struct lockref *lockref); /* Must be called under spinlock for reliable results */ -static inline bool __lockref_is_dead(const struct lockref *l) +static inline bool lockref_is_dead(const struct lockref *l) +{ + return (READ_ONCE(l->count) == __LOCKREF_DEAD_VAL); +} + +static inline bool lockref_is_dead_or_zero(const struct lockref *l) { - return ((int)l->count < 0); + int count = READ_ONCE(l->count); + return (count == __LOCKREF_DEAD_VAL || count == 0); } #endif /* __LINUX_LOCKREF_H */ diff --git a/lib/lockref.c b/lib/lockref.c index 5d8e3ef3860e..9b3dd688d8cd 100644 --- a/lib/lockref.c +++ b/lib/lockref.c @@ -131,7 +131,7 @@ EXPORT_SYMBOL(lockref_put_or_lock); void lockref_mark_dead(struct lockref *lockref) { assert_spin_locked(&lockref->lock); - lockref->count = -128; + lockref->count = __LOCKREF_DEAD_VAL; } EXPORT_SYMBOL(lockref_mark_dead); -- cgit v1.2.3 From b6f946cc42f62b82f014d67bc5eea0662d4f5584 Mon Sep 17 00:00:00 2001 From: Mateusz Guzik Date: Fri, 24 Jul 2026 19:14:22 +0200 Subject: dcache: use lockref routines for dead count checks Signed-off-by: Mateusz Guzik Link: https://patch.msgid.link/20260724171422.429284-3-mjguzik@gmail.com Signed-off-by: Christian Brauner (Amutable) --- fs/dcache.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/fs/dcache.c b/fs/dcache.c index 3e9af9de7074..2aee85f3fbaa 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -434,7 +434,7 @@ static inline void __d_clear_type_and_inode(struct dentry *dentry) static void dentry_free(struct dentry *dentry) { DENTRY_WARN_ONCE(d_really_is_positive(dentry), dentry); - DENTRY_WARN_ONCE(dentry->d_lockref.count >= 0, dentry); + DENTRY_WARN_ONCE(!lockref_is_dead(&dentry->d_lockref), dentry); D_FLAG_VERIFY(dentry, 0); if (unlikely(dname_external(dentry))) { struct external_name *p = external_name(dentry); @@ -782,7 +782,7 @@ static bool lock_for_kill(struct dentry *dentry) * * If @dentry is idle and remains such after we assemble the full * locking environment for eviction (see lock_for_kill() for details) - * we mark it doomed (->d_lockref.count < 0) and proceed to detaching + * we mark it doomed (see lockref_mark_dead()) and proceed to detaching * it from any filesystem objects. Otherwise we drop ->d_lock and * return %NULL. * @@ -946,7 +946,7 @@ static inline bool fast_dput(struct dentry *dentry) if (unlikely(ret < 0)) { spin_lock(&dentry->d_lock); rcu_read_unlock(); - if (WARN_ON_ONCE(dentry->d_lockref.count <= 0)) { + if (WARN_ON_ONCE(lockref_is_dead_or_zero(&dentry->d_lockref))) { spin_unlock(&dentry->d_lock); return true; } @@ -1644,7 +1644,7 @@ static enum d_walk_ret select_collect(void *_data, struct dentry *dentry) if (data->start == dentry) goto out; - if (dentry->d_lockref.count <= 0) { + if (lockref_is_dead_or_zero(&dentry->d_lockref)) { __move_to_shrink_list(dentry, &data->dispose); data->found++; } @@ -1676,7 +1676,7 @@ static enum d_walk_ret select_collect2(void *_data, struct dentry *dentry) if (data->start == dentry) goto out; - if (dentry->d_lockref.count <= 0) { + if (lockref_is_dead_or_zero(&dentry->d_lockref)) { if (!__move_to_shrink_list(dentry, &data->dispose)) { /* * We need an enter RCU read-side critical area that @@ -1747,7 +1747,7 @@ static void shrink_dcache_tree(struct dentry *parent, bool for_umount) spin_lock(&v->d_lock); rcu_read_unlock(); - if (unlikely(v->d_lockref.count < 0)) { + if (unlikely(lockref_is_dead(&v->d_lockref))) { // It's doomed; if it isn't dead yet, notify us // once it becomes invisible to d_walk(). need_wait = d_add_waiter(v, &wait); @@ -1823,7 +1823,7 @@ void shrink_dcache_for_umount(struct super_block *sb) spin_unlock(&sb->s_roots_lock); spin_lock(&dentry->d_lock); rcu_read_unlock(); - if (unlikely(dentry->d_lockref.count < 0)) { + if (unlikely(lockref_is_dead(&dentry->d_lockref))) { struct completion_list wait; bool need_wait = d_add_waiter(dentry, &wait); @@ -2822,7 +2822,7 @@ retry: spin_lock(&dentry->d_lock); rcu_read_unlock(); /* now we can try to grab a reference */ - if (unlikely(dentry->d_lockref.count < 0)) { + if (unlikely(lockref_is_dead(&dentry->d_lockref))) { spin_unlock(&dentry->d_lock); goto retry; } -- cgit v1.2.3 From 42c8ed5835921a7b2523517fd6caad1d79b69146 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Thu, 23 Jul 2026 20:10:21 -0700 Subject: nstree: add/fix struct ns_id_req kernel-doc member fields - drop non-existent @filter - add missing descriptions for @ns_type and @spare2 - change the descriptions of @ns_id and @user_ns_id based on their commit to prevent these kernel-doc warnings: Warning: ../include/uapi/linux/nsfs.h:117 struct member 'ns_type' not described in 'ns_id_req' Warning: ../include/uapi/linux/nsfs.h:117 struct member 'spare2' not described in 'ns_id_req' Warning: ../include/uapi/linux/nsfs.h:117 Excess struct member 'filter' description in 'ns_id_req' Fixes: 76b6f5dfb3fd ("nstree: add listns()") Signed-off-by: Randy Dunlap Link: https://patch.msgid.link/20260724031021.814599-1-rdunlap@infradead.org Signed-off-by: Christian Brauner (Amutable) --- include/uapi/linux/nsfs.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/include/uapi/linux/nsfs.h b/include/uapi/linux/nsfs.h index a25e38d1c874..007fed5971b4 100644 --- a/include/uapi/linux/nsfs.h +++ b/include/uapi/linux/nsfs.h @@ -96,9 +96,10 @@ enum ns_type { * struct ns_id_req - namespace ID request structure * @size: size of this structure * @spare: reserved for future use - * @filter: filter mask - * @ns_id: last namespace id - * @user_ns_id: owning user namespace ID + * @ns_id: last namespace ID + * @ns_type: bit mask of namespace types to include + * @spare2: reserved for future use + * @user_ns_id: filter on this user namespace ID (or 0) * * Structure for passing namespace ID and miscellaneous parameters to * statns(2) and listns(2). -- cgit v1.2.3 From 4902e56525076d2f7241e3a2f612d19be84900a9 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 27 Jul 2026 13:57:30 -0600 Subject: seq_file: rename mangle_path to seq_mangle_path The symbol mangle_path conflicts with a gcov symbol which can break the build of ARCH=um with gcov, and it's also not very specific and descriptive. Rename mangle_path() to seq_mangle_path(), and also remove the export since it's not needed or used by any modules. Signed-off-by: Johannes Berg Signed-off-by: Alex Hung Link: https://patch.msgid.link/20260727195730.2306887-1-alex.hung@amd.com Signed-off-by: Christian Brauner (Amutable) --- fs/seq_file.c | 11 +++++------ include/linux/seq_file.h | 2 +- lib/seq_buf.c | 2 +- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/fs/seq_file.c b/fs/seq_file.c index 4745db2a34d1..456c78719fd0 100644 --- a/fs/seq_file.c +++ b/fs/seq_file.c @@ -428,7 +428,7 @@ EXPORT_SYMBOL(seq_bprintf); #endif /* CONFIG_BINARY_PRINTF */ /** - * mangle_path - mangle and copy path to buffer beginning + * seq_mangle_path - mangle and copy path to buffer beginning * @s: buffer start * @p: beginning of path in above buffer * @esc: set of characters that need escaping @@ -438,7 +438,7 @@ EXPORT_SYMBOL(seq_bprintf); * Returns pointer past last written character in @s, or NULL in case of * failure. */ -char *mangle_path(char *s, const char *p, const char *esc) +char *seq_mangle_path(char *s, const char *p, const char *esc) { while (s <= p) { char c = *p++; @@ -457,7 +457,6 @@ char *mangle_path(char *s, const char *p, const char *esc) } return NULL; } -EXPORT_SYMBOL(mangle_path); /** * seq_path - seq_file interface to print a pathname @@ -477,7 +476,7 @@ int seq_path(struct seq_file *m, const struct path *path, const char *esc) if (size) { char *p = d_path(path, buf, size); if (!IS_ERR(p)) { - char *end = mangle_path(buf, p, esc); + char *end = seq_mangle_path(buf, p, esc); if (end) res = end - buf; } @@ -520,7 +519,7 @@ int seq_path_root(struct seq_file *m, const struct path *path, return SEQ_SKIP; res = PTR_ERR(p); if (!IS_ERR(p)) { - char *end = mangle_path(buf, p, esc); + char *end = seq_mangle_path(buf, p, esc); if (end) res = end - buf; else @@ -544,7 +543,7 @@ int seq_dentry(struct seq_file *m, struct dentry *dentry, const char *esc) if (size) { char *p = dentry_path(dentry, buf, size); if (!IS_ERR(p)) { - char *end = mangle_path(buf, p, esc); + char *end = seq_mangle_path(buf, p, esc); if (end) res = end - buf; } diff --git a/include/linux/seq_file.h b/include/linux/seq_file.h index 2fb266ea69fa..dc0e8c62d9e0 100644 --- a/include/linux/seq_file.h +++ b/include/linux/seq_file.h @@ -104,7 +104,7 @@ static inline void seq_setwidth(struct seq_file *m, size_t size) } void seq_pad(struct seq_file *m, char c); -char *mangle_path(char *s, const char *p, const char *esc); +char *seq_mangle_path(char *s, const char *p, const char *esc); int seq_open(struct file *, const struct seq_operations *); ssize_t seq_read(struct file *, char __user *, size_t, loff_t *); ssize_t seq_read_iter(struct kiocb *iocb, struct iov_iter *iter); diff --git a/lib/seq_buf.c b/lib/seq_buf.c index b59488fa8135..a92093f346da 100644 --- a/lib/seq_buf.c +++ b/lib/seq_buf.c @@ -321,7 +321,7 @@ int seq_buf_path(struct seq_buf *s, const struct path *path, const char *esc) if (size) { char *p = d_path(path, buf, size); if (!IS_ERR(p)) { - char *end = mangle_path(buf, p, esc); + char *end = seq_mangle_path(buf, p, esc); if (end) res = end - buf; } -- cgit v1.2.3 From 78db93943210df61c8446aae35af6836e2cf04aa Mon Sep 17 00:00:00 2001 From: Karl Mehltretter Date: Wed, 29 Jul 2026 02:59:33 +0200 Subject: dcache: keep shrink_dcache_for_umount() making progress on busy roots Commit e9895609cb7f ("wind ->s_roots via ->d_sib instead of ->d_hash") moved secondary roots from ->d_hash to ->d_sib. Secondary roots are now d_unhashed(), so __d_drop() returns without removing them from ->s_roots. Consequently, d_drop() in do_one_tree() no longer guarantees progress through the list. If a secondary root is still busy once do_one_tree() is done with it, its final dput() cannot evict it. The root remains ->s_roots.first and the loop selects it forever, holding ->s_umount for write and repeatedly reporting the same dentry. The root does not need a leaked reference of its own for that. Every child pins its parent (d_alloc() takes a reference on it) and umount_check() deliberately reports a busy descendant instead of complaining about its ancestors, so a single leaked dentry reference anywhere below a secondary root is enough. For filesystems that build ->s_root with d_obtain_root() - nfs, ceph, nilfs2 snapshot mounts - that is the entire tree. Before e9895609cb7f, ___d_drop() special-cased IS_ROOT dentries and removed them from ->s_roots regardless of their refcount, so the d_drop() in do_one_tree() detached the root from the superblock no matter what. Commit 9c8c10e262e0 ("more graceful recovery in umount_collect()") deliberately made busy dentries nonfatal: report them and finish the unmount rather than BUG() while holding ->s_umount. Restore that by detaching the root in do_one_tree() itself, next to the d_drop() that used to do it. That covers both callers - the ->s_roots loop and ->s_root, which for the filesystems above is a secondary root as well. In the normal case dentry_unlist() finds ->d_sib already unhashed when eviction occurs. A permanently leaked reference remains leaked after unmount, as it did before e9895609cb7f; if the extra reference is merely delayed, its final dput() may run after teardown has advanced. Leaving the root on ->s_roots is not an alternative: the superblock would then be freed with a live dentry still linked into it, and that dentry's dentry_unlist() would take ->s_roots_lock on freed memory. Christian Brauner says: Moved the ->s_roots removal from the shrink_dcache_for_umount() loop into do_one_tree(), so a busy ->s_root obtained from d_obtain_root() is detached on the first pass instead of being reported a second time when the loop picks it off ->s_roots. Extended the commit message with the pinned-ancestor case. Fixes: e9895609cb7f ("wind ->s_roots via ->d_sib instead of ->d_hash") Signed-off-by: Karl Mehltretter Link: https://patch.msgid.link/20260729005933.15858-1-kmehltretter@gmail.com Signed-off-by: Christian Brauner (Amutable) --- fs/dcache.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/fs/dcache.c b/fs/dcache.c index 2aee85f3fbaa..1b1a81f10da6 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1794,7 +1794,12 @@ static void do_one_tree(struct dentry *dentry) { shrink_dcache_tree(dentry, true); d_walk(dentry, dentry, umount_check); - d_drop(dentry); + spin_lock(&dentry->d_lock); + __d_drop(dentry); + /* A busy root survives the dput() below so don't leave it on ->s_roots. */ + if (unlikely(!hlist_unhashed(&dentry->d_sib))) + unlink_secondary_root(dentry); + spin_unlock(&dentry->d_lock); dput(dentry); } -- cgit v1.2.3 From bf6c571a80a1ae9141610ce94efcf4e5b93bb805 Mon Sep 17 00:00:00 2001 From: Jann Horn Date: Mon, 3 Aug 2026 21:46:19 +0200 Subject: fs: document semantics of kstat::{uid,gid} fields The uid stored in struct kstat is logically a vfsuid; file systems initialize it by converting a kuid (filesystem perspective) to a vfsuid (mount perspective), then use vfsuid_into_kuid(), which essentially just typecasts from vfsuid to kuid. For now, just add a comment to note this mismatch between C type and semantic type. Below are some notes for anyone who wants to refactor this in the future. There are probably two options to refactor this away: 1. Change the type of kstat::uid to vfsuid_t, and perform the conversion from vfsuid to userspace-uid in the VFS layer. This wouldn't change machine code, just be more semantically correct. 2. Change the semantics of kstat::uid to really be a kuid_t, and let the VFS layer take care of doing the translation from kuid to vfsuid that is currently done in filesystem code (or in generic_fillattr, on behalf of the filesystem code). Option 2 is probably neater since it moves more logic into the generic VFS layer, and this is something that is expected to work the same way in all file systems? The following coccinelle script: ``` virtual context @@ struct kstat *stat; @@ * stat->uid @@ struct kstat *stat; @@ * stat->gid @@ struct kstat stat; @@ * stat.uid @@ struct kstat stat; @@ * stat.gid ``` detects 43 field accesses to these uid/gid fields. Signed-off-by: Jann Horn Link: https://patch.msgid.link/20260803-vfs-comment-stat-uid-v1-1-162d062b737c@google.com Reviewed-by: Jan Kara Signed-off-by: Christian Brauner (Amutable) --- include/linux/stat.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/stat.h b/include/linux/stat.h index e3d00e7bb26d..9c5709132862 100644 --- a/include/linux/stat.h +++ b/include/linux/stat.h @@ -41,8 +41,8 @@ struct kstat { u64 ino; dev_t dev; dev_t rdev; - kuid_t uid; - kgid_t gid; + kuid_t uid; /* This is logically a vfsuid_t. */ + kgid_t gid; /* This is logically a vfsgid_t. */ loff_t size; struct timespec64 atime; struct timespec64 mtime; -- cgit v1.2.3 From ed6e2047da1f64fb3d88af21627255114f07f9a1 Mon Sep 17 00:00:00 2001 From: Manush Prajwal Date: Sat, 8 Aug 2026 23:58:16 +0530 Subject: fs: fix switch/case indentation in sysfs() syscall The case labels in the sysfs(2) syscall implementation are indented one level deeper than the switch statement itself, which does not match the kernel coding style (switch and case should be at the same indentation level). Fix the indentation; no functional change. Signed-off-by: Manush Prajwal Link: https://patch.msgid.link/20260808182816.2399-1-manushprajwal555@gmail.com Signed-off-by: Christian Brauner (Amutable) --- fs/filesystems.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/fs/filesystems.c b/fs/filesystems.c index 673a03b5f32b..3083d904df9b 100644 --- a/fs/filesystems.c +++ b/fs/filesystems.c @@ -201,17 +201,17 @@ SYSCALL_DEFINE3(sysfs, int, option, unsigned long, arg1, unsigned long, arg2) int retval = -EINVAL; switch (option) { - case 1: - retval = fs_index((const char __user *) arg1); - break; + case 1: + retval = fs_index((const char __user *) arg1); + break; - case 2: - retval = fs_name(arg1, (char __user *) arg2); - break; + case 2: + retval = fs_name(arg1, (char __user *) arg2); + break; - case 3: - retval = fs_maxindex(); - break; + case 3: + retval = fs_maxindex(); + break; } return retval; } -- cgit v1.2.3 From 400696d41b469610a260a6c8a532ccfc977c91fe Mon Sep 17 00:00:00 2001 From: Yichong Chen Date: Wed, 5 Aug 2026 10:41:49 +0800 Subject: fs: remove stale inode_insert5() kernel-doc parameter inode_insert5() no longer has an isnew argument, but its kernel-doc still documents one. This triggers a W=1 kernel-doc warning. Remove the stale parameter description. Signed-off-by: Yichong Chen Link: https://patch.msgid.link/20260805024149.935769-1-chenyichong@uniontech.com Reviewed-by: Jan Kara Signed-off-by: Christian Brauner (Amutable) --- fs/inode.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/inode.c b/fs/inode.c index 238fcd1cad6e..ba7da39be4a3 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -1282,7 +1282,6 @@ EXPORT_SYMBOL(unlock_two_nondirectories); * @test: callback used for comparisons between inodes * @set: callback used to initialize a new struct inode * @data: opaque data pointer to pass to @test and @set - * @isnew: pointer to a bool which will indicate whether I_NEW is set * * Search for the inode specified by @hashval and @data in the inode cache, * and if present return it with an increased reference count. This is a -- cgit v1.2.3 From f2381b546e7e6a35c9fcee0d0ccb6c042a9aeb5d Mon Sep 17 00:00:00 2001 From: Baokun Li Date: Tue, 4 Aug 2026 11:42:04 +0800 Subject: fs: fix user path of nested backing files backing_file_open() derives the path to be stored in the new backing file from user_file->f_path. This is incorrect when user_file itself is a backing file, which is the case for nested stacking filesystems, e.g. overlayfs mounts where the lowerdir of one overlayfs is the merged directory of another. Since commit def3ae83da02 ("fs: store real path instead of fake path in backing file f_path") the f_path of a backing file holds the real path of the intermediate layer, not the path that the user opened. Commit 924577e4f6ca ("ovl: Fix nested backing file paths") fixed this for such configurations by passing file_user_path() from ovl_open_realfile(). However, commit 6af36aeb147a ("lsm: add backing_file LSM hooks") changed the first argument of backing_file_open() from the user path back to the user file and derived the path from user_file->f_path again, silently re-introducing the problem. As a result, files mapped through a nested overlayfs show the wrong path in /proc//maps and in perf/ftrace mmap records. For example, with two nested overlayfs mounts: mkdir -p /ovl/{lower,upper,work,merged} /ovl/nested echo hello > /ovl/lower/foo mount -t overlay overlay \ -o lowerdir=/ovl/lower,upperdir=/ovl/upper,workdir=/ovl/work \ /ovl/merged # at least two lowerdirs are needed when upperdir is nonexistent mount -t overlay overlay \ -o lowerdir=/ovl/merged:/ovl/lower /ovl/nested mapping /ovl/nested/foo shows a disconnected path instead of the user path: # readlink /proc/self/fd/3 /ovl/nested/foo # grep foo /proc/self/maps 7f6e2c100000-7f6e2c101000 r--s 00000000 00:24 15813027 /foo The bogus path is derived from the f_path of the intermediate backing file, whose mount is a private clone that d_path() cannot resolve. Fix this by using file_user_path(), which returns the outermost user-visible path for backing files and falls back to &user_file->f_path for regular files. This restores the behavior of commit 924577e4f6ca ("ovl: Fix nested backing file paths") for overlayfs and also fixes the same problem for the other backing_file_open() callers, fuse passthrough and erofs ishare, when their user file is itself a backing file. backing_tmpfile_open() has the same pattern but is not affected: it is only called by ovl_create_tmpfile() for the upper layer, and another overlayfs is rejected as upperdir by the DCACHE_OP_REAL check in ovl_mount_dir_check(), so its user_file can never be a backing file. Fixes: 6af36aeb147a ("lsm: add backing_file LSM hooks") Cc: stable@vger.kernel.org Signed-off-by: Baokun Li Link: https://patch.msgid.link/20260804034204.3487077-1-libaokun@linux.alibaba.com Tested-by: Paul Moore Signed-off-by: Christian Brauner (Amutable) --- fs/backing-file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/backing-file.c b/fs/backing-file.c index 080c99696cd0..cc101143f921 100644 --- a/fs/backing-file.c +++ b/fs/backing-file.c @@ -35,7 +35,7 @@ struct file *backing_file_open(const struct file *user_file, int flags, const struct path *real_path, const struct cred *cred) { - const struct path *user_path = &user_file->f_path; + const struct path *user_path = file_user_path(user_file); struct file *f; int error; -- cgit v1.2.3 From 9688a46802939da28f00cb40e8129615d5d4af39 Mon Sep 17 00:00:00 2001 From: Chen Linxuan Date: Fri, 31 Jul 2026 22:50:08 +0800 Subject: pidfd: hold exec_update_lock around namespace ioctl The PIDFD_GET_*_NAMESPACE ioctls in pidfd_ioctl() perform a filesystem credentials ptrace access check before handing out a namespace file descriptor. The accompanying comment states that the code "mirrors nsfs behavior", but, unlike the corresponding procfs paths, it does so without holding the target task's exec_update_lock. proc_ns_get_link() and proc_ns_readlink() both take exec_update_lock for reading around the ptrace check and the namespace lookup, so that the credentials used for the access decision match those of the task when its namespace is read. Without it, a caller can pass the check against the target's old credentials and then read the namespace after the target has execve()'d a setuid binary and committed new credentials -- accessing namespace information it should have been denied. Hold exec_update_lock for reading around the ptrace check and the namespace lookup so that pidfd truly mirrors nsfs behavior, as the comment already claims. open_namespace() itself runs outside the lock: once a namespace reference is obtained it carries its own refcount and is opened with the caller's own credentials, so a concurrent execve() on the target can no longer affect the outcome. Fixes: 5b08bd408534 ("pidfs: allow retrieval of namespace file descriptors") Cc: stable@vger.kernel.org Signed-off-by: Chen Linxuan Link: https://patch.msgid.link/20260731-pidfd-exec-update-lock-v1-1-b388f2f3a8b0@black-desk.cn Signed-off-by: Christian Brauner (Amutable) --- fs/pidfs.c | 43 +++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/fs/pidfs.c b/fs/pidfs.c index aaa609ddab04..7fc3c1f5a578 100644 --- a/fs/pidfs.c +++ b/fs/pidfs.c @@ -531,6 +531,7 @@ static long pidfd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) struct task_struct *task __free(put_task) = NULL; struct nsproxy *nsp __free(put_nsproxy) = NULL; struct ns_common *ns_common = NULL; + int error; if (!pidfs_ioctl_valid(cmd)) return -ENOIOCTLCMD; @@ -554,20 +555,33 @@ static long pidfd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (arg) return -EINVAL; + /* + * We're trying to open a file descriptor to the namespace so perform a + * filesystem cred ptrace check. Hold @task's exec_update_lock for the + * duration of the ptrace check and the namespace lookup so that the + * credentials used for the access decision match those of @task at the + * time its namespace is read, preventing a concurrent execve() from + * swapping the task's credentials in between the check and the use. We + * mirror nsfs behavior. + */ + error = down_read_killable(&task->signal->exec_update_lock); + if (error) + return error; + + if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) { + error = -EACCES; + goto out_unlock; + } + scoped_guard(task_lock, task) { nsp = task->nsproxy; if (nsp) get_nsproxy(nsp); } - if (!nsp) - return -ESRCH; /* just pretend it didn't exist */ - - /* - * We're trying to open a file descriptor to the namespace so perform a - * filesystem cred ptrace check. Also, we mirror nsfs behavior. - */ - if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) - return -EACCES; + if (!nsp) { + error = -ESRCH; /* just pretend it didn't exist */ + goto out_unlock; + } switch (cmd) { /* Namespaces that hang of nsproxy. */ @@ -649,11 +663,16 @@ static long pidfd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) #endif break; default: - return -ENOIOCTLCMD; + error = -ENOIOCTLCMD; } - if (!ns_common) - return -EOPNOTSUPP; + if (!error && !ns_common) + error = -EOPNOTSUPP; + +out_unlock: + up_read(&task->signal->exec_update_lock); + if (error) + return error; /* open_namespace() unconditionally consumes the reference */ return open_namespace(ns_common); -- cgit v1.2.3 From f1cebdec9739911cf066e6ce2e02c511f0d124d5 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Mon, 3 Aug 2026 14:46:25 +0200 Subject: pipe: only enable the extra wake_up(rd_wait) for EPOLLET consumers pipe_poll() unconditionally sets ->poll_usage on the first call, forcing anon_pipe_write() to wake up readers on every write even if the pipe was not empty. The reason is that some legacy epoll(EPOLLET) users depend on historical per-write wakeups, see commit 3a34b13a88ca ("pipe: make pipe writes always wake up readers"). Test-case: #include #include #include int main(void) { int pfd[2], efd; struct epoll_event evt = { .events = EPOLLIN | EPOLLET }; pipe(pfd); efd = epoll_create1(0); epoll_ctl(efd, EPOLL_CTL_ADD, pfd[0], &evt); for (int i = 0; i < 2; ++i) { write(pfd[1], "", 1); assert(epoll_wait(efd, &evt, 1, 0) == 1); } return 0; } it fails if WRITE_ONCE(poll_usage, true) is removed from pipe_poll(). However, without EPOLLET in .events, it does not need the extra wakeup and succeeds even if write() is called only once before the main loop. Currently io_uring without (unsupported) IORING_POLL_ADD_LEVEL always sets EPOLLET, and in IORING_POLL_ADD_MULTI mode it depends on per-write wakeups the same way: #include #include #include #include #include #include int main(void) { struct io_uring_params p = {}; int fd, pfd[2]; pipe(pfd); fd = syscall(SYS_io_uring_setup, 2, &p); assert(fd >= 0); void *ring = mmap(0, p.cq_off.cqes + p.cq_entries * sizeof(struct io_uring_cqe), PROT_READ | PROT_WRITE, MAP_SHARED, fd, IORING_OFF_SQ_RING); assert(ring != MAP_FAILED); *(unsigned *)(ring + p.sq_off.tail) = 1; struct io_uring_sqe *sqes = mmap(0, p.sq_entries * sizeof(*sqes), PROT_READ | PROT_WRITE, MAP_SHARED, fd, IORING_OFF_SQES); assert(sqes != MAP_FAILED); sqes[0].opcode = IORING_OP_POLL_ADD; sqes[0].fd = pfd[0]; sqes[0].len = IORING_POLL_ADD_MULTI; sqes[0].poll32_events = EPOLLIN; syscall(SYS_io_uring_enter, fd, 1, 0, 0, 0, 0); unsigned *cq_head = ring + p.cq_off.head; unsigned *cq_tail = ring + p.cq_off.tail; for (int i = 0; i < 2; ++i) { write(pfd[1], "", 1); syscall(SYS_io_uring_enter, fd, 0, 0, IORING_ENTER_GETEVENTS, 0, 0); assert(*cq_tail == ++*cq_head); } return 0; } the 2nd assert() in the main loop fails without ->poll_usage == true. Rename ->poll_usage to ->pseudo_edgetrigger to make the purpose clearer, update the comments, and change pipe_poll() to set ->pseudo_edgetrigger only if wait->_key & EPOLLET is true. This check should catch both users, and this way poll/select and epoll without EPOLLET users will not pay for the extra wakeup. Signed-off-by: Oleg Nesterov Link: https://patch.msgid.link/anCNoW-x0bcB2ggg@redhat.com Signed-off-by: Christian Brauner (Amutable) --- fs/pipe.c | 20 +++++++++++++------- include/linux/pipe_fs_i.h | 4 ++-- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/fs/pipe.c b/fs/pipe.c index 3c6061cefe79..0c77191a050c 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -665,10 +665,9 @@ out: * how (for example) the GNU make jobserver uses small writes to * wake up pending jobs * - * Epoll nonsensically wants a wakeup whether the pipe - * was already empty or not. + * ->pseudo_edgetrigger enables per-write wakeups, see pipe_poll() */ - if (was_empty || pipe->poll_usage) + if (was_empty || READ_ONCE(pipe->pseudo_edgetrigger)) wake_up_interruptible_sync_poll(&pipe->rd_wait, EPOLLIN | EPOLLRDNORM); kill_fasync(&pipe->fasync_readers, SIGIO, POLL_IN); if (wake_next_writer) @@ -731,7 +730,6 @@ static long pipe_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) } } -/* No kernel lock held - fine */ static __poll_t pipe_poll(struct file *filp, poll_table *wait) { @@ -739,9 +737,17 @@ pipe_poll(struct file *filp, poll_table *wait) struct pipe_inode_info *pipe = filp->private_data; union pipe_index idx; - /* Epoll has some historical nasty semantics, this enables them */ - if (unlikely(!READ_ONCE(pipe->poll_usage))) - WRITE_ONCE(pipe->poll_usage, true); + /* + * Legacy epoll(EPOLLET) users depend on historical per-write wakeups, + * see 3a34b13a88ca ("pipe: make pipe writes always wake up readers") + * and the ->pseudo_edgetrigger check in anon_pipe_write(). + * Currently io_uring sets EPOLLET for multishot polls, so it gets the + * same behaviour. + */ + if ((filp->f_mode & FMODE_READ) && + wait && (wait->_key & EPOLLET) && + unlikely(!READ_ONCE(pipe->pseudo_edgetrigger))) + WRITE_ONCE(pipe->pseudo_edgetrigger, true); /* * Reading pipe state only -- no need for acquiring the semaphore. diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h index 9e8b60ad806a..6402930282e5 100644 --- a/include/linux/pipe_fs_i.h +++ b/include/linux/pipe_fs_i.h @@ -92,7 +92,7 @@ struct anon_pipe_prealloc { * @files: number of struct file referring this pipe (protected by ->i_lock) * @r_counter: reader counter * @w_counter: writer counter - * @poll_usage: is this pipe used for epoll, which has crazy wakeups? + * @pseudo_edgetrigger: has an EPOLLET consumer, enable per-write wakeups * @fasync_readers: reader side fasync * @fasync_writers: writer side fasync * @bufs: the circular array of pipe buffers @@ -113,7 +113,7 @@ struct pipe_inode_info { unsigned int files; unsigned int r_counter; unsigned int w_counter; - bool poll_usage; + bool pseudo_edgetrigger; #ifdef CONFIG_WATCH_QUEUE bool note_loss; #endif -- cgit v1.2.3 From ab018b7733bd8f5f998d476528c24924ebeb9959 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 29 Jul 2026 13:33:45 +0200 Subject: selftests/epoll: add a regression test for pipe->poll_usage pipe->poll_usage was added to ensure that edge-triggered epoll consumers get a wakeup on every write, even if the pipe was already non-empty. However, none of the existing epoll_wakeup_test cases cover this; the test suite passes even with WRITE_ONCE(pipe->poll_usage, true) removed. Add a test that writes twice to a pipe and verifies that epoll_wait with EPOLLET reports data each time. This covers the pipe-specific per-write wakeup behavior that edge-triggered consumers depend on. Signed-off-by: Oleg Nesterov Link: https://patch.msgid.link/amnlGZesXu-SUK2H@redhat.com Signed-off-by: Christian Brauner (Amutable) --- .../filesystems/epoll/epoll_wakeup_test.c | 23 ++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tools/testing/selftests/filesystems/epoll/epoll_wakeup_test.c b/tools/testing/selftests/filesystems/epoll/epoll_wakeup_test.c index f6f1a7ff01b0..81a994943e12 100644 --- a/tools/testing/selftests/filesystems/epoll/epoll_wakeup_test.c +++ b/tools/testing/selftests/filesystems/epoll/epoll_wakeup_test.c @@ -3538,4 +3538,27 @@ TEST(epoll65) close(ctx.efd[1]); } +TEST(epoll66) +{ + struct epoll_event event; + int pfd[2], efd; + + ASSERT_EQ(pipe(pfd), 0); + + efd = epoll_create1(0); + ASSERT_GE(efd, 0); + + event.events = EPOLLIN | EPOLLET; + ASSERT_EQ(epoll_ctl(efd, EPOLL_CTL_ADD, pfd[0], &event), 0); + + for (int i = 0; i < 2; ++i) { + ASSERT_EQ(write(pfd[1], "", 1), 1); + EXPECT_EQ(epoll_wait(efd, &event, 1, 0), 1); + } + + close(pfd[0]); + close(pfd[1]); + close(efd); +} + TEST_HARNESS_MAIN -- cgit v1.2.3 From dcacab904fe78d60840ba947a104993ee9ded887 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20B=2E=20Marli=C3=A8re=20=28SUSE=29?= Date: Mon, 10 Aug 2026 17:56:00 -0300 Subject: selftests/namespaces: Fix racy pipe handshake in timens and pidns_separate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In timens_separate and pidns_separate both the child and the grandchild write a 'Y' readiness byte to the same pipe, but the parent expects a single 'Y' followed by the grandchild's pid. If the grandchild's byte arrives first, the parent takes it for the child's and reads the pid misaligned, ending up with a garbage value. The parent stores that pid in self->grandchild_pid so that FIXTURE_TEARDOWN() can kill the grandchild. A garbage pid leaves the real grandchild alive in pause(), holding the test runner's TAP pipe open and hanging the whole collection. The grandchild has nothing to report, so drop its write() and leave the child as the sole writer. Fixes: fdb48976b637 ("selftests/namespaces: Kill grandchild in nsid fixture teardown") Signed-off-by: Ricardo B. Marlière (SUSE) Link: https://patch.msgid.link/20260810-selftests-namespaces_race-v1-1-4307e833783e@marliere.net Signed-off-by: Christian Brauner (Amutable) --- tools/testing/selftests/namespaces/nsid_test.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tools/testing/selftests/namespaces/nsid_test.c b/tools/testing/selftests/namespaces/nsid_test.c index 46dc838cba82..a16f31f41d38 100644 --- a/tools/testing/selftests/namespaces/nsid_test.c +++ b/tools/testing/selftests/namespaces/nsid_test.c @@ -649,8 +649,6 @@ TEST_F(nsid, timens_separate) /* Fork a grandchild to actually enter the new namespace */ pid_t grandchild = fork(); if (grandchild == 0) { - /* Grandchild is in the new namespace */ - write(pipefd[1], "Y", 1); close(pipefd[1]); pause(); _exit(0); @@ -771,8 +769,6 @@ TEST_F(nsid, pidns_separate) /* Fork a grandchild to actually enter the new namespace */ pid_t grandchild = fork(); if (grandchild == 0) { - /* Grandchild is in the new namespace */ - write(pipefd[1], "Y", 1); close(pipefd[1]); pause(); _exit(0); -- cgit v1.2.3