update aufs to it's latest standalone 3.2 branch
authorGrazvydas Ignotas <notasas@gmail.com>
Sat, 7 Feb 2015 23:37:21 +0000 (01:37 +0200)
committerGrazvydas Ignotas <notasas@gmail.com>
Fri, 20 Feb 2015 20:11:07 +0000 (22:11 +0200)
git tree: git://git.code.sf.net/p/aufs/aufs3-standalone
branch: aufs3.2.x
commit: 62e53757b8b

78 files changed:
Documentation/ABI/testing/debugfs-aufs
Documentation/ABI/testing/sysfs-aufs
Documentation/filesystems/aufs/README
Documentation/filesystems/aufs/design/01intro.txt
Documentation/filesystems/aufs/design/02struct.txt
Documentation/filesystems/aufs/design/03lookup.txt
Documentation/filesystems/aufs/design/04branch.txt
Documentation/filesystems/aufs/design/05wbr_policy.txt
Documentation/filesystems/aufs/design/06mmap.txt
Documentation/filesystems/aufs/design/07export.txt
Documentation/filesystems/aufs/design/08shwh.txt
Documentation/filesystems/aufs/design/10dynop.txt
Documentation/filesystems/aufs/design/99plan.txt
fs/aufs/aufs.h
fs/aufs/branch.c
fs/aufs/branch.h
fs/aufs/cpup.c
fs/aufs/cpup.h
fs/aufs/dbgaufs.c
fs/aufs/dbgaufs.h
fs/aufs/dcsub.c
fs/aufs/dcsub.h
fs/aufs/debug.c
fs/aufs/debug.h
fs/aufs/dentry.c
fs/aufs/dentry.h
fs/aufs/dinfo.c
fs/aufs/dir.c
fs/aufs/dir.h
fs/aufs/dynop.c
fs/aufs/dynop.h
fs/aufs/export.c
fs/aufs/f_op.c
fs/aufs/f_op_sp.c
fs/aufs/file.c
fs/aufs/file.h
fs/aufs/finfo.c
fs/aufs/fstype.h
fs/aufs/hfsnotify.c
fs/aufs/hfsplus.c
fs/aufs/hnotify.c
fs/aufs/i_op.c
fs/aufs/i_op_add.c
fs/aufs/i_op_del.c
fs/aufs/i_op_ren.c
fs/aufs/iinfo.c
fs/aufs/inode.c
fs/aufs/inode.h
fs/aufs/ioctl.c
fs/aufs/loop.c
fs/aufs/loop.h
fs/aufs/module.c
fs/aufs/module.h
fs/aufs/opts.c
fs/aufs/opts.h
fs/aufs/plink.c
fs/aufs/poll.c
fs/aufs/procfs.c
fs/aufs/rdu.c
fs/aufs/rwsem.h
fs/aufs/sbinfo.c
fs/aufs/spl.h
fs/aufs/super.c
fs/aufs/super.h
fs/aufs/sysaufs.c
fs/aufs/sysaufs.h
fs/aufs/sysfs.c
fs/aufs/sysrq.c
fs/aufs/vdir.c
fs/aufs/vfsub.c
fs/aufs/vfsub.h
fs/aufs/wbr_policy.c
fs/aufs/whout.c
fs/aufs/whout.h
fs/aufs/wkq.c
fs/aufs/wkq.h
fs/aufs/xino.c
include/linux/aufs_type.h

index 7d2e65f..a58f0d0 100644 (file)
@@ -6,6 +6,19 @@ Description:
                per aufs mount, where <id> is a unique id generated
                internally.
 
+What:          /debug/aufs/si_<id>/plink
+Date:          Apr 2013
+Contact:       J. R. Okajima <hooanon05@yahoo.co.jp>
+Description:
+               It has three lines and shows the information about the
+               pseudo-link. The first line is a single number
+               representing a number of buckets. The second line is a
+               number of pseudo-links per buckets (separated by a
+               blank). The last line is a single number representing a
+               total number of psedo-links.
+               When the aufs mount option 'noplink' is specified, it
+               will show "1\n0\n0\n".
+
 What:          /debug/aufs/si_<id>/xib
 Date:          March 2009
 Contact:       J. R. Okajima <hooanon05@yahoo.co.jp>
index 7af6dc0..066916d 100644 (file)
@@ -13,6 +13,13 @@ Description:
                It shows the abolute path of a member directory (which
                is called branch) in aufs, and its permission.
 
+What:          /sys/fs/aufs/si_<id>/brid0, brid1 ... bridN
+Date:          July 2013
+Contact:       J. R. Okajima <hooanon05@yahoo.co.jp>
+Description:
+               It shows the id of a member directory (which is called
+               branch) in aufs.
+
 What:          /sys/fs/aufs/si_<id>/xi_path
 Date:          March 2009
 Contact:       J. R. Okajima <hooanon05@yahoo.co.jp>
index 3098d03..51b4923 100644 (file)
@@ -103,7 +103,7 @@ or aufs3-standalone.
 The aufs3-linux tree includes the whole linux mainline GIT tree,
 git://git.kernel.org/.../torvalds/linux.git.
 And you cannot select CONFIG_AUFS_FS=m for this version, eg. you cannot
-build aufs3 as an externel kernel module.
+build aufs3 as an external kernel module.
 
 On the other hand, the aufs3-standalone tree has only aufs source files
 and necessary patches, and you can select CONFIG_AUFS_FS=m.
@@ -115,20 +115,20 @@ represents the linux kernel version, "linux-3.x". For instance,
 
 o aufs3-linux tree
 $ git clone --reference /your/linux/git/tree \
-       git://aufs.git.sourceforge.net/gitroot/aufs/aufs3-linux.git \
+       git://git.code.sf.net/p/aufs/aufs3-linux aufs-aufs3-linux \
        aufs3-linux.git
 - if you don't have linux GIT tree, then remove "--reference ..."
 $ cd aufs3-linux.git
 $ git checkout origin/aufs3.0
 
 o aufs3-standalone tree
-$ git clone git://aufs.git.sourceforge.net/gitroot/aufs/aufs3-standalone.git \
+$ git clone git://git.code.sf.net/p/aufs/aufs3-standalone \
        aufs3-standalone.git
 $ cd aufs3-standalone.git
 $ git checkout origin/aufs3.0
 
 o aufs-util tree
-$ git clone git://aufs.git.sourceforge.net/gitroot/aufs/aufs-util.git \
+$ git clone git://git.code.sf.net/p/aufs/aufs-util \
        aufs-util.git
 $ cd aufs-util.git
 $ git checkout origin/aufs3.0
@@ -147,7 +147,7 @@ nearest lower number.
 
 For (an unreleased) example:
 If you are using "linux-3.10" and the "aufs3.10" branch
-does not exit in aufs-util repository, then "aufs3.9", "aufs3.8"
+does not exist in aufs-util repository, then "aufs3.9", "aufs3.8"
 or something numerically smaller is the branch for your kernel.
 
 Also you can view all branches by
@@ -168,44 +168,44 @@ There are several ways to build.
 1.
 - apply ./aufs3-kbuild.patch to your kernel source files.
 - apply ./aufs3-base.patch too.
-- apply ./aufs3-proc_map.patch too, if you want to make /proc/PID/maps (and
-  others including lsof(1)) show the file path on aufs instead of the
-  path on the branch fs.
+- apply ./aufs3-mmap.patch too.
 - apply ./aufs3-standalone.patch too, if you have a plan to set
   CONFIG_AUFS_FS=m. otherwise you don't need ./aufs3-standalone.patch.
 - copy ./{Documentation,fs,include/linux/aufs_type.h} files to your
-  kernel source tree. Never copy ./include/linux/Kbuild.
+  kernel source tree. Never copy $PWD/include/linux/Kbuild.
 - enable CONFIG_EXPERIMENTAL and CONFIG_AUFS_FS, you can select either
   =m or =y.
 - and build your kernel as usual.
 - install the built kernel.
-- install the header files too by "make headers_install".
+- install the header files too by "make headers_install" to the
+  directory where you specify. By default, it is $PWD/usr.
+  "make help" shows a brief note for headers_install.
 - and reboot your system.
 
 2.
 - module only (CONFIG_AUFS_FS=m).
 - apply ./aufs3-base.patch to your kernel source files.
-- apply ./aufs3-proc_map.patch too to your kernel source files,
-  if you want to make /proc/PID/maps (and others including lsof(1)) show
-  the file path on aufs instead of the path on the branch fs.
+- apply ./aufs3-mmap.patch too.
 - apply ./aufs3-standalone.patch too.
 - build your kernel, don't forget "make headers_install", and reboot.
 - edit ./config.mk and set other aufs configurations if necessary.
-  Note: You should read ./fs/aufs/Kconfig carefully which describes
+  Note: You should read $PWD/fs/aufs/Kconfig carefully which describes
   every aufs configurations.
 - build the module by simple "make".
 - you can specify ${KDIR} make variable which points to your kernel
   source tree.
 - install the files
   + run "make install" to install the aufs module, or copy the built
-    ./aufs.ko to /lib/modules/... and run depmod -a (or reboot simply).
-  + run "make headers_install" to install the aufs header file (you can
-    specify DESTDIR), or copty ./usr/include/linux/aufs_type.h to
-    /usr/include/linux or wherever you like.
+    $PWD/aufs.ko to /lib/modules/... and run depmod -a (or reboot simply).
+  + run "make install_headers" (instead of headers_install) to install
+    the modified aufs header file (you can specify DESTDIR which is
+    available in aufs standalone version's Makefile only), or copy
+    $PWD/usr/include/linux/aufs_type.h to /usr/include/linux or wherever
+    you like manually. By default, the target directory is $PWD/usr.
 - no need to apply aufs3-kbuild.patch, nor copying source files to your
   kernel source tree.
 
-Note: The haeder file aufs_type.h is necessary to build aufs-util
+Note: The header file aufs_type.h is necessary to build aufs-util
       as well as "make headers_install" in the kernel source tree.
       headers_install is subject to be forgotten, but it is essentially
       necessary, not only for building aufs-util.
@@ -289,7 +289,7 @@ Tomas Matejicek(slax.org) made a donation (much more than once).
        Since Apr 2010, Tomas M (the author of Slax and Linux Live
        scripts) is making "doubling" donations.
        Unfortunately I cannot list all of the donators, but I really
-       appriciate.
+       appreciate.
        It ends Aug 2010, but the ordinary donation URL is still available.
        <http://sourceforge.net/donate/index.php?group_id=167503>
 Dai Itasaka made a donation (2007/8).
@@ -307,8 +307,18 @@ New Dream Network, LLC (www.dreamhost.com) made a donation (2009/11).
 Pavel Pronskiy made a donation (2011/2).
 Iridium and Inmarsat satellite phone retailer (www.mailasail.com), Nippy
        Networks (Ed Wildgoose) made a donation for hardware (2011/3).
-Max Lekomcev (DOM-TV project) made a donation (2011/7 and 12).
+Max Lekomcev (DOM-TV project) made a donation (2011/7, 12, 2012/3, 6 and
+11).
 Sam Liddicott made a donation (2011/9).
+Era Scarecrow made a donation (2013/4).
+Bor Ratajc made a donation (2013/4).
+Alessandro Gorreta made a donation (2013/4).
+POIRETTE Marc made a donation (2013/4).
+Alessandro Gorreta made a donation (2013/4).
+lauri kasvandik made a donation (2013/5).
+"pemasu from Finland" made a donation (2013/7).
+The Parted Magic Project made a donation (2013/9).
+Pavel Barta made a donation (2013/10).
 
 Thank you very much.
 Donations are always, including future donations, very important and
index 2c1c6f3..e60f8c6 100644 (file)
@@ -1,5 +1,5 @@
 
-# Copyright (C) 2005-2011 Junjiro R. Okajima
+# Copyright (C) 2005-2013 Junjiro R. Okajima
 # 
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
index 2a24edd..f54d654 100644 (file)
@@ -1,5 +1,5 @@
 
-# Copyright (C) 2005-2011 Junjiro R. Okajima
+# Copyright (C) 2005-2013 Junjiro R. Okajima
 # 
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
index 4058b8a..d3ca527 100644 (file)
@@ -1,5 +1,5 @@
 
-# Copyright (C) 2005-2011 Junjiro R. Okajima
+# Copyright (C) 2005-2013 Junjiro R. Okajima
 # 
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
index 45ad571..f85f3a8 100644 (file)
@@ -1,5 +1,5 @@
 
-# Copyright (C) 2005-2011 Junjiro R. Okajima
+# Copyright (C) 2005-2013 Junjiro R. Okajima
 # 
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
index 29993a3..2bb8e58 100644 (file)
@@ -1,5 +1,5 @@
 
-# Copyright (C) 2005-2011 Junjiro R. Okajima
+# Copyright (C) 2005-2013 Junjiro R. Okajima
 # 
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
index 0d0d990..55524d6 100644 (file)
@@ -1,5 +1,5 @@
 
-# Copyright (C) 2005-2011 Junjiro R. Okajima
+# Copyright (C) 2005-2013 Junjiro R. Okajima
 # 
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
index 8493225..ecf42a4 100644 (file)
@@ -1,5 +1,5 @@
 
-# Copyright (C) 2005-2011 Junjiro R. Okajima
+# Copyright (C) 2005-2013 Junjiro R. Okajima
 # 
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
index a0ac3d0..18b889c 100644 (file)
@@ -1,5 +1,5 @@
 
-# Copyright (C) 2005-2011 Junjiro R. Okajima
+# Copyright (C) 2005-2013 Junjiro R. Okajima
 # 
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
index b5d536e..49e9a53 100644 (file)
@@ -1,5 +1,5 @@
 
-# Copyright (C) 2010-2011 Junjiro R. Okajima
+# Copyright (C) 2010-2013 Junjiro R. Okajima
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
index 1b5a726..a21f133 100644 (file)
@@ -1,5 +1,5 @@
 
-# Copyright (C) 2005-2011 Junjiro R. Okajima
+# Copyright (C) 2005-2013 Junjiro R. Okajima
 # 
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
index 33963a8..8adb4f2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
index 4cfe9e2..cca3117 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
 /*
  * 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;
@@ -56,7 +84,12 @@ static void au_br_do_free(struct au_branch *br)
                else
                        break;
 
-       mntput(br->br_mnt);
+       au_br_dflags_restore(br);
+
+       /* recursive lock, s_umount of branch's */
+       lockdep_off();
+       path_put(&br->br_path);
+       lockdep_on();
        kfree(wbr);
        kfree(br);
 }
@@ -253,12 +286,12 @@ static int test_add(struct super_block *sb, struct au_opt_add *add, int remount)
                if ((h_inode->i_mode & S_IALLUGO) != (inode->i_mode & S_IALLUGO)
                    || h_inode->i_uid != inode->i_uid
                    || h_inode->i_gid != inode->i_gid)
-                       pr_warning("uid/gid/perm %s %u/%u/0%o, %u/%u/0%o\n",
-                                  add->pathname,
-                                  inode->i_uid, inode->i_gid,
-                                  (inode->i_mode & S_IALLUGO),
-                                  h_inode->i_uid, h_inode->i_gid,
-                                  (h_inode->i_mode & S_IALLUGO));
+                       pr_warn("uid/gid/perm %s %u/%u/0%o, %u/%u/0%o\n",
+                               add->pathname,
+                               inode->i_uid, inode->i_gid,
+                               (inode->i_mode & S_IALLUGO),
+                               h_inode->i_uid, h_inode->i_gid,
+                               (h_inode->i_mode & S_IALLUGO));
        }
 
 out:
@@ -269,7 +302,7 @@ out:
  * 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;
@@ -287,14 +320,14 @@ static int au_br_init_wh(struct super_block *sb, struct au_branch *br,
                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)
@@ -312,12 +345,11 @@ static int au_br_init_wh(struct super_block *sb, struct au_branch *br,
 }
 
 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);
@@ -329,17 +361,16 @@ static int au_wbr_init(struct au_branch *br, struct super_block *sb,
         * 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;
@@ -355,17 +386,19 @@ static int au_br_init(struct au_branch *br, struct super_block *sb,
        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);
-       br->br_xino_upper = AUFS_XINO_TRUNC_INIT;
        atomic_set(&br->br_xino_running, 0);
        br->br_id = au_new_br_id(sb);
        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;
        }
@@ -380,11 +413,11 @@ static int au_br_init(struct au_branch *br, struct super_block *sb,
        }
 
        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;
 }
@@ -436,17 +469,20 @@ static void au_br_do_add_hip(struct au_iinfo *iinfo, aufs_bindex_t bindex,
                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);
@@ -489,15 +525,15 @@ int au_br_add(struct super_block *sb, struct au_opt_add *add, int remount)
        }
 
        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;
@@ -632,7 +668,7 @@ static int test_inode_busy(struct super_block *sb, aufs_bindex_t bindex,
                        continue;
 
                /* AuDbgInode(i); */
-               if (au_iigen(i) == sigen)
+               if (au_iigen(i, NULL) == sigen)
                        ii_read_lock_child(i);
                else {
                        ii_write_lock_child(i);
@@ -803,6 +839,7 @@ int au_br_del(struct super_block *sb, struct au_opt_del *del, int remount)
                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);
@@ -851,10 +888,10 @@ int au_br_del(struct super_block *sb, struct au_opt_del *del, int remount)
 
 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_warning("failed re-creating base whiteout, %s. (%d)\n",
-                          del->pathname, rerr);
+               pr_warn("failed re-creating base whiteout, %s. (%d)\n",
+                       del->pathname, rerr);
 out:
        return err;
 }
@@ -1088,7 +1125,6 @@ int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount,
 {
        int err, rerr;
        aufs_bindex_t bindex;
-       struct path path;
        struct dentry *root;
        struct au_branch *br;
 
@@ -1108,12 +1144,13 @@ int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount,
                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;
 
@@ -1130,12 +1167,8 @@ int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount,
                                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);
@@ -1148,9 +1181,7 @@ int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount,
                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;
@@ -1159,6 +1190,12 @@ int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount,
        }
 
        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;
        }
index 3870bc3..998a92e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -61,6 +61,18 @@ struct au_wbr {
 /* ext2 has 3 types of operations at least, ext3 has 4 */
 #define AuBrDynOp (AuDyLast * 4)
 
+/* sysfs entries */
+struct au_brsysfs {
+       char                    name[16];
+       struct attribute        attr;
+};
+
+enum {
+       AuBrSysfs_BR,
+       AuBrSysfs_BRID,
+       AuBrSysfs_Last
+};
+
 /* protected by superblock rwsem */
 struct au_branch {
        struct au_xino_file     br_xino;
@@ -68,7 +80,8 @@ struct au_branch {
        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;
@@ -76,7 +89,6 @@ struct au_branch {
        struct au_wbr           *br_wbr;
 
        /* xino truncation */
-       blkcnt_t                br_xino_upper;  /* watermark in blocks */
        atomic_t                br_xino_running;
 
 #ifdef CONFIG_AUFS_HFSNOTIFY
@@ -85,14 +97,28 @@ struct au_branch {
 #endif
 
 #ifdef CONFIG_SYSFS
-       /* an entry under sysfs per mount-point */
-       char                    br_name[8];
-       struct attribute        br_attr;
+       /* entries under sysfs per mount-point */
+       struct au_brsysfs       br_sysfs[AuBrSysfs_Last];
 #endif
 };
 
 /* ---------------------------------------------------------------------- */
 
+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 */
@@ -103,6 +129,9 @@ struct au_branch {
 
 #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;
@@ -120,7 +149,7 @@ static inline int au_br_wh_linkable(int brperm)
 
 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;
 }
@@ -190,13 +219,13 @@ aufs_bindex_t au_sbr_id(struct super_block *sb, aufs_bindex_t bindex)
 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)
index 7bf2672..7c0085f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
 #include <linux/mm.h>
 #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;
+               | S_NOATIME | S_NOCMTIME | S_AUTOMOUNT;
 
-       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;
 }
@@ -83,7 +85,7 @@ void au_cpup_attr_changeable(struct inode *inode)
        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)
@@ -139,18 +141,27 @@ void au_dtime_revert(struct au_dtime *dt)
 
        err = vfsub_notify_change(&dt->dt_h_path, &attr);
        if (unlikely(err))
-               pr_warning("restoring timestamps failed(%d). ignored\n", err);
+               pr_warn("restoring timestamps failed(%d). ignored\n", err);
 }
 
 /* ---------------------------------------------------------------------- */
 
