Unionfs: implement lockdep classes
authorErez Zadok <ezk@cs.sunysb.edu>
Fri, 28 Dec 2007 18:38:12 +0000 (13:38 -0500)
committerErez Zadok <ezk@cs.sunysb.edu>
Sun, 23 Mar 2008 03:49:16 +0000 (23:49 -0400)
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 <ezk@cs.sunysb.edu>
16 files changed:
fs/unionfs/commonfops.c
fs/unionfs/copyup.c
fs/unionfs/dentry.c
fs/unionfs/dirfops.c
fs/unionfs/fanout.h
fs/unionfs/file.c
fs/unionfs/inode.c
fs/unionfs/lookup.c
fs/unionfs/main.c
fs/unionfs/mmap.c
fs/unionfs/rename.c
fs/unionfs/subr.c
fs/unionfs/super.c
fs/unionfs/union.h
fs/unionfs/unlink.c
fs/unionfs/xattr.c

index f714e2f213e9f7c19d4d1860e2631b72398779d0..4077907ef901ce40500da7e8530b41009e5e37e7 100644 (file)
@@ -311,7 +311,7 @@ int unionfs_file_revalidate(struct file *file, bool willwrite)
        int err = 0;
 
        dentry = file->f_path.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_path.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);
 
@@ -705,7 +705,7 @@ static int unionfs_ioctl_queryfile(struct file *file, unsigned int cmd,
        struct vfsmount *mnt;
 
        dentry = file->f_path.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);
@@ -755,7 +755,7 @@ long unionfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
        long err;
 
-       unionfs_read_lock(file->f_path.dentry->d_sb);
+       unionfs_read_lock(file->f_path.dentry->d_sb, UNIONFS_SMUTEX_PARENT);
 
        err = unionfs_file_revalidate(file, true);
        if (unlikely(err))
@@ -794,7 +794,7 @@ int unionfs_flush(struct file *file, fl_owner_t id)
        struct dentry *dentry = file->f_path.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))
index f7a7f32f975b5c6d05dd78d4bac666ea6026d3bc..b22d23c644d31fdf0375f029ac16a86c0d9e8eeb 100644 (file)
@@ -711,7 +711,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 =
@@ -746,7 +746,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;
@@ -837,7 +837,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;
 }
index 6512586ffa796356c81bb0b05b610e0ec2683a46..a2ee52cac3734872d89a370ff56523136a2e2fc9 100644 (file)
@@ -340,7 +340,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);
@@ -414,9 +414,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);
@@ -435,7 +435,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 */
index 88df635e536aeb3066a290524fe04d02005837de..a6138627a300094d96efb91a164d7a711a001157 100644 (file)
@@ -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_path.dentry->d_sb);
+       unionfs_read_lock(file->f_path.dentry->d_sb, UNIONFS_SMUTEX_PARENT);
 
        err = unionfs_file_revalidate(file, false);
        if (unlikely(err))
@@ -208,7 +208,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_path.dentry->d_sb);
+       unionfs_read_lock(file->f_path.dentry->d_sb, UNIONFS_SMUTEX_PARENT);
 
        err = unionfs_file_revalidate(file, false);
        if (unlikely(err))
index 864383ebf16b69a22de21ab4f3198287c90d1eec..5f31015d40394ada98f0cdf3ddd46110219583a7 100644 (file)
@@ -284,10 +284,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)
index c8ff783fd87a879b3ac5f35e74c5159ef1dbbadf..fd7f5a7a1b0fe559bc702d816e44f880b5526532 100644 (file)
@@ -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_path.dentry->d_sb);
+       unionfs_read_lock(file->f_path.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_path.dentry->d_sb);
+       unionfs_read_lock(file->f_path.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_path.dentry->d_sb);
+       unionfs_read_lock(file->f_path.dentry->d_sb, UNIONFS_SMUTEX_PARENT);
        err = unionfs_file_revalidate(file, true);
        if (unlikely(err))
                goto out;
