[packages/kernel/LINUX_4_9] - up to 4.9.94

arekm arekm at pld-linux.org
Sun Apr 15 09:01:14 CEST 2018


commit ae9dfd79ace7e0a2a98099f2c18227fc16643bf3
Author: Arkadiusz Miśkiewicz <arekm at maven.pl>
Date:   Sun Apr 15 09:01:04 2018 +0200

    - up to 4.9.94

 kernel-aufs4.patch    | 4222 ++++++++++++++++++++++++++++++++++++-------------
 kernel-patches.config |    1 +
 kernel.spec           |    4 +-
 3 files changed, 3120 insertions(+), 1107 deletions(-)
---
diff --git a/kernel.spec b/kernel.spec
index 82fa32e7..c7ecbcc9 100644
--- a/kernel.spec
+++ b/kernel.spec
@@ -73,7 +73,7 @@
 
 %define		rel		1
 %define		basever		4.9
-%define		postver		.93
+%define		postver		.94
 
 # define this to '-%{basever}' for longterm branch
 %define		versuffix	-%{basever}
@@ -125,7 +125,7 @@ Source0:	https://www.kernel.org/pub/linux/kernel/v4.x/linux-%{basever}.tar.xz
 # Source0-md5:	0a68ef3615c64bd5ee54a3320e46667d
 %if "%{postver}" != ".0"
 Patch0:		https://www.kernel.org/pub/linux/kernel/v4.x/patch-%{version}.xz
-# Patch0-md5:	677b70806e24b607152f47b5d75bcd56
+# Patch0-md5:	866dbf79661ef17708f5722b9fab7623
 %endif
 Source1:	kernel.sysconfig
 
diff --git a/kernel-aufs4.patch b/kernel-aufs4.patch
index 16f65cba..c117b634 100644
--- a/kernel-aufs4.patch
+++ b/kernel-aufs4.patch
@@ -127,7 +127,7 @@ diff --git a/fs/inode.c b/fs/inode.c
 index 88110fd..9a9ba3a 100644
 --- a/fs/inode.c
 +++ b/fs/inode.c
-@@ -1642,7 +1642,7 @@ int generic_update_time(struct inode *inode, struct timespec *time, int flags)
+@@ -1642,7 +1642,7 @@ EXPORT_SYMBOL(generic_update_time);
   * This does the actual work of updating an inodes time or version.  Must have
   * had called mnt_want_write() before calling this.
   */
@@ -136,6 +136,23 @@ index 88110fd..9a9ba3a 100644
  {
  	int (*update_time)(struct inode *, struct timespec *, int);
  
+diff --git a/fs/namespace.c b/fs/namespace.c
+index e6c234b..db0b1ac 100644
+--- a/fs/namespace.c
++++ b/fs/namespace.c
+@@ -787,6 +787,12 @@ static inline int check_mnt(struct mount *mnt)
+ 	return mnt->mnt_ns == current->nsproxy->mnt_ns;
+ }
+ 
++/* for aufs, CONFIG_AUFS_BR_FUSE */
++int is_current_mnt_ns(struct vfsmount *mnt)
++{
++	return check_mnt(real_mount(mnt));
++}
++
+ /*
+  * vfsmount lock must be held for write
+  */
 diff --git a/fs/read_write.c b/fs/read_write.c
 index 190e0d36..4052813 100644
 --- a/fs/read_write.c
@@ -173,7 +190,7 @@ diff --git a/fs/splice.c b/fs/splice.c
 index 5a7750b..28160a7 100644
 --- a/fs/splice.c
 +++ b/fs/splice.c