+/* 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;
@@ -159,17 +170,32 @@ int cpup_iattr(struct dentry *dst, aufs_bindex_t bindex, struct dentry *h_src)
        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? */
@@ -235,6 +261,8 @@ static int au_do_copy_file(struct file *dst, struct file *src, loff_t len,
                                wbytes -= b;
                                p += b;
                        }
+                       if (unlikely(err < 0))
+                               break;
                } else {
                        loff_t res;
 
@@ -318,8 +346,7 @@ out:
  * 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_cp_generic *cpg)
 {
        int err, i;
        enum { SRC, DST };
@@ -331,14 +358,14 @@ static int au_cp_regular(struct dentry *dentry, aufs_bindex_t bdst,
                void *label, *label_file;
        } *f, file[] = {
                {
-                       .bindex = bsrc,
+                       .bindex = cpg->bsrc,
                        .flags = O_RDONLY | O_NOATIME | O_LARGEFILE,
                        .file = NULL,
                        .label = &&out,
                        .label_file = &&out_src
                },
                {
-                       .bindex = bdst,
+                       .bindex = cpg->bdst,
                        .flags = O_WRONLY | O_NOATIME | O_LARGEFILE,
                        .file = NULL,
                        .label = &&out_src,
@@ -348,11 +375,12 @@ static int au_cp_regular(struct dentry *dentry, aufs_bindex_t bdst,
        struct super_block *sb;
 
        /* bsrc branch can be ro/rw. */
-       sb = dentry->d_sb;
+       sb = cpg->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(cpg->dentry, f->bindex);
+               f->file = au_h_open(cpg->dentry, f->bindex, f->flags,
+                                   /*file*/NULL);
                err = PTR_ERR(f->file);
                if (IS_ERR(f->file))
                        goto *f->label;
@@ -363,7 +391,7 @@ static int au_cp_regular(struct dentry *dentry, aufs_bindex_t bdst,
 
        /* 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, cpg->len);
 
 out_dst:
        fput(file[DST].file);
@@ -375,27 +403,39 @@ out:
        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_cp_generic *cpg,
+                             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 */
-
-       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_inode = au_h_iptr(cpg->dentry->d_inode, cpg->bsrc);
+       l = i_size_read(h_src_inode);
+       if (cpg->len == -1 || l < cpg->len)
+               cpg->len = l;
+       if (cpg->len) {
+               /* try stopping to update while we are referencing */
+               mutex_lock_nested(&h_src_inode->i_mutex, AuLsc_I_CHILD);
+               au_pin_hdir_unlock(cpg->pin);
+
+               h_src_dentry = au_h_dptr(cpg->dentry, cpg->bsrc);
+               h_src_mnt = au_sbr_mnt(cpg->dentry->d_sb, cpg->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(cpg);
+               mutex_unlock(&h_src_inode->i_mutex);
+               rerr = au_pin_hdir_relock(cpg->pin);
+               if (!err && rerr)
+                       err = rerr;
        }
 
 out:
@@ -438,17 +478,15 @@ out:
        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_cp_generic *cpg, struct dentry *dst_parent,
+              struct au_cpup_reg_attr *h_src_attr)
 {
        int err;
        umode_t mode;
        unsigned int mnt_flags;
        unsigned char isdir;
-       const unsigned char do_dt = !!au_ftest_cpup(flags, DTIME);
+       const unsigned char do_dt = !!au_ftest_cpup(cpg->flags, DTIME);
        struct au_dtime dt;
        struct path h_path;
        struct dentry *h_src, *h_dst, *h_parent;
@@ -456,19 +494,22 @@ int cpup_entry(struct dentry *dentry, aufs_bindex_t bdst,
        struct super_block *sb;
 
        /* bsrc branch can be ro/rw. */
-       h_src = au_h_dptr(dentry, bsrc);
+       h_src = au_h_dptr(cpg->dentry, cpg->bsrc);
        h_inode = h_src->d_inode;
-       AuDebugOn(h_inode != au_h_iptr(dentry->d_inode, bsrc));
+       AuDebugOn(h_inode != au_h_iptr(cpg->dentry->d_inode, cpg->bsrc));
 
        /* try stopping to be referenced while we are creating */
-       h_dst = au_h_dptr(dentry, bdst);
+       h_dst = au_h_dptr(cpg->dentry, cpg->bdst);
+       if (au_ftest_cpup(cpg->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 = cpg->dentry->d_sb;
+       h_path.mnt = au_sbr_mnt(sb, cpg->bdst);
        if (do_dt) {
                h_path.dentry = h_parent;
                au_dtime_store(&dt, dst_parent, &h_path);
@@ -479,13 +520,9 @@ int cpup_entry(struct dentry *dentry, aufs_bindex_t bdst,
        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);
                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(cpg, h_src_attr);
                break;
        case S_IFDIR:
                isdir = 1;
@@ -495,10 +532,10 @@ int cpup_entry(struct dentry *dentry, aufs_bindex_t bdst,
                         * strange behaviour from the users view,
                         * particularry setattr case
                         */
-                       if (au_ibstart(dst_parent->d_inode) == bdst)
+                       if (au_ibstart(dst_parent->d_inode) == cpg->bdst)
                                au_cpup_attr_nlink(dst_parent->d_inode,
                                                   /*force*/1);
-                       au_cpup_attr_nlink(dentry->d_inode, /*force*/1);
+                       au_cpup_attr_nlink(cpg->dentry->d_inode, /*force*/1);
                }
                break;
        case S_IFLNK:
@@ -523,10 +560,10 @@ int cpup_entry(struct dentry *dentry, aufs_bindex_t bdst,
            && au_opt_test(mnt_flags, XINO)
            && h_inode->i_nlink == 1
            /* todo: unnecessary? */
-           /* && dentry->d_inode->i_nlink == 1 */
-           && bdst < bsrc
-           && !au_ftest_cpup(flags, KEEPLINO))
-               au_xino_write(sb, bsrc, h_inode->i_ino, /*ino*/0);
+           /* && cpg->dentry->d_inode->i_nlink == 1 */
+           && cpg->bdst < cpg->bsrc
+           && !au_ftest_cpup(cpg->flags, KEEPLINO))
+               au_xino_write(sb, cpg->bsrc, h_inode->i_ino, /*ino*/0);
                /* ignore this error */
 
        if (do_dt)
@@ -534,160 +571,232 @@ int cpup_entry(struct dentry *dentry, aufs_bindex_t bdst,
        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_cp_generic *cpg, struct dentry *dst_parent)
 {
        int err, rerr;
        aufs_bindex_t old_ibstart;
        unsigned char isdir, plink;
-       struct au_dtime dt;
-       struct path h_path;
        struct dentry *h_src, *h_dst, *h_parent;
        struct inode *dst_inode, *h_dir, *inode;
        struct super_block *sb;
+       struct au_branch *br;
+       /* to reuduce stack size */
+       struct {
+               struct au_dtime dt;
+               struct path h_path;
+               struct au_cpup_reg_attr h_src_attr;
+       } *a;
 
-       AuDebugOn(bsrc <= bdst);
+       AuDebugOn(cpg->bsrc <= cpg->bdst);
 
-       sb = dentry->d_sb;
-       h_path.mnt = au_sbr_mnt(sb, bdst);
-       h_dst = au_h_dptr(dentry, bdst);
+       err = -ENOMEM;
+       a = kmalloc(sizeof(*a), GFP_NOFS);
+       if (unlikely(!a))
+               goto out;
+       a->h_src_attr.valid = 0;
+
+       sb = cpg->dentry->d_sb;
+       br = au_sbr(sb, cpg->bdst);
+       a->h_path.mnt = au_br_mnt(br);
+       h_dst = au_h_dptr(cpg->dentry, cpg->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(cpg->dentry, cpg->bsrc);
+       inode = cpg->dentry->d_inode;
 
        if (!dst_parent)
-               dst_parent = dget_parent(dentry);
+               dst_parent = dget_parent(cpg->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, cpg->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);
-                       goto out;
+                               dst_inode->i_ino, inode->i_ino, cpg->bdst);
+                       goto out_parent;
                }
 
                if (dst_inode->i_nlink) {
-                       const int do_dt = au_ftest_cpup(flags, DTIME);
+                       const int do_dt = au_ftest_cpup(cpg->flags, DTIME);
 
-                       h_src = au_plink_lkup(inode, bdst);
+                       h_src = au_plink_lkup(inode, cpg->bdst);
                        err = PTR_ERR(h_src);
                        if (IS_ERR(h_src))
-                               goto out;
+                               goto out_parent;
                        if (unlikely(!h_src->d_inode)) {
                                err = -EIO;
                                AuIOErr("i%lu exists on a upper branch "
                                        "but not pseudo-linked\n",
                                        inode->i_ino);
                                dput(h_src);
-                               goto out;
+                               goto out_parent;
                        }
 
                        if (do_dt) {
-                               h_path.dentry = h_parent;
-                               au_dtime_store(&dt, dst_parent, &h_path);
+                               a->h_path.dentry = h_parent;
+                               au_dtime_store(&a->dt, dst_parent, &a->h_path);
                        }
-                       h_path.dentry = h_dst;
-                       err = vfsub_link(h_src, h_dir, &h_path);
+
+                       a->h_path.dentry = h_dst;
+                       err = vfsub_link(h_src, h_dir, &a->h_path);
+                       if (!err && au_ftest_cpup(cpg->flags, RENAME))
+                               err = au_do_ren_after_cpup
+                                       (cpg->dentry, cpg->bdst, &a->h_path);
                        if (do_dt)
-                               au_dtime_revert(&dt);
+                               au_dtime_revert(&a->dt);
                        dput(h_src);
-                       goto out;
+                       goto out_parent;
                } else
                        /* todo: cpup_wh_file? */
                        /* udba work */
                        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(cpg, dst_parent, &a->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(cpg->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;
+       err = cpup_iattr(cpg->dentry, cpg->bdst, h_src, &a->h_src_attr);
+       if (unlikely(err)) {
+               /* todo: necessary? */
+               /* au_pin_hdir_relock(cpg->pin); */ /* ignore an error */
+               mutex_unlock(&dst_inode->i_mutex);
+               goto out_rev;
+       }
+
+       if (cpg->bdst < old_ibstart) {
+               if (S_ISREG(inode->i_mode)) {
+                       err = au_dy_iaop(inode, cpg->bdst, dst_inode);
+                       if (unlikely(err)) {
+                               /* ignore an error */
+                               /* au_pin_hdir_relock(cpg->pin); */
+                               mutex_unlock(&dst_inode->i_mutex);
+                               goto out_rev;
                        }
-                       au_set_ibstart(inode, bdst);
                }
-               au_set_h_iptr(inode, bdst, au_igrab(dst_inode),
-                             au_hi_flags(inode, isdir));
-               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 */
+               au_set_ibstart(inode, cpg->bdst);
+       }
+       au_set_h_iptr(inode, cpg->bdst, au_igrab(dst_inode),
+                     au_hi_flags(inode, isdir));
+
+       /* todo: necessary? */
+       /* err = au_pin_hdir_relock(cpg->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, cpg->bdst, h_dst);
+
+       if (au_ftest_cpup(cpg->flags, RENAME)) {
+               a->h_path.dentry = h_dst;
+               err = au_do_ren_after_cpup(cpg->dentry, cpg->bdst, &a->h_path);
        }
+       if (!err)
+               goto out_parent; /* 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);
-       au_dtime_revert(&dt);
+       a->h_path.dentry = h_parent;
+       au_dtime_store(&a->dt, dst_parent, &a->h_path);
+       a->h_path.dentry = h_dst;
+       rerr = 0;
+       if (h_dst->d_inode) {
+               if (!isdir)
+                       rerr = vfsub_unlink(h_dir, &a->h_path, /*force*/0);
+               else
+                       rerr = vfsub_rmdir(h_dir, &a->h_path);
+       }
+       au_dtime_revert(&a->dt);
        if (rerr) {
                AuIOErr("failed removing broken entry(%d, %d)\n", err, rerr);
                err = -EIO;
        }
-
-out:
+out_parent:
        dput(dst_parent);
+       kfree(a);
+out:
        return err;
 }
 
+#if 0 /* unused */
 struct au_cpup_single_args {
        int *errp;
-       struct dentry *dentry;
-       aufs_bindex_t bdst, bsrc;
-       loff_t len;
-       unsigned int flags;
+       struct au_cp_generic *cpg;
        struct dentry *dst_parent;
 };
 
 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->cpg->pin);
+       *a->errp = au_cpup_single(a->cpg, a->dst_parent);
+       au_pin_hdir_release(a->cpg->pin);
 }
+#endif
 
 /*
  * prevent SIGXFSZ in copy-up.
  * testing CAP_MKNOD is for generic fs,
  * but CAP_FSETID is for xfs only, currently.
  */
-static int au_cpup_sio_test(struct super_block *sb, umode_t mode)
+static int au_cpup_sio_test(struct au_pin *pin, umode_t mode)
 {
        int do_sio;
+       struct super_block *sb;
+       struct inode *h_dir;
 
        do_sio = 0;
+       sb = au_pinned_parent(pin)->d_sb;
        if (!au_wkq_test()
            && (!au_sbi(sb)->si_plink_maint_pid
                || au_plink_maint(sb, AuLock_NOPLM))) {
@@ -704,30 +813,29 @@ static int au_cpup_sio_test(struct super_block *sb, umode_t mode)
                if (!do_sio)
                        do_sio = ((mode & (S_ISUID | S_ISGID))
                                  && !capable(CAP_FSETID));
+               /* this workaround may be removed in the future */
+               if (!do_sio) {
+                       h_dir = au_pinned_h_dir(pin);
+                       do_sio = h_dir->i_mode & S_ISVTX;
+               }
        }
 
        return do_sio;
 }
 
-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)
+#if 0 /* unused */
+int au_sio_cpup_single(struct au_cp_generic *cpg, struct dentry *dst_parent)
 {
        int err, wkq_err;
        struct dentry *h_dentry;
 
-       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);
+       h_dentry = au_h_dptr(cpg->dentry, cpg->bsrc);
+       if (!au_cpup_sio_test(pin, h_dentry->d_inode->i_mode))
+               err = au_cpup_single(cpg, dst_parent);
        else {
                struct au_cpup_single_args args = {
                        .errp           = &err,
-                       .dentry         = dentry,
-                       .bdst           = bdst,
-                       .bsrc           = bsrc,
-                       .len            = len,
-                       .flags          = flags,
+                       .cpg            = cpg,
                        .dst_parent     = dst_parent
                };
                wkq_err = au_wkq_wait(au_call_cpup_single, &args);
@@ -737,31 +845,47 @@ int au_sio_cpup_single(struct dentry *dentry, aufs_bindex_t bdst,
 
        return err;
 }
+#endif
 
 /*
  * copyup the @dentry from the first active lower branch to @bdst,
  * using au_cpup_single().
  */
-static int au_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
-                         unsigned int flags)
+static int au_cpup_simple(struct au_cp_generic *cpg)
 {
        int err;
+       unsigned int flags_orig;
        aufs_bindex_t bsrc, bend;
+       struct dentry *dentry, *h_dentry;
+
+       dentry = cpg->dentry;
+       DiMustWriteLock(dentry);
 
        bend = au_dbend(dentry);
-       for (bsrc = bdst + 1; bsrc <= bend; bsrc++)
-               if (au_h_dptr(dentry, bsrc))
-                       break;
+       if (cpg->bsrc < 0) {
+               for (bsrc = cpg->bdst + 1; bsrc <= bend; bsrc++) {
+                       h_dentry = au_h_dptr(dentry, bsrc);
+                       if (h_dentry) {
+                               AuDebugOn(!h_dentry->d_inode);
+                               break;
+                       }
+               }
+               AuDebugOn(bsrc > bend);
+               cpg->bsrc = bsrc;
+       }
 
-       err = au_lkup_neg(dentry, bdst);
+       err = au_lkup_neg(dentry, cpg->bdst, /*wh*/1);
        if (!err) {
-               err = au_cpup_single(dentry, bdst, bsrc, len, flags, NULL);
+               flags_orig = cpg->flags;
+               au_fset_cpup(cpg->flags, RENAME);
+               err = au_cpup_single(cpg, NULL);
+               cpg->flags = flags_orig;
                if (!err)
                        return 0; /* success */
 
                /* revert */
-               au_set_h_dptr(dentry, bdst, NULL);
-               au_set_dbstart(dentry, bsrc);
+               au_set_h_dptr(dentry, cpg->bdst, NULL);
+               au_set_dbstart(dentry, cpg->bsrc);
        }
 
        return err;
@@ -769,37 +893,44 @@ static int au_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
 
 struct au_cpup_simple_args {
        int *errp;
-       struct dentry *dentry;
-       aufs_bindex_t bdst;
-       loff_t len;
-       unsigned int flags;
+       struct au_cp_generic *cpg;
 };
 
 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->cpg->pin);
+       *a->errp = au_cpup_simple(a->cpg);
+       au_pin_hdir_release(a->cpg->pin);
 }
 
-int au_sio_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
-                      unsigned int flags)
+int au_sio_cpup_simple(struct au_cp_generic *cpg)
 {
        int err, wkq_err;
-       struct dentry *parent;
+       struct dentry *dentry, *parent;
+       struct file *h_file;
        struct inode *h_dir;
 
+       dentry = cpg->dentry;
+       h_file = NULL;
+       if (au_ftest_cpup(cpg->flags, HOPEN)) {
+               AuDebugOn(cpg->bsrc < 0);
+               h_file = au_h_open_pre(dentry, cpg->bsrc);
+               err = PTR_ERR(h_file);
+               if (IS_ERR(h_file))
+                       goto out;
+       }
+
        parent = dget_parent(dentry);
-       h_dir = au_h_iptr(parent->d_inode, bdst);
+       h_dir = au_h_iptr(parent->d_inode, cpg->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);
+           && !au_cpup_sio_test(cpg->pin, dentry->d_inode->i_mode))
+               err = au_cpup_simple(cpg);
        else {
                struct au_cpup_simple_args args = {
                        .errp           = &err,
-                       .dentry         = dentry,
-                       .bdst           = bdst,
-                       .len            = len,
-                       .flags          = flags
+                       .cpg            = cpg
                };
                wkq_err = au_wkq_wait(au_call_cpup_simple, &args);
                if (unlikely(wkq_err))
@@ -807,6 +938,10 @@ int au_sio_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
        }
 
        dput(parent);
+       if (h_file)
+               au_h_open_post(dentry, cpg->bsrc, h_file);
+
+out:
        return err;
 }
 
@@ -815,50 +950,57 @@ int au_sio_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
 /*
  * copyup the deleted file for writing.
  */
-static int au_do_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst,
-                        struct dentry *wh_dentry, struct file *file,
-                        loff_t len)
+static int au_do_cpup_wh(struct au_cp_generic *cpg, struct dentry *wh_dentry,
+                        struct file *file)
 {
        int err;
-       aufs_bindex_t bstart;
-       struct au_dinfo *dinfo;
+       unsigned int flags_orig;
+       aufs_bindex_t bsrc_orig;
        struct dentry *h_d_dst, *h_d_start;
+       struct au_dinfo *dinfo;
        struct au_hdentry *hdp;
 
-       dinfo = au_di(dentry);
+       dinfo = au_di(cpg->dentry);
        AuRwMustWriteLock(&dinfo->di_rwsem);
 
-       bstart = dinfo->di_bstart;
+       bsrc_orig = cpg->bsrc;
+       cpg->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_dst = hdp[0 + cpg->bdst].hd_dentry;
+       dinfo->di_bstart = cpg->bdst;
+       hdp[0 + cpg->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 + cpg->bsrc].hd_dentry;
+               hdp[0 + cpg->bsrc].hd_dentry = au_hf_top(file)->f_dentry;
        }
-       err = au_cpup_single(dentry, bdst, bstart, len, !AuCpup_DTIME,
-                            /*h_parent*/NULL);
+       flags_orig = cpg->flags;
+       cpg->flags = !AuCpup_DTIME;
+       err = au_cpup_single(cpg, /*h_parent*/NULL);
+       cpg->flags = flags_orig;
        if (file) {
                if (!err)
                        err = au_reopen_nondir(file);
-               hdp[0 + bstart].hd_dentry = h_d_start;
+               hdp[0 + cpg->bsrc].hd_dentry = h_d_start;
        }
-       hdp[0 + bdst].hd_dentry = h_d_dst;
-       dinfo->di_bstart = bstart;
+       hdp[0 + cpg->bdst].hd_dentry = h_d_dst;
+       dinfo->di_bstart = cpg->bsrc;
+       cpg->bsrc = bsrc_orig;
 
        return err;
 }
 
