mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +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;
|
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;
|
unsigned seq;
|
||||||
const unsigned char *s;
|
const unsigned char *s;
|
||||||
|
|
@ -375,8 +376,12 @@ void take_dentry_name_snapshot(struct name_snapshot *name, struct dentry *dentry
|
||||||
struct external_name *p;
|
struct external_name *p;
|
||||||
p = container_of(s, struct external_name, name[0]);
|
p = container_of(s, struct external_name, name[0]);
|
||||||
// get a valid reference
|
// get a valid reference
|
||||||
if (unlikely(!atomic_inc_not_zero(&p->count)))
|
if (unlikely(!atomic_inc_not_zero(&p->count))) {
|
||||||
goto retry;
|
if (retry)
|
||||||
|
goto retry;
|
||||||
|
rcu_read_unlock();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
name->name.name = s;
|
name->name.name = s;
|
||||||
}
|
}
|
||||||
if (read_seqcount_retry(&dentry->d_seq, seq)) {
|
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;
|
goto retry;
|
||||||
}
|
}
|
||||||
rcu_read_unlock();
|
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);
|
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)
|
void release_dentry_name_snapshot(struct name_snapshot *name)
|
||||||
{
|
{
|
||||||
if (unlikely(name->name.name != name->inline_name.string)) {
|
if (unlikely(name->name.name != name->inline_name.string)) {
|
||||||
|
|
|
||||||
|
|
@ -629,6 +629,7 @@ struct name_snapshot {
|
||||||
union shortname_store inline_name;
|
union shortname_store inline_name;
|
||||||
};
|
};
|
||||||
void take_dentry_name_snapshot(struct name_snapshot *, struct dentry *);
|
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 *);
|
void release_dentry_name_snapshot(struct name_snapshot *);
|
||||||
|
|
||||||
static inline struct dentry *d_first_child(const struct dentry *dentry)
|
static inline struct dentry *d_first_child(const struct dentry *dentry)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user