From 8f3bacc883e408feb430b86a449fb528f88634b6 Mon Sep 17 00:00:00 2001
From: Bruce Ashfield <bruce.ashfield@windriver.com>
Date: Sat, 23 Dec 2017 23:56:44 -0500
Subject: [PATCH] aufs4: refresh for build fixes

Signed-off-by: Bruce Ashfield <bruce.ashfield@windriver.com>
---
 fs/aufs/Kconfig      |   13 +
 fs/aufs/Makefile     |    1 +
 fs/aufs/aufs.h       |    5 +-
 fs/aufs/branch.c     |   64 ++-
 fs/aufs/branch.h     |   19 +-
 fs/aufs/conf.mk      |    1 +
 fs/aufs/cpup.c       |   62 ++-
 fs/aufs/cpup.h       |    7 +-
 fs/aufs/dbgaufs.c    |   17 +-
 fs/aufs/dbgaufs.h    |    2 +-
 fs/aufs/dcsub.c      |    8 +-
 fs/aufs/dcsub.h      |    2 +-
 fs/aufs/debug.c      |    4 +-
 fs/aufs/debug.h      |    2 +-
 fs/aufs/dentry.c     |   78 +--
 fs/aufs/dentry.h     |   21 +-
 fs/aufs/dinfo.c      |   16 +-
 fs/aufs/dir.c        |   31 +-
 fs/aufs/dir.h        |   14 +-
 fs/aufs/dirren.c     | 1314 ++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/aufs/dirren.h     |  145 ++++++
 fs/aufs/dynop.c      |   66 +--
 fs/aufs/dynop.h      |    4 +-
 fs/aufs/export.c     |    7 +-
 fs/aufs/f            |  298 ++++++++++++
 fs/aufs/f_op.c       |  179 ++++---
 fs/aufs/fhsm.c       |    2 +-
 fs/aufs/file.c       |   73 ++-
 fs/aufs/file.h       |   56 ++-
 fs/aufs/finfo.c      |    9 +-
 fs/aufs/fstype.h     |    2 +-
 fs/aufs/hbl.h        |   64 +++
 fs/aufs/hfsnotify.c  |   10 +-
 fs/aufs/hfsplus.c    |    2 +-
 fs/aufs/hnotify.c    |   44 +-
 fs/aufs/i_op.c       |   99 ++--
 fs/aufs/i_op_add.c   |   12 +-
 fs/aufs/i_op_del.c   |    6 +-
 fs/aufs/i_op_ren.c   |  611 +++++++++++++++--------
 fs/aufs/iinfo.c      |    6 +-
 fs/aufs/inode.c      |   80 +--
 fs/aufs/inode.h      |   48 +-
 fs/aufs/ioctl.c      |    2 +-
 fs/aufs/loop.c       |    4 +-
 fs/aufs/loop.h       |    2 +-
 fs/aufs/module.c     |  107 +---
 fs/aufs/module.h     |   65 +--
 fs/aufs/mvdown.c     |    4 +-
 fs/aufs/opts.c       |   71 ++-
 fs/aufs/opts.h       |   17 +-
 fs/aufs/plink.c      |   71 +--
 fs/aufs/poll.c       |    4 +-
 fs/aufs/posix_acl.c  |   12 +-
 fs/aufs/procfs.c     |    9 +-
 fs/aufs/rdu.c        |    2 +-
 fs/aufs/rwsem.h      |    2 +-
 fs/aufs/sbinfo.c     |   69 +--
 fs/aufs/super.c      |   76 +--
 fs/aufs/super.h      |   77 ++-
 fs/aufs/sysaufs.c    |    2 +-
 fs/aufs/sysaufs.h    |    2 +-
 fs/aufs/sysfs.c      |    8 +-
 fs/aufs/sysrq.c      |   14 +-
 fs/aufs/vdir.c       |   40 +-
 fs/aufs/vfsub.c      |   28 +-
 fs/aufs/vfsub.h      |   43 +-
 fs/aufs/wbr_policy.c |   69 ++-
 fs/aufs/whout.c      |   19 +-
 fs/aufs/whout.h      |    2 +-
 fs/aufs/wkq.c        |    9 +-
 fs/aufs/wkq.h        |    4 +-
 fs/aufs/xattr.c      |  110 +++--
 fs/aufs/xino.c       |  115 ++++-
 fs/sync.c            |    3 +-
 include/linux/fs.h   |    1 +
 75 files changed, 3460 insertions(+), 1107 deletions(-)
 create mode 100644 fs/aufs/dirren.c
 create mode 100644 fs/aufs/dirren.h
 create mode 100644 fs/aufs/f
 create mode 100644 fs/aufs/hbl.h

diff --git a/fs/aufs/Kconfig b/fs/aufs/Kconfig
index 63560ceda3a4..bef60e2119bc 100644
--- a/fs/aufs/Kconfig
+++ b/fs/aufs/Kconfig
@@ -116,6 +116,19 @@ config AUFS_RDU
 	shows better performance in most cases.
 	See detail in aufs.5.
 
