Skip to content

Commit 1c9be84

Browse files
author
Al Viro
committed
make take_dentry_name_snapshot() lockless
Use ->d_seq instead of grabbing ->d_lock; in case of shortname dentries that avoids any stores to shared data objects and in case of long names we are down to (unavoidable) atomic_inc on the external_name refcount. Makes the thing safer as well - the areas where ->d_seq is held odd are all nested inside the areas where ->d_lock is held, and the latter are much more numerous. NOTE: now that there is a lockless path where we might try to grab a reference to an already doomed external_name instance, it is no longer possible for external_name.u.count and external_name.u.head to share space (kudos to Linus for spotting that). To reduce the noise this commit just make external_name.u a struct (instead of union); the next commit will dissolve it. Reviewed-by: Jeff Layton <[email protected]> Reviewed-by: Jan Kara <[email protected]> Signed-off-by: Al Viro <[email protected]>
1 parent 58cf9c3 commit 1c9be84

File tree

1 file changed

+25
-10
lines changed

1 file changed

+25
-10
lines changed

fs/dcache.c

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -296,9 +296,9 @@ static inline int dentry_cmp(const struct dentry *dentry, const unsigned char *c
296296
}
297297

298298
struct external_name {
299-
union {
300-
atomic_t count;
301-
struct rcu_head head;
299+
struct {
300+
atomic_t count; // ->count and ->head can't be combined
301+
struct rcu_head head; // see take_dentry_name_snapshot()
302302
} u;
303303
unsigned char name[];
304304
};
@@ -329,15 +329,30 @@ static inline int dname_external(const struct dentry *dentry)
329329

330330
void take_dentry_name_snapshot(struct name_snapshot *name, struct dentry *dentry)
331331
{
332-
spin_lock(&dentry->d_lock);
333-
name->name = dentry->d_name;
334-
if (unlikely(dname_external(dentry))) {
335-
atomic_inc(&external_name(dentry)->u.count);
336-
} else {
332+
unsigned seq;
333+
const unsigned char *s;
334+
335+
rcu_read_lock();
336+
retry:
337+
seq = read_seqcount_begin(&dentry->d_seq);
338+
s = READ_ONCE(dentry->d_name.name);
339+
name->name.hash_len = dentry->d_name.hash_len;
340+
name->name.name = name->inline_name.string;
341+
if (likely(s == dentry->d_shortname.string)) {
337342
name->inline_name = dentry->d_shortname;
338-
name->name.name = name->inline_name.string;
343+
} else {
344+
struct external_name *p;
345+
p = container_of(s, struct external_name, name[0]);
346+
// get a valid reference
347+
if (unlikely(!atomic_inc_not_zero(&p->u.count)))
348+
goto retry;
349+
name->name.name = s;
339350
}
340-
spin_unlock(&dentry->d_lock);
351+
if (read_seqcount_retry(&dentry->d_seq, seq)) {
352+
release_dentry_name_snapshot(name);
353+
goto retry;
354+
}
355+
rcu_read_unlock();
341356
}
342357
EXPORT_SYMBOL(take_dentry_name_snapshot);
343358

0 commit comments

Comments
 (0)