-@@ -855,8 +855,8 @@ ssize_t generic_splice_sendpage(struct pipe_inode_info *pipe, struct file *out,
+@@ -855,8 +855,8 @@ EXPORT_SYMBOL(generic_splice_sendpage);
  /*
   * Attempt to initiate a splice from pipe to file.
   */
@@ -197,11 +214,24 @@ index 5a7750b..28160a7 100644
  {
  	ssize_t (*splice_read)(struct file *, loff_t *,
  			       struct pipe_inode_info *, size_t, unsigned int);
+diff --git a/fs/sync.c b/fs/sync.c
+index 2a54c1f..7a5fa3f 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);
 diff --git a/include/linux/file.h b/include/linux/file.h
 index 7444f5f..bdac0be 100644
 --- a/include/linux/file.h
 +++ b/include/linux/file.h
-@@ -19,6 +19,7 @@
+@@ -19,6 +19,7 @@ struct dentry;
  struct path;
  extern struct file *alloc_file(struct path *, fmode_t mode,
  	const struct file_operations *fop);
@@ -210,10 +240,10 @@ index 7444f5f..bdac0be 100644
  static inline void fput_light(struct file *file, int fput_needed)
  {
 diff --git a/include/linux/fs.h b/include/linux/fs.h
-index dc0478c..27c05e7 100644
+index dc0478c..a02be40d 100644
 --- a/include/linux/fs.h
 +++ b/include/linux/fs.h
-@@ -1291,6 +1291,7 @@ struct fasync_struct {
+@@ -1291,6 +1291,7 @@ extern void fasync_free(struct fasync_struct *);
  /* can be called from interrupts */
  extern void kill_fasync(struct fasync_struct **, int, int);
  
@@ -242,7 +272,7 @@ index dc0478c..27c05e7 100644
  extern ssize_t __vfs_read(struct file *, char __user *, size_t, loff_t *);
  extern ssize_t __vfs_write(struct file *, const char __user *, size_t, loff_t *);
  extern ssize_t vfs_read(struct file *, char __user *, size_t, loff_t *);
-@@ -2140,6 +2148,7 @@ extern int iterate_mounts(int (*)(struct vfsmount *, void *), void *,
+@@ -2140,6 +2148,7 @@ extern int current_umask(void);
  extern void ihold(struct inode * inode);
  extern void iput(struct inode *);
  extern int generic_update_time(struct inode *, struct timespec *, int);
@@ -250,11 +280,38 @@ index dc0478c..27c05e7 100644
  
  /* /sys/fs */
  extern struct kobject *fs_kobj;
+@@ -2419,6 +2428,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;
+diff --git a/include/linux/mnt_namespace.h b/include/linux/mnt_namespace.h
+index 12b2ab5..8b810d1 100644
+--- a/include/linux/mnt_namespace.h
++++ b/include/linux/mnt_namespace.h
+@@ -5,11 +5,14 @@
+ struct mnt_namespace;
+ struct fs_struct;
+ struct user_namespace;
++struct vfsmount;
+ 
+ extern struct mnt_namespace *copy_mnt_ns(unsigned long, struct mnt_namespace *,
+ 		struct user_namespace *, struct fs_struct *);
+ extern void put_mnt_ns(struct mnt_namespace *ns);
+ 
++extern int is_current_mnt_ns(struct vfsmount *mnt);
++
+ extern const struct file_operations proc_mounts_operations;
+ extern const struct file_operations proc_mountinfo_operations;
+ extern const struct file_operations proc_mountstats_operations;
 diff --git a/include/linux/splice.h b/include/linux/splice.h
 index 00a2116..1f0a4a2 100644
 --- a/include/linux/splice.h
 +++ b/include/linux/splice.h
-@@ -86,4 +86,10 @@ extern ssize_t splice_direct_to_actor(struct file *, struct splice_desc *,
+@@ -86,4 +86,10 @@ extern void spd_release_page(struct splice_pipe_desc *, unsigned int);
  
  extern const struct pipe_buf_operations page_cache_pipe_buf_ops;
  extern const struct pipe_buf_operations default_pipe_buf_ops;
@@ -300,7 +357,7 @@ diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
 index 35b92d8..5b981db 100644
 --- a/fs/proc/task_mmu.c
 +++ b/fs/proc/task_mmu.c
-@@ -291,7 +291,10 @@ static int is_stack(struct proc_maps_private *priv,
+@@ -291,7 +291,10 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma, int is_pid)
  	const char *name = NULL;
  
  	if (file) {
@@ -492,7 +549,7 @@ index 1af87c1..95b0ff4 100644
  	unlink_anon_vmas(new);
   out_free_mpol:
  	mpol_put(vma_policy(new));
-@@ -2703,7 +2703,7 @@ int vm_munmap(unsigned long start, size_t len)
+@@ -2703,7 +2703,7 @@ SYSCALL_DEFINE5(remap_file_pages, unsigned long, start, unsigned long, size,
  	struct vm_area_struct *vma;
  	unsigned long populate = 0;
  	unsigned long ret = -EINVAL;
@@ -501,7 +558,7 @@ index 1af87c1..95b0ff4 100644
  
  	pr_warn_once("%s (%d) uses deprecated remap_file_pages() syscall. See Documentation/vm/remap_file_pages.txt.\n",
  		     current->comm, current->pid);
-@@ -2778,10 +2778,27 @@ int vm_munmap(unsigned long start, size_t len)
+@@ -2778,10 +2778,27 @@ SYSCALL_DEFINE5(remap_file_pages, unsigned long, start, unsigned long, size,
  		}
  	}
  
@@ -585,18 +642,18 @@ index 8b8faaf..5d26ed94 100644
  
 diff --git a/mm/prfile.c b/mm/prfile.c
 new file mode 100644
-index 0000000..b323b8a
+index 0000000..86e01bd
 --- /dev/null
 +++ b/mm/prfile.c
-@@ -0,0 +1,86 @@
+@@ -0,0 +1,85 @@
 +/*
-+ * Mainly for aufs which mmap(2) diffrent file and wants to print different path
-+ * in /proc/PID/maps.
++ * Mainly for aufs which mmap(2) different file and wants to print different
++ * path in /proc/PID/maps.
 + * Call these functions via macros defined in linux/mm.h.
 + *
 + * See Documentation/filesystems/aufs/design/06mmap.txt
 + *
-+ * Copyright (c) 2014 Junjro R. Okajima
++ * Copyright (c) 2014-2018 Junjro R. Okajima
 + * Copyright (c) 2014 Ian Campbell
 + */
 +
@@ -610,8 +667,7 @@ index 0000000..b323b8a
 +{
 +#ifdef PRFILE_TRACE
 +	if (pr)
-+		pr_info("%s:%d: %s, %s\n", func, line, func2,
-+			f ? (char *)f->f_path.dentry->d_name.name : "(null)");
++		pr_info("%s:%d: %s, %pD2\n", func, line, func2, f);
 +#endif
 +}
 +
@@ -758,7 +814,7 @@ index ad17e05..ae9f267 100644
  void __init files_init(void)
  { 
 diff --git a/fs/inode.c b/fs/inode.c
-index 9a9ba3a..a3a18d8 100644
+index 9a9ba3a..a3a18d83 100644
 --- a/fs/inode.c
 +++ b/fs/inode.c
 @@ -1651,6 +1651,7 @@ int update_time(struct inode *inode, struct timespec *time, int flags)
@@ -770,7 +826,7 @@ index 9a9ba3a..a3a18d8 100644
  /**
   *	touch_atime	-	update the access time
 diff --git a/fs/namespace.c b/fs/namespace.c
-index e6c234b..8d13f7b 100644
+index db0b1ac..2d1e8ff 100644
 --- a/fs/namespace.c
 +++ b/fs/namespace.c
 @@ -466,6 +466,7 @@ void __mnt_drop_write(struct vfsmount *mnt)
@@ -781,7 +837,15 @@ index e6c234b..8d13f7b 100644
  
  /**
   * mnt_drop_write - give up write access to a mount
-@@ -1823,6 +1824,7 @@ int iterate_mounts(int (*f)(struct vfsmount *, void *), void *arg,
+@@ -792,6 +793,7 @@ int is_current_mnt_ns(struct vfsmount *mnt)
+ {
+ 	return check_mnt(real_mount(mnt));
+ }
++EXPORT_SYMBOL_GPL(is_current_mnt_ns);
+ 
+ /*
+  * vfsmount lock must be held for write
+@@ -1829,6 +1831,7 @@ int iterate_mounts(int (*f)(struct vfsmount *, void *), void *arg,
  	}
  	return 0;
  }
@@ -921,11 +985,23 @@ index 28160a7..98c1902 100644
  
  /**
   * splice_direct_to_actor - splices data directly between two non-pipes
+diff --git a/fs/sync.c b/fs/sync.c
+index 7a5fa3f..c9b9d46 100644
+--- a/fs/sync.c
++++ b/fs/sync.c
+@@ -38,6 +38,7 @@ 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/fs/xattr.c b/fs/xattr.c
 index 2d13b4e..41c2bcd 100644
 --- a/fs/xattr.c
 +++ b/fs/xattr.c
-@@ -296,6 +296,7 @@ int __vfs_setxattr_noperm(struct dentry *dentry, const char *name,
+@@ -296,6 +296,7 @@ vfs_getxattr_alloc(struct dentry *dentry, const char *name, char **xattr_value,
  	*xattr_value = value;
  	return error;
  }
@@ -1067,7 +1143,7 @@ index f825304..8dd441d 100644
  {
 diff -urN /usr/share/empty/Documentation/ABI/testing/debugfs-aufs linux/Documentation/ABI/testing/debugfs-aufs
 --- /usr/share/empty/Documentation/ABI/testing/debugfs-aufs	1970-01-01 01:00:00.000000000 +0100
-+++ linux/Documentation/ABI/testing/debugfs-aufs	2016-10-09 16:55:36.476034536 +0200
++++ linux/Documentation/ABI/testing/debugfs-aufs	2017-07-29 12:14:25.893041746 +0200
 @@ -0,0 +1,50 @@
 +What:		/debug/aufs/si_<id>/
 +Date:		March 2009
@@ -1121,7 +1197,7 @@ diff -urN /usr/share/empty/Documentation/ABI/testing/debugfs-aufs linux/Document
 +		will be empty. About XINO files, see the aufs manual.
 diff -urN /usr/share/empty/Documentation/ABI/testing/sysfs-aufs linux/Documentation/ABI/testing/sysfs-aufs
 --- /usr/share/empty/Documentation/ABI/testing/sysfs-aufs	1970-01-01 01:00:00.000000000 +0100
-+++ linux/Documentation/ABI/testing/sysfs-aufs	2016-10-09 16:55:36.476034536 +0200
++++ linux/Documentation/ABI/testing/sysfs-aufs	2017-07-29 12:14:25.893041746 +0200
 @@ -0,0 +1,31 @@
 +What:		/sys/fs/aufs/si_<id>/
 +Date:		March 2009
@@ -1156,10 +1232,10 @@ diff -urN /usr/share/empty/Documentation/ABI/testing/sysfs-aufs linux/Documentat
 +		will be empty. About XINO files, see the aufs manual.
 diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/01intro.txt linux/Documentation/filesystems/aufs/design/01intro.txt
 --- /usr/share/empty/Documentation/filesystems/aufs/design/01intro.txt	1970-01-01 01:00:00.000000000 +0100
-+++ linux/Documentation/filesystems/aufs/design/01intro.txt	2016-10-09 16:55:36.479367956 +0200
-@@ -0,0 +1,170 @@
++++ linux/Documentation/filesystems/aufs/design/01intro.txt	2018-04-15 08:49:13.394483860 +0200
+@@ -0,0 +1,171 @@
 +
-+# Copyright (C) 2005-2016 Junjiro R. Okajima
++# Copyright (C) 2005-2018 Junjiro R. Okajima
 +# 
 +# This program is free software; you can redistribute it and/or modify
 +# it under the terms of the GNU General Public License as published by
@@ -1177,12 +1253,13 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/01intro.txt lin
 +Introduction
 +----------------------------------------
 +
-+aufs [ei ju: ef es] | [a u f s]
++aufs [ei ju: ef es] | /ey-yoo-ef-es/ | [a u f s]
 +1. abbrev. for "advanced multi-layered unification filesystem".
 +2. abbrev. for "another unionfs".
 +3. abbrev. for "auf das" in German which means "on the" in English.
 +   Ex. "Butter aufs Brot"(G) means "butter onto bread"(E).
 +   But "Filesystem aufs Filesystem" is hard to understand.
++4. abbrev. for "African Urban Fashion Show".
 +
 +AUFS is a filesystem with features:
 +- multi layered stackable unification filesystem, the member directory
@@ -1330,10 +1407,10 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/01intro.txt lin
 +about it. But currently I have implemented it in kernel space.
 diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/02struct.txt linux/Documentation/filesystems/aufs/design/02struct.txt
 --- /usr/share/empty/Documentation/filesystems/aufs/design/02struct.txt	1970-01-01 01:00:00.000000000 +0100
-+++ linux/Documentation/filesystems/aufs/design/02struct.txt	2016-10-09 16:55:36.479367956 +0200
++++ linux/Documentation/filesystems/aufs/design/02struct.txt	2018-04-15 08:49:13.394483860 +0200
 @@ -0,0 +1,258 @@
 +
-+# Copyright (C) 2005-2016 Junjiro R. Okajima
++# Copyright (C) 2005-2018 Junjiro R. Okajima
 +# 
 +# This program is free software; you can redistribute it and/or modify
 +# it under the terms of the GNU General Public License as published by
@@ -1592,10 +1669,10 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/02struct.txt li
 +For this purpose, use "aumvdown" command in aufs-util.git.
 diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/03atomic_open.txt linux/Documentation/filesystems/aufs/design/03atomic_open.txt
 --- /usr/share/empty/Documentation/filesystems/aufs/design/03atomic_open.txt	1970-01-01 01:00:00.000000000 +0100
-+++ linux/Documentation/filesystems/aufs/design/03atomic_open.txt	2016-10-09 16:55:36.479367956 +0200
++++ linux/Documentation/filesystems/aufs/design/03atomic_open.txt	2018-04-15 08:49:13.394483860 +0200
 @@ -0,0 +1,85 @@
 +
-+# Copyright (C) 2015-2016 Junjiro R. Okajima
++# Copyright (C) 2015-2018 Junjiro R. Okajima
 +# 
 +# This program is free software; you can redistribute it and/or modify
 +# it under the terms of the GNU General Public License as published by
@@ -1681,10 +1758,10 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/03atomic_open.t
 +       be implemented in aufs, but not all I am afraid.
 diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/03lookup.txt linux/Documentation/filesystems/aufs/design/03lookup.txt
 --- /usr/share/empty/Documentation/filesystems/aufs/design/03lookup.txt	1970-01-01 01:00:00.000000000 +0100
-+++ linux/Documentation/filesystems/aufs/design/03lookup.txt	2016-10-09 16:55:36.479367956 +0200
++++ linux/Documentation/filesystems/aufs/design/03lookup.txt	2018-04-15 08:49:13.394483860 +0200
 @@ -0,0 +1,113 @@
 +
-+# Copyright (C) 2005-2016 Junjiro R. Okajima
++# Copyright (C) 2005-2018 Junjiro R. Okajima
 +# 
 +# This program is free software; you can redistribute it and/or modify
 +# it under the terms of the GNU General Public License as published by
@@ -1798,10 +1875,10 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/03lookup.txt li
 +   by over-mounting something (or another method).
 diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/04branch.txt linux/Documentation/filesystems/aufs/design/04branch.txt
 --- /usr/share/empty/Documentation/filesystems/aufs/design/04branch.txt	1970-01-01 01:00:00.000000000 +0100
-+++ linux/Documentation/filesystems/aufs/design/04branch.txt	2016-10-09 16:55:36.482701377 +0200
++++ linux/Documentation/filesystems/aufs/design/04branch.txt	2018-04-15 08:49:13.394483860 +0200
 @@ -0,0 +1,74 @@
 +
-+# Copyright (C) 2005-2016 Junjiro R. Okajima
++# Copyright (C) 2005-2018 Junjiro R. Okajima
 +# 
 +# This program is free software; you can redistribute it and/or modify
 +# it under the terms of the GNU General Public License as published by
@@ -1876,10 +1953,10 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/04branch.txt li
 +    same named entry on the upper branch.
 diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/05wbr_policy.txt linux/Documentation/filesystems/aufs/design/05wbr_policy.txt
 --- /usr/share/empty/Documentation/filesystems/aufs/design/05wbr_policy.txt	1970-01-01 01:00:00.000000000 +0100
-+++ linux/Documentation/filesystems/aufs/design/05wbr_policy.txt	2016-10-09 16:55:36.482701377 +0200
++++ linux/Documentation/filesystems/aufs/design/05wbr_policy.txt	2018-04-15 08:49:13.394483860 +0200
 @@ -0,0 +1,64 @@
 +
-+# Copyright (C) 2005-2016 Junjiro R. Okajima
++# Copyright (C) 2005-2018 Junjiro R. Okajima
 +# 
 +# This program is free software; you can redistribute it and/or modify
 +# it under the terms of the GNU General Public License as published by
@@ -1942,12 +2019,153 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/05wbr_policy.tx
 +  where the source and the target exists and selects the higher
 +  one. If the selected branch is readonly, then aufs follows the
 +  copyup policy.
+diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/06dirren.dot linux/Documentation/filesystems/aufs/design/06dirren.dot
+--- /usr/share/empty/Documentation/filesystems/aufs/design/06dirren.dot	1970-01-01 01:00:00.000000000 +0100
++++ linux/Documentation/filesystems/aufs/design/06dirren.dot	2018-04-15 08:49:13.394483860 +0200
+@@ -0,0 +1,31 @@
++
++// to view this graph, run dot(1) command in GRAPHVIZ.
++
++digraph G {
++node [shape=box];
++whinfo [label="detailed info file\n(lower_brid_root-hinum, h_inum, namelen, old name)"];
++
++node [shape=oval];
++
++aufs_rename -> whinfo [label="store/remove"];
++
++node [shape=oval];
++inode_list [label="h_inum list in branch\ncache"];
++
++node [shape=box];
++whinode [label="h_inum list file"];
++
++node [shape=oval];
++brmgmt [label="br_add/del/mod/umount"];
++
++brmgmt -> inode_list [label="create/remove"];
++brmgmt -> whinode [label="load/store"];
++
++inode_list -> whinode [style=dashed,dir=both];
++
++aufs_rename -> inode_list [label="add/del"];
++
++aufs_lookup -> inode_list [label="search"];
++
++aufs_lookup -> whinfo [label="load/remove"];
++}
+diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/06dirren.txt linux/Documentation/filesystems/aufs/design/06dirren.txt
+--- /usr/share/empty/Documentation/filesystems/aufs/design/06dirren.txt	1970-01-01 01:00:00.000000000 +0100
++++ linux/Documentation/filesystems/aufs/design/06dirren.txt	2018-04-15 08:49:13.394483860 +0200
+@@ -0,0 +1,102 @@
++
++# Copyright (C) 2017-2018 Junjiro R. Okajima
++#
++# This program is free software; you can redistribute it and/or modify
++# it under the terms of the GNU General Public License as published by
++# the Free Software Foundation; either version 2 of the License, or
++# (at your option) any later version.
++#
++# This program is distributed in the hope that it will be useful,
++# but WITHOUT ANY WARRANTY; without even the implied warranty of
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++# GNU General Public License for more details.
++#
++# You should have received a copy of the GNU General Public License
++# along with this program.  If not, see <http://www.gnu.org/licenses/>.
++
++Special handling for renaming a directory (DIRREN)
++----------------------------------------------------------------------
++First, let's assume we have a simple usecase.
++
++- /u = /rw + /ro
++- /rw/dirA exists
++- /ro/dirA and /ro/dirA/file exist too
++- there is no dirB on both branches
++- a user issues rename("dirA", "dirB")
++
++Now, what should aufs behave against this rename(2)?
++There are a few possible cases.
++
++A. returns EROFS.
++   since dirA exists on a readonly branch which cannot be renamed.
++B. returns EXDEV.
++   it is possible to copy-up dirA (only the dir itself), but the child
++   entries ("file" in this case) should not be. it must be a bad
++   approach to copy-up recursively.
++C. returns a success.
++   even the branch /ro is readonly, aufs tries renaming it. Obviously it
++   is a violation of aufs' policy.
++D. construct an extra information which indicates that /ro/dirA should
++   be handled as the name of dirB.
++   overlayfs has a similar feature called REDIRECT.
++
++Until now, aufs implements the case B only which returns EXDEV, and
++expects the userspace application behaves like mv(1) which tries
++issueing rename(2) recursively.
++
++A new aufs feature called DIRREN is introduced which implements the case
++D. There are several "extra information" added.
++
++1. detailed info per renamed directory
++   path: /rw/dirB/$AUFS_WH_DR_INFO_PFX.<lower branch-id>
++2. the inode-number list of directories on a branch
++   path: /rw/dirB/$AUFS_WH_DR_BRHINO
++
++The filename of "detailed info per directory" represents the lower
++branch, and its format is
++- a type of the branch id
++  one of these.
++  + uuid (not implemented yet)
++  + fsid
++  + dev
++- the inode-number of the branch root dir
++
++And it contains these info in a single regular file.
++- magic number
++- branch's inode-number of the logically renamed dir
++- the name of the before-renamed dir
++
++The "detailed info per directory" file is created in aufs rename(2), and
++loaded in any lookup.
++The info is considered in lookup for the matching case only. Here
++"matching" means that the root of branch (in the info filename) is same
++to the current looking-up branch. After looking-up the before-renamed
++name, the inode-number is compared. And the matched dentry is used.
++
++The "inode-number list of directories" is a regular file which contains
++simply the inode-numbers on the branch. The file is created or updated
++in removing the branch, and loaded in adding the branch. Its lifetime is
++equal to the branch.
++The list is refered in lookup, and when the current target inode is
++found in the list, the aufs tries loading the "detailed info per
++directory" and get the changed and valid name of the dir.
++
++Theoretically these "extra informaiton" may be able to be put into XATTR
++in the dir inode. But aufs doesn't choose this way because
++1. XATTR may not be supported by the branch (or its configuration)
++2. XATTR may have its size limit.
++3. XATTR may be less easy to convert than a regular file, when the
++   format of the info is changed in the future.
++At the same time, I agree that the regular file approach is much slower
++than XATTR approach. So, in the future, aufs may take the XATTR or other
++better approach.
++
++This DIRREN feature is enabled by aufs configuration, and is activated
++by a new mount option.
++
++For the more complicated case, there is a work with UDBA option, which
++is to dected the direct access to the branches (by-passing aufs) and to
++maintain the cashes in aufs. Since a single cached aufs dentry may
++contains two names, before- and after-rename, the name comparision in
++UDBA handler may not work correctly. In this case, the behaviour will be
++equivalen to udba=reval case.
 diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/06fhsm.txt linux/Documentation/filesystems/aufs/design/06fhsm.txt
 --- /usr/share/empty/Documentation/filesystems/aufs/design/06fhsm.txt	1970-01-01 01:00:00.000000000 +0100
-+++ linux/Documentation/filesystems/aufs/design/06fhsm.txt	2016-10-09 16:55:36.482701377 +0200
++++ linux/Documentation/filesystems/aufs/design/06fhsm.txt	2018-04-15 08:49:13.394483860 +0200
 @@ -0,0 +1,120 @@
 +
-+# Copyright (C) 2011-2016 Junjiro R. Okajima
++# Copyright (C) 2011-2018 Junjiro R. Okajima
 +# 
 +# This program is free software; you can redistribute it and/or modify
 +# it under the terms of the GNU General Public License as published by
@@ -2068,10 +2286,10 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/06fhsm.txt linu
 +should restore the original file state after an error happens.
 diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/06mmap.txt linux/Documentation/filesystems/aufs/design/06mmap.txt
 --- /usr/share/empty/Documentation/filesystems/aufs/design/06mmap.txt	1970-01-01 01:00:00.000000000 +0100
-+++ linux/Documentation/filesystems/aufs/design/06mmap.txt	2016-10-09 16:55:36.482701377 +0200
++++ linux/Documentation/filesystems/aufs/design/06mmap.txt	2018-04-15 08:49:13.394483860 +0200
 @@ -0,0 +1,72 @@
 +
-+# Copyright (C) 2005-2016 Junjiro R. Okajima
++# Copyright (C) 2005-2018 Junjiro R. Okajima
 +# 
 +# This program is free software; you can redistribute it and/or modify
 +# it under the terms of the GNU General Public License as published by
@@ -2144,10 +2362,10 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/06mmap.txt linu
 +I have to give up this "looks-smater" approach.
 diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/06xattr.txt linux/Documentation/filesystems/aufs/design/06xattr.txt
 --- /usr/share/empty/Documentation/filesystems/aufs/design/06xattr.txt	1970-01-01 01:00:00.000000000 +0100
-+++ linux/Documentation/filesystems/aufs/design/06xattr.txt	2016-10-09 16:55:36.482701377 +0200
++++ linux/Documentation/filesystems/aufs/design/06xattr.txt	2018-04-15 08:49:13.394483860 +0200
 @@ -0,0 +1,96 @@
 +
-+# Copyright (C) 2014-2016 Junjiro R. Okajima
++# Copyright (C) 2014-2018 Junjiro R. Okajima
 +#
 +# This program is free software; you can redistribute it and/or modify
 +# it under the terms of the GNU General Public License as published by
@@ -2244,10 +2462,10 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/06xattr.txt lin
 +now, aufs implements the branch attributes to ignore the error.
 diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/07export.txt linux/Documentation/filesystems/aufs/design/07export.txt
 --- /usr/share/empty/Documentation/filesystems/aufs/design/07export.txt	1970-01-01 01:00:00.000000000 +0100
-+++ linux/Documentation/filesystems/aufs/design/07export.txt	2016-10-09 16:55:36.482701377 +0200
++++ linux/Documentation/filesystems/aufs/design/07export.txt	2018-04-15 08:49:13.394483860 +0200
 @@ -0,0 +1,58 @@
 +
-+# Copyright (C) 2005-2016 Junjiro R. Okajima
++# Copyright (C) 2005-2018 Junjiro R. Okajima
 +# 
 +# This program is free software; you can redistribute it and/or modify
 +# it under the terms of the GNU General Public License as published by
@@ -2306,10 +2524,10 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/07export.txt li
 +  lookup_one_len(), vfs_getattr(), encode_fh() and others.
 diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/08shwh.txt linux/Documentation/filesystems/aufs/design/08shwh.txt
 --- /usr/share/empty/Documentation/filesystems/aufs/design/08shwh.txt	1970-01-01 01:00:00.000000000 +0100
-+++ linux/Documentation/filesystems/aufs/design/08shwh.txt	2016-10-09 16:55:36.482701377 +0200
++++ linux/Documentation/filesystems/aufs/design/08shwh.txt	2018-04-15 08:49:13.394483860 +0200
 @@ -0,0 +1,52 @@
 +
-+# Copyright (C) 2005-2016 Junjiro R. Okajima
++# Copyright (C) 2005-2018 Junjiro R. Okajima
 +# 
 +# This program is free software; you can redistribute it and/or modify
 +# it under the terms of the GNU General Public License as published by
@@ -2362,10 +2580,10 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/08shwh.txt linu
 +initramfs will use it to replace the old one at the next boot.
 diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/10dynop.txt linux/Documentation/filesystems/aufs/design/10dynop.txt
 --- /usr/share/empty/Documentation/filesystems/aufs/design/10dynop.txt	1970-01-01 01:00:00.000000000 +0100
-+++ linux/Documentation/filesystems/aufs/design/10dynop.txt	2016-10-09 16:55:36.482701377 +0200
++++ linux/Documentation/filesystems/aufs/design/10dynop.txt	2018-04-15 08:49:13.394483860 +0200
 @@ -0,0 +1,47 @@
 +
-+# Copyright (C) 2010-2016 Junjiro R. Okajima
++# Copyright (C) 2010-2018 Junjiro R. Okajima
 +#
 +# This program is free software; you can redistribute it and/or modify
 +# it under the terms of the GNU General Public License as published by
@@ -2413,7 +2631,7 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/10dynop.txt lin
 +regular files only.
 diff -urN /usr/share/empty/Documentation/filesystems/aufs/README linux/Documentation/filesystems/aufs/README
 --- /usr/share/empty/Documentation/filesystems/aufs/README	1970-01-01 01:00:00.000000000 +0100
-+++ linux/Documentation/filesystems/aufs/README	2016-12-17 12:28:17.595211562 +0100
++++ linux/Documentation/filesystems/aufs/README	2017-07-29 12:14:25.893041746 +0200
 @@ -0,0 +1,393 @@
 +
 +Aufs4 -- advanced multi layered unification filesystem version 4.x
@@ -2810,10 +3028,10 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/README linux/Documenta
 +# End: ;
 diff -urN /usr/share/empty/fs/aufs/aufs.h linux/fs/aufs/aufs.h
 --- /usr/share/empty/fs/aufs/aufs.h	1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/aufs.h	2016-10-09 16:55:36.486034798 +0200
-@@ -0,0 +1,59 @@
++++ linux/fs/aufs/aufs.h	2018-04-15 08:49:13.394483860 +0200
+@@ -0,0 +1,60 @@
 +/*
-+ * Copyright (C) 2005-2016 Junjiro R. Okajima
++ * Copyright (C) 2005-2018 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
@@ -2854,15 +3072,16 @@ diff -urN /usr/share/empty/fs/aufs/aufs.h linux/fs/aufs/aufs.h
 +#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"
@@ -2873,10 +3092,10 @@ diff -urN /usr/share/empty/fs/aufs/aufs.h linux/fs/aufs/aufs.h
 +#endif /* __AUFS_H__ */
 diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
 --- /usr/share/empty/fs/aufs/branch.c	1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/branch.c	2016-10-09 16:55:38.886097714 +0200
-@@ -0,0 +1,1412 @@
++++ linux/fs/aufs/branch.c	2018-04-15 08:49:13.394483860 +0200
+@@ -0,0 +1,1432 @@
 +/*
-+ * Copyright (C) 2005-2016 Junjiro R. Okajima
++ * Copyright (C) 2005-2018 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
@@ -2910,10 +3129,14 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
 +	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);
@@ -2928,7 +3151,7 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
 +
 +	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;
@@ -2942,9 +3165,8 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
 +	lockdep_off();
 +	path_put(&br->br_path);
 +	lockdep_on();
-+	if (wbr)
-+		au_delayed_kfree(wbr);
-+	au_delayed_kfree(br);
++	kfree(wbr);
++	kfree(br);
 +}
 +
 +/*
@@ -3008,14 +3230,19 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
 +	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 */
@@ -3031,23 +3258,26 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
 +			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);
 +}
@@ -3213,7 +3443,7 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
 +	br->br_perm = old_perm;
 +
 +	if (!err && wbr && !au_br_writable(new_perm)) {
-+		au_delayed_kfree(wbr);
++		kfree(wbr);
 +		br->br_wbr = NULL;
 +	}
 +
@@ -3259,7 +3489,8 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
 +	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);
@@ -3268,6 +3499,11 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
 +	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))
@@ -3434,14 +3670,15 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
 +{
 +	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)) {
@@ -3451,7 +3688,7 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
 +			AuDebugOn(n > max);
 +		}
 +	}
