[kernel] r21782 - in dists/trunk/linux/debian: . config patches patches/features/all/aufs3

Ben Hutchings benh at moszumanska.debian.org
Mon Sep 8 01:24:43 UTC 2014


Author: benh
Date: Mon Sep  8 01:24:43 2014
New Revision: 21782

Log:
aufs: Update to aufs3.16-20140908

Deleted:
   dists/trunk/linux/debian/patches/features/all/aufs3/aufs3-remove-circular-includes.patch
Modified:
   dists/trunk/linux/debian/changelog
   dists/trunk/linux/debian/config/config
   dists/trunk/linux/debian/patches/features/all/aufs3/aufs3-add.patch
   dists/trunk/linux/debian/patches/features/all/aufs3/aufs3-base.patch
   dists/trunk/linux/debian/patches/features/all/aufs3/aufs3-kbuild.patch
   dists/trunk/linux/debian/patches/features/all/aufs3/aufs3-mmap.patch
   dists/trunk/linux/debian/patches/features/all/aufs3/aufs3-standalone.patch
   dists/trunk/linux/debian/patches/series

Modified: dists/trunk/linux/debian/changelog
==============================================================================
--- dists/trunk/linux/debian/changelog	Sun Sep  7 23:25:42 2014	(r21781)
+++ dists/trunk/linux/debian/changelog	Mon Sep  8 01:24:43 2014	(r21782)
@@ -44,6 +44,26 @@
     - mnt: Add tests for unprivileged remount cases that have found to be
       faulty
   * [armel/kirkwood] mm: Enable HIGHMEM (Closes: #760786)
+  * aufs: Update to aufs3.16-20140908:
+    - bugfix, missing mnt_want_write in moo
+    - new ioctl BRINFO
+    - bugfix, restore the lost unlock in an error path
+    - allow deleting a branch who has an opened dir
+    - bugfix, stop passing an error code to dput()
+    - possible bugfix, ptr in an array
+    - implement fhsm (not enabled)
+    - si_files has all opened files
+    - bugfix, use id instead of index to identify a branch
+    - new move-down flag AUFS_MVDOWN_FHSM_LOWER
+    - branch attr 'fhsm' is independent from rw/ro attrib
+    - support for a branch ro+fhsm
+    - fhsm notify after fixing inode attrib
+    - bugfix, hfile test in br_del_file()
+    - bugfix, pinning in mvdown
+    - bugfix, instantiate-revalidate race
+    - possible bugfix, temporary d_inode
+    - fhsm and br_del, allow the root dir only
+    - bugfix, get a removed dentry from an inode
 
   [ Vagrant Cascadian ]
   * [armmp] Enable IMX_IPUV3_CORE (closes: #756810).

Modified: dists/trunk/linux/debian/config/config
==============================================================================
--- dists/trunk/linux/debian/config/config	Sun Sep  7 23:25:42 2014	(r21781)
+++ dists/trunk/linux/debian/config/config	Mon Sep  8 01:24:43 2014	(r21782)
@@ -4173,6 +4173,7 @@
 ## end choice
 # CONFIG_AUFS_HNOTIFY is not set
 CONFIG_AUFS_EXPORT=y
+# CONFIG_AUFS_FHSM is not set
 # CONFIG_AUFS_RDU is not set
 # CONFIG_AUFS_SHWH is not set
 # CONFIG_AUFS_BR_RAMFS is not set

Modified: dists/trunk/linux/debian/patches/features/all/aufs3/aufs3-add.patch
==============================================================================
--- dists/trunk/linux/debian/patches/features/all/aufs3/aufs3-add.patch	Sun Sep  7 23:25:42 2014	(r21781)
+++ dists/trunk/linux/debian/patches/features/all/aufs3/aufs3-add.patch	Mon Sep  8 01:24:43 2014	(r21782)
@@ -1,7 +1,7 @@
 From: J. R. Okajima <hooanon05 at yahoo.co.jp>
-Date: Thu Jul 10 02:32:20 2014 +0900
-Subject: aufs3.x-rcN-20140714
-Origin: http://sourceforge.net/p/aufs/aufs3-standalone/ci/501539c2f9478ef69fa42acfb43ef1420d9bb524/tree/
+Date: Thu Sep 4 19:46:58 2014 +0900
+Subject: aufs3.16-20140908
+Origin: http://sourceforge.net/p/aufs/aufs3-standalone/ci/888949daf96bf7e2b857dc38e22029513f94d4ae/tree/
 Bug-Debian: https://bugs.debian.org/541828
 
 Patch generated by debian/patches/features/all/aufs3/gen-patch
@@ -94,8 +94,8 @@
 +		When the aufs mount option 'noxino' is specified, it
 +		will be empty. About XINO files, see the aufs manual.
 --- a/Documentation/filesystems/aufs/README	1970-01-01 01:00:00.000000000 +0100
-+++ b/Documentation/filesystems/aufs/README	2014-07-15 14:04:48.720871625 +0100
-@@ -0,0 +1,368 @@
++++ b/Documentation/filesystems/aufs/README	2014-09-08 00:38:33.510569903 +0100
+@@ -0,0 +1,370 @@
 +
 +Aufs3 -- advanced multi layered unification filesystem version 3.x
 +http://aufs.sf.net
@@ -447,6 +447,8 @@
 +The Parted Magic Project made a donation (2013/9 and 11).
 +Pavel Barta made a donation (2013/10).
 +Nikolay Pertsev made a donation (2014/5).
++James B made a donation (2014/7).
++Stefano Di Biase made a donation (2014/8).
 +
 +Thank you very much.
 +Donations are always, including future donations, very important and
@@ -629,8 +631,8 @@
 +helper, instead of doing in kernel space. Actually I am still thinking
 +about it. But currently I have implemented it in kernel space.
 --- a/Documentation/filesystems/aufs/design/02struct.txt	1970-01-01 01:00:00.000000000 +0100
-+++ b/Documentation/filesystems/aufs/design/02struct.txt	2014-07-15 14:04:48.720871625 +0100
-@@ -0,0 +1,249 @@
++++ b/Documentation/filesystems/aufs/design/02struct.txt	2014-09-08 00:38:33.510569903 +0100
+@@ -0,0 +1,251 @@
 +
 +# Copyright (C) 2005-2014 Junjiro R. Okajima
 +# 
@@ -872,6 +874,8 @@
 +- free space on the lower branch will reduce.
 +- another access to the file may happen during moving-down, including
 +  UDBA.
++- the file should not be hard-linked nor pseudo-linked. they should be
++  handled by auplink utility later.
 +
 +Sometimes users want to move-down a file from the upper writable branch
 +to the lower readonly or writable branch. For instance,
@@ -1161,6 +1165,129 @@
 +  where the source and the target exists and selects the higher
 +  one. If the selected branch is readonly, then aufs follows the
 +  copyup policy.
+--- a/Documentation/filesystems/aufs/design/06fhsm.txt	1970-01-01 01:00:00.000000000 +0100
++++ b/Documentation/filesystems/aufs/design/06fhsm.txt	2014-09-08 00:38:33.510569903 +0100
+@@ -0,0 +1,120 @@
++
++# Copyright (C) 2011-2014 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
++# the Free Software Foundation; either version 2 of the License, or
++# (at your option) any later version.
++# 
++# This program is distributed in the hope that it will be useful,
++# but WITHOUT ANY WARRANTY; without even the implied warranty of
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++# GNU General Public License for more details.
++# 
++# You should have received a copy of the GNU General Public License
++# along with this program; if not, write to the Free Software
++# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
++
++
++File-based Hierarchical Storage Management (FHSM)
++----------------------------------------------------------------------
++Hierarchical Storage Management (or HSM) is a well-known feature in the
++storage world. Aufs provides this feature as file-based with multiple
++writable branches, based upon the principle of "Colder-Lower".
++Here the word "colder" means that the less used files, and "lower" means
++that the position in the order of the stacked branches.
++These multiple writable branches are prioritized, ie. the topmost one
++should be the fastest drive and be used heavily.
++
++o Characters in aufs FHSM story
++- aufs itself and a new branch attribute.
++- a new ioctl interface to move-down and to establish a connection with
++  the daemon ("move-down" is a converse of "copy-up").
++- userspace tool and daemon.
++
++The userspace daemon establishes a connection with aufs and waits for
++the notification. The notified information is very similar to struct
++statfs containing the number of consumed blocks and inodes.
++When the consumed blocks/inodes of a branch exceeds the user-specified
++upper watermark, the daemon activates its move-down process until the
++consumed blocks/inodes reaches the user-specified lower watermark.
++
++The actual move-down is done by aufs based upon the request from
++user-space since we need to maintain the inode number and the internal
++pointer arrays in aufs.
++
++Currently aufs FHSM handles the regular files only. Additionally they
++must not be hard-linked nor pseudo-linked.
++
++
++o Cowork of aufs and the user-space daemon
++  During the userspace daemon established the connection, aufs sends a
++  small notification to it whenever aufs writes something into the
++  writable branch. But it may cost high since aufs issues statfs(2)
++  internally. So user can specify a new option to cache the
++  info. Actually the notification is controlled by these factors.
++  + the specified cache time.
++  + classified as "force" by aufs internally.
++  Until the specified time expires, aufs doesn't send the info
++  except the forced cases. When aufs decide forcing, the info is always
++  notified to userspace.
++  For example, the number of free inodes is generally large enough and
++  the shortage of it happens rarely. So aufs doesn't force the
++  notification when creating a new file, directory and others. This is
++  the typical case which aufs doesn't force.
++  When aufs writes the actual filedata and the files consumes any of new
++  blocks, the aufs forces notifying.
++
++
++o Interfaces in aufs
++- New branch attribute.
++  + fhsm
++    Specifies that the branch is managed by FHSM feature. In other word,
++    participant in the FHSM.
++    When nofhsm is set to the branch, it will not be the source/target
++    branch of the move-down operation. This attribute is set
++    independently from coo and moo attributes, and if you want full
++    FHSM, you should specify them as well.
++- New mount option.
++  + fhsm_sec
++    Specifies a second to suppress many less important info to be
++    notified.
++- New ioctl.
++  + AUFS_CTL_FHSM_FD
++    create a new file descriptor which userspace can read the notification
++    (a subset of struct statfs) from aufs.
++- Module parameter 'brs'
++  It has to be set to 1. Otherwise the new mount option 'fhsm' will not
++  be set.
++- mount helpers /sbin/mount.aufs and /sbin/umount.aufs
++  When there are two or more branches with fhsm attributes,
++  /sbin/mount.aufs invokes the user-space daemon and /sbin/umount.aufs
++  terminates it. As a result of remounting and branch-manipulation, the
++  number of branches with fhsm attribute can be one. In this case,
++  /sbin/mount.aufs will terminate the user-space daemon.
++
++
++Finally the operation is done as these steps in kernel-space.
++- make sure that,
++  + no one else is using the file.
++  + the file is not hard-linked.
++  + the file is not pseudo-linked.
++  + the file is a regular file.
++  + the parent dir is not opaqued.
++- find the target writable branch.
++- make sure the file is not whiteout-ed by the upper (than the target)
++  branch.
++- make the parent dir on the target branch.
++- mutex lock the inode on the branch.
++- unlink the whiteout on the target branch (if exists).
++- lookup and create the whiteout-ed temporary name on the target branch.
++- copy the file as the whiteout-ed temporary name on the target branch.
++- rename the whiteout-ed temporary name to the original name.
++- unlink the file on the source branch.
++- maintain the internal pointer array and the external inode number
++  table (XINO).
++- maintain the timestamps and other attributes of the parent dir and the
++  file.
++
++And of course, in every step, an error may happen. So the operation
++should restore the original file state after an error happens.
 --- a/Documentation/filesystems/aufs/design/06mmap.txt	1970-01-01 01:00:00.000000000 +0100
 +++ b/Documentation/filesystems/aufs/design/06mmap.txt	2014-01-20 03:24:33.508760970 +0000
 @@ -0,0 +1,46 @@
@@ -1437,8 +1564,8 @@
 +/new.
 +Otherwise from /new.
 --- a/fs/aufs/Kconfig	1970-01-01 01:00:00.000000000 +0100
-+++ b/fs/aufs/Kconfig	2014-07-15 14:04:48.724871625 +0100
-@@ -0,0 +1,168 @@
++++ b/fs/aufs/Kconfig	2014-09-08 00:38:33.510569903 +0100
+@@ -0,0 +1,177 @@
 +config AUFS_FS
 +	tristate "Aufs (Advanced multi layered unification filesystem) support"
 +	help
@@ -1528,6 +1655,15 @@
 +	/* typedef unsigned long/int __kernel_ino_t */
 +	/* alpha and s390x are int */
 +
++config AUFS_FHSM
++	bool "File-based Hierarchical Storage Management"
++	help
++	Hierarchical Storage Management (or HSM) is a well-known feature
++	in the storage world. Aufs provides this feature as file-based.
++	with multiple branches.
++	These multiple branches are prioritized, ie. the topmost one
++	should be the fastest drive and be used heavily.
++
 +config AUFS_RDU
 +	bool "Readdir in userspace"
 +	help
@@ -1608,8 +1744,8 @@
 +	When aufs supports Magic SysRq, enabled automatically.
 +endif
 --- a/fs/aufs/Makefile	1970-01-01 01:00:00.000000000 +0100
-+++ b/fs/aufs/Makefile	2014-07-15 14:04:48.724871625 +0100
-@@ -0,0 +1,41 @@
++++ b/fs/aufs/Makefile	2014-09-08 00:38:33.510569903 +0100
+@@ -0,0 +1,42 @@
 +
 +include ${src}/magic.mk
 +ifeq (${CONFIG_AUFS_FS},m)
@@ -1646,6 +1782,7 @@
 +aufs-$(CONFIG_AUFS_HNOTIFY) += hnotify.o
 +aufs-$(CONFIG_AUFS_HFSNOTIFY) += hfsnotify.o
 +aufs-$(CONFIG_AUFS_EXPORT) += export.o
++aufs-$(CONFIG_AUFS_FHSM) += fhsm.o
 +aufs-$(CONFIG_AUFS_POLL) += poll.o
 +aufs-$(CONFIG_AUFS_RDU) += rdu.o
 +aufs-$(CONFIG_AUFS_BR_HFSPLUS) += hfsplus.o
@@ -1714,8 +1851,8 @@
 +#endif /* __KERNEL__ */
 +#endif /* __AUFS_H__ */
 --- a/fs/aufs/branch.c	1970-01-01 01:00:00.000000000 +0100
-+++ b/fs/aufs/branch.c	2014-07-15 14:04:48.724871625 +0100
-@@ -0,0 +1,1238 @@
++++ b/fs/aufs/branch.c	2014-09-08 00:38:33.510569903 +0100
+@@ -0,0 +1,1445 @@
 +/*
 + * Copyright (C) 2005-2014 Junjiro R. Okajima
 + *
@@ -1794,6 +1931,11 @@
 +		AuRwDestroy(&wbr->wbr_wh_rwsem);
 +	}
 +
++	if (br->br_fhsm) {
++		au_br_fhsm_fin(br->br_fhsm);
++		kfree(br->br_fhsm);
++	}
++
 +	key = br->br_dykey;
 +	for (i = 0; i < AuBrDynOp; i++, key++)
 +		if (*key)
@@ -1889,6 +2031,13 @@
 +			goto out_hnotify;
 +	}
 +
++	add_branch->br_fhsm = NULL;
++	if (au_br_fhsm(perm)) {
++		err = au_fhsm_br_alloc(add_branch);
++		if (unlikely(err))
++			goto out_wbr;
++	}
++
 +	err = au_sbr_realloc(au_sbi(sb), new_nbranch);
 +	if (!err)
 +		err = au_di_realloc(au_di(root), new_nbranch);
@@ -1897,8 +2046,8 @@
 +	if (!err)
 +		return add_branch; /* success */
 +
++out_wbr:
 +	kfree(add_branch->br_wbr);
-+
 +out_hnotify:
 +	au_hnotify_fin_br(add_branch);
 +out_br:
@@ -2281,6 +2430,54 @@
 +
 +/* ---------------------------------------------------------------------- */
 +
++static unsigned long long au_farray_cb(void *a,
++				       unsigned long long max __maybe_unused,
++				       void *arg)
++{
++	unsigned long long n;
++	struct file **p, *f;
++	struct au_sphlhead *files;
++	struct au_finfo *finfo;
++	struct super_block *sb = arg;
++
++	n = 0;
++	p = a;
++	files = &au_sbi(sb)->si_files;
++	spin_lock(&files->spin);
++	hlist_for_each_entry(finfo, &files->head, fi_hlist) {
++		f = finfo->fi_file;
++		if (file_count(f)
++		    && !special_file(file_inode(f)->i_mode)) {
++			get_file(f);
++			*p++ = f;
++			n++;
++			AuDebugOn(n > max);
++		}
++	}
++	spin_unlock(&files->spin);
++
++	return n;
++}
++
++static struct file **au_farray_alloc(struct super_block *sb,
++				     unsigned long long *max)
++{
++	*max = atomic_long_read(&au_sbi(sb)->si_nfiles);
++	return au_array_alloc(max, au_farray_cb, sb);
++}
++
++static void au_farray_free(struct file **a, unsigned long long max)
++{
++	unsigned long long ull;
++
++	for (ull = 0; ull < max; ull++)
++		if (a[ull])
++			fput(a[ull]);
++	au_array_free(a);
++}
++
++/* ---------------------------------------------------------------------- */
++
 +/*
 + * delete a branch
 + */
@@ -2387,6 +2584,8 @@
 +	AuDbg("b%d\n", bindex);
 +	for (ull = 0; !err && ull < max; ull++) {
 +		i = array[ull];
++		if (unlikely(!i))
++			break;
 +		if (i->i_ino == AUFS_ROOT_INO)
 +			continue;
 +
@@ -2441,6 +2640,137 @@
 +	return err;
 +}
 +
++static int test_dir_busy(struct file *file, aufs_bindex_t br_id,
++			 struct file **to_free, int *idx)
++{
++	int err;
++	unsigned char matched, root;
++	aufs_bindex_t bindex, bend;
++	struct au_fidir *fidir;
++	struct au_hfile *hfile;
++
++	err = 0;
++	root = IS_ROOT(file->f_dentry);
++	if (root) {
++		get_file(file);
++		to_free[*idx] = file;
++		(*idx)++;
++		goto out;
++	}
++
++	matched = 0;
++	fidir = au_fi(file)->fi_hdir;
++	AuDebugOn(!fidir);
++	bend = au_fbend_dir(file);
++	for (bindex = au_fbstart(file); bindex <= bend; bindex++) {
++		hfile = fidir->fd_hfile + bindex;
++		if (!hfile->hf_file)
++			continue;
++
++		if (hfile->hf_br->br_id == br_id) {
++			matched = 1;
++			break;
++		}
++	}
++	if (matched)
++		err = -EBUSY;
++
++out:
++	return err;
++}
++
++static int test_file_busy(struct super_block *sb, aufs_bindex_t br_id,
++			  struct file **to_free, int opened)
++{
++	int err, idx;
++	unsigned long long ull, max;
++	aufs_bindex_t bstart;
++	struct file *file, **array;
++	struct inode *inode;
++	struct dentry *root;
++	struct au_hfile *hfile;
++
++	array = au_farray_alloc(sb, &max);
++	err = PTR_ERR(array);
++	if (IS_ERR(array))
++		goto out;
++
++	err = 0;
++	idx = 0;
++	root = sb->s_root;
++	di_write_unlock(root);
++	for (ull = 0; ull < max; ull++) {
++		file = array[ull];
++		if (unlikely(!file))
++			break;
++
++		/* AuDbg("%pD\n", file); */
++		fi_read_lock(file);
++		bstart = au_fbstart(file);
++		inode = file_inode(file);
++		if (!S_ISDIR(inode->i_mode)) {
++			hfile = &au_fi(file)->fi_htop;
++			if (hfile->hf_br->br_id == br_id)
++				err = -EBUSY;
++		} else
++			err = test_dir_busy(file, br_id, to_free, &idx);
++		fi_read_unlock(file);
++		if (unlikely(err))
++			break;
++	}
++	di_write_lock_child(root);
++	au_farray_free(array, max);
++	AuDebugOn(idx > opened);
++
++out:
++	return err;
++}
++
++static void br_del_file(struct file **to_free, unsigned long long opened,
++			  aufs_bindex_t br_id)
++{
++	unsigned long long ull;
++	aufs_bindex_t bindex, bstart, bend, bfound;
++	struct file *file;
++	struct au_fidir *fidir;
++	struct au_hfile *hfile;
++
++	for (ull = 0; ull < opened; ull++) {
++		file = to_free[ull];
++		if (unlikely(!file))
++			break;
++
++		/* AuDbg("%pD\n", file); */
++		AuDebugOn(!S_ISDIR(file_inode(file)->i_mode));
++		bfound = -1;
++		fidir = au_fi(file)->fi_hdir;
++		AuDebugOn(!fidir);
++		fi_write_lock(file);
++		bstart = au_fbstart(file);
++		bend = au_fbend_dir(file);
++		for (bindex = bstart; bindex <= bend; bindex++) {
++			hfile = fidir->fd_hfile + bindex;
++			if (!hfile->hf_file)
++				continue;
++
++			if (hfile->hf_br->br_id == br_id) {
++				bfound = bindex;
++				break;
++			}
++		}
++		AuDebugOn(bfound < 0);
++		au_set_h_fptr(file, bfound, NULL);
++		if (bfound == bstart) {
++			for (bstart++; bstart <= bend; bstart++)
++				if (au_hf_dir(file, bstart)) {
++					au_set_fbstart(file, bstart);
++					break;
++				}
++		}
++		fi_write_unlock(file);
++	}
++}
++
 +static void au_br_do_del_brp(struct au_sbinfo *sbinfo,
 +			     const aufs_bindex_t bindex,
 +			     const aufs_bindex_t bend)
@@ -2533,17 +2863,29 @@
 +	au_br_do_free(br);
 +}
 +
++static unsigned long long empty_cb(void *array, unsigned long long max,
++				   void *arg)
++{
++	return max;
++}
++
 +int au_br_del(struct super_block *sb, struct au_opt_del *del, int remount)
 +{
 +	int err, rerr, i;
++	unsigned long long opened;
 +	unsigned int mnt_flags;
 +	aufs_bindex_t bindex, bend, br_id;
 +	unsigned char do_wh, verbose;
 +	struct au_branch *br;
 +	struct au_wbr *wbr;
++	struct dentry *root;
++	struct file **to_free;
 +
 +	err = 0;
-+	bindex = au_find_dbindex(sb->s_root, del->h_path.dentry);
++	opened = 0;
++	to_free = NULL;
++	root = sb->s_root;
++	bindex = au_find_dbindex(root, del->h_path.dentry);
 +	if (bindex < 0) {
 +		if (remount)
 +			goto out; /* success */
@@ -2563,10 +2905,20 @@
 +	}
 +	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);
-+		goto out;
++
++	br_id = br->br_id;
++	opened = atomic_read(&br->br_count);
++	if (unlikely(opened)) {
++		to_free = au_array_alloc(&opened, empty_cb, NULL);
++		err = PTR_ERR(to_free);
++		if (IS_ERR(to_free))
++			goto out;
++
++		err = test_file_busy(sb, br_id, to_free, opened);
++		if (unlikely(err)) {
++			AuVerbose(verbose, "%llu file(s) opened\n", opened);
++			goto out;
++		}
 +	}
 +
 +	wbr = br->br_wbr;
@@ -2580,7 +2932,7 @@
 +		}
 +	}
 +
