aboutsummaryrefslogtreecommitdiffstats
path: root/fs/dcache.c
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2016-04-15 02:42:04 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2016-05-02 19:49:27 -0400
commit94bdd655caba2080ae81d83d756d325abdffcb9f (patch)
treeaff6bb4952f700479d7fc6e61b24c106d3ab3262 /fs/dcache.c
parent84e710da2a1dfacfc87f604869a4d22df91ce6cd (diff)
parallel lookups machinery, part 3
We will need to be able to check if there is an in-lookup dentry with matching parent/name. Right now it's impossible, but as soon as start locking directories shared such beasts will appear. Add a secondary hash for locating those. Hash chains go through the same space where d_alias will be once it's not in-lookup anymore. Search is done under the same bitlock we use for modifications - with the primary hash we can rely on d_rehash() into the wrong chain being the worst that could happen, but here the pointers are buggered once it's removed from the chain. On the other hand, the chains are not going to be long and normally we'll end up adding to the chain anyway. That allows us to avoid bothering with ->d_lock when doing the comparisons - everything is stable until removed from chain. New helper: d_alloc_parallel(). Right now it allocates, verifies that no hashed and in-lookup matches exist and adds to in-lookup hash. Returns ERR_PTR() for error, hashed match (in the unlikely case it's been found) or new dentry. In-lookup matches trigger BUG() for now; that will change in the next commit when we introduce waiting for ongoing lookup to finish. Note that in-lookup matches won't be possible until we actually go for shared locking. lookup_slow() switched to use of d_alloc_parallel(). Again, these commits are separated only for making it easier to review. All this machinery will start doing something useful only when we go for shared locking; it's just that the combination is too large for my taste. Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/dcache.c')
-rw-r--r--fs/dcache.c104
1 files changed, 104 insertions, 0 deletions
diff --git a/fs/dcache.c b/fs/dcache.c
index 10988f7e5a23..ea2de7c19b08 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -111,6 +111,17 @@ static inline struct hlist_bl_head *d_hash(const struct dentry *parent,
111 return dentry_hashtable + hash_32(hash, d_hash_shift); 111 return dentry_hashtable + hash_32(hash, d_hash_shift);
112} 112}
113 113
114#define IN_LOOKUP_SHIFT 10
115static struct hlist_bl_head in_lookup_hashtable[1 << IN_LOOKUP_SHIFT];
116
117static inline struct hlist_bl_head *in_lookup_hash(const struct dentry *parent,
118 unsigned int hash)
119{
120 hash += (unsigned long) parent / L1_CACHE_BYTES;
121 return in_lookup_hashtable + hash_32(hash, IN_LOOKUP_SHIFT);
122}
123
124
114/* Statistics gathering. */ 125/* Statistics gathering. */
115struct dentry_stat_t dentry_stat = { 126struct dentry_stat_t dentry_stat = {
116 .age_limit = 45, 127 .age_limit = 45,
@@ -2380,9 +2391,102 @@ static inline void end_dir_add(struct inode *dir, unsigned n)
2380 smp_store_release(&dir->i_dir_seq, n + 2); 2391 smp_store_release(&dir->i_dir_seq, n + 2);
2381} 2392}
2382 2393
2394struct dentry *d_alloc_parallel(struct dentry *parent,
2395 const struct qstr *name)
2396{
2397 unsigned int len = name->len;
2398 unsigned int hash = name->hash;
2399 const unsigned char *str = name->name;
2400 struct hlist_bl_head *b = in_lookup_hash(parent, hash);
2401 struct hlist_bl_node *node;
2402 struct dentry *new = d_alloc(parent, name);
2403 struct dentry *dentry;
2404 unsigned seq, r_seq, d_seq;
2405
2406 if (unlikely(!new))
2407 return ERR_PTR(-ENOMEM);
2408
2409retry:
2410 rcu_read_lock();
2411 seq = smp_load_acquire(&parent->d_inode->i_dir_seq) & ~1;
2412 r_seq = read_seqbegin(&rename_lock);
2413 dentry = __d_lookup_rcu(parent, name, &d_seq);
2414 if (unlikely(dentry)) {
2415 if (!lockref_get_not_dead(&dentry->d_lockref)) {
2416 rcu_read_unlock();
2417 goto retry;
2418 }
2419 if (read_seqcount_retry(&dentry->d_seq, d_seq)) {
2420 rcu_read_unlock();
2421 dput(dentry);
2422 goto retry;
2423 }
2424 rcu_read_unlock();
2425 dput(new);
2426 return dentry;
2427 }
2428 if (unlikely(read_seqretry(&rename_lock, r_seq))) {
2429 rcu_read_unlock();
2430 goto retry;
2431 }
2432 hlist_bl_lock(b);
2433 if (unlikely(parent->d_inode->i_dir_seq != seq)) {
2434 hlist_bl_unlock(b);
2435 rcu_read_unlock();
2436 goto retry;
2437 }
2438 rcu_read_unlock();
2439 /*
2440 * No changes for the parent since the beginning of d_lookup().
2441 * Since all removals from the chain happen with hlist_bl_lock(),
2442 * any potential in-lookup matches are going to stay here until
2443 * we unlock the chain. All fields are stable in everything
2444 * we encounter.
2445 */
2446 hlist_bl_for_each_entry(dentry, node, b, d_u.d_in_lookup_hash) {
2447 if (dentry->d_name.hash != hash)
2448 continue;
2449 if (dentry->d_parent != parent)
2450 continue;
2451 if (d_unhashed(dentry))
2452 continue;
2453 if (parent->d_flags & DCACHE_OP_COMPARE) {
2454 int tlen = dentry->d_name.len;
2455 const char *tname = dentry->d_name.name;
2456 if (parent->d_op->d_compare(parent, dentry, tlen, tname, name))
2457 continue;
2458 } else {
2459 if (dentry->d_name.len != len)
2460 continue;
2461 if (dentry_cmp(dentry, str, len))
2462 continue;
2463 }
2464 dget(dentry);
2465 hlist_bl_unlock(b);
2466 /* impossible until we actually enable parallel lookups */
2467 BUG();
2468 /* and this will be "wait for it to stop being in-lookup" */
2469 /* this one will be handled in the next commit */
2470 dput(new);
2471 return dentry;
2472 }
2473 /* we can't take ->d_lock here; it's OK, though. */
2474 new->d_flags |= DCACHE_PAR_LOOKUP;
2475 hlist_bl_add_head_rcu(&new->d_u.d_in_lookup_hash, b);
2476 hlist_bl_unlock(b);
2477 return new;
2478}
2479EXPORT_SYMBOL(d_alloc_parallel);
2480
2383void __d_lookup_done(struct dentry *dentry) 2481void __d_lookup_done(struct dentry *dentry)
2384{ 2482{
2483 struct hlist_bl_head *b = in_lookup_hash(dentry->d_parent,
2484 dentry->d_name.hash);
2485 hlist_bl_lock(b);
2385 dentry->d_flags &= ~DCACHE_PAR_LOOKUP; 2486 dentry->d_flags &= ~DCACHE_PAR_LOOKUP;
2487 __hlist_bl_del(&dentry->d_u.d_in_lookup_hash);
2488 hlist_bl_unlock(b);
2489 INIT_HLIST_NODE(&dentry->d_u.d_alias);
2386 /* more stuff will land here */ 2490 /* more stuff will land here */
2387} 2491}
2388EXPORT_SYMBOL(__d_lookup_done); 2492EXPORT_SYMBOL(__d_lookup_done);