-+	spin_unlock(&files->spin);
++	hlist_bl_unlock(files);
 +
 +	return n;
 +}
@@ -3854,6 +4091,9 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
 +	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);
@@ -4239,7 +4479,7 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
 +		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;
 +			}
 +		}
@@ -4251,7 +4491,7 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
 +		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))
@@ -4263,8 +4503,7 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
 +	goto out; /* success */
 +
 +out_bf:
-+	if (bf)
-+		au_delayed_kfree(bf);
++	kfree(bf);
 +out:
 +	AuTraceErr(err);
 +	return err;
@@ -4289,10 +4528,10 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
 +}
 diff -urN /usr/share/empty/fs/aufs/branch.h linux/fs/aufs/branch.h
 --- /usr/share/empty/fs/aufs/branch.h	1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/branch.h	2016-10-09 16:55:36.486034798 +0200
-@@ -0,0 +1,309 @@
++++ linux/fs/aufs/branch.h	2018-04-15 08:49:13.394483860 +0200
+@@ -0,0 +1,324 @@
 +/*
-+ * Copyright (C) 2005-2016 Junjiro R. Okajima
++ * Copyright (C) 2005-2018 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
@@ -4318,6 +4557,7 @@ diff -urN /usr/share/empty/fs/aufs/branch.h linux/fs/aufs/branch.h
 +#ifdef __KERNEL__
 +
 +#include <linux/mount.h>
++#include "dirren.h"
 +#include "dynop.h"
 +#include "rwsem.h"
 +#include "super.h"
@@ -4327,7 +4567,14 @@ diff -urN /usr/share/empty/fs/aufs/branch.h linux/fs/aufs/branch.h
 +/* 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 */
 +
@@ -4408,6 +4655,8 @@ diff -urN /usr/share/empty/fs/aufs/branch.h linux/fs/aufs/branch.h
 +	/* entries under sysfs per mount-point */
 +	struct au_brsysfs	br_sysfs[AuBrSysfs_Last];
 +#endif
++
++	struct au_dr_br		br_dirren;
 +};
 +
 +/* ---------------------------------------------------------------------- */
@@ -4526,6 +4775,11 @@ diff -urN /usr/share/empty/fs/aufs/branch.h linux/fs/aufs/branch.h
 +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 */
@@ -4602,8 +4856,8 @@ diff -urN /usr/share/empty/fs/aufs/branch.h linux/fs/aufs/branch.h
 +#endif /* __AUFS_BRANCH_H__ */
 diff -urN /usr/share/empty/fs/aufs/conf.mk linux/fs/aufs/conf.mk
 --- /usr/share/empty/fs/aufs/conf.mk	1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/conf.mk	2016-10-09 16:55:36.486034798 +0200
-@@ -0,0 +1,38 @@
++++ linux/fs/aufs/conf.mk	2018-04-15 08:49:13.394483860 +0200
+@@ -0,0 +1,39 @@
 +
 +AuConfStr = CONFIG_AUFS_FS=${CONFIG_AUFS_FS}
 +
@@ -4620,6 +4874,7 @@ diff -urN /usr/share/empty/fs/aufs/conf.mk linux/fs/aufs/conf.mk
 +	XATTR \
 +	FHSM \
 +	RDU \
++	DIRREN \
 +	SHWH \
 +	BR_RAMFS \
 +	BR_FUSE POLL \
@@ -4644,10 +4899,10 @@ diff -urN /usr/share/empty/fs/aufs/conf.mk linux/fs/aufs/conf.mk
 +-include ${srctree}/${src}/conf_priv.mk
 diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c
 --- /usr/share/empty/fs/aufs/cpup.c	1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/cpup.c	2016-12-17 12:28:17.595211562 +0100
-@@ -0,0 +1,1394 @@
++++ linux/fs/aufs/cpup.c	2018-04-15 08:49:13.397817296 +0200
+@@ -0,0 +1,1414 @@
 +/*
-+ * Copyright (C) 2005-2016 Junjiro R. Okajima
++ * Copyright (C) 2005-2018 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
@@ -5005,9 +5260,9 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c
 +	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;
@@ -5041,7 +5296,7 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c
 +			.label = &&out_src
 +		}
 +	};
-+	struct super_block *sb;
++	struct super_block *sb, *h_src_sb;
 +	struct inode *h_src_inode;
 +	struct task_struct *tsk = current;
 +
@@ -5059,16 +5314,35 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c
 +
 +	/* try stopping to update while we copyup */
 +	h_src_inode = d_inode(file[SRC].dentry);
-+	if (!au_test_nfs(h_src_inode->i_sb))
++	h_src_sb = h_src_inode->i_sb;
++	if (!au_test_nfs(h_src_sb))
 +		IMustLock(h_src_inode);
-+	err = au_copy_file(file[DST].file, file[SRC].file, cpg->len);
++
++	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)
 +		__fput_sync(file[DST].file);
 +	else {
-+		WARN(1, "%pD\nPlease report this warning to aufs-users ML",
-+		     file[DST].file);
++		/* it happend actually */
 +		fput(file[DST].file);
 +		/*
 +		 * too bad.
@@ -5102,7 +5376,7 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c
 +		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);
@@ -5111,20 +5385,21 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c
 +		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;
 +		if (!au_test_nfs(h_src_inode->i_sb)) {
 +			err = au_cp_regular(cpg);
-+			inode_unlock(h_src_inode);
++			inode_unlock_shared(h_src_inode);
 +		} else {
-+			inode_unlock(h_src_inode);
++			inode_unlock_shared(h_src_inode);
 +			err = au_cp_regular(cpg);
 +		}
 +		rerr = au_pin_hdir_relock(cpg->pin);
@@ -5175,7 +5450,7 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c
 +		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;
@@ -5549,7 +5824,7 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c
 +	}
 +out_parent:
 +	dput(dst_parent);
-+	au_delayed_kfree(a);
++	kfree(a);
 +out:
 +	return err;
 +}
@@ -6042,10 +6317,10 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c
 +}
 diff -urN /usr/share/empty/fs/aufs/cpup.h linux/fs/aufs/cpup.h
 --- /usr/share/empty/fs/aufs/cpup.h	1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/cpup.h	2016-10-09 16:55:36.486034798 +0200
-@@ -0,0 +1,94 @@
++++ linux/fs/aufs/cpup.h	2018-04-15 08:49:13.397817296 +0200
+@@ -0,0 +1,99 @@
 +/*
-+ * Copyright (C) 2005-2016 Junjiro R. Okajima
++ * Copyright (C) 2005-2018 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
@@ -6105,6 +6380,11 @@ diff -urN /usr/share/empty/fs/aufs/cpup.h linux/fs/aufs/cpup.h
 +#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)
@@ -6140,10 +6420,10 @@ diff -urN /usr/share/empty/fs/aufs/cpup.h linux/fs/aufs/cpup.h
 +#endif /* __AUFS_CPUP_H__ */
 diff -urN /usr/share/empty/fs/aufs/dbgaufs.c linux/fs/aufs/dbgaufs.c
 --- /usr/share/empty/fs/aufs/dbgaufs.c	1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/dbgaufs.c	2016-10-09 16:55:38.886097714 +0200
-@@ -0,0 +1,438 @@
++++ linux/fs/aufs/dbgaufs.c	2018-04-15 08:49:13.397817296 +0200
+@@ -0,0 +1,437 @@
 +/*
-+ * Copyright (C) 2005-2016 Junjiro R. Okajima
++ * Copyright (C) 2005-2018 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
@@ -6185,7 +6465,7 @@ diff -urN /usr/share/empty/fs/aufs/dbgaufs.c linux/fs/aufs/dbgaufs.c
 +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;
 +}
 +
@@ -6247,7 +6527,7 @@ diff -urN /usr/share/empty/fs/aufs/dbgaufs.c linux/fs/aufs/dbgaufs.c
 +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;
 +}
 +
@@ -6258,7 +6538,7 @@ diff -urN /usr/share/empty/fs/aufs/dbgaufs.c linux/fs/aufs/dbgaufs.c
 +	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);
@@ -6278,10 +6558,9 @@ diff -urN /usr/share/empty/fs/aufs/dbgaufs.c linux/fs/aufs/dbgaufs.c
 +		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);
@@ -6311,7 +6590,7 @@ diff -urN /usr/share/empty/fs/aufs/dbgaufs.c linux/fs/aufs/dbgaufs.c
 +	goto out; /* success */
 +
 +out_free:
-+	au_delayed_free_page((unsigned long)p);
++	free_page((unsigned long)p);
 +out:
 +	return err;
 +}