-+	err = test_children_busy(sb->s_root, bindex, verbose);
++	err = test_children_busy(root, bindex, verbose);
 +	if (unlikely(err)) {
 +		if (do_wh)
 +			goto out_wh;
@@ -2588,7 +2940,16 @@
 +	}
 +
 +	err = 0;
-+	br_id = br->br_id;
++	if (to_free) {
++		/*
++		 * now we confirmed the branch is deletable.
++		 * let's free the remaining opened dirs on the branch.
++		 */
++		di_write_unlock(root);
++		br_del_file(to_free, opened, br_id);
++		di_write_lock_child(root);
++	}
++
 +	if (!remount)
 +		au_br_do_del(sb, bindex, br);
 +	else {
@@ -2598,10 +2959,10 @@
 +	}
 +
 +	if (!bindex) {
-+		au_cpup_attr_all(sb->s_root->d_inode, /*force*/1);
++		au_cpup_attr_all(root->d_inode, /*force*/1);
 +		sb->s_maxbytes = au_sbr_sb(sb, 0)->s_maxbytes;
 +	} else
-+		au_sub_nlink(sb->s_root->d_inode, del->h_path.dentry->d_inode);
++		au_sub_nlink(root->d_inode, del->h_path.dentry->d_inode);
 +	if (au_opt_test(mnt_flags, PLINK))
 +		au_plink_half_refresh(sb, br_id);
 +
@@ -2616,6 +2977,8 @@
 +		pr_warn("failed re-creating base whiteout, %s. (%d)\n",
 +			del->pathname, rerr);
 +out:
++	if (to_free)
++		au_farray_free(to_free, opened);
 +	return err;
 +}
 +
@@ -2715,52 +3078,6 @@
 +		|| do_need_sigen_inc(new, old);
 +}
 +
-+static unsigned long long au_farray_cb(void *a,
-+				       unsigned long long max __maybe_unused,
-+				       void *arg)
-+{
-+	unsigned long long n;
-+	struct file **p, *f;
-+	struct au_sphlhead *files;
-+	struct au_finfo *finfo;
-+	struct super_block *sb = arg;
-+
-+	n = 0;
-+	p = a;
-+	files = &au_sbi(sb)->si_files;
-+	spin_lock(&files->spin);
-+	hlist_for_each_entry(finfo, &files->head, fi_hlist) {
-+		f = finfo->fi_file;
-+		if (file_count(f)
-+		    && !special_file(file_inode(f)->i_mode)) {
-+			get_file(f);
-+			*p++ = f;
-+			n++;
-+			AuDebugOn(n > max);
-+		}
-+	}
-+	spin_unlock(&files->spin);
-+
-+	return n;
-+}
-+
-+static struct file **au_farray_alloc(struct super_block *sb,
-+				     unsigned long long *max)
-+{
-+	*max = atomic_long_read(&au_sbi(sb)->si_nfiles);
-+	return au_array_alloc(max, au_farray_cb, sb);
-+}
-+
-+static void au_farray_free(struct file **a, unsigned long long max)
-+{
-+	unsigned long long ull;
-+
-+	for (ull = 0; ull < max; ull++)
-+		if (a[ull])
-+			fput(a[ull]);
-+	au_array_free(a);
-+}
-+
 +static int au_br_mod_files_ro(struct super_block *sb, aufs_bindex_t bindex)
 +{
 +	int err, do_warn;
@@ -2784,6 +3101,8 @@
 +	br_id = au_sbr_id(sb, bindex);
 +	for (ull = 0; ull < max; ull++) {
 +		file = array[ull];
++		if (unlikely(!file))
++			break;
 +
 +		/* AuDbg("%pD\n", file); */
 +		fi_read_lock(file);
@@ -2859,6 +3178,7 @@
 +	aufs_bindex_t bindex;
 +	struct dentry *root;
 +	struct au_branch *br;
++	struct au_br_fhsm *bf;
 +
 +	root = sb->s_root;
 +	bindex = au_find_dbindex(root, mod->h_root);
@@ -2880,11 +3200,21 @@
 +	if (br->br_perm == mod->perm)
 +		return 0; /* success */
 +
++	/* pre-allocate for non-fhsm --> fhsm */
++	bf = NULL;
++	if (!au_br_fhsm(br->br_perm) && au_br_fhsm(mod->perm)) {
++		err = au_fhsm_br_alloc(br);
++		if (unlikely(err))
++			goto out;
++		bf = br->br_fhsm;
++		br->br_fhsm = NULL;
++	}
++
 +	if (au_br_writable(br->br_perm)) {
 +		/* remove whiteout base */
 +		err = au_br_init_wh(sb, br, mod->perm);
 +		if (unlikely(err))
-+			goto out;
++			goto out_bf;
 +
 +		if (!au_br_writable(mod->perm)) {
 +			/* rw --> ro, file might be mmapped */
@@ -2920,18 +3250,32 @@
 +			}
 +		}
 +	}
++	if (unlikely(err))
++		goto out_bf;
 +
-+	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;
-+	}
++	if (au_br_fhsm(br->br_perm)) {
++		if (!au_br_fhsm(mod->perm)) {
++			/* fhsm --> non-fhsm */
++			au_br_fhsm_fin(br->br_fhsm);
++			kfree(br->br_fhsm);
++			br->br_fhsm = NULL;
++		}
++	} else if (au_br_fhsm(mod->perm))
++		/* non-fhsm --> fhsm */
++		br->br_fhsm = bf;
++
++	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;
++	goto out; /* success */
 +
++out_bf:
++	kfree(bf);
 +out:
 +	AuTraceErr(err);
 +	return err;
@@ -2955,8 +3299,8 @@
 +	return err;
 +}
 --- a/fs/aufs/branch.h	1970-01-01 01:00:00.000000000 +0100
-+++ b/fs/aufs/branch.h	2014-07-15 14:04:48.724871625 +0100
-@@ -0,0 +1,290 @@
++++ b/fs/aufs/branch.h	2014-09-08 00:38:33.510569903 +0100
+@@ -0,0 +1,268 @@
 +/*
 + * Copyright (C) 2005-2014 Junjiro R. Okajima
 + *
@@ -3002,6 +3346,16 @@
 +#endif
 +};
 +
++/* File-based Hierarchical Storage Management */
++struct au_br_fhsm {
++#ifdef CONFIG_AUFS_FHSM
++	struct mutex		bf_lock;
++	unsigned long		bf_jiffy;
++	struct aufs_stfs	bf_stfs;
++	int			bf_readable;
++#endif
++};
++
 +/* members for writable branch only */
 +enum {AuBrWh_BASE, AuBrWh_PLINK, AuBrWh_ORPH, AuBrWh_Last};
 +struct au_wbr {
@@ -3052,6 +3406,7 @@
 +	atomic_t		br_count;
 +
 +	struct au_wbr		*br_wbr;
++	struct au_br_fhsm	*br_fhsm;
 +
 +	/* xino truncation */
 +	atomic_t		br_xino_running;
@@ -3083,64 +3438,12 @@
 +	return au_br_mnt(br)->mnt_sb;
 +}
 +
-+/* branch permissions and attributes */
-+#define AuBrPerm_RW		1		/* writable, hardlinkable wh */
-+#define AuBrPerm_RO		(1 << 1)	/* readonly */
-+#define AuBrPerm_RR		(1 << 2)	/* natively readonly */
-+#define AuBrPerm_Mask		(AuBrPerm_RW | AuBrPerm_RO | AuBrPerm_RR)
-+
-+#define AuBrAttr_COO_REG	(1 << 3)	/* copy-up on open */
-+#define AuBrAttr_COO_ALL	(1 << 4)
-+#define AuBrAttr_COO_Mask	(AuBrAttr_COO_REG | AuBrAttr_COO_ALL)
-+
-+#define AuBrAttr_UNPIN		(1 << 5)	/* rename-able top dir of
-+						   branch */
-+
-+#define AuBrRAttr_WH		(1 << 6)	/* whiteout-able */
-+#define AuBrRAttr_Mask		AuBrRAttr_WH
-+
-+#define AuBrWAttr_NoLinkWH	(1 << 7)	/* un-hardlinkable whiteouts */
-+#define AuBrWAttr_MOO		(1 << 8)	/* move-up on open */
-+#define AuBrWAttr_Mask		(AuBrWAttr_NoLinkWH | AuBrWAttr_MOO)
-+
-+#define AuBrAttr_CMOO_Mask	(AuBrAttr_COO_Mask | AuBrWAttr_MOO)
-+
-+/* the longest combination */
-+#define AuBrPermStrSz	sizeof(AUFS_BRPERM_RW		\
-+			       "+" AUFS_BRATTR_COO_REG	\
-+			       "+" AUFS_BRATTR_UNPIN	\
-+			       "+" AUFS_BRWATTR_NLWH)
-+
-+typedef struct {
-+	char a[AuBrPermStrSz];
-+} au_br_perm_str_t;
-+
-+static inline int au_br_writable(int brperm)
-+{
-+	return brperm & AuBrPerm_RW;
-+}
-+
-+static inline int au_br_whable(int brperm)
-+{
-+	return brperm & (AuBrPerm_RW | AuBrRAttr_WH);
-+}
-+
-+static inline int au_br_wh_linkable(int brperm)
-+{
-+	return !(brperm & AuBrWAttr_NoLinkWH);
-+}
-+
-+static inline int au_br_rdonly(struct au_branch *br)
-+{
-+	return ((au_br_sb(br)->s_flags & MS_RDONLY)
-+		|| !au_br_writable(br->br_perm))
-+		? -EROFS : 0;
-+}
-+
-+static inline int au_br_cmoo(int brperm)
-+{
-+	return brperm & AuBrAttr_CMOO_Mask;
-+}
++static inline int au_br_rdonly(struct au_branch *br)
++{
++	return ((au_br_sb(br)->s_flags & MS_RDONLY)
++		|| !au_br_writable(br->br_perm))
++		? -EROFS : 0;
++}
 +
 +static inline int au_br_hnotifyable(int brperm __maybe_unused)
 +{
@@ -3245,11 +3548,30 @@
 +#define WbrWhMustAnyLock(wbr)	AuRwMustAnyLock(&wbr->wbr_wh_rwsem)
 +#define WbrWhMustWriteLock(wbr)	AuRwMustWriteLock(&wbr->wbr_wh_rwsem)
 +
++/* ---------------------------------------------------------------------- */
++
++#ifdef CONFIG_AUFS_FHSM
++static inline void au_br_fhsm_init(struct au_br_fhsm *brfhsm)
++{
++	mutex_init(&brfhsm->bf_lock);
++	brfhsm->bf_jiffy = 0;
++	brfhsm->bf_readable = 0;
++}
++
++static inline void au_br_fhsm_fin(struct au_br_fhsm *brfhsm)
++{
++	mutex_destroy(&brfhsm->bf_lock);
++}
++#else
++AuStubVoid(au_br_fhsm_init, struct au_br_fhsm *brfhsm)
++AuStubVoid(au_br_fhsm_fin, struct au_br_fhsm *brfhsm)
++#endif
++
 +#endif /* __KERNEL__ */
 +#endif /* __AUFS_BRANCH_H__ */
 --- a/fs/aufs/conf.mk	1970-01-01 01:00:00.000000000 +0100
-+++ b/fs/aufs/conf.mk	2014-07-15 14:04:48.724871625 +0100
-@@ -0,0 +1,36 @@
++++ b/fs/aufs/conf.mk	2014-09-08 00:38:33.510569903 +0100
+@@ -0,0 +1,37 @@
 +
 +AuConfStr = CONFIG_AUFS_FS=${CONFIG_AUFS_FS}
 +
@@ -3263,6 +3585,7 @@
 +	SBILIST \
 +	HNOTIFY HFSNOTIFY \
 +	EXPORT INO_T_64 \
++	FHSM \
 +	RDU \
 +	SHWH \
 +	BR_RAMFS \
@@ -3287,8 +3610,8 @@
 +
 +-include ${srctree}/${src}/conf_priv.mk
 --- a/fs/aufs/cpup.c	1970-01-01 01:00:00.000000000 +0100
-+++ b/fs/aufs/cpup.c	2014-07-15 14:04:48.724871625 +0100
-@@ -0,0 +1,1289 @@
++++ b/fs/aufs/cpup.c	2014-09-08 00:38:33.510569903 +0100
+@@ -0,0 +1,1301 @@
 +/*
 + * Copyright (C) 2005-2014 Junjiro R. Okajima
 + *
@@ -3788,7 +4111,7 @@
 +	int err;
 +	umode_t mode;
 +	unsigned int mnt_flags;
-+	unsigned char isdir;
++	unsigned char isdir, isreg, force;
 +	const unsigned char do_dt = !!au_ftest_cpup(cpg->flags, DTIME);
 +	struct au_dtime dt;
 +	struct path h_path;
@@ -3819,10 +4142,12 @@
 +	}
 +	h_path.dentry = h_dst;
 +
++	isreg = 0;
 +	isdir = 0;
 +	mode = h_inode->i_mode;
 +	switch (mode & S_IFMT) {
 +	case S_IFREG:
++		isreg = 1;
 +		err = vfsub_create(h_dir, &h_path, mode | S_IWUSR,
 +				   /*want_excl*/true);
 +		if (!err)
@@ -3871,6 +4196,16 @@
 +		au_xino_write(sb, cpg->bsrc, h_inode->i_ino, /*ino*/0);
 +		/* ignore this error */
 +
++	if (!err) {
++		force = 0;
++		if (isreg) {
++			force = !!cpg->len;
++			if (cpg->len == -1)
++				force = !!i_size_read(h_inode);
++		}
++		au_fhsm_wrote(sb, cpg->bdst, force);
++	}
++
 +	if (do_dt)
 +		au_dtime_revert(&dt);
 +	return err;
@@ -5531,8 +5866,8 @@
 +#endif /* __KERNEL__ */
 +#endif /* __AUFS_DCSUB_H__ */
 --- a/fs/aufs/debug.c	1970-01-01 01:00:00.000000000 +0100
