Commit 48f5ec21 authored by Al Viro's avatar Al Viro

split read_seqretry_or_unlock(), convert d_walk() to resulting primitives

Separate "check if we need to retry" from "unlock if we are done and
had seq_writelock"; that allows to use these guys in d_walk(), where
we need to recheck every time we ascend back to parent, but do *not*
want to unlock until the very end.  Lift rcu_read_lock/rcu_read_unlock
out into callers.
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 232d2d60
...@@ -100,30 +100,21 @@ static struct kmem_cache *dentry_cache __read_mostly; ...@@ -100,30 +100,21 @@ static struct kmem_cache *dentry_cache __read_mostly;
*/ */
static inline void read_seqbegin_or_lock(seqlock_t *lock, int *seq) static inline void read_seqbegin_or_lock(seqlock_t *lock, int *seq)
{ {
if (!(*seq & 1)) { /* Even */ if (!(*seq & 1)) /* Even */
*seq = read_seqbegin(lock); *seq = read_seqbegin(lock);
rcu_read_lock(); else /* Odd */
} else /* Odd */
write_seqlock(lock); write_seqlock(lock);
} }
/** static inline int need_seqretry(seqlock_t *lock, int seq)
* read_seqretry_or_unlock - end a seqretry or lock block & return retry status
* lock : sequence lock
* seq : sequence number
* Return: 1 to retry operation again, 0 to continue
*/
static inline int read_seqretry_or_unlock(seqlock_t *lock, int *seq)
{ {
if (!(*seq & 1)) { /* Even */ return !(seq & 1) && read_seqretry(lock, seq);
rcu_read_unlock(); }
if (read_seqretry(lock, *seq)) {
(*seq)++; /* Take writer lock */ static inline void done_seqretry(seqlock_t *lock, int seq)
return 1; {
} if (seq & 1)
} else /* Odd */
write_sequnlock(lock); write_sequnlock(lock);
return 0;
} }
/* /*
...@@ -1047,7 +1038,7 @@ void shrink_dcache_for_umount(struct super_block *sb) ...@@ -1047,7 +1038,7 @@ void shrink_dcache_for_umount(struct super_block *sb)
* the parenthood after dropping the lock and check * the parenthood after dropping the lock and check
* that the sequence number still matches. * that the sequence number still matches.
*/ */
static struct dentry *try_to_ascend(struct dentry *old, int locked, unsigned seq) static struct dentry *try_to_ascend(struct dentry *old, unsigned seq)
{ {
struct dentry *new = old->d_parent; struct dentry *new = old->d_parent;
...@@ -1061,7 +1052,7 @@ static struct dentry *try_to_ascend(struct dentry *old, int locked, unsigned seq ...@@ -1061,7 +1052,7 @@ static struct dentry *try_to_ascend(struct dentry *old, int locked, unsigned seq
*/ */
if (new != old->d_parent || if (new != old->d_parent ||
(old->d_flags & DCACHE_DENTRY_KILLED) || (old->d_flags & DCACHE_DENTRY_KILLED) ||
(!locked && read_seqretry(&rename_lock, seq))) { need_seqretry(&rename_lock, seq)) {
spin_unlock(&new->d_lock); spin_unlock(&new->d_lock);
new = NULL; new = NULL;
} }
...@@ -1098,13 +1089,12 @@ static void d_walk(struct dentry *parent, void *data, ...@@ -1098,13 +1089,12 @@ static void d_walk(struct dentry *parent, void *data,
{ {
struct dentry *this_parent; struct dentry *this_parent;
struct list_head *next; struct list_head *next;
unsigned seq; unsigned seq = 0;
int locked = 0;
enum d_walk_ret ret; enum d_walk_ret ret;
bool retry = true; bool retry = true;
seq = read_seqbegin(&rename_lock);
again: again:
read_seqbegin_or_lock(&rename_lock, &seq);
this_parent = parent; this_parent = parent;
spin_lock(&this_parent->d_lock); spin_lock(&this_parent->d_lock);
...@@ -1158,13 +1148,13 @@ static void d_walk(struct dentry *parent, void *data, ...@@ -1158,13 +1148,13 @@ static void d_walk(struct dentry *parent, void *data,
*/ */
if (this_parent != parent) { if (this_parent != parent) {
struct dentry *child = this_parent; struct dentry *child = this_parent;
this_parent = try_to_ascend(this_parent, locked, seq); this_parent = try_to_ascend(this_parent, seq);
if (!this_parent) if (!this_parent)
goto rename_retry; goto rename_retry;
next = child->d_u.d_child.next; next = child->d_u.d_child.next;
goto resume; goto resume;
} }
if (!locked && read_seqretry(&rename_lock, seq)) { if (need_seqretry(&rename_lock, seq)) {
spin_unlock(&this_parent->d_lock); spin_unlock(&this_parent->d_lock);
goto rename_retry; goto rename_retry;
} }
...@@ -1173,17 +1163,13 @@ static void d_walk(struct dentry *parent, void *data, ...@@ -1173,17 +1163,13 @@ static void d_walk(struct dentry *parent, void *data,
out_unlock: out_unlock:
spin_unlock(&this_parent->d_lock); spin_unlock(&this_parent->d_lock);
if (locked) done_seqretry(&rename_lock, seq);
write_sequnlock(&rename_lock);
return; return;
rename_retry: rename_retry:
if (!retry) if (!retry)
return; return;
if (locked) seq = 1;
goto again;
locked = 1;
write_seqlock(&rename_lock);
goto again; goto again;
} }
...@@ -2745,6 +2731,7 @@ static int prepend_path(const struct path *path, ...@@ -2745,6 +2731,7 @@ static int prepend_path(const struct path *path,
char *bptr; char *bptr;
int blen; int blen;
rcu_read_lock();
restart: restart:
bptr = *buffer; bptr = *buffer;
blen = *buflen; blen = *buflen;
...@@ -2783,8 +2770,13 @@ static int prepend_path(const struct path *path, ...@@ -2783,8 +2770,13 @@ static int prepend_path(const struct path *path,
dentry = parent; dentry = parent;
} }
if (read_seqretry_or_unlock(&rename_lock, &seq)) if (!(seq & 1))
rcu_read_unlock();
if (need_seqretry(&rename_lock, seq)) {
seq = 1;
goto restart; goto restart;
}
done_seqretry(&rename_lock, seq);
if (error >= 0 && bptr == *buffer) { if (error >= 0 && bptr == *buffer) {
if (--blen < 0) if (--blen < 0)
...@@ -2957,6 +2949,7 @@ static char *__dentry_path(struct dentry *dentry, char *buf, int buflen) ...@@ -2957,6 +2949,7 @@ static char *__dentry_path(struct dentry *dentry, char *buf, int buflen)
int len, seq = 0; int len, seq = 0;
int error = 0; int error = 0;
rcu_read_lock();
restart: restart:
end = buf + buflen; end = buf + buflen;
len = buflen; len = buflen;
...@@ -2979,8 +2972,13 @@ static char *__dentry_path(struct dentry *dentry, char *buf, int buflen) ...@@ -2979,8 +2972,13 @@ static char *__dentry_path(struct dentry *dentry, char *buf, int buflen)
retval = end; retval = end;
dentry = parent; dentry = parent;
} }
if (read_seqretry_or_unlock(&rename_lock, &seq)) if (!(seq & 1))
rcu_read_unlock();
if (need_seqretry(&rename_lock, seq)) {
seq = 1;
goto restart; goto restart;
}
done_seqretry(&rename_lock, seq);
if (error) if (error)
goto Elong; goto Elong;
return retval; return retval;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment