mirror of
https://github.com/torvalds/linux.git
synced 2026-11-06 21:58:01 +01:00
vfs: add try_take_dentry_name_snapshot()
take_dentry_name_snapshot() requires the caller to keep the dentry alive. The external name of a dead dentry has a zero refcount that never changes, so atomic_inc_not_zero() fails on every retry and the function never returns. A caller in RCU walk holds no reference on the dentry. Add a variant that fails instead of retrying when it cannot get a reference on the external name, so such a caller can return -ECHILD. Reviewed-by: Christian Brauner (Amutable) <brauner@kernel.org> Signed-off-by: Amir Goldstein <amir73il@gmail.com>
This commit is contained in:
parent
65bf000962
commit
15c6bd5cf1
29
fs/dcache.c
29
fs/dcache.c
|
|
@ -358,7 +358,8 @@ static inline int dname_external(const struct dentry *dentry)
|
|||
return dentry->d_name.name != dentry->d_shortname.string;
|
||||
}
|
||||
|
||||
void take_dentry_name_snapshot(struct name_snapshot *name, struct dentry *dentry)
|
||||
static bool __take_dentry_name_snapshot(struct name_snapshot *name,
|
||||
struct dentry *dentry, bool retry)
|
||||
{
|
||||
unsigned seq;
|
||||
const unsigned char *s;
|
||||
|
|
@ -375,8 +376,12 @@ void take_dentry_name_snapshot(struct name_snapshot *name, struct dentry *dentry
|
|||
struct external_name *p;
|
||||
p = container_of(s, struct external_name, name[0]);
|
||||
// get a valid reference
|
||||
if (unlikely(!atomic_inc_not_zero(&p->count)))
|
||||
goto retry;
|
||||
if (unlikely(!atomic_inc_not_zero(&p->count))) {
|
||||
if (retry)
|
||||
goto retry;
|
||||
rcu_read_unlock();
|
||||
return false;
|
||||
}
|
||||
name->name.name = s;
|
||||
}
|
||||
if (read_seqcount_retry(&dentry->d_seq, seq)) {
|
||||
|
|
@ -384,9 +389,27 @@ void take_dentry_name_snapshot(struct name_snapshot *name, struct dentry *dentry
|
|||
goto retry;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
return true;
|
||||
}
|
||||
|
||||
void take_dentry_name_snapshot(struct name_snapshot *name, struct dentry *dentry)
|
||||
{
|
||||
__take_dentry_name_snapshot(name, dentry, true);
|
||||
}
|
||||
EXPORT_SYMBOL(take_dentry_name_snapshot);
|
||||
|
||||
/*
|
||||
* For a caller that holds no reference on @dentry, e.g. in RCU walk.
|
||||
* The external name of a dead dentry has a zero refcount forever, so
|
||||
* take_dentry_name_snapshot() would retry without end. Fail instead.
|
||||
*/
|
||||
bool try_take_dentry_name_snapshot(struct name_snapshot *name,
|
||||
struct dentry *dentry)
|
||||
{
|
||||
return __take_dentry_name_snapshot(name, dentry, false);
|
||||
}
|
||||
EXPORT_SYMBOL(try_take_dentry_name_snapshot);
|
||||
|
||||
void release_dentry_name_snapshot(struct name_snapshot *name)
|
||||
{
|
||||
if (unlikely(name->name.name != name->inline_name.string)) {
|
||||
|
|
|
|||
|
|
@ -629,6 +629,7 @@ struct name_snapshot {
|
|||
union shortname_store inline_name;
|
||||
};
|
||||
void take_dentry_name_snapshot(struct name_snapshot *, struct dentry *);
|
||||
bool try_take_dentry_name_snapshot(struct name_snapshot *, struct dentry *);
|
||||
void release_dentry_name_snapshot(struct name_snapshot *);
|
||||
|
||||
static inline struct dentry *d_first_child(const struct dentry *dentry)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user