-+++ b/fs/aufs/debug.c	2014-07-15 14:04:48.728871625 +0100
-@@ -0,0 +1,519 @@
++++ b/fs/aufs/debug.c	2014-09-08 00:38:33.514569904 +0100
+@@ -0,0 +1,520 @@
 +/*
 + * Copyright (C) 2005-2014 Junjiro R. Okajima
 + *
@@ -5591,6 +5926,7 @@
 +MODULE_PARM_DESC(debug, "debug print");
 +module_param_named(debug, aufs_debug, atomic_t, S_IRUGO | S_IWUSR | S_IWGRP);
 +
++DEFINE_MUTEX(au_dbg_mtx);	/* just to serialize the dbg msgs */
 +char *au_plevel = KERN_DEBUG;
 +#define dpri(fmt, ...) do {					\
 +	if ((au_plevel						\
@@ -6053,8 +6389,8 @@
 +	return 0;
 +}
 --- a/fs/aufs/debug.h	1970-01-01 01:00:00.000000000 +0100
-+++ b/fs/aufs/debug.h	2014-01-20 03:24:33.508760970 +0000
-@@ -0,0 +1,247 @@
++++ b/fs/aufs/debug.h	2014-09-08 00:38:33.514569904 +0100
+@@ -0,0 +1,262 @@
 +/*
 + * Copyright (C) 2005-2014 Junjiro R. Okajima
 + *
@@ -6167,6 +6503,7 @@
 +struct au_finfo;
 +struct dentry;
 +#ifdef CONFIG_AUFS_DEBUG
++extern struct mutex au_dbg_mtx;
 +extern char *au_plevel;
 +struct au_nhash;
 +void au_dpri_whlist(struct au_nhash *whlist);
@@ -6195,38 +6532,52 @@
 +int __init au_debug_init(void);
 +void au_debug_sbinfo_init(struct au_sbinfo *sbinfo);
 +#define AuDbgWhlist(w) do { \
++	mutex_lock(&au_dbg_mtx); \
 +	AuDbg(#w "\n"); \
 +	au_dpri_whlist(w); \
++	mutex_unlock(&au_dbg_mtx); \
 +} while (0)
 +
 +#define AuDbgVdir(v) do { \
++	mutex_lock(&au_dbg_mtx); \
 +	AuDbg(#v "\n"); \
 +	au_dpri_vdir(v); \
++	mutex_unlock(&au_dbg_mtx); \
 +} while (0)
 +
 +#define AuDbgInode(i) do { \
++	mutex_lock(&au_dbg_mtx); \
 +	AuDbg(#i "\n"); \
 +	au_dpri_inode(i); \
++	mutex_unlock(&au_dbg_mtx); \
 +} while (0)
 +
 +#define AuDbgDAlias(i) do { \
++	mutex_lock(&au_dbg_mtx); \
 +	AuDbg(#i "\n"); \
 +	au_dpri_dalias(i); \
++	mutex_unlock(&au_dbg_mtx); \
 +} while (0)
 +
 +#define AuDbgDentry(d) do { \
++	mutex_lock(&au_dbg_mtx); \
 +	AuDbg(#d "\n"); \
 +	au_dpri_dentry(d); \
++	mutex_unlock(&au_dbg_mtx); \
 +} while (0)
 +
 +#define AuDbgFile(f) do { \
++	mutex_lock(&au_dbg_mtx); \
 +	AuDbg(#f "\n"); \
 +	au_dpri_file(f); \
++	mutex_unlock(&au_dbg_mtx); \
 +} while (0)
 +
 +#define AuDbgSb(sb) do { \
++	mutex_lock(&au_dbg_mtx); \
 +	AuDbg(#sb "\n"); \
 +	au_dpri_sb(sb); \
++	mutex_unlock(&au_dbg_mtx); \
 +} while (0)
 +
 +#define AuDbgSleep(sec) do { \
@@ -6303,8 +6654,8 @@
 +#endif /* __KERNEL__ */
 +#endif /* __AUFS_DEBUG_H__ */
 --- a/fs/aufs/dentry.c	1970-01-01 01:00:00.000000000 +0100
-+++ b/fs/aufs/dentry.c	2014-07-15 14:04:48.728871625 +0100
-@@ -0,0 +1,1094 @@
++++ b/fs/aufs/dentry.c	2014-09-08 00:38:33.514569904 +0100
+@@ -0,0 +1,1096 @@
 +/*
 + * Copyright (C) 2005-2014 Junjiro R. Okajima
 + *
@@ -7311,10 +7662,6 @@
 +	if (unlikely(!au_di(dentry)))
 +		goto out;
 +
-+	inode = dentry->d_inode;
-+	if (inode && is_bad_inode(inode))
-+		goto out;
-+
 +	valid = 1;
 +	sb = dentry->d_sb;
 +	/*
@@ -7328,6 +7675,12 @@
 +		AuTraceErr(err);
 +		goto out;
 +	}
++	inode = dentry->d_inode;
++	if (unlikely(inode && is_bad_inode(inode))) {
++		err = -EINVAL;
++		AuTraceErr(err);
++		goto out_dgrade;
++	}
 +	if (unlikely(au_dbrange_test(dentry))) {
 +		err = -EINVAL;
 +		AuTraceErr(err);
@@ -8183,8 +8536,8 @@
 +	return -1;
 +}
 --- a/fs/aufs/dir.c	1970-01-01 01:00:00.000000000 +0100
-+++ b/fs/aufs/dir.c	2014-07-15 14:04:48.728871625 +0100
-@@ -0,0 +1,643 @@
++++ b/fs/aufs/dir.c	2014-09-08 00:38:33.514569904 +0100
+@@ -0,0 +1,645 @@
 +/*
 + * Copyright (C) 2005-2014 Junjiro R. Okajima
 + *
@@ -8404,6 +8757,8 @@
 +	finfo = au_fi(file);
 +	fidir = finfo->fi_hdir;
 +	if (fidir) {
++		au_sphl_del(&finfo->fi_hlist,
++			    &au_sbi(file->f_dentry->d_sb)->si_files);
 +		vdir_cache = fidir->fd_vdir_cache; /* lock-free */
 +		if (vdir_cache)
 +			au_vdir_free(vdir_cache);
@@ -9428,7 +9783,7 @@
 +#endif /* __KERNEL__ */
 +#endif /* __AUFS_DYNOP_H__ */
 --- a/fs/aufs/export.c	1970-01-01 01:00:00.000000000 +0100
-+++ b/fs/aufs/export.c	2014-01-20 03:24:33.512760970 +0000
++++ b/fs/aufs/export.c	2014-09-08 00:38:33.514569904 +0100
 @@ -0,0 +1,831 @@
 +/*
 + * Copyright (C) 2005-2014 Junjiro R. Okajima
@@ -10146,7 +10501,7 @@
 +	ii_read_lock_child(inode);
 +	bindex = au_ibstart(inode);
 +	if (!dir) {
-+		dentry = d_find_alias(inode);
++		dentry = d_find_any_alias(inode);
 +		if (unlikely(!dentry))
 +			goto out_unlock;
 +		AuDebugOn(au_test_anon(dentry));
@@ -10162,7 +10517,7 @@
 +	ii_read_unlock(dir);
 +	if (unlikely(!h_dir))
 +		goto out_parent;
-+	h_parent = d_find_alias(h_dir);
++	h_parent = d_find_any_alias(h_dir);
 +	if (unlikely(!h_parent))
 +		goto out_hparent;
 +
@@ -10262,8 +10617,8 @@
 +	atomic_set(&sbinfo->si_xigen_next, u);
 +}
 --- a/fs/aufs/f_op.c	1970-01-01 01:00:00.000000000 +0100
-+++ b/fs/aufs/f_op.c	2014-07-15 14:04:48.728871625 +0100
-@@ -0,0 +1,791 @@
++++ b/fs/aufs/f_op.c	2014-09-08 00:38:33.514569904 +0100
+@@ -0,0 +1,813 @@
 +/*
 + * Copyright (C) 2005-2014 Junjiro R. Okajima
 + *
@@ -10322,8 +10677,6 @@
 +		au_set_fbstart(file, bindex);
 +		au_set_h_fptr(file, bindex, h_file);
 +		au_update_figen(file);
-+		finfo->fi_file = file;
-+		au_sphl_add(&finfo->fi_hlist, &au_sbi(dentry->d_sb)->si_files);
 +		/* todo: necessary? */
 +		/* file->f_ra = h_file->f_ra; */
 +	}
@@ -10449,10 +10802,12 @@
 +			  size_t count, loff_t *ppos)
 +{
 +	ssize_t err;
++	blkcnt_t blks;
++	aufs_bindex_t bstart;
 +	struct au_pin pin;
 +	struct dentry *dentry;
++	struct inode *inode, *h_inode;
 +	struct super_block *sb;
-+	struct inode *inode;
 +	struct file *h_file;
 +	char __user *buf = (char __user *)ubuf;
 +
@@ -10473,8 +10828,11 @@
 +		goto out;
 +	}
 +
++	bstart = au_fbstart(file);
 +	h_file = au_hf_top(file);
 +	get_file(h_file);
++	h_inode = h_file->f_dentry->d_inode;
++	blks = h_inode->i_blocks;
 +	au_unpin(&pin);
 +	di_read_unlock(dentry, AuLock_IR);
 +	fi_write_unlock(file);
@@ -10483,6 +10841,9 @@
 +	ii_write_lock_child(inode);
 +	au_cpup_attr_timesizes(inode);
 +	inode->i_mode = file_inode(h_file)->i_mode;
++	AuDbg("blks %llu, %llu\n", (u64)blks, (u64)h_inode->i_blocks);
++	if (err > 0)
++		au_fhsm_wrote(sb, bstart, /*force*/h_inode->i_blocks > blks);
 +	ii_write_unlock(inode);
 +	fput(h_file);
 +
@@ -10570,9 +10931,11 @@
 +static ssize_t aufs_write_iter(struct kiocb *kio, struct iov_iter *iov_iter)
 +{
 +	ssize_t err;
++	blkcnt_t blks;
++	aufs_bindex_t bstart;
 +	struct au_pin pin;
 +	struct dentry *dentry;
-+	struct inode *inode;
++	struct inode *inode, *h_inode;
 +	struct file *file, *h_file;
 +	struct super_block *sb;
 +
@@ -10594,8 +10957,11 @@
 +		goto out;
 +	}
 +
++	bstart = au_fbstart(file);
 +	h_file = au_hf_top(file);
 +	get_file(h_file);
++	h_inode = h_file->f_dentry->d_inode;
++	blks = h_inode->i_blocks;
 +	au_unpin(&pin);
 +	di_read_unlock(dentry, AuLock_IR);
 +	fi_write_unlock(file);
@@ -10604,6 +10970,9 @@
 +	ii_write_lock_child(inode);
 +	au_cpup_attr_timesizes(inode);
 +	inode->i_mode = file_inode(h_file)->i_mode;
++	AuDbg("blks %llu, %llu\n", (u64)blks, (u64)h_inode->i_blocks);
++	if (err > 0)
++		au_fhsm_wrote(sb, bstart, /*force*/h_inode->i_blocks > blks);
 +	ii_write_unlock(inode);
 +	fput(h_file);
 +
@@ -10659,11 +11028,13 @@
 +		  size_t len, unsigned int flags)
 +{
 +	ssize_t err;
++	blkcnt_t blks;
++	aufs_bindex_t bstart;
 +	struct au_pin pin;
 +	struct dentry *dentry;
-+	struct inode *inode;
-+	struct file *h_file;
++	struct inode *inode, *h_inode;
 +	struct super_block *sb;
++	struct file *h_file;
 +
 +	dentry = file->f_dentry;
 +	sb = dentry->d_sb;
@@ -10682,8 +11053,11 @@
 +		goto out;
 +	}
 +
++	bstart = au_fbstart(file);
 +	h_file = au_hf_top(file);
 +	get_file(h_file);
++	h_inode = h_file->f_dentry->d_inode;
++	blks = h_inode->i_blocks;
 +	au_unpin(&pin);
 +	di_read_unlock(dentry, AuLock_IR);
 +	fi_write_unlock(file);
@@ -10692,6 +11066,9 @@
 +	ii_write_lock_child(inode);
 +	au_cpup_attr_timesizes(inode);
 +	inode->i_mode = file_inode(h_file)->i_mode;
++	AuDbg("blks %llu, %llu\n", (u64)blks, (u64)h_inode->i_blocks);
++	if (err > 0)
++		au_fhsm_wrote(sb, bstart, /*force*/h_inode->i_blocks > blks);
 +	ii_write_unlock(inode);
 +	fput(h_file);
 +
@@ -10878,186 +11255,615 @@
 +	return err;
 +}
 +
-+/* ---------------------------------------------------------------------- */
-+
-+static int aufs_fsync_nondir(struct file *file, loff_t start, loff_t end,
-+			     int datasync)
++/* ---------------------------------------------------------------------- */
++
++static int aufs_fsync_nondir(struct file *file, loff_t start, loff_t end,
++			     int datasync)
++{
++	int err;
++	struct au_pin pin;
++	struct dentry *dentry;
++	struct inode *inode;
++	struct file *h_file;
++	struct super_block *sb;
++
++	dentry = file->f_dentry;
++	inode = dentry->d_inode;
++	sb = dentry->d_sb;
++	mutex_lock(&inode->i_mutex);
++	err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM);
++	if (unlikely(err))
++		goto out;
++
++	err = 0; /* -EBADF; */ /* posix? */
++	if (unlikely(!(file->f_mode & FMODE_WRITE)))
++		goto out_si;
++	err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1);
++	if (unlikely(err))
++		goto out_si;
++
++	err = au_ready_to_write(file, -1, &pin);
++	di_downgrade_lock(dentry, AuLock_IR);
++	if (unlikely(err))
++		goto out_unlock;
++	au_unpin(&pin);
++
++	err = -EINVAL;
++	h_file = au_hf_top(file);
++	err = vfsub_fsync(h_file, &h_file->f_path, datasync);
++	au_cpup_attr_timesizes(inode);
++
++out_unlock:
++	di_read_unlock(dentry, AuLock_IR);
++	fi_write_unlock(file);
++out_si:
++	si_read_unlock(sb);
++out:
++	mutex_unlock(&inode->i_mutex);
++	return err;
++}
++
++/* no one supports this operation, currently */
++#if 0
++static int aufs_aio_fsync_nondir(struct kiocb *kio, int datasync)
++{
++	int err;
++	struct au_pin pin;
++	struct dentry *dentry;
++	struct inode *inode;
++	struct file *file, *h_file;
++
++	file = kio->ki_filp;
++	dentry = file->f_dentry;
++	inode = dentry->d_inode;
++	au_mtx_and_read_lock(inode);
++
++	err = 0; /* -EBADF; */ /* posix? */
++	if (unlikely(!(file->f_mode & FMODE_WRITE)))
++		goto out;
++	err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1);
++	if (unlikely(err))
++		goto out;
++
++	err = au_ready_to_write(file, -1, &pin);
++	di_downgrade_lock(dentry, AuLock_IR);
++	if (unlikely(err))
++		goto out_unlock;
++	au_unpin(&pin);
++
++	err = -ENOSYS;
++	h_file = au_hf_top(file);
++	if (h_file->f_op->aio_fsync) {
++		struct mutex *h_mtx;
++
++		h_mtx = &file_inode(h_file)->i_mutex;
++		if (!is_sync_kiocb(kio)) {
++			get_file(h_file);
++			fput(file);
++		}
++		kio->ki_filp = h_file;
++		err = h_file->f_op->aio_fsync(kio, datasync);
++		mutex_lock_nested(h_mtx, AuLsc_I_CHILD);
++		if (!err)
++			vfsub_update_h_iattr(&h_file->f_path, /*did*/NULL);
++		/*ignore*/
++		au_cpup_attr_timesizes(inode);
++		mutex_unlock(h_mtx);
++	}
++
++out_unlock:
++	di_read_unlock(dentry, AuLock_IR);
++	fi_write_unlock(file);
++out:
++	si_read_unlock(inode->sb);
++	mutex_unlock(&inode->i_mutex);
++	return err;
++}
++#endif
++
++static int aufs_fasync(int fd, struct file *file, int flag)
++{
++	int err;
++	struct file *h_file;
++	struct dentry *dentry;
++	struct super_block *sb;
++
++	dentry = file->f_dentry;
++	sb = dentry->d_sb;
++	si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW);
++	err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/0);
++	if (unlikely(err))
++		goto out;
++
++	h_file = au_hf_top(file);
++	if (h_file->f_op->fasync)
++		err = h_file->f_op->fasync(fd, h_file, flag);
++
++	di_read_unlock(dentry, AuLock_IR);
++	fi_read_unlock(file);
++
++out:
++	si_read_unlock(sb);
++	return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++/* no one supports this operation, currently */
++#if 0
++static ssize_t aufs_sendpage(struct file *file, struct page *page, int offset,
++			     size_t len, loff_t *pos , int more)
++{
++}
++#endif
++
++/* ---------------------------------------------------------------------- */
++
++const struct file_operations aufs_file_fop = {
++	.owner		= THIS_MODULE,
++
++	.llseek		= default_llseek,
++
++	.read		= aufs_read,
++	.write		= aufs_write,
++	.read_iter	= aufs_read_iter,
++	.write_iter	= aufs_write_iter,
++
++#ifdef CONFIG_AUFS_POLL
++	.poll		= aufs_poll,
++#endif
++	.unlocked_ioctl	= aufs_ioctl_nondir,
++#ifdef CONFIG_COMPAT
++	.compat_ioctl	= aufs_compat_ioctl_nondir,
++#endif
++	.mmap		= aufs_mmap,
++	.open		= aufs_open_nondir,
++	.flush		= aufs_flush_nondir,
++	.release	= aufs_release_nondir,
++	.fsync		= aufs_fsync_nondir,
++	/* .aio_fsync	= aufs_aio_fsync_nondir, */
++	.fasync		= aufs_fasync,
++	/* .sendpage	= aufs_sendpage, */
++	.splice_write	= aufs_splice_write,
++	.splice_read	= aufs_splice_read,
++#if 0
++	.aio_splice_write = aufs_aio_splice_write,
++	.aio_splice_read  = aufs_aio_splice_read,
++#endif
++	.fallocate	= aufs_fallocate
++};
+--- a/fs/aufs/fhsm.c	1970-01-01 01:00:00.000000000 +0100
++++ b/fs/aufs/fhsm.c	2014-09-08 00:38:33.514569904 +0100
+@@ -0,0 +1,426 @@
++/*
++ * Copyright (C) 2011-2014 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
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
++ */
++
++/*
++ * File-based Hierarchy Storage Management
++ */
++
++#include <linux/anon_inodes.h>
++#include <linux/poll.h>
++#include <linux/seq_file.h>
++#include <linux/statfs.h>
++#include "aufs.h"
++
++static aufs_bindex_t au_fhsm_bottom(struct super_block *sb)
++{
++	struct au_sbinfo *sbinfo;
++	struct au_fhsm *fhsm;
++
++	SiMustAnyLock(sb);
++
++	sbinfo = au_sbi(sb);
++	fhsm = &sbinfo->si_fhsm;
++	AuDebugOn(!fhsm);
++	return fhsm->fhsm_bottom;
++}
++
++void au_fhsm_set_bottom(struct super_block *sb, aufs_bindex_t bindex)
++{
++	struct au_sbinfo *sbinfo;
++	struct au_fhsm *fhsm;
++
++	SiMustWriteLock(sb);
++
++	sbinfo = au_sbi(sb);
++	fhsm = &sbinfo->si_fhsm;
++	AuDebugOn(!fhsm);
++	fhsm->fhsm_bottom = bindex;
++}
++
++/* ---------------------------------------------------------------------- */
++
++static int au_fhsm_test_jiffy(struct au_sbinfo *sbinfo, struct au_branch *br)
++{
++	struct au_br_fhsm *bf;
++
++	bf = br->br_fhsm;
++	MtxMustLock(&bf->bf_lock);
++
++	return !bf->bf_readable
++		|| time_after(jiffies,
++			      bf->bf_jiffy + sbinfo->si_fhsm.fhsm_expire);
++}
++
++/* ---------------------------------------------------------------------- */
++
++static void au_fhsm_notify(struct super_block *sb, int val)
++{
++	struct au_sbinfo *sbinfo;
++	struct au_fhsm *fhsm;
++
++	SiMustAnyLock(sb);
++
++	sbinfo = au_sbi(sb);
++	fhsm = &sbinfo->si_fhsm;
++	if (au_fhsm_pid(fhsm)
++	    && atomic_read(&fhsm->fhsm_readable) != -1) {
++		atomic_set(&fhsm->fhsm_readable, val);
++		if (val)
++			wake_up(&fhsm->fhsm_wqh);
++	}
++}
++
++static int au_fhsm_stfs(struct super_block *sb, aufs_bindex_t bindex,
++			struct aufs_stfs *rstfs, int do_lock, int do_notify)
++{
++	int err;
++	struct au_branch *br;
++	struct au_br_fhsm *bf;
++
++	br = au_sbr(sb, bindex);
++	AuDebugOn(au_br_rdonly(br));
++	bf = br->br_fhsm;
++	AuDebugOn(!bf);
++
++	if (do_lock)
++		mutex_lock(&bf->bf_lock);
++	else
++		MtxMustLock(&bf->bf_lock);
++
++	/* sb->s_root for NFS is unreliable */
++	err = au_br_stfs(br, &bf->bf_stfs);
++	if (unlikely(err)) {
++		AuErr1("FHSM failed (%d), b%d, ignored.\n", bindex, err);
++		goto out;
++	}
++
++	bf->bf_jiffy = jiffies;
++	bf->bf_readable = 1;
++	if (do_notify)
++		au_fhsm_notify(sb, /*val*/1);
++	if (rstfs)
++		*rstfs = bf->bf_stfs;
++
++out:
++	if (do_lock)
++		mutex_unlock(&bf->bf_lock);
++	au_fhsm_notify(sb, /*val*/1);
++
++	return err;
++}
++
++void au_fhsm_wrote(struct super_block *sb, aufs_bindex_t bindex, int force)
++{
++	int err;
++	struct au_sbinfo *sbinfo;
++	struct au_fhsm *fhsm;
++	struct au_branch *br;
++	struct au_br_fhsm *bf;
++
++	AuDbg("b%d, force %d\n", bindex, force);
++	SiMustAnyLock(sb);
++
++	sbinfo = au_sbi(sb);
++	fhsm = &sbinfo->si_fhsm;
++	if (!au_ftest_si(sbinfo, FHSM)
++	    || fhsm->fhsm_bottom == bindex)
++		return;
++
++	br = au_sbr(sb, bindex);
++	bf = br->br_fhsm;
++	AuDebugOn(!bf);
++	mutex_lock(&bf->bf_lock);
++	if (force
++	    || au_fhsm_pid(fhsm)
++	    || au_fhsm_test_jiffy(sbinfo, br))
++		err = au_fhsm_stfs(sb, bindex, /*rstfs*/NULL, /*do_lock*/0,
++				  /*do_notify*/1);
++	mutex_unlock(&bf->bf_lock);
++}
++
++void au_fhsm_wrote_all(struct super_block *sb, int force)
++{
++	aufs_bindex_t bindex, bend;
++	struct au_branch *br;
++
++	/* exclude the bottom */
++	bend = au_fhsm_bottom(sb);
++	for (bindex = 0; bindex < bend; bindex++) {
++		br = au_sbr(sb, bindex);
++		if (au_br_fhsm(br->br_perm))
++			au_fhsm_wrote(sb, bindex, force);
++	}
++}
++
++/* ---------------------------------------------------------------------- */
++
++static unsigned int au_fhsm_poll(struct file *file,
++				 struct poll_table_struct *wait)
++{
++	unsigned int mask;
++	struct au_sbinfo *sbinfo;
++	struct au_fhsm *fhsm;
++
++	mask = 0;
++	sbinfo = file->private_data;
++	fhsm = &sbinfo->si_fhsm;
++	poll_wait(file, &fhsm->fhsm_wqh, wait);
++	if (atomic_read(&fhsm->fhsm_readable))
++		mask = POLLIN /* | POLLRDNORM */;
++
++	AuTraceErr((int)mask);
++	return mask;
++}
++
++static int au_fhsm_do_read_one(struct aufs_stbr __user *stbr,
++			      struct aufs_stfs *stfs, __s16 brid)
++{
++	int err;
++
++	err = copy_to_user(&stbr->stfs, stfs, sizeof(*stfs));
++	if (!err)
++		err = __put_user(brid, &stbr->brid);
++	if (unlikely(err))
++		err = -EFAULT;
++
++	return err;
++}
++
++static ssize_t au_fhsm_do_read(struct super_block *sb,
++			       struct aufs_stbr __user *stbr, size_t count)
++{
++	ssize_t err;
++	int nstbr;
++	aufs_bindex_t bindex, bend;
++	struct au_branch *br;
++	struct au_br_fhsm *bf;
++
++	/* except the bottom branch */
++	err = 0;
++	nstbr = 0;
++	bend = au_fhsm_bottom(sb);
++	for (bindex = 0; !err && bindex < bend; bindex++) {
++		br = au_sbr(sb, bindex);
++		if (!au_br_fhsm(br->br_perm))
++			continue;
++
++		bf = br->br_fhsm;
++		mutex_lock(&bf->bf_lock);
++		if (bf->bf_readable) {
++			err = -EFAULT;
++			if (count >= sizeof(*stbr))
++				err = au_fhsm_do_read_one(stbr++, &bf->bf_stfs,
++							  br->br_id);
++			if (!err) {
++				bf->bf_readable = 0;
++				count -= sizeof(*stbr);
++				nstbr++;
++			}
++		}
++		mutex_unlock(&bf->bf_lock);
++	}
++	if (!err)
++		err = sizeof(*stbr) * nstbr;
++
++	return err;
++}
++
++static ssize_t au_fhsm_read(struct file *file, char __user *buf, size_t count,
++			   loff_t *pos)
 +{
-+	int err;
-+	struct au_pin pin;
-+	struct dentry *dentry;
-+	struct inode *inode;
-+	struct file *h_file;
++	ssize_t err;
++	int readable;
++	aufs_bindex_t nfhsm, bindex, bend;
++	struct au_sbinfo *sbinfo;
++	struct au_fhsm *fhsm;
++	struct au_branch *br;
 +	struct super_block *sb;
 +
-+	dentry = file->f_dentry;
-+	inode = dentry->d_inode;
-+	sb = dentry->d_sb;
-+	mutex_lock(&inode->i_mutex);
-+	err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM);
++	err = 0;
++	sbinfo = file->private_data;
++	fhsm = &sbinfo->si_fhsm;
++need_data:
++	spin_lock_irq(&fhsm->fhsm_wqh.lock);
++	if (!atomic_read(&fhsm->fhsm_readable)) {
++		if (vfsub_file_flags(file) & O_NONBLOCK)
++			err = -EAGAIN;
++		else
++			err = wait_event_interruptible_locked_irq
++				(fhsm->fhsm_wqh,
++				 atomic_read(&fhsm->fhsm_readable));
++	}
++	spin_unlock_irq(&fhsm->fhsm_wqh.lock);
 +	if (unlikely(err))
 +		goto out;
 +
