Merge branch 'work.autofs' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
Pull autofs updates from Al Viro: "The most interesting part here is getting rid of the last trylock loop on dentry->d_lock. The ones in fs/dcache.c had been dealt with several years ago, but there'd been leftovers in fs/autofs/expire.c" * 'work.autofs' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: autofs_lookup(): hold ->d_lock over playing with ->d_flags get rid of autofs_info->active_count autofs: simplify get_next_positive_...(), get rid of trylocks
This commit is contained in:
commit
8e6ee05d8a
@ -58,7 +58,6 @@ struct autofs_info {
|
|||||||
struct completion expire_complete;
|
struct completion expire_complete;
|
||||||
|
|
||||||
struct list_head active;
|
struct list_head active;
|
||||||
int active_count;
|
|
||||||
|
|
||||||
struct list_head expiring;
|
struct list_head expiring;
|
||||||
|
|
||||||
|
@ -70,6 +70,27 @@ done:
|
|||||||
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,
|
|||||||
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,
|
|||||||
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:
|
while (1) {
|
||||||
next = p->d_subdirs.next;
|
struct dentry *parent;
|
||||||
if (next == &p->d_subdirs) {
|
|
||||||
while (1) {
|
|
||||||
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);
|
parent = p->d_parent;
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
parent = p->d_parent;
|
|
||||||
if (!spin_trylock(&parent->d_lock)) {
|
|
||||||
spin_unlock(&p->d_lock);
|
|
||||||
cpu_relax();
|
|
||||||
goto relock;
|
|
||||||
}
|
|
||||||
spin_unlock(&p->d_lock);
|
|
||||||
next = p->d_child.next;
|
|
||||||
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);
|
spin_unlock(&p->d_lock);
|
||||||
lock_set_subclass(&ret->d_lock.dep_map, 0, _RET_IP_);
|
spin_lock(&parent->d_lock);
|
||||||
p = ret;
|
d = p;
|
||||||
goto again;
|
p = parent;
|
||||||
}
|
}
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,38 +60,15 @@ const struct dentry_operations autofs_dentry_operations = {
|
|||||||
.d_release = autofs_dentry_release,
|
.d_release = autofs_dentry_release,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void autofs_add_active(struct dentry *dentry)
|
|
||||||
{
|
|
||||||
struct autofs_sb_info *sbi = autofs_sbi(dentry->d_sb);
|
|
||||||
struct autofs_info *ino;
|
|
||||||
|
|
||||||
ino = autofs_dentry_ino(dentry);
|
|
||||||
if (ino) {
|
|
||||||
spin_lock(&sbi->lookup_lock);
|
|
||||||
if (!ino->active_count) {
|
|
||||||
if (list_empty(&ino->active))
|
|
||||||
list_add(&ino->active, &sbi->active_list);
|
|
||||||
}
|
|
||||||
ino->active_count++;
|
|
||||||
spin_unlock(&sbi->lookup_lock);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void autofs_del_active(struct dentry *dentry)
|
static void autofs_del_active(struct dentry *dentry)
|
||||||
{
|
{
|
||||||
struct autofs_sb_info *sbi = autofs_sbi(dentry->d_sb);
|
struct autofs_sb_info *sbi = autofs_sbi(dentry->d_sb);
|
||||||
struct autofs_info *ino;
|
struct autofs_info *ino;
|
||||||
|
|
||||||
ino = autofs_dentry_ino(dentry);
|
ino = autofs_dentry_ino(dentry);
|
||||||
if (ino) {
|
spin_lock(&sbi->lookup_lock);
|
||||||
spin_lock(&sbi->lookup_lock);
|
list_del_init(&ino->active);
|
||||||
ino->active_count--;
|
spin_unlock(&sbi->lookup_lock);
|
||||||
if (!ino->active_count) {
|
|
||||||
if (!list_empty(&ino->active))
|
|
||||||
list_del_init(&ino->active);
|
|
||||||
}
|
|
||||||
spin_unlock(&sbi->lookup_lock);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int autofs_dir_open(struct inode *inode, struct file *file)
|
static int autofs_dir_open(struct inode *inode, struct file *file)
|
||||||
@ -527,19 +504,22 @@ static struct dentry *autofs_lookup(struct inode *dir,
|
|||||||
if (!autofs_oz_mode(sbi) && !IS_ROOT(dentry->d_parent))
|
if (!autofs_oz_mode(sbi) && !IS_ROOT(dentry->d_parent))
|
||||||
return ERR_PTR(-ENOENT);
|
return ERR_PTR(-ENOENT);
|
||||||
|
|
||||||
/* Mark entries in the root as mount triggers */
|
|
||||||
if (IS_ROOT(dentry->d_parent) &&
|
|
||||||
autofs_type_indirect(sbi->type))
|
|
||||||
__managed_dentry_set_managed(dentry);
|
|
||||||
|
|
||||||
ino = autofs_new_ino(sbi);
|
ino = autofs_new_ino(sbi);
|
||||||
if (!ino)
|
if (!ino)
|
||||||
return ERR_PTR(-ENOMEM);
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
|
spin_lock(&sbi->lookup_lock);
|
||||||
|
spin_lock(&dentry->d_lock);
|
||||||
|
/* Mark entries in the root as mount triggers */
|
||||||
|
if (IS_ROOT(dentry->d_parent) &&
|
||||||
|
autofs_type_indirect(sbi->type))
|
||||||
|
__managed_dentry_set_managed(dentry);
|
||||||
dentry->d_fsdata = ino;
|
dentry->d_fsdata = ino;
|
||||||
ino->dentry = dentry;
|
ino->dentry = dentry;
|
||||||
|
|
||||||
autofs_add_active(dentry);
|
list_add(&ino->active, &sbi->active_list);
|
||||||
|
spin_unlock(&sbi->lookup_lock);
|
||||||
|
spin_unlock(&dentry->d_lock);
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user