Skip to content

Commit 159a0d9

Browse files
committed
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>
1 parent b28ddcc commit 159a0d9

File tree

3 files changed

+73
-66
lines changed

3 files changed

+73
-66
lines changed

fs/libfs.c

Lines changed: 40 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1988,11 +1988,11 @@ static inline struct dentry *get_stashed_dentry(struct dentry *stashed)
19881988
return dentry;
19891989
}
19901990

1991-
static struct dentry *stash_dentry(struct dentry **stashed, unsigned long ino,
1992-
struct super_block *sb,
1993-
const struct file_operations *fops,
1994-
const struct inode_operations *iops,
1995-
void *data)
1991+
static struct dentry *prepare_anon_dentry(unsigned long ino,
1992+
struct super_block *sb,
1993+
const struct file_operations *fops,
1994+
const struct inode_operations *iops,
1995+
void *data)
19961996
{
19971997
struct dentry *dentry;
19981998
struct inode *inode;
@@ -2021,15 +2021,29 @@ static struct dentry *stash_dentry(struct dentry **stashed, unsigned long ino,
20212021

20222022
/* @data is now owned by the fs */
20232023
d_instantiate(dentry, inode);
2024+
return dentry;
2025+
}
20242026

2025-
if (cmpxchg(stashed, NULL, dentry)) {
2026-
d_delete(dentry); /* make sure ->d_prune() does nothing */
2027-
dput(dentry);
2028-
cpu_relax();
2029-
return ERR_PTR(-EAGAIN);
2030-
}
2027+
static struct dentry *stash_dentry(struct dentry **stashed,
2028+
struct dentry *dentry)
2029+
{
2030+
guard(rcu)();
2031+
for (;;) {
2032+
struct dentry *old;
20312033

2032-
return dentry;
2034+
/* Assume any old dentry was cleared out. */
2035+
old = cmpxchg(stashed, NULL, dentry);
2036+
if (likely(!old))
2037+
return dentry;
2038+
2039+
/* Check if somebody else installed a reusable dentry. */
2040+
if (lockref_get_not_dead(&old->d_lockref))
2041+
return old;
2042+
2043+
/* There's an old dead dentry there, try to take it over. */
2044+
if (likely(try_cmpxchg(stashed, &old, dentry)))
2045+
return dentry;
2046+
}
20332047
}
20342048

20352049
/**
@@ -2044,15 +2058,14 @@ static struct dentry *stash_dentry(struct dentry **stashed, unsigned long ino,
20442058
*
20452059
* The function tries to retrieve a stashed dentry from @stashed. If the dentry
20462060
* is still valid then it will be reused. If the dentry isn't able the function
2047-
* will allocate a new dentry and inode. It will then try to update @stashed
2048-
* with the newly added dentry. If it fails -EAGAIN is returned and the caller
2049-
* my retry.
2061+
* will allocate a new dentry and inode. It will then check again whether it
2062+
* can reuse an existing dentry in case one has been added in the meantime or
2063+
* update @stashed with the newly added dentry.
20502064
*
20512065
* Special-purpose helper for nsfs and pidfs.
20522066
*
20532067
* Return: If 0 or an error is returned the caller can be sure that @data must
2054-
* be cleaned up. If 1 or -EAGAIN is returned @data is owned by the
2055-
* filesystem.
2068+
* be cleaned up. If 1 is returned @data is owned by the filesystem.
20562069
*/
20572070
int path_from_stashed(struct dentry **stashed, unsigned long ino,
20582071
struct vfsmount *mnt, const struct file_operations *fops,
@@ -2062,17 +2075,23 @@ int path_from_stashed(struct dentry **stashed, unsigned long ino,
20622075
struct dentry *dentry;
20632076
int ret = 0;
20642077

2065-
dentry = get_stashed_dentry(*stashed);
2066-
if (dentry)
2078+
/* See if dentry can be reused. */
2079+
path->dentry = get_stashed_dentry(*stashed);
2080+
if (path->dentry)
20672081
goto out_path;
20682082

2069-
dentry = stash_dentry(stashed, ino, mnt->mnt_sb, fops, iops, data);
2083+
/* Allocate a new dentry. */
2084+
dentry = prepare_anon_dentry(ino, mnt->mnt_sb, fops, iops, data);
20702085
if (IS_ERR(dentry))
20712086
return PTR_ERR(dentry);
2087+
2088+
/* Added a new dentry. @data is now owned by the filesystem. */
2089+
path->dentry = stash_dentry(stashed, dentry);
2090+
if (path->dentry != dentry)
2091+
dput(dentry);
20722092
ret = 1;
20732093

20742094
out_path:
2075-
path->dentry = dentry;
20762095
path->mnt = mntget(mnt);
20772096
return ret;
20782097
}

fs/nsfs.c

Lines changed: 22 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,12 @@ static char *ns_dname(struct dentry *dentry, char *buffer, int buflen)
3636

3737
static void ns_prune_dentry(struct dentry *dentry)
3838
{
39-
struct inode *inode = d_inode(dentry);
39+
struct inode *inode;
40+
41+
inode = d_inode(dentry);
4042
if (inode) {
4143
struct ns_common *ns = inode->i_private;
42-
WRITE_ONCE(ns->stashed, NULL);
44+
cmpxchg(&ns->stashed, dentry, NULL);
4345
}
4446
}
4547

@@ -61,20 +63,17 @@ int ns_get_path_cb(struct path *path, ns_get_path_helper_t *ns_get_cb,
6163
void *private_data)
6264
{
6365
int ret;
66+
struct ns_common *ns;
6467

65-
do {
66-
struct ns_common *ns = ns_get_cb(private_data);
67-
if (!ns)
68-
return -ENOENT;
69-
ret = path_from_stashed(&ns->stashed, ns->inum, nsfs_mnt,
70-
&ns_file_operations, NULL, ns, path);
71-
if (ret <= 0 && ret != -EAGAIN)
72-
ns->ops->put(ns);
73-
} while (ret == -EAGAIN);
74-
68+
ns = ns_get_cb(private_data);
69+
if (!ns)
70+
return -ENOENT;
71+
ret = path_from_stashed(&ns->stashed, ns->inum, nsfs_mnt,
72+
&ns_file_operations, NULL, ns, path);
73+
if (ret <= 0)
74+
ns->ops->put(ns);
7575
if (ret < 0)
7676
return ret;
77-
7877
return 0;
7978
}
8079

@@ -105,6 +104,7 @@ int open_related_ns(struct ns_common *ns,
105104
struct ns_common *(*get_ns)(struct ns_common *ns))
106105
{
107106
struct path path = {};
107+
struct ns_common *relative;
108108
struct file *f;
109109
int err;
110110
int fd;
@@ -113,22 +113,16 @@ int open_related_ns(struct ns_common *ns,
113113
if (fd < 0)
114114
return fd;
115115

116-
do {
117-
struct ns_common *relative;
118-
119-
relative = get_ns(ns);
120-
if (IS_ERR(relative)) {
121-
put_unused_fd(fd);
122-
return PTR_ERR(relative);
123-
}
124-
125-
err = path_from_stashed(&relative->stashed, relative->inum,
126-
nsfs_mnt, &ns_file_operations, NULL,
127-
relative, &path);
128-
if (err <= 0 && err != -EAGAIN)
129-
relative->ops->put(relative);
130-
} while (err == -EAGAIN);
116+
relative = get_ns(ns);
117+
if (IS_ERR(relative)) {
118+
put_unused_fd(fd);
119+
return PTR_ERR(relative);
120+
}
131121

122+
err = path_from_stashed(&relative->stashed, relative->inum, nsfs_mnt,
123+
&ns_file_operations, NULL, relative, &path);
124+
if (err <= 0)
125+
relative->ops->put(relative);
132126
if (err < 0) {
133127
put_unused_fd(fd);
134128
return err;

fs/pidfs.c

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,6 @@ struct pid *pidfd_pid(const struct file *file)
140140

141141
#ifdef CONFIG_FS_PID
142142
static struct vfsmount *pidfs_mnt __ro_after_init;
143-
static struct super_block *pidfs_sb __ro_after_init;
144143

145144
/*
146145
* 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)
195194
inode = d_inode(dentry);
196195
if (inode) {
197196
struct pid *pid = inode->i_private;
198-
WRITE_ONCE(pid->stashed, NULL);
197+
cmpxchg(&pid->stashed, dentry, NULL);
199198
}
200199
}
201200

@@ -231,19 +230,16 @@ struct file *pidfs_alloc_file(struct pid *pid, unsigned int flags)
231230
struct path path;
232231
int ret;
233232

234-
do {
235-
/*
236-
* Inode numbering for pidfs start at RESERVED_PIDS + 1.
237-
* This avoids collisions with the root inode which is 1
238-
* for pseudo filesystems.
239-
*/
240-
ret = path_from_stashed(&pid->stashed, pid->ino, pidfs_mnt,
241-
&pidfs_file_operations,
242-
&pidfs_inode_operations, get_pid(pid),
243-
&path);
244-
if (ret <= 0 && ret != -EAGAIN)
245-
put_pid(pid);
246-
} while (ret == -EAGAIN);
233+
/*
234+
* Inode numbering for pidfs start at RESERVED_PIDS + 1.
235+
* This avoids collisions with the root inode which is 1
236+
* for pseudo filesystems.
237+
*/
238+
ret = path_from_stashed(&pid->stashed, pid->ino, pidfs_mnt,
239+
&pidfs_file_operations, &pidfs_inode_operations,
240+
get_pid(pid), &path);
241+
if (ret <= 0)
242+
put_pid(pid);
247243
if (ret < 0)
248244
return ERR_PTR(ret);
249245

@@ -257,8 +253,6 @@ void __init pidfs_init(void)
257253
pidfs_mnt = kern_mount(&pidfs_type);
258254
if (IS_ERR(pidfs_mnt))
259255
panic("Failed to mount pidfs pseudo filesystem");
260-
261-
pidfs_sb = pidfs_mnt->mnt_sb;
262256
}
263257

264258
bool is_pidfs_sb(const struct super_block *sb)

0 commit comments

Comments
 (0)