-+	err = 0; /* -EBADF; */ /* posix? */
-+	if (unlikely(!(file->f_mode & FMODE_WRITE)))
-+		goto out_si;
-+	err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1);
-+	if (unlikely(err))
-+		goto out_si;
-+
-+	err = au_ready_to_write(file, -1, &pin);
-+	di_downgrade_lock(dentry, AuLock_IR);
-+	if (unlikely(err))
-+		goto out_unlock;
-+	au_unpin(&pin);
-+
-+	err = -EINVAL;
-+	h_file = au_hf_top(file);
-+	err = vfsub_fsync(h_file, &h_file->f_path, datasync);
-+	au_cpup_attr_timesizes(inode);
++	/* sb may already be dead */
++	au_rw_read_lock(&sbinfo->si_rwsem);
++	readable = atomic_read(&fhsm->fhsm_readable);
++	if (readable > 0) {
++		sb = sbinfo->si_sb;
++		AuDebugOn(!sb);
++		/* exclude the bottom branch */
++		nfhsm = 0;
++		bend = au_fhsm_bottom(sb);
++		for (bindex = 0; bindex < bend; bindex++) {
++			br = au_sbr(sb, bindex);
++			if (au_br_fhsm(br->br_perm))
++				nfhsm++;
++		}
++		err = -EMSGSIZE;
++		if (nfhsm * sizeof(struct aufs_stbr) <= count) {
++			atomic_set(&fhsm->fhsm_readable, 0);
++			err = au_fhsm_do_read(sbinfo->si_sb, (void __user *)buf,
++					     count);
++		}
++	}
++	au_rw_read_unlock(&sbinfo->si_rwsem);
++	if (!readable)
++		goto need_data;
 +
-+out_unlock:
-+	di_read_unlock(dentry, AuLock_IR);
-+	fi_write_unlock(file);
-+out_si:
-+	si_read_unlock(sb);
 +out:
-+	mutex_unlock(&inode->i_mutex);
 +	return err;
 +}
 +
-+/* no one supports this operation, currently */
-+#if 0
-+static int aufs_aio_fsync_nondir(struct kiocb *kio, int datasync)
++static int au_fhsm_release(struct inode *inode, struct file *file)
 +{
-+	int err;
-+	struct au_pin pin;
-+	struct dentry *dentry;
-+	struct inode *inode;
-+	struct file *file, *h_file;
++	struct au_sbinfo *sbinfo;
++	struct au_fhsm *fhsm;
 +
-+	file = kio->ki_filp;
-+	dentry = file->f_dentry;
-+	inode = dentry->d_inode;
-+	au_mtx_and_read_lock(inode);
++	/* sb may already be dead */
++	sbinfo = file->private_data;
++	fhsm = &sbinfo->si_fhsm;
++	spin_lock(&fhsm->fhsm_spin);
++	fhsm->fhsm_pid = 0;
++	spin_unlock(&fhsm->fhsm_spin);
++	kobject_put(&sbinfo->si_kobj);
 +
-+	err = 0; /* -EBADF; */ /* posix? */
-+	if (unlikely(!(file->f_mode & FMODE_WRITE)))
++	return 0;
++}
++
++static const struct file_operations au_fhsm_fops = {
++	.owner		= THIS_MODULE,
++	.llseek		= noop_llseek,
++	.read		= au_fhsm_read,
++	.poll		= au_fhsm_poll,
++	.release	= au_fhsm_release
++};
++
++int au_fhsm_fd(struct super_block *sb, int oflags)
++{
++	int err, fd;
++	struct au_sbinfo *sbinfo;
++	struct au_fhsm *fhsm;
++
++	err = -EPERM;
++	if (unlikely(!capable(CAP_SYS_ADMIN)))
 +		goto out;
-+	err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1);
-+	if (unlikely(err))
++
++	err = -EINVAL;
++	if (unlikely(oflags & ~(O_CLOEXEC | O_NONBLOCK)))
 +		goto out;
 +
-+	err = au_ready_to_write(file, -1, &pin);
-+	di_downgrade_lock(dentry, AuLock_IR);
++	err = 0;
++	sbinfo = au_sbi(sb);
++	fhsm = &sbinfo->si_fhsm;
++	spin_lock(&fhsm->fhsm_spin);
++	if (!fhsm->fhsm_pid)
++		fhsm->fhsm_pid = current->pid;
++	else
++		err = -EBUSY;
++	spin_unlock(&fhsm->fhsm_spin);
 +	if (unlikely(err))
-+		goto out_unlock;
-+	au_unpin(&pin);
++		goto out;
 +
-+	err = -ENOSYS;
-+	h_file = au_hf_top(file);
-+	if (h_file->f_op->aio_fsync) {
-+		struct mutex *h_mtx;
++	oflags |= O_RDONLY;
++	/* oflags |= FMODE_NONOTIFY; */
++	fd = anon_inode_getfd("[aufs_fhsm]", &au_fhsm_fops, sbinfo, oflags);
++	err = fd;
++	if (unlikely(fd < 0))
++		goto out_pid;
 +
-+		h_mtx = &file_inode(h_file)->i_mutex;
-+		if (!is_sync_kiocb(kio)) {
-+			get_file(h_file);
-+			fput(file);
-+		}
-+		kio->ki_filp = h_file;
-+		err = h_file->f_op->aio_fsync(kio, datasync);
-+		mutex_lock_nested(h_mtx, AuLsc_I_CHILD);
-+		if (!err)
-+			vfsub_update_h_iattr(&h_file->f_path, /*did*/NULL);
-+		/*ignore*/
-+		au_cpup_attr_timesizes(inode);
-+		mutex_unlock(h_mtx);
-+	}
++	/* succeed reglardless 'fhsm' status */
++	kobject_get(&sbinfo->si_kobj);
++	si_noflush_read_lock(sb);
++	if (au_ftest_si(sbinfo, FHSM))
++		au_fhsm_wrote_all(sb, /*force*/0);
++	si_read_unlock(sb);
++	goto out; /* success */
 +
-+out_unlock:
-+	di_read_unlock(dentry, AuLock_IR);
-+	fi_write_unlock(file);
++out_pid:
++	spin_lock(&fhsm->fhsm_spin);
++	fhsm->fhsm_pid = 0;
++	spin_unlock(&fhsm->fhsm_spin);
 +out:
-+	si_read_unlock(inode->sb);
-+	mutex_unlock(&inode->i_mutex);
++	AuTraceErr(err);
 +	return err;
 +}
-+#endif
 +
-+static int aufs_fasync(int fd, struct file *file, int flag)
++/* ---------------------------------------------------------------------- */
++
++int au_fhsm_br_alloc(struct au_branch *br)
 +{
 +	int err;
-+	struct file *h_file;
-+	struct dentry *dentry;
-+	struct super_block *sb;
-+
-+	dentry = file->f_dentry;
-+	sb = dentry->d_sb;
-+	si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW);
-+	err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/0);
-+	if (unlikely(err))
-+		goto out;
-+
-+	h_file = au_hf_top(file);
-+	if (h_file->f_op->fasync)
-+		err = h_file->f_op->fasync(fd, h_file, flag);
 +
-+	di_read_unlock(dentry, AuLock_IR);
-+	fi_read_unlock(file);
++	err = 0;
++	br->br_fhsm = kmalloc(sizeof(*br->br_fhsm), GFP_NOFS);
++	if (br->br_fhsm)
++		au_br_fhsm_init(br->br_fhsm);
++	else
++		err = -ENOMEM;
 +
-+out:
-+	si_read_unlock(sb);
 +	return err;
 +}
 +
 +/* ---------------------------------------------------------------------- */
 +
-+/* no one supports this operation, currently */
-+#if 0
-+static ssize_t aufs_sendpage(struct file *file, struct page *page, int offset,
-+			     size_t len, loff_t *pos , int more)
++void au_fhsm_fin(struct super_block *sb)
 +{
++	au_fhsm_notify(sb, /*val*/-1);
 +}
-+#endif
 +
-+/* ---------------------------------------------------------------------- */
++void au_fhsm_init(struct au_sbinfo *sbinfo)
++{
++	struct au_fhsm *fhsm;
 +
-+const struct file_operations aufs_file_fop = {
-+	.owner		= THIS_MODULE,
++	fhsm = &sbinfo->si_fhsm;
++	spin_lock_init(&fhsm->fhsm_spin);
++	init_waitqueue_head(&fhsm->fhsm_wqh);
++	atomic_set(&fhsm->fhsm_readable, 0);
++	fhsm->fhsm_expire
++		= msecs_to_jiffies(AUFS_FHSM_CACHE_DEF_SEC * MSEC_PER_SEC);
++	fhsm->fhsm_bottom = -1;
++}
 +
-+	.llseek		= default_llseek,
++void au_fhsm_set(struct au_sbinfo *sbinfo, unsigned int sec)
++{
++	sbinfo->si_fhsm.fhsm_expire
++		= msecs_to_jiffies(sec * MSEC_PER_SEC);
++}
 +
-+	.read		= aufs_read,
-+	.write		= aufs_write,
-+	.read_iter	= aufs_read_iter,
-+	.write_iter	= aufs_write_iter,
++void au_fhsm_show(struct seq_file *seq, struct au_sbinfo *sbinfo)
++{
++	unsigned int u;
 +
-+#ifdef CONFIG_AUFS_POLL
-+	.poll		= aufs_poll,
-+#endif
-+	.unlocked_ioctl	= aufs_ioctl_nondir,
-+#ifdef CONFIG_COMPAT
-+	.compat_ioctl	= aufs_compat_ioctl_nondir,
-+#endif
-+	.mmap		= aufs_mmap,
-+	.open		= aufs_open_nondir,
-+	.flush		= aufs_flush_nondir,
-+	.release	= aufs_release_nondir,
-+	.fsync		= aufs_fsync_nondir,
-+	/* .aio_fsync	= aufs_aio_fsync_nondir, */
-+	.fasync		= aufs_fasync,
-+	/* .sendpage	= aufs_sendpage, */
-+	.splice_write	= aufs_splice_write,
-+	.splice_read	= aufs_splice_read,
-+#if 0
-+	.aio_splice_write = aufs_aio_splice_write,
-+	.aio_splice_read  = aufs_aio_splice_read,
-+#endif
-+	.fallocate	= aufs_fallocate
-+};
++	if (!au_ftest_si(sbinfo, FHSM))
++		return;
++
++	u = jiffies_to_msecs(sbinfo->si_fhsm.fhsm_expire) / MSEC_PER_SEC;
++	if (u != AUFS_FHSM_CACHE_DEF_SEC)
++		seq_printf(seq, ",fhsm_sec=%u", u);
++}
 --- a/fs/aufs/file.c	1970-01-01 01:00:00.000000000 +0100
-+++ b/fs/aufs/file.c	2014-07-15 14:04:48.728871625 +0100
-@@ -0,0 +1,808 @@
++++ b/fs/aufs/file.c	2014-09-08 00:38:33.514569904 +0100
+@@ -0,0 +1,833 @@
 +/*
 + * Copyright (C) 2005-2014 Junjiro R. Okajima
 + *
@@ -11172,6 +11978,7 @@
 +static int au_cmoo(struct dentry *dentry)
 +{
 +	int err, cmoo;
++	unsigned int udba;
 +	struct path h_path;
 +	struct au_pin pin;
 +	struct au_cp_generic cpg = {
@@ -11184,6 +11991,9 @@
 +	};
 +	struct inode *inode, *delegated;
 +	struct super_block *sb;
++	struct au_sbinfo *sbinfo;
++	struct au_fhsm *fhsm;
++	pid_t pid;
 +	struct au_branch *br;
 +	struct dentry *parent;
 +	struct au_hinode *hdir;
@@ -11200,6 +12010,14 @@
 +		goto out;
 +
 +	sb = dentry->d_sb;
++	sbinfo = au_sbi(sb);
++	fhsm = &sbinfo->si_fhsm;
++	pid = au_fhsm_pid(fhsm);
++	if (pid
++	    && (current->pid == pid
++		|| current->real_parent->pid == pid))
++		goto out;
++
 +	br = au_sbr(sb, cpg.bsrc);
 +	cmoo = au_br_cmoo(br->br_perm);
 +	if (!cmoo)
@@ -11225,34 +12043,42 @@
 +		goto out_dgrade;
 +
 +	di_downgrade_lock(parent, AuLock_IR);
-+	err = au_pin(&pin, dentry, cpg.bdst, au_opt_udba(sb),
++	udba = au_opt_udba(sb);
++	err = au_pin(&pin, dentry, cpg.bdst, udba,
 +		     AuPin_DI_LOCKED | AuPin_MNT_WRITE);
-+	if (!err) {
-+		err = au_sio_cpup_simple(&cpg);
-+		au_unpin(&pin);
++	if (unlikely(err))
++		goto out_parent;
++
++	err = au_sio_cpup_simple(&cpg);
++	au_unpin(&pin);
++	if (unlikely(err))
++		goto out_parent;
++	if (!(cmoo & AuBrWAttr_MOO))
++		goto out_parent; /* success */
++
++	err = au_pin(&pin, dentry, cpg.bsrc, udba,
++		     AuPin_DI_LOCKED | AuPin_MNT_WRITE);
++	if (unlikely(err))
++		goto out_parent;
++
++	h_path.mnt = au_br_mnt(br);
++	h_path.dentry = au_h_dptr(dentry, cpg.bsrc);
++	hdir = au_hi(parent->d_inode, cpg.bsrc);
++	delegated = NULL;
++	err = vfsub_unlink(hdir->hi_inode, &h_path, &delegated, /*force*/1);
++	au_unpin(&pin);
++	/* todo: keep h_dentry or not? */
++	if (unlikely(err == -EWOULDBLOCK)) {
++		pr_warn("cannot retry for NFSv4 delegation"
++			" for an internal unlink\n");
++		iput(delegated);
 +	}