@@ -6582,10 +6861,10 @@ diff -urN /usr/share/empty/fs/aufs/dbgaufs.c linux/fs/aufs/dbgaufs.c
 +}
 diff -urN /usr/share/empty/fs/aufs/dbgaufs.h linux/fs/aufs/dbgaufs.h
 --- /usr/share/empty/fs/aufs/dbgaufs.h	1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/dbgaufs.h	2016-10-09 16:55:36.486034798 +0200
++++ linux/fs/aufs/dbgaufs.h	2018-04-15 08:49:13.397817296 +0200
 @@ -0,0 +1,48 @@
 +/*
-+ * Copyright (C) 2005-2016 Junjiro R. Okajima
++ * Copyright (C) 2005-2018 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
@@ -6634,10 +6913,10 @@ diff -urN /usr/share/empty/fs/aufs/dbgaufs.h linux/fs/aufs/dbgaufs.h
 +#endif /* __DBGAUFS_H__ */
 diff -urN /usr/share/empty/fs/aufs/dcsub.c linux/fs/aufs/dcsub.c
 --- /usr/share/empty/fs/aufs/dcsub.c	1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/dcsub.c	2016-10-09 16:55:38.886097714 +0200
++++ linux/fs/aufs/dcsub.c	2018-04-15 08:49:13.397817296 +0200
 @@ -0,0 +1,225 @@
 +/*
-+ * Copyright (C) 2005-2016 Junjiro R. Okajima
++ * Copyright (C) 2005-2018 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
@@ -6667,7 +6946,7 @@ diff -urN /usr/share/empty/fs/aufs/dcsub.c linux/fs/aufs/dcsub.c
 +	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)
@@ -6690,7 +6969,7 @@ diff -urN /usr/share/empty/fs/aufs/dcsub.c linux/fs/aufs/dcsub.c
 +	return 0; /* success */
 +
 +out_dpages:
-+	au_delayed_kfree(dpages->dpages);
++	kfree(dpages->dpages);
 +out:
 +	return err;
 +}
@@ -6703,7 +6982,7 @@ diff -urN /usr/share/empty/fs/aufs/dcsub.c linux/fs/aufs/dcsub.c
 +	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,
@@ -6863,10 +7142,10 @@ diff -urN /usr/share/empty/fs/aufs/dcsub.c linux/fs/aufs/dcsub.c
 +}
 diff -urN /usr/share/empty/fs/aufs/dcsub.h linux/fs/aufs/dcsub.h
 --- /usr/share/empty/fs/aufs/dcsub.h	1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/dcsub.h	2016-10-09 16:55:36.486034798 +0200
++++ linux/fs/aufs/dcsub.h	2018-04-15 08:49:13.397817296 +0200
 @@ -0,0 +1,136 @@
 +/*
-+ * Copyright (C) 2005-2016 Junjiro R. Okajima
++ * Copyright (C) 2005-2018 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
@@ -7003,10 +7282,10 @@ diff -urN /usr/share/empty/fs/aufs/dcsub.h linux/fs/aufs/dcsub.h
 +#endif /* __AUFS_DCSUB_H__ */
 diff -urN /usr/share/empty/fs/aufs/debug.c linux/fs/aufs/debug.c
 --- /usr/share/empty/fs/aufs/debug.c	1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/debug.c	2016-10-09 16:55:36.486034798 +0200
++++ linux/fs/aufs/debug.c	2018-04-15 08:49:13.397817296 +0200
 @@ -0,0 +1,440 @@
 +/*
-+ * Copyright (C) 2005-2016 Junjiro R. Okajima
++ * Copyright (C) 2005-2018 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
@@ -7343,7 +7622,7 @@ diff -urN /usr/share/empty/fs/aufs/debug.c linux/fs/aufs/debug.c
 +	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;
@@ -7447,10 +7726,10 @@ diff -urN /usr/share/empty/fs/aufs/debug.c linux/fs/aufs/debug.c
 +}
 diff -urN /usr/share/empty/fs/aufs/debug.h linux/fs/aufs/debug.h
 --- /usr/share/empty/fs/aufs/debug.h	1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/debug.h	2016-10-09 16:55:36.486034798 +0200
++++ linux/fs/aufs/debug.h	2018-04-15 08:49:13.397817296 +0200
 @@ -0,0 +1,225 @@
 +/*
-+ * Copyright (C) 2005-2016 Junjiro R. Okajima
++ * Copyright (C) 2005-2018 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
@@ -7676,10 +7955,10 @@ diff -urN /usr/share/empty/fs/aufs/debug.h linux/fs/aufs/debug.h
 +#endif /* __AUFS_DEBUG_H__ */
 diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
 --- /usr/share/empty/fs/aufs/dentry.c	1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/dentry.c	2016-10-09 16:55:38.889431135 +0200
-@@ -0,0 +1,1130 @@
++++ linux/fs/aufs/dentry.c	2018-04-15 08:49:13.397817296 +0200
+@@ -0,0 +1,1152 @@
 +/*
-+ * Copyright (C) 2005-2016 Junjiro R. Okajima
++ * Copyright (C) 2005-2018 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
@@ -7702,19 +7981,13 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
 +#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;
@@ -7729,7 +8002,7 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
 +	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;
@@ -7744,9 +8017,9 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
 +
 +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)
@@ -7761,6 +8034,13 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
 +	} 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);
@@ -7773,9 +8053,9 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
 +	    || (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)) {
@@ -7809,26 +8089,28 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
 +{
 +	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);
@@ -7836,6 +8118,7 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
 +	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) {
@@ -7847,11 +8130,17 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
 +		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;
@@ -7878,6 +8167,15 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
 +			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) {
@@ -7894,7 +8192,9 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
 +
 +out_parent:
 +	dput(parent);
-+	au_delayed_kfree(whname.name);
++	kfree(args.whname.name);
++	if (dirren)
++		au_dr_lkup_fin(&args);
 +out:
 +	return err;
 +}
@@ -8503,7 +8803,7 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
 +
 +/* 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;
@@ -8554,7 +8854,7 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
 +			     && !is_root
 +			     && ((!h_nfs
 +				  && (unhashed != !!d_unhashed(h_dentry)
-+				      || (!tmpfile
++				      || (!tmpfile && !dirren
 +					  && !au_qstreq(name, h_name))
 +					  ))
 +				 || (h_nfs
@@ -8695,7 +8995,7 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
 +{
 +	int valid, err;
 +	unsigned int sigen;
-+	unsigned char do_udba;
++	unsigned char do_udba, dirren;
 +	struct super_block *sb;
 +	struct inode *inode;
 +
@@ -8768,7 +9068,8 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
 +		}
 +	}
 +
-+	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",
@@ -8810,10 +9111,10 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
 +};
 diff -urN /usr/share/empty/fs/aufs/dentry.h linux/fs/aufs/dentry.h
 --- /usr/share/empty/fs/aufs/dentry.h	1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/dentry.h	2016-10-09 16:55:38.889431135 +0200
-@@ -0,0 +1,255 @@
++++ linux/fs/aufs/dentry.h	2018-04-15 08:49:13.397817296 +0200
+@@ -0,0 +1,266 @@
 +/*
-+ * Copyright (C) 2005-2016 Junjiro R. Okajima
++ * Copyright (C) 2005-2018 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
@@ -8839,6 +9140,7 @@ diff -urN /usr/share/empty/fs/aufs/dentry.h linux/fs/aufs/dentry.h
 +#ifdef __KERNEL__
 +
 +#include <linux/dcache.h>
++#include "dirren.h"
 +#include "rwsem.h"
 +
 +struct au_hdentry {
@@ -8852,10 +9154,7 @@ diff -urN /usr/share/empty/fs/aufs/dentry.h linux/fs/aufs/dentry.h
 +	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;
 +
 +/* ---------------------------------------------------------------------- */
@@ -8863,12 +9162,25 @@ diff -urN /usr/share/empty/fs/aufs/dentry.h linux/fs/aufs/dentry.h
 +/* 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 */
