summaryrefslogtreecommitdiff
path: root/include/linux
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-08-17 12:56:12 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-08-17 12:56:12 -0700
commit1c3e8cef79ea5f1415cff0d3c507e2e07b71ade8 (patch)
tree250813712d5f13fe5cb38a9923d8a70ee0043aab /include/linux
parentab5ed08f2d8396fb8e3942569bbbd5cd569a753e (diff)
parentdcacab904fe78d60840ba947a104993ee9ded887 (diff)
Merge tag 'vfs-7.3-rc1.misc' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
Pull misc vfs updates from Christian Brauner: "Bigger cleanups: - The lockref dead-count handling is tidied up. The open-coded check for a count below zero as the dead marker relies on information the caller should not have. - make put_mnt_ns() leave mounts connected. Destroying a mount namespace disconnected its mounts from their mount points. So a file descriptor still open on the parent of a mount point could be used to peek under it. Locked mounts were already kept connected to prevent exactly that. But a mount is only locked when its tree is copied across a user namespace boundary. So a mount namespace set up by a privileged component had no locked mounts and its mounts were disconnected. Passing UMOUNT_CONNECTED keeps every mount connected and prevents that bug. - vfs_prepare_mode() passes S_IFDIR for directories. I meant to fix that ago but didn't get to it. So now someone finally did it. This kills the exception where the mode could be 0 when a directory was created whereas every other creation operation passed it explicitly already. - move long delayed work for ufs, jffs2, hfsplus, hfs and affs from the per-cpu system_long_wq to the new unbound system_dfl_long_wq. None of that work relies on per-cpu state and the work item is enqueued with queue_delayed_work() whose timer is global anyway. So it may as well benefit from scheduler task placement. Smaller fixes and cleanups: - unlock_buffer() and journal_end_buffer_io_sync() use clear_and_wake_up_bit() - the pipe page pools are unified into a single per-pipe pool and the extra wake_up(rd_wait) is limited to EPOLLET consumers - eventpoll now computes its timer slack lazily in ep_poll() - shrink_dcache_for_umount() keeps making progress on busy roots - excess xarray nodes are freed in clear_inode() - romfs detects hard link cycles - the user path of nested backing files is fixed - pidfd holds exec_update_lock around the namespace ioctl - non-memcg-aware nr_cached_objects is skipped during memcg slab shrink - iomap_write_iter() always returns status - mangle_path() is renamed to seq_mangle_path() - inode timestamp accessors are annotated - new regression test for pipe->poll_usage. - a few documentation, kernel-doc and selftest fixes" * tag 'vfs-7.3-rc1.misc' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs: (67 commits) selftests/namespaces: Fix racy pipe handshake in timens and pidns_separate selftests/epoll: add a regression test for pipe->poll_usage pipe: only enable the extra wake_up(rd_wait) for EPOLLET consumers pidfd: hold exec_update_lock around namespace ioctl fs: fix user path of nested backing files fs: remove stale inode_insert5() kernel-doc parameter fs: fix switch/case indentation in sysfs() syscall fs: document semantics of kstat::{uid,gid} fields dcache: keep shrink_dcache_for_umount() making progress on busy roots seq_file: rename mangle_path to seq_mangle_path nstree: add/fix struct ns_id_req kernel-doc member fields dcache: use lockref routines for dead count checks lockref: tidy up dead count handling initramfs: fix typo in reserve_initrd_mem comment fs/pipe: unify the page pools into a single per-pipe pool fs: annotate inode timestamp accessors eventpoll: compute timer slack lazily in ep_poll() selftests/filesystems: add mntns cleanup test put_mnt_ns(): leave mounts connected affs: Move long delayed work on system_dfl_long_wq ...
Diffstat (limited to 'include/linux')
-rw-r--r--include/linux/fs.h20
-rw-r--r--include/linux/lockref.h12
-rw-r--r--include/linux/pipe_fs_i.h26
-rw-r--r--include/linux/seq_file.h2
-rw-r--r--include/linux/stat.h4
5 files changed, 45 insertions, 19 deletions
diff --git a/include/linux/fs.h b/include/linux/fs.h
index c3c4a40c90a0..2f243b1554d8 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)
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/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h
index 7f6a92ac9704..6402930282e5 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
@@ -59,6 +62,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
* @rd_wait: reader wait point in case of empty pipe
@@ -68,13 +86,13 @@ 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)
* @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
@@ -95,11 +113,11 @@ 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
- struct page *tmp_page[2];
+ struct anon_pipe_prealloc prealloc;
struct fasync_struct *fasync_readers;
struct fasync_struct *fasync_writers;
struct pipe_buffer *bufs;
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/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;