Skip to content

Commit

Permalink
fs: dcache scale subdirs
Browse files Browse the repository at this point in the history
Protect d_subdirs and d_child with d_lock, except in filesystems that aren't
using dcache_lock for these anyway (eg. using i_mutex).

Note: if we change the locking rule in future so that ->d_child protection is
provided only with ->d_parent->d_lock, it may allow us to reduce some locking.
But it would be an exception to an otherwise regular locking scheme, so we'd
have to see some good results. Probably not worthwhile.

Signed-off-by: Nick Piggin <[email protected]>
  • Loading branch information
Nick Piggin committed Jan 7, 2011
1 parent da50295 commit 2fd6b7f
Show file tree
Hide file tree
Showing 16 changed files with 339 additions and 160 deletions.
4 changes: 4 additions & 0 deletions drivers/staging/smbfs/cache.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,15 @@ smb_invalidate_dircache_entries(struct dentry *parent)
struct dentry *dentry;

spin_lock(&dcache_lock);
spin_lock(&parent->d_lock);
next = parent->d_subdirs.next;
while (next != &parent->d_subdirs) {
dentry = list_entry(next, struct dentry, d_u.d_child);
dentry->d_fsdata = NULL;
smb_age_dentry(server, dentry);
next = next->next;
}
spin_unlock(&parent->d_lock);
spin_unlock(&dcache_lock);
}

Expand Down Expand Up @@ -97,6 +99,7 @@ smb_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos)

/* If a pointer is invalid, we search the dentry. */
spin_lock(&dcache_lock);
spin_lock(&parent->d_lock);
next = parent->d_subdirs.next;
while (next != &parent->d_subdirs) {
dent = list_entry(next, struct dentry, d_u.d_child);
Expand All @@ -111,6 +114,7 @@ smb_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos)
}
dent = NULL;
out_unlock:
spin_unlock(&parent->d_lock);
spin_unlock(&dcache_lock);
return dent;
}
Expand Down
8 changes: 5 additions & 3 deletions drivers/usb/core/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -344,18 +344,20 @@ static int usbfs_empty (struct dentry *dentry)
struct list_head *list;

spin_lock(&dcache_lock);

spin_lock(&dentry->d_lock);
list_for_each(list, &dentry->d_subdirs) {
struct dentry *de = list_entry(list, struct dentry, d_u.d_child);
spin_lock(&de->d_lock);

spin_lock_nested(&de->d_lock, DENTRY_D_LOCK_NESTED);
if (usbfs_positive(de)) {
spin_unlock(&de->d_lock);
spin_unlock(&dentry->d_lock);
spin_unlock(&dcache_lock);
return 0;
}
spin_unlock(&de->d_lock);
}

spin_unlock(&dentry->d_lock);
spin_unlock(&dcache_lock);
return 1;
}
Expand Down
11 changes: 11 additions & 0 deletions fs/autofs4/autofs_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,17 @@ static inline int simple_positive(struct dentry *dentry)
return dentry->d_inode && !d_unhashed(dentry);
}

static inline void __autofs4_add_expiring(struct dentry *dentry)
{
struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
struct autofs_info *ino = autofs4_dentry_ino(dentry);
if (ino) {
if (list_empty(&ino->expiring))
list_add(&ino->expiring, &sbi->expiring_list);
}
return;
}

static inline void autofs4_add_expiring(struct dentry *dentry)
{
struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
Expand Down
127 changes: 60 additions & 67 deletions fs/autofs4/expire.c
Original file line number Diff line number Diff line change
Expand Up @@ -91,24 +91,64 @@ static int autofs4_mount_busy(struct vfsmount *mnt, struct dentry *dentry)
}

