Commit ff09297e authored by Al Viro's avatar Al Viro

autofs: simplify get_next_positive_...(), get rid of trylocks

* new helper: positive_after(parent, child); parent->d_lock is
held by caller, grabs and returns the first thing after child
in the list of children that has simple_positive() true.  NULL
if nothing's found; NULL child == search the entire list.

* get_next_positive_subdir() loses the redundant check for
d_count and switches to use of that helper.  BTW, dput(NULL) is
a no-op for a good reason...

* get_next_positive_dentry() switched to the same helper.  Logics:
look for positive child in prev; if not found, look for the
positive child of prev's parent following prev, etc.  That way
we are guaranteed that we are only moving rootwards through the
ancestors of prev, which is pinned and thus not going anywhere.
Since ->d_parent on autofs never changes, the same goes for
the entire chain of ancestors and we don't need overlapping
->d_lock on them.  Which avoids the trylock loops, in addition
to simplifying the logics in there...
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 5f9e832c
...@@ -70,6 +70,27 @@ static int autofs_mount_busy(struct vfsmount *mnt, ...@@ -70,6 +70,27 @@ static int autofs_mount_busy(struct vfsmount *mnt,
return status; return status;
} }
/* p->d_lock held */
static struct dentry *positive_after(struct dentry *p, struct dentry *child)
{
if (child)
child = list_next_entry(child, d_child);
else
child = list_first_entry(&p->d_subdirs, struct dentry, d_child);
list_for_each_entry_from(child, &p->d_subdirs, d_child) {
spin_lock_nested(&child->d_lock, DENTRY_D_LOCK_NESTED);
if (simple_positive(child)) {
dget_dlock(child);
spin_unlock(&child->d_lock);
return child;
}
spin_unlock(&child->d_lock);
}
return NULL;
}
/* /*
* Calculate and dget next entry in the subdirs list under root. * Calculate and dget next entry in the subdirs list under root.
*/ */
...@@ -77,43 +98,14 @@ static struct dentry *get_next_positive_subdir(struct dentry *prev, ...@@ -77,43 +98,14 @@ static struct dentry *get_next_positive_subdir(struct dentry *prev,
struct dentry *root) struct dentry *root)
{ {
struct autofs_sb_info *sbi = autofs_sbi(root->d_sb); struct autofs_sb_info *sbi = autofs_sbi(root->d_sb);
struct list_head *next;
struct dentry *q; struct dentry *q;
spin_lock(&sbi->lookup_lock); spin_lock(&sbi->lookup_lock);
spin_lock(&root->d_lock); spin_lock(&root->d_lock);
q = positive_after(root, prev);
if (prev)
next = prev->d_child.next;
else {
prev = dget_dlock(root);
next = prev->d_subdirs.next;
}
cont:
if (next == &root->d_subdirs) {
spin_unlock(&root->d_lock);
spin_unlock(&sbi->lookup_lock);
dput(prev);
return NULL;
}
q = list_entry(next, struct dentry, d_child);
spin_lock_nested(&q->d_lock, DENTRY_D_LOCK_NESTED);
/* Already gone or negative dentry (under construction) - try next */
if (!d_count(q) || !simple_positive(q)) {
spin_unlock(&q->d_lock);
next = q->d_child.next;
goto cont;
}
dget_dlock(q);
spin_unlock(&q->d_lock);
spin_unlock(&root->d_lock); spin_unlock(&root->d_lock);
spin_unlock(&sbi->lookup_lock); spin_unlock(&sbi->lookup_lock);
dput(prev); dput(prev);
return q; return q;
} }
...@@ -124,59 +116,28 @@ static struct dentry *get_next_positive_dentry(struct dentry *prev, ...@@ -124,59 +116,28 @@ static struct dentry *get_next_positive_dentry(struct dentry *prev,
struct dentry *root) struct dentry *root)
{ {
struct autofs_sb_info *sbi = autofs_sbi(root->d_sb); struct autofs_sb_info *sbi = autofs_sbi(root->d_sb);
struct list_head *next; struct dentry *p = prev, *ret = NULL, *d = NULL;
struct dentry *p, *ret;
if (prev == NULL) if (prev == NULL)
return dget(root); return dget(root);
spin_lock(&sbi->lookup_lock); spin_lock(&sbi->lookup_lock);
relock:
p = prev;
spin_lock(&p->d_lock); spin_lock(&p->d_lock);
again:
next = p->d_subdirs.next;
if (next == &p->d_subdirs) {
while (1) { while (1) {
struct dentry *parent; struct dentry *parent;
if (p == root) { ret = positive_after(p, d);
spin_unlock(&p->d_lock); if (ret || p == root)
spin_unlock(&sbi->lookup_lock); break;
dput(prev);
return NULL;
}
parent = p->d_parent; parent = p->d_parent;
if (!spin_trylock(&parent->d_lock)) {
spin_unlock(&p->d_lock); spin_unlock(&p->d_lock);
cpu_relax(); spin_lock(&parent->d_lock);
goto relock; d = p;
}
spin_unlock(&p->d_lock);
next = p->d_child.next;
p = parent; p = parent;
if (next != &parent->d_subdirs)
break;
} }
}
ret = list_entry(next, struct dentry, d_child);
spin_lock_nested(&ret->d_lock, DENTRY_D_LOCK_NESTED);
/* Negative dentry - try next */
if (!simple_positive(ret)) {
spin_unlock(&p->d_lock);
lock_set_subclass(&ret->d_lock.dep_map, 0, _RET_IP_);
p = ret;
goto again;
}
dget_dlock(ret);
spin_unlock(&ret->d_lock);
spin_unlock(&p->d_lock); spin_unlock(&p->d_lock);
spin_unlock(&sbi->lookup_lock); spin_unlock(&sbi->lookup_lock);
dput(prev); dput(prev);
return ret; return ret;
} }
......
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