From 8994c1719a8f783cc37314859e5f979239287107 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Thu, 9 May 2013 00:37:34 +0000 Subject: [PATCH] aufs: Update to aufs3.8-20130506 svn path=/dists/sid/linux/; revision=20061 --- debian/changelog | 8 + .../features/all/aufs3/aufs3-add.patch | 1615 +++++++++++------ 2 files changed, 1069 insertions(+), 554 deletions(-) diff --git a/debian/changelog b/debian/changelog index 3cb1b34a6..0b36f439c 100644 --- a/debian/changelog +++ b/debian/changelog @@ -50,6 +50,14 @@ linux (3.8.12-1) UNRELEASED; urgency=low - time/timekeeping: shadow tk->cycle_last together with clock->cycle_last - sched/workqueue: Only wake up idle workers if not blocked on sleeping spin lock + * aufs: Update to aufs3.8-20130506 + - for linux-3.7, btrfs limits the link count explicitly + - for linux-3.6, push mnt_want_write() outside of i_mutex + - possible bugfix, make sure the target branch is upper + - possible bugfix, error handling in reopen_nondir() + - new copyup implementation + - pin the branch dir + - convert the plink list into hlist -- Ben Hutchings Mon, 06 May 2013 03:59:09 +0100 diff --git a/debian/patches/features/all/aufs3/aufs3-add.patch b/debian/patches/features/all/aufs3/aufs3-add.patch index 775e6fc43..c9fdc3fa9 100644 --- a/debian/patches/features/all/aufs3/aufs3-add.patch +++ b/debian/patches/features/all/aufs3/aufs3-add.patch @@ -313,8 +313,8 @@ +#endif /* __KERNEL__ */ +#endif /* __AUFS_H__ */ --- a/fs/aufs/branch.c 1970-01-01 01:00:00.000000000 +0100 -+++ b/fs/aufs/branch.c 2013-05-04 18:39:17.031157464 +0100 -@@ -0,0 +1,1172 @@ ++++ b/fs/aufs/branch.c 2013-05-09 01:36:20.741181631 +0100 +@@ -0,0 +1,1213 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -344,6 +344,34 @@ +/* + * free a single branch + */ ++ ++/* prohibit rmdir to the root of the branch */ ++/* todo: another new flag? */ ++static void au_br_dflags_force(struct au_branch *br) ++{ ++ struct dentry *h_dentry; ++ ++ h_dentry = au_br_dentry(br); ++ spin_lock(&h_dentry->d_lock); ++ br->br_dflags = h_dentry->d_flags & DCACHE_MOUNTED; ++ h_dentry->d_flags |= DCACHE_MOUNTED; ++ spin_unlock(&h_dentry->d_lock); ++} ++ ++/* restore its d_flags */ ++static void au_br_dflags_restore(struct au_branch *br) ++{ ++ struct dentry *h_dentry; ++ ++ if (br->br_dflags) ++ return; ++ ++ h_dentry = au_br_dentry(br); ++ spin_lock(&h_dentry->d_lock); ++ h_dentry->d_flags &= ~DCACHE_MOUNTED; ++ spin_unlock(&h_dentry->d_lock); ++} ++ +static void au_br_do_free(struct au_branch *br) +{ + int i; @@ -373,9 +401,11 @@ + else + break; + ++ au_br_dflags_restore(br); ++ + /* recursive lock, s_umount of branch's */ + lockdep_off(); -+ mntput(br->br_mnt); ++ path_put(&br->br_path); + lockdep_on(); + kfree(wbr); + kfree(br); @@ -589,7 +619,7 @@ + * initialize or clean the whiteouts for an adding branch + */ +static int au_br_init_wh(struct super_block *sb, struct au_branch *br, -+ int new_perm, struct dentry *h_root) ++ int new_perm) +{ + int err, old_perm; + aufs_bindex_t bindex; @@ -597,6 +627,10 @@ + struct au_wbr *wbr; + struct au_hinode *hdir; + ++ err = vfsub_mnt_want_write(au_br_mnt(br)); ++ if (unlikely(err)) ++ goto out; ++ + wbr = br->br_wbr; + old_perm = br->br_perm; + br->br_perm = new_perm; @@ -607,20 +641,21 @@ + hdir = au_hi(sb->s_root->d_inode, bindex); + au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); + } else { -+ h_mtx = &h_root->d_inode->i_mutex; ++ h_mtx = &au_br_dentry(br)->d_inode->i_mutex; + mutex_lock_nested(h_mtx, AuLsc_I_PARENT); + } + if (!wbr) -+ err = au_wh_init(h_root, br, sb); ++ err = au_wh_init(br, sb); + else { + wbr_wh_write_lock(wbr); -+ err = au_wh_init(h_root, br, sb); ++ err = au_wh_init(br, sb); + wbr_wh_write_unlock(wbr); + } + if (hdir) + au_hn_imtx_unlock(hdir); + else + mutex_unlock(h_mtx); ++ vfsub_mnt_drop_write(au_br_mnt(br)); + br->br_perm = old_perm; + + if (!err && wbr && !au_br_writable(new_perm)) { @@ -628,16 +663,16 @@ + br->br_wbr = NULL; + } + ++out: + return err; +} + +static int au_wbr_init(struct au_branch *br, struct super_block *sb, -+ int perm, struct path *path) ++ int perm) +{ + int err; + struct kstatfs kst; + struct au_wbr *wbr; -+ struct dentry *h_dentry; + + wbr = br->br_wbr; + au_rw_init(&wbr->wbr_wh_rwsem); @@ -649,17 +684,16 @@ + * a limit for rmdir/rename a dir + * cf. AUFS_MAX_NAMELEN in include/linux/aufs_type.h + */ -+ err = vfs_statfs(path, &kst); ++ err = vfs_statfs(&br->br_path, &kst); + if (unlikely(err)) + goto out; + err = -EINVAL; -+ h_dentry = path->dentry; + if (kst.f_namelen >= NAME_MAX) -+ err = au_br_init_wh(sb, br, perm, h_dentry); ++ err = au_br_init_wh(sb, br, perm); + else + pr_err("%.*s(%s), unsupported namelen %ld\n", -+ AuDLNPair(h_dentry), au_sbtype(h_dentry->d_sb), -+ kst.f_namelen); ++ AuDLNPair(au_br_dentry(br)), ++ au_sbtype(au_br_dentry(br)->d_sb), kst.f_namelen); + +out: + return err; @@ -675,7 +709,10 @@ + memset(&br->br_xino, 0, sizeof(br->br_xino)); + mutex_init(&br->br_xino.xi_nondir_mtx); + br->br_perm = add->perm; -+ br->br_mnt = add->path.mnt; /* set first, mntget() later */ ++ BUILD_BUG_ON(sizeof(br->br_dflags) ++ != sizeof(br->br_path.dentry->d_flags)); ++ br->br_dflags = DCACHE_MOUNTED; ++ br->br_path = add->path; /* set first, path_get() later */ + spin_lock_init(&br->br_dykey_lock); + memset(br->br_dykey, 0, sizeof(br->br_dykey)); + atomic_set(&br->br_count, 0); @@ -685,7 +722,7 @@ + AuDebugOn(br->br_id < 0); + + if (au_br_writable(add->perm)) { -+ err = au_wbr_init(br, sb, add->perm, &add->path); ++ err = au_wbr_init(br, sb, add->perm); + if (unlikely(err)) + goto out_err; + } @@ -700,11 +737,11 @@ + } + + sysaufs_br_init(br); -+ mntget(add->path.mnt); ++ path_get(&br->br_path); + goto out; /* success */ + +out_err: -+ br->br_mnt = NULL; ++ memset(&br->br_path, 0, sizeof(br->br_path)); +out: + return err; +} @@ -756,17 +793,20 @@ + iinfo->ii_bstart = 0; +} + -+static void au_br_do_add(struct super_block *sb, struct dentry *h_dentry, -+ struct au_branch *br, aufs_bindex_t bindex) ++static void au_br_do_add(struct super_block *sb, struct au_branch *br, ++ aufs_bindex_t bindex) +{ -+ struct dentry *root; ++ struct dentry *root, *h_dentry; + struct inode *root_inode; + aufs_bindex_t bend, amount; + ++ au_br_dflags_force(br); ++ + root = sb->s_root; + root_inode = root->d_inode; + bend = au_sbend(sb); + amount = bend + 1 - bindex; ++ h_dentry = au_br_dentry(br); + au_sbilist_lock(); + au_br_do_add_brp(au_sbi(sb), bindex, br, bend, amount); + au_br_do_add_hdp(au_di(root), bindex, bend, amount); @@ -809,15 +849,15 @@ + } + + add_bindex = add->bindex; -+ h_dentry = add->path.dentry; + if (!remount) -+ au_br_do_add(sb, h_dentry, add_branch, add_bindex); ++ au_br_do_add(sb, add_branch, add_bindex); + else { + sysaufs_brs_del(sb, add_bindex); -+ au_br_do_add(sb, h_dentry, add_branch, add_bindex); ++ au_br_do_add(sb, add_branch, add_bindex); + sysaufs_brs_add(sb, add_bindex); + } + ++ h_dentry = add->path.dentry; + if (!add_bindex) { + au_cpup_attr_all(root_inode, /*force*/1); + sb->s_maxbytes = h_dentry->d_sb->s_maxbytes; @@ -1123,6 +1163,7 @@ + goto out; + } + br = au_sbr(sb, bindex); ++ AuDebugOn(!path_equal(&br->br_path, &del->h_path)); + i = atomic_read(&br->br_count); + if (unlikely(i)) { + AuVerbose(verbose, "%d file(s) opened\n", i); @@ -1171,7 +1212,7 @@ + +out_wh: + /* revert */ -+ rerr = au_br_init_wh(sb, br, br->br_perm, del->h_path.dentry); ++ rerr = au_br_init_wh(sb, br, br->br_perm); + if (rerr) + pr_warn("failed re-creating base whiteout, %s. (%d)\n", + del->pathname, rerr); @@ -1408,7 +1449,6 @@ +{ + int err, rerr; + aufs_bindex_t bindex; -+ struct path path; + struct dentry *root; + struct au_branch *br; + @@ -1428,12 +1468,13 @@ + goto out; + + br = au_sbr(sb, bindex); ++ AuDebugOn(mod->h_root != au_br_dentry(br)); + if (br->br_perm == mod->perm) + return 0; /* success */ + + if (au_br_writable(br->br_perm)) { + /* remove whiteout base */ -+ err = au_br_init_wh(sb, br, mod->perm, mod->h_root); ++ err = au_br_init_wh(sb, br, mod->perm); + if (unlikely(err)) + goto out; + @@ -1450,12 +1491,8 @@ + rerr = -ENOMEM; + br->br_wbr = kmalloc(sizeof(*br->br_wbr), + GFP_NOFS); -+ if (br->br_wbr) { -+ path.mnt = br->br_mnt; -+ path.dentry = mod->h_root; -+ rerr = au_wbr_init(br, sb, br->br_perm, -+ &path); -+ } ++ if (br->br_wbr) ++ rerr = au_wbr_init(br, sb, br->br_perm); + if (unlikely(rerr)) { + AuIOErr("nested error %d (%d)\n", + rerr, err); @@ -1468,9 +1505,7 @@ + err = -ENOMEM; + br->br_wbr = kmalloc(sizeof(*br->br_wbr), GFP_NOFS); + if (br->br_wbr) { -+ path.mnt = br->br_mnt; -+ path.dentry = mod->h_root; -+ err = au_wbr_init(br, sb, mod->perm, &path); ++ err = au_wbr_init(br, sb, mod->perm); + if (unlikely(err)) { + kfree(br->br_wbr); + br->br_wbr = NULL; @@ -1479,6 +1514,12 @@ + } + + if (!err) { ++ if ((br->br_perm & AuBrAttr_UNPIN) ++ && !(mod->perm & AuBrAttr_UNPIN)) ++ au_br_dflags_force(br); ++ else if (!(br->br_perm & AuBrAttr_UNPIN) ++ && (mod->perm & AuBrAttr_UNPIN)) ++ au_br_dflags_restore(br); + *do_refresh |= need_sigen_inc(br->br_perm, mod->perm); + br->br_perm = mod->perm; + } @@ -1488,8 +1529,8 @@ + return err; +} --- a/fs/aufs/branch.h 1970-01-01 01:00:00.000000000 +0100 -+++ b/fs/aufs/branch.h 2013-05-04 18:39:11.231157327 +0100 -@@ -0,0 +1,236 @@ ++++ b/fs/aufs/branch.h 2013-05-09 01:36:20.741181631 +0100 +@@ -0,0 +1,255 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -1567,7 +1608,8 @@ + aufs_bindex_t br_id; + + int br_perm; -+ struct vfsmount *br_mnt; ++ unsigned int br_dflags; ++ struct path br_path; + spinlock_t br_dykey_lock; + struct au_dykey *br_dykey[AuBrDynOp]; + atomic_t br_count; @@ -1591,6 +1633,21 @@ + +/* ---------------------------------------------------------------------- */ + ++static inline struct vfsmount *au_br_mnt(struct au_branch *br) ++{ ++ return br->br_path.mnt; ++} ++ ++static inline struct dentry *au_br_dentry(struct au_branch *br) ++{ ++ return br->br_path.dentry; ++} ++ ++static inline struct super_block *au_br_sb(struct au_branch *br) ++{ ++ return au_br_mnt(br)->mnt_sb; ++} ++ +/* branch permissions and attributes */ +#define AuBrPerm_RW 1 /* writable, hardlinkable wh */ +#define AuBrPerm_RO (1 << 1) /* readonly */ @@ -1601,6 +1658,9 @@ + +#define AuBrWAttr_NoLinkWH (1 << 4) /* un-hardlinkable whiteouts */ + ++#define AuBrAttr_UNPIN (1 << 5) /* rename-able top dir of ++ branch */ ++ +static inline int au_br_writable(int brperm) +{ + return brperm & AuBrPerm_RW; @@ -1618,7 +1678,7 @@ + +static inline int au_br_rdonly(struct au_branch *br) +{ -+ return ((br->br_mnt->mnt_sb->s_flags & MS_RDONLY) ++ return ((au_br_sb(br)->s_flags & MS_RDONLY) + || !au_br_writable(br->br_perm)) + ? -EROFS : 0; +} @@ -1688,13 +1748,13 @@ +static inline +struct vfsmount *au_sbr_mnt(struct super_block *sb, aufs_bindex_t bindex) +{ -+ return au_sbr(sb, bindex)->br_mnt; ++ return au_br_mnt(au_sbr(sb, bindex)); +} + +static inline +struct super_block *au_sbr_sb(struct super_block *sb, aufs_bindex_t bindex) +{ -+ return au_sbr_mnt(sb, bindex)->mnt_sb; ++ return au_br_sb(au_sbr(sb, bindex)); +} + +static inline void au_sbr_put(struct super_block *sb, aufs_bindex_t bindex) @@ -1768,8 +1828,8 @@ + +-include ${srctree}/${src}/conf_priv.mk --- a/fs/aufs/cpup.c 1970-01-01 01:00:00.000000000 +0100 -+++ b/fs/aufs/cpup.c 2013-05-04 18:39:11.231157327 +0100 -@@ -0,0 +1,1085 @@ ++++ b/fs/aufs/cpup.c 2013-05-09 01:36:20.741181631 +0100 +@@ -0,0 +1,1221 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -1796,12 +1856,14 @@ +#include +#include "aufs.h" + -+void au_cpup_attr_flags(struct inode *dst, struct inode *src) ++void au_cpup_attr_flags(struct inode *dst, unsigned int iflags) +{ + const unsigned int mask = S_DEAD | S_SWAPFILE | S_PRIVATE + | S_NOATIME | S_NOCMTIME; + -+ dst->i_flags |= src->i_flags & ~mask; ++ BUILD_BUG_ON(sizeof(iflags) != sizeof(dst->i_flags)); ++ ++ dst->i_flags |= iflags & ~mask; + if (au_test_fs_notime(dst->i_sb)) + dst->i_flags |= S_NOATIME | S_NOCMTIME; +} @@ -1860,7 +1922,7 @@ + inode->i_uid = h_inode->i_uid; + inode->i_gid = h_inode->i_gid; + au_cpup_attr_timesizes(inode); -+ au_cpup_attr_flags(inode, h_inode); ++ au_cpup_attr_flags(inode, h_inode->i_flags); +} + +void au_cpup_igen(struct inode *inode, struct inode *h_inode) @@ -1921,13 +1983,22 @@ + +/* ---------------------------------------------------------------------- */ + ++/* internal use only */ ++struct au_cpup_reg_attr { ++ int valid; ++ struct kstat st; ++ unsigned int iflags; /* inode->i_flags */ ++}; ++ +static noinline_for_stack -+int cpup_iattr(struct dentry *dst, aufs_bindex_t bindex, struct dentry *h_src) ++int cpup_iattr(struct dentry *dst, aufs_bindex_t bindex, struct dentry *h_src, ++ struct au_cpup_reg_attr *h_src_attr) +{ + int err, sbits; + struct iattr ia; + struct path h_path; + struct inode *h_isrc, *h_idst; ++ struct kstat *h_st; + + h_path.dentry = au_h_dptr(dst, bindex); + h_idst = h_path.dentry->d_inode; @@ -1936,17 +2007,32 @@ + ia.ia_valid = ATTR_FORCE | ATTR_UID | ATTR_GID + | ATTR_ATIME | ATTR_MTIME + | ATTR_ATIME_SET | ATTR_MTIME_SET; -+ ia.ia_uid = h_isrc->i_uid; -+ ia.ia_gid = h_isrc->i_gid; -+ ia.ia_atime = h_isrc->i_atime; -+ ia.ia_mtime = h_isrc->i_mtime; -+ if (h_idst->i_mode != h_isrc->i_mode -+ && !S_ISLNK(h_idst->i_mode)) { -+ ia.ia_valid |= ATTR_MODE; -+ ia.ia_mode = h_isrc->i_mode; ++ if (h_src_attr && h_src_attr->valid) { ++ h_st = &h_src_attr->st; ++ ia.ia_uid = h_st->uid; ++ ia.ia_gid = h_st->gid; ++ ia.ia_atime = h_st->atime; ++ ia.ia_mtime = h_st->mtime; ++ if (h_idst->i_mode != h_st->mode ++ && !S_ISLNK(h_idst->i_mode)) { ++ ia.ia_valid |= ATTR_MODE; ++ ia.ia_mode = h_st->mode; ++ } ++ sbits = !!(h_st->mode & (S_ISUID | S_ISGID)); ++ au_cpup_attr_flags(h_idst, h_src_attr->iflags); ++ } else { ++ ia.ia_uid = h_isrc->i_uid; ++ ia.ia_gid = h_isrc->i_gid; ++ ia.ia_atime = h_isrc->i_atime; ++ ia.ia_mtime = h_isrc->i_mtime; ++ if (h_idst->i_mode != h_isrc->i_mode ++ && !S_ISLNK(h_idst->i_mode)) { ++ ia.ia_valid |= ATTR_MODE; ++ ia.ia_mode = h_isrc->i_mode; ++ } ++ sbits = !!(h_isrc->i_mode & (S_ISUID | S_ISGID)); ++ au_cpup_attr_flags(h_idst, h_isrc->i_flags); + } -+ sbits = !!(h_isrc->i_mode & (S_ISUID | S_ISGID)); -+ au_cpup_attr_flags(h_idst, h_isrc); + err = vfsub_notify_change(&h_path, &ia); + + /* is this nfs only? */ @@ -2091,12 +2177,18 @@ + return err; +} + ++/* internal use only */ ++struct au_cpup_basic { ++ struct dentry *dentry; ++ aufs_bindex_t bdst, bsrc; ++ loff_t len; ++}; ++ +/* + * to support a sparse file which is opened with O_APPEND, + * we need to close the file. + */ -+static int au_cp_regular(struct dentry *dentry, aufs_bindex_t bdst, -+ aufs_bindex_t bsrc, loff_t len) ++static int au_cp_regular(struct au_cpup_basic *basic) +{ + int err, i; + enum { SRC, DST }; @@ -2108,14 +2200,14 @@ + void *label, *label_file; + } *f, file[] = { + { -+ .bindex = bsrc, ++ .bindex = basic->bsrc, + .flags = O_RDONLY | O_NOATIME | O_LARGEFILE, + .file = NULL, + .label = &&out, + .label_file = &&out_src + }, + { -+ .bindex = bdst, ++ .bindex = basic->bdst, + .flags = O_WRONLY | O_NOATIME | O_LARGEFILE, + .file = NULL, + .label = &&out_src, @@ -2125,11 +2217,12 @@ + struct super_block *sb; + + /* bsrc branch can be ro/rw. */ -+ sb = dentry->d_sb; ++ sb = basic->dentry->d_sb; + f = file; + for (i = 0; i < 2; i++, f++) { -+ f->dentry = au_h_dptr(dentry, f->bindex); -+ f->file = au_h_open(dentry, f->bindex, f->flags, /*file*/NULL); ++ f->dentry = au_h_dptr(basic->dentry, f->bindex); ++ f->file = au_h_open(basic->dentry, f->bindex, f->flags, ++ /*file*/NULL); + err = PTR_ERR(f->file); + if (IS_ERR(f->file)) + goto *f->label; @@ -2140,7 +2233,7 @@ + + /* try stopping to update while we copyup */ + IMustLock(file[SRC].dentry->d_inode); -+ err = au_copy_file(file[DST].file, file[SRC].file, len); ++ err = au_copy_file(file[DST].file, file[SRC].file, basic->len); + +out_dst: + fput(file[DST].file); @@ -2152,27 +2245,39 @@ + return err; +} + -+static int au_do_cpup_regular(struct dentry *dentry, aufs_bindex_t bdst, -+ aufs_bindex_t bsrc, loff_t len, -+ struct inode *h_dir, struct path *h_path) ++static int au_do_cpup_regular(struct au_cpup_basic *basic, struct au_pin *pin, ++ struct au_cpup_reg_attr *h_src_attr) +{ + int err, rerr; + loff_t l; ++ struct dentry *h_src_dentry; ++ struct inode *h_src_inode; ++ struct vfsmount *h_src_mnt; + + err = 0; -+ l = i_size_read(au_h_iptr(dentry->d_inode, bsrc)); -+ if (len == -1 || l < len) -+ len = l; -+ if (len) -+ err = au_cp_regular(dentry, bdst, bsrc, len); -+ if (!err) -+ goto out; /* success */ ++ h_src_inode = au_h_iptr(basic->dentry->d_inode, basic->bsrc); ++ l = i_size_read(h_src_inode); ++ if (basic->len == -1 || l < basic->len) ++ basic->len = l; ++ if (basic->len) { ++ /* try stopping to update while we are referencing */ ++ mutex_lock_nested(&h_src_inode->i_mutex, AuLsc_I_CHILD); ++ au_pin_hdir_unlock(pin); + -+ rerr = vfsub_unlink(h_dir, h_path, /*force*/0); -+ if (rerr) { -+ AuIOErr("failed unlinking cpup-ed %.*s(%d, %d)\n", -+ AuDLNPair(h_path->dentry), err, rerr); -+ err = -EIO; ++ h_src_dentry = au_h_dptr(basic->dentry, basic->bsrc); ++ h_src_mnt = au_sbr_mnt(basic->dentry->d_sb, basic->bsrc); ++ h_src_attr->iflags = h_src_inode->i_flags; ++ err = vfs_getattr(h_src_mnt, h_src_dentry, &h_src_attr->st); ++ if (unlikely(err)) { ++ mutex_unlock(&h_src_inode->i_mutex); ++ goto out; ++ } ++ h_src_attr->valid = 1; ++ err = au_cp_regular(basic); ++ mutex_unlock(&h_src_inode->i_mutex); ++ rerr = au_pin_hdir_relock(pin); ++ if (!err && rerr) ++ err = rerr; + } + +out: @@ -2215,11 +2320,10 @@ + return err; +} + -+/* return with the lower dst inode is locked */ +static noinline_for_stack -+int cpup_entry(struct dentry *dentry, aufs_bindex_t bdst, -+ aufs_bindex_t bsrc, loff_t len, unsigned int flags, -+ struct dentry *dst_parent) ++int cpup_entry(struct au_cpup_basic *basic, unsigned int flags, ++ struct dentry *dst_parent, struct au_pin *pin, ++ struct au_cpup_reg_attr *h_src_attr) +{ + int err; + umode_t mode; @@ -2233,19 +2337,22 @@ + struct super_block *sb; + + /* bsrc branch can be ro/rw. */ -+ h_src = au_h_dptr(dentry, bsrc); ++ h_src = au_h_dptr(basic->dentry, basic->bsrc); + h_inode = h_src->d_inode; -+ AuDebugOn(h_inode != au_h_iptr(dentry->d_inode, bsrc)); ++ AuDebugOn(h_inode != au_h_iptr(basic->dentry->d_inode, basic->bsrc)); + + /* try stopping to be referenced while we are creating */ -+ h_dst = au_h_dptr(dentry, bdst); ++ h_dst = au_h_dptr(basic->dentry, basic->bdst); ++ if (au_ftest_cpup(flags, RENAME)) ++ AuDebugOn(strncmp(h_dst->d_name.name, AUFS_WH_PFX, ++ AUFS_WH_PFX_LEN)); + h_parent = h_dst->d_parent; /* dir inode is locked */ + h_dir = h_parent->d_inode; + IMustLock(h_dir); + AuDebugOn(h_parent != h_dst->d_parent); + -+ sb = dentry->d_sb; -+ h_path.mnt = au_sbr_mnt(sb, bdst); ++ sb = basic->dentry->d_sb; ++ h_path.mnt = au_sbr_mnt(sb, basic->bdst); + if (do_dt) { + h_path.dentry = h_parent; + au_dtime_store(&dt, dst_parent, &h_path); @@ -2256,14 +2363,10 @@ + mode = h_inode->i_mode; + switch (mode & S_IFMT) { + case S_IFREG: -+ /* try stopping to update while we are referencing */ -+ IMustLock(h_inode); + err = vfsub_create(h_dir, &h_path, mode | S_IWUSR, + /*want_excl*/true); + if (!err) -+ err = au_do_cpup_regular -+ (dentry, bdst, bsrc, len, -+ au_h_iptr(dst_parent->d_inode, bdst), &h_path); ++ err = au_do_cpup_regular(basic, pin, h_src_attr); + break; + case S_IFDIR: + isdir = 1; @@ -2273,10 +2376,10 @@ + * strange behaviour from the users view, + * particularry setattr case + */ -+ if (au_ibstart(dst_parent->d_inode) == bdst) ++ if (au_ibstart(dst_parent->d_inode) == basic->bdst) + au_cpup_attr_nlink(dst_parent->d_inode, + /*force*/1); -+ au_cpup_attr_nlink(dentry->d_inode, /*force*/1); ++ au_cpup_attr_nlink(basic->dentry->d_inode, /*force*/1); + } + break; + case S_IFLNK: @@ -2301,10 +2404,10 @@ + && au_opt_test(mnt_flags, XINO) + && h_inode->i_nlink == 1 + /* todo: unnecessary? */ -+ /* && dentry->d_inode->i_nlink == 1 */ -+ && bdst < bsrc ++ /* && basic->dentry->d_inode->i_nlink == 1 */ ++ && basic->bdst < basic->bsrc + && !au_ftest_cpup(flags, KEEPLINO)) -+ au_xino_write(sb, bsrc, h_inode->i_ino, /*ino*/0); ++ au_xino_write(sb, basic->bsrc, h_inode->i_ino, /*ino*/0); + /* ignore this error */ + + if (do_dt) @@ -2312,15 +2415,42 @@ + return err; +} + ++static int au_do_ren_after_cpup(struct dentry *dentry, aufs_bindex_t bdst, ++ struct path *h_path) ++{ ++ int err; ++ struct dentry *h_dentry, *h_parent; ++ struct inode *h_dir; ++ ++ h_dentry = dget(au_h_dptr(dentry, bdst)); ++ au_set_h_dptr(dentry, bdst, NULL); ++ err = au_lkup_neg(dentry, bdst, /*wh*/0); ++ if (unlikely(err)) { ++ au_set_h_dptr(dentry, bdst, h_dentry); ++ goto out; ++ } ++ ++ h_path->dentry = dget(au_h_dptr(dentry, bdst)); ++ au_set_h_dptr(dentry, bdst, h_dentry); ++ h_parent = h_dentry->d_parent; /* dir inode is locked */ ++ h_dir = h_parent->d_inode; ++ IMustLock(h_dir); ++ AuDbg("%.*s %.*s\n", AuDLNPair(h_dentry), AuDLNPair(h_path->dentry)); ++ err = vfsub_rename(h_dir, h_dentry, h_dir, h_path); ++ dput(h_path->dentry); ++ ++out: ++ return err; ++} ++ +/* + * copyup the @dentry from @bsrc to @bdst. + * the caller must set the both of lower dentries. + * @len is for truncating when it is -1 copyup the entire file. + * in link/rename cases, @dst_parent may be different from the real one. + */ -+static int au_cpup_single(struct dentry *dentry, aufs_bindex_t bdst, -+ aufs_bindex_t bsrc, loff_t len, unsigned int flags, -+ struct dentry *dst_parent) ++static int au_cpup_single(struct au_cpup_basic *basic, unsigned int flags, ++ struct dentry *dst_parent, struct au_pin *pin) +{ + int err, rerr; + aufs_bindex_t old_ibstart; @@ -2330,39 +2460,44 @@ + struct dentry *h_src, *h_dst, *h_parent; + struct inode *dst_inode, *h_dir, *inode; + struct super_block *sb; ++ struct au_branch *br; ++ struct au_cpup_reg_attr h_src_attr = { ++ .valid = 0 ++ }; + -+ AuDebugOn(bsrc <= bdst); ++ AuDebugOn(basic->bsrc <= basic->bdst); + -+ sb = dentry->d_sb; -+ h_path.mnt = au_sbr_mnt(sb, bdst); -+ h_dst = au_h_dptr(dentry, bdst); ++ sb = basic->dentry->d_sb; ++ br = au_sbr(sb, basic->bdst); ++ h_path.mnt = au_br_mnt(br); ++ h_dst = au_h_dptr(basic->dentry, basic->bdst); + h_parent = h_dst->d_parent; /* dir inode is locked */ + h_dir = h_parent->d_inode; + IMustLock(h_dir); + -+ h_src = au_h_dptr(dentry, bsrc); -+ inode = dentry->d_inode; ++ h_src = au_h_dptr(basic->dentry, basic->bsrc); ++ inode = basic->dentry->d_inode; + + if (!dst_parent) -+ dst_parent = dget_parent(dentry); ++ dst_parent = dget_parent(basic->dentry); + else + dget(dst_parent); + + plink = !!au_opt_test(au_mntflags(sb), PLINK); -+ dst_inode = au_h_iptr(inode, bdst); ++ dst_inode = au_h_iptr(inode, basic->bdst); + if (dst_inode) { + if (unlikely(!plink)) { + err = -EIO; + AuIOErr("hi%lu(i%lu) exists on b%d " + "but plink is disabled\n", -+ dst_inode->i_ino, inode->i_ino, bdst); ++ dst_inode->i_ino, inode->i_ino, basic->bdst); + goto out; + } + + if (dst_inode->i_nlink) { + const int do_dt = au_ftest_cpup(flags, DTIME); + -+ h_src = au_plink_lkup(inode, bdst); ++ h_src = au_plink_lkup(inode, basic->bdst); + err = PTR_ERR(h_src); + if (IS_ERR(h_src)) + goto out; @@ -2379,8 +2514,12 @@ + h_path.dentry = h_parent; + au_dtime_store(&dt, dst_parent, &h_path); + } ++ + h_path.dentry = h_dst; + err = vfsub_link(h_src, h_dir, &h_path); ++ if (!err && au_ftest_cpup(flags, RENAME)) ++ err = au_do_ren_after_cpup ++ (basic->dentry, basic->bdst, &h_path); + if (do_dt) + au_dtime_revert(&dt); + dput(h_src); @@ -2391,50 +2530,73 @@ + au_update_ibrange(inode, /*do_put_zero*/1); + } + ++ isdir = S_ISDIR(inode->i_mode); + old_ibstart = au_ibstart(inode); -+ err = cpup_entry(dentry, bdst, bsrc, len, flags, dst_parent); ++ err = cpup_entry(basic, flags, dst_parent, pin, &h_src_attr); + if (unlikely(err)) -+ goto out; ++ goto out_rev; + dst_inode = h_dst->d_inode; + mutex_lock_nested(&dst_inode->i_mutex, AuLsc_I_CHILD2); ++ /* todo: necessary? */ ++ /* au_pin_hdir_unlock(pin); */ + -+ err = cpup_iattr(dentry, bdst, h_src); -+ isdir = S_ISDIR(dst_inode->i_mode); -+ if (!err) { -+ if (bdst < old_ibstart) { -+ if (S_ISREG(inode->i_mode)) { -+ err = au_dy_iaop(inode, bdst, dst_inode); -+ if (unlikely(err)) -+ goto out_rev; -+ } -+ au_set_ibstart(inode, bdst); -+ } -+ au_set_h_iptr(inode, bdst, au_igrab(dst_inode), -+ au_hi_flags(inode, isdir)); ++ err = cpup_iattr(basic->dentry, basic->bdst, h_src, &h_src_attr); ++ if (unlikely(err)) { ++ /* todo: necessary? */ ++ /* au_pin_hdir_relock(pin); */ /* ignore an error */ + mutex_unlock(&dst_inode->i_mutex); -+ if (!isdir -+ && h_src->d_inode->i_nlink > 1 -+ && plink) -+ au_plink_append(inode, bdst, h_dst); -+ goto out; /* success */ ++ goto out_rev; + } + ++ if (basic->bdst < old_ibstart) { ++ if (S_ISREG(inode->i_mode)) { ++ err = au_dy_iaop(inode, basic->bdst, dst_inode); ++ if (unlikely(err)) { ++ /* au_pin_hdir_relock(pin); ignore an error */ ++ mutex_unlock(&dst_inode->i_mutex); ++ goto out_rev; ++ } ++ } ++ au_set_ibstart(inode, basic->bdst); ++ } ++ au_set_h_iptr(inode, basic->bdst, au_igrab(dst_inode), ++ au_hi_flags(inode, isdir)); ++ ++ /* todo: necessary? */ ++ /* err = au_pin_hdir_relock(pin); */ ++ mutex_unlock(&dst_inode->i_mutex); ++ if (unlikely(err)) ++ goto out_rev; ++ ++ if (!isdir ++ && h_src->d_inode->i_nlink > 1 ++ && plink) ++ au_plink_append(inode, basic->bdst, h_dst); ++ ++ if (au_ftest_cpup(flags, RENAME)) { ++ h_path.dentry = h_dst; ++ err = au_do_ren_after_cpup(basic->dentry, basic->bdst, &h_path); ++ } ++ if (!err) ++ goto out; /* success */ ++ + /* revert */ +out_rev: + h_path.dentry = h_parent; -+ mutex_unlock(&dst_inode->i_mutex); + au_dtime_store(&dt, dst_parent, &h_path); + h_path.dentry = h_dst; -+ if (!isdir) -+ rerr = vfsub_unlink(h_dir, &h_path, /*force*/0); -+ else -+ rerr = vfsub_rmdir(h_dir, &h_path); ++ rerr = 0; ++ if (h_dst->d_inode) { ++ if (!isdir) ++ rerr = vfsub_unlink(h_dir, &h_path, /*force*/0); ++ else ++ rerr = vfsub_rmdir(h_dir, &h_path); ++ } + au_dtime_revert(&dt); + if (rerr) { + AuIOErr("failed removing broken entry(%d, %d)\n", err, rerr); + err = -EIO; + } -+ +out: + dput(dst_parent); + return err; @@ -2442,18 +2604,19 @@ + +struct au_cpup_single_args { + int *errp; -+ struct dentry *dentry; -+ aufs_bindex_t bdst, bsrc; -+ loff_t len; ++ struct au_cpup_basic *basic; + unsigned int flags; + struct dentry *dst_parent; ++ struct au_pin *pin; +}; + +static void au_call_cpup_single(void *args) +{ + struct au_cpup_single_args *a = args; -+ *a->errp = au_cpup_single(a->dentry, a->bdst, a->bsrc, a->len, -+ a->flags, a->dst_parent); ++ ++ au_pin_hdir_acquire_nest(a->pin); ++ *a->errp = au_cpup_single(a->basic, a->flags, a->dst_parent, a->pin); ++ au_pin_hdir_release(a->pin); +} + +/* @@ -2489,24 +2652,27 @@ + +int au_sio_cpup_single(struct dentry *dentry, aufs_bindex_t bdst, + aufs_bindex_t bsrc, loff_t len, unsigned int flags, -+ struct dentry *dst_parent) ++ struct dentry *dst_parent, struct au_pin *pin) +{ + int err, wkq_err; + struct dentry *h_dentry; ++ struct au_cpup_basic basic = { ++ .dentry = dentry, ++ .bdst = bdst, ++ .bsrc = bsrc, ++ .len = len ++ }; + + h_dentry = au_h_dptr(dentry, bsrc); + if (!au_cpup_sio_test(dentry->d_sb, h_dentry->d_inode->i_mode)) -+ err = au_cpup_single(dentry, bdst, bsrc, len, flags, -+ dst_parent); ++ err = au_cpup_single(&basic, flags, dst_parent, pin); + else { + struct au_cpup_single_args args = { + .errp = &err, -+ .dentry = dentry, -+ .bdst = bdst, -+ .bsrc = bsrc, -+ .len = len, ++ .basic = &basic, + .flags = flags, -+ .dst_parent = dst_parent ++ .dst_parent = dst_parent, ++ .pin = pin + }; + wkq_err = au_wkq_wait(au_call_cpup_single, &args); + if (unlikely(wkq_err)) @@ -2521,19 +2687,32 @@ + * using au_cpup_single(). + */ +static int au_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len, -+ unsigned int flags) ++ unsigned int flags, struct au_pin *pin) +{ + int err; + aufs_bindex_t bsrc, bend; ++ struct dentry *h_dentry; + ++ DiMustWriteLock(dentry); + bend = au_dbend(dentry); -+ for (bsrc = bdst + 1; bsrc <= bend; bsrc++) -+ if (au_h_dptr(dentry, bsrc)) ++ for (bsrc = bdst + 1; bsrc <= bend; bsrc++) { ++ h_dentry = au_h_dptr(dentry, bsrc); ++ if (h_dentry) { ++ AuDebugOn(!h_dentry->d_inode); + break; ++ } ++ } ++ AuDebugOn(bsrc > bend); + -+ err = au_lkup_neg(dentry, bdst); ++ err = au_lkup_neg(dentry, bdst, /*wh*/1); + if (!err) { -+ err = au_cpup_single(dentry, bdst, bsrc, len, flags, NULL); ++ struct au_cpup_basic basic = { ++ .dentry = dentry, ++ .bdst = bdst, ++ .bsrc = bsrc, ++ .len = len ++ }; ++ err = au_cpup_single(&basic, flags | AuCpup_RENAME, NULL, pin); + if (!err) + return 0; /* success */ + @@ -2551,16 +2730,20 @@ + aufs_bindex_t bdst; + loff_t len; + unsigned int flags; ++ struct au_pin *pin; +}; + +static void au_call_cpup_simple(void *args) +{ + struct au_cpup_simple_args *a = args; -+ *a->errp = au_cpup_simple(a->dentry, a->bdst, a->len, a->flags); ++ ++ au_pin_hdir_acquire_nest(a->pin); ++ *a->errp = au_cpup_simple(a->dentry, a->bdst, a->len, a->flags, a->pin); ++ au_pin_hdir_release(a->pin); +} + +int au_sio_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len, -+ unsigned int flags) ++ unsigned int flags, struct au_pin *pin) +{ + int err, wkq_err; + struct dentry *parent; @@ -2570,14 +2753,15 @@ + h_dir = au_h_iptr(parent->d_inode, bdst); + if (!au_test_h_perm_sio(h_dir, MAY_EXEC | MAY_WRITE) + && !au_cpup_sio_test(dentry->d_sb, dentry->d_inode->i_mode)) -+ err = au_cpup_simple(dentry, bdst, len, flags); ++ err = au_cpup_simple(dentry, bdst, len, flags, pin); + else { + struct au_cpup_simple_args args = { + .errp = &err, + .dentry = dentry, + .bdst = bdst, + .len = len, -+ .flags = flags ++ .flags = flags, ++ .pin = pin + }; + wkq_err = au_wkq_wait(au_call_cpup_simple, &args); + if (unlikely(wkq_err)) @@ -2595,10 +2779,15 @@ + */ +static int au_do_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, + struct dentry *wh_dentry, struct file *file, -+ loff_t len) ++ loff_t len, struct au_pin *pin) +{ + int err; -+ aufs_bindex_t bstart; ++ struct au_cpup_basic basic = { ++ .dentry = dentry, ++ .bdst = bdst, ++ .bsrc = -1, ++ .len = len ++ }; + struct au_dinfo *dinfo; + struct dentry *h_d_dst, *h_d_start; + struct au_hdentry *hdp; @@ -2606,30 +2795,30 @@ + dinfo = au_di(dentry); + AuRwMustWriteLock(&dinfo->di_rwsem); + -+ bstart = dinfo->di_bstart; ++ basic.bsrc = dinfo->di_bstart; + hdp = dinfo->di_hdentry; + h_d_dst = hdp[0 + bdst].hd_dentry; + dinfo->di_bstart = bdst; + hdp[0 + bdst].hd_dentry = wh_dentry; ++ h_d_start = NULL; + if (file) { -+ h_d_start = hdp[0 + bstart].hd_dentry; -+ hdp[0 + bstart].hd_dentry = au_hf_top(file)->f_dentry; ++ h_d_start = hdp[0 + basic.bsrc].hd_dentry; ++ hdp[0 + basic.bsrc].hd_dentry = au_hf_top(file)->f_dentry; + } -+ err = au_cpup_single(dentry, bdst, bstart, len, !AuCpup_DTIME, -+ /*h_parent*/NULL); ++ err = au_cpup_single(&basic, !AuCpup_DTIME, /*h_parent*/NULL, pin); + if (file) { + if (!err) + err = au_reopen_nondir(file); -+ hdp[0 + bstart].hd_dentry = h_d_start; ++ hdp[0 + basic.bsrc].hd_dentry = h_d_start; + } + hdp[0 + bdst].hd_dentry = h_d_dst; -+ dinfo->di_bstart = bstart; ++ dinfo->di_bstart = basic.bsrc; + + return err; +} + +static int au_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, loff_t len, -+ struct file *file) ++ struct file *file, struct au_pin *pin) +{ + int err; + struct au_dtime dt; @@ -2646,9 +2835,9 @@ + goto out; + + h_path.dentry = h_parent; -+ h_path.mnt = br->br_mnt; ++ h_path.mnt = au_br_mnt(br); + au_dtime_store(&dt, parent, &h_path); -+ err = au_do_cpup_wh(dentry, bdst, wh_dentry, file, len); ++ err = au_do_cpup_wh(dentry, bdst, wh_dentry, file, len, pin); + if (unlikely(err)) + goto out_wh; + @@ -2679,21 +2868,26 @@ + aufs_bindex_t bdst; + loff_t len; + struct file *file; ++ struct au_pin *pin; +}; + +static void au_call_cpup_wh(void *args) +{ + struct au_cpup_wh_args *a = args; -+ *a->errp = au_cpup_wh(a->dentry, a->bdst, a->len, a->file); ++ ++ au_pin_hdir_acquire_nest(a->pin); ++ *a->errp = au_cpup_wh(a->dentry, a->bdst, a->len, a->file, a->pin); ++ au_pin_hdir_release(a->pin); +} + +int au_sio_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, loff_t len, -+ struct file *file) ++ struct file *file, struct au_pin *pin) +{ + int err, wkq_err; + struct dentry *parent, *h_orph, *h_parent, *h_dentry; -+ struct inode *dir, *h_dir, *h_tmpdir, *h_inode; ++ struct inode *dir, *h_dir, *h_tmpdir; + struct au_wbr *wbr; ++ struct au_pin wh_pin; + + parent = dget_parent(dentry); + dir = parent->d_inode; @@ -2710,29 +2904,29 @@ + h_tmpdir = h_orph->d_inode; + au_set_h_iptr(dir, bdst, au_igrab(h_tmpdir), /*flags*/0); + -+ /* this temporary unlock is safe */ + if (file) + h_dentry = au_hf_top(file)->f_dentry; + else + h_dentry = au_h_dptr(dentry, au_dbstart(dentry)); -+ h_inode = h_dentry->d_inode; -+ IMustLock(h_inode); -+ mutex_unlock(&h_inode->i_mutex); + mutex_lock_nested(&h_tmpdir->i_mutex, AuLsc_I_PARENT3); -+ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); + /* todo: au_h_open_pre()? */ ++ ++ au_pin_init(&wh_pin, dentry, bdst, AuLsc_DI_PARENT, ++ AuLsc_I_PARENT3, pin->udba, AuPin_DI_LOCKED); ++ pin = &wh_pin; + } + + if (!au_test_h_perm_sio(h_tmpdir, MAY_EXEC | MAY_WRITE) + && !au_cpup_sio_test(dentry->d_sb, dentry->d_inode->i_mode)) -+ err = au_cpup_wh(dentry, bdst, len, file); ++ err = au_cpup_wh(dentry, bdst, len, file, pin); + else { + struct au_cpup_wh_args args = { + .errp = &err, + .dentry = dentry, + .bdst = bdst, + .len = len, -+ .file = file ++ .file = file, ++ .pin = pin + }; + wkq_err = au_wkq_wait(au_call_cpup_wh, &args); + if (unlikely(wkq_err)) @@ -2759,6 +2953,7 @@ +/* cf. revalidate function in file.c */ +int au_cp_dirs(struct dentry *dentry, aufs_bindex_t bdst, + int (*cp)(struct dentry *dentry, aufs_bindex_t bdst, ++ struct au_pin *pin, + struct dentry *h_parent, void *arg), + void *arg) +{ @@ -2804,7 +2999,7 @@ + au_pin_set_dentry(&pin, d); + err = au_do_pin(&pin); + if (!err) { -+ err = cp(d, bdst, h_parent, arg); ++ err = cp(d, bdst, &pin, h_parent, arg); + au_unpin(&pin); + } + } @@ -2821,10 +3016,11 @@ +} + +static int au_cpup_dir(struct dentry *dentry, aufs_bindex_t bdst, ++ struct au_pin *pin, + struct dentry *h_parent __maybe_unused , + void *arg __maybe_unused) +{ -+ return au_sio_cpup_simple(dentry, bdst, -1, AuCpup_DTIME); ++ return au_sio_cpup_simple(dentry, bdst, -1, AuCpup_DTIME, pin); +} + +int au_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst) @@ -2856,8 +3052,8 @@ + return err; +} --- a/fs/aufs/cpup.h 1970-01-01 01:00:00.000000000 +0100 -+++ b/fs/aufs/cpup.h 2013-03-10 01:48:58.459093058 +0000 -@@ -0,0 +1,81 @@ ++++ b/fs/aufs/cpup.h 2013-05-09 01:36:20.741181631 +0100 +@@ -0,0 +1,84 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -2889,8 +3085,9 @@ + +struct inode; +struct file; ++struct au_pin; + -+void au_cpup_attr_flags(struct inode *dst, struct inode *src); ++void au_cpup_attr_flags(struct inode *dst, unsigned int iflags); +void au_cpup_attr_timesizes(struct inode *inode); +void au_cpup_attr_nlink(struct inode *inode, int force); +void au_cpup_attr_changeable(struct inode *inode); @@ -2903,6 +3100,7 @@ +#define AuCpup_DTIME 1 /* do dtime_store/revert */ +#define AuCpup_KEEPLINO (1 << 1) /* do not clear the lower xino, + for link(2) */ ++#define AuCpup_RENAME (1 << 2) /* rename after cpup */ +#define au_ftest_cpup(flags, name) ((flags) & AuCpup_##name) +#define au_fset_cpup(flags, name) \ + do { (flags) |= AuCpup_##name; } while (0) @@ -2912,14 +3110,15 @@ +int au_copy_file(struct file *dst, struct file *src, loff_t len); +int au_sio_cpup_single(struct dentry *dentry, aufs_bindex_t bdst, + aufs_bindex_t bsrc, loff_t len, unsigned int flags, -+ struct dentry *dst_parent); ++ struct dentry *dst_parent, struct au_pin *pin); +int au_sio_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len, -+ unsigned int flags); ++ unsigned int flags, struct au_pin *pin); +int au_sio_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, loff_t len, -+ struct file *file); ++ struct file *file, struct au_pin *pin); + +int au_cp_dirs(struct dentry *dentry, aufs_bindex_t bdst, + int (*cp)(struct dentry *dentry, aufs_bindex_t bdst, ++ struct au_pin *pin, + struct dentry *h_parent, void *arg), + void *arg); +int au_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst); @@ -2940,8 +3139,8 @@ +#endif /* __KERNEL__ */ +#endif /* __AUFS_CPUP_H__ */ --- a/fs/aufs/dbgaufs.c 1970-01-01 01:00:00.000000000 +0100 -+++ b/fs/aufs/dbgaufs.c 2013-05-04 18:39:17.031157464 +0100 -@@ -0,0 +1,332 @@ ++++ b/fs/aufs/dbgaufs.c 2013-05-09 01:36:20.741181631 +0100 +@@ -0,0 +1,433 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -3040,6 +3239,101 @@ + +/* ---------------------------------------------------------------------- */ + ++struct dbgaufs_plink_arg { ++ int n; ++ char a[]; ++}; ++ ++static int dbgaufs_plink_release(struct inode *inode __maybe_unused, ++ struct file *file) ++{ ++ free_page((unsigned long)file->private_data); ++ return 0; ++} ++ ++static int dbgaufs_plink_open(struct inode *inode, struct file *file) ++{ ++ int err, i, limit; ++ unsigned long n, sum; ++ struct dbgaufs_plink_arg *p; ++ struct au_sbinfo *sbinfo; ++ struct super_block *sb; ++ struct au_sphlhead *sphl; ++ ++ err = -ENOMEM; ++ p = (void *)get_zeroed_page(GFP_NOFS); ++ if (unlikely(!p)) ++ goto out; ++ ++ err = -EFBIG; ++ sbinfo = inode->i_private; ++ sb = sbinfo->si_sb; ++ si_noflush_read_lock(sb); ++ if (au_opt_test(au_mntflags(sb), PLINK)) { ++ limit = PAGE_SIZE - sizeof(p->n); ++ ++ /* the number of buckets */ ++ n = snprintf(p->a + p->n, limit, "%d\n", AuPlink_NHASH); ++ p->n += n; ++ limit -= n; ++ ++ sum = 0; ++ for (i = 0, sphl = sbinfo->si_plink; ++ i < AuPlink_NHASH; ++ i++, sphl++) { ++ n = au_sphl_count(sphl); ++ sum += n; ++ ++ n = snprintf(p->a + p->n, limit, "%lu ", n); ++ p->n += n; ++ limit -= n; ++ if (unlikely(limit <= 0)) ++ goto out_free; ++ } ++ p->a[p->n - 1] = '\n'; ++ ++ /* the sum of plinks */ ++ n = snprintf(p->a + p->n, limit, "%lu\n", sum); ++ p->n += n; ++ limit -= n; ++ if (unlikely(limit <= 0)) ++ goto out_free; ++ } else { ++#define str "1\n0\n0\n" ++ p->n = sizeof(str) - 1; ++ strcpy(p->a, str); ++#undef str ++ } ++ si_read_unlock(sb); ++ ++ err = 0; ++ file->private_data = p; ++ goto out; /* success */ ++ ++out_free: ++ free_page((unsigned long)p); ++out: ++ return err; ++} ++ ++static ssize_t dbgaufs_plink_read(struct file *file, char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct dbgaufs_plink_arg *p; ++ ++ p = file->private_data; ++ return simple_read_from_buffer(buf, count, ppos, p->a, p->n); ++} ++ ++static const struct file_operations dbgaufs_plink_fop = { ++ .owner = THIS_MODULE, ++ .open = dbgaufs_plink_open, ++ .release = dbgaufs_plink_release, ++ .read = dbgaufs_plink_read ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ +static int dbgaufs_xib_open(struct inode *inode, struct file *file) +{ + int err; @@ -3247,6 +3541,12 @@ + if (unlikely(!sbinfo->si_dbgaufs_xib)) + goto out_dir; + ++ sbinfo->si_dbgaufs_plink = debugfs_create_file ++ ("plink", dbgaufs_mode, sbinfo->si_dbgaufs, sbinfo, ++ &dbgaufs_plink_fop); ++ if (unlikely(!sbinfo->si_dbgaufs_plink)) ++ goto out_dir; ++ + err = dbgaufs_xigen_init(sbinfo); + if (!err) + goto out; /* success */ @@ -3670,7 +3970,7 @@ +#endif /* __KERNEL__ */ +#endif /* __AUFS_DCSUB_H__ */ --- a/fs/aufs/debug.c 1970-01-01 01:00:00.000000000 +0100 -+++ b/fs/aufs/debug.c 2013-05-04 18:39:17.031157464 +0100 ++++ b/fs/aufs/debug.c 2013-05-09 01:36:20.741181631 +0100 @@ -0,0 +1,493 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima @@ -3931,7 +4231,7 @@ + + if (!br || IS_ERR(br)) + goto out; -+ mnt = br->br_mnt; ++ mnt = au_br_mnt(br); + if (!mnt || IS_ERR(mnt)) + goto out; + sb = mnt->mnt_sb; @@ -3972,7 +4272,7 @@ + + a->mnt.mnt_sb = sb; + a->fake.br_perm = 0; -+ a->fake.br_mnt = &a->mnt; ++ a->fake.br_path.mnt = &a->mnt; + a->fake.br_xino.xi_file = NULL; + atomic_set(&a->fake.br_count, 0); + smp_mb(); /* atomic_set */ @@ -4411,8 +4711,8 @@ +#endif /* __KERNEL__ */ +#endif /* __AUFS_DEBUG_H__ */ --- a/fs/aufs/dentry.c 1970-01-01 01:00:00.000000000 +0100 -+++ b/fs/aufs/dentry.c 2013-05-04 18:39:11.235157327 +0100 -@@ -0,0 +1,1060 @@ ++++ b/fs/aufs/dentry.c 2013-05-09 01:36:20.741181631 +0100 +@@ -0,0 +1,1064 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -4661,15 +4961,19 @@ +/* + * lookup @dentry on @bindex which should be negative. + */ -+int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex) ++int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex, int wh) +{ + int err; + struct dentry *parent, *h_parent, *h_dentry; ++ struct au_branch *br; + + parent = dget_parent(dentry); + h_parent = au_h_dptr(parent, bindex); -+ h_dentry = au_sio_lkup_one(&dentry->d_name, h_parent, -+ au_sbr(dentry->d_sb, bindex)); ++ br = au_sbr(dentry->d_sb, bindex); ++ if (wh) ++ h_dentry = au_whtmp_lkup(h_parent, br, &dentry->d_name); ++ else ++ h_dentry = au_sio_lkup_one(&dentry->d_name, h_parent, br); + err = PTR_ERR(h_dentry); + if (IS_ERR(h_dentry)) + goto out; @@ -5474,7 +5778,7 @@ + .d_release = aufs_d_release +}; --- a/fs/aufs/dentry.h 1970-01-01 01:00:00.000000000 +0100 -+++ b/fs/aufs/dentry.h 2013-05-04 18:39:11.235157327 +0100 ++++ b/fs/aufs/dentry.h 2013-05-09 01:36:20.741181631 +0100 @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima @@ -5530,7 +5834,7 @@ + struct dentry *h_parent, struct au_branch *br); + +int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type); -+int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex); ++int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex, int wh); +int au_refresh_dentry(struct dentry *dentry, struct dentry *parent); +int au_reval_dpath(struct dentry *dentry, unsigned int sigen); + @@ -7033,7 +7337,7 @@ +#endif /* __KERNEL__ */ +#endif /* __AUFS_DIR_H__ */ --- a/fs/aufs/dynop.c 1970-01-01 01:00:00.000000000 +0100 -+++ b/fs/aufs/dynop.c 2013-05-04 18:39:11.235157327 +0100 ++++ b/fs/aufs/dynop.c 2013-05-09 01:36:20.741181631 +0100 @@ -0,0 +1,379 @@ +/* + * Copyright (C) 2010-2013 Junjiro R. Okajima @@ -7276,7 +7580,7 @@ + + key->dk_op.dy_hop = op->dy_hop; + kref_init(&key->dk_kref); -+ p->set(key, op->dy_hop, br->br_mnt->mnt_sb); ++ p->set(key, op->dy_hop, au_br_sb(br)); + old = dy_gadd(spl, key); + if (old) { + kfree(key); @@ -7494,7 +7798,7 @@ +#endif /* __KERNEL__ */ +#endif /* __AUFS_DYNOP_H__ */ --- a/fs/aufs/export.c 1970-01-01 01:00:00.000000000 +0100 -+++ b/fs/aufs/export.c 2013-05-04 18:39:11.235157327 +0100 ++++ b/fs/aufs/export.c 2013-05-09 01:36:20.741181631 +0100 @@ -0,0 +1,827 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima @@ -8006,7 +8310,7 @@ + struct path path; + + br = au_sbr(sb, nsi_lock->bindex); -+ h_mnt = br->br_mnt; ++ h_mnt = au_br_mnt(br); + h_sb = h_mnt->mnt_sb; + /* todo: call lower fh_to_dentry()? fh_to_parent()? */ + h_parent = exportfs_decode_fh(h_mnt, (void *)(fh + Fh_tail), @@ -8230,7 +8534,7 @@ + + err = -EPERM; + br = au_sbr(sb, bindex); -+ h_sb = br->br_mnt->mnt_sb; ++ h_sb = au_br_sb(br); + if (unlikely(!h_sb->s_export_op)) { + AuErr1("%s branch is not exportable\n", au_sbtype(h_sb)); + goto out_hparent; @@ -9050,7 +9354,7 @@ +#endif +}; --- a/fs/aufs/f_op_sp.c 1970-01-01 01:00:00.000000000 +0100 -+++ b/fs/aufs/f_op_sp.c 2013-05-04 18:39:11.235157327 +0100 ++++ b/fs/aufs/f_op_sp.c 2013-05-09 01:36:20.745181631 +0100 @@ -0,0 +1,295 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima @@ -9250,7 +9554,7 @@ + err = au_pin(&pin, dentry, bcpup, au_opt_udba(dentry->d_sb), + AuPin_MNT_WRITE); + if (!err) { -+ err = au_sio_cpup_simple(dentry, bcpup, -1, AuCpup_DTIME); ++ err = au_sio_cpup_simple(dentry, bcpup, -1, AuCpup_DTIME, &pin); + au_unpin(&pin); + } + @@ -9348,8 +9652,8 @@ + return ret; +} --- a/fs/aufs/file.c 1970-01-01 01:00:00.000000000 +0100 -+++ b/fs/aufs/file.c 2013-05-04 18:39:11.235157327 +0100 -@@ -0,0 +1,683 @@ ++++ b/fs/aufs/file.c 2013-05-09 01:36:20.745181631 +0100 +@@ -0,0 +1,692 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -9419,7 +9723,7 @@ + br = au_sbr(sb, bindex); + h_file = ERR_PTR(-EACCES); + exec_flag = flags & __FMODE_EXEC; -+ if (exec_flag && (br->br_mnt->mnt_flags & MNT_NOEXEC)) ++ if (exec_flag && (au_br_mnt(br)->mnt_flags & MNT_NOEXEC)) + goto out; + + /* drop flags for writing */ @@ -9428,7 +9732,7 @@ + flags &= ~O_CREAT; + atomic_inc(&br->br_count); + h_path.dentry = h_dentry; -+ h_path.mnt = br->br_mnt; ++ h_path.mnt = au_br_mnt(br); + if (!au_special_file(h_inode->i_mode)) + h_file = vfsub_dentry_open(&h_path, flags); + else { @@ -9506,13 +9810,29 @@ + au_set_h_fptr(file, bstart, NULL); + } + AuDebugOn(au_fi(file)->fi_hdir); -+ AuDebugOn(au_fbstart(file) < bstart); ++ /* ++ * it can happen ++ * file exists on both of rw and ro ++ * open --> dbstart and fbstart are both 0 ++ * prepend a branch as rw, "rw" become ro ++ * remove rw/file ++ * delete the top branch, "rw" becomes rw again ++ * --> dbstart is 1, fbstart is still 0 ++ * write --> fbstart is 0 but dbstart is 1 ++ */ ++ /* AuDebugOn(au_fbstart(file) < bstart); */ + + h_file = au_h_open(dentry, bstart, vfsub_file_flags(file) & ~O_TRUNC, + file); + err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) ++ if (IS_ERR(h_file)) { ++ if (h_file_tmp) { ++ atomic_inc(&au_sbr(dentry->d_sb, bstart)->br_count); ++ au_set_h_fptr(file, bstart, h_file_tmp); ++ h_file_tmp = NULL; ++ } + goto out; /* todo: close all? */ ++ } + + err = 0; + au_set_fbstart(file, bstart); @@ -9554,7 +9874,7 @@ +} + +static int au_ready_to_write_wh(struct file *file, loff_t len, -+ aufs_bindex_t bcpup) ++ aufs_bindex_t bcpup, struct au_pin *pin) +{ + int err; + struct inode *inode, *h_inode; @@ -9571,7 +9891,7 @@ + } + hi_wh = au_hi_wh(inode, bcpup); + if (!hi_wh && !h_inode) -+ err = au_sio_cpup_wh(dentry, bcpup, len, file); ++ err = au_sio_cpup_wh(dentry, bcpup, len, file, pin); + else + /* already copied-up after unlink */ + err = au_reopen_wh(file, bcpup, hi_wh); @@ -9592,7 +9912,7 @@ + int err; + aufs_bindex_t bstart, bcpup, dbstart; + struct dentry *dentry, *parent, *h_dentry; -+ struct inode *h_inode, *inode; ++ struct inode *inode; + struct super_block *sb; + struct file *h_file; + @@ -9628,43 +9948,35 @@ + goto out_dgrade; + + h_dentry = au_hf_top(file)->f_dentry; -+ h_inode = h_dentry->d_inode; + dbstart = au_dbstart(dentry); + if (dbstart <= bcpup) { + h_dentry = au_h_dptr(dentry, bcpup); + AuDebugOn(!h_dentry); -+ h_inode = h_dentry->d_inode; -+ AuDebugOn(!h_inode); + bstart = bcpup; + } + + if (dbstart <= bcpup /* just reopen */ + || !d_unhashed(dentry) /* copyup and reopen */ + ) { -+ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); + h_file = au_h_open_pre(dentry, bstart); -+ if (IS_ERR(h_file)) { ++ if (IS_ERR(h_file)) + err = PTR_ERR(h_file); -+ h_file = NULL; -+ } else { ++ else { + di_downgrade_lock(parent, AuLock_IR); + if (dbstart > bcpup) + err = au_sio_cpup_simple(dentry, bcpup, len, -+ AuCpup_DTIME); ++ AuCpup_DTIME, pin); + if (!err) + err = au_reopen_nondir(file); ++ au_h_open_post(dentry, bstart, h_file); + } -+ mutex_unlock(&h_inode->i_mutex); -+ au_h_open_post(dentry, bstart, h_file); + } else { /* copyup as wh and reopen */ + /* + * since writable hfsplus branch is not supported, + * h_open_pre/post() are unnecessary. + */ -+ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); -+ err = au_ready_to_write_wh(file, len, bcpup); ++ err = au_ready_to_write_wh(file, len, bcpup, pin); + di_downgrade_lock(parent, AuLock_IR); -+ mutex_unlock(&h_inode->i_mutex); + } + + if (!err) { @@ -9749,7 +10061,8 @@ + if (!S_ISDIR(inode->i_mode) + && au_opt_test(au_mntflags(sb), PLINK) + && au_plink_test(inode) -+ && !d_unhashed(dentry)) { ++ && !d_unhashed(dentry) ++ && bstart < au_dbstart(dentry)) { + err = au_test_and_cpup_dirs(dentry, bstart); + if (unlikely(err)) + goto out_unlock; @@ -9759,7 +10072,7 @@ + AuPin_DI_LOCKED | AuPin_MNT_WRITE); + if (!err) + err = au_sio_cpup_simple(dentry, bstart, -1, -+ AuCpup_DTIME); ++ AuCpup_DTIME, &pin); + au_unpin(&pin); + } else if (hi_wh) { + /* already copied-up after unlink */ @@ -10495,8 +10808,8 @@ + return err; +} --- a/fs/aufs/fstype.h 1970-01-01 01:00:00.000000000 +0100 -+++ b/fs/aufs/fstype.h 2013-05-04 18:39:11.235157327 +0100 -@@ -0,0 +1,481 @@ ++++ b/fs/aufs/fstype.h 2013-05-09 01:36:20.745181631 +0100 +@@ -0,0 +1,480 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -10910,7 +11223,6 @@ + || au_test_ramfs(sb) +#endif + || au_test_ubifs(sb) -+ || au_test_btrfs(sb) + || au_test_hfsplus(sb); +} + @@ -11278,8 +11590,8 @@ + .init_br = au_hfsn_init_br +}; --- a/fs/aufs/hfsplus.c 1970-01-01 01:00:00.000000000 +0100 -+++ b/fs/aufs/hfsplus.c 2013-03-10 01:48:58.463093058 +0000 -@@ -0,0 +1,57 @@ ++++ b/fs/aufs/hfsplus.c 2013-05-09 01:36:20.745181631 +0100 +@@ -0,0 +1,56 @@ +/* + * Copyright (C) 2010-2013 Junjiro R. Okajima + * @@ -11318,7 +11630,6 @@ + h_dentry = au_h_dptr(dentry, bindex); + AuDebugOn(!h_dentry); + AuDebugOn(!h_dentry->d_inode); -+ IMustLock(h_dentry->d_inode); + + h_file = NULL; + if (au_test_hfsplus(h_dentry->d_sb) @@ -12054,8 +12365,8 @@ + au_hn_destroy_cache(); +} --- a/fs/aufs/i_op.c 1970-01-01 01:00:00.000000000 +0100 -+++ b/fs/aufs/i_op.c 2013-05-04 18:39:11.235157327 +0100 -@@ -0,0 +1,1030 @@ ++++ b/fs/aufs/i_op.c 2013-05-09 01:36:20.745181631 +0100 +@@ -0,0 +1,1107 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -12172,7 +12483,7 @@ + err = 0; + bindex = au_ibstart(inode); + br = au_sbr(sb, bindex); -+ err = h_permission(h_inode, mask, br->br_mnt, br->br_perm); ++ err = h_permission(h_inode, mask, au_br_mnt(br), br->br_perm); + if (write_mask + && !err + && !special_file(h_inode->i_mode)) { @@ -12198,7 +12509,7 @@ + break; + + br = au_sbr(sb, bindex); -+ err = h_permission(h_inode, mask, br->br_mnt, ++ err = h_permission(h_inode, mask, au_br_mnt(br), + br->br_perm); + } + } @@ -12327,7 +12638,8 @@ + h_parent = au_h_dptr(parent, bcpup); + h_dir = h_parent->d_inode; + mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT); -+ err = au_lkup_neg(dentry, bcpup); ++ err = au_lkup_neg(dentry, bcpup, ++ au_ftest_wrdir(add_entry, TMP_WHENTRY)); + /* todo: no unlock here */ + mutex_unlock(&h_dir->i_mutex); + @@ -12358,8 +12670,9 @@ +{ + int err; + aufs_bindex_t bcpup, bstart, src_bstart; -+ const unsigned char add_entry = !!au_ftest_wrdir(args->flags, -+ ADD_ENTRY); ++ const unsigned char add_entry ++ = au_ftest_wrdir(args->flags, ADD_ENTRY) ++ | au_ftest_wrdir(args->flags, TMP_WHENTRY); + struct super_block *sb; + struct dentry *parent; + struct au_sbinfo *sbinfo; @@ -12423,6 +12736,85 @@ + +/* ---------------------------------------------------------------------- */ + ++void au_pin_hdir_unlock(struct au_pin *p) ++{ ++ if (p->hdir) ++ au_hn_imtx_unlock(p->hdir); ++} ++ ++static int au_pin_hdir_lock(struct au_pin *p) ++{ ++ int err; ++ ++ err = 0; ++ if (!p->hdir) ++ goto out; ++ ++ /* even if an error happens later, keep this lock */ ++ au_hn_imtx_lock_nested(p->hdir, p->lsc_hi); ++ ++ err = -EBUSY; ++ if (unlikely(p->hdir->hi_inode != p->h_parent->d_inode)) ++ goto out; ++ ++ err = 0; ++ if (p->h_dentry) ++ err = au_h_verify(p->h_dentry, p->udba, p->hdir->hi_inode, ++ p->h_parent, p->br); ++ ++out: ++ return err; ++} ++ ++int au_pin_hdir_relock(struct au_pin *p) ++{ ++ int err, i; ++ struct inode *h_i; ++ struct dentry *h_d[] = { ++ p->h_dentry, ++ p->h_parent ++ }; ++ ++ err = au_pin_hdir_lock(p); ++ if (unlikely(err)) ++ goto out; ++ ++ for (i = 0; !err && i < sizeof(h_d)/sizeof(*h_d); i++) { ++ if (!h_d[i]) ++ continue; ++ h_i = h_d[i]->d_inode; ++ if (h_i) ++ err = !h_i->i_nlink; ++ } ++ ++out: ++ return err; ++} ++ ++void au_pin_hdir_set_owner(struct au_pin *p, struct task_struct *task) ++{ ++#if defined(CONFIG_DEBUG_MUTEXES) || defined(CONFIG_SMP) ++ p->hdir->hi_inode->i_mutex.owner = task; ++#endif ++} ++ ++void au_pin_hdir_acquire_nest(struct au_pin *p) ++{ ++ if (p->hdir) { ++ mutex_acquire_nest(&p->hdir->hi_inode->i_mutex.dep_map, ++ p->lsc_hi, 0, NULL, _RET_IP_); ++ au_pin_hdir_set_owner(p, current); ++ } ++} ++ ++void au_pin_hdir_release(struct au_pin *p) ++{ ++ if (p->hdir) { ++ au_pin_hdir_set_owner(p, p->task); ++ mutex_release(&p->hdir->hi_inode->i_mutex.dep_map, 1, _RET_IP_); ++ } ++} ++ +struct dentry *au_pinned_h_parent(struct au_pin *pin) +{ + if (pin && pin->parent) @@ -12432,12 +12824,13 @@ + +void au_unpin(struct au_pin *p) +{ ++ if (p->hdir) ++ au_pin_hdir_unlock(p); + if (p->h_mnt && au_ftest_pin(p->flags, MNT_WRITE)) + vfsub_mnt_drop_write(p->h_mnt); + if (!p->hdir) + return; + -+ au_hn_imtx_unlock(p->hdir); + if (!au_ftest_pin(p->flags, DI_LOCKED)) + di_read_unlock(p->parent, AuLock_IR); + iput(p->hdir->hi_inode); @@ -12445,22 +12838,21 @@ + p->parent = NULL; + p->hdir = NULL; + p->h_mnt = NULL; ++ /* do not clear p->task */ +} + +int au_do_pin(struct au_pin *p) +{ + int err; + struct super_block *sb; -+ struct dentry *h_dentry, *h_parent; -+ struct au_branch *br; + struct inode *h_dir; + + err = 0; + sb = p->dentry->d_sb; -+ br = au_sbr(sb, p->bindex); ++ p->br = au_sbr(sb, p->bindex); + if (IS_ROOT(p->dentry)) { + if (au_ftest_pin(p->flags, MNT_WRITE)) { -+ p->h_mnt = br->br_mnt; ++ p->h_mnt = au_br_mnt(p->br); + err = vfsub_mnt_want_write(p->h_mnt); + if (unlikely(err)) { + au_fclr_pin(p->flags, MNT_WRITE); @@ -12470,16 +12862,16 @@ + goto out; + } + -+ h_dentry = NULL; ++ p->h_dentry = NULL; + if (p->bindex <= au_dbend(p->dentry)) -+ h_dentry = au_h_dptr(p->dentry, p->bindex); ++ p->h_dentry = au_h_dptr(p->dentry, p->bindex); + + p->parent = dget_parent(p->dentry); + if (!au_ftest_pin(p->flags, DI_LOCKED)) + di_read_lock(p->parent, AuLock_IR, p->lsc_di); + + h_dir = NULL; -+ h_parent = au_h_dptr(p->parent, p->bindex); ++ p->h_parent = au_h_dptr(p->parent, p->bindex); + p->hdir = au_hi(p->parent->d_inode, p->bindex); + if (p->hdir) + h_dir = p->hdir->hi_inode; @@ -12489,7 +12881,7 @@ + * if DI_LOCKED is not set, then p->parent may be different + * and h_parent can be NULL. + */ -+ if (unlikely(!p->hdir || !h_dir || !h_parent)) { ++ if (unlikely(!p->hdir || !h_dir || !p->h_parent)) { + err = -EBUSY; + if (!au_ftest_pin(p->flags, DI_LOCKED)) + di_read_unlock(p->parent, AuLock_IR); @@ -12498,33 +12890,24 @@ + goto out_err; + } + -+ au_igrab(h_dir); -+ au_hn_imtx_lock_nested(p->hdir, p->lsc_hi); -+ -+ if (unlikely(p->hdir->hi_inode != h_parent->d_inode)) { -+ err = -EBUSY; -+ goto out_unpin; -+ } -+ if (h_dentry) { -+ err = au_h_verify(h_dentry, p->udba, h_dir, h_parent, br); -+ if (unlikely(err)) { -+ au_fclr_pin(p->flags, MNT_WRITE); -+ goto out_unpin; -+ } -+ } -+ + if (au_ftest_pin(p->flags, MNT_WRITE)) { -+ p->h_mnt = br->br_mnt; ++ p->h_mnt = au_br_mnt(p->br); + err = vfsub_mnt_want_write(p->h_mnt); + if (unlikely(err)) { + au_fclr_pin(p->flags, MNT_WRITE); -+ goto out_unpin; ++ if (!au_ftest_pin(p->flags, DI_LOCKED)) ++ di_read_unlock(p->parent, AuLock_IR); ++ dput(p->parent); ++ p->parent = NULL; ++ goto out_err; + } + } -+ goto out; /* success */ + -+out_unpin: -+ au_unpin(p); ++ au_igrab(h_dir); ++ err = au_pin_hdir_lock(p); ++ if (!err) ++ goto out; /* success */ ++ +out_err: + pr_err("err %d\n", err); + err = au_busy_or_stale(); @@ -12546,6 +12929,11 @@ + p->parent = NULL; + p->hdir = NULL; + p->h_mnt = NULL; ++ ++ p->h_dentry = NULL; ++ p->h_parent = NULL; ++ p->br = NULL; ++ p->task = current; +} + +int au_pin(struct au_pin *pin, struct dentry *dentry, aufs_bindex_t bindex, @@ -12651,13 +13039,15 @@ + sz = -1; + if ((ia->ia_valid & ATTR_SIZE) && ia->ia_size < i_size_read(a->h_inode)) + sz = ia->ia_size; ++ mutex_unlock(&a->h_inode->i_mutex); + + h_file = NULL; + hi_wh = NULL; + if (au_ftest_icpup(a->flags, DID_CPUP) && d_unlinked(dentry)) { + hi_wh = au_hi_wh(inode, a->btgt); + if (!hi_wh) { -+ err = au_sio_cpup_wh(dentry, a->btgt, sz, /*file*/NULL); ++ err = au_sio_cpup_wh(dentry, a->btgt, sz, /*file*/NULL, ++ &a->pin); + if (unlikely(err)) + goto out_unlock; + hi_wh = au_hi_wh(inode, a->btgt); @@ -12676,12 +13066,13 @@ + + if (!d_unhashed(dentry)) { + h_file = au_h_open_pre(dentry, bstart); -+ if (IS_ERR(h_file)) { ++ if (IS_ERR(h_file)) + err = PTR_ERR(h_file); -+ h_file = NULL; -+ } else ++ else { + err = au_sio_cpup_simple(dentry, a->btgt, sz, -+ AuCpup_DTIME); ++ AuCpup_DTIME, &a->pin); ++ au_h_open_post(dentry, bstart, h_file); ++ } + if (!err) + a->h_path.dentry = au_h_dptr(dentry, a->btgt); + } else if (!hi_wh) @@ -12690,14 +13081,9 @@ + a->h_path.dentry = hi_wh; /* do not dget here */ + +out_unlock: -+ mutex_unlock(&a->h_inode->i_mutex); -+ au_h_open_post(dentry, bstart, h_file); + a->h_inode = a->h_path.dentry->d_inode; -+ if (!err) { -+ mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD); ++ if (!err) + goto out; /* success */ -+ } -+ + au_unpin(&a->pin); +out_parent: + if (parent) { @@ -12705,6 +13091,8 @@ + dput(parent); + } +out: ++ if (!err) ++ mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD); + return err; +} + @@ -12773,8 +13161,7 @@ + goto out_unlock; + } else if ((ia->ia_valid & (ATTR_UID | ATTR_GID)) + && (ia->ia_valid & ATTR_CTIME)) { -+ err = security_path_chown(&a->h_path, vfsub_ia_uid(ia), -+ vfsub_ia_gid(ia)); ++ err = security_path_chown(&a->h_path, ia->ia_uid, ia->ia_gid); + if (unlikely(err)) + goto out_unlock; + } @@ -12824,8 +13211,9 @@ + unsigned int n; + + inode->i_mode = st->mode; -+ i_uid_write(inode, st->uid); -+ i_gid_write(inode, st->gid); ++ /* don't i_[ug]id_write() here */ ++ inode->i_uid = st->uid; ++ inode->i_gid = st->gid; + inode->i_atime = st->atime; + inode->i_mtime = st->mtime; + inode->i_ctime = st->ctime; @@ -13087,8 +13475,8 @@ + .update_time = aufs_update_time +}; --- a/fs/aufs/i_op_add.c 1970-01-01 01:00:00.000000000 +0100 -+++ b/fs/aufs/i_op_add.c 2013-05-04 18:39:11.235157327 +0100 -@@ -0,0 +1,713 @@ ++++ b/fs/aufs/i_op_add.c 2013-05-09 01:36:20.745181631 +0100 +@@ -0,0 +1,722 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -13281,7 +13669,7 @@ + if (dt) { + struct path tmp = { + .dentry = h_parent, -+ .mnt = br->br_mnt ++ .mnt = au_br_mnt(br) + }; + au_dtime_store(dt, au_pinned_parent(pin), &tmp); + } @@ -13450,7 +13838,6 @@ +{ + int err; + struct dentry *h_src_dentry; -+ struct mutex *h_mtx; + struct file *h_file; + + di_read_lock_parent(a->src_parent, AuLock_IR); @@ -13459,22 +13846,20 @@ + goto out; + + h_src_dentry = au_h_dptr(src_dentry, a->bsrc); -+ h_mtx = &h_src_dentry->d_inode->i_mutex; + err = au_pin(&a->pin, src_dentry, a->bdst, + au_opt_udba(src_dentry->d_sb), + AuPin_DI_LOCKED | AuPin_MNT_WRITE); + if (unlikely(err)) + goto out; -+ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); + h_file = au_h_open_pre(src_dentry, a->bsrc); -+ if (IS_ERR(h_file)) { ++ if (IS_ERR(h_file)) + err = PTR_ERR(h_file); -+ h_file = NULL; -+ } else ++ else { + err = au_sio_cpup_simple(src_dentry, a->bdst, -1, -+ AuCpup_DTIME /* | AuCpup_KEEPLINO */); -+ mutex_unlock(h_mtx); -+ au_h_open_post(src_dentry, a->bsrc, h_file); ++ AuCpup_DTIME /* | AuCpup_KEEPLINO */, ++ &a->pin); ++ au_h_open_post(src_dentry, a->bsrc, h_file); ++ } + au_unpin(&a->pin); + +out: @@ -13482,12 +13867,14 @@ + return err; +} + -+static int au_cpup_or_link(struct dentry *src_dentry, struct au_link_args *a) ++static int au_cpup_or_link(struct dentry *src_dentry, struct dentry *dentry, ++ struct au_link_args *a) +{ + int err; + unsigned char plink; -+ struct inode *h_inode, *inode; ++ aufs_bindex_t bend; + struct dentry *h_src_dentry; ++ struct inode *h_inode, *inode; + struct super_block *sb; + struct file *h_file; + @@ -13499,22 +13886,31 @@ + h_inode = au_h_iptr(inode, a->bdst); + if (!h_inode || !h_inode->i_nlink) { + /* copyup src_dentry as the name of dentry. */ -+ au_set_dbstart(src_dentry, a->bdst); -+ au_set_h_dptr(src_dentry, a->bdst, dget(a->h_path.dentry)); -+ h_inode = au_h_dptr(src_dentry, a->bsrc)->d_inode; -+ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); -+ h_file = au_h_open_pre(src_dentry, a->bsrc); -+ if (IS_ERR(h_file)) { ++ bend = au_dbend(dentry); ++ if (bend < a->bsrc) ++ au_set_dbend(dentry, a->bsrc); ++ au_set_h_dptr(dentry, a->bsrc, ++ dget(au_h_dptr(src_dentry, a->bsrc))); ++ dget(a->h_path.dentry); ++ au_set_h_dptr(dentry, a->bdst, NULL); ++ dentry->d_inode = src_dentry->d_inode; /* tmp */ ++ h_file = au_h_open_pre(dentry, a->bsrc); ++ if (IS_ERR(h_file)) + err = PTR_ERR(h_file); -+ h_file = NULL; -+ } else -+ err = au_sio_cpup_single(src_dentry, a->bdst, a->bsrc, -+ -1, AuCpup_KEEPLINO, -+ a->parent); -+ mutex_unlock(&h_inode->i_mutex); -+ au_h_open_post(src_dentry, a->bsrc, h_file); -+ au_set_h_dptr(src_dentry, a->bdst, NULL); -+ au_set_dbstart(src_dentry, a->bsrc); ++ else { ++ err = au_sio_cpup_simple(dentry, a->bdst, -1, ++ AuCpup_KEEPLINO, &a->pin); ++ au_h_open_post(dentry, a->bsrc, h_file); ++ if (!err) { ++ dput(a->h_path.dentry); ++ a->h_path.dentry = au_h_dptr(dentry, a->bdst); ++ } else ++ au_set_h_dptr(dentry, a->bdst, ++ a->h_path.dentry); ++ } ++ dentry->d_inode = NULL; /* restore */ ++ au_set_h_dptr(dentry, a->bsrc, NULL); ++ au_set_dbend(dentry, bend); + } else { + /* the inode of src_dentry already exists on a.bdst branch */ + h_src_dentry = d_find_alias(h_inode); @@ -13613,7 +14009,7 @@ + if (au_opt_test(au_mntflags(sb), PLINK)) { + if (a->bdst < a->bsrc + /* && h_src_dentry->d_sb != a->h_path.dentry->d_sb */) -+ err = au_cpup_or_link(src_dentry, a); ++ err = au_cpup_or_link(src_dentry, dentry, a); + else + err = vfsub_link(h_src_dentry, au_pinned_h_dir(&a->pin), + &a->h_path); @@ -13692,6 +14088,7 @@ +out_kfree: + kfree(a); +out: ++ AuTraceErr(err); + return err; +} + @@ -14283,8 +14680,8 @@ + return err; +} --- a/fs/aufs/i_op_ren.c 1970-01-01 01:00:00.000000000 +0100 -+++ b/fs/aufs/i_op_ren.c 2013-05-04 18:39:11.235157327 +0100 -@@ -0,0 +1,1026 @@ ++++ b/fs/aufs/i_op_ren.c 2013-05-09 01:36:20.745181631 +0100 +@@ -0,0 +1,1053 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -14496,22 +14893,22 @@ + err = vfsub_rename(a->src_h_dir, au_h_dptr(d, a->btgt), + a->dst_h_dir, &a->h_path); + } else { -+ struct mutex *h_mtx = &a->src_h_dentry->d_inode->i_mutex; ++#if 1 ++ BUG(); ++#else + struct file *h_file; + + au_fset_ren(a->flags, CPUP); -+ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); + au_set_dbstart(d, a->btgt); + au_set_h_dptr(d, a->btgt, dget(a->dst_h_dentry)); + h_file = au_h_open_pre(d, a->src_bstart); -+ if (IS_ERR(h_file)) { ++ if (IS_ERR(h_file)) + err = PTR_ERR(h_file); -+ h_file = NULL; -+ } else ++ else { + err = au_sio_cpup_single(d, a->btgt, a->src_bstart, -1, + !AuCpup_DTIME, a->dst_parent); -+ mutex_unlock(h_mtx); -+ au_h_open_post(d, a->src_bstart, h_file); ++ au_h_open_post(d, a->src_bstart, h_file); ++ } + if (!err) { + d = a->dst_dentry; + au_set_h_dptr(d, a->btgt, NULL); @@ -14520,6 +14917,7 @@ + au_set_h_dptr(d, a->btgt, NULL); + au_set_dbstart(d, a->src_bstart); + } ++#endif + } + if (!err && a->h_dst) + /* it will be set to dinfo later */ @@ -14621,7 +15019,7 @@ + + d = a->dst_dentry; + au_set_h_dptr(d, a->btgt, NULL); -+ err = au_lkup_neg(d, a->btgt); ++ err = au_lkup_neg(d, a->btgt, /*wh*/0); + if (unlikely(err)) + goto out_whtmp; + a->dst_h_dentry = au_h_dptr(d, a->btgt); @@ -14629,22 +15027,22 @@ + + /* cpup src */ + if (a->dst_h_dentry->d_inode && a->src_bstart != a->btgt) { -+ struct mutex *h_mtx = &a->src_h_dentry->d_inode->i_mutex; ++#if 1 ++ BUG(); ++#else + struct file *h_file; + -+ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); -+ AuDebugOn(au_dbstart(a->src_dentry) != a->src_bstart); + h_file = au_h_open_pre(a->src_dentry, a->src_bstart); -+ if (IS_ERR(h_file)) { ++ if (IS_ERR(h_file)) + err = PTR_ERR(h_file); -+ h_file = NULL; -+ } else ++ else { + err = au_sio_cpup_simple(a->src_dentry, a->btgt, -1, + !AuCpup_DTIME); -+ mutex_unlock(h_mtx); -+ au_h_open_post(a->src_dentry, a->src_bstart, h_file); ++ au_h_open_post(a->src_dentry, a->src_bstart, h_file); ++ } + if (unlikely(err)) + goto out_whtmp; ++#endif + } + + /* rename by vfs_rename or cpup */ @@ -14897,13 +15295,10 @@ + */ +static void au_ren_unlock(struct au_ren_args *a) +{ -+ struct super_block *sb; -+ -+ sb = a->dst_dentry->d_sb; -+ if (au_ftest_ren(a->flags, MNT_WRITE)) -+ vfsub_mnt_drop_write(a->br->br_mnt); + vfsub_unlock_rename(a->src_h_parent, a->src_hdir, + a->dst_h_parent, a->dst_hdir); ++ if (au_ftest_ren(a->flags, MNT_WRITE)) ++ vfsub_mnt_drop_write(au_br_mnt(a->br)); +} + +static int au_ren_lock(struct au_ren_args *a) @@ -14916,6 +15311,11 @@ + a->src_hdir = au_hi(a->src_dir, a->btgt); + a->dst_h_parent = au_h_dptr(a->dst_parent, a->btgt); + a->dst_hdir = au_hi(a->dst_dir, a->btgt); ++ ++ err = vfsub_mnt_want_write(au_br_mnt(a->br)); ++ if (unlikely(err)) ++ goto out; ++ au_fset_ren(a->flags, MNT_WRITE); + a->h_trap = vfsub_lock_rename(a->src_h_parent, a->src_hdir, + a->dst_h_parent, a->dst_hdir); + udba = au_opt_udba(a->src_dentry->d_sb); @@ -14930,18 +15330,12 @@ + err = au_h_verify(a->dst_h_dentry, udba, + a->dst_h_parent->d_inode, a->dst_h_parent, + a->br); -+ if (!err) { -+ err = vfsub_mnt_want_write(a->br->br_mnt); -+ if (unlikely(err)) -+ goto out_unlock; -+ au_fset_ren(a->flags, MNT_WRITE); ++ if (!err) + goto out; /* success */ -+ } + + err = au_busy_or_stale(); -+ -+out_unlock: + au_ren_unlock(a); ++ +out: + return err; +} @@ -15211,7 +15605,7 @@ + if (unlikely(err < 0)) + goto out_parent; + a->br = au_sbr(a->dst_dentry->d_sb, a->btgt); -+ a->h_path.mnt = a->br->br_mnt; ++ a->h_path.mnt = au_br_mnt(a->br); + + /* are they available to be renamed */ + err = au_ren_may_dir(a); @@ -15249,9 +15643,39 @@ + if (err) + au_fset_ren(a->flags, WHSRC); + ++ /* cpup src */ ++ if (a->src_bstart != a->btgt) { ++ struct file *h_file; ++ struct au_pin pin; ++ ++ err = au_pin(&pin, a->src_dentry, a->btgt, ++ au_opt_udba(a->src_dentry->d_sb), ++ AuPin_DI_LOCKED | AuPin_MNT_WRITE); ++ if (unlikely(err)) ++ goto out_children; ++ ++ AuDebugOn(au_dbstart(a->src_dentry) != a->src_bstart); ++ h_file = au_h_open_pre(a->src_dentry, a->src_bstart); ++ if (IS_ERR(h_file)) { ++ err = PTR_ERR(h_file); ++ h_file = NULL; ++ } else { ++ err = au_sio_cpup_simple(a->src_dentry, a->btgt, -1, ++ AuCpup_DTIME, &pin); ++ au_h_open_post(a->src_dentry, a->src_bstart, h_file); ++ } ++ au_unpin(&pin); ++ if (unlikely(err)) ++ goto out_children; ++ a->src_bstart = a->btgt; ++ a->src_h_dentry = au_h_dptr(a->src_dentry, a->btgt); ++ au_fset_ren(a->flags, WHSRC); ++ } ++ + /* lock them all */ + err = au_ren_lock(a); + if (unlikely(err)) ++ /* leave the copied-up one */ + goto out_children; + + if (!au_opt_test(au_mntflags(a->dst_dir->i_sb), UDBA_NONE)) @@ -16086,8 +16510,8 @@ + return au_test_h_perm(h_inode, mask); +} --- a/fs/aufs/inode.h 1970-01-01 01:00:00.000000000 +0100 -+++ b/fs/aufs/inode.h 2013-05-04 18:39:11.239157326 +0100 -@@ -0,0 +1,588 @@ ++++ b/fs/aufs/inode.h 2013-05-09 01:36:20.745181631 +0100 +@@ -0,0 +1,600 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -16191,8 +16615,19 @@ + struct dentry *parent; + struct au_hinode *hdir; + struct vfsmount *h_mnt; ++ ++ /* temporary unlock/relock for copyup */ ++ struct dentry *h_dentry, *h_parent; ++ struct au_branch *br; ++ struct task_struct *task; +}; + ++void au_pin_hdir_unlock(struct au_pin *p); ++int au_pin_hdir_relock(struct au_pin *p); ++void au_pin_hdir_set_owner(struct au_pin *p, struct task_struct *task); ++void au_pin_hdir_acquire_nest(struct au_pin *p); ++void au_pin_hdir_release(struct au_pin *p); ++ +/* ---------------------------------------------------------------------- */ + +static inline struct au_iinfo *au_ii(struct inode *inode) @@ -16234,7 +16669,8 @@ + +/* au_wr_dir flags */ +#define AuWrDir_ADD_ENTRY 1 -+#define AuWrDir_ISDIR (1 << 1) ++#define AuWrDir_TMP_WHENTRY (1 << 1) ++#define AuWrDir_ISDIR (1 << 2) +#define au_ftest_wrdir(flags, name) ((flags) & AuWrDir_##name) +#define au_fset_wrdir(flags, name) \ + do { (flags) |= AuWrDir_##name; } while (0) @@ -17437,8 +17873,8 @@ +#endif /* __KERNEL__ */ +#endif /* __AUFS_MODULE_H__ */ --- a/fs/aufs/opts.c 1970-01-01 01:00:00.000000000 +0100 -+++ b/fs/aufs/opts.c 2013-03-10 01:48:58.463093058 +0000 -@@ -0,0 +1,1677 @@ ++++ b/fs/aufs/opts.c 2013-05-09 01:36:20.745181631 +0100 +@@ -0,0 +1,1697 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -17627,17 +18063,16 @@ + {0, NULL} +}; + -+static match_table_t brrattr = { ++static match_table_t brattr = { ++ {AuBrAttr_UNPIN, AUFS_BRATTR_UNPIN}, + {AuBrRAttr_WH, AUFS_BRRATTR_WH}, -+ {0, NULL} -+}; -+ -+static match_table_t brwattr = { + {AuBrWAttr_NoLinkWH, AUFS_BRWATTR_NLWH}, + {0, NULL} +}; + -+#define AuBrStr_LONGEST AUFS_BRPERM_RW "+" AUFS_BRWATTR_NLWH ++#define AuBrStr_LONGEST AUFS_BRPERM_RW \ ++ "+" AUFS_BRATTR_UNPIN \ ++ "+" AUFS_BRWATTR_NLWH + +static int br_attr_val(char *str, match_table_t table, substring_t args[]) +{ @@ -17668,7 +18103,7 @@ +static int noinline_for_stack br_perm_val(char *perm) +{ + int val; -+ char *p; ++ char *p, *q; + substring_t args[MAX_OPT_ARGS]; + + p = strchr(perm, '+'); @@ -17685,13 +18120,33 @@ + if (!p) + goto out; + -+ switch (val) { ++ p++; ++ while (1) { ++ q = strchr(p, '+'); ++ if (q) ++ *q = 0; ++ val |= br_attr_val(p, brattr, args); ++ if (q) { ++ *q = '+'; ++ p = q + 1; ++ } else ++ break; ++ } ++ switch (val & AuBrPerm_Mask) { + case AuBrPerm_RO: + case AuBrPerm_RR: -+ val |= br_attr_val(p + 1, brrattr, args); ++ if (unlikely(val & AuBrWAttr_NoLinkWH)) { ++ pr_warn("ignored branch attribute %s\n", ++ AUFS_BRWATTR_NLWH); ++ val &= ~AuBrWAttr_NoLinkWH; ++ } + break; + case AuBrPerm_RW: -+ val |= br_attr_val(p + 1, brwattr, args); ++ if (unlikely(val & AuBrRAttr_WH)) { ++ pr_warn("ignored branch attribute %s\n", ++ AUFS_BRRATTR_WH); ++ val &= ~AuBrRAttr_WH; ++ } + break; + } + @@ -17734,6 +18189,7 @@ + AuDebugOn(1); + } + ++ AppendAttr(AuBrAttr_UNPIN, AUFS_BRATTR_UNPIN); + AppendAttr(AuBrRAttr_WH, AUFS_BRRATTR_WH); + AppendAttr(AuBrWAttr_NoLinkWH, AUFS_BRWATTR_NLWH); + @@ -18959,7 +19415,7 @@ + au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); + if (wbr) + wbr_wh_write_lock(wbr); -+ err = au_wh_init(au_h_dptr(root, bindex), br, sb); ++ err = au_wh_init(br, sb); + if (wbr) + wbr_wh_write_unlock(wbr); + au_hn_imtx_unlock(hdir); @@ -19329,8 +19785,8 @@ +#endif /* __KERNEL__ */ +#endif /* __AUFS_OPTS_H__ */ --- a/fs/aufs/plink.c 1970-01-01 01:00:00.000000000 +0100 -+++ b/fs/aufs/plink.c 2013-05-04 18:39:11.239157326 +0100 -@@ -0,0 +1,511 @@ ++++ b/fs/aufs/plink.c 2013-05-09 01:36:20.745181631 +0100 +@@ -0,0 +1,523 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -19452,19 +19908,13 @@ + +/* ---------------------------------------------------------------------- */ + -+struct pseudo_link { -+ union { -+ struct list_head list; -+ struct rcu_head rcu; -+ }; -+ struct inode *inode; -+}; -+ +#ifdef CONFIG_AUFS_DEBUG +void au_plink_list(struct super_block *sb) +{ ++ int i; + struct au_sbinfo *sbinfo; -+ struct list_head *plink_list; ++ struct hlist_head *plink_hlist; ++ struct hlist_node *pos; + struct pseudo_link *plink; + + SiMustAnyLock(sb); @@ -19473,20 +19923,23 @@ + AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); + AuDebugOn(au_plink_maint(sb, AuLock_NOPLM)); + -+ plink_list = &sbinfo->si_plink.head; -+ rcu_read_lock(); -+ list_for_each_entry_rcu(plink, plink_list, list) -+ AuDbg("%lu\n", plink->inode->i_ino); -+ rcu_read_unlock(); ++ for (i = 0; i < AuPlink_NHASH; i++) { ++ plink_hlist = &sbinfo->si_plink[i].head; ++ rcu_read_lock(); ++ hlist_for_each_entry_rcu(plink, pos, plink_hlist, hlist) ++ AuDbg("%lu\n", plink->inode->i_ino); ++ rcu_read_unlock(); ++ } +} +#endif + +/* is the inode pseudo-linked? */ +int au_plink_test(struct inode *inode) +{ -+ int found; ++ int found, i; + struct au_sbinfo *sbinfo; -+ struct list_head *plink_list; ++ struct hlist_head *plink_hlist; ++ struct hlist_node *pos; + struct pseudo_link *plink; + + sbinfo = au_sbi(inode->i_sb); @@ -19495,9 +19948,10 @@ + AuDebugOn(au_plink_maint(inode->i_sb, AuLock_NOPLM)); + + found = 0; -+ plink_list = &sbinfo->si_plink.head; ++ i = au_plink_hash(inode->i_ino); ++ plink_hlist = &sbinfo->si_plink[i].head; + rcu_read_lock(); -+ list_for_each_entry_rcu(plink, plink_list, list) ++ hlist_for_each_entry_rcu(plink, pos, plink_hlist, hlist) + if (plink->inode == inode) { + found = 1; + break; @@ -19593,7 +20047,7 @@ +{ + int err; + struct path h_path = { -+ .mnt = br->br_mnt ++ .mnt = au_br_mnt(br) + }; + struct inode *h_dir; + @@ -19676,7 +20130,7 @@ +static void do_put_plink(struct pseudo_link *plink, int do_del) +{ + if (do_del) -+ list_del(&plink->list); ++ hlist_del(&plink->hlist); + iput(plink->inode); + kfree(plink); +} @@ -19699,30 +20153,24 @@ +{ + struct super_block *sb; + struct au_sbinfo *sbinfo; -+ struct list_head *plink_list; ++ struct hlist_head *plink_hlist; ++ struct hlist_node *pos; + struct pseudo_link *plink, *tmp; -+ int found, err, cnt; ++ struct au_sphlhead *sphl; ++ int found, err, cnt, i; + + sb = inode->i_sb; + sbinfo = au_sbi(sb); + AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); + AuDebugOn(au_plink_maint(sb, AuLock_NOPLM)); + -+ cnt = 0; -+ found = 0; -+ plink_list = &sbinfo->si_plink.head; -+ rcu_read_lock(); -+ list_for_each_entry_rcu(plink, plink_list, list) { -+ cnt++; -+ if (plink->inode == inode) { -+ found = 1; -+ break; -+ } -+ } -+ rcu_read_unlock(); ++ found = au_plink_test(inode); + if (found) + return; + ++ i = au_plink_hash(inode->i_ino); ++ sphl = sbinfo->si_plink + i; ++ plink_hlist = &sphl->head; + tmp = kmalloc(sizeof(*plink), GFP_NOFS); + if (tmp) + tmp->inode = au_igrab(inode); @@ -19731,20 +20179,22 @@ + goto out; + } + -+ spin_lock(&sbinfo->si_plink.spin); -+ list_for_each_entry(plink, plink_list, list) { ++ spin_lock(&sphl->spin); ++ hlist_for_each_entry(plink, pos, plink_hlist, hlist) { + if (plink->inode == inode) { + found = 1; + break; + } + } + if (!found) -+ list_add_rcu(&tmp->list, plink_list); -+ spin_unlock(&sbinfo->si_plink.spin); ++ hlist_add_head_rcu(&tmp->hlist, plink_hlist); ++ spin_unlock(&sphl->spin); + if (!found) { -+ cnt++; -+ WARN_ONCE(cnt > AUFS_PLINK_WARN, -+ "unexpectedly many pseudo links, %d\n", cnt); ++ cnt = au_sphl_count(sphl); ++#define msg "unexpectedly unblanced or too many pseudo-links" ++ if (cnt > AUFS_PLINK_WARN) ++ AuWarn1(msg ", %d\n", cnt); ++#undef msg + err = whplink(h_dentry, inode, bindex, au_sbr(sb, bindex)); + } else { + do_put_plink(tmp, 0); @@ -19755,7 +20205,7 @@ + if (unlikely(err)) { + pr_warn("err %d, damaged pseudo link.\n", err); + if (tmp) { -+ au_spl_del_rcu(&tmp->list, &sbinfo->si_plink); ++ au_sphl_del_rcu(&tmp->hlist, sphl); + call_rcu(&tmp->rcu, do_put_plink_rcu); + } + } @@ -19764,9 +20214,11 @@ +/* free all plinks */ +void au_plink_put(struct super_block *sb, int verbose) +{ ++ int i, warned; + struct au_sbinfo *sbinfo; -+ struct list_head *plink_list; -+ struct pseudo_link *plink, *tmp; ++ struct hlist_head *plink_hlist; ++ struct hlist_node *pos, *tmp; ++ struct pseudo_link *plink; + + SiMustWriteLock(sb); + @@ -19774,12 +20226,18 @@ + AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); + AuDebugOn(au_plink_maint(sb, AuLock_NOPLM)); + -+ plink_list = &sbinfo->si_plink.head; + /* no spin_lock since sbinfo is write-locked */ -+ WARN(verbose && !list_empty(plink_list), "pseudo-link is not flushed"); -+ list_for_each_entry_safe(plink, tmp, plink_list, list) -+ do_put_plink(plink, 0); -+ INIT_LIST_HEAD(plink_list); ++ warned = 0; ++ for (i = 0; i < AuPlink_NHASH; i++) { ++ plink_hlist = &sbinfo->si_plink[i].head; ++ if (!warned && verbose && !hlist_empty(plink_hlist)) { ++ pr_warn("pseudo-link is not flushed"); ++ warned = 1; ++ } ++ hlist_for_each_entry_safe(plink, pos, tmp, plink_hlist, hlist) ++ do_put_plink(plink, 0); ++ INIT_HLIST_HEAD(plink_hlist); ++ } +} + +void au_plink_clean(struct super_block *sb, int verbose) @@ -19793,15 +20251,44 @@ + aufs_write_unlock(root); +} + ++static int au_plink_do_half_refresh(struct inode *inode, aufs_bindex_t br_id) ++{ ++ int do_put; ++ aufs_bindex_t bstart, bend, bindex; ++ ++ do_put = 0; ++ bstart = au_ibstart(inode); ++ bend = au_ibend(inode); ++ if (bstart >= 0) { ++ for (bindex = bstart; bindex <= bend; bindex++) { ++ if (!au_h_iptr(inode, bindex) ++ || au_ii_br_id(inode, bindex) != br_id) ++ continue; ++ au_set_h_iptr(inode, bindex, NULL, 0); ++ do_put = 1; ++ break; ++ } ++ if (do_put) ++ for (bindex = bstart; bindex <= bend; bindex++) ++ if (au_h_iptr(inode, bindex)) { ++ do_put = 0; ++ break; ++ } ++ } else ++ do_put = 1; ++ ++ return do_put; ++} ++ +/* free the plinks on a branch specified by @br_id */ +void au_plink_half_refresh(struct super_block *sb, aufs_bindex_t br_id) +{ + struct au_sbinfo *sbinfo; -+ struct list_head *plink_list; -+ struct pseudo_link *plink, *tmp; ++ struct hlist_head *plink_hlist; ++ struct hlist_node *pos, *tmp; ++ struct pseudo_link *plink; + struct inode *inode; -+ aufs_bindex_t bstart, bend, bindex; -+ unsigned char do_put; ++ int i, do_put; + + SiMustWriteLock(sb); + @@ -19809,37 +20296,18 @@ + AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); + AuDebugOn(au_plink_maint(sb, AuLock_NOPLM)); + -+ plink_list = &sbinfo->si_plink.head; + /* no spin_lock since sbinfo is write-locked */ -+ list_for_each_entry_safe(plink, tmp, plink_list, list) { -+ do_put = 0; -+ inode = au_igrab(plink->inode); -+ ii_write_lock_child(inode); -+ bstart = au_ibstart(inode); -+ bend = au_ibend(inode); -+ if (bstart >= 0) { -+ for (bindex = bstart; bindex <= bend; bindex++) { -+ if (!au_h_iptr(inode, bindex) -+ || au_ii_br_id(inode, bindex) != br_id) -+ continue; -+ au_set_h_iptr(inode, bindex, NULL, 0); -+ do_put = 1; -+ break; -+ } -+ } else -+ do_put_plink(plink, 1); -+ -+ if (do_put) { -+ for (bindex = bstart; bindex <= bend; bindex++) -+ if (au_h_iptr(inode, bindex)) { -+ do_put = 0; -+ break; -+ } ++ for (i = 0; i < AuPlink_NHASH; i++) { ++ plink_hlist = &sbinfo->si_plink[i].head; ++ hlist_for_each_entry_safe(plink, pos, tmp, plink_hlist, hlist) { ++ inode = au_igrab(plink->inode); ++ ii_write_lock_child(inode); ++ do_put = au_plink_do_half_refresh(inode, br_id); + if (do_put) + do_put_plink(plink, 1); ++ ii_write_unlock(inode); ++ iput(inode); + } -+ ii_write_unlock(inode); -+ iput(inode); + } +} --- a/fs/aufs/poll.c 1970-01-01 01:00:00.000000000 +0100 @@ -20653,8 +21121,8 @@ +#endif /* __KERNEL__ */ +#endif /* __AUFS_RWSEM_H__ */ --- a/fs/aufs/sbinfo.c 1970-01-01 01:00:00.000000000 +0100 -+++ b/fs/aufs/sbinfo.c 2013-03-10 01:48:58.463093058 +0000 -@@ -0,0 +1,343 @@ ++++ b/fs/aufs/sbinfo.c 2013-05-09 01:36:20.745181631 +0100 +@@ -0,0 +1,346 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -20684,11 +21152,13 @@ + */ +void au_si_free(struct kobject *kobj) +{ ++ int i; + struct au_sbinfo *sbinfo; + char *locked __maybe_unused; /* debug only */ + + sbinfo = container_of(kobj, struct au_sbinfo, si_kobj); -+ AuDebugOn(!list_empty(&sbinfo->si_plink.head)); ++ for (i = 0; i < AuPlink_NHASH; i++) ++ AuDebugOn(!hlist_empty(&sbinfo->si_plink[i].head)); + AuDebugOn(atomic_read(&sbinfo->si_nowait.nw_len)); + + au_rw_write_lock(&sbinfo->si_rwsem); @@ -20710,7 +21180,7 @@ + +int au_si_alloc(struct super_block *sb) +{ -+ int err; ++ int err, i; + struct au_sbinfo *sbinfo; + static struct lock_class_key aufs_si; + @@ -20763,7 +21233,8 @@ + sbinfo->si_rdhash = AUFS_RDHASH_DEF; + sbinfo->si_dirwh = AUFS_DIRWH_DEF; + -+ au_spl_init(&sbinfo->si_plink); ++ for (i = 0; i < AuPlink_NHASH; i++) ++ au_sphl_init(sbinfo->si_plink + i); + init_waitqueue_head(&sbinfo->si_plink_wq); + spin_lock_init(&sbinfo->si_plink_maint_lock); + @@ -20999,8 +21470,8 @@ + spin_unlock(&sbinfo->au_si_pid.tree_lock); +} --- a/fs/aufs/spl.h 1970-01-01 01:00:00.000000000 +0100 -+++ b/fs/aufs/spl.h 2013-03-10 01:48:58.463093058 +0000 -@@ -0,0 +1,62 @@ ++++ b/fs/aufs/spl.h 2013-05-09 01:36:20.745181631 +0100 +@@ -0,0 +1,112 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -21061,10 +21532,60 @@ + spin_unlock(&spl->spin); +} + ++/* ---------------------------------------------------------------------- */ ++ ++struct au_sphlhead { ++ spinlock_t spin; ++ struct hlist_head head; ++}; ++ ++static inline void au_sphl_init(struct au_sphlhead *sphl) ++{ ++ spin_lock_init(&sphl->spin); ++ INIT_HLIST_HEAD(&sphl->head); ++} ++ ++static inline void au_sphl_add(struct hlist_node *hlist, ++ struct au_sphlhead *sphl) ++{ ++ spin_lock(&sphl->spin); ++ hlist_add_head(hlist, &sphl->head); ++ spin_unlock(&sphl->spin); ++} ++ ++static inline void au_sphl_del(struct hlist_node *hlist, ++ struct au_sphlhead *sphl) ++{ ++ spin_lock(&sphl->spin); ++ hlist_del(hlist); ++ spin_unlock(&sphl->spin); ++} ++ ++static inline void au_sphl_del_rcu(struct hlist_node *hlist, ++ struct au_sphlhead *sphl) ++{ ++ spin_lock(&sphl->spin); ++ hlist_del_rcu(hlist); ++ spin_unlock(&sphl->spin); ++} ++ ++static inline unsigned long au_sphl_count(struct au_sphlhead *sphl) ++{ ++ unsigned long cnt; ++ struct hlist_node *pos; ++ ++ cnt = 0; ++ spin_lock(&sphl->spin); ++ hlist_for_each(pos, &sphl->head) ++ cnt++; ++ spin_unlock(&sphl->spin); ++ return cnt; ++} ++ +#endif /* __KERNEL__ */ +#endif /* __AUFS_SPL_H__ */ --- a/fs/aufs/super.c 1970-01-01 01:00:00.000000000 +0100 -+++ b/fs/aufs/super.c 2013-05-04 18:39:11.239157326 +0100 ++++ b/fs/aufs/super.c 2013-05-09 01:36:20.745181631 +0100 @@ -0,0 +1,993 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima @@ -21172,7 +21693,7 @@ + hdp = au_di(sb->s_root)->di_hdentry; + for (bindex = 0; !err && bindex <= bend; bindex++) { + br = au_sbr(sb, bindex); -+ path.mnt = br->br_mnt; ++ path.mnt = au_br_mnt(br); + path.dentry = hdp[bindex].hd_dentry; + err = au_seq_path(seq, &path); + if (err > 0) { @@ -22060,8 +22581,8 @@ + .owner = THIS_MODULE, +}; --- a/fs/aufs/super.h 1970-01-01 01:00:00.000000000 +0100 -+++ b/fs/aufs/super.h 2013-03-10 01:48:58.463093058 +0000 -@@ -0,0 +1,538 @@ ++++ b/fs/aufs/super.h 2013-05-09 01:36:20.745181631 +0100 +@@ -0,0 +1,555 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -22119,6 +22640,20 @@ + unsigned long long mfsrr_watermark; +}; + ++struct pseudo_link { ++ union { ++ struct hlist_node hlist; ++ struct rcu_head rcu; ++ }; ++ struct inode *inode; ++}; ++ ++#define AuPlink_NHASH 100 ++static inline int au_plink_hash(ino_t ino) ++{ ++ return ino % AuPlink_NHASH; ++} ++ +struct au_branch; +struct au_sbinfo { + /* nowait tasks in the system-wide workqueue */ @@ -22209,7 +22744,7 @@ + /* int si_rendir; */ + + /* pseudo_link list */ -+ struct au_splhead si_plink; ++ struct au_sphlhead si_plink[AuPlink_NHASH]; + wait_queue_head_t si_plink_wq; + spinlock_t si_plink_maint_lock; + pid_t si_plink_maint_pid; @@ -22222,7 +22757,9 @@ + */ + struct kobject si_kobj; +#ifdef CONFIG_DEBUG_FS -+ struct dentry *si_dbgaufs, *si_dbgaufs_xib; ++ struct dentry *si_dbgaufs; ++ struct dentry *si_dbgaufs_plink; ++ struct dentry *si_dbgaufs_xib; +#ifdef CONFIG_AUFS_EXPORT + struct dentry *si_dbgaufs_xigen; +#endif @@ -22413,6 +22950,7 @@ + /* AuRwMustWriteLock(&sbinfo->si_rwsem); */ +#ifdef CONFIG_DEBUG_FS + sbinfo->si_dbgaufs = NULL; ++ sbinfo->si_dbgaufs_plink = NULL; + sbinfo->si_dbgaufs_xib = NULL; +#ifdef CONFIG_AUFS_EXPORT + sbinfo->si_dbgaufs_xigen = NULL; @@ -22816,7 +23354,7 @@ +#endif /* __KERNEL__ */ +#endif /* __SYSAUFS_H__ */ --- a/fs/aufs/sysfs.c 1970-01-01 01:00:00.000000000 +0100 -+++ b/fs/aufs/sysfs.c 2013-03-10 01:48:58.463093058 +0000 ++++ b/fs/aufs/sysfs.c 2013-05-09 01:36:20.745181631 +0100 @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima @@ -22912,7 +23450,7 @@ + root = sb->s_root; + di_read_lock_parent(root, !AuLock_IR); + br = au_sbr(sb, bindex); -+ path.mnt = br->br_mnt; ++ path.mnt = au_br_mnt(br); + path.dentry = au_h_dptr(root, bindex); + au_seq_path(seq, &path); + di_read_unlock(root, !AuLock_IR); @@ -24890,8 +25428,8 @@ + return err; +} --- a/fs/aufs/vfsub.h 1970-01-01 01:00:00.000000000 +0100 -+++ b/fs/aufs/vfsub.h 2013-05-04 18:39:17.031157464 +0100 -@@ -0,0 +1,302 @@ ++++ b/fs/aufs/vfsub.h 2013-05-09 01:36:20.745181631 +0100 +@@ -0,0 +1,289 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -24997,19 +25535,6 @@ + +/* ---------------------------------------------------------------------- */ + -+/* cf. i_[ug]id_read() in linux/include/fs.h */ -+static inline uid_t vfsub_ia_uid(struct iattr *ia) -+{ -+ return from_kuid(&init_user_ns, ia->ia_uid); -+} -+ -+static inline gid_t vfsub_ia_gid(struct iattr *ia) -+{ -+ return from_kgid(&init_user_ns, ia->ia_gid); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ +int vfsub_update_h_iattr(struct path *h_path, int *did); +struct file *vfsub_dentry_open(struct path *path, int flags); +struct file *vfsub_filp_open(const char *path, int oflags, int mode); @@ -25195,8 +25720,8 @@ +#endif /* __KERNEL__ */ +#endif /* __AUFS_VFSUB_H__ */ --- a/fs/aufs/wbr_policy.c 1970-01-01 01:00:00.000000000 +0100 -+++ b/fs/aufs/wbr_policy.c 2013-03-10 01:48:58.463093058 +0000 -@@ -0,0 +1,700 @@ ++++ b/fs/aufs/wbr_policy.c 2013-05-09 01:36:20.745181631 +0100 +@@ -0,0 +1,701 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -25236,7 +25761,7 @@ + ia.ia_uid = h_isrc->i_uid; + ia.ia_gid = h_isrc->i_gid; + sbits = !!(ia.ia_mode & (S_ISUID | S_ISGID)); -+ au_cpup_attr_flags(h_path->dentry->d_inode, h_isrc); ++ au_cpup_attr_flags(h_path->dentry->d_inode, h_isrc->i_flags); + err = vfsub_sio_notify_change(h_path, &ia); + + /* is this nfs only? */ @@ -25296,7 +25821,7 @@ + + err = 0; + if (h_path.dentry->d_inode) { -+ h_path.mnt = br->br_mnt; ++ h_path.mnt = au_br_mnt(br); + err = au_wh_unlink_dentry(au_h_iptr(dir, bdst), &h_path, + dentry); + } @@ -25307,6 +25832,7 @@ +} + +static int au_cpdown_dir(struct dentry *dentry, aufs_bindex_t bdst, ++ struct au_pin *pin, + struct dentry *h_parent, void *arg) +{ + int err, rerr; @@ -25324,7 +25850,7 @@ + AuDebugOn(h_dir != au_h_iptr(dir, bdst)); + IMustLock(h_dir); + -+ err = au_lkup_neg(dentry, bdst); ++ err = au_lkup_neg(dentry, bdst, /*wh*/0); + if (unlikely(err < 0)) + goto out; + h_path.dentry = au_h_dptr(dentry, bdst); @@ -25630,7 +26156,7 @@ + continue; + + /* sb->s_root for NFS is unreliable */ -+ h_path.mnt = br->br_mnt; ++ h_path.mnt = au_br_mnt(br); + h_path.dentry = h_path.mnt->mnt_root; + err = vfs_statfs(&h_path, st); + if (unlikely(err)) { @@ -25898,8 +26424,8 @@ + } +}; --- a/fs/aufs/whout.c 1970-01-01 01:00:00.000000000 +0100 -+++ b/fs/aufs/whout.c 2013-05-04 18:39:11.239157326 +0100 -@@ -0,0 +1,1042 @@ ++++ b/fs/aufs/whout.c 2013-05-09 01:36:20.749181631 +0100 +@@ -0,0 +1,1023 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -26076,7 +26602,7 @@ +{ + int err; + struct path h_path = { -+ .mnt = br->br_mnt ++ .mnt = au_br_mnt(br) + }; + struct inode *h_dir; + struct dentry *h_parent; @@ -26135,7 +26661,7 @@ +{ + int err; + struct path h_path = { -+ .mnt = br->br_mnt ++ .mnt = au_br_mnt(br) + }; + + err = 0; @@ -26165,14 +26691,10 @@ + if (!whpath->dentry->d_inode) + return; + -+ err = vfsub_mnt_want_write(whpath->mnt); -+ if (!err) { -+ if (isdir) -+ err = vfsub_rmdir(h_dir, whpath); -+ else -+ err = vfsub_unlink(h_dir, whpath, /*force*/0); -+ vfsub_mnt_drop_write(whpath->mnt); -+ } ++ if (isdir) ++ err = vfsub_rmdir(h_dir, whpath); ++ else ++ err = vfsub_unlink(h_dir, whpath, /*force*/0); + if (unlikely(err)) + pr_warn("failed removing %.*s (%d), ignored.\n", + AuDLNPair(whpath->dentry), err); @@ -26201,11 +26723,7 @@ + + if (au_test_nfs(path->dentry->d_sb)) + mode |= S_IXUGO; -+ err = vfsub_mnt_want_write(path->mnt); -+ if (!err) { -+ err = vfsub_mkdir(h_dir, path, mode); -+ vfsub_mnt_drop_write(path->mnt); -+ } ++ err = vfsub_mkdir(h_dir, path, mode); + } else if (S_ISDIR(path->dentry->d_inode->i_mode)) + err = 0; + else @@ -26299,13 +26817,8 @@ + err = -EEXIST; + h_dir = h_root->d_inode; + if (!base[AuBrWh_BASE].dentry->d_inode) { -+ err = vfsub_mnt_want_write(h_path->mnt); -+ if (!err) { -+ h_path->dentry = base[AuBrWh_BASE].dentry; -+ err = vfsub_create(h_dir, h_path, WH_MASK, -+ /*want_excl*/true); -+ vfsub_mnt_drop_write(h_path->mnt); -+ } ++ h_path->dentry = base[AuBrWh_BASE].dentry; ++ err = vfsub_create(h_dir, h_path, WH_MASK, /*want_excl*/true); + } else if (S_ISREG(base[AuBrWh_BASE].dentry->d_inode->i_mode)) + err = 0; + else @@ -26337,16 +26850,14 @@ +/* + * initialize the whiteout base file/dir for @br. + */ -+int au_wh_init(struct dentry *h_root, struct au_branch *br, -+ struct super_block *sb) ++int au_wh_init(struct au_branch *br, struct super_block *sb) +{ + int err, i; + const unsigned char do_plink + = !!au_opt_test(au_mntflags(sb), PLINK); -+ struct path path = { -+ .mnt = br->br_mnt -+ }; + struct inode *h_dir; ++ struct path path = br->br_path; ++ struct dentry *h_root = path.dentry; + struct au_wbr *wbr = br->br_wbr; + static const struct qstr base_name[] = { + [AuBrWh_BASE] = QSTR_INIT(AUFS_BASE_NAME, @@ -26460,19 +26971,16 @@ + dir = a->sb->s_root->d_inode; + hdir = au_hi(dir, bindex); + h_root = au_h_dptr(a->sb->s_root, bindex); ++ AuDebugOn(h_root != au_br_dentry(a->br)); + + au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); + wbr_wh_write_lock(wbr); + err = au_h_verify(wbr->wbr_whbase, au_opt_udba(a->sb), hdir->hi_inode, + h_root, a->br); + if (!err) { -+ err = vfsub_mnt_want_write(a->br->br_mnt); -+ if (!err) { -+ h_path.dentry = wbr->wbr_whbase; -+ h_path.mnt = a->br->br_mnt; -+ err = vfsub_unlink(hdir->hi_inode, &h_path, /*force*/0); -+ vfsub_mnt_drop_write(a->br->br_mnt); -+ } ++ h_path.dentry = wbr->wbr_whbase; ++ h_path.mnt = au_br_mnt(a->br); ++ err = vfsub_unlink(hdir->hi_inode, &h_path, /*force*/0); + } else { + pr_warn("%.*s is moved, ignored\n", + AuDLNPair(wbr->wbr_whbase)); @@ -26481,7 +26989,7 @@ + dput(wbr->wbr_whbase); + wbr->wbr_whbase = NULL; + if (!err) -+ err = au_wh_init(h_root, a->br, a->sb); ++ err = au_wh_init(a->br, a->sb); + wbr_wh_write_unlock(wbr); + au_hn_imtx_unlock(hdir); + di_read_unlock(a->sb->s_root, AuLock_IR); @@ -26552,7 +27060,7 @@ + IMustLock(h_dir); + + br = au_sbr(sb, bindex); -+ h_path.mnt = br->br_mnt; ++ h_path.mnt = au_br_mnt(br); + wbr = br->br_wbr; + wbr_wh_read_lock(wbr); + if (wbr->wbr_whbase) { @@ -26601,7 +27109,7 @@ + } else { + struct path tmp = { + .dentry = opq_dentry, -+ .mnt = br->br_mnt ++ .mnt = au_br_mnt(br) + }; + err = do_unlink_wh(au_h_iptr(dentry->d_inode, bindex), &tmp); + if (!err) @@ -26853,7 +27361,7 @@ + + if (!err) { + h_tmp.dentry = wh_dentry; -+ h_tmp.mnt = br->br_mnt; ++ h_tmp.mnt = au_br_mnt(br); + err = vfsub_rmdir(h_dir, &h_tmp); + } + @@ -26897,21 +27405,20 @@ + h_parent = dget_parent(a->wh_dentry); + h_dir = h_parent->d_inode; + hdir = au_hi(a->dir, bindex); ++ err = vfsub_mnt_want_write(au_br_mnt(a->br)); ++ if (unlikely(err)) ++ goto out_mnt; + au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); + err = au_h_verify(a->wh_dentry, au_opt_udba(sb), h_dir, h_parent, + a->br); -+ if (!err) { -+ err = vfsub_mnt_want_write(a->br->br_mnt); -+ if (!err) { -+ err = au_whtmp_rmdir(a->dir, bindex, a->wh_dentry, -+ &a->whlist); -+ vfsub_mnt_drop_write(a->br->br_mnt); -+ } -+ } ++ if (!err) ++ err = au_whtmp_rmdir(a->dir, bindex, a->wh_dentry, &a->whlist); + au_hn_imtx_unlock(hdir); ++ vfsub_mnt_drop_write(au_br_mnt(a->br)); ++ ++out_mnt: + dput(h_parent); + ii_write_unlock(a->dir); -+ +out: + /* mutex_unlock(&a->dir->i_mutex); */ + au_whtmp_rmdir_free(a); @@ -26943,8 +27450,8 @@ + } +} --- a/fs/aufs/whout.h 1970-01-01 01:00:00.000000000 +0100 -+++ b/fs/aufs/whout.h 2013-03-10 01:48:58.463093058 +0000 -@@ -0,0 +1,88 @@ ++++ b/fs/aufs/whout.h 2013-05-09 01:36:20.749181631 +0100 +@@ -0,0 +1,87 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -26985,8 +27492,7 @@ +int au_whtmp_ren(struct dentry *h_dentry, struct au_branch *br); +int au_wh_unlink_dentry(struct inode *h_dir, struct path *h_path, + struct dentry *dentry); -+int au_wh_init(struct dentry *h_parent, struct au_branch *br, -+ struct super_block *sb); ++int au_wh_init(struct au_branch *br, struct super_block *sb); + +/* diropq flags */ +#define AuDiropq_CREATE 1 @@ -27346,7 +27852,7 @@ +#endif /* __KERNEL__ */ +#endif /* __AUFS_WKQ_H__ */ --- a/fs/aufs/xino.c 1970-01-01 01:00:00.000000000 +0100 -+++ b/fs/aufs/xino.c 2013-05-04 18:39:11.239157326 +0100 ++++ b/fs/aufs/xino.c 2013-05-09 01:36:20.749181631 +0100 @@ -0,0 +1,1265 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima @@ -27619,12 +28125,12 @@ + fput(file); + br->br_xino.xi_file = new_xino; + -+ h_sb = br->br_mnt->mnt_sb; ++ h_sb = au_br_sb(br); + for (bi = 0; bi <= bend; bi++) { + if (unlikely(bi == bindex)) + continue; + br = au_sbr(sb, bi); -+ if (br->br_mnt->mnt_sb != h_sb) ++ if (au_br_sb(br) != h_sb) + continue; + + fput(br->br_xino.xi_file); @@ -27759,7 +28265,7 @@ + h_ino, ino); + if (!err) { + if (au_opt_test(mnt_flags, TRUNC_XINO) -+ && au_test_fs_trunc_xino(br->br_mnt->mnt_sb)) ++ && au_test_fs_trunc_xino(au_br_sb(br))) + xino_try_trunc(sb, br); + return 0; /* success */ + } @@ -27912,7 +28418,7 @@ + err = au_xino_do_write(xwrite, br->br_xino.xi_file, + h_inode->i_ino, /*ino*/0); + if (!err && try_trunc -+ && au_test_fs_trunc_xino(br->br_mnt->mnt_sb)) ++ && au_test_fs_trunc_xino(au_br_sb(br))) + xino_try_trunc(sb, br); + } +} @@ -28119,10 +28625,10 @@ + shared_br = NULL; + bend = au_sbend(sb); + if (do_test) { -+ tgt_sb = br->br_mnt->mnt_sb; ++ tgt_sb = au_br_sb(br); + for (bindex = 0; bindex <= bend; bindex++) { + b = au_sbr(sb, bindex); -+ if (tgt_sb == b->br_mnt->mnt_sb) { ++ if (tgt_sb == au_br_sb(b)) { + shared_br = b; + break; + } @@ -28551,7 +29057,7 @@ + for (bindex = 0; bindex <= bend; bindex++) { + br = au_sbr(sb, bindex); + if (au_br_writable(br->br_perm) -+ && !au_test_fs_bad_xino(br->br_mnt->mnt_sb)) { ++ && !au_test_fs_bad_xino(au_br_sb(br))) { + bwr = bindex; + break; + } @@ -28562,7 +29068,7 @@ + page = (void *)__get_free_page(GFP_NOFS); + if (unlikely(!page)) + goto out; -+ path.mnt = br->br_mnt; ++ path.mnt = au_br_mnt(br); + path.dentry = au_h_dptr(sb->s_root, bwr); + p = d_path(&path, page, PATH_MAX - sizeof(AUFS_XINO_FNAME)); + file = (void *)p; @@ -28636,8 +29142,8 @@ + +#include --- a/include/uapi/linux/aufs_type.h 1970-01-01 01:00:00.000000000 +0100 -+++ b/include/uapi/linux/aufs_type.h 2013-05-04 18:39:17.031157464 +0100 -@@ -0,0 +1,234 @@ ++++ b/include/uapi/linux/aufs_type.h 2013-05-09 01:36:20.749181631 +0100 +@@ -0,0 +1,235 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -28680,7 +29186,7 @@ + +#include + -+#define AUFS_VERSION "3.8-20130325" ++#define AUFS_VERSION "3.8-20130506" + +/* todo? move this to linux-2.6.19/include/magic.h */ +#define AUFS_SUPER_MAGIC ('a' << 24 | 'u' << 16 | 'f' << 8 | 's') @@ -28717,7 +29223,7 @@ +#define AUFS_WH_PFX ".wh." +#define AUFS_WH_PFX_LEN ((int)sizeof(AUFS_WH_PFX) - 1) +#define AUFS_WH_TMP_LEN 4 -+/* a limit for rmdir/rename a dir */ ++/* a limit for rmdir/rename a dir and copyup */ +#define AUFS_MAX_NAMELEN (NAME_MAX \ + - AUFS_WH_PFX_LEN * 2 /* doubly whiteouted */\ + - 1 /* dot */\ @@ -28734,7 +29240,7 @@ +#define AUFS_WKQ_NAME AUFS_NAME "d" +#define AUFS_MFS_DEF_SEC 30 /* seconds */ +#define AUFS_MFS_MAX_SEC 3600 /* seconds */ -+#define AUFS_PLINK_WARN 100 /* number of plinks */ ++#define AUFS_PLINK_WARN 50 /* number of plinks in a single bucket */ + +/* pseudo-link maintenace under /proc */ +#define AUFS_PLINK_MAINT_NAME "plink_maint" @@ -28759,6 +29265,7 @@ +#define AUFS_BRPERM_RR "rr" +#define AUFS_BRRATTR_WH "wh" +#define AUFS_BRWATTR_NLWH "nolwh" ++#define AUFS_BRATTR_UNPIN "unpin" + +/* ---------------------------------------------------------------------- */ +