/*
* Calculate next entry in top down tree traversal.
* From next_mnt in namespace.c - elegant.
* Calculate and dget next entry in top down tree traversal.
*/
static struct dentry *next_dentry(struct dentry *p, struct dentry *root)
static struct dentry *get_next_positive_dentry(struct dentry *prev,
struct dentry *root)
{
struct list_head *next = p->d_subdirs.next;
struct list_head *next;
struct dentry *p, *ret;

if (prev == NULL)
return dget(prev);

spin_lock(&dcache_lock);
relock:
p = prev;
spin_lock(&p->d_lock);
again:
next = p->d_subdirs.next;
if (next == &p->d_subdirs) {
while (1) {
if (p == root)
struct dentry *parent;

if (p == root) {
spin_unlock(&p->d_lock);
spin_unlock(&dcache_lock);
dput(prev);
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_u.d_child.next;
if (next != &p->d_parent->d_subdirs)
p = parent;
if (next != &parent->d_subdirs)
break;
p = p->d_parent;
}
}
return list_entry(next, struct dentry, d_u.d_child);
ret = list_entry(next, struct dentry, d_u.d_child);

spin_lock_nested(&ret->d_lock, DENTRY_D_LOCK_NESTED);
/* Negative dentry - try next */
if (!simple_positive(ret)) {
spin_unlock(&ret->d_lock);
p = ret;
goto again;
}
dget_dlock(ret);
spin_unlock(&ret->d_lock);
spin_unlock(&p->d_lock);
spin_unlock(&dcache_lock);

dput(prev);

return ret;
}

/*
Expand Down Expand Up @@ -158,22 +198,11 @@ static int autofs4_tree_busy(struct vfsmount *mnt,
if (!simple_positive(top))
return 1;

spin_lock(&dcache_lock);
for (p = top; p; p = next_dentry(p, top)) {
spin_lock(&p->d_lock);
/* Negative dentry - give up */
if (!simple_positive(p)) {
spin_unlock(&p->d_lock);
continue;
}

p = NULL;
while ((p = get_next_positive_dentry(p, top))) {
DPRINTK("dentry %p %.*s",
p, (int) p->d_name.len, p->d_name.name);

p = dget_dlock(p);
spin_unlock(&p->d_lock);
spin_unlock(&dcache_lock);

/*
* Is someone visiting anywhere in the subtree ?
* If there's no mount we need to check the usage
Expand Down Expand Up @@ -208,10 +237,7 @@ static int autofs4_tree_busy(struct vfsmount *mnt,
return 1;
}
}
dput(p);
spin_lock(&dcache_lock);
}
spin_unlock(&dcache_lock);

/* Timeout of a tree mount is ultimately determined by its top dentry */
if (!autofs4_can_expire(top, timeout, do_now))
Expand All @@ -230,36 +256,21 @@ static struct dentry *autofs4_check_leaves(struct vfsmount *mnt,
DPRINTK("parent %p %.*s",
parent, (int)parent->d_name.len, parent->d_name.name);

spin_lock(&dcache_lock);
for (p = parent; p; p = next_dentry(p, parent)) {
spin_lock(&p->d_lock);
/* Negative dentry - give up */
if (!simple_positive(p)) {
spin_unlock(&p->d_lock);
continue;
}

p = NULL;
while ((p = get_next_positive_dentry(p, parent))) {
DPRINTK("dentry %p %.*s",
p, (int) p->d_name.len, p->d_name.name);

p = dget_dlock(p);
spin_unlock(&p->d_lock);
spin_unlock(&dcache_lock);

if (d_mountpoint(p)) {
/* Can we umount this guy */
if (autofs4_mount_busy(mnt, p))
goto cont;
continue;

/* Can we expire this guy */
if (autofs4_can_expire(p, timeout, do_now))
return p;
}
cont:
dput(p);
spin_lock(&dcache_lock);
}
spin_unlock(&dcache_lock);
return NULL;
}

Expand Down Expand Up @@ -310,8 +321,8 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
{
unsigned long timeout;
struct dentry *root = sb->s_root;
struct dentry *dentry;
struct dentry *expired = NULL;
struct list_head *next;
int do_now = how & AUTOFS_EXP_IMMEDIATE;
int exp_leaves = how & AUTOFS_EXP_LEAVES;
struct autofs_info *ino;
Expand All @@ -323,26 +334,8 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
now = jiffies;
timeout = sbi->exp_timeout;

spin_lock(&dcache_lock);
next = root->d_subdirs.next;

/* On exit from the loop expire is set to a dgot dentry
* to expire or it's NULL */
while ( next != &root->d_subdirs ) {
struct dentry *dentry = list_entry(next, struct dentry, d_u.d_child);

/* Negative dentry - give up */
spin_lock(&dentry->d_lock);
if (!simple_positive(dentry)) {
next = next->next;
spin_unlock(&dentry->d_lock);
continue;
}

dentry = dget_dlock(dentry);
spin_unlock(&dentry->d_lock);
spin_unlock(&dcache_lock);

dentry = NULL;
while ((dentry = get_next_positive_dentry(dentry, root))) {
spin_lock(&sbi->fs_lock);
ino = autofs4_dentry_ino(dentry);

Expand Down Expand Up @@ -405,11 +398,7 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
}
next:
spin_unlock(&sbi->fs_lock);
dput(dentry);
spin_lock(&dcache_lock);
next = next->next;
}
spin_unlock(&dcache_lock);
return NULL;

found:
Expand All @@ -420,7 +409,11 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
init_completion(&ino->expire_complete);
spin_unlock(&sbi->fs_lock);
spin_lock(&dcache_lock);
spin_lock(&expired->d_parent->d_lock);
spin_lock_nested(&expired->d_lock, DENTRY_D_LOCK_NESTED);
list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child);
spin_unlock(&expired->d_lock);
spin_unlock(&expired->d_parent->d_lock);
spin_unlock(&dcache_lock);
return expired;
}
Expand Down
18 changes: 16 additions & 2 deletions fs/autofs4/root.c
Original file line number Diff line number Diff line change
Expand Up @@ -143,10 +143,13 @@ static int autofs4_dir_open(struct inode *inode, struct file *file)
* it.
*/
spin_lock(&dcache_lock);
spin_lock(&dentry->d_lock);
if (!d_mountpoint(dentry) && list_empty(&dentry->d_subdirs)) {
spin_unlock(&dentry->d_lock);
spin_unlock(&dcache_lock);
return -ENOENT;
}
spin_unlock(&dentry->d_lock);
spin_unlock(&dcache_lock);

out:
Expand Down Expand Up @@ -253,7 +256,9 @@ static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd)
lookup_type = autofs4_need_mount(nd->flags);
spin_lock(&sbi->fs_lock);
spin_lock(&dcache_lock);
spin_lock(&dentry->d_lock);
if (!(lookup_type || ino->flags & AUTOFS_INF_PENDING)) {
spin_unlock(&dentry->d_lock);
spin_unlock(&dcache_lock);
spin_unlock(&sbi->fs_lock);
goto follow;
Expand All @@ -266,6 +271,7 @@ static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd)
*/
if (ino->flags & AUTOFS_INF_PENDING ||
(!d_mountpoint(dentry) && list_empty(&dentry->d_subdirs))) {
spin_unlock(&dentry->d_lock);
spin_unlock(&dcache_lock);
spin_unlock(&sbi->fs_lock);

Expand All @@ -275,6 +281,7 @@ static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd)

goto follow;
}
spin_unlock(&dentry->d_lock);
spin_unlock(&dcache_lock);
spin_unlock(&sbi->fs_lock);
follow:
Expand Down Expand Up @@ -347,10 +354,12 @@ static int autofs4_revalidate(struct dentry *dentry, struct nameidata *nd)

