diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2016-06-06 20:55:34 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2016-06-20 17:11:29 -0400 |
commit | ebaaa80e8f20ff2cbbccd6823f73a99565487502 (patch) | |
tree | 6e0ba18f132e5b9699754e3db13571c3c6b7464d | |
parent | 4f42c1b5b9c27b6228e6b9c57eee4beb3118b6b0 (diff) |
lockless next_positive()
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r-- | fs/libfs.c | 32 |
1 files changed, 27 insertions, 5 deletions
diff --git a/fs/libfs.c b/fs/libfs.c index b05b74ae3f16..74dc8b9e7f53 100644 --- a/fs/libfs.c +++ b/fs/libfs.c | |||
@@ -89,31 +89,53 @@ static struct dentry *next_positive(struct dentry *parent, | |||
89 | struct list_head *from, | 89 | struct list_head *from, |
90 | int count) | 90 | int count) |
91 | { | 91 | { |
92 | struct dentry *res = NULL; | 92 | unsigned *seq = &parent->d_inode->i_dir_seq, n; |
93 | struct dentry *res; | ||
93 | struct list_head *p; | 94 | struct list_head *p; |
95 | bool skipped; | ||
96 | int i; | ||
94 | 97 | ||
95 | spin_lock(&parent->d_lock); | 98 | retry: |
99 | i = count; | ||
100 | skipped = false; | ||
101 | n = smp_load_acquire(seq) & ~1; | ||
102 | res = NULL; | ||
103 | rcu_read_lock(); | ||
96 | for (p = from->next; p != &parent->d_subdirs; p = p->next) { | 104 | for (p = from->next; p != &parent->d_subdirs; p = p->next) { |
97 | struct dentry *d = list_entry(p, struct dentry, d_child); | 105 | struct dentry *d = list_entry(p, struct dentry, d_child); |
98 | if (simple_positive(d) && !--count) { | 106 | if (!simple_positive(d)) { |
107 | skipped = true; | ||
108 | } else if (!--i) { | ||
99 | res = d; | 109 | res = d; |
100 | break; | 110 | break; |
101 | } | 111 | } |
102 | } | 112 | } |
103 | spin_unlock(&parent->d_lock); | 113 | rcu_read_unlock(); |
114 | if (skipped) { | ||
115 | smp_rmb(); | ||
116 | if (unlikely(*seq != n)) | ||
117 | goto retry; | ||
118 | } | ||
104 | return res; | 119 | return res; |
105 | } | 120 | } |
106 | 121 | ||
107 | static void move_cursor(struct dentry *cursor, struct list_head *after) | 122 | static void move_cursor(struct dentry *cursor, struct list_head *after) |
108 | { | 123 | { |
109 | struct dentry *parent = cursor->d_parent; | 124 | struct dentry *parent = cursor->d_parent; |
110 | 125 | unsigned n, *seq = &parent->d_inode->i_dir_seq; | |
111 | spin_lock(&parent->d_lock); | 126 | spin_lock(&parent->d_lock); |
127 | for (;;) { | ||
128 | n = *seq; | ||
129 | if (!(n & 1) && cmpxchg(seq, n, n + 1) == n) | ||
130 | break; | ||
131 | cpu_relax(); | ||
132 | } | ||
112 | __list_del(cursor->d_child.prev, cursor->d_child.next); | 133 | __list_del(cursor->d_child.prev, cursor->d_child.next); |
113 | if (after) | 134 | if (after) |
114 | list_add(&cursor->d_child, after); | 135 | list_add(&cursor->d_child, after); |
115 | else | 136 | else |
116 | list_add_tail(&cursor->d_child, &parent->d_subdirs); | 137 | list_add_tail(&cursor->d_child, &parent->d_subdirs); |
138 | smp_store_release(seq, n + 2); | ||
117 | spin_unlock(&parent->d_lock); | 139 | spin_unlock(&parent->d_lock); |
118 | } | 140 | } |
119 | 141 | ||