-static int au_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
-                     struct file *file)
+static int au_cpup_wh(struct au_cp_generic *cpg, struct file *file)
 {
        int err;
+       aufs_bindex_t bdst;
        struct au_dtime dt;
-       struct dentry *parent, *h_parent, *wh_dentry;
+       struct dentry *dentry, *parent, *h_parent, *wh_dentry;
        struct au_branch *br;
        struct path h_path;
 
+       dentry = cpg->dentry;
+       bdst = cpg->bdst;
        br = au_sbr(dentry->d_sb, bdst);
        parent = dget_parent(dentry);
        h_parent = au_h_dptr(parent, bdst);
@@ -868,9 +1010,9 @@ static int au_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
                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(cpg, wh_dentry, file);
        if (unlikely(err))
                goto out_wh;
 
@@ -897,32 +1039,37 @@ out:
 
 struct au_cpup_wh_args {
        int *errp;
-       struct dentry *dentry;
-       aufs_bindex_t bdst;
-       loff_t len;
+       struct au_cp_generic *cpg;
        struct file *file;
 };
 
 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->cpg->pin);
+       *a->errp = au_cpup_wh(a->cpg, a->file);
+       au_pin_hdir_release(a->cpg->pin);
 }
 
-int au_sio_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
-                  struct file *file)
+int au_sio_cpup_wh(struct au_cp_generic *cpg, struct file *file)
 {
        int err, wkq_err;
-       struct dentry *parent, *h_orph, *h_parent, *h_dentry;
-       struct inode *dir, *h_dir, *h_tmpdir, *h_inode;
+       aufs_bindex_t bdst;
+       struct dentry *dentry, *parent, *h_orph, *h_parent, *h_dentry;
+       struct inode *dir, *h_dir, *h_tmpdir;
        struct au_wbr *wbr;
+       struct au_pin wh_pin, *pin_orig;
 
+       dentry = cpg->dentry;
+       bdst = cpg->bdst;
        parent = dget_parent(dentry);
        dir = parent->d_inode;
        h_orph = NULL;
        h_parent = NULL;
        h_dir = au_igrab(au_h_iptr(dir, bdst));
        h_tmpdir = h_dir;
+       pin_orig = NULL;
        if (!h_dir->i_nlink) {
                wbr = au_sbr(dentry->d_sb, bdst)->br_wbr;
                h_orph = wbr->wbr_orph;
@@ -932,28 +1079,26 @@ int au_sio_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
                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()? */
+
+               pin_orig = cpg->pin;
+               au_pin_init(&wh_pin, dentry, bdst, AuLsc_DI_PARENT,
+                           AuLsc_I_PARENT3, cpg->pin->udba, AuPin_DI_LOCKED);
+               cpg->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);
+           && !au_cpup_sio_test(cpg->pin, dentry->d_inode->i_mode))
+               err = au_cpup_wh(cpg, file);
        else {
                struct au_cpup_wh_args args = {
                        .errp   = &err,
-                       .dentry = dentry,
-                       .bdst   = bdst,
-                       .len    = len,
+                       .cpg    = cpg,
                        .file   = file
                };
                wkq_err = au_wkq_wait(au_call_cpup_wh, &args);
@@ -966,6 +1111,8 @@ int au_sio_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
                /* todo: au_h_open_post()? */
                au_set_h_iptr(dir, bdst, au_igrab(h_dir), /*flags*/0);
                au_set_h_dptr(parent, bdst, h_parent);
+               AuDebugOn(!pin_orig);
+               cpg->pin = pin_orig;
        }
        iput(h_dir);
        dput(parent);
@@ -981,6 +1128,7 @@ int au_sio_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
 /* 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)
 {
@@ -1026,7 +1174,7 @@ int au_cp_dirs(struct dentry *dentry, aufs_bindex_t bdst,
                        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);
                        }
                }
@@ -1043,10 +1191,19 @@ out:
 }
 
 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);
+       struct au_cp_generic cpg = {
+               .dentry = dentry,
+               .bdst   = bdst,
+               .bsrc   = -1,
+               .len    = 0,
+               .pin    = pin,
+               .flags  = AuCpup_DTIME
+       };
+       return au_sio_cpup_simple(&cpg);
 }
 
 int au_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst)
index 2e4fbe0..40eef8c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -29,8 +29,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);
@@ -39,10 +40,21 @@ void au_cpup_attr_all(struct inode *inode, int force);
 
 /* ---------------------------------------------------------------------- */
 
+struct au_cp_generic {
+       struct dentry   *dentry;
+       aufs_bindex_t   bdst, bsrc;
+       loff_t          len;
+       struct au_pin   *pin;
+       unsigned int    flags;
+};
+
 /* cpup flags */
 #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 AuCpup_HOPEN   (1 << 3)        /* call h_open_pre/post() in cpup */
+
 #define au_ftest_cpup(flags, name)     ((flags) & AuCpup_##name)
 #define au_fset_cpup(flags, name) \
        do { (flags) |= AuCpup_##name; } while (0)
@@ -50,16 +62,12 @@ void au_cpup_attr_all(struct inode *inode, int force);
        do { (flags) &= ~AuCpup_##name; } while (0)
 
 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);
-int au_sio_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
-                      unsigned int flags);
-int au_sio_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
-                  struct file *file);
+int au_sio_cpup_simple(struct au_cp_generic *cpg);
+int au_sio_cpup_wh(struct au_cp_generic *cpg, struct file *file);
 
 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);
index d1dbfd2..6ba07ec 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -96,6 +96,101 @@ static ssize_t dbgaufs_xi_read(struct file *file, char __user *buf,
 
 /* ---------------------------------------------------------------------- */
 
+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;
@@ -175,10 +270,8 @@ void dbgaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex)
        for (; bindex <= bend; bindex++) {
                br = au_sbr(sb, bindex);
                xi = &br->br_xino;
-               if (xi->xi_dbgaufs) {
-                       debugfs_remove(xi->xi_dbgaufs);
-                       xi->xi_dbgaufs = NULL;
-               }
+               debugfs_remove(xi->xi_dbgaufs);
+               xi->xi_dbgaufs = NULL;
        }
 }
 
@@ -305,6 +398,12 @@ int dbgaufs_si_init(struct au_sbinfo *sbinfo)
        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 */
index 8fe4974..f418c92 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
index 5e8321e..5b3d904 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
index 3d10731..53dcbd7 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
index 9c9091d..7d42feb 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
 #include <linux/vt_kern.h>
 #include "aufs.h"
 
-int aufs_debug;
+/* Returns 0, or -errno.  arg is in kp->arg. */
+static int param_atomic_t_set(const char *val, const struct kernel_param *kp)
+{
+       int err, n;
+
+       err = kstrtoint(val, 0, &n);
+       if (!err) {
+               if (n > 0)
+                       au_debug_on();
+               else
+                       au_debug_off();
+       }
+       return err;
+}
+
+/* Returns length written or -errno.  Buffer is 4k (ie. be short!) */
+static int param_atomic_t_get(char *buffer, const struct kernel_param *kp)
+{
+       atomic_t *a;
+
+       a = kp->arg;
+       return sprintf(buffer, "%d", atomic_read(a));
+}
+
+static struct kernel_param_ops param_ops_atomic_t = {
+       .set = param_atomic_t_set,
+       .get = param_atomic_t_get
+       /* void (*free)(void *arg) */
+};
+
+atomic_t aufs_debug = ATOMIC_INIT(0);
 MODULE_PARM_DESC(debug, "debug print");
-module_param_named(debug, aufs_debug, int, S_IRUGO | S_IWUSR | S_IWGRP);
+module_param_named(debug, aufs_debug, atomic_t, S_IRUGO | S_IWUSR | S_IWGRP);
 
 char *au_plevel = KERN_DEBUG;
 #define dpri(fmt, ...) do {                                    \
@@ -88,7 +118,7 @@ static int do_pri_inode(aufs_bindex_t bindex, struct inode *inode, int hn,
                return -1;
        }
 
-       /* the type of i_blocks depends upon CONFIG_LSF */
+       /* the type of i_blocks depends upon CONFIG_LBDAF */
        BUILD_BUG_ON(sizeof(inode->i_blocks) != sizeof(unsigned long)
                     && sizeof(inode->i_blocks) != sizeof(u64));
        if (wh) {
@@ -124,7 +154,7 @@ void au_dpri_inode(struct inode *inode)
        if (!iinfo)
                return;
        dpri("i-1: bstart %d, bend %d, gen %d\n",
-            iinfo->ii_bstart, iinfo->ii_bend, au_iigen(inode));
+            iinfo->ii_bstart, iinfo->ii_bend, au_iigen(inode, NULL));
        if (iinfo->ii_bstart < 0)
                return;
        hn = 0;
@@ -256,7 +286,7 @@ static int do_pri_br(aufs_bindex_t bindex, struct au_branch *br)
 
        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;
@@ -297,7 +327,7 @@ void au_dpri_sb(struct super_block *sb)
 
        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 */
