diff options
author | Christian Brauner <brauner@kernel.org> | 2024-02-18 14:52:24 +0100 |
---|---|---|
committer | Christian Brauner <brauner@kernel.org> | 2024-03-01 12:26:23 +0100 |
commit | 159a0d9fd50b92cc48e4c82cde79c4cb34c85953 (patch) | |
tree | 56292c34e377f407bb97433118e750f28cd9c5c6 /fs/pidfs.c | |
parent | b28ddcc32d8fa3e20745b3a47dff863fe0376d79 (diff) |
libfs: improve path_from_stashed() helper
In earlier patches we moved both nsfs and pidfs to path_from_stashed().
The helper currently tries to add and stash a new dentry if a reusable
dentry couldn't be found and returns EAGAIN if it lost the race to stash
the dentry. The caller can use EAGAIN to retry.
The helper and the two filesystems be written in a way that makes
returning EAGAIN unnecessary. To do this we need to change the
dentry->d_prune() implementation of nsfs and pidfs to not simply replace
the stashed dentry with NULL but to use a cmpxchg() and only replace
their own dentry.
Then path_from_stashed() can then be changed to not just stash a new
dentry when no dentry is currently stashed but also when an already dead
dentry is stashed. If another task managed to install a dentry in the
meantime it can simply be reused. Pack that into a loop and call it a
day.
Suggested-by: Linus Torvalds <torvalds@linux-foundation.org>
Link: https://lore.kernel.org/r/CAHk-=wgtLF5Z5=15-LKAczWm=-tUjHO+Bpf7WjBG+UU3s=fEQw@mail.gmail.com
Signed-off-by: Christian Brauner <brauner@kernel.org>
Diffstat (limited to 'fs/pidfs.c')
-rw-r--r-- | fs/pidfs.c | 28 |
1 files changed, 11 insertions, 17 deletions
diff --git a/fs/pidfs.c b/fs/pidfs.c index cf606f15def5..5f33c820b7f8 100644 --- a/fs/pidfs.c +++ b/fs/pidfs.c @@ -140,7 +140,6 @@ struct pid *pidfd_pid(const struct file *file) #ifdef CONFIG_FS_PID static struct vfsmount *pidfs_mnt __ro_after_init; -static struct super_block *pidfs_sb __ro_after_init; /* * The vfs falls back to simple_setattr() if i_op->setattr() isn't @@ -195,7 +194,7 @@ static void pidfs_prune_dentry(struct dentry *dentry) inode = d_inode(dentry); if (inode) { struct pid *pid = inode->i_private; - WRITE_ONCE(pid->stashed, NULL); + cmpxchg(&pid->stashed, dentry, NULL); } } @@ -231,19 +230,16 @@ struct file *pidfs_alloc_file(struct pid *pid, unsigned int flags) struct path path; int ret; - do { - /* - * Inode numbering for pidfs start at RESERVED_PIDS + 1. - * This avoids collisions with the root inode which is 1 - * for pseudo filesystems. - */ - ret = path_from_stashed(&pid->stashed, pid->ino, pidfs_mnt, - &pidfs_file_operations, - &pidfs_inode_operations, get_pid(pid), - &path); - if (ret <= 0 && ret != -EAGAIN) - put_pid(pid); - } while (ret == -EAGAIN); + /* + * Inode numbering for pidfs start at RESERVED_PIDS + 1. + * This avoids collisions with the root inode which is 1 + * for pseudo filesystems. + */ + ret = path_from_stashed(&pid->stashed, pid->ino, pidfs_mnt, + &pidfs_file_operations, &pidfs_inode_operations, + get_pid(pid), &path); + if (ret <= 0) + put_pid(pid); if (ret < 0) return ERR_PTR(ret); @@ -257,8 +253,6 @@ void __init pidfs_init(void) pidfs_mnt = kern_mount(&pidfs_type); if (IS_ERR(pidfs_mnt)) panic("Failed to mount pidfs pseudo filesystem"); - - pidfs_sb = pidfs_mnt->mnt_sb; } bool is_pidfs_sb(const struct super_block *sb) |