commit 968957120f901af89196d4acfe2a76e43f7bf664
Author: Erez Zadok <[EMAIL PROTECTED]>
Date: Fri Dec 28 13:38:20 2007 -0500
Unionfs: implement lockdep classes
Lockdep fixes. Support locking order/classes (e.g., parent -> child ->
whiteout). Remove locking from create_parents: it's enough to just dget the
dentries in question. Move parent locking to from lookup_backend to caller,
unionfs_lookup.
Signed-off-by: Erez Zadok <[EMAIL PROTECTED]>
diff --git a/fs/unionfs/commonfops.c b/fs/unionfs/commonfops.c
index 2c3e10d..b582dae 100644
--- a/fs/unionfs/commonfops.c
+++ b/fs/unionfs/commonfops.c
@@ -310,7 +310,7 @@ int unionfs_file_revalidate(struct file *file, bool
willwrite)
int err = 0;
dentry = file->f_dentry;
- unionfs_lock_dentry(dentry);
+ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
sb = dentry->d_sb;
/*
@@ -519,7 +519,7 @@ int unionfs_open(struct inode *inode, struct file *file)
int bindex = 0, bstart = 0, bend = 0;
int size;
- unionfs_read_lock(inode->i_sb);
+ unionfs_read_lock(inode->i_sb, UNIONFS_SMUTEX_PARENT);
file->private_data =
kzalloc(sizeof(struct unionfs_file_info), GFP_KERNEL);
@@ -546,7 +546,7 @@ int unionfs_open(struct inode *inode, struct file *file)
}
dentry = file->f_dentry;
- unionfs_lock_dentry(dentry);
+ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
bstart = fbstart(file) = dbstart(dentry);
bend = fbend(file) = dbend(dentry);
@@ -607,7 +607,7 @@ int unionfs_file_release(struct inode *inode, struct file
*file)
int bindex, bstart, bend;
int fgen, err = 0;
- unionfs_read_lock(sb);
+ unionfs_read_lock(sb, UNIONFS_SMUTEX_PARENT);
/*
* Yes, we have to revalidate this file even if it's being released.
* This is important for open-but-unlinked files, as well as mmap
@@ -626,7 +626,7 @@ int unionfs_file_release(struct inode *inode, struct file
*file)
bstart = fbstart(file);
bend = fbend(file);
- unionfs_lock_dentry(dentry);
+ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
for (bindex = bstart; bindex <= bend; bindex++) {
lower_file = unionfs_lower_file_idx(file, bindex);
@@ -702,7 +702,7 @@ static int unionfs_ioctl_queryfile(struct file *file,
unsigned int cmd,
struct vfsmount *mnt;
dentry = file->f_dentry;
- unionfs_lock_dentry(dentry);
+ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
orig_bstart = dbstart(dentry);
orig_bend = dbend(dentry);
err = unionfs_partial_lookup(dentry);
@@ -753,7 +753,7 @@ int unionfs_ioctl(struct inode *inode, struct file *file,
{
long err;
- unionfs_read_lock(file->f_dentry->d_sb);
+ unionfs_read_lock(file->f_dentry->d_sb, UNIONFS_SMUTEX_PARENT);
err = unionfs_file_revalidate(file, true);
if (unlikely(err))
@@ -792,7 +792,7 @@ int unionfs_flush(struct file *file)
struct dentry *dentry = file->f_dentry;
int bindex, bstart, bend;
- unionfs_read_lock(dentry->d_sb);
+ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT);
err = unionfs_file_revalidate(file, true);
if (unlikely(err))
diff --git a/fs/unionfs/compat.h b/fs/unionfs/compat.h
index 3701e8d..71ebb81 100644
--- a/fs/unionfs/compat.h
+++ b/fs/unionfs/compat.h
@@ -42,6 +42,9 @@ extern void *krealloc2(size_t oldsize, void *ptr, size_t
newsize, int flags);
/* no lockdep support, but keep hooks */
#define lockdep_off()
#define lockdep_on()
+#define mutex_lock_nested(x,y) mutex_lock((x))
+#define down_read_nested(x,y) down_read((x))
+#define I_MUTEX_PARENT
struct path {
struct vfsmount *mnt;
diff --git a/fs/unionfs/copyup.c b/fs/unionfs/copyup.c
index e35d977..a8ad8e0 100644
--- a/fs/unionfs/copyup.c
+++ b/fs/unionfs/copyup.c
@@ -712,7 +712,7 @@ struct dentry *create_parents(struct inode *dir, struct
dentry *dentry,
/* find the parent directory dentry in unionfs */
parent_dentry = child_dentry->d_parent;
- unionfs_lock_dentry(parent_dentry);
+ dget(parent_dentry);
/* find out the lower_parent_dentry in the given branch */
lower_parent_dentry =
@@ -749,7 +749,7 @@ struct dentry *create_parents(struct inode *dir, struct
dentry *dentry,
begin:
/* get lower parent dir in the current branch */
lower_parent_dentry = unionfs_lower_dentry_idx(parent_dentry, bindex);
- unionfs_unlock_dentry(parent_dentry);
+ dput(parent_dentry);
/* init the values to lookup */
childname = child_dentry->d_name.name;
@@ -840,7 +840,7 @@ out:
/* cleanup any leftover locks from the do/while loop above */
if (IS_ERR(lower_dentry))
while (count)
- unionfs_unlock_dentry(path[count--]);
+ dput(path[count--]);
kfree(path);
return lower_dentry;
}
diff --git a/fs/unionfs/dentry.c b/fs/unionfs/dentry.c
index fda0b43..82e85b2 100644
--- a/fs/unionfs/dentry.c
+++ b/fs/unionfs/dentry.c
@@ -342,7 +342,7 @@ bool __unionfs_d_revalidate_chain(struct dentry *dentry,
struct nameidata *nd,
* to child order.
*/
for (i = 0; i < chain_len; i++) {
- unionfs_lock_dentry(chain[i]);
+ unionfs_lock_dentry(chain[i], UNIONFS_DMUTEX_REVAL+i);
saved_bstart = dbstart(chain[i]);
saved_bend = dbend(chain[i]);
sbgen = atomic_read(&UNIONFS_SB(dentry->d_sb)->generation);
@@ -416,9 +416,9 @@ static int unionfs_d_revalidate(struct dentry *dentry,
struct nameidata *nd)
{
int err;
- unionfs_read_lock(dentry->d_sb);
+ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
- unionfs_lock_dentry(dentry);
+ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
err = __unionfs_d_revalidate_chain(dentry, nd, false);
if (likely(err > 0)) /* true==1: dentry is valid */
unionfs_check_dentry(dentry);
@@ -437,7 +437,7 @@ static void unionfs_d_release(struct dentry *dentry)
{
int bindex, bstart, bend;
- unionfs_read_lock(dentry->d_sb);
+ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
unionfs_check_dentry(dentry);
/* this could be a negative dentry, so check first */
diff --git a/fs/unionfs/dirfops.c b/fs/unionfs/dirfops.c
index a160f5b..f423254 100644
--- a/fs/unionfs/dirfops.c
+++ b/fs/unionfs/dirfops.c
@@ -103,7 +103,7 @@ static int unionfs_readdir(struct file *file, void *dirent,
filldir_t filldir)
int bend;
loff_t offset;
- unionfs_read_lock(file->f_dentry->d_sb);
+ unionfs_read_lock(file->f_dentry->d_sb, UNIONFS_SMUTEX_PARENT);
err = unionfs_file_revalidate(file, false);
if (unlikely(err))
@@ -207,7 +207,7 @@ static loff_t unionfs_dir_llseek(struct file *file, loff_t
offset, int origin)
struct unionfs_dir_state *rdstate;
loff_t err;
- unionfs_read_lock(file->f_dentry->d_sb);
+ unionfs_read_lock(file->f_dentry->d_sb, UNIONFS_SMUTEX_PARENT);
err = unionfs_file_revalidate(file, false);
if (unlikely(err))
diff --git a/fs/unionfs/fanout.h b/fs/unionfs/fanout.h
index 6c87079..07eb223 100644
--- a/fs/unionfs/fanout.h
+++ b/fs/unionfs/fanout.h
@@ -285,10 +285,20 @@ static inline struct vfsmount *unionfs_lower_mnt(const
struct dentry *dent)
}
/* Macros for locking a dentry. */
-static inline void unionfs_lock_dentry(struct dentry *d)
+enum unionfs_dentry_lock_class {
+ UNIONFS_DMUTEX_NORMAL,
+ UNIONFS_DMUTEX_ROOT,
+ UNIONFS_DMUTEX_PARENT,
+ UNIONFS_DMUTEX_CHILD,
+ UNIONFS_DMUTEX_WHITEOUT,
+ UNIONFS_DMUTEX_REVAL, /* for file/dentry revalidate */
+};
+
+static inline void unionfs_lock_dentry(struct dentry *d,
+ unsigned int subclass)
{
BUG_ON(!d);
- mutex_lock(&UNIONFS_D(d)->lock);
+ mutex_lock_nested(&UNIONFS_D(d)->lock, subclass);
}
static inline void unionfs_unlock_dentry(struct dentry *d)
diff --git a/fs/unionfs/file.c b/fs/unionfs/file.c
index 26f0121..fe22949 100644
--- a/fs/unionfs/file.c
+++ b/fs/unionfs/file.c
@@ -30,7 +30,7 @@ static int unionfs_mmap(struct file *file, struct
vm_area_struct *vma)
bool willwrite;
struct file *lower_file;
- unionfs_read_lock(file->f_dentry->d_sb);
+ unionfs_read_lock(file->f_dentry->d_sb, UNIONFS_SMUTEX_PARENT);
/* This might be deferred to mmap's writepage */
willwrite = ((vma->vm_flags | VM_SHARED | VM_WRITE) == vma->vm_flags);
@@ -80,7 +80,7 @@ int unionfs_fsync(struct file *file, struct dentry *dentry,
int datasync)
struct inode *lower_inode, *inode;
int err = -EINVAL;
- unionfs_read_lock(file->f_dentry->d_sb);
+ unionfs_read_lock(file->f_dentry->d_sb, UNIONFS_SMUTEX_PARENT);
err = unionfs_file_revalidate(file, true);
if (unlikely(err))
goto out;
@@ -128,7 +128,7 @@ int unionfs_fasync(int fd, struct file *file, int flag)
struct inode *lower_inode, *inode;
int err = 0;
- unionfs_read_lock(file->f_dentry->d_sb);
+ unionfs_read_lock(file->f_dentry->d_sb, UNIONFS_SMUTEX_PARENT);
err = unionfs_file_revalidate(file, true);
if (unlikely(err))
goto out;
diff --git a/fs/unionfs/inode.c b/fs/unionfs/inode.c
index 4ee875e..8326039 100644
--- a/fs/unionfs/inode.c
+++ b/fs/unionfs/inode.c
@@ -28,8 +28,8 @@ static int unionfs_create(struct inode *parent, struct dentry
*dentry,
char *name = NULL;
int valid = 0;
- unionfs_read_lock(dentry->d_sb);
- unionfs_lock_dentry(dentry);
+ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
+ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
valid = __unionfs_d_revalidate_chain(dentry, nd, false);
/*
@@ -78,7 +78,7 @@ static int unionfs_create(struct inode *parent, struct dentry
*dentry,
*/
struct dentry *lower_dir_dentry;
- lower_dir_dentry = lock_parent(wh_dentry);
+ lower_dir_dentry = lock_parent_wh(wh_dentry);
/* see Documentation/filesystems/unionfs/issues.txt */
lockdep_off();
err = vfs_unlink(lower_dir_dentry->d_inode, wh_dentry);
@@ -170,7 +170,9 @@ static struct dentry *unionfs_lookup(struct inode *parent,
struct path path_save;
struct dentry *ret;
- unionfs_read_lock(dentry->d_sb);
+ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
+ if (dentry != dentry->d_parent)
+ unionfs_lock_dentry(dentry->d_parent, UNIONFS_DMUTEX_PARENT);
/* save the dentry & vfsmnt from namei */
if (nd) {
@@ -201,6 +203,9 @@ static struct dentry *unionfs_lookup(struct inode *parent,
unionfs_check_dentry(dentry->d_parent);
if (!IS_ERR(ret))
unionfs_unlock_dentry(dentry);
+
+ if (dentry != dentry->d_parent)
+ unionfs_unlock_dentry(dentry->d_parent);
unionfs_read_unlock(dentry->d_sb);
return ret;
@@ -216,7 +221,7 @@ static int unionfs_link(struct dentry *old_dentry, struct
inode *dir,
struct dentry *whiteout_dentry;
char *name = NULL;
- unionfs_read_lock(old_dentry->d_sb);
+ unionfs_read_lock(old_dentry->d_sb, UNIONFS_SMUTEX_CHILD);
unionfs_double_lock_dentry(new_dentry, old_dentry);
if (unlikely(!__unionfs_d_revalidate_chain(old_dentry, NULL, false))) {
@@ -254,7 +259,7 @@ static int unionfs_link(struct dentry *old_dentry, struct
inode *dir,
whiteout_dentry = NULL;
} else {
/* found a .wh.foo entry, unlink it and then call vfs_link() */
- lower_dir_dentry = lock_parent(whiteout_dentry);
+ lower_dir_dentry = lock_parent_wh(whiteout_dentry);
err = is_robranch_super(new_dentry->d_sb, dbstart(new_dentry));
if (!err) {
/* see Documentation/filesystems/unionfs/issues.txt */
@@ -380,8 +385,8 @@ static int unionfs_symlink(struct inode *parent, struct
dentry *dentry,
int valid = 0;
umode_t mode;
- unionfs_read_lock(dentry->d_sb);
- unionfs_lock_dentry(dentry);
+ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
+ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
if (unlikely(dentry->d_inode &&
!__unionfs_d_revalidate_chain(dentry, NULL, false))) {
@@ -435,7 +440,7 @@ static int unionfs_symlink(struct inode *parent, struct
dentry *dentry,
*/
struct dentry *lower_dir_dentry;
- lower_dir_dentry = lock_parent(wh_dentry);
+ lower_dir_dentry = lock_parent_wh(wh_dentry);
err = vfs_unlink(lower_dir_dentry->d_inode, wh_dentry);
unlock_dir(lower_dir_dentry);
@@ -523,8 +528,8 @@ static int unionfs_mkdir(struct inode *parent, struct
dentry *dentry, int mode)
int whiteout_unlinked = 0;
struct sioq_args args;
- unionfs_read_lock(dentry->d_sb);
- unionfs_lock_dentry(dentry);
+ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
+ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
if (unlikely(dentry->d_inode &&
!__unionfs_d_revalidate_chain(dentry, NULL, false))) {
@@ -557,7 +562,7 @@ static int unionfs_mkdir(struct inode *parent, struct
dentry *dentry, int mode)
dput(whiteout_dentry);
whiteout_dentry = NULL;
} else {
- lower_parent_dentry = lock_parent(whiteout_dentry);
+ lower_parent_dentry = lock_parent_wh(whiteout_dentry);
/* found a.wh.foo entry, remove it then do vfs_mkdir */
err = is_robranch_super(dentry->d_sb, bstart);
@@ -677,8 +682,8 @@ static int unionfs_mknod(struct inode *parent, struct
dentry *dentry, int mode,
char *name = NULL;
int valid = 0;
- unionfs_read_lock(dentry->d_sb);
- unionfs_lock_dentry(dentry);
+ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
+ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
if (unlikely(dentry->d_inode &&
!__unionfs_d_revalidate_chain(dentry, NULL, false))) {
@@ -732,7 +737,7 @@ static int unionfs_mknod(struct inode *parent, struct
dentry *dentry, int mode,
*/
struct dentry *lower_dir_dentry;
- lower_dir_dentry = lock_parent(wh_dentry);
+ lower_dir_dentry = lock_parent_wh(wh_dentry);
err = vfs_unlink(lower_dir_dentry->d_inode, wh_dentry);
unlock_dir(lower_dir_dentry);
@@ -814,8 +819,8 @@ static int unionfs_readlink(struct dentry *dentry, char
__user *buf,
int err;
struct dentry *lower_dentry;
- unionfs_read_lock(dentry->d_sb);
- unionfs_lock_dentry(dentry);
+ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
+ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
if (unlikely(!__unionfs_d_revalidate_chain(dentry, NULL, false))) {
err = -ESTALE;
@@ -890,9 +895,9 @@ out:
/* FIXME: We may not have to lock here */
static void unionfs_put_link(struct dentry *dentry, struct nameidata *nd)
{
- unionfs_read_lock(dentry->d_sb);
+ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
- unionfs_lock_dentry(dentry);
+ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
if (unlikely(!__unionfs_d_revalidate_chain(dentry, nd, false)))
printk(KERN_ERR
"unionfs: put_link failed to revalidate dentry\n");
@@ -998,8 +1003,8 @@ static int unionfs_setattr(struct dentry *dentry, struct
iattr *ia)
int bstart, bend, bindex;
loff_t size;
- unionfs_read_lock(dentry->d_sb);
- unionfs_lock_dentry(dentry);
+ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
+ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
if (unlikely(!__unionfs_d_revalidate_chain(dentry, NULL, false))) {
err = -ESTALE;
diff --git a/fs/unionfs/lookup.c b/fs/unionfs/lookup.c
index d264220..fce2a3b 100644
--- a/fs/unionfs/lookup.c
+++ b/fs/unionfs/lookup.c
@@ -98,7 +98,6 @@ struct dentry *unionfs_lookup_backend(struct dentry *dentry,
struct dentry *first_dentry = NULL;
struct dentry *first_lower_dentry = NULL;
struct vfsmount *first_lower_mnt = NULL;
- int locked_parent = 0;
int opaque;
char *whname = NULL;
const unsigned char *name;
@@ -119,7 +118,7 @@ struct dentry *unionfs_lookup_backend(struct dentry *dentry,
case INTERPOSE_PARTIAL:
break;
case INTERPOSE_LOOKUP:
- err = new_dentry_private_data(dentry);
+ err = new_dentry_private_data(dentry, UNIONFS_DMUTEX_CHILD);
if (unlikely(err))
goto out;
break;
@@ -136,10 +135,7 @@ struct dentry *unionfs_lookup_backend(struct dentry
*dentry,
parent_dentry = dget_parent(dentry);
/* We never partial lookup the root directory. */
- if (parent_dentry != dentry) {
- unionfs_lock_dentry(parent_dentry);
- locked_parent = 1;
- } else {
+ if (parent_dentry == dentry) {
dput(parent_dentry);
parent_dentry = NULL;
goto out;
@@ -438,8 +434,6 @@ out:
}
}
kfree(whname);
- if (locked_parent)
- unionfs_unlock_dentry(parent_dentry);
dput(parent_dentry);
if (err && (lookupmode == INTERPOSE_LOOKUP))
unionfs_unlock_dentry(dentry);
@@ -453,6 +447,7 @@ out:
/*
* This is a utility function that fills in a unionfs dentry.
+ * Caller must lock this dentry with unionfs_lock_dentry.
*
* Returns: 0 (ok), or -ERRNO if an error occurred.
*/
@@ -543,7 +538,7 @@ static int realloc_dentry_private_data(struct dentry
*dentry)
}
/* allocate new dentry private data */
-int new_dentry_private_data(struct dentry *dentry)
+int new_dentry_private_data(struct dentry *dentry, int subclass)
{
struct unionfs_dentry_info *info = UNIONFS_D(dentry);
@@ -554,7 +549,7 @@ int new_dentry_private_data(struct dentry *dentry)
return -ENOMEM;
mutex_init(&info->lock);
- mutex_lock(&info->lock);
+ mutex_lock_nested(&info->lock, subclass);
info->lower_paths = NULL;
diff --git a/fs/unionfs/main.c b/fs/unionfs/main.c
index b02e4e7..86efde6 100644
--- a/fs/unionfs/main.c
+++ b/fs/unionfs/main.c
@@ -653,7 +653,7 @@ static int unionfs_read_super(struct super_block *sb, void
*raw_data,
/* link the upper and lower dentries */
sb->s_root->d_fsdata = NULL;
- err = new_dentry_private_data(sb->s_root);
+ err = new_dentry_private_data(sb->s_root, UNIONFS_DMUTEX_ROOT);
if (unlikely(err))
goto out_freedpd;
diff --git a/fs/unionfs/mmap.c b/fs/unionfs/mmap.c
index 91d3451..654874c 100644
--- a/fs/unionfs/mmap.c
+++ b/fs/unionfs/mmap.c
@@ -179,7 +179,7 @@ static int unionfs_readpage(struct file *file, struct page
*page)
char *page_data = NULL;
mode_t orig_mode;
- unionfs_read_lock(file->f_dentry->d_sb);
+ unionfs_read_lock(file->f_dentry->d_sb, UNIONFS_SMUTEX_PARENT);
err = unionfs_file_revalidate(file, false);
if (unlikely(err))
goto out;
@@ -272,7 +272,7 @@ static int unionfs_commit_write(struct file *file, struct
page *page,
BUG_ON(file == NULL);
- unionfs_read_lock(file->f_dentry->d_sb);
+ unionfs_read_lock(file->f_dentry->d_sb, UNIONFS_SMUTEX_PARENT);
err = unionfs_file_revalidate(file, true);
if (unlikely(err))
goto out;
diff --git a/fs/unionfs/rename.c b/fs/unionfs/rename.c
index 132e22d..178f3f9 100644
--- a/fs/unionfs/rename.c
+++ b/fs/unionfs/rename.c
@@ -76,7 +76,7 @@ static int __unionfs_rename(struct inode *old_dir, struct
dentry *old_dentry,
goto out;
}
- lower_wh_dir_dentry = lock_parent(lower_wh_dentry);
+ lower_wh_dir_dentry = lock_parent_wh(lower_wh_dentry);
err = is_robranch_super(old_dentry->d_sb, bindex);
if (!err)
err = vfs_unlink(lower_wh_dir_dentry->d_inode,
@@ -296,7 +296,7 @@ static int do_unionfs_rename(struct inode *old_dir,
err = -EIO;
goto out;
}
- lower_parent = lock_parent(wh_old);
+ lower_parent = lock_parent_wh(wh_old);
local_err = vfs_create(lower_parent->d_inode, wh_old, S_IRUGO,
NULL);
unlock_dir(lower_parent);
@@ -383,7 +383,7 @@ static struct dentry *lookup_whiteout(struct dentry *dentry)
return (void *)whname;
parent = dget_parent(dentry);
- unionfs_lock_dentry(parent);
+ unionfs_lock_dentry(parent, UNIONFS_DMUTEX_WHITEOUT);
bstart = dbstart(parent);
bend = dbend(parent);
wh_dentry = ERR_PTR(-ENOENT);
@@ -442,7 +442,7 @@ int unionfs_rename(struct inode *old_dir, struct dentry
*old_dentry,
int err = 0;
struct dentry *wh_dentry;
- unionfs_read_lock(old_dentry->d_sb);
+ unionfs_read_lock(old_dentry->d_sb, UNIONFS_SMUTEX_CHILD);
unionfs_double_lock_dentry(old_dentry, new_dentry);
if (unlikely(!__unionfs_d_revalidate_chain(old_dentry, NULL, false))) {
diff --git a/fs/unionfs/subr.c b/fs/unionfs/subr.c
index 438bce9..595af66 100644
--- a/fs/unionfs/subr.c
+++ b/fs/unionfs/subr.c
@@ -86,7 +86,7 @@ int create_whiteout(struct dentry *dentry, int start)
goto out;
}
- lower_dir_dentry = lock_parent(lower_wh_dentry);
+ lower_dir_dentry = lock_parent_wh(lower_wh_dentry);
err = is_robranch_super(dentry->d_sb, bindex);
if (!err)
err = vfs_create(lower_dir_dentry->d_inode,
@@ -121,7 +121,7 @@ int unionfs_refresh_lower_dentry(struct dentry *dentry, int
bindex)
verify_locked(dentry);
- unionfs_lock_dentry(dentry->d_parent);
+ unionfs_lock_dentry(dentry->d_parent, UNIONFS_DMUTEX_CHILD);
lower_parent = unionfs_lower_dentry_idx(dentry->d_parent, bindex);
unionfs_unlock_dentry(dentry->d_parent);
diff --git a/fs/unionfs/super.c b/fs/unionfs/super.c
index 504b23a..873a8ad 100644
--- a/fs/unionfs/super.c
+++ b/fs/unionfs/super.c
@@ -132,8 +132,8 @@ static int unionfs_statfs(struct super_block *sb, struct
kstatfs *buf)
struct super_block *lower_sb;
struct dentry *dentry = sb->s_root;
- unionfs_read_lock(sb);
- unionfs_lock_dentry(dentry);
+ unionfs_read_lock(sb, UNIONFS_SMUTEX_CHILD);
+ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
if (unlikely(!__unionfs_d_revalidate_chain(dentry, NULL, false))) {
err = -ESTALE;
@@ -933,7 +933,7 @@ static void unionfs_umount_begin(struct super_block *sb)
struct super_block *lower_sb;
int bindex, bstart, bend;
- unionfs_read_lock(sb);
+ unionfs_read_lock(sb, UNIONFS_SMUTEX_CHILD);
bstart = sbstart(sb);
bend = sbend(sb);
@@ -957,9 +957,9 @@ static int unionfs_show_options(struct seq_file *m, struct
vfsmount *mnt)
int bindex, bstart, bend;
int perms;
- unionfs_read_lock(sb);
+ unionfs_read_lock(sb, UNIONFS_SMUTEX_CHILD);
- unionfs_lock_dentry(sb->s_root);
+ unionfs_lock_dentry(sb->s_root, UNIONFS_DMUTEX_CHILD);
tmp_page = (char *) __get_free_page(GFP_KERNEL);
if (unlikely(!tmp_page)) {
diff --git a/fs/unionfs/union.h b/fs/unionfs/union.h
index 47127ca..34c9a93 100644
--- a/fs/unionfs/union.h
+++ b/fs/unionfs/union.h
@@ -250,12 +250,18 @@ static inline off_t rdstate2offset(struct
unionfs_dir_state *buf)
return tmp;
}
-static inline void unionfs_read_lock(struct super_block *sb)
+/* Macros for locking a super_block. */
+enum unionfs_super_lock_class {
+ UNIONFS_SMUTEX_NORMAL,
+ UNIONFS_SMUTEX_PARENT, /* when locking on behalf of file */
+ UNIONFS_SMUTEX_CHILD, /* when locking on behalf of dentry */
+};
+static inline void unionfs_read_lock(struct super_block *sb, int subclass)
{
if (UNIONFS_SB(sb)->write_lock_owner &&
UNIONFS_SB(sb)->write_lock_owner == current->pid)
return;
- down_read(&UNIONFS_SB(sb)->rwsem);
+ down_read_nested(&UNIONFS_SB(sb)->rwsem, subclass);
}
static inline void unionfs_read_unlock(struct super_block *sb)
{
@@ -278,16 +284,17 @@ static inline void unionfs_write_unlock(struct
super_block *sb)
static inline void unionfs_double_lock_dentry(struct dentry *d1,
struct dentry *d2)
{
- if (d2 < d1) {
- struct dentry *tmp = d1;
- d1 = d2;
- d2 = tmp;
+ BUG_ON(d1 == d2);
+ if (d1 < d2) {
+ unionfs_lock_dentry(d1, UNIONFS_DMUTEX_PARENT);
+ unionfs_lock_dentry(d2, UNIONFS_DMUTEX_CHILD);
+ } else {
+ unionfs_lock_dentry(d2, UNIONFS_DMUTEX_PARENT);
+ unionfs_lock_dentry(d1, UNIONFS_DMUTEX_CHILD);
}
- unionfs_lock_dentry(d1);
- unionfs_lock_dentry(d2);
}
-extern int new_dentry_private_data(struct dentry *dentry);
+extern int new_dentry_private_data(struct dentry *dentry, int subclass);
extern void free_dentry_private_data(struct dentry *dentry);
extern void update_bstart(struct dentry *dentry);
@@ -482,15 +489,18 @@ extern char *alloc_whname(const unsigned char *name, int
len);
extern int check_branch(struct nameidata *nd);
extern int parse_branch_mode(const char *name, int *perms);
-/*
- * These two functions are here because it is kind of daft to copy and paste
- * the contents of the two functions to 32+ places in unionfs
- */
+/* locking helpers */
static inline struct dentry *lock_parent(struct dentry *dentry)
{
struct dentry *dir = dget(dentry->d_parent);
+ mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT);
+ return dir;
+}
+static inline struct dentry *lock_parent_wh(struct dentry *dentry)
+{
+ struct dentry *dir = dget(dentry->d_parent);
- mutex_lock(&dir->d_inode->i_mutex);
+ mutex_lock_nested(&dir->d_inode->i_mutex, UNIONFS_DMUTEX_WHITEOUT);
return dir;
}
diff --git a/fs/unionfs/unlink.c b/fs/unionfs/unlink.c
index 677a5ae..1e81494 100644
--- a/fs/unionfs/unlink.c
+++ b/fs/unionfs/unlink.c
@@ -92,8 +92,8 @@ int unionfs_unlink(struct inode *dir, struct dentry *dentry)
{
int err = 0;
- unionfs_read_lock(dentry->d_sb);
- unionfs_lock_dentry(dentry);
+ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
+ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
if (unlikely(!__unionfs_d_revalidate_chain(dentry, NULL, false))) {
err = -ESTALE;
@@ -167,8 +167,8 @@ int unionfs_rmdir(struct inode *dir, struct dentry *dentry)
struct unionfs_dir_state *namelist = NULL;
int dstart, dend;
- unionfs_read_lock(dentry->d_sb);
- unionfs_lock_dentry(dentry);
+ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
+ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
if (unlikely(!__unionfs_d_revalidate_chain(dentry, NULL, false))) {
err = -ESTALE;
diff --git a/fs/unionfs/xattr.c b/fs/unionfs/xattr.c
index 6386fb7..c46eaef 100644
--- a/fs/unionfs/xattr.c
+++ b/fs/unionfs/xattr.c
@@ -47,8 +47,8 @@ ssize_t unionfs_getxattr(struct dentry *dentry, const char
*name, void *value,
struct dentry *lower_dentry = NULL;
int err = -EOPNOTSUPP;
- unionfs_read_lock(dentry->d_sb);
- unionfs_lock_dentry(dentry);
+ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
+ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
if (unlikely(!__unionfs_d_revalidate_chain(dentry, NULL, false))) {
err = -ESTALE;
@@ -76,8 +76,8 @@ int unionfs_setxattr(struct dentry *dentry, const char *name,
struct dentry *lower_dentry = NULL;
int err = -EOPNOTSUPP;
- unionfs_read_lock(dentry->d_sb);
- unionfs_lock_dentry(dentry);
+ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
+ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
if (unlikely(!__unionfs_d_revalidate_chain(dentry, NULL, false))) {
err = -ESTALE;
@@ -105,8 +105,8 @@ int unionfs_removexattr(struct dentry *dentry, const char
*name)
struct dentry *lower_dentry = NULL;
int err = -EOPNOTSUPP;
- unionfs_read_lock(dentry->d_sb);
- unionfs_lock_dentry(dentry);
+ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
+ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
if (unlikely(!__unionfs_d_revalidate_chain(dentry, NULL, false))) {
err = -ESTALE;
@@ -134,8 +134,8 @@ ssize_t unionfs_listxattr(struct dentry *dentry, char
*list, size_t size)
int err = -EOPNOTSUPP;
char *encoded_list = NULL;
- unionfs_read_lock(dentry->d_sb);
- unionfs_lock_dentry(dentry);
+ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
+ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
if (unlikely(!__unionfs_d_revalidate_chain(dentry, NULL, false))) {
err = -ESTALE;
_______________________________________________
unionfs-cvs mailing list: http://unionfs.filesystems.org/
[email protected]
http://www.fsl.cs.sunysb.edu/mailman/listinfo/unionfs-cvs