@@ -327,8 +357,11 @@ void au_dbg_sleep_jiffy(int jiffy)
 
 void au_dbg_iattr(struct iattr *ia)
 {
-#define AuBit(name)    if (ia->ia_valid & ATTR_ ## name) \
-                               dpri(#name "\n")
+#define AuBit(name)                                    \
+       do {                                            \
+               if (ia->ia_valid & ATTR_ ## name)       \
+                       dpri(#name "\n");               \
+       } while (0)
        AuBit(MODE);
        AuBit(UID);
        AuBit(GID);
@@ -376,14 +409,11 @@ void __au_dbg_verify_dinode(struct dentry *dentry, const char *func, int line)
                        continue;
                h_inode = au_h_iptr(inode, bindex);
                if (unlikely(h_inode != h_dentry->d_inode)) {
-                       int old = au_debug_test();
-                       if (!old)
-                               au_debug(1);
+                       au_debug_on();
                        AuDbg("b%d, %s:%d\n", bindex, func, line);
                        AuDbgDentry(dentry);
                        AuDbgInode(inode);
-                       if (!old)
-                               au_debug(0);
+                       au_debug_off();
                        BUG();
                }
        }
@@ -478,7 +508,7 @@ int __init au_debug_init(void)
        AuDebugOn(destr.len < NAME_MAX);
 
 #ifdef CONFIG_4KSTACKS
-       pr_warning("CONFIG_4KSTACKS is defined.\n");
+       pr_warn("CONFIG_4KSTACKS is defined.\n");
 #endif
 
 #ifdef AuForceNoBrs
index c4d5610..f5b8fbd 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -26,6 +26,7 @@
 #ifdef __KERNEL__
 
 #include <asm/system.h>
+#include <linux/atomic.h>
 #include <linux/module.h>
 #include <linux/kallsyms.h>
 #include <linux/sysrq.h>
 #define AuDebugOn(a)           BUG_ON(a)
 
 /* module parameter */
-extern int aufs_debug;
-static inline void au_debug(int n)
+extern atomic_t aufs_debug;
+static inline void au_debug_on(void)
 {
-       aufs_debug = n;
-       smp_mb();
+       atomic_inc(&aufs_debug);
+}
+static inline void au_debug_off(void)
+{
+       atomic_dec_if_positive(&aufs_debug);
 }
 
 static inline int au_debug_test(void)
 {
-       return aufs_debug;
+       return atomic_read(&aufs_debug) > 0;
 }
 #else
 #define AuDebugOn(a)           do {} while (0)
-AuStubVoid(au_debug, int n)
+AuStubVoid(au_debug_on, void)
+AuStubVoid(au_debug_off, void)
 AuStubInt0(au_debug_test, void)
 #endif /* CONFIG_AUFS_DEBUG */
 
+#define param_check_atomic_t(name, p) __param_check(name, p, atomic_t)
+
 /* ---------------------------------------------------------------------- */
 
 /* debug print */
@@ -64,7 +71,7 @@ AuStubInt0(au_debug_test, void)
 #define AuWarn1(fmt, ...) do { \
        static unsigned char _c; \
        if (!_c++) \
-               pr_warning(fmt, ##__VA_ARGS__); \
+               pr_warn(fmt, ##__VA_ARGS__); \
 } while (0)
 
 #define AuErr1(fmt, ...) do { \
index 11ed8c2..98e94d3 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -60,7 +60,7 @@ struct dentry *au_lkup_one(struct qstr *name, struct dentry *h_parent,
 
        au_h_nd(&h_nd, nd);
        h_nd.path.dentry = h_parent;
-       h_nd.path.mnt = br->br_mnt;
+       h_nd.path.mnt = au_br_mnt(br);
 
        err = vfsub_name_hash(name->name, &h_nd.last, name->len);
        h_dentry = ERR_PTR(err);
@@ -308,15 +308,19 @@ struct dentry *au_sio_lkup_one(struct qstr *name, struct dentry *parent,
 /*
  * 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;
@@ -849,7 +853,7 @@ int au_do_h_d_reval(struct dentry *h_dentry, struct nameidata *nd,
                BUG_ON(bindex > au_dbend(parent));
                h_nd.path.dentry = au_h_dptr(parent, bindex);
                BUG_ON(!h_nd.path.dentry);
-               h_nd.path.mnt = au_sbr(parent->d_sb, bindex)->br_mnt;
+               h_nd.path.mnt = au_sbr_mnt(parent->d_sb, bindex);
                path_get(&h_nd.path);
                valid = reval(h_dentry, &h_nd);
                path_put(&h_nd.path);
index bbd9dc6..c8dd79e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -55,7 +55,7 @@ int au_h_verify(struct dentry *h_dentry, unsigned int udba, struct inode *h_dir,
 
 int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type,
                   struct nameidata *nd);
-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);
 
index 24f22a9..2a92487 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
index ec5a236..ca3ef14 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -33,6 +33,7 @@ void au_add_nlink(struct inode *dir, struct inode *h_dir)
        nlink += h_dir->i_nlink - 2;
        if (h_dir->i_nlink < 2)
                nlink += 2;
+       smp_mb();
        set_nlink(dir, nlink);
 }
 
@@ -46,6 +47,7 @@ void au_sub_nlink(struct inode *dir, struct inode *h_dir)
        nlink -= h_dir->i_nlink - 2;
        if (h_dir->i_nlink < 2)
                nlink -= 2;
+       smp_mb();
        set_nlink(dir, nlink);
 }
 
index dc52a63..fb237ba 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
index 2aedc11..fe323b4 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010-2012 Junjiro R. Okajima
+ * Copyright (C) 2010-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -237,7 +237,7 @@ static struct au_dykey *dy_get(struct au_dynop *op, struct au_branch *br)
 
        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);
index 029b08c..3f2c9aa 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010-2012 Junjiro R. Okajima
+ * Copyright (C) 2010-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
index 962d8e0..505062c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -88,6 +88,21 @@ static int au_test_anon(struct dentry *dentry)
        return !!(dentry->d_flags & DCACHE_DISCONNECTED);
 }
 
+int au_test_nfsd(void)
+{
+       int ret;
+       struct task_struct *tsk = current;
+       char comm[sizeof(tsk->comm)];
+
+       ret = 0;
+       if (tsk->flags & PF_KTHREAD) {
+               get_task_comm(comm, tsk);
+               ret = !strcmp(comm, "nfsd");
+       }
+
+       return ret;
+}
+
 /* ---------------------------------------------------------------------- */
 /* inode generation external table */
 
@@ -220,7 +235,7 @@ static struct dentry *decode_by_ino(struct super_block *sb, ino_t ino,
        sigen = au_sigen(sb);
        if (unlikely(is_bad_inode(inode)
                     || IS_DEADDIR(inode)
-                    || sigen != au_iigen(inode)))
+                    || sigen != au_iigen(inode, NULL)))
                goto out_iput;
 
        dentry = NULL;
@@ -380,7 +395,8 @@ static struct dentry *au_lkup_by_ino(struct path *path, ino_t ino,
        dentry = ERR_PTR(err);
        if (unlikely(err))
                goto out_name;
-       dentry = ERR_PTR(-ENOENT);
+       /* instead of ENOENT */
+       dentry = ERR_PTR(-ESTALE);
        if (!arg.found)
                goto out_name;
 
@@ -492,7 +508,7 @@ struct dentry *decode_by_path(struct super_block *sb, ino_t ino, __u32 *fh,
        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),
@@ -710,7 +726,7 @@ static int aufs_encode_fh(struct dentry *dentry, __u32 *fh, int *max_len,
 
        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_dput;
index 942e71a..d6318f0 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
index 48b8aa1..9eeedd6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -177,12 +177,19 @@ static void au_init_fop_sp(struct file *file)
 static int au_cpup_sp(struct dentry *dentry)
 {
        int err;
-       aufs_bindex_t bcpup;
        struct au_pin pin;
        struct au_wr_dir_args wr_dir_args = {
                .force_btgt     = -1,
                .flags          = 0
        };
+       struct au_cp_generic cpg = {
+               .dentry = dentry,
+               .bdst   = -1,
+               .bsrc   = -1,
+               .len    = -1,
+               .pin    = &pin,
+               .flags  = AuCpup_DTIME
+       };
 
        AuDbg("%.*s\n", AuDLNPair(dentry));
 
@@ -191,15 +198,15 @@ static int au_cpup_sp(struct dentry *dentry)
        err = au_wr_dir(dentry, /*src_dentry*/NULL, &wr_dir_args);
        if (unlikely(err < 0))
                goto out;
-       bcpup = err;
+       cpg.bdst = err;
        err = 0;
-       if (bcpup == au_dbstart(dentry))
+       if (cpg.bdst == au_dbstart(dentry))
                goto out; /* success */
 
-       err = au_pin(&pin, dentry, bcpup, au_opt_udba(dentry->d_sb),
+       err = au_pin(&pin, dentry, cpg.bdst, 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(&cpg);
                au_unpin(&pin);
        }
 
index 0b9aa48..599d73f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -20,6 +20,9 @@
  * handling file/dir, and address_space operation
  */
 
+#ifdef CONFIG_AUFS_DEBUG
+#include <linux/migrate.h>
+#endif
 #include <linux/pagemap.h>
 #include "aufs.h"
 
@@ -64,7 +67,7 @@ struct file *au_h_open(struct dentry *dentry, aufs_bindex_t bindex, int flags,
        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 */
@@ -73,7 +76,7 @@ struct file *au_h_open(struct dentry *dentry, aufs_bindex_t bindex, int flags,
        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 {
@@ -151,13 +154,29 @@ int au_reopen_nondir(struct file *file)
                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);
@@ -199,32 +218,39 @@ static int au_reopen_wh(struct file *file, aufs_bindex_t btgt,
 }
 
 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;
-       struct dentry *dentry, *h_dentry, *hi_wh;
-
-       dentry = file->f_dentry;
-       au_update_dbstart(dentry);
-       inode = dentry->d_inode;
+       struct dentry *h_dentry, *hi_wh;
+       struct au_cp_generic cpg = {
+               .dentry = file->f_dentry,
+               .bdst   = bcpup,
+               .bsrc   = -1,
+               .len    = len,
+               .pin    = pin
+       };
+
+       au_update_dbstart(cpg.dentry);
+       inode = cpg.dentry->d_inode;
        h_inode = NULL;
-       if (au_dbstart(dentry) <= bcpup && au_dbend(dentry) >= bcpup) {
-               h_dentry = au_h_dptr(dentry, bcpup);
+       if (au_dbstart(cpg.dentry) <= bcpup
+           && au_dbend(cpg.dentry) >= bcpup) {
+               h_dentry = au_h_dptr(cpg.dentry, bcpup);
                if (h_dentry)
                        h_inode = h_dentry->d_inode;
        }
        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(&cpg, file);
        else
                /* already copied-up after unlink */
                err = au_reopen_wh(file, bcpup, hi_wh);
 
        if (!err
            && inode->i_nlink > 1
-           && au_opt_test(au_mntflags(dentry->d_sb), PLINK))
-               au_plink_append(inode, bcpup, au_h_dptr(dentry, bcpup));
+           && au_opt_test(au_mntflags(cpg.dentry->d_sb), PLINK))
+               au_plink_append(inode, bcpup, au_h_dptr(cpg.dentry, bcpup));
 
        return err;
 }
@@ -235,81 +261,80 @@ static int au_ready_to_write_wh(struct file *file, loff_t len,
 int au_ready_to_write(struct file *file, loff_t len, struct au_pin *pin)
 {
        int err;
-       aufs_bindex_t bstart, bcpup, dbstart;
-       struct dentry *dentry, *parent, *h_dentry;
-       struct inode *h_inode, *inode;
+       aufs_bindex_t dbstart;
+       struct dentry *parent, *h_dentry;
+       struct inode *inode;
        struct super_block *sb;
        struct file *h_file;
-
-       dentry = file->f_dentry;
-       sb = dentry->d_sb;
-       inode = dentry->d_inode;
+       struct au_cp_generic cpg = {
+               .dentry = file->f_dentry,
+               .bdst   = -1,
+               .bsrc   = -1,
+               .len    = len,
+               .pin    = pin,
+               .flags  = AuCpup_DTIME
+       };
+
+       sb = cpg.dentry->d_sb;
+       inode = cpg.dentry->d_inode;
        AuDebugOn(au_special_file(inode->i_mode));
-       bstart = au_fbstart(file);
-       err = au_test_ro(sb, bstart, inode);
+       cpg.bsrc = au_fbstart(file);
+       err = au_test_ro(sb, cpg.bsrc, inode);
        if (!err && (au_hf_top(file)->f_mode & FMODE_WRITE)) {
-               err = au_pin(pin, dentry, bstart, AuOpt_UDBA_NONE, /*flags*/0);
+               err = au_pin(pin, cpg.dentry, cpg.bsrc, AuOpt_UDBA_NONE,
+                            /*flags*/0);
                goto out;
        }
 
        /* need to cpup or reopen */
-       parent = dget_parent(dentry);
+       parent = dget_parent(cpg.dentry);
        di_write_lock_parent(parent);
-       err = AuWbrCopyup(au_sbi(sb), dentry);
-       bcpup = err;
+       err = AuWbrCopyup(au_sbi(sb), cpg.dentry);
+       cpg.bdst = err;
        if (unlikely(err < 0))
                goto out_dgrade;
        err = 0;
 
-       if (!d_unhashed(dentry) && !au_h_dptr(parent, bcpup)) {
-               err = au_cpup_dirs(dentry, bcpup);
+       if (!d_unhashed(cpg.dentry) && !au_h_dptr(parent, cpg.bdst)) {
+               err = au_cpup_dirs(cpg.dentry, cpg.bdst);
                if (unlikely(err))
                        goto out_dgrade;
        }
 
-       err = au_pin(pin, dentry, bcpup, AuOpt_UDBA_NONE,
+       err = au_pin(pin, cpg.dentry, cpg.bdst, AuOpt_UDBA_NONE,
                     AuPin_DI_LOCKED | AuPin_MNT_WRITE);
        if (unlikely(err))
                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);
+       dbstart = au_dbstart(cpg.dentry);
+       if (dbstart <= cpg.bdst) {
+               h_dentry = au_h_dptr(cpg.dentry, cpg.bdst);
                AuDebugOn(!h_dentry);
-               h_inode = h_dentry->d_inode;
-               AuDebugOn(!h_inode);
-               bstart = bcpup;
+               cpg.bsrc = cpg.bdst;
        }
 
-       if (dbstart <= bcpup            /* just reopen */
-           || !d_unhashed(dentry)      /* copyup and reopen */
+       if (dbstart <= cpg.bdst         /* just reopen */
+           || !d_unhashed(cpg.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)) {
+               h_file = au_h_open_pre(cpg.dentry, cpg.bsrc);
+               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);
+                       if (dbstart > cpg.bdst)
+                               err = au_sio_cpup_simple(&cpg);
                        if (!err)
                                err = au_reopen_nondir(file);
+                       au_h_open_post(cpg.dentry, cpg.bsrc, 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, cpg.bdst, pin);
                di_downgrade_lock(parent, AuLock_IR);
-               mutex_unlock(&h_inode->i_mutex);
        }
 
        if (!err) {
@@ -360,29 +385,35 @@ int au_do_flush(struct file *file, fl_owner_t id,
 static int au_file_refresh_by_inode(struct file *file, int *need_reopen)
 {
        int err;
-       aufs_bindex_t bstart;
        struct au_pin pin;
        struct au_finfo *finfo;
-       struct dentry *dentry, *parent, *hi_wh;
+       struct dentry *parent, *hi_wh;
        struct inode *inode;
        struct super_block *sb;
+       struct au_cp_generic cpg = {
+               .dentry = file->f_dentry,
+               .bdst   = -1,
+               .bsrc   = -1,
+               .len    = -1,
+               .pin    = &pin,
+               .flags  = AuCpup_DTIME
+       };
 
        FiMustWriteLock(file);
 
        err = 0;
        finfo = au_fi(file);
-       dentry = file->f_dentry;
-       sb = dentry->d_sb;
-       inode = dentry->d_inode;
-       bstart = au_ibstart(inode);
-       if (bstart == finfo->fi_btop || IS_ROOT(dentry))
+       sb = cpg.dentry->d_sb;
+       inode = cpg.dentry->d_inode;
+       cpg.bdst = au_ibstart(inode);
+       if (cpg.bdst == finfo->fi_btop || IS_ROOT(cpg.dentry))
                goto out;
 
-       parent = dget_parent(dentry);
-       if (au_test_ro(sb, bstart, inode)) {
+       parent = dget_parent(cpg.dentry);
+       if (au_test_ro(sb, cpg.bdst, inode)) {
                di_read_lock_parent(parent, !AuLock_IR);
-               err = AuWbrCopyup(au_sbi(sb), dentry);
-               bstart = err;
+               err = AuWbrCopyup(au_sbi(sb), cpg.dentry);
+               cpg.bdst = err;
                di_read_unlock(parent, !AuLock_IR);
                if (unlikely(err < 0))
                        goto out_parent;
@@ -390,25 +421,26 @@ static int au_file_refresh_by_inode(struct file *file, int *need_reopen)
        }
 
        di_read_lock_parent(parent, AuLock_IR);
-       hi_wh = au_hi_wh(inode, bstart);
+       hi_wh = au_hi_wh(inode, cpg.bdst);
        if (!S_ISDIR(inode->i_mode)
            && au_opt_test(au_mntflags(sb), PLINK)
            && au_plink_test(inode)
-           && !d_unhashed(dentry)) {
-               err = au_test_and_cpup_dirs(dentry, bstart);
+           && !d_unhashed(cpg.dentry)
+           && cpg.bdst < au_dbstart(cpg.dentry)) {
+               err = au_test_and_cpup_dirs(cpg.dentry, cpg.bdst);
                if (unlikely(err))
                        goto out_unlock;
 
                /* always superio. */
-               err = au_pin(&pin, dentry, bstart, AuOpt_UDBA_NONE,
+               err = au_pin(&pin, cpg.dentry, cpg.bdst, AuOpt_UDBA_NONE,
                             AuPin_DI_LOCKED | AuPin_MNT_WRITE);
-               if (!err)
-                       err = au_sio_cpup_simple(dentry, bstart, -1,
-                                                AuCpup_DTIME);
-               au_unpin(&pin);
+               if (!err) {
+                       err = au_sio_cpup_simple(&cpg);
+                       au_unpin(&pin);
+               }
        } else if (hi_wh) {
                /* already copied-up after unlink */
-               err = au_reopen_wh(file, bstart, hi_wh);
+               err = au_reopen_wh(file, cpg.bdst, hi_wh);
                *need_reopen = 0;
        }
 
@@ -638,7 +670,7 @@ static void aufs_invalidatepage(struct page *page, unsigned long offset)
 static int aufs_releasepage(struct page *page, gfp_t gfp)
 { AuUnsupport(); return 0; }
 static int aufs_migratepage(struct address_space *mapping, struct page *newpage,
-                           struct page *page)
+                           struct page *page, enum migrate_mode mode)
 { AuUnsupport(); return 0; }
 static int aufs_launder_page(struct page *page)
 { AuUnsupport(); return 0; }
index 8bd6e3b..afd27de 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -227,7 +227,7 @@ static inline void au_set_mmapped(struct file *f)
 {
        if (atomic_inc_return(&au_fi(f)->fi_mmapped))
                return;
-       pr_warning("fi_mmapped wrapped around\n");
+       pr_warn("fi_mmapped wrapped around\n");
        while (!atomic_inc_return(&au_fi(f)->fi_mmapped))
                ;
 }
index 3208510..2111355 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -127,7 +127,7 @@ void au_fi_init_once(void *_finfo)
 
 int au_finfo_init(struct file *file, struct au_fidir *fidir)
 {
-       int err, lc_idx;
+       int err;
        struct au_finfo *finfo;
        struct dentry *dentry;
 
@@ -139,10 +139,11 @@ int au_finfo_init(struct file *file, struct au_fidir *fidir)
 
        err = 0;
        au_nfiles_inc(dentry->d_sb);
-       lc_idx = AuLcNonDir_FIINFO;
-       if (fidir)
-               lc_idx = AuLcDir_FIINFO;
-       au_rw_class(&finfo->fi_rwsem, au_lc_key + lc_idx);
+       /* verbose coding for lock class name */
+       if (!fidir)
+               au_rw_class(&finfo->fi_rwsem, au_lc_key + AuLcNonDir_FIINFO);
+       else
+               au_rw_class(&finfo->fi_rwsem, au_lc_key + AuLcDir_FIINFO);
        au_rw_write_lock(&finfo->fi_rwsem);
        finfo->fi_btop = -1;
        finfo->fi_hdir = fidir;
index a6b1037..90d8e30 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -110,15 +110,6 @@ static inline int au_test_ecryptfs(struct super_block *sb __maybe_unused)
 #endif
 }
 
-static inline int au_test_smbfs(struct super_block *sb __maybe_unused)
-{
-#if defined(CONFIG_SMB_FS) || defined(CONFIG_SMB_FS_MODULE)
-       return sb->s_magic == SMB_SUPER_MAGIC;
-#else
-       return 0;
-#endif
-}
-
 static inline int au_test_ocfs2(struct super_block *sb __maybe_unused)
 {
 #if defined(CONFIG_OCFS2_FS) || defined(CONFIG_OCFS2_FS_MODULE)
@@ -157,7 +148,7 @@ static inline int au_test_v9fs(struct super_block *sb __maybe_unused)
 
 static inline int au_test_ext4(struct super_block *sb __maybe_unused)
 {
-#if defined(CONFIG_EXT4DEV_FS) || defined(CONFIG_EXT4DEV_FS_MODULE)
+#if defined(CONFIG_EXT4_FS) || defined(CONFIG_EXT4_FS_MODULE)
        return sb->s_magic == EXT4_SUPER_MAGIC;
 #else
        return 0;
@@ -375,7 +366,6 @@ static inline int au_test_fs_refresh_iattr(struct super_block *sb)
 {
        return au_test_nfs(sb)
                || au_test_fuse(sb)
-               /* || au_test_smbfs(sb) */      /* untested */
                /* || au_test_ocfs2(sb) */      /* untested */
                /* || au_test_btrfs(sb) */      /* untested */
                /* || au_test_coda(sb) */       /* untested */
index c48c315..db8a602 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
 static const __u32 AuHfsnMask = (FS_MOVED_TO | FS_MOVED_FROM | FS_DELETE
                                 | FS_CREATE | FS_EVENT_ON_CHILD);
 static DECLARE_WAIT_QUEUE_HEAD(au_hfsn_wq);
+static __cacheline_aligned_in_smp atomic64_t au_hfsn_ifree = ATOMIC64_INIT(0);
 
 static void au_hfsn_free_mark(struct fsnotify_mark *mark)
 {
        struct au_hnotify *hn = container_of(mark, struct au_hnotify,
                                             hn_mark);
        AuDbg("here\n");
-       hn->hn_mark_dead = 1;
-       smp_mb();
-       wake_up_all(&au_hfsn_wq);
+       au_cache_free_hnotify(hn);
+       smp_mb__before_atomic_dec();
+       atomic64_dec(&au_hfsn_ifree);
+       wake_up(&au_hfsn_wq);
 }
 
 static int au_hfsn_alloc(struct au_hinode *hinode)
@@ -49,7 +51,6 @@ static int au_hfsn_alloc(struct au_hinode *hinode)
        sb = hn->hn_aufs_inode->i_sb;
        bindex = au_br_index(sb, hinode->hi_id);
        br = au_sbr(sb, bindex);
-       hn->hn_mark_dead = 0;
        mark = &hn->hn_mark;
        fsnotify_init_mark(mark, au_hfsn_free_mark);
        mark->mask = AuHfsnMask;
@@ -61,18 +62,20 @@ static int au_hfsn_alloc(struct au_hinode *hinode)
                                 /*mnt*/NULL, /*allow_dups*/1);
 }
 
-static void au_hfsn_free(struct au_hinode *hinode)
+static int au_hfsn_free(struct au_hinode *hinode, struct au_hnotify *hn)
 {
-       struct au_hnotify *hn;
        struct fsnotify_mark *mark;
+       unsigned long long ull;
+
+       ull = atomic64_inc_return(&au_hfsn_ifree);
+       BUG_ON(!ull);
 
-       hn = hinode->hi_notify;
        mark = &hn->hn_mark;
        fsnotify_destroy_mark(mark);
        fsnotify_put_mark(mark);
 
-       /* TODO: bad approach */
-       wait_event(au_hfsn_wq, hn->hn_mark_dead);
+       /* free hn by myself */
+       return 0;
 }
 
 /* ---------------------------------------------------------------------- */
@@ -101,8 +104,11 @@ static void au_hfsn_ctl(struct au_hinode *hinode, int do_set)
 static char *au_hfsn_name(u32 mask)
 {
 #ifdef CONFIG_AUFS_DEBUG
-#define test_ret(flag) if (mask & flag) \
-                               return #flag;
+#define test_ret(flag)                         \
+       do {                                    \
+               if (mask & flag)                \
+                       return #flag;           \
+       } while (0)
        test_ret(FS_ACCESS);
        test_ret(FS_MODIFY);
        test_ret(FS_ATTRIB);
@@ -157,7 +163,7 @@ static int au_hfsn_handle_event(struct fsnotify_group *group,
        h_dir = event->to_tell;
        h_inode = event->inode;
 #ifdef AuDbgHnotify
-       au_debug(1);
+       au_debug_on();
        if (1 || h_child_qstr.len != sizeof(AUFS_XINO_FNAME) - 1
            || strncmp(h_child_qstr.name, AUFS_XINO_FNAME, h_child_qstr.len)) {
                AuDbg("i%lu, mask 0x%x %s, hcname %.*s, hi%lu\n",
@@ -165,7 +171,7 @@ static int au_hfsn_handle_event(struct fsnotify_group *group,
                      AuLNPair(&h_child_qstr), h_inode ? h_inode->i_ino : 0);
                /* WARN_ON(1); */
        }
-       au_debug(0);
+       au_debug_off();
 #endif
 
        AuDebugOn(!inode_mark);
@@ -236,11 +242,21 @@ out:
        return err;
 }
 
+/* ---------------------------------------------------------------------- */
+
+static void au_hfsn_fin(void)
+{
+       AuDbg("au_hfsn_ifree %lld\n", (long long)atomic64_read(&au_hfsn_ifree));
+       wait_event(au_hfsn_wq, !atomic64_read(&au_hfsn_ifree));
+}
+
 const struct au_hnotify_op au_hnotify_op = {
        .ctl            = au_hfsn_ctl,
        .alloc          = au_hfsn_alloc,
        .free           = au_hfsn_free,
 
+       .fin            = au_hfsn_fin,
+
        .reset_br       = au_hfsn_reset_br,
        .fin_br         = au_hfsn_fin_br,
        .init_br        = au_hfsn_init_br
index 9941f76..c77a2a3 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010-2012 Junjiro R. Okajima
+ * Copyright (C) 2010-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -36,7 +36,6 @@ struct file *au_h_open_pre(struct dentry *dentry, aufs_bindex_t bindex)
        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)
index 251588c..5904667 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -58,9 +58,9 @@ void au_hn_free(struct au_hinode *hinode)
 
        hn = hinode->hi_notify;
        if (hn) {
-               au_hnotify_op.free(hinode);
-               au_cache_free_hnotify(hn);
                hinode->hi_notify = NULL;
+               if (au_hnotify_op.free(hinode, hn))
+                       au_cache_free_hnotify(hn);
        }
 }
 
@@ -108,7 +108,7 @@ static int hn_xino(struct inode *inode, struct inode *h_inode)
 
        err = 0;
        if (unlikely(inode->i_ino == AUFS_ROOT_INO)) {
-               pr_warning("branch root dir was changed\n");
+               pr_warn("branch root dir was changed\n");
                goto out;
        }
 
@@ -203,7 +203,7 @@ static int hn_gen_by_inode(char *name, unsigned int nlen, struct inode *inode,
 
        err = 1;
        if (unlikely(inode->i_ino == AUFS_ROOT_INO)) {
-               pr_warning("branch root dir was changed\n");
+               pr_warn("branch root dir was changed\n");
                err = 0;
                goto out;
        }
@@ -259,7 +259,7 @@ static int hn_gen_by_name(struct dentry *dentry, const unsigned int isdir)
        if (IS_ROOT(dentry)
            /* || (inode && inode->i_ino == AUFS_ROOT_INO) */
                ) {
-               pr_warning("branch root dir was changed\n");
+               pr_warn("branch root dir was changed\n");
                return 0;
        }
 
@@ -358,8 +358,8 @@ static int hn_job(struct hn_job_args *a)
        if (au_ftest_hnjob(a->flags, MNTPNT)
            && a->dentry
            && d_mountpoint(a->dentry))
-               pr_warning("mount-point %.*s is removed or renamed\n",
-                          AuDLNPair(a->dentry));
+               pr_warn("mount-point %.*s is removed or renamed\n",
+                       AuDLNPair(a->dentry));
 
        return 0;
 }
@@ -421,7 +421,7 @@ static struct inode *lookup_wlock_by_ino(struct super_block *sb,
                goto out;
 
        if (unlikely(inode->i_ino == AUFS_ROOT_INO)) {
-               pr_warning("wrong root branch\n");
+               pr_warn("wrong root branch\n");
                iput(inode);
                inode = NULL;
                goto out;
index fb0e646..affa6b1 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -113,7 +113,7 @@ static int aufs_permission(struct inode *inode, int mask)
                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)) {
@@ -139,7 +139,7 @@ static int aufs_permission(struct inode *inode, int mask)
                                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);
                }
        }
@@ -158,19 +158,25 @@ static struct dentry *aufs_lookup(struct inode *dir, struct dentry *dentry,
        struct dentry *ret, *parent;
        struct inode *inode;
        struct super_block *sb;
-       int err, npositive, lc_idx;
+       int err, npositive;
 
        IMustLock(dir);
 
+       /* todo: support rcu-walk? */
+       ret = ERR_PTR(-ECHILD);
+       if (nd && (nd->flags & LOOKUP_RCU))
+               goto out;
+
+       ret = ERR_PTR(-ENAMETOOLONG);
+       if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN))
+               goto out;
+
        sb = dir->i_sb;
        err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM);
        ret = ERR_PTR(err);
        if (unlikely(err))
                goto out;
 
-       ret = ERR_PTR(-ENAMETOOLONG);
-       if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN))
-               goto out_si;
        err = au_di_init(dentry);
        ret = ERR_PTR(err);
        if (unlikely(err))
@@ -203,26 +209,32 @@ static struct dentry *aufs_lookup(struct inode *dir, struct dentry *dentry,
        }
 
        ret = d_splice_alias(inode, dentry);
+#if 0
+       if (unlikely(d_need_lookup(dentry))) {
+               spin_lock(&dentry->d_lock);
+               dentry->d_flags &= ~DCACHE_NEED_LOOKUP;
+               spin_unlock(&dentry->d_lock);
+       } else
+#endif
        if (unlikely(IS_ERR(ret) && inode)) {
                ii_write_unlock(inode);
-               lc_idx = AuLcNonDir_IIINFO;
-               if (S_ISLNK(inode->i_mode))
-                       lc_idx = AuLcSymlink_IIINFO;
-               else if (S_ISDIR(inode->i_mode))
-                       lc_idx = AuLcDir_IIINFO;
-               au_rw_class(&au_ii(inode)->ii_rwsem, au_lc_key + lc_idx);
                iput(inode);
+               inode = NULL;
        }
 
 out_unlock:
        di_write_unlock(dentry);
-       if (unlikely(IS_ERR(ret) && inode)) {
-               lc_idx = AuLcNonDir_DIINFO;
-               if (S_ISLNK(inode->i_mode))
-                       lc_idx = AuLcSymlink_DIINFO;
-               else if (S_ISDIR(inode->i_mode))
-                       lc_idx = AuLcDir_DIINFO;
-               au_rw_class(&au_di(dentry)->di_rwsem, au_lc_key + lc_idx);
+       if (inode) {
+               /* verbose coding for lock class name */
+               if (unlikely(S_ISLNK(inode->i_mode)))
+                       au_rw_class(&au_di(dentry)->di_rwsem,
+                                   au_lc_key + AuLcSymlink_DIINFO);
+               else if (unlikely(S_ISDIR(inode->i_mode)))
+                       au_rw_class(&au_di(dentry)->di_rwsem,
+                                   au_lc_key + AuLcDir_DIINFO);
+               else /* likely */
+                       au_rw_class(&au_di(dentry)->di_rwsem,
+                                   au_lc_key + AuLcNonDir_DIINFO);
        }
 out_si:
        si_read_unlock(sb);
@@ -247,16 +259,19 @@ static int au_wr_dir_cpup(struct dentry *dentry, struct dentry *parent,
 
        err = 0;
        if (!au_h_dptr(parent, bcpup)) {
-               if (bstart < bcpup)
+               if (bstart > bcpup)
+                       err = au_cpup_dirs(dentry, bcpup);
+               else if (bstart < bcpup)
                        err = au_cpdown_dirs(dentry, bcpup);
                else
-                       err = au_cpup_dirs(dentry, bcpup);
+                       BUG();
        }
        if (!err && add_entry) {
                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);
 
@@ -286,9 +301,11 @@ int au_wr_dir(struct dentry *dentry, struct dentry *src_dentry,
              struct au_wr_dir_args *args)
 {
        int err;
+       unsigned int flags;
        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;
@@ -304,8 +321,10 @@ int au_wr_dir(struct dentry *dentry, struct dentry *src_dentry,
                        if (src_bstart < bstart)
                                bcpup = src_bstart;
                } else if (add_entry) {
-                       err = AuWbrCreate(sbinfo, dentry,
-                                         au_ftest_wrdir(args->flags, ISDIR));
+                       flags = 0;
+                       if (au_ftest_wrdir(args->flags, ISDIR))
+                               au_fset_wbr(flags, DIR);
+                       err = AuWbrCreate(sbinfo, dentry, flags);
                        bcpup = err;
                }
 
@@ -352,6 +371,85 @@ out:
 
 /* ---------------------------------------------------------------------- */
 
+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)
@@ -366,7 +464,7 @@ void au_unpin(struct au_pin *p)
        if (!p->hdir)
                return;
 
-       au_hn_imtx_unlock(p->hdir);
+       au_pin_hdir_unlock(p);
        if (!au_ftest_pin(p->flags, DI_LOCKED))
                di_read_unlock(p->parent, AuLock_IR);
        iput(p->hdir->hi_inode);
@@ -374,22 +472,21 @@ void au_unpin(struct au_pin *p)
        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 = mnt_want_write(p->h_mnt);
                        if (unlikely(err)) {
                                au_fclr_pin(p->flags, MNT_WRITE);
@@ -399,16 +496,16 @@ int au_do_pin(struct au_pin *p)
                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;
@@ -418,7 +515,7 @@ int au_do_pin(struct au_pin *p)
         * 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);
@@ -428,22 +525,12 @@ int au_do_pin(struct au_pin *p)
        }
 
        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;
+       err = au_pin_hdir_lock(p);
+       if (unlikely(err))
                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 = mnt_want_write(p->h_mnt);
                if (unlikely(err)) {
                        au_fclr_pin(p->flags, MNT_WRITE);
@@ -475,6 +562,11 @@ void au_pin_init(struct au_pin *p, struct dentry *dentry,
        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,
@@ -540,7 +632,6 @@ static int au_pin_and_icpup(struct dentry *dentry, struct iattr *ia,
        aufs_bindex_t bstart, ibstart;
        struct dentry *hi_wh, *parent;
        struct inode *inode;
-       struct file *h_file;
        struct au_wr_dir_args wr_dir_args = {
                .force_btgt     = -1,
                .flags          = 0
@@ -580,13 +671,20 @@ static int au_pin_and_icpup(struct dentry *dentry, struct iattr *ia,
        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);
+                       struct au_cp_generic cpg = {
+                               .dentry = dentry,
+                               .bdst   = a->btgt,
+                               .bsrc   = -1,
+                               .len    = sz,
+                               .pin    = &a->pin
+                       };
+                       err = au_sio_cpup_wh(&cpg, /*file*/NULL);
                        if (unlikely(err))
                                goto out_unlock;
                        hi_wh = au_hi_wh(inode, a->btgt);
@@ -604,13 +702,15 @@ static int au_pin_and_icpup(struct dentry *dentry, struct iattr *ia,
                goto out; /* success */
 
        if (!d_unhashed(dentry)) {
-               h_file = au_h_open_pre(dentry, bstart);
-               if (IS_ERR(h_file)) {
-                       err = PTR_ERR(h_file);
-                       h_file = NULL;
-               } else
-                       err = au_sio_cpup_simple(dentry, a->btgt, sz,
-                                                AuCpup_DTIME);
+               struct au_cp_generic cpg = {
+                       .dentry = dentry,
+                       .bdst   = a->btgt,
+                       .bsrc   = bstart,
+                       .len    = sz,
+                       .pin    = &a->pin,
+                       .flags  = AuCpup_DTIME | AuCpup_HOPEN
+               };
+               err = au_sio_cpup_simple(&cpg);
                if (!err)
                        a->h_path.dentry = au_h_dptr(dentry, a->btgt);
        } else if (!hi_wh)
@@ -619,14 +719,9 @@ static int au_pin_and_icpup(struct dentry *dentry, struct iattr *ia,
                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) {
@@ -634,6 +729,8 @@ out_parent:
                dput(parent);
        }
 out:
+       if (!err)
+               mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD);
        return err;
 }
 
@@ -764,6 +861,7 @@ static void au_refresh_iattr(struct inode *inode, struct kstat *st,
                n = inode->i_nlink;
                n -= nlink;
                n += st->nlink;
+               smp_mb();
                set_nlink(inode, n);
        }
 
index c5255c6..1812d28 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -190,7 +190,7 @@ lock_hdir_lkup_wh(struct dentry *dentry, struct au_dtime *dt,
        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);
        }
@@ -234,46 +234,54 @@ static int add_simple(struct inode *dir, struct dentry *dentry,
        int err;
        aufs_bindex_t bstart;
        unsigned char created;
-       struct au_dtime dt;
-       struct au_pin pin;
-       struct path h_path;
        struct dentry *wh_dentry, *parent;
        struct inode *h_dir;
-       struct au_wr_dir_args wr_dir_args = {
-               .force_btgt     = -1,
-               .flags          = AuWrDir_ADD_ENTRY
-       };
+       /* to reuduce stack size */
+       struct {
+               struct au_dtime dt;
+               struct au_pin pin;
+               struct path h_path;
+               struct au_wr_dir_args wr_dir_args;
+       } *a;
 
        AuDbg("%.*s\n", AuDLNPair(dentry));
        IMustLock(dir);
 
+       err = -ENOMEM;
+       a = kmalloc(sizeof(*a), GFP_NOFS);
+       if (unlikely(!a))
+               goto out;
+       a->wr_dir_args.force_btgt = -1;
+       a->wr_dir_args.flags = AuWrDir_ADD_ENTRY;
+
        parent = dentry->d_parent; /* dir inode is locked */
        err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN);
        if (unlikely(err))
-               goto out;
+               goto out_free;
        err = au_d_may_add(dentry);
        if (unlikely(err))
                goto out_unlock;
        di_write_lock_parent(parent);
-       wh_dentry = lock_hdir_lkup_wh(dentry, &dt, /*src_dentry*/NULL, &pin,
-                                     &wr_dir_args);
+       wh_dentry = lock_hdir_lkup_wh(dentry, &a->dt, /*src_dentry*/NULL,
+                                     &a->pin, &a->wr_dir_args);
        err = PTR_ERR(wh_dentry);
        if (IS_ERR(wh_dentry))
                goto out_parent;
 
        bstart = au_dbstart(dentry);
-       h_path.dentry = au_h_dptr(dentry, bstart);
-       h_path.mnt = au_sbr_mnt(dentry->d_sb, bstart);
-       h_dir = au_pinned_h_dir(&pin);
+       a->h_path.dentry = au_h_dptr(dentry, bstart);
+       a->h_path.mnt = au_sbr_mnt(dentry->d_sb, bstart);
+       h_dir = au_pinned_h_dir(&a->pin);
        switch (arg->type) {
        case Creat:
-               err = vfsub_create(h_dir, &h_path, arg->u.c.mode);
+               err = vfsub_create(h_dir, &a->h_path, arg->u.c.mode);
                break;
        case Symlink:
-               err = vfsub_symlink(h_dir, &h_path, arg->u.s.symname);
+               err = vfsub_symlink(h_dir, &a->h_path, arg->u.s.symname);
                break;
        case Mknod:
-               err = vfsub_mknod(h_dir, &h_path, arg->u.m.mode, arg->u.m.dev);
+               err = vfsub_mknod(h_dir, &a->h_path, arg->u.m.mode,
+                                 arg->u.m.dev);
                break;
        default:
                BUG();
@@ -283,18 +291,18 @@ static int add_simple(struct inode *dir, struct dentry *dentry,
                err = epilog(dir, bstart, wh_dentry, dentry);
 
        /* revert */
-       if (unlikely(created && err && h_path.dentry->d_inode)) {
+       if (unlikely(created && err && a->h_path.dentry->d_inode)) {
                int rerr;
-               rerr = vfsub_unlink(h_dir, &h_path, /*force*/0);
+               rerr = vfsub_unlink(h_dir, &a->h_path, /*force*/0);
                if (rerr) {
                        AuIOErr("%.*s revert failure(%d, %d)\n",
                                AuDLNPair(dentry), err, rerr);
                        err = -EIO;
                }
-               au_dtime_revert(&dt);
+               au_dtime_revert(&a->dt);
        }
 
-       au_unpin(&pin);
+       au_unpin(&a->pin);
        dput(wh_dentry);
 
 out_parent:
@@ -305,6 +313,8 @@ out_unlock:
                d_drop(dentry);
        }
        aufs_read_unlock(dentry, AuLock_DW);
+out_free:
+       kfree(a);
 out:
        return err;
 }
@@ -357,8 +367,14 @@ static int au_cpup_before_link(struct dentry *src_dentry,
 {
        int err;
        struct dentry *h_src_dentry;
-       struct mutex *h_mtx;
-       struct file *h_file;
+       struct au_cp_generic cpg = {
+               .dentry = src_dentry,
+               .bdst   = a->bdst,
+               .bsrc   = a->bsrc,
+               .len    = -1,
+               .pin    = &a->pin,
+               .flags  = AuCpup_DTIME | AuCpup_HOPEN /* | AuCpup_KEEPLINO */
+       };
 
        di_read_lock_parent(a->src_parent, AuLock_IR);
        err = au_test_and_cpup_dirs(src_dentry, a->bdst);
@@ -366,22 +382,13 @@ static int au_cpup_before_link(struct dentry *src_dentry,
                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)) {
-               err = PTR_ERR(h_file);
-               h_file = NULL;
-       } 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);
+
+       err = au_sio_cpup_simple(&cpg);
        au_unpin(&a->pin);
 
 out:
@@ -389,12 +396,14 @@ out:
        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;
 
@@ -406,22 +415,38 @@ static int au_cpup_or_link(struct dentry *src_dentry, struct au_link_args *a)
                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 {
+                       struct au_cp_generic cpg = {
+                               .dentry = dentry,
+                               .bdst   = a->bdst,
+                               .bsrc   = -1,
+                               .len    = -1,
+                               .pin    = &a->pin,
+                               .flags  = AuCpup_KEEPLINO
+                       };
+                       err = au_sio_cpup_simple(&cpg);
+                       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);
@@ -520,7 +545,7 @@ int aufs_link(struct dentry *src_dentry, struct inode *dir,
        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);
@@ -599,6 +624,7 @@ out_unlock:
 out_kfree:
        kfree(a);
 out:
+       AuTraceErr(err);
        return err;
 }
 
index e69c151..f37ed37 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -300,17 +300,25 @@ int aufs_unlink(struct inode *dir, struct dentry *dentry)
 {
        int err;
        aufs_bindex_t bwh, bindex, bstart;
-       struct au_dtime dt;
-       struct au_pin pin;
-       struct path h_path;
        struct inode *inode, *h_dir;
        struct dentry *parent, *wh_dentry;
+       /* to reuduce stack size */
+       struct {
+               struct au_dtime dt;
+               struct au_pin pin;
+               struct path h_path;
+       } *a;
 
        IMustLock(dir);
 
+       err = -ENOMEM;
+       a = kmalloc(sizeof(*a), GFP_NOFS);
+       if (unlikely(!a))
+               goto out;
+
        err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN);
        if (unlikely(err))
-               goto out;
+               goto out_free;
        err = au_d_hashed_positive(dentry);
        if (unlikely(err))
                goto out_unlock;
@@ -325,17 +333,18 @@ int aufs_unlink(struct inode *dir, struct dentry *dentry)
        bindex = -1;
        parent = dentry->d_parent; /* dir inode is locked */
        di_write_lock_parent(parent);
-       wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/0, &bindex, &dt, &pin);
+       wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/0, &bindex, &a->dt,
+                                       &a->pin);
        err = PTR_ERR(wh_dentry);
        if (IS_ERR(wh_dentry))
                goto out_parent;
 
-       h_path.mnt = au_sbr_mnt(dentry->d_sb, bstart);
-       h_path.dentry = au_h_dptr(dentry, bstart);
-       dget(h_path.dentry);
+       a->h_path.mnt = au_sbr_mnt(dentry->d_sb, bstart);
+       a->h_path.dentry = au_h_dptr(dentry, bstart);
+       dget(a->h_path.dentry);
        if (bindex == bstart) {
-               h_dir = au_pinned_h_dir(&pin);
-               err = vfsub_unlink(h_dir, &h_path, /*force*/0);
+               h_dir = au_pinned_h_dir(&a->pin);
+               err = vfsub_unlink(h_dir, &a->h_path, /*force*/0);
        } else {
                /* dir inode is locked */
                h_dir = wh_dentry->d_parent->d_inode;
@@ -349,8 +358,9 @@ int aufs_unlink(struct inode *dir, struct dentry *dentry)
 
                /* update target timestamps */
                if (bindex == bstart) {
-                       vfsub_update_h_iattr(&h_path, /*did*/NULL); /*ignore*/
-                       inode->i_ctime = h_path.dentry->d_inode->i_ctime;
+                       vfsub_update_h_iattr(&a->h_path, /*did*/NULL);
+                       /*ignore*/
+                       inode->i_ctime = a->h_path.dentry->d_inode->i_ctime;
                } else
                        /* todo: this timestamp may be reverted later */
                        inode->i_ctime = h_dir->i_ctime;
@@ -361,19 +371,22 @@ int aufs_unlink(struct inode *dir, struct dentry *dentry)
        if (wh_dentry) {
                int rerr;
 
-               rerr = do_revert(err, dir, bindex, bwh, wh_dentry, dentry, &dt);
+               rerr = do_revert(err, dir, bindex, bwh, wh_dentry, dentry,
+                                &a->dt);
                if (rerr)
                        err = rerr;
        }
 
 out_unpin:
-       au_unpin(&pin);
+       au_unpin(&a->pin);
        dput(wh_dentry);
-       dput(h_path.dentry);
+       dput(a->h_path.dentry);
 out_parent:
        di_write_unlock(parent);
 out_unlock:
        aufs_read_unlock(dentry, AuLock_DW);
+out_free:
+       kfree(a);
 out:
        return err;
 }
@@ -382,17 +395,25 @@ int aufs_rmdir(struct inode *dir, struct dentry *dentry)
 {
        int err, rmdir_later;
        aufs_bindex_t bwh, bindex, bstart;
-       struct au_dtime dt;
-       struct au_pin pin;
        struct inode *inode;
        struct dentry *parent, *wh_dentry, *h_dentry;
        struct au_whtmp_rmdir *args;
+       /* to reuduce stack size */
+       struct {
+               struct au_dtime dt;
+               struct au_pin pin;
+       } *a;
 
        IMustLock(dir);
 
+       err = -ENOMEM;
+       a = kmalloc(sizeof(*a), GFP_NOFS);
+       if (unlikely(!a))
+               goto out;
+
        err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH | AuLock_GEN);
        if (unlikely(err))
-               goto out;
+               goto out_free;
        err = au_alive_dir(dentry);
        if (unlikely(err))
                goto out_unlock;
@@ -416,7 +437,8 @@ int aufs_rmdir(struct inode *dir, struct dentry *dentry)
        bstart = au_dbstart(dentry);
        bwh = au_dbwh(dentry);
        bindex = -1;
-       wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/1, &bindex, &dt, &pin);
+       wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/1, &bindex, &a->dt,
+                                       &a->pin);
        err = PTR_ERR(wh_dentry);
        if (IS_ERR(wh_dentry))
                goto out_parent;
@@ -457,13 +479,14 @@ int aufs_rmdir(struct inode *dir, struct dentry *dentry)
        if (wh_dentry) {
                int rerr;
 
-               rerr = do_revert(err, dir, bindex, bwh, wh_dentry, dentry, &dt);
+               rerr = do_revert(err, dir, bindex, bwh, wh_dentry, dentry,
+                                &a->dt);
                if (rerr)
                        err = rerr;
        }
 
 out_unpin:
-       au_unpin(&pin);
+       au_unpin(&a->pin);
        dput(wh_dentry);
        dput(h_dentry);
 out_parent:
@@ -472,6 +495,8 @@ out_parent:
                au_whtmp_rmdir_free(args);
 out_unlock:
        aufs_read_unlock(dentry, AuLock_DW);
+out_free:
+       kfree(a);
 out:
        AuTraceErr(err);
        return err;
index f253bec..dbdcb37 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -208,32 +208,9 @@ static int au_ren_or_cpup(struct au_ren_args *a)
                AuDebugOn(au_dbstart(d) != a->btgt);
                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;
-               struct file *h_file;
+       } else
+               BUG();
 
-               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)) {
-                       err = PTR_ERR(h_file);
-                       h_file = NULL;
-               } 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);
-               if (!err) {
-                       d = a->dst_dentry;
-                       au_set_h_dptr(d, a->btgt, NULL);
-                       au_update_dbstart(d);
-               } else {
-                       au_set_h_dptr(d, a->btgt, NULL);
-                       au_set_dbstart(d, a->src_bstart);
-               }
-       }
        if (!err && a->h_dst)
                /* it will be set to dinfo later */
                dget(a->h_dst);
@@ -254,8 +231,8 @@ static int au_ren_del_whtmp(struct au_ren_args *a)
            || au_test_fs_remote(a->h_dst->d_sb)) {
                err = au_whtmp_rmdir(dir, a->btgt, a->h_dst, &a->whlist);
                if (unlikely(err))
-                       pr_warning("failed removing whtmp dir %.*s (%d), "
-                                  "ignored.\n", AuDLNPair(a->h_dst), err);
+                       pr_warn("failed removing whtmp dir %.*s (%d), "
+                               "ignored.\n", AuDLNPair(a->h_dst), err);
        } else {
                au_nhash_wh_free(&a->thargs->whlist);
                a->thargs->whlist = a->whlist;
@@ -334,31 +311,13 @@ static int do_rename(struct au_ren_args *a)
 
                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);
        }
 
-       /* 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;
-               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)) {
-                       err = PTR_ERR(h_file);
-                       h_file = NULL;
-               } 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);
-               if (unlikely(err))
-                       goto out_whtmp;
-       }
+       BUG_ON(a->dst_h_dentry->d_inode && a->src_bstart != a->btgt);
 
        /* rename by vfs_rename or cpup */
        d = a->dst_dentry;
@@ -614,7 +573,7 @@ static void au_ren_unlock(struct au_ren_args *a)
 
        sb = a->dst_dentry->d_sb;
        if (au_ftest_ren(a->flags, MNT_WRITE))
-               mnt_drop_write(a->br->br_mnt);
+               mnt_drop_write(au_br_mnt(a->br));
        vfsub_unlock_rename(a->src_h_parent, a->src_hdir,
                            a->dst_h_parent, a->dst_hdir);
 }
@@ -644,7 +603,7 @@ static int au_ren_lock(struct au_ren_args *a)
                                  a->dst_h_parent->d_inode, a->dst_h_parent,
                                  a->br);
        if (!err) {
-               err = mnt_want_write(a->br->br_mnt);
+               err = mnt_want_write(au_br_mnt(a->br));
                if (unlikely(err))
                        goto out_unlock;
                au_fset_ren(a->flags, MNT_WRITE);
@@ -907,6 +866,15 @@ int aufs_rename(struct inode *_src_dir, struct dentry *_src_dentry,
        } else if (unlikely(d_unhashed(a->dst_dentry)))
                goto out_unlock;
 
+       /*
+        * is it possible?
+        * yes, it happend (in linux-3.3-rcN) but I don't know why.
+        * there may exist a problem somewhere else.
+        */
+       err = -EINVAL;
+       if (unlikely(a->dst_parent->d_inode == a->src_dentry->d_inode))
+               goto out_unlock;
+
        au_fset_ren(a->flags, ISSAMEDIR); /* temporary */
        di_write_lock_parent(a->dst_parent);
 
@@ -915,7 +883,7 @@ int aufs_rename(struct inode *_src_dir, struct dentry *_src_dentry,
        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);
@@ -953,9 +921,37 @@ int aufs_rename(struct inode *_src_dir, struct dentry *_src_dentry,
        if (err)
                au_fset_ren(a->flags, WHSRC);
 
+       /* cpup src */
+       if (a->src_bstart != a->btgt) {
+               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 (!err) {
+                       struct au_cp_generic cpg = {
+                               .dentry = a->src_dentry,
+                               .bdst   = a->btgt,
+                               .bsrc   = a->src_bstart,
+                               .len    = -1,
+                               .pin    = &pin,
+                               .flags  = AuCpup_DTIME | AuCpup_HOPEN
+                       };
+                       AuDebugOn(au_dbstart(a->src_dentry) != a->src_bstart);
+                       err = au_sio_cpup_simple(&cpg);
+                       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))
index d9571c4..b82ebbf 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -110,10 +110,22 @@ void au_set_hi_wh(struct inode *inode, aufs_bindex_t bindex,
        hinode->hi_whdentry = h_wh;
 }
 
-void au_update_iigen(struct inode *inode)
+void au_update_iigen(struct inode *inode, int half)
 {
-       atomic_set(&au_ii(inode)->ii_generation, au_sigen(inode->i_sb));
-       /* smp_mb(); */ /* atomic_set */
+       struct au_iinfo *iinfo;
+       struct au_iigen *iigen;
+       unsigned int sigen;
+
+       sigen = au_sigen(inode->i_sb);
+       iinfo = au_ii(inode);
+       iigen = &iinfo->ii_generation;
+       spin_lock(&iinfo->ii_genspin);
+       iigen->ig_generation = sigen;
+       if (half)
+               au_ig_fset(iigen->ig_flags, HALF_REFRESHED);
+       else
+               au_ig_fclr(iigen->ig_flags, HALF_REFRESHED);
+       spin_unlock(&iinfo->ii_genspin);
 }
 
 /* it may be called at remount time, too */
@@ -164,6 +176,7 @@ void au_icntnr_init_once(void *_c)
        struct au_iinfo *iinfo = &c->iinfo;
        static struct lock_class_key aufs_ii;
 
+       spin_lock_init(&iinfo->ii_genspin);
        au_rw_init(&iinfo->ii_rwsem);
        au_rw_class(&iinfo->ii_rwsem, &aufs_ii);
        inode_init_once(&c->vfs_inode);
@@ -186,8 +199,7 @@ int au_iinfo_init(struct inode *inode)
                for (i = 0; i < nbr; i++)
                        iinfo->ii_hinode[i].hi_id = -1;
 
-               atomic_set(&iinfo->ii_generation, au_sigen(sb));
-               /* smp_mb(); */ /* atomic_set */
+               iinfo->ii_generation.ig_generation = au_sigen(sb);
                iinfo->ii_bstart = -1;
                iinfo->ii_bend = -1;
                iinfo->ii_vdir = NULL;
index df70ede..905d240 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -34,7 +34,7 @@ struct inode *au_igrab(struct inode *inode)
 static void au_refresh_hinode_attr(struct inode *inode, int do_version)
 {
        au_cpup_attr_all(inode, /*force*/0);
-       au_update_iigen(inode);
+       au_update_iigen(inode, /*half*/1);
        if (do_version)
                inode->i_version++;
 }
@@ -253,6 +253,8 @@ out:
 static int reval_inode(struct inode *inode, struct dentry *dentry)
 {
        int err;
+       unsigned int gen;
+       struct au_iigen iigen;
        aufs_bindex_t bindex, bend;
        struct inode *h_inode, *h_dinode;
 
@@ -271,12 +273,20 @@ static int reval_inode(struct inode *inode, struct dentry *dentry)
        bend = au_ibend(inode);
        for (bindex = au_ibstart(inode); bindex <= bend; bindex++) {
                h_inode = au_h_iptr(inode, bindex);
-               if (h_inode && h_inode == h_dinode) {
-                       err = 0;
-                       if (au_iigen_test(inode, au_digen(dentry)))
-                               err = au_refresh_hinode(inode, dentry);
+               if (!h_inode || h_inode != h_dinode)
+                       continue;
+
+               err = 0;
+               gen = au_iigen(inode, &iigen);
+               if (gen == au_digen(dentry)
+                   && !au_ig_ftest(iigen.ig_flags, HALF_REFRESHED))
                        break;
-               }
+
+               /* fully refresh inode using dentry */
+               err = au_refresh_hinode(inode, dentry);
+               if (!err)
+                       au_update_iigen(inode, /*half*/0);
+               break;
        }
 
        if (unlikely(err))
@@ -367,6 +377,17 @@ new_ino:
 
        AuDbg("%lx, new %d\n", inode->i_state, !!(inode->i_state & I_NEW));
        if (inode->i_state & I_NEW) {
+               /* verbose coding for lock class name */
+               if (unlikely(S_ISLNK(h_inode->i_mode)))
+                       au_rw_class(&au_ii(inode)->ii_rwsem,
+                                   au_lc_key + AuLcSymlink_IIINFO);
+               else if (unlikely(S_ISDIR(h_inode->i_mode)))
+                       au_rw_class(&au_ii(inode)->ii_rwsem,
+                                   au_lc_key + AuLcDir_IIINFO);
+               else /* likely */
+                       au_rw_class(&au_ii(inode)->ii_rwsem,
+                                   au_lc_key + AuLcNonDir_IIINFO);
+
                ii_write_lock_new_child(inode);
                err = set_inode(inode, dentry);
                if (!err) {
index 1fe58f6..22e565a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -35,7 +35,6 @@ struct au_hnotify {
 #ifdef CONFIG_AUFS_HFSNOTIFY
        /* never use fsnotify_add_vfsmount_mark() */
        struct fsnotify_mark            hn_mark;
-       int                             hn_mark_dead;
 #endif
        struct inode                    *hn_aufs_inode; /* no get/put */
 #endif
@@ -52,9 +51,22 @@ struct au_hinode {
        struct dentry           *hi_whdentry;
 };
 
+/* ig_flags */
+#define AuIG_HALF_REFRESHED            1
+#define au_ig_ftest(flags, name)       ((flags) & AuIG_##name)
+#define au_ig_fset(flags, name) \
+       do { (flags) |= AuIG_##name; } while (0)
+#define au_ig_fclr(flags, name) \
+       do { (flags) &= ~AuIG_##name; } while (0)
+
+struct au_iigen {
+       __u32           ig_generation, ig_flags;
+};
+
 struct au_vdir;
 struct au_iinfo {
-       atomic_t                ii_generation;
+       spinlock_t              ii_genspin;
+       struct au_iigen         ii_generation;
        struct super_block      *ii_hsb1;       /* no get/put */
 
        struct au_rwsem         ii_rwsem;
@@ -89,8 +101,19 @@ struct au_pin {
        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)
@@ -132,7 +155,8 @@ extern struct inode_operations aufs_iop, aufs_symlink_iop, aufs_dir_iop;
 
 /* 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)
@@ -202,7 +226,7 @@ unsigned int au_hi_flags(struct inode *inode, int isdir);
 void au_set_h_iptr(struct inode *inode, aufs_bindex_t bindex,
                   struct inode *h_inode, unsigned int flags);
 
-void au_update_iigen(struct inode *inode);
+void au_update_iigen(struct inode *inode, int half);
 void au_update_ibrange(struct inode *inode, int do_put_zero);
 
 void au_icntnr_init_once(void *_c);
@@ -310,9 +334,19 @@ static inline void au_icntnr_init(struct au_icntnr *c)
 #endif
 }
 
-static inline unsigned int au_iigen(struct inode *inode)
+static inline unsigned int au_iigen(struct inode *inode, struct au_iigen *iigen)
 {
-       return atomic_read(&au_ii(inode)->ii_generation);
+       unsigned int gen;
+       struct au_iinfo *iinfo;
+
+       iinfo = au_ii(inode);
+       spin_lock(&iinfo->ii_genspin);
+       if (iigen)
+               *iigen = iinfo->ii_generation;
+       gen = iinfo->ii_generation.ig_generation;
+       spin_unlock(&iinfo->ii_genspin);
+
+       return gen;
 }
 
 /* tiny test for inode number */
@@ -329,7 +363,12 @@ static inline int au_test_higen(struct inode *inode, struct inode *h_inode)
 
 static inline void au_iigen_dec(struct inode *inode)
 {
-       atomic_dec(&au_ii(inode)->ii_generation);
+       struct au_iinfo *iinfo;
+
+       iinfo = au_ii(inode);
+       spin_lock(&iinfo->ii_genspin);
+       iinfo->ii_generation.ig_generation--;
+       spin_unlock(&iinfo->ii_genspin);
 }
 
 static inline int au_iigen_test(struct inode *inode, unsigned int sigen)
@@ -337,7 +376,7 @@ static inline int au_iigen_test(struct inode *inode, unsigned int sigen)
        int err;
 
        err = 0;
-       if (unlikely(inode && au_iigen(inode) != sigen))
+       if (unlikely(inode && au_iigen(inode, NULL) != sigen))
                err = -EIO;
 
        return err;
@@ -455,7 +494,13 @@ struct au_branch;
 struct au_hnotify_op {
        void (*ctl)(struct au_hinode *hinode, int do_set);
        int (*alloc)(struct au_hinode *hinode);
-       void (*free)(struct au_hinode *hinode);
+
+       /*
+        * if it returns true, the the caller should free hinode->hi_notify,
+        * otherwise ->free() frees it.
+        */
+       int (*free)(struct au_hinode *hinode,
+                   struct au_hnotify *hn) __must_check;
 
        void (*fin)(void);
        int (*init)(void);
index 30a49df..dcb837f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
index 317f0e4..ccae19c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -51,12 +51,14 @@ int au_test_loopback_kthread(void)
 {
        int ret;
        struct task_struct *tsk = current;
+       char c, comm[sizeof(tsk->comm)];
 
        ret = 0;
        if (tsk->flags & PF_KTHREAD) {
-               const char c = tsk->comm[4];
+               get_task_comm(comm, tsk);
+               c = comm[4];
                ret = ('0' <= c && c <= '9'
-                      && !strncmp(tsk->comm, "loop", 4));
+                      && !strncmp(comm, "loop", 4));
        }
 
        return ret;
@@ -107,8 +109,8 @@ void au_warn_loopback(struct super_block *h_sb)
 
 pr:
        spin_unlock(&spin);
-       pr_warning("you may want to try another patch for loopback file "
-                  "on %s(0x%lx) branch\n", au_sbtype(h_sb), magic);
+       pr_warn("you may want to try another patch for loopback file "
+               "on %s(0x%lx) branch\n", au_sbtype(h_sb), magic);
 }
 
 int au_loopback_init(void)
index b7af6a7..88d019c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
index ef288b8..4e58dbf 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -65,8 +65,9 @@ static void au_cache_fin(void)
 {
        int i;
 
-       /* including AuCache_HNOTIFY */
-       for (i = 0; i < AuCache_Last; i++)
+       /* excluding AuCache_HNOTIFY */
+       BUILD_BUG_ON(AuCache_HNOTIFY + 1 != AuCache_Last);
+       for (i = 0; i < AuCache_HNOTIFY; i++)
                if (au_cachep[i]) {
                        kmem_cache_destroy(au_cachep[i]);
                        au_cachep[i] = NULL;
index 242f4ea..52bf472 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -74,9 +74,7 @@ enum {
        AuCache_FINFO,
        AuCache_VDIR,
        AuCache_DEHSTR,
-#ifdef CONFIG_AUFS_HNOTIFY
-       AuCache_HNOTIFY,
-#endif
+       AuCache_HNOTIFY, /* must be last */
        AuCache_Last
 };
 
index 35c18ef..76a6f80 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -186,17 +186,16 @@ static match_table_t brperm = {
        {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[])
 {
@@ -214,7 +213,7 @@ static int br_attr_val(char *str, match_table_t table, substring_t args[])
                else {
                        if (p)
                                *p = '+';
-                       pr_warning("ignored branch attribute %s\n", str);
+                       pr_warn("ignored branch attribute %s\n", str);
                        break;
                }
                if (p)
@@ -227,7 +226,7 @@ static int br_attr_val(char *str, match_table_t table, substring_t args[])
 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, '+');
@@ -237,20 +236,40 @@ static int noinline_for_stack br_perm_val(char *perm)
        if (!val) {
                if (p)
                        *p = '+';
-               pr_warning("ignored branch permission %s\n", perm);
+               pr_warn("ignored branch permission %s\n", perm);
                val = AuBrPerm_RO;
                goto out;
        }
        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;
        }
 
@@ -293,6 +312,7 @@ char *au_optstr_br_perm(int brperm)
                AuDebugOn(1);
        }
 
+       AppendAttr(AuBrAttr_UNPIN, AUFS_BRATTR_UNPIN);
        AppendAttr(AuBrRAttr_WH, AUFS_BRRATTR_WH);
        AppendAttr(AuBrWAttr_NoLinkWH, AUFS_BRWATTR_NLWH);
 
@@ -344,6 +364,8 @@ static match_table_t au_wbr_create_policy = {
        {AuWbrCreate_MFSRRV, "mfsrr:%d:%d"},
        {AuWbrCreate_PMFS, "pmfs"},
        {AuWbrCreate_PMFSV, "pmfs:%d"},
+       {AuWbrCreate_PMFSRR, "pmfsrr:%d"},
+       {AuWbrCreate_PMFSRRV, "pmfsrr:%d:%d"},
 
        {-1, NULL}
 };
@@ -413,6 +435,7 @@ au_wbr_create_val(char *str, struct au_opt_wbr_create *create)
        create->wbr_create = err;
        switch (err) {
        case AuWbrCreate_MFSRRV:
+       case AuWbrCreate_PMFSRRV:
                e = au_wbr_mfs_wmark(&args[0], str, create);
                if (!e)
                        e = au_wbr_mfs_sec(&args[1], str, create);
@@ -420,6 +443,7 @@ au_wbr_create_val(char *str, struct au_opt_wbr_create *create)
                        err = e;
                break;
        case AuWbrCreate_MFSRR:
+       case AuWbrCreate_PMFSRR:
                e = au_wbr_mfs_wmark(&args[0], str, create);
                if (unlikely(e)) {
                        err = e;
@@ -636,6 +660,7 @@ static void dump_opts(struct au_opts *opts)
                                          u.create->mfsrr_watermark);
                                break;
                        case AuWbrCreate_MFSRRV:
+                       case AuWbrCreate_PMFSRRV:
                                AuDbg("%llu watermark, %d sec\n",
                                          u.create->mfsrr_watermark,
                                          u.create->mfs_second);
@@ -1123,7 +1148,7 @@ int au_opts_parse(struct super_block *sb, char *str, struct au_opts *opts)
                        break;
 
                case Opt_ignore:
-                       pr_warning("ignored %s\n", opt_str);
+                       pr_warn("ignored %s\n", opt_str);
                        /*FALLTHROUGH*/
                case Opt_ignore_silent:
                        skipped = 1;
@@ -1175,6 +1200,8 @@ static int au_opt_wbr_create(struct super_block *sb,
        switch (create->wbr_create) {
        case AuWbrCreate_MFSRRV:
        case AuWbrCreate_MFSRR:
+       case AuWbrCreate_PMFSRR:
+       case AuWbrCreate_PMFSRRV:
                sbinfo->si_wbr_mfs.mfsrr_watermark = create->mfsrr_watermark;
                /*FALLTHROUGH*/
        case AuWbrCreate_MFS:
@@ -1459,14 +1486,14 @@ int au_opts_verify(struct super_block *sb, unsigned long sb_flags,
 
        if (!(sb_flags & MS_RDONLY)) {
                if (unlikely(!au_br_writable(au_sbr_perm(sb, 0))))
-                       pr_warning("first branch should be rw\n");
+                       pr_warn("first branch should be rw\n");
                if (unlikely(au_opt_test(sbinfo->si_mntflags, SHWH)))
-                       pr_warning("shwh should be used with ro\n");
+                       pr_warn("shwh should be used with ro\n");
        }
 
        if (au_opt_test((sbinfo->si_mntflags | pending), UDBA_HNOTIFY)
            && !au_opt_test(sbinfo->si_mntflags, XINO))
-               pr_warning("udba=*notify requires xino\n");
+               pr_warn("udba=*notify requires xino\n");
 
        err = 0;
        root = sb->s_root;
@@ -1518,7 +1545,7 @@ int au_opts_verify(struct super_block *sb, unsigned long sb_flags,
                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);
index 958f511..182a464 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -102,6 +102,8 @@ enum {
        AuWbrCreate_MFSRRV,     /* mfs then rr with seconds */
        AuWbrCreate_PMFS,       /* parent and mfs */
        AuWbrCreate_PMFSV,      /* parent and mfs with seconds */
+       AuWbrCreate_PMFSRR,     /* parent, mfs and round-robin */
+       AuWbrCreate_PMFSRRV,    /* plus seconds */
 
        AuWbrCreate_Def = AuWbrCreate_TDP
 };
index ca95fb3..f88a4c8 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -119,19 +119,13 @@ int au_plink_maint_enter(struct super_block *sb)
 
 /* ---------------------------------------------------------------------- */
 
-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);
@@ -140,20 +134,23 @@ void au_plink_list(struct super_block *sb)
        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);
@@ -162,9 +159,10 @@ int au_plink_test(struct inode *inode)
        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;
@@ -262,7 +260,7 @@ static int do_whplink(struct qstr *tgt, struct dentry *h_parent,
 {
        int err;
        struct path h_path = {
-               .mnt = br->br_mnt
+               .mnt = au_br_mnt(br)
        };
        struct inode *h_dir;
 
@@ -347,7 +345,7 @@ static int whplink(struct dentry *h_dentry, struct inode *inode,
 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);
 }
@@ -370,30 +368,24 @@ void au_plink_append(struct inode *inode, aufs_bindex_t bindex,
 {
        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);
@@ -402,20 +394,22 @@ void au_plink_append(struct inode *inode, aufs_bindex_t bindex,
                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);
@@ -424,9 +418,9 @@ void au_plink_append(struct inode *inode, aufs_bindex_t bindex,
 
 out:
        if (unlikely(err)) {
-               pr_warning("err %d, damaged pseudo link.\n", 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);
                }
        }
@@ -435,9 +429,11 @@ out:
 /* 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);
 
@@ -445,12 +441,18 @@ void au_plink_put(struct super_block *sb, int verbose)
        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)
@@ -464,15 +466,44 @@ void au_plink_clean(struct super_block *sb, int verbose)
        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);
 
@@ -480,36 +511,17 @@ void au_plink_half_refresh(struct super_block *sb, aufs_bindex_t br_id)
        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);
        }
 }
index 59d1a4b..aa5e2ae 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
index 8169782..7201cdf 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010-2012 Junjiro R. Okajima
+ * Copyright (C) 2010-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
index 7656503..9c6046a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
index d6c1b37..a1eb04b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
index 2448efe..5e8713a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  */
 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);
@@ -53,7 +55,7 @@ void au_si_free(struct kobject *kobj)
 
 int au_si_alloc(struct super_block *sb)
 {
-       int err;
+       int err, i;
        struct au_sbinfo *sbinfo;
        static struct lock_class_key aufs_si;
 
@@ -89,6 +91,7 @@ int au_si_alloc(struct super_block *sb)
        atomic_long_set(&sbinfo->si_nfiles, 0);
 
        sbinfo->si_bend = -1;
+       sbinfo->si_last_br_id = AUFS_BRANCH_MAX / 2;
 
        sbinfo->si_wbr_copyup = AuWbrCopyup_Def;
        sbinfo->si_wbr_create = AuWbrCreate_Def;
@@ -97,6 +100,9 @@ int au_si_alloc(struct super_block *sb)
 
        sbinfo->si_mntflags = au_opts_plink(AuOpt_Def);
 
+       sbinfo->si_xino_jiffy = jiffies;
+       sbinfo->si_xino_expire
+               = msecs_to_jiffies(AUFS_XINO_DEF_SEC * MSEC_PER_SEC);
        mutex_init(&sbinfo->si_xib_mtx);
        sbinfo->si_xino_brid = -1;
        /* leave si_xib_last_pindex and si_xib_next_bit */
@@ -106,7 +112,8 @@ int au_si_alloc(struct super_block *sb)
        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);
 
@@ -157,7 +164,7 @@ unsigned int au_sigen_inc(struct super_block *sb)
 
        gen = ++au_sbi(sb)->si_generation;
        au_update_digen(sb->s_root);
-       au_update_iigen(sb->s_root->d_inode);
+       au_update_iigen(sb->s_root->d_inode, /*half*/0);
        sb->s_root->d_inode->i_version++;
        return gen;
 }
index 743a307..2d53e87 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -58,5 +58,55 @@ static inline void au_spl_del_rcu(struct list_head *list,
        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__ */
index c798792..d105672 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -104,7 +104,7 @@ static int au_show_brs(struct seq_file *seq, struct super_block *sb)
        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) {
@@ -131,14 +131,14 @@ static void au_show_wbr_create(struct seq_file *m, int v,
 
        AuRwMustAnyLock(&sbinfo->si_rwsem);
 
-       seq_printf(m, ",create=");
+       seq_puts(m, ",create=");
        pat = au_optstr_wbr_create(v);
        switch (v) {
        case AuWbrCreate_TDP:
        case AuWbrCreate_RR:
        case AuWbrCreate_MFS:
        case AuWbrCreate_PMFS:
-               seq_printf(m, pat);
+               seq_puts(m, pat);
                break;
        case AuWbrCreate_MFSV:
                seq_printf(m, /*pat*/"mfs:%lu",
@@ -160,6 +160,16 @@ static void au_show_wbr_create(struct seq_file *m, int v,
                           jiffies_to_msecs(sbinfo->si_wbr_mfs.mfs_expire)
                           / MSEC_PER_SEC);
                break;
+       case AuWbrCreate_PMFSRR:
+               seq_printf(m, /*pat*/"pmfsrr:%llu",
+                          sbinfo->si_wbr_mfs.mfsrr_watermark);
+               break;
+       case AuWbrCreate_PMFSRRV:
+               seq_printf(m, /*pat*/"pmfsrr:%llu:%lu",
+                          sbinfo->si_wbr_mfs.mfsrr_watermark,
+                          jiffies_to_msecs(sbinfo->si_wbr_mfs.mfs_expire)
+                          / MSEC_PER_SEC);
+               break;
        }
 }
 
@@ -305,7 +315,18 @@ static u64 au_add_till_max(u64 a, u64 b)
 
        old = a;
        a += b;
-       if (old < a)
+       if (old <= a)
+               return a;
+       return ULLONG_MAX;
+}
+
+static u64 au_mul_till_max(u64 a, long mul)
+{
+       u64 old;
+
+       old = a;
+       a *= mul;
+       if (old <= a)
                return a;
        return ULLONG_MAX;
 }
@@ -313,25 +334,26 @@ static u64 au_add_till_max(u64 a, u64 b)
 static int au_statfs_sum(struct super_block *sb, struct kstatfs *buf)
 {
        int err;
+       long bsize, factor;
        u64 blocks, bfree, bavail, files, ffree;
        aufs_bindex_t bend, bindex, i;
        unsigned char shared;
        struct path h_path;
        struct super_block *h_sb;
 
+       err = 0;
+       bsize = LONG_MAX;
+       files = 0;
+       ffree = 0;
        blocks = 0;
        bfree = 0;
        bavail = 0;
-       files = 0;
-       ffree = 0;
-
-       err = 0;
        bend = au_sbend(sb);
-       for (bindex = bend; bindex >= 0; bindex--) {
+       for (bindex = 0; bindex <= bend; bindex++) {
                h_path.mnt = au_sbr_mnt(sb, bindex);
                h_sb = h_path.mnt->mnt_sb;
                shared = 0;
-               for (i = bindex + 1; !shared && i <= bend; i++)
+               for (i = 0; !shared && i < bindex; i++)
                        shared = (au_sbr_sb(sb, i) == h_sb);
                if (shared)
                        continue;
@@ -342,18 +364,36 @@ static int au_statfs_sum(struct super_block *sb, struct kstatfs *buf)
                if (unlikely(err))
                        goto out;
 
-               blocks = au_add_till_max(blocks, buf->f_blocks);
-               bfree = au_add_till_max(bfree, buf->f_bfree);
-               bavail = au_add_till_max(bavail, buf->f_bavail);
+               if (bsize > buf->f_bsize) {
+                       /*
+                        * we will reduce bsize, so we have to expand blocks
+                        * etc. to match them again
+                        */
+                       factor = (bsize / buf->f_bsize);
+                       blocks = au_mul_till_max(blocks, factor);
+                       bfree = au_mul_till_max(bfree, factor);
+                       bavail = au_mul_till_max(bavail, factor);
+                       bsize = buf->f_bsize;
+               }
+
+               factor = (buf->f_bsize / bsize);
+               blocks = au_add_till_max(blocks,
+                               au_mul_till_max(buf->f_blocks, factor));
+               bfree = au_add_till_max(bfree,
+                               au_mul_till_max(buf->f_bfree, factor));
+               bavail = au_add_till_max(bavail,
+                               au_mul_till_max(buf->f_bavail, factor));
                files = au_add_till_max(files, buf->f_files);
                ffree = au_add_till_max(ffree, buf->f_ffree);
        }
 
+       buf->f_bsize = bsize;
        buf->f_blocks = blocks;
        buf->f_bfree = bfree;
        buf->f_bavail = bavail;
        buf->f_files = files;
        buf->f_ffree = ffree;
+       buf->f_frsize = 0;
 
 out:
        return err;
@@ -389,6 +429,36 @@ static int aufs_statfs(struct dentry *dentry, struct kstatfs *buf)
 
 /* ---------------------------------------------------------------------- */
 
+static int aufs_sync_fs(struct super_block *sb, int wait)
+{
+       int err, e;
+       aufs_bindex_t bend, bindex;
+       struct au_branch *br;
+       struct super_block *h_sb;
+
+       err = 0;
+       si_noflush_read_lock(sb);
+       bend = au_sbend(sb);
+       for (bindex = 0; bindex <= bend; bindex++) {
+               br = au_sbr(sb, bindex);
+               if (!au_br_writable(br->br_perm))
+                       continue;
+
+               h_sb = au_sbr_sb(sb, bindex);
+               if (h_sb->s_op->sync_fs) {
+                       e = h_sb->s_op->sync_fs(h_sb, wait);
+                       if (unlikely(e && !err))
+                               err = e;
+                       /* go on even if an error happens */
+               }
+       }
+       si_read_unlock(sb);
+
+       return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
 /* final actions when unmounting a file system */
 static void aufs_put_super(struct super_block *sb)
 {
@@ -599,7 +669,7 @@ static int au_refresh_i(struct super_block *sb)
        sigen = au_sigen(sb);
        for (ull = 0; ull < max; ull++) {
                inode = array[ull];
-               if (au_iigen(inode) != sigen) {
+               if (au_iigen(inode, NULL) != sigen) {
                        ii_write_lock_child(inode);
                        e = au_refresh_hinode_self(inode);
                        ii_write_unlock(inode);
@@ -752,6 +822,7 @@ static const struct super_operations aufs_sop = {
        .show_options   = aufs_show_options,
        .statfs         = aufs_statfs,
        .put_super      = aufs_put_super,
+       .sync_fs        = aufs_sync_fs,
        .remount_fs     = aufs_remount_fs
 };
 
index 9967dee..14b1c07 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -39,8 +39,15 @@ struct au_wbr_copyup_operations {
        int (*copyup)(struct dentry *dentry);
 };
 
+#define AuWbr_DIR      1               /* target is a dir */
+#define AuWbr_PARENT   (1 << 1)        /* always require a parent */
+
+#define au_ftest_wbr(flags, name)      ((flags) & AuWbr_##name)
+#define au_fset_wbr(flags, name)       { (flags) |= AuWbr_##name; }
+#define au_fclr_wbr(flags, name)       { (flags) &= ~AuWbr_##name; }
+
 struct au_wbr_create_operations {
-       int (*create)(struct dentry *dentry, int isdir);
+       int (*create)(struct dentry *dentry, unsigned int flags);
        int (*init)(struct super_block *sb);
        int (*fin)(struct super_block *sb);
 };
@@ -55,6 +62,20 @@ struct au_wbr_mfs {
        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 */
@@ -116,6 +137,8 @@ struct au_sbinfo {
        unsigned long           si_xib_last_pindex;
        int                     si_xib_next_bit;
        aufs_bindex_t           si_xino_brid;
+       unsigned long           si_xino_jiffy;
+       unsigned long           si_xino_expire;
        /* reserved for future use */
        /* unsigned long long   si_xib_limit; */        /* Max xib file size */
 
@@ -145,7 +168,7 @@ struct au_sbinfo {
        /* 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;
@@ -158,7 +181,9 @@ struct au_sbinfo {
         */
        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
@@ -266,16 +291,8 @@ static inline struct au_sbinfo *au_sbi(struct super_block *sb)
 /* ---------------------------------------------------------------------- */
 
 #ifdef CONFIG_AUFS_EXPORT
+int au_test_nfsd(void);
 void au_export_init(struct super_block *sb);
-
-static inline int au_test_nfsd(void)
-{
-       struct task_struct *tsk = current;
-
-       return (tsk->flags & PF_KTHREAD)
-               && !strcmp(tsk->comm, "nfsd");
-}
-
 void au_xigen_inc(struct inode *inode);
 int au_xigen_new(struct inode *inode);
 int au_xigen_set(struct super_block *sb, struct file *base);
@@ -288,8 +305,8 @@ static inline int au_busy_or_stale(void)
        return -ESTALE;
 }
 #else
-AuStubVoid(au_export_init, struct super_block *sb)
 AuStubInt0(au_test_nfsd, void)
+AuStubVoid(au_export_init, struct super_block *sb)
 AuStubVoid(au_xigen_inc, struct inode *inode)
 AuStubInt0(au_xigen_new, struct inode *inode)
 AuStubInt0(au_xigen_set, struct super_block *sb, struct file *base)
@@ -357,6 +374,7 @@ static inline void dbgaufs_si_null(struct au_sbinfo *sbinfo)
        /* 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;
index a675324..f68e844 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
index 5aaff3b..2fc17d9 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
index f72519d..722152b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -78,7 +78,7 @@ int sysaufs_si_xi_path(struct seq_file *seq, struct super_block *sb)
  * unlinked.
  */
 static int sysaufs_si_br(struct seq_file *seq, struct super_block *sb,
-                        aufs_bindex_t bindex)
+                        aufs_bindex_t bindex, int idx)
 {
        int err;
        struct path path;
@@ -92,18 +92,30 @@ static int sysaufs_si_br(struct seq_file *seq, struct super_block *sb,
        root = sb->s_root;
        di_read_lock_parent(root, !AuLock_IR);
        br = au_sbr(sb, bindex);
-       path.mnt = br->br_mnt;
-       path.dentry = au_h_dptr(root, bindex);
-       au_seq_path(seq, &path);
-       di_read_unlock(root, !AuLock_IR);
-       perm = au_optstr_br_perm(br->br_perm);
-       if (perm) {
-               err = seq_printf(seq, "=%s\n", perm);
-               kfree(perm);
+
+       switch (idx) {
+       case AuBrSysfs_BR:
+               path.mnt = au_br_mnt(br);
+               path.dentry = au_h_dptr(root, bindex);
+               au_seq_path(seq, &path);
+               di_read_unlock(root, !AuLock_IR);
+               perm = au_optstr_br_perm(br->br_perm);
+               if (perm) {
+                       err = seq_printf(seq, "=%s\n", perm);
+                       kfree(perm);
+                       if (err == -1)
+                               err = -E2BIG;
+               } else
+                       err = -ENOMEM;
+               break;
+       case AuBrSysfs_BRID:
+               err = seq_printf(seq, "%d\n", br->br_id);
+               di_read_unlock(root, !AuLock_IR);
                if (err == -1)
                        err = -E2BIG;
-       } else
-               err = -ENOMEM;
+               break;
+       }
+
        return err;
 }
 
@@ -125,13 +137,15 @@ static struct seq_file *au_seq(char *p, ssize_t len)
        return seq;
 }
 
-#define SysaufsBr_PREFIX "br"
+#define SysaufsBr_PREFIX       "br"
+#define SysaufsBrid_PREFIX     "brid"
 
 /* todo: file size may exceed PAGE_SIZE */
 ssize_t sysaufs_si_show(struct kobject *kobj, struct attribute *attr,
                        char *buf)
 {
        ssize_t err;
+       int idx;
        long l;
        aufs_bindex_t bend;
        struct au_sbinfo *sbinfo;
@@ -173,19 +187,25 @@ ssize_t sysaufs_si_show(struct kobject *kobj, struct attribute *attr,
                cattr++;
        }
 
-       bend = au_sbend(sb);
-       if (!strncmp(name, SysaufsBr_PREFIX, sizeof(SysaufsBr_PREFIX) - 1)) {
+       if (!strncmp(name, SysaufsBrid_PREFIX,
+                    sizeof(SysaufsBrid_PREFIX) - 1)) {
+               idx = AuBrSysfs_BRID;
+               name += sizeof(SysaufsBrid_PREFIX) - 1;
+       } else if (!strncmp(name, SysaufsBr_PREFIX,
+                           sizeof(SysaufsBr_PREFIX) - 1)) {
+               idx = AuBrSysfs_BR;
                name += sizeof(SysaufsBr_PREFIX) - 1;
-               err = kstrtol(name, 10, &l);
-               if (!err) {
-                       if (l <= bend)
-                               err = sysaufs_si_br(seq, sb, (aufs_bindex_t)l);
-                       else
-                               err = -ENOENT;
-               }
-               goto out_seq;
+       } else
+                 BUG();
+
+       err = kstrtol(name, 10, &l);
+       if (!err) {
+               bend = au_sbend(sb);
+               if (l <= bend)
+                       err = sysaufs_si_br(seq, sb, (aufs_bindex_t)l, idx);
+               else
+                       err = -ENOENT;
        }
-       BUG();
 
 out_seq:
        if (!err) {
@@ -205,17 +225,26 @@ out:
 
 void sysaufs_br_init(struct au_branch *br)
 {
-       struct attribute *attr = &br->br_attr;
-
-       sysfs_attr_init(attr);
-       attr->name = br->br_name;
-       attr->mode = S_IRUGO;
+       int i;
+       struct au_brsysfs *br_sysfs;
+       struct attribute *attr;
+
+       br_sysfs = br->br_sysfs;
+       for (i = 0; i < ARRAY_SIZE(br->br_sysfs); i++) {
+               attr = &br_sysfs->attr;
+               sysfs_attr_init(attr);
+               attr->name = br_sysfs->name;
+               attr->mode = S_IRUGO;
+               br_sysfs++;
+       }
 }
 
 void sysaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex)
 {
        struct au_branch *br;
        struct kobject *kobj;
+       struct au_brsysfs *br_sysfs;
+       int i;
        aufs_bindex_t bend;
 
        dbgaufs_brs_del(sb, bindex);
@@ -227,16 +256,21 @@ void sysaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex)
        bend = au_sbend(sb);
        for (; bindex <= bend; bindex++) {
                br = au_sbr(sb, bindex);
-               sysfs_remove_file(kobj, &br->br_attr);
+               br_sysfs = br->br_sysfs;
+               for (i = 0; i < ARRAY_SIZE(br->br_sysfs); i++) {
+                       sysfs_remove_file(kobj, &br_sysfs->attr);
+                       br_sysfs++;
+               }
        }
 }
 
 void sysaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex)
 {
-       int err;
+       int err, i;
        aufs_bindex_t bend;
        struct kobject *kobj;
        struct au_branch *br;
+       struct au_brsysfs *br_sysfs;
 
        dbgaufs_brs_add(sb, bindex);
 
@@ -247,11 +281,17 @@ void sysaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex)
        bend = au_sbend(sb);
        for (; bindex <= bend; bindex++) {
                br = au_sbr(sb, bindex);
-               snprintf(br->br_name, sizeof(br->br_name), SysaufsBr_PREFIX
-                        "%d", bindex);
-               err = sysfs_create_file(kobj, &br->br_attr);
-               if (unlikely(err))
-                       pr_warning("failed %s under sysfs(%d)\n",
-                                  br->br_name, err);
+               br_sysfs = br->br_sysfs;
+               snprintf(br_sysfs[AuBrSysfs_BR].name, sizeof(br_sysfs->name),
+                        SysaufsBr_PREFIX "%d", bindex);
+               snprintf(br_sysfs[AuBrSysfs_BRID].name, sizeof(br_sysfs->name),
+                        SysaufsBrid_PREFIX "%d", bindex);
+               for (i = 0; i < ARRAY_SIZE(br->br_sysfs); i++) {
+                       err = sysfs_create_file(kobj, &br_sysfs->attr);
+                       if (unlikely(err))
+                               pr_warn("failed %s under sysfs(%d)\n",
+                                       br_sysfs->name, err);
+                       br_sysfs++;
+               }
        }
 }
index c481d2b..54f3250 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -35,16 +35,18 @@ static void sysrq_sb(struct super_block *sb)
        plevel = au_plevel;
        au_plevel = KERN_WARNING;
 
-       sbinfo = au_sbi(sb);
        /* since we define pr_fmt, call printk directly */
+#define pr(str) printk(KERN_WARNING AUFS_NAME ": " str)
+
+       sbinfo = au_sbi(sb);
        printk(KERN_WARNING "si=%lx\n", sysaufs_si_id(sbinfo));
-       printk(KERN_WARNING AUFS_NAME ": superblock\n");
+       pr("superblock\n");
        au_dpri_sb(sb);
 
 #if 0
-       printk(KERN_WARNING AUFS_NAME ": root dentry\n");
+       pr("root dentry\n");
        au_dpri_dentry(sb->s_root);
-       printk(KERN_WARNING AUFS_NAME ": root inode\n");
+       pr("root inode\n");
        au_dpri_inode(sb->s_root->d_inode);
 #endif
 
@@ -72,7 +74,7 @@ static void sysrq_sb(struct super_block *sb)
 #if 1
        {
                struct inode *i;
-               printk(KERN_WARNING AUFS_NAME ": isolated inode\n");
+               pr("isolated inode\n");
                spin_lock(&inode_sb_list_lock);
                list_for_each_entry(i, &sb->s_inodes, i_sb_list) {
                        spin_lock(&i->i_lock);
@@ -83,7 +85,7 @@ static void sysrq_sb(struct super_block *sb)
                spin_unlock(&inode_sb_list_lock);
        }
 #endif
-       printk(KERN_WARNING AUFS_NAME ": files\n");
+       pr("files\n");
        lg_global_lock(files_lglock);
        do_file_list_for_each_entry(sb, file) {
                umode_t mode;
@@ -92,8 +94,9 @@ static void sysrq_sb(struct super_block *sb)
                        au_dpri_file(file);
        } while_file_list_for_each_entry;
        lg_global_unlock(files_lglock);
-       printk(KERN_WARNING AUFS_NAME ": done\n");
+       pr("done\n");
 
+#undef pr
        au_plevel = plevel;
 }
 
index 3f16d51..fe4db05 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
index 331ab17..fbb8d4b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
index ce4d5b6..1fa62b4 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
index 2bf2efa..638e860 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -37,7 +37,7 @@ int au_cpdown_attr(struct path *h_path, struct dentry *h_src)
        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? */
@@ -60,13 +60,8 @@ int au_cpdown_attr(struct path *h_path, struct dentry *h_src)
 #define au_fclr_cpdown(flags, name) \
        do { (flags) &= ~AuCpdown_##name; } while (0)
 
-struct au_cpdown_dir_args {
-       struct dentry *parent;
-       unsigned int flags;
-};
-
 static int au_cpdown_dir_opq(struct dentry *dentry, aufs_bindex_t bdst,
-                            struct au_cpdown_dir_args *a)
+                            unsigned int *flags)
 {
        int err;
        struct dentry *opq_dentry;
@@ -76,7 +71,7 @@ static int au_cpdown_dir_opq(struct dentry *dentry, aufs_bindex_t bdst,
        if (IS_ERR(opq_dentry))
                goto out;
        dput(opq_dentry);
-       au_fset_cpdown(a->flags, DIROPQ);
+       au_fset_cpdown(*flags, DIROPQ);
 
 out:
        return err;
@@ -97,7 +92,7 @@ static int au_cpdown_dir_wh(struct dentry *dentry, struct dentry *h_parent,
 
        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);
        }
@@ -108,6 +103,7 @@ out:
 }
 
 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;
@@ -115,7 +111,7 @@ static int au_cpdown_dir(struct dentry *dentry, aufs_bindex_t bdst,
        struct path h_path;
        struct dentry *parent;
        struct inode *h_dir, *h_inode, *inode, *dir;
-       struct au_cpdown_dir_args *args = arg;
+       unsigned int *flags = arg;
 
        bstart = au_dbstart(dentry);
        /* dentry is di-locked */
@@ -125,7 +121,7 @@ static int au_cpdown_dir(struct dentry *dentry, aufs_bindex_t bdst,
        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);
@@ -134,19 +130,19 @@ static int au_cpdown_dir(struct dentry *dentry, aufs_bindex_t bdst,
                              S_IRWXU | S_IRUGO | S_IXUGO);
        if (unlikely(err))
                goto out_put;
-       au_fset_cpdown(args->flags, MADE_DIR);
+       au_fset_cpdown(*flags, MADE_DIR);
 
        bopq = au_dbdiropq(dentry);
-       au_fclr_cpdown(args->flags, WHED);
-       au_fclr_cpdown(args->flags, DIROPQ);
+       au_fclr_cpdown(*flags, WHED);
+       au_fclr_cpdown(*flags, DIROPQ);
        if (au_dbwh(dentry) == bdst)
-               au_fset_cpdown(args->flags, WHED);
-       if (!au_ftest_cpdown(args->flags, PARENT_OPQ) && bopq <= bdst)
-               au_fset_cpdown(args->flags, PARENT_OPQ);
+               au_fset_cpdown(*flags, WHED);
+       if (!au_ftest_cpdown(*flags, PARENT_OPQ) && bopq <= bdst)
+               au_fset_cpdown(*flags, PARENT_OPQ);
        h_inode = h_path.dentry->d_inode;
        mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD);
-       if (au_ftest_cpdown(args->flags, WHED)) {
-               err = au_cpdown_dir_opq(dentry, bdst, args);
+       if (au_ftest_cpdown(*flags, WHED)) {
+               err = au_cpdown_dir_opq(dentry, bdst, flags);
                if (unlikely(err)) {
                        mutex_unlock(&h_inode->i_mutex);
                        goto out_dir;
@@ -158,7 +154,7 @@ static int au_cpdown_dir(struct dentry *dentry, aufs_bindex_t bdst,
        if (unlikely(err))
                goto out_opq;
 
-       if (au_ftest_cpdown(args->flags, WHED)) {
+       if (au_ftest_cpdown(*flags, WHED)) {
                err = au_cpdown_dir_wh(dentry, h_parent, dir, bdst);
                if (unlikely(err))
                        goto out_opq;
@@ -173,7 +169,7 @@ static int au_cpdown_dir(struct dentry *dentry, aufs_bindex_t bdst,
 
        /* revert */
 out_opq:
-       if (au_ftest_cpdown(args->flags, DIROPQ)) {
+       if (au_ftest_cpdown(*flags, DIROPQ)) {
                mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD);
                rerr = au_diropq_remove(dentry, bdst);
                mutex_unlock(&h_inode->i_mutex);
@@ -185,7 +181,7 @@ out_opq:
                }
        }
 out_dir:
-       if (au_ftest_cpdown(args->flags, MADE_DIR)) {
+       if (au_ftest_cpdown(*flags, MADE_DIR)) {
                rerr = vfsub_sio_rmdir(au_h_iptr(dir, bdst), &h_path);
                if (unlikely(rerr)) {
                        AuIOErr("failed removing %.*s b%d (%d)\n",
@@ -205,13 +201,10 @@ out:
 int au_cpdown_dirs(struct dentry *dentry, aufs_bindex_t bdst)
 {
        int err;
-       struct au_cpdown_dir_args args = {
-               .parent = dget_parent(dentry),
-               .flags  = 0
-       };
+       unsigned int flags;
 
-       err = au_cp_dirs(dentry, bdst, au_cpdown_dir, &args);
-       dput(args.parent);
+       flags = 0;
+       err = au_cp_dirs(dentry, bdst, au_cpdown_dir, &flags);
 
        return err;
 }
@@ -267,7 +260,8 @@ static int au_wbr_bu(struct super_block *sb, aufs_bindex_t bindex)
 }
 
 /* top down parent */
-static int au_wbr_create_tdp(struct dentry *dentry, int isdir __maybe_unused)
+static int au_wbr_create_tdp(struct dentry *dentry,
+                            unsigned int flags __maybe_unused)
 {
        int err;
        aufs_bindex_t bstart, bindex;
@@ -356,7 +350,7 @@ static int au_wbr_create_init_rr(struct super_block *sb)
        return err;
 }
 
-static int au_wbr_create_rr(struct dentry *dentry, int isdir)
+static int au_wbr_create_rr(struct dentry *dentry, unsigned int flags)
 {
        int err, nbr;
        unsigned int u;
@@ -373,7 +367,7 @@ static int au_wbr_create_rr(struct dentry *dentry, int isdir)
        bend = au_sbend(sb);
        nbr = bend + 1;
        for (bindex = 0; bindex <= bend; bindex++) {
-               if (!isdir) {
+               if (!au_ftest_wbr(flags, DIR)) {
                        err = atomic_dec_return(next) + 1;
                        /* modulo for 0 is meaningless */
                        if (unlikely(!err))
@@ -400,11 +394,12 @@ out:
 /* ---------------------------------------------------------------------- */
 
 /* most free space */
-static void au_mfs(struct dentry *dentry)
+static void au_mfs(struct dentry *dentry, struct dentry *parent)
 {
        struct super_block *sb;
        struct au_branch *br;
        struct au_wbr_mfs *mfs;
+       struct dentry *h_parent;
        aufs_bindex_t bindex, bend;
        int err;
        unsigned long long b, bavail;
@@ -424,14 +419,26 @@ static void au_mfs(struct dentry *dentry)
        MtxMustLock(&mfs->mfs_lock);
        mfs->mfs_bindex = -EROFS;
        mfs->mfsrr_bytes = 0;
-       bend = au_sbend(sb);
-       for (bindex = 0; bindex <= bend; bindex++) {
+       if (!parent) {
+               bindex = 0;
+               bend = au_sbend(sb);
+       } else {
+               bindex = au_dbstart(parent);
+               bend = au_dbtaildir(parent);
+       }
+
+       for (; bindex <= bend; bindex++) {
+               if (parent) {
+                       h_parent = au_h_dptr(parent, bindex);
+                       if (!h_parent || !h_parent->d_inode)
+                               continue;
+               }
                br = au_sbr(sb, bindex);
                if (au_br_rdonly(br))
                        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)) {
@@ -456,9 +463,10 @@ static void au_mfs(struct dentry *dentry)
        kfree(st);
 }
 
-static int au_wbr_create_mfs(struct dentry *dentry, int isdir __maybe_unused)
+static int au_wbr_create_mfs(struct dentry *dentry, unsigned int flags)
 {
        int err;
+       struct dentry *parent;
        struct super_block *sb;
        struct au_wbr_mfs *mfs;
 
@@ -467,14 +475,18 @@ static int au_wbr_create_mfs(struct dentry *dentry, int isdir __maybe_unused)
                goto out;
 
        sb = dentry->d_sb;
+       parent = NULL;
+       if (au_ftest_wbr(flags, PARENT))
+               parent = dget_parent(dentry);
        mfs = &au_sbi(sb)->si_wbr_mfs;
        mutex_lock(&mfs->mfs_lock);
        if (time_after(jiffies, mfs->mfs_jiffy + mfs->mfs_expire)
            || mfs->mfs_bindex < 0
            || au_br_rdonly(au_sbr(sb, mfs->mfs_bindex)))
-               au_mfs(dentry);
+               au_mfs(dentry, parent);
        mutex_unlock(&mfs->mfs_lock);
        err = mfs->mfs_bindex;
+       dput(parent);
 
        if (err >= 0)
                err = au_wbr_nonopq(dentry, err);
@@ -505,17 +517,17 @@ static int au_wbr_create_fin_mfs(struct super_block *sb __maybe_unused)
 /* ---------------------------------------------------------------------- */
 
 /* most free space and then round robin */
-static int au_wbr_create_mfsrr(struct dentry *dentry, int isdir)
+static int au_wbr_create_mfsrr(struct dentry *dentry, unsigned int flags)
 {
        int err;
        struct au_wbr_mfs *mfs;
 
-       err = au_wbr_create_mfs(dentry, isdir);
+       err = au_wbr_create_mfs(dentry, flags);
        if (err >= 0) {
                mfs = &au_sbi(dentry->d_sb)->si_wbr_mfs;
                mutex_lock(&mfs->mfs_lock);
                if (mfs->mfsrr_bytes < mfs->mfsrr_watermark)
-                       err = au_wbr_create_rr(dentry, isdir);
+                       err = au_wbr_create_rr(dentry, flags);
                mutex_unlock(&mfs->mfs_lock);
        }
 
@@ -536,7 +548,7 @@ static int au_wbr_create_init_mfsrr(struct super_block *sb)
 /* ---------------------------------------------------------------------- */
 
 /* top down parent and most free space */
-static int au_wbr_create_pmfs(struct dentry *dentry, int isdir)
+static int au_wbr_create_pmfs(struct dentry *dentry, unsigned int flags)
 {
        int err, e2;
        unsigned long long b;
@@ -545,7 +557,7 @@ static int au_wbr_create_pmfs(struct dentry *dentry, int isdir)
        struct dentry *parent, *h_parent;
        struct au_branch *br;
 
-       err = au_wbr_create_tdp(dentry, isdir);
+       err = au_wbr_create_tdp(dentry, flags);
        if (unlikely(err < 0))
                goto out;
        parent = dget_parent(dentry);
@@ -554,7 +566,7 @@ static int au_wbr_create_pmfs(struct dentry *dentry, int isdir)
        if (bstart == bend)
                goto out_parent; /* success */
 
-       e2 = au_wbr_create_mfs(dentry, isdir);
+       e2 = au_wbr_create_mfs(dentry, flags);
        if (e2 < 0)
                goto out_parent; /* success */
 
@@ -589,12 +601,46 @@ out:
 
 /* ---------------------------------------------------------------------- */
 
+/*
+ * - top down parent
+ * - most free space with parent
+ * - most free space round-robin regardless parent
+ */
+static int au_wbr_create_pmfsrr(struct dentry *dentry, unsigned int flags)
+{
+       int err;
+       unsigned long long watermark;
+       struct super_block *sb;
+       struct au_branch *br;
+       struct au_wbr_mfs *mfs;
+
+       err = au_wbr_create_pmfs(dentry, flags | AuWbr_PARENT);
+       if (unlikely(err < 0))
+               goto out;
+
+       sb = dentry->d_sb;
+       br = au_sbr(sb, err);
+       mfs = &au_sbi(sb)->si_wbr_mfs;
+       mutex_lock(&mfs->mfs_lock);
+       watermark = mfs->mfsrr_watermark;
+       mutex_unlock(&mfs->mfs_lock);
+       if (br->br_wbr->wbr_bytes < watermark)
+               /* regardless the parent dir */
+               err = au_wbr_create_mfsrr(dentry, flags);
+
+out:
+       AuDbg("b%d\n", err);
+       return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
 /* policies for copyup */
 
 /* top down parent */
 static int au_wbr_copyup_tdp(struct dentry *dentry)
 {
-       return au_wbr_create_tdp(dentry, /*isdir, anything is ok*/0);
+       return au_wbr_create_tdp(dentry, /*flags, anything is ok*/0);
 }
 
 /* bottom up parent */
@@ -696,5 +742,15 @@ struct au_wbr_create_operations au_wbr_create_ops[] = {
                .create = au_wbr_create_pmfs,
                .init   = au_wbr_create_init_mfs,
                .fin    = au_wbr_create_fin_mfs
+       },
+       [AuWbrCreate_PMFSRR] = {
+               .create = au_wbr_create_pmfsrr,
+               .init   = au_wbr_create_init_mfsrr,
+               .fin    = au_wbr_create_fin_mfs
+       },
+       [AuWbrCreate_PMFSRRV] = {
+               .create = au_wbr_create_pmfsrr,
+               .init   = au_wbr_create_init_mfsrr,
+               .fin    = au_wbr_create_fin_mfs
        }
 };
index 6764863..ca5990a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -156,7 +156,7 @@ struct dentry *au_whtmp_lkup(struct dentry *h_parent, struct au_branch *br,
                        goto out_name;
                dput(dentry);
        }
-       /* pr_warning("could not get random name\n"); */
+       /* pr_warn("could not get random name\n"); */
        dentry = ERR_PTR(-EEXIST);
        AuDbg("%.*s\n", AuLNPair(&qs));
        BUG();
@@ -176,7 +176,7 @@ int au_whtmp_ren(struct dentry *h_dentry, struct au_branch *br)
 {
        int err;
        struct path h_path = {
-               .mnt = br->br_mnt
+               .mnt = au_br_mnt(br)
        };
        struct inode *h_dir;
        struct dentry *h_parent;
@@ -235,7 +235,7 @@ static int unlink_wh_name(struct dentry *h_parent, struct qstr *wh,
 {
        int err;
        struct path h_path = {
-               .mnt = br->br_mnt
+               .mnt = au_br_mnt(br)
        };
 
        err = 0;
@@ -274,8 +274,8 @@ static void au_wh_clean(struct inode *h_dir, struct path *whpath,
                mnt_drop_write(whpath->mnt);
        }
        if (unlikely(err))
-               pr_warning("failed removing %.*s (%d), ignored.\n",
-                          AuDLNPair(whpath->dentry), err);
+               pr_warn("failed removing %.*s (%d), ignored.\n",
+                       AuDLNPair(whpath->dentry), err);
 }
 
 static int test_linkable(struct dentry *h_root)
@@ -436,16 +436,14 @@ out:
 /*
  * 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] = {
@@ -565,28 +563,29 @@ static void reinit_br_wh(void *arg)
        dir = a->sb->s_root->d_inode;
        hdir = au_hi(dir, bindex);
        h_root = au_h_dptr(a->sb->s_root, bindex);
+&nb