index 6b388cc9316124f3c8432204206b2c79f5639300..4eb72faf029421cc0b0d61069ba0a044c7ae5039 100644 (file)
@@ -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;
@@ -891,9 +896,9 @@ out:
 static void unionfs_put_link(struct dentry *dentry, struct nameidata *nd,
                             void *cookie)
 {
-       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");
@@ -999,8 +1004,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;
index 62f7e2c543c1c77d32baaf69b8faecaffd629ab1..d13b17a1bad7c19a9fc499dbe98bf55acfe2f49a 100644 (file)
@@ -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 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;
@@ -436,8 +432,6 @@ out:
                }
        }
        kfree(whname);
-       if (locked_parent)
-               unionfs_unlock_dentry(parent_dentry);
        dput(parent_dentry);
        if (err && (lookupmode == INTERPOSE_LOOKUP))
                unionfs_unlock_dentry(dentry);
@@ -451,6 +445,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.
  */
@@ -540,7 +535,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);
 
@@ -551,7 +546,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;
 
index ea8976d9b7ec90fb04ca5b99e9d0a75267aec9b2..92f0e9d5f915b6221c9d5a3bd332b8fea5d976df 100644 (file)
@@ -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;
 
index 9b7e99bf235c945271502469dbe70993f5e20db5..ec5696a39ebecfb3475503515f2d020152c6fd0f 100644 (file)
@@ -184,7 +184,7 @@ static int unionfs_readpage(struct file *file, struct page *page)
        char *page_data = NULL;
        mode_t orig_mode;
 
-       unionfs_read_lock(file->f_path.dentry->d_sb);
+       unionfs_read_lock(file->f_path.dentry->d_sb, UNIONFS_SMUTEX_PARENT);
        err = unionfs_file_revalidate(file, false);
        if (unlikely(err))
                goto out;
@@ -277,7 +277,7 @@ static int unionfs_commit_write(struct file *file, struct page *page,
 
        BUG_ON(file == NULL);
 
-       unionfs_read_lock(file->f_path.dentry->d_sb);
+       unionfs_read_lock(file->f_path.dentry->d_sb, UNIONFS_SMUTEX_PARENT);
        err = unionfs_file_revalidate(file, true);
        if (unlikely(err))
                goto out;
index 37b83f69e1b4f4245b02ba9f728e6f1f110bea11..ac4a1f989f01a93cab0d245a45deeef39df9b74b 100644 (file)
@@ -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,
@@ -270,7 +270,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);
@@ -357,7 +357,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);
@@ -416,7 +416,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))) {
index 861de057583dce8c5d2d0ca6dcc4b97661888ca7..803971d6c5681dc0015492257ba53d71133ec7d8 100644 (file)
@@ -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);
 
index 64a761158084efdf357ce3a91f1d36bdd5039ab7..3d1afaff7ec3581377abcd7d5e260e17f9e68e49 100644 (file)
@@ -134,8 +134,8 @@ static int unionfs_statfs(struct dentry *dentry, struct kstatfs *buf)
 
        sb = dentry->d_sb;
 
-       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;
@@ -944,7 +944,7 @@ static void unionfs_umount_begin(struct vfsmount *mnt, int flags)
 
        sb = mnt->mnt_sb;
 
-       unionfs_read_lock(sb);
+       unionfs_read_lock(sb, UNIONFS_SMUTEX_CHILD);
 
        bstart = sbstart(sb);
        bend = sbend(sb);
@@ -969,9 +969,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)) {
index b4b13ab52f61611cc444df72d8998aaa7a3f280a..bbcb310174f968a24822934e444d49d5c3757503 100644 (file)
@@ -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 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;
 }
 
index 677a5aee5bc4fd6f37cd638b06bdbb7428ae3464..1e81494faf78c856499b6f077408eb3e04c64700 100644 (file)
@@ -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;
index 00c6d0dc0e833471335057fef52bce42972c5a6b..8001c651b9f511f21b9fba53ddfd33c19a7823fd 100644 (file)
@@ -45,8 +45,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;
@@ -74,8 +74,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;
@@ -103,8 +103,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;
@@ -132,8 +132,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;