+config AUFS_DIRREN
+	bool "Workaround for rename(2)-ing a directory"
+	help
+	By default, aufs returns EXDEV error in renameing a dir who has
+	his child on the lower branch, since it is a bad idea to issue
+	rename(2) internally for every lower branch. But user may not
+	accept this behaviour. So here is a workaround to allow such
+	rename(2) and store some extra infromation on the writable
+	branch. Obviously this costs high (and I don't like it).
+	To use this feature, you need to enable this configuration AND
+	to specify the mount option `dirren.'
+	See details in aufs.5 and the design documents.
+
 config AUFS_SHWH
 	bool "Show whiteouts"
 	help
diff --git a/fs/aufs/Makefile b/fs/aufs/Makefile
index c7a501e3718e..7c77aa196eac 100644
--- a/fs/aufs/Makefile
+++ b/fs/aufs/Makefile
@@ -36,6 +36,7 @@ aufs-$(CONFIG_AUFS_HFSNOTIFY) += hfsnotify.o
 aufs-$(CONFIG_AUFS_EXPORT) += export.o
 aufs-$(CONFIG_AUFS_XATTR) += xattr.o
 aufs-$(CONFIG_FS_POSIX_ACL) += posix_acl.o
+aufs-$(CONFIG_AUFS_DIRREN) += dirren.o
 aufs-$(CONFIG_AUFS_FHSM) += fhsm.o
 aufs-$(CONFIG_AUFS_POLL) += poll.o
 aufs-$(CONFIG_AUFS_RDU) += rdu.o
diff --git a/fs/aufs/aufs.h b/fs/aufs/aufs.h
index e48d26804867..f725331e3a00 100644
--- a/fs/aufs/aufs.h
+++ b/fs/aufs/aufs.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 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
@@ -40,15 +40,16 @@
 #include "dbgaufs.h"
 #include "dentry.h"
 #include "dir.h"
+#include "dirren.h"
 #include "dynop.h"
 #include "file.h"
 #include "fstype.h"
+#include "hbl.h"
 #include "inode.h"
 #include "loop.h"
 #include "module.h"
 #include "opts.h"
 #include "rwsem.h"
-#include "spl.h"
 #include "super.h"
 #include "sysaufs.h"
 #include "vfsub.h"
diff --git a/fs/aufs/branch.c b/fs/aufs/branch.c
index 66495d2bf311..ac9c535a47aa 100644
--- a/fs/aufs/branch.c
+++ b/fs/aufs/branch.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -33,10 +33,14 @@ static void au_br_do_free(struct au_branch *br)
 	struct au_dykey **key;
 
 	au_hnotify_fin_br(br);
+	/* always, regardless the mount option */
+	au_dr_hino_free(&br->br_dirren);
 
 	if (br->br_xino.xi_file)
 		fput(br->br_xino.xi_file);
-	mutex_destroy(&br->br_xino.xi_nondir_mtx);
+	for (i = br->br_xino.xi_nondir.total - 1; i >= 0; i--)
+		AuDebugOn(br->br_xino.xi_nondir.array[i]);
+	kfree(br->br_xino.xi_nondir.array);
 
 	AuDebugOn(au_br_count(br));
 	au_br_count_fin(br);
@@ -51,7 +55,7 @@ static void au_br_do_free(struct au_branch *br)
 
 	if (br->br_fhsm) {
 		au_br_fhsm_fin(br->br_fhsm);
-		au_delayed_kfree(br->br_fhsm);
+		kfree(br->br_fhsm);
 	}
 
 	key = br->br_dykey;
@@ -65,9 +69,8 @@ static void au_br_do_free(struct au_branch *br)
 	lockdep_off();
 	path_put(&br->br_path);
 	lockdep_on();
-	if (wbr)
-		au_delayed_kfree(wbr);
-	au_delayed_kfree(br);
+	kfree(wbr);
+	kfree(br);
 }
 
 /*
@@ -131,14 +134,19 @@ static struct au_branch *au_br_alloc(struct super_block *sb, int new_nbranch,
 	int err;
 
 	err = -ENOMEM;
-	root = sb->s_root;
 	add_branch = kzalloc(sizeof(*add_branch), GFP_NOFS);
 	if (unlikely(!add_branch))
 		goto out;
+	add_branch->br_xino.xi_nondir.total = 8; /* initial size */
+	add_branch->br_xino.xi_nondir.array
+		= kcalloc(add_branch->br_xino.xi_nondir.total, sizeof(ino_t),
+			  GFP_NOFS);
+	if (unlikely(!add_branch->br_xino.xi_nondir.array))
+		goto out_br;
 
 	err = au_hnotify_init_br(add_branch, perm);
 	if (unlikely(err))
-		goto out_br;
+		goto out_xinondir;
 
 	if (au_br_writable(perm)) {
 		/* may be freed separately at changing the branch permission */
@@ -154,23 +162,26 @@ static struct au_branch *au_br_alloc(struct super_block *sb, int new_nbranch,
 			goto out_wbr;
 	}
 
+	root = sb->s_root;
 	err = au_sbr_realloc(au_sbi(sb), new_nbranch, /*may_shrink*/0);
 	if (!err)
 		err = au_di_realloc(au_di(root), new_nbranch, /*may_shrink*/0);
 	if (!err) {
 		inode = d_inode(root);
-		err = au_hinode_realloc(au_ii(inode), new_nbranch, /*may_shrink*/0);
+		err = au_hinode_realloc(au_ii(inode), new_nbranch,
+					/*may_shrink*/0);
 	}
 	if (!err)
 		return add_branch; /* success */
 
 out_wbr:
-	if (add_branch->br_wbr)
-		au_delayed_kfree(add_branch->br_wbr);
+	kfree(add_branch->br_wbr);
 out_hnotify:
 	au_hnotify_fin_br(add_branch);
+out_xinondir:
+	kfree(add_branch->br_xino.xi_nondir.array);
 out_br:
-	au_delayed_kfree(add_branch);
+	kfree(add_branch);
 out:
 	return ERR_PTR(err);
 }
@@ -336,7 +347,7 @@ static int au_br_init_wh(struct super_block *sb, struct au_branch *br,
 	br->br_perm = old_perm;
 
 	if (!err && wbr && !au_br_writable(new_perm)) {
-		au_delayed_kfree(wbr);
+		kfree(wbr);
 		br->br_wbr = NULL;
 	}
 
@@ -382,7 +393,8 @@ static int au_br_init(struct au_branch *br, struct super_block *sb,
 	struct inode *h_inode;
 
 	err = 0;
-	mutex_init(&br->br_xino.xi_nondir_mtx);
+	spin_lock_init(&br->br_xino.xi_nondir.spin);
+	init_waitqueue_head(&br->br_xino.xi_nondir.wqh);
 	br->br_perm = add->perm;
 	br->br_path = add->path; /* set first, path_get() later */
 	spin_lock_init(&br->br_dykey_lock);
@@ -391,6 +403,11 @@ static int au_br_init(struct au_branch *br, struct super_block *sb,
 	br->br_id = au_new_br_id(sb);
 	AuDebugOn(br->br_id < 0);
 
+	/* always, regardless the given option */
+	err = au_dr_br_init(sb, br, &add->path);
+	if (unlikely(err))
+		goto out_err;
+
 	if (au_br_writable(add->perm)) {
 		err = au_wbr_init(br, sb, add->perm);
 		if (unlikely(err))
@@ -557,14 +574,15 @@ static unsigned long long au_farray_cb(struct super_block *sb, void *a,
 {
 	unsigned long long n;
 	struct file **p, *f;
-	struct au_sphlhead *files;
+	struct hlist_bl_head *files;
+	struct hlist_bl_node *pos;
 	struct au_finfo *finfo;
 
 	n = 0;
 	p = a;
 	files = &au_sbi(sb)->si_files;
-	spin_lock(&files->spin);
-	hlist_for_each_entry(finfo, &files->head, fi_hlist) {
+	hlist_bl_lock(files);
+	hlist_bl_for_each_entry(finfo, pos, files, fi_hlist) {
 		f = finfo->fi_file;
 		if (file_count(f)
 		    && !special_file(file_inode(f)->i_mode)) {
@@ -574,7 +592,7 @@ static unsigned long long au_farray_cb(struct super_block *sb, void *a,
 			AuDebugOn(n > max);
 		}
 	}
-	spin_unlock(&files->spin);
+	hlist_bl_unlock(files);
 
 	return n;
 }
@@ -977,6 +995,9 @@ static void au_br_do_del(struct super_block *sb, aufs_bindex_t bindex,
 	au_br_do_del_hip(au_ii(inode), bindex, bbot);
 	au_sbilist_unlock();
 
+	/* ignore an error */
+	au_dr_br_fin(sb, br); /* always, regardless the mount option */
+
 	dput(h_root);
 	iput(h_inode);
 	au_br_do_free(br);
@@ -1362,7 +1383,7 @@ int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount,
 		if (br->br_wbr) {
 			err = au_wbr_init(br, sb, mod->perm);
 			if (unlikely(err)) {
-				au_delayed_kfree(br->br_wbr);
+				kfree(br->br_wbr);
 				br->br_wbr = NULL;
 			}
 		}
@@ -1374,7 +1395,7 @@ int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount,
 		if (!au_br_fhsm(mod->perm)) {
 			/* fhsm --> non-fhsm */
 			au_br_fhsm_fin(br->br_fhsm);
-			au_delayed_kfree(br->br_fhsm);
+			kfree(br->br_fhsm);
 			br->br_fhsm = NULL;
 		}
 	} else if (au_br_fhsm(mod->perm))
@@ -1386,8 +1407,7 @@ int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount,
 	goto out; /* success */
 
 out_bf:
-	if (bf)
-		au_delayed_kfree(bf);
+	kfree(bf);
 out:
 	AuTraceErr(err);
 	return err;
diff --git a/fs/aufs/branch.h b/fs/aufs/branch.h
index bbfa76b14cd0..e19cafb7d95f 100644
--- a/fs/aufs/branch.h
+++ b/fs/aufs/branch.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 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
@@ -25,6 +25,7 @@
 #ifdef __KERNEL__
 
 #include <linux/mount.h>
+#include "dirren.h"
 #include "dynop.h"
 #include "rwsem.h"
 #include "super.h"
@@ -34,7 +35,14 @@
 /* a xino file */
 struct au_xino_file {
 	struct file		*xi_file;
-	struct mutex		xi_nondir_mtx;
+	struct {
+		spinlock_t		spin;
+		ino_t			*array;
+		int			total;
+		/* reserved for future use */
+		/* unsigned long	*bitmap; */
+		wait_queue_head_t	wqh;
+	} xi_nondir;
 
 	/* todo: make xino files an array to support huge inode number */
 
@@ -115,6 +123,8 @@ struct au_branch {
 	/* entries under sysfs per mount-point */
 	struct au_brsysfs	br_sysfs[AuBrSysfs_Last];
 #endif
+
+	struct au_dr_br		br_dirren;
 };
 
 /* ---------------------------------------------------------------------- */
@@ -233,6 +243,11 @@ void au_xino_clr(struct super_block *sb);
 struct file *au_xino_def(struct super_block *sb);
 int au_xino_path(struct seq_file *seq, struct file *file);
 
+void au_xinondir_leave(struct super_block *sb, aufs_bindex_t bindex,
+		       ino_t h_ino, int idx);
+int au_xinondir_enter(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
+		      int *idx);
+
 /* ---------------------------------------------------------------------- */
 
 /* Superblock to branch */
diff --git a/fs/aufs/conf.mk b/fs/aufs/conf.mk
index 0bbb2d3a5285..bf3c3a4f1037 100644
--- a/fs/aufs/conf.mk
+++ b/fs/aufs/conf.mk
@@ -14,6 +14,7 @@ AuConfAll = BRANCH_MAX_127 BRANCH_MAX_511 BRANCH_MAX_1023 BRANCH_MAX_32767 \
 	XATTR \
 	FHSM \
 	RDU \
+	DIRREN \
 	SHWH \
 	BR_RAMFS \
 	BR_FUSE POLL \
diff --git a/fs/aufs/cpup.c b/fs/aufs/cpup.c
index 0a06bf256d67..1e0f46378cfa 100644
--- a/fs/aufs/cpup.c
+++ b/fs/aufs/cpup.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 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
@@ -357,9 +357,9 @@ int au_copy_file(struct file *dst, struct file *src, loff_t len)
 	dst->f_pos = 0;
 	err = au_do_copy_file(dst, src, len, buf, blksize);
 	if (do_kfree)
-		au_delayed_kfree(buf);
+		kfree(buf);
 	else
-		au_delayed_free_page((unsigned long)buf);
+		free_page((unsigned long)buf);
 
 out:
 	return err;
@@ -393,7 +393,8 @@ static int au_cp_regular(struct au_cp_generic *cpg)
 			.label = &&out_src
 		}
 	};
-	struct super_block *sb;
+	struct super_block *sb, *h_src_sb;
+	struct inode *h_src_inode;
 	struct task_struct *tsk = current;
 
 	/* bsrc branch can be ro/rw. */
@@ -409,8 +410,30 @@ static int au_cp_regular(struct au_cp_generic *cpg)
 	}
 
 	/* try stopping to update while we copyup */
-	IMustLock(d_inode(file[SRC].dentry));
-	err = au_copy_file(file[DST].file, file[SRC].file, cpg->len);
+	h_src_inode = d_inode(file[SRC].dentry);
+	h_src_sb = h_src_inode->i_sb;
+	if (!au_test_nfs(h_src_sb))
+		IMustLock(h_src_inode);
+
+	if (h_src_sb != file_inode(file[DST].file)->i_sb
+	    || !file[DST].file->f_op->clone_file_range)
+		err = au_copy_file(file[DST].file, file[SRC].file, cpg->len);
+	else {
+		if (!au_test_nfs(h_src_sb)) {
+			inode_unlock_shared(h_src_inode);
+			err = vfsub_clone_file_range(file[SRC].file,
+						     file[DST].file, cpg->len);
+			vfsub_inode_lock_shared_nested(h_src_inode,
+						       AuLsc_I_CHILD);
+		} else
+			err = vfsub_clone_file_range(file[SRC].file,
+						     file[DST].file, cpg->len);
+		if (unlikely(err == -EOPNOTSUPP && au_test_nfs(h_src_sb)))
+			/* the backend fs on NFS may not support cloning */
+			err = au_copy_file(file[DST].file, file[SRC].file,
+					   cpg->len);
+		AuTraceErr(err);
+	}
 
 	/* i wonder if we had O_NO_DELAY_FPUT flag */
 	if (tsk->flags & PF_KTHREAD)
@@ -451,7 +474,7 @@ static int au_do_cpup_regular(struct au_cp_generic *cpg,
 		cpg->len = l;
 	if (cpg->len) {
 		/* try stopping to update while we are referencing */
-		inode_lock_nested(h_src_inode, AuLsc_I_CHILD);
+		vfsub_inode_lock_shared_nested(h_src_inode, AuLsc_I_CHILD);
 		au_pin_hdir_unlock(cpg->pin);
 
 		h_path.dentry = au_h_dptr(cpg->dentry, cpg->bsrc);
@@ -460,17 +483,23 @@ static int au_do_cpup_regular(struct au_cp_generic *cpg,
 		if (!au_test_nfs(h_src_inode->i_sb))
 			err = vfs_getattr(&h_path, &h_src_attr->st);
 		else {
-			inode_unlock(h_src_inode);
+			inode_unlock_shared(h_src_inode);
 			err = vfs_getattr(&h_path, &h_src_attr->st);
-			inode_lock_nested(h_src_inode, AuLsc_I_CHILD);
+			vfsub_inode_lock_shared_nested(h_src_inode,
+						       AuLsc_I_CHILD);
 		}
 		if (unlikely(err)) {
-			inode_unlock(h_src_inode);
+			inode_unlock_shared(h_src_inode);
 			goto out;
 		}
 		h_src_attr->valid = 1;
-		err = au_cp_regular(cpg);
-		inode_unlock(h_src_inode);
+		if (!au_test_nfs(h_src_inode->i_sb)) {
+			err = au_cp_regular(cpg);
+			inode_unlock_shared(h_src_inode);
+		} else {
+			inode_unlock_shared(h_src_inode);
+			err = au_cp_regular(cpg);
+		}
 		rerr = au_pin_hdir_relock(cpg->pin);
 		if (!err && rerr)
 			err = rerr;
@@ -519,7 +548,7 @@ static int au_do_cpup_symlink(struct path *h_path, struct dentry *h_src,
 		sym.k[symlen] = 0;
 		err = vfsub_symlink(h_dir, h_path, sym.k);
 	}
-	au_delayed_free_page((unsigned long)sym.k);
+	free_page((unsigned long)sym.k);
 
 out:
 	return err;
@@ -711,7 +740,8 @@ static int au_do_ren_after_cpup(struct au_cp_generic *cpg, struct path *h_path)
 	IMustLock(h_dir);
 	AuDbg("%pd %pd\n", h_dentry, h_path->dentry);
 	/* no delegation since it is just created */
-	err = vfsub_rename(h_dir, h_dentry, h_dir, h_path, /*delegated*/NULL);
+	err = vfsub_rename(h_dir, h_dentry, h_dir, h_path, /*delegated*/NULL,
+			   /*flags*/0);
 	dput(h_path->dentry);
 
 out:
@@ -724,6 +754,8 @@ static int au_do_ren_after_cpup(struct au_cp_generic *cpg, struct path *h_path)
  * @len is for truncating when it is -1 copyup the entire file.
  * in link/rename cases, @dst_parent may be different from the real one.
  * basic->bsrc can be larger than basic->bdst.
+ * aufs doesn't touch the credential so
+ * security_inode_copy_up{,_xattr}() are unnecrssary.
  */
 static int au_cpup_single(struct au_cp_generic *cpg, struct dentry *dst_parent)
 {
@@ -890,7 +922,7 @@ static int au_cpup_single(struct au_cp_generic *cpg, struct dentry *dst_parent)
 	}
 out_parent:
 	dput(dst_parent);
-	au_delayed_kfree(a);
+	kfree(a);
 out:
 	return err;
 }
diff --git a/fs/aufs/cpup.h b/fs/aufs/cpup.h
index 7721429fa8ef..894e076a7c17 100644
--- a/fs/aufs/cpup.h
+++ b/fs/aufs/cpup.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 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
@@ -59,6 +59,11 @@ struct au_cp_generic {
 #define AuCpup_RWDST		(1 << 5)	/* force write target even if
 						   the branch is marked as RO */
 
+#ifndef CONFIG_AUFS_BR_HFSPLUS
+#undef AuCpup_HOPEN
+#define AuCpup_HOPEN		0
+#endif
+
 #define au_ftest_cpup(flags, name)	((flags) & AuCpup_##name)
 #define au_fset_cpup(flags, name) \
 	do { (flags) |= AuCpup_##name; } while (0)
diff --git a/fs/aufs/dbgaufs.c b/fs/aufs/dbgaufs.c
index 4ab45c00750f..e577f0fb7a3e 100644
--- a/fs/aufs/dbgaufs.c
+++ b/fs/aufs/dbgaufs.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 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
@@ -41,7 +41,7 @@ struct dbgaufs_arg {
 static int dbgaufs_xi_release(struct inode *inode __maybe_unused,
 			      struct file *file)
 {
-	au_delayed_kfree(file->private_data);
+	kfree(file->private_data);
 	return 0;
 }
 
@@ -103,7 +103,7 @@ struct dbgaufs_plink_arg {
 static int dbgaufs_plink_release(struct inode *inode __maybe_unused,
 				 struct file *file)
 {
-	au_delayed_free_page((unsigned long)file->private_data);
+	free_page((unsigned long)file->private_data);
 	return 0;
 }
 
@@ -114,7 +114,7 @@ static int dbgaufs_plink_open(struct inode *inode, struct file *file)
 	struct dbgaufs_plink_arg *p;
 	struct au_sbinfo *sbinfo;
 	struct super_block *sb;
-	struct au_sphlhead *sphl;
+	struct hlist_bl_head *hbl;
 
 	err = -ENOMEM;
 	p = (void *)get_zeroed_page(GFP_NOFS);
@@ -134,10 +134,9 @@ static int dbgaufs_plink_open(struct inode *inode, struct file *file)
 		limit -= n;
 
 		sum = 0;
-		for (i = 0, sphl = sbinfo->si_plink;
-		     i < AuPlink_NHASH;
-		     i++, sphl++) {
-			n = au_sphl_count(sphl);
+		for (i = 0, hbl = sbinfo->si_plink; i < AuPlink_NHASH;
+		     i++, hbl++) {
+			n = au_hbl_count(hbl);
 			sum += n;
 
 			n = snprintf(p->a + p->n, limit, "%lu ", n);
@@ -167,7 +166,7 @@ static int dbgaufs_plink_open(struct inode *inode, struct file *file)
 	goto out; /* success */
 
 out_free:
-	au_delayed_free_page((unsigned long)p);
+	free_page((unsigned long)p);
 out:
 	return err;
 }
diff --git a/fs/aufs/dbgaufs.h b/fs/aufs/dbgaufs.h
index d1e09bd4729e..d0c01c89c878 100644
--- a/fs/aufs/dbgaufs.h
+++ b/fs/aufs/dbgaufs.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 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
diff --git a/fs/aufs/dcsub.c b/fs/aufs/dcsub.c
index 583ba750b105..0e02131bd6f3 100644
--- a/fs/aufs/dcsub.c
+++ b/fs/aufs/dcsub.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -29,7 +29,7 @@ static void au_dpage_free(struct au_dpage *dpage)
 	p = dpage->dentries;
 	for (i = 0; i < dpage->ndentry; i++)
 		dput(*p++);
-	au_delayed_free_page((unsigned long)dpage->dentries);
+	free_page((unsigned long)dpage->dentries);
 }
 
 int au_dpages_init(struct au_dcsub_pages *dpages, gfp_t gfp)
@@ -52,7 +52,7 @@ int au_dpages_init(struct au_dcsub_pages *dpages, gfp_t gfp)
 	return 0; /* success */
 
 out_dpages:
-	au_delayed_kfree(dpages->dpages);
+	kfree(dpages->dpages);
 out:
 	return err;
 }
@@ -65,7 +65,7 @@ void au_dpages_free(struct au_dcsub_pages *dpages)
 	p = dpages->dpages;
 	for (i = 0; i < dpages->ndpage; i++)
 		au_dpage_free(p++);
-	au_delayed_kfree(dpages->dpages);
+	kfree(dpages->dpages);
 }
 
 static int au_dpages_append(struct au_dcsub_pages *dpages,
diff --git a/fs/aufs/dcsub.h b/fs/aufs/dcsub.h
index 9f4a2b578f74..92d6f91107d1 100644
--- a/fs/aufs/dcsub.h
+++ b/fs/aufs/dcsub.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 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
diff --git a/fs/aufs/debug.c b/fs/aufs/debug.c
index 47e1c06c9264..12cc993af390 100644
--- a/fs/aufs/debug.c
+++ b/fs/aufs/debug.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 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
@@ -336,7 +336,7 @@ void au_dpri_sb(struct super_block *sb)
 	au_br_count_init(&a->fake);
 	err = do_pri_br(-1, &a->fake);
 	au_br_count_fin(&a->fake);
-	au_delayed_kfree(a);
+	kfree(a);
 	dpri("dev 0x%x\n", sb->s_dev);
 	if (err || !au_test_aufs(sb))
 		return;
diff --git a/fs/aufs/debug.h b/fs/aufs/debug.h
index cd5fc3f987f7..270628d747bb 100644
--- a/fs/aufs/debug.h
+++ b/fs/aufs/debug.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 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
diff --git a/fs/aufs/dentry.c b/fs/aufs/dentry.c
index 0b6a3d42c4df..230db04b1aee 100644
--- a/fs/aufs/dentry.c
+++ b/fs/aufs/dentry.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 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
@@ -22,19 +22,13 @@
 #include <linux/namei.h>
 #include "aufs.h"
 
-struct au_do_lookup_args {
-	unsigned int		flags;
-	mode_t			type;
-};
-
 /*
  * returns positive/negative dentry, NULL or an error.
  * NULL means whiteout-ed or not-found.
  */
 static struct dentry*
 au_do_lookup(struct dentry *h_parent, struct dentry *dentry,
-	     aufs_bindex_t bindex, struct qstr *wh_name,
-	     struct au_do_lookup_args *args)
+	     aufs_bindex_t bindex, struct au_do_lookup_args *args)
 {
 	struct dentry *h_dentry;
 	struct inode *h_inode;
@@ -49,7 +43,7 @@ au_do_lookup(struct dentry *h_parent, struct dentry *dentry,
 	br = au_sbr(dentry->d_sb, bindex);
 	wh_able = !!au_br_whable(br->br_perm);
 	if (wh_able)
-		wh_found = au_wh_test(h_parent, wh_name, ignore_perm);
+		wh_found = au_wh_test(h_parent, &args->whname, ignore_perm);
 	h_dentry = ERR_PTR(wh_found);
 	if (!wh_found)
 		goto real_lookup;
@@ -64,9 +58,9 @@ au_do_lookup(struct dentry *h_parent, struct dentry *dentry,
 
 real_lookup:
 	if (!ignore_perm)
-		h_dentry = vfsub_lkup_one(&dentry->d_name, h_parent);
+		h_dentry = vfsub_lkup_one(args->name, h_parent);
 	else
-		h_dentry = au_sio_lkup_one(&dentry->d_name, h_parent);
+		h_dentry = au_sio_lkup_one(args->name, h_parent);
 	if (IS_ERR(h_dentry)) {
 		if (PTR_ERR(h_dentry) == -ENAMETOOLONG
 		    && !allow_neg)
@@ -81,6 +75,13 @@ au_do_lookup(struct dentry *h_parent, struct dentry *dentry,
 	} else if (wh_found
 		   || (args->type && args->type != (h_inode->i_mode & S_IFMT)))
 		goto out_neg;
+	else if (au_ftest_lkup(args->flags, DIRREN)
+		 /* && h_inode */
+		 && !au_dr_lkup_h_ino(args, bindex, h_inode->i_ino)) {
+		AuDbg("b%d %pd ignored hi%llu\n", bindex, h_dentry,
+		      (unsigned long long)h_inode->i_ino);
+		goto out_neg;
+	}
 
 	if (au_dbbot(dentry) <= bindex)
 		au_set_dbbot(dentry, bindex);
@@ -93,9 +94,9 @@ au_do_lookup(struct dentry *h_parent, struct dentry *dentry,
 	    || (d_really_is_positive(dentry) && !d_is_dir(dentry)))
 		goto out; /* success */
 
-	inode_lock_nested(h_inode, AuLsc_I_CHILD);
+	vfsub_inode_lock_shared_nested(h_inode, AuLsc_I_CHILD);
 	opq = au_diropq_test(h_dentry);
-	inode_unlock(h_inode);
+	inode_unlock_shared(h_inode);
 	if (opq > 0)
 		au_set_dbdiropq(dentry, bindex);
 	else if (unlikely(opq < 0)) {
@@ -129,26 +130,28 @@ int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t btop,
 {
 	int npositive, err;
 	aufs_bindex_t bindex, btail, bdiropq;
-	unsigned char isdir, dirperm1;
-	struct qstr whname;
+	unsigned char isdir, dirperm1, dirren;
 	struct au_do_lookup_args args = {
-		.flags		= flags
+		.flags		= flags,
+		.name		= &dentry->d_name
 	};
-	const struct qstr *name = &dentry->d_name;
 	struct dentry *parent;
 	struct super_block *sb;
 
 	sb = dentry->d_sb;
-	err = au_test_shwh(sb, name);
+	err = au_test_shwh(sb, args.name);
 	if (unlikely(err))
 		goto out;
 
-	err = au_wh_name_alloc(&whname, name);
+	err = au_wh_name_alloc(&args.whname, args.name);
 	if (unlikely(err))
 		goto out;
 
 	isdir = !!d_is_dir(dentry);
 	dirperm1 = !!au_opt_test(au_mntflags(sb), DIRPERM1);
+	dirren = !!au_opt_test(au_mntflags(sb), DIRREN);
+	if (dirren)
+		au_fset_lkup(args.flags, DIRREN);
 
 	npositive = 0;
 	parent = dget_parent(dentry);
@@ -156,6 +159,7 @@ int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t btop,
 	for (bindex = btop; bindex <= btail; bindex++) {
 		struct dentry *h_parent, *h_dentry;
 		struct inode *h_inode, *h_dir;
+		struct au_branch *br;
 
 		h_dentry = au_h_dptr(dentry, bindex);
 		if (h_dentry) {
@@ -167,11 +171,17 @@ int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t btop,
 		if (!h_parent || !d_is_dir(h_parent))
 			continue;
 
+		if (dirren) {
+			/* if the inum matches, then use the prepared name */
+			err = au_dr_lkup_name(&args, bindex);
+			if (unlikely(err))
+				goto out_parent;
+		}
+
 		h_dir = d_inode(h_parent);
-		inode_lock_nested(h_dir, AuLsc_I_PARENT);
-		h_dentry = au_do_lookup(h_parent, dentry, bindex, &whname,
-					&args);
-		inode_unlock(h_dir);
+		vfsub_inode_lock_shared_nested(h_dir, AuLsc_I_PARENT);
+		h_dentry = au_do_lookup(h_parent, dentry, bindex, &args);
+		inode_unlock_shared(h_dir);
 		err = PTR_ERR(h_dentry);
 		if (IS_ERR(h_dentry))
 			goto out_parent;
@@ -198,6 +208,15 @@ int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t btop,
 			if (bdiropq >= 0 && bdiropq <= bindex)
 				break;
 		}
+		br = au_sbr(sb, bindex);
+		if (dirren
+		    && au_dr_hino_test_add(&br->br_dirren, h_inode->i_ino,
+					   /*add_ent*/NULL)) {
+			/* prepare next name to lookup */
+			err = au_dr_lkup(&args, dentry, bindex);
+			if (unlikely(err))
+				goto out_parent;
+		}
 	}
 
 	if (npositive) {
@@ -214,7 +233,9 @@ int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t btop,
 
 out_parent:
 	dput(parent);
-	au_delayed_kfree(whname.name);
+	kfree(args.whname.name);
+	if (dirren)
+		au_dr_lkup_fin(&args);
 out:
 	return err;
 }
@@ -823,7 +844,7 @@ static int au_do_h_d_reval(struct dentry *h_dentry, unsigned int flags,
 
 /* todo: remove this */
 static int h_d_revalidate(struct dentry *dentry, struct inode *inode,
-			  unsigned int flags, int do_udba)
+			  unsigned int flags, int do_udba, int dirren)
 {
 	int err;
 	umode_t mode, h_mode;
@@ -874,7 +895,7 @@ static int h_d_revalidate(struct dentry *dentry, struct inode *inode,
 			     && !is_root
 			     && ((!h_nfs
 				  && (unhashed != !!d_unhashed(h_dentry)
-				      || (!tmpfile
+				      || (!tmpfile && !dirren
 					  && !au_qstreq(name, h_name))
 					  ))
 				 || (h_nfs
@@ -1015,7 +1036,7 @@ static int aufs_d_revalidate(struct dentry *dentry, unsigned int flags)
 {
 	int valid, err;
 	unsigned int sigen;
-	unsigned char do_udba;
+	unsigned char do_udba, dirren;
 	struct super_block *sb;
 	struct inode *inode;
 
@@ -1088,7 +1109,8 @@ static int aufs_d_revalidate(struct dentry *dentry, unsigned int flags)
 		}
 	}
 
-	err = h_d_revalidate(dentry, inode, flags, do_udba);
+	dirren = !!au_opt_test(au_mntflags(sb), DIRREN);
+	err = h_d_revalidate(dentry, inode, flags, do_udba, dirren);
 	if (unlikely(!err && do_udba && au_dbtop(dentry) < 0)) {
 		err = -EIO;
 		AuDbg("both of real entry and whiteout found, %p, err %d\n",
diff --git a/fs/aufs/dentry.h b/fs/aufs/dentry.h
index 9ea6eddf91fa..ea45862d6709 100644
--- a/fs/aufs/dentry.h
+++ b/fs/aufs/dentry.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 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
@@ -25,6 +25,7 @@
 #ifdef __KERNEL__
 
 #include <linux/dcache.h>
+#include "dirren.h"
 #include "rwsem.h"
 
 struct au_hdentry {
@@ -38,10 +39,7 @@ struct au_dinfo {
 	struct au_rwsem		di_rwsem;
 	aufs_bindex_t		di_btop, di_bbot, di_bwh, di_bdiropq;
 	unsigned char		di_tmpfile; /* to allow the different name */
-	union {
-		struct au_hdentry	*di_hdentry;
-		struct llist_node	di_lnode;	/* delayed free */
-	};
+	struct au_hdentry	*di_hdentry;
 } ____cacheline_aligned_in_smp;
 
 /* ---------------------------------------------------------------------- */
@@ -49,12 +47,25 @@ struct au_dinfo {
 /* flags for au_lkup_dentry() */
 #define AuLkup_ALLOW_NEG	1
 #define AuLkup_IGNORE_PERM	(1 << 1)
+#define AuLkup_DIRREN		(1 << 2)
 #define au_ftest_lkup(flags, name)	((flags) & AuLkup_##name)
 #define au_fset_lkup(flags, name) \
 	do { (flags) |= AuLkup_##name; } while (0)
 #define au_fclr_lkup(flags, name) \
 	do { (flags) &= ~AuLkup_##name; } while (0)
 
+#ifndef CONFIG_AUFS_DIRREN
+#undef AuLkup_DIRREN
+#define AuLkup_DIRREN 0
+#endif
+
+struct au_do_lookup_args {
+	unsigned int		flags;
+	mode_t			type;
+	struct qstr		whname, *name;
+	struct au_dr_lookup	dirren;
+};
+
 /* ---------------------------------------------------------------------- */
 
 /* dentry.c */
diff --git a/fs/aufs/dinfo.c b/fs/aufs/dinfo.c
index 3bc31e277eeb..8b19f94c5654 100644
--- a/fs/aufs/dinfo.c
+++ b/fs/aufs/dinfo.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 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
@@ -53,7 +53,7 @@ struct au_dinfo *au_di_alloc(struct super_block *sb, unsigned int lsc)
 		goto out;
 	}
 
-	au_cache_dfree_dinfo(dinfo);
+	au_cache_free_dinfo(dinfo);
 	dinfo = NULL;
 
 out:
@@ -73,8 +73,8 @@ void au_di_free(struct au_dinfo *dinfo)
 		while (bindex++ <= bbot)
 			au_hdput(p++);
 	}
-	au_delayed_kfree(dinfo->di_hdentry);
-	au_cache_dfree_dinfo(dinfo);
+	kfree(dinfo->di_hdentry);
+	au_cache_free_dinfo(dinfo);
 }
 
 void au_di_swap(struct au_dinfo *a, struct au_dinfo *b)
@@ -276,11 +276,11 @@ void di_write_lock2_child(struct dentry *d1, struct dentry *d2, int isdir)
 		  || d_inode(d1) == d_inode(d2)
 		  || d1->d_sb != d2->d_sb);
 
-	if (isdir && au_test_subdir(d1, d2)) {
+	if ((isdir && au_test_subdir(d1, d2))
+	    || d1 < d2) {
 		di_write_lock_child(d1);
 		di_write_lock_child2(d2);
 	} else {
-		/* there should be no races */
 		di_write_lock_child(d2);
 		di_write_lock_child2(d1);
 	}
@@ -292,11 +292,11 @@ void di_write_lock2_parent(struct dentry *d1, struct dentry *d2, int isdir)
 		  || d_inode(d1) == d_inode(d2)
 		  || d1->d_sb != d2->d_sb);
 
-	if (isdir && au_test_subdir(d1, d2)) {
+	if ((isdir && au_test_subdir(d1, d2))
+	    || d1 < d2) {
 		di_write_lock_parent(d1);
 		di_write_lock_parent2(d2);
 	} else {
-		/* there should be no races */
 		di_write_lock_parent(d2);
 		di_write_lock_parent2(d1);
 	}
diff --git a/fs/aufs/dir.c b/fs/aufs/dir.c
index bd6c868059cf..8dffb0098dab 100644
--- a/fs/aufs/dir.c
+++ b/fs/aufs/dir.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -156,7 +156,7 @@ static void au_do_dir_ts(void *arg)
 out:
 	dput(a->dentry);
 	au_nwt_done(&au_sbi(sb)->si_nowait);
-	au_delayed_kfree(arg);
+	kfree(arg);
 }
 
 void au_dir_ts(struct inode *dir, aufs_bindex_t bindex)
@@ -192,7 +192,7 @@ void au_dir_ts(struct inode *dir, aufs_bindex_t bindex)
 	if (unlikely(wkq_err)) {
 		pr_err("wkq %d\n", wkq_err);
 		dput(dentry);
-		au_delayed_kfree(arg);
+		kfree(arg);
 	}
 
 out:
@@ -311,7 +311,7 @@ static int aufs_open_dir(struct inode *inode __maybe_unused,
 		};
 		err = au_do_open(file, &args);
 		if (unlikely(err))
-			au_delayed_kfree(fidir);
+			kfree(fidir);
 	}
 	si_read_unlock(sb);
 	return err;
@@ -325,21 +325,18 @@ static int aufs_release_dir(struct inode *inode __maybe_unused,
 	struct au_fidir *fidir;
 	struct au_hfile *hf;
 	aufs_bindex_t bindex, bbot;
-	int execed, delayed;
 
-	delayed = (current->flags & PF_KTHREAD) || in_interrupt();
 	finfo = au_fi(file);
 	fidir = finfo->fi_hdir;
 	if (fidir) {
-		au_sphl_del(&finfo->fi_hlist,
-			    &au_sbi(file->f_path.dentry->d_sb)->si_files);
+		au_hbl_del(&finfo->fi_hlist,
+			   &au_sbi(file->f_path.dentry->d_sb)->si_files);
 		vdir_cache = fidir->fd_vdir_cache; /* lock-free */
 		if (vdir_cache)
-			au_vdir_free(vdir_cache, delayed);
+			au_vdir_free(vdir_cache);
 
 		bindex = finfo->fi_btop;
 		if (bindex >= 0) {
-			execed = vfsub_file_execed(file);
 			hf = fidir->fd_hfile + bindex;
 			/*
 			 * calls fput() instead of filp_close(),
@@ -348,12 +345,12 @@ static int aufs_release_dir(struct inode *inode __maybe_unused,
 			bbot = fidir->fd_bbot;
 			for (; bindex <= bbot; bindex++, hf++)
 				if (hf->hf_file)
-					au_hfput(hf, execed);
+					au_hfput(hf, /*execed*/0);
 		}
-		au_delayed_kfree(fidir);
+		kfree(fidir);
 		finfo->fi_hdir = NULL;
 	}
-	au_finfo_fin(file, delayed);
+	au_finfo_fin(file);
 	return 0;
 }
 
@@ -418,7 +415,7 @@ static int au_do_fsync_dir(struct file *file, int datasync)
 	struct super_block *sb;
 	struct inode *inode;
 
-	err = au_reval_and_lock_fdi(file, reopen_dir, /*wlock*/1);
+	err = au_reval_and_lock_fdi(file, reopen_dir, /*wlock*/1, /*fi_lsc*/0);
 	if (unlikely(err))
 		goto out;
 
@@ -487,7 +484,7 @@ static int aufs_iterate_shared(struct file *file, struct dir_context *ctx)
 
 	sb = dentry->d_sb;
 	si_read_lock(sb, AuLock_FLUSH);
-	err = au_reval_and_lock_fdi(file, reopen_dir, /*wlock*/1);
+	err = au_reval_and_lock_fdi(file, reopen_dir, /*wlock*/1, /*fi_lsc*/0);
 	if (unlikely(err))
 		goto out;
 	err = au_alive_dir(dentry);
@@ -640,9 +637,9 @@ static int sio_test_empty(struct dentry *dentry, struct test_empty_arg *arg)
 	h_dentry = au_h_dptr(dentry, arg->bindex);
 	h_inode = d_inode(h_dentry);
 	/* todo: i_mode changes anytime? */
-	inode_lock_nested(h_inode, AuLsc_I_CHILD);
+	vfsub_inode_lock_shared_nested(h_inode, AuLsc_I_CHILD);
 	err = au_test_h_perm_sio(h_inode, MAY_EXEC | MAY_READ);
-	inode_unlock(h_inode);
+	inode_unlock_shared(h_inode);
 	if (!err)
 		err = do_test_empty(dentry, arg);
 	else {
diff --git a/fs/aufs/dir.h b/fs/aufs/dir.h
index 8a145f1d9fb3..b107309a3d60 100644
--- a/fs/aufs/dir.h
+++ b/fs/aufs/dir.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 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
@@ -42,10 +42,7 @@ struct au_vdir_destr {
 
 struct au_vdir_dehstr {
 	struct hlist_node	hash;
-	union {
-		struct au_vdir_destr	*str;
-		struct llist_node	lnode;	/* delayed free */
-	};
+	struct au_vdir_destr	*str;
 } ____cacheline_aligned_in_smp;
 
 struct au_vdir_de {
@@ -83,10 +80,7 @@ struct au_vdir {
 
 	unsigned long	vd_version;
 	unsigned int	vd_deblk_sz;
-	union {
-		unsigned long		vd_jiffy;
-		struct llist_node	vd_lnode;	/* delayed free */
-	};
+	unsigned long		vd_jiffy;
 } ____cacheline_aligned_in_smp;
 
 /* ---------------------------------------------------------------------- */
@@ -110,7 +104,7 @@ int au_nhash_test_known_wh(struct au_nhash *whlist, char *name, int nlen);
 int au_nhash_append_wh(struct au_nhash *whlist, char *name, int nlen, ino_t ino,
 		       unsigned int d_type, aufs_bindex_t bindex,
 		       unsigned char shwh);
-void au_vdir_free(struct au_vdir *vdir, int atonce);
+void au_vdir_free(struct au_vdir *vdir);
 int au_vdir_init(struct file *file);
 int au_vdir_fill_de(struct file *file, struct dir_context *ctx);
 
diff --git a/fs/aufs/dirren.c b/fs/aufs/dirren.c
new file mode 100644
index 000000000000..a92ef4ba58b5
--- /dev/null
+++ b/fs/aufs/dirren.c
@@ -0,0 +1,1314 @@
+/*
+ * Copyright (C) 2017 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, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * special handling in renaming a directoy
+ * in order to support looking-up the before-renamed name on the lower readonly
+ * branches
+ */
+
+#include <linux/byteorder/generic.h>
+#include "aufs.h"
+
+static void au_dr_hino_del(struct au_dr_br *dr, struct au_dr_hino *ent)
+{
+	int idx;
+
+	idx = au_dr_ihash(ent->dr_h_ino);
+	au_hbl_del(&ent->dr_hnode, dr->dr_h_ino + idx);
+}
+
+static int au_dr_hino_test_empty(struct au_dr_br *dr)
+{
+	int ret, i;
+	struct hlist_bl_head *hbl;
+
+	ret = 1;
+	for (i = 0; ret && i < AuDirren_NHASH; i++) {
+		hbl = dr->dr_h_ino + i;
+		hlist_bl_lock(hbl);
+		ret &= hlist_bl_empty(hbl);
+		hlist_bl_unlock(hbl);
+	}
+
+	return ret;
+}
+
+static struct au_dr_hino *au_dr_hino_find(struct au_dr_br *dr, ino_t ino)
+{
+	struct au_dr_hino *found, *ent;
+	struct hlist_bl_head *hbl;
+	struct hlist_bl_node *pos;
+	int idx;
+
+	found = NULL;
+	idx = au_dr_ihash(ino);
+	hbl = dr->dr_h_ino + idx;
+	hlist_bl_lock(hbl);
+	hlist_bl_for_each_entry(ent, pos, hbl, dr_hnode)
+		if (ent->dr_h_ino == ino) {
+			found = ent;
+			break;
+		}
+	hlist_bl_unlock(hbl);
+
+	return found;
+}
+
+int au_dr_hino_test_add(struct au_dr_br *dr, ino_t ino,
+			struct au_dr_hino *add_ent)
+{
+	int found, idx;
+	struct hlist_bl_head *hbl;
+	struct hlist_bl_node *pos;
+	struct au_dr_hino *ent;
+
+	found = 0;
+	idx = au_dr_ihash(ino);
+	hbl = dr->dr_h_ino + idx;
+#if 0
+	{
+		struct hlist_bl_node *tmp;
+
+		hlist_bl_for_each_entry_safe(ent, pos, tmp, hbl, dr_hnode)
+			AuDbg("hi%llu\n", (unsigned long long)ent->dr_h_ino);
+	}
+#endif
+	hlist_bl_lock(hbl);
+	hlist_bl_for_each_entry(ent, pos, hbl, dr_hnode)
+		if (ent->dr_h_ino == ino) {
+			found = 1;
+			break;
+		}
+	if (!found && add_ent)
+		hlist_bl_add_head(&add_ent->dr_hnode, hbl);
+	hlist_bl_unlock(hbl);
+
+	if (!found && add_ent)
+		AuDbg("i%llu added\n", (unsigned long long)add_ent->dr_h_ino);
+
+	return found;
+}
+
+void au_dr_hino_free(struct au_dr_br *dr)
+{
+	int i;
+	struct hlist_bl_head *hbl;
+	struct hlist_bl_node *pos, *tmp;
+	struct au_dr_hino *ent;
+
+	/* SiMustWriteLock(sb); */
+
+	for (i = 0; i < AuDirren_NHASH; i++) {
+		hbl = dr->dr_h_ino + i;
+		/* no spinlock since sbinfo must be write-locked */
+		hlist_bl_for_each_entry_safe(ent, pos, tmp, hbl, dr_hnode)
+			kfree(ent);
+		INIT_HLIST_BL_HEAD(hbl);
+	}
+}
+
+/* returns the number of inodes or an error */
+static int au_dr_hino_store(struct super_block *sb, struct au_branch *br,
+			    struct file *hinofile)
+{
+	int err, i;
+	ssize_t ssz;
+	loff_t pos, oldsize;
+	uint64_t u64;
+	struct inode *hinoinode;
+	struct hlist_bl_head *hbl;
+	struct hlist_bl_node *n1, *n2;
+	struct au_dr_hino *ent;
+
+	SiMustWriteLock(sb);
+	AuDebugOn(!au_br_writable(br->br_perm));
+
+	hinoinode = file_inode(hinofile);
+	oldsize = i_size_read(hinoinode);
+
+	err = 0;
+	pos = 0;
+	hbl = br->br_dirren.dr_h_ino;
+	for (i = 0; !err && i < AuDirren_NHASH; i++, hbl++) {
+		/* no bit-lock since sbinfo must be write-locked */
+		hlist_bl_for_each_entry_safe(ent, n1, n2, hbl, dr_hnode) {
+			AuDbg("hi%llu, %pD2\n",
+			      (unsigned long long)ent->dr_h_ino, hinofile);
+			u64 = cpu_to_be64(ent->dr_h_ino);
+			ssz = vfsub_write_k(hinofile, &u64, sizeof(u64), &pos);
+			if (ssz == sizeof(u64))
+				continue;
+
+			/* write error */
+			pr_err("ssz %zd, %pD2\n", ssz, hinofile);
+			err = -ENOSPC;
+			if (ssz < 0)
+				err = ssz;
+			break;
+		}
+	}
+	/* regardless the error */
+	if (pos < oldsize) {
+		err = vfsub_trunc(&hinofile->f_path, pos, /*attr*/0, hinofile);
+		AuTraceErr(err);
+	}
+
+	AuTraceErr(err);
+	return err;
+}
+
+static int au_dr_hino_load(struct au_dr_br *dr, struct file *hinofile)
+{
+	int err, hidx;
+	ssize_t ssz;
+	size_t sz, n;
+	loff_t pos;
+	uint64_t u64;
+	struct au_dr_hino *ent;
+	struct inode *hinoinode;
+	struct hlist_bl_head *hbl;
+
+	err = 0;
+	pos = 0;
+	hbl = dr->dr_h_ino;
+	hinoinode = file_inode(hinofile);
+	sz = i_size_read(hinoinode);
+	AuDebugOn(sz % sizeof(u64));
+	n = sz / sizeof(u64);
+	while (n--) {
+		ssz = vfsub_read_k(hinofile, &u64, sizeof(u64), &pos);
+		if (unlikely(ssz != sizeof(u64))) {
+			pr_err("ssz %zd, %pD2\n", ssz, hinofile);
+			err = -EINVAL;
+			if (ssz < 0)
+				err = ssz;
+			goto out_free;
+		}
+
+		ent = kmalloc(sizeof(*ent), GFP_NOFS);
+		if (!ent) {
+			err = -ENOMEM;
+			AuTraceErr(err);
+			goto out_free;
+		}
+		ent->dr_h_ino = be64_to_cpu(u64);
+		AuDbg("hi%llu, %pD2\n",
+		      (unsigned long long)ent->dr_h_ino, hinofile);
+		hidx = au_dr_ihash(ent->dr_h_ino);
+		au_hbl_add(&ent->dr_hnode, hbl + hidx);
+	}
+	goto out; /* success */
+
+out_free:
+	au_dr_hino_free(dr);
+out:
+	AuTraceErr(err);
+	return err;
+}
+
+/*
+ * @bindex/@br is a switch to distinguish whether suspending hnotify or not.
+ * @path is a switch to distinguish load and store.
+ */
+static int au_dr_hino(struct super_block *sb, aufs_bindex_t bindex,
+		      struct au_branch *br, const struct path *path)
+{
+	int err, flags;
+	unsigned char load, suspend;
+	struct file *hinofile;
+	struct au_hinode *hdir;
+	struct inode *dir, *delegated;
+	struct path hinopath;
+	struct qstr hinoname = QSTR_INIT(AUFS_WH_DR_BRHINO,
+					 sizeof(AUFS_WH_DR_BRHINO) - 1);
+
+	AuDebugOn(bindex < 0 && !br);
+	AuDebugOn(bindex >= 0 && br);
+
+	err = -EINVAL;
+	suspend = !br;
+	if (suspend)
+		br = au_sbr(sb, bindex);
+	load = !!path;
+	if (!load) {
+		path = &br->br_path;
+		AuDebugOn(!au_br_writable(br->br_perm));
+		if (unlikely(!au_br_writable(br->br_perm)))
+			goto out;
+	}
+
+	hdir = NULL;
+	if (suspend) {
+		dir = d_inode(sb->s_root);
+		hdir = au_hinode(au_ii(dir), bindex);
+		dir = hdir->hi_inode;
+		au_hn_inode_lock_nested(hdir, AuLsc_I_CHILD);
+	} else {
+		dir = d_inode(path->dentry);
+		inode_lock_nested(dir, AuLsc_I_CHILD);
+	}
+	hinopath.dentry = vfsub_lkup_one(&hinoname, path->dentry);
+	err = PTR_ERR(hinopath.dentry);
+	if (IS_ERR(hinopath.dentry))
+		goto out_unlock;
+
+	err = 0;
+	flags = O_RDONLY;
+	if (load) {
+		if (d_is_negative(hinopath.dentry))
+			goto out_dput; /* success */
+	} else {
+		if (au_dr_hino_test_empty(&br->br_dirren)) {
+			if (d_is_positive(hinopath.dentry)) {
+				delegated = NULL;
+				err = vfsub_unlink(dir, &hinopath, &delegated,
+						   /*force*/0);
+				AuTraceErr(err);
+				if (unlikely(err))
+					pr_err("ignored err %d, %pd2\n",
+					       err, hinopath.dentry);
+				if (unlikely(err == -EWOULDBLOCK))
+					iput(delegated);
+				err = 0;
+			}
+			goto out_dput;
+		} else if (!d_is_positive(hinopath.dentry)) {
+			err = vfsub_create(dir, &hinopath, 0600,
+					   /*want_excl*/false);
+			AuTraceErr(err);
+			if (unlikely(err))
+				goto out_dput;
+		}
+		flags = O_WRONLY;
+	}
+	hinopath.mnt = path->mnt;
+	hinofile = vfsub_dentry_open(&hinopath, flags);
+	if (suspend)
+		au_hn_inode_unlock(hdir);
+	else
+		inode_unlock(dir);
+	dput(hinopath.dentry);
+	AuTraceErrPtr(hinofile);
+	if (IS_ERR(hinofile)) {
+		err = PTR_ERR(hinofile);
+		goto out;
+	}
+
+	if (load)
+		err = au_dr_hino_load(&br->br_dirren, hinofile);
+	else
+		err = au_dr_hino_store(sb, br, hinofile);
+	fput(hinofile);
+	goto out;
+
+out_dput:
+	dput(hinopath.dentry);
+out_unlock:
+	if (suspend)
+		au_hn_inode_unlock(hdir);
+	else
+		inode_unlock(dir);
+out:
+	AuTraceErr(err);
+	return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static int au_dr_brid_init(struct au_dr_brid *brid, const struct path *path)
+{
+	int err;
+	struct kstatfs kstfs;
+	dev_t dev;
+	struct dentry *dentry;
+	struct super_block *sb;
+
+	err = vfs_statfs((void *)path, &kstfs);
+	AuTraceErr(err);
+	if (unlikely(err))
+		goto out;
+
+	/* todo: support for UUID */
+
+	if (kstfs.f_fsid.val[0] || kstfs.f_fsid.val[1]) {
+		brid->type = AuBrid_FSID;
+		brid->fsid = kstfs.f_fsid;
+	} else {
+		dentry = path->dentry;
+		sb = dentry->d_sb;
+		dev = sb->s_dev;
+		if (dev) {
+			brid->type = AuBrid_DEV;
+			brid->dev = dev;
+		}
+	}
+
+out:
+	return err;
+}
+
+int au_dr_br_init(struct super_block *sb, struct au_branch *br,
+		  const struct path *path)
+{
+	int err, i;
+	struct au_dr_br *dr;
+	struct hlist_bl_head *hbl;
+
+	dr = &br->br_dirren;
+	hbl = dr->dr_h_ino;
+	for (i = 0; i < AuDirren_NHASH; i++, hbl++)
+		INIT_HLIST_BL_HEAD(hbl);
+
+	err = au_dr_brid_init(&dr->dr_brid, path);
+	if (unlikely(err))
+		goto out;
+
+	if (au_opt_test(au_mntflags(sb), DIRREN))
+		err = au_dr_hino(sb, /*bindex*/-1, br, path);
+
+out:
+	AuTraceErr(err);
+	return err;
+}
+
+int au_dr_br_fin(struct super_block *sb, struct au_branch *br)
+{
+	int err;
+
+	err = 0;
+	if (au_br_writable(br->br_perm))
+		err = au_dr_hino(sb, /*bindex*/-1, br, /*path*/NULL);
+	if (!err)
+		au_dr_hino_free(&br->br_dirren);
+
+	return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static int au_brid_str(struct au_dr_brid *brid, struct inode *h_inode,
+		       char *buf, size_t sz)
+{
+	int err;
+	unsigned int major, minor;
+	char *p;
+
+	p = buf;
+	err = snprintf(p, sz, "%d_", brid->type);
+	AuDebugOn(err > sz);
+	p += err;
+	sz -= err;
+	switch (brid->type) {
+	case AuBrid_Unset:
+		return -EINVAL;
+	case AuBrid_UUID:
+		err = snprintf(p, sz, "%pU", brid->uuid.__u_bits);
+		break;
+	case AuBrid_FSID:
+		err = snprintf(p, sz, "%08x-%08x",
+			       brid->fsid.val[0], brid->fsid.val[1]);
+		break;
+	case AuBrid_DEV:
+		major = MAJOR(brid->dev);
+		minor = MINOR(brid->dev);
+		if (major <= 0xff && minor <= 0xff)
+			err = snprintf(p, sz, "%02x%02x", major, minor);
+		else
+			err = snprintf(p, sz, "%03x:%05x", major, minor);
+		break;
+	}
+	AuDebugOn(err > sz);
+	p += err;
+	sz -= err;
+	err = snprintf(p, sz, "_%llu", (unsigned long long)h_inode->i_ino);
+	AuDebugOn(err > sz);
+	p += err;
+	sz -= err;
+
+	return p - buf;
+}
+
+static int au_drinfo_name(struct au_branch *br, char *name, int len)
+{
+	int rlen;
+	struct dentry *br_dentry;
+	struct inode *br_inode;
+
+	br_dentry = au_br_dentry(br);
+	br_inode = d_inode(br_dentry);
+	rlen = au_brid_str(&br->br_dirren.dr_brid, br_inode, name, len);
+	AuDebugOn(rlen >= AUFS_DIRREN_ENV_VAL_SZ);
+	AuDebugOn(rlen > len);
+
+	return rlen;
+}
+
+/* ---------------------------------------------------------------------- */
+
+/*
+ * from the given @h_dentry, construct drinfo at @*fdata.
+ * when the size of @*fdata is not enough, reallocate and return new @fdata and
+ * @allocated.
+ */
+static int au_drinfo_construct(struct au_drinfo_fdata **fdata,
+			       struct dentry *h_dentry,
+			       unsigned char *allocated)
+{
+	int err, v;
+	struct au_drinfo_fdata *f, *p;
+	struct au_drinfo *drinfo;
+	struct inode *h_inode;
+	struct qstr *qname;
+
+	err = 0;
+	f = *fdata;
+	h_inode = d_inode(h_dentry);
+	qname = &h_dentry->d_name;
+	drinfo = &f->drinfo;
+	drinfo->ino = cpu_to_be64(h_inode->i_ino);
+	drinfo->oldnamelen = qname->len;
+	if (*allocated < sizeof(*f) + qname->len) {
+		v = roundup_pow_of_two(*allocated + qname->len);
+		p = au_krealloc(f, v, GFP_NOFS, /*may_shrink*/0);
+		if (unlikely(!p)) {
+			err = -ENOMEM;
+			AuTraceErr(err);
+			goto out;
+		}
+		f = p;
+		*fdata = f;
+		*allocated = v;
+		drinfo = &f->drinfo;
+	}
+	memcpy(drinfo->oldname, qname->name, qname->len);
+	AuDbg("i%llu, %.*s\n",
+	      be64_to_cpu(drinfo->ino), drinfo->oldnamelen, drinfo->oldname);
+
+out:
+	AuTraceErr(err);
+	return err;
+}
+
+/* callers have to free the return value */
+static struct au_drinfo *au_drinfo_read_k(struct file *file, ino_t h_ino)
+{
+	struct au_drinfo *ret, *drinfo;
+	struct au_drinfo_fdata fdata;
+	int len;
+	loff_t pos;
+	ssize_t ssz;
+
+	ret = ERR_PTR(-EIO);
+	pos = 0;
+	ssz = vfsub_read_k(file, &fdata, sizeof(fdata), &pos);
+	if (unlikely(ssz != sizeof(fdata))) {
+		AuIOErr("ssz %zd, %u, %pD2\n",
+			ssz, (unsigned int)sizeof(fdata), file);
+		goto out;
+	}
+
+	fdata.magic = ntohl(fdata.magic);
+	switch (fdata.magic) {
+	case AUFS_DRINFO_MAGIC_V1:
+		break;
+	default:
+		AuIOErr("magic-num 0x%x, 0x%x, %pD2\n",
+			fdata.magic, AUFS_DRINFO_MAGIC_V1, file);
+		goto out;
+	}
+
+	drinfo = &fdata.drinfo;
+	len = drinfo->oldnamelen;
+	if (!len) {
+		AuIOErr("broken drinfo %pD2\n", file);
+		goto out;
+	}
+
+	ret = NULL;
+	drinfo->ino = be64_to_cpu(drinfo->ino);
+	if (unlikely(h_ino && drinfo->ino != h_ino)) {
+		AuDbg("ignored i%llu, i%llu, %pD2\n",
+		      (unsigned long long)drinfo->ino,
+		      (unsigned long long)h_ino, file);
+		goto out; /* success */
+	}
+
+	ret = kmalloc(sizeof(*ret) + len, GFP_NOFS);
+	if (unlikely(!ret)) {
+		ret = ERR_PTR(-ENOMEM);
+		AuTraceErrPtr(ret);
+		goto out;
+	}
+
+	*ret = *drinfo;
+	ssz = vfsub_read_k(file, (void *)ret->oldname, len, &pos);
+	if (unlikely(ssz != len)) {
+		kfree(ret);
+		ret = ERR_PTR(-EIO);
+		AuIOErr("ssz %zd, %u, %pD2\n", ssz, len, file);
+		goto out;
+	}
+
+	AuDbg("oldname %.*s\n", ret->oldnamelen, ret->oldname);
+
+out:
+	return ret;
+}
+
+/* ---------------------------------------------------------------------- */
+
+/* in order to be revertible */
+struct au_drinfo_rev_elm {
+	int			created;
+	struct dentry		*info_dentry;
+	struct au_drinfo	*info_last;
+};
+
+struct au_drinfo_rev {
+	unsigned char			already;
+	aufs_bindex_t			nelm;
+	struct au_drinfo_rev_elm	elm[0];
+};
+
+/* todo: isn't it too large? */
+struct au_drinfo_store {
+	struct path h_ppath;
+	struct dentry *h_dentry;
+	struct au_drinfo_fdata *fdata;
+	char *infoname;			/* inside of whname, just after PFX */
+	char whname[sizeof(AUFS_WH_DR_INFO_PFX) + AUFS_DIRREN_ENV_VAL_SZ];
+	aufs_bindex_t btgt, btail;
+	unsigned char no_sio,
+		allocated,		/* current size of *fdata */
+		infonamelen,		/* room size for p */
+		whnamelen,		/* length of the genarated name */
+		renameback;		/* renamed back */
+};
+
+/* on rename(2) error, the caller should revert it using @elm */
+static int au_drinfo_do_store(struct au_drinfo_store *w,
+			      struct au_drinfo_rev_elm *elm)
+{
+	int err, len;
+	ssize_t ssz;
+	loff_t pos;
+	struct path infopath = {
+		.mnt = w->h_ppath.mnt
+	};
+	struct inode *h_dir, *h_inode, *delegated;
+	struct file *infofile;
+	struct qstr *qname;
+
+	AuDebugOn(elm
+		  && memcmp(elm, page_address(ZERO_PAGE(0)), sizeof(*elm)));
+
+	infopath.dentry = vfsub_lookup_one_len(w->whname, w->h_ppath.dentry,
+					       w->whnamelen);
+	AuTraceErrPtr(infopath.dentry);
+	if (IS_ERR(infopath.dentry)) {
+		err = PTR_ERR(infopath.dentry);
+		goto out;
+	}
+
+	err = 0;
+	h_dir = d_inode(w->h_ppath.dentry);
+	if (elm && d_is_negative(infopath.dentry)) {
+		err = vfsub_create(h_dir, &infopath, 0600, /*want_excl*/true);
+		AuTraceErr(err);
+		if (unlikely(err))
+			goto out_dput;
+		elm->created = 1;
+		elm->info_dentry = dget(infopath.dentry);
+	}
+
+	infofile = vfsub_dentry_open(&infopath, O_RDWR);
+	AuTraceErrPtr(infofile);
+	if (IS_ERR(infofile)) {
+		err = PTR_ERR(infofile);
+		goto out_dput;
+	}
+
+	h_inode = d_inode(infopath.dentry);
+	if (elm && i_size_read(h_inode)) {
+		h_inode = d_inode(w->h_dentry);
+		elm->info_last = au_drinfo_read_k(infofile, h_inode->i_ino);
+		AuTraceErrPtr(elm->info_last);
+		if (IS_ERR(elm->info_last)) {
+			err = PTR_ERR(elm->info_last);
+			elm->info_last = NULL;
+			AuDebugOn(elm->info_dentry);
+			goto out_fput;
+		}
+	}
+
+	if (elm && w->renameback) {
+		delegated = NULL;
+		err = vfsub_unlink(h_dir, &infopath, &delegated, /*force*/0);
+		AuTraceErr(err);
+		if (unlikely(err == -EWOULDBLOCK))
+			iput(delegated);
+		goto out_fput;
+	}
+
+	pos = 0;
+	qname = &w->h_dentry->d_name;
+	len = sizeof(*w->fdata) + qname->len;
+	if (!elm)
+		len = sizeof(*w->fdata) + w->fdata->drinfo.oldnamelen;
+	ssz = vfsub_write_k(infofile, w->fdata, len, &pos);
+	if (ssz == len) {
+		AuDbg("hi%llu, %.*s\n", w->fdata->drinfo.ino,
+		      w->fdata->drinfo.oldnamelen, w->fdata->drinfo.oldname);
+		goto out_fput; /* success */
+	} else {
+		err = -EIO;
+		if (ssz < 0)
+			err = ssz;
+		/* the caller should revert it using @elm */
+	}
+
+out_fput:
+	fput(infofile);
+out_dput:
+	dput(infopath.dentry);
+out:
+	AuTraceErr(err);
+	return err;
+}
+
+struct au_call_drinfo_do_store_args {
+	int *errp;
+	struct au_drinfo_store *w;
+	struct au_drinfo_rev_elm *elm;
+};
+
+static void au_call_drinfo_do_store(void *args)
+{
+	struct au_call_drinfo_do_store_args *a = args;
+
+	*a->errp = au_drinfo_do_store(a->w, a->elm);
+}
+
+static int au_drinfo_store_sio(struct au_drinfo_store *w,
+			       struct au_drinfo_rev_elm *elm)
+{
+	int err, wkq_err;
+
+	if (w->no_sio)
+		err = au_drinfo_do_store(w, elm);
+	else {
+		struct au_call_drinfo_do_store_args a = {
+			.errp	= &err,
+			.w	= w,
+			.elm	= elm
+		};
+		wkq_err = au_wkq_wait(au_call_drinfo_do_store, &a);
+		if (unlikely(wkq_err))
+			err = wkq_err;
+	}
+	AuTraceErr(err);
+
+	return err;
+}
+
+static int au_drinfo_store_work_init(struct au_drinfo_store *w,
+				     aufs_bindex_t btgt)
+{
+	int err;
+
+	memset(w, 0, sizeof(*w));
+	w->allocated = roundup_pow_of_two(sizeof(*w->fdata) + 40);
+	strcpy(w->whname, AUFS_WH_DR_INFO_PFX);
+	w->infoname = w->whname + sizeof(AUFS_WH_DR_INFO_PFX) - 1;
+	w->infonamelen = sizeof(w->whname) - sizeof(AUFS_WH_DR_INFO_PFX);
+	w->btgt = btgt;
+	w->no_sio = !!uid_eq(current_fsuid(), GLOBAL_ROOT_UID);
+
+	err = -ENOMEM;
+	w->fdata = kcalloc(1, w->allocated, GFP_NOFS);
+	if (unlikely(!w->fdata)) {
+		AuTraceErr(err);
+		goto out;
+	}
+	w->fdata->magic = htonl(AUFS_DRINFO_MAGIC_V1);
+	err = 0;
+
+out:
+	return err;
+}
+
+static void au_drinfo_store_work_fin(struct au_drinfo_store *w)
+{
+	kfree(w->fdata);
+}
+
+static void au_drinfo_store_rev(struct au_drinfo_rev *rev,
+				struct au_drinfo_store *w)
+{
+	struct au_drinfo_rev_elm *elm;
+	struct inode *h_dir, *delegated;
+	int err, nelm;
+	struct path infopath = {
+		.mnt = w->h_ppath.mnt
+	};
+
+	h_dir = d_inode(w->h_ppath.dentry);
+	IMustLock(h_dir);
+
+	err = 0;
+	elm = rev->elm;
+	for (nelm = rev->nelm; nelm > 0; nelm--, elm++) {
+		AuDebugOn(elm->created && elm->info_last);
+		if (elm->created) {
+			AuDbg("here\n");
+			delegated = NULL;
+			infopath.dentry = elm->info_dentry;
+			err = vfsub_unlink(h_dir, &infopath, &delegated,
+					   !w->no_sio);
+			AuTraceErr(err);
+			if (unlikely(err == -EWOULDBLOCK))
+				iput(delegated);
+			dput(elm->info_dentry);
+		} else if (elm->info_last) {
+			AuDbg("here\n");
+			w->fdata->drinfo = *elm->info_last;
+			memcpy(w->fdata->drinfo.oldname,
+			       elm->info_last->oldname,
+			       elm->info_last->oldnamelen);
+			err = au_drinfo_store_sio(w, /*elm*/NULL);
+			kfree(elm->info_last);
+		}
+		if (unlikely(err))
+			AuIOErr("%d, %s\n", err, w->whname);
+		/* go on even if err */
+	}
+}
+
+/* caller has to call au_dr_rename_fin() later */
+static int au_drinfo_store(struct dentry *dentry, aufs_bindex_t btgt,
+			   struct qstr *dst_name, void *_rev)
+{
+	int err, sz, nelm;
+	aufs_bindex_t bindex, btail;
+	struct au_drinfo_store work;
+	struct au_drinfo_rev *rev, **p;
+	struct au_drinfo_rev_elm *elm;
+	struct super_block *sb;
+	struct au_branch *br;
+	struct au_hinode *hdir;
+
+	err = au_drinfo_store_work_init(&work, btgt);
+	AuTraceErr(err);
+	if (unlikely(err))
+		goto out;
+
+	err = -ENOMEM;
+	btail = au_dbtaildir(dentry);
+	nelm = btail - btgt;
+	sz = sizeof(*rev) + sizeof(*elm) * nelm;
+	rev = kcalloc(1, sz, GFP_NOFS);
+	if (unlikely(!rev)) {
+		AuTraceErr(err);
+		goto out_args;
+	}
+	rev->nelm = nelm;
+	elm = rev->elm;
+	p = _rev;
+	*p = rev;
+
+	err = 0;
+	sb = dentry->d_sb;
+	work.h_ppath.dentry = au_h_dptr(dentry, btgt);
+	work.h_ppath.mnt = au_sbr_mnt(sb, btgt);
+	hdir = au_hi(d_inode(dentry), btgt);
+	au_hn_inode_lock_nested(hdir, AuLsc_I_CHILD);
+	for (bindex = btgt + 1; bindex <= btail; bindex++, elm++) {
+		work.h_dentry = au_h_dptr(dentry, bindex);
+		if (!work.h_dentry)
+			continue;
+
+		err = au_drinfo_construct(&work.fdata, work.h_dentry,
+					  &work.allocated);
+		AuTraceErr(err);
+		if (unlikely(err))
+			break;
+
+		work.renameback = au_qstreq(&work.h_dentry->d_name, dst_name);
+		br = au_sbr(sb, bindex);
+		work.whnamelen = sizeof(AUFS_WH_DR_INFO_PFX) - 1;
+		work.whnamelen += au_drinfo_name(br, work.infoname,
+						 work.infonamelen);
+		AuDbg("whname %.*s, i%llu, %.*s\n",
+		      work.whnamelen, work.whname,
+		      be64_to_cpu(work.fdata->drinfo.ino),
+		      work.fdata->drinfo.oldnamelen,
+		      work.fdata->drinfo.oldname);
+
+		err = au_drinfo_store_sio(&work, elm);
+		AuTraceErr(err);
+		if (unlikely(err))
+			break;
+	}
+	if (unlikely(err)) {
+		/* revert all drinfo */
+		au_drinfo_store_rev(rev, &work);
+		kfree(rev);
+		*p = NULL;
+	}
+	au_hn_inode_unlock(hdir);
+
+out_args:
+	au_drinfo_store_work_fin(&work);
+out:
+	return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+int au_dr_rename(struct dentry *src, aufs_bindex_t bindex,
+		 struct qstr *dst_name, void *_rev)
+{
+	int err, already;
+	ino_t ino;
+	struct super_block *sb;
+	struct au_branch *br;
+	struct au_dr_br *dr;
+	struct dentry *h_dentry;
+	struct inode *h_inode;
+	struct au_dr_hino *ent;
+	struct au_drinfo_rev *rev, **p;
+
+	AuDbg("bindex %d\n", bindex);
+
+	err = -ENOMEM;
+	ent = kmalloc(sizeof(*ent), GFP_NOFS);
+	if (unlikely(!ent))
+		goto out;
+
+	sb = src->d_sb;
+	br = au_sbr(sb, bindex);
+	dr = &br->br_dirren;
+	h_dentry = au_h_dptr(src, bindex);
+	h_inode = d_inode(h_dentry);
+	ino = h_inode->i_ino;
+	ent->dr_h_ino = ino;
+	already = au_dr_hino_test_add(dr, ino, ent);
+	AuDbg("b%d, hi%llu, already %d\n",
+	      bindex, (unsigned long long)ino, already);
+
+	err = au_drinfo_store(src, bindex, dst_name, _rev);
+	AuTraceErr(err);
+	if (!err) {
+		p = _rev;
+		rev = *p;
+		rev->already = already;
+		goto out; /* success */
+	}
+
+	/* revert */
+	if (!already)
+		au_dr_hino_del(dr, ent);
+	kfree(ent);
+
+out:
+	AuTraceErr(err);
+	return err;
+}
+
+void au_dr_rename_fin(struct dentry *src, aufs_bindex_t btgt, void *_rev)
+{
+	struct au_drinfo_rev *rev;
+	struct au_drinfo_rev_elm *elm;
+	int nelm;
+
+	rev = _rev;
+	elm = rev->elm;
+	for (nelm = rev->nelm; nelm > 0; nelm--, elm++) {
+		dput(elm->info_dentry);
+		kfree(elm->info_last);
+	}
+	kfree(rev);
+}
+
+void au_dr_rename_rev(struct dentry *src, aufs_bindex_t btgt, void *_rev)
+{
+	int err;
+	struct au_drinfo_store work;
+	struct au_drinfo_rev *rev = _rev;
+	struct super_block *sb;
+	struct au_branch *br;
+	struct inode *h_inode;
+	struct au_dr_br *dr;
+	struct au_dr_hino *ent;
+
+	err = au_drinfo_store_work_init(&work, btgt);
+	if (unlikely(err))
+		goto out;
+
+	sb = src->d_sb;
+	br = au_sbr(sb, btgt);
+	work.h_ppath.dentry = au_h_dptr(src, btgt);
+	work.h_ppath.mnt = au_br_mnt(br);
+	au_drinfo_store_rev(rev, &work);
+	au_drinfo_store_work_fin(&work);
+	if (rev->already)
+		goto out;
+
+	dr = &br->br_dirren;
+	h_inode = d_inode(work.h_ppath.dentry);
+	ent = au_dr_hino_find(dr, h_inode->i_ino);
+	BUG_ON(!ent);
+	au_dr_hino_del(dr, ent);
+	kfree(ent);
+
+out:
+	kfree(rev);
+	if (unlikely(err))
+		pr_err("failed to remove dirren info\n");
+}
+
+/* ---------------------------------------------------------------------- */
+
+static struct au_drinfo *au_drinfo_do_load(struct path *h_ppath,
+					   char *whname, int whnamelen,
+					   struct dentry **info_dentry)
+{
+	struct au_drinfo *drinfo;
+	struct file *f;
+	struct inode *h_dir;
+	struct path infopath;
+	int unlocked;
+
+	AuDbg("%pd/%.*s\n", h_ppath->dentry, whnamelen, whname);
+
+	*info_dentry = NULL;
+	drinfo = NULL;
+	unlocked = 0;
+	h_dir = d_inode(h_ppath->dentry);
+	vfsub_inode_lock_shared_nested(h_dir, AuLsc_I_PARENT);
+	infopath.dentry = vfsub_lookup_one_len(whname, h_ppath->dentry,
+					       whnamelen);
+	if (IS_ERR(infopath.dentry)) {
+		drinfo = (void *)infopath.dentry;
+		goto out;
+	}
+
+	if (d_is_negative(infopath.dentry))
+		goto out_dput; /* success */
+
+	infopath.mnt = h_ppath->mnt;
+	f = vfsub_dentry_open(&infopath, O_RDONLY);
+	inode_unlock_shared(h_dir);
+	unlocked = 1;
+	if (IS_ERR(f)) {
+		drinfo = (void *)f;
+		goto out_dput;
+	}
+
+	drinfo = au_drinfo_read_k(f, /*h_ino*/0);
+	if (IS_ERR_OR_NULL(drinfo))
+		goto out_fput;
+
+	AuDbg("oldname %.*s\n", drinfo->oldnamelen, drinfo->oldname);
+	*info_dentry = dget(infopath.dentry); /* keep it alive */
+
+out_fput:
+	fput(f);
+out_dput:
+	dput(infopath.dentry);
+out:
+	if (!unlocked)
+		inode_unlock_shared(h_dir);
+	AuTraceErrPtr(drinfo);
+	return drinfo;
+}
+
+struct au_drinfo_do_load_args {
+	struct au_drinfo **drinfop;
+	struct path *h_ppath;
+	char *whname;
+	int whnamelen;
+	struct dentry **info_dentry;
+};
+
+static void au_call_drinfo_do_load(void *args)
+{
+	struct au_drinfo_do_load_args *a = args;
+
+	*a->drinfop = au_drinfo_do_load(a->h_ppath, a->whname, a->whnamelen,
+					a->info_dentry);
+}
+
+struct au_drinfo_load {
+	struct path h_ppath;
+	struct qstr *qname;
+	unsigned char no_sio;
+
+	aufs_bindex_t ninfo;
+	struct au_drinfo **drinfo;
+};
+
+static int au_drinfo_load(struct au_drinfo_load *w, aufs_bindex_t bindex,
+			  struct au_branch *br)
+{
+	int err, wkq_err, whnamelen, e;
+	char whname[sizeof(AUFS_WH_DR_INFO_PFX) + AUFS_DIRREN_ENV_VAL_SZ]
+		= AUFS_WH_DR_INFO_PFX;
+	struct au_drinfo *drinfo;
+	struct qstr oldname;
+	struct inode *h_dir, *delegated;
+	struct dentry *info_dentry;
+	struct path infopath;
+
+	whnamelen = sizeof(AUFS_WH_DR_INFO_PFX) - 1;
+	whnamelen += au_drinfo_name(br, whname + whnamelen,
+				    sizeof(whname) - whnamelen);
+	if (w->no_sio)
+		drinfo = au_drinfo_do_load(&w->h_ppath, whname, whnamelen,
+					   &info_dentry);
+	else {
+		struct au_drinfo_do_load_args args = {
+			.drinfop	= &drinfo,
+			.h_ppath	= &w->h_ppath,
+			.whname		= whname,
+			.whnamelen	= whnamelen,
+			.info_dentry	= &info_dentry
+		};
+		wkq_err = au_wkq_wait(au_call_drinfo_do_load, &args);
+		if (unlikely(wkq_err))
+			drinfo = ERR_PTR(wkq_err);
+	}
+	err = PTR_ERR(drinfo);
+	if (IS_ERR_OR_NULL(drinfo))
+		goto out;
+
+	err = 0;
+	oldname.len = drinfo->oldnamelen;
+	oldname.name = drinfo->oldname;
+	if (au_qstreq(w->qname, &oldname)) {
+		/* the name is renamed back */
+		kfree(drinfo);
+		drinfo = NULL;
+
+		infopath.dentry = info_dentry;
+		infopath.mnt = w->h_ppath.mnt;
+		h_dir = d_inode(w->h_ppath.dentry);
+		delegated = NULL;
+		inode_lock_nested(h_dir, AuLsc_I_PARENT);
+		e = vfsub_unlink(h_dir, &infopath, &delegated, !w->no_sio);
+		inode_unlock(h_dir);
+		if (unlikely(e))
+			AuIOErr("ignored %d, %pd2\n", e, &infopath.dentry);
+		if (unlikely(e == -EWOULDBLOCK))
+			iput(delegated);
+	}
+	kfree(w->drinfo[bindex]);
+	w->drinfo[bindex] = drinfo;
+	dput(info_dentry);
+
+out:
+	AuTraceErr(err);
+	return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static void au_dr_lkup_free(struct au_drinfo **drinfo, int n)
+{
+	struct au_drinfo **p = drinfo;
+
+	while (n-- > 0)
+		kfree(*drinfo++);
+	kfree(p);
+}
+
+int au_dr_lkup(struct au_do_lookup_args *lkup, struct dentry *dentry,
+	       aufs_bindex_t btgt)
+{
+	int err, ninfo;
+	struct au_drinfo_load w;
+	aufs_bindex_t bindex, bbot;
+	struct au_branch *br;
+	struct inode *h_dir;
+	struct au_dr_hino *ent;
+	struct super_block *sb;
+
+	AuDbg("%.*s, name %.*s, whname %.*s, b%d\n",
+	      AuLNPair(&dentry->d_name), AuLNPair(&lkup->dirren.dr_name),
+	      AuLNPair(&lkup->whname), btgt);
+
+	sb = dentry->d_sb;
+	bbot = au_sbbot(sb);
+	w.ninfo = bbot + 1;
+	if (!lkup->dirren.drinfo) {
+		lkup->dirren.drinfo = kcalloc(w.ninfo,
+					      sizeof(*lkup->dirren.drinfo),
+					      GFP_NOFS);
+		if (unlikely(!lkup->dirren.drinfo)) {
+			err = -ENOMEM;
+			goto out;
+		}
+		lkup->dirren.ninfo = w.ninfo;
+	}
+	w.drinfo = lkup->dirren.drinfo;
+	w.no_sio = !!uid_eq(current_fsuid(), GLOBAL_ROOT_UID);
+	w.h_ppath.dentry = au_h_dptr(dentry, btgt);
+	AuDebugOn(!w.h_ppath.dentry);
+	w.h_ppath.mnt = au_sbr_mnt(sb, btgt);
+	w.qname = &dentry->d_name;
+
+	ninfo = 0;
+	for (bindex = btgt + 1; bindex <= bbot; bindex++) {
+		br = au_sbr(sb, bindex);
+		err = au_drinfo_load(&w, bindex, br);
+		if (unlikely(err))
+			goto out_free;
+		if (w.drinfo[bindex])
+			ninfo++;
+	}
+	if (!ninfo) {
+		br = au_sbr(sb, btgt);
+		h_dir = d_inode(w.h_ppath.dentry);
+		ent = au_dr_hino_find(&br->br_dirren, h_dir->i_ino);
+		AuDebugOn(!ent);
+		au_dr_hino_del(&br->br_dirren, ent);
+		kfree(ent);
+	}
+	goto out; /* success */
+
+out_free:
+	au_dr_lkup_free(lkup->dirren.drinfo, lkup->dirren.ninfo);
+	lkup->dirren.ninfo = 0;
+	lkup->dirren.drinfo = NULL;
+out:
+	AuTraceErr(err);
+	return err;
+}
+
+void au_dr_lkup_fin(struct au_do_lookup_args *lkup)
+{
+	au_dr_lkup_free(lkup->dirren.drinfo, lkup->dirren.ninfo);
+}
+
+int au_dr_lkup_name(struct au_do_lookup_args *lkup, aufs_bindex_t btgt)
+{
+	int err;
+	struct au_drinfo *drinfo;
+
+	err = 0;
+	if (!lkup->dirren.drinfo)
+		goto out;
+	AuDebugOn(lkup->dirren.ninfo < btgt + 1);
+	drinfo = lkup->dirren.drinfo[btgt + 1];
+	if (!drinfo)
+		goto out;
+
+	kfree(lkup->whname.name);
+	lkup->whname.name = NULL;
+	lkup->dirren.dr_name.len = drinfo->oldnamelen;
+	lkup->dirren.dr_name.name = drinfo->oldname;
+	lkup->name = &lkup->dirren.dr_name;
+	err = au_wh_name_alloc(&lkup->whname, lkup->name);
+	if (!err)
+		AuDbg("name %.*s, whname %.*s, b%d\n",
+		      AuLNPair(lkup->name), AuLNPair(&lkup->whname),
+		      btgt);
+
+out:
+	AuTraceErr(err);
+	return err;
+}
+
+int au_dr_lkup_h_ino(struct au_do_lookup_args *lkup, aufs_bindex_t bindex,
+		     ino_t h_ino)
+{
+	int match;
+	struct au_drinfo *drinfo;
+
+	match = 1;
+	if (!lkup->dirren.drinfo)
+		goto out;
+	AuDebugOn(lkup->dirren.ninfo < bindex + 1);
+	drinfo = lkup->dirren.drinfo[bindex + 1];
+	if (!drinfo)
+		goto out;
+
+	match = (drinfo->ino == h_ino);
+	AuDbg("match %d\n", match);
+
+out:
+	return match;
+}
+
+/* ---------------------------------------------------------------------- */
+
+int au_dr_opt_set(struct super_block *sb)
+{
+	int err;
+	aufs_bindex_t bindex, bbot;
+	struct au_branch *br;
+
+	err = 0;
+	bbot = au_sbbot(sb);
+	for (bindex = 0; !err && bindex <= bbot; bindex++) {
+		br = au_sbr(sb, bindex);
+		err = au_dr_hino(sb, bindex, /*br*/NULL, &br->br_path);
+	}
+
+	return err;
+}
+
+int au_dr_opt_flush(struct super_block *sb)
+{
+	int err;
+	aufs_bindex_t bindex, bbot;
+	struct au_branch *br;
+
+	err = 0;
+	bbot = au_sbbot(sb);
+	for (bindex = 0; !err && bindex <= bbot; bindex++) {
+		br = au_sbr(sb, bindex);
+		if (au_br_writable(br->br_perm))
+			err = au_dr_hino(sb, bindex, /*br*/NULL, /*path*/NULL);
+	}
+
+	return err;
+}
+
+int au_dr_opt_clr(struct super_block *sb, int no_flush)
+{
+	int err;
+	aufs_bindex_t bindex, bbot;
+	struct au_branch *br;
+
+	err = 0;
+	if (!no_flush) {
+		err = au_dr_opt_flush(sb);
+		if (unlikely(err))
+			goto out;
+	}
+
+	bbot = au_sbbot(sb);
+	for (bindex = 0; bindex <= bbot; bindex++) {
+		br = au_sbr(sb, bindex);
+		au_dr_hino_free(&br->br_dirren);
+	}
+
+out:
+	return err;
+}
diff --git a/fs/aufs/dirren.h b/fs/aufs/dirren.h
new file mode 100644
index 000000000000..878f60ddb736
--- /dev/null
+++ b/fs/aufs/dirren.h
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2017 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, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * renamed dir info
+ */
+
+#ifndef __AUFS_DIRREN_H__
+#define __AUFS_DIRREN_H__
+
+#ifdef __KERNEL__
+
+#include <linux/dcache.h>
+#include <linux/statfs.h>
+#include "hbl.h"
+
+#define AuDirren_NHASH 100
+
+#ifdef CONFIG_AUFS_DIRREN
+/* copied from linux/fs/xfs/uuid.h */
+typedef struct {
+	unsigned char	__u_bits[16];
+} uuid_t;
+
+#define __UUID_TMPLT		"01234567-0123-4567-0123-456701234567"
+
+enum au_brid_type {
+	AuBrid_Unset,
+	AuBrid_UUID,
+	AuBrid_FSID,
+	AuBrid_DEV
+};
+
+struct au_dr_brid {
+	enum au_brid_type	type;
+	union {
+		uuid_t	uuid;	/* unimplemented yet */
+		fsid_t	fsid;
+		dev_t	dev;
+	};
+};
+
+/* 20 is the max digits length of ulong 64 */
+/* brid-type "_" uuid "_" inum */
+#define AUFS_DIRREN_FNAME_SZ	(1 + 1 + sizeof(__UUID_TMPLT) + 20)
+#define AUFS_DIRREN_ENV_VAL_SZ	(AUFS_DIRREN_FNAME_SZ + 1 + 20)
+
+struct au_dr_hino {
+	struct hlist_bl_node	dr_hnode;
+	ino_t			dr_h_ino;
+};
+
+struct au_dr_br {
+	struct hlist_bl_head	dr_h_ino[AuDirren_NHASH];
+	struct au_dr_brid	dr_brid;
+};
+
+struct au_dr_lookup {
+	/* dr_name is pointed by struct au_do_lookup_args.name */
+	struct qstr		dr_name; /* subset of dr_info */
+	aufs_bindex_t		ninfo;
+	struct au_drinfo	**drinfo;
+};
+#else
+struct au_dr_hino;
+/* empty */
+struct au_dr_br { };
+struct au_dr_lookup { };
+#endif
+
+/* ---------------------------------------------------------------------- */
+
+struct au_branch;
+struct au_do_lookup_args;
+struct au_hinode;
+#ifdef CONFIG_AUFS_DIRREN
+int au_dr_hino_test_add(struct au_dr_br *dr, ino_t h_ino,
+			struct au_dr_hino *add_ent);
+void au_dr_hino_free(struct au_dr_br *dr);
+int au_dr_br_init(struct super_block *sb, struct au_branch *br,
+		  const struct path *path);
+int au_dr_br_fin(struct super_block *sb, struct au_branch *br);
+int au_dr_rename(struct dentry *src, aufs_bindex_t bindex,
+		 struct qstr *dst_name, void *_rev);
+void au_dr_rename_fin(struct dentry *src, aufs_bindex_t btgt, void *rev);
+void au_dr_rename_rev(struct dentry *src, aufs_bindex_t bindex, void *rev);
+int au_dr_lkup(struct au_do_lookup_args *lkup, struct dentry *dentry,
+	       aufs_bindex_t bindex);
+int au_dr_lkup_name(struct au_do_lookup_args *lkup, aufs_bindex_t btgt);
+int au_dr_lkup_h_ino(struct au_do_lookup_args *lkup, aufs_bindex_t bindex,
+		     ino_t h_ino);
+void au_dr_lkup_fin(struct au_do_lookup_args *lkup);
+int au_dr_opt_set(struct super_block *sb);
+int au_dr_opt_flush(struct super_block *sb);
+int au_dr_opt_clr(struct super_block *sb, int no_flush);
+#else
+AuStubInt0(au_dr_hino_test_add, struct au_dr_br *dr, ino_t h_ino,
+	   struct au_dr_hino *add_ent);
+AuStubVoid(au_dr_hino_free, struct au_dr_br *dr);
+AuStubInt0(au_dr_br_init, struct super_block *sb, struct au_branch *br,
+	   const struct path *path);
+AuStubInt0(au_dr_br_fin, struct super_block *sb, struct au_branch *br);
+AuStubInt0(au_dr_rename, struct dentry *src, aufs_bindex_t bindex,
+	   struct qstr *dst_name, void *_rev);
+AuStubVoid(au_dr_rename_fin, struct dentry *src, aufs_bindex_t btgt, void *rev);
+AuStubVoid(au_dr_rename_rev, struct dentry *src, aufs_bindex_t bindex,
+	   void *rev);
+AuStubInt0(au_dr_lkup, struct au_do_lookup_args *lkup, struct dentry *dentry,
+	   aufs_bindex_t bindex);
+AuStubInt0(au_dr_lkup_name, struct au_do_lookup_args *lkup, aufs_bindex_t btgt);
+AuStubInt0(au_dr_lkup_h_ino, struct au_do_lookup_args *lkup,
+	   aufs_bindex_t bindex, ino_t h_ino);
+AuStubVoid(au_dr_lkup_fin, struct au_do_lookup_args *lkup);
+AuStubInt0(au_dr_opt_set, struct super_block *sb);
+AuStubInt0(au_dr_opt_flush, struct super_block *sb);
+AuStubInt0(au_dr_opt_clr, struct super_block *sb, int no_flush);
+#endif
+
+/* ---------------------------------------------------------------------- */
+
+#ifdef CONFIG_AUFS_DIRREN
+static inline int au_dr_ihash(ino_t h_ino)
+{
+	return h_ino % AuDirren_NHASH;
+}
+#else
+AuStubInt0(au_dr_ihash, ino_t h_ino);
+#endif
+
+#endif /* __KERNEL__ */
+#endif /* __AUFS_DIRREN_H__ */
diff --git a/fs/aufs/dynop.c b/fs/aufs/dynop.c
index 197be54aa332..72fd326705a9 100644
--- a/fs/aufs/dynop.c
+++ b/fs/aufs/dynop.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010-2016 Junjiro R. Okajima
+ * Copyright (C) 2010-2017 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
@@ -27,23 +27,23 @@
  * How large will these lists be?
  * Usually just a few elements, 20-30 at most for each, I guess.
  */
-static struct au_sphlhead dynop[AuDyLast];
+static struct hlist_bl_head dynop[AuDyLast];
 
-static struct au_dykey *dy_gfind_get(struct au_sphlhead *sphl, const void *h_op)
+static struct au_dykey *dy_gfind_get(struct hlist_bl_head *hbl,
+				     const void *h_op)
 {
 	struct au_dykey *key, *tmp;
-	struct hlist_head *head;
+	struct hlist_bl_node *pos;
 
 	key = NULL;
-	head = &sphl->head;
-	rcu_read_lock();
-	hlist_for_each_entry_rcu(tmp, head, dk_hnode)
+	hlist_bl_lock(hbl);
+	hlist_bl_for_each_entry(tmp, pos, hbl, dk_hnode)
 		if (tmp->dk_op.dy_hop == h_op) {
 			key = tmp;
 			kref_get(&key->dk_kref);
 			break;
 		}
-	rcu_read_unlock();
+	hlist_bl_unlock(hbl);
 
 	return key;
 }
@@ -84,24 +84,23 @@ static struct au_dykey *dy_bradd(struct au_branch *br, struct au_dykey *key)
 }
 
 /* kref_get() if @key is already added */
-static struct au_dykey *dy_gadd(struct au_sphlhead *sphl, struct au_dykey *key)
+static struct au_dykey *dy_gadd(struct hlist_bl_head *hbl, struct au_dykey *key)
 {
 	struct au_dykey *tmp, *found;
-	struct hlist_head *head;
+	struct hlist_bl_node *pos;
 	const void *h_op = key->dk_op.dy_hop;
 
 	found = NULL;
-	head = &sphl->head;
-	spin_lock(&sphl->spin);
-	hlist_for_each_entry(tmp, head, dk_hnode)
+	hlist_bl_lock(hbl);
+	hlist_bl_for_each_entry(tmp, pos, hbl, dk_hnode)
 		if (tmp->dk_op.dy_hop == h_op) {
 			kref_get(&tmp->dk_kref);
 			found = tmp;
 			break;
 		}
 	if (!found)
-		hlist_add_head_rcu(&key->dk_hnode, head);
-	spin_unlock(&sphl->spin);
+		hlist_bl_add_head(&key->dk_hnode, hbl);
+	hlist_bl_unlock(hbl);
 
 	if (!found)
 		DyPrSym(key);
@@ -114,17 +113,17 @@ static void dy_free_rcu(struct rcu_head *rcu)
 
 	key = container_of(rcu, struct au_dykey, dk_rcu);
 	DyPrSym(key);
-	kfree(key);	/* not delayed */
+	kfree(key);
 }
 
 static void dy_free(struct kref *kref)
 {
 	struct au_dykey *key;
-	struct au_sphlhead *sphl;
+	struct hlist_bl_head *hbl;
 
 	key = container_of(kref, struct au_dykey, dk_kref);
-	sphl = dynop + key->dk_op.dy_type;
-	au_sphl_del_rcu(&key->dk_hnode, sphl);
+	hbl = dynop + key->dk_op.dy_type;
+	au_hbl_del(&key->dk_hnode, hbl);
 	call_rcu(&key->dk_rcu, dy_free_rcu);
 }
 
@@ -189,6 +188,8 @@ static void dy_aop(struct au_dykey *key, const void *h_op,
 	/* this one will be changed according to an aufs mount option */
 	DySetAop(direct_IO);
 	DySetAop(migratepage);
+	DySetAop(isolate_page);
+	DySetAop(putback_page);
 	DySetAop(launder_page);
 	DySetAop(is_partially_uptodate);
 	DySetAop(is_dirty_writeback);
@@ -209,7 +210,7 @@ static void dy_bug(struct kref *kref)
 static struct au_dykey *dy_get(struct au_dynop *op, struct au_branch *br)
 {
 	struct au_dykey *key, *old;
-	struct au_sphlhead *sphl;
+	struct hlist_bl_head *hbl;
 	struct op {
 		unsigned int sz;
 		void (*set)(struct au_dykey *key, const void *h_op,
@@ -223,8 +224,8 @@ static struct au_dykey *dy_get(struct au_dynop *op, struct au_branch *br)
 	};
 	const struct op *p;
 
-	sphl = dynop + op->dy_type;
-	key = dy_gfind_get(sphl, op->dy_hop);
+	hbl = dynop + op->dy_type;
+	key = dy_gfind_get(hbl, op->dy_hop);
 	if (key)
 		goto out_add; /* success */
 
@@ -238,9 +239,9 @@ static struct au_dykey *dy_get(struct au_dynop *op, struct au_branch *br)
 	key->dk_op.dy_hop = op->dy_hop;
 	kref_init(&key->dk_kref);
 	p->set(key, op->dy_hop, au_br_sb(br));
-	old = dy_gadd(sphl, key);
+	old = dy_gadd(hbl, key);
 	if (old) {
-		au_delayed_kfree(key);
+		kfree(key);
 		key = old;
 	}
 
@@ -335,16 +336,15 @@ int au_dy_irefresh(struct inode *inode)
 
 void au_dy_arefresh(int do_dx)
 {
-	struct au_sphlhead *sphl;
-	struct hlist_head *head;
+	struct hlist_bl_head *hbl;
+	struct hlist_bl_node *pos;
 	struct au_dykey *key;
 
-	sphl = dynop + AuDy_AOP;
-	head = &sphl->head;
-	spin_lock(&sphl->spin);
-	hlist_for_each_entry(key, head, dk_hnode)
+	hbl = dynop + AuDy_AOP;
+	hlist_bl_lock(hbl);
+	hlist_bl_for_each_entry(key, pos, hbl, dk_hnode)
 		dy_adx((void *)key, do_dx);
-	spin_unlock(&sphl->spin);
+	hlist_bl_unlock(hbl);
 }
 
 /* ---------------------------------------------------------------------- */
@@ -357,7 +357,7 @@ void __init au_dy_init(void)
 	BUILD_BUG_ON(offsetof(struct au_dyaop, da_key));
 
 	for (i = 0; i < AuDyLast; i++)
-		au_sphl_init(dynop + i);
+		INIT_HLIST_BL_HEAD(dynop + i);
 }
 
 void au_dy_fin(void)
@@ -365,5 +365,5 @@ void au_dy_fin(void)
 	int i;
 
 	for (i = 0; i < AuDyLast; i++)
-		WARN_ON(!hlist_empty(&dynop[i].head));
+		WARN_ON(!hlist_bl_empty(dynop + i));
 }
diff --git a/fs/aufs/dynop.h b/fs/aufs/dynop.h
index 054e7032f184..e379dd18c8cf 100644
--- a/fs/aufs/dynop.h
+++ b/fs/aufs/dynop.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010-2016 Junjiro R. Okajima
+ * Copyright (C) 2010-2017 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -39,7 +39,7 @@ struct au_dynop {
 
 struct au_dykey {
 	union {
-		struct hlist_node	dk_hnode;
+		struct hlist_bl_node	dk_hnode;
 		struct rcu_head		dk_rcu;
 	};
 	struct au_dynop		dk_op;
diff --git a/fs/aufs/export.c b/fs/aufs/export.c
index 8ffc8e7f592a..34b391c0dc47 100644
--- a/fs/aufs/export.c
+++ b/fs/aufs/export.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 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
@@ -25,7 +25,6 @@
 #include <linux/nsproxy.h>
 #include <linux/random.h>
 #include <linux/writeback.h>
-#include "../fs/mount.h"
 #include "aufs.h"
 
 union conv {
@@ -417,7 +416,7 @@ static struct dentry *au_lkup_by_ino(struct path *path, ino_t ino,
 	}
 
 out_name:
-	au_delayed_free_page((unsigned long)arg.name);
+	free_page((unsigned long)arg.name);
 out_file:
 	fput(file);
 out:
@@ -571,7 +570,7 @@ struct dentry *decode_by_path(struct super_block *sb, ino_t ino, __u32 *fh,
 			dentry = ERR_PTR(-ESTALE);
 		}
 out_pathname:
-	au_delayed_free_page((unsigned long)pathname);
+	free_page((unsigned long)pathname);
 out_h_parent:
 	dput(h_parent);
 out:
diff --git a/fs/aufs/f b/fs/aufs/f
new file mode 100644
index 000000000000..500a2ab32cb6
--- /dev/null
+++ b/fs/aufs/f
@@ -0,0 +1,298 @@
+--- /home/bruce/poky/build/tmp/work-shared/qemux86-64/kernel-source/fs/aufs/i_op.c	2017-12-23 23:06:53.241340968 -0500
++++ i_op.c	2017-12-23 23:14:58.070185032 -0500
+@@ -1,5 +1,5 @@
+ /*
+- * Copyright (C) 2005-2016 Junjiro R. Okajima
++ * Copyright (C) 2005-2017 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
+@@ -31,12 +31,15 @@
+ 	int err;
+ 	const unsigned char write_mask = !!(mask & (MAY_WRITE | MAY_APPEND));
+ 
++	err = -EPERM;
++	if (write_mask && IS_IMMUTABLE(h_inode))
++		goto out;
++
+ 	err = -EACCES;
+-	if ((write_mask && IS_IMMUTABLE(h_inode))
+-	    || ((mask & MAY_EXEC)
+-		&& S_ISREG(h_inode->i_mode)
+-		&& (path_noexec(h_path)
+-		    || !(h_inode->i_mode & S_IXUGO))))
++	if (((mask & MAY_EXEC)
++	     && S_ISREG(h_inode->i_mode)
++	     && (path_noexec(h_path)
++		 || !(h_inode->i_mode & S_IXUGO))))
+ 		goto out;
+ 
+ 	/*
+@@ -249,27 +252,28 @@
+ /* ---------------------------------------------------------------------- */
+ 
+ struct aopen_node {
+-	struct hlist_node hlist;
++	struct hlist_bl_node hblist;
+ 	struct file *file, *h_file;
+ };
+ 
+ static int au_do_aopen(struct inode *inode, struct file *file)
+ {
+-	struct au_sphlhead *aopen;
++	struct hlist_bl_head *aopen;
++	struct hlist_bl_node *pos;
+ 	struct aopen_node *node;
+ 	struct au_do_open_args args = {
+-		.no_lock	= 1,
+-		.open		= au_do_open_nondir
++		.aopen	= 1,
++		.open	= au_do_open_nondir
+ 	};
+ 
+ 	aopen = &au_sbi(inode->i_sb)->si_aopen;
+-	spin_lock(&aopen->spin);
+-	hlist_for_each_entry(node, &aopen->head, hlist)
++	hlist_bl_lock(aopen);
++	hlist_bl_for_each_entry(node, pos, aopen, hblist)
+ 		if (node->file == file) {
+ 			args.h_file = node->h_file;
+ 			break;
+ 		}
+-	spin_unlock(&aopen->spin);
++	hlist_bl_unlock(aopen);
+ 	/* AuDebugOn(!args.h_file); */
+ 
+ 	return au_do_open(file, &args);
+@@ -279,10 +283,10 @@
+ 			    struct file *file, unsigned int open_flag,
+ 			    umode_t create_mode, int *opened)
+ {
+-	int err, h_opened = *opened;
++	int err, unlocked, h_opened = *opened;
+ 	unsigned int lkup_flags;
+ 	struct dentry *parent, *d;
+-	struct au_sphlhead *aopen;
++	struct hlist_bl_head *aopen;
+ 	struct vfsub_aopen_args args = {
+ 		.open_flag	= open_flag,
+ 		.create_mode	= create_mode,
+@@ -324,6 +328,7 @@
+ 	    || !(open_flag & O_CREAT))
+ 		goto out_no_open;
+ 
++	unlocked = 0;
+ 	err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH | AuLock_GEN);
+ 	if (unlikely(err))
+ 		goto out;
+@@ -354,6 +359,9 @@
+ 			put_filp(args.file);
+ 		goto out_unlock;
+ 	}
++	di_write_unlock(parent);
++	di_write_unlock(dentry);
++	unlocked = 1;
+ 
+ 	/* some filesystems don't set FILE_CREATED while succeeded? */
+ 	*opened |= FILE_CREATED;
+@@ -364,17 +372,21 @@
+ 		args.file = NULL;
+ 	}
+ 	aopen = &au_sbi(dir->i_sb)->si_aopen;
+-	au_sphl_add(&aopen_node.hlist, aopen);
++	au_hbl_add(&aopen_node.hblist, aopen);
+ 	err = finish_open(file, dentry, au_do_aopen, opened);
+-	au_sphl_del(&aopen_node.hlist, aopen);
++	au_hbl_del(&aopen_node.hblist, aopen);
+ 	AuTraceErr(err);
+ 	AuDbgFile(file);
+ 	if (aopen_node.h_file)
+ 		fput(aopen_node.h_file);
+ 
+ out_unlock:
+-	di_write_unlock(parent);
+-	aufs_read_unlock(dentry, AuLock_DW);
++	if (unlocked)
++		si_read_unlock(dentry->d_sb);
++	else {
++		di_write_unlock(parent);
++		aufs_read_unlock(dentry, AuLock_DW);
++	}
+ 	AuDbgDentry(dentry);
+ 	if (unlikely(err < 0))
+ 		goto out;
+@@ -420,10 +432,10 @@
+ 	if (!err && add_entry && !au_ftest_wrdir(add_entry, TMPFILE)) {
+ 		h_parent = au_h_dptr(parent, bcpup);
+ 		h_dir = d_inode(h_parent);
+-		inode_lock_nested(h_dir, AuLsc_I_PARENT);
++		vfsub_inode_lock_shared_nested(h_dir, AuLsc_I_PARENT);
+ 		err = au_lkup_neg(dentry, bcpup, /*wh*/0);
+ 		/* todo: no unlock here */
+-		inode_unlock(h_dir);
++		inode_unlock_shared(h_dir);
+ 
+ 		AuDbg("bcpup %d\n", bcpup);
+ 		if (!err) {
+@@ -807,10 +819,10 @@
+ 	a->h_path.dentry = au_h_dptr(dentry, btop);
+ 	a->h_inode = d_inode(a->h_path.dentry);
+ 	if (ia && (ia->ia_valid & ATTR_SIZE)) {
+-		inode_lock_nested(a->h_inode, AuLsc_I_CHILD);
++		vfsub_inode_lock_shared_nested(a->h_inode, AuLsc_I_CHILD);
+ 		if (ia->ia_size < i_size_read(a->h_inode))
+ 			sz = ia->ia_size;
+-		inode_unlock(a->h_inode);
++		inode_unlock_shared(a->h_inode);
+ 	}
+ 
+ 	hi_wh = NULL;
+@@ -885,6 +897,10 @@
+ 	inode = d_inode(dentry);
+ 	IMustLock(inode);
+ 
++	err = setattr_prepare(dentry, ia);
++	if (unlikely(err))
++		goto out;
++
+ 	err = -ENOMEM;
+ 	a = kzalloc(sizeof(*a), GFP_NOFS);
+ 	if (unlikely(!a))
+@@ -903,7 +919,8 @@
+ 		/* currently ftruncate(2) only */
+ 		AuDebugOn(!d_is_reg(dentry));
+ 		file = ia->ia_file;
+-		err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1);
++		err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1,
++					    /*fi_lsc*/0);
+ 		if (unlikely(err))
+ 			goto out_si;
+ 		ia->ia_file = au_hf_top(file);
+@@ -993,7 +1010,7 @@
+ out_si:
+ 	si_read_unlock(sb);
+ out_kfree:
+-	au_delayed_kfree(a);
++	kfree(a);
+ out:
+ 	AuTraceErr(err);
+ 	return err;
+@@ -1028,8 +1045,8 @@
+ 	return err;
+ }
+ 
+-ssize_t au_srxattr(struct dentry *dentry, struct inode *inode,
+-		   struct au_srxattr *arg)
++ssize_t au_sxattr(struct dentry *dentry, struct inode *inode,
++		  struct au_sxattr *arg)
+ {
+ 	int err;
+ 	struct path h_path;
+@@ -1063,13 +1080,11 @@
+ 				     arg->u.set.name, arg->u.set.value,
+ 				     arg->u.set.size, arg->u.set.flags);
+ 		break;
+-	case AU_XATTR_REMOVE:
+-		err = vfsub_removexattr(h_path.dentry, arg->u.remove.name);
+-		break;
+ 	case AU_ACL_SET:
+ 		err = -EOPNOTSUPP;
+ 		h_inode = d_inode(h_path.dentry);
+ 		if (h_inode->i_op->set_acl)
++			/* this will call posix_acl_update_mode */
+ 			err = h_inode->i_op->set_acl(h_inode,
+ 						     arg->u.acl_set.acl,
+ 						     arg->u.acl_set.type);
+@@ -1086,7 +1101,7 @@
+ 	di_write_unlock(dentry);
+ 	si_read_unlock(sb);
+ out_kfree:
+-	au_delayed_kfree(a);
++	kfree(a);
+ out:
+ 	AuTraceErr(err);
+ 	return err;
+@@ -1123,11 +1138,12 @@
+ }
+ 
+ /*
+- * common routine for aufs_getattr() and aufs_getxattr().
++ * common routine for aufs_getattr() and au_getxattr().
+  * returns zero or negative (an error).
+  * @dentry will be read-locked in success.
+  */
+-int au_h_path_getattr(struct dentry *dentry, int force, struct path *h_path)
++int au_h_path_getattr(struct dentry *dentry, int force, struct path *h_path,
++		      int locked)
+ {
+ 	int err;
+ 	unsigned int mnt_flags, sigen;
+@@ -1144,6 +1160,9 @@
+ 	mnt_flags = au_mntflags(sb);
+ 	udba_none = !!au_opt_test(mnt_flags, UDBA_NONE);
+ 
++	if (unlikely(locked))
++		goto body; /* skip locking dinfo */
++
+ 	/* support fstat(2) */
+ 	if (!d_unlinked(dentry) && !udba_none) {
+ 		sigen = au_sigen(sb);
+@@ -1171,6 +1190,7 @@
+ 	} else
+ 		di_read_lock_child(dentry, AuLock_IR);
+ 
++body:
+ 	inode = d_inode(dentry);
+ 	bindex = au_ibtop(inode);
+ 	h_path->mnt = au_sbr_mnt(sb, bindex);
+@@ -1209,7 +1229,7 @@
+ 	err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM);
+ 	if (unlikely(err))
+ 		goto out;
+-	err = au_h_path_getattr(dentry, /*force*/0, &h_path);
++	err = au_h_path_getattr(dentry, /*force*/0, &h_path, /*locked*/0);
+ 	if (unlikely(err))
+ 		goto out_si;
+ 	if (unlikely(!h_path.dentry))
+@@ -1290,7 +1310,7 @@
+ 	err = 0;
+ 	AuDbg("%pf\n", h_inode->i_op->get_link);
+ 	AuDbgDentry(h_dentry);
+-	ret = h_inode->i_op->get_link(h_dentry, h_inode, done);
++	ret = vfs_get_link(h_dentry, done);
+ 	dput(h_dentry);
+ 	if (IS_ERR(ret))
+ 		err = PTR_ERR(ret);
+@@ -1384,10 +1404,7 @@
+ 		.getattr	= aufs_getattr,
+ 
+ #ifdef CONFIG_AUFS_XATTR
+-		.setxattr	= aufs_setxattr,
+-		.getxattr	= aufs_getxattr,
+ 		.listxattr	= aufs_listxattr,
+-		.removexattr	= aufs_removexattr,
+ #endif
+ 
+ 		.readlink	= generic_readlink,
+@@ -1416,10 +1433,7 @@
+ 		.getattr	= aufs_getattr,
+ 
+ #ifdef CONFIG_AUFS_XATTR
+-		.setxattr	= aufs_setxattr,
+-		.getxattr	= aufs_getxattr,
+ 		.listxattr	= aufs_listxattr,
+-		.removexattr	= aufs_removexattr,
+ #endif
+ 
+ 		.update_time	= aufs_update_time,
+@@ -1437,10 +1451,7 @@
+ 		.getattr	= aufs_getattr,
+ 
+ #ifdef CONFIG_AUFS_XATTR
+-		.setxattr	= aufs_setxattr,
+-		.getxattr	= aufs_getxattr,
+ 		.listxattr	= aufs_listxattr,
+-		.removexattr	= aufs_removexattr,
+ #endif
+ 
+ 		.update_time	= aufs_update_time
diff --git a/fs/aufs/f_op.c b/fs/aufs/f_op.c
index 0f570bc24d03..f4447d8b3b2f 100644
--- a/fs/aufs/f_op.c
+++ b/fs/aufs/f_op.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 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
@@ -99,17 +99,15 @@ int aufs_release_nondir(struct inode *inode __maybe_unused, struct file *file)
 {
 	struct au_finfo *finfo;
 	aufs_bindex_t bindex;
-	int delayed;
 
 	finfo = au_fi(file);
-	au_sphl_del(&finfo->fi_hlist,
-		    &au_sbi(file->f_path.dentry->d_sb)->si_files);
+	au_hbl_del(&finfo->fi_hlist,
+		   &au_sbi(file->f_path.dentry->d_sb)->si_files);
 	bindex = finfo->fi_btop;
 	if (bindex >= 0)
 		au_set_h_fptr(file, bindex, NULL);
 
-	delayed = (current->flags & PF_KTHREAD) || in_interrupt();
-	au_finfo_fin(file, delayed);
+	au_finfo_fin(file);
 	return 0;
 }
 
@@ -142,12 +140,12 @@ static int aufs_flush_nondir(struct file *file, fl_owner_t id)
  */
 
 /* Callers should call au_read_post() or fput() in the end */
-struct file *au_read_pre(struct file *file, int keep_fi)
+struct file *au_read_pre(struct file *file, int keep_fi, unsigned int lsc)
 {
 	struct file *h_file;
 	int err;
 
-	err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/0);
+	err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/0, lsc);
 	if (!err) {
 		di_read_unlock(file->f_path.dentry, AuLock_IR);
 		h_file = au_hf_top(file);
@@ -168,6 +166,10 @@ static void au_read_post(struct inode *inode, struct file *h_file)
 }
 
 struct au_write_pre {
+	/* input */
+	unsigned int lsc;
+
+	/* output */
 	blkcnt_t blks;
 	aufs_bindex_t btop;
 };
@@ -183,9 +185,13 @@ static struct file *au_write_pre(struct file *file, int do_ready,
 	struct file *h_file;
 	struct dentry *dentry;
 	int err;
+	unsigned int lsc;
 	struct au_pin pin;
 
-	err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1);
+	lsc = 0;
+	if (wpre)
+		lsc = wpre->lsc;
+	err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1, lsc);
 	h_file = ERR_PTR(err);
 	if (unlikely(err))
 		goto out;
@@ -227,12 +233,11 @@ static void au_write_post(struct inode *inode, struct file *h_file,
 	h_inode = file_inode(h_file);
 	inode->i_mode = h_inode->i_mode;
 	ii_write_unlock(inode);
-	fput(h_file);
-
 	/* AuDbg("blks %llu, %llu\n", (u64)blks, (u64)h_inode->i_blocks); */
 	if (written > 0)
 		au_fhsm_wrote(inode->i_sb, wpre->btop,
 			      /*force*/h_inode->i_blocks > wpre->blks);
+	fput(h_file);
 }
 
 static ssize_t aufs_read(struct file *file, char __user *buf, size_t count,
@@ -247,7 +252,7 @@ static ssize_t aufs_read(struct file *file, char __user *buf, size_t count,
 	sb = inode->i_sb;
 	si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW);
 
-	h_file = au_read_pre(file, /*keep_fi*/0);
+	h_file = au_read_pre(file, /*keep_fi*/0, /*lsc*/0);
 	err = PTR_ERR(h_file);
 	if (IS_ERR(h_file))
 		goto out;
@@ -297,6 +302,7 @@ static ssize_t aufs_write(struct file *file, const char __user *ubuf,
 	inode = file_inode(file);
 	au_mtx_and_read_lock(inode);
 
+	wpre.lsc = 0;
 	h_file = au_write_pre(file, /*do_ready*/1, &wpre);
 	err = PTR_ERR(h_file);
 	if (IS_ERR(h_file))
@@ -356,7 +362,7 @@ static ssize_t aufs_read_iter(struct kiocb *kio, struct iov_iter *iov_iter)
 	sb = inode->i_sb;
 	si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW);
 
-	h_file = au_read_pre(file, /*keep_fi*/1);
+	h_file = au_read_pre(file, /*keep_fi*/1, /*lsc*/0);
 	err = PTR_ERR(h_file);
 	if (IS_ERR(h_file))
 		goto out;
@@ -391,6 +397,7 @@ static ssize_t aufs_write_iter(struct kiocb *kio, struct iov_iter *iov_iter)
 	inode = file_inode(file);
 	au_mtx_and_read_lock(inode);
 
+	wpre.lsc = 0;
 	h_file = au_write_pre(file, /*do_ready*/1, &wpre);
 	err = PTR_ERR(h_file);
 	if (IS_ERR(h_file))
@@ -418,7 +425,7 @@ static ssize_t aufs_splice_read(struct file *file, loff_t *ppos,
 	sb = inode->i_sb;
 	si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW);
 
-	h_file = au_read_pre(file, /*keep_fi*/0);
+	h_file = au_read_pre(file, /*keep_fi*/0, /*lsc*/0);
 	err = PTR_ERR(h_file);
 	if (IS_ERR(h_file))
 		goto out;
@@ -445,6 +452,7 @@ aufs_splice_write(struct pipe_inode_info *pipe, struct file *file, loff_t *ppos,
 	inode = file_inode(file);
 	au_mtx_and_read_lock(inode);
 
+	wpre.lsc = 0;
 	h_file = au_write_pre(file, /*do_ready*/1, &wpre);
 	err = PTR_ERR(h_file);
 	if (IS_ERR(h_file))
@@ -470,6 +478,7 @@ static long aufs_fallocate(struct file *file, int mode, loff_t offset,
 	inode = file_inode(file);
 	au_mtx_and_read_lock(inode);
 
+	wpre.lsc = 0;
 	h_file = au_write_pre(file, /*do_ready*/1, &wpre);
 	err = PTR_ERR(h_file);
 	if (IS_ERR(h_file))
@@ -486,6 +495,88 @@ static long aufs_fallocate(struct file *file, int mode, loff_t offset,
 	return err;
 }
 
+static ssize_t aufs_copy_file_range(struct file *src, loff_t src_pos,
+				    struct file *dst, loff_t dst_pos,
+				    size_t len, unsigned int flags)
+{
+	ssize_t err;
+	struct au_write_pre wpre;
+	enum { SRC, DST };
+	struct {
+		struct inode *inode;
+		struct file *h_file;
+		struct super_block *h_sb;
+	} a[2];
+#define a_src	a[SRC]
+#define a_dst	a[DST]
+
+	err = -EINVAL;
+	a_src.inode = file_inode(src);
+	if (unlikely(!S_ISREG(a_src.inode->i_mode)))
+		goto out;
+	a_dst.inode = file_inode(dst);
+	if (unlikely(!S_ISREG(a_dst.inode->i_mode)))
+		goto out;
+
+	au_mtx_and_read_lock(a_dst.inode);
+	/*
+	 * in order to match the order in di_write_lock2_{child,parent}(),
+	 * use f_path.dentry for this comparision.
+	 */
+	if (src->f_path.dentry < dst->f_path.dentry) {
+		a_src.h_file = au_read_pre(src, /*keep_fi*/1, AuLsc_FI_1);
+		err = PTR_ERR(a_src.h_file);
+		if (IS_ERR(a_src.h_file))
+			goto out_si;
+
+		wpre.lsc = AuLsc_FI_2;
+		a_dst.h_file = au_write_pre(dst, /*do_ready*/1, &wpre);
+		err = PTR_ERR(a_dst.h_file);
+		if (IS_ERR(a_dst.h_file)) {
+			au_read_post(a_src.inode, a_src.h_file);
+			goto out_si;
+		}
+	} else {
+		wpre.lsc = AuLsc_FI_1;
+		a_dst.h_file = au_write_pre(dst, /*do_ready*/1, &wpre);
+		err = PTR_ERR(a_dst.h_file);
+		if (IS_ERR(a_dst.h_file))
+			goto out_si;
+
+		a_src.h_file = au_read_pre(src, /*keep_fi*/1, AuLsc_FI_2);
+		err = PTR_ERR(a_src.h_file);
+		if (IS_ERR(a_src.h_file)) {
+			au_write_post(a_dst.inode, a_dst.h_file, &wpre,
+				      /*written*/0);
+			goto out_si;
+		}
+	}
+
+	err = -EXDEV;
+	a_src.h_sb = file_inode(a_src.h_file)->i_sb;
+	a_dst.h_sb = file_inode(a_dst.h_file)->i_sb;
+	if (unlikely(a_src.h_sb != a_dst.h_sb)) {
+		AuDbgFile(src);
+		AuDbgFile(dst);
+		goto out_file;
+	}
+
+	err = vfsub_copy_file_range(a_src.h_file, src_pos, a_dst.h_file,
+				    dst_pos, len, flags);
+
+out_file:
+	au_write_post(a_dst.inode, a_dst.h_file, &wpre, err);
+	fi_read_unlock(src);
+	au_read_post(a_src.inode, a_src.h_file);
+out_si:
+	si_read_unlock(a_dst.inode->i_sb);
+	inode_unlock(a_dst.inode);
+out:
+	return err;
+#undef a_src
+#undef a_dst
+}
+
 /* ---------------------------------------------------------------------- */
 
 /*
@@ -615,6 +706,7 @@ static int aufs_fsync_nondir(struct file *file, loff_t start, loff_t end,
 	inode = file_inode(file);
 	au_mtx_and_read_lock(inode);
 
+	wpre.lsc = 0;
 	h_file = au_write_pre(file, /*do_ready*/1, &wpre);
 	err = PTR_ERR(h_file);
 	if (IS_ERR(h_file))
@@ -630,54 +722,6 @@ static int aufs_fsync_nondir(struct file *file, loff_t start, loff_t end,
 	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_write_pre wpre;
-	struct inode *inode, *h_inode;
-	struct file *file, *h_file;
-
-	err = 0; /* -EBADF; */ /* posix? */
-	if (unlikely(!(file->f_mode & FMODE_WRITE)))
-		goto out;
-
-	file = kio->ki_filp;
-	inode = file_inode(file);
-	au_mtx_and_read_lock(inode);
-
-	h_file = au_write_pre(file, /*do_ready*/1, &wpre);
-	err = PTR_ERR(h_file);
-	if (IS_ERR(h_file))
-		goto out_unlock;
-
-	err = -ENOSYS;
-	h_file = au_hf_top(file);
-	if (h_file->f_op->aio_fsync) {
-		h_inode = file_inode(h_file);
-		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);
-		inode_lock_nested(h_inode, AuLsc_I_CHILD);
-		if (!err)
-			vfsub_update_h_iattr(&h_file->f_path, /*did*/NULL);
-		/*ignore*/
-		inode_unlock(h_inode);
-	}
-	au_write_post(inode, h_file, &wpre, /*written*/0);
-
-out_unlock:
-	si_read_unlock(inode->sb);
-	inode_unlock(inode);
-out:
-	return err;
-}
-#endif
-
 static int aufs_fasync(int fd, struct file *file, int flag)
 {
 	int err;
@@ -687,7 +731,7 @@ static int aufs_fasync(int fd, struct file *file, int flag)
 	sb = file->f_path.dentry->d_sb;
 	si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW);
 
-	h_file = au_read_pre(file, /*keep_fi*/0);
+	h_file = au_read_pre(file, /*keep_fi*/0, /*lsc*/0);
 	err = PTR_ERR(h_file);
 	if (IS_ERR(h_file))
 		goto out;
@@ -710,12 +754,13 @@ static int aufs_setfl(struct file *file, unsigned long arg)
 	sb = file->f_path.dentry->d_sb;
 	si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW);
 
-	h_file = au_read_pre(file, /*keep_fi*/0);
+	h_file = au_read_pre(file, /*keep_fi*/0, /*lsc*/0);
 	err = PTR_ERR(h_file);
 	if (IS_ERR(h_file))
 		goto out;
 
-	arg |= vfsub_file_flags(file) & FASYNC; /* stop calling h_file->fasync */
+	/* stop calling h_file->fasync */
+	arg |= vfsub_file_flags(file) & FASYNC;
 	err = setfl(/*unused fd*/-1, h_file, arg);
 	fput(h_file); /* instead of au_read_post() */
 
@@ -758,7 +803,6 @@ const struct file_operations aufs_file_fop = {
 	.flush		= aufs_flush_nondir,
 	.release	= aufs_release_nondir,
 	.fsync		= aufs_fsync_nondir,
-	/* .aio_fsync	= aufs_aio_fsync_nondir, */
 	.fasync		= aufs_fasync,
 	/* .sendpage	= aufs_sendpage, */
 	.setfl		= aufs_setfl,
@@ -768,5 +812,6 @@ const struct file_operations aufs_file_fop = {
 	.aio_splice_write = aufs_aio_splice_write,
 	.aio_splice_read  = aufs_aio_splice_read,
 #endif
-	.fallocate	= aufs_fallocate
+	.fallocate	= aufs_fallocate,
+	.copy_file_range = aufs_copy_file_range
 };
diff --git a/fs/aufs/fhsm.c b/fs/aufs/fhsm.c
index 40289e4e6950..ef6f99e985c8 100644
--- a/fs/aufs/fhsm.c
+++ b/fs/aufs/fhsm.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011-2016 Junjiro R. Okajima
+ * Copyright (C) 2011-2017 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
diff --git a/fs/aufs/file.c b/fs/aufs/file.c
index 232a97df406c..c15b0e89f639 100644
--- a/fs/aufs/file.c
+++ b/fs/aufs/file.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 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
@@ -108,7 +108,7 @@ struct file *au_h_open(struct dentry *dentry, aufs_bindex_t bindex, int flags,
 
 static int au_cmoo(struct dentry *dentry)
 {
-	int err, cmoo;
+	int err, cmoo, matched;
 	unsigned int udba;
 	struct path h_path;
 	struct au_pin pin;
@@ -143,9 +143,12 @@ static int au_cmoo(struct dentry *dentry)
 	sbinfo = au_sbi(sb);
 	fhsm = &sbinfo->si_fhsm;
 	pid = au_fhsm_pid(fhsm);
-	if (pid
-	    && (current->pid == pid
-		|| current->real_parent->pid == pid))
+	rcu_read_lock();
+	matched = (pid
+		   && (current->pid == pid
+		       || rcu_dereference(current->real_parent)->pid == pid));
+	rcu_read_unlock();
+	if (matched)
 		goto out;
 
 	br = au_sbr(sb, cpg.bsrc);
@@ -222,11 +225,11 @@ static int au_cmoo(struct dentry *dentry)
 
 int au_do_open(struct file *file, struct au_do_open_args *args)
 {
-	int err, no_lock = args->no_lock;
+	int err, aopen = args->aopen;
 	struct dentry *dentry;
 	struct au_finfo *finfo;
 
-	if (!no_lock)
+	if (!aopen)
 		err = au_finfo_init(file, args->fidir);
 	else {
 		lockdep_off();
@@ -238,33 +241,20 @@ int au_do_open(struct file *file, struct au_do_open_args *args)
 
 	dentry = file->f_path.dentry;
 	AuDebugOn(IS_ERR_OR_NULL(dentry));
-	if (!no_lock) {
-		di_write_lock_child(dentry);
-		err = au_cmoo(dentry);
-		di_downgrade_lock(dentry, AuLock_IR);
-		if (!err)
-			err = args->open(file, vfsub_file_flags(file), NULL);
-		di_read_unlock(dentry, AuLock_IR);
-	} else {
-		err = au_cmoo(dentry);
-		if (!err)
-			err = args->open(file, vfsub_file_flags(file),
-					 args->h_file);
-		if (!err && au_fbtop(file) != au_dbtop(dentry))
-			/*
-			 * cmoo happens after h_file was opened.
-			 * need to refresh file later.
-			 */
-			atomic_dec(&au_fi(file)->fi_generation);
-	}
+	di_write_lock_child(dentry);
+	err = au_cmoo(dentry);
+	di_downgrade_lock(dentry, AuLock_IR);
+	if (!err)
+		err = args->open(file, vfsub_file_flags(file), NULL);
+	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_path.dentry->d_sb)->si_files);
+		au_hbl_add(&finfo->fi_hlist,
+			   &au_sbi(file->f_path.dentry->d_sb)->si_files);
 	}
-	if (!no_lock)
+	if (!aopen)
 		fi_write_unlock(file);
 	else {
 		lockdep_off();
@@ -273,7 +263,7 @@ int au_do_open(struct file *file, struct au_do_open_args *args)
 	}
 	if (unlikely(err)) {
 		finfo->fi_hdir = NULL;
-		au_finfo_fin(file, /*atonce*/0);
+		au_finfo_fin(file);
 	}
 
 out:
@@ -593,7 +583,6 @@ static int au_file_refresh_by_inode(struct file *file, int *need_reopen)
 
 static void au_do_refresh_dir(struct file *file)
 {
-	int execed;
 	aufs_bindex_t bindex, bbot, new_bindex, brid;
 	struct au_hfile *p, tmp, *q;
 	struct au_finfo *finfo;
@@ -632,7 +621,6 @@ static void au_do_refresh_dir(struct file *file)
 		}
 	}
 
-	execed = vfsub_file_execed(file);
 	p = fidir->fd_hfile;
 	if (!au_test_mmapped(file) && !d_unlinked(file->f_path.dentry)) {
 		bbot = au_sbbot(sb);
@@ -641,14 +629,14 @@ static void au_do_refresh_dir(struct file *file)
 			if (p->hf_file) {
 				if (file_inode(p->hf_file))
 					break;
-				au_hfput(p, execed);
+				au_hfput(p, /*execed*/0);
 			}
 	} else {
 		bbot = au_br_index(sb, brid);
 		for (finfo->fi_btop = 0; finfo->fi_btop < bbot;
 		     finfo->fi_btop++, p++)
 			if (p->hf_file)
-				au_hfput(p, execed);
+				au_hfput(p, /*execed*/0);
 		bbot = au_sbbot(sb);
 	}
 
@@ -658,7 +646,7 @@ static void au_do_refresh_dir(struct file *file)
 		if (p->hf_file) {
 			if (file_inode(p->hf_file))
 				break;
-			au_hfput(p, execed);
+			au_hfput(p, /*execed*/0);
 		}
 	AuDebugOn(fidir->fd_bbot < finfo->fi_btop);
 }
@@ -720,7 +708,7 @@ static int refresh_file(struct file *file, int (*reopen)(struct file *file))
 
 /* common function to regular file and dir */
 int au_reval_and_lock_fdi(struct file *file, int (*reopen)(struct file *file),
-			  int wlock)
+			  int wlock, unsigned int fi_lsc)
 {
 	int err;
 	unsigned int sigen, figen;
@@ -733,9 +721,12 @@ int au_reval_and_lock_fdi(struct file *file, int (*reopen)(struct file *file),
 	dentry = file->f_path.dentry;
 	inode = d_inode(dentry);
 	sigen = au_sigen(dentry->d_sb);
-	fi_write_lock(file);
+	fi_write_lock_nested(file, fi_lsc);
 	figen = au_figen(file);
-	di_write_lock_child(dentry);
+	if (!fi_lsc)
+		di_write_lock_child(dentry);
+	else
+		di_write_lock_child2(dentry);
 	btop = au_dbtop(dentry);
 	pseudo_link = (btop != au_ibtop(inode));
 	if (sigen == figen && !pseudo_link && au_fbtop(file) == btop) {
@@ -807,6 +798,10 @@ static int aufs_migratepage(struct address_space *mapping, struct page *newpage,
 			    struct page *page, enum migrate_mode mode)
 { AuUnsupport(); return 0; }
 #endif
+static bool aufs_isolate_page(struct page *page, isolate_mode_t mode)
+{ AuUnsupport(); return true; }
+static void aufs_putback_page(struct page *page)
+{ AuUnsupport(); }
 static int aufs_launder_page(struct page *page)
 { AuUnsupport(); return 0; }
 static int aufs_is_partially_uptodate(struct page *page,
@@ -841,6 +836,8 @@ const struct address_space_operations aufs_aop = {
 	.releasepage		= aufs_releasepage,
 	/* is fallback_migrate_page ok? */
 	/* .migratepage		= aufs_migratepage, */
+	.isolate_page		= aufs_isolate_page,
+	.putback_page		= aufs_putback_page,
 	.launder_page		= aufs_launder_page,
 	.is_partially_uptodate	= aufs_is_partially_uptodate,
 	.is_dirty_writeback	= aufs_is_dirty_writeback,
diff --git a/fs/aufs/file.h b/fs/aufs/file.h
index 47cd520c7b7c..7eed2bcb6789 100644
--- a/fs/aufs/file.h
+++ b/fs/aufs/file.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 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
@@ -62,11 +62,8 @@ struct au_finfo {
 	};
 	struct au_fidir		*fi_hdir;	/* for dir only */
 
-	struct hlist_node	fi_hlist;
-	union {
-		struct file		*fi_file;	/* very ugly */
-		struct llist_node	fi_lnode;	/* delayed free */
-	};
+	struct hlist_bl_node	fi_hlist;
+	struct file		*fi_file;	/* very ugly */
 } ____cacheline_aligned_in_smp;
 
 /* ---------------------------------------------------------------------- */
@@ -77,7 +74,7 @@ unsigned int au_file_roflags(unsigned int flags);
 struct file *au_h_open(struct dentry *dentry, aufs_bindex_t bindex, int flags,
 		       struct file *file, int force_wr);
 struct au_do_open_args {
-	int		no_lock;
+	int		aopen;
 	int		(*open)(struct file *file, int flags,
 				struct file *h_file);
 	struct au_fidir	*fidir;
@@ -88,7 +85,7 @@ int au_reopen_nondir(struct file *file);
 struct au_pin;
 int au_ready_to_write(struct file *file, loff_t len, struct au_pin *pin);
 int au_reval_and_lock_fdi(struct file *file, int (*reopen)(struct file *file),
-			  int wlock);
+			  int wlock, unsigned int fi_lsc);
 int au_do_flush(struct file *file, fl_owner_t id,
 		int (*flush)(struct file *file, fl_owner_t id));
 
@@ -114,7 +111,7 @@ AuStubVoid(au_h_open_post, struct dentry *dentry, aufs_bindex_t bindex,
 extern const struct file_operations aufs_file_fop;
 int au_do_open_nondir(struct file *file, int flags, struct file *h_file);
 int aufs_release_nondir(struct inode *inode __maybe_unused, struct file *file);
-struct file *au_read_pre(struct file *file, int keep_fi);
+struct file *au_read_pre(struct file *file, int keep_fi, unsigned int lsc);
 
 /* finfo.c */
 void au_hfput(struct au_hfile *hf, int execed);
@@ -126,7 +123,7 @@ struct au_fidir *au_fidir_alloc(struct super_block *sb);
 int au_fidir_realloc(struct au_finfo *finfo, int nbr, int may_shrink);
 
 void au_fi_init_once(void *_fi);
-void au_finfo_fin(struct file *file, int atonce);
+void au_finfo_fin(struct file *file);
 int au_finfo_init(struct file *file, struct au_fidir *fidir);
 
 /* ioctl.c */
@@ -153,6 +150,45 @@ static inline struct au_finfo *au_fi(struct file *file)
  */
 AuSimpleRwsemFuncs(fi, struct file *f, &au_fi(f)->fi_rwsem);
 
+/* lock subclass for finfo */
+enum {
+	AuLsc_FI_1,
+	AuLsc_FI_2
+};
+
+static inline void fi_read_lock_nested(struct file *f, unsigned int lsc)
+{
+	au_rw_read_lock_nested(&au_fi(f)->fi_rwsem, lsc);
+}
+
+static inline void fi_write_lock_nested(struct file *f, unsigned int lsc)
+{
+	au_rw_write_lock_nested(&au_fi(f)->fi_rwsem, lsc);
+}
+
+/*
+ * fi_read_lock_1, fi_write_lock_1,
+ * fi_read_lock_2, fi_write_lock_2
+ */
+#define AuReadLockFunc(name) \
+static inline void fi_read_lock_##name(struct file *f) \
+{ fi_read_lock_nested(f, AuLsc_FI_##name); }
+
+#define AuWriteLockFunc(name) \
+static inline void fi_write_lock_##name(struct file *f) \
+{ fi_write_lock_nested(f, AuLsc_FI_##name); }
+
+#define AuRWLockFuncs(name) \
+	AuReadLockFunc(name) \
+	AuWriteLockFunc(name)
+
+AuRWLockFuncs(1);
+AuRWLockFuncs(2);
+
+#undef AuReadLockFunc
+#undef AuWriteLockFunc
+#undef AuRWLockFuncs
+
 #define FiMustNoWaiters(f)	AuRwMustNoWaiters(&au_fi(f)->fi_rwsem)
 #define FiMustAnyLock(f)	AuRwMustAnyLock(&au_fi(f)->fi_rwsem)
 #define FiMustWriteLock(f)	AuRwMustWriteLock(&au_fi(f)->fi_rwsem)
diff --git a/fs/aufs/finfo.c b/fs/aufs/finfo.c
index 4f6e92a24e4f..3a8131d43a86 100644
--- a/fs/aufs/finfo.c
+++ b/fs/aufs/finfo.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -102,7 +102,7 @@ int au_fidir_realloc(struct au_finfo *finfo, int nbr, int may_shrink)
 
 /* ---------------------------------------------------------------------- */
 
-void au_finfo_fin(struct file *file, int atonce)
+void au_finfo_fin(struct file *file)
 {
 	struct au_finfo *finfo;
 
@@ -111,10 +111,7 @@ void au_finfo_fin(struct file *file, int atonce)
 	finfo = au_fi(file);
 	AuDebugOn(finfo->fi_hdir);
 	AuRwDestroy(&finfo->fi_rwsem);
-	if (!atonce)
-		au_cache_dfree_finfo(finfo);
-	else
-		au_cache_free_finfo(finfo);
+	au_cache_free_finfo(finfo);
 }
 
 void au_fi_init_once(void *_finfo)
diff --git a/fs/aufs/fstype.h b/fs/aufs/fstype.h
index 6c4a5d5ebe73..4624f1ef222f 100644
--- a/fs/aufs/fstype.h
+++ b/fs/aufs/fstype.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 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
diff --git a/fs/aufs/hbl.h b/fs/aufs/hbl.h
new file mode 100644
index 000000000000..971f79307f72
--- /dev/null
+++ b/fs/aufs/hbl.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 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, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * helpers for hlist_bl.h
+ */
+
+#ifndef __AUFS_HBL_H__
+#define __AUFS_HBL_H__
+
+#ifdef __KERNEL__
+
+#include <linux/list_bl.h>
+
+static inline void au_hbl_add(struct hlist_bl_node *node,
+			      struct hlist_bl_head *hbl)
+{
+	hlist_bl_lock(hbl);
+	hlist_bl_add_head(node, hbl);
+	hlist_bl_unlock(hbl);
+}
+
+static inline void au_hbl_del(struct hlist_bl_node *node,
+			      struct hlist_bl_head *hbl)
+{
+	hlist_bl_lock(hbl);
+	hlist_bl_del(node);
+	hlist_bl_unlock(hbl);
+}
+
+#define au_hbl_for_each(pos, head)					\
+	for (pos = hlist_bl_first(head);				\
+	     pos;							\
+	     pos = pos->next)
+
+static inline unsigned long au_hbl_count(struct hlist_bl_head *hbl)
+{
+	unsigned long cnt;
+	struct hlist_bl_node *pos;
+
+	cnt = 0;
+	hlist_bl_lock(hbl);
+	au_hbl_for_each(pos, hbl)
+		cnt++;
+	hlist_bl_unlock(hbl);
+	return cnt;
+}
+
+#endif /* __KERNEL__ */
+#endif /* __AUFS_HBL_H__ */
diff --git a/fs/aufs/hfsnotify.c b/fs/aufs/hfsnotify.c
index 485587aaa882..49ba77da041d 100644
--- a/fs/aufs/hfsnotify.c
+++ b/fs/aufs/hfsnotify.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 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
@@ -32,8 +32,8 @@ static void au_hfsn_free_mark(struct fsnotify_mark *mark)
 	struct au_hnotify *hn = container_of(mark, struct au_hnotify,
 					     hn_mark);
 	/* AuDbg("here\n"); */
-	au_cache_dfree_hnotify(hn);
-	smp_mb__before_atomic();
+	au_cache_free_hnotify(hn);
+	smp_mb__before_atomic(); /* for atomic64_dec */
 	if (atomic64_dec_and_test(&au_hfsn_ifree))
 		wake_up(&au_hfsn_wq);
 }
@@ -156,7 +156,7 @@ static void au_hfsn_free_group(struct fsnotify_group *group)
 	struct au_br_hfsnotify *hfsn = group->private;
 
 	/* AuDbg("here\n"); */
-	au_delayed_kfree(hfsn);
+	kfree(hfsn);
 }
 
 static int au_hfsn_handle_event(struct fsnotify_group *group,
@@ -250,7 +250,7 @@ static int au_hfsn_init_br(struct au_branch *br, int perm)
 	goto out; /* success */
 
 out_hfsn:
-	au_delayed_kfree(hfsn);
+	kfree(hfsn);
 out:
 	return err;
 }
diff --git a/fs/aufs/hfsplus.c b/fs/aufs/hfsplus.c
index af256faa02fe..b5b6547024e5 100644
--- a/fs/aufs/hfsplus.c
+++ b/fs/aufs/hfsplus.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010-2016 Junjiro R. Okajima
+ * Copyright (C) 2010-2017 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
diff --git a/fs/aufs/hnotify.c b/fs/aufs/hnotify.c
index 4444fe1250cf..16ee23197e3e 100644
--- a/fs/aufs/hnotify.c
+++ b/fs/aufs/hnotify.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -35,7 +35,7 @@ int au_hn_alloc(struct au_hinode *hinode, struct inode *inode)
 		AuTraceErr(err);
 		if (unlikely(err)) {
 			hinode->hi_notify = NULL;
-			au_cache_dfree_hnotify(hn);
+			au_cache_free_hnotify(hn);
 			/*
 			 * The upper dir was removed by udba, but the same named
 			 * dir left. In this case, aufs assignes a new inode
@@ -59,7 +59,7 @@ void au_hn_free(struct au_hinode *hinode)
 	if (hn) {
 		hinode->hi_notify = NULL;
 		if (au_hnotify_op.free(hinode, hn))
-			au_cache_dfree_hnotify(hn);
+			au_cache_free_hnotify(hn);
 	}
 }
 
@@ -322,11 +322,11 @@ static int hn_job(struct hn_job_args *a)
 	if (au_ftest_hnjob(a->flags, TRYXINO0)
 	    && a->inode
 	    && a->h_inode) {
-		inode_lock_nested(a->h_inode, AuLsc_I_CHILD);
+		vfsub_inode_lock_shared_nested(a->h_inode, AuLsc_I_CHILD);
 		if (!a->h_inode->i_nlink
 		    && !(a->h_inode->i_state & I_LINKABLE))
 			hn_xino(a->inode, a->h_inode); /* ignore this error */
-		inode_unlock(a->h_inode);
+		inode_unlock_shared(a->h_inode);
 	}
 
 	/* make the generation obsolete */
@@ -462,6 +462,14 @@ static void au_hn_bh(void *_args)
 	AuDebugOn(!sbinfo);
 	si_write_lock(sb, AuLock_NOPLMW);
 
+	if (au_opt_test(sbinfo->si_mntflags, DIRREN))
+		switch (a->mask & FS_EVENTS_POSS_ON_CHILD) {
+		case FS_MOVED_FROM:
+		case FS_MOVED_TO:
+			AuWarn1("DIRREN with UDBA may not work correctly "
+				"for the direct rename(2)\n");
+		}
+
 	ii_read_lock_parent(a->dir);
 	bfound = -1;
 	bbot = au_ibbot(a->dir);
@@ -532,7 +540,7 @@ static void au_hn_bh(void *_args)
 	iput(a->dir);
 	si_write_unlock(sb);
 	au_nwt_done(&sbinfo->si_nowait);
-	au_delayed_kfree(a);
+	kfree(a);
 }
 
 /* ---------------------------------------------------------------------- */
@@ -638,7 +646,7 @@ int au_hnotify(struct inode *h_dir, struct au_hnotify *hnotify, u32 mask,
 		iput(args->h_child_inode);
 		iput(args->h_dir);
 		iput(args->dir);
-		au_delayed_kfree(args);
+		kfree(args);
 	}
 
 out:
@@ -679,26 +687,17 @@ void au_hnotify_fin_br(struct au_branch *br)
 
 static void au_hn_destroy_cache(void)
 {
-	struct au_cache *cp;
-
-	flush_delayed_work(&au_dfree.dwork);
-	cp = au_dfree.cache + AuCache_HNOTIFY;
-	AuDebugOn(!llist_empty(&cp->llist));
-	kmem_cache_destroy(cp->cache);
-	cp->cache = NULL;
+	kmem_cache_destroy(au_cache[AuCache_HNOTIFY]);
+	au_cache[AuCache_HNOTIFY] = NULL;
 }
 
-AU_CACHE_DFREE_FUNC(hnotify, HNOTIFY, hn_lnode);
-
 int __init au_hnotify_init(void)
 {
 	int err;
-	struct au_cache *cp;
 
 	err = -ENOMEM;
-	cp = au_dfree.cache + AuCache_HNOTIFY;
-	cp->cache = AuCache(au_hnotify);
-	if (cp->cache) {
+	au_cache[AuCache_HNOTIFY] = AuCache(au_hnotify);
+	if (au_cache[AuCache_HNOTIFY]) {
 		err = 0;
 		if (au_hnotify_op.init)
 			err = au_hnotify_op.init();
@@ -711,13 +710,10 @@ int __init au_hnotify_init(void)
 
 void au_hnotify_fin(void)
 {
-	struct au_cache *cp;
-
 	if (au_hnotify_op.fin)
 		au_hnotify_op.fin();
 
 	/* cf. au_cache_fin() */
-	cp = au_dfree.cache + AuCache_HNOTIFY;
-	if (cp->cache)
+	if (au_cache[AuCache_HNOTIFY])
 		au_hn_destroy_cache();
 }
diff --git a/fs/aufs/i_op.c b/fs/aufs/i_op.c
index f9318215fd6b..5d0f0fc85f73 100644
--- a/fs/aufs/i_op.c
+++ b/fs/aufs/i_op.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 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
@@ -31,12 +31,15 @@ static int h_permission(struct inode *h_inode, int mask,
 	int err;
 	const unsigned char write_mask = !!(mask & (MAY_WRITE | MAY_APPEND));
 
+	err = -EPERM;
+	if (write_mask && IS_IMMUTABLE(h_inode))
+		goto out;
+
 	err = -EACCES;
-	if ((write_mask && IS_IMMUTABLE(h_inode))
-	    || ((mask & MAY_EXEC)
-		&& S_ISREG(h_inode->i_mode)
-		&& (path_noexec(h_path)
-		    || !(h_inode->i_mode & S_IXUGO))))
+	if (((mask & MAY_EXEC)
+	     && S_ISREG(h_inode->i_mode)
+	     && (path_noexec(h_path)
+		 || !(h_inode->i_mode & S_IXUGO))))
 		goto out;
 
 	/*
@@ -249,27 +252,28 @@ static struct dentry *aufs_lookup(struct inode *dir, struct dentry *dentry,
 /* ---------------------------------------------------------------------- */
 
 struct aopen_node {
-	struct hlist_node hlist;
+	struct hlist_bl_node hblist;
 	struct file *file, *h_file;
 };
 
 static int au_do_aopen(struct inode *inode, struct file *file)
 {
-	struct au_sphlhead *aopen;
+	struct hlist_bl_head *aopen;
+	struct hlist_bl_node *pos;
 	struct aopen_node *node;
 	struct au_do_open_args args = {
-		.no_lock	= 1,
-		.open		= au_do_open_nondir
+		.aopen	= 1,
+		.open	= au_do_open_nondir
 	};
 
 	aopen = &au_sbi(inode->i_sb)->si_aopen;
-	spin_lock(&aopen->spin);
-	hlist_for_each_entry(node, &aopen->head, hlist)
+	hlist_bl_lock(aopen);
+	hlist_bl_for_each_entry(node, pos, aopen, hblist)
 		if (node->file == file) {
 			args.h_file = node->h_file;
 			break;
 		}
-	spin_unlock(&aopen->spin);
+	hlist_bl_unlock(aopen);
 	/* AuDebugOn(!args.h_file); */
 
 	return au_do_open(file, &args);
@@ -279,10 +283,10 @@ static int aufs_atomic_open(struct inode *dir, struct dentry *dentry,
 			    struct file *file, unsigned int open_flag,
 			    umode_t create_mode, int *opened)
 {
-	int err, h_opened = *opened;
+	int err, unlocked, h_opened = *opened;
 	unsigned int lkup_flags;
 	struct dentry *parent, *d;
-	struct au_sphlhead *aopen;
+	struct hlist_bl_head *aopen;
 	struct vfsub_aopen_args args = {
 		.open_flag	= open_flag,
 		.create_mode	= create_mode,
@@ -324,6 +328,7 @@ static int aufs_atomic_open(struct inode *dir, struct dentry *dentry,
 	    || !(open_flag & O_CREAT))
 		goto out_no_open;
 
+	unlocked = 0;
 	err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH | AuLock_GEN);
 	if (unlikely(err))
 		goto out;
@@ -354,6 +359,9 @@ static int aufs_atomic_open(struct inode *dir, struct dentry *dentry,
 			put_filp(args.file);
 		goto out_unlock;
 	}
+	di_write_unlock(parent);
+	di_write_unlock(dentry);
+	unlocked = 1;
 
 	/* some filesystems don't set FILE_CREATED while succeeded? */
 	*opened |= FILE_CREATED;
@@ -364,17 +372,21 @@ static int aufs_atomic_open(struct inode *dir, struct dentry *dentry,
 		args.file = NULL;
 	}
 	aopen = &au_sbi(dir->i_sb)->si_aopen;
-	au_sphl_add(&aopen_node.hlist, aopen);
+	au_hbl_add(&aopen_node.hblist, aopen);
 	err = finish_open(file, dentry, au_do_aopen, opened);
-	au_sphl_del(&aopen_node.hlist, aopen);
+	au_hbl_del(&aopen_node.hblist, aopen);
 	AuTraceErr(err);
 	AuDbgFile(file);
 	if (aopen_node.h_file)
 		fput(aopen_node.h_file);
 
 out_unlock:
-	di_write_unlock(parent);
-	aufs_read_unlock(dentry, AuLock_DW);
+	if (unlocked)
+		si_read_unlock(dentry->d_sb);
+	else {
+		di_write_unlock(parent);
+		aufs_read_unlock(dentry, AuLock_DW);
+	}
 	AuDbgDentry(dentry);
 	if (unlikely(err < 0))
 		goto out;
@@ -420,10 +432,10 @@ static int au_wr_dir_cpup(struct dentry *dentry, struct dentry *parent,
 	if (!err && add_entry && !au_ftest_wrdir(add_entry, TMPFILE)) {
 		h_parent = au_h_dptr(parent, bcpup);
 		h_dir = d_inode(h_parent);
-		inode_lock_nested(h_dir, AuLsc_I_PARENT);
+		vfsub_inode_lock_shared_nested(h_dir, AuLsc_I_PARENT);
 		err = au_lkup_neg(dentry, bcpup, /*wh*/0);
 		/* todo: no unlock here */
-		inode_unlock(h_dir);
+		inode_unlock_shared(h_dir);
 
 		AuDbg("bcpup %d\n", bcpup);
 		if (!err) {
@@ -807,10 +819,10 @@ int au_pin_and_icpup(struct dentry *dentry, struct iattr *ia,
 	a->h_path.dentry = au_h_dptr(dentry, btop);
 	a->h_inode = d_inode(a->h_path.dentry);
 	if (ia && (ia->ia_valid & ATTR_SIZE)) {
-		inode_lock_nested(a->h_inode, AuLsc_I_CHILD);
+		vfsub_inode_lock_shared_nested(a->h_inode, AuLsc_I_CHILD);
 		if (ia->ia_size < i_size_read(a->h_inode))
 			sz = ia->ia_size;
-		inode_unlock(a->h_inode);
+		inode_unlock_shared(a->h_inode);
 	}
 
 	hi_wh = NULL;
@@ -885,6 +897,10 @@ static int aufs_setattr(struct dentry *dentry, struct iattr *ia)
 	inode = d_inode(dentry);
 	IMustLock(inode);
 
+	err = setattr_prepare(dentry, ia);
+	if (unlikely(err))
+		goto out;
+
 	err = -ENOMEM;
 	a = kzalloc(sizeof(*a), GFP_NOFS);
 	if (unlikely(!a))
@@ -903,7 +919,8 @@ static int aufs_setattr(struct dentry *dentry, struct iattr *ia)
 		/* currently ftruncate(2) only */
 		AuDebugOn(!d_is_reg(dentry));
 		file = ia->ia_file;
-		err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1);
+		err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1,
+					    /*fi_lsc*/0);
 		if (unlikely(err))
 			goto out_si;
 		ia->ia_file = au_hf_top(file);
@@ -993,7 +1010,7 @@ static int aufs_setattr(struct dentry *dentry, struct iattr *ia)
 out_si:
 	si_read_unlock(sb);
 out_kfree:
-	au_delayed_kfree(a);
+	kfree(a);
 out:
 	AuTraceErr(err);
 	return err;
@@ -1028,8 +1045,8 @@ static int au_h_path_to_set_attr(struct dentry *dentry,
 	return err;
 }
 
-ssize_t au_srxattr(struct dentry *dentry, struct inode *inode,
-		   struct au_srxattr *arg)
+ssize_t au_sxattr(struct dentry *dentry, struct inode *inode,
+		  struct au_sxattr *arg)
 {
 	int err;
 	struct path h_path;
@@ -1063,13 +1080,11 @@ ssize_t au_srxattr(struct dentry *dentry, struct inode *inode,
 				     arg->u.set.name, arg->u.set.value,
 				     arg->u.set.size, arg->u.set.flags);
 		break;
-	case AU_XATTR_REMOVE:
-		err = vfsub_removexattr(h_path.dentry, arg->u.remove.name);
-		break;
 	case AU_ACL_SET:
 		err = -EOPNOTSUPP;
 		h_inode = d_inode(h_path.dentry);
 		if (h_inode->i_op->set_acl)
+			/* this will call posix_acl_update_mode */
 			err = h_inode->i_op->set_acl(h_inode,
 						     arg->u.acl_set.acl,
 						     arg->u.acl_set.type);
@@ -1086,7 +1101,7 @@ ssize_t au_srxattr(struct dentry *dentry, struct inode *inode,
 	di_write_unlock(dentry);
 	si_read_unlock(sb);
 out_kfree:
-	au_delayed_kfree(a);
+	kfree(a);
 out:
 	AuTraceErr(err);
 	return err;
@@ -1123,11 +1138,12 @@ static void au_refresh_iattr(struct inode *inode, struct kstat *st,
 }
 
 /*
- * common routine for aufs_getattr() and aufs_getxattr().
+ * common routine for aufs_getattr() and au_getxattr().
  * returns zero or negative (an error).
  * @dentry will be read-locked in success.
  */
-int au_h_path_getattr(struct dentry *dentry, int force, struct path *h_path)
+int au_h_path_getattr(struct dentry *dentry, int force, struct path *h_path,
+		      int locked)
 {
 	int err;
 	unsigned int mnt_flags, sigen;
@@ -1144,6 +1160,9 @@ int au_h_path_getattr(struct dentry *dentry, int force, struct path *h_path)
 	mnt_flags = au_mntflags(sb);
 	udba_none = !!au_opt_test(mnt_flags, UDBA_NONE);
 
+	if (unlikely(locked))
+		goto body; /* skip locking dinfo */
+
 	/* support fstat(2) */
 	if (!d_unlinked(dentry) && !udba_none) {
 		sigen = au_sigen(sb);
@@ -1171,6 +1190,7 @@ int au_h_path_getattr(struct dentry *dentry, int force, struct path *h_path)
 	} else
 		di_read_lock_child(dentry, AuLock_IR);
 
+body:
 	inode = d_inode(dentry);
 	bindex = au_ibtop(inode);
 	h_path->mnt = au_sbr_mnt(sb, bindex);
@@ -1209,7 +1229,7 @@ static int aufs_getattr(struct vfsmount *mnt __maybe_unused,
 	err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM);
 	if (unlikely(err))
 		goto out;
-	err = au_h_path_getattr(dentry, /*force*/0, &h_path);
+	err = au_h_path_getattr(dentry, /*force*/0, &h_path, /*locked*/0);
 	if (unlikely(err))
 		goto out_si;
 	if (unlikely(!h_path.dentry))
@@ -1290,7 +1310,7 @@ static const char *aufs_get_link(struct dentry *dentry, struct inode *inode,
 	err = 0;
 	AuDbg("%pf\n", h_inode->i_op->get_link);
 	AuDbgDentry(h_dentry);
-	ret = h_inode->i_op->get_link(h_dentry, h_inode, done);
+	ret = vfs_get_link(h_dentry, done);
 	dput(h_dentry);
 	if (IS_ERR(ret))
 		err = PTR_ERR(ret);
@@ -1384,10 +1404,7 @@ struct inode_operations aufs_iop_nogetattr[AuIop_Last],
 		.getattr	= aufs_getattr,
 
 #ifdef CONFIG_AUFS_XATTR
-		.setxattr	= aufs_setxattr,
-		.getxattr	= aufs_getxattr,
 		.listxattr	= aufs_listxattr,
-		.removexattr	= aufs_removexattr,
 #endif
 
 		.readlink	= generic_readlink,
@@ -1416,10 +1433,7 @@ struct inode_operations aufs_iop_nogetattr[AuIop_Last],
 		.getattr	= aufs_getattr,
 
 #ifdef CONFIG_AUFS_XATTR
-		.setxattr	= aufs_setxattr,
-		.getxattr	= aufs_getxattr,
 		.listxattr	= aufs_listxattr,
-		.removexattr	= aufs_removexattr,
 #endif
 
 		.update_time	= aufs_update_time,
@@ -1437,10 +1451,7 @@ struct inode_operations aufs_iop_nogetattr[AuIop_Last],
 		.getattr	= aufs_getattr,
 
 #ifdef CONFIG_AUFS_XATTR
-		.setxattr	= aufs_setxattr,
-		.getxattr	= aufs_getxattr,
 		.listxattr	= aufs_listxattr,
-		.removexattr	= aufs_removexattr,
 #endif
 
 		.update_time	= aufs_update_time
diff --git a/fs/aufs/i_op_add.c b/fs/aufs/i_op_add.c
index 4ce147bc53d0..c3bd0f00f838 100644
--- a/fs/aufs/i_op_add.c
+++ b/fs/aufs/i_op_add.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 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
@@ -345,7 +345,7 @@ static int add_simple(struct inode *dir, struct dentry *dentry,
 	if (!try_aopen)
 		aufs_read_unlock(dentry, AuLock_DW);
 out_free:
-	au_delayed_kfree(a);
+	kfree(a);
 out:
 	return err;
 }
@@ -712,6 +712,10 @@ int aufs_link(struct dentry *src_dentry, struct inode *dir,
 		goto out_parent;
 	}
 
+	/*
+	 * aufs doesn't touch the credential so
+	 * security_dentry_create_files_as() is unnecrssary.
+	 */
 	if (au_opt_test(au_mntflags(sb), PLINK)) {
 		if (a->bdst < a->bsrc
 		    /* && h_src_dentry->d_sb != a->h_path.dentry->d_sb */)
@@ -809,7 +813,7 @@ int aufs_link(struct dentry *src_dentry, struct inode *dir,
 	}
 	aufs_read_and_write_unlock2(dentry, src_dentry);
 out_kfree:
-	au_delayed_kfree(a);
+	kfree(a);
 out:
 	AuTraceErr(err);
 	return err;
@@ -918,7 +922,7 @@ int aufs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
 	}
 	aufs_read_unlock(dentry, AuLock_DW);
 out_free:
-	au_delayed_kfree(a);
+	kfree(a);
 out:
 	return err;
 }
diff --git a/fs/aufs/i_op_del.c b/fs/aufs/i_op_del.c
index 27c1fb43571b..f67b74b2eb3a 100644
--- a/fs/aufs/i_op_del.c
+++ b/fs/aufs/i_op_del.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 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
@@ -394,7 +394,7 @@ int aufs_unlink(struct inode *dir, struct dentry *dentry)
 out_unlock:
 	aufs_read_unlock(dentry, AuLock_DW);
 out_free:
-	au_delayed_kfree(a);
+	kfree(a);
 out:
 	return err;
 }
@@ -504,7 +504,7 @@ int aufs_rmdir(struct inode *dir, struct dentry *dentry)
 out_unlock:
 	aufs_read_unlock(dentry, AuLock_DW);
 out_free:
-	au_delayed_kfree(a);
+	kfree(a);
 out:
 	AuTraceErr(err);
 	return err;
diff --git a/fs/aufs/i_op_ren.c b/fs/aufs/i_op_ren.c
index 200b4d547240..22124b1fa9e6 100644
--- a/fs/aufs/i_op_ren.c
+++ b/fs/aufs/i_op_ren.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 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
@@ -25,27 +25,37 @@
 enum { AuSRC, AuDST, AuSrcDst };
 enum { AuPARENT, AuCHILD, AuParentChild };
 
-#define AuRen_ISDIR	1
-#define AuRen_ISSAMEDIR	(1 << 1)
-#define AuRen_WHSRC	(1 << 2)
-#define AuRen_WHDST	(1 << 3)
-#define AuRen_MNT_WRITE	(1 << 4)
-#define AuRen_DT_DSTDIR	(1 << 5)
-#define AuRen_DIROPQ	(1 << 6)
+#define AuRen_ISDIR_SRC		1
+#define AuRen_ISDIR_DST		(1 << 1)
+#define AuRen_ISSAMEDIR		(1 << 2)
+#define AuRen_WHSRC		(1 << 3)
+#define AuRen_WHDST		(1 << 4)
+#define AuRen_MNT_WRITE		(1 << 5)
+#define AuRen_DT_DSTDIR		(1 << 6)
+#define AuRen_DIROPQ_SRC	(1 << 7)
+#define AuRen_DIROPQ_DST	(1 << 8)
+#define AuRen_DIRREN		(1 << 9)
+#define AuRen_DROPPED_SRC	(1 << 10)
+#define AuRen_DROPPED_DST	(1 << 11)
 #define au_ftest_ren(flags, name)	((flags) & AuRen_##name)
 #define au_fset_ren(flags, name) \
 	do { (flags) |= AuRen_##name; } while (0)
 #define au_fclr_ren(flags, name) \
 	do { (flags) &= ~AuRen_##name; } while (0)
 
+#ifndef CONFIG_AUFS_DIRREN
+#undef AuRen_DIRREN
+#define AuRen_DIRREN		0
+#endif
+
 struct au_ren_args {
 	struct {
 		struct dentry *dentry, *h_dentry, *parent, *h_parent,
 			*wh_dentry;
 		struct inode *dir, *inode;
-		struct au_hinode *hdir;
+		struct au_hinode *hdir, *hinode;
 		struct au_dtime dt[AuParentChild];
-		aufs_bindex_t btop;
+		aufs_bindex_t btop, bdiropq;
 	} sd[AuSrcDst];
 
 #define src_dentry	sd[AuSRC].dentry
@@ -56,9 +66,11 @@ struct au_ren_args {
 #define src_h_parent	sd[AuSRC].h_parent
 #define src_wh_dentry	sd[AuSRC].wh_dentry
 #define src_hdir	sd[AuSRC].hdir
+#define src_hinode	sd[AuSRC].hinode
 #define src_h_dir	sd[AuSRC].hdir->hi_inode
 #define src_dt		sd[AuSRC].dt
 #define src_btop	sd[AuSRC].btop
+#define src_bdiropq	sd[AuSRC].bdiropq
 
 #define dst_dentry	sd[AuDST].dentry
 #define dst_dir		sd[AuDST].dir
@@ -68,21 +80,27 @@ struct au_ren_args {
 #define dst_h_parent	sd[AuDST].h_parent
 #define dst_wh_dentry	sd[AuDST].wh_dentry
 #define dst_hdir	sd[AuDST].hdir
+#define dst_hinode	sd[AuDST].hinode
 #define dst_h_dir	sd[AuDST].hdir->hi_inode
 #define dst_dt		sd[AuDST].dt
 #define dst_btop	sd[AuDST].btop
+#define dst_bdiropq	sd[AuDST].bdiropq
 
 	struct dentry *h_trap;
 	struct au_branch *br;
-	struct au_hinode *src_hinode;
 	struct path h_path;
 	struct au_nhash whlist;
-	aufs_bindex_t btgt, src_bwh, src_bdiropq;
+	aufs_bindex_t btgt, src_bwh;
 
-	unsigned int flags;
+	struct {
+		unsigned short auren_flags;
+		unsigned char flags;	/* syscall parameter */
+		unsigned char exchange;
+	} __packed;
 
 	struct au_whtmp_rmdir *thargs;
 	struct dentry *h_dst;
+	struct au_hinode *h_root;
 };
 
 /* ---------------------------------------------------------------------- */
@@ -101,16 +119,29 @@ struct au_ren_args {
 	err = -EIO; \
 } while (0)
 
-static void au_ren_rev_diropq(int err, struct au_ren_args *a)
+static void au_ren_do_rev_diropq(int err, struct au_ren_args *a, int idx)
 {
 	int rerr;
+	struct dentry *d;
+#define src_or_dst(member) a->sd[idx].member
 
-	au_hn_inode_lock_nested(a->src_hinode, AuLsc_I_CHILD);
-	rerr = au_diropq_remove(a->src_dentry, a->btgt);
-	au_hn_inode_unlock(a->src_hinode);
-	au_set_dbdiropq(a->src_dentry, a->src_bdiropq);
+	d = src_or_dst(dentry); /* {src,dst}_dentry */
+	au_hn_inode_lock_nested(src_or_dst(hinode), AuLsc_I_CHILD);
+	rerr = au_diropq_remove(d, a->btgt);
+	au_hn_inode_unlock(src_or_dst(hinode));
+	au_set_dbdiropq(d, src_or_dst(bdiropq));
 	if (rerr)
-		RevertFailure("remove diropq %pd", a->src_dentry);
+		RevertFailure("remove diropq %pd", d);
+
+#undef src_or_dst_
+}
+
+static void au_ren_rev_diropq(int err, struct au_ren_args *a)
+{
+	if (au_ftest_ren(a->auren_flags, DIROPQ_SRC))
+		au_ren_do_rev_diropq(err, a, AuSRC);
+	if (au_ftest_ren(a->auren_flags, DIROPQ_DST))
+		au_ren_do_rev_diropq(err, a, AuDST);
 }
 
 static void au_ren_rev_rename(int err, struct au_ren_args *a)
@@ -129,7 +160,7 @@ static void au_ren_rev_rename(int err, struct au_ren_args *a)
 	delegated = NULL;
 	rerr = vfsub_rename(a->dst_h_dir,
 			    au_h_dptr(a->src_dentry, a->btgt),
-			    a->src_h_dir, &a->h_path, &delegated);
+			    a->src_h_dir, &a->h_path, &delegated, a->flags);
 	if (unlikely(rerr == -EWOULDBLOCK)) {
 		pr_warn("cannot retry for NFSv4 delegation"
 			" for an internal rename\n");
@@ -162,7 +193,7 @@ static void au_ren_rev_whtmp(int err, struct au_ren_args *a)
 
 	delegated = NULL;
 	rerr = vfsub_rename(a->dst_h_dir, a->h_dst, a->dst_h_dir, &a->h_path,
-			    &delegated);
+			    &delegated, a->flags);
 	if (unlikely(rerr == -EWOULDBLOCK)) {
 		pr_warn("cannot retry for NFSv4 delegation"
 			" for an internal rename\n");
@@ -204,13 +235,11 @@ static int au_ren_or_cpup(struct au_ren_args *a)
 	d = a->src_dentry;
 	if (au_dbtop(d) == a->btgt) {
 		a->h_path.dentry = a->dst_h_dentry;
-		if (au_ftest_ren(a->flags, DIROPQ)
-		    && au_dbdiropq(d) == a->btgt)
-			au_fclr_ren(a->flags, DIROPQ);
 		AuDebugOn(au_dbtop(d) != a->btgt);
 		delegated = NULL;
 		err = vfsub_rename(a->src_h_dir, au_h_dptr(d, a->btgt),
-				   a->dst_h_dir, &a->h_path, &delegated);
+				   a->dst_h_dir, &a->h_path, &delegated,
+				   a->flags);
 		if (unlikely(err == -EWOULDBLOCK)) {
 			pr_warn("cannot retry for NFSv4 delegation"
 				" for an internal rename\n");
@@ -254,127 +283,186 @@ static int au_ren_del_whtmp(struct au_ren_args *a)
 }
 
 /* make it 'opaque' dir. */
-static int au_ren_diropq(struct au_ren_args *a)
+static int au_ren_do_diropq(struct au_ren_args *a, int idx)
 {
 	int err;
-	struct dentry *diropq;
+	struct dentry *d, *diropq;
+#define src_or_dst(member) a->sd[idx].member
 
 	err = 0;
-	a->src_bdiropq = au_dbdiropq(a->src_dentry);
-	a->src_hinode = au_hi(a->src_inode, a->btgt);
-	au_hn_inode_lock_nested(a->src_hinode, AuLsc_I_CHILD);
-	diropq = au_diropq_create(a->src_dentry, a->btgt);
-	au_hn_inode_unlock(a->src_hinode);
+	d = src_or_dst(dentry); /* {src,dst}_dentry */
+	src_or_dst(bdiropq) = au_dbdiropq(d);
+	src_or_dst(hinode) = au_hi(src_or_dst(inode), a->btgt);
+	au_hn_inode_lock_nested(src_or_dst(hinode), AuLsc_I_CHILD);
+	diropq = au_diropq_create(d, a->btgt);
+	au_hn_inode_unlock(src_or_dst(hinode));
 	if (IS_ERR(diropq))
 		err = PTR_ERR(diropq);
 	else
 		dput(diropq);
 
+#undef src_or_dst_
 	return err;
 }
 
-static int do_rename(struct au_ren_args *a)
+static int au_ren_diropq(struct au_ren_args *a)
 {
 	int err;
-	struct dentry *d, *h_d;
+	unsigned char always;
+	struct dentry *d;
 
-	/* prepare workqueue args for asynchronous rmdir */
-	h_d = a->dst_h_dentry;
-	if (au_ftest_ren(a->flags, ISDIR) && d_is_positive(h_d)) {
-		err = -ENOMEM;
-		a->thargs = au_whtmp_rmdir_alloc(a->src_dentry->d_sb, GFP_NOFS);
-		if (unlikely(!a->thargs))
-			goto out;
-		a->h_dst = dget(h_d);
-	}
-
-	/* create whiteout for src_dentry */
-	if (au_ftest_ren(a->flags, WHSRC)) {
-		a->src_bwh = au_dbwh(a->src_dentry);
-		AuDebugOn(a->src_bwh >= 0);
-		a->src_wh_dentry
-			= au_wh_create(a->src_dentry, a->btgt, a->src_h_parent);
-		err = PTR_ERR(a->src_wh_dentry);
-		if (IS_ERR(a->src_wh_dentry))
-			goto out_thargs;
-	}
-
-	/* lookup whiteout for dentry */
-	if (au_ftest_ren(a->flags, WHDST)) {
-		h_d = au_wh_lkup(a->dst_h_parent, &a->dst_dentry->d_name,
-				 a->br);
-		err = PTR_ERR(h_d);
-		if (IS_ERR(h_d))
-			goto out_whsrc;
-		if (d_is_negative(h_d))
-			dput(h_d);
-		else
-			a->dst_wh_dentry = h_d;
-	}
-
-	/* rename dentry to tmpwh */
-	if (a->thargs) {
-		err = au_whtmp_ren(a->dst_h_dentry, a->br);
+	err = 0;
+	d = a->dst_dentry; /* already renamed on the branch */
+	always = !!au_opt_test(au_mntflags(d->d_sb), ALWAYS_DIROPQ);
+	if (au_ftest_ren(a->auren_flags, ISDIR_SRC)
+	    && !au_ftest_ren(a->auren_flags, DIRREN)
+	    && a->btgt != au_dbdiropq(a->src_dentry)
+	    && (a->dst_wh_dentry
+		|| a->btgt <= au_dbdiropq(d)
+		/* hide the lower to keep xino */
+		/* the lowers may not be a dir, but we hide them anyway */
+		|| a->btgt < au_dbbot(d)
+		|| always)) {
+		AuDbg("here\n");
+		err = au_ren_do_diropq(a, AuSRC);
 		if (unlikely(err))
-			goto out_whdst;
+			goto out;
+		au_fset_ren(a->auren_flags, DIROPQ_SRC);
+	}
+	if (!a->exchange)
+		goto out; /* success */
 
-		d = a->dst_dentry;
-		au_set_h_dptr(d, a->btgt, NULL);
-		err = au_lkup_neg(d, a->btgt, /*wh*/0);
+	d = a->src_dentry; /* already renamed on the branch */
+	if (au_ftest_ren(a->auren_flags, ISDIR_DST)
+	    && a->btgt != au_dbdiropq(a->dst_dentry)
+	    && (a->btgt < au_dbdiropq(d)
+		|| a->btgt < au_dbbot(d)
+		|| always)) {
+		AuDbgDentry(a->src_dentry);
+		AuDbgDentry(a->dst_dentry);
+		err = au_ren_do_diropq(a, AuDST);
 		if (unlikely(err))
-			goto out_whtmp;
-		a->dst_h_dentry = au_h_dptr(d, a->btgt);
+			goto out_rev_src;
+		au_fset_ren(a->auren_flags, DIROPQ_DST);
+	}
+	goto out; /* success */
+
+out_rev_src:
+	AuDbg("err %d, reverting src\n", err);
+	au_ren_rev_diropq(err, a);
+out:
+	return err;
+}
+
+static int do_rename(struct au_ren_args *a)
+{
+	int err;
+	struct dentry *d, *h_d;
+
+	if (!a->exchange) {
+		/* prepare workqueue args for asynchronous rmdir */
+		h_d = a->dst_h_dentry;
+		if (au_ftest_ren(a->auren_flags, ISDIR_DST)
+		    /* && !au_ftest_ren(a->auren_flags, DIRREN) */
+		    && d_is_positive(h_d)) {
+			err = -ENOMEM;
+			a->thargs = au_whtmp_rmdir_alloc(a->src_dentry->d_sb,
+							 GFP_NOFS);
+			if (unlikely(!a->thargs))
+				goto out;
+			a->h_dst = dget(h_d);
+		}
+
+		/* create whiteout for src_dentry */
+		if (au_ftest_ren(a->auren_flags, WHSRC)) {
+			a->src_bwh = au_dbwh(a->src_dentry);
+			AuDebugOn(a->src_bwh >= 0);
+			a->src_wh_dentry = au_wh_create(a->src_dentry, a->btgt,
+							a->src_h_parent);
+			err = PTR_ERR(a->src_wh_dentry);
+			if (IS_ERR(a->src_wh_dentry))
+				goto out_thargs;
+		}
+
+		/* lookup whiteout for dentry */
+		if (au_ftest_ren(a->auren_flags, WHDST)) {
+			h_d = au_wh_lkup(a->dst_h_parent,
+					 &a->dst_dentry->d_name, a->br);
+			err = PTR_ERR(h_d);
+			if (IS_ERR(h_d))
+				goto out_whsrc;
+			if (d_is_negative(h_d))
+				dput(h_d);
+			else
+				a->dst_wh_dentry = h_d;
+		}
+
+		/* rename dentry to tmpwh */
+		if (a->thargs) {
+			err = au_whtmp_ren(a->dst_h_dentry, a->br);
+			if (unlikely(err))
+				goto out_whdst;
+
+			d = a->dst_dentry;
+			au_set_h_dptr(d, a->btgt, NULL);
+			err = au_lkup_neg(d, a->btgt, /*wh*/0);
+			if (unlikely(err))
+				goto out_whtmp;
+			a->dst_h_dentry = au_h_dptr(d, a->btgt);
+		}
 	}
 
 	BUG_ON(d_is_positive(a->dst_h_dentry) && a->src_btop != a->btgt);
+#if 0
+	BUG_ON(!au_ftest_ren(a->auren_flags, DIRREN)
+	       && d_is_positive(a->dst_h_dentry)
+	       && a->src_btop != a->btgt);
+#endif
 
 	/* rename by vfs_rename or cpup */
-	d = a->dst_dentry;
-	if (au_ftest_ren(a->flags, ISDIR)
-	    && (a->dst_wh_dentry
-		|| au_dbdiropq(d) == a->btgt
-		/* hide the lower to keep xino */
-		|| a->btgt < au_dbbot(d)
-		|| au_opt_test(au_mntflags(d->d_sb), ALWAYS_DIROPQ)))
-		au_fset_ren(a->flags, DIROPQ);
 	err = au_ren_or_cpup(a);
 	if (unlikely(err))
 		/* leave the copied-up one */
 		goto out_whtmp;
 
 	/* make dir opaque */
-	if (au_ftest_ren(a->flags, DIROPQ)) {
-		err = au_ren_diropq(a);
-		if (unlikely(err))
-			goto out_rename;
-	}
+	err = au_ren_diropq(a);
+	if (unlikely(err))
+		goto out_rename;
 
 	/* update target timestamps */
+	if (a->exchange) {
+		AuDebugOn(au_dbtop(a->dst_dentry) != a->btgt);
+		a->h_path.dentry = au_h_dptr(a->dst_dentry, a->btgt);
+		vfsub_update_h_iattr(&a->h_path, /*did*/NULL); /*ignore*/
+		a->dst_inode->i_ctime = d_inode(a->h_path.dentry)->i_ctime;
+	}
 	AuDebugOn(au_dbtop(a->src_dentry) != a->btgt);
 	a->h_path.dentry = au_h_dptr(a->src_dentry, a->btgt);
 	vfsub_update_h_iattr(&a->h_path, /*did*/NULL); /*ignore*/
 	a->src_inode->i_ctime = d_inode(a->h_path.dentry)->i_ctime;
 
-	/* remove whiteout for dentry */
-	if (a->dst_wh_dentry) {
-		a->h_path.dentry = a->dst_wh_dentry;
-		err = au_wh_unlink_dentry(a->dst_h_dir, &a->h_path,
-					  a->dst_dentry);
-		if (unlikely(err))
-			goto out_diropq;
-	}
+	if (!a->exchange) {
+		/* remove whiteout for dentry */
+		if (a->dst_wh_dentry) {
+			a->h_path.dentry = a->dst_wh_dentry;
+			err = au_wh_unlink_dentry(a->dst_h_dir, &a->h_path,
+						  a->dst_dentry);
+			if (unlikely(err))
+				goto out_diropq;
+		}
 
-	/* remove whtmp */
-	if (a->thargs)
-		au_ren_del_whtmp(a); /* ignore this error */
+		/* remove whtmp */
+		if (a->thargs)
+			au_ren_del_whtmp(a); /* ignore this error */
 
-	au_fhsm_wrote(a->src_dentry->d_sb, a->btgt, /*force*/0);
+		au_fhsm_wrote(a->src_dentry->d_sb, a->btgt, /*force*/0);
+	}
 	err = 0;
 	goto out_success;
 
 out_diropq:
-	if (au_ftest_ren(a->flags, DIROPQ))
-		au_ren_rev_diropq(err, a);
+	au_ren_rev_diropq(err, a);
 out_rename:
 	au_ren_rev_rename(err, a);
 	dput(a->h_dst);
@@ -412,25 +500,35 @@ static int may_rename_dstdir(struct dentry *dentry, struct au_nhash *whlist)
 }
 
 /*
- * test if @dentry dir can be rename source or not.
- * if it can, return 0 and @children is filled.
+ * test if @a->src_dentry dir can be rename source or not.
+ * if it can, return 0.
  * success means,
  * - it is a logically empty dir.
  * - or, it exists on writable branch and has no children including whiteouts
- *       on the lower branch.
+ *   on the lower branch unless DIRREN is on.
  */
-static int may_rename_srcdir(struct dentry *dentry, aufs_bindex_t btgt)
+static int may_rename_srcdir(struct au_ren_args *a)
 {
 	int err;
 	unsigned int rdhash;
-	aufs_bindex_t btop;
+	aufs_bindex_t btop, btgt;
+	struct dentry *dentry;
+	struct super_block *sb;
+	struct au_sbinfo *sbinfo;
+
+	dentry = a->src_dentry;
+	sb = dentry->d_sb;
+	sbinfo = au_sbi(sb);
+	if (au_opt_test(sbinfo->si_mntflags, DIRREN))
+		au_fset_ren(a->auren_flags, DIRREN);
 
+	btgt = a->btgt;
 	btop = au_dbtop(dentry);
 	if (btop != btgt) {
 		struct au_nhash whlist;
 
-		SiMustAnyLock(dentry->d_sb);
-		rdhash = au_sbi(dentry->d_sb)->si_rdhash;
+		SiMustAnyLock(sb);
+		rdhash = sbinfo->si_rdhash;
 		if (!rdhash)
 			rdhash = au_rdhash_est(au_dir_size(/*file*/NULL,
 							   dentry));
@@ -449,9 +547,13 @@ static int may_rename_srcdir(struct dentry *dentry, aufs_bindex_t btgt)
 
 out:
 	if (err == -ENOTEMPTY) {
-		AuWarn1("renaming dir who has child(ren) on multiple branches,"
-			" is not supported\n");
-		err = -EXDEV;
+		if (au_ftest_ren(a->auren_flags, DIRREN)) {
+			err = 0;
+		} else {
+			AuWarn1("renaming dir who has child(ren) on multiple "
+				"branches, is not supported\n");
+			err = -EXDEV;
+		}
 	}
 	return err;
 }
@@ -467,7 +569,7 @@ static int au_ren_may_dir(struct au_ren_args *a)
 	SiMustAnyLock(d->d_sb);
 
 	err = 0;
-	if (au_ftest_ren(a->flags, ISDIR) && a->dst_inode) {
+	if (au_ftest_ren(a->auren_flags, ISDIR_DST) && a->dst_inode) {
 		rdhash = au_sbi(d->d_sb)->si_rdhash;
 		if (!rdhash)
 			rdhash = au_rdhash_est(au_dir_size(/*file*/NULL, d));
@@ -475,9 +577,12 @@ static int au_ren_may_dir(struct au_ren_args *a)
 		if (unlikely(err))
 			goto out;
 
-		au_set_dbtop(d, a->dst_btop);
-		err = may_rename_dstdir(d, &a->whlist);
-		au_set_dbtop(d, a->btgt);
+		if (!a->exchange) {
+			au_set_dbtop(d, a->dst_btop);
+			err = may_rename_dstdir(d, &a->whlist);
+			au_set_dbtop(d, a->btgt);
+		} else
+			err = may_rename_srcdir(a);
 	}
 	a->dst_h_dentry = au_h_dptr(d, au_dbtop(d));
 	if (unlikely(err))
@@ -485,8 +590,8 @@ static int au_ren_may_dir(struct au_ren_args *a)
 
 	d = a->src_dentry;
 	a->src_h_dentry = au_h_dptr(d, au_dbtop(d));
-	if (au_ftest_ren(a->flags, ISDIR)) {
-		err = may_rename_srcdir(d, a->btgt);
+	if (au_ftest_ren(a->auren_flags, ISDIR_SRC)) {
+		err = may_rename_srcdir(a);
 		if (unlikely(err)) {
 			au_nhash_wh_free(&a->whlist);
 			a->whlist.nh_num = 0;
@@ -509,7 +614,7 @@ static int au_may_ren(struct au_ren_args *a)
 
 	if (a->src_btop == a->btgt) {
 		err = au_may_del(a->src_dentry, a->btgt, a->src_h_parent,
-				 au_ftest_ren(a->flags, ISDIR));
+				 au_ftest_ren(a->auren_flags, ISDIR_SRC));
 		if (unlikely(err))
 			goto out;
 		err = -EINVAL;
@@ -526,7 +631,7 @@ static int au_may_ren(struct au_ren_args *a)
 		goto out;
 
 	err = -EIO;
-	isdir = !!au_ftest_ren(a->flags, ISDIR);
+	isdir = !!au_ftest_ren(a->auren_flags, ISDIR_DST);
 	if (d_really_is_negative(a->dst_dentry)) {
 		if (d_is_negative(a->dst_h_dentry))
 			err = au_may_add(a->dst_dentry, a->btgt,
@@ -576,7 +681,10 @@ static void au_ren_unlock(struct au_ren_args *a)
 {
 	vfsub_unlock_rename(a->src_h_parent, a->src_hdir,
 			    a->dst_h_parent, a->dst_hdir);
-	if (au_ftest_ren(a->flags, MNT_WRITE))
+	if (au_ftest_ren(a->auren_flags, DIRREN)
+	    && a->h_root)
+		au_hn_inode_unlock(a->h_root);
+	if (au_ftest_ren(a->auren_flags, MNT_WRITE))
 		vfsub_mnt_drop_write(au_br_mnt(a->br));
 }
 
@@ -594,7 +702,24 @@ static int au_ren_lock(struct au_ren_args *a)
 	err = vfsub_mnt_want_write(au_br_mnt(a->br));
 	if (unlikely(err))
 		goto out;
-	au_fset_ren(a->flags, MNT_WRITE);
+	au_fset_ren(a->auren_flags, MNT_WRITE);
+	if (au_ftest_ren(a->auren_flags, DIRREN)) {
+		struct dentry *root;
+		struct inode *dir;
+
+		/*
+		 * sbinfo is already locked, so this ii_read_lock is
+		 * unnecessary. but our debugging feature checks it.
+		 */
+		root = a->src_inode->i_sb->s_root;
+		if (root != a->src_parent && root != a->dst_parent) {
+			dir = d_inode(root);
+			ii_read_lock_parent3(dir);
+			a->h_root = au_hi(dir, a->btgt);
+			ii_read_unlock(dir);
+			au_hn_inode_lock_nested(a->h_root, AuLsc_I_PARENT3);
+		}
+	}
 	a->h_trap = vfsub_lock_rename(a->src_h_parent, a->src_hdir,
 				      a->dst_h_parent, a->dst_hdir);
 	udba = au_opt_udba(a->src_dentry->d_sb);
@@ -627,20 +752,30 @@ static void au_ren_refresh_dir(struct au_ren_args *a)
 
 	dir = a->dst_dir;
 	dir->i_version++;
-	if (au_ftest_ren(a->flags, ISDIR)) {
+	if (au_ftest_ren(a->auren_flags, ISDIR_SRC)) {
 		/* is this updating defined in POSIX? */
 		au_cpup_attr_timesizes(a->src_inode);
 		au_cpup_attr_nlink(dir, /*force*/1);
 	}
-
 	au_dir_ts(dir, a->btgt);
 
-	if (au_ftest_ren(a->flags, ISSAMEDIR))
+	if (a->exchange) {
+		dir = a->src_dir;
+		dir->i_version++;
+		if (au_ftest_ren(a->auren_flags, ISDIR_DST)) {
+			/* is this updating defined in POSIX? */
+			au_cpup_attr_timesizes(a->dst_inode);
+			au_cpup_attr_nlink(dir, /*force*/1);
+		}
+		au_dir_ts(dir, a->btgt);
+	}
+
+	if (au_ftest_ren(a->auren_flags, ISSAMEDIR))
 		return;
 
 	dir = a->src_dir;
 	dir->i_version++;
-	if (au_ftest_ren(a->flags, ISDIR))
+	if (au_ftest_ren(a->auren_flags, ISDIR_SRC))
 		au_cpup_attr_nlink(dir, /*force*/1);
 	au_dir_ts(dir, a->btgt);
 }
@@ -660,13 +795,16 @@ static void au_ren_refresh(struct au_ren_args *a)
 
 	i = a->dst_inode;
 	if (i) {
-		if (!au_ftest_ren(a->flags, ISDIR))
-			vfsub_drop_nlink(i);
-		else {
-			vfsub_dead_dir(i);
-			au_cpup_attr_timesizes(i);
-		}
-		au_update_dbrange(d, /*do_put_zero*/1);
+		if (!a->exchange) {
+			if (!au_ftest_ren(a->auren_flags, ISDIR_DST))
+				vfsub_drop_nlink(i);
+			else {
+				vfsub_dead_dir(i);
+				au_cpup_attr_timesizes(i);
+			}
+			au_update_dbrange(d, /*do_put_zero*/1);
+		} else
+			au_cpup_attr_nlink(i, /*force*/1);
 	} else {
 		bbot = a->btgt;
 		for (bindex = au_dbtop(d); bindex < bbot; bindex++)
@@ -677,6 +815,14 @@ static void au_ren_refresh(struct au_ren_args *a)
 		au_update_dbrange(d, /*do_put_zero*/0);
 	}
 
+	if (a->exchange
+	    || au_ftest_ren(a->auren_flags, DIRREN)) {
+		d_drop(a->src_dentry);
+		if (au_ftest_ren(a->auren_flags, DIRREN))
+			au_set_dbwh(a->src_dentry, -1);
+		return;
+	}
+
 	d = a->src_dentry;
 	au_set_dbwh(d, -1);
 	bbot = au_dbbot(d);
@@ -739,7 +885,8 @@ static int au_ren_wbr(struct au_ren_args *a)
 
 	a->src_btop = au_dbtop(a->src_dentry);
 	a->dst_btop = au_dbtop(a->dst_dentry);
-	if (au_ftest_ren(a->flags, ISDIR))
+	if (au_ftest_ren(a->auren_flags, ISDIR_SRC)
+	    || au_ftest_ren(a->auren_flags, ISDIR_DST))
 		au_fset_wrdir(wr_dir_args.flags, ISDIR);
 	wr_dir_args.force_btgt = a->src_btop;
 	if (a->dst_inode && a->dst_btop < a->src_btop)
@@ -747,6 +894,8 @@ static int au_ren_wbr(struct au_ren_args *a)
 	wr_dir_args.force_btgt = au_wbr(a->dst_dentry, wr_dir_args.force_btgt);
 	err = au_wr_dir(a->dst_dentry, a->src_dentry, &wr_dir_args);
 	a->btgt = err;
+	if (a->exchange)
+		au_update_dbtop(a->dst_dentry);
 
 	return err;
 }
@@ -755,19 +904,20 @@ static void au_ren_dt(struct au_ren_args *a)
 {
 	a->h_path.dentry = a->src_h_parent;
 	au_dtime_store(a->src_dt + AuPARENT, a->src_parent, &a->h_path);
-	if (!au_ftest_ren(a->flags, ISSAMEDIR)) {
+	if (!au_ftest_ren(a->auren_flags, ISSAMEDIR)) {
 		a->h_path.dentry = a->dst_h_parent;
 		au_dtime_store(a->dst_dt + AuPARENT, a->dst_parent, &a->h_path);
 	}
 
-	au_fclr_ren(a->flags, DT_DSTDIR);
-	if (!au_ftest_ren(a->flags, ISDIR))
+	au_fclr_ren(a->auren_flags, DT_DSTDIR);
+	if (!au_ftest_ren(a->auren_flags, ISDIR_SRC)
+	    && !a->exchange)
 		return;
 
 	a->h_path.dentry = a->src_h_dentry;
 	au_dtime_store(a->src_dt + AuCHILD, a->src_dentry, &a->h_path);
 	if (d_is_positive(a->dst_h_dentry)) {
-		au_fset_ren(a->flags, DT_DSTDIR);
+		au_fset_ren(a->auren_flags, DT_DSTDIR);
 		a->h_path.dentry = a->dst_h_dentry;
 		au_dtime_store(a->dst_dt + AuCHILD, a->dst_dentry, &a->h_path);
 	}
@@ -779,17 +929,17 @@ static void au_ren_rev_dt(int err, struct au_ren_args *a)
 	struct inode *h_inode;
 
 	au_dtime_revert(a->src_dt + AuPARENT);
-	if (!au_ftest_ren(a->flags, ISSAMEDIR))
+	if (!au_ftest_ren(a->auren_flags, ISSAMEDIR))
 		au_dtime_revert(a->dst_dt + AuPARENT);
 
-	if (au_ftest_ren(a->flags, ISDIR) && err != -EIO) {
+	if (au_ftest_ren(a->auren_flags, ISDIR_SRC) && err != -EIO) {
 		h_d = a->src_dt[AuCHILD].dt_h_path.dentry;
 		h_inode = d_inode(h_d);
 		inode_lock_nested(h_inode, AuLsc_I_CHILD);
 		au_dtime_revert(a->src_dt + AuCHILD);
 		inode_unlock(h_inode);
 
-		if (au_ftest_ren(a->flags, DT_DSTDIR)) {
+		if (au_ftest_ren(a->auren_flags, DT_DSTDIR)) {
 			h_d = a->dst_dt[AuCHILD].dt_h_path.dentry;
 			h_inode = d_inode(h_d);
 			inode_lock_nested(h_inode, AuLsc_I_CHILD);
@@ -802,22 +952,31 @@ static void au_ren_rev_dt(int err, struct au_ren_args *a)
 /* ---------------------------------------------------------------------- */
 
 int aufs_rename(struct inode *_src_dir, struct dentry *_src_dentry,
-		struct inode *_dst_dir, struct dentry *_dst_dentry)
+		struct inode *_dst_dir, struct dentry *_dst_dentry,
+		unsigned int _flags)
 {
-	int err, flags;
+	int err, lock_flags;
+	void *rev;
 	/* reduce stack space */
 	struct au_ren_args *a;
+	struct au_pin pin;
 
-	AuDbg("%pd, %pd\n", _src_dentry, _dst_dentry);
+	AuDbg("%pd, %pd, 0x%x\n", _src_dentry, _dst_dentry, _flags);
 	IMustLock(_src_dir);
 	IMustLock(_dst_dir);
 
+	err = -EINVAL;
+	if (unlikely(_flags & RENAME_WHITEOUT))
+		goto out;
+
 	err = -ENOMEM;
 	BUILD_BUG_ON(sizeof(*a) > PAGE_SIZE);
 	a = kzalloc(sizeof(*a), GFP_NOFS);
 	if (unlikely(!a))
 		goto out;
 
+	a->flags = _flags;
+	a->exchange = _flags & RENAME_EXCHANGE;
 	a->src_dir = _src_dir;
 	a->src_dentry = _src_dentry;
 	a->src_inode = NULL;
@@ -831,20 +990,34 @@ int aufs_rename(struct inode *_src_dir, struct dentry *_src_dentry,
 		a->dst_inode = d_inode(a->dst_dentry);
 	a->dst_parent = a->dst_dentry->d_parent; /* dir inode is locked */
 	if (a->dst_inode) {
-		IMustLock(a->dst_inode);
+		/*
+		 * if EXCHANGE && src is non-dir && dst is dir,
+		 * dst is not locked.
+		 */
+		/* IMustLock(a->dst_inode); */
 		au_igrab(a->dst_inode);
 	}
 
 	err = -ENOTDIR;
-	flags = AuLock_FLUSH | AuLock_NOPLM | AuLock_GEN;
+	lock_flags = AuLock_FLUSH | AuLock_NOPLM | AuLock_GEN;
 	if (d_is_dir(a->src_dentry)) {
-		au_fset_ren(a->flags, ISDIR);
-		if (unlikely(d_really_is_positive(a->dst_dentry)
+		au_fset_ren(a->auren_flags, ISDIR_SRC);
+		if (unlikely(!a->exchange
+			     && d_really_is_positive(a->dst_dentry)
 			     && !d_is_dir(a->dst_dentry)))
 			goto out_free;
-		flags |= AuLock_DIRS;
+		lock_flags |= AuLock_DIRS;
 	}
-	err = aufs_read_and_write_lock2(a->dst_dentry, a->src_dentry, flags);
+	if (a->dst_inode && d_is_dir(a->dst_dentry)) {
+		au_fset_ren(a->auren_flags, ISDIR_DST);
+		if (unlikely(!a->exchange
+			     && d_really_is_positive(a->src_dentry)
+			     && !d_is_dir(a->src_dentry)))
+			goto out_free;
+		lock_flags |= AuLock_DIRS;
+	}
+	err = aufs_read_and_write_lock2(a->dst_dentry, a->src_dentry,
+					lock_flags);
 	if (unlikely(err))
 		goto out_free;
 
@@ -854,14 +1027,14 @@ int aufs_rename(struct inode *_src_dir, struct dentry *_src_dentry,
 	err = -ENOENT;
 	if (a->dst_inode) {
 		/*
-		 * If it is a dir, VFS unhash dst_dentry before this
+		 * If it is a dir, VFS unhash it before this
 		 * function. It means we cannot rely upon d_unhashed().
 		 */
 		if (unlikely(!a->dst_inode->i_nlink))
 			goto out_unlock;
-		if (!S_ISDIR(a->dst_inode->i_mode)) {
+		if (!au_ftest_ren(a->auren_flags, ISDIR_DST)) {
 			err = au_d_hashed_positive(a->dst_dentry);
-			if (unlikely(err))
+			if (unlikely(err && !a->exchange))
 				goto out_unlock;
 		} else if (unlikely(IS_DEADDIR(a->dst_inode)))
 			goto out_unlock;
@@ -877,7 +1050,7 @@ int aufs_rename(struct inode *_src_dir, struct dentry *_src_dentry,
 	if (unlikely(d_inode(a->dst_parent) == d_inode(a->src_dentry)))
 		goto out_unlock;
 
-	au_fset_ren(a->flags, ISSAMEDIR); /* temporary */
+	au_fset_ren(a->auren_flags, ISSAMEDIR); /* temporary */
 	di_write_lock_parent(a->dst_parent);
 
 	/* which branch we process */
@@ -894,39 +1067,43 @@ int aufs_rename(struct inode *_src_dir, struct dentry *_src_dentry,
 
 	/* prepare the writable parent dir on the same branch */
 	if (a->dst_btop == a->btgt) {
-		au_fset_ren(a->flags, WHDST);
+		au_fset_ren(a->auren_flags, WHDST);
 	} else {
 		err = au_cpup_dirs(a->dst_dentry, a->btgt);
 		if (unlikely(err))
 			goto out_children;
 	}
 
-	if (a->src_dir != a->dst_dir) {
-		/*
-		 * this temporary unlock is safe,
-		 * because both dir->i_mutex are locked.
-		 */
-		di_write_unlock(a->dst_parent);
-		di_write_lock_parent(a->src_parent);
-		err = au_wr_dir_need_wh(a->src_dentry,
-					au_ftest_ren(a->flags, ISDIR),
-					&a->btgt);
-		di_write_unlock(a->src_parent);
-		di_write_lock2_parent(a->src_parent, a->dst_parent, /*isdir*/1);
-		au_fclr_ren(a->flags, ISSAMEDIR);
-	} else
-		err = au_wr_dir_need_wh(a->src_dentry,
-					au_ftest_ren(a->flags, ISDIR),
-					&a->btgt);
+	err = 0;
+	if (!a->exchange) {
+		if (a->src_dir != a->dst_dir) {
+			/*
+			 * this temporary unlock is safe,
+			 * because both dir->i_mutex are locked.
+			 */
+			di_write_unlock(a->dst_parent);
+			di_write_lock_parent(a->src_parent);
+			err = au_wr_dir_need_wh(a->src_dentry,
+						au_ftest_ren(a->auren_flags,
+							     ISDIR_SRC),
+						&a->btgt);
+			di_write_unlock(a->src_parent);
+			di_write_lock2_parent(a->src_parent, a->dst_parent,
+					      /*isdir*/1);
+			au_fclr_ren(a->auren_flags, ISSAMEDIR);
+		} else
+			err = au_wr_dir_need_wh(a->src_dentry,
+						au_ftest_ren(a->auren_flags,
+							     ISDIR_SRC),
+						&a->btgt);
+	}
 	if (unlikely(err < 0))
 		goto out_children;
 	if (err)
-		au_fset_ren(a->flags, WHSRC);
+		au_fset_ren(a->auren_flags, WHSRC);
 
 	/* cpup src */
 	if (a->src_btop != a->btgt) {
-		struct au_pin pin;
-
 		err = au_pin(&pin, a->src_dentry, a->btgt,
 			     au_opt_udba(a->src_dentry->d_sb),
 			     AuPin_DI_LOCKED | AuPin_MNT_WRITE);
@@ -947,7 +1124,32 @@ int aufs_rename(struct inode *_src_dir, struct dentry *_src_dentry,
 			goto out_children;
 		a->src_btop = a->btgt;
 		a->src_h_dentry = au_h_dptr(a->src_dentry, a->btgt);
-		au_fset_ren(a->flags, WHSRC);
+		if (!a->exchange)
+			au_fset_ren(a->auren_flags, WHSRC);
+	}
+
+	/* cpup dst */
+	if (a->exchange && a->dst_inode
+	    && a->dst_btop != a->btgt) {
+		err = au_pin(&pin, a->dst_dentry, a->btgt,
+			     au_opt_udba(a->dst_dentry->d_sb),
+			     AuPin_DI_LOCKED | AuPin_MNT_WRITE);
+		if (!err) {
+			struct au_cp_generic cpg = {
+				.dentry	= a->dst_dentry,
+				.bdst	= a->btgt,
+				.bsrc	= a->dst_btop,
+				.len	= -1,
+				.pin	= &pin,
+				.flags	= AuCpup_DTIME | AuCpup_HOPEN
+			};
+			err = au_sio_cpup_simple(&cpg);
+			au_unpin(&pin);
+		}
+		if (unlikely(err))
+			goto out_children;
+		a->dst_btop = a->btgt;
+		a->dst_h_dentry = au_h_dptr(a->dst_dentry, a->btgt);
 	}
 
 	/* lock them all */
@@ -956,20 +1158,34 @@ int aufs_rename(struct inode *_src_dir, struct dentry *_src_dentry,
 		/* leave the copied-up one */
 		goto out_children;
 
-	if (!au_opt_test(au_mntflags(a->dst_dir->i_sb), UDBA_NONE))
-		err = au_may_ren(a);
-	else if (unlikely(a->dst_dentry->d_name.len > AUFS_MAX_NAMELEN))
-		err = -ENAMETOOLONG;
-	if (unlikely(err))
-		goto out_hdir;
+	if (!a->exchange) {
+		if (!au_opt_test(au_mntflags(a->dst_dir->i_sb), UDBA_NONE))
+			err = au_may_ren(a);
+		else if (unlikely(a->dst_dentry->d_name.len > AUFS_MAX_NAMELEN))
+			err = -ENAMETOOLONG;
+		if (unlikely(err))
+			goto out_hdir;
+	}
 
 	/* store timestamps to be revertible */
 	au_ren_dt(a);
 
+	/* store dirren info */
+	if (au_ftest_ren(a->auren_flags, DIRREN)) {
+		err = au_dr_rename(a->src_dentry, a->btgt,
+				   &a->dst_dentry->d_name, &rev);
+		AuTraceErr(err);
+		if (unlikely(err))
+			goto out_dt;
+	}
+
 	/* here we go */
 	err = do_rename(a);
 	if (unlikely(err))
-		goto out_dt;
+		goto out_dirren;
+
+	if (au_ftest_ren(a->auren_flags, DIRREN))
+		au_dr_rename_fin(a->src_dentry, a->btgt, rev);
 
 	/* update dir attributes */
 	au_ren_refresh_dir(a);
@@ -979,6 +1195,9 @@ int aufs_rename(struct inode *_src_dir, struct dentry *_src_dentry,
 
 	goto out_hdir; /* success */
 
+out_dirren:
+	if (au_ftest_ren(a->auren_flags, DIRREN))
+		au_dr_rename_rev(a->src_dentry, a->btgt, rev);
 out_dt:
 	au_ren_rev_dt(err, a);
 out_hdir:
@@ -991,14 +1210,26 @@ int aufs_rename(struct inode *_src_dir, struct dentry *_src_dentry,
 		au_set_dbtop(a->dst_dentry, a->dst_btop);
 	}
 out_parent:
-	if (!err)
-		d_move(a->src_dentry, a->dst_dentry);
-	else {
+	if (!err) {
+		if (d_unhashed(a->src_dentry))
+			au_fset_ren(a->auren_flags, DROPPED_SRC);
+		if (d_unhashed(a->dst_dentry))
+			au_fset_ren(a->auren_flags, DROPPED_DST);
+		if (!a->exchange)
+			d_move(a->src_dentry, a->dst_dentry);
+		else {
+			d_exchange(a->src_dentry, a->dst_dentry);
+			if (au_ftest_ren(a->auren_flags, DROPPED_DST))
+				d_drop(a->dst_dentry);
+		}
+		if (au_ftest_ren(a->auren_flags, DROPPED_SRC))
+			d_drop(a->src_dentry);
+	} else {
 		au_update_dbtop(a->dst_dentry);
 		if (!a->dst_inode)
 			d_drop(a->dst_dentry);
 	}
-	if (au_ftest_ren(a->flags, ISSAMEDIR))
+	if (au_ftest_ren(a->auren_flags, ISSAMEDIR))
 		di_write_unlock(a->dst_parent);
 	else
 		di_write_unlock2(a->src_parent, a->dst_parent);
@@ -1008,7 +1239,7 @@ int aufs_rename(struct inode *_src_dir, struct dentry *_src_dentry,
 	iput(a->dst_inode);
 	if (a->thargs)
 		au_whtmp_rmdir_free(a->thargs);
-	au_delayed_kfree(a);
+	kfree(a);
 out:
 	AuTraceErr(err);
 	return err;
diff --git a/fs/aufs/iinfo.c b/fs/aufs/iinfo.c
index 17cc9e9256b9..4d3a55cb196a 100644
--- a/fs/aufs/iinfo.c
+++ b/fs/aufs/iinfo.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 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
@@ -268,7 +268,7 @@ void au_iinfo_fin(struct inode *inode)
 
 	iinfo = au_ii(inode);
 	if (iinfo->ii_vdir)
-		au_vdir_free(iinfo->ii_vdir, /*atonce*/0);
+		au_vdir_free(iinfo->ii_vdir);
 
 	bindex = iinfo->ii_btop;
 	if (bindex >= 0) {
@@ -280,6 +280,6 @@ void au_iinfo_fin(struct inode *inode)
 			hi++;
 		}
 	}
-	au_delayed_kfree(iinfo->ii_hinode);
+	kfree(iinfo->ii_hinode);
 	AuRwDestroy(&iinfo->ii_rwsem);
 }
diff --git a/fs/aufs/inode.c b/fs/aufs/inode.c
index 16f0a372e979..d361e25280dc 100644
--- a/fs/aufs/inode.c
+++ b/fs/aufs/inode.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 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
@@ -335,32 +335,34 @@ static int reval_inode(struct inode *inode, struct dentry *dentry)
 int au_ino(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
 	   unsigned int d_type, ino_t *ino)
 {
-	int err;
-	struct mutex *mtx;
+	int err, idx;
+	const int isnondir = d_type != DT_DIR;
 
 	/* prevent hardlinked inode number from race condition */
-	mtx = NULL;
-	if (d_type != DT_DIR) {
-		mtx = &au_sbr(sb, bindex)->br_xino.xi_nondir_mtx;
-		mutex_lock(mtx);
+	if (isnondir) {
+		err = au_xinondir_enter(sb, bindex, h_ino, &idx);
+		if (unlikely(err))
+			goto out;
 	}
+
 	err = au_xino_read(sb, bindex, h_ino, ino);
 	if (unlikely(err))
-		goto out;
+		goto out_xinondir;
 
 	if (!*ino) {
 		err = -EIO;
 		*ino = au_xino_new_ino(sb);
 		if (unlikely(!*ino))
-			goto out;
+			goto out_xinondir;
 		err = au_xino_write(sb, bindex, h_ino, *ino);
 		if (unlikely(err))
-			goto out;
+			goto out_xinondir;
 	}
 
+out_xinondir:
+	if (isnondir && idx >= 0)
+		au_xinondir_leave(sb, bindex, h_ino, idx);
 out:
-	if (mtx)
-		mutex_unlock(mtx);
 	return err;
 }
 
@@ -371,9 +373,8 @@ struct inode *au_new_inode(struct dentry *dentry, int must_new)
 	struct inode *inode, *h_inode;
 	struct dentry *h_dentry;
 	struct super_block *sb;
-	struct mutex *mtx;
 	ino_t h_ino, ino;
-	int err;
+	int err, idx, hlinked;
 	aufs_bindex_t btop;
 
 	sb = dentry->d_sb;
@@ -381,28 +382,30 @@ struct inode *au_new_inode(struct dentry *dentry, int must_new)
 	h_dentry = au_h_dptr(dentry, btop);
 	h_inode = d_inode(h_dentry);
 	h_ino = h_inode->i_ino;
+	hlinked = !d_is_dir(h_dentry) && h_inode->i_nlink > 1;
 
+new_ino:
 	/*
 	 * stop 'race'-ing between hardlinks under different
 	 * parents.
 	 */
-	mtx = NULL;
-	if (!d_is_dir(h_dentry))
-		mtx = &au_sbr(sb, btop)->br_xino.xi_nondir_mtx;
+	if (hlinked) {
+		err = au_xinondir_enter(sb, btop, h_ino, &idx);
+		inode = ERR_PTR(err);
+		if (unlikely(err))
+			goto out;
+	}
 
-new_ino:
-	if (mtx)
-		mutex_lock(mtx);
 	err = au_xino_read(sb, btop, h_ino, &ino);
 	inode = ERR_PTR(err);
 	if (unlikely(err))
-		goto out;
+		goto out_xinondir;
 
 	if (!ino) {
 		ino = au_xino_new_ino(sb);
 		if (unlikely(!ino)) {
 			inode = ERR_PTR(-EIO);
-			goto out;
+			goto out_xinondir;
 		}
 	}
 
@@ -410,7 +413,7 @@ struct inode *au_new_inode(struct dentry *dentry, int must_new)
 	inode = au_iget_locked(sb, ino);
 	err = PTR_ERR(inode);
 	if (IS_ERR(inode))
-		goto out;
+		goto out_xinondir;
 
 	AuDbg("%lx, new %d\n", inode->i_state, !!(inode->i_state & I_NEW));
 	if (inode->i_state & I_NEW) {
@@ -418,7 +421,7 @@ struct inode *au_new_inode(struct dentry *dentry, int must_new)
 		err = set_inode(inode, dentry);
 		if (!err) {
 			unlock_new_inode(inode);
-			goto out; /* success */
+			goto out_xinondir; /* success */
 		}
 
 		/*
@@ -437,19 +440,23 @@ struct inode *au_new_inode(struct dentry *dentry, int must_new)
 		 * horrible race condition between lookup, readdir and copyup
 		 * (or something).
 		 */
-		if (mtx)
-			mutex_unlock(mtx);
+		if (hlinked && idx >= 0)
+			au_xinondir_leave(sb, btop, h_ino, idx);
 		err = reval_inode(inode, dentry);
 		if (unlikely(err < 0)) {
-			mtx = NULL;
+			hlinked = 0;
 			goto out_iput;
 		}
-
-		if (!err) {
-			mtx = NULL;
+		if (!err)
 			goto out; /* success */
-		} else if (mtx)
-			mutex_lock(mtx);
+		else if (hlinked && idx >= 0) {
+			err = au_xinondir_enter(sb, btop, h_ino, &idx);
+			if (unlikely(err)) {
+				iput(inode);
+				inode = ERR_PTR(err);
+				goto out;
+			}
+		}
 	}
 
 	if (unlikely(au_test_fs_unique_ino(h_inode)))
@@ -461,17 +468,18 @@ struct inode *au_new_inode(struct dentry *dentry, int must_new)
 	err = au_xino_write(sb, btop, h_ino, /*ino*/0);
 	if (!err) {
 		iput(inode);
-		if (mtx)
-			mutex_unlock(mtx);
+		if (hlinked && idx >= 0)
+			au_xinondir_leave(sb, btop, h_ino, idx);
 		goto new_ino;
 	}
 
 out_iput:
 	iput(inode);
 	inode = ERR_PTR(err);
+out_xinondir:
+	if (hlinked && idx >= 0)
+		au_xinondir_leave(sb, btop, h_ino, idx);
 out:
-	if (mtx)
-		mutex_unlock(mtx);
 	return inode;
 }
 
diff --git a/fs/aufs/inode.h b/fs/aufs/inode.h
index 2f8c1c6b9be7..b4ed08da0fdb 100644
--- a/fs/aufs/inode.h
+++ b/fs/aufs/inode.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -35,10 +35,7 @@ struct au_hnotify {
 	/* never use fsnotify_add_vfsmount_mark() */
 	struct fsnotify_mark		hn_mark;
 #endif
-	union {
-		struct inode		*hn_aufs_inode;	/* no get/put */
-		struct llist_node	hn_lnode;	/* delayed free */
-	};
+	struct inode		*hn_aufs_inode;	/* no get/put */
 #endif
 } ____cacheline_aligned_in_smp;
 
@@ -81,10 +78,7 @@ struct au_iinfo {
 struct au_icntnr {
 	struct au_iinfo iinfo;
 	struct inode vfs_inode;
-	union {
-		struct hlist_node	plink;
-		struct llist_node	lnode;	/* delayed free */
-	};
+	struct hlist_bl_node plink;
 } ____cacheline_aligned_in_smp;
 
 /* au_pin flags */
@@ -210,7 +204,8 @@ struct au_icpup_args {
 int au_pin_and_icpup(struct dentry *dentry, struct iattr *ia,
 		     struct au_icpup_args *a);
 
-int au_h_path_getattr(struct dentry *dentry, int force, struct path *h_path);
+int au_h_path_getattr(struct dentry *dentry, int force, struct path *h_path,
+		      int locked);
 
 /* i_op_add.c */
 int au_may_add(struct dentry *dentry, aufs_bindex_t bindex,
@@ -238,7 +233,8 @@ int aufs_rmdir(struct inode *dir, struct dentry *dentry);
 /* i_op_ren.c */
 int au_wbr(struct dentry *dentry, aufs_bindex_t btgt);
 int aufs_rename(struct inode *src_dir, struct dentry *src_dentry,
-		struct inode *dir, struct dentry *dentry);
+		struct inode *dir, struct dentry *dentry,
+		unsigned int flags);
 
 /* iinfo.c */
 struct inode *au_h_iptr(struct inode *inode, aufs_bindex_t bindex);
@@ -311,17 +307,11 @@ AuStubVoid(au_plink_half_refresh, struct super_block *sb, aufs_bindex_t br_id);
 int au_cpup_xattr(struct dentry *h_dst, struct dentry *h_src, int ignore_flags,
 		  unsigned int verbose);
 ssize_t aufs_listxattr(struct dentry *dentry, char *list, size_t size);
-ssize_t aufs_getxattr(struct dentry *dentry, struct inode *inode,
-		      const char *name, void *value, size_t size);
-int aufs_setxattr(struct dentry *dentry, struct inode *inode, const char *name,
-		  const void *value, size_t size, int flags);
-int aufs_removexattr(struct dentry *dentry, const char *name);
-
-/* void au_xattr_init(struct super_block *sb); */
+void au_xattr_init(struct super_block *sb);
 #else
 AuStubInt0(au_cpup_xattr, struct dentry *h_dst, struct dentry *h_src,
 	   int ignore_flags, unsigned int verbose);
-/* AuStubVoid(au_xattr_init, struct super_block *sb); */
+AuStubVoid(au_xattr_init, struct super_block *sb);
 #endif
 
 #ifdef CONFIG_FS_POSIX_ACL
@@ -332,11 +322,10 @@ int aufs_set_acl(struct inode *inode, struct posix_acl *acl, int type);
 #if IS_ENABLED(CONFIG_AUFS_XATTR) || IS_ENABLED(CONFIG_FS_POSIX_ACL)
 enum {
 	AU_XATTR_SET,
-	AU_XATTR_REMOVE,
 	AU_ACL_SET
 };
 
-struct au_srxattr {
+struct au_sxattr {
 	int type;
 	union {
 		struct {
@@ -346,16 +335,13 @@ struct au_srxattr {
 			int		flags;
 		} set;
 		struct {
-			const char	*name;
-		} remove;
-		struct {
 			struct posix_acl *acl;
 			int		type;
 		} acl_set;
 	} u;
 };
-ssize_t au_srxattr(struct dentry *dentry, struct inode *inode,
-		   struct au_srxattr *arg);
+ssize_t au_sxattr(struct dentry *dentry, struct inode *inode,
+		  struct au_sxattr *arg);
 #endif
 
 /* ---------------------------------------------------------------------- */
@@ -690,6 +676,16 @@ static inline void au_hn_inode_lock_nested(struct au_hinode *hdir,
 	au_hn_suspend(hdir);
 }
 
+#if 0 /* unused */
+#include "vfsub.h"
+static inline void au_hn_inode_lock_shared_nested(struct au_hinode *hdir,
+						  unsigned int sc)
+{
+	vfsub_inode_lock_shared_nested(hdir->hi_inode, sc);
+	au_hn_suspend(hdir);
+}
+#endif
+
 static inline void au_hn_inode_unlock(struct au_hinode *hdir)
 {
 	au_hn_resume(hdir);
diff --git a/fs/aufs/ioctl.c b/fs/aufs/ioctl.c
index fc5529b150cc..5e501c5d4ead 100644
--- a/fs/aufs/ioctl.c
+++ b/fs/aufs/ioctl.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 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
diff --git a/fs/aufs/loop.c b/fs/aufs/loop.c
index eeb2a30b3e32..1acb82f0bf07 100644
--- a/fs/aufs/loop.c
+++ b/fs/aufs/loop.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 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
@@ -143,5 +143,5 @@ void au_loopback_fin(void)
 {
 	if (backing_file_func)
 		symbol_put(loop_backing_file);
-	au_delayed_kfree(au_warn_loopback_array);
+	kfree(au_warn_loopback_array);
 }
diff --git a/fs/aufs/loop.h b/fs/aufs/loop.h
index 35f744617d0e..9b02d32905f4 100644
--- a/fs/aufs/loop.h
+++ b/fs/aufs/loop.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 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
diff --git a/fs/aufs/module.c b/fs/aufs/module.c
index ba19f08c427c..744242aa41b7 100644
--- a/fs/aufs/module.c
+++ b/fs/aufs/module.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -34,7 +34,7 @@ void *au_krealloc(void *p, unsigned int new_sz, gfp_t gfp, int may_shrink)
 	if (p) {
 #if 0 /* unused */
 		if (!new_sz) {
-			au_delayed_kfree(p);
+			kfree(p);
 			p = NULL;
 			goto out;
 		}
@@ -58,7 +58,7 @@ void *au_krealloc(void *p, unsigned int new_sz, gfp_t gfp, int may_shrink)
 		if (q) {
 			if (p) {
 				memcpy(q, p, new_sz);
-				au_delayed_kfree(p);
+				kfree(p);
 			}
 			p = q;
 		} else
@@ -82,61 +82,11 @@ void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp,
 /*
  * aufs caches
  */
-
-struct au_dfree au_dfree;
-
-/* delayed free */
-static void au_do_dfree(struct work_struct *work __maybe_unused)
-{
-	struct llist_head *head;
-	struct llist_node *node, *next;
-
-#define AU_CACHE_DFREE_DO_BODY(name, idx, lnode) do {			\
-		head = &au_dfree.cache[AuCache_##idx].llist;		\
-		node = llist_del_all(head);				\
-		for (; node; node = next) {				\
-			struct au_##name *p				\
-				= llist_entry(node, struct au_##name,	\
-					      lnode);			\
-			next = llist_next(node);			\
-			au_cache_free_##name(p);			\
-		}							\
-	} while (0)
-
-	AU_CACHE_DFREE_DO_BODY(dinfo, DINFO, di_lnode);
-	AU_CACHE_DFREE_DO_BODY(icntnr, ICNTNR, lnode);
-	AU_CACHE_DFREE_DO_BODY(finfo, FINFO, fi_lnode);
-	AU_CACHE_DFREE_DO_BODY(vdir, VDIR, vd_lnode);
-	AU_CACHE_DFREE_DO_BODY(vdir_dehstr, DEHSTR, lnode);
-#ifdef CONFIG_AUFS_HNOTIFY
-	AU_CACHE_DFREE_DO_BODY(hnotify, HNOTIFY, hn_lnode);
-#endif
-
-#define AU_DFREE_DO_BODY(llist, func) do {		\
-		node = llist_del_all(llist);		\
-		for (; node; node = next) {		\
-			next = llist_next(node);	\
-			func(node);			\
-		}					\
-	} while (0)
-
-	AU_DFREE_DO_BODY(au_dfree.llist + AU_DFREE_KFREE, kfree);
-	AU_DFREE_DO_BODY(au_dfree.llist + AU_DFREE_FREE_PAGE, au_free_page);
-
-#undef AU_CACHE_DFREE_DO_BODY
-#undef AU_DFREE_DO_BODY
-}
-
-AU_CACHE_DFREE_FUNC(dinfo, DINFO, di_lnode);
-AU_CACHE_DFREE_FUNC(icntnr, ICNTNR, lnode);
-AU_CACHE_DFREE_FUNC(finfo, FINFO, fi_lnode);
-AU_CACHE_DFREE_FUNC(vdir, VDIR, vd_lnode);
-AU_CACHE_DFREE_FUNC(vdir_dehstr, DEHSTR, lnode);
+struct kmem_cache *au_cache[AuCache_Last];
 
 static void au_cache_fin(void)
 {
 	int i;
-	struct au_cache *cp;
 
 	/*
 	 * Make sure all delayed rcu free inodes are flushed before we
@@ -146,33 +96,27 @@ static void au_cache_fin(void)
 
 	/* excluding AuCache_HNOTIFY */
 	BUILD_BUG_ON(AuCache_HNOTIFY + 1 != AuCache_Last);
-	flush_delayed_work(&au_dfree.dwork);
 	for (i = 0; i < AuCache_HNOTIFY; i++) {
-		cp = au_dfree.cache + i;
-		AuDebugOn(!llist_empty(&cp->llist));
-		kmem_cache_destroy(cp->cache);
-		cp->cache = NULL;
+		kmem_cache_destroy(au_cache[i]);
+		au_cache[i] = NULL;
 	}
 }
 
 static int __init au_cache_init(void)
 {
-	struct au_cache *cp;
-
-	cp = au_dfree.cache;
-	cp[AuCache_DINFO].cache = AuCacheCtor(au_dinfo, au_di_init_once);
-	if (cp[AuCache_DINFO].cache)
+	au_cache[AuCache_DINFO] = AuCacheCtor(au_dinfo, au_di_init_once);
+	if (au_cache[AuCache_DINFO])
 		/* SLAB_DESTROY_BY_RCU */
-		cp[AuCache_ICNTNR].cache = AuCacheCtor(au_icntnr,
+		au_cache[AuCache_ICNTNR] = AuCacheCtor(au_icntnr,
 						       au_icntnr_init_once);
-	if (cp[AuCache_ICNTNR].cache)
-		cp[AuCache_FINFO].cache = AuCacheCtor(au_finfo,
+	if (au_cache[AuCache_ICNTNR])
+		au_cache[AuCache_FINFO] = AuCacheCtor(au_finfo,
 						      au_fi_init_once);
-	if (cp[AuCache_FINFO].cache)
-		cp[AuCache_VDIR].cache = AuCache(au_vdir);
-	if (cp[AuCache_VDIR].cache)
-		cp[AuCache_DEHSTR].cache = AuCache(au_vdir_dehstr);
-	if (cp[AuCache_DEHSTR].cache)
+	if (au_cache[AuCache_FINFO])
+		au_cache[AuCache_VDIR] = AuCache(au_vdir);
+	if (au_cache[AuCache_VDIR])
+		au_cache[AuCache_DEHSTR] = AuCache(au_vdir_dehstr);
+	if (au_cache[AuCache_DEHSTR])
 		return 0;
 
 	au_cache_fin();
@@ -188,7 +132,7 @@ int au_dir_roflags;
  * iterate_supers_type() doesn't protect us from
  * remounting (branch management)
  */
-struct au_sphlhead au_sbilist;
+struct hlist_bl_head au_sbilist;
 #endif
 
 /*
@@ -221,9 +165,9 @@ int au_seq_path(struct seq_file *seq, struct path *path)
 	int err;
 
 	err = seq_path(seq, path, au_esc_chars);
-	if (err > 0)
+	if (err >= 0)
 		err = 0;
-	else if (err < 0)
+	else
 		err = -ENOMEM;
 
 	return err;
@@ -235,7 +179,6 @@ static int __init aufs_init(void)
 {
 	int err, i;
 	char *p;
-	struct au_cache *cp;
 
 	p = au_esc_chars;
 	for (i = 1; i <= ' '; i++)
@@ -250,15 +193,7 @@ static int __init aufs_init(void)
 	for (i = 0; i < AuIop_Last; i++)
 		aufs_iop_nogetattr[i].getattr = NULL;
 
-	/* First, initialize au_dfree */
-	for (i = 0; i < AuCache_Last; i++) {	/* including hnotify */
-		cp = au_dfree.cache + i;
-		cp->cache = NULL;
-		init_llist_head(&cp->llist);
-	}
-	for (i = 0; i < AU_DFREE_Last; i++)
-		init_llist_head(au_dfree.llist + i);
-	INIT_DELAYED_WORK(&au_dfree.dwork, au_do_dfree);
+	memset(au_cache, 0, sizeof(au_cache));	/* including hnotify */
 
 	au_sbilist_init();
 	sysaufs_brs_init();
@@ -310,7 +245,6 @@ static int __init aufs_init(void)
 out_sysaufs:
 	sysaufs_fin();
 	au_dy_fin();
-	flush_delayed_work(&au_dfree.dwork);
 out:
 	return err;
 }
@@ -326,7 +260,6 @@ static void __exit aufs_exit(void)
 	au_procfs_fin();
 	sysaufs_fin();
 	au_dy_fin();
-	flush_delayed_work(&au_dfree.dwork);
 }
 
 module_init(aufs_init);
diff --git a/fs/aufs/module.h b/fs/aufs/module.h
index cb3ae3043036..4f5727cb8088 100644
--- a/fs/aufs/module.h
+++ b/fs/aufs/module.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 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
@@ -25,7 +25,6 @@
 #ifdef __KERNEL__
 
 #include <linux/slab.h>
-#include "debug.h"
 
 struct path;
 struct seq_file;
@@ -64,7 +63,7 @@ AuStubVoid(au_procfs_fin, void);
 
 /* ---------------------------------------------------------------------- */
 
-/* kmem cache and delayed free */
+/* kmem cache */
 enum {
 	AuCache_DINFO,
 	AuCache_ICNTNR,
@@ -75,28 +74,7 @@ enum {
 	AuCache_Last
 };
 
-enum {
-	AU_DFREE_KFREE,
-	AU_DFREE_FREE_PAGE,
-	AU_DFREE_Last
-};
-
-struct au_cache {
-	struct kmem_cache	*cache;
-	struct llist_head	llist;	/* delayed free */
-};
-
-/*
- * in order to reduce the cost of the internal timer, consolidate all the
- * delayed free works into a single delayed_work.
- */
-struct au_dfree {
-	struct au_cache		cache[AuCache_Last];
-	struct llist_head	llist[AU_DFREE_Last];
-	struct delayed_work	dwork;
-};
-
-extern struct au_dfree au_dfree;
+extern struct kmem_cache *au_cache[AuCache_Last];
 
 #define AuCacheFlags		(SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD)
 #define AuCache(type)		KMEM_CACHE(type, AuCacheFlags)
@@ -104,25 +82,11 @@ extern struct au_dfree au_dfree;
 	kmem_cache_create(#type, sizeof(struct type), \
 			  __alignof__(struct type), AuCacheFlags, ctor)
 
-#define AU_DFREE_DELAY		msecs_to_jiffies(10)
-#define AU_DFREE_BODY(lnode, llist) do {				\
-		if (llist_add(lnode, llist))				\
-			schedule_delayed_work(&au_dfree.dwork,		\
-					      AU_DFREE_DELAY);		\
-	} while (0)
-#define AU_CACHE_DFREE_FUNC(name, idx, lnode)				\
-	void au_cache_dfree_##name(struct au_##name *p)			\
-	{								\
-		struct au_cache *cp = au_dfree.cache + AuCache_##idx;	\
-		AU_DFREE_BODY(&p->lnode, &cp->llist);			\
-	}
-
 #define AuCacheFuncs(name, index) \
 static inline struct au_##name *au_cache_alloc_##name(void) \
-{ return kmem_cache_alloc(au_dfree.cache[AuCache_##index].cache, GFP_NOFS); } \
+{ return kmem_cache_alloc(au_cache[AuCache_##index], GFP_NOFS); } \
 static inline void au_cache_free_##name(struct au_##name *p) \
-{ kmem_cache_free(au_dfree.cache[AuCache_##index].cache, p); } \
-void au_cache_dfree_##name(struct au_##name *p)
+{ kmem_cache_free(au_cache[AuCache_##index], p); }
 
 AuCacheFuncs(dinfo, DINFO);
 AuCacheFuncs(icntnr, ICNTNR);
@@ -133,24 +97,5 @@ AuCacheFuncs(vdir_dehstr, DEHSTR);
 AuCacheFuncs(hnotify, HNOTIFY);
 #endif
 
-static inline void au_delayed_kfree(const void *p)
-{
-	AuDebugOn(!p);
-	AuDebugOn(ksize(p) < sizeof(struct llist_node));
-
-	AU_DFREE_BODY((void *)p, au_dfree.llist + AU_DFREE_KFREE);
-}
-
-/* cast only */
-static inline void au_free_page(void *p)
-{
-	free_page((unsigned long)p);
-}
-
-static inline void au_delayed_free_page(unsigned long addr)
-{
-	AU_DFREE_BODY((void *)addr, au_dfree.llist + AU_DFREE_FREE_PAGE);
-}
-
 #endif /* __KERNEL__ */
 #endif /* __AUFS_MODULE_H__ */
diff --git a/fs/aufs/mvdown.c b/fs/aufs/mvdown.c
index 802ef09e90cd..0fb18b841e94 100644
--- a/fs/aufs/mvdown.c
+++ b/fs/aufs/mvdown.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011-2016 Junjiro R. Okajima
+ * Copyright (C) 2011-2017 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
@@ -697,7 +697,7 @@ int au_mvdown(struct dentry *dentry, struct aufs_mvdown __user *uarg)
 	e = copy_to_user(uarg, &args->mvdown, sizeof(args->mvdown));
 	if (unlikely(e))
 		err = -EFAULT;
-	au_delayed_kfree(args);
+	kfree(args);
 out:
 	AuTraceErr(err);
 	return err;
diff --git a/fs/aufs/opts.c b/fs/aufs/opts.c
index ddb296503847..fc0496cd6a96 100644
--- a/fs/aufs/opts.c
+++ b/fs/aufs/opts.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 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
@@ -47,6 +47,7 @@ enum {
 	Opt_verbose, Opt_noverbose,
 	Opt_sum, Opt_nosum, Opt_wsum,
 	Opt_dirperm1, Opt_nodirperm1,
+	Opt_dirren, Opt_nodirren,
 	Opt_acl, Opt_noacl,
 	Opt_tail, Opt_ignore, Opt_ignore_silent, Opt_err
 };
@@ -101,10 +102,18 @@ static match_table_t options = {
 	{Opt_dio, "dio"},
 	{Opt_nodio, "nodio"},
 
+#ifdef CONFIG_AUFS_DIRREN
+	{Opt_dirren, "dirren"},
+	{Opt_nodirren, "nodirren"},
+#else
+	{Opt_ignore, "dirren"},
+	{Opt_ignore_silent, "nodirren"},
+#endif
+
 #ifdef CONFIG_AUFS_FHSM
 	{Opt_fhsm_sec, "fhsm_sec=%d"},
 #else
-	{Opt_ignore_silent, "fhsm_sec=%d"},
+	{Opt_ignore, "fhsm_sec=%d"},
 #endif
 
 	{Opt_diropq_a, "diropq=always"},
@@ -117,7 +126,7 @@ static match_table_t options = {
 
 	/* keep them temporary */
 	{Opt_ignore_silent, "nodlgt"},
-	{Opt_ignore_silent, "clean_plink"},
+	{Opt_ignore, "clean_plink"},
 
 #ifdef CONFIG_AUFS_SHWH
 	{Opt_shwh, "shwh"},
@@ -155,7 +164,7 @@ static match_table_t options = {
 	{Opt_acl, "acl"},
 	{Opt_noacl, "noacl"},
 #else
-	{Opt_ignore_silent, "acl"},
+	{Opt_ignore, "acl"},
 	{Opt_ignore_silent, "noacl"},
 #endif
 
@@ -424,6 +433,10 @@ static match_table_t au_wbr_create_policy = {
 	{AuWbrCreate_MFSV, "mfs:%d"},
 	{AuWbrCreate_MFSV, "most-free-space:%d"},
 
+	/* top-down regardless the parent, and then mfs */
+	{AuWbrCreate_TDMFS, "tdmfs:%d"},
+	{AuWbrCreate_TDMFSV, "tdmfs:%d:%d"},
+
 	{AuWbrCreate_MFSRR, "mfsrr:%d"},
 	{AuWbrCreate_MFSRRV, "mfsrr:%d:%d"},
 	{AuWbrCreate_PMFS, "pmfs"},
@@ -499,6 +512,7 @@ au_wbr_create_val(char *str, struct au_opt_wbr_create *create)
 	create->wbr_create = err;
 	switch (err) {
 	case AuWbrCreate_MFSRRV:
+	case AuWbrCreate_TDMFSV:
 	case AuWbrCreate_PMFSRRV:
 		e = au_wbr_mfs_wmark(&args[0], str, create);
 		if (!e)
@@ -507,6 +521,7 @@ au_wbr_create_val(char *str, struct au_opt_wbr_create *create)
 			err = e;
 		break;
 	case AuWbrCreate_MFSRR:
+	case AuWbrCreate_TDMFS:
 	case AuWbrCreate_PMFSRR:
 		e = au_wbr_mfs_wmark(&args[0], str, create);
 		if (unlikely(e)) {
@@ -717,10 +732,12 @@ static void dump_opts(struct au_opts *opts)
 				AuDbg("%d sec\n", u.create->mfs_second);
 				break;
 			case AuWbrCreate_MFSRR:
+			case AuWbrCreate_TDMFS:
 				AuDbg("%llu watermark\n",
 					  u.create->mfsrr_watermark);
 				break;
 			case AuWbrCreate_MFSRRV:
+			case AuWbrCreate_TDMFSV:
 			case AuWbrCreate_PMFSRRV:
 				AuDbg("%llu watermark, %d sec\n",
 					  u.create->mfsrr_watermark,
@@ -735,6 +752,12 @@ static void dump_opts(struct au_opts *opts)
 		case Opt_fhsm_sec:
 			AuDbg("fhsm_sec %u\n", opt->fhsm_second);
 			break;
+		case Opt_dirren:
+			AuLabel(dirren);
+			break;
+		case Opt_nodirren:
+			AuLabel(nodirren);
+			break;
 		case Opt_acl:
 			AuLabel(acl);
 			break;
@@ -1185,6 +1208,8 @@ int au_opts_parse(struct super_block *sb, char *str, struct au_opts *opts)
 		case Opt_wsum:
 		case Opt_rdblk_def:
 		case Opt_rdhash_def:
+		case Opt_dirren:
+		case Opt_nodirren:
 		case Opt_acl:
 		case Opt_noacl:
 			err = 0;
@@ -1256,7 +1281,7 @@ int au_opts_parse(struct super_block *sb, char *str, struct au_opts *opts)
 		}
 	}
 
-	au_delayed_kfree(a);
+	kfree(a);
 	dump_opts(opts);
 	if (unlikely(err))
 		au_opts_free(opts);
@@ -1286,6 +1311,8 @@ static int au_opt_wbr_create(struct super_block *sb,
 	switch (create->wbr_create) {
 	case AuWbrCreate_MFSRRV:
 	case AuWbrCreate_MFSRR:
+	case AuWbrCreate_TDMFS:
+	case AuWbrCreate_TDMFSV:
 	case AuWbrCreate_PMFSRR:
 	case AuWbrCreate_PMFSRRV:
 		sbinfo->si_wbr_mfs.mfsrr_watermark = create->mfsrr_watermark;
@@ -1449,6 +1476,28 @@ static int au_opt_simple(struct super_block *sb, struct au_opt *opt,
 		au_fclr_opts(opts->flags, TRUNC_XIB);
 		break;
 
+	case Opt_dirren:
+		err = 1;
+		if (!au_opt_test(sbinfo->si_mntflags, DIRREN)) {
+			err = au_dr_opt_set(sb);
+			if (!err)
+				err = 1;
+		}
+		if (err == 1)
+			au_opt_set(sbinfo->si_mntflags, DIRREN);
+		break;
+	case Opt_nodirren:
+		err = 1;
+		if (au_opt_test(sbinfo->si_mntflags, DIRREN)) {
+			err = au_dr_opt_clr(sb, au_ftest_opts(opts->flags,
+							      DR_FLUSHED));
+			if (!err)
+				err = 1;
+		}
+		if (err == 1)
+			au_opt_clr(sbinfo->si_mntflags, DIRREN);
+		break;
+
 	case Opt_acl:
 		sb->s_flags |= MS_POSIXACL;
 		break;
@@ -1517,7 +1566,6 @@ static int au_opt_br(struct super_block *sb, struct au_opt *opt,
 		}
 		break;
 	}
-
 	return err;
 }
 
@@ -1678,8 +1726,7 @@ int au_opts_verify(struct super_block *sb, unsigned long sb_flags,
 		au_hn_inode_unlock(hdir);
 
 		if (!err && do_free) {
-			if (wbr)
-				au_delayed_kfree(wbr);
+			kfree(wbr);
 			br->br_wbr = NULL;
 		}
 	}
@@ -1808,7 +1855,11 @@ int au_opts_remount(struct super_block *sb, struct au_opts *opts)
 
 	SiMustWriteLock(sb);
 
-	err = 0;
+	err = au_dr_opt_flush(sb);
+	if (unlikely(err))
+		goto out;
+	au_fset_opts(opts->flags, DR_FLUSHED);
+
 	dir = d_inode(sb->s_root);
 	sbinfo = au_sbi(sb);
 	opt_xino = NULL;
@@ -1849,6 +1900,8 @@ int au_opts_remount(struct super_block *sb, struct au_opts *opts)
 		au_fset_opts(opts->flags, REFRESH);
 
 	AuDbg("status 0x%x\n", opts->flags);
+
+out:
 	return err;
 }
 
diff --git a/fs/aufs/opts.h b/fs/aufs/opts.h
index 8d0c5341cc85..abcbaf62170d 100644
--- a/fs/aufs/opts.h
+++ b/fs/aufs/opts.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 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
@@ -27,7 +27,6 @@
 #include <linux/path.h>
 
 struct file;
-struct super_block;
 
 /* ---------------------------------------------------------------------- */
 
@@ -48,11 +47,16 @@ struct super_block;
 #define AuOpt_WARN_PERM		(1 << 12)	/* warn when add-branch */
 #define AuOpt_VERBOSE		(1 << 13)	/* busy inode when del-branch */
 #define AuOpt_DIO		(1 << 14)	/* direct io */
+#define AuOpt_DIRREN		(1 << 15)	/* directory rename */
 
 #ifndef CONFIG_AUFS_HNOTIFY
 #undef AuOpt_UDBA_HNOTIFY
 #define AuOpt_UDBA_HNOTIFY	0
 #endif
+#ifndef CONFIG_AUFS_DIRREN
+#undef AuOpt_DIRREN
+#define AuOpt_DIRREN		0
+#endif
 #ifndef CONFIG_AUFS_SHWH
 #undef AuOpt_SHWH
 #define AuOpt_SHWH		0
@@ -99,6 +103,8 @@ enum {
 	AuWbrCreate_MFSV,	/* mfs with seconds */
 	AuWbrCreate_MFSRR,	/* mfs then rr */
 	AuWbrCreate_MFSRRV,	/* mfs then rr with seconds */
+	AuWbrCreate_TDMFS,	/* top down regardless parent and mfs */
+	AuWbrCreate_TDMFSV,	/* top down regardless parent and mfs */
 	AuWbrCreate_PMFS,	/* parent and mfs */
 	AuWbrCreate_PMFSV,	/* parent and mfs with seconds */
 	AuWbrCreate_PMFSRR,	/* parent, mfs and round-robin */
@@ -175,12 +181,18 @@ struct au_opt {
 #define AuOpts_TRUNC_XIB	(1 << 2)
 #define AuOpts_REFRESH_DYAOP	(1 << 3)
 #define AuOpts_REFRESH_IDOP	(1 << 4)
+#define AuOpts_DR_FLUSHED	(1 << 5)
 #define au_ftest_opts(flags, name)	((flags) & AuOpts_##name)
 #define au_fset_opts(flags, name) \
 	do { (flags) |= AuOpts_##name; } while (0)
 #define au_fclr_opts(flags, name) \
 	do { (flags) &= ~AuOpts_##name; } while (0)
 
+#ifndef CONFIG_AUFS_DIRREN
+#undef AuOpts_DR_FLUSHED
+#define AuOpts_DR_FLUSHED	0
+#endif
+
 struct au_opts {
 	struct au_opt	*opt;
 	int		max_opt;
@@ -199,6 +211,7 @@ const char *au_optstr_wbr_copyup(int wbr_copyup);
 const char *au_optstr_wbr_create(int wbr_create);
 
 void au_opts_free(struct au_opts *opts);
+struct super_block;
 int au_opts_parse(struct super_block *sb, char *str, struct au_opts *opts);
 int au_opts_verify(struct super_block *sb, unsigned long sb_flags,
 		   unsigned int pending);
diff --git a/fs/aufs/plink.c b/fs/aufs/plink.c
index 8a816b9b911d..f16a2d9cb12e 100644
--- a/fs/aufs/plink.c
+++ b/fs/aufs/plink.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 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
@@ -135,7 +135,8 @@ void au_plink_list(struct super_block *sb)
 {
 	int i;
 	struct au_sbinfo *sbinfo;
-	struct hlist_head *plink_hlist;
+	struct hlist_bl_head *hbl;
+	struct hlist_bl_node *pos;
 	struct au_icntnr *icntnr;
 
 	SiMustAnyLock(sb);
@@ -145,11 +146,11 @@ void au_plink_list(struct super_block *sb)
 	AuDebugOn(au_plink_maint(sb, AuLock_NOPLM));
 
 	for (i = 0; i < AuPlink_NHASH; i++) {
-		plink_hlist = &sbinfo->si_plink[i].head;
-		rcu_read_lock();
-		hlist_for_each_entry_rcu(icntnr, plink_hlist, plink)
+		hbl = sbinfo->si_plink + i;
+		hlist_bl_lock(hbl);
+		hlist_bl_for_each_entry(icntnr, pos, hbl, plink)
 			AuDbg("%lu\n", icntnr->vfs_inode.i_ino);
-		rcu_read_unlock();
+		hlist_bl_unlock(hbl);
 	}
 }
 #endif
@@ -159,7 +160,8 @@ int au_plink_test(struct inode *inode)
 {
 	int found, i;
 	struct au_sbinfo *sbinfo;
-	struct hlist_head *plink_hlist;
+	struct hlist_bl_head *hbl;
+	struct hlist_bl_node *pos;
 	struct au_icntnr *icntnr;
 
 	sbinfo = au_sbi(inode->i_sb);
@@ -169,14 +171,14 @@ int au_plink_test(struct inode *inode)
 
 	found = 0;
 	i = au_plink_hash(inode->i_ino);
-	plink_hlist = &sbinfo->si_plink[i].head;
-	rcu_read_lock();
-	hlist_for_each_entry_rcu(icntnr, plink_hlist, plink)
+	hbl =  sbinfo->si_plink + i;
+	hlist_bl_lock(hbl);
+	hlist_bl_for_each_entry(icntnr, pos, hbl, plink)
 		if (&icntnr->vfs_inode == inode) {
 			found = 1;
 			break;
 		}
-	rcu_read_unlock();
+	hlist_bl_unlock(hbl);
 	return found;
 }
 
@@ -215,9 +217,9 @@ static struct dentry *au_do_plink_lkup(struct qstr *tgtname,
 	struct inode *h_inode;
 
 	h_inode = d_inode(h_parent);
-	inode_lock_nested(h_inode, AuLsc_I_CHILD2);
+	vfsub_inode_lock_shared_nested(h_inode, AuLsc_I_CHILD2);
 	h_dentry = vfsub_lkup_one(tgtname, h_parent);
-	inode_unlock(h_inode);
+	inode_unlock_shared(h_inode);
 	return h_dentry;
 }
 
@@ -364,9 +366,9 @@ void au_plink_append(struct inode *inode, aufs_bindex_t bindex,
 {
 	struct super_block *sb;
 	struct au_sbinfo *sbinfo;
-	struct hlist_head *plink_hlist;
+	struct hlist_bl_head *hbl;
+	struct hlist_bl_node *pos;
 	struct au_icntnr *icntnr;
-	struct au_sphlhead *sphl;
 	int found, err, cnt, i;
 
 	sb = inode->i_sb;
@@ -379,12 +381,11 @@ void au_plink_append(struct inode *inode, aufs_bindex_t bindex,
 		return;
 
 	i = au_plink_hash(inode->i_ino);
-	sphl = sbinfo->si_plink + i;
-	plink_hlist = &sphl->head;
+	hbl = sbinfo->si_plink + i;
 	au_igrab(inode);
 
-	spin_lock(&sphl->spin);
-	hlist_for_each_entry(icntnr, plink_hlist, plink) {
+	hlist_bl_lock(hbl);
+	hlist_bl_for_each_entry(icntnr, pos, hbl, plink) {
 		if (&icntnr->vfs_inode == inode) {
 			found = 1;
 			break;
@@ -392,11 +393,11 @@ void au_plink_append(struct inode *inode, aufs_bindex_t bindex,
 	}
 	if (!found) {
 		icntnr = container_of(inode, struct au_icntnr, vfs_inode);
-		hlist_add_head_rcu(&icntnr->plink, plink_hlist);
+		hlist_bl_add_head(&icntnr->plink, hbl);
 	}
-	spin_unlock(&sphl->spin);
+	hlist_bl_unlock(hbl);
 	if (!found) {
-		cnt = au_sphl_count(sphl);
+		cnt = au_hbl_count(hbl);
 #define msg "unexpectedly unblanced or too many pseudo-links"
 		if (cnt > AUFS_PLINK_WARN)
 			AuWarn1(msg ", %d\n", cnt);
@@ -404,7 +405,7 @@ void au_plink_append(struct inode *inode, aufs_bindex_t bindex,
 		err = whplink(h_dentry, inode, bindex, au_sbr(sb, bindex));
 		if (unlikely(err)) {
 			pr_warn("err %d, damaged pseudo link.\n", err);
-			au_sphl_del_rcu(&icntnr->plink, sphl);
+			au_hbl_del(&icntnr->plink, hbl);
 			iput(&icntnr->vfs_inode);
 		}
 	} else
@@ -416,8 +417,8 @@ void au_plink_put(struct super_block *sb, int verbose)
 {
 	int i, warned;
 	struct au_sbinfo *sbinfo;
-	struct hlist_head *plink_hlist;
-	struct hlist_node *tmp;
+	struct hlist_bl_head *hbl;
+	struct hlist_bl_node *pos, *tmp;
 	struct au_icntnr *icntnr;
 
 	SiMustWriteLock(sb);
@@ -429,14 +430,14 @@ void au_plink_put(struct super_block *sb, int verbose)
 	/* no spin_lock since sbinfo is write-locked */
 	warned = 0;
 	for (i = 0; i < AuPlink_NHASH; i++) {
-		plink_hlist = &sbinfo->si_plink[i].head;
-		if (!warned && verbose && !hlist_empty(plink_hlist)) {
+		hbl = sbinfo->si_plink + i;
+		if (!warned && verbose && !hlist_bl_empty(hbl)) {
 			pr_warn("pseudo-link is not flushed");
 			warned = 1;
 		}
-		hlist_for_each_entry_safe(icntnr, tmp, plink_hlist, plink)
+		hlist_bl_for_each_entry_safe(icntnr, pos, tmp, hbl, plink)
 			iput(&icntnr->vfs_inode);
-		INIT_HLIST_HEAD(plink_hlist);
+		INIT_HLIST_BL_HEAD(hbl);
 	}
 }
 
@@ -484,8 +485,8 @@ static int au_plink_do_half_refresh(struct inode *inode, aufs_bindex_t br_id)
 void au_plink_half_refresh(struct super_block *sb, aufs_bindex_t br_id)
 {
 	struct au_sbinfo *sbinfo;
-	struct hlist_head *plink_hlist;
-	struct hlist_node *tmp;
+	struct hlist_bl_head *hbl;
+	struct hlist_bl_node *pos, *tmp;
 	struct au_icntnr *icntnr;
 	struct inode *inode;
 	int i, do_put;
@@ -496,15 +497,15 @@ void au_plink_half_refresh(struct super_block *sb, aufs_bindex_t br_id)
 	AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK));
 	AuDebugOn(au_plink_maint(sb, AuLock_NOPLM));
 
-	/* no spin_lock since sbinfo is write-locked */
+	/* no bit_lock since sbinfo is write-locked */
 	for (i = 0; i < AuPlink_NHASH; i++) {
-		plink_hlist = &sbinfo->si_plink[i].head;
-		hlist_for_each_entry_safe(icntnr, tmp, plink_hlist, plink) {
+		hbl = sbinfo->si_plink + i;
+		hlist_bl_for_each_entry_safe(icntnr, pos, tmp, hbl, plink) {
 			inode = au_igrab(&icntnr->vfs_inode);
 			ii_write_lock_child(inode);
 			do_put = au_plink_do_half_refresh(inode, br_id);
 			if (do_put) {
-				hlist_del(&icntnr->plink);
+				hlist_bl_del(&icntnr->plink);
 				iput(inode);
 			}
 			ii_write_unlock(inode);
diff --git a/fs/aufs/poll.c b/fs/aufs/poll.c
index 720b2eda1f95..1aea1948fd39 100644
--- a/fs/aufs/poll.c
+++ b/fs/aufs/poll.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -34,7 +34,7 @@ unsigned int aufs_poll(struct file *file, poll_table *wait)
 	sb = file->f_path.dentry->d_sb;
 	si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW);
 
-	h_file = au_read_pre(file, /*keep_fi*/0);
+	h_file = au_read_pre(file, /*keep_fi*/0, /*lsc*/0);
 	err = PTR_ERR(h_file);
 	if (IS_ERR(h_file))
 		goto out;
diff --git a/fs/aufs/posix_acl.c b/fs/aufs/posix_acl.c
index 7981c4322f7e..816a47c7df84 100644
--- a/fs/aufs/posix_acl.c
+++ b/fs/aufs/posix_acl.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014-2016 Junjiro R. Okajima
+ * Copyright (C) 2014-2017 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
@@ -49,6 +49,8 @@ struct posix_acl *aufs_get_acl(struct inode *inode, int type)
 
 	/* always topmost only */
 	acl = get_acl(h_inode, type);
+	if (!IS_ERR_OR_NULL(acl))
+		set_cached_acl(inode, type, acl);
 
 out:
 	ii_read_unlock(inode);
@@ -63,7 +65,7 @@ int aufs_set_acl(struct inode *inode, struct posix_acl *acl, int type)
 	int err;
 	ssize_t ssz;
 	struct dentry *dentry;
-	struct au_srxattr arg = {
+	struct au_sxattr arg = {
 		.type = AU_ACL_SET,
 		.u.acl_set = {
 			.acl	= acl,
@@ -87,11 +89,13 @@ int aufs_set_acl(struct inode *inode, struct posix_acl *acl, int type)
 		}
 	}
 
-	ssz = au_srxattr(dentry, inode, &arg);
+	ssz = au_sxattr(dentry, inode, &arg);
 	dput(dentry);
 	err = ssz;
-	if (ssz >= 0)
+	if (ssz >= 0) {
 		err = 0;
+		set_cached_acl(inode, type, acl);
+	}
 
 out:
 	return err;
diff --git a/fs/aufs/procfs.c b/fs/aufs/procfs.c
index a334330a23ea..7d69697fac35 100644
--- a/fs/aufs/procfs.c
+++ b/fs/aufs/procfs.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010-2016 Junjiro R. Okajima
+ * Copyright (C) 2010-2017 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
@@ -49,6 +49,7 @@ static int au_procfs_plm_write_si(struct file *file, unsigned long id)
 	int err;
 	struct super_block *sb;
 	struct au_sbinfo *sbinfo;
+	struct hlist_bl_node *pos;
 
 	err = -EBUSY;
 	if (unlikely(file->private_data))
@@ -56,14 +57,14 @@ static int au_procfs_plm_write_si(struct file *file, unsigned long id)
 
 	sb = NULL;
 	/* don't use au_sbilist_lock() here */
-	spin_lock(&au_sbilist.spin);
-	hlist_for_each_entry(sbinfo, &au_sbilist.head, si_list)
+	hlist_bl_lock(&au_sbilist);
+	hlist_bl_for_each_entry(sbinfo, pos, &au_sbilist, si_list)
 		if (id == sysaufs_si_id(sbinfo)) {
 			kobject_get(&sbinfo->si_kobj);
 			sb = sbinfo->si_sb;
 			break;
 		}
-	spin_unlock(&au_sbilist.spin);
+	hlist_bl_unlock(&au_sbilist);
 
 	err = -EINVAL;
 	if (unlikely(!sb))
diff --git a/fs/aufs/rdu.c b/fs/aufs/rdu.c
index 238c56858eac..1f0d8c65e1f7 100644
--- a/fs/aufs/rdu.c
+++ b/fs/aufs/rdu.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 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
diff --git a/fs/aufs/rwsem.h b/fs/aufs/rwsem.h
index 678fe6fca966..2abe89fb29ff 100644
--- a/fs/aufs/rwsem.h
+++ b/fs/aufs/rwsem.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 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
diff --git a/fs/aufs/sbinfo.c b/fs/aufs/sbinfo.c
index 0b7e93be910f..30be8c97eaad 100644
--- a/fs/aufs/sbinfo.c
+++ b/fs/aufs/sbinfo.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 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
@@ -32,7 +32,7 @@ void au_si_free(struct kobject *kobj)
 
 	sbinfo = container_of(kobj, struct au_sbinfo, si_kobj);
 	for (i = 0; i < AuPlink_NHASH; i++)
-		AuDebugOn(!hlist_empty(&sbinfo->si_plink[i].head));
+		AuDebugOn(!hlist_bl_empty(sbinfo->si_plink + i));
 	AuDebugOn(atomic_read(&sbinfo->si_nowait.nw_len));
 
 	AuDebugOn(percpu_counter_sum(&sbinfo->si_ninodes));
@@ -44,15 +44,11 @@ void au_si_free(struct kobject *kobj)
 	au_br_free(sbinfo);
 	au_rw_write_unlock(&sbinfo->si_rwsem);
 
-	au_delayed_kfree(sbinfo->si_branch);
-	for (i = 0; i < AU_NPIDMAP; i++)
-		if (sbinfo->au_si_pid.pid_bitmap[i])
-			au_delayed_kfree(sbinfo->au_si_pid.pid_bitmap[i]);
-	mutex_destroy(&sbinfo->au_si_pid.pid_mtx);
+	kfree(sbinfo->si_branch);
 	mutex_destroy(&sbinfo->si_xib_mtx);
 	AuRwDestroy(&sbinfo->si_rwsem);
 
-	au_delayed_kfree(sbinfo);
+	kfree(sbinfo);
 }
 
 int au_si_alloc(struct super_block *sb)
@@ -76,7 +72,6 @@ int au_si_alloc(struct super_block *sb)
 
 	au_nwt_init(&sbinfo->si_nowait);
 	au_rw_init_wlock(&sbinfo->si_rwsem);
-	mutex_init(&sbinfo->au_si_pid.pid_mtx);
 
 	percpu_counter_init(&sbinfo->si_ninodes, 0, GFP_NOFS);
 	percpu_counter_init(&sbinfo->si_nfiles, 0, GFP_NOFS);
@@ -100,7 +95,7 @@ int au_si_alloc(struct super_block *sb)
 	sbinfo->si_xino_brid = -1;
 	/* leave si_xib_last_pindex and si_xib_next_bit */
 
-	au_sphl_init(&sbinfo->si_aopen);
+	INIT_HLIST_BL_HEAD(&sbinfo->si_aopen);
 
 	sbinfo->si_rdcache = msecs_to_jiffies(AUFS_RDCACHE_DEF * MSEC_PER_SEC);
 	sbinfo->si_rdblk = AUFS_RDBLK_DEF;
@@ -108,11 +103,11 @@ int au_si_alloc(struct super_block *sb)
 	sbinfo->si_dirwh = AUFS_DIRWH_DEF;
 
 	for (i = 0; i < AuPlink_NHASH; i++)
-		au_sphl_init(sbinfo->si_plink + i);
+		INIT_HLIST_BL_HEAD(sbinfo->si_plink + i);
 	init_waitqueue_head(&sbinfo->si_plink_wq);
 	spin_lock_init(&sbinfo->si_plink_maint_lock);
 
-	au_sphl_init(&sbinfo->si_files);
+	INIT_HLIST_BL_HEAD(&sbinfo->si_files);
 
 	/* with getattr by default */
 	sbinfo->si_iop_array = aufs_iop;
@@ -124,9 +119,9 @@ int au_si_alloc(struct super_block *sb)
 	return 0; /* success */
 
 out_br:
-	au_delayed_kfree(sbinfo->si_branch);
+	kfree(sbinfo->si_branch);
 out_sbinfo:
-	au_delayed_kfree(sbinfo);
+	kfree(sbinfo);
 out:
 	return err;
 }
@@ -307,49 +302,3 @@ void aufs_read_and_write_unlock2(struct dentry *d1, struct dentry *d2)
 	di_write_unlock2(d1, d2);
 	si_read_unlock(d1->d_sb);
 }
-
-/* ---------------------------------------------------------------------- */
-
-static void si_pid_alloc(struct au_si_pid *au_si_pid, int idx)
-{
-	unsigned long *p;
-
-	BUILD_BUG_ON(sizeof(unsigned long) !=
-		     sizeof(*au_si_pid->pid_bitmap));
-
-	mutex_lock(&au_si_pid->pid_mtx);
-	p = au_si_pid->pid_bitmap[idx];
-	while (!p) {
-		/*
-		 * bad approach.
-		 * but keeping 'si_pid_set()' void is more important.
-		 */
-		p = kcalloc(BITS_TO_LONGS(AU_PIDSTEP),
-			    sizeof(*au_si_pid->pid_bitmap),
-			    GFP_NOFS);
-		if (p)
-			break;
-		cond_resched();
-	}
-	au_si_pid->pid_bitmap[idx] = p;
-	mutex_unlock(&au_si_pid->pid_mtx);
-}
-
-void si_pid_set(struct super_block *sb)
-{
-	pid_t bit;
-	int idx;
-	unsigned long *bitmap;
-	struct au_si_pid *au_si_pid;
-
-	si_pid_idx_bit(&idx, &bit);
-	au_si_pid = &au_sbi(sb)->au_si_pid;
-	bitmap = au_si_pid->pid_bitmap[idx];
-	if (!bitmap) {
-		si_pid_alloc(au_si_pid, idx);
-		bitmap = au_si_pid->pid_bitmap[idx];
-	}
-	AuDebugOn(test_bit(bit, bitmap));
-	set_bit(bit, bitmap);
-	/* smp_mb(); */
-}
diff --git a/fs/aufs/super.c b/fs/aufs/super.c
index 093fba6e249b..a02f68bf2145 100644
--- a/fs/aufs/super.c
+++ b/fs/aufs/super.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 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
@@ -46,7 +46,7 @@ static void aufs_destroy_inode_cb(struct rcu_head *head)
 {
 	struct inode *inode = container_of(head, struct inode, i_rcu);
 
-	au_cache_dfree_icntnr(container_of(inode, struct au_icntnr, vfs_inode));
+	au_cache_free_icntnr(container_of(inode, struct au_icntnr, vfs_inode));
 }
 
 static void aufs_destroy_inode(struct inode *inode)
@@ -118,15 +118,31 @@ static int au_show_brs(struct seq_file *seq, struct super_block *sb)
 	return err;
 }
 
+static void au_gen_fmt(char *fmt, int len __maybe_unused, const char *pat,
+		       const char *append)
+{
+	char *p;
+
+	p = fmt;
+	while (*pat != ':')
+		*p++ = *pat++;
+	*p++ = *pat++;
+	strcpy(p, append);
+	AuDebugOn(strlen(fmt) >= len);
+}
+
 static void au_show_wbr_create(struct seq_file *m, int v,
 			       struct au_sbinfo *sbinfo)
 {
 	const char *pat;
+	char fmt[32];
+	struct au_wbr_mfs *mfs;
 
 	AuRwMustAnyLock(&sbinfo->si_rwsem);
 
 	seq_puts(m, ",create=");
 	pat = au_optstr_wbr_create(v);
+	mfs = &sbinfo->si_wbr_mfs;
 	switch (v) {
 	case AuWbrCreate_TDP:
 	case AuWbrCreate_RR:
@@ -134,36 +150,28 @@ static void au_show_wbr_create(struct seq_file *m, int v,
 	case AuWbrCreate_PMFS:
 		seq_puts(m, pat);
 		break;
-	case AuWbrCreate_MFSV:
-		seq_printf(m, /*pat*/"mfs:%lu",
-			   jiffies_to_msecs(sbinfo->si_wbr_mfs.mfs_expire)
-			   / MSEC_PER_SEC);
+	case AuWbrCreate_MFSRR:
+	case AuWbrCreate_TDMFS:
+	case AuWbrCreate_PMFSRR:
+		au_gen_fmt(fmt, sizeof(fmt), pat, "%llu");
+		seq_printf(m, fmt, mfs->mfsrr_watermark);
 		break;
+	case AuWbrCreate_MFSV:
 	case AuWbrCreate_PMFSV:
-		seq_printf(m, /*pat*/"pmfs:%lu",
-			   jiffies_to_msecs(sbinfo->si_wbr_mfs.mfs_expire)
+		au_gen_fmt(fmt, sizeof(fmt), pat, "%lu");
+		seq_printf(m, fmt,
+			   jiffies_to_msecs(mfs->mfs_expire)
 			   / MSEC_PER_SEC);
 		break;
-	case AuWbrCreate_MFSRR:
-		seq_printf(m, /*pat*/"mfsrr:%llu",
-			   sbinfo->si_wbr_mfs.mfsrr_watermark);
-		break;
 	case AuWbrCreate_MFSRRV:
-		seq_printf(m, /*pat*/"mfsrr:%llu:%lu",
-			   sbinfo->si_wbr_mfs.mfsrr_watermark,
-			   jiffies_to_msecs(sbinfo->si_wbr_mfs.mfs_expire)
-			   / MSEC_PER_SEC);
-		break;
-	case AuWbrCreate_PMFSRR:
-		seq_printf(m, /*pat*/"pmfsrr:%llu",
-			   sbinfo->si_wbr_mfs.mfsrr_watermark);
-		break;
+	case AuWbrCreate_TDMFSV:
 	case AuWbrCreate_PMFSRRV:
-		seq_printf(m, /*pat*/"pmfsrr:%llu:%lu",
-			   sbinfo->si_wbr_mfs.mfsrr_watermark,
-			   jiffies_to_msecs(sbinfo->si_wbr_mfs.mfs_expire)
-			   / MSEC_PER_SEC);
+		au_gen_fmt(fmt, sizeof(fmt), pat, "%llu:%lu");
+		seq_printf(m, fmt, mfs->mfsrr_watermark,
+			   jiffies_to_msecs(mfs->mfs_expire) / MSEC_PER_SEC);
 		break;
+	default:
+		BUG();
 	}
 }
 
@@ -280,6 +288,7 @@ static int aufs_show_options(struct seq_file *m, struct dentry *dentry)
 
 	au_fhsm_show(m, sbinfo);
 
+	AuBool(DIRREN, dirren);
 	AuBool(SUM, sum);
 	/* AuBool(SUM_W, wsum); */
 	AuBool(WARN_PERM, warn_perm);
@@ -439,12 +448,10 @@ static int aufs_sync_fs(struct super_block *sb, int wait)
 			continue;
 
 		h_sb = au_sbr_sb(sb, bindex);
-		if (h_sb->s_op->sync_fs) {
-			e = h_sb->s_op->sync_fs(h_sb, wait);
-			if (unlikely(e && !err))
-				err = e;
-			/* go on even if an error happens */
-		}
+		e = vfsub_sync_filesystem(h_sb, wait);
+		if (unlikely(e && !err))
+			err = e;
+		/* go on even if an error happens */
 	}
 	si_read_unlock(sb);
 
@@ -828,7 +835,7 @@ static int aufs_remount_fs(struct super_block *sb, int *flags, char *data)
 out_mtx:
 	inode_unlock(inode);
 out_opts:
-	au_delayed_free_page((unsigned long)opts.opt);
+	free_page((unsigned long)opts.opt);
 out:
 	err = cvt_err(err);
 	AuTraceErr(err);
@@ -923,7 +930,7 @@ static int aufs_fill_super(struct super_block *sb, void *raw_data,
 	sb->s_maxbytes = 0;
 	sb->s_stack_depth = 1;
 	au_export_init(sb);
-	/* au_xattr_init(sb); */
+	au_xattr_init(sb);
 
 	err = alloc_root(sb);
 	if (unlikely(err)) {
@@ -969,7 +976,7 @@ static int aufs_fill_super(struct super_block *sb, void *raw_data,
 	kobject_put(&sbinfo->si_kobj);
 	sb->s_fs_info = NULL;
 out_opts:
-	au_delayed_free_page((unsigned long)opts.opt);
+	free_page((unsigned long)opts.opt);
 out:
 	AuTraceErr(err);
 	err = cvt_err(err);
@@ -1020,6 +1027,7 @@ static void aufs_kill_sb(struct super_block *sb)
 		if (au_opt_test(sbinfo->si_mntflags, PLINK))
 			au_plink_put(sb, /*verbose*/1);
 		au_xino_clr(sb);
+		au_dr_opt_flush(sb);
 		sbinfo->si_sb = NULL;
 		aufs_write_unlock(sb->s_root);
 		au_nwt_flush(&sbinfo->si_nowait);
diff --git a/fs/aufs/super.h b/fs/aufs/super.h
index 37ea182b4189..54ac19081c7f 100644
--- a/fs/aufs/super.h
+++ b/fs/aufs/super.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -26,8 +26,8 @@
 
 #include <linux/fs.h>
 #include <linux/kobject.h>
+#include "hbl.h"
 #include "rwsem.h"
-#include "spl.h"
 #include "wkq.h"
 
 /* policies to select one among multiple writable branches */
@@ -79,13 +79,6 @@ struct au_fhsm {
 #endif
 };
 
-#define AU_PIDSTEP	(int)(BITS_TO_LONGS(PID_MAX_DEFAULT) * BITS_PER_LONG)
-#define AU_NPIDMAP	(int)DIV_ROUND_UP(PID_MAX_LIMIT, AU_PIDSTEP)
-struct au_si_pid {
-	unsigned long	*pid_bitmap[AU_NPIDMAP];
-	struct mutex	pid_mtx;
-};
-
 struct au_branch;
 struct au_sbinfo {
 	/* nowait tasks in the system-wide workqueue */
@@ -97,9 +90,6 @@ struct au_sbinfo {
 	 */
 	struct au_rwsem		si_rwsem;
 
-	/* prevent recursive locking in deleting inode */
-	struct au_si_pid	au_si_pid;
-
 	/*
 	 * dirty approach to protect sb->sb_inodes and ->s_files (gone) from
 	 * remount.
@@ -159,7 +149,7 @@ struct au_sbinfo {
 #endif
 
 	/* dirty trick to suppoer atomic_open */
-	struct au_sphlhead	si_aopen;
+	struct hlist_bl_head	si_aopen;
 
 	/* vdir parameters */
 	unsigned long		si_rdcache;	/* max cache time in jiffies */
@@ -175,13 +165,13 @@ struct au_sbinfo {
 	unsigned int		si_dirwh;
 
 	/* pseudo_link list */
-	struct au_sphlhead	si_plink[AuPlink_NHASH];
+	struct hlist_bl_head	si_plink[AuPlink_NHASH];
 	wait_queue_head_t	si_plink_wq;
 	spinlock_t		si_plink_maint_lock;
 	pid_t			si_plink_maint_pid;
 
 	/* file list */
-	struct au_sphlhead	si_files;
+	struct hlist_bl_head	si_files;
 
 	/* with/without getattr, brother of sb->s_d_op */
 	struct inode_operations *si_iop_array;
@@ -203,7 +193,7 @@ struct au_sbinfo {
 #endif
 
 #ifdef CONFIG_AUFS_SBILIST
-	struct hlist_node	si_list;
+	struct hlist_bl_node	si_list;
 #endif
 
 	/* dirty, necessary for unmounting, sysfs and sysrq */
@@ -255,6 +245,7 @@ static inline unsigned char au_do_ftest_si(struct au_sbinfo *sbi,
 #define AuLock_IW		(1 << 2)	/* write-lock inode */
 #define AuLock_FLUSH		(1 << 3)	/* wait for 'nowait' tasks */
 #define AuLock_DIRS		(1 << 4)	/* target is a pair of dirs */
+						/* except RENAME_EXCHANGE */
 #define AuLock_NOPLM		(1 << 5)	/* return err in plm mode */
 #define AuLock_NOPLMW		(1 << 6)	/* wait for plm mode ends */
 #define AuLock_GEN		(1 << 7)	/* test digen/iigen */
@@ -377,32 +368,32 @@ AuStub(int, au_busy_or_stale, return -EBUSY, void)
 
 #ifdef CONFIG_AUFS_SBILIST
 /* module.c */
-extern struct au_sphlhead au_sbilist;
+extern struct hlist_bl_head au_sbilist;
 
 static inline void au_sbilist_init(void)
 {
-	au_sphl_init(&au_sbilist);
+	INIT_HLIST_BL_HEAD(&au_sbilist);
 }
 
 static inline void au_sbilist_add(struct super_block *sb)
 {
-	au_sphl_add(&au_sbi(sb)->si_list, &au_sbilist);
+	au_hbl_add(&au_sbi(sb)->si_list, &au_sbilist);
 }
 
 static inline void au_sbilist_del(struct super_block *sb)
 {
-	au_sphl_del(&au_sbi(sb)->si_list, &au_sbilist);
+	au_hbl_del(&au_sbi(sb)->si_list, &au_sbilist);
 }
 
 #ifdef CONFIG_AUFS_MAGIC_SYSRQ
 static inline void au_sbilist_lock(void)
 {
-	spin_lock(&au_sbilist.spin);
+	hlist_bl_lock(&au_sbilist);
 }
 
 static inline void au_sbilist_unlock(void)
 {
-	spin_unlock(&au_sbilist.spin);
+	hlist_bl_unlock(&au_sbilist);
 }
 #define AuGFP_SBILIST	GFP_ATOMIC
 #else
@@ -440,42 +431,30 @@ static inline void dbgaufs_si_null(struct au_sbinfo *sbinfo)
 
 /* ---------------------------------------------------------------------- */
 
-static inline void si_pid_idx_bit(int *idx, pid_t *bit)
-{
-	/* the origin of pid is 1, but the bitmap's is 0 */
-	*bit = current->pid - 1;
-	*idx = *bit / AU_PIDSTEP;
-	*bit %= AU_PIDSTEP;
-}
+/* current->atomic_flags */
+/* this value should never corrupt the ones defined in linux/sched.h */
+#define PFA_AUFS	7
+
+TASK_PFA_TEST(AUFS, test_aufs)	/* task_test_aufs */
+TASK_PFA_SET(AUFS, aufs)	/* task_set_aufs */
+TASK_PFA_CLEAR(AUFS, aufs)	/* task_clear_aufs */
 
 static inline int si_pid_test(struct super_block *sb)
 {
-	pid_t bit;
-	int idx;
-	unsigned long *bitmap;
-
-	si_pid_idx_bit(&idx, &bit);
-	bitmap = au_sbi(sb)->au_si_pid.pid_bitmap[idx];
-	if (bitmap)
-		return test_bit(bit, bitmap);
-	return 0;
+	return !!task_test_aufs(current);
 }
 
 static inline void si_pid_clr(struct super_block *sb)
 {
-	pid_t bit;
-	int idx;
-	unsigned long *bitmap;
-
-	si_pid_idx_bit(&idx, &bit);
-	bitmap = au_sbi(sb)->au_si_pid.pid_bitmap[idx];
-	BUG_ON(!bitmap);
-	AuDebugOn(!test_bit(bit, bitmap));
-	clear_bit(bit, bitmap);
-	/* smp_mb(); */
+	AuDebugOn(!task_test_aufs(current));
+	task_clear_aufs(current);
 }
 
-void si_pid_set(struct super_block *sb);
+static inline void si_pid_set(struct super_block *sb)
+{
+	AuDebugOn(task_test_aufs(current));
+	task_set_aufs(current);
+}
 
 /* ---------------------------------------------------------------------- */
 
diff --git a/fs/aufs/sysaufs.c b/fs/aufs/sysaufs.c
index 75c9c24a66d0..3f172fd022e4 100644
--- a/fs/aufs/sysaufs.c
+++ b/fs/aufs/sysaufs.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 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
diff --git a/fs/aufs/sysaufs.h b/fs/aufs/sysaufs.h
index 14975c9da383..33307336ff75 100644
--- a/fs/aufs/sysaufs.h
+++ b/fs/aufs/sysaufs.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 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
diff --git a/fs/aufs/sysfs.c b/fs/aufs/sysfs.c
index 36e66d6bfa74..096bde996740 100644
--- a/fs/aufs/sysfs.c
+++ b/fs/aufs/sysfs.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 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
@@ -209,7 +209,7 @@ ssize_t sysaufs_si_show(struct kobject *kobj, struct attribute *attr,
 		if (unlikely(err == PAGE_SIZE))
 			err = -EFBIG;
 	}
-	au_delayed_kfree(seq);
+	kfree(seq);
 out_unlock:
 	si_read_unlock(sb);
 out:
@@ -280,9 +280,9 @@ static int au_brinfo(struct super_block *sb, union aufs_brinfo __user *arg)
 		err = -EFAULT;
 
 out_seq:
-	au_delayed_kfree(seq);
+	kfree(seq);
 out_buf:
-	au_delayed_free_page((unsigned long)buf);
+	free_page((unsigned long)buf);
 out:
 	si_read_unlock(sb);
 	return err;
diff --git a/fs/aufs/sysrq.c b/fs/aufs/sysrq.c
index cbebb376d1e4..97c68ab42a71 100644
--- a/fs/aufs/sysrq.c
+++ b/fs/aufs/sysrq.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 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
@@ -30,7 +30,8 @@ static void sysrq_sb(struct super_block *sb)
 	char *plevel;
 	struct au_sbinfo *sbinfo;
 	struct file *file;
-	struct au_sphlhead *files;
+	struct hlist_bl_head *files;
+	struct hlist_bl_node *pos;
 	struct au_finfo *finfo;
 
 	plevel = au_plevel;
@@ -89,8 +90,8 @@ static void sysrq_sb(struct super_block *sb)
 #endif
 	pr("files\n");
 	files = &au_sbi(sb)->si_files;
-	spin_lock(&files->spin);
-	hlist_for_each_entry(finfo, &files->head, fi_hlist) {
+	hlist_bl_lock(files);
+	hlist_bl_for_each_entry(finfo, pos, files, fi_hlist) {
 		umode_t mode;
 
 		file = finfo->fi_file;
@@ -98,7 +99,7 @@ static void sysrq_sb(struct super_block *sb)
 		if (!special_file(mode))
 			au_dpri_file(file);
 	}
-	spin_unlock(&files->spin);
+	hlist_bl_unlock(files);
 	pr("done\n");
 
 #undef pr
@@ -115,10 +116,11 @@ MODULE_PARM_DESC(sysrq, "MagicSysRq key for " AUFS_NAME);
 static void au_sysrq(int key __maybe_unused)
 {
 	struct au_sbinfo *sbinfo;
+	struct hlist_bl_node *pos;
 
 	lockdep_off();
 	au_sbilist_lock();
-	hlist_for_each_entry(sbinfo, &au_sbilist.head, si_list)
+	hlist_bl_for_each_entry(sbinfo, pos, &au_sbilist, si_list)
 		sysrq_sb(sbinfo->si_sb);
 	au_sbilist_unlock();
 	lockdep_on();
diff --git a/fs/aufs/vdir.c b/fs/aufs/vdir.c
index 9a087ce6dc15..b7583e9f0d36 100644
--- a/fs/aufs/vdir.c
+++ b/fs/aufs/vdir.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 Junjiro R. Okajima
  *
  * This program, aufs is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -110,7 +110,7 @@ static void au_nhash_wh_do_free(struct hlist_head *head)
 	struct hlist_node *node;
 
 	hlist_for_each_entry_safe(pos, node, head, wh_hash)
-		au_delayed_kfree(pos);
+		kfree(pos);
 }
 
 static void au_nhash_de_do_free(struct hlist_head *head)
@@ -119,7 +119,7 @@ static void au_nhash_de_do_free(struct hlist_head *head)
 	struct hlist_node *node;
 
 	hlist_for_each_entry_safe(pos, node, head, hash)
-		au_cache_dfree_vdir_dehstr(pos);
+		au_cache_free_vdir_dehstr(pos);
 }
 
 static void au_nhash_do_free(struct au_nhash *nhash,
@@ -137,7 +137,7 @@ static void au_nhash_do_free(struct au_nhash *nhash,
 		nhash_count(head);
 		free(head++);
 	}
-	au_delayed_kfree(nhash->nh_head);
+	kfree(nhash->nh_head);
 }
 
 void au_nhash_wh_free(struct au_nhash *whlist)
@@ -350,23 +350,15 @@ static int append_de(struct au_vdir *vdir, char *name, int nlen, ino_t ino,
 
 /* ---------------------------------------------------------------------- */
 
-void au_vdir_free(struct au_vdir *vdir, int atonce)
+void au_vdir_free(struct au_vdir *vdir)
 {
 	unsigned char **deblk;
 
 	deblk = vdir->vd_deblk;
-	if (!atonce) {
-		while (vdir->vd_nblk--)
-			au_delayed_kfree(*deblk++);
-		au_delayed_kfree(vdir->vd_deblk);
-		au_cache_dfree_vdir(vdir);
-	} else {
-		/* not delayed */
-		while (vdir->vd_nblk--)
-			kfree(*deblk++);
-		kfree(vdir->vd_deblk);
-		au_cache_free_vdir(vdir);
-	}
+	while (vdir->vd_nblk--)
+		kfree(*deblk++);
+	kfree(vdir->vd_deblk);
+	au_cache_free_vdir(vdir);
 }
 
 static struct au_vdir *alloc_vdir(struct file *file)
@@ -400,10 +392,10 @@ static struct au_vdir *alloc_vdir(struct file *file)
 	if (!err)
 		return vdir; /* success */
 
-	au_delayed_kfree(vdir->vd_deblk);
+	kfree(vdir->vd_deblk);
 
 out_free:
-	au_cache_dfree_vdir(vdir);
+	au_cache_free_vdir(vdir);
 out:
 	vdir = ERR_PTR(err);
 	return vdir;
@@ -415,7 +407,7 @@ static int reinit_vdir(struct au_vdir *vdir)
 	union au_vdir_deblk_p p, deblk_end;
 
 	while (vdir->vd_nblk > 1) {
-		au_delayed_kfree(vdir->vd_deblk[vdir->vd_nblk - 1]);
+		kfree(vdir->vd_deblk[vdir->vd_nblk - 1]);
 		/* vdir->vd_deblk[vdir->vd_nblk - 1] = NULL; */
 		vdir->vd_nblk--;
 	}
@@ -546,7 +538,7 @@ static int au_handle_shwh(struct super_block *sb, struct au_vdir *vdir,
 		}
 	}
 
-	au_delayed_free_page((unsigned long)o);
+	free_page((unsigned long)o);
 
 out:
 	AuTraceErr(err);
@@ -685,7 +677,7 @@ static int read_vdir(struct file *file, int may_read)
 		if (allocated)
 			au_set_ivdir(inode, allocated);
 	} else if (allocated)
-		au_vdir_free(allocated, /*atonce*/0);
+		au_vdir_free(allocated);
 
 out:
 	return err;
@@ -780,7 +772,7 @@ int au_vdir_init(struct file *file)
 		if (allocated)
 			au_set_fvdir_cache(file, allocated);
 	} else if (allocated)
-		au_vdir_free(allocated, /*atonce*/0);
+		au_vdir_free(allocated);
 
 out:
 	return err;
@@ -849,7 +841,7 @@ static int seek_vdir(struct file *file, struct dir_context *ctx)
 
 out:
 	/* smp_mb(); */
-	AuTraceErr(valid - 1);
+	AuTraceErr(!valid);
 	return valid;
 }
 
diff --git a/fs/aufs/vfsub.c b/fs/aufs/vfsub.c
index 2d01dd891c27..a4d798a432a1 100644
--- a/fs/aufs/vfsub.c
+++ b/fs/aufs/vfsub.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 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
@@ -19,27 +19,36 @@
  * sub-routines for VFS
  */
 
+#include <linux/mnt_namespace.h>
 #include <linux/namei.h>
 #include <linux/nsproxy.h>
 #include <linux/security.h>
 #include <linux/splice.h>
-#include "../fs/mount.h"
 #include "aufs.h"
 
 #ifdef CONFIG_AUFS_BR_FUSE
 int vfsub_test_mntns(struct vfsmount *mnt, struct super_block *h_sb)
 {
-	struct nsproxy *ns;
-
 	if (!au_test_fuse(h_sb) || !au_userns)
 		return 0;
 
-	ns = current->nsproxy;
-	/* no {get,put}_nsproxy(ns) */
-	return real_mount(mnt)->mnt_ns == ns->mnt_ns ? 0 : -EACCES;
+	return is_current_mnt_ns(mnt) ? 0 : -EACCES;
 }
 #endif
 
+int vfsub_sync_filesystem(struct super_block *h_sb, int wait)
+{
+	int err;
+
+	lockdep_off();
+	down_read(&h_sb->s_umount);
+	err = __sync_filesystem(h_sb, wait);
+	up_read(&h_sb->s_umount);
+	lockdep_on();
+
+	return err;
+}
+
 /* ---------------------------------------------------------------------- */
 
 int vfsub_update_h_iattr(struct path *h_path, int *did)
@@ -383,7 +392,7 @@ int vfsub_link(struct dentry *src_dentry, struct inode *dir, struct path *path,
 
 int vfsub_rename(struct inode *src_dir, struct dentry *src_dentry,
 		 struct inode *dir, struct path *path,
-		 struct inode **delegated_inode)
+		 struct inode **delegated_inode, unsigned int flags)
 {
 	int err;
 	struct path tmp = {
@@ -404,7 +413,7 @@ int vfsub_rename(struct inode *src_dir, struct dentry *src_dentry,
 
 	lockdep_off();
 	err = vfs_rename(src_dir, src_dentry, dir, path->dentry,
-			 delegated_inode, /*flags*/0);
+			 delegated_inode, flags);
 	lockdep_on();
 	if (!err) {
 		int did;
@@ -583,6 +592,7 @@ int vfsub_iterate_dir(struct file *file, struct dir_context *ctx)
 	lockdep_on();
 	if (err >= 0)
 		vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/
+
 	return err;
 }
 
diff --git a/fs/aufs/vfsub.h b/fs/aufs/vfsub.h
index 69c60e962874..cb574e0bfc65 100644
--- a/fs/aufs/vfsub.h
+++ b/fs/aufs/vfsub.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 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
@@ -54,6 +54,13 @@ enum {
 #define MtxMustLock(mtx)	AuDebugOn(!mutex_is_locked(mtx))
 #define IMustLock(i)		AuDebugOn(!inode_is_locked(i))
 
+/* why VFS doesn't define it? */
+static inline
+void vfsub_inode_lock_shared_nested(struct inode *inode, unsigned int sc)
+{
+	down_read_nested(&inode->i_rwsem, sc);
+}
+
 /* ---------------------------------------------------------------------- */
 
 static inline void vfsub_drop_nlink(struct inode *inode)
@@ -83,6 +90,8 @@ int vfsub_test_mntns(struct vfsmount *mnt, struct super_block *h_sb);
 AuStubInt0(vfsub_test_mntns, struct vfsmount *mnt, struct super_block *h_sb);
 #endif
 
+int vfsub_sync_filesystem(struct super_block *h_sb, int wait);
+
 /* ---------------------------------------------------------------------- */
 
 int vfsub_update_h_iattr(struct path *h_path, int *did);
@@ -163,7 +172,7 @@ int vfsub_link(struct dentry *src_dentry, struct inode *dir,
 	       struct path *path, struct inode **delegated_inode);
 int vfsub_rename(struct inode *src_hdir, struct dentry *src_dentry,
 		 struct inode *hdir, struct path *path,
-		 struct inode **delegated_inode);
+		 struct inode **delegated_inode, unsigned int flags);
 int vfsub_mkdir(struct inode *dir, struct path *path, int mode);
 int vfsub_rmdir(struct inode *dir, struct path *path);
 
@@ -264,6 +273,36 @@ int vfsub_trunc(struct path *h_path, loff_t length, unsigned int attr,
 		struct file *h_file);
 int vfsub_fsync(struct file *file, struct path *path, int datasync);
 
+/*
+ * re-use branch fs's ioctl(FICLONE) while aufs itself doesn't support such
+ * ioctl.
+ */
+static inline int vfsub_clone_file_range(struct file *src, struct file *dst,
+					 u64 len)
+{
+	int err;
+
+	lockdep_off();
+	err = vfs_clone_file_range(src, 0, dst, 0, len);
+	lockdep_on();
+
+	return err;
+}
+
+/* copy_file_range(2) is a systemcall */
+static inline ssize_t vfsub_copy_file_range(struct file *src, loff_t src_pos,
+					    struct file *dst, loff_t dst_pos,
+					    size_t len, unsigned int flags)
+{
+	ssize_t ssz;
+
+	lockdep_off();
+	ssz = vfs_copy_file_range(src, src_pos, dst, dst_pos, len, flags);
+	lockdep_on();
+
+	return ssz;
+}
+
 /* ---------------------------------------------------------------------- */
 
 static inline loff_t vfsub_llseek(struct file *file, loff_t offset, int origin)
diff --git a/fs/aufs/wbr_policy.c b/fs/aufs/wbr_policy.c
index 9e508d8ec671..a28296d9cd31 100644
--- a/fs/aufs/wbr_policy.c
+++ b/fs/aufs/wbr_policy.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 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
@@ -461,7 +461,7 @@ static void au_mfs(struct dentry *dentry, struct dentry *parent)
 
 	mfs->mfsrr_bytes = bavail;
 	AuDbg("b%d\n", mfs->mfs_bindex);
-	au_delayed_kfree(st);
+	kfree(st);
 }
 
 static int au_wbr_create_mfs(struct dentry *dentry, unsigned int flags)
@@ -517,6 +517,61 @@ static int au_wbr_create_fin_mfs(struct super_block *sb __maybe_unused)
 
 /* ---------------------------------------------------------------------- */
 
+/* top down regardless parent, and then mfs */
+static int au_wbr_create_tdmfs(struct dentry *dentry,
+			       unsigned int flags __maybe_unused)
+{
+	int err;
+	aufs_bindex_t bwh, btail, bindex, bfound, bmfs;
+	unsigned long long watermark;
+	struct super_block *sb;
+	struct au_wbr_mfs *mfs;
+	struct au_branch *br;
+	struct dentry *parent;
+
+	sb = dentry->d_sb;
+	mfs = &au_sbi(sb)->si_wbr_mfs;
+	mutex_lock(&mfs->mfs_lock);
+	if (time_after(jiffies, mfs->mfs_jiffy + mfs->mfs_expire)
+	    || mfs->mfs_bindex < 0)
+		au_mfs(dentry, /*parent*/NULL);
+	watermark = mfs->mfsrr_watermark;
+	bmfs = mfs->mfs_bindex;
+	mutex_unlock(&mfs->mfs_lock);
+
+	/* another style of au_wbr_create_exp() */
+	bwh = au_dbwh(dentry);
+	parent = dget_parent(dentry);
+	btail = au_dbtaildir(parent);
+	if (bwh >= 0 && bwh < btail)
+		btail = bwh;
+
+	err = au_wbr_nonopq(dentry, btail);
+	if (unlikely(err < 0))
+		goto out;
+	btail = err;
+	bfound = -1;
+	for (bindex = 0; bindex <= btail; bindex++) {
+		br = au_sbr(sb, bindex);
+		if (au_br_rdonly(br))
+			continue;
+		if (br->br_wbr->wbr_bytes > watermark) {
+			bfound = bindex;
+			break;
+		}
+	}
+	err = bfound;
+	if (err < 0)
+		err = bmfs;
+
+out:
+	dput(parent);
+	AuDbg("b%d\n", err);
+	return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
 /* most free space and then round robin */
 static int au_wbr_create_mfsrr(struct dentry *dentry, unsigned int flags)
 {
@@ -742,6 +797,16 @@ struct au_wbr_create_operations au_wbr_create_ops[] = {
 		.init	= au_wbr_create_init_mfsrr,
 		.fin	= au_wbr_create_fin_mfs
 	},
+	[AuWbrCreate_TDMFS] = {
+		.create	= au_wbr_create_tdmfs,
+		.init	= au_wbr_create_init_mfs,
+		.fin	= au_wbr_create_fin_mfs
+	},
+	[AuWbrCreate_TDMFSV] = {
+		.create	= au_wbr_create_tdmfs,
+		.init	= au_wbr_create_init_mfs,
+		.fin	= au_wbr_create_fin_mfs
+	},
 	[AuWbrCreate_PMFS] = {
 		.create	= au_wbr_create_pmfs,
 		.init	= au_wbr_create_init_mfs,
diff --git a/fs/aufs/whout.c b/fs/aufs/whout.c
index cdd3a8fd7ea6..05c069ebb7c4 100644
--- a/fs/aufs/whout.c
+++ b/fs/aufs/whout.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 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
@@ -162,7 +162,7 @@ struct dentry *au_whtmp_lkup(struct dentry *h_parent, struct au_branch *br,
 
 out_name:
 	if (name != defname)
-		au_delayed_kfree(name);
+		kfree(name);
 out:
 	AuTraceErrPtr(dentry);
 	return dentry;
@@ -191,7 +191,8 @@ int au_whtmp_ren(struct dentry *h_dentry, struct au_branch *br)
 
 	/* under the same dir, no need to lock_rename() */
 	delegated = NULL;
-	err = vfsub_rename(h_dir, h_dentry, h_dir, &h_path, &delegated);
+	err = vfsub_rename(h_dir, h_dentry, h_dir, &h_path, &delegated,
+			   /*flags*/0);
 	AuTraceErr(err);
 	if (unlikely(err == -EWOULDBLOCK)) {
 		pr_warn("cannot retry for NFSv4 delegation"
@@ -601,7 +602,7 @@ static void reinit_br_wh(void *arg)
 	au_br_put(a->br);
 	si_write_unlock(a->sb);
 	au_nwt_done(&au_sbi(a->sb)->si_nowait);
-	au_delayed_kfree(arg);
+	kfree(arg);
 	if (unlikely(err))
 		AuIOErr("err %d\n", err);
 }
@@ -629,7 +630,7 @@ static void kick_reinit_br_wh(struct super_block *sb, struct au_branch *br)
 		if (unlikely(wkq_err)) {
 			atomic_dec(&br->br_wbr->wbr_wh_running);
 			au_br_put(br);
-			au_delayed_kfree(arg);
+			kfree(arg);
 		}
 		do_dec = 0;
 	}
@@ -788,7 +789,7 @@ struct dentry *au_wh_lkup(struct dentry *h_parent, struct qstr *base_name,
 	wh_dentry = ERR_PTR(err);
 	if (!err) {
 		wh_dentry = vfsub_lkup_one(&wh_name, h_parent);
-		au_delayed_kfree(wh_name.name);
+		kfree(wh_name.name);
 	}
 	return wh_dentry;
 }
@@ -864,7 +865,7 @@ static int del_wh_children(struct dentry *h_dentry, struct au_nhash *whlist,
 			break;
 		}
 	}
-	au_delayed_free_page((unsigned long)wh_name.name);
+	free_page((unsigned long)wh_name.name);
 
 out:
 	return err;
@@ -906,7 +907,7 @@ struct au_whtmp_rmdir *au_whtmp_rmdir_alloc(struct super_block *sb, gfp_t gfp)
 		rdhash = AUFS_RDHASH_DEF;
 	err = au_nhash_alloc(&whtmp->whlist, rdhash, gfp);
 	if (unlikely(err)) {
-		au_delayed_kfree(whtmp);
+		kfree(whtmp);
 		whtmp = ERR_PTR(err);
 	}
 
@@ -921,7 +922,7 @@ void au_whtmp_rmdir_free(struct au_whtmp_rmdir *whtmp)
 	dput(whtmp->wh_dentry);
 	iput(whtmp->dir);
 	au_nhash_wh_free(&whtmp->whlist);
-	au_delayed_kfree(whtmp);
+	kfree(whtmp);
 }
 
 /*
diff --git a/fs/aufs/whout.h b/fs/aufs/whout.h
index 5a5c37879d52..eb4b1823bd49 100644
--- a/fs/aufs/whout.h
+++ b/fs/aufs/whout.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 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
diff --git a/fs/aufs/wkq.c b/fs/aufs/wkq.c
index b18cdab42e03..9f2ee30cfe56 100644
--- a/fs/aufs/wkq.c
+++ b/fs/aufs/wkq.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 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
@@ -54,7 +54,7 @@ static void wkq_func(struct work_struct *wk)
 	else {
 		kobject_put(wkinfo->kobj);
 		module_put(THIS_MODULE); /* todo: ?? */
-		au_delayed_kfree(wkinfo);
+		kfree(wkinfo);
 	}
 }
 
@@ -77,7 +77,7 @@ static int au_wkq_comp_alloc(struct au_wkinfo *wkinfo, struct completion **comp)
 
 static void au_wkq_comp_free(struct completion *comp)
 {
-	au_delayed_kfree(comp);
+	kfree(comp);
 }
 
 #else
@@ -141,11 +141,10 @@ int au_wkq_do_wait(unsigned int flags, au_wkq_func_t func, void *args)
 		/* no timeout, no interrupt */
 		wait_for_completion(wkinfo.comp);
 		au_wkq_comp_free(comp);
-		destroy_work_on_stack(&wkinfo.wk);
 	}
 
+	destroy_work_on_stack(&wkinfo.wk);
 	return err;
-
 }
 
 /*
diff --git a/fs/aufs/wkq.h b/fs/aufs/wkq.h
index 9b878361fe67..ac1cd5e563ea 100644
--- a/fs/aufs/wkq.h
+++ b/fs/aufs/wkq.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 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
@@ -25,7 +25,7 @@
 
 #ifdef __KERNEL__
 
-#include <linux/percpu_counter.h>
+#include <linux/wait.h>
 
 struct super_block;
 
diff --git a/fs/aufs/xattr.c b/fs/aufs/xattr.c
index e91904450ca4..ffff4efbbc22 100644
--- a/fs/aufs/xattr.c
+++ b/fs/aufs/xattr.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014-2016 Junjiro R. Okajima
+ * Copyright (C) 2014-2017 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
@@ -19,6 +19,8 @@
  * handling xattr functions
  */
 
+#include <linux/fs.h>
+#include <linux/posix_acl_xattr.h>
 #include <linux/xattr.h>
 #include "aufs.h"
 
@@ -114,7 +116,7 @@ int au_cpup_xattr(struct dentry *h_dst, struct dentry *h_src, int ignore_flags,
 	h_isrc = d_inode(h_src);
 	h_idst = d_inode(h_dst);
 	inode_unlock(h_idst);
-	inode_lock_nested(h_isrc, AuLsc_I_CHILD);
+	vfsub_inode_lock_shared_nested(h_isrc, AuLsc_I_CHILD);
 	inode_lock_nested(h_idst, AuLsc_I_CHILD2);
 	unlocked = 0;
 
@@ -140,7 +142,7 @@ int au_cpup_xattr(struct dentry *h_dst, struct dentry *h_src, int ignore_flags,
 			goto out;
 		err = vfs_listxattr(h_src, p, ssz);
 	}
-	inode_unlock(h_isrc);
+	inode_unlock_shared(h_isrc);
 	unlocked = 1;
 	AuDbg("err %d, ssz %zd\n", err, ssz);
 	if (unlikely(err < 0))
@@ -176,21 +178,32 @@ int au_cpup_xattr(struct dentry *h_dst, struct dentry *h_src, int ignore_flags,
 		AuTraceErr(err);
 	}
 
-	if (value)
-		au_delayed_kfree(value);
+	kfree(value);
 
 out_free:
-	if (o)
-		au_delayed_kfree(o);
+	kfree(o);
 out:
 	if (!unlocked)
-		inode_unlock(h_isrc);
+		inode_unlock_shared(h_isrc);
 	AuTraceErr(err);
 	return err;
 }
 
 /* ---------------------------------------------------------------------- */
 
+static int au_smack_reentering(struct super_block *sb)
+{
+#if IS_ENABLED(CONFIG_SECURITY_SMACK)
+	/*
+	 * as a part of lookup, smack_d_instantiate() is called, and it calls
+	 * i_op->getxattr(). ouch.
+	 */
+	return si_pid_test(sb);
+#else
+	return 0;
+#endif
+}
+
 enum {
 	AU_XATTR_LIST,
 	AU_XATTR_GET
@@ -214,14 +227,18 @@ struct au_lgxattr {
 static ssize_t au_lgxattr(struct dentry *dentry, struct au_lgxattr *arg)
 {
 	ssize_t err;
+	int reenter;
 	struct path h_path;
 	struct super_block *sb;
 
 	sb = dentry->d_sb;
-	err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM);
-	if (unlikely(err))
-		goto out;
-	err = au_h_path_getattr(dentry, /*force*/1, &h_path);
+	reenter = au_smack_reentering(sb);
+	if (!reenter) {
+		err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM);
+		if (unlikely(err))
+			goto out;
+	}
+	err = au_h_path_getattr(dentry, /*force*/1, &h_path, reenter);
 	if (unlikely(err))
 		goto out_si;
 	if (unlikely(!h_path.dentry))
@@ -243,9 +260,11 @@ static ssize_t au_lgxattr(struct dentry *dentry, struct au_lgxattr *arg)
 	}
 
 out_di:
-	di_read_unlock(dentry, AuLock_IR);
+	if (!reenter)
+		di_read_unlock(dentry, AuLock_IR);
 out_si:
-	si_read_unlock(sb);
+	if (!reenter)
+		si_read_unlock(sb);
 out:
 	AuTraceErr(err);
 	return err;
@@ -264,8 +283,9 @@ ssize_t aufs_listxattr(struct dentry *dentry, char *list, size_t size)
 	return au_lgxattr(dentry, &arg);
 }
 
-ssize_t aufs_getxattr(struct dentry *dentry, struct inode *inode __maybe_unused,
-		      const char *name, void *value, size_t size)
+static ssize_t au_getxattr(struct dentry *dentry,
+			   struct inode *inode __maybe_unused,
+			   const char *name, void *value, size_t size)
 {
 	struct au_lgxattr arg = {
 		.type = AU_XATTR_GET,
@@ -279,10 +299,11 @@ ssize_t aufs_getxattr(struct dentry *dentry, struct inode *inode __maybe_unused,
 	return au_lgxattr(dentry, &arg);
 }
 
-int aufs_setxattr(struct dentry *dentry, struct inode *inode, const char *name,
-		  const void *value, size_t size, int flags)
+static int au_setxattr(struct dentry *dentry, struct inode *inode,
+		       const char *name, const void *value, size_t size,
+		       int flags)
 {
-	struct au_srxattr arg = {
+	struct au_sxattr arg = {
 		.type = AU_XATTR_SET,
 		.u.set = {
 			.name	= name,
@@ -292,56 +313,43 @@ int aufs_setxattr(struct dentry *dentry, struct inode *inode, const char *name,
 		},
 	};
 
-	return au_srxattr(dentry, inode, &arg);
-}
-
-int aufs_removexattr(struct dentry *dentry, const char *name)
-{
-	struct au_srxattr arg = {
-		.type = AU_XATTR_REMOVE,
-		.u.remove = {
-			.name	= name
-		},
-	};
-
-	return au_srxattr(dentry, d_inode(dentry), &arg);
+	return au_sxattr(dentry, inode, &arg);
 }
 
 /* ---------------------------------------------------------------------- */
 
-#if 0
-static size_t au_xattr_list(struct dentry *dentry, char *list, size_t list_size,
-			    const char *name, size_t name_len, int type)
+static int au_xattr_get(const struct xattr_handler *handler,
+			struct dentry *dentry, struct inode *inode,
+			const char *name, void *buffer, size_t size)
 {
-	return aufs_listxattr(dentry, list, list_size);
+	return au_getxattr(dentry, inode, name, buffer, size);
 }
 
-static int au_xattr_get(struct dentry *dentry, const char *name, void *buffer,
-			size_t size, int type)
+static int au_xattr_set(const struct xattr_handler *handler,
+			struct dentry *dentry, struct inode *inode,
+			const char *name, const void *value, size_t size,
+			int flags)
 {
-	return aufs_getxattr(dentry, name, buffer, size);
-}
-
-static int au_xattr_set(struct dentry *dentry, const char *name,
-			const void *value, size_t size, int flags, int type)
-{
-	return aufs_setxattr(dentry, name, value, size, flags);
+	return au_setxattr(dentry, inode, name, value, size, flags);
 }
 
 static const struct xattr_handler au_xattr_handler = {
-	/* no prefix, no flags */
-	.list	= au_xattr_list,
+	.name	= "",
+	.prefix	= "",
 	.get	= au_xattr_get,
 	.set	= au_xattr_set
-	/* why no remove? */
 };
 
 static const struct xattr_handler *au_xattr_handlers[] = {
-	&au_xattr_handler
+#ifdef CONFIG_FS_POSIX_ACL
+	&posix_acl_access_xattr_handler,
+	&posix_acl_default_xattr_handler,
+#endif
+	&au_xattr_handler, /* must be last */
+	NULL
 };
 
 void au_xattr_init(struct super_block *sb)
 {
-	/* sb->s_xattr = au_xattr_handlers; */
+	sb->s_xattr = au_xattr_handlers;
 }
-#endif
diff --git a/fs/aufs/xino.c b/fs/aufs/xino.c
index 7f62beb75426..1d41d5752048 100644
--- a/fs/aufs/xino.c
+++ b/fs/aufs/xino.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2016 Junjiro R. Okajima
+ * Copyright (C) 2005-2017 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
@@ -338,7 +338,7 @@ int au_xino_trunc(struct super_block *sb, aufs_bindex_t bindex)
 		AuErr1("statfs err %d, ignored\n", err);
 
 out_st:
-	au_delayed_kfree(st);
+	kfree(st);
 out:
 	return err;
 }
@@ -373,7 +373,7 @@ static void xino_do_trunc(void *_args)
 	au_br_put(br);
 	si_write_unlock(sb);
 	au_nwt_done(&au_sbi(sb)->si_nowait);
-	au_delayed_kfree(args);
+	kfree(args);
 }
 
 static int xino_trunc_test(struct super_block *sb, struct au_branch *br)
@@ -427,7 +427,7 @@ static void xino_try_trunc(struct super_block *sb, struct au_branch *br)
 
 	pr_err("wkq %d\n", wkq_err);
 	au_br_put(br);
-	au_delayed_kfree(args);
+	kfree(args);
 
 out:
 	atomic_dec(&br->br_xino_running);
@@ -951,7 +951,7 @@ static int xib_restore(struct super_block *sb)
 				(sb, au_sbr(sb, bindex)->br_xino.xi_file, page);
 		else
 			AuDbg("b%d\n", bindex);
-	au_delayed_free_page((unsigned long)page);
+	free_page((unsigned long)page);
 
 out:
 	return err;
@@ -1029,7 +1029,7 @@ static void xino_clear_xib(struct super_block *sb)
 		fput(sbinfo->si_xib);
 	sbinfo->si_xib = NULL;
 	if (sbinfo->si_xib_buf)
-		au_delayed_free_page((unsigned long)sbinfo->si_xib_buf);
+		free_page((unsigned long)sbinfo->si_xib_buf);
 	sbinfo->si_xib_buf = NULL;
 }
 
@@ -1073,7 +1073,7 @@ static int au_xino_set_xib(struct super_block *sb, struct file *base)
 
 out_free:
 	if (sbinfo->si_xib_buf)
-		au_delayed_free_page((unsigned long)sbinfo->si_xib_buf);
+		free_page((unsigned long)sbinfo->si_xib_buf);
 	sbinfo->si_xib_buf = NULL;
 	if (err >= 0)
 		err = -EIO;
@@ -1166,7 +1166,7 @@ static int au_xino_set_br(struct super_block *sb, struct file *base)
 			fput(p->new);
 		else
 			break;
-	au_delayed_kfree(fpair);
+	kfree(fpair);
 out:
 	return err;
 }
@@ -1277,7 +1277,7 @@ struct file *au_xino_def(struct super_block *sb)
 			if (!IS_ERR(file))
 				au_xino_brid_set(sb, br->br_id);
 		}
-		au_delayed_free_page((unsigned long)page);
+		free_page((unsigned long)page);
 	} else {
 		file = au_xino_create(sb, AUFS_XINO_DEFPATH, /*silent*/0);
 		if (IS_ERR(file))
@@ -1316,3 +1316,100 @@ int au_xino_path(struct seq_file *seq, struct file *file)
 out:
 	return err;
 }
+
+/* ---------------------------------------------------------------------- */
+
+void au_xinondir_leave(struct super_block *sb, aufs_bindex_t bindex,
+		       ino_t h_ino, int idx)
+{
+	struct au_xino_file *xino;
+
+	AuDebugOn(!au_opt_test(au_mntflags(sb), XINO));
+	xino = &au_sbr(sb, bindex)->br_xino;
+	AuDebugOn(idx < 0 || xino->xi_nondir.total <= idx);
+
+	spin_lock(&xino->xi_nondir.spin);
+	AuDebugOn(xino->xi_nondir.array[idx] != h_ino);
+	xino->xi_nondir.array[idx] = 0;
+	spin_unlock(&xino->xi_nondir.spin);
+	wake_up_all(&xino->xi_nondir.wqh);
+}
+
+static int au_xinondir_find(struct au_xino_file *xino, ino_t h_ino)
+{
+	int found, total, i;
+
+	found = -1;
+	total = xino->xi_nondir.total;
+	for (i = 0; i < total; i++) {
+		if (xino->xi_nondir.array[i] != h_ino)
+			continue;
+		found = i;
+		break;
+	}
+
+	return found;
+}
+
+static int au_xinondir_expand(struct au_xino_file *xino)
+{
+	int err, sz;
+	ino_t *p;
+
+	BUILD_BUG_ON(KMALLOC_MAX_SIZE > INT_MAX);
+
+	err = -ENOMEM;
+	sz = xino->xi_nondir.total * sizeof(ino_t);
+	if (unlikely(sz > KMALLOC_MAX_SIZE / 2))
+		goto out;
+	p = au_kzrealloc(xino->xi_nondir.array, sz, sz << 1, GFP_ATOMIC,
+			 /*may_shrink*/0);
+	if (p) {
+		xino->xi_nondir.array = p;
+		xino->xi_nondir.total <<= 1;
+		AuDbg("xi_nondir.total %d\n", xino->xi_nondir.total);
+		err = 0;
+	}
+
+out:
+	return err;
+}
+
+int au_xinondir_enter(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
+		      int *idx)
+{
+	int err, found, empty;
+	struct au_xino_file *xino;
+
+	err = 0;
+	*idx = -1;
+	if (!au_opt_test(au_mntflags(sb), XINO))
+		goto out; /* no xino */
+
+	xino = &au_sbr(sb, bindex)->br_xino;
+
+again:
+	spin_lock(&xino->xi_nondir.spin);
+	found = au_xinondir_find(xino, h_ino);
+	if (found == -1) {
+		empty = au_xinondir_find(xino, /*h_ino*/0);
+		if (empty == -1) {
+			empty = xino->xi_nondir.total;
+			err = au_xinondir_expand(xino);
+			if (unlikely(err))
+				goto out_unlock;
+		}
+		xino->xi_nondir.array[empty] = h_ino;
+		*idx = empty;
+	} else {
+		spin_unlock(&xino->xi_nondir.spin);
+		wait_event(xino->xi_nondir.wqh,
+			   xino->xi_nondir.array[found] != h_ino);
+		goto again;
+	}
+
+out_unlock:
+	spin_unlock(&xino->xi_nondir.spin);
+out:
+	return err;
+}
diff --git a/fs/sync.c b/fs/sync.c
index 2a54c1f22035..c9b9d46ee888 100644
--- a/fs/sync.c
+++ b/fs/sync.c
@@ -27,7 +27,7 @@
  * wait == 1 case since in that case write_inode() functions do
  * sync_dirty_buffer() and thus effectively write one block at a time.
  */
-static int __sync_filesystem(struct super_block *sb, int wait)
+int __sync_filesystem(struct super_block *sb, int wait)
 {
 	if (wait)
 		sync_inodes_sb(sb);
@@ -38,6 +38,7 @@ static int __sync_filesystem(struct super_block *sb, int wait)
 		sb->s_op->sync_fs(sb, wait);
 	return __sync_blockdev(sb->s_bdev, wait);
 }
+EXPORT_SYMBOL_GPL(__sync_filesystem);
 
 /*
  * Write out and wait upon all dirty data associated with this
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 7c4f0ad39812..0f55c9d3be24 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2433,6 +2433,7 @@ static inline bool sb_is_blkdev_sb(struct super_block *sb)
 	return false;
 }
 #endif
+extern int __sync_filesystem(struct super_block *, int);
 extern int sync_filesystem(struct super_block *);
 extern const struct file_operations def_blk_fops;
 extern const struct file_operations def_chr_fops;
-- 
2.5.0