-+	if (!err && (cmoo & AuBrWAttr_MOO)) {
-+		/* todo: keep h_dentry? */
-+		h_path.mnt = au_br_mnt(br);
-+		h_path.dentry = au_h_dptr(dentry, cpg.bsrc);
-+		hdir = au_hi(parent->d_inode, cpg.bsrc);
-+		delegated = NULL;
-+		au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT2);
-+		err = vfsub_unlink(hdir->hi_inode, &h_path, &delegated,
-+				   /*force*/1);
-+		au_hn_imtx_unlock(hdir);
-+		if (unlikely(err == -EWOULDBLOCK)) {
-+			pr_warn("cannot retry for NFSv4 delegation"
-+				" for an internal unlink\n");
-+			iput(delegated);
-+		}
-+		if (unlikely(err)) {
-+			pr_err("unlink %pd after coo failed (%d), ignored\n",
-+			       dentry, err);
-+			err = 0;
-+		}
++	if (unlikely(err)) {
++		pr_err("unlink %pd after coo failed (%d), ignored\n",
++		       dentry, err);
++		err = 0;
 +	}
-+	goto out_parent;
++	goto out_parent; /* success */
 +
 +out_dgrade:
 +	di_downgrade_lock(parent, AuLock_IR);
@@ -11269,6 +12095,7 @@
 +{
 +	int err;
 +	struct dentry *dentry;
++	struct au_finfo *finfo;
 +
 +	err = au_finfo_init(file, fidir);
 +	if (unlikely(err))
@@ -11282,9 +12109,15 @@
 +		err = open(file, vfsub_file_flags(file));
 +	di_read_unlock(dentry, AuLock_IR);
 +
++	finfo = au_fi(file);
++	if (!err) {
++		finfo->fi_file = file;
++		au_sphl_add(&finfo->fi_hlist,
++			    &au_sbi(file->f_dentry->d_sb)->si_files);
++	}
 +	fi_write_unlock(file);
 +	if (unlikely(err)) {
-+		au_fi(file)->fi_hdir = NULL;
++		finfo->fi_hdir = NULL;
 +		au_finfo_fin(file);
 +	}
 +
@@ -11655,8 +12488,7 @@
 +			if (p->hf_file) {
 +				if (file_inode(p->hf_file))
 +					break;
-+				else
-+					au_hfput(p, file);
++				au_hfput(p, file);
 +			}
 +	} else {
 +		bend = au_br_index(sb, brid);
@@ -11673,8 +12505,7 @@
 +		if (p->hf_file) {
 +			if (file_inode(p->hf_file))
 +				break;
-+			else
-+				au_hfput(p, file);
++			au_hfput(p, file);
 +		}
 +	AuDebugOn(fidir->fd_bbot < finfo->fi_btop);
 +}
@@ -12790,8 +13621,8 @@
 +#endif /* __KERNEL__ */
 +#endif /* __AUFS_FSTYPE_H__ */
 --- a/fs/aufs/hfsnotify.c	1970-01-01 01:00:00.000000000 +0100
-+++ b/fs/aufs/hfsnotify.c	2014-07-15 14:04:48.728871625 +0100
-@@ -0,0 +1,281 @@
++++ b/fs/aufs/hfsnotify.c	2014-09-08 00:38:33.514569904 +0100
+@@ -0,0 +1,288 @@
 +/*
 + * Copyright (C) 2005-2014 Junjiro R. Okajima
 + *
@@ -12854,10 +13685,12 @@
 +	 * by udba rename or rmdir, aufs assign a new inode to the known
 +	 * h_inode, so specify 1 to allow dups.
 +	 */
++	lockdep_off();
 +	err = fsnotify_add_mark(mark, br->br_hfsn->hfsn_group, hinode->hi_inode,
 +				 /*mnt*/NULL, /*allow_dups*/1);
 +	/* even if err */
 +	fsnotify_put_mark(mark);
++	lockdep_on();
 +
 +	return err;
 +}
@@ -12876,8 +13709,10 @@
 +	group = mark->group;
 +	fsnotify_get_group(group);
 +	spin_unlock(&mark->lock);
++	lockdep_off();
 +	fsnotify_destroy_mark(mark, group);
 +	fsnotify_put_group(group);
++	lockdep_on();
 +
 +	/* free hn by myself */
 +	return 0;
@@ -13004,8 +13839,11 @@
 +	struct au_br_hfsnotify *hfsn;
 +
 +	hfsn = br->br_hfsn;
-+	if (hfsn)
++	if (hfsn) {
++		lockdep_off();
 +		fsnotify_put_group(hfsn->hfsn_group);
++		lockdep_on();
++	}
 +}
 +
 +static int au_hfsn_init_br(struct au_branch *br, int perm)
@@ -13133,7 +13971,7 @@
 +	}
 +}
 --- a/fs/aufs/hnotify.c	1970-01-01 01:00:00.000000000 +0100
-+++ b/fs/aufs/hnotify.c	2014-07-15 14:04:48.732871625 +0100
++++ b/fs/aufs/hnotify.c	2014-09-08 00:38:33.514569904 +0100
 @@ -0,0 +1,714 @@
 +/*
 + * Copyright (C) 2005-2014 Junjiro R. Okajima
@@ -13364,7 +14202,7 @@
 +		spin_unlock(&inode->i_lock);
 +	} else {
 +		au_fset_si(au_sbi(inode->i_sb), FAILED_REFRESH_DIR);
-+		d = d_find_alias(inode);
++		d = d_find_any_alias(inode);
 +		if (!d) {
 +			au_iigen_dec(inode);
 +			goto out;
@@ -13509,7 +14347,7 @@
 +	struct dentry *dentry, *d, *parent;
 +	struct qstr *dname;
 +
-+	parent = d_find_alias(dir);
++	parent = d_find_any_alias(dir);
 +	if (!parent)
 +		return NULL;
 +
@@ -13850,8 +14688,8 @@
 +		au_hn_destroy_cache();
 +}
 --- a/fs/aufs/i_op.c	1970-01-01 01:00:00.000000000 +0100
-+++ b/fs/aufs/i_op.c	2014-07-15 14:04:48.732871625 +0100
-@@ -0,0 +1,1140 @@
++++ b/fs/aufs/i_op.c	2014-09-08 00:39:05.266571221 +0100
+@@ -0,0 +1,1142 @@
 +/*
 + * Copyright (C) 2005-2014 Junjiro R. Okajima
 + *
@@ -14057,11 +14895,11 @@
 +
 +	if (npositive) {
 +		inode = au_new_inode(dentry, /*must_new*/0);
-+		ret = (void *)inode;
-+	}
-+	if (IS_ERR(inode)) {
-+		inode = NULL;
-+		goto out_unlock;
++		if (IS_ERR(inode)) {
++			ret = (void *)inode;
++			inode = NULL;
++			goto out_unlock;
++		}
 +	}
 +
 +	ret = d_splice_alias(inode, dentry);
@@ -14236,7 +15074,7 @@
 +		au_hn_imtx_unlock(p->hdir);
 +}
 +
-+static int au_pin_hdir_lock(struct au_pin *p)
++int au_pin_hdir_lock(struct au_pin *p)
 +{
 +	int err;
 +
@@ -14402,6 +15240,8 @@
 +	if (!err)
 +		goto out; /* success */
 +
++	au_unpin(p);
++
 +out_err:
 +	pr_err("err %d\n", err);
 +	err = au_busy_or_stale();
@@ -14993,8 +15833,8 @@
 +	.update_time	= aufs_update_time
 +};
 --- a/fs/aufs/i_op_add.c	1970-01-01 01:00:00.000000000 +0100
-+++ b/fs/aufs/i_op_add.c	2014-07-15 14:04:48.732871625 +0100
-@@ -0,0 +1,880 @@
++++ b/fs/aufs/i_op_add.c	2014-09-08 00:38:33.514569904 +0100
+@@ -0,0 +1,891 @@
 +/*
 + * Copyright (C) 2005-2014 Junjiro R. Okajima
 + *
@@ -15030,17 +15870,19 @@
 +	int err, rerr;
 +	aufs_bindex_t bwh;
 +	struct path h_path;
++	struct super_block *sb;
 +	struct inode *inode, *h_dir;
 +	struct dentry *wh;
 +
 +	bwh = -1;
++	sb = dir->i_sb;
 +	if (wh_dentry) {
 +		h_dir = wh_dentry->d_parent->d_inode; /* dir inode is locked */
 +		IMustLock(h_dir);
 +		AuDebugOn(au_h_iptr(dir, bindex) != h_dir);
 +		bwh = au_dbwh(dentry);
 +		h_path.dentry = wh_dentry;
-+		h_path.mnt = au_sbr_mnt(dir->i_sb, bindex);
++		h_path.mnt = au_sbr_mnt(sb, bindex);
 +		err = au_wh_unlink_dentry(au_h_iptr(dir, bindex), &h_path,
 +					  dentry);
 +		if (unlikely(err))
@@ -15055,6 +15897,7 @@
 +		if (au_ibstart(dir) == au_dbstart(dentry))
 +			au_cpup_attr_timesizes(dir);
 +		dir->i_version++;
++		au_fhsm_wrote(sb, bindex, /*force*/0);
 +		return 0; /* success */
 +	}
 +
@@ -15536,7 +16379,10 @@
 +			      dget(au_h_dptr(src_dentry, a->bsrc)));
 +		dget(a->h_path.dentry);
 +		au_set_h_dptr(dentry, a->bdst, NULL);
++		AuDbg("temporary d_inode...\n");
++		spin_lock(&dentry->d_lock);
 +		dentry->d_inode = src_dentry->d_inode; /* tmp */
++		spin_unlock(&dentry->d_lock);
 +		h_file = au_h_open_pre(dentry, a->bsrc, /*force_wr*/0);
 +		if (IS_ERR(h_file))
 +			err = PTR_ERR(h_file);
@@ -15558,7 +16404,10 @@
 +				au_set_h_dptr(dentry, a->bdst,
 +					      a->h_path.dentry);
 +		}
++		spin_lock(&dentry->d_lock);
 +		dentry->d_inode = NULL; /* restore */
++		spin_unlock(&dentry->d_lock);
++		AuDbg("temporary d_inode...done\n");
 +		au_set_h_dptr(dentry, a->bsrc, NULL);
 +		au_set_dbend(dentry, bend);
 +	} else {
@@ -15737,6 +16586,8 @@
 +	if (d_unhashed(a->h_path.dentry))
 +		/* some filesystem calls d_drop() */
 +		d_drop(dentry);
++	/* some filesystems consume an inode even hardlink */
++	au_fhsm_wrote(sb, a->bdst, /*force*/0);
 +	goto out_unpin; /* success */
 +
 +out_revert:
@@ -16386,8 +17237,8 @@
 +	return err;
 +}
 --- a/fs/aufs/i_op_ren.c	1970-01-01 01:00:00.000000000 +0100
-+++ b/fs/aufs/i_op_ren.c	2014-01-20 03:24:33.512760970 +0000
-@@ -0,0 +1,1032 @@
++++ b/fs/aufs/i_op_ren.c	2014-09-08 00:38:33.514569904 +0100
+@@ -0,0 +1,1034 @@
 +/*
 + * Copyright (C) 2005-2014 Junjiro R. Okajima
 + *
@@ -16672,7 +17523,8 @@
 +	au_hn_imtx_unlock(a->src_hinode);
 +	if (IS_ERR(diropq))
 +		err = PTR_ERR(diropq);
-+	dput(diropq);
++	else
++		dput(diropq);
 +
 +	return err;
 +}
@@ -16772,6 +17624,7 @@
 +	if (a->thargs)
 +		au_ren_del_whtmp(a); /* ignore this error */
 +
++	au_fhsm_wrote(a->src_dentry->d_sb, a->btgt, /*force*/0);
 +	err = 0;
 +	goto out_success;
 +
@@ -18196,8 +19049,8 @@
 +	return au_test_h_perm(h_inode, mask);
 +}
 --- a/fs/aufs/inode.h	1970-01-01 01:00:00.000000000 +0100
-+++ b/fs/aufs/inode.h	2014-07-15 14:04:48.732871625 +0100
-@@ -0,0 +1,601 @@
++++ b/fs/aufs/inode.h	2014-09-08 00:38:33.514569904 +0100
+@@ -0,0 +1,602 @@
 +/*
 + * Copyright (C) 2005-2014 Junjiro R. Okajima
 + *
@@ -18308,6 +19161,7 @@
 +};
 +
 +void au_pin_hdir_unlock(struct au_pin *p);
++int au_pin_hdir_lock(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);
@@ -18800,8 +19654,8 @@
 +#endif /* __KERNEL__ */
 +#endif /* __AUFS_INODE_H__ */
 --- a/fs/aufs/ioctl.c	1970-01-01 01:00:00.000000000 +0100
-+++ b/fs/aufs/ioctl.c	2014-01-20 03:24:33.516760970 +0000
-@@ -0,0 +1,201 @@
++++ b/fs/aufs/ioctl.c	2014-09-08 00:38:33.514569904 +0100
+@@ -0,0 +1,219 @@
 +/*
 + * Copyright (C) 2005-2014 Junjiro R. Okajima
 + *
@@ -18824,6 +19678,7 @@
 + * plink-management and readdir in userspace.
 + * assist the pathconf(3) wrapper library.
 + * move-down
++ * File-based Hierarchical Storage Management.
 + */
 +
 +#include <linux/compat.h>
@@ -18925,6 +19780,7 @@
 +long aufs_ioctl_dir(struct file *file, unsigned int cmd, unsigned long arg)
 +{
 +	long err;
++	struct dentry *dentry;
 +
 +	switch (cmd) {
 +	case AUFS_CTL_RDU:
@@ -18940,6 +19796,18 @@
 +		err = au_ibusy_ioctl(file, arg);
 +		break;
 +
++	case AUFS_CTL_BRINFO:
++		err = au_brinfo_ioctl(file, arg);
++		break;
++
++	case AUFS_CTL_FHSM_FD:
++		dentry = file->f_dentry;
++		if (IS_ROOT(dentry))
++			err = au_fhsm_fd(dentry->d_sb, arg);
++		else
++			err = -ENOTTY;
++		break;
++
 +	default:
 +		/* do not call the lower */
 +		AuDbg("0x%x\n", cmd);
@@ -18989,6 +19857,10 @@
 +		err = au_ibusy_compat_ioctl(file, arg);
 +		break;
 +
++	case AUFS_CTL_BRINFO:
++		err = au_brinfo_compat_ioctl(file, arg);
++		break;
++
 +	default:
 +		err = aufs_ioctl_dir(file, cmd, arg);
 +	}
@@ -19584,8 +20456,8 @@
 +#endif /* __KERNEL__ */
 +#endif /* __AUFS_MODULE_H__ */
 --- a/fs/aufs/mvdown.c	1970-01-01 01:00:00.000000000 +0100
-+++ b/fs/aufs/mvdown.c	2014-07-15 14:04:48.732871625 +0100
-@@ -0,0 +1,653 @@
++++ b/fs/aufs/mvdown.c	2014-09-08 00:38:33.514569904 +0100
+@@ -0,0 +1,694 @@
 +/*
 + * Copyright (C) 2011-2014 Junjiro R. Okajima
 + *
@@ -19615,6 +20487,7 @@
 +		struct dentry *h_parent;
 +		struct au_hinode *hdir;
 +		struct inode *h_dir, *h_inode;
++		struct au_pin pin;
 +	} info[AUFS_MVDOWN_NARRAY];
 +
 +	struct aufs_mvdown mvdown;
@@ -19623,7 +20496,6 @@
 +	struct super_block *sb;
 +	aufs_bindex_t bopq, bwh, bfound;
 +	unsigned char rename_lock;
-+	struct au_pin pin;
 +};
 +
 +#define mvd_errno		mvdown.au_errno
@@ -19637,12 +20509,14 @@
 +#define mvd_hdir_src		info[AUFS_MVDOWN_UPPER].hdir
 +#define mvd_h_src_dir		info[AUFS_MVDOWN_UPPER].h_dir
 +#define mvd_h_src_inode		info[AUFS_MVDOWN_UPPER].h_inode
++#define mvd_pin_src		info[AUFS_MVDOWN_UPPER].pin
 +
 +#define mvd_h_dst_sb		info[AUFS_MVDOWN_LOWER].h_sb
 +#define mvd_h_dst_parent	info[AUFS_MVDOWN_LOWER].h_parent
 +#define mvd_hdir_dst		info[AUFS_MVDOWN_LOWER].hdir
 +#define mvd_h_dst_dir		info[AUFS_MVDOWN_LOWER].h_dir
 +#define mvd_h_dst_inode		info[AUFS_MVDOWN_LOWER].h_inode
++#define mvd_pin_dst		info[AUFS_MVDOWN_LOWER].pin
 +
 +#define AU_MVD_PR(flag, ...) do {			\
 +		if (flag)				\
@@ -19658,13 +20532,20 @@
 +	sb = a->sb;
 +	bindex = a->mvd_bsrc;
 +	bend = au_sbend(sb);
-+	if (!(a->mvdown.flags & AUFS_MVDOWN_ROLOWER)) {
++	if (a->mvdown.flags & AUFS_MVDOWN_FHSM_LOWER)
++		for (bindex++; bindex <= bend; bindex++) {
++			br = au_sbr(sb, bindex);
++			if (au_br_fhsm(br->br_perm)
++			    && (!(au_br_sb(br)->s_flags & MS_RDONLY)))
++				return bindex;
++		}
++	else if (!(a->mvdown.flags & AUFS_MVDOWN_ROLOWER))
 +		for (bindex++; bindex <= bend; bindex++) {
 +			br = au_sbr(sb, bindex);
 +			if (!au_br_rdonly(br))
 +				return bindex;
 +		}
-+	} else {
++	else
 +		for (bindex++; bindex <= bend; bindex++) {
 +			br = au_sbr(sb, bindex);
 +			if (!(au_br_sb(br)->s_flags & MS_RDONLY)) {
@@ -19674,7 +20555,6 @@
 +				return bindex;
 +			}
 +		}
-+	}
 +
 +	return -1;
 +}
@@ -19713,22 +20593,44 @@
 +
 +	a->mvd_h_src_sb = au_sbr_sb(a->sb, a->mvd_bsrc);
 +	a->mvd_h_dst_sb = au_sbr_sb(a->sb, a->mvd_bdst);
++	err = au_pin(&a->mvd_pin_dst, a->dentry, a->mvd_bdst,
++		     au_opt_udba(a->sb),
++		     AuPin_MNT_WRITE | AuPin_DI_LOCKED);
++	AuTraceErr(err);
++	if (unlikely(err)) {
++		AU_MVD_PR(dmsg, "pin_dst failed\n");
++		goto out;
++	}
++
 +	if (a->mvd_h_src_sb != a->mvd_h_dst_sb) {
 +		a->rename_lock = 0;
-+		err = au_pin(&a->pin, a->dentry, a->mvd_bdst,
-+			     au_opt_udba(a->sb),
-+			     AuPin_MNT_WRITE | AuPin_DI_LOCKED);
-+		if (!err) {
-+			a->mvd_h_src_dir = a->mvd_h_src_parent->d_inode;
-+			mutex_lock_nested(&a->mvd_h_src_dir->i_mutex,
-+					  AuLsc_I_PARENT3);
-+		} else
-+			AU_MVD_PR(dmsg, "pin failed\n");
-+		goto out;
++		au_pin_init(&a->mvd_pin_src, a->dentry, a->mvd_bsrc,
++			    AuLsc_DI_PARENT, AuLsc_I_PARENT3,
++			    au_opt_udba(a->sb),
++			    AuPin_MNT_WRITE | AuPin_DI_LOCKED);
++		err = au_do_pin(&a->mvd_pin_src);
++		AuTraceErr(err);
++		a->mvd_h_src_dir = a->mvd_h_src_parent->d_inode;
++		if (unlikely(err)) {
++			AU_MVD_PR(dmsg, "pin_src failed\n");
++			goto out_dst;
++		}
++		goto out; /* success */
 +	}
 +
