Skip to content

Commit ae2a823

Browse files
committed
dcache: move the DCACHE_OP_COMPARE case out of the __d_lookup_rcu loop
__d_lookup_rcu() is one of the hottest functions in the kernel on certain loads, and it is complicated by filesystems that might want to have their own name compare function. We can improve code generation by moving the test of DCACHE_OP_COMPARE outside the loop, which makes the loop itself much simpler, at the cost of some code duplication. But both cases end up being simpler, and the "native" direct case-sensitive compare particularly so. Cc: Al Viro <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 274a2ee commit ae2a823

File tree

1 file changed

+49
-23
lines changed

1 file changed

+49
-23
lines changed

fs/dcache.c

Lines changed: 49 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2270,6 +2270,48 @@ bool d_same_name(const struct dentry *dentry, const struct dentry *parent,
22702270
}
22712271
EXPORT_SYMBOL_GPL(d_same_name);
22722272

2273+
/*
2274+
* This is __d_lookup_rcu() when the parent dentry has
2275+
* DCACHE_OP_COMPARE, which makes things much nastier.
2276+
*/
2277+
static noinline struct dentry *__d_lookup_rcu_op_compare(
2278+
const struct dentry *parent,
2279+
const struct qstr *name,
2280+
unsigned *seqp)
2281+
{
2282+
u64 hashlen = name->hash_len;
2283+
struct hlist_bl_head *b = d_hash(hashlen_hash(hashlen));
2284+
struct hlist_bl_node *node;
2285+
struct dentry *dentry;
2286+
2287+
hlist_bl_for_each_entry_rcu(dentry, node, b, d_hash) {
2288+
int tlen;
2289+
const char *tname;
2290+
unsigned seq;
2291+
2292+
seqretry:
2293+
seq = raw_seqcount_begin(&dentry->d_seq);
2294+
if (dentry->d_parent != parent)
2295+
continue;
2296+
if (d_unhashed(dentry))
2297+
continue;
2298+
if (dentry->d_name.hash != hashlen_hash(hashlen))
2299+
continue;
2300+
tlen = dentry->d_name.len;
2301+
tname = dentry->d_name.name;
2302+
/* we want a consistent (name,len) pair */
2303+
if (read_seqcount_retry(&dentry->d_seq, seq)) {
2304+
cpu_relax();
2305+
goto seqretry;
2306+
}
2307+
if (parent->d_op->d_compare(dentry, tlen, tname, name) != 0)
2308+
continue;
2309+
*seqp = seq;
2310+
return dentry;
2311+
}
2312+
return NULL;
2313+
}
2314+
22732315
/**
22742316
* __d_lookup_rcu - search for a dentry (racy, store-free)
22752317
* @parent: parent dentry
@@ -2316,6 +2358,9 @@ struct dentry *__d_lookup_rcu(const struct dentry *parent,
23162358
* Keep the two functions in sync.
23172359
*/
23182360

2361+
if (unlikely(parent->d_flags & DCACHE_OP_COMPARE))
2362+
return __d_lookup_rcu_op_compare(parent, name, seqp);
2363+
23192364
/*
23202365
* The hash list is protected using RCU.
23212366
*
@@ -2332,7 +2377,6 @@ struct dentry *__d_lookup_rcu(const struct dentry *parent,
23322377
hlist_bl_for_each_entry_rcu(dentry, node, b, d_hash) {
23332378
unsigned seq;
23342379

2335-
seqretry:
23362380
/*
23372381
* The dentry sequence count protects us from concurrent
23382382
* renames, and thus protects parent and name fields.
@@ -2355,28 +2399,10 @@ struct dentry *__d_lookup_rcu(const struct dentry *parent,
23552399
continue;
23562400
if (d_unhashed(dentry))
23572401
continue;
2358-
2359-
if (unlikely(parent->d_flags & DCACHE_OP_COMPARE)) {
2360-
int tlen;
2361-
const char *tname;
2362-
if (dentry->d_name.hash != hashlen_hash(hashlen))
2363-
continue;
2364-
tlen = dentry->d_name.len;
2365-
tname = dentry->d_name.name;
2366-
/* we want a consistent (name,len) pair */
2367-
if (read_seqcount_retry(&dentry->d_seq, seq)) {
2368-
cpu_relax();
2369-
goto seqretry;
2370-
}
2371-
if (parent->d_op->d_compare(dentry,
2372-
tlen, tname, name) != 0)
2373-
continue;
2374-
} else {
2375-
if (dentry->d_name.hash_len != hashlen)
2376-
continue;
2377-
if (dentry_cmp(dentry, str, hashlen_len(hashlen)) != 0)
2378-
continue;
2379-
}
2402+
if (dentry->d_name.hash_len != hashlen)
2403+
continue;
2404+
if (dentry_cmp(dentry, str, hashlen_len(hashlen)) != 0)
2405+
continue;
23802406
*seqp = seq;
23812407
return dentry;
23822408
}

0 commit comments

Comments
 (0)