@@ -9069,10 +9381,10 @@ diff -urN /usr/share/empty/fs/aufs/dentry.h linux/fs/aufs/dentry.h
 +#endif /* __AUFS_DENTRY_H__ */
 diff -urN /usr/share/empty/fs/aufs/dinfo.c linux/fs/aufs/dinfo.c
 --- /usr/share/empty/fs/aufs/dinfo.c	1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/dinfo.c	2016-10-09 16:55:38.889431135 +0200
++++ linux/fs/aufs/dinfo.c	2018-04-15 08:49:13.397817296 +0200
 @@ -0,0 +1,553 @@
 +/*
-+ * Copyright (C) 2005-2016 Junjiro R. Okajima
++ * Copyright (C) 2005-2018 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
@@ -9126,7 +9438,7 @@ diff -urN /usr/share/empty/fs/aufs/dinfo.c linux/fs/aufs/dinfo.c
 +		goto out;
 +	}
 +
-+	au_cache_dfree_dinfo(dinfo);
++	au_cache_free_dinfo(dinfo);
 +	dinfo = NULL;
 +
 +out:
@@ -9146,8 +9458,8 @@ diff -urN /usr/share/empty/fs/aufs/dinfo.c linux/fs/aufs/dinfo.c
 +		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)
@@ -9349,11 +9661,11 @@ diff -urN /usr/share/empty/fs/aufs/dinfo.c linux/fs/aufs/dinfo.c
 +		  || 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);
 +	}
@@ -9365,11 +9677,11 @@ diff -urN /usr/share/empty/fs/aufs/dinfo.c linux/fs/aufs/dinfo.c
 +		  || 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);
 +	}
@@ -9626,10 +9938,10 @@ diff -urN /usr/share/empty/fs/aufs/dinfo.c linux/fs/aufs/dinfo.c
 +}
 diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c
 --- /usr/share/empty/fs/aufs/dir.c	1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/dir.c	2016-10-09 16:55:36.489368218 +0200
-@@ -0,0 +1,762 @@
++++ linux/fs/aufs/dir.c	2018-04-15 08:49:13.397817296 +0200
+@@ -0,0 +1,759 @@
 +/*
-+ * Copyright (C) 2005-2016 Junjiro R. Okajima
++ * Copyright (C) 2005-2018 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
@@ -9786,7 +10098,7 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c
 +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)
@@ -9822,7 +10134,7 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c
 +	if (unlikely(wkq_err)) {
 +		pr_err("wkq %d\n", wkq_err);
 +		dput(dentry);
-+		au_delayed_kfree(arg);
++		kfree(arg);
 +	}
 +
 +out:
@@ -9941,7 +10253,7 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c
 +		};
 +		err = au_do_open(file, &args);
 +		if (unlikely(err))
-+			au_delayed_kfree(fidir);
++			kfree(fidir);
 +	}
 +	si_read_unlock(sb);
 +	return err;
@@ -9955,21 +10267,18 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c
 +	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(),
@@ -9978,12 +10287,12 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c
 +			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;
 +}
 +
@@ -10048,7 +10357,7 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c
 +	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;
 +
@@ -10117,7 +10426,7 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c
 +
 +	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);
@@ -10270,9 +10579,9 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c
 +	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 {
@@ -10392,10 +10701,10 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c
 +};
 diff -urN /usr/share/empty/fs/aufs/dir.h linux/fs/aufs/dir.h
 --- /usr/share/empty/fs/aufs/dir.h	1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/dir.h	2016-10-09 16:55:36.489368218 +0200
-@@ -0,0 +1,137 @@
++++ linux/fs/aufs/dir.h	2018-04-15 08:49:13.397817296 +0200
+@@ -0,0 +1,131 @@
 +/*
-+ * Copyright (C) 2005-2016 Junjiro R. Okajima
++ * Copyright (C) 2005-2018 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
@@ -10438,10 +10747,7 @@ diff -urN /usr/share/empty/fs/aufs/dir.h linux/fs/aufs/dir.h
 +
 +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 {
@@ -10479,10 +10785,7 @@ diff -urN /usr/share/empty/fs/aufs/dir.h linux/fs/aufs/dir.h
 +
 +	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;
 +
 +/* ---------------------------------------------------------------------- */
@@ -10506,7 +10809,7 @@ diff -urN /usr/share/empty/fs/aufs/dir.h linux/fs/aufs/dir.h
 +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);
 +
@@ -10531,12 +10834,1479 @@ diff -urN /usr/share/empty/fs/aufs/dir.h linux/fs/aufs/dir.h
 +
 +#endif /* __KERNEL__ */
 +#endif /* __AUFS_DIR_H__ */
+diff -urN /usr/share/empty/fs/aufs/dirren.c linux/fs/aufs/dirren.c
+--- /usr/share/empty/fs/aufs/dirren.c	1970-01-01 01:00:00.000000000 +0100
++++ linux/fs/aufs/dirren.c	2018-04-15 08:49:13.397817296 +0200
+@@ -0,0 +1,1314 @@
++/*
++ * Copyright (C) 2017-2018 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 -urN /usr/share/empty/fs/aufs/dirren.h linux/fs/aufs/dirren.h
+--- /usr/share/empty/fs/aufs/dirren.h	1970-01-01 01:00:00.000000000 +0100
++++ linux/fs/aufs/dirren.h	2018-04-15 08:49:13.397817296 +0200
+@@ -0,0 +1,145 @@
++/*
++ * Copyright (C) 2017-2018 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 -urN /usr/share/empty/fs/aufs/dynop.c linux/fs/aufs/dynop.c
 --- /usr/share/empty/fs/aufs/dynop.c	1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/dynop.c	2016-10-09 16:55:36.489368218 +0200
-@@ -0,0 +1,371 @@
++++ linux/fs/aufs/dynop.c	2018-04-15 08:49:13.397817296 +0200
+@@ -0,0 +1,369 @@
 +/*
-+ * Copyright (C) 2010-2016 Junjiro R. Okajima
++ * Copyright (C) 2010-2018 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
@@ -10564,23 +12334,23 @@ diff -urN /usr/share/empty/fs/aufs/dynop.c linux/fs/aufs/dynop.c
 + * 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;
 +}
@@ -10621,24 +12391,23 @@ diff -urN /usr/share/empty/fs/aufs/dynop.c linux/fs/aufs/dynop.c
 +}
 +
 +/* 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);
@@ -10651,17 +12420,17 @@ diff -urN /usr/share/empty/fs/aufs/dynop.c linux/fs/aufs/dynop.c
 +
 +	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);
 +}
 +
@@ -10748,7 +12517,7 @@ diff -urN /usr/share/empty/fs/aufs/dynop.c linux/fs/aufs/dynop.c
 +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,
@@ -10762,8 +12531,8 @@ diff -urN /usr/share/empty/fs/aufs/dynop.c linux/fs/aufs/dynop.c
 +	};
 +	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 */
 +
@@ -10777,9 +12546,9 @@ diff -urN /usr/share/empty/fs/aufs/dynop.c linux/fs/aufs/dynop.c
 +	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;
 +	}
 +
@@ -10874,16 +12643,15 @@ diff -urN /usr/share/empty/fs/aufs/dynop.c linux/fs/aufs/dynop.c
 +
 +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);
 +}
 +
 +/* ---------------------------------------------------------------------- */
@@ -10896,7 +12664,7 @@ diff -urN /usr/share/empty/fs/aufs/dynop.c linux/fs/aufs/dynop.c
 +	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)
@@ -10904,14 +12672,14 @@ diff -urN /usr/share/empty/fs/aufs/dynop.c linux/fs/aufs/dynop.c
 +	int i;
 +
 +	for (i = 0; i < AuDyLast; i++)
-+		WARN_ON(!hlist_empty(&dynop[i].head));
++		WARN_ON(!hlist_bl_empty(dynop + i));
 +}
 diff -urN /usr/share/empty/fs/aufs/dynop.h linux/fs/aufs/dynop.h
 --- /usr/share/empty/fs/aufs/dynop.h	1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/dynop.h	2016-10-09 16:55:36.489368218 +0200
++++ linux/fs/aufs/dynop.h	2018-04-15 08:49:13.397817296 +0200
 @@ -0,0 +1,74 @@
 +/*
-+ * Copyright (C) 2010-2016 Junjiro R. Okajima
++ * Copyright (C) 2010-2018 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
@@ -10951,7 +12719,7 @@ diff -urN /usr/share/empty/fs/aufs/dynop.h linux/fs/aufs/dynop.h
 +
 +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;
@@ -10986,10 +12754,10 @@ diff -urN /usr/share/empty/fs/aufs/dynop.h linux/fs/aufs/dynop.h
 +#endif /* __AUFS_DYNOP_H__ */
 diff -urN /usr/share/empty/fs/aufs/export.c linux/fs/aufs/export.c
 --- /usr/share/empty/fs/aufs/export.c	1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/export.c	2016-12-17 12:28:17.595211562 +0100
