diff --git a/fs/dcache.c b/fs/dcache.c index f44c7b39c3b3..21ac036b3167 100644 --- a/fs/dcache.c +++ b/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)) { diff --git a/include/linux/dcache.h b/include/linux/dcache.h index 4b1ff99608e0..a4be11ca2df1 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -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)