|
@@ -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);
|
|
|
}
|
|
|
|