++++ linux/fs/aufs/export.c	2018-04-15 08:49:13.397817296 +0200
 @@ -0,0 +1,836 @@
 +/*
-+ * Copyright (C) 2005-2016 Junjiro R. Okajima
++ * Copyright (C) 2005-2018 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
@@ -11406,7 +13174,7 @@ diff -urN /usr/share/empty/fs/aufs/export.c linux/fs/aufs/export.c
 +	}
 +
 +out_name:
-+	au_delayed_free_page((unsigned long)arg.name);
++	free_page((unsigned long)arg.name);
 +out_file:
 +	fput(file);
 +out:
@@ -11560,7 +13328,7 @@ diff -urN /usr/share/empty/fs/aufs/export.c linux/fs/aufs/export.c
 +			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:
@@ -11826,10 +13594,10 @@ diff -urN /usr/share/empty/fs/aufs/export.c linux/fs/aufs/export.c
 +}
 diff -urN /usr/share/empty/fs/aufs/fhsm.c linux/fs/aufs/fhsm.c
 --- /usr/share/empty/fs/aufs/fhsm.c	1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/fhsm.c	2016-10-09 16:55:36.489368218 +0200
-@@ -0,0 +1,426 @@
++++ linux/fs/aufs/fhsm.c	2018-04-15 08:49:13.397817296 +0200
+@@ -0,0 +1,427 @@
 +/*
-+ * Copyright (C) 2011-2016 Junjiro R. Okajima
++ * Copyright (C) 2011-2018 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
@@ -12013,7 +13781,8 @@ diff -urN /usr/share/empty/fs/aufs/fhsm.c linux/fs/aufs/fhsm.c
 +	if (atomic_read(&fhsm->fhsm_readable))
 +		mask = POLLIN /* | POLLRDNORM */;
 +
-+	AuTraceErr((int)mask);
++	if (!mask)
++		AuDbg("mask 0x%x\n", mask);
 +	return mask;
 +}
 +
@@ -12256,10 +14025,10 @@ diff -urN /usr/share/empty/fs/aufs/fhsm.c linux/fs/aufs/fhsm.c
 +}
 diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c
 --- /usr/share/empty/fs/aufs/file.c	1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/file.c	2016-10-09 16:55:38.889431135 +0200
-@@ -0,0 +1,857 @@
++++ linux/fs/aufs/file.c	2018-04-15 08:49:13.397817296 +0200
+@@ -0,0 +1,848 @@
 +/*
-+ * Copyright (C) 2005-2016 Junjiro R. Okajima
++ * Copyright (C) 2005-2018 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
@@ -12368,7 +14137,7 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c
 +
 +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;
@@ -12403,9 +14172,12 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c
 +	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);
@@ -12482,11 +14254,11 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c
 +
 +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();
@@ -12498,33 +14270,20 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c
 +
 +	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();
@@ -12533,7 +14292,7 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c
 +	}
 +	if (unlikely(err)) {
 +		finfo->fi_hdir = NULL;
-+		au_finfo_fin(file, /*atonce*/0);
++		au_finfo_fin(file);
 +	}
 +
 +out:
@@ -12853,7 +14612,6 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c
 +
 +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;
@@ -12892,7 +14650,6 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c
 +		}
 +	}
 +
-+	execed = vfsub_file_execed(file);
 +	p = fidir->fd_hfile;
 +	if (!au_test_mmapped(file) && !d_unlinked(file->f_path.dentry)) {
 +		bbot = au_sbbot(sb);
@@ -12901,14 +14658,14 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c
 +			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);
 +	}
 +
@@ -12918,7 +14675,7 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c
 +		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);
 +}
@@ -12980,7 +14737,7 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c
 +
 +/* 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;
@@ -12993,9 +14750,12 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c
 +	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) {
@@ -13117,10 +14877,10 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c
 +};
 diff -urN /usr/share/empty/fs/aufs/file.h linux/fs/aufs/file.h
 --- /usr/share/empty/fs/aufs/file.h	1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/file.h	2016-10-09 16:55:38.889431135 +0200
-@@ -0,0 +1,294 @@
++++ linux/fs/aufs/file.h	2018-04-15 08:49:13.397817296 +0200
+@@ -0,0 +1,330 @@
 +/*
-+ * Copyright (C) 2005-2016 Junjiro R. Okajima
++ * Copyright (C) 2005-2018 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
@@ -13183,11 +14943,8 @@ diff -urN /usr/share/empty/fs/aufs/file.h linux/fs/aufs/file.h
 +	};
 +	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;
 +
 +/* ---------------------------------------------------------------------- */
@@ -13198,7 +14955,7 @@ diff -urN /usr/share/empty/fs/aufs/file.h linux/fs/aufs/file.h
 +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;
@@ -13209,7 +14966,7 @@ diff -urN /usr/share/empty/fs/aufs/file.h linux/fs/aufs/file.h
 +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));
 +
@@ -13235,7 +14992,7 @@ diff -urN /usr/share/empty/fs/aufs/file.h linux/fs/aufs/file.h
 +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);
@@ -13247,7 +15004,7 @@ diff -urN /usr/share/empty/fs/aufs/file.h linux/fs/aufs/file.h
 +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 */
@@ -13274,6 +15031,45 @@ diff -urN /usr/share/empty/fs/aufs/file.h linux/fs/aufs/file.h
 + */
 +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)
@@ -13415,10 +15211,10 @@ diff -urN /usr/share/empty/fs/aufs/file.h linux/fs/aufs/file.h
 +#endif /* __AUFS_FILE_H__ */
 diff -urN /usr/share/empty/fs/aufs/finfo.c linux/fs/aufs/finfo.c
 --- /usr/share/empty/fs/aufs/finfo.c	1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/finfo.c	2016-10-09 16:55:38.889431135 +0200
-@@ -0,0 +1,151 @@
++++ linux/fs/aufs/finfo.c	2018-04-15 08:49:13.397817296 +0200
+@@ -0,0 +1,148 @@
 +/*
-+ * Copyright (C) 2005-2016 Junjiro R. Okajima
++ * Copyright (C) 2005-2018 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
@@ -13521,7 +15317,7 @@ diff -urN /usr/share/empty/fs/aufs/finfo.c linux/fs/aufs/finfo.c
 +
 +/* ---------------------------------------------------------------------- */
 +
-+void au_finfo_fin(struct file *file, int atonce)
++void au_finfo_fin(struct file *file)
 +{
 +	struct au_finfo *finfo;
 +
@@ -13530,10 +15326,7 @@ diff -urN /usr/share/empty/fs/aufs/finfo.c linux/fs/aufs/finfo.c
 +	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)
@@ -13570,10 +15363,10 @@ diff -urN /usr/share/empty/fs/aufs/finfo.c linux/fs/aufs/finfo.c
 +}
 diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c
 --- /usr/share/empty/fs/aufs/f_op.c	1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/f_op.c	2016-12-17 12:28:17.595211562 +0100
-@@ -0,0 +1,723 @@
++++ linux/fs/aufs/f_op.c	2018-04-15 08:49:13.397817296 +0200
+@@ -0,0 +1,817 @@
 +/*
-+ * Copyright (C) 2005-2016 Junjiro R. Okajima
++ * Copyright (C) 2005-2018 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
@@ -13673,17 +15466,15 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c
 +{
 +	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;
 +}
 +
@@ -13716,12 +15507,12 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c
 + */
 +
 +/* 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);
@@ -13742,6 +15533,10 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c
 +}
 +
 +struct au_write_pre {
++	/* input */
++	unsigned int lsc;
++
++	/* output */
 +	blkcnt_t blks;
 +	aufs_bindex_t btop;
 +};
@@ -13757,9 +15552,13 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c
 +	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;
@@ -13801,12 +15600,11 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c
 +	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,
@@ -13821,7 +15619,7 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c
 +	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;
@@ -13871,6 +15669,7 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c
 +	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))
@@ -13930,7 +15729,7 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c
 +	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;
@@ -13965,6 +15764,7 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c
 +	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))
@@ -13992,7 +15792,7 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c
 +	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;
@@ -14019,6 +15819,7 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c
 +	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))
@@ -14044,6 +15845,7 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c
 +	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))
@@ -14060,6 +15862,88 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c
 +	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)) {
<Skipped 3743 lines>
================================================================

---- gitweb:

http://git.pld-linux.org/gitweb.cgi/packages/kernel.git/commitdiff/ae9dfd79ace7e0a2a98099f2c18227fc16643bf3



More information about the pld-cvs-commit mailing list