Commit 2fd6b7f5 authored by Nick Piggin's avatar Nick Piggin

fs: dcache scale subdirs

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: default avatarNick Piggin <npiggin@kernel.dk>
parent da502956
...@@ -63,6 +63,7 @@ smb_invalidate_dircache_entries(struct dentry *parent) ...@@ -63,6 +63,7 @@ smb_invalidate_dircache_entries(struct dentry *parent)
struct dentry *dentry; struct dentry *dentry;
spin_lock(&dcache_lock); spin_lock(&dcache_lock);
spin_lock(&parent->d_lock);
next = parent->d_subdirs.next; next = parent->d_subdirs.next;
while (next != &parent->d_subdirs) { while (next != &parent->d_subdirs) {
dentry = list_entry(next, struct dentry, d_u.d_child); dentry = list_entry(next, struct dentry, d_u.d_child);
...@@ -70,6 +71,7 @@ smb_invalidate_dircache_entries(struct dentry *parent) ...@@ -70,6 +71,7 @@ smb_invalidate_dircache_entries(struct dentry *parent)
smb_age_dentry(server, dentry); smb_age_dentry(server, dentry);
next = next->next; next = next->next;
} }
spin_unlock(&parent->d_lock);
spin_unlock(&dcache_lock); spin_unlock(&dcache_lock);
} }
...@@ -97,6 +99,7 @@ smb_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos) ...@@ -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. */ /* If a pointer is invalid, we search the dentry. */
spin_lock(&dcache_lock); spin_lock(&dcache_lock);
spin_lock(&parent->d_lock);
next = parent->d_subdirs.next; next = parent->d_subdirs.next;
while (next != &parent->d_subdirs) { while (next != &parent->d_subdirs) {
dent = list_entry(next, struct dentry, d_u.d_child); dent = list_entry(next, struct dentry, d_u.d_child);
...@@ -111,6 +114,7 @@ smb_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos) ...@@ -111,6 +114,7 @@ smb_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos)
} }
dent = NULL; dent = NULL;
out_unlock: out_unlock:
spin_unlock(&parent->d_lock);
spin_unlock(&dcache_lock); spin_unlock(&dcache_lock);
return dent; return dent;
} }
......
...@@ -344,18 +344,20 @@ static int usbfs_empty (struct dentry *dentry) ...@@ -344,18 +344,20 @@ static int usbfs_empty (struct dentry *dentry)
struct list_head *list; struct list_head *list;
spin_lock(&dcache_lock); spin_lock(&dcache_lock);
spin_lock(&dentry->d_lock);
list_for_each(list, &dentry->d_subdirs) { list_for_each(list, &dentry->d_subdirs) {
struct dentry *de = list_entry(list, struct dentry, d_u.d_child); 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)) { if (usbfs_positive(de)) {
spin_unlock(&de->d_lock); spin_unlock(&de->d_lock);
spin_unlock(&dentry->d_lock);
spin_unlock(&dcache_lock); spin_unlock(&dcache_lock);
return 0; return 0;
} }
spin_unlock(&de->d_lock); spin_unlock(&de->d_lock);
} }
spin_unlock(&dentry->d_lock);
spin_unlock(&dcache_lock); spin_unlock(&dcache_lock);
return 1; return 1;
} }
......
...@@ -254,6 +254,17 @@ static inline int simple_positive(struct dentry *dentry) ...@@ -254,6 +254,17 @@ static inline int simple_positive(struct dentry *dentry)
return dentry->d_inode && !d_unhashed(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) static inline void autofs4_add_expiring(struct dentry *dentry)
{ {
struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
......
...@@ -91,24 +91,64 @@ static int autofs4_mount_busy(struct vfsmount *mnt, struct dentry *dentry) ...@@ -91,24 +91,64 @@ static int autofs4_mount_busy(struct vfsmount *mnt, struct dentry *dentry)
} }
/* /*
* Calculate next entry in top down tree traversal. * Calculate and dget next entry in top down tree traversal.
* From next_mnt in namespace.c - elegant.
*/ */
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) { if (next == &p->d_subdirs) {
while (1) { while (1) {
if (p == root) struct dentry *parent;
if (p == root) {
spin_unlock(&p->d_lock);
spin_unlock(&dcache_lock);
dput(prev);
return NULL; 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; next = p->d_u.d_child.next;
if (next != &p->d_parent->d_subdirs) p = parent;
if (next != &parent->d_subdirs)
break; 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;
} }
/* /*
...@@ -158,22 +198,11 @@ static int autofs4_tree_busy(struct vfsmount *mnt, ...@@ -158,22 +198,11 @@ static int autofs4_tree_busy(struct vfsmount *mnt,
if (!simple_positive(top)) if (!simple_positive(top))
return 1; return 1;
spin_lock(&dcache_lock); p = NULL;
for (p = top; p; p = next_dentry(p, top)) { while ((p = get_next_positive_dentry(p, top))) {
spin_lock(&p->d_lock);
/* Negative dentry - give up */
if (!simple_positive(p)) {
spin_unlock(&p->d_lock);
continue;
}
DPRINTK("dentry %p %.*s", DPRINTK("dentry %p %.*s",
p, (int) p->d_name.len, p->d_name.name); 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 ? * Is someone visiting anywhere in the subtree ?
* If there's no mount we need to check the usage * If there's no mount we need to check the usage
...@@ -208,10 +237,7 @@ static int autofs4_tree_busy(struct vfsmount *mnt, ...@@ -208,10 +237,7 @@ static int autofs4_tree_busy(struct vfsmount *mnt,
return 1; return 1;
} }
} }
dput(p);
spin_lock(&dcache_lock);
} }
spin_unlock(&dcache_lock);
/* Timeout of a tree mount is ultimately determined by its top dentry */ /* Timeout of a tree mount is ultimately determined by its top dentry */
if (!autofs4_can_expire(top, timeout, do_now)) if (!autofs4_can_expire(top, timeout, do_now))
...@@ -230,36 +256,21 @@ static struct dentry *autofs4_check_leaves(struct vfsmount *mnt, ...@@ -230,36 +256,21 @@ static struct dentry *autofs4_check_leaves(struct vfsmount *mnt,
DPRINTK("parent %p %.*s", DPRINTK("parent %p %.*s",
parent, (int)parent->d_name.len, parent->d_name.name); parent, (int)parent->d_name.len, parent->d_name.name);
spin_lock(&dcache_lock); p = NULL;
for (p = parent; p; p = next_dentry(p, parent)) { while ((p = get_next_positive_dentry(p, parent))) {
spin_lock(&p->d_lock);
/* Negative dentry - give up */
if (!simple_positive(p)) {
spin_unlock(&p->d_lock);
continue;
}
DPRINTK("dentry %p %.*s", DPRINTK("dentry %p %.*s",
p, (int) p->d_name.len, p->d_name.name); 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)) { if (d_mountpoint(p)) {
/* Can we umount this guy */ /* Can we umount this guy */
if (autofs4_mount_busy(mnt, p)) if (autofs4_mount_busy(mnt, p))
goto cont; continue;
/* Can we expire this guy */ /* Can we expire this guy */
if (autofs4_can_expire(p, timeout, do_now)) if (autofs4_can_expire(p, timeout, do_now))
return p; return p;
} }
cont:
dput(p);
spin_lock(&dcache_lock);
} }
spin_unlock(&dcache_lock);
return NULL; return NULL;
} }
...@@ -310,8 +321,8 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb, ...@@ -310,8 +321,8 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
{ {
unsigned long timeout; unsigned long timeout;
struct dentry *root = sb->s_root; struct dentry *root = sb->s_root;
struct dentry *dentry;
struct dentry *expired = NULL; struct dentry *expired = NULL;
struct list_head *next;
int do_now = how & AUTOFS_EXP_IMMEDIATE; int do_now = how & AUTOFS_EXP_IMMEDIATE;
int exp_leaves = how & AUTOFS_EXP_LEAVES; int exp_leaves = how & AUTOFS_EXP_LEAVES;
struct autofs_info *ino; struct autofs_info *ino;
...@@ -323,26 +334,8 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb, ...@@ -323,26 +334,8 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
now = jiffies; now = jiffies;
timeout = sbi->exp_timeout; timeout = sbi->exp_timeout;
spin_lock(&dcache_lock); dentry = NULL;
next = root->d_subdirs.next; while ((dentry = get_next_positive_dentry(dentry, root))) {
/* 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);
spin_lock(&sbi->fs_lock); spin_lock(&sbi->fs_lock);
ino = autofs4_dentry_ino(dentry); ino = autofs4_dentry_ino(dentry);
...@@ -405,11 +398,7 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb, ...@@ -405,11 +398,7 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
} }
next: next:
spin_unlock(&sbi->fs_lock); spin_unlock(&sbi->fs_lock);
dput(dentry);
spin_lock(&dcache_lock);
next = next->next;
} }
spin_unlock(&dcache_lock);
return NULL; return NULL;
found: found:
...@@ -420,7 +409,11 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb, ...@@ -420,7 +409,11 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
init_completion(&ino->expire_complete); init_completion(&ino->expire_complete);
spin_unlock(&sbi->fs_lock); spin_unlock(&sbi->fs_lock);
spin_lock(&dcache_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); 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); spin_unlock(&dcache_lock);
return expired; return expired;
} }
......
...@@ -143,10 +143,13 @@ static int autofs4_dir_open(struct inode *inode, struct file *file) ...@@ -143,10 +143,13 @@ static int autofs4_dir_open(struct inode *inode, struct file *file)
* it. * it.
*/ */
spin_lock(&dcache_lock); spin_lock(&dcache_lock);
spin_lock(&dentry->d_lock);
if (!d_mountpoint(dentry) && list_empty(&dentry->d_subdirs)) { if (!d_mountpoint(dentry) && list_empty(&dentry->d_subdirs)) {
spin_unlock(&dentry->d_lock);
spin_unlock(&dcache_lock); spin_unlock(&dcache_lock);
return -ENOENT; return -ENOENT;
} }
spin_unlock(&dentry->d_lock);
spin_unlock(&dcache_lock); spin_unlock(&dcache_lock);
out: out:
...@@ -253,7 +256,9 @@ static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd) ...@@ -253,7 +256,9 @@ static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd)
lookup_type = autofs4_need_mount(nd->flags); lookup_type = autofs4_need_mount(nd->flags);
spin_lock(&sbi->fs_lock); spin_lock(&sbi->fs_lock);
spin_lock(&dcache_lock); spin_lock(&dcache_lock);
spin_lock(&dentry->d_lock);
if (!(lookup_type || ino->flags & AUTOFS_INF_PENDING)) { if (!(lookup_type || ino->flags & AUTOFS_INF_PENDING)) {
spin_unlock(&dentry->d_lock);
spin_unlock(&dcache_lock); spin_unlock(&dcache_lock);
spin_unlock(&sbi->fs_lock); spin_unlock(&sbi->fs_lock);
goto follow; goto follow;
...@@ -266,6 +271,7 @@ static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd) ...@@ -266,6 +271,7 @@ static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd)
*/ */
if (ino->flags & AUTOFS_INF_PENDING || if (ino->flags & AUTOFS_INF_PENDING ||
(!d_mountpoint(dentry) && list_empty(&dentry->d_subdirs))) { (!d_mountpoint(dentry) && list_empty(&dentry->d_subdirs))) {
spin_unlock(&dentry->d_lock);
spin_unlock(&dcache_lock); spin_unlock(&dcache_lock);
spin_unlock(&sbi->fs_lock); spin_unlock(&sbi->fs_lock);
...@@ -275,6 +281,7 @@ static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd) ...@@ -275,6 +281,7 @@ static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd)
goto follow; goto follow;
} }
spin_unlock(&dentry->d_lock);
spin_unlock(&dcache_lock); spin_unlock(&dcache_lock);
spin_unlock(&sbi->fs_lock); spin_unlock(&sbi->fs_lock);
follow: follow:
...@@ -347,10 +354,12 @@ static int autofs4_revalidate(struct dentry *dentry, struct nameidata *nd) ...@@ -347,10 +354,12 @@ static int autofs4_revalidate(struct dentry *dentry, struct nameidata *nd)
/* Check for a non-mountpoint directory with no contents */ /* Check for a non-mountpoint directory with no contents */
spin_lock(&dcache_lock); spin_lock(&dcache_lock);
spin_lock(&dentry->d_lock);
if (S_ISDIR(dentry->d_inode->i_mode) && if (S_ISDIR(dentry->d_inode->i_mode) &&
!d_mountpoint(dentry) && list_empty(&dentry->d_subdirs)) { !d_mountpoint(dentry) && list_empty(&dentry->d_subdirs)) {
DPRINTK("dentry=%p %.*s, emptydir", DPRINTK("dentry=%p %.*s, emptydir",
dentry, dentry->d_name.len, dentry->d_name.name); dentry, dentry->d_name.len, dentry->d_name.name);
spin_unlock(&dentry->d_lock);
spin_unlock(&dcache_lock); spin_unlock(&dcache_lock);
/* The daemon never causes a mount to trigger */ /* The daemon never causes a mount to trigger */
...@@ -367,6 +376,7 @@ static int autofs4_revalidate(struct dentry *dentry, struct nameidata *nd) ...@@ -367,6 +376,7 @@ static int autofs4_revalidate(struct dentry *dentry, struct nameidata *nd)
return status; return status;
} }
spin_unlock(&dentry->d_lock);
spin_unlock(&dcache_lock); spin_unlock(&dcache_lock);
return 1; return 1;
...@@ -776,12 +786,16 @@ static int autofs4_dir_rmdir(struct inode *dir, struct dentry *dentry) ...@@ -776,12 +786,16 @@ static int autofs4_dir_rmdir(struct inode *dir, struct dentry *dentry)
return -EACCES; return -EACCES;
spin_lock(&dcache_lock); spin_lock(&dcache_lock);
spin_lock(&sbi->lookup_lock);
spin_lock(&dentry->d_lock);
if (!list_empty(&dentry->d_subdirs)) { if (!list_empty(&dentry->d_subdirs)) {
spin_unlock(&dentry->d_lock);
spin_unlock(&sbi->lookup_lock);
spin_unlock(&dcache_lock); spin_unlock(&dcache_lock);
return -ENOTEMPTY; return -ENOTEMPTY;
} }
autofs4_add_expiring(dentry); __autofs4_add_expiring(dentry);
spin_lock(&dentry->d_lock); spin_unlock(&sbi->lookup_lock);
__d_drop(dentry); __d_drop(dentry);
spin_unlock(&dentry->d_lock); spin_unlock(&dentry->d_lock);
spin_unlock(&dcache_lock); spin_unlock(&dcache_lock);
......
...@@ -113,6 +113,7 @@ static int __dcache_readdir(struct file *filp, ...@@ -113,6 +113,7 @@ static int __dcache_readdir(struct file *filp,
last); last);
spin_lock(&dcache_lock); spin_lock(&dcache_lock);
spin_lock(&parent->d_lock);
/* start at beginning? */ /* start at beginning? */
if (filp->f_pos == 2 || last == NULL || if (filp->f_pos == 2 || last == NULL ||
...@@ -136,7 +137,7 @@ static int __dcache_readdir(struct file *filp, ...@@ -136,7 +137,7 @@ static int __dcache_readdir(struct file *filp,
fi->at_end = 1; fi->at_end = 1;
goto out_unlock; goto out_unlock;
} }
spin_lock(&dentry->d_lock); spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
if (!d_unhashed(dentry) && dentry->d_inode && if (!d_unhashed(dentry) && dentry->d_inode &&
ceph_snap(dentry->d_inode) != CEPH_SNAPDIR && ceph_snap(dentry->d_inode) != CEPH_SNAPDIR &&
ceph_ino(dentry->d_inode) != CEPH_INO_CEPH && ceph_ino(dentry->d_inode) != CEPH_INO_CEPH &&
...@@ -154,6 +155,7 @@ static int __dcache_readdir(struct file *filp, ...@@ -154,6 +155,7 @@ static int __dcache_readdir(struct file *filp,
dget_dlock(dentry); dget_dlock(dentry);
spin_unlock(&dentry->d_lock); spin_unlock(&dentry->d_lock);
spin_unlock(&parent->d_lock);
spin_unlock(&dcache_lock); spin_unlock(&dcache_lock);
dout(" %llu (%llu) dentry %p %.*s %p\n", di->offset, filp->f_pos, dout(" %llu (%llu) dentry %p %.*s %p\n", di->offset, filp->f_pos,
...@@ -188,10 +190,12 @@ static int __dcache_readdir(struct file *filp, ...@@ -188,10 +190,12 @@ static int __dcache_readdir(struct file *filp,
} }
spin_lock(&dcache_lock); spin_lock(&dcache_lock);
spin_lock(&parent->d_lock);
p = p->prev; /* advance to next dentry */ p = p->prev; /* advance to next dentry */
goto more; goto more;
out_unlock: out_unlock:
spin_unlock(&parent->d_lock);
spin_unlock(&dcache_lock); spin_unlock(&dcache_lock);
out: out:
if (last) if (last)
......
...@@ -842,11 +842,13 @@ static void ceph_set_dentry_offset(struct dentry *dn) ...@@ -842,11 +842,13 @@ static void ceph_set_dentry_offset(struct dentry *dn)
spin_unlock(&inode->i_lock); spin_unlock(&inode->i_lock);
spin_lock(&dcache_lock); spin_lock(&dcache_lock);
spin_lock(&dn->d_lock); spin_lock(&dir->d_lock);
spin_lock_nested(&dn->d_lock, DENTRY_D_LOCK_NESTED);
list_move(&dn->d_u.d_child, &dir->d_subdirs); list_move(&dn->d_u.d_child, &dir->d_subdirs);
dout("set_dentry_offset %p %lld (%p %p)\n", dn, di->offset, dout("set_dentry_offset %p %lld (%p %p)\n", dn, di->offset,
dn->d_u.d_child.prev, dn->d_u.d_child.next); dn->d_u.d_child.prev, dn->d_u.d_child.next);
spin_unlock(&dn->d_lock); spin_unlock(&dn->d_lock);
spin_unlock(&dir->d_lock);
spin_unlock(&dcache_lock); spin_unlock(&dcache_lock);
} }
...@@ -1232,9 +1234,11 @@ int ceph_readdir_prepopulate(struct ceph_mds_request *req, ...@@ -1232,9 +1234,11 @@ int ceph_readdir_prepopulate(struct ceph_mds_request *req,
} else { } else {
/* reorder parent's d_subdirs */ /* reorder parent's d_subdirs */
spin_lock(&dcache_lock); spin_lock(&dcache_lock);
spin_lock(&dn->d_lock); spin_lock(&parent->d_lock);
spin_lock_nested(&dn->d_lock, DENTRY_D_LOCK_NESTED);
list_move(&dn->d_u.d_child, &parent->d_subdirs); list_move(&dn->d_u.d_child, &parent->d_subdirs);
spin_unlock(&dn->d_lock); spin_unlock(&dn->d_lock);
spin_unlock(&parent->d_lock);
spin_unlock(&dcache_lock); spin_unlock(&dcache_lock);
} }
......
...@@ -94,6 +94,7 @@ static void coda_flag_children(struct dentry *parent, int flag) ...@@ -94,6 +94,7 @@ static void coda_flag_children(struct dentry *parent, int flag)
struct dentry *de; struct dentry *de;
spin_lock(&dcache_lock); spin_lock(&dcache_lock);
spin_lock(&parent->d_lock);
list_for_each(child, &parent->d_subdirs) list_for_each(child, &parent->d_subdirs)
{ {
de = list_entry(child, struct dentry, d_u.d_child); de = list_entry(child, struct dentry, d_u.d_child);
...@@ -102,6 +103,7 @@ static void coda_flag_children(struct dentry *parent, int flag) ...@@ -102,6 +103,7 @@ static void coda_flag_children(struct dentry *parent, int flag)
continue; continue;
coda_flag_inode(de->d_inode, flag); coda_flag_inode(de->d_inode, flag);
} }
spin_unlock(&parent->d_lock);
spin_unlock(&dcache_lock); spin_unlock(&dcache_lock);
return; return;
} }
......
This diff is collapsed.
...@@ -81,7 +81,8 @@ int dcache_dir_close(struct inode *inode, struct file *file) ...@@ -81,7 +81,8 @@ int dcache_dir_close(struct inode *inode, struct file *file)
loff_t dcache_dir_lseek(struct file *file, loff_t offset, int origin) loff_t dcache_dir_lseek(struct file *file, loff_t offset, int origin)
{ {
mutex_lock(&file->f_path.dentry->d_inode->i_mutex); struct dentry *dentry = file->f_path.dentry;
mutex_lock(&dentry->d_inode->i_mutex);
switch (origin) { switch (origin) {
case 1: case 1:
offset += file->f_pos; offset += file->f_pos;
...@@ -89,7 +90,7 @@ loff_t dcache_dir_lseek(struct file *file, loff_t offset, int origin) ...@@ -89,7 +90,7 @@ loff_t dcache_dir_lseek(struct file *file, loff_t offset, int origin)
if (offset >= 0) if (offset >= 0)
break; break;
default: default:
mutex_unlock(&file->f_path.dentry->d_inode->i_mutex); mutex_unlock(&dentry->d_inode->i_mutex);
return -EINVAL; return -EINVAL;
} }
if (offset != file->f_pos) { if (offset != file->f_pos) {
...@@ -100,22 +101,25 @@ loff_t dcache_dir_lseek(struct file *file, loff_t offset, int origin) ...@@ -100,22 +101,25 @@ loff_t dcache_dir_lseek(struct file *file, loff_t offset, int origin)
loff_t n = file->f_pos - 2; loff_t n = file->f_pos - 2;
spin_lock(&dcache_lock); spin_lock(&dcache_lock);
spin_lock(&dentry->d_lock);
/* d_lock not required for cursor */
list_del(&cursor->d_u.d_child); list_del(&cursor->d_u.d_child);
p = file->f_path.dentry->d_subdirs.next; p = dentry->d_subdirs.next;
while (n && p != &file->f_path.dentry->d_subdirs) { while (n && p != &dentry->d_subdirs) {
struct dentry *next; struct dentry *next;
next = list_entry(p, struct dentry, d_u.d_child); next = list_entry(p, struct dentry, d_u.d_child);
spin_lock(&next->d_lock); spin_lock_nested(&next->d_lock, DENTRY_D_LOCK_NESTED);
if (simple_positive(next)) if (simple_positive(next))
n--; n--;
spin_unlock(&next->d_lock); spin_unlock(&next->d_lock);
p = p->next; p = p->next;
} }
list_add_tail(&cursor->d_u.d_child, p); list_add_tail(&cursor->d_u.d_child, p);
spin_unlock(&dentry->d_lock);
spin_unlock(&dcache_lock); spin_unlock(&dcache_lock);
} }
} }
mutex_unlock(&file->f_path.dentry->d_inode->i_mutex); mutex_unlock(&dentry->d_inode->i_mutex);
return offset; return offset;
} }
...@@ -156,6 +160,7 @@ int dcache_readdir(struct file * filp, void * dirent, filldir_t filldir) ...@@ -156,6 +160,7 @@ int dcache_readdir(struct file * filp, void * dirent, filldir_t filldir)
/* fallthrough */ /* fallthrough */
default: default:
spin_lock(&dcache_lock); spin_lock(&dcache_lock);
spin_lock(&dentry->d_lock);
if (filp->f_pos == 2) if (filp->f_pos == 2)
list_move(q, &dentry->d_subdirs); list_move(q, &dentry->d_subdirs);
...@@ -169,6 +174,7 @@ int dcache_readdir(struct file * filp, void * dirent, filldir_t filldir) ...@@ -169,6 +174,7 @@ int dcache_readdir(struct file * filp, void * dirent, filldir_t filldir)
} }
spin_unlock(&next->d_lock); spin_unlock(&next->d_lock);
spin_unlock(&dentry->d_lock);
spin_unlock(&dcache_lock); spin_unlock(&dcache_lock);
if (filldir(dirent, next->d_name.name, if (filldir(dirent, next->d_name.name,
next->d_name.len, filp->f_pos, next->d_name.len, filp->f_pos,
...@@ -176,11 +182,15 @@ int dcache_readdir(struct file * filp, void * dirent, filldir_t filldir) ...@@ -176,11 +182,15 @@ int dcache_readdir(struct file * filp, void * dirent, filldir_t filldir)
dt_type(next->d_inode)) < 0) dt_type(next->d_inode)) < 0)
return 0; return 0;
spin_lock(&dcache_lock); spin_lock(&dcache_lock);
spin_lock(&dentry->d_lock);
spin_lock_nested(&next->d_lock, DENTRY_D_LOCK_NESTED);
/* next is still alive */ /* next is still alive */
list_move(q, p); list_move(q, p);
spin_unlock(&next->d_lock);
p = q; p = q;
filp->f_pos++; filp->f_pos++;
} }
spin_unlock(&dentry->d_lock);
spin_unlock(&dcache_lock); spin_unlock(&dcache_lock);
} }
return 0; return 0;
...@@ -276,6 +286,7 @@ int simple_empty(struct dentry *dentry) ...@@ -276,6 +286,7 @@ int simple_empty(struct dentry *dentry)
int ret = 0; int ret = 0;
spin_lock(&dcache_lock); spin_lock(&dcache_lock);
spin_lock(&dentry->d_lock);
list_for_each_entry(child, &dentry->d_subdirs, d_u.d_child) { list_for_each_entry(child, &dentry->d_subdirs, d_u.d_child) {
spin_lock_nested(&child->d_lock, DENTRY_D_LOCK_NESTED); spin_lock_nested(&child->d_lock, DENTRY_D_LOCK_NESTED);
if (simple_positive(child)) { if (simple_positive(child)) {
...@@ -286,6 +297,7 @@ int simple_empty(struct dentry *dentry) ...@@ -286,6 +297,7 @@ int simple_empty(struct dentry *dentry)
} }
ret = 1; ret = 1;
out: out:
spin_unlock(&dentry->d_lock);
spin_unlock(&dcache_lock); spin_unlock(&dcache_lock);
return ret; return ret;
} }
......
...@@ -392,6 +392,7 @@ ncp_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos) ...@@ -392,6 +392,7 @@ ncp_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos)
/* If a pointer is invalid, we search the dentry. */ /* If a pointer is invalid, we search the dentry. */
spin_lock(&dcache_lock); spin_lock(&dcache_lock);
spin_lock(&parent->d_lock);
next = parent->d_subdirs.next; next = parent->d_subdirs.next;
while (next != &parent->d_subdirs) { while (next != &parent->d_subdirs) {
dent = list_entry(next, struct dentry, d_u.d_child); dent = list_entry(next, struct dentry, d_u.d_child);
...@@ -400,11 +401,13 @@ ncp_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos) ...@@ -400,11 +401,13 @@ ncp_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos)
dget_locked(dent); dget_locked(dent);
else else
dent = NULL; dent = NULL;
spin_unlock(&parent->d_lock);
spin_unlock(&dcache_lock); spin_unlock(&dcache_lock);
goto out; goto out;
} }
next = next->next; next = next->next;
} }
spin_unlock(&parent->d_lock);
spin_unlock(&dcache_lock); spin_unlock(&dcache_lock);
return NULL; return NULL;
......
...@@ -194,6 +194,7 @@ ncp_renew_dentries(struct dentry *parent) ...@@ -194,6 +194,7 @@ ncp_renew_dentries(struct dentry *parent)
struct dentry *dentry; struct dentry *dentry;
spin_lock(&dcache_lock); spin_lock(&dcache_lock);
spin_lock(&parent->d_lock);
next = parent->d_subdirs.next; next = parent->d_subdirs.next;
while (next != &parent->d_subdirs) { while (next != &parent->d_subdirs) {
dentry = list_entry(next, struct dentry, d_u.d_child); dentry = list_entry(next, struct dentry, d_u.d_child);
...@@ -205,6 +206,7 @@ ncp_renew_dentries(struct dentry *parent) ...@@ -205,6 +206,7 @@ ncp_renew_dentries(struct dentry *parent)
next = next->next; next = next->next;
} }
spin_unlock(&parent->d_lock);
spin_unlock(&dcache_lock); spin_unlock(&dcache_lock);
} }
...@@ -216,6 +218,7 @@ ncp_invalidate_dircache_entries(struct dentry *parent) ...@@ -216,6 +218,7 @@ ncp_invalidate_dircache_entries(struct dentry *parent)
struct dentry *dentry; struct dentry *dentry;
spin_lock(&dcache_lock); spin_lock(&dcache_lock);
spin_lock(&parent->d_lock);
next = parent->d_subdirs.next; next = parent->d_subdirs.next;
while (next != &parent->d_subdirs) { while (next != &parent->d_subdirs) {
dentry = list_entry(next, struct dentry, d_u.d_child); dentry = list_entry(next, struct dentry, d_u.d_child);
...@@ -223,6 +226,7 @@ ncp_invalidate_dircache_entries(struct dentry *parent) ...@@ -223,6 +226,7 @@ ncp_invalidate_dircache_entries(struct dentry *parent)
ncp_age_dentry(server, dentry); ncp_age_dentry(server, dentry);
next = next->next; next = next->next;
} }
spin_unlock(&parent->d_lock);
spin_unlock(&dcache_lock); spin_unlock(&dcache_lock);
} }
......
...@@ -68,17 +68,19 @@ void __fsnotify_update_child_dentry_flags(struct inode *inode) ...@@ -68,17 +68,19 @@ void __fsnotify_update_child_dentry_flags(struct inode *inode)
/* run all of the children of the original inode and fix their /* run all of the children of the original inode and fix their
* d_flags to indicate parental interest (their parent is the * d_flags to indicate parental interest (their parent is the
* original inode) */ * original inode) */
spin_lock(&alias->d_lock);
list_for_each_entry(child, &alias->d_subdirs, d_u.d_child) { list_for_each_entry(child, &alias->d_subdirs, d_u.d_child) {
if (!child->d_inode) if (!child->d_inode)
continue; continue;
spin_lock(&child->d_lock); spin_lock_nested(&child->d_lock, DENTRY_D_LOCK_NESTED);
if (watched) if (watched)
child->d_flags |= DCACHE_FSNOTIFY_PARENT_WATCHED; child->d_flags |= DCACHE_FSNOTIFY_PARENT_WATCHED;
else else
child->d_flags &= ~DCACHE_FSNOTIFY_PARENT_WATCHED; child->d_flags &= ~DCACHE_FSNOTIFY_PARENT_WATCHED;
spin_unlock(&child->d_lock); spin_unlock(&child->d_lock);
} }
spin_unlock(&alias->d_lock);
} }
spin_unlock(&dcache_lock); spin_unlock(&dcache_lock);
} }
......
...@@ -305,6 +305,7 @@ static inline struct dentry *dget_dlock(struct dentry *dentry) ...@@ -305,6 +305,7 @@ static inline struct dentry *dget_dlock(struct dentry *dentry)
} }
return dentry; return dentry;
} }
static inline struct dentry *dget(struct dentry *dentry) static inline struct dentry *dget(struct dentry *dentry)
{ {
if (dentry) { if (dentry) {
......
...@@ -877,23 +877,31 @@ static void cgroup_clear_directory(struct dentry *dentry) ...@@ -877,23 +877,31 @@ static void cgroup_clear_directory(struct dentry *dentry)
BUG_ON(!mutex_is_locked(&dentry->d_inode->i_mutex)); BUG_ON(!mutex_is_locked(&dentry->d_inode->i_mutex));
spin_lock(&dcache_lock); spin_lock(&dcache_lock);
spin_lock(&dentry->d_lock);
node = dentry->d_subdirs.next; node = dentry->d_subdirs.next;
while (node != &dentry->d_subdirs) { while (node != &dentry->d_subdirs) {
struct dentry *d = list_entry(node, struct dentry, d_u.d_child); struct dentry *d = list_entry(node, struct dentry, d_u.d_child);
spin_lock_nested(&d->d_lock, DENTRY_D_LOCK_NESTED);
list_del_init(node); list_del_init(node);
if (d->d_inode) { if (d->d_inode) {
/* This should never be called on a cgroup /* This should never be called on a cgroup
* directory with child cgroups */ * directory with child cgroups */
BUG_ON(d->d_inode->i_mode & S_IFDIR); BUG_ON(d->d_inode->i_mode & S_IFDIR);
d = dget_locked(d); dget_locked_dlock(d);
spin_unlock(&d->d_lock);
spin_unlock(&dentry->d_lock);
spin_unlock(&dcache_lock); spin_unlock(&dcache_lock);
d_delete(d); d_delete(d);
simple_unlink(dentry->d_inode, d); simple_unlink(dentry->d_inode, d);
dput(d); dput(d);
spin_lock(&dcache_lock); spin_lock(&dcache_lock);
} spin_lock(&dentry->d_lock);
} else
spin_unlock(&d->d_lock);
node = dentry->d_subdirs.next; node = dentry->d_subdirs.next;
} }
spin_unlock(&dentry->d_lock);
spin_unlock(&dcache_lock); spin_unlock(&dcache_lock);
} }
...@@ -902,10 +910,17 @@ static void cgroup_clear_directory(struct dentry *dentry) ...@@ -902,10 +910,17 @@ static void cgroup_clear_directory(struct dentry *dentry)
*/ */
static void cgroup_d_remove_dir(struct dentry *dentry) static void cgroup_d_remove_dir(struct dentry *dentry)
{ {
struct dentry *parent;
cgroup_clear_directory(dentry); cgroup_clear_directory(dentry);
spin_lock(&dcache_lock); spin_lock(&dcache_lock);
parent = dentry->d_parent;
spin_lock(&parent->d_lock);
spin_lock(&dentry->d_lock);
list_del_init(&dentry->d_u.d_child); list_del_init(&dentry->d_u.d_child);
spin_unlock(&dentry->d_lock);
spin_unlock(&parent->d_lock);
spin_unlock(&dcache_lock); spin_unlock(&dcache_lock);
remove_dir(dentry); remove_dir(dentry);
} }
......
...@@ -1146,22 +1146,30 @@ static void sel_remove_entries(struct dentry *de) ...@@ -1146,22 +1146,30 @@ static void sel_remove_entries(struct dentry *de)
struct list_head *node; struct list_head *node;
spin_lock(&dcache_lock); spin_lock(&dcache_lock);
spin_lock(&de->d_lock);
node = de->d_subdirs.next; node = de->d_subdirs.next;
while (node != &de->d_subdirs) { while (node != &de->d_subdirs) {
struct dentry *d = list_entry(node, struct dentry, d_u.d_child); struct dentry *d = list_entry(node, struct dentry, d_u.d_child);
spin_lock_nested(&d->d_lock, DENTRY_D_LOCK_NESTED);
list_del_init(node); list_del_init(node);
if (d->d_inode) { if (d->d_inode) {
d = dget_locked(d); dget_locked_dlock(d);
spin_unlock(&de->d_lock);
spin_unlock(&d->d_lock);
spin_unlock(&dcache_lock); spin_unlock(&dcache_lock);
d_delete(d); d_delete(d);
simple_unlink(de->d_inode, d); simple_unlink(de->d_inode, d);
dput(d); dput(d);
spin_lock(&dcache_lock); spin_lock(&dcache_lock);
} spin_lock(&de->d_lock);
} else
spin_unlock(&d->d_lock);
node = de->d_subdirs.next; node = de->d_subdirs.next;
} }
spin_unlock(&de->d_lock);
spin_unlock(&dcache_lock); spin_unlock(&dcache_lock);
} }
......
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