lockless next_positive()
authorAl Viro <viro@zeniv.linux.org.uk>
Tue, 7 Jun 2016 00:55:34 +0000 (20:55 -0400)
committerAl Viro <viro@zeniv.linux.org.uk>
Mon, 20 Jun 2016 21:11:29 +0000 (17:11 -0400)
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
fs/libfs.c

index b05b74ae3f167da9f996de6cef86c35c75e661fb..74dc8b9e7f53ab394113783c96fb754c045735fa 100644 (file)
@@ -89,31 +89,53 @@ static struct dentry *next_positive(struct dentry *parent,
                                    struct list_head *from,
                                    int count)
 {
-       struct dentry *res = NULL;
+       unsigned *seq = &parent->d_inode->i_dir_seq, n;
+       struct dentry *res;
        struct list_head *p;
+       bool skipped;
+       int i;
 
-       spin_lock(&parent->d_lock);
+retry:
+       i = count;
+       skipped = false;
+       n = smp_load_acquire(seq) & ~1;
+       res = NULL;
+       rcu_read_lock();
        for (p = from->next; p != &parent->d_subdirs; p = p->next) {
                struct dentry *d = list_entry(p, struct dentry, d_child);
-               if (simple_positive(d) && !--count) {
+               if (!simple_positive(d)) {
+                       skipped = true;
+               } else if (!--i) {
                        res = d;
                        break;
                }
        }
-       spin_unlock(&parent->d_lock);
+       rcu_read_unlock();
+       if (skipped) {
+               smp_rmb();
+               if (unlikely(*seq != n))
+                       goto retry;
+       }
        return res;
 }
 
 static void move_cursor(struct dentry *cursor, struct list_head *after)
 {
        struct dentry *parent = cursor->d_parent;
-
+       unsigned n, *seq = &parent->d_inode->i_dir_seq;
        spin_lock(&parent->d_lock);
+       for (;;) {
+               n = *seq;
+               if (!(n & 1) && cmpxchg(seq, n, n + 1) == n)
+                       break;
+               cpu_relax();
+       }
        __list_del(cursor->d_child.prev, cursor->d_child.next);
        if (after)
                list_add(&cursor->d_child, after);
        else
                list_add_tail(&cursor->d_child, &parent->d_subdirs);
+       smp_store_release(seq, n + 2);
        spin_unlock(&parent->d_lock);
 }