summaryrefslogtreecommitdiff
path: root/fs/nsfs.c
diff options
context:
space:
mode:
authorChristian Brauner <brauner@kernel.org>2024-02-18 14:52:24 +0100
committerChristian Brauner <brauner@kernel.org>2024-03-01 12:26:23 +0100
commit159a0d9fd50b92cc48e4c82cde79c4cb34c85953 (patch)
tree56292c34e377f407bb97433118e750f28cd9c5c6 /fs/nsfs.c
parentb28ddcc32d8fa3e20745b3a47dff863fe0376d79 (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/nsfs.c')
-rw-r--r--fs/nsfs.c50
1 files changed, 22 insertions, 28 deletions
diff --git a/fs/nsfs.c b/fs/nsfs.c
index e2da645c3d02..3a36bb62353c 100644
--- a/fs/nsfs.c
+++ b/fs/nsfs.c
@@ -36,10 +36,12 @@ static char *ns_dname(struct dentry *dentry, char *buffer, int buflen)
static void ns_prune_dentry(struct dentry *dentry)
{
- struct inode *inode = d_inode(dentry);
+ struct inode *inode;
+
+ inode = d_inode(dentry);
if (inode) {
struct ns_common *ns = inode->i_private;
- WRITE_ONCE(ns->stashed, NULL);
+ cmpxchg(&ns->stashed, dentry, NULL);
}
}
@@ -61,20 +63,17 @@ int ns_get_path_cb(struct path *path, ns_get_path_helper_t *ns_get_cb,
void *private_data)
{
int ret;
+ struct ns_common *ns;
- do {
- struct ns_common *ns = ns_get_cb(private_data);
- if (!ns)
- return -ENOENT;
- ret = path_from_stashed(&ns->stashed, ns->inum, nsfs_mnt,
- &ns_file_operations, NULL, ns, path);
- if (ret <= 0 && ret != -EAGAIN)
- ns->ops->put(ns);
- } while (ret == -EAGAIN);
-
+ ns = ns_get_cb(private_data);
+ if (!ns)
+ return -ENOENT;
+ ret = path_from_stashed(&ns->stashed, ns->inum, nsfs_mnt,
+ &ns_file_operations, NULL, ns, path);
+ if (ret <= 0)
+ ns->ops->put(ns);
if (ret < 0)
return ret;
-
return 0;
}
@@ -105,6 +104,7 @@ int open_related_ns(struct ns_common *ns,
struct ns_common *(*get_ns)(struct ns_common *ns))
{
struct path path = {};
+ struct ns_common *relative;
struct file *f;
int err;
int fd;
@@ -113,22 +113,16 @@ int open_related_ns(struct ns_common *ns,
if (fd < 0)
return fd;
- do {
- struct ns_common *relative;
-
- relative = get_ns(ns);
- if (IS_ERR(relative)) {
- put_unused_fd(fd);
- return PTR_ERR(relative);
- }
-
- err = path_from_stashed(&relative->stashed, relative->inum,
- nsfs_mnt, &ns_file_operations, NULL,
- relative, &path);
- if (err <= 0 && err != -EAGAIN)
- relative->ops->put(relative);
- } while (err == -EAGAIN);
+ relative = get_ns(ns);
+ if (IS_ERR(relative)) {
+ put_unused_fd(fd);
+ return PTR_ERR(relative);
+ }
+ err = path_from_stashed(&relative->stashed, relative->inum, nsfs_mnt,
+ &ns_file_operations, NULL, relative, &path);
+ if (err <= 0)
+ relative->ops->put(relative);
if (err < 0) {
put_unused_fd(fd);
return err;