/* Check for a non-mountpoint directory with no contents */
spin_lock(&dcache_lock);
spin_lock(&dentry->d_lock);
if (S_ISDIR(dentry->d_inode->i_mode) &&
!d_mountpoint(dentry) && list_empty(&dentry->d_subdirs)) {
DPRINTK("dentry=%p %.*s, emptydir",
dentry, dentry->d_name.len, dentry->d_name.name);
spin_unlock(&dentry->d_lock);
spin_unlock(&dcache_lock);

/* The daemon never causes a mount to trigger */
Expand All @@ -367,6 +376,7 @@ static int autofs4_revalidate(struct dentry *dentry, struct nameidata *nd)

return status;
}
spin_unlock(&dentry->d_lock);
spin_unlock(&dcache_lock);

return 1;
Expand Down Expand Up @@ -776,12 +786,16 @@ static int autofs4_dir_rmdir(struct inode *dir, struct dentry *dentry)
return -EACCES;

spin_lock(&dcache_lock);
spin_lock(&sbi->lookup_lock);
spin_lock(&dentry->d_lock);
if (!list_empty(&dentry->d_subdirs)) {
spin_unlock(&dentry->d_lock);
spin_unlock(&sbi->lookup_lock);
spin_unlock(&dcache_lock);
return -ENOTEMPTY;
}
autofs4_add_expiring(dentry);
spin_lock(&dentry->d_lock);
__autofs4_add_expiring(dentry);
spin_unlock(&sbi->lookup_lock);
__d_drop(dentry);
spin_unlock(&dentry->d_lock);
spin_unlock(&dcache_lock);
Expand Down
Loading

0 comments on commit 2fd6b7f

Please sign in to comment.