-+	err = 0;
 +	a->rename_lock = 1;
++	au_pin_hdir_unlock(&a->mvd_pin_dst);
++	err = au_pin(&a->mvd_pin_src, a->dentry, a->mvd_bsrc,
++		     au_opt_udba(a->sb),
++		     AuPin_MNT_WRITE | AuPin_DI_LOCKED);
++	AuTraceErr(err);
++	a->mvd_h_src_dir = a->mvd_h_src_parent->d_inode;
++	if (unlikely(err)) {
++		AU_MVD_PR(dmsg, "pin_src failed\n");
++		au_pin_hdir_lock(&a->mvd_pin_dst);
++		goto out_dst;
++	}
++	au_pin_hdir_unlock(&a->mvd_pin_src);
 +	h_trap = vfsub_lock_rename(a->mvd_h_src_parent, a->mvd_hdir_src,
 +				   a->mvd_h_dst_parent, a->mvd_hdir_dst);
 +	if (h_trap) {
@@ -19737,7 +20639,20 @@
 +			err = (h_trap != a->mvd_h_dst_parent);
 +	}
 +	BUG_ON(err); /* it should never happen */
++	if (unlikely(a->mvd_h_src_dir != au_pinned_h_dir(&a->mvd_pin_src))) {
++		err = -EBUSY;
++		AuTraceErr(err);
++		vfsub_unlock_rename(a->mvd_h_src_parent, a->mvd_hdir_src,
++				    a->mvd_h_dst_parent, a->mvd_hdir_dst);
++		au_pin_hdir_lock(&a->mvd_pin_src);
++		au_unpin(&a->mvd_pin_src);
++		au_pin_hdir_lock(&a->mvd_pin_dst);
++		goto out_dst;
++	}
++	goto out; /* success */
 +
++out_dst:
++	au_unpin(&a->mvd_pin_dst);
 +out:
 +	AuTraceErr(err);
 +	return err;
@@ -19745,12 +20660,16 @@
 +
 +static void au_do_unlock(const unsigned char dmsg, struct au_mvd_args *a)
 +{
-+	if (!a->rename_lock) {
-+		mutex_unlock(&a->mvd_h_src_dir->i_mutex);
-+		au_unpin(&a->pin);
-+	} else
++	if (!a->rename_lock)
++		au_unpin(&a->mvd_pin_src);
++	else {
 +		vfsub_unlock_rename(a->mvd_h_src_parent, a->mvd_hdir_src,
 +				    a->mvd_h_dst_parent, a->mvd_hdir_dst);
++		au_pin_hdir_lock(&a->mvd_pin_src);
++		au_unpin(&a->mvd_pin_src);
++		au_pin_hdir_lock(&a->mvd_pin_dst);
++	}
++	au_unpin(&a->mvd_pin_dst);
 +}
 +
 +/* copy-down the file */
@@ -19762,7 +20681,7 @@
 +		.bdst	= a->mvd_bdst,
 +		.bsrc	= a->mvd_bsrc,
 +		.len	= -1,
-+		.pin	= &a->pin,
++		.pin	= &a->mvd_pin_dst,
 +		.flags	= AuCpup_DTIME | AuCpup_HOPEN
 +	};
 +
@@ -19821,8 +20740,6 @@
 +
 +/*
 + * unlink the topmost h_dentry
-+ * Note: the target file MAY be modified by UDBA between this mutex_unlock() and
-+ *	mutex_lock() in vfs_unlink(). in this case, such changes may be lost.
 + */
 +static int au_do_unlink(const unsigned char dmsg, struct au_mvd_args *a)
 +{
@@ -19895,6 +20812,8 @@
 +	if (unlikely(err))
 +		goto out_unlock;
 +
++	AuDbg("%pd2, 0x%x, %d --> %d\n",
++	      a->dentry, a->mvdown.flags, a->mvd_bsrc, a->mvd_bdst);
 +	if (find_lower_writable(a) < 0)
 +		a->mvdown.flags |= AUFS_MVDOWN_BOTTOM;
 +
@@ -20167,8 +21086,6 @@
 +	if (unlikely(!capable(CAP_SYS_ADMIN)))
 +		goto out;
 +
-+	WARN_ONCE(1, "move-down is still testing...\n");
-+
 +	err = -ENOMEM;
 +	args = kmalloc(sizeof(*args), GFP_NOFS);
 +	if (unlikely(!args))
@@ -20210,13 +21127,9 @@
 +	if (unlikely(err))
 +		goto out_parent;
 +
-+	AuDbgDentry(dentry);
-+	AuDbgInode(args->inode);
 +	err = au_do_mvdown(dmsg, args);
 +	if (unlikely(err))
 +		goto out_parent;
-+	AuDbgDentry(dentry);
-+	AuDbgInode(args->inode);
 +
 +	au_cpup_attr_timesizes(args->dir);
 +	au_cpup_attr_timesizes(args->inode);
@@ -20240,8 +21153,8 @@
 +	return err;
 +}
 --- a/fs/aufs/opts.c	1970-01-01 01:00:00.000000000 +0100
-+++ b/fs/aufs/opts.c	2014-07-15 14:04:48.732871625 +0100
-@@ -0,0 +1,1750 @@
++++ b/fs/aufs/opts.c	2014-09-08 00:38:33.518569904 +0100
+@@ -0,0 +1,1799 @@
 +/*
 + * Copyright (C) 2005-2014 Junjiro R. Okajima
 + *
@@ -20289,6 +21202,7 @@
 +	Opt_diropq_a, Opt_diropq_w,
 +	Opt_warn_perm, Opt_nowarn_perm,
 +	Opt_wbr_copyup, Opt_wbr_create,
++	Opt_fhsm_sec,
 +	Opt_refrof, Opt_norefrof,
 +	Opt_verbose, Opt_noverbose,
 +	Opt_sum, Opt_nosum, Opt_wsum,
@@ -20346,6 +21260,12 @@
 +	{Opt_dio, "dio"},
 +	{Opt_nodio, "nodio"},
 +
++#ifdef CONFIG_AUFS_FHSM
++	{Opt_fhsm_sec, "fhsm_sec=%d"},
++#else
++	{Opt_ignore_silent, "fhsm_sec=%d"},
++#endif
++
 +	{Opt_diropq_a, "diropq=always"},
 +	{Opt_diropq_a, "diropq=a"},
 +	{Opt_diropq_w, "diropq=whiteouted"},
@@ -20456,6 +21376,7 @@
 +	{AuBrAttr_COO_REG, AUFS_BRATTR_COO_REG},
 +	{AuBrAttr_COO_ALL, AUFS_BRATTR_COO_ALL},
 +	{AuBrAttr_UNPIN, AUFS_BRATTR_UNPIN},
++	{AuBrAttr_FHSM, AUFS_BRATTR_FHSM},
 +
 +	/* ro/rr branch */
 +	{AuBrRAttr_WH, AUFS_BRRATTR_WH},
@@ -20947,6 +21868,9 @@
 +			AuDbg("copyup %d, %s\n", opt->wbr_copyup,
 +				  au_optstr_wbr_copyup(opt->wbr_copyup));
 +			break;
++		case Opt_fhsm_sec:
++			AuDbg("fhsm_sec %u\n", opt->fhsm_second);
++			break;
 +		default:
 +			BUG();
 +		}
@@ -21425,6 +22349,20 @@
 +				pr_err("wrong value, %s\n", opt_str);
 +			break;
 +
++		case Opt_fhsm_sec:
++			if (unlikely(match_int(&a->args[0], &n)
++				     || n < 0)) {
++				pr_err("bad integer in %s\n", opt_str);
++				break;
++			}
++			if (sysaufs_brs) {
++				opt->fhsm_second = n;
++				opt->type = token;
++			} else
++				pr_warn("ignored %s\n", opt_str);
++			err = 0;
++			break;
++
 +		case Opt_ignore:
 +			pr_warn("ignored %s\n", opt_str);
 +			/*FALLTHROUGH*/
@@ -21541,6 +22479,10 @@
 +		au_fset_opts(opts->flags, REFRESH_DYAOP);
 +		break;
 +
++	case Opt_fhsm_sec:
++		au_fhsm_set(sbinfo, opt->fhsm_second);
++		break;
++
 +	case Opt_diropq_a:
 +		au_opt_set(sbinfo->si_mntflags, ALWAYS_DIROPQ);
 +		break;
@@ -21754,7 +22696,7 @@
 +int au_opts_verify(struct super_block *sb, unsigned long sb_flags,
 +		   unsigned int pending)
 +{
-+	int err;
++	int err, fhsm;
 +	aufs_bindex_t bindex, bend;
 +	unsigned char do_plink, skip, do_free;
 +	struct au_branch *br;
@@ -21785,6 +22727,7 @@
 +			" by the permission bits on the lower branch\n");
 +
 +	err = 0;
++	fhsm = 0;
 +	root = sb->s_root;
 +	dir = root->d_inode;
 +	do_plink = !!au_opt_test(sbinfo->si_mntflags, PLINK);
@@ -21827,6 +22770,11 @@
 +		if (wbr)
 +			wbr_wh_read_unlock(wbr);
 +
++		if (au_br_fhsm(br->br_perm)) {
++			fhsm++;
++			AuDebugOn(!br->br_fhsm);
++		}
++
 +		if (skip)
 +			continue;
 +
@@ -21845,6 +22793,20 @@
 +		}
 +	}
 +
++	if (fhsm >= 2) {
++		au_fset_si(sbinfo, FHSM);
++		for (bindex = bend; bindex >= 0; bindex--) {
++			br = au_sbr(sb, bindex);
++			if (au_br_fhsm(br->br_perm)) {
++				au_fhsm_set_bottom(sb, bindex);
++				break;
++			}
++		}
++	} else {
++		au_fclr_si(sbinfo, FHSM);
++		au_fhsm_set_bottom(sb, -1);
++	}
++
 +	return err;
 +}
 +
@@ -21993,8 +22955,8 @@
 +	return au_mntflags(sb) & AuOptMask_UDBA;
 +}
 --- a/fs/aufs/opts.h	1970-01-01 01:00:00.000000000 +0100
-+++ b/fs/aufs/opts.h	2014-07-15 14:04:48.732871625 +0100
-@@ -0,0 +1,212 @@
++++ b/fs/aufs/opts.h	2014-09-08 00:38:33.518569904 +0100
+@@ -0,0 +1,213 @@
 +/*
 + * Copyright (C) 2005-2014 Junjiro R. Okajima
 + *
@@ -22164,6 +23126,7 @@
 +		int			udba;
 +		struct au_opt_wbr_create wbr_create;
 +		int			wbr_copyup;
++		unsigned int		fhsm_second;
 +	};
 +};
 +
@@ -23558,8 +24521,8 @@
 +#endif /* __KERNEL__ */
 +#endif /* __AUFS_RWSEM_H__ */
 --- a/fs/aufs/sbinfo.c	1970-01-01 01:00:00.000000000 +0100
-+++ b/fs/aufs/sbinfo.c	2014-01-20 03:24:33.516760970 +0000
-@@ -0,0 +1,351 @@
++++ b/fs/aufs/sbinfo.c	2014-09-08 00:38:33.518569904 +0100
+@@ -0,0 +1,353 @@
 +/*
 + * Copyright (C) 2005-2014 Junjiro R. Okajima
 + *
@@ -23659,6 +24622,8 @@
 +	sbinfo->si_wbr_copyup_ops = au_wbr_copyup_ops + sbinfo->si_wbr_copyup;
 +	sbinfo->si_wbr_create_ops = au_wbr_create_ops + sbinfo->si_wbr_create;
 +
++	au_fhsm_init(sbinfo);
++
 +	sbinfo->si_mntflags = au_opts_plink(AuOpt_Def);
 +
 +	sbinfo->si_xino_jiffy = jiffies;
@@ -24026,8 +24991,8 @@
 +#endif /* __KERNEL__ */
 +#endif /* __AUFS_SPL_H__ */
 --- a/fs/aufs/super.c	1970-01-01 01:00:00.000000000 +0100
-+++ b/fs/aufs/super.c	2014-07-15 14:04:48.736871625 +0100
-@@ -0,0 +1,998 @@
++++ b/fs/aufs/super.c	2014-09-08 00:38:33.518569904 +0100
+@@ -0,0 +1,1004 @@
 +/*
 + * Copyright (C) 2005-2014 Junjiro R. Okajima
 + *
@@ -24309,6 +25274,8 @@
 +	AuUInt(RDBLK, rdblk, sbinfo->si_rdblk);
 +	AuUInt(RDHASH, rdhash, sbinfo->si_rdhash);
 +
++	au_fhsm_show(m, sbinfo);
++
 +	AuBool(SUM, sum);
 +	/* AuBool(SUM_W, wsum); */
 +	AuBool(WARN_PERM, warn_perm);
@@ -24524,9 +25491,9 @@
 +	}
 +
 +	sz = sizeof(array) * *hint;
-+	array = kmalloc(sz, GFP_NOFS);
++	array = kzalloc(sz, GFP_NOFS);
 +	if (unlikely(!array))
-+		array = vmalloc(sz);
++		array = vzalloc(sz);
 +	if (unlikely(!array)) {
 +		array = ERR_PTR(-ENOMEM);
 +		goto out;
@@ -24693,6 +25660,8 @@
 +	sigen = au_sigen(sb);
 +	for (ull = 0; ull < max; ull++) {
 +		inode = array[ull];
++		if (unlikely(!inode))
++			break;
 +		if (au_iigen(inode, NULL) != sigen) {
 +			ii_write_lock_child(inode);
 +			e = au_refresh_hinode_self(inode);
@@ -24826,6 +25795,7 @@
 +		au_dy_arefresh(do_dx);
 +	}
 +
++	au_fhsm_wrote_all(sb, /*force*/1); /* ?? */
 +	aufs_write_unlock(root);
 +
 +out_mtx:
@@ -25001,6 +25971,7 @@
 +	if (sbinfo) {
 +		au_sbilist_del(sb);
 +		aufs_write_lock(sb->s_root);
++		au_fhsm_fin(sb);
 +		if (sbinfo->si_wbr_create_ops->fin)
 +			sbinfo->si_wbr_create_ops->fin(sb);
 +		if (au_opt_test(sbinfo->si_mntflags, UDBA_HNOTIFY)) {
@@ -25027,8 +25998,8 @@
 +	.owner		= THIS_MODULE,
 +};
 --- a/fs/aufs/super.h	1970-01-01 01:00:00.000000000 +0100
-+++ b/fs/aufs/super.h	2014-07-15 14:04:48.736871625 +0100
-@@ -0,0 +1,582 @@
++++ b/fs/aufs/super.h	2014-09-08 00:38:33.518569904 +0100
+@@ -0,0 +1,644 @@
 +/*
 + * Copyright (C) 2005-2014 Junjiro R. Okajima
 + *
@@ -25106,6 +26077,21 @@
 +	return ino % AuPlink_NHASH;
 +}
 +
++/* File-based Hierarchical Storage Management */
++struct au_fhsm {
++#ifdef CONFIG_AUFS_FHSM
++	/* allow only one process who can receive the notification */
++	spinlock_t		fhsm_spin;
++	pid_t			fhsm_pid;
++	wait_queue_head_t	fhsm_wqh;
++	atomic_t		fhsm_readable;
++
++	/* these are protected by si_rwsem */
++	unsigned long		fhsm_expire;
++	aufs_bindex_t		fhsm_bottom;
++#endif
++};
++
 +struct au_branch;
 +struct au_sbinfo {
 +	/* nowait tasks in the system-wide workqueue */
@@ -25155,6 +26141,9 @@
 +	/* most free space */
 +	struct au_wbr_mfs	si_wbr_mfs;
 +
++	/* File-based Hierarchical Storage Management */
++	struct au_fhsm		si_fhsm;
++
 +	/* mount flags */
 +	/* include/asm-ia64/siginfo.h defines a macro named si_flags */
 +	unsigned int		si_mntflags;
@@ -25238,6 +26227,14 @@
 + * if it is false, refreshing dirs at access time is unnecesary
 + */
 +#define AuSi_FAILED_REFRESH_DIR	1
++
++#define AuSi_FHSM		(1 << 1)	/* fhsm is active now */
++
++#ifndef CONFIG_AUFS_FHSM
++#undef AuSi_FHSM
++#define AuSi_FHSM		0
++#endif
++
 +static inline unsigned char au_do_ftest_si(struct au_sbinfo *sbi,
 +					   unsigned int flag)
 +{
@@ -25320,6 +26317,43 @@
 +/* mvdown.c */
 +int au_mvdown(struct dentry *dentry, struct aufs_mvdown __user *arg);
 +
++#ifdef CONFIG_AUFS_FHSM
++/* fhsm.c */
++
++static inline pid_t au_fhsm_pid(struct au_fhsm *fhsm)
++{
++	pid_t pid;
++
++	spin_lock(&fhsm->fhsm_spin);
++	pid = fhsm->fhsm_pid;
++	spin_unlock(&fhsm->fhsm_spin);
++
++	return pid;
++}
++
++void au_fhsm_wrote(struct super_block *sb, aufs_bindex_t bindex, int force);
++void au_fhsm_wrote_all(struct super_block *sb, int force);
++int au_fhsm_fd(struct super_block *sb, int oflags);
++int au_fhsm_br_alloc(struct au_branch *br);
++void au_fhsm_set_bottom(struct super_block *sb, aufs_bindex_t bindex);
++void au_fhsm_fin(struct super_block *sb);
++void au_fhsm_init(struct au_sbinfo *sbinfo);
++void au_fhsm_set(struct au_sbinfo *sbinfo, unsigned int sec);
++void au_fhsm_show(struct seq_file *seq, struct au_sbinfo *sbinfo);
++#else
++AuStubVoid(au_fhsm_wrote, struct super_block *sb, aufs_bindex_t bindex,
++	   int force)
++AuStubVoid(au_fhsm_wrote_all, struct super_block *sb, int force)
++AuStub(int, au_fhsm_fd, return -EOPNOTSUPP, struct super_block *sb, int oflags)
++AuStub(pid_t, au_fhsm_pid, return 0, struct au_fhsm *fhsm)
++AuStubInt0(au_fhsm_br_alloc, struct au_branch *br)
++AuStubVoid(au_fhsm_set_bottom, struct super_block *sb, aufs_bindex_t bindex)
++AuStubVoid(au_fhsm_fin, struct super_block *sb)
++AuStubVoid(au_fhsm_init, struct au_sbinfo *sbinfo)
++AuStubVoid(au_fhsm_set, struct au_sbinfo *sbinfo, unsigned int sec)
++AuStubVoid(au_fhsm_show, struct seq_file *seq, struct au_sbinfo *sbinfo)
++#endif
++
 +/* ---------------------------------------------------------------------- */
 +
 +static inline struct au_sbinfo *au_sbi(struct super_block *sb)
@@ -25436,8 +26470,7 @@
 +	bit = si_pid_bit();
 +	if (bit < PID_MAX_DEFAULT)
 +		return test_bit(bit, au_sbi(sb)->au_si_pid.bitmap);
-+	else
-+		return si_pid_test_slow(sb);
++	return si_pid_test_slow(sb);
 +}
 +
 +static inline void si_pid_set(struct super_block *sb)
@@ -25719,8 +26752,8 @@
 +	return err;
 +}
 --- a/fs/aufs/sysaufs.h	1970-01-01 01:00:00.000000000 +0100
-+++ b/fs/aufs/sysaufs.h	2014-01-20 03:24:33.516760970 +0000
-@@ -0,0 +1,103 @@
++++ b/fs/aufs/sysaufs.h	2014-09-08 00:38:33.518569904 +0100
+@@ -0,0 +1,107 @@
 +/*
 + * Copyright (C) 2005-2014 Junjiro R. Okajima
 + *
@@ -25792,6 +26825,10 @@
 +int sysaufs_si_xi_path(struct seq_file *seq, struct super_block *sb);
 +ssize_t sysaufs_si_show(struct kobject *kobj, struct attribute *attr,
 +			 char *buf);
++long au_brinfo_ioctl(struct file *file, unsigned long arg);
++#ifdef CONFIG_COMPAT
++long au_brinfo_compat_ioctl(struct file *file, unsigned long arg);
++#endif
 +
 +void sysaufs_br_init(struct au_branch *br);
 +void sysaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex);
@@ -25825,8 +26862,8 @@
 +#endif /* __KERNEL__ */
 +#endif /* __SYSAUFS_H__ */
 --- a/fs/aufs/sysfs.c	1970-01-01 01:00:00.000000000 +0100
-+++ b/fs/aufs/sysfs.c	2014-07-15 14:04:48.736871625 +0100
-@@ -0,0 +1,289 @@
++++ b/fs/aufs/sysfs.c	2014-09-08 00:38:33.518569904 +0100
+@@ -0,0 +1,372 @@
 +/*
 + * Copyright (C) 2005-2014 Junjiro R. Okajima
 + *
@@ -25848,6 +26885,7 @@
 + * sysfs interface
 + */
 +
++#include <linux/compat.h>
 +#include <linux/seq_file.h>
 +#include "aufs.h"
 +
@@ -26044,6 +27082,88 @@
 +
 +/* ---------------------------------------------------------------------- */
 +
++static int au_brinfo(struct super_block *sb, union aufs_brinfo __user *arg)
++{
++	int err;
++	int16_t brid;
++	aufs_bindex_t bindex, bend;
++	size_t sz;
++	char *buf;
++	struct seq_file *seq;
++	struct au_branch *br;
++
++	si_read_lock(sb, AuLock_FLUSH);
++	bend = au_sbend(sb);
++	err = bend + 1;
++	if (!arg)
++		goto out;
++
++	err = -ENOMEM;
++	buf = (void *)__get_free_page(GFP_NOFS);
++	if (unlikely(!buf))
++		goto out;
++
++	seq = au_seq(buf, PAGE_SIZE);
++	err = PTR_ERR(seq);
++	if (IS_ERR(seq))
++		goto out_buf;
++
++	sz = sizeof(*arg) - offsetof(union aufs_brinfo, path);
++	for (bindex = 0; bindex <= bend; bindex++, arg++) {
++		err = !access_ok(VERIFY_WRITE, arg, sizeof(*arg));
++		if (unlikely(err))
++			break;
++
++		br = au_sbr(sb, bindex);
++		brid = br->br_id;
++		BUILD_BUG_ON(sizeof(brid) != sizeof(arg->id));
++		err = __put_user(brid, &arg->id);
++		if (unlikely(err))
++			break;
++
++		BUILD_BUG_ON(sizeof(br->br_perm) != sizeof(arg->perm));
++		err = __put_user(br->br_perm, &arg->perm);
++		if (unlikely(err))
++			break;
++
++		au_seq_path(seq, &br->br_path);
++		err = seq_putc(seq, '\0');
++		if (!err && seq->count <= sz) {
++			err = copy_to_user(arg->path, seq->buf, seq->count);
++			seq->count = 0;
++			if (unlikely(err))
++				break;
++		} else {
++			err = -E2BIG;
++			goto out_seq;
++		}
++	}
++	if (unlikely(err))
++		err = -EFAULT;
++
++out_seq:
++	kfree(seq);
++out_buf:
++	free_page((unsigned long)buf);
++out:
++	si_read_unlock(sb);
++	return err;
++}
++
++long au_brinfo_ioctl(struct file *file, unsigned long arg)
++{
++	return au_brinfo(file->f_dentry->d_sb, (void __user *)arg);
++}
++
++#ifdef CONFIG_COMPAT
++long au_brinfo_compat_ioctl(struct file *file, unsigned long arg)
++{
++	return au_brinfo(file->f_dentry->d_sb, compat_ptr(arg));
++}
++#endif
++
++/* ---------------------------------------------------------------------- */
++
 +void sysaufs_br_init(struct au_branch *br)
 +{
 +	int i;
@@ -27169,8 +28289,8 @@
 +	return 0;
 +}
 --- a/fs/aufs/vfsub.c	1970-01-01 01:00:00.000000000 +0100
-+++ b/fs/aufs/vfsub.c	2014-07-15 14:04:40.008871417 +0100
-@@ -0,0 +1,782 @@
++++ b/fs/aufs/vfsub.c	2014-09-08 00:38:33.518569904 +0100
+@@ -0,0 +1,796 @@
 +/*
 + * Copyright (C) 2005-2014 Junjiro R. Okajima
 + *
@@ -27331,7 +28451,9 @@
 +	if (unlikely(err))
 +		goto out;
 +
++	lockdep_off();
 +	err = vfs_create(dir, path->dentry, mode, want_excl);
++	lockdep_on();
 +	if (!err) {
 +		struct path tmp = *path;
 +		int did;
@@ -27362,7 +28484,9 @@
 +	if (unlikely(err))
 +		goto out;
 +
++	lockdep_off();
 +	err = vfs_symlink(dir, path->dentry, symname);
++	lockdep_on();
 +	if (!err) {
 +		struct path tmp = *path;
 +		int did;
@@ -27393,7 +28517,9 @@
 +	if (unlikely(err))
 +		goto out;
 +
++	lockdep_off();
 +	err = vfs_mknod(dir, path->dentry, mode, dev);
++	lockdep_on();
 +	if (!err) {
 +		struct path tmp = *path;
 +		int did;
@@ -27519,7 +28645,9 @@
 +	if (unlikely(err))
 +		goto out;
 +
++	lockdep_off();
 +	err = vfs_mkdir(dir, path->dentry, mode);
++	lockdep_on();
 +	if (!err) {
 +		struct path tmp = *path;
 +		int did;
@@ -27765,9 +28893,11 @@
 +	int err, do_sio, wkq_err;
 +
 +	do_sio = au_test_h_perm_sio(dir, MAY_EXEC | MAY_WRITE);
-+	if (!do_sio)
++	if (!do_sio) {
++		lockdep_off();
 +		err = vfsub_mkdir(dir, path, mode);
-+	else {
++		lockdep_on();
++	} else {
 +		struct au_vfsub_mkdir_args args = {
 +			.errp	= &err,
 +			.dir	= dir,
@@ -27799,9 +28929,11 @@
 +	int err, do_sio, wkq_err;
 +
 +	do_sio = au_test_h_perm_sio(dir, MAY_EXEC | MAY_WRITE);
-+	if (!do_sio)
++	if (!do_sio) {
++		lockdep_off();
 +		err = vfsub_rmdir(dir, path);
-+	else {
++		lockdep_on();
++	} else {
 +		struct au_vfsub_rmdir_args args = {
 +			.errp	= &err,
 +			.dir	= dir,
@@ -27834,8 +28966,10 @@
 +
 +	*a->errp = -EPERM;
 +	if (!IS_IMMUTABLE(h_inode) && !IS_APPEND(h_inode)) {
++		lockdep_off();
 +		*a->errp = notify_change(a->path->dentry, a->ia,
 +					 a->delegated_inode);
++		lockdep_on();
 +		if (!*a->errp)
 +			vfsub_update_h_iattr(a->path, /*did*/NULL); /*ignore*/
 +	}
@@ -28241,8 +29375,8 @@
 +#endif /* __KERNEL__ */
 +#endif /* __AUFS_VFSUB_H__ */
 --- a/fs/aufs/wbr_policy.c	1970-01-01 01:00:00.000000000 +0100
-+++ b/fs/aufs/wbr_policy.c	2014-07-15 14:04:48.736871625 +0100
-@@ -0,0 +1,764 @@
++++ b/fs/aufs/wbr_policy.c	2014-09-08 00:38:33.518569904 +0100
+@@ -0,0 +1,765 @@
 +/*
 + * Copyright (C) 2005-2014 Junjiro R. Okajima
 + *
@@ -28410,6 +29544,7 @@
 +		au_set_ibend(inode, bdst);
 +	au_set_h_iptr(inode, bdst, au_igrab(h_inode),
 +		      au_hi_flags(inode, /*isdir*/1));
++	au_fhsm_wrote(dentry->d_sb, bdst, /*force*/0);
 +	goto out; /* success */
 +
 +	/* revert */
@@ -29008,8 +30143,8 @@
 +	}
 +};
 --- a/fs/aufs/whout.c	1970-01-01 01:00:00.000000000 +0100
-+++ b/fs/aufs/whout.c	2014-07-15 14:04:48.736871625 +0100
-@@ -0,0 +1,1051 @@
++++ b/fs/aufs/whout.c	2014-09-08 00:38:33.518569904 +0100
+@@ -0,0 +1,1056 @@
 +/*
 + * Copyright (C) 2005-2014 Junjiro R. Okajima
 + *
@@ -29602,6 +30737,8 @@
 +	wbr_wh_write_unlock(wbr);
 +	au_hn_imtx_unlock(hdir);
 +	di_read_unlock(a->sb->s_root, AuLock_IR);
++	if (!err)
++		au_fhsm_wrote(a->sb, bindex, /*force*/0);
 +
 +out:
 +	if (wbr)
@@ -29689,6 +30826,8 @@
 +
 +	/* return this error in this context */
 +	err = vfsub_create(h_dir, &h_path, WH_MASK, /*want_excl*/true);
++	if (!err)
++		au_fhsm_wrote(sb, bindex, /*force*/0);
 +
 +out:
 +	wbr_wh_read_unlock(wbr);
@@ -29813,9 +30952,10 @@
 +	wh_dentry = au_wh_lkup(h_parent, &dentry->d_name, au_sbr(sb, bindex));
 +	if (!IS_ERR(wh_dentry) && !wh_dentry->d_inode) {
 +		err = link_or_create_wh(sb, bindex, wh_dentry);
-+		if (!err)
++		if (!err) {
 +			au_set_dbwh(dentry, bindex);
-+		else {
++			au_fhsm_wrote(sb, bindex, /*force*/0);
++		} else {
 +			dput(wh_dentry);
 +			wh_dentry = ERR_PTR(err);
 +		}
@@ -31779,8 +32919,8 @@
 +	return err;
 +}
 --- a/include/uapi/linux/aufs_type.h	1970-01-01 01:00:00.000000000 +0100
-+++ b/include/uapi/linux/aufs_type.h	2014-07-15 14:04:48.740871626 +0100
-@@ -0,0 +1,301 @@
++++ b/include/uapi/linux/aufs_type.h	2014-09-08 00:39:05.266571221 +0100
+@@ -0,0 +1,380 @@
 +/*
 + * Copyright (C) 2005-2014 Junjiro R. Okajima
 + *
@@ -31822,7 +32962,7 @@
 +
 +#include <linux/limits.h>
 +
-+#define AUFS_VERSION	"3.x-rcN-20140714"
++#define AUFS_VERSION	"3.16-20140908"
 +
 +/* todo? move this to linux-2.6.19/include/magic.h */
 +#define AUFS_SUPER_MAGIC	('a' << 24 | 'u' << 16 | 'f' << 8 | 's')
@@ -31876,6 +33016,7 @@
 +#define AUFS_WKQ_NAME		AUFS_NAME "d"
 +#define AUFS_MFS_DEF_SEC	30 /* seconds */
 +#define AUFS_MFS_MAX_SEC	3600 /* seconds */
++#define AUFS_FHSM_CACHE_DEF_SEC	30 /* seconds */
 +#define AUFS_PLINK_WARN		50 /* number of plinks in a single bucket */
 +
 +/* pseudo-link maintenace under /proc */
@@ -31901,11 +33042,77 @@
 +#define AUFS_BRPERM_RR		"rr"
 +#define AUFS_BRATTR_COO_REG	"coo_reg"
 +#define AUFS_BRATTR_COO_ALL	"coo_all"
++#define AUFS_BRATTR_FHSM	"fhsm"
 +#define AUFS_BRATTR_UNPIN	"unpin"
 +#define AUFS_BRRATTR_WH		"wh"
 +#define AUFS_BRWATTR_NLWH	"nolwh"
 +#define AUFS_BRWATTR_MOO	"moo"
 +
++#define AuBrPerm_RW		1		/* writable, hardlinkable wh */
++#define AuBrPerm_RO		(1 << 1)	/* readonly */
++#define AuBrPerm_RR		(1 << 2)	/* natively readonly */
++#define AuBrPerm_Mask		(AuBrPerm_RW | AuBrPerm_RO | AuBrPerm_RR)
++
++#define AuBrAttr_COO_REG	(1 << 3)	/* copy-up on open */
++#define AuBrAttr_COO_ALL	(1 << 4)
++#define AuBrAttr_COO_Mask	(AuBrAttr_COO_REG | AuBrAttr_COO_ALL)
++
++#define AuBrAttr_FHSM		(1 << 5)	/* file-based hsm */
++#define AuBrAttr_UNPIN		(1 << 6)	/* rename-able top dir of
++						   branch */
++
++#define AuBrRAttr_WH		(1 << 7)	/* whiteout-able */
++#define AuBrRAttr_Mask		AuBrRAttr_WH
++
++#define AuBrWAttr_NoLinkWH	(1 << 8)	/* un-hardlinkable whiteouts */
++#define AuBrWAttr_MOO		(1 << 9)	/* move-up on open */
++#define AuBrWAttr_Mask		(AuBrWAttr_NoLinkWH | AuBrWAttr_MOO)
++
++#define AuBrAttr_CMOO_Mask	(AuBrAttr_COO_Mask | AuBrWAttr_MOO)
++
++#ifdef __KERNEL__
++#ifndef CONFIG_AUFS_FHSM
++#undef AuBrAttr_FHSM
++#define AuBrAttr_FHSM		0
++#endif
++#endif
++
++/* the longest combination */
++#define AuBrPermStrSz	sizeof(AUFS_BRPERM_RW		\
++			       "+" AUFS_BRATTR_COO_REG	\
++			       "+" AUFS_BRATTR_FHSM	\
++			       "+" AUFS_BRATTR_UNPIN	\
++			       "+" AUFS_BRWATTR_NLWH)
++
++typedef struct {
++	char a[AuBrPermStrSz];
++} au_br_perm_str_t;
++
++static inline int au_br_writable(int brperm)
++{
++	return brperm & AuBrPerm_RW;
++}
++
++static inline int au_br_whable(int brperm)
++{
++	return brperm & (AuBrPerm_RW | AuBrRAttr_WH);
++}
++
++static inline int au_br_wh_linkable(int brperm)
++{
++	return !(brperm & AuBrWAttr_NoLinkWH);
++}
++
++static inline int au_br_cmoo(int brperm)
++{
++	return brperm & AuBrAttr_CMOO_Mask;
++}
++
++static inline int au_br_fhsm(int brperm)
++{
++	return brperm & AuBrAttr_FHSM;
++}
++
 +/* ---------------------------------------------------------------------- */
 +
 +/* ioctl */
@@ -31914,14 +33121,11 @@
 +	AuCtl_RDU,
 +	AuCtl_RDU_INO,
 +
-+	/* pathconf wrapper */
-+	AuCtl_WBR_FD,
-+
-+	/* busy inode */
-+	AuCtl_IBUSY,
-+
-+	/* move-down */
-+	AuCtl_MVDOWN
++	AuCtl_WBR_FD,	/* pathconf wrapper */
++	AuCtl_IBUSY,	/* busy inode */
++	AuCtl_MVDOWN,	/* move-down */
++	AuCtl_BR,	/* info about branches */
++	AuCtl_FHSM_FD	/* connection for fhsm */
 +};
 +
 +/* borrowed from linux/include/linux/kernel.h */
@@ -32035,9 +33239,10 @@
 +#define AUFS_MVDOWN_ROUPPER_R	(1 << 6)	/* did on upper RO */
 +#define AUFS_MVDOWN_BRID_UPPER	(1 << 7)	/* upper brid */
 +#define AUFS_MVDOWN_BRID_LOWER	(1 << 8)	/* lower brid */
-+#define AUFS_MVDOWN_STFS	(1 << 9)	/* req. stfs */
-+#define AUFS_MVDOWN_STFS_FAILED	(1 << 10)	/* output: stfs is unusable */
-+#define AUFS_MVDOWN_BOTTOM	(1 << 11)	/* output: no more lowers */
++#define AUFS_MVDOWN_FHSM_LOWER	(1 << 9)	/* find fhsm attr for lower */
++#define AUFS_MVDOWN_STFS	(1 << 10)	/* req. stfs */
++#define AUFS_MVDOWN_STFS_FAILED	(1 << 11)	/* output: stfs is unusable */
++#define AUFS_MVDOWN_BOTTOM	(1 << 12)	/* output: no more lowers */
 +
 +/* index for move-down */
 +enum {
@@ -32072,6 +33277,18 @@
 +
 +/* ---------------------------------------------------------------------- */
 +
++union aufs_brinfo {
++	/* PATH_MAX may differ between kernel-space and user-space */
++	char	_spacer[4096];
++	struct {
++		int16_t	id;
++		int	perm;
++		char	path[0];
++	};
++} __aligned(8);
++
++/* ---------------------------------------------------------------------- */
++
 +#define AuCtlType		'A'
 +#define AUFS_CTL_RDU		_IOWR(AuCtlType, AuCtl_RDU, struct aufs_rdu)
 +#define AUFS_CTL_RDU_INO	_IOWR(AuCtlType, AuCtl_RDU_INO, struct aufs_rdu)
@@ -32080,5 +33297,7 @@
 +#define AUFS_CTL_IBUSY		_IOWR(AuCtlType, AuCtl_IBUSY, struct aufs_ibusy)
 +#define AUFS_CTL_MVDOWN		_IOWR(AuCtlType, AuCtl_MVDOWN, \
 +				      struct aufs_mvdown)
++#define AUFS_CTL_BRINFO		_IOW(AuCtlType, AuCtl_BR, union aufs_brinfo)
++#define AUFS_CTL_FHSM_FD	_IOW(AuCtlType, AuCtl_FHSM_FD, int)
 +
 +#endif /* __AUFS_TYPE_H__ */

Modified: dists/trunk/linux/debian/patches/features/all/aufs3/aufs3-base.patch
==============================================================================
--- dists/trunk/linux/debian/patches/features/all/aufs3/aufs3-base.patch	Sun Sep  7 23:25:42 2014	(r21781)
+++ dists/trunk/linux/debian/patches/features/all/aufs3/aufs3-base.patch	Mon Sep  8 01:24:43 2014	(r21782)
@@ -1,13 +1,38 @@
 From: J. R. Okajima <hooanon05 at yahoo.co.jp>
-Date: Mon Jun 30 09:36:40 2014 +0900
-Subject: aufs3.x-rcN base patch
-Origin: http://sourceforge.net/p/aufs/aufs3-standalone/ci/501539c2f9478ef69fa42acfb43ef1420d9bb524/tree/
+Date: Sat Aug 23 03:27:11 2014 +0900
+Subject: aufs3.16 base patch
+Origin: http://sourceforge.net/p/aufs/aufs3-standalone/ci/888949daf96bf7e2b857dc38e22029513f94d4ae/tree/
 Bug-Debian: https://bugs.debian.org/541828
 
 Patch headers added by debian/patches/features/all/aufs3/gen-patch
 
-aufs3.x-rcN base patch
+aufs3.16 base patch
 
+diff --git a/MAINTAINERS b/MAINTAINERS
+index c2066f4..f07a989 100644
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -1698,6 +1698,20 @@ F:	include/linux/audit.h
+ F:	include/uapi/linux/audit.h
+ F:	kernel/audit*
+ 
++AUFS (advanced multi layered unification filesystem) FILESYSTEM
++M:	"J. R. Okajima" <hooanon05g at gmail.com>
++L:	linux-unionfs at vger.kernel.org
++L:	aufs-users at lists.sourceforge.net (members only)
++W:	http://aufs.sourceforge.net
++T:	git://git.code.sf.net/p/aufs/aufs3-linux
++T:	git://github.com/sfjro/aufs3-linux.git
++S:	Supported
++F:	Documentation/filesystems/aufs/
++F:	Documentation/ABI/testing/debugfs-aufs
++F:	Documentation/ABI/testing/sysfs-aufs
++F:	fs/aufs/
++F:	include/uapi/linux/aufs_type.h
++
+ AUXILIARY DISPLAY DRIVERS
+ M:	Miguel Ojeda Sandonis <miguel.ojeda.sandonis at gmail.com>
+ W:	http://miguelojeda.es/auxdisplay.htm
 diff --git a/drivers/block/loop.c b/drivers/block/loop.c
 index 6cb1beb..30efd68 100644
 --- a/drivers/block/loop.c

Modified: dists/trunk/linux/debian/patches/features/all/aufs3/aufs3-kbuild.patch
==============================================================================
--- dists/trunk/linux/debian/patches/features/all/aufs3/aufs3-kbuild.patch	Sun Sep  7 23:25:42 2014	(r21781)
+++ dists/trunk/linux/debian/patches/features/all/aufs3/aufs3-kbuild.patch	Mon Sep  8 01:24:43 2014	(r21782)
@@ -1,12 +1,12 @@
 From: J. R. Okajima <hooanon05 at yahoo.co.jp>
-Date: Sun Jun 15 19:47:55 2014 +0900
-Subject: aufs3.x-rcN kbuild patch
-Origin: http://sourceforge.net/p/aufs/aufs3-standalone/ci/501539c2f9478ef69fa42acfb43ef1420d9bb524/tree/
+Date: Thu Aug 7 21:42:20 2014 +0900
+Subject: aufs3.16 kbuild patch
+Origin: http://sourceforge.net/p/aufs/aufs3-standalone/ci/888949daf96bf7e2b857dc38e22029513f94d4ae/tree/
 Bug-Debian: https://bugs.debian.org/541828
 
 Patch headers added by debian/patches/features/all/aufs3/gen-patch
 
-aufs3.x-rcN kbuild patch
+aufs3.16 kbuild patch
 
 diff --git a/fs/Kconfig b/fs/Kconfig
 index 312393f..78632ed 100644

Modified: dists/trunk/linux/debian/patches/features/all/aufs3/aufs3-mmap.patch
==============================================================================
--- dists/trunk/linux/debian/patches/features/all/aufs3/aufs3-mmap.patch	Sun Sep  7 23:25:42 2014	(r21781)
+++ dists/trunk/linux/debian/patches/features/all/aufs3/aufs3-mmap.patch	Mon Sep  8 01:24:43 2014	(r21782)
@@ -1,12 +1,12 @@
 From: J. R. Okajima <hooanon05 at yahoo.co.jp>
-Date: Thu Jul 10 02:32:20 2014 +0900
-Subject: aufs3.x-rcN mmap patch
-Origin: http://sourceforge.net/p/aufs/aufs3-standalone/ci/501539c2f9478ef69fa42acfb43ef1420d9bb524/tree/
+Date: Thu Aug 7 21:42:20 2014 +0900
+Subject: aufs3.16 mmap patch
+Origin: http://sourceforge.net/p/aufs/aufs3-standalone/ci/888949daf96bf7e2b857dc38e22029513f94d4ae/tree/
 Bug-Debian: https://bugs.debian.org/541828
 
 Patch headers added by debian/patches/features/all/aufs3/gen-patch
 
-aufs3.x-rcN mmap patch
+aufs3.16 mmap patch
 
 diff --git a/fs/buffer.c b/fs/buffer.c
 index eba6e4f..31f0b2d 100644
@@ -79,109 +79,34 @@
  		ino = inode->i_ino;
  		pgoff = (loff_t)vma->vm_pgoff << PAGE_SHIFT;
 diff --git a/include/linux/mm.h b/include/linux/mm.h
-index e03dd29..f849643 100644
+index e03dd29..dd32624 100644
 --- a/include/linux/mm.h
 +++ b/include/linux/mm.h
-@@ -18,6 +18,9 @@
- #include <linux/pfn.h>
- #include <linux/bit_spinlock.h>
- #include <linux/shrinker.h>
-+#include <linux/dcache.h>
-+#include <linux/file.h>
-+#include <linux/fs.h>
- 
- struct mempolicy;
- struct anon_vma;
-@@ -1184,6 +1187,93 @@ static inline int fixup_user_fault(struct task_struct *tsk,
+@@ -1184,6 +1184,28 @@ static inline int fixup_user_fault(struct task_struct *tsk,
  }
  #endif
  
-+/*
-+ * Mainly for aufs which mmap(2) diffrent file and wants to print different path
-+ * in /proc/PID/maps.
-+ */
-+/* #define AUFS_DEBUG_MMAP */
-+static inline void aufs_trace(struct file *f, struct file *pr,
-+			      const char func[], int line, const char func2[])
-+{
-+#ifdef AUFS_DEBUG_MMAP
-+	if (pr)
-+		pr_info("%s:%d: %s, %p\n", func, line, func2,
-+			f ? (char *)f->f_dentry->d_name.name : "(null)");
-+#endif
-+}
-+
-+static inline struct file *vmr_do_pr_or_file(struct vm_region *region,
-+					     const char func[], int line)
-+{
-+	struct file *f = region->vm_file, *pr = region->vm_prfile;
-+
-+	aufs_trace(f, pr, func, line, __func__);
-+	return (f && pr) ? pr : f;
-+}
-+
-+static inline void vmr_do_fput(struct vm_region *region,
-+			       const char func[], int line)
-+{
-+	struct file *f = region->vm_file, *pr = region->vm_prfile;
-+
-+	aufs_trace(f, pr, func, line, __func__);
-+	fput(f);
-+	if (f && pr)
-+		fput(pr);
-+}
++#ifdef CONFIG_MMU
++extern void vma_do_file_update_time(struct vm_area_struct *, const char[], int);
++extern struct file *vma_do_pr_or_file(struct vm_area_struct *, const char[],
++				      int);
++extern void vma_do_get_file(struct vm_area_struct *, const char[], int);
++extern void vma_do_fput(struct vm_area_struct *, const char[], int);
 +
-+static inline void vma_do_file_update_time(struct vm_area_struct *vma,
-+					   const char func[], int line)
-+{
-+	struct file *f = vma->vm_file, *pr = vma->vm_prfile;
-+
-+	aufs_trace(f, pr, func, line, __func__);
-+	file_update_time(f);
-+	if (f && pr)
-+		file_update_time(pr);
-+}
-+
-+static inline struct file *vma_do_pr_or_file(struct vm_area_struct *vma,
-+					     const char func[], int line)
-+{
-+	struct file *f = vma->vm_file, *pr = vma->vm_prfile;
-+
-+	aufs_trace(f, pr, func, line, __func__);
-+	return (f && pr) ? pr : f;
-+}
-+
-+static inline void vma_do_get_file(struct vm_area_struct *vma,
-+				   const char func[], int line)
-+{
-+	struct file *f = vma->vm_file, *pr = vma->vm_prfile;
-+
-+	aufs_trace(f, pr, func, line, __func__);
-+	get_file(f);
-+	if (f && pr)
-+		get_file(pr);
-+}
-+
-+static inline void vma_do_fput(struct vm_area_struct *vma,
-+			       const char func[], int line)
-+{
-+	struct file *f = vma->vm_file, *pr = vma->vm_prfile;
-+
-+	aufs_trace(f, pr, func, line, __func__);
-+	fput(f);
-+	if (f && pr)
-+		fput(pr);
-+}
-+
-+#define vmr_pr_or_file(region)		vmr_do_pr_or_file(region, __func__, \
-+							  __LINE__)
-+#define vmr_fput(region)		vmr_do_fput(region, __func__, __LINE__)
 +#define vma_file_update_time(vma)	vma_do_file_update_time(vma, __func__, \
 +								__LINE__)
 +#define vma_pr_or_file(vma)		vma_do_pr_or_file(vma, __func__, \
 +							  __LINE__)
 +#define vma_get_file(vma)		vma_do_get_file(vma, __func__, __LINE__)
 +#define vma_fput(vma)			vma_do_fput(vma, __func__, __LINE__)
++#else
++extern struct file *vmr_do_pr_or_file(struct vm_region *, const char[], int);
++extern void vmr_do_fput(struct vm_region *, const char[], int);
++
++#define vmr_pr_or_file(region)		vmr_do_pr_or_file(region, __func__, \
++							  __LINE__)
++#define vmr_fput(region)		vmr_do_fput(region, __func__, __LINE__)
++#endif /* CONFIG_MMU */
 +
  extern int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, int len, int write);
  extern int access_remote_vm(struct mm_struct *mm, unsigned long addr,
@@ -219,11 +144,24 @@
  			if (tmp->vm_flags & VM_DENYWRITE)
  				atomic_dec(&inode->i_writecount);
  			mutex_lock(&mapping->i_mmap_mutex);
+diff --git a/mm/Makefile b/mm/Makefile
+index 4064f3e..0003fdf 100644
+--- a/mm/Makefile
++++ b/mm/Makefile
+@@ -18,7 +18,7 @@ obj-y			:= filemap.o mempool.o oom_kill.o fadvise.o \
+ 			   mm_init.o mmu_context.o percpu.o slab_common.o \
+ 			   compaction.o balloon_compaction.o vmacache.o \
+ 			   interval_tree.o list_lru.o workingset.o \
+-			   iov_iter.o $(mmu-y)
++			   iov_iter.o prfile.o $(mmu-y)
+ 
+ obj-y += init-mm.o
+ 
 diff --git a/mm/filemap.c b/mm/filemap.c
-index dafb06f..f8c0ba3 100644
+index 900edfa..f4dda0c 100644
 --- a/mm/filemap.c
 +++ b/mm/filemap.c
-@@ -2037,7 +2037,7 @@ int filemap_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
+@@ -2040,7 +2040,7 @@ int filemap_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
  	int ret = VM_FAULT_LOCKED;
  
  	sb_start_pagefault(inode->i_sb);
@@ -287,7 +225,7 @@
  	return error;
  }
 diff --git a/mm/memory.c b/mm/memory.c
-index d67fd9f..aa1e55d 100644
+index 8b44f76..69a72bf 100644
 --- a/mm/memory.c
 +++ b/mm/memory.c
 @@ -2161,7 +2161,7 @@ reuse:
@@ -422,3 +360,95 @@
  	kmem_cache_free(vm_area_cachep, vma);
  	kleave(" = %d", ret);
  	return ret;
+diff --git a/mm/prfile.c b/mm/prfile.c
+new file mode 100644
+index 0000000..fc708d2
+--- /dev/null
++++ b/mm/prfile.c
+@@ -0,0 +1,86 @@
++/*
++ * Mainly for aufs which mmap(2) diffrent file and wants to print different path
++ * in /proc/PID/maps.
++ * Call these functions via macros defined in linux/mm.h.
++ *
++ * See Documentation/filesystems/aufs/design/06mmap.txt
++ *
++ * Copyright (c) 2014 Junjro R. Okajima
++ * Copyright (c) 2014 Ian Campbell
++ */
++
++#include <linux/mm.h>
++#include <linux/file.h>
++#include <linux/fs.h>
++
++/* #define PRFILE_TRACE */
++static inline void prfile_trace(struct file *f, struct file *pr,
++			      const char func[], int line, const char func2[])
++{
++#ifdef PRFILE_TRACE
++	if (pr)
++		pr_info("%s:%d: %s, %p\n", func, line, func2,
++			f ? (char *)f->f_dentry->d_name.name : "(null)");
++#endif
++}
++
++#ifdef CONFIG_MMU
++void vma_do_file_update_time(struct vm_area_struct *vma, const char func[],
++			     int line)
++{
++	struct file *f = vma->vm_file, *pr = vma->vm_prfile;
++
++	prfile_trace(f, pr, func, line, __func__);
++	file_update_time(f);
++	if (f && pr)
++		file_update_time(pr);
++}
++
++struct file *vma_do_pr_or_file(struct vm_area_struct *vma, const char func[],
++			       int line)
++{
++	struct file *f = vma->vm_file, *pr = vma->vm_prfile;
++
++	prfile_trace(f, pr, func, line, __func__);
++	return (f && pr) ? pr : f;
++}
++
++void vma_do_get_file(struct vm_area_struct *vma, const char func[], int line)
++{
++	struct file *f = vma->vm_file, *pr = vma->vm_prfile;
++
++	prfile_trace(f, pr, func, line, __func__);
++	get_file(f);
++	if (f && pr)
++		get_file(pr);
++}
++
++void vma_do_fput(struct vm_area_struct *vma, const char func[], int line)
++{
++	struct file *f = vma->vm_file, *pr = vma->vm_prfile;
++
++	prfile_trace(f, pr, func, line, __func__);
++	fput(f);
++	if (f && pr)
++		fput(pr);
++}
++#else
++struct file *vmr_do_pr_or_file(struct vm_region *region, const char func[],
++			       int line)
++{
++	struct file *f = region->vm_file, *pr = region->vm_prfile;
++
++	prfile_trace(f, pr, func, line, __func__);
++	return (f && pr) ? pr : f;
++}
++
++void vmr_do_fput(struct vm_region *region, const char func[], int line)
++{
++	struct file *f = region->vm_file, *pr = region->vm_prfile;
++
++	prfile_trace(f, pr, func, line, __func__);
++	fput(f);
++	if (f && pr)
++		fput(pr);
++}
++#endif /* CONFIG_MMU */

Modified: dists/trunk/linux/debian/patches/features/all/aufs3/aufs3-standalone.patch
==============================================================================
--- dists/trunk/linux/debian/patches/features/all/aufs3/aufs3-standalone.patch	Sun Sep  7 23:25:42 2014	(r21781)
+++ dists/trunk/linux/debian/patches/features/all/aufs3/aufs3-standalone.patch	Mon Sep  8 01:24:43 2014	(r21782)
@@ -1,12 +1,12 @@
 From: J. R. Okajima <hooanon05 at yahoo.co.jp>
-Date: Fri Jun 20 09:25:26 2014 +0900
-Subject: aufs3.x-rcN standalone patch
-Origin: http://sourceforge.net/p/aufs/aufs3-standalone/ci/501539c2f9478ef69fa42acfb43ef1420d9bb524/tree/
+Date: Thu Aug 7 21:42:20 2014 +0900
+Subject: aufs3.16 standalone patch
+Origin: http://sourceforge.net/p/aufs/aufs3-standalone/ci/888949daf96bf7e2b857dc38e22029513f94d4ae/tree/
 Bug-Debian: https://bugs.debian.org/541828
 
 Patch headers added by debian/patches/features/all/aufs3/gen-patch
 
-aufs3.x-rcN standalone patch
+aufs3.16 standalone patch
 
 diff --git a/fs/inode.c b/fs/inode.c
 index b225c0f..73259c8 100644
@@ -121,7 +121,7 @@
  static int fsnotify_mark_destroy(void *ignored)
  {
 diff --git a/fs/open.c b/fs/open.c
-index 36662d0..9a7e1e0 100644
+index d6fd3ac..5e99d8b 100644
 --- a/fs/open.c
 +++ b/fs/open.c
 @@ -62,6 +62,7 @@ int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,
@@ -132,7 +132,7 @@
  
  long vfs_truncate(struct path *path, loff_t length)
  {
-@@ -299,6 +300,7 @@ int do_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
+@@ -298,6 +299,7 @@ int do_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
  	sb_end_write(inode->i_sb);
  	return ret;
  }

Modified: dists/trunk/linux/debian/patches/series
==============================================================================
--- dists/trunk/linux/debian/patches/series	Sun Sep  7 23:25:42 2014	(r21781)
+++ dists/trunk/linux/debian/patches/series	Mon Sep  8 01:24:43 2014	(r21782)
@@ -24,7 +24,6 @@
 features/all/aufs3/aufs3-standalone.patch
 features/all/aufs3/aufs3-add.patch
 # Debian-specific changes
-features/all/aufs3/aufs3-remove-circular-includes.patch
 debian/aufs3-mark-as-staging.patch
 
 # Change some defaults for security reasons



More information about the Kernel-svn-changes mailing list