diff --git a/kernel/kernel/files/patches/linux/patch-4.2.2.xz b/kernel/kernel/files/patches/linux/patch-4.2.2.xz deleted file mode 100644 index 573d719e..00000000 Binary files a/kernel/kernel/files/patches/linux/patch-4.2.2.xz and /dev/null differ diff --git a/kernel/kernel/files/patches/linux/patch-4.2.3.xz b/kernel/kernel/files/patches/linux/patch-4.2.3.xz deleted file mode 100644 index db5ae4ca..00000000 Binary files a/kernel/kernel/files/patches/linux/patch-4.2.3.xz and /dev/null differ diff --git a/kernel/kernel/files/patches/linux/patch-4.2.4.xz b/kernel/kernel/files/patches/linux/patch-4.2.4.xz deleted file mode 100644 index 03a7b891..00000000 Binary files a/kernel/kernel/files/patches/linux/patch-4.2.4.xz and /dev/null differ diff --git a/kernel/kernel/files/patches/linux/patch-4.2.5.xz b/kernel/kernel/files/patches/linux/patch-4.2.5.xz deleted file mode 100644 index fbb95167..00000000 Binary files a/kernel/kernel/files/patches/linux/patch-4.2.5.xz and /dev/null differ diff --git a/kernel/kernel/files/patches/linux/patch-4.2.6.xz b/kernel/kernel/files/patches/linux/patch-4.2.6.xz deleted file mode 100644 index 06d8f455..00000000 Binary files a/kernel/kernel/files/patches/linux/patch-4.2.6.xz and /dev/null differ diff --git a/kernel/kernel/files/patches/linux/patch-4.6.3.xz b/kernel/kernel/files/patches/linux/patch-4.6.3.xz new file mode 100644 index 00000000..ea360870 Binary files /dev/null and b/kernel/kernel/files/patches/linux/patch-4.6.3.xz differ diff --git a/kernel/kernel/files/patches/mageia/3rd-3rdparty-merge.patch b/kernel/kernel/files/patches/mageia/3rd-3rdparty-merge.patch index fb5e2b7e..1624466e 100644 --- a/kernel/kernel/files/patches/mageia/3rd-3rdparty-merge.patch +++ b/kernel/kernel/files/patches/mageia/3rd-3rdparty-merge.patch @@ -83,9 +83,9 @@ diff -Nurp linux-4.2.2/Makefile linux-4.2.2-3rd/Makefile net-y := net/ libs-y := lib/ core-y := usr/ -diff -Nurp linux-4.4.0/scripts/kconfig/Makefile linux-4.4.0-3rd/scripts/kconfig/Makefile ---- linux-4.4.0/scripts/kconfig/Makefile -+++ linux-4.4.0-3rd/scripts/kconfig/Makefile +diff -Nurp linux-4.6/scripts/kconfig/Makefile linux-4.6-3rd/scripts/kconfig/Makefile +--- linux-4.6/scripts/kconfig/Makefile ++++ linux-4.6-3rd/scripts/kconfig/Makefile @@ -18,26 +18,26 @@ endif # We need this, in case the user has it in its environment unexport CONFIG_ @@ -144,9 +144,9 @@ diff -Nurp linux-4.4.0/scripts/kconfig/Makefile linux-4.4.0-3rd/scripts/kconfig/ +defconfig: $(obj)/conf 3rdparty/Makefile ifeq ($(KBUILD_DEFCONFIG),) $< $(silent) --defconfig $(Kconfig) - else ifneq ($(wildcard $(srctree)/arch/$(SRCARCH)/configs/$(KBUILD_DEFCONFIG)),) -@@ -104,26 +104,26 @@ else - $(Q)$(MAKE) -f $(srctree)/Makefile $(KBUILD_DEFCONFIG) + else +@@ -106,26 +106,26 @@ else + endif endif -%_defconfig: $(obj)/conf @@ -173,11 +173,11 @@ diff -Nurp linux-4.4.0/scripts/kconfig/Makefile linux-4.4.0-3rd/scripts/kconfig/ PHONY += tinyconfig -tinyconfig: -+tinyconfig: 3rdparty/Makefile ++tinyconfig: $(Q)$(MAKE) -f $(srctree)/Makefile allnoconfig tiny.config # Help text used by make help -@@ -186,6 +186,9 @@ gconf-objs := gconf.o zconf.tab.o +@@ -188,6 +188,9 @@ gconf-objs := gconf.o zconf.tab.o hostprogs-y := conf nconf mconf kxgettext qconf gconf diff --git a/kernel/kernel/files/patches/mageia/3rd-ndiswrapper-1.59.patch b/kernel/kernel/files/patches/mageia/3rd-ndiswrapper-1.60.patch similarity index 98% rename from kernel/kernel/files/patches/mageia/3rd-ndiswrapper-1.59.patch rename to kernel/kernel/files/patches/mageia/3rd-ndiswrapper-1.60.patch index 63ccc093..b328f41a 100644 --- a/kernel/kernel/files/patches/mageia/3rd-ndiswrapper-1.59.patch +++ b/kernel/kernel/files/patches/mageia/3rd-ndiswrapper-1.60.patch @@ -1,6 +1,6 @@ -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/crt.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/crt.c ---- linux-3.12.2/3rdparty/ndiswrapper/crt.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/crt.c 2013-11-28 21:42:10.000000000 +0200 +diff -Nurp linux-4.6-rc6/3rdparty/ndiswrapper/crt.c linux-4.6-rc6-ndis/3rdparty/ndiswrapper/crt.c +--- linux-4.6-rc6/3rdparty/ndiswrapper/crt.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-4.6-rc6-ndis/3rdparty/ndiswrapper/crt.c 2016-05-01 18:25:43.000000000 +0300 @@ -0,0 +1,589 @@ +/* + * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani @@ -471,7 +471,7 @@ diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/crt.c linux-3.12.2-ndiswrapper-1.59 +noregparm void WIN_FUNC(_win_srand,1) + (UINT seed) +{ -+ net_srandom(seed); ++ prandom_seed(seed); +} + +noregparm int WIN_FUNC(rand,0) @@ -591,9 +591,9 @@ diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/crt.c linux-3.12.2-ndiswrapper-1.59 + printk(KERN_DEBUG "%s: %p: %s\n", ctx, from, buf); + kfree(buf); +} -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/divdi3.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/divdi3.c ---- linux-3.12.2/3rdparty/ndiswrapper/divdi3.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/divdi3.c 2013-11-28 21:42:10.000000000 +0200 +diff -Nurp linux-4.6-rc6/3rdparty/ndiswrapper/divdi3.c linux-4.6-rc6-ndis/3rdparty/ndiswrapper/divdi3.c +--- linux-4.6-rc6/3rdparty/ndiswrapper/divdi3.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-4.6-rc6-ndis/3rdparty/ndiswrapper/divdi3.c 2016-05-01 18:25:43.000000000 +0300 @@ -0,0 +1,329 @@ +/* 64-bit multiplication and division + Copyright (C) 1989, 1992-1999, 2000, 2001, 2002, 2003 @@ -924,9 +924,9 @@ diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/divdi3.c linux-3.12.2-ndiswrapper-1 + __udivmoddi4 (u, v, &w); + return w; +} -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/hal.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/hal.c ---- linux-3.12.2/3rdparty/ndiswrapper/hal.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/hal.c 2013-11-28 21:42:10.000000000 +0200 +diff -Nurp linux-4.6-rc6/3rdparty/ndiswrapper/hal.c linux-4.6-rc6-ndis/3rdparty/ndiswrapper/hal.c +--- linux-4.6-rc6/3rdparty/ndiswrapper/hal.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-4.6-rc6-ndis/3rdparty/ndiswrapper/hal.c 2016-05-01 18:25:43.000000000 +0300 @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani @@ -1085,9 +1085,9 @@ diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/hal.c linux-3.12.2-ndiswrapper-1.59 +#endif + nt_spin_unlock(lock); +} -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/iw_ndis.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/iw_ndis.c ---- linux-3.12.2/3rdparty/ndiswrapper/iw_ndis.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/iw_ndis.c 2013-11-28 21:42:10.000000000 +0200 +diff -Nurp linux-4.6-rc6/3rdparty/ndiswrapper/iw_ndis.c linux-4.6-rc6-ndis/3rdparty/ndiswrapper/iw_ndis.c +--- linux-4.6-rc6/3rdparty/ndiswrapper/iw_ndis.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-4.6-rc6-ndis/3rdparty/ndiswrapper/iw_ndis.c 2016-05-01 18:25:43.000000000 +0300 @@ -0,0 +1,2001 @@ + /* + * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani @@ -3090,9 +3090,9 @@ diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/iw_ndis.c linux-3.12.2-ndiswrapper- +}; + +#endif -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/iw_ndis.h linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/iw_ndis.h ---- linux-3.12.2/3rdparty/ndiswrapper/iw_ndis.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/iw_ndis.h 2013-11-28 21:42:10.000000000 +0200 +diff -Nurp linux-4.6-rc6/3rdparty/ndiswrapper/iw_ndis.h linux-4.6-rc6-ndis/3rdparty/ndiswrapper/iw_ndis.h +--- linux-4.6-rc6/3rdparty/ndiswrapper/iw_ndis.h 1970-01-01 02:00:00.000000000 +0200 ++++ linux-4.6-rc6-ndis/3rdparty/ndiswrapper/iw_ndis.h 2016-05-01 18:25:43.000000000 +0300 @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani @@ -3288,9 +3288,9 @@ diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/iw_ndis.h linux-3.12.2-ndiswrapper- + KEY_MGMT_802_1X_NO_WPA, KEY_MGMT_WPA_NONE }; + +#endif // IW_NDIS_H -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/lin2win.h linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/lin2win.h ---- linux-3.12.2/3rdparty/ndiswrapper/lin2win.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/lin2win.h 2013-11-28 21:42:10.000000000 +0200 +diff -Nurp linux-4.6-rc6/3rdparty/ndiswrapper/lin2win.h linux-4.6-rc6-ndis/3rdparty/ndiswrapper/lin2win.h +--- linux-4.6-rc6/3rdparty/ndiswrapper/lin2win.h 1970-01-01 02:00:00.000000000 +0200 ++++ linux-4.6-rc6-ndis/3rdparty/ndiswrapper/lin2win.h 2016-05-01 18:25:43.000000000 +0300 @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2006 Giridhar Pemmasani @@ -3403,9 +3403,9 @@ diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/lin2win.h linux-3.12.2-ndiswrapper- +}) + +#endif // CONFIG_X86_64 -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/lin2win.S linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/lin2win.S ---- linux-3.12.2/3rdparty/ndiswrapper/lin2win.S 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/lin2win.S 2013-11-28 21:42:10.000000000 +0200 +diff -Nurp linux-4.6-rc6/3rdparty/ndiswrapper/lin2win.S linux-4.6-rc6-ndis/3rdparty/ndiswrapper/lin2win.S +--- linux-4.6-rc6/3rdparty/ndiswrapper/lin2win.S 1970-01-01 02:00:00.000000000 +0200 ++++ linux-4.6-rc6-ndis/3rdparty/ndiswrapper/lin2win.S 2016-05-01 18:25:43.000000000 +0300 @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2011 Pavel Roskin @@ -3545,9 +3545,9 @@ diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/lin2win.S linux-3.12.2-ndiswrapper- +lin2win lin2win4, 4 +lin2win lin2win5, 5 +lin2win lin2win6, 6 -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/loader.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/loader.c ---- linux-3.12.2/3rdparty/ndiswrapper/loader.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/loader.c 2013-11-28 21:42:10.000000000 +0200 +diff -Nurp linux-4.6-rc6/3rdparty/ndiswrapper/loader.c linux-4.6-rc6-ndis/3rdparty/ndiswrapper/loader.c +--- linux-4.6-rc6/3rdparty/ndiswrapper/loader.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-4.6-rc6-ndis/3rdparty/ndiswrapper/loader.c 2016-05-01 18:25:43.000000000 +0300 @@ -0,0 +1,967 @@ +/* + * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani @@ -4516,10 +4516,10 @@ diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/loader.c linux-3.12.2-ndiswrapper-1 + mutex_unlock(&loader_mutex); + EXIT1(return); +} -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/loader.h linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/loader.h ---- linux-3.12.2/3rdparty/ndiswrapper/loader.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/loader.h 2013-11-28 21:42:10.000000000 +0200 -@@ -0,0 +1,108 @@ +diff -Nurp linux-4.6-rc6/3rdparty/ndiswrapper/loader.h linux-4.6-rc6-ndis/3rdparty/ndiswrapper/loader.h +--- linux-4.6-rc6/3rdparty/ndiswrapper/loader.h 1970-01-01 02:00:00.000000000 +0200 ++++ linux-4.6-rc6-ndis/3rdparty/ndiswrapper/loader.h 2016-05-01 18:25:43.000000000 +0300 +@@ -0,0 +1,112 @@ +/* + * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani + * @@ -4538,6 +4538,10 @@ diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/loader.h linux-3.12.2-ndiswrapper-1 +#ifndef _LOADER_H_ +#define _LOADER_H_ + ++#if LINUX_VERSION_CODE > KERNEL_VERSION(4,0,0) ++#include ++#endif ++ +#include "ndiswrapper.h" + +#ifndef __KERNEL__ @@ -4628,9 +4632,9 @@ diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/loader.h linux-3.12.2-ndiswrapper-1 +#endif + +#endif /* LOADER_H */ -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/longlong.h linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/longlong.h ---- linux-3.12.2/3rdparty/ndiswrapper/longlong.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/longlong.h 2013-11-28 21:42:10.000000000 +0200 +diff -Nurp linux-4.6-rc6/3rdparty/ndiswrapper/longlong.h linux-4.6-rc6-ndis/3rdparty/ndiswrapper/longlong.h +--- linux-4.6-rc6/3rdparty/ndiswrapper/longlong.h 1970-01-01 02:00:00.000000000 +0200 ++++ linux-4.6-rc6-ndis/3rdparty/ndiswrapper/longlong.h 2016-05-01 18:25:43.000000000 +0300 @@ -0,0 +1,1333 @@ +/* longlong.h -- definitions for mixed size 32/64 bit arithmetic. + Copyright (C) 1991, 1992, 1994, 1995, 1996, 1997, 1998, 1999, 2000 @@ -5965,9 +5969,9 @@ diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/longlong.h linux-3.12.2-ndiswrapper +#ifndef UDIV_NEEDS_NORMALIZATION +#define UDIV_NEEDS_NORMALIZATION 0 +#endif -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/Makefile linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/Makefile ---- linux-3.12.2/3rdparty/ndiswrapper/Makefile 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/Makefile 2013-11-28 21:42:10.000000000 +0200 +diff -Nurp linux-4.6-rc6/3rdparty/ndiswrapper/Makefile linux-4.6-rc6-ndis/3rdparty/ndiswrapper/Makefile +--- linux-4.6-rc6/3rdparty/ndiswrapper/Makefile 1970-01-01 02:00:00.000000000 +0200 ++++ linux-4.6-rc6-ndis/3rdparty/ndiswrapper/Makefile 2016-05-01 18:25:43.000000000 +0300 @@ -0,0 +1,213 @@ +# Name of the module +MODNAME = ndiswrapper @@ -6182,9 +6186,9 @@ diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/Makefile linux-3.12.2-ndiswrapper-1 + done + +.PHONY: all modules clean install config_check dist -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/mkexport.sh linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/mkexport.sh ---- linux-3.12.2/3rdparty/ndiswrapper/mkexport.sh 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/mkexport.sh 2013-11-28 21:42:11.000000000 +0200 +diff -Nurp linux-4.6-rc6/3rdparty/ndiswrapper/mkexport.sh linux-4.6-rc6-ndis/3rdparty/ndiswrapper/mkexport.sh +--- linux-4.6-rc6/3rdparty/ndiswrapper/mkexport.sh 1970-01-01 02:00:00.000000000 +0200 ++++ linux-4.6-rc6-ndis/3rdparty/ndiswrapper/mkexport.sh 2016-05-01 18:25:43.000000000 +0300 @@ -0,0 +1,42 @@ +#! /bin/sh + @@ -6228,9 +6232,9 @@ diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/mkexport.sh linux-3.12.2-ndiswrappe + +echo " {NULL, NULL}" +echo "};" -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/mkstubs.sh linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/mkstubs.sh ---- linux-3.12.2/3rdparty/ndiswrapper/mkstubs.sh 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/mkstubs.sh 2013-11-28 21:42:11.000000000 +0200 +diff -Nurp linux-4.6-rc6/3rdparty/ndiswrapper/mkstubs.sh linux-4.6-rc6-ndis/3rdparty/ndiswrapper/mkstubs.sh +--- linux-4.6-rc6/3rdparty/ndiswrapper/mkstubs.sh 1970-01-01 02:00:00.000000000 +0200 ++++ linux-4.6-rc6-ndis/3rdparty/ndiswrapper/mkstubs.sh 2016-05-01 18:25:43.000000000 +0300 @@ -0,0 +1,12 @@ +#! /bin/sh + @@ -6244,9 +6248,9 @@ diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/mkstubs.sh linux-3.12.2-ndiswrapper + win2lin(\1, \2)/p' \ + $file | sed -e 's/[ \t ]\+//' | sort -u; \ +done -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/ndis.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/ndis.c ---- linux-3.12.2/3rdparty/ndiswrapper/ndis.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/ndis.c 2013-11-28 21:42:11.000000000 +0200 +diff -Nurp linux-4.6-rc6/3rdparty/ndiswrapper/ndis.c linux-4.6-rc6-ndis/3rdparty/ndiswrapper/ndis.c +--- linux-4.6-rc6/3rdparty/ndiswrapper/ndis.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-4.6-rc6-ndis/3rdparty/ndiswrapper/ndis.c 2016-05-01 18:25:43.000000000 +0300 @@ -0,0 +1,3025 @@ +/* + * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani @@ -6688,7 +6692,7 @@ diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/ndis.c linux-3.12.2-ndiswrapper-1.5 + struct wrap_device_setting *setting; + mutex_lock(&loader_mutex); + nt_list_for_each_entry(setting, setting_list, list) { -+ if (strnicmp(keyname, setting->name, length) == 0) { ++ if (strncasecmp(keyname, setting->name, length) == 0) { + TRACE2("setting %s='%s'", keyname, setting->value); + mutex_unlock(&loader_mutex); + *param = ndis_encode_setting(setting, type); @@ -6752,7 +6756,7 @@ diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/ndis.c linux-3.12.2-ndiswrapper-1.5 + + mutex_lock(&loader_mutex); + nt_list_for_each_entry(setting, &nmb->wnd->wd->settings, list) { -+ if (strnicmp(keyname, setting->name, ansi.length) == 0) { ++ if (strncasecmp(keyname, setting->name, ansi.length) == 0) { + mutex_unlock(&loader_mutex); + if (ndis_decode_setting(setting, param)) + *status = NDIS_STATUS_FAILURE; @@ -9273,9 +9277,9 @@ diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/ndis.c linux-3.12.2-ndiswrapper-1.5 + destroy_workqueue(ndis_wq); + EXIT1(return); +} -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/ndis.h linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/ndis.h ---- linux-3.12.2/3rdparty/ndiswrapper/ndis.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/ndis.h 2013-11-28 21:42:11.000000000 +0200 +diff -Nurp linux-4.6-rc6/3rdparty/ndiswrapper/ndis.h linux-4.6-rc6-ndis/3rdparty/ndiswrapper/ndis.h +--- linux-4.6-rc6/3rdparty/ndiswrapper/ndis.h 1970-01-01 02:00:00.000000000 +0200 ++++ linux-4.6-rc6-ndis/3rdparty/ndiswrapper/ndis.h 2016-05-01 18:25:43.000000000 +0300 @@ -0,0 +1,1309 @@ +/* + * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani @@ -10586,9 +10590,9 @@ diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/ndis.h linux-3.12.2-ndiswrapper-1.5 +} + +#endif /* NDIS_H */ -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/ndiswrapper.h linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/ndiswrapper.h ---- linux-3.12.2/3rdparty/ndiswrapper/ndiswrapper.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/ndiswrapper.h 2013-11-28 21:42:11.000000000 +0200 +diff -Nurp linux-4.6-rc6/3rdparty/ndiswrapper/ndiswrapper.h linux-4.6-rc6-ndis/3rdparty/ndiswrapper/ndiswrapper.h +--- linux-4.6-rc6/3rdparty/ndiswrapper/ndiswrapper.h 1970-01-01 02:00:00.000000000 +0200 ++++ linux-4.6-rc6-ndis/3rdparty/ndiswrapper/ndiswrapper.h 2016-05-01 18:25:43.000000000 +0300 @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani @@ -10608,7 +10612,7 @@ diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/ndiswrapper.h linux-3.12.2-ndiswrap +#ifndef _NDISWRAPPER_H_ +#define _NDISWRAPPER_H_ + -+#define DRIVER_VERSION "1.59" ++#define DRIVER_VERSION "1.60" +#define UTILS_VERSION "1.9" + +#define DRIVER_NAME "ndiswrapper" @@ -10809,9 +10813,9 @@ diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/ndiswrapper.h linux-3.12.2-ndiswrap +#endif // __KERNEL__ + +#endif // NDISWRAPPER_H -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/ntoskernel.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/ntoskernel.c ---- linux-3.12.2/3rdparty/ndiswrapper/ntoskernel.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/ntoskernel.c 2013-11-28 21:42:11.000000000 +0200 +diff -Nurp linux-4.6-rc6/3rdparty/ndiswrapper/ntoskernel.c linux-4.6-rc6-ndis/3rdparty/ndiswrapper/ntoskernel.c +--- linux-4.6-rc6/3rdparty/ndiswrapper/ntoskernel.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-4.6-rc6-ndis/3rdparty/ndiswrapper/ntoskernel.c 2016-05-01 18:25:43.000000000 +0300 @@ -0,0 +1,2677 @@ +/* + * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani @@ -13490,10 +13494,10 @@ diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/ntoskernel.c linux-3.12.2-ndiswrapp + + EXIT2(return); +} -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/ntoskernel.h linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/ntoskernel.h ---- linux-3.12.2/3rdparty/ndiswrapper/ntoskernel.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/ntoskernel.h 2013-11-28 21:42:11.000000000 +0200 -@@ -0,0 +1,1083 @@ +diff -Nurp linux-4.6-rc6/3rdparty/ndiswrapper/ntoskernel.h linux-4.6-rc6-ndis/3rdparty/ndiswrapper/ntoskernel.h +--- linux-4.6-rc6/3rdparty/ndiswrapper/ntoskernel.h 1970-01-01 02:00:00.000000000 +0200 ++++ linux-4.6-rc6-ndis/3rdparty/ndiswrapper/ntoskernel.h 2016-05-01 18:25:43.000000000 +0300 +@@ -0,0 +1,1090 @@ +/* + * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani + * @@ -13843,13 +13847,21 @@ diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/ntoskernel.h linux-3.12.2-ndiswrapp +#define netdev_notifier_info_to_dev(x) ((struct net_device *)(x)) +#endif + -+#ifdef INIT_COMPLETION ++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,13,0) +static inline void reinit_completion(struct completion *x) +{ + INIT_COMPLETION(*x); +} +#endif + ++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0) ++#define prandom_seed(seed) net_srandom(seed) ++#endif ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) ++#define strncasecmp strnicmp ++#endif ++ +/* TICK is 100ns */ +#define TICKSPERSEC 10000000 +#define TICKSPERMSEC 10000 @@ -14293,9 +14305,8 @@ diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/ntoskernel.h linux-3.12.2-ndiswrapp +#define nt_spin_unlock_irqrestore(lock, flags) \ +do { \ + nt_spin_unlock(lock); \ -+ preempt_enable_no_resched(); \ ++ preempt_enable(); \ + local_irq_restore(flags); \ -+ preempt_check_resched(); \ +} while (0) + +static inline ULONG SPAN_PAGES(void *ptr, SIZE_T length) @@ -14577,9 +14588,9 @@ diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/ntoskernel.h linux-3.12.2-ndiswrapp +#endif + +#endif // _NTOSKERNEL_H_ -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/ntoskernel_io.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/ntoskernel_io.c ---- linux-3.12.2/3rdparty/ndiswrapper/ntoskernel_io.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/ntoskernel_io.c 2013-11-28 21:42:11.000000000 +0200 +diff -Nurp linux-4.6-rc6/3rdparty/ndiswrapper/ntoskernel_io.c linux-4.6-rc6-ndis/3rdparty/ndiswrapper/ntoskernel_io.c +--- linux-4.6-rc6/3rdparty/ndiswrapper/ntoskernel_io.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-4.6-rc6-ndis/3rdparty/ndiswrapper/ntoskernel_io.c 2016-05-01 18:25:43.000000000 +0300 @@ -0,0 +1,1161 @@ +/* + * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani @@ -15742,9 +15753,9 @@ diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/ntoskernel_io.c linux-3.12.2-ndiswr + IOTRACE("LowLimit: 0x%lx, HighLimit: 0x%lx", *LowLimit, *HighLimit); + IOEXIT(return); +} -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/pe_linker.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/pe_linker.c ---- linux-3.12.2/3rdparty/ndiswrapper/pe_linker.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/pe_linker.c 2013-11-28 21:42:11.000000000 +0200 +diff -Nurp linux-4.6-rc6/3rdparty/ndiswrapper/pe_linker.c linux-4.6-rc6-ndis/3rdparty/ndiswrapper/pe_linker.c +--- linux-4.6-rc6/3rdparty/ndiswrapper/pe_linker.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-4.6-rc6-ndis/3rdparty/ndiswrapper/pe_linker.c 2016-05-01 18:25:43.000000000 +0300 @@ -0,0 +1,600 @@ +/* + * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani @@ -16346,9 +16357,9 @@ diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/pe_linker.c linux-3.12.2-ndiswrappe + } + return 0; +} -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/pe_linker.h linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/pe_linker.h ---- linux-3.12.2/3rdparty/ndiswrapper/pe_linker.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/pe_linker.h 2013-11-28 21:42:11.000000000 +0200 +diff -Nurp linux-4.6-rc6/3rdparty/ndiswrapper/pe_linker.h linux-4.6-rc6-ndis/3rdparty/ndiswrapper/pe_linker.h +--- linux-4.6-rc6/3rdparty/ndiswrapper/pe_linker.h 1970-01-01 02:00:00.000000000 +0200 ++++ linux-4.6-rc6-ndis/3rdparty/ndiswrapper/pe_linker.h 2016-05-01 18:25:43.000000000 +0300 @@ -0,0 +1,992 @@ +/* + * This file is an excerpt of winnt.h from WINE, which bears the @@ -17342,9 +17353,9 @@ diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/pe_linker.h linux-3.12.2-ndiswrappe + WORD Length; + CHAR NameString[ 1 ]; +} IMAGE_RESOURCE_DIRECTORY_STRING,*PIMAGE_RESOURCE_DIRECTORY_STRING; -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/pnp.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/pnp.c ---- linux-3.12.2/3rdparty/ndiswrapper/pnp.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/pnp.c 2013-11-28 21:42:11.000000000 +0200 +diff -Nurp linux-4.6-rc6/3rdparty/ndiswrapper/pnp.c linux-4.6-rc6-ndis/3rdparty/ndiswrapper/pnp.c +--- linux-4.6-rc6/3rdparty/ndiswrapper/pnp.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-4.6-rc6-ndis/3rdparty/ndiswrapper/pnp.c 2016-05-01 18:25:43.000000000 +0300 @@ -0,0 +1,697 @@ +/* + * Copyright (C) 2005 Giridhar Pemmasani @@ -18043,9 +18054,9 @@ diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/pnp.c linux-3.12.2-ndiswrapper-1.59 +} + +#endif // USB -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/pnp.h linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/pnp.h ---- linux-3.12.2/3rdparty/ndiswrapper/pnp.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/pnp.h 2013-11-28 21:42:11.000000000 +0200 +diff -Nurp linux-4.6-rc6/3rdparty/ndiswrapper/pnp.h linux-4.6-rc6-ndis/3rdparty/ndiswrapper/pnp.h +--- linux-4.6-rc6/3rdparty/ndiswrapper/pnp.h 1970-01-01 02:00:00.000000000 +0200 ++++ linux-4.6-rc6-ndis/3rdparty/ndiswrapper/pnp.h 2016-05-01 18:25:43.000000000 +0300 @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2005 Giridhar Pemmasani @@ -18083,9 +18094,9 @@ diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/pnp.h linux-3.12.2-ndiswrapper-1.59 +int wrap_pnp_resume_usb_device(struct usb_interface *intf); + +#endif -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/proc.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/proc.c ---- linux-3.12.2/3rdparty/ndiswrapper/proc.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/proc.c 2013-11-28 21:42:11.000000000 +0200 +diff -Nurp linux-4.6-rc6/3rdparty/ndiswrapper/proc.c linux-4.6-rc6-ndis/3rdparty/ndiswrapper/proc.c +--- linux-4.6-rc6/3rdparty/ndiswrapper/proc.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-4.6-rc6-ndis/3rdparty/ndiswrapper/proc.c 2016-05-01 18:25:43.000000000 +0300 @@ -0,0 +1,588 @@ +/* + * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani @@ -18675,9 +18686,9 @@ diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/proc.c linux-3.12.2-ndiswrapper-1.5 + remove_proc_entry("debug", wrap_procfs_entry); + proc_remove(wrap_procfs_entry); +} -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/rtl.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/rtl.c ---- linux-3.12.2/3rdparty/ndiswrapper/rtl.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/rtl.c 2013-11-28 21:42:11.000000000 +0200 +diff -Nurp linux-4.6-rc6/3rdparty/ndiswrapper/rtl.c linux-4.6-rc6-ndis/3rdparty/ndiswrapper/rtl.c +--- linux-4.6-rc6/3rdparty/ndiswrapper/rtl.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-4.6-rc6-ndis/3rdparty/ndiswrapper/rtl.c 2016-05-01 18:25:43.000000000 +0300 @@ -0,0 +1,715 @@ +/* + * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani @@ -19394,9 +19405,9 @@ diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/rtl.c linux-3.12.2-ndiswrapper-1.59 + TRACE1("version: %d", version); + return TRUE; +} -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/usb.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/usb.c ---- linux-3.12.2/3rdparty/ndiswrapper/usb.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/usb.c 2013-11-28 21:42:11.000000000 +0200 +diff -Nurp linux-4.6-rc6/3rdparty/ndiswrapper/usb.c linux-4.6-rc6-ndis/3rdparty/ndiswrapper/usb.c +--- linux-4.6-rc6/3rdparty/ndiswrapper/usb.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-4.6-rc6-ndis/3rdparty/ndiswrapper/usb.c 2016-05-01 18:25:43.000000000 +0300 @@ -0,0 +1,1496 @@ +/* + * Copyright (C) 2004 Jan Kiszka @@ -20894,9 +20905,9 @@ diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/usb.c linux-3.12.2-ndiswrapper-1.59 + kill_all_urbs(wd, 0); + USBEXIT(return); +} -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/usb.h linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/usb.h ---- linux-3.12.2/3rdparty/ndiswrapper/usb.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/usb.h 2013-11-28 21:42:11.000000000 +0200 +diff -Nurp linux-4.6-rc6/3rdparty/ndiswrapper/usb.h linux-4.6-rc6-ndis/3rdparty/ndiswrapper/usb.h +--- linux-4.6-rc6/3rdparty/ndiswrapper/usb.h 1970-01-01 02:00:00.000000000 +0200 ++++ linux-4.6-rc6-ndis/3rdparty/ndiswrapper/usb.h 2016-05-01 18:25:43.000000000 +0300 @@ -0,0 +1,360 @@ +/* + * Copyright (C) 2004 Jan Kiszka @@ -21258,9 +21269,9 @@ diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/usb.h linux-3.12.2-ndiswrapper-1.59 + struct io_stack_location *irp_sl); + +#endif /* USB_H */ -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/win2lin_stubs.S linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/win2lin_stubs.S ---- linux-3.12.2/3rdparty/ndiswrapper/win2lin_stubs.S 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/win2lin_stubs.S 2013-11-28 21:42:11.000000000 +0200 +diff -Nurp linux-4.6-rc6/3rdparty/ndiswrapper/win2lin_stubs.S linux-4.6-rc6-ndis/3rdparty/ndiswrapper/win2lin_stubs.S +--- linux-4.6-rc6/3rdparty/ndiswrapper/win2lin_stubs.S 1970-01-01 02:00:00.000000000 +0200 ++++ linux-4.6-rc6-ndis/3rdparty/ndiswrapper/win2lin_stubs.S 2016-05-01 18:25:43.000000000 +0300 @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2005 Karl Vogel, Giridhar Pemmasani @@ -21515,9 +21526,9 @@ diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/win2lin_stubs.S linux-3.12.2-ndiswr +#include "win2lin_stubs.h" + +#endif /* CONFIG_X86_64 */ -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/winnt_types.h linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/winnt_types.h ---- linux-3.12.2/3rdparty/ndiswrapper/winnt_types.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/winnt_types.h 2013-11-28 21:42:11.000000000 +0200 +diff -Nurp linux-4.6-rc6/3rdparty/ndiswrapper/winnt_types.h linux-4.6-rc6-ndis/3rdparty/ndiswrapper/winnt_types.h +--- linux-4.6-rc6/3rdparty/ndiswrapper/winnt_types.h 1970-01-01 02:00:00.000000000 +0200 ++++ linux-4.6-rc6-ndis/3rdparty/ndiswrapper/winnt_types.h 2016-05-01 18:25:43.000000000 +0300 @@ -0,0 +1,1701 @@ +/* + * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani @@ -23220,9 +23231,9 @@ diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/winnt_types.h linux-3.12.2-ndiswrap + + +#endif /* WINNT_TYPES_H */ -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/workqueue.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/workqueue.c ---- linux-3.12.2/3rdparty/ndiswrapper/workqueue.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/workqueue.c 2013-11-28 21:42:11.000000000 +0200 +diff -Nurp linux-4.6-rc6/3rdparty/ndiswrapper/workqueue.c linux-4.6-rc6-ndis/3rdparty/ndiswrapper/workqueue.c +--- linux-4.6-rc6/3rdparty/ndiswrapper/workqueue.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-4.6-rc6-ndis/3rdparty/ndiswrapper/workqueue.c 2016-05-01 18:25:43.000000000 +0300 @@ -0,0 +1,279 @@ +/* + * Copyright (C) 2006 Giridhar Pemmasani @@ -23503,9 +23514,9 @@ diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/workqueue.c linux-3.12.2-ndiswrappe + wrap_destroy_wq_on(workq, i); + kfree(workq); +} -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/wrapmem.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/wrapmem.c ---- linux-3.12.2/3rdparty/ndiswrapper/wrapmem.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/wrapmem.c 2013-11-28 21:42:11.000000000 +0200 +diff -Nurp linux-4.6-rc6/3rdparty/ndiswrapper/wrapmem.c linux-4.6-rc6-ndis/3rdparty/ndiswrapper/wrapmem.c +--- linux-4.6-rc6/3rdparty/ndiswrapper/wrapmem.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-4.6-rc6-ndis/3rdparty/ndiswrapper/wrapmem.c 2016-05-01 18:25:43.000000000 +0300 @@ -0,0 +1,366 @@ +/* + * Copyright (C) 2006 Giridhar Pemmasani @@ -23873,9 +23884,9 @@ diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/wrapmem.c linux-3.12.2-ndiswrapper- +#endif + return; +} -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/wrapmem.h linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/wrapmem.h ---- linux-3.12.2/3rdparty/ndiswrapper/wrapmem.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/wrapmem.h 2013-11-28 21:42:11.000000000 +0200 +diff -Nurp linux-4.6-rc6/3rdparty/ndiswrapper/wrapmem.h linux-4.6-rc6-ndis/3rdparty/ndiswrapper/wrapmem.h +--- linux-4.6-rc6/3rdparty/ndiswrapper/wrapmem.h 1970-01-01 02:00:00.000000000 +0200 ++++ linux-4.6-rc6-ndis/3rdparty/ndiswrapper/wrapmem.h 2016-05-01 18:25:43.000000000 +0300 @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2006 Giridhar Pemmasani @@ -23974,9 +23985,9 @@ diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/wrapmem.h linux-3.12.2-ndiswrapper- +#endif // ALLOC_DEBUG + +#endif -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/wrapndis.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/wrapndis.c ---- linux-3.12.2/3rdparty/ndiswrapper/wrapndis.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/wrapndis.c 2013-11-28 21:42:11.000000000 +0200 +diff -Nurp linux-4.6-rc6/3rdparty/ndiswrapper/wrapndis.c linux-4.6-rc6-ndis/3rdparty/ndiswrapper/wrapndis.c +--- linux-4.6-rc6/3rdparty/ndiswrapper/wrapndis.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-4.6-rc6-ndis/3rdparty/ndiswrapper/wrapndis.c 2016-05-01 18:25:43.000000000 +0300 @@ -0,0 +1,2186 @@ +/* + * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani @@ -26164,9 +26175,9 @@ diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/wrapndis.c linux-3.12.2-ndiswrapper + if (wrapndis_wq) + destroy_workqueue(wrapndis_wq); +} -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/wrapndis.h linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/wrapndis.h ---- linux-3.12.2/3rdparty/ndiswrapper/wrapndis.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/wrapndis.h 2013-11-28 21:42:11.000000000 +0200 +diff -Nurp linux-4.6-rc6/3rdparty/ndiswrapper/wrapndis.h linux-4.6-rc6-ndis/3rdparty/ndiswrapper/wrapndis.h +--- linux-4.6-rc6/3rdparty/ndiswrapper/wrapndis.h 1970-01-01 02:00:00.000000000 +0200 ++++ linux-4.6-rc6-ndis/3rdparty/ndiswrapper/wrapndis.h 2016-05-01 18:25:43.000000000 +0300 @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani @@ -26254,9 +26265,9 @@ diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/wrapndis.h linux-3.12.2-ndiswrapper +struct iw_statistics *get_iw_stats(struct net_device *dev); + +#endif -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/wrapper.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/wrapper.c ---- linux-3.12.2/3rdparty/ndiswrapper/wrapper.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/wrapper.c 2013-11-28 21:42:11.000000000 +0200 +diff -Nurp linux-4.6-rc6/3rdparty/ndiswrapper/wrapper.c linux-4.6-rc6-ndis/3rdparty/ndiswrapper/wrapper.c +--- linux-4.6-rc6/3rdparty/ndiswrapper/wrapper.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-4.6-rc6-ndis/3rdparty/ndiswrapper/wrapper.c 2016-05-01 18:25:43.000000000 +0300 @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani @@ -26369,9 +26380,9 @@ diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/wrapper.c linux-3.12.2-ndiswrapper- + +module_init(wrapper_init); +module_exit(wrapper_exit); -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/wrapper.h linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/wrapper.h ---- linux-3.12.2/3rdparty/ndiswrapper/wrapper.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/wrapper.h 2013-11-28 21:42:11.000000000 +0200 +diff -Nurp linux-4.6-rc6/3rdparty/ndiswrapper/wrapper.h linux-4.6-rc6-ndis/3rdparty/ndiswrapper/wrapper.h +--- linux-4.6-rc6/3rdparty/ndiswrapper/wrapper.h 1970-01-01 02:00:00.000000000 +0200 ++++ linux-4.6-rc6-ndis/3rdparty/ndiswrapper/wrapper.h 2016-05-01 18:25:43.000000000 +0300 @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani diff --git a/kernel/kernel/files/patches/mageia/3rd-ndiswrapper-4.0-buildfix.patch b/kernel/kernel/files/patches/mageia/3rd-ndiswrapper-4.0-buildfix.patch deleted file mode 100644 index 52f39680..00000000 --- a/kernel/kernel/files/patches/mageia/3rd-ndiswrapper-4.0-buildfix.patch +++ /dev/null @@ -1,32 +0,0 @@ - -Adapt for strnicmp has been replaced by strncasecmp, -and removed upstream in v4.0-rc1~98: - -From af3cd13501eb04ca61d017ff4406f1cbffafdc04 Mon Sep 17 00:00:00 2001 -From: Rasmus Villemoes -Date: Thu, 12 Feb 2015 15:02:15 -0800 -Subject: lib/string.c: remove strnicmp() - - -Signed-off-by: Thomas Backlund - ---- linux/3rdparty/ndiswrapper/ndis.c.orig -+++ linux/3rdparty/ndiswrapper/ndis.c -@@ -438,7 +438,7 @@ static int read_setting(struct nt_list * - struct wrap_device_setting *setting; - mutex_lock(&loader_mutex); - nt_list_for_each_entry(setting, setting_list, list) { -- if (strnicmp(keyname, setting->name, length) == 0) { -+ if (strncasecmp(keyname, setting->name, length) == 0) { - TRACE2("setting %s='%s'", keyname, setting->value); - mutex_unlock(&loader_mutex); - *param = ndis_encode_setting(setting, type); -@@ -502,7 +502,7 @@ wstdcall void WIN_FUNC(NdisWriteConfigur - - mutex_lock(&loader_mutex); - nt_list_for_each_entry(setting, &nmb->wnd->wd->settings, list) { -- if (strnicmp(keyname, setting->name, ansi.length) == 0) { -+ if (strncasecmp(keyname, setting->name, ansi.length) == 0) { - mutex_unlock(&loader_mutex); - if (ndis_decode_setting(setting, param)) - *status = NDIS_STATUS_FAILURE; diff --git a/kernel/kernel/files/patches/mageia/3rd-ndiswrapper-4.2-buildfix.patch b/kernel/kernel/files/patches/mageia/3rd-ndiswrapper-4.2-buildfix.patch deleted file mode 100644 index fa965f2a..00000000 --- a/kernel/kernel/files/patches/mageia/3rd-ndiswrapper-4.2-buildfix.patch +++ /dev/null @@ -1,30 +0,0 @@ ---- linux/3rdparty/ndiswrapper/loader.c.orig 2015-09-30 00:51:13.000000000 +0300 -+++ linux/3rdparty/ndiswrapper/loader.c 2015-09-30 01:27:16.209213900 +0300 -@@ -21,6 +21,7 @@ - #include - #include - #include -+#include - #include - - /* ---- linux/3rdparty/ndiswrapper/ntoskernel.c.orig 2015-09-30 00:51:13.000000000 +0300 -+++ linux/3rdparty/ndiswrapper/ntoskernel.c 2015-09-30 01:29:08.011792825 +0300 -@@ -13,6 +13,7 @@ - * - */ - -+#include - #include "ntoskernel.h" - #include "ndis.h" - #include "usb.h" ---- linux/3rdparty/ndiswrapper/pe_linker.c.orig 2015-09-30 00:51:13.000000000 +0300 -+++ linux/3rdparty/ndiswrapper/pe_linker.c 2015-09-30 01:30:49.965320953 +0300 -@@ -19,6 +19,7 @@ - - #else - -+#include - #include - #include - diff --git a/kernel/kernel/files/patches/mageia/3rd-ndiswrapper-linux-3.14-buildfix.patch b/kernel/kernel/files/patches/mageia/3rd-ndiswrapper-linux-3.14-buildfix.patch deleted file mode 100644 index 6550b9f2..00000000 --- a/kernel/kernel/files/patches/mageia/3rd-ndiswrapper-linux-3.14-buildfix.patch +++ /dev/null @@ -1,33 +0,0 @@ - -Fix build with kernel-3.14 - -Signed-off-by: Thomas Backlund - - 3rdparty/ndiswrapper/crt.c | 2 +- - 3rdparty/ndiswrapper/ntoskernel.h | 3 +-- - 2 files changed, 2 insertions(+), 3 deletions(-) - ---- linux/3rdparty/ndiswrapper/crt.c.orig 2014-04-26 19:10:55.000000000 +0300 -+++ linux/3rdparty/ndiswrapper/crt.c 2014-04-26 19:25:27.766116082 +0300 -@@ -467,7 +467,7 @@ noregparm int WIN_FUNC(_win_memcmp,3) - noregparm void WIN_FUNC(_win_srand,1) - (UINT seed) - { -- net_srandom(seed); -+ prandom_seed((__force u32) seed); - } - - noregparm int WIN_FUNC(rand,0) ---- linux/3rdparty/ndiswrapper/ntoskernel.h.orig 2014-04-26 19:42:32.000000000 +0300 -+++ linux/3rdparty/ndiswrapper/ntoskernel.h 2014-04-26 19:59:13.662522942 +0300 -@@ -798,9 +798,8 @@ do { \ - #define nt_spin_unlock_irqrestore(lock, flags) \ - do { \ - nt_spin_unlock(lock); \ -- preempt_enable_no_resched(); \ -+ preempt_enable(); \ - local_irq_restore(flags); \ -- preempt_check_resched(); \ - } while (0) - - static inline ULONG SPAN_PAGES(void *ptr, SIZE_T length) diff --git a/kernel/kernel/files/patches/mageia/acpi-ACPICA-Namespace-Fix-deadlock-triggered-by-MLC-suppo.patch b/kernel/kernel/files/patches/mageia/acpi-ACPICA-Namespace-Fix-deadlock-triggered-by-MLC-suppo.patch new file mode 100644 index 00000000..7a090fcd --- /dev/null +++ b/kernel/kernel/files/patches/mageia/acpi-ACPICA-Namespace-Fix-deadlock-triggered-by-MLC-suppo.patch @@ -0,0 +1,98 @@ +From 2f38b1b16d9280689e5cfa47a4c50956bf437f0d Mon Sep 17 00:00:00 2001 +From: Lv Zheng +Date: Tue, 21 Jun 2016 12:34:15 +0800 +Subject: [PATCH] ACPICA: Namespace: Fix deadlock triggered by MLC support in + dynamic table loading + +The new module-level code (MLC) approach invokes MLC on the per-table +basis, but the dynamic loading support of this is incorrect because +of the lock order: + + acpi_ns_evaluate + acpi_ex_enter_intperter + acpi_ns_load_table (triggered by Load opcode) + acpi_ns_exec_module_code_list + acpi_ex_enter_intperter + +The regression is introduced by the following commit: + + Commit: 2785ce8d0da1cac9d8f78615e116cf929e9a9123 + ACPICA Commit: 071eff738c59eda1792ac24b3b688b61691d7e7c + Subject: ACPICA: Add per-table execution of module-level code + +This patch fixes this regression by unlocking the interpreter lock +before invoking MLC. However, the unlocking is done to the +acpi_ns_load_table(), in which the interpreter lock should be locked +by acpi_ns_parse_table() but it wasn't. + +Fixes: 2785ce8d0da1 (ACPICA: Add per-table execution of module-level code) +Reported-by: Mika Westerberg +Tested-by: Mika Westerberg +Signed-off-by: Lv Zheng +Cc: 4.5+ # 4.5+ +[ rjw : Subject ] +Signed-off-by: Rafael J. Wysocki +--- + drivers/acpi/acpica/exconfig.c | 2 ++ + drivers/acpi/acpica/nsparse.c | 9 +++++++-- + 2 files changed, 9 insertions(+), 2 deletions(-) + +diff --git a/drivers/acpi/acpica/exconfig.c b/drivers/acpi/acpica/exconfig.c +index a1d177d..21932d6 100644 +--- a/drivers/acpi/acpica/exconfig.c ++++ b/drivers/acpi/acpica/exconfig.c +@@ -108,7 +108,9 @@ acpi_ex_add_table(u32 table_index, + + /* Add the table to the namespace */ + ++ acpi_ex_exit_interpreter(); + status = acpi_ns_load_table(table_index, parent_node); ++ acpi_ex_enter_interpreter(); + if (ACPI_FAILURE(status)) { + acpi_ut_remove_reference(obj_desc); + *ddb_handle = NULL; +diff --git a/drivers/acpi/acpica/nsparse.c b/drivers/acpi/acpica/nsparse.c +index f631a47..1783cd7 100644 +--- a/drivers/acpi/acpica/nsparse.c ++++ b/drivers/acpi/acpica/nsparse.c +@@ -47,6 +47,7 @@ + #include "acparser.h" + #include "acdispat.h" + #include "actables.h" ++#include "acinterp.h" + + #define _COMPONENT ACPI_NAMESPACE + ACPI_MODULE_NAME("nsparse") +@@ -170,6 +171,8 @@ acpi_ns_parse_table(u32 table_index, struct acpi_namespace_node *start_node) + + ACPI_FUNCTION_TRACE(ns_parse_table); + ++ acpi_ex_enter_interpreter(); ++ + /* + * AML Parse, pass 1 + * +@@ -185,7 +188,7 @@ acpi_ns_parse_table(u32 table_index, struct acpi_namespace_node *start_node) + status = acpi_ns_one_complete_parse(ACPI_IMODE_LOAD_PASS1, + table_index, start_node); + if (ACPI_FAILURE(status)) { +- return_ACPI_STATUS(status); ++ goto error_exit; + } + + /* +@@ -201,8 +204,10 @@ acpi_ns_parse_table(u32 table_index, struct acpi_namespace_node *start_node) + status = acpi_ns_one_complete_parse(ACPI_IMODE_LOAD_PASS2, + table_index, start_node); + if (ACPI_FAILURE(status)) { +- return_ACPI_STATUS(status); ++ goto error_exit; + } + ++error_exit: ++ acpi_ex_exit_interpreter(); + return_ACPI_STATUS(status); + } +-- +2.9.0 + diff --git a/kernel/kernel/files/patches/mageia/arm-0001-ARM-bcm2835-dt-Add-the-ethernet-to-the-device-trees.patch b/kernel/kernel/files/patches/mageia/arm-0001-ARM-bcm2835-dt-Add-the-ethernet-to-the-device-trees.patch new file mode 100644 index 00000000..4b18bdd9 --- /dev/null +++ b/kernel/kernel/files/patches/mageia/arm-0001-ARM-bcm2835-dt-Add-the-ethernet-to-the-device-trees.patch @@ -0,0 +1,148 @@ +From f7c8d3a509e51f4a555371ded4957b225f1818ad Mon Sep 17 00:00:00 2001 +From: Lubomir Rintel +Date: Mon, 2 May 2016 09:06:51 +0200 +Subject: [PATCH] ARM: bcm2835: dt: Add the ethernet to the device trees + +The hub and the ethernet in its port 1 are hardwired on the board. + +Compared to the adapters that can be plugged into the USB ports, this +one has no serial EEPROM to store its MAC. Nevertheless, the Raspberry Pi +has the MAC address for this adapter in its ROM, accessible from its +firmware. + +U-Boot can read out the address and set the local-mac-address property of the +node with "ethernet" alias. Let's add the node so that U-Boot can do its +business. + +Model B rev2 and Model B+ entries were verified by me, the hierarchy and +pid/vid pair for the Version 2 was provided by Peter Chen. Original +Model B is a blind shot, though very likely correct. + +Signed-off-by: Lubomir Rintel +Acked-by: Stephen Warren +--- + arch/arm/boot/dts/bcm2835-rpi-b-plus.dts | 1 + + arch/arm/boot/dts/bcm2835-rpi-b-rev2.dts | 1 + + arch/arm/boot/dts/bcm2835-rpi-b.dts | 1 + + arch/arm/boot/dts/bcm2836-rpi-2-b.dts | 1 + + arch/arm/boot/dts/bcm283x-rpi-smsc9512.dtsi | 19 +++++++++++++++++++ + arch/arm/boot/dts/bcm283x-rpi-smsc9514.dtsi | 19 +++++++++++++++++++ + arch/arm/boot/dts/bcm283x.dtsi | 2 ++ + 7 files changed, 44 insertions(+) + create mode 100644 arch/arm/boot/dts/bcm283x-rpi-smsc9512.dtsi + create mode 100644 arch/arm/boot/dts/bcm283x-rpi-smsc9514.dtsi + +diff --git a/arch/arm/boot/dts/bcm2835-rpi-b-plus.dts b/arch/arm/boot/dts/bcm2835-rpi-b-plus.dts +index ef54050..c3e01ce 100644 +--- a/arch/arm/boot/dts/bcm2835-rpi-b-plus.dts ++++ b/arch/arm/boot/dts/bcm2835-rpi-b-plus.dts +@@ -1,6 +1,7 @@ + /dts-v1/; + #include "bcm2835.dtsi" + #include "bcm2835-rpi.dtsi" ++#include "bcm283x-rpi-smsc9514.dtsi" + + / { + compatible = "raspberrypi,model-b-plus", "brcm,bcm2835"; +diff --git a/arch/arm/boot/dts/bcm2835-rpi-b-rev2.dts b/arch/arm/boot/dts/bcm2835-rpi-b-rev2.dts +index 86f1f2f..364086a 100644 +--- a/arch/arm/boot/dts/bcm2835-rpi-b-rev2.dts ++++ b/arch/arm/boot/dts/bcm2835-rpi-b-rev2.dts +@@ -1,6 +1,7 @@ + /dts-v1/; + #include "bcm2835.dtsi" + #include "bcm2835-rpi.dtsi" ++#include "bcm283x-rpi-smsc9512.dtsi" + + / { + compatible = "raspberrypi,model-b-rev2", "brcm,bcm2835"; +diff --git a/arch/arm/boot/dts/bcm2835-rpi-b.dts b/arch/arm/boot/dts/bcm2835-rpi-b.dts +index 4859e9d..4420b09 100644 +--- a/arch/arm/boot/dts/bcm2835-rpi-b.dts ++++ b/arch/arm/boot/dts/bcm2835-rpi-b.dts +@@ -1,6 +1,7 @@ + /dts-v1/; + #include "bcm2835.dtsi" + #include "bcm2835-rpi.dtsi" ++#include "bcm283x-rpi-smsc9512.dtsi" + + / { + compatible = "raspberrypi,model-b", "brcm,bcm2835"; +diff --git a/arch/arm/boot/dts/bcm2836-rpi-2-b.dts b/arch/arm/boot/dts/bcm2836-rpi-2-b.dts +index ff94666..b975f29 100644 +--- a/arch/arm/boot/dts/bcm2836-rpi-2-b.dts ++++ b/arch/arm/boot/dts/bcm2836-rpi-2-b.dts +@@ -1,6 +1,7 @@ + /dts-v1/; + #include "bcm2836.dtsi" + #include "bcm2835-rpi.dtsi" ++#include "bcm283x-rpi-smsc9514.dtsi" + + / { + compatible = "raspberrypi,2-model-b", "brcm,bcm2836"; +diff --git a/arch/arm/boot/dts/bcm283x-rpi-smsc9512.dtsi b/arch/arm/boot/dts/bcm283x-rpi-smsc9512.dtsi +new file mode 100644 +index 0000000..12c981e +--- /dev/null ++++ b/arch/arm/boot/dts/bcm283x-rpi-smsc9512.dtsi +@@ -0,0 +1,19 @@ ++/ { ++ aliases { ++ ethernet = ðernet; ++ }; ++}; ++ ++&usb { ++ usb1@1 { ++ compatible = "usb424,9512"; ++ reg = <1>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ ethernet: usbether@1 { ++ compatible = "usb424,ec00"; ++ reg = <1>; ++ }; ++ }; ++}; +diff --git a/arch/arm/boot/dts/bcm283x-rpi-smsc9514.dtsi b/arch/arm/boot/dts/bcm283x-rpi-smsc9514.dtsi +new file mode 100644 +index 0000000..3f0a56e +--- /dev/null ++++ b/arch/arm/boot/dts/bcm283x-rpi-smsc9514.dtsi +@@ -0,0 +1,19 @@ ++/ { ++ aliases { ++ ethernet = ðernet; ++ }; ++}; ++ ++&usb { ++ usb1@1 { ++ compatible = "usb424,9514"; ++ reg = <1>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ ethernet: usbether@1 { ++ compatible = "usb424,ec00"; ++ reg = <1>; ++ }; ++ }; ++}; +diff --git a/arch/arm/boot/dts/bcm283x.dtsi b/arch/arm/boot/dts/bcm283x.dtsi +index 8aaf193..04504b7 100644 +--- a/arch/arm/boot/dts/bcm283x.dtsi ++++ b/arch/arm/boot/dts/bcm283x.dtsi +@@ -230,6 +230,8 @@ + compatible = "brcm,bcm2835-usb"; + reg = <0x7e980000 0x10000>; + interrupts = <1 9>; ++ #address-cells = <1>; ++ #size-cells = <0>; + }; + }; + +-- +2.8.1 + diff --git a/kernel/kernel/files/patches/mageia/arm-0001-dt-bindings-Add-root-properties-for-Raspberry-Pi-2.patch b/kernel/kernel/files/patches/mageia/arm-0001-dt-bindings-Add-root-properties-for-Raspberry-Pi-2.patch deleted file mode 100644 index 4ffdb909..00000000 --- a/kernel/kernel/files/patches/mageia/arm-0001-dt-bindings-Add-root-properties-for-Raspberry-Pi-2.patch +++ /dev/null @@ -1,29 +0,0 @@ -From 57e5c6d95b2cde884634586d833b02f54ba1c79d Mon Sep 17 00:00:00 2001 -From: Eric Anholt -Date: Tue, 21 Apr 2015 09:42:21 -0700 -Subject: [PATCH 1/3] dt-bindings: Add root properties for Raspberry Pi 2 - -Signed-off-by: Eric Anholt -Acked-by: Rob Herring ---- - Documentation/devicetree/bindings/arm/bcm/brcm,bcm2835.txt | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/Documentation/devicetree/bindings/arm/bcm/brcm,bcm2835.txt b/Documentation/devicetree/bindings/arm/bcm/brcm,bcm2835.txt -index c78576b..11d3056 100644 ---- a/Documentation/devicetree/bindings/arm/bcm/brcm,bcm2835.txt -+++ b/Documentation/devicetree/bindings/arm/bcm/brcm,bcm2835.txt -@@ -26,6 +26,10 @@ Raspberry Pi Model B+ - Required root node properties: - compatible = "raspberrypi,model-b-plus", "brcm,bcm2835"; - -+Raspberry Pi 2 Model B -+Required root node properties: -+compatible = "raspberrypi,2-model-b", "brcm,bcm2836"; -+ - Raspberry Pi Compute Module - Required root node properties: - compatible = "raspberrypi,compute-module", "brcm,bcm2835"; --- -2.3.10 - diff --git a/kernel/kernel/files/patches/mageia/arm-0002-ARM-bcm2835-Add-a-compat-string-for-bcm2836-machine-.patch b/kernel/kernel/files/patches/mageia/arm-0002-ARM-bcm2835-Add-a-compat-string-for-bcm2836-machine-.patch deleted file mode 100644 index 8504dde0..00000000 --- a/kernel/kernel/files/patches/mageia/arm-0002-ARM-bcm2835-Add-a-compat-string-for-bcm2836-machine-.patch +++ /dev/null @@ -1,34 +0,0 @@ -From c1be3c1fc6178ca48750b4e66f1acb7c22b64997 Mon Sep 17 00:00:00 2001 -From: Eric Anholt -Date: Wed, 16 Dec 2015 15:55:14 -0800 -Subject: [PATCH 2/3] ARM: bcm2835: Add a compat string for bcm2836 machine - probe - -Supporting the 2836 requires using the new interrupt controller, which -we have support for. - -Signed-off-by: Eric Anholt ---- - arch/arm/mach-bcm/board_bcm2835.c | 5 +++++ - 1 file changed, 5 insertions(+) - -diff --git a/arch/arm/mach-bcm/board_bcm2835.c b/arch/arm/mach-bcm/board_bcm2835.c -index 0f7b9ea..834d676 100644 ---- a/arch/arm/mach-bcm/board_bcm2835.c -+++ b/arch/arm/mach-bcm/board_bcm2835.c -@@ -36,7 +36,12 @@ static void __init bcm2835_init(void) - } - - static const char * const bcm2835_compat[] = { -+#ifdef CONFIG_ARCH_MULTI_V6 - "brcm,bcm2835", -+#endif -+#ifdef CONFIG_ARCH_MULTI_V7 -+ "brcm,bcm2836", -+#endif - NULL - }; - --- -2.3.10 - diff --git a/kernel/kernel/files/patches/mageia/arm-0003-ARM-bcm2835-Add-Kconfig-support-for-bcm2836.patch b/kernel/kernel/files/patches/mageia/arm-0003-ARM-bcm2835-Add-Kconfig-support-for-bcm2836.patch deleted file mode 100644 index 24b15a66..00000000 --- a/kernel/kernel/files/patches/mageia/arm-0003-ARM-bcm2835-Add-Kconfig-support-for-bcm2836.patch +++ /dev/null @@ -1,79 +0,0 @@ -From 5234c34e4cd7695647ccc1cabb50c3e7720dd3fb Mon Sep 17 00:00:00 2001 -From: Eric Anholt -Date: Tue, 24 Feb 2015 15:07:55 +0000 -Subject: [PATCH 3/3] ARM: bcm2835: Add Kconfig support for bcm2836 - -This should be a complete port of bcm2835 functionality to bcm2836 -(Raspberry Pi 2). - -Signed-off-by: Eric Anholt ---- - arch/arm/Kconfig.debug | 10 ++++++++-- - arch/arm/mach-bcm/Kconfig | 9 +++++---- - 2 files changed, 13 insertions(+), 6 deletions(-) - -diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug -index 259c0ca..957b876 100644 ---- a/arch/arm/Kconfig.debug -+++ b/arch/arm/Kconfig.debug -@@ -143,7 +143,12 @@ choice - - config DEBUG_BCM2835 - bool "Kernel low-level debugging on BCM2835 PL011 UART" -- depends on ARCH_BCM2835 -+ depends on ARCH_BCM2835 && ARCH_MULTI_V6 -+ select DEBUG_UART_PL01X -+ -+ config DEBUG_BCM2836 -+ bool "Kernel low-level debugging on BCM2836 PL011 UART" -+ depends on ARCH_BCM2835 && ARCH_MULTI_V7 - select DEBUG_UART_PL01X - - config DEBUG_BCM_5301X -@@ -1402,6 +1407,7 @@ config DEBUG_UART_PHYS - default 0x20064000 if DEBUG_RK29_UART1 || DEBUG_RK3X_UART2 - default 0x20068000 if DEBUG_RK29_UART2 || DEBUG_RK3X_UART3 - default 0x20201000 if DEBUG_BCM2835 -+ default 0x3f201000 if DEBUG_BCM2836 - default 0x3e000000 if DEBUG_BCM_KONA_UART - default 0x4000e400 if DEBUG_LL_UART_EFM32 - default 0x40081000 if DEBUG_LPC18XX_UART0 -@@ -1485,7 +1491,7 @@ config DEBUG_UART_VIRT - default 0xf0000be0 if ARCH_EBSA110 - default 0xf0010000 if DEBUG_ASM9260_UART - default 0xf01fb000 if DEBUG_NOMADIK_UART -- default 0xf0201000 if DEBUG_BCM2835 -+ default 0xf0201000 if DEBUG_BCM2835 || DEBUG_BCM2836 - default 0xf1000300 if DEBUG_BCM_5301X - default 0xf1002000 if DEBUG_MT8127_UART0 - default 0xf1006000 if DEBUG_MT6589_UART0 -diff --git a/arch/arm/mach-bcm/Kconfig b/arch/arm/mach-bcm/Kconfig -index 8c53c55..3b2acf4 100644 ---- a/arch/arm/mach-bcm/Kconfig -+++ b/arch/arm/mach-bcm/Kconfig -@@ -122,17 +122,18 @@ config ARCH_BCM_MOBILE_SMP - comment "Other Architectures" - - config ARCH_BCM2835 -- bool "Broadcom BCM2835 family" if ARCH_MULTI_V6 -+ bool "Broadcom BCM2835 family" if ARCH_MULTI_V6 || ARCH_MULTI_V7 - select ARCH_REQUIRE_GPIOLIB - select ARM_AMBA -- select ARM_ERRATA_411920 -+ select ARM_ERRATA_411920 if ARCH_MULTI_V6 - select ARM_TIMER_SP804 -+ select HAVE_ARM_ARCH_TIMER if ARCH_MULTI_V7 - select CLKSRC_OF - select PINCTRL - select PINCTRL_BCM2835 - help -- This enables support for the Broadcom BCM2835 SoC. This SoC is -- used in the Raspberry Pi and Roku 2 devices. -+ This enables support for the Broadcom BCM2835 and BCM2836 SoCs. -+ This SoC is used in the Raspberry Pi and Roku 2 devices. - - config ARCH_BCM_63XX - bool "Broadcom BCM63xx DSL SoC" if ARCH_MULTI_V7 --- -2.3.10 - diff --git a/kernel/kernel/files/patches/mageia/arm-0011-ARM-bcm2835-Define-two-new-packets-from-the-latest-f.patch b/kernel/kernel/files/patches/mageia/arm-0011-ARM-bcm2835-Define-two-new-packets-from-the-latest-f.patch deleted file mode 100644 index b1035384..00000000 --- a/kernel/kernel/files/patches/mageia/arm-0011-ARM-bcm2835-Define-two-new-packets-from-the-latest-f.patch +++ /dev/null @@ -1,36 +0,0 @@ -From 60d56333e869be6ad6926cdba3ba974512b2183b Mon Sep 17 00:00:00 2001 -From: Eric Anholt -Date: Tue, 1 Dec 2015 16:49:12 -0800 -Subject: [PATCH 1/3] ARM: bcm2835: Define two new packets from the latest - firmware. - -These packets give us direct access to the firmware's power management -code, as opposed to GET/SET_POWER_STATE packets that only had a couple -of domains implemented. - -Signed-off-by: Eric Anholt -Reviewed-by: Kevin Hilman ---- - include/soc/bcm2835/raspberrypi-firmware.h | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/include/soc/bcm2835/raspberrypi-firmware.h b/include/soc/bcm2835/raspberrypi-firmware.h -index c07d74a..3fb3571 100644 ---- a/include/soc/bcm2835/raspberrypi-firmware.h -+++ b/include/soc/bcm2835/raspberrypi-firmware.h -@@ -72,10 +72,12 @@ enum rpi_firmware_property_tag { - RPI_FIRMWARE_SET_ENABLE_QPU = 0x00030012, - RPI_FIRMWARE_GET_DISPMANX_RESOURCE_MEM_HANDLE = 0x00030014, - RPI_FIRMWARE_GET_EDID_BLOCK = 0x00030020, -+ RPI_FIRMWARE_GET_DOMAIN_STATE = 0x00030030, - RPI_FIRMWARE_SET_CLOCK_STATE = 0x00038001, - RPI_FIRMWARE_SET_CLOCK_RATE = 0x00038002, - RPI_FIRMWARE_SET_VOLTAGE = 0x00038003, - RPI_FIRMWARE_SET_TURBO = 0x00038009, -+ RPI_FIRMWARE_SET_DOMAIN_STATE = 0x00038030, - - /* Dispmanx TAGS */ - RPI_FIRMWARE_FRAMEBUFFER_ALLOCATE = 0x00040001, --- -2.3.10 - diff --git a/kernel/kernel/files/patches/mageia/arm-0012-dt-bindings-add-rpi-power-domain-driver-bindings.patch b/kernel/kernel/files/patches/mageia/arm-0012-dt-bindings-add-rpi-power-domain-driver-bindings.patch deleted file mode 100644 index eb730a35..00000000 --- a/kernel/kernel/files/patches/mageia/arm-0012-dt-bindings-add-rpi-power-domain-driver-bindings.patch +++ /dev/null @@ -1,74 +0,0 @@ -From 4c8b338f9ae38dee9c77bda023babc7f7543f52c Mon Sep 17 00:00:00 2001 -From: Alexander Aring -Date: Wed, 16 Dec 2015 16:26:48 -0800 -Subject: [PATCH 2/3] dt-bindings: add rpi power domain driver bindings - -This patch adds devicetree tree bindings for the Raspberry Pi power -domain driver. - -Signed-off-by: Alexander Aring -Signed-off-by: Eric Anholt -Acked-by: Rob Herring -Reviewed-by: Ulf Hansson -Reviewed-by: Kevin Hilman ---- - .../bindings/soc/bcm/raspberrypi,bcm2835-power.txt | 47 ++++++++++++++++++++++ - 1 file changed, 47 insertions(+) - create mode 100644 Documentation/devicetree/bindings/soc/bcm/raspberrypi,bcm2835-power.txt - -diff --git a/Documentation/devicetree/bindings/soc/bcm/raspberrypi,bcm2835-power.txt b/Documentation/devicetree/bindings/soc/bcm/raspberrypi,bcm2835-power.txt -new file mode 100644 -index 0000000..30942cf ---- /dev/null -+++ b/Documentation/devicetree/bindings/soc/bcm/raspberrypi,bcm2835-power.txt -@@ -0,0 +1,47 @@ -+Raspberry Pi power domain driver -+ -+Required properties: -+ -+- compatible: Should be "raspberrypi,bcm2835-power". -+- firmware: Reference to the RPi firmware device node. -+- #power-domain-cells: Should be <1>, we providing multiple power domains. -+ -+The valid defines for power domain are: -+ -+ RPI_POWER_DOMAIN_I2C0 -+ RPI_POWER_DOMAIN_I2C1 -+ RPI_POWER_DOMAIN_I2C2 -+ RPI_POWER_DOMAIN_VIDEO_SCALER -+ RPI_POWER_DOMAIN_VPU1 -+ RPI_POWER_DOMAIN_HDMI -+ RPI_POWER_DOMAIN_USB -+ RPI_POWER_DOMAIN_VEC -+ RPI_POWER_DOMAIN_JPEG -+ RPI_POWER_DOMAIN_H264 -+ RPI_POWER_DOMAIN_V3D -+ RPI_POWER_DOMAIN_ISP -+ RPI_POWER_DOMAIN_UNICAM0 -+ RPI_POWER_DOMAIN_UNICAM1 -+ RPI_POWER_DOMAIN_CCP2RX -+ RPI_POWER_DOMAIN_CSI2 -+ RPI_POWER_DOMAIN_CPI -+ RPI_POWER_DOMAIN_DSI0 -+ RPI_POWER_DOMAIN_DSI1 -+ RPI_POWER_DOMAIN_TRANSPOSER -+ RPI_POWER_DOMAIN_CCP2TX -+ RPI_POWER_DOMAIN_CDP -+ RPI_POWER_DOMAIN_ARM -+ -+Example: -+ -+power: power { -+ compatible = "raspberrypi,bcm2835-power"; -+ firmware = <&firmware>; -+ #power-domain-cells = <1>; -+}; -+ -+Example for using power domain: -+ -+&usb { -+ power-domains = <&power RPI_POWER_DOMAIN_USB>; -+}; --- -2.3.10 - diff --git a/kernel/kernel/files/patches/mageia/arm-0013-ARM-bcm2835-add-rpi-power-domain-driver.patch b/kernel/kernel/files/patches/mageia/arm-0013-ARM-bcm2835-add-rpi-power-domain-driver.patch deleted file mode 100644 index 5f250719..00000000 --- a/kernel/kernel/files/patches/mageia/arm-0013-ARM-bcm2835-add-rpi-power-domain-driver.patch +++ /dev/null @@ -1,379 +0,0 @@ -From a09cd356586d33f64cbe64ee4f5c1a7c4a6abee5 Mon Sep 17 00:00:00 2001 -From: Alexander Aring -Date: Wed, 16 Dec 2015 16:26:47 -0800 -Subject: [PATCH 3/3] ARM: bcm2835: add rpi power domain driver - -This patch adds support for several power domains on Raspberry Pi, -including USB (so it can be enabled even if the bootloader didn't do -it), and graphics. - -This patch is the combined work of Eric Anholt (who wrote USB support -inside of the Raspberry Pi firmware driver, and wrote the non-USB -domain support) and Alexander Aring (who separated the original USB -work out from the firmware driver). - -Signed-off-by: Alexander Aring -Signed-off-by: Eric Anholt -Reviewed-by: Ulf Hansson -Reviewed-by: Kevin Hilman ---- - drivers/soc/Kconfig | 1 + - drivers/soc/Makefile | 1 + - drivers/soc/bcm/Kconfig | 9 + - drivers/soc/bcm/Makefile | 1 + - drivers/soc/bcm/raspberrypi-power.c | 247 ++++++++++++++++++++++++++ - include/dt-bindings/power/raspberrypi-power.h | 41 +++++ - 6 files changed, 300 insertions(+) - create mode 100644 drivers/soc/bcm/Kconfig - create mode 100644 drivers/soc/bcm/Makefile - create mode 100644 drivers/soc/bcm/raspberrypi-power.c - create mode 100644 include/dt-bindings/power/raspberrypi-power.h - -diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig -index 4e853ed..8441426 100644 ---- a/drivers/soc/Kconfig -+++ b/drivers/soc/Kconfig -@@ -1,5 +1,6 @@ - menu "SOC (System On Chip) specific Drivers" - -+source "drivers/soc/bcm/Kconfig" - source "drivers/soc/brcmstb/Kconfig" - source "drivers/soc/mediatek/Kconfig" - source "drivers/soc/qcom/Kconfig" -diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile -index f2ba2e9..f3f955c 100644 ---- a/drivers/soc/Makefile -+++ b/drivers/soc/Makefile -@@ -2,6 +2,7 @@ - # Makefile for the Linux Kernel SOC specific device drivers. - # - -+obj-y += bcm/ - obj-$(CONFIG_SOC_BRCMSTB) += brcmstb/ - obj-$(CONFIG_MACH_DOVE) += dove/ - obj-$(CONFIG_ARCH_MEDIATEK) += mediatek/ -diff --git a/drivers/soc/bcm/Kconfig b/drivers/soc/bcm/Kconfig -new file mode 100644 -index 0000000..5ba1827 ---- /dev/null -+++ b/drivers/soc/bcm/Kconfig -@@ -0,0 +1,9 @@ -+config RASPBERRYPI_POWER -+ bool "Raspberry Pi power domain driver" -+ depends on ARCH_BCM2835 || COMPILE_TEST -+ depends on RASPBERRYPI_FIRMWARE -+ select PM_GENERIC_DOMAINS if PM -+ select PM_GENERIC_DOMAINS_OF if PM -+ help -+ This enables support for the RPi power domains which can be enabled -+ or disabled via the RPi firmware. -diff --git a/drivers/soc/bcm/Makefile b/drivers/soc/bcm/Makefile -new file mode 100644 -index 0000000..63aa3eb ---- /dev/null -+++ b/drivers/soc/bcm/Makefile -@@ -0,0 +1 @@ -+obj-$(CONFIG_RASPBERRYPI_POWER) += raspberrypi-power.o -diff --git a/drivers/soc/bcm/raspberrypi-power.c b/drivers/soc/bcm/raspberrypi-power.c -new file mode 100644 -index 0000000..fe96a8b ---- /dev/null -+++ b/drivers/soc/bcm/raspberrypi-power.c -@@ -0,0 +1,247 @@ -+/* (C) 2015 Pengutronix, Alexander Aring -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ * -+ * Authors: -+ * Alexander Aring -+ * Eric Anholt -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* -+ * Firmware indices for the old power domains interface. Only a few -+ * of them were actually implemented. -+ */ -+#define RPI_OLD_POWER_DOMAIN_USB 3 -+#define RPI_OLD_POWER_DOMAIN_V3D 10 -+ -+struct rpi_power_domain { -+ u32 domain; -+ bool enabled; -+ bool old_interface; -+ struct generic_pm_domain base; -+ struct rpi_firmware *fw; -+}; -+ -+struct rpi_power_domains { -+ bool has_new_interface; -+ struct genpd_onecell_data xlate; -+ struct rpi_firmware *fw; -+ struct rpi_power_domain domains[RPI_POWER_DOMAIN_COUNT]; -+}; -+ -+/* -+ * Packet definition used by RPI_FIRMWARE_SET_POWER_STATE and -+ * RPI_FIRMWARE_SET_DOMAIN_STATE -+ */ -+struct rpi_power_domain_packet { -+ u32 domain; -+ u32 on; -+} __packet; -+ -+/* -+ * Asks the firmware to enable or disable power on a specific power -+ * domain. -+ */ -+static int rpi_firmware_set_power(struct rpi_power_domain *rpi_domain, bool on) -+{ -+ struct rpi_power_domain_packet packet; -+ -+ packet.domain = rpi_domain->domain; -+ packet.on = on; -+ return rpi_firmware_property(rpi_domain->fw, -+ rpi_domain->old_interface ? -+ RPI_FIRMWARE_SET_POWER_STATE : -+ RPI_FIRMWARE_SET_DOMAIN_STATE, -+ &packet, sizeof(packet)); -+} -+ -+static int rpi_domain_off(struct generic_pm_domain *domain) -+{ -+ struct rpi_power_domain *rpi_domain = -+ container_of(domain, struct rpi_power_domain, base); -+ -+ return rpi_firmware_set_power(rpi_domain, false); -+} -+ -+static int rpi_domain_on(struct generic_pm_domain *domain) -+{ -+ struct rpi_power_domain *rpi_domain = -+ container_of(domain, struct rpi_power_domain, base); -+ -+ return rpi_firmware_set_power(rpi_domain, true); -+} -+ -+static void rpi_common_init_power_domain(struct rpi_power_domains *rpi_domains, -+ int xlate_index, const char *name) -+{ -+ struct rpi_power_domain *dom = &rpi_domains->domains[xlate_index]; -+ -+ dom->fw = rpi_domains->fw; -+ -+ dom->base.name = name; -+ dom->base.power_on = rpi_domain_on; -+ dom->base.power_off = rpi_domain_off; -+ -+ /* -+ * Treat all power domains as off at boot. -+ * -+ * The firmware itself may be keeping some domains on, but -+ * from Linux's perspective all we control is the refcounts -+ * that we give to the firmware, and we can't ask the firmware -+ * to turn off something that we haven't ourselves turned on. -+ */ -+ pm_genpd_init(&dom->base, NULL, true); -+ -+ rpi_domains->xlate.domains[xlate_index] = &dom->base; -+} -+ -+static void rpi_init_power_domain(struct rpi_power_domains *rpi_domains, -+ int xlate_index, const char *name) -+{ -+ struct rpi_power_domain *dom = &rpi_domains->domains[xlate_index]; -+ -+ if (!rpi_domains->has_new_interface) -+ return; -+ -+ /* The DT binding index is the firmware's domain index minus one. */ -+ dom->domain = xlate_index + 1; -+ -+ rpi_common_init_power_domain(rpi_domains, xlate_index, name); -+} -+ -+static void rpi_init_old_power_domain(struct rpi_power_domains *rpi_domains, -+ int xlate_index, int domain, -+ const char *name) -+{ -+ struct rpi_power_domain *dom = &rpi_domains->domains[xlate_index]; -+ -+ dom->old_interface = true; -+ dom->domain = domain; -+ -+ rpi_common_init_power_domain(rpi_domains, xlate_index, name); -+} -+ -+/* -+ * Detects whether the firmware supports the new power domains interface. -+ * -+ * The firmware doesn't actually return an error on an unknown tag, -+ * and just skips over it, so we do the detection by putting an -+ * unexpected value in the return field and checking if it was -+ * unchanged. -+ */ -+static bool -+rpi_has_new_domain_support(struct rpi_power_domains *rpi_domains) -+{ -+ struct rpi_power_domain_packet packet; -+ int ret; -+ -+ packet.domain = RPI_POWER_DOMAIN_ARM; -+ packet.on = ~0; -+ -+ ret = rpi_firmware_property(rpi_domains->fw, -+ RPI_FIRMWARE_GET_DOMAIN_STATE, -+ &packet, sizeof(packet)); -+ -+ return ret == 0 && packet.on != ~0; -+} -+ -+static int rpi_power_probe(struct platform_device *pdev) -+{ -+ struct device_node *fw_np; -+ struct device *dev = &pdev->dev; -+ struct rpi_power_domains *rpi_domains; -+ -+ rpi_domains = devm_kzalloc(dev, sizeof(*rpi_domains), GFP_KERNEL); -+ if (!rpi_domains) -+ return -ENOMEM; -+ -+ rpi_domains->xlate.domains = -+ devm_kzalloc(dev, sizeof(*rpi_domains->xlate.domains) * -+ RPI_POWER_DOMAIN_COUNT, GFP_KERNEL); -+ if (!rpi_domains->xlate.domains) -+ return -ENOMEM; -+ -+ rpi_domains->xlate.num_domains = RPI_POWER_DOMAIN_COUNT; -+ -+ fw_np = of_parse_phandle(pdev->dev.of_node, "firmware", 0); -+ if (!fw_np) { -+ dev_err(&pdev->dev, "no firmware node\n"); -+ return -ENODEV; -+ } -+ -+ rpi_domains->fw = rpi_firmware_get(fw_np); -+ of_node_put(fw_np); -+ if (!rpi_domains->fw) -+ return -EPROBE_DEFER; -+ -+ rpi_domains->has_new_interface = -+ rpi_has_new_domain_support(rpi_domains); -+ -+ rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_I2C0, "I2C0"); -+ rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_I2C1, "I2C1"); -+ rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_I2C2, "I2C2"); -+ rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_VIDEO_SCALER, -+ "VIDEO_SCALER"); -+ rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_VPU1, "VPU1"); -+ rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_HDMI, "HDMI"); -+ -+ /* -+ * Use the old firmware interface for USB power, so that we -+ * can turn it on even if the firmware hasn't been updated. -+ */ -+ rpi_init_old_power_domain(rpi_domains, RPI_POWER_DOMAIN_USB, -+ RPI_OLD_POWER_DOMAIN_USB, "USB"); -+ -+ rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_VEC, "VEC"); -+ rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_JPEG, "JPEG"); -+ rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_H264, "H264"); -+ rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_V3D, "V3D"); -+ rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_ISP, "ISP"); -+ rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_UNICAM0, "UNICAM0"); -+ rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_UNICAM1, "UNICAM1"); -+ rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_CCP2RX, "CCP2RX"); -+ rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_CSI2, "CSI2"); -+ rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_CPI, "CPI"); -+ rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_DSI0, "DSI0"); -+ rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_DSI1, "DSI1"); -+ rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_TRANSPOSER, -+ "TRANSPOSER"); -+ rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_CCP2TX, "CCP2TX"); -+ rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_CDP, "CDP"); -+ rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_ARM, "ARM"); -+ -+ of_genpd_add_provider_onecell(dev->of_node, &rpi_domains->xlate); -+ -+ platform_set_drvdata(pdev, rpi_domains); -+ -+ return 0; -+} -+ -+static const struct of_device_id rpi_power_of_match[] = { -+ { .compatible = "raspberrypi,bcm2835-power", }, -+ {}, -+}; -+MODULE_DEVICE_TABLE(of, rpi_power_of_match); -+ -+static struct platform_driver rpi_power_driver = { -+ .driver = { -+ .name = "raspberrypi-power", -+ .of_match_table = rpi_power_of_match, -+ }, -+ .probe = rpi_power_probe, -+}; -+builtin_platform_driver(rpi_power_driver); -+ -+MODULE_AUTHOR("Alexander Aring "); -+MODULE_AUTHOR("Eric Anholt "); -+MODULE_DESCRIPTION("Raspberry Pi power domain driver"); -+MODULE_LICENSE("GPL v2"); -diff --git a/include/dt-bindings/power/raspberrypi-power.h b/include/dt-bindings/power/raspberrypi-power.h -new file mode 100644 -index 0000000..b3ff8e0 ---- /dev/null -+++ b/include/dt-bindings/power/raspberrypi-power.h -@@ -0,0 +1,41 @@ -+/* -+ * Copyright © 2015 Broadcom -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#ifndef _DT_BINDINGS_ARM_BCM2835_RPI_POWER_H -+#define _DT_BINDINGS_ARM_BCM2835_RPI_POWER_H -+ -+/* These power domain indices are the firmware interface's indices -+ * minus one. -+ */ -+#define RPI_POWER_DOMAIN_I2C0 0 -+#define RPI_POWER_DOMAIN_I2C1 1 -+#define RPI_POWER_DOMAIN_I2C2 2 -+#define RPI_POWER_DOMAIN_VIDEO_SCALER 3 -+#define RPI_POWER_DOMAIN_VPU1 4 -+#define RPI_POWER_DOMAIN_HDMI 5 -+#define RPI_POWER_DOMAIN_USB 6 -+#define RPI_POWER_DOMAIN_VEC 7 -+#define RPI_POWER_DOMAIN_JPEG 8 -+#define RPI_POWER_DOMAIN_H264 9 -+#define RPI_POWER_DOMAIN_V3D 10 -+#define RPI_POWER_DOMAIN_ISP 11 -+#define RPI_POWER_DOMAIN_UNICAM0 12 -+#define RPI_POWER_DOMAIN_UNICAM1 13 -+#define RPI_POWER_DOMAIN_CCP2RX 14 -+#define RPI_POWER_DOMAIN_CSI2 15 -+#define RPI_POWER_DOMAIN_CPI 16 -+#define RPI_POWER_DOMAIN_DSI0 17 -+#define RPI_POWER_DOMAIN_DSI1 18 -+#define RPI_POWER_DOMAIN_TRANSPOSER 19 -+#define RPI_POWER_DOMAIN_CCP2TX 20 -+#define RPI_POWER_DOMAIN_CDP 21 -+#define RPI_POWER_DOMAIN_ARM 22 -+ -+#define RPI_POWER_DOMAIN_COUNT 23 -+ -+#endif /* _DT_BINDINGS_ARM_BCM2835_RPI_POWER_H */ --- -2.3.10 - diff --git a/kernel/kernel/files/patches/mageia/arm-0021-ARM-bcm2835-Split-the-DT-for-peripherals-from-the-DT.patch b/kernel/kernel/files/patches/mageia/arm-0021-ARM-bcm2835-Split-the-DT-for-peripherals-from-the-DT.patch deleted file mode 100644 index 2111be1e..00000000 --- a/kernel/kernel/files/patches/mageia/arm-0021-ARM-bcm2835-Split-the-DT-for-peripherals-from-the-DT.patch +++ /dev/null @@ -1,443 +0,0 @@ -From 482626063d446eac1809e025a79ad0a7d45bc22d Mon Sep 17 00:00:00 2001 -From: Eric Anholt -Date: Wed, 16 Dec 2015 13:24:40 -0800 -Subject: [PATCH 1/4] ARM: bcm2835: Split the DT for peripherals from the DT - for the CPU - -The set of peripherals remained constant across bcm2835 (Raspberry Pi -1) and bcm2836 (Raspberry Pi 2), but the CPU was swapped out. Split -the files so that we can include just peripheral setup in 2836. - -Signed-off-by: Eric Anholt ---- - arch/arm/boot/dts/bcm2835.dtsi | 194 +------------------------------------- - arch/arm/boot/dts/bcm283x.dtsi | 205 +++++++++++++++++++++++++++++++++++++++++ - 2 files changed, 206 insertions(+), 193 deletions(-) - create mode 100644 arch/arm/boot/dts/bcm283x.dtsi - -diff --git a/arch/arm/boot/dts/bcm2835.dtsi b/arch/arm/boot/dts/bcm2835.dtsi -index aef64de..b83b326 100644 ---- a/arch/arm/boot/dts/bcm2835.dtsi -+++ b/arch/arm/boot/dts/bcm2835.dtsi -@@ -1,206 +1,14 @@ --#include --#include --#include "skeleton.dtsi" -+#include "bcm283x.dtsi" - - / { - compatible = "brcm,bcm2835"; -- model = "BCM2835"; -- interrupt-parent = <&intc>; -- -- chosen { -- bootargs = "earlyprintk console=ttyAMA0"; -- }; - - soc { -- compatible = "simple-bus"; -- #address-cells = <1>; -- #size-cells = <1>; - ranges = <0x7e000000 0x20000000 0x02000000>; - dma-ranges = <0x40000000 0x00000000 0x20000000>; - -- timer@7e003000 { -- compatible = "brcm,bcm2835-system-timer"; -- reg = <0x7e003000 0x1000>; -- interrupts = <1 0>, <1 1>, <1 2>, <1 3>; -- /* This could be a reference to BCM2835_CLOCK_TIMER, -- * but we don't have the driver using the common clock -- * support yet. -- */ -- clock-frequency = <1000000>; -- }; -- -- dma: dma@7e007000 { -- compatible = "brcm,bcm2835-dma"; -- reg = <0x7e007000 0xf00>; -- interrupts = <1 16>, -- <1 17>, -- <1 18>, -- <1 19>, -- <1 20>, -- <1 21>, -- <1 22>, -- <1 23>, -- <1 24>, -- <1 25>, -- <1 26>, -- <1 27>, -- <1 28>; -- -- #dma-cells = <1>; -- brcm,dma-channel-mask = <0x7f35>; -- }; -- -- intc: interrupt-controller@7e00b200 { -- compatible = "brcm,bcm2835-armctrl-ic"; -- reg = <0x7e00b200 0x200>; -- interrupt-controller; -- #interrupt-cells = <2>; -- }; -- -- watchdog@7e100000 { -- compatible = "brcm,bcm2835-pm-wdt"; -- reg = <0x7e100000 0x28>; -- }; -- -- clocks: cprman@7e101000 { -- compatible = "brcm,bcm2835-cprman"; -- #clock-cells = <1>; -- reg = <0x7e101000 0x2000>; -- -- /* CPRMAN derives everything from the platform's -- * oscillator. -- */ -- clocks = <&clk_osc>; -- }; -- -- rng@7e104000 { -- compatible = "brcm,bcm2835-rng"; -- reg = <0x7e104000 0x10>; -- }; -- -- mailbox: mailbox@7e00b800 { -- compatible = "brcm,bcm2835-mbox"; -- reg = <0x7e00b880 0x40>; -- interrupts = <0 1>; -- #mbox-cells = <0>; -- }; -- -- gpio: gpio@7e200000 { -- compatible = "brcm,bcm2835-gpio"; -- reg = <0x7e200000 0xb4>; -- /* -- * The GPIO IP block is designed for 3 banks of GPIOs. -- * Each bank has a GPIO interrupt for itself. -- * There is an overall "any bank" interrupt. -- * In order, these are GIC interrupts 17, 18, 19, 20. -- * Since the BCM2835 only has 2 banks, the 2nd bank -- * interrupt output appears to be mirrored onto the -- * 3rd bank's interrupt signal. -- * So, a bank0 interrupt shows up on 17, 20, and -- * a bank1 interrupt shows up on 18, 19, 20! -- */ -- interrupts = <2 17>, <2 18>, <2 19>, <2 20>; -- -- gpio-controller; -- #gpio-cells = <2>; -- -- interrupt-controller; -- #interrupt-cells = <2>; -- }; -- -- uart0: uart@7e201000 { -- compatible = "brcm,bcm2835-pl011", "arm,pl011", "arm,primecell"; -- reg = <0x7e201000 0x1000>; -- interrupts = <2 25>; -- clocks = <&clocks BCM2835_CLOCK_UART>, -- <&clocks BCM2835_CLOCK_VPU>; -- clock-names = "uartclk", "apb_pclk"; -- arm,primecell-periphid = <0x00241011>; -- }; -- -- i2s: i2s@7e203000 { -- compatible = "brcm,bcm2835-i2s"; -- reg = <0x7e203000 0x20>, -- <0x7e101098 0x02>; -- -- dmas = <&dma 2>, -- <&dma 3>; -- dma-names = "tx", "rx"; -- status = "disabled"; -- }; -- -- spi: spi@7e204000 { -- compatible = "brcm,bcm2835-spi"; -- reg = <0x7e204000 0x1000>; -- interrupts = <2 22>; -- clocks = <&clocks BCM2835_CLOCK_VPU>; -- #address-cells = <1>; -- #size-cells = <0>; -- status = "disabled"; -- }; -- -- i2c0: i2c@7e205000 { -- compatible = "brcm,bcm2835-i2c"; -- reg = <0x7e205000 0x1000>; -- interrupts = <2 21>; -- clocks = <&clocks BCM2835_CLOCK_VPU>; -- #address-cells = <1>; -- #size-cells = <0>; -- status = "disabled"; -- }; -- -- sdhci: sdhci@7e300000 { -- compatible = "brcm,bcm2835-sdhci"; -- reg = <0x7e300000 0x100>; -- interrupts = <2 30>; -- clocks = <&clocks BCM2835_CLOCK_EMMC>; -- status = "disabled"; -- }; -- -- i2c1: i2c@7e804000 { -- compatible = "brcm,bcm2835-i2c"; -- reg = <0x7e804000 0x1000>; -- interrupts = <2 21>; -- clocks = <&clocks BCM2835_CLOCK_VPU>; -- #address-cells = <1>; -- #size-cells = <0>; -- status = "disabled"; -- }; -- -- i2c2: i2c@7e805000 { -- compatible = "brcm,bcm2835-i2c"; -- reg = <0x7e805000 0x1000>; -- interrupts = <2 21>; -- clocks = <&clocks BCM2835_CLOCK_VPU>; -- #address-cells = <1>; -- #size-cells = <0>; -- status = "disabled"; -- }; -- -- usb@7e980000 { -- compatible = "brcm,bcm2835-usb"; -- reg = <0x7e980000 0x10000>; -- interrupts = <1 9>; -- }; -- - arm-pmu { - compatible = "arm,arm1176-pmu"; - }; - }; -- -- clocks { -- compatible = "simple-bus"; -- #address-cells = <1>; -- #size-cells = <0>; -- -- /* The oscillator is the root of the clock tree. */ -- clk_osc: clock@3 { -- compatible = "fixed-clock"; -- reg = <3>; -- #clock-cells = <0>; -- clock-output-names = "osc"; -- clock-frequency = <19200000>; -- }; -- -- }; - }; -diff --git a/arch/arm/boot/dts/bcm283x.dtsi b/arch/arm/boot/dts/bcm283x.dtsi -new file mode 100644 -index 0000000..8a7e727 ---- /dev/null -+++ b/arch/arm/boot/dts/bcm283x.dtsi -@@ -0,0 +1,205 @@ -+#include -+#include -+#include "skeleton.dtsi" -+ -+/* This include file covers the common peripherals and configuration between -+ * bcm2835 and bcm2836 implementations, leaving the CPU configuration to -+ * bcm2835.dtsi and bcm2836.dtsi. -+ */ -+ -+/ { -+ compatible = "brcm,bcm2835"; -+ model = "BCM2835"; -+ interrupt-parent = <&intc>; -+ -+ chosen { -+ bootargs = "earlyprintk console=ttyAMA0"; -+ }; -+ -+ soc { -+ compatible = "simple-bus"; -+ #address-cells = <1>; -+ #size-cells = <1>; -+ -+ timer@7e003000 { -+ compatible = "brcm,bcm2835-system-timer"; -+ reg = <0x7e003000 0x1000>; -+ interrupts = <1 0>, <1 1>, <1 2>, <1 3>; -+ /* This could be a reference to BCM2835_CLOCK_TIMER, -+ * but we don't have the driver using the common clock -+ * support yet. -+ */ -+ clock-frequency = <1000000>; -+ }; -+ -+ dma: dma@7e007000 { -+ compatible = "brcm,bcm2835-dma"; -+ reg = <0x7e007000 0xf00>; -+ interrupts = <1 16>, -+ <1 17>, -+ <1 18>, -+ <1 19>, -+ <1 20>, -+ <1 21>, -+ <1 22>, -+ <1 23>, -+ <1 24>, -+ <1 25>, -+ <1 26>, -+ <1 27>, -+ <1 28>; -+ -+ #dma-cells = <1>; -+ brcm,dma-channel-mask = <0x7f35>; -+ }; -+ -+ intc: interrupt-controller@7e00b200 { -+ compatible = "brcm,bcm2835-armctrl-ic"; -+ reg = <0x7e00b200 0x200>; -+ interrupt-controller; -+ #interrupt-cells = <2>; -+ }; -+ -+ watchdog@7e100000 { -+ compatible = "brcm,bcm2835-pm-wdt"; -+ reg = <0x7e100000 0x28>; -+ }; -+ -+ clocks: cprman@7e101000 { -+ compatible = "brcm,bcm2835-cprman"; -+ #clock-cells = <1>; -+ reg = <0x7e101000 0x2000>; -+ -+ /* CPRMAN derives everything from the platform's -+ * oscillator. -+ */ -+ clocks = <&clk_osc>; -+ }; -+ -+ rng@7e104000 { -+ compatible = "brcm,bcm2835-rng"; -+ reg = <0x7e104000 0x10>; -+ }; -+ -+ mailbox: mailbox@7e00b800 { -+ compatible = "brcm,bcm2835-mbox"; -+ reg = <0x7e00b880 0x40>; -+ interrupts = <0 1>; -+ #mbox-cells = <0>; -+ }; -+ -+ gpio: gpio@7e200000 { -+ compatible = "brcm,bcm2835-gpio"; -+ reg = <0x7e200000 0xb4>; -+ /* -+ * The GPIO IP block is designed for 3 banks of GPIOs. -+ * Each bank has a GPIO interrupt for itself. -+ * There is an overall "any bank" interrupt. -+ * In order, these are GIC interrupts 17, 18, 19, 20. -+ * Since the BCM2835 only has 2 banks, the 2nd bank -+ * interrupt output appears to be mirrored onto the -+ * 3rd bank's interrupt signal. -+ * So, a bank0 interrupt shows up on 17, 20, and -+ * a bank1 interrupt shows up on 18, 19, 20! -+ */ -+ interrupts = <2 17>, <2 18>, <2 19>, <2 20>; -+ -+ gpio-controller; -+ #gpio-cells = <2>; -+ -+ interrupt-controller; -+ #interrupt-cells = <2>; -+ }; -+ -+ uart0: uart@7e201000 { -+ compatible = "brcm,bcm2835-pl011", "arm,pl011", "arm,primecell"; -+ reg = <0x7e201000 0x1000>; -+ interrupts = <2 25>; -+ clocks = <&clocks BCM2835_CLOCK_UART>, -+ <&clocks BCM2835_CLOCK_VPU>; -+ clock-names = "uartclk", "apb_pclk"; -+ arm,primecell-periphid = <0x00241011>; -+ }; -+ -+ i2s: i2s@7e203000 { -+ compatible = "brcm,bcm2835-i2s"; -+ reg = <0x7e203000 0x20>, -+ <0x7e101098 0x02>; -+ -+ dmas = <&dma 2>, -+ <&dma 3>; -+ dma-names = "tx", "rx"; -+ status = "disabled"; -+ }; -+ -+ spi: spi@7e204000 { -+ compatible = "brcm,bcm2835-spi"; -+ reg = <0x7e204000 0x1000>; -+ interrupts = <2 22>; -+ clocks = <&clocks BCM2835_CLOCK_VPU>; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ status = "disabled"; -+ }; -+ -+ i2c0: i2c@7e205000 { -+ compatible = "brcm,bcm2835-i2c"; -+ reg = <0x7e205000 0x1000>; -+ interrupts = <2 21>; -+ clocks = <&clocks BCM2835_CLOCK_VPU>; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ status = "disabled"; -+ }; -+ -+ sdhci: sdhci@7e300000 { -+ compatible = "brcm,bcm2835-sdhci"; -+ reg = <0x7e300000 0x100>; -+ interrupts = <2 30>; -+ clocks = <&clocks BCM2835_CLOCK_EMMC>; -+ status = "disabled"; -+ }; -+ -+ i2c1: i2c@7e804000 { -+ compatible = "brcm,bcm2835-i2c"; -+ reg = <0x7e804000 0x1000>; -+ interrupts = <2 21>; -+ clocks = <&clocks BCM2835_CLOCK_VPU>; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ status = "disabled"; -+ }; -+ -+ i2c2: i2c@7e805000 { -+ compatible = "brcm,bcm2835-i2c"; -+ reg = <0x7e805000 0x1000>; -+ interrupts = <2 21>; -+ clocks = <&clocks BCM2835_CLOCK_VPU>; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ status = "disabled"; -+ }; -+ -+ usb@7e980000 { -+ compatible = "brcm,bcm2835-usb"; -+ reg = <0x7e980000 0x10000>; -+ interrupts = <1 9>; -+ }; -+ }; -+ -+ clocks { -+ compatible = "simple-bus"; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ -+ /* The oscillator is the root of the clock tree. */ -+ clk_osc: clock@3 { -+ compatible = "fixed-clock"; -+ reg = <3>; -+ #clock-cells = <0>; -+ clock-output-names = "osc"; -+ clock-frequency = <19200000>; -+ }; -+ -+ }; -+}; --- -2.3.10 - diff --git a/kernel/kernel/files/patches/mageia/arm-0022-ARM-bcm2835-Move-the-CPU-peripheral-include-out-of-c.patch b/kernel/kernel/files/patches/mageia/arm-0022-ARM-bcm2835-Move-the-CPU-peripheral-include-out-of-c.patch deleted file mode 100644 index b2b8ddb0..00000000 --- a/kernel/kernel/files/patches/mageia/arm-0022-ARM-bcm2835-Move-the-CPU-peripheral-include-out-of-c.patch +++ /dev/null @@ -1,71 +0,0 @@ -From bafa68c08c33ddde3bc10d2d7e5d3b77b4a6c8ed Mon Sep 17 00:00:00 2001 -From: Eric Anholt -Date: Wed, 16 Dec 2015 15:55:12 -0800 -Subject: [PATCH 2/4] ARM: bcm2835: Move the CPU/peripheral include out of - common RPi DT. - -For Raspberry Pi 2, we want to use the same general pin assignment -bits, but need to use bcm2836.dtsi for the CPU instead. - -Signed-off-by: Eric Anholt ---- - arch/arm/boot/dts/bcm2835-rpi-a-plus.dts | 1 + - arch/arm/boot/dts/bcm2835-rpi-b-plus.dts | 1 + - arch/arm/boot/dts/bcm2835-rpi-b-rev2.dts | 1 + - arch/arm/boot/dts/bcm2835-rpi-b.dts | 1 + - arch/arm/boot/dts/bcm2835-rpi.dtsi | 2 -- - 5 files changed, 4 insertions(+), 2 deletions(-) - -diff --git a/arch/arm/boot/dts/bcm2835-rpi-a-plus.dts b/arch/arm/boot/dts/bcm2835-rpi-a-plus.dts -index b2bff43..228614f 100644 ---- a/arch/arm/boot/dts/bcm2835-rpi-a-plus.dts -+++ b/arch/arm/boot/dts/bcm2835-rpi-a-plus.dts -@@ -1,4 +1,5 @@ - /dts-v1/; -+#include "bcm2835.dtsi" - #include "bcm2835-rpi.dtsi" - - / { -diff --git a/arch/arm/boot/dts/bcm2835-rpi-b-plus.dts b/arch/arm/boot/dts/bcm2835-rpi-b-plus.dts -index 668442b..ef54050 100644 ---- a/arch/arm/boot/dts/bcm2835-rpi-b-plus.dts -+++ b/arch/arm/boot/dts/bcm2835-rpi-b-plus.dts -@@ -1,4 +1,5 @@ - /dts-v1/; -+#include "bcm2835.dtsi" - #include "bcm2835-rpi.dtsi" - - / { -diff --git a/arch/arm/boot/dts/bcm2835-rpi-b-rev2.dts b/arch/arm/boot/dts/bcm2835-rpi-b-rev2.dts -index eab8b591..86f1f2f 100644 ---- a/arch/arm/boot/dts/bcm2835-rpi-b-rev2.dts -+++ b/arch/arm/boot/dts/bcm2835-rpi-b-rev2.dts -@@ -1,4 +1,5 @@ - /dts-v1/; -+#include "bcm2835.dtsi" - #include "bcm2835-rpi.dtsi" - - / { -diff --git a/arch/arm/boot/dts/bcm2835-rpi-b.dts b/arch/arm/boot/dts/bcm2835-rpi-b.dts -index ff6b2d1..4859e9d 100644 ---- a/arch/arm/boot/dts/bcm2835-rpi-b.dts -+++ b/arch/arm/boot/dts/bcm2835-rpi-b.dts -@@ -1,4 +1,5 @@ - /dts-v1/; -+#include "bcm2835.dtsi" - #include "bcm2835-rpi.dtsi" - - / { -diff --git a/arch/arm/boot/dts/bcm2835-rpi.dtsi b/arch/arm/boot/dts/bcm2835-rpi.dtsi -index 3572f03..3afb9fe 100644 ---- a/arch/arm/boot/dts/bcm2835-rpi.dtsi -+++ b/arch/arm/boot/dts/bcm2835-rpi.dtsi -@@ -1,5 +1,3 @@ --#include "bcm2835.dtsi" -- - / { - memory { - reg = <0 0x10000000>; --- -2.3.10 - diff --git a/kernel/kernel/files/patches/mageia/arm-0023-ARM-bcm2835-Add-devicetree-for-bcm2836-and-Raspberry.patch b/kernel/kernel/files/patches/mageia/arm-0023-ARM-bcm2835-Add-devicetree-for-bcm2836-and-Raspberry.patch deleted file mode 100644 index 33c91d5d..00000000 --- a/kernel/kernel/files/patches/mageia/arm-0023-ARM-bcm2835-Add-devicetree-for-bcm2836-and-Raspberry.patch +++ /dev/null @@ -1,160 +0,0 @@ -From c33319cd945001741d1b381655c8b7310d756163 Mon Sep 17 00:00:00 2001 -From: Eric Anholt -Date: Thu, 16 Apr 2015 15:26:45 -0700 -Subject: [PATCH 3/4] ARM: bcm2835: Add devicetree for bcm2836 and Raspberry Pi - 2 B - -The Pi 2 B ends up like a Pi 1 B+, with the same peripherals and -pinout, but the CPU and memory layout changed to use the 2836. - -Signed-off-by: Eric Anholt ---- - arch/arm/boot/dts/Makefile | 3 +- - arch/arm/boot/dts/bcm2836-rpi-2-b.dts | 35 ++++++++++++++++ - arch/arm/boot/dts/bcm2836.dtsi | 78 +++++++++++++++++++++++++++++++++++ - 3 files changed, 115 insertions(+), 1 deletion(-) - create mode 100644 arch/arm/boot/dts/bcm2836-rpi-2-b.dts - create mode 100644 arch/arm/boot/dts/bcm2836.dtsi - -diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile -index 30bbc37..54e8f6b 100644 ---- a/arch/arm/boot/dts/Makefile -+++ b/arch/arm/boot/dts/Makefile -@@ -60,7 +60,8 @@ dtb-$(CONFIG_ARCH_BCM2835) += \ - bcm2835-rpi-b.dtb \ - bcm2835-rpi-b-rev2.dtb \ - bcm2835-rpi-b-plus.dtb \ -- bcm2835-rpi-a-plus.dtb -+ bcm2835-rpi-a-plus.dtb \ -+ bcm2836-rpi-2-b.dtb - dtb-$(CONFIG_ARCH_BCM_5301X) += \ - bcm4708-asus-rt-ac56u.dtb \ - bcm4708-asus-rt-ac68u.dtb \ -diff --git a/arch/arm/boot/dts/bcm2836-rpi-2-b.dts b/arch/arm/boot/dts/bcm2836-rpi-2-b.dts -new file mode 100644 -index 0000000..ff94666 ---- /dev/null -+++ b/arch/arm/boot/dts/bcm2836-rpi-2-b.dts -@@ -0,0 +1,35 @@ -+/dts-v1/; -+#include "bcm2836.dtsi" -+#include "bcm2835-rpi.dtsi" -+ -+/ { -+ compatible = "raspberrypi,2-model-b", "brcm,bcm2836"; -+ model = "Raspberry Pi 2 Model B"; -+ -+ memory { -+ reg = <0 0x40000000>; -+ }; -+ -+ leds { -+ act { -+ gpios = <&gpio 47 0>; -+ }; -+ -+ pwr { -+ label = "PWR"; -+ gpios = <&gpio 35 0>; -+ default-state = "keep"; -+ linux,default-trigger = "default-on"; -+ }; -+ }; -+}; -+ -+&gpio { -+ pinctrl-0 = <&gpioout &alt0 &i2s_alt0 &alt3>; -+ -+ /* I2S interface */ -+ i2s_alt0: i2s_alt0 { -+ brcm,pins = <18 19 20 21>; -+ brcm,function = ; -+ }; -+}; -diff --git a/arch/arm/boot/dts/bcm2836.dtsi b/arch/arm/boot/dts/bcm2836.dtsi -new file mode 100644 -index 0000000..9d0651d ---- /dev/null -+++ b/arch/arm/boot/dts/bcm2836.dtsi -@@ -0,0 +1,78 @@ -+#include "bcm283x.dtsi" -+ -+/ { -+ compatible = "brcm,bcm2836"; -+ -+ soc { -+ ranges = <0x7e000000 0x3f000000 0x1000000>, -+ <0x40000000 0x40000000 0x00001000>; -+ dma-ranges = <0xc0000000 0x00000000 0x3f000000>; -+ -+ local_intc: local_intc { -+ compatible = "brcm,bcm2836-l1-intc"; -+ reg = <0x40000000 0x100>; -+ interrupt-controller; -+ #interrupt-cells = <1>; -+ interrupt-parent = <&local_intc>; -+ }; -+ -+ arm-pmu { -+ compatible = "arm,cortex-a7-pmu"; -+ interrupt-parent = <&local_intc>; -+ interrupts = <9>; -+ }; -+ }; -+ -+ timer { -+ compatible = "arm,armv7-timer"; -+ interrupt-parent = <&local_intc>; -+ interrupts = <0>, // PHYS_SECURE_PPI -+ <1>, // PHYS_NONSECURE_PPI -+ <3>, // VIRT_PPI -+ <2>; // HYP_PPI -+ always-on; -+ }; -+ -+ cpus: cpus { -+ #address-cells = <1>; -+ #size-cells = <0>; -+ -+ v7_cpu0: cpu@0 { -+ device_type = "cpu"; -+ compatible = "arm,cortex-a7"; -+ reg = <0xf00>; -+ clock-frequency = <800000000>; -+ }; -+ -+ v7_cpu1: cpu@1 { -+ device_type = "cpu"; -+ compatible = "arm,cortex-a7"; -+ reg = <0xf01>; -+ clock-frequency = <800000000>; -+ }; -+ -+ v7_cpu2: cpu@2 { -+ device_type = "cpu"; -+ compatible = "arm,cortex-a7"; -+ reg = <0xf02>; -+ clock-frequency = <800000000>; -+ }; -+ -+ v7_cpu3: cpu@3 { -+ device_type = "cpu"; -+ compatible = "arm,cortex-a7"; -+ reg = <0xf03>; -+ clock-frequency = <800000000>; -+ }; -+ }; -+}; -+ -+/* Make the BCM2835-style global interrupt controller be a child of the -+ * CPU-local interrupt controller. -+ */ -+&intc { -+ compatible = "brcm,bcm2836-armctrl-ic"; -+ reg = <0x7e00b200 0x200>; -+ interrupt-parent = <&local_intc>; -+ interrupts = <8>; -+}; --- -2.3.10 - diff --git a/kernel/kernel/files/patches/mageia/arm-0024-ARM-bcm2835-Add-the-auxiliary-clocks-to-the-device-t.patch b/kernel/kernel/files/patches/mageia/arm-0024-ARM-bcm2835-Add-the-auxiliary-clocks-to-the-device-t.patch deleted file mode 100644 index ba32aae4..00000000 --- a/kernel/kernel/files/patches/mageia/arm-0024-ARM-bcm2835-Add-the-auxiliary-clocks-to-the-device-t.patch +++ /dev/null @@ -1,34 +0,0 @@ -From 53b6084357a44d7c34044504e1bf149d9156934f Mon Sep 17 00:00:00 2001 -From: Eric Anholt -Date: Tue, 15 Dec 2015 15:35:59 -0800 -Subject: [PATCH 4/4] ARM: bcm2835: Add the auxiliary clocks to the device - tree. - -These will be used for enabling UART1, SPI1, and SPI2. - -Signed-off-by: Eric Anholt ---- - arch/arm/boot/dts/bcm283x.dtsi | 7 +++++++ - 1 file changed, 7 insertions(+) - -diff --git a/arch/arm/boot/dts/bcm283x.dtsi b/arch/arm/boot/dts/bcm283x.dtsi -index 8a7e727..971e741 100644 ---- a/arch/arm/boot/dts/bcm283x.dtsi -+++ b/arch/arm/boot/dts/bcm283x.dtsi -@@ -152,6 +152,13 @@ - status = "disabled"; - }; - -+ aux: aux@0x7e215000 { -+ compatible = "brcm,bcm2835-aux"; -+ #clock-cells = <1>; -+ reg = <0x7e215000 0x8>; -+ clocks = <&clocks BCM2835_CLOCK_VPU>; -+ }; -+ - sdhci: sdhci@7e300000 { - compatible = "brcm,bcm2835-sdhci"; - reg = <0x7e300000 0x100>; --- -2.3.10 - diff --git a/kernel/kernel/files/patches/mageia/arm-0031-ARM-bcm2835-enable-all-bcm2835-relevant-in-defconfig.patch b/kernel/kernel/files/patches/mageia/arm-0031-ARM-bcm2835-enable-all-bcm2835-relevant-in-defconfig.patch deleted file mode 100644 index 00e62d25..00000000 --- a/kernel/kernel/files/patches/mageia/arm-0031-ARM-bcm2835-enable-all-bcm2835-relevant-in-defconfig.patch +++ /dev/null @@ -1,132 +0,0 @@ -From 8c821c590f791ade672609c2f59b1ce0af750653 Mon Sep 17 00:00:00 2001 -From: Stefan Wahren -Date: Tue, 27 Oct 2015 19:08:58 +0100 -Subject: [PATCH 1/2] ARM: bcm2835: enable all bcm2835-relevant in defconfig - -Rebuild bcm2835_defconfig using "make bcm2835_defconfig; -make savedefconfig", and enable manually the following features: - -* all bcm2835-relevant drivers (MBOX, WDT, DMA, PWM, SND) -* enable regular stackprotector because CONFIG_CC_STACKPROTECTOR - disappear -* enable some new dependencies in order to keep LED heartbeat - ( CONFIG_NEW_LEDS, CONFIG_LEDS_CLASS, CONFIG_LEDS_TRIGGERS ) - -The following options were removed, because they are enabled implicit: - -CONFIG_RD_BZIP2, CONFIG_RD_LZMA, CONFIG_RD_XZ, CONFIG_RD_LZO, -CONFIG_USB_DWC2_HOST, CONFIG_EXT4_FS, CONFIG_EXT4_FS_POSIX_ACL - -These options became obsolete: - -CONFIG_RESOURCE_COUNTERS, CONFIG_SCSI_MULTI_LUN - -Signed-off-by: Stefan Wahren -Signed-off-by: Eric Anholt -Acked-by: Stephen Warren ---- - arch/arm/configs/bcm2835_defconfig | 28 +++++++++++++++++----------- - 1 file changed, 17 insertions(+), 11 deletions(-) - -diff --git a/arch/arm/configs/bcm2835_defconfig b/arch/arm/configs/bcm2835_defconfig -index 31cb073..0fda844 100644 ---- a/arch/arm/configs/bcm2835_defconfig -+++ b/arch/arm/configs/bcm2835_defconfig -@@ -10,7 +10,6 @@ CONFIG_CGROUP_FREEZER=y - CONFIG_CGROUP_DEVICE=y - CONFIG_CPUSETS=y - CONFIG_CGROUP_CPUACCT=y --CONFIG_RESOURCE_COUNTERS=y - CONFIG_CGROUP_PERF=y - CONFIG_CFS_BANDWIDTH=y - CONFIG_RT_GROUP_SCHED=y -@@ -18,10 +17,6 @@ CONFIG_NAMESPACES=y - CONFIG_SCHED_AUTOGROUP=y - CONFIG_RELAY=y - CONFIG_BLK_DEV_INITRD=y --CONFIG_RD_BZIP2=y --CONFIG_RD_LZMA=y --CONFIG_RD_XZ=y --CONFIG_RD_LZO=y - CONFIG_CC_OPTIMIZE_FOR_SIZE=y - CONFIG_KALLSYMS_ALL=y - CONFIG_EMBEDDED=y -@@ -29,6 +24,7 @@ CONFIG_EMBEDDED=y - CONFIG_PROFILING=y - CONFIG_OPROFILE=y - CONFIG_JUMP_LABEL=y -+CONFIG_CC_STACKPROTECTOR_REGULAR=y - CONFIG_ARCH_MULTI_V6=y - # CONFIG_ARCH_MULTI_V7 is not set - CONFIG_ARCH_BCM=y -@@ -38,7 +34,6 @@ CONFIG_AEABI=y - CONFIG_KSM=y - CONFIG_CLEANCACHE=y - CONFIG_SECCOMP=y --CONFIG_CC_STACKPROTECTOR=y - CONFIG_KEXEC=y - CONFIG_CRASH_DUMP=y - CONFIG_VFP=y -@@ -57,7 +52,6 @@ CONFIG_DEVTMPFS_MOUNT=y - # CONFIG_STANDALONE is not set - CONFIG_SCSI=y - CONFIG_BLK_DEV_SD=y --CONFIG_SCSI_MULTI_LUN=y - CONFIG_SCSI_CONSTANTS=y - CONFIG_SCSI_SCAN_ASYNC=y - CONFIG_NETDEVICES=y -@@ -77,17 +71,27 @@ CONFIG_SPI=y - CONFIG_SPI_BCM2835=y - CONFIG_GPIO_SYSFS=y - # CONFIG_HWMON is not set -+CONFIG_WATCHDOG=y -+CONFIG_BCM2835_WDT=y - CONFIG_FB=y - CONFIG_FB_SIMPLE=y - CONFIG_FRAMEBUFFER_CONSOLE=y - CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y -+CONFIG_SOUND=y -+CONFIG_SND=y -+CONFIG_SND_SOC=y -+CONFIG_SND_BCM2835_SOC_I2S=y - CONFIG_USB=y - CONFIG_USB_STORAGE=y -+CONFIG_USB_DWC2=y - CONFIG_MMC=y - CONFIG_MMC_SDHCI=y - CONFIG_MMC_SDHCI_PLTFM=y - CONFIG_MMC_SDHCI_BCM2835=y -+CONFIG_NEW_LEDS=y -+CONFIG_LEDS_CLASS=y - CONFIG_LEDS_GPIO=y -+CONFIG_LEDS_TRIGGERS=y - CONFIG_LEDS_TRIGGER_TIMER=y - CONFIG_LEDS_TRIGGER_ONESHOT=y - CONFIG_LEDS_TRIGGER_HEARTBEAT=y -@@ -96,17 +100,19 @@ CONFIG_LEDS_TRIGGER_GPIO=y - CONFIG_LEDS_TRIGGER_DEFAULT_ON=y - CONFIG_LEDS_TRIGGER_TRANSIENT=y - CONFIG_LEDS_TRIGGER_CAMERA=y -+CONFIG_DMADEVICES=y -+CONFIG_DMA_BCM2835=y - CONFIG_STAGING=y --CONFIG_USB_DWC2=y --CONFIG_USB_DWC2_HOST=y -+CONFIG_MAILBOX=y -+CONFIG_BCM2835_MBOX=y - # CONFIG_IOMMU_SUPPORT is not set -+CONFIG_PWM=y -+CONFIG_PWM_BCM2835=y - CONFIG_EXT2_FS=y - CONFIG_EXT2_FS_XATTR=y - CONFIG_EXT2_FS_POSIX_ACL=y - CONFIG_EXT3_FS=y - CONFIG_EXT3_FS_POSIX_ACL=y --CONFIG_EXT4_FS=y --CONFIG_EXT4_FS_POSIX_ACL=y - CONFIG_FANOTIFY=y - CONFIG_MSDOS_FS=y - CONFIG_VFAT_FS=y --- -2.3.10 - diff --git a/kernel/kernel/files/patches/mageia/arm-0032-ARM-bcm2835-enable-auxiliary-spi-driver-in-defconfig.patch b/kernel/kernel/files/patches/mageia/arm-0032-ARM-bcm2835-enable-auxiliary-spi-driver-in-defconfig.patch deleted file mode 100644 index 9a9c8678..00000000 --- a/kernel/kernel/files/patches/mageia/arm-0032-ARM-bcm2835-enable-auxiliary-spi-driver-in-defconfig.patch +++ /dev/null @@ -1,28 +0,0 @@ -From b63074fccbeb81e46b2553a200de54ff3bfadd10 Mon Sep 17 00:00:00 2001 -From: Martin Sperl -Date: Fri, 11 Sep 2015 11:22:06 +0000 -Subject: [PATCH 2/2] ARM: bcm2835: enable auxiliary spi driver in defconfig - -add the auxiliary spi driver to the default config - -Signed-off-by: Martin Sperl -Signed-off-by: Eric Anholt ---- - arch/arm/configs/bcm2835_defconfig | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/arch/arm/configs/bcm2835_defconfig b/arch/arm/configs/bcm2835_defconfig -index 0fda844..72def20 100644 ---- a/arch/arm/configs/bcm2835_defconfig -+++ b/arch/arm/configs/bcm2835_defconfig -@@ -69,6 +69,7 @@ CONFIG_I2C_CHARDEV=y - CONFIG_I2C_BCM2835=y - CONFIG_SPI=y - CONFIG_SPI_BCM2835=y -+CONFIG_SPI_BCM2835AUX=y - CONFIG_GPIO_SYSFS=y - # CONFIG_HWMON is not set - CONFIG_WATCHDOG=y --- -2.3.10 - diff --git a/kernel/kernel/files/patches/mageia/block-sd-Fix-rw_max-for-devices-that-report-an-optimal-xfe.patch b/kernel/kernel/files/patches/mageia/block-sd-Fix-rw_max-for-devices-that-report-an-optimal-xfe.patch new file mode 100644 index 00000000..a74950b6 --- /dev/null +++ b/kernel/kernel/files/patches/mageia/block-sd-Fix-rw_max-for-devices-that-report-an-optimal-xfe.patch @@ -0,0 +1,60 @@ +From 6b7e9cde49691e04314342b7dce90c67ad567fcc Mon Sep 17 00:00:00 2001 +From: "Martin K. Petersen" +Date: Thu, 12 May 2016 22:17:34 -0400 +Subject: [PATCH] sd: Fix rw_max for devices that report an optimal xfer size + +For historic reasons, io_opt is in bytes and max_sectors in block layer +sectors. This interface inconsistency is error prone and should be +fixed. But for 4.4--4.7 let's make the unit difference explicit via a +wrapper function. + +Fixes: d0eb20a863ba ("sd: Optimal I/O size is in bytes, not sectors") +Cc: stable@vger.kernel.org # 4.4+ +Reported-by: Fam Zheng +Reviewed-by: Bart Van Assche +Reviewed-by: Christoph Hellwig +Tested-by: Andrew Patterson +Signed-off-by: Martin K. Petersen +--- + drivers/scsi/sd.c | 8 ++++---- + drivers/scsi/sd.h | 5 +++++ + 2 files changed, 9 insertions(+), 4 deletions(-) + +diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c +index f459dff..60bff78 100644 +--- a/drivers/scsi/sd.c ++++ b/drivers/scsi/sd.c +@@ -2867,10 +2867,10 @@ static int sd_revalidate_disk(struct gendisk *disk) + if (sdkp->opt_xfer_blocks && + sdkp->opt_xfer_blocks <= dev_max && + sdkp->opt_xfer_blocks <= SD_DEF_XFER_BLOCKS && +- sdkp->opt_xfer_blocks * sdp->sector_size >= PAGE_SIZE) +- rw_max = q->limits.io_opt = +- sdkp->opt_xfer_blocks * sdp->sector_size; +- else ++ logical_to_bytes(sdp, sdkp->opt_xfer_blocks) >= PAGE_SIZE) { ++ q->limits.io_opt = logical_to_bytes(sdp, sdkp->opt_xfer_blocks); ++ rw_max = logical_to_sectors(sdp, sdkp->opt_xfer_blocks); ++ } else + rw_max = BLK_DEF_MAX_SECTORS; + + /* Combine with controller limits */ +diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h +index 654630b..765a6f1 100644 +--- a/drivers/scsi/sd.h ++++ b/drivers/scsi/sd.h +@@ -151,6 +151,11 @@ static inline sector_t logical_to_sectors(struct scsi_device *sdev, sector_t blo + return blocks << (ilog2(sdev->sector_size) - 9); + } + ++static inline unsigned int logical_to_bytes(struct scsi_device *sdev, sector_t blocks) ++{ ++ return blocks * sdev->sector_size; ++} ++ + /* + * A DIF-capable target device can be formatted with different + * protection schemes. Currently 0 through 3 are defined: +-- +2.9.0 + diff --git a/kernel/kernel/files/patches/mageia/fs-aufs-4.4-modular.patch b/kernel/kernel/files/patches/mageia/fs-aufs-4.6-modular.patch similarity index 64% rename from kernel/kernel/files/patches/mageia/fs-aufs-4.4-modular.patch rename to kernel/kernel/files/patches/mageia/fs-aufs-4.6-modular.patch index 6d4da033..7b6c6d36 100644 --- a/kernel/kernel/files/patches/mageia/fs-aufs-4.4-modular.patch +++ b/kernel/kernel/files/patches/mageia/fs-aufs-4.6-modular.patch @@ -17,9 +17,9 @@ security/security.c | 10 ++++++++++ 16 files changed, 40 insertions(+), 1 deletion(-) -diff -Nurp linux-4.4.3-aufs/fs/aufs/Kconfig linux-4.4.3-aufs-mod/fs/aufs/Kconfig ---- linux-4.4.3-aufs/fs/aufs/Kconfig 2016-03-02 17:59:37.360498681 +0200 -+++ linux-4.4.3-aufs-mod/fs/aufs/Kconfig 2016-03-03 09:50:41.232631276 +0200 +diff -Nurp linux-4.6-aufs/fs/aufs/Kconfig linux-4.6-aufs-mod/fs/aufs/Kconfig +--- linux-4.6-aufs/fs/aufs/Kconfig 2016-06-01 22:44:04.029679796 +0300 ++++ linux-4.6-aufs-mod/fs/aufs/Kconfig 2016-06-01 22:44:52.623752681 +0300 @@ -1,5 +1,5 @@ config AUFS_FS - bool "Aufs (Advanced multi layered unification filesystem) support" @@ -27,10 +27,10 @@ diff -Nurp linux-4.4.3-aufs/fs/aufs/Kconfig linux-4.4.3-aufs-mod/fs/aufs/Kconfig help Aufs is a stackable unification filesystem such as Unionfs, which unifies several directories and provides a merged single -diff -Nurp linux-4.4.3-aufs/fs/dcache.c linux-4.4.3-aufs-mod/fs/dcache.c ---- linux-4.4.3-aufs/fs/dcache.c 2016-03-02 17:59:37.376498796 +0200 -+++ linux-4.4.3-aufs-mod/fs/dcache.c 2016-03-03 09:50:34.633586562 +0200 -@@ -1272,6 +1272,7 @@ rename_retry: +diff -Nurp linux-4.6-aufs/fs/dcache.c linux-4.6-aufs-mod/fs/dcache.c +--- linux-4.6-aufs/fs/dcache.c 2016-06-01 22:44:04.041679813 +0300 ++++ linux-4.6-aufs-mod/fs/dcache.c 2016-06-01 22:44:52.623752681 +0300 +@@ -1261,6 +1261,7 @@ rename_retry: seq = 1; goto again; } @@ -38,10 +38,10 @@ diff -Nurp linux-4.4.3-aufs/fs/dcache.c linux-4.4.3-aufs-mod/fs/dcache.c /* * Search for at least 1 mount point in the dentry's subdirs. -diff -Nurp linux-4.4.3-aufs/fs/exec.c linux-4.4.3-aufs-mod/fs/exec.c ---- linux-4.4.3-aufs/fs/exec.c 2016-01-11 01:01:32.000000000 +0200 -+++ linux-4.4.3-aufs-mod/fs/exec.c 2016-03-03 09:50:34.633586562 +0200 -@@ -103,6 +103,7 @@ bool path_noexec(const struct path *path +diff -Nurp linux-4.6-aufs/fs/exec.c linux-4.6-aufs-mod/fs/exec.c +--- linux-4.6-aufs/fs/exec.c 2016-05-16 01:43:13.000000000 +0300 ++++ linux-4.6-aufs-mod/fs/exec.c 2016-06-01 22:44:52.624752682 +0300 +@@ -104,6 +104,7 @@ bool path_noexec(const struct path *path return (path->mnt->mnt_flags & MNT_NOEXEC) || (path->mnt->mnt_sb->s_iflags & SB_I_NOEXEC); } @@ -49,10 +49,10 @@ diff -Nurp linux-4.4.3-aufs/fs/exec.c linux-4.4.3-aufs-mod/fs/exec.c #ifdef CONFIG_USELIB /* -diff -Nurp linux-4.4.3-aufs/fs/fcntl.c linux-4.4.3-aufs-mod/fs/fcntl.c ---- linux-4.4.3-aufs/fs/fcntl.c 2016-03-02 17:59:37.376498796 +0200 -+++ linux-4.4.3-aufs-mod/fs/fcntl.c 2016-03-03 09:50:34.633586562 +0200 -@@ -81,6 +81,7 @@ int setfl(int fd, struct file * filp, un +diff -Nurp linux-4.6-aufs/fs/fcntl.c linux-4.6-aufs-mod/fs/fcntl.c +--- linux-4.6-aufs/fs/fcntl.c 2016-06-01 22:44:04.041679813 +0300 ++++ linux-4.6-aufs-mod/fs/fcntl.c 2016-06-01 22:44:52.624752682 +0300 +@@ -82,6 +82,7 @@ int setfl(int fd, struct file * filp, un out: return error; } @@ -60,9 +60,9 @@ diff -Nurp linux-4.4.3-aufs/fs/fcntl.c linux-4.4.3-aufs-mod/fs/fcntl.c static void f_modown(struct file *filp, struct pid *pid, enum pid_type type, int force) -diff -Nurp linux-4.4.3-aufs/fs/file_table.c linux-4.4.3-aufs-mod/fs/file_table.c ---- linux-4.4.3-aufs/fs/file_table.c 2016-01-11 01:01:32.000000000 +0200 -+++ linux-4.4.3-aufs-mod/fs/file_table.c 2016-03-03 09:50:34.633586562 +0200 +diff -Nurp linux-4.6-aufs/fs/file_table.c linux-4.6-aufs-mod/fs/file_table.c +--- linux-4.6-aufs/fs/file_table.c 2016-05-16 01:43:13.000000000 +0300 ++++ linux-4.6-aufs-mod/fs/file_table.c 2016-06-01 22:44:52.624752682 +0300 @@ -147,6 +147,7 @@ over: } return ERR_PTR(-ENFILE); @@ -95,9 +95,9 @@ diff -Nurp linux-4.4.3-aufs/fs/file_table.c linux-4.4.3-aufs-mod/fs/file_table.c void __init files_init(void) { -diff -Nurp linux-4.4.3-aufs/fs/namespace.c linux-4.4.3-aufs-mod/fs/namespace.c ---- linux-4.4.3-aufs/fs/namespace.c 2016-01-11 01:01:32.000000000 +0200 -+++ linux-4.4.3-aufs-mod/fs/namespace.c 2016-03-03 09:50:34.634586569 +0200 +diff -Nurp linux-4.6-aufs/fs/namespace.c linux-4.6-aufs-mod/fs/namespace.c +--- linux-4.6-aufs/fs/namespace.c 2016-05-16 01:43:13.000000000 +0300 ++++ linux-4.6-aufs-mod/fs/namespace.c 2016-06-01 22:44:52.625752684 +0300 @@ -463,6 +463,7 @@ void __mnt_drop_write(struct vfsmount *m mnt_dec_writers(real_mount(mnt)); preempt_enable(); @@ -106,7 +106,7 @@ diff -Nurp linux-4.4.3-aufs/fs/namespace.c linux-4.4.3-aufs-mod/fs/namespace.c /** * mnt_drop_write - give up write access to a mount -@@ -1803,6 +1804,7 @@ int iterate_mounts(int (*f)(struct vfsmo +@@ -1811,6 +1812,7 @@ int iterate_mounts(int (*f)(struct vfsmo } return 0; } @@ -114,9 +114,9 @@ diff -Nurp linux-4.4.3-aufs/fs/namespace.c linux-4.4.3-aufs-mod/fs/namespace.c static void cleanup_group_ids(struct mount *mnt, struct mount *end) { -diff -Nurp linux-4.4.3-aufs/fs/notify/group.c linux-4.4.3-aufs-mod/fs/notify/group.c ---- linux-4.4.3-aufs/fs/notify/group.c 2016-01-11 01:01:32.000000000 +0200 -+++ linux-4.4.3-aufs-mod/fs/notify/group.c 2016-03-03 09:50:34.634586569 +0200 +diff -Nurp linux-4.6-aufs/fs/notify/group.c linux-4.6-aufs-mod/fs/notify/group.c +--- linux-4.6-aufs/fs/notify/group.c 2016-05-16 01:43:13.000000000 +0300 ++++ linux-4.6-aufs-mod/fs/notify/group.c 2016-06-01 22:44:52.625752684 +0300 @@ -22,6 +22,7 @@ #include #include @@ -149,10 +149,10 @@ diff -Nurp linux-4.4.3-aufs/fs/notify/group.c linux-4.4.3-aufs-mod/fs/notify/gro int fsnotify_fasync(int fd, struct file *file, int on) { -diff -Nurp linux-4.4.3-aufs/fs/notify/mark.c linux-4.4.3-aufs-mod/fs/notify/mark.c ---- linux-4.4.3-aufs/fs/notify/mark.c 2016-01-11 01:01:32.000000000 +0200 -+++ linux-4.4.3-aufs-mod/fs/notify/mark.c 2016-03-03 09:50:34.634586569 +0200 -@@ -109,6 +109,7 @@ void fsnotify_put_mark(struct fsnotify_m +diff -Nurp linux-4.6-aufs/fs/notify/mark.c linux-4.6-aufs-mod/fs/notify/mark.c +--- linux-4.6-aufs/fs/notify/mark.c 2016-05-16 01:43:13.000000000 +0300 ++++ linux-4.6-aufs-mod/fs/notify/mark.c 2016-06-01 22:44:52.625752684 +0300 +@@ -113,6 +113,7 @@ void fsnotify_put_mark(struct fsnotify_m mark->free_mark(mark); } } @@ -160,7 +160,7 @@ diff -Nurp linux-4.4.3-aufs/fs/notify/mark.c linux-4.4.3-aufs-mod/fs/notify/mark /* Calculate mask of events for a list of marks */ u32 fsnotify_recalc_mask(struct hlist_head *head) -@@ -208,6 +209,7 @@ void fsnotify_destroy_mark(struct fsnoti +@@ -213,6 +214,7 @@ void fsnotify_destroy_mark(struct fsnoti mutex_unlock(&group->mark_mutex); fsnotify_free_mark(mark); } @@ -168,7 +168,7 @@ diff -Nurp linux-4.4.3-aufs/fs/notify/mark.c linux-4.4.3-aufs-mod/fs/notify/mark void fsnotify_destroy_marks(struct hlist_head *head, spinlock_t *lock) { -@@ -392,6 +394,7 @@ err: +@@ -398,6 +400,7 @@ err: return ret; } @@ -176,19 +176,19 @@ diff -Nurp linux-4.4.3-aufs/fs/notify/mark.c linux-4.4.3-aufs-mod/fs/notify/mark int fsnotify_add_mark(struct fsnotify_mark *mark, struct fsnotify_group *group, struct inode *inode, struct vfsmount *mnt, int allow_dups) -@@ -492,6 +495,7 @@ void fsnotify_init_mark(struct fsnotify_ +@@ -498,6 +501,7 @@ void fsnotify_init_mark(struct fsnotify_ atomic_set(&mark->refcnt, 1); mark->free_mark = free_mark; } +EXPORT_SYMBOL_GPL(fsnotify_init_mark); - static int fsnotify_mark_destroy(void *ignored) + static void fsnotify_mark_destroy(struct work_struct *work) { -diff -Nurp linux-4.4.3-aufs/fs/open.c linux-4.4.3-aufs-mod/fs/open.c ---- linux-4.4.3-aufs/fs/open.c 2016-01-11 01:01:32.000000000 +0200 -+++ linux-4.4.3-aufs-mod/fs/open.c 2016-03-03 09:50:34.634586569 +0200 +diff -Nurp linux-4.6-aufs/fs/open.c linux-4.6-aufs-mod/fs/open.c +--- linux-4.6-aufs/fs/open.c 2016-05-16 01:43:13.000000000 +0300 ++++ linux-4.6-aufs-mod/fs/open.c 2016-06-01 22:44:52.625752684 +0300 @@ -64,6 +64,7 @@ int do_truncate(struct dentry *dentry, l - mutex_unlock(&dentry->d_inode->i_mutex); + inode_unlock(dentry->d_inode); return ret; } +EXPORT_SYMBOL_GPL(do_truncate); @@ -203,10 +203,10 @@ diff -Nurp linux-4.4.3-aufs/fs/open.c linux-4.4.3-aufs-mod/fs/open.c static int do_dentry_open(struct file *f, struct inode *inode, -diff -Nurp linux-4.4.3-aufs/fs/read_write.c linux-4.4.3-aufs-mod/fs/read_write.c ---- linux-4.4.3-aufs/fs/read_write.c 2016-03-02 17:59:37.377498803 +0200 -+++ linux-4.4.3-aufs-mod/fs/read_write.c 2016-03-03 09:50:34.634586569 +0200 -@@ -504,6 +504,7 @@ vfs_readf_t vfs_readf(struct file *file) +diff -Nurp linux-4.6-aufs/fs/read_write.c linux-4.6-aufs-mod/fs/read_write.c +--- linux-4.6-aufs/fs/read_write.c 2016-06-01 22:44:04.042679815 +0300 ++++ linux-4.6-aufs-mod/fs/read_write.c 2016-06-01 22:44:52.626752685 +0300 +@@ -544,6 +544,7 @@ vfs_readf_t vfs_readf(struct file *file) return new_sync_read; return ERR_PTR(-ENOSYS); } @@ -214,7 +214,7 @@ diff -Nurp linux-4.4.3-aufs/fs/read_write.c linux-4.4.3-aufs-mod/fs/read_write.c vfs_writef_t vfs_writef(struct file *file) { -@@ -515,6 +516,7 @@ vfs_writef_t vfs_writef(struct file *fil +@@ -555,6 +556,7 @@ vfs_writef_t vfs_writef(struct file *fil return new_sync_write; return ERR_PTR(-ENOSYS); } @@ -222,10 +222,10 @@ diff -Nurp linux-4.4.3-aufs/fs/read_write.c linux-4.4.3-aufs-mod/fs/read_write.c ssize_t __kernel_write(struct file *file, const char *buf, size_t count, loff_t *pos) { -diff -Nurp linux-4.4.3-aufs/fs/splice.c linux-4.4.3-aufs-mod/fs/splice.c ---- linux-4.4.3-aufs/fs/splice.c 2016-03-02 17:59:37.377498803 +0200 -+++ linux-4.4.3-aufs-mod/fs/splice.c 2016-03-03 09:50:34.635586575 +0200 -@@ -1123,6 +1123,7 @@ long do_splice_from(struct pipe_inode_in +diff -Nurp linux-4.6-aufs/fs/splice.c linux-4.6-aufs-mod/fs/splice.c +--- linux-4.6-aufs/fs/splice.c 2016-06-01 22:44:04.042679815 +0300 ++++ linux-4.6-aufs-mod/fs/splice.c 2016-06-01 22:44:52.626752685 +0300 +@@ -1124,6 +1124,7 @@ long do_splice_from(struct pipe_inode_in return splice_write(pipe, out, ppos, len, flags); } @@ -233,7 +233,7 @@ diff -Nurp linux-4.4.3-aufs/fs/splice.c linux-4.4.3-aufs-mod/fs/splice.c /* * Attempt to initiate a splice from a file to a pipe. -@@ -1149,6 +1150,7 @@ long do_splice_to(struct file *in, loff_ +@@ -1153,6 +1154,7 @@ long do_splice_to(struct file *in, loff_ return splice_read(in, ppos, pipe, len, flags); } @@ -241,28 +241,28 @@ diff -Nurp linux-4.4.3-aufs/fs/splice.c linux-4.4.3-aufs-mod/fs/splice.c /** * splice_direct_to_actor - splices data directly between two non-pipes -diff -Nurp linux-4.4.3-aufs/fs/xattr.c linux-4.4.3-aufs-mod/fs/xattr.c ---- linux-4.4.3-aufs/fs/xattr.c 2016-01-11 01:01:32.000000000 +0200 -+++ linux-4.4.3-aufs-mod/fs/xattr.c 2016-03-03 09:50:34.635586575 +0200 +diff -Nurp linux-4.6-aufs/fs/xattr.c linux-4.6-aufs-mod/fs/xattr.c +--- linux-4.6-aufs/fs/xattr.c 2016-05-16 01:43:13.000000000 +0300 ++++ linux-4.6-aufs-mod/fs/xattr.c 2016-06-01 22:44:52.626752685 +0300 @@ -207,6 +207,7 @@ vfs_getxattr_alloc(struct dentry *dentry *xattr_value = value; return error; } +EXPORT_SYMBOL_GPL(vfs_getxattr_alloc); - /* Compare an extended attribute value with the given value */ - int vfs_xattr_cmp(struct dentry *dentry, const char *xattr_name, -diff -Nurp linux-4.4.3-aufs/kernel/task_work.c linux-4.4.3-aufs-mod/kernel/task_work.c ---- linux-4.4.3-aufs/kernel/task_work.c 2016-01-11 01:01:32.000000000 +0200 -+++ linux-4.4.3-aufs-mod/kernel/task_work.c 2016-03-03 09:50:34.635586575 +0200 + ssize_t + vfs_getxattr(struct dentry *dentry, const char *name, void *value, size_t size) +diff -Nurp linux-4.6-aufs/kernel/task_work.c linux-4.6-aufs-mod/kernel/task_work.c +--- linux-4.6-aufs/kernel/task_work.c 2016-05-16 01:43:13.000000000 +0300 ++++ linux-4.6-aufs-mod/kernel/task_work.c 2016-06-01 22:44:52.626752685 +0300 @@ -118,3 +118,4 @@ void task_work_run(void) } while (work); } } +EXPORT_SYMBOL_GPL(task_work_run); -diff -Nurp linux-4.4.3-aufs/security/commoncap.c linux-4.4.3-aufs-mod/security/commoncap.c ---- linux-4.4.3-aufs/security/commoncap.c 2016-03-02 17:42:59.463713410 +0200 -+++ linux-4.4.3-aufs-mod/security/commoncap.c 2016-03-03 09:50:34.635586575 +0200 +diff -Nurp linux-4.6-aufs/security/commoncap.c linux-4.6-aufs-mod/security/commoncap.c +--- linux-4.6-aufs/security/commoncap.c 2016-05-16 01:43:13.000000000 +0300 ++++ linux-4.6-aufs-mod/security/commoncap.c 2016-06-01 22:44:52.627752687 +0300 @@ -1058,12 +1058,14 @@ int cap_mmap_addr(unsigned long addr) } return ret; @@ -278,9 +278,9 @@ diff -Nurp linux-4.4.3-aufs/security/commoncap.c linux-4.4.3-aufs-mod/security/c #ifdef CONFIG_SECURITY -diff -Nurp linux-4.4.3-aufs/security/device_cgroup.c linux-4.4.3-aufs-mod/security/device_cgroup.c ---- linux-4.4.3-aufs/security/device_cgroup.c 2016-01-11 01:01:32.000000000 +0200 -+++ linux-4.4.3-aufs-mod/security/device_cgroup.c 2016-03-03 09:50:34.635586575 +0200 +diff -Nurp linux-4.6-aufs/security/device_cgroup.c linux-4.6-aufs-mod/security/device_cgroup.c +--- linux-4.6-aufs/security/device_cgroup.c 2016-05-16 01:43:13.000000000 +0300 ++++ linux-4.6-aufs-mod/security/device_cgroup.c 2016-06-01 22:44:52.627752687 +0300 @@ -7,6 +7,7 @@ #include #include @@ -297,9 +297,9 @@ diff -Nurp linux-4.4.3-aufs/security/device_cgroup.c linux-4.4.3-aufs-mod/securi int devcgroup_inode_mknod(int mode, dev_t dev) { -diff -Nurp linux-4.4.3-aufs/security/security.c linux-4.4.3-aufs-mod/security/security.c ---- linux-4.4.3-aufs/security/security.c 2016-01-11 01:01:32.000000000 +0200 -+++ linux-4.4.3-aufs-mod/security/security.c 2016-03-03 09:50:34.636586582 +0200 +diff -Nurp linux-4.6-aufs/security/security.c linux-4.6-aufs-mod/security/security.c +--- linux-4.6-aufs/security/security.c 2016-05-16 01:43:13.000000000 +0300 ++++ linux-4.6-aufs-mod/security/security.c 2016-06-01 22:44:52.627752687 +0300 @@ -433,6 +433,7 @@ int security_path_rmdir(struct path *dir return 0; return call_int_hook(path_rmdir, 0, dir, dentry); diff --git a/kernel/kernel/files/patches/mageia/fs-aufs-4.4.patch b/kernel/kernel/files/patches/mageia/fs-aufs-4.6.patch similarity index 90% rename from kernel/kernel/files/patches/mageia/fs-aufs-4.4.patch rename to kernel/kernel/files/patches/mageia/fs-aufs-4.6.patch index fc6f4fdc..8fa2df7c 100644 --- a/kernel/kernel/files/patches/mageia/fs-aufs-4.4.patch +++ b/kernel/kernel/files/patches/mageia/fs-aufs-4.6.patch @@ -1,120 +1,122 @@ - Documentation/ABI/testing/debugfs-aufs | 50 - Documentation/ABI/testing/sysfs-aufs | 31 - Documentation/filesystems/aufs/README | 391 +++ - Documentation/filesystems/aufs/design/01intro.txt | 157 + - Documentation/filesystems/aufs/design/02struct.txt | 245 ++ - Documentation/filesystems/aufs/design/03atomic_open.txt | 72 - Documentation/filesystems/aufs/design/03lookup.txt | 100 - Documentation/filesystems/aufs/design/04branch.txt | 61 - Documentation/filesystems/aufs/design/05wbr_policy.txt | 51 - Documentation/filesystems/aufs/design/06fhsm.txt | 105 - Documentation/filesystems/aufs/design/06mmap.txt | 59 - Documentation/filesystems/aufs/design/06xattr.txt | 81 - Documentation/filesystems/aufs/design/07export.txt | 45 - Documentation/filesystems/aufs/design/08shwh.txt | 39 - Documentation/filesystems/aufs/design/10dynop.txt | 34 - MAINTAINERS | 13 - drivers/block/loop.c | 18 - fs/Kconfig | 1 - fs/Makefile | 1 - fs/aufs/Kconfig | 185 + - fs/aufs/Makefile | 36 - fs/aufs/aufs.h | 46 - fs/aufs/branch.c | 1394 ++++++++++++ - fs/aufs/branch.h | 266 ++ - fs/aufs/cpup.c | 1366 +++++++++++ - fs/aufs/cpup.h | 81 - fs/aufs/dbgaufs.c | 419 +++ - fs/aufs/dbgaufs.h | 35 - fs/aufs/dcsub.c | 211 + - fs/aufs/dcsub.h | 123 + - fs/aufs/debug.c | 425 +++ - fs/aufs/debug.h | 212 + - fs/aufs/dentry.c | 1123 +++++++++ - fs/aufs/dentry.h | 221 + - fs/aufs/dinfo.c | 537 ++++ - fs/aufs/dir.c | 745 ++++++ - fs/aufs/dir.h | 118 + - fs/aufs/dynop.c | 356 +++ - fs/aufs/dynop.h | 61 - fs/aufs/export.c | 819 +++++++ - fs/aufs/f_op.c | 759 ++++++ - fs/aufs/fhsm.c | 412 +++ - fs/aufs/file.c | 831 +++++++ - fs/aufs/file.h | 278 ++ - fs/aufs/finfo.c | 143 + - fs/aufs/fstype.h | 387 +++ - fs/aufs/hfsnotify.c | 275 ++ - fs/aufs/hfsplus.c | 43 - fs/aufs/hnotify.c | 697 ++++++ - fs/aufs/i_op.c | 1477 ++++++++++++ - fs/aufs/i_op_add.c | 919 +++++++ - fs/aufs/i_op_del.c | 497 ++++ - fs/aufs/i_op_ren.c | 1002 ++++++++ - fs/aufs/iinfo.c | 264 ++ - fs/aufs/inode.c | 514 ++++ - fs/aufs/inode.h | 672 +++++ - fs/aufs/ioctl.c | 206 + - fs/aufs/loop.c | 133 + - fs/aufs/loop.h | 39 - fs/aufs/magic.mk | 30 - fs/aufs/module.c | 207 + - fs/aufs/module.h | 92 - fs/aufs/mvdown.c | 690 +++++ - fs/aufs/opts.c | 1846 ++++++++++++++++ - fs/aufs/opts.h | 198 + - fs/aufs/plink.c | 515 ++++ - fs/aufs/poll.c | 39 - fs/aufs/posix_acl.c | 85 - fs/aufs/procfs.c | 156 + - fs/aufs/rdu.c | 375 +++ - fs/aufs/rwsem.h | 178 + - fs/aufs/sbinfo.c | 353 +++ - fs/aufs/spl.h | 98 - fs/aufs/super.c | 1026 ++++++++ - fs/aufs/super.h | 628 +++++ - fs/aufs/sysaufs.c | 91 - fs/aufs/sysaufs.h | 88 - fs/aufs/sysfs.c | 340 ++ - fs/aufs/sysrq.c | 144 + - fs/aufs/vdir.c | 875 +++++++ - fs/aufs/vfsub.c | 853 +++++++ - fs/aufs/vfsub.h | 295 ++ - fs/aufs/wbr_policy.c | 752 ++++++ - fs/aufs/whout.c | 1047 +++++++++ - fs/aufs/whout.h | 72 - fs/aufs/wkq.c | 200 + - fs/aufs/wkq.h | 78 - fs/aufs/xattr.c | 331 ++ - fs/aufs/xino.c | 1305 +++++++++++ - fs/dcache.c | 2 - fs/fcntl.c | 4 - fs/proc/base.c | 2 - fs/proc/nommu.c | 5 - fs/proc/task_mmu.c | 7 - fs/proc/task_nommu.c | 5 - fs/read_write.c | 22 - fs/splice.c | 10 - include/linux/file.h | 1 - include/linux/fs.h | 8 - include/linux/mm.h | 22 - include/linux/mm_types.h | 2 - include/linux/splice.h | 6 - include/uapi/linux/Kbuild | 1 - include/uapi/linux/aufs_type.h | 406 +++ - kernel/fork.c | 2 - mm/Makefile | 2 - mm/filemap.c | 2 - mm/memory.c | 2 - mm/mmap.c | 17 - mm/nommu.c | 10 - mm/prfile.c | 86 - 111 files changed, 33462 insertions(+), 30 deletions(-) + Documentation/ABI/testing/debugfs-aufs | 50 + + Documentation/ABI/testing/sysfs-aufs | 31 + + Documentation/filesystems/aufs/README | 392 +++++ + Documentation/filesystems/aufs/design/01intro.txt | 157 ++ + Documentation/filesystems/aufs/design/02struct.txt | 245 +++ + .../filesystems/aufs/design/03atomic_open.txt | 72 + + Documentation/filesystems/aufs/design/03lookup.txt | 100 ++ + Documentation/filesystems/aufs/design/04branch.txt | 61 + + .../filesystems/aufs/design/05wbr_policy.txt | 51 + + Documentation/filesystems/aufs/design/06fhsm.txt | 105 ++ + Documentation/filesystems/aufs/design/06mmap.txt | 59 + + Documentation/filesystems/aufs/design/06xattr.txt | 81 + + Documentation/filesystems/aufs/design/07export.txt | 45 + + Documentation/filesystems/aufs/design/08shwh.txt | 39 + + Documentation/filesystems/aufs/design/10dynop.txt | 34 + + MAINTAINERS | 13 + + drivers/block/loop.c | 18 + + fs/Kconfig | 1 + + fs/Makefile | 1 + + fs/aufs/Kconfig | 185 ++ + fs/aufs/Makefile | 36 + + fs/aufs/aufs.h | 46 + + fs/aufs/branch.c | 1393 +++++++++++++++ + fs/aufs/branch.h | 296 ++++ + fs/aufs/cpup.c | 1366 +++++++++++++++ + fs/aufs/cpup.h | 81 + + fs/aufs/dbgaufs.c | 419 +++++ + fs/aufs/dbgaufs.h | 35 + + fs/aufs/dcsub.c | 211 +++ + fs/aufs/dcsub.h | 123 ++ + fs/aufs/debug.c | 429 +++++ + fs/aufs/debug.h | 212 +++ + fs/aufs/dentry.c | 1123 ++++++++++++ + fs/aufs/dentry.h | 221 +++ + fs/aufs/dinfo.c | 535 ++++++ + fs/aufs/dir.c | 743 ++++++++ + fs/aufs/dir.h | 118 ++ + fs/aufs/dynop.c | 356 ++++ + fs/aufs/dynop.h | 61 + + fs/aufs/export.c | 824 +++++++++ + fs/aufs/f_op.c | 757 ++++++++ + fs/aufs/fhsm.c | 412 +++++ + fs/aufs/file.c | 831 +++++++++ + fs/aufs/file.h | 278 +++ + fs/aufs/finfo.c | 136 ++ + fs/aufs/fstype.h | 387 ++++ + fs/aufs/hfsnotify.c | 274 +++ + fs/aufs/hfsplus.c | 43 + + fs/aufs/hnotify.c | 697 ++++++++ + fs/aufs/i_op.c | 1394 +++++++++++++++ + fs/aufs/i_op_add.c | 911 ++++++++++ + fs/aufs/i_op_del.c | 497 ++++++ + fs/aufs/i_op_ren.c | 1002 +++++++++++ + fs/aufs/iinfo.c | 267 +++ + fs/aufs/inode.c | 504 ++++++ + fs/aufs/inode.h | 676 +++++++ + fs/aufs/ioctl.c | 206 +++ + fs/aufs/loop.c | 133 ++ + fs/aufs/loop.h | 39 + + fs/aufs/magic.mk | 30 + + fs/aufs/module.c | 209 +++ + fs/aufs/module.h | 76 + + fs/aufs/mvdown.c | 690 ++++++++ + fs/aufs/opts.c | 1846 ++++++++++++++++++++ + fs/aufs/opts.h | 198 +++ + fs/aufs/plink.c | 489 ++++++ + fs/aufs/poll.c | 39 + + fs/aufs/posix_acl.c | 85 + + fs/aufs/procfs.c | 156 ++ + fs/aufs/rdu.c | 376 ++++ + fs/aufs/rwsem.h | 185 ++ + fs/aufs/sbinfo.c | 340 ++++ + fs/aufs/spl.h | 98 ++ + fs/aufs/super.c | 1026 +++++++++++ + fs/aufs/super.h | 625 +++++++ + fs/aufs/sysaufs.c | 91 + + fs/aufs/sysaufs.h | 88 + + fs/aufs/sysfs.c | 340 ++++ + fs/aufs/sysrq.c | 144 ++ + fs/aufs/vdir.c | 875 ++++++++++ + fs/aufs/vfsub.c | 871 +++++++++ + fs/aufs/vfsub.h | 297 ++++ + fs/aufs/wbr_policy.c | 752 ++++++++ + fs/aufs/whout.c | 1047 +++++++++++ + fs/aufs/whout.h | 72 + + fs/aufs/wkq.c | 205 +++ + fs/aufs/wkq.h | 80 + + fs/aufs/xattr.c | 331 ++++ + fs/aufs/xino.c | 1304 ++++++++++++++ + fs/dcache.c | 2 +- + fs/fcntl.c | 4 +- + fs/proc/base.c | 2 +- + fs/proc/nommu.c | 5 +- + fs/proc/task_mmu.c | 7 +- + fs/proc/task_nommu.c | 5 +- + fs/read_write.c | 22 + + fs/splice.c | 10 +- + include/linux/file.h | 1 + + include/linux/fs.h | 8 + + include/linux/mm.h | 22 + + include/linux/mm_types.h | 2 + + include/linux/splice.h | 6 + + include/uapi/linux/Kbuild | 1 + + include/uapi/linux/aufs_type.h | 406 +++++ + kernel/fork.c | 2 +- + mm/Makefile | 2 +- + mm/filemap.c | 2 +- + mm/memory.c | 2 +- + mm/mmap.c | 33 +- + mm/nommu.c | 10 +- + mm/prfile.c | 86 + + 111 files changed, 33388 insertions(+), 29 deletions(-) -diff -Nurp linux-4.4.3/Documentation/ABI/testing/debugfs-aufs linux-4.4.3-aufs/Documentation/ABI/testing/debugfs-aufs ---- linux-4.4.3/Documentation/ABI/testing/debugfs-aufs 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/Documentation/ABI/testing/debugfs-aufs 2016-03-02 17:59:37.356498653 +0200 +diff --git a/Documentation/ABI/testing/debugfs-aufs b/Documentation/ABI/testing/debugfs-aufs +new file mode 100644 +index 0000000..99642d1 +--- /dev/null ++++ b/Documentation/ABI/testing/debugfs-aufs @@ -0,0 +1,50 @@ +What: /debug/aufs/si_/ +Date: March 2009 @@ -166,9 +168,11 @@ diff -Nurp linux-4.4.3/Documentation/ABI/testing/debugfs-aufs linux-4.4.3-aufs/D + be created. + When the aufs mount option 'noxino' is specified, it + will be empty. About XINO files, see the aufs manual. -diff -Nurp linux-4.4.3/Documentation/ABI/testing/sysfs-aufs linux-4.4.3-aufs/Documentation/ABI/testing/sysfs-aufs ---- linux-4.4.3/Documentation/ABI/testing/sysfs-aufs 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/Documentation/ABI/testing/sysfs-aufs 2016-03-02 17:59:37.356498653 +0200 +diff --git a/Documentation/ABI/testing/sysfs-aufs b/Documentation/ABI/testing/sysfs-aufs +new file mode 100644 +index 0000000..82f9518 +--- /dev/null ++++ b/Documentation/ABI/testing/sysfs-aufs @@ -0,0 +1,31 @@ +What: /sys/fs/aufs/si_/ +Date: March 2009 @@ -201,1107 +205,12 @@ diff -Nurp linux-4.4.3/Documentation/ABI/testing/sysfs-aufs linux-4.4.3-aufs/Doc + even if it is the default path. + When the aufs mount option 'noxino' is specified, it + will be empty. About XINO files, see the aufs manual. -diff -Nurp linux-4.4.3/Documentation/filesystems/aufs/design/01intro.txt linux-4.4.3-aufs/Documentation/filesystems/aufs/design/01intro.txt ---- linux-4.4.3/Documentation/filesystems/aufs/design/01intro.txt 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/Documentation/filesystems/aufs/design/01intro.txt 2016-03-02 17:59:37.357498660 +0200 -@@ -0,0 +1,157 @@ -+ -+# Copyright (C) 2005-2016 Junjiro R. Okajima -+ -+Introduction -+---------------------------------------- -+ -+aufs [ei ju: 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. -+ -+AUFS is a filesystem with features: -+- multi layered stackable unification filesystem, the member directory -+ is called as a branch. -+- branch permission and attribute, 'readonly', 'real-readonly', -+ 'readwrite', 'whiteout-able', 'link-able whiteout', etc. and their -+ combination. -+- internal "file copy-on-write". -+- logical deletion, whiteout. -+- dynamic branch manipulation, adding, deleting and changing permission. -+- allow bypassing aufs, user's direct branch access. -+- external inode number translation table and bitmap which maintains the -+ persistent aufs inode number. -+- seekable directory, including NFS readdir. -+- file mapping, mmap and sharing pages. -+- pseudo-link, hardlink over branches. -+- loopback mounted filesystem as a branch. -+- several policies to select one among multiple writable branches. -+- revert a single systemcall when an error occurs in aufs. -+- and more... -+ -+ -+Multi Layered Stackable Unification Filesystem -+---------------------------------------------------------------------- -+Most people already knows what it is. -+It is a filesystem which unifies several directories and provides a -+merged single directory. When users access a file, the access will be -+passed/re-directed/converted (sorry, I am not sure which English word is -+correct) to the real file on the member filesystem. The member -+filesystem is called 'lower filesystem' or 'branch' and has a mode -+'readonly' and 'readwrite.' And the deletion for a file on the lower -+readonly branch is handled by creating 'whiteout' on the upper writable -+branch. -+ -+On LKML, there have been discussions about UnionMount (Jan Blunck, -+Bharata B Rao and Valerie Aurora) and Unionfs (Erez Zadok). They took -+different approaches to implement the merged-view. -+The former tries putting it into VFS, and the latter implements as a -+separate filesystem. -+(If I misunderstand about these implementations, please let me know and -+I shall correct it. Because it is a long time ago when I read their -+source files last time). -+ -+UnionMount's approach will be able to small, but may be hard to share -+branches between several UnionMount since the whiteout in it is -+implemented in the inode on branch filesystem and always -+shared. According to Bharata's post, readdir does not seems to be -+finished yet. -+There are several missing features known in this implementations such as -+- for users, the inode number may change silently. eg. copy-up. -+- link(2) may break by copy-up. -+- read(2) may get an obsoleted filedata (fstat(2) too). -+- fcntl(F_SETLK) may be broken by copy-up. -+- unnecessary copy-up may happen, for example mmap(MAP_PRIVATE) after -+ open(O_RDWR). -+ -+In linux-3.18, "overlay" filesystem (formerly known as "overlayfs") was -+merged into mainline. This is another implementation of UnionMount as a -+separated filesystem. All the limitations and known problems which -+UnionMount are equally inherited to "overlay" filesystem. -+ -+Unionfs has a longer history. When I started implementing a stackable -+filesystem (Aug 2005), it already existed. It has virtual super_block, -+inode, dentry and file objects and they have an array pointing lower -+same kind objects. After contributing many patches for Unionfs, I -+re-started my project AUFS (Jun 2006). -+ -+In AUFS, the structure of filesystem resembles to Unionfs, but I -+implemented my own ideas, approaches and enhancements and it became -+totally different one. -+ -+Comparing DM snapshot and fs based implementation -+- the number of bytes to be copied between devices is much smaller. -+- the type of filesystem must be one and only. -+- the fs must be writable, no readonly fs, even for the lower original -+ device. so the compression fs will not be usable. but if we use -+ loopback mount, we may address this issue. -+ for instance, -+ mount /cdrom/squashfs.img /sq -+ losetup /sq/ext2.img -+ losetup /somewhere/cow -+ dmsetup "snapshot /dev/loop0 /dev/loop1 ..." -+- it will be difficult (or needs more operations) to extract the -+ difference between the original device and COW. -+- DM snapshot-merge may help a lot when users try merging. in the -+ fs-layer union, users will use rsync(1). -+ -+You may want to read my old paper "Filesystems in LiveCD" -+(http://aufs.sourceforge.net/aufs2/report/sq/sq.pdf). -+ -+ -+Several characters/aspects/persona of aufs -+---------------------------------------------------------------------- -+ -+Aufs has several characters, aspects or persona. -+1. a filesystem, callee of VFS helper -+2. sub-VFS, caller of VFS helper for branches -+3. a virtual filesystem which maintains persistent inode number -+4. reader/writer of files on branches such like an application -+ -+1. Callee of VFS Helper -+As an ordinary linux filesystem, aufs is a callee of VFS. For instance, -+unlink(2) from an application reaches sys_unlink() kernel function and -+then vfs_unlink() is called. vfs_unlink() is one of VFS helper and it -+calls filesystem specific unlink operation. Actually aufs implements the -+unlink operation but it behaves like a redirector. -+ -+2. Caller of VFS Helper for Branches -+aufs_unlink() passes the unlink request to the branch filesystem as if -+it were called from VFS. So the called unlink operation of the branch -+filesystem acts as usual. As a caller of VFS helper, aufs should handle -+every necessary pre/post operation for the branch filesystem. -+- acquire the lock for the parent dir on a branch -+- lookup in a branch -+- revalidate dentry on a branch -+- mnt_want_write() for a branch -+- vfs_unlink() for a branch -+- mnt_drop_write() for a branch -+- release the lock on a branch -+ -+3. Persistent Inode Number -+One of the most important issue for a filesystem is to maintain inode -+numbers. This is particularly important to support exporting a -+filesystem via NFS. Aufs is a virtual filesystem which doesn't have a -+backend block device for its own. But some storage is necessary to -+keep and maintain the inode numbers. It may be a large space and may not -+suit to keep in memory. Aufs rents some space from its first writable -+branch filesystem (by default) and creates file(s) on it. These files -+are created by aufs internally and removed soon (currently) keeping -+opened. -+Note: Because these files are removed, they are totally gone after -+ unmounting aufs. It means the inode numbers are not persistent -+ across unmount or reboot. I have a plan to make them really -+ persistent which will be important for aufs on NFS server. -+ -+4. Read/Write Files Internally (copy-on-write) -+Because a branch can be readonly, when you write a file on it, aufs will -+"copy-up" it to the upper writable branch internally. And then write the -+originally requested thing to the file. Generally kernel doesn't -+open/read/write file actively. In aufs, even a single write may cause a -+internal "file copy". This behaviour is very similar to cp(1) command. -+ -+Some people may think it is better to pass such work to user space -+helper, instead of doing in kernel space. Actually I am still thinking -+about it. But currently I have implemented it in kernel space. -diff -Nurp linux-4.4.3/Documentation/filesystems/aufs/design/02struct.txt linux-4.4.3-aufs/Documentation/filesystems/aufs/design/02struct.txt ---- linux-4.4.3/Documentation/filesystems/aufs/design/02struct.txt 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/Documentation/filesystems/aufs/design/02struct.txt 2016-03-02 17:59:37.357498660 +0200 -@@ -0,0 +1,245 @@ -+ -+# Copyright (C) 2005-2016 Junjiro R. Okajima -+ -+Basic Aufs Internal Structure -+ -+Superblock/Inode/Dentry/File Objects -+---------------------------------------------------------------------- -+As like an ordinary filesystem, aufs has its own -+superblock/inode/dentry/file objects. All these objects have a -+dynamically allocated array and store the same kind of pointers to the -+lower filesystem, branch. -+For example, when you build a union with one readwrite branch and one -+readonly, mounted /au, /rw and /ro respectively. -+- /au = /rw + /ro -+- /ro/fileA exists but /rw/fileA -+ -+Aufs lookup operation finds /ro/fileA and gets dentry for that. These -+pointers are stored in a aufs dentry. The array in aufs dentry will be, -+- [0] = NULL (because /rw/fileA doesn't exist) -+- [1] = /ro/fileA -+ -+This style of an array is essentially same to the aufs -+superblock/inode/dentry/file objects. -+ -+Because aufs supports manipulating branches, ie. add/delete/change -+branches dynamically, these objects has its own generation. When -+branches are changed, the generation in aufs superblock is -+incremented. And a generation in other object are compared when it is -+accessed. When a generation in other objects are obsoleted, aufs -+refreshes the internal array. -+ -+ -+Superblock -+---------------------------------------------------------------------- -+Additionally aufs superblock has some data for policies to select one -+among multiple writable branches, XIB files, pseudo-links and kobject. -+See below in detail. -+About the policies which supports copy-down a directory, see -+wbr_policy.txt too. -+ -+ -+Branch and XINO(External Inode Number Translation Table) -+---------------------------------------------------------------------- -+Every branch has its own xino (external inode number translation table) -+file. The xino file is created and unlinked by aufs internally. When two -+members of a union exist on the same filesystem, they share the single -+xino file. -+The struct of a xino file is simple, just a sequence of aufs inode -+numbers which is indexed by the lower inode number. -+In the above sample, assume the inode number of /ro/fileA is i111 and -+aufs assigns the inode number i999 for fileA. Then aufs writes 999 as -+4(8) bytes at 111 * 4(8) bytes offset in the xino file. -+ -+When the inode numbers are not contiguous, the xino file will be sparse -+which has a hole in it and doesn't consume as much disk space as it -+might appear. If your branch filesystem consumes disk space for such -+holes, then you should specify 'xino=' option at mounting aufs. -+ -+Aufs has a mount option to free the disk blocks for such holes in XINO -+files on tmpfs or ramdisk. But it is not so effective actually. If you -+meet a problem of disk shortage due to XINO files, then you should try -+"tmpfs-ino.patch" (and "vfs-ino.patch" too) in aufs4-standalone.git. -+The patch localizes the assignment inumbers per tmpfs-mount and avoid -+the holes in XINO files. -+ -+Also a writable branch has three kinds of "whiteout bases". All these -+are existed when the branch is joined to aufs, and their names are -+whiteout-ed doubly, so that users will never see their names in aufs -+hierarchy. -+1. a regular file which will be hardlinked to all whiteouts. -+2. a directory to store a pseudo-link. -+3. a directory to store an "orphan"-ed file temporary. -+ -+1. Whiteout Base -+ When you remove a file on a readonly branch, aufs handles it as a -+ logical deletion and creates a whiteout on the upper writable branch -+ as a hardlink of this file in order not to consume inode on the -+ writable branch. -+2. Pseudo-link Dir -+ See below, Pseudo-link. -+3. Step-Parent Dir -+ When "fileC" exists on the lower readonly branch only and it is -+ opened and removed with its parent dir, and then user writes -+ something into it, then aufs copies-up fileC to this -+ directory. Because there is no other dir to store fileC. After -+ creating a file under this dir, the file is unlinked. -+ -+Because aufs supports manipulating branches, ie. add/delete/change -+dynamically, a branch has its own id. When the branch order changes, -+aufs finds the new index by searching the branch id. -+ -+ -+Pseudo-link -+---------------------------------------------------------------------- -+Assume "fileA" exists on the lower readonly branch only and it is -+hardlinked to "fileB" on the branch. When you write something to fileA, -+aufs copies-up it to the upper writable branch. Additionally aufs -+creates a hardlink under the Pseudo-link Directory of the writable -+branch. The inode of a pseudo-link is kept in aufs super_block as a -+simple list. If fileB is read after unlinking fileA, aufs returns -+filedata from the pseudo-link instead of the lower readonly -+branch. Because the pseudo-link is based upon the inode, to keep the -+inode number by xino (see above) is essentially necessary. -+ -+All the hardlinks under the Pseudo-link Directory of the writable branch -+should be restored in a proper location later. Aufs provides a utility -+to do this. The userspace helpers executed at remounting and unmounting -+aufs by default. -+During this utility is running, it puts aufs into the pseudo-link -+maintenance mode. In this mode, only the process which began the -+maintenance mode (and its child processes) is allowed to operate in -+aufs. Some other processes which are not related to the pseudo-link will -+be allowed to run too, but the rest have to return an error or wait -+until the maintenance mode ends. If a process already acquires an inode -+mutex (in VFS), it has to return an error. -+ -+ -+XIB(external inode number bitmap) -+---------------------------------------------------------------------- -+Addition to the xino file per a branch, aufs has an external inode number -+bitmap in a superblock object. It is also an internal file such like a -+xino file. -+It is a simple bitmap to mark whether the aufs inode number is in-use or -+not. -+To reduce the file I/O, aufs prepares a single memory page to cache xib. -+ -+As well as XINO files, aufs has a feature to truncate/refresh XIB to -+reduce the number of consumed disk blocks for these files. -+ -+ -+Virtual or Vertical Dir, and Readdir in Userspace -+---------------------------------------------------------------------- -+In order to support multiple layers (branches), aufs readdir operation -+constructs a virtual dir block on memory. For readdir, aufs calls -+vfs_readdir() internally for each dir on branches, merges their entries -+with eliminating the whiteout-ed ones, and sets it to file (dir) -+object. So the file object has its entry list until it is closed. The -+entry list will be updated when the file position is zero and becomes -+obsoleted. This decision is made in aufs automatically. -+ -+The dynamically allocated memory block for the name of entries has a -+unit of 512 bytes (by default) and stores the names contiguously (no -+padding). Another block for each entry is handled by kmem_cache too. -+During building dir blocks, aufs creates hash list and judging whether -+the entry is whiteouted by its upper branch or already listed. -+The merged result is cached in the corresponding inode object and -+maintained by a customizable life-time option. -+ -+Some people may call it can be a security hole or invite DoS attack -+since the opened and once readdir-ed dir (file object) holds its entry -+list and becomes a pressure for system memory. But I'd say it is similar -+to files under /proc or /sys. The virtual files in them also holds a -+memory page (generally) while they are opened. When an idea to reduce -+memory for them is introduced, it will be applied to aufs too. -+For those who really hate this situation, I've developed readdir(3) -+library which operates this merging in userspace. You just need to set -+LD_PRELOAD environment variable, and aufs will not consume no memory in -+kernel space for readdir(3). -+ -+ -+Workqueue -+---------------------------------------------------------------------- -+Aufs sometimes requires privilege access to a branch. For instance, -+in copy-up/down operation. When a user process is going to make changes -+to a file which exists in the lower readonly branch only, and the mode -+of one of ancestor directories may not be writable by a user -+process. Here aufs copy-up the file with its ancestors and they may -+require privilege to set its owner/group/mode/etc. -+This is a typical case of a application character of aufs (see -+Introduction). -+ -+Aufs uses workqueue synchronously for this case. It creates its own -+workqueue. The workqueue is a kernel thread and has privilege. Aufs -+passes the request to call mkdir or write (for example), and wait for -+its completion. This approach solves a problem of a signal handler -+simply. -+If aufs didn't adopt the workqueue and changed the privilege of the -+process, then the process may receive the unexpected SIGXFSZ or other -+signals. -+ -+Also aufs uses the system global workqueue ("events" kernel thread) too -+for asynchronous tasks, such like handling inotify/fsnotify, re-creating a -+whiteout base and etc. This is unrelated to a privilege. -+Most of aufs operation tries acquiring a rw_semaphore for aufs -+superblock at the beginning, at the same time waits for the completion -+of all queued asynchronous tasks. -+ -+ -+Whiteout -+---------------------------------------------------------------------- -+The whiteout in aufs is very similar to Unionfs's. That is represented -+by its filename. UnionMount takes an approach of a file mode, but I am -+afraid several utilities (find(1) or something) will have to support it. -+ -+Basically the whiteout represents "logical deletion" which stops aufs to -+lookup further, but also it represents "dir is opaque" which also stop -+further lookup. -+ -+In aufs, rmdir(2) and rename(2) for dir uses whiteout alternatively. -+In order to make several functions in a single systemcall to be -+revertible, aufs adopts an approach to rename a directory to a temporary -+unique whiteouted name. -+For example, in rename(2) dir where the target dir already existed, aufs -+renames the target dir to a temporary unique whiteouted name before the -+actual rename on a branch, and then handles other actions (make it opaque, -+update the attributes, etc). If an error happens in these actions, aufs -+simply renames the whiteouted name back and returns an error. If all are -+succeeded, aufs registers a function to remove the whiteouted unique -+temporary name completely and asynchronously to the system global -+workqueue. -+ -+ -+Copy-up -+---------------------------------------------------------------------- -+It is a well-known feature or concept. -+When user modifies a file on a readonly branch, aufs operate "copy-up" -+internally and makes change to the new file on the upper writable branch. -+When the trigger systemcall does not update the timestamps of the parent -+dir, aufs reverts it after copy-up. -+ -+ -+Move-down (aufs3.9 and later) -+---------------------------------------------------------------------- -+"Copy-up" is one of the essential feature in aufs. It copies a file from -+the lower readonly branch to the upper writable branch when a user -+changes something about the file. -+"Move-down" is an opposite action of copy-up. Basically this action is -+ran manually instead of automatically and internally. -+For desgin and implementation, aufs has to consider these issues. -+- whiteout for the file may exist on the lower branch. -+- ancestor directories may not exist on the lower branch. -+- diropq for the ancestor directories may exist on the upper branch. -+- free space on the lower branch will reduce. -+- another access to the file may happen during moving-down, including -+ UDBA (see "Revalidate Dentry and UDBA"). -+- the file should not be hard-linked nor pseudo-linked. they should be -+ handled by auplink utility later. -+ -+Sometimes users want to move-down a file from the upper writable branch -+to the lower readonly or writable branch. For instance, -+- the free space of the upper writable branch is going to run out. -+- create a new intermediate branch between the upper and lower branch. -+- etc. -+ -+For this purpose, use "aumvdown" command in aufs-util.git. -diff -Nurp linux-4.4.3/Documentation/filesystems/aufs/design/03atomic_open.txt linux-4.4.3-aufs/Documentation/filesystems/aufs/design/03atomic_open.txt ---- linux-4.4.3/Documentation/filesystems/aufs/design/03atomic_open.txt 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/Documentation/filesystems/aufs/design/03atomic_open.txt 2016-03-02 17:59:37.357498660 +0200 -@@ -0,0 +1,72 @@ -+ -+# Copyright (C) 2015-2016 Junjiro R. Okajima -+ -+Support for a branch who has its ->atomic_open() -+---------------------------------------------------------------------- -+The filesystems who implement its ->atomic_open() are not majority. For -+example NFSv4 does, and aufs should call NFSv4 ->atomic_open, -+particularly for open(O_CREAT|O_EXCL, 0400) case. Other than -+->atomic_open(), NFSv4 returns an error for this open(2). While I am not -+sure whether all filesystems who have ->atomic_open() behave like this, -+but NFSv4 surely returns the error. -+ -+In order to support ->atomic_open() for aufs, there are a few -+approaches. -+ -+A. Introduce aufs_atomic_open() -+ - calls one of VFS:do_last(), lookup_open() or atomic_open() for -+ branch fs. -+B. Introduce aufs_atomic_open() calling create, open and chmod. this is -+ an aufs user Pip Cet's approach -+ - calls aufs_create(), VFS finish_open() and notify_change(). -+ - pass fake-mode to finish_open(), and then correct the mode by -+ notify_change(). -+C. Extend aufs_open() to call branch fs's ->atomic_open() -+ - no aufs_atomic_open(). -+ - aufs_lookup() registers the TID to an aufs internal object. -+ - aufs_create() does nothing when the matching TID is registered, but -+ registers the mode. -+ - aufs_open() calls branch fs's ->atomic_open() when the matching -+ TID is registered. -+D. Extend aufs_open() to re-try branch fs's ->open() with superuser's -+ credential -+ - no aufs_atomic_open(). -+ - aufs_create() registers the TID to an internal object. this info -+ represents "this process created this file just now." -+ - when aufs gets EACCES from branch fs's ->open(), then confirm the -+ registered TID and re-try open() with superuser's credential. -+ -+Pros and cons for each approach. -+ -+A. -+ - straightforward but highly depends upon VFS internal. -+ - the atomic behavaiour is kept. -+ - some of parameters such as nameidata are hard to reproduce for -+ branch fs. -+ - large overhead. -+B. -+ - easy to implement. -+ - the atomic behavaiour is lost. -+C. -+ - the atomic behavaiour is kept. -+ - dirty and tricky. -+ - VFS checks whether the file is created correctly after calling -+ ->create(), which means this approach doesn't work. -+D. -+ - easy to implement. -+ - the atomic behavaiour is lost. -+ - to open a file with superuser's credential and give it to a user -+ process is a bad idea, since the file object keeps the credential -+ in it. It may affect LSM or something. This approach doesn't work -+ either. -+ -+The approach A is ideal, but it hard to implement. So here is a -+variation of A, which is to be implemented. -+ -+A-1. Introduce aufs_atomic_open() -+ - calls branch fs ->atomic_open() if exists. otherwise calls -+ vfs_create() and finish_open(). -+ - the demerit is that the several checks after branch fs -+ ->atomic_open() are lost. in the ordinary case, the checks are -+ done by VFS:do_last(), lookup_open() and atomic_open(). some can -+ be implemented in aufs, but not all I am afraid. -diff -Nurp linux-4.4.3/Documentation/filesystems/aufs/design/03lookup.txt linux-4.4.3-aufs/Documentation/filesystems/aufs/design/03lookup.txt ---- linux-4.4.3/Documentation/filesystems/aufs/design/03lookup.txt 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/Documentation/filesystems/aufs/design/03lookup.txt 2016-03-02 17:59:37.358498667 +0200 -@@ -0,0 +1,100 @@ -+ -+# Copyright (C) 2005-2016 Junjiro R. Okajima -+ -+Lookup in a Branch -+---------------------------------------------------------------------- -+Since aufs has a character of sub-VFS (see Introduction), it operates -+lookup for branches as VFS does. It may be a heavy work. But almost all -+lookup operation in aufs is the simplest case, ie. lookup only an entry -+directly connected to its parent. Digging down the directory hierarchy -+is unnecessary. VFS has a function lookup_one_len() for that use, and -+aufs calls it. -+ -+When a branch is a remote filesystem, aufs basically relies upon its -+->d_revalidate(), also aufs forces the hardest revalidate tests for -+them. -+For d_revalidate, aufs implements three levels of revalidate tests. See -+"Revalidate Dentry and UDBA" in detail. -+ -+ -+Test Only the Highest One for the Directory Permission (dirperm1 option) -+---------------------------------------------------------------------- -+Let's try case study. -+- aufs has two branches, upper readwrite and lower readonly. -+ /au = /rw + /ro -+- "dirA" exists under /ro, but /rw. and its mode is 0700. -+- user invoked "chmod a+rx /au/dirA" -+- the internal copy-up is activated and "/rw/dirA" is created and its -+ permission bits are set to world readable. -+- then "/au/dirA" becomes world readable? -+ -+In this case, /ro/dirA is still 0700 since it exists in readonly branch, -+or it may be a natively readonly filesystem. If aufs respects the lower -+branch, it should not respond readdir request from other users. But user -+allowed it by chmod. Should really aufs rejects showing the entries -+under /ro/dirA? -+ -+To be honest, I don't have a good solution for this case. So aufs -+implements 'dirperm1' and 'nodirperm1' mount options, and leave it to -+users. -+When dirperm1 is specified, aufs checks only the highest one for the -+directory permission, and shows the entries. Otherwise, as usual, checks -+every dir existing on all branches and rejects the request. -+ -+As a side effect, dirperm1 option improves the performance of aufs -+because the number of permission check is reduced when the number of -+branch is many. -+ -+ -+Revalidate Dentry and UDBA (User's Direct Branch Access) -+---------------------------------------------------------------------- -+Generally VFS helpers re-validate a dentry as a part of lookup. -+0. digging down the directory hierarchy. -+1. lock the parent dir by its i_mutex. -+2. lookup the final (child) entry. -+3. revalidate it. -+4. call the actual operation (create, unlink, etc.) -+5. unlock the parent dir -+ -+If the filesystem implements its ->d_revalidate() (step 3), then it is -+called. Actually aufs implements it and checks the dentry on a branch is -+still valid. -+But it is not enough. Because aufs has to release the lock for the -+parent dir on a branch at the end of ->lookup() (step 2) and -+->d_revalidate() (step 3) while the i_mutex of the aufs dir is still -+held by VFS. -+If the file on a branch is changed directly, eg. bypassing aufs, after -+aufs released the lock, then the subsequent operation may cause -+something unpleasant result. -+ -+This situation is a result of VFS architecture, ->lookup() and -+->d_revalidate() is separated. But I never say it is wrong. It is a good -+design from VFS's point of view. It is just not suitable for sub-VFS -+character in aufs. -+ -+Aufs supports such case by three level of revalidation which is -+selectable by user. -+1. Simple Revalidate -+ Addition to the native flow in VFS's, confirm the child-parent -+ relationship on the branch just after locking the parent dir on the -+ branch in the "actual operation" (step 4). When this validation -+ fails, aufs returns EBUSY. ->d_revalidate() (step 3) in aufs still -+ checks the validation of the dentry on branches. -+2. Monitor Changes Internally by Inotify/Fsnotify -+ Addition to above, in the "actual operation" (step 4) aufs re-lookup -+ the dentry on the branch, and returns EBUSY if it finds different -+ dentry. -+ Additionally, aufs sets the inotify/fsnotify watch for every dir on branches -+ during it is in cache. When the event is notified, aufs registers a -+ function to kernel 'events' thread by schedule_work(). And the -+ function sets some special status to the cached aufs dentry and inode -+ private data. If they are not cached, then aufs has nothing to -+ do. When the same file is accessed through aufs (step 0-3) later, -+ aufs will detect the status and refresh all necessary data. -+ In this mode, aufs has to ignore the event which is fired by aufs -+ itself. -+3. No Extra Validation -+ This is the simplest test and doesn't add any additional revalidation -+ test, and skip the revalidation in step 4. It is useful and improves -+ aufs performance when system surely hide the aufs branches from user, -+ by over-mounting something (or another method). -diff -Nurp linux-4.4.3/Documentation/filesystems/aufs/design/04branch.txt linux-4.4.3-aufs/Documentation/filesystems/aufs/design/04branch.txt ---- linux-4.4.3/Documentation/filesystems/aufs/design/04branch.txt 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/Documentation/filesystems/aufs/design/04branch.txt 2016-03-02 17:59:37.358498667 +0200 -@@ -0,0 +1,61 @@ -+ -+# Copyright (C) 2005-2016 Junjiro R. Okajima -+ -+Branch Manipulation -+ -+Since aufs supports dynamic branch manipulation, ie. add/remove a branch -+and changing its permission/attribute, there are a lot of works to do. -+ -+ -+Add a Branch -+---------------------------------------------------------------------- -+o Confirm the adding dir exists outside of aufs, including loopback -+ mount, and its various attributes. -+o Initialize the xino file and whiteout bases if necessary. -+ See struct.txt. -+ -+o Check the owner/group/mode of the directory -+ When the owner/group/mode of the adding directory differs from the -+ existing branch, aufs issues a warning because it may impose a -+ security risk. -+ For example, when a upper writable branch has a world writable empty -+ top directory, a malicious user can create any files on the writable -+ branch directly, like copy-up and modify manually. If something like -+ /etc/{passwd,shadow} exists on the lower readonly branch but the upper -+ writable branch, and the writable branch is world-writable, then a -+ malicious guy may create /etc/passwd on the writable branch directly -+ and the infected file will be valid in aufs. -+ I am afraid it can be a security issue, but aufs can do nothing except -+ producing a warning. -+ -+ -+Delete a Branch -+---------------------------------------------------------------------- -+o Confirm the deleting branch is not busy -+ To be general, there is one merit to adopt "remount" interface to -+ manipulate branches. It is to discard caches. At deleting a branch, -+ aufs checks the still cached (and connected) dentries and inodes. If -+ there are any, then they are all in-use. An inode without its -+ corresponding dentry can be alive alone (for example, inotify/fsnotify case). -+ -+ For the cached one, aufs checks whether the same named entry exists on -+ other branches. -+ If the cached one is a directory, because aufs provides a merged view -+ to users, as long as one dir is left on any branch aufs can show the -+ dir to users. In this case, the branch can be removed from aufs. -+ Otherwise aufs rejects deleting the branch. -+ -+ If any file on the deleting branch is opened by aufs, then aufs -+ rejects deleting. -+ -+ -+Modify the Permission of a Branch -+---------------------------------------------------------------------- -+o Re-initialize or remove the xino file and whiteout bases if necessary. -+ See struct.txt. -+ -+o rw --> ro: Confirm the modifying branch is not busy -+ Aufs rejects the request if any of these conditions are true. -+ - a file on the branch is mmap-ed. -+ - a regular file on the branch is opened for write and there is no -+ same named entry on the upper branch. -diff -Nurp linux-4.4.3/Documentation/filesystems/aufs/design/05wbr_policy.txt linux-4.4.3-aufs/Documentation/filesystems/aufs/design/05wbr_policy.txt ---- linux-4.4.3/Documentation/filesystems/aufs/design/05wbr_policy.txt 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/Documentation/filesystems/aufs/design/05wbr_policy.txt 2016-03-02 17:59:37.358498667 +0200 -@@ -0,0 +1,51 @@ -+ -+# Copyright (C) 2005-2016 Junjiro R. Okajima -+ -+Policies to Select One among Multiple Writable Branches -+---------------------------------------------------------------------- -+When the number of writable branch is more than one, aufs has to decide -+the target branch for file creation or copy-up. By default, the highest -+writable branch which has the parent (or ancestor) dir of the target -+file is chosen (top-down-parent policy). -+By user's request, aufs implements some other policies to select the -+writable branch, for file creation several policies, round-robin, -+most-free-space, and other policies. For copy-up, top-down-parent, -+bottom-up-parent, bottom-up and others. -+ -+As expected, the round-robin policy selects the branch in circular. When -+you have two writable branches and creates 10 new files, 5 files will be -+created for each branch. mkdir(2) systemcall is an exception. When you -+create 10 new directories, all will be created on the same branch. -+And the most-free-space policy selects the one which has most free -+space among the writable branches. The amount of free space will be -+checked by aufs internally, and users can specify its time interval. -+ -+The policies for copy-up is more simple, -+top-down-parent is equivalent to the same named on in create policy, -+bottom-up-parent selects the writable branch where the parent dir -+exists and the nearest upper one from the copyup-source, -+bottom-up selects the nearest upper writable branch from the -+copyup-source, regardless the existence of the parent dir. -+ -+There are some rules or exceptions to apply these policies. -+- If there is a readonly branch above the policy-selected branch and -+ the parent dir is marked as opaque (a variation of whiteout), or the -+ target (creating) file is whiteout-ed on the upper readonly branch, -+ then the result of the policy is ignored and the target file will be -+ created on the nearest upper writable branch than the readonly branch. -+- If there is a writable branch above the policy-selected branch and -+ the parent dir is marked as opaque or the target file is whiteouted -+ on the branch, then the result of the policy is ignored and the target -+ file will be created on the highest one among the upper writable -+ branches who has diropq or whiteout. In case of whiteout, aufs removes -+ it as usual. -+- link(2) and rename(2) systemcalls are exceptions in every policy. -+ They try selecting the branch where the source exists as possible -+ since copyup a large file will take long time. If it can't be, -+ ie. the branch where the source exists is readonly, then they will -+ follow the copyup policy. -+- There is an exception for rename(2) when the target exists. -+ If the rename target exists, aufs compares the index of the branches -+ 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 -Nurp linux-4.4.3/Documentation/filesystems/aufs/design/06fhsm.txt linux-4.4.3-aufs/Documentation/filesystems/aufs/design/06fhsm.txt ---- linux-4.4.3/Documentation/filesystems/aufs/design/06fhsm.txt 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/Documentation/filesystems/aufs/design/06fhsm.txt 2016-03-02 17:59:37.358498667 +0200 -@@ -0,0 +1,105 @@ -+ -+# Copyright (C) 2011-2016 Junjiro R. Okajima -+ -+File-based Hierarchical Storage Management (FHSM) -+---------------------------------------------------------------------- -+Hierarchical Storage Management (or HSM) is a well-known feature in the -+storage world. Aufs provides this feature as file-based with multiple -+writable branches, based upon the principle of "Colder, the Lower". -+Here the word "colder" means that the less used files, and "lower" means -+that the position in the order of the stacked branches vertically. -+These multiple writable branches are prioritized, ie. the topmost one -+should be the fastest drive and be used heavily. -+ -+o Characters in aufs FHSM story -+- aufs itself and a new branch attribute. -+- a new ioctl interface to move-down and to establish a connection with -+ the daemon ("move-down" is a converse of "copy-up"). -+- userspace tool and daemon. -+ -+The userspace daemon establishes a connection with aufs and waits for -+the notification. The notified information is very similar to struct -+statfs containing the number of consumed blocks and inodes. -+When the consumed blocks/inodes of a branch exceeds the user-specified -+upper watermark, the daemon activates its move-down process until the -+consumed blocks/inodes reaches the user-specified lower watermark. -+ -+The actual move-down is done by aufs based upon the request from -+user-space since we need to maintain the inode number and the internal -+pointer arrays in aufs. -+ -+Currently aufs FHSM handles the regular files only. Additionally they -+must not be hard-linked nor pseudo-linked. -+ -+ -+o Cowork of aufs and the user-space daemon -+ During the userspace daemon established the connection, aufs sends a -+ small notification to it whenever aufs writes something into the -+ writable branch. But it may cost high since aufs issues statfs(2) -+ internally. So user can specify a new option to cache the -+ info. Actually the notification is controlled by these factors. -+ + the specified cache time. -+ + classified as "force" by aufs internally. -+ Until the specified time expires, aufs doesn't send the info -+ except the forced cases. When aufs decide forcing, the info is always -+ notified to userspace. -+ For example, the number of free inodes is generally large enough and -+ the shortage of it happens rarely. So aufs doesn't force the -+ notification when creating a new file, directory and others. This is -+ the typical case which aufs doesn't force. -+ When aufs writes the actual filedata and the files consumes any of new -+ blocks, the aufs forces notifying. -+ -+ -+o Interfaces in aufs -+- New branch attribute. -+ + fhsm -+ Specifies that the branch is managed by FHSM feature. In other word, -+ participant in the FHSM. -+ When nofhsm is set to the branch, it will not be the source/target -+ branch of the move-down operation. This attribute is set -+ independently from coo and moo attributes, and if you want full -+ FHSM, you should specify them as well. -+- New mount option. -+ + fhsm_sec -+ Specifies a second to suppress many less important info to be -+ notified. -+- New ioctl. -+ + AUFS_CTL_FHSM_FD -+ create a new file descriptor which userspace can read the notification -+ (a subset of struct statfs) from aufs. -+- Module parameter 'brs' -+ It has to be set to 1. Otherwise the new mount option 'fhsm' will not -+ be set. -+- mount helpers /sbin/mount.aufs and /sbin/umount.aufs -+ When there are two or more branches with fhsm attributes, -+ /sbin/mount.aufs invokes the user-space daemon and /sbin/umount.aufs -+ terminates it. As a result of remounting and branch-manipulation, the -+ number of branches with fhsm attribute can be one. In this case, -+ /sbin/mount.aufs will terminate the user-space daemon. -+ -+ -+Finally the operation is done as these steps in kernel-space. -+- make sure that, -+ + no one else is using the file. -+ + the file is not hard-linked. -+ + the file is not pseudo-linked. -+ + the file is a regular file. -+ + the parent dir is not opaqued. -+- find the target writable branch. -+- make sure the file is not whiteout-ed by the upper (than the target) -+ branch. -+- make the parent dir on the target branch. -+- mutex lock the inode on the branch. -+- unlink the whiteout on the target branch (if exists). -+- lookup and create the whiteout-ed temporary name on the target branch. -+- copy the file as the whiteout-ed temporary name on the target branch. -+- rename the whiteout-ed temporary name to the original name. -+- unlink the file on the source branch. -+- maintain the internal pointer array and the external inode number -+ table (XINO). -+- maintain the timestamps and other attributes of the parent dir and the -+ file. -+ -+And of course, in every step, an error may happen. So the operation -+should restore the original file state after an error happens. -diff -Nurp linux-4.4.3/Documentation/filesystems/aufs/design/06mmap.txt linux-4.4.3-aufs/Documentation/filesystems/aufs/design/06mmap.txt ---- linux-4.4.3/Documentation/filesystems/aufs/design/06mmap.txt 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/Documentation/filesystems/aufs/design/06mmap.txt 2016-03-02 17:59:37.358498667 +0200 -@@ -0,0 +1,59 @@ -+ -+# Copyright (C) 2005-2016 Junjiro R. Okajima -+ -+mmap(2) -- File Memory Mapping -+---------------------------------------------------------------------- -+In aufs, the file-mapped pages are handled by a branch fs directly, no -+interaction with aufs. It means aufs_mmap() calls the branch fs's -+->mmap(). -+This approach is simple and good, but there is one problem. -+Under /proc, several entries show the mmapped files by its path (with -+device and inode number), and the printed path will be the path on the -+branch fs's instead of virtual aufs's. -+This is not a problem in most cases, but some utilities lsof(1) (and its -+user) may expect the path on aufs. -+ -+To address this issue, aufs adds a new member called vm_prfile in struct -+vm_area_struct (and struct vm_region). The original vm_file points to -+the file on the branch fs in order to handle everything correctly as -+usual. The new vm_prfile points to a virtual file in aufs, and the -+show-functions in procfs refers to vm_prfile if it is set. -+Also we need to maintain several other places where touching vm_file -+such like -+- fork()/clone() copies vma and the reference count of vm_file is -+ incremented. -+- merging vma maintains the ref count too. -+ -+This is not a good approach. It just fakes the printed path. But it -+leaves all behaviour around f_mapping unchanged. This is surely an -+advantage. -+Actually aufs had adopted another complicated approach which calls -+generic_file_mmap() and handles struct vm_operations_struct. In this -+approach, aufs met a hard problem and I could not solve it without -+switching the approach. -+ -+There may be one more another approach which is -+- bind-mount the branch-root onto the aufs-root internally -+- grab the new vfsmount (ie. struct mount) -+- lazy-umount the branch-root internally -+- in open(2) the aufs-file, open the branch-file with the hidden -+ vfsmount (instead of the original branch's vfsmount) -+- ideally this "bind-mount and lazy-umount" should be done atomically, -+ but it may be possible from userspace by the mount helper. -+ -+Adding the internal hidden vfsmount and using it in opening a file, the -+file path under /proc will be printed correctly. This approach looks -+smarter, but is not possible I am afraid. -+- aufs-root may be bind-mount later. when it happens, another hidden -+ vfsmount will be required. -+- it is hard to get the chance to bind-mount and lazy-umount -+ + in kernel-space, FS can have vfsmount in open(2) via -+ file->f_path, and aufs can know its vfsmount. But several locks are -+ already acquired, and if aufs tries to bind-mount and lazy-umount -+ here, then it may cause a deadlock. -+ + in user-space, bind-mount doesn't invoke the mount helper. -+- since /proc shows dev and ino, aufs has to give vma these info. it -+ means a new member vm_prinode will be necessary. this is essentially -+ equivalent to vm_prfile described above. -+ -+I have to give up this "looks-smater" approach. -diff -Nurp linux-4.4.3/Documentation/filesystems/aufs/design/06xattr.txt linux-4.4.3-aufs/Documentation/filesystems/aufs/design/06xattr.txt ---- linux-4.4.3/Documentation/filesystems/aufs/design/06xattr.txt 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/Documentation/filesystems/aufs/design/06xattr.txt 2016-03-02 17:59:37.358498667 +0200 -@@ -0,0 +1,81 @@ -+ -+# Copyright (C) 2014-2016 Junjiro R. Okajima -+ -+Listing XATTR/EA and getting the value -+---------------------------------------------------------------------- -+For the inode standard attributes (owner, group, timestamps, etc.), aufs -+shows the values from the topmost existing file. This behaviour is good -+for the non-dir entries since the bahaviour exactly matches the shown -+information. But for the directories, aufs considers all the same named -+entries on the lower branches. Which means, if one of the lower entry -+rejects readdir call, then aufs returns an error even if the topmost -+entry allows it. This behaviour is necessary to respect the branch fs's -+security, but can make users confused since the user-visible standard -+attributes don't match the behaviour. -+To address this issue, aufs has a mount option called dirperm1 which -+checks the permission for the topmost entry only, and ignores the lower -+entry's permission. -+ -+A similar issue can happen around XATTR. -+getxattr(2) and listxattr(2) families behave as if dirperm1 option is -+always set. Otherwise these very unpleasant situation would happen. -+- listxattr(2) may return the duplicated entries. -+- users may not be able to remove or reset the XATTR forever, -+ -+ -+XATTR/EA support in the internal (copy,move)-(up,down) -+---------------------------------------------------------------------- -+Generally the extended attributes of inode are categorized as these. -+- "security" for LSM and capability. -+- "system" for posix ACL, 'acl' mount option is required for the branch -+ fs generally. -+- "trusted" for userspace, CAP_SYS_ADMIN is required. -+- "user" for userspace, 'user_xattr' mount option is required for the -+ branch fs generally. -+ -+Moreover there are some other categories. Aufs handles these rather -+unpopular categories as the ordinary ones, ie. there is no special -+condition nor exception. -+ -+In copy-up, the support for XATTR on the dst branch may differ from the -+src branch. In this case, the copy-up operation will get an error and -+the original user operation which triggered the copy-up will fail. It -+can happen that even all copy-up will fail. -+When both of src and dst branches support XATTR and if an error occurs -+during copying XATTR, then the copy-up should fail obviously. That is a -+good reason and aufs should return an error to userspace. But when only -+the src branch support that XATTR, aufs should not return an error. -+For example, the src branch supports ACL but the dst branch doesn't -+because the dst branch may natively un-support it or temporary -+un-support it due to "noacl" mount option. Of course, the dst branch fs -+may NOT return an error even if the XATTR is not supported. It is -+totally up to the branch fs. -+ -+Anyway when the aufs internal copy-up gets an error from the dst branch -+fs, then aufs tries removing the just copied entry and returns the error -+to the userspace. The worst case of this situation will be all copy-up -+will fail. -+ -+For the copy-up operation, there two basic approaches. -+- copy the specified XATTR only (by category above), and return the -+ error unconditionally if it happens. -+- copy all XATTR, and ignore the error on the specified category only. -+ -+In order to support XATTR and to implement the correct behaviour, aufs -+chooses the latter approach and introduces some new branch attributes, -+"icexsec", "icexsys", "icextr", "icexusr", and "icexoth". -+They correspond to the XATTR namespaces (see above). Additionally, to be -+convenient, "icex" is also provided which means all "icex*" attributes -+are set (here the word "icex" stands for "ignore copy-error on XATTR"). -+ -+The meaning of these attributes is to ignore the error from setting -+XATTR on that branch. -+Note that aufs tries copying all XATTR unconditionally, and ignores the -+error from the dst branch according to the specified attributes. -+ -+Some XATTR may have its default value. The default value may come from -+the parent dir or the environment. If the default value is set at the -+file creating-time, it will be overwritten by copy-up. -+Some contradiction may happen I am afraid. -+Do we need another attribute to stop copying XATTR? I am unsure. For -+now, aufs implements the branch attributes to ignore the error. -diff -Nurp linux-4.4.3/Documentation/filesystems/aufs/design/07export.txt linux-4.4.3-aufs/Documentation/filesystems/aufs/design/07export.txt ---- linux-4.4.3/Documentation/filesystems/aufs/design/07export.txt 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/Documentation/filesystems/aufs/design/07export.txt 2016-03-02 17:59:37.358498667 +0200 -@@ -0,0 +1,45 @@ -+ -+# Copyright (C) 2005-2016 Junjiro R. Okajima -+ -+Export Aufs via NFS -+---------------------------------------------------------------------- -+Here is an approach. -+- like xino/xib, add a new file 'xigen' which stores aufs inode -+ generation. -+- iget_locked(): initialize aufs inode generation for a new inode, and -+ store it in xigen file. -+- destroy_inode(): increment aufs inode generation and store it in xigen -+ file. it is necessary even if it is not unlinked, because any data of -+ inode may be changed by UDBA. -+- encode_fh(): for a root dir, simply return FILEID_ROOT. otherwise -+ build file handle by -+ + branch id (4 bytes) -+ + superblock generation (4 bytes) -+ + inode number (4 or 8 bytes) -+ + parent dir inode number (4 or 8 bytes) -+ + inode generation (4 bytes)) -+ + return value of exportfs_encode_fh() for the parent on a branch (4 -+ bytes) -+ + file handle for a branch (by exportfs_encode_fh()) -+- fh_to_dentry(): -+ + find the index of a branch from its id in handle, and check it is -+ still exist in aufs. -+ + 1st level: get the inode number from handle and search it in cache. -+ + 2nd level: if not found in cache, get the parent inode number from -+ the handle and search it in cache. and then open the found parent -+ dir, find the matching inode number by vfs_readdir() and get its -+ name, and call lookup_one_len() for the target dentry. -+ + 3rd level: if the parent dir is not cached, call -+ exportfs_decode_fh() for a branch and get the parent on a branch, -+ build a pathname of it, convert it a pathname in aufs, call -+ path_lookup(). now aufs gets a parent dir dentry, then handle it as -+ the 2nd level. -+ + to open the dir, aufs needs struct vfsmount. aufs keeps vfsmount -+ for every branch, but not itself. to get this, (currently) aufs -+ searches in current->nsproxy->mnt_ns list. it may not be a good -+ idea, but I didn't get other approach. -+ + test the generation of the gotten inode. -+- every inode operation: they may get EBUSY due to UDBA. in this case, -+ convert it into ESTALE for NFSD. -+- readdir(): call lockdep_on/off() because filldir in NFSD calls -+ lookup_one_len(), vfs_getattr(), encode_fh() and others. -diff -Nurp linux-4.4.3/Documentation/filesystems/aufs/design/08shwh.txt linux-4.4.3-aufs/Documentation/filesystems/aufs/design/08shwh.txt ---- linux-4.4.3/Documentation/filesystems/aufs/design/08shwh.txt 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/Documentation/filesystems/aufs/design/08shwh.txt 2016-03-02 17:59:37.358498667 +0200 -@@ -0,0 +1,39 @@ -+ -+# Copyright (C) 2005-2016 Junjiro R. Okajima -+ -+Show Whiteout Mode (shwh) -+---------------------------------------------------------------------- -+Generally aufs hides the name of whiteouts. But in some cases, to show -+them is very useful for users. For instance, creating a new middle layer -+(branch) by merging existing layers. -+ -+(borrowing aufs1 HOW-TO from a user, Michael Towers) -+When you have three branches, -+- Bottom: 'system', squashfs (underlying base system), read-only -+- Middle: 'mods', squashfs, read-only -+- Top: 'overlay', ram (tmpfs), read-write -+ -+The top layer is loaded at boot time and saved at shutdown, to preserve -+the changes made to the system during the session. -+When larger changes have been made, or smaller changes have accumulated, -+the size of the saved top layer data grows. At this point, it would be -+nice to be able to merge the two overlay branches ('mods' and 'overlay') -+and rewrite the 'mods' squashfs, clearing the top layer and thus -+restoring save and load speed. -+ -+This merging is simplified by the use of another aufs mount, of just the -+two overlay branches using the 'shwh' option. -+# mount -t aufs -o ro,shwh,br:/livesys/overlay=ro+wh:/livesys/mods=rr+wh \ -+ aufs /livesys/merge_union -+ -+A merged view of these two branches is then available at -+/livesys/merge_union, and the new feature is that the whiteouts are -+visible! -+Note that in 'shwh' mode the aufs mount must be 'ro', which will disable -+writing to all branches. Also the default mode for all branches is 'ro'. -+It is now possible to save the combined contents of the two overlay -+branches to a new squashfs, e.g.: -+# mksquashfs /livesys/merge_union /path/to/newmods.squash -+ -+This new squashfs archive can be stored on the boot device and the -+initramfs will use it to replace the old one at the next boot. -diff -Nurp linux-4.4.3/Documentation/filesystems/aufs/design/10dynop.txt linux-4.4.3-aufs/Documentation/filesystems/aufs/design/10dynop.txt ---- linux-4.4.3/Documentation/filesystems/aufs/design/10dynop.txt 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/Documentation/filesystems/aufs/design/10dynop.txt 2016-03-02 17:59:37.359498674 +0200 -@@ -0,0 +1,34 @@ -+ -+# Copyright (C) 2010-2016 Junjiro R. Okajima -+ -+Dynamically customizable FS operations -+---------------------------------------------------------------------- -+Generally FS operations (struct inode_operations, struct -+address_space_operations, struct file_operations, etc.) are defined as -+"static const", but it never means that FS have only one set of -+operation. Some FS have multiple sets of them. For instance, ext2 has -+three sets, one for XIP, for NOBH, and for normal. -+Since aufs overrides and redirects these operations, sometimes aufs has -+to change its behaviour according to the branch FS type. More importantly -+VFS acts differently if a function (member in the struct) is set or -+not. It means aufs should have several sets of operations and select one -+among them according to the branch FS definition. -+ -+In order to solve this problem and not to affect the behaviour of VFS, -+aufs defines these operations dynamically. For instance, aufs defines -+dummy direct_IO function for struct address_space_operations, but it may -+not be set to the address_space_operations actually. When the branch FS -+doesn't have it, aufs doesn't set it to its address_space_operations -+while the function definition itself is still alive. So the behaviour -+itself will not change, and it will return an error when direct_IO is -+not set. -+ -+The lifetime of these dynamically generated operation object is -+maintained by aufs branch object. When the branch is removed from aufs, -+the reference counter of the object is decremented. When it reaches -+zero, the dynamically generated operation object will be freed. -+ -+This approach is designed to support AIO (io_submit), Direct I/O and -+XIP (DAX) mainly. -+Currently this approach is applied to address_space_operations for -+regular files only. -diff -Nurp linux-4.4.3/Documentation/filesystems/aufs/README linux-4.4.3-aufs/Documentation/filesystems/aufs/README ---- linux-4.4.3/Documentation/filesystems/aufs/README 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/Documentation/filesystems/aufs/README 2016-03-02 17:59:37.357498660 +0200 -@@ -0,0 +1,391 @@ +diff --git a/Documentation/filesystems/aufs/README b/Documentation/filesystems/aufs/README +new file mode 100644 +index 0000000..36df674 +--- /dev/null ++++ b/Documentation/filesystems/aufs/README +@@ -0,0 +1,392 @@ + +Aufs4 -- advanced multi layered unification filesystem version 4.x +http://aufs.sf.net @@ -1676,6 +585,7 @@ diff -Nurp linux-4.4.3/Documentation/filesystems/aufs/README linux-4.4.3-aufs/Do +Stefano Di Biase made a donation (2014/8). +Daniel Epellei made a donation (2015/1). +OmegaPhil made a donation (2016/1). ++Tomasz Szewczyk made a donation (2016/4). + +Thank you very much. +Donations are always, including future donations, very important and @@ -1693,10 +603,1156 @@ diff -Nurp linux-4.4.3/Documentation/filesystems/aufs/README linux-4.4.3-aufs/Do +# Local variables: ; +# mode: text; +# End: ; -diff -Nurp linux-4.4.3/drivers/block/loop.c linux-4.4.3-aufs/drivers/block/loop.c ---- linux-4.4.3/drivers/block/loop.c 2016-01-11 01:01:32.000000000 +0200 -+++ linux-4.4.3-aufs/drivers/block/loop.c 2016-03-02 17:59:37.360498681 +0200 -@@ -706,6 +706,24 @@ static inline int is_loop_device(struct +diff --git a/Documentation/filesystems/aufs/design/01intro.txt b/Documentation/filesystems/aufs/design/01intro.txt +new file mode 100644 +index 0000000..5d01214 +--- /dev/null ++++ b/Documentation/filesystems/aufs/design/01intro.txt +@@ -0,0 +1,157 @@ ++ ++# Copyright (C) 2005-2016 Junjiro R. Okajima ++ ++Introduction ++---------------------------------------- ++ ++aufs [ei ju: 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. ++ ++AUFS is a filesystem with features: ++- multi layered stackable unification filesystem, the member directory ++ is called as a branch. ++- branch permission and attribute, 'readonly', 'real-readonly', ++ 'readwrite', 'whiteout-able', 'link-able whiteout', etc. and their ++ combination. ++- internal "file copy-on-write". ++- logical deletion, whiteout. ++- dynamic branch manipulation, adding, deleting and changing permission. ++- allow bypassing aufs, user's direct branch access. ++- external inode number translation table and bitmap which maintains the ++ persistent aufs inode number. ++- seekable directory, including NFS readdir. ++- file mapping, mmap and sharing pages. ++- pseudo-link, hardlink over branches. ++- loopback mounted filesystem as a branch. ++- several policies to select one among multiple writable branches. ++- revert a single systemcall when an error occurs in aufs. ++- and more... ++ ++ ++Multi Layered Stackable Unification Filesystem ++---------------------------------------------------------------------- ++Most people already knows what it is. ++It is a filesystem which unifies several directories and provides a ++merged single directory. When users access a file, the access will be ++passed/re-directed/converted (sorry, I am not sure which English word is ++correct) to the real file on the member filesystem. The member ++filesystem is called 'lower filesystem' or 'branch' and has a mode ++'readonly' and 'readwrite.' And the deletion for a file on the lower ++readonly branch is handled by creating 'whiteout' on the upper writable ++branch. ++ ++On LKML, there have been discussions about UnionMount (Jan Blunck, ++Bharata B Rao and Valerie Aurora) and Unionfs (Erez Zadok). They took ++different approaches to implement the merged-view. ++The former tries putting it into VFS, and the latter implements as a ++separate filesystem. ++(If I misunderstand about these implementations, please let me know and ++I shall correct it. Because it is a long time ago when I read their ++source files last time). ++ ++UnionMount's approach will be able to small, but may be hard to share ++branches between several UnionMount since the whiteout in it is ++implemented in the inode on branch filesystem and always ++shared. According to Bharata's post, readdir does not seems to be ++finished yet. ++There are several missing features known in this implementations such as ++- for users, the inode number may change silently. eg. copy-up. ++- link(2) may break by copy-up. ++- read(2) may get an obsoleted filedata (fstat(2) too). ++- fcntl(F_SETLK) may be broken by copy-up. ++- unnecessary copy-up may happen, for example mmap(MAP_PRIVATE) after ++ open(O_RDWR). ++ ++In linux-3.18, "overlay" filesystem (formerly known as "overlayfs") was ++merged into mainline. This is another implementation of UnionMount as a ++separated filesystem. All the limitations and known problems which ++UnionMount are equally inherited to "overlay" filesystem. ++ ++Unionfs has a longer history. When I started implementing a stackable ++filesystem (Aug 2005), it already existed. It has virtual super_block, ++inode, dentry and file objects and they have an array pointing lower ++same kind objects. After contributing many patches for Unionfs, I ++re-started my project AUFS (Jun 2006). ++ ++In AUFS, the structure of filesystem resembles to Unionfs, but I ++implemented my own ideas, approaches and enhancements and it became ++totally different one. ++ ++Comparing DM snapshot and fs based implementation ++- the number of bytes to be copied between devices is much smaller. ++- the type of filesystem must be one and only. ++- the fs must be writable, no readonly fs, even for the lower original ++ device. so the compression fs will not be usable. but if we use ++ loopback mount, we may address this issue. ++ for instance, ++ mount /cdrom/squashfs.img /sq ++ losetup /sq/ext2.img ++ losetup /somewhere/cow ++ dmsetup "snapshot /dev/loop0 /dev/loop1 ..." ++- it will be difficult (or needs more operations) to extract the ++ difference between the original device and COW. ++- DM snapshot-merge may help a lot when users try merging. in the ++ fs-layer union, users will use rsync(1). ++ ++You may want to read my old paper "Filesystems in LiveCD" ++(http://aufs.sourceforge.net/aufs2/report/sq/sq.pdf). ++ ++ ++Several characters/aspects/persona of aufs ++---------------------------------------------------------------------- ++ ++Aufs has several characters, aspects or persona. ++1. a filesystem, callee of VFS helper ++2. sub-VFS, caller of VFS helper for branches ++3. a virtual filesystem which maintains persistent inode number ++4. reader/writer of files on branches such like an application ++ ++1. Callee of VFS Helper ++As an ordinary linux filesystem, aufs is a callee of VFS. For instance, ++unlink(2) from an application reaches sys_unlink() kernel function and ++then vfs_unlink() is called. vfs_unlink() is one of VFS helper and it ++calls filesystem specific unlink operation. Actually aufs implements the ++unlink operation but it behaves like a redirector. ++ ++2. Caller of VFS Helper for Branches ++aufs_unlink() passes the unlink request to the branch filesystem as if ++it were called from VFS. So the called unlink operation of the branch ++filesystem acts as usual. As a caller of VFS helper, aufs should handle ++every necessary pre/post operation for the branch filesystem. ++- acquire the lock for the parent dir on a branch ++- lookup in a branch ++- revalidate dentry on a branch ++- mnt_want_write() for a branch ++- vfs_unlink() for a branch ++- mnt_drop_write() for a branch ++- release the lock on a branch ++ ++3. Persistent Inode Number ++One of the most important issue for a filesystem is to maintain inode ++numbers. This is particularly important to support exporting a ++filesystem via NFS. Aufs is a virtual filesystem which doesn't have a ++backend block device for its own. But some storage is necessary to ++keep and maintain the inode numbers. It may be a large space and may not ++suit to keep in memory. Aufs rents some space from its first writable ++branch filesystem (by default) and creates file(s) on it. These files ++are created by aufs internally and removed soon (currently) keeping ++opened. ++Note: Because these files are removed, they are totally gone after ++ unmounting aufs. It means the inode numbers are not persistent ++ across unmount or reboot. I have a plan to make them really ++ persistent which will be important for aufs on NFS server. ++ ++4. Read/Write Files Internally (copy-on-write) ++Because a branch can be readonly, when you write a file on it, aufs will ++"copy-up" it to the upper writable branch internally. And then write the ++originally requested thing to the file. Generally kernel doesn't ++open/read/write file actively. In aufs, even a single write may cause a ++internal "file copy". This behaviour is very similar to cp(1) command. ++ ++Some people may think it is better to pass such work to user space ++helper, instead of doing in kernel space. Actually I am still thinking ++about it. But currently I have implemented it in kernel space. +diff --git a/Documentation/filesystems/aufs/design/02struct.txt b/Documentation/filesystems/aufs/design/02struct.txt +new file mode 100644 +index 0000000..783328a +--- /dev/null ++++ b/Documentation/filesystems/aufs/design/02struct.txt +@@ -0,0 +1,245 @@ ++ ++# Copyright (C) 2005-2016 Junjiro R. Okajima ++ ++Basic Aufs Internal Structure ++ ++Superblock/Inode/Dentry/File Objects ++---------------------------------------------------------------------- ++As like an ordinary filesystem, aufs has its own ++superblock/inode/dentry/file objects. All these objects have a ++dynamically allocated array and store the same kind of pointers to the ++lower filesystem, branch. ++For example, when you build a union with one readwrite branch and one ++readonly, mounted /au, /rw and /ro respectively. ++- /au = /rw + /ro ++- /ro/fileA exists but /rw/fileA ++ ++Aufs lookup operation finds /ro/fileA and gets dentry for that. These ++pointers are stored in a aufs dentry. The array in aufs dentry will be, ++- [0] = NULL (because /rw/fileA doesn't exist) ++- [1] = /ro/fileA ++ ++This style of an array is essentially same to the aufs ++superblock/inode/dentry/file objects. ++ ++Because aufs supports manipulating branches, ie. add/delete/change ++branches dynamically, these objects has its own generation. When ++branches are changed, the generation in aufs superblock is ++incremented. And a generation in other object are compared when it is ++accessed. When a generation in other objects are obsoleted, aufs ++refreshes the internal array. ++ ++ ++Superblock ++---------------------------------------------------------------------- ++Additionally aufs superblock has some data for policies to select one ++among multiple writable branches, XIB files, pseudo-links and kobject. ++See below in detail. ++About the policies which supports copy-down a directory, see ++wbr_policy.txt too. ++ ++ ++Branch and XINO(External Inode Number Translation Table) ++---------------------------------------------------------------------- ++Every branch has its own xino (external inode number translation table) ++file. The xino file is created and unlinked by aufs internally. When two ++members of a union exist on the same filesystem, they share the single ++xino file. ++The struct of a xino file is simple, just a sequence of aufs inode ++numbers which is indexed by the lower inode number. ++In the above sample, assume the inode number of /ro/fileA is i111 and ++aufs assigns the inode number i999 for fileA. Then aufs writes 999 as ++4(8) bytes at 111 * 4(8) bytes offset in the xino file. ++ ++When the inode numbers are not contiguous, the xino file will be sparse ++which has a hole in it and doesn't consume as much disk space as it ++might appear. If your branch filesystem consumes disk space for such ++holes, then you should specify 'xino=' option at mounting aufs. ++ ++Aufs has a mount option to free the disk blocks for such holes in XINO ++files on tmpfs or ramdisk. But it is not so effective actually. If you ++meet a problem of disk shortage due to XINO files, then you should try ++"tmpfs-ino.patch" (and "vfs-ino.patch" too) in aufs4-standalone.git. ++The patch localizes the assignment inumbers per tmpfs-mount and avoid ++the holes in XINO files. ++ ++Also a writable branch has three kinds of "whiteout bases". All these ++are existed when the branch is joined to aufs, and their names are ++whiteout-ed doubly, so that users will never see their names in aufs ++hierarchy. ++1. a regular file which will be hardlinked to all whiteouts. ++2. a directory to store a pseudo-link. ++3. a directory to store an "orphan"-ed file temporary. ++ ++1. Whiteout Base ++ When you remove a file on a readonly branch, aufs handles it as a ++ logical deletion and creates a whiteout on the upper writable branch ++ as a hardlink of this file in order not to consume inode on the ++ writable branch. ++2. Pseudo-link Dir ++ See below, Pseudo-link. ++3. Step-Parent Dir ++ When "fileC" exists on the lower readonly branch only and it is ++ opened and removed with its parent dir, and then user writes ++ something into it, then aufs copies-up fileC to this ++ directory. Because there is no other dir to store fileC. After ++ creating a file under this dir, the file is unlinked. ++ ++Because aufs supports manipulating branches, ie. add/delete/change ++dynamically, a branch has its own id. When the branch order changes, ++aufs finds the new index by searching the branch id. ++ ++ ++Pseudo-link ++---------------------------------------------------------------------- ++Assume "fileA" exists on the lower readonly branch only and it is ++hardlinked to "fileB" on the branch. When you write something to fileA, ++aufs copies-up it to the upper writable branch. Additionally aufs ++creates a hardlink under the Pseudo-link Directory of the writable ++branch. The inode of a pseudo-link is kept in aufs super_block as a ++simple list. If fileB is read after unlinking fileA, aufs returns ++filedata from the pseudo-link instead of the lower readonly ++branch. Because the pseudo-link is based upon the inode, to keep the ++inode number by xino (see above) is essentially necessary. ++ ++All the hardlinks under the Pseudo-link Directory of the writable branch ++should be restored in a proper location later. Aufs provides a utility ++to do this. The userspace helpers executed at remounting and unmounting ++aufs by default. ++During this utility is running, it puts aufs into the pseudo-link ++maintenance mode. In this mode, only the process which began the ++maintenance mode (and its child processes) is allowed to operate in ++aufs. Some other processes which are not related to the pseudo-link will ++be allowed to run too, but the rest have to return an error or wait ++until the maintenance mode ends. If a process already acquires an inode ++mutex (in VFS), it has to return an error. ++ ++ ++XIB(external inode number bitmap) ++---------------------------------------------------------------------- ++Addition to the xino file per a branch, aufs has an external inode number ++bitmap in a superblock object. It is also an internal file such like a ++xino file. ++It is a simple bitmap to mark whether the aufs inode number is in-use or ++not. ++To reduce the file I/O, aufs prepares a single memory page to cache xib. ++ ++As well as XINO files, aufs has a feature to truncate/refresh XIB to ++reduce the number of consumed disk blocks for these files. ++ ++ ++Virtual or Vertical Dir, and Readdir in Userspace ++---------------------------------------------------------------------- ++In order to support multiple layers (branches), aufs readdir operation ++constructs a virtual dir block on memory. For readdir, aufs calls ++vfs_readdir() internally for each dir on branches, merges their entries ++with eliminating the whiteout-ed ones, and sets it to file (dir) ++object. So the file object has its entry list until it is closed. The ++entry list will be updated when the file position is zero and becomes ++obsoleted. This decision is made in aufs automatically. ++ ++The dynamically allocated memory block for the name of entries has a ++unit of 512 bytes (by default) and stores the names contiguously (no ++padding). Another block for each entry is handled by kmem_cache too. ++During building dir blocks, aufs creates hash list and judging whether ++the entry is whiteouted by its upper branch or already listed. ++The merged result is cached in the corresponding inode object and ++maintained by a customizable life-time option. ++ ++Some people may call it can be a security hole or invite DoS attack ++since the opened and once readdir-ed dir (file object) holds its entry ++list and becomes a pressure for system memory. But I'd say it is similar ++to files under /proc or /sys. The virtual files in them also holds a ++memory page (generally) while they are opened. When an idea to reduce ++memory for them is introduced, it will be applied to aufs too. ++For those who really hate this situation, I've developed readdir(3) ++library which operates this merging in userspace. You just need to set ++LD_PRELOAD environment variable, and aufs will not consume no memory in ++kernel space for readdir(3). ++ ++ ++Workqueue ++---------------------------------------------------------------------- ++Aufs sometimes requires privilege access to a branch. For instance, ++in copy-up/down operation. When a user process is going to make changes ++to a file which exists in the lower readonly branch only, and the mode ++of one of ancestor directories may not be writable by a user ++process. Here aufs copy-up the file with its ancestors and they may ++require privilege to set its owner/group/mode/etc. ++This is a typical case of a application character of aufs (see ++Introduction). ++ ++Aufs uses workqueue synchronously for this case. It creates its own ++workqueue. The workqueue is a kernel thread and has privilege. Aufs ++passes the request to call mkdir or write (for example), and wait for ++its completion. This approach solves a problem of a signal handler ++simply. ++If aufs didn't adopt the workqueue and changed the privilege of the ++process, then the process may receive the unexpected SIGXFSZ or other ++signals. ++ ++Also aufs uses the system global workqueue ("events" kernel thread) too ++for asynchronous tasks, such like handling inotify/fsnotify, re-creating a ++whiteout base and etc. This is unrelated to a privilege. ++Most of aufs operation tries acquiring a rw_semaphore for aufs ++superblock at the beginning, at the same time waits for the completion ++of all queued asynchronous tasks. ++ ++ ++Whiteout ++---------------------------------------------------------------------- ++The whiteout in aufs is very similar to Unionfs's. That is represented ++by its filename. UnionMount takes an approach of a file mode, but I am ++afraid several utilities (find(1) or something) will have to support it. ++ ++Basically the whiteout represents "logical deletion" which stops aufs to ++lookup further, but also it represents "dir is opaque" which also stop ++further lookup. ++ ++In aufs, rmdir(2) and rename(2) for dir uses whiteout alternatively. ++In order to make several functions in a single systemcall to be ++revertible, aufs adopts an approach to rename a directory to a temporary ++unique whiteouted name. ++For example, in rename(2) dir where the target dir already existed, aufs ++renames the target dir to a temporary unique whiteouted name before the ++actual rename on a branch, and then handles other actions (make it opaque, ++update the attributes, etc). If an error happens in these actions, aufs ++simply renames the whiteouted name back and returns an error. If all are ++succeeded, aufs registers a function to remove the whiteouted unique ++temporary name completely and asynchronously to the system global ++workqueue. ++ ++ ++Copy-up ++---------------------------------------------------------------------- ++It is a well-known feature or concept. ++When user modifies a file on a readonly branch, aufs operate "copy-up" ++internally and makes change to the new file on the upper writable branch. ++When the trigger systemcall does not update the timestamps of the parent ++dir, aufs reverts it after copy-up. ++ ++ ++Move-down (aufs3.9 and later) ++---------------------------------------------------------------------- ++"Copy-up" is one of the essential feature in aufs. It copies a file from ++the lower readonly branch to the upper writable branch when a user ++changes something about the file. ++"Move-down" is an opposite action of copy-up. Basically this action is ++ran manually instead of automatically and internally. ++For desgin and implementation, aufs has to consider these issues. ++- whiteout for the file may exist on the lower branch. ++- ancestor directories may not exist on the lower branch. ++- diropq for the ancestor directories may exist on the upper branch. ++- free space on the lower branch will reduce. ++- another access to the file may happen during moving-down, including ++ UDBA (see "Revalidate Dentry and UDBA"). ++- the file should not be hard-linked nor pseudo-linked. they should be ++ handled by auplink utility later. ++ ++Sometimes users want to move-down a file from the upper writable branch ++to the lower readonly or writable branch. For instance, ++- the free space of the upper writable branch is going to run out. ++- create a new intermediate branch between the upper and lower branch. ++- etc. ++ ++For this purpose, use "aumvdown" command in aufs-util.git. +diff --git a/Documentation/filesystems/aufs/design/03atomic_open.txt b/Documentation/filesystems/aufs/design/03atomic_open.txt +new file mode 100644 +index 0000000..741ad6d +--- /dev/null ++++ b/Documentation/filesystems/aufs/design/03atomic_open.txt +@@ -0,0 +1,72 @@ ++ ++# Copyright (C) 2015-2016 Junjiro R. Okajima ++ ++Support for a branch who has its ->atomic_open() ++---------------------------------------------------------------------- ++The filesystems who implement its ->atomic_open() are not majority. For ++example NFSv4 does, and aufs should call NFSv4 ->atomic_open, ++particularly for open(O_CREAT|O_EXCL, 0400) case. Other than ++->atomic_open(), NFSv4 returns an error for this open(2). While I am not ++sure whether all filesystems who have ->atomic_open() behave like this, ++but NFSv4 surely returns the error. ++ ++In order to support ->atomic_open() for aufs, there are a few ++approaches. ++ ++A. Introduce aufs_atomic_open() ++ - calls one of VFS:do_last(), lookup_open() or atomic_open() for ++ branch fs. ++B. Introduce aufs_atomic_open() calling create, open and chmod. this is ++ an aufs user Pip Cet's approach ++ - calls aufs_create(), VFS finish_open() and notify_change(). ++ - pass fake-mode to finish_open(), and then correct the mode by ++ notify_change(). ++C. Extend aufs_open() to call branch fs's ->atomic_open() ++ - no aufs_atomic_open(). ++ - aufs_lookup() registers the TID to an aufs internal object. ++ - aufs_create() does nothing when the matching TID is registered, but ++ registers the mode. ++ - aufs_open() calls branch fs's ->atomic_open() when the matching ++ TID is registered. ++D. Extend aufs_open() to re-try branch fs's ->open() with superuser's ++ credential ++ - no aufs_atomic_open(). ++ - aufs_create() registers the TID to an internal object. this info ++ represents "this process created this file just now." ++ - when aufs gets EACCES from branch fs's ->open(), then confirm the ++ registered TID and re-try open() with superuser's credential. ++ ++Pros and cons for each approach. ++ ++A. ++ - straightforward but highly depends upon VFS internal. ++ - the atomic behavaiour is kept. ++ - some of parameters such as nameidata are hard to reproduce for ++ branch fs. ++ - large overhead. ++B. ++ - easy to implement. ++ - the atomic behavaiour is lost. ++C. ++ - the atomic behavaiour is kept. ++ - dirty and tricky. ++ - VFS checks whether the file is created correctly after calling ++ ->create(), which means this approach doesn't work. ++D. ++ - easy to implement. ++ - the atomic behavaiour is lost. ++ - to open a file with superuser's credential and give it to a user ++ process is a bad idea, since the file object keeps the credential ++ in it. It may affect LSM or something. This approach doesn't work ++ either. ++ ++The approach A is ideal, but it hard to implement. So here is a ++variation of A, which is to be implemented. ++ ++A-1. Introduce aufs_atomic_open() ++ - calls branch fs ->atomic_open() if exists. otherwise calls ++ vfs_create() and finish_open(). ++ - the demerit is that the several checks after branch fs ++ ->atomic_open() are lost. in the ordinary case, the checks are ++ done by VFS:do_last(), lookup_open() and atomic_open(). some can ++ be implemented in aufs, but not all I am afraid. +diff --git a/Documentation/filesystems/aufs/design/03lookup.txt b/Documentation/filesystems/aufs/design/03lookup.txt +new file mode 100644 +index 0000000..5b6b000 +--- /dev/null ++++ b/Documentation/filesystems/aufs/design/03lookup.txt +@@ -0,0 +1,100 @@ ++ ++# Copyright (C) 2005-2016 Junjiro R. Okajima ++ ++Lookup in a Branch ++---------------------------------------------------------------------- ++Since aufs has a character of sub-VFS (see Introduction), it operates ++lookup for branches as VFS does. It may be a heavy work. But almost all ++lookup operation in aufs is the simplest case, ie. lookup only an entry ++directly connected to its parent. Digging down the directory hierarchy ++is unnecessary. VFS has a function lookup_one_len() for that use, and ++aufs calls it. ++ ++When a branch is a remote filesystem, aufs basically relies upon its ++->d_revalidate(), also aufs forces the hardest revalidate tests for ++them. ++For d_revalidate, aufs implements three levels of revalidate tests. See ++"Revalidate Dentry and UDBA" in detail. ++ ++ ++Test Only the Highest One for the Directory Permission (dirperm1 option) ++---------------------------------------------------------------------- ++Let's try case study. ++- aufs has two branches, upper readwrite and lower readonly. ++ /au = /rw + /ro ++- "dirA" exists under /ro, but /rw. and its mode is 0700. ++- user invoked "chmod a+rx /au/dirA" ++- the internal copy-up is activated and "/rw/dirA" is created and its ++ permission bits are set to world readable. ++- then "/au/dirA" becomes world readable? ++ ++In this case, /ro/dirA is still 0700 since it exists in readonly branch, ++or it may be a natively readonly filesystem. If aufs respects the lower ++branch, it should not respond readdir request from other users. But user ++allowed it by chmod. Should really aufs rejects showing the entries ++under /ro/dirA? ++ ++To be honest, I don't have a good solution for this case. So aufs ++implements 'dirperm1' and 'nodirperm1' mount options, and leave it to ++users. ++When dirperm1 is specified, aufs checks only the highest one for the ++directory permission, and shows the entries. Otherwise, as usual, checks ++every dir existing on all branches and rejects the request. ++ ++As a side effect, dirperm1 option improves the performance of aufs ++because the number of permission check is reduced when the number of ++branch is many. ++ ++ ++Revalidate Dentry and UDBA (User's Direct Branch Access) ++---------------------------------------------------------------------- ++Generally VFS helpers re-validate a dentry as a part of lookup. ++0. digging down the directory hierarchy. ++1. lock the parent dir by its i_mutex. ++2. lookup the final (child) entry. ++3. revalidate it. ++4. call the actual operation (create, unlink, etc.) ++5. unlock the parent dir ++ ++If the filesystem implements its ->d_revalidate() (step 3), then it is ++called. Actually aufs implements it and checks the dentry on a branch is ++still valid. ++But it is not enough. Because aufs has to release the lock for the ++parent dir on a branch at the end of ->lookup() (step 2) and ++->d_revalidate() (step 3) while the i_mutex of the aufs dir is still ++held by VFS. ++If the file on a branch is changed directly, eg. bypassing aufs, after ++aufs released the lock, then the subsequent operation may cause ++something unpleasant result. ++ ++This situation is a result of VFS architecture, ->lookup() and ++->d_revalidate() is separated. But I never say it is wrong. It is a good ++design from VFS's point of view. It is just not suitable for sub-VFS ++character in aufs. ++ ++Aufs supports such case by three level of revalidation which is ++selectable by user. ++1. Simple Revalidate ++ Addition to the native flow in VFS's, confirm the child-parent ++ relationship on the branch just after locking the parent dir on the ++ branch in the "actual operation" (step 4). When this validation ++ fails, aufs returns EBUSY. ->d_revalidate() (step 3) in aufs still ++ checks the validation of the dentry on branches. ++2. Monitor Changes Internally by Inotify/Fsnotify ++ Addition to above, in the "actual operation" (step 4) aufs re-lookup ++ the dentry on the branch, and returns EBUSY if it finds different ++ dentry. ++ Additionally, aufs sets the inotify/fsnotify watch for every dir on branches ++ during it is in cache. When the event is notified, aufs registers a ++ function to kernel 'events' thread by schedule_work(). And the ++ function sets some special status to the cached aufs dentry and inode ++ private data. If they are not cached, then aufs has nothing to ++ do. When the same file is accessed through aufs (step 0-3) later, ++ aufs will detect the status and refresh all necessary data. ++ In this mode, aufs has to ignore the event which is fired by aufs ++ itself. ++3. No Extra Validation ++ This is the simplest test and doesn't add any additional revalidation ++ test, and skip the revalidation in step 4. It is useful and improves ++ aufs performance when system surely hide the aufs branches from user, ++ by over-mounting something (or another method). +diff --git a/Documentation/filesystems/aufs/design/04branch.txt b/Documentation/filesystems/aufs/design/04branch.txt +new file mode 100644 +index 0000000..e68f4d3 +--- /dev/null ++++ b/Documentation/filesystems/aufs/design/04branch.txt +@@ -0,0 +1,61 @@ ++ ++# Copyright (C) 2005-2016 Junjiro R. Okajima ++ ++Branch Manipulation ++ ++Since aufs supports dynamic branch manipulation, ie. add/remove a branch ++and changing its permission/attribute, there are a lot of works to do. ++ ++ ++Add a Branch ++---------------------------------------------------------------------- ++o Confirm the adding dir exists outside of aufs, including loopback ++ mount, and its various attributes. ++o Initialize the xino file and whiteout bases if necessary. ++ See struct.txt. ++ ++o Check the owner/group/mode of the directory ++ When the owner/group/mode of the adding directory differs from the ++ existing branch, aufs issues a warning because it may impose a ++ security risk. ++ For example, when a upper writable branch has a world writable empty ++ top directory, a malicious user can create any files on the writable ++ branch directly, like copy-up and modify manually. If something like ++ /etc/{passwd,shadow} exists on the lower readonly branch but the upper ++ writable branch, and the writable branch is world-writable, then a ++ malicious guy may create /etc/passwd on the writable branch directly ++ and the infected file will be valid in aufs. ++ I am afraid it can be a security issue, but aufs can do nothing except ++ producing a warning. ++ ++ ++Delete a Branch ++---------------------------------------------------------------------- ++o Confirm the deleting branch is not busy ++ To be general, there is one merit to adopt "remount" interface to ++ manipulate branches. It is to discard caches. At deleting a branch, ++ aufs checks the still cached (and connected) dentries and inodes. If ++ there are any, then they are all in-use. An inode without its ++ corresponding dentry can be alive alone (for example, inotify/fsnotify case). ++ ++ For the cached one, aufs checks whether the same named entry exists on ++ other branches. ++ If the cached one is a directory, because aufs provides a merged view ++ to users, as long as one dir is left on any branch aufs can show the ++ dir to users. In this case, the branch can be removed from aufs. ++ Otherwise aufs rejects deleting the branch. ++ ++ If any file on the deleting branch is opened by aufs, then aufs ++ rejects deleting. ++ ++ ++Modify the Permission of a Branch ++---------------------------------------------------------------------- ++o Re-initialize or remove the xino file and whiteout bases if necessary. ++ See struct.txt. ++ ++o rw --> ro: Confirm the modifying branch is not busy ++ Aufs rejects the request if any of these conditions are true. ++ - a file on the branch is mmap-ed. ++ - a regular file on the branch is opened for write and there is no ++ same named entry on the upper branch. +diff --git a/Documentation/filesystems/aufs/design/05wbr_policy.txt b/Documentation/filesystems/aufs/design/05wbr_policy.txt +new file mode 100644 +index 0000000..1726d5d +--- /dev/null ++++ b/Documentation/filesystems/aufs/design/05wbr_policy.txt +@@ -0,0 +1,51 @@ ++ ++# Copyright (C) 2005-2016 Junjiro R. Okajima ++ ++Policies to Select One among Multiple Writable Branches ++---------------------------------------------------------------------- ++When the number of writable branch is more than one, aufs has to decide ++the target branch for file creation or copy-up. By default, the highest ++writable branch which has the parent (or ancestor) dir of the target ++file is chosen (top-down-parent policy). ++By user's request, aufs implements some other policies to select the ++writable branch, for file creation several policies, round-robin, ++most-free-space, and other policies. For copy-up, top-down-parent, ++bottom-up-parent, bottom-up and others. ++ ++As expected, the round-robin policy selects the branch in circular. When ++you have two writable branches and creates 10 new files, 5 files will be ++created for each branch. mkdir(2) systemcall is an exception. When you ++create 10 new directories, all will be created on the same branch. ++And the most-free-space policy selects the one which has most free ++space among the writable branches. The amount of free space will be ++checked by aufs internally, and users can specify its time interval. ++ ++The policies for copy-up is more simple, ++top-down-parent is equivalent to the same named on in create policy, ++bottom-up-parent selects the writable branch where the parent dir ++exists and the nearest upper one from the copyup-source, ++bottom-up selects the nearest upper writable branch from the ++copyup-source, regardless the existence of the parent dir. ++ ++There are some rules or exceptions to apply these policies. ++- If there is a readonly branch above the policy-selected branch and ++ the parent dir is marked as opaque (a variation of whiteout), or the ++ target (creating) file is whiteout-ed on the upper readonly branch, ++ then the result of the policy is ignored and the target file will be ++ created on the nearest upper writable branch than the readonly branch. ++- If there is a writable branch above the policy-selected branch and ++ the parent dir is marked as opaque or the target file is whiteouted ++ on the branch, then the result of the policy is ignored and the target ++ file will be created on the highest one among the upper writable ++ branches who has diropq or whiteout. In case of whiteout, aufs removes ++ it as usual. ++- link(2) and rename(2) systemcalls are exceptions in every policy. ++ They try selecting the branch where the source exists as possible ++ since copyup a large file will take long time. If it can't be, ++ ie. the branch where the source exists is readonly, then they will ++ follow the copyup policy. ++- There is an exception for rename(2) when the target exists. ++ If the rename target exists, aufs compares the index of the branches ++ 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 --git a/Documentation/filesystems/aufs/design/06fhsm.txt b/Documentation/filesystems/aufs/design/06fhsm.txt +new file mode 100644 +index 0000000..84b46dc +--- /dev/null ++++ b/Documentation/filesystems/aufs/design/06fhsm.txt +@@ -0,0 +1,105 @@ ++ ++# Copyright (C) 2011-2016 Junjiro R. Okajima ++ ++File-based Hierarchical Storage Management (FHSM) ++---------------------------------------------------------------------- ++Hierarchical Storage Management (or HSM) is a well-known feature in the ++storage world. Aufs provides this feature as file-based with multiple ++writable branches, based upon the principle of "Colder, the Lower". ++Here the word "colder" means that the less used files, and "lower" means ++that the position in the order of the stacked branches vertically. ++These multiple writable branches are prioritized, ie. the topmost one ++should be the fastest drive and be used heavily. ++ ++o Characters in aufs FHSM story ++- aufs itself and a new branch attribute. ++- a new ioctl interface to move-down and to establish a connection with ++ the daemon ("move-down" is a converse of "copy-up"). ++- userspace tool and daemon. ++ ++The userspace daemon establishes a connection with aufs and waits for ++the notification. The notified information is very similar to struct ++statfs containing the number of consumed blocks and inodes. ++When the consumed blocks/inodes of a branch exceeds the user-specified ++upper watermark, the daemon activates its move-down process until the ++consumed blocks/inodes reaches the user-specified lower watermark. ++ ++The actual move-down is done by aufs based upon the request from ++user-space since we need to maintain the inode number and the internal ++pointer arrays in aufs. ++ ++Currently aufs FHSM handles the regular files only. Additionally they ++must not be hard-linked nor pseudo-linked. ++ ++ ++o Cowork of aufs and the user-space daemon ++ During the userspace daemon established the connection, aufs sends a ++ small notification to it whenever aufs writes something into the ++ writable branch. But it may cost high since aufs issues statfs(2) ++ internally. So user can specify a new option to cache the ++ info. Actually the notification is controlled by these factors. ++ + the specified cache time. ++ + classified as "force" by aufs internally. ++ Until the specified time expires, aufs doesn't send the info ++ except the forced cases. When aufs decide forcing, the info is always ++ notified to userspace. ++ For example, the number of free inodes is generally large enough and ++ the shortage of it happens rarely. So aufs doesn't force the ++ notification when creating a new file, directory and others. This is ++ the typical case which aufs doesn't force. ++ When aufs writes the actual filedata and the files consumes any of new ++ blocks, the aufs forces notifying. ++ ++ ++o Interfaces in aufs ++- New branch attribute. ++ + fhsm ++ Specifies that the branch is managed by FHSM feature. In other word, ++ participant in the FHSM. ++ When nofhsm is set to the branch, it will not be the source/target ++ branch of the move-down operation. This attribute is set ++ independently from coo and moo attributes, and if you want full ++ FHSM, you should specify them as well. ++- New mount option. ++ + fhsm_sec ++ Specifies a second to suppress many less important info to be ++ notified. ++- New ioctl. ++ + AUFS_CTL_FHSM_FD ++ create a new file descriptor which userspace can read the notification ++ (a subset of struct statfs) from aufs. ++- Module parameter 'brs' ++ It has to be set to 1. Otherwise the new mount option 'fhsm' will not ++ be set. ++- mount helpers /sbin/mount.aufs and /sbin/umount.aufs ++ When there are two or more branches with fhsm attributes, ++ /sbin/mount.aufs invokes the user-space daemon and /sbin/umount.aufs ++ terminates it. As a result of remounting and branch-manipulation, the ++ number of branches with fhsm attribute can be one. In this case, ++ /sbin/mount.aufs will terminate the user-space daemon. ++ ++ ++Finally the operation is done as these steps in kernel-space. ++- make sure that, ++ + no one else is using the file. ++ + the file is not hard-linked. ++ + the file is not pseudo-linked. ++ + the file is a regular file. ++ + the parent dir is not opaqued. ++- find the target writable branch. ++- make sure the file is not whiteout-ed by the upper (than the target) ++ branch. ++- make the parent dir on the target branch. ++- mutex lock the inode on the branch. ++- unlink the whiteout on the target branch (if exists). ++- lookup and create the whiteout-ed temporary name on the target branch. ++- copy the file as the whiteout-ed temporary name on the target branch. ++- rename the whiteout-ed temporary name to the original name. ++- unlink the file on the source branch. ++- maintain the internal pointer array and the external inode number ++ table (XINO). ++- maintain the timestamps and other attributes of the parent dir and the ++ file. ++ ++And of course, in every step, an error may happen. So the operation ++should restore the original file state after an error happens. +diff --git a/Documentation/filesystems/aufs/design/06mmap.txt b/Documentation/filesystems/aufs/design/06mmap.txt +new file mode 100644 +index 0000000..991c0b1 +--- /dev/null ++++ b/Documentation/filesystems/aufs/design/06mmap.txt +@@ -0,0 +1,59 @@ ++ ++# Copyright (C) 2005-2016 Junjiro R. Okajima ++ ++mmap(2) -- File Memory Mapping ++---------------------------------------------------------------------- ++In aufs, the file-mapped pages are handled by a branch fs directly, no ++interaction with aufs. It means aufs_mmap() calls the branch fs's ++->mmap(). ++This approach is simple and good, but there is one problem. ++Under /proc, several entries show the mmapped files by its path (with ++device and inode number), and the printed path will be the path on the ++branch fs's instead of virtual aufs's. ++This is not a problem in most cases, but some utilities lsof(1) (and its ++user) may expect the path on aufs. ++ ++To address this issue, aufs adds a new member called vm_prfile in struct ++vm_area_struct (and struct vm_region). The original vm_file points to ++the file on the branch fs in order to handle everything correctly as ++usual. The new vm_prfile points to a virtual file in aufs, and the ++show-functions in procfs refers to vm_prfile if it is set. ++Also we need to maintain several other places where touching vm_file ++such like ++- fork()/clone() copies vma and the reference count of vm_file is ++ incremented. ++- merging vma maintains the ref count too. ++ ++This is not a good approach. It just fakes the printed path. But it ++leaves all behaviour around f_mapping unchanged. This is surely an ++advantage. ++Actually aufs had adopted another complicated approach which calls ++generic_file_mmap() and handles struct vm_operations_struct. In this ++approach, aufs met a hard problem and I could not solve it without ++switching the approach. ++ ++There may be one more another approach which is ++- bind-mount the branch-root onto the aufs-root internally ++- grab the new vfsmount (ie. struct mount) ++- lazy-umount the branch-root internally ++- in open(2) the aufs-file, open the branch-file with the hidden ++ vfsmount (instead of the original branch's vfsmount) ++- ideally this "bind-mount and lazy-umount" should be done atomically, ++ but it may be possible from userspace by the mount helper. ++ ++Adding the internal hidden vfsmount and using it in opening a file, the ++file path under /proc will be printed correctly. This approach looks ++smarter, but is not possible I am afraid. ++- aufs-root may be bind-mount later. when it happens, another hidden ++ vfsmount will be required. ++- it is hard to get the chance to bind-mount and lazy-umount ++ + in kernel-space, FS can have vfsmount in open(2) via ++ file->f_path, and aufs can know its vfsmount. But several locks are ++ already acquired, and if aufs tries to bind-mount and lazy-umount ++ here, then it may cause a deadlock. ++ + in user-space, bind-mount doesn't invoke the mount helper. ++- since /proc shows dev and ino, aufs has to give vma these info. it ++ means a new member vm_prinode will be necessary. this is essentially ++ equivalent to vm_prfile described above. ++ ++I have to give up this "looks-smater" approach. +diff --git a/Documentation/filesystems/aufs/design/06xattr.txt b/Documentation/filesystems/aufs/design/06xattr.txt +new file mode 100644 +index 0000000..7bfa94f +--- /dev/null ++++ b/Documentation/filesystems/aufs/design/06xattr.txt +@@ -0,0 +1,81 @@ ++ ++# Copyright (C) 2014-2016 Junjiro R. Okajima ++ ++Listing XATTR/EA and getting the value ++---------------------------------------------------------------------- ++For the inode standard attributes (owner, group, timestamps, etc.), aufs ++shows the values from the topmost existing file. This behaviour is good ++for the non-dir entries since the bahaviour exactly matches the shown ++information. But for the directories, aufs considers all the same named ++entries on the lower branches. Which means, if one of the lower entry ++rejects readdir call, then aufs returns an error even if the topmost ++entry allows it. This behaviour is necessary to respect the branch fs's ++security, but can make users confused since the user-visible standard ++attributes don't match the behaviour. ++To address this issue, aufs has a mount option called dirperm1 which ++checks the permission for the topmost entry only, and ignores the lower ++entry's permission. ++ ++A similar issue can happen around XATTR. ++getxattr(2) and listxattr(2) families behave as if dirperm1 option is ++always set. Otherwise these very unpleasant situation would happen. ++- listxattr(2) may return the duplicated entries. ++- users may not be able to remove or reset the XATTR forever, ++ ++ ++XATTR/EA support in the internal (copy,move)-(up,down) ++---------------------------------------------------------------------- ++Generally the extended attributes of inode are categorized as these. ++- "security" for LSM and capability. ++- "system" for posix ACL, 'acl' mount option is required for the branch ++ fs generally. ++- "trusted" for userspace, CAP_SYS_ADMIN is required. ++- "user" for userspace, 'user_xattr' mount option is required for the ++ branch fs generally. ++ ++Moreover there are some other categories. Aufs handles these rather ++unpopular categories as the ordinary ones, ie. there is no special ++condition nor exception. ++ ++In copy-up, the support for XATTR on the dst branch may differ from the ++src branch. In this case, the copy-up operation will get an error and ++the original user operation which triggered the copy-up will fail. It ++can happen that even all copy-up will fail. ++When both of src and dst branches support XATTR and if an error occurs ++during copying XATTR, then the copy-up should fail obviously. That is a ++good reason and aufs should return an error to userspace. But when only ++the src branch support that XATTR, aufs should not return an error. ++For example, the src branch supports ACL but the dst branch doesn't ++because the dst branch may natively un-support it or temporary ++un-support it due to "noacl" mount option. Of course, the dst branch fs ++may NOT return an error even if the XATTR is not supported. It is ++totally up to the branch fs. ++ ++Anyway when the aufs internal copy-up gets an error from the dst branch ++fs, then aufs tries removing the just copied entry and returns the error ++to the userspace. The worst case of this situation will be all copy-up ++will fail. ++ ++For the copy-up operation, there two basic approaches. ++- copy the specified XATTR only (by category above), and return the ++ error unconditionally if it happens. ++- copy all XATTR, and ignore the error on the specified category only. ++ ++In order to support XATTR and to implement the correct behaviour, aufs ++chooses the latter approach and introduces some new branch attributes, ++"icexsec", "icexsys", "icextr", "icexusr", and "icexoth". ++They correspond to the XATTR namespaces (see above). Additionally, to be ++convenient, "icex" is also provided which means all "icex*" attributes ++are set (here the word "icex" stands for "ignore copy-error on XATTR"). ++ ++The meaning of these attributes is to ignore the error from setting ++XATTR on that branch. ++Note that aufs tries copying all XATTR unconditionally, and ignores the ++error from the dst branch according to the specified attributes. ++ ++Some XATTR may have its default value. The default value may come from ++the parent dir or the environment. If the default value is set at the ++file creating-time, it will be overwritten by copy-up. ++Some contradiction may happen I am afraid. ++Do we need another attribute to stop copying XATTR? I am unsure. For ++now, aufs implements the branch attributes to ignore the error. +diff --git a/Documentation/filesystems/aufs/design/07export.txt b/Documentation/filesystems/aufs/design/07export.txt +new file mode 100644 +index 0000000..c23930b +--- /dev/null ++++ b/Documentation/filesystems/aufs/design/07export.txt +@@ -0,0 +1,45 @@ ++ ++# Copyright (C) 2005-2016 Junjiro R. Okajima ++ ++Export Aufs via NFS ++---------------------------------------------------------------------- ++Here is an approach. ++- like xino/xib, add a new file 'xigen' which stores aufs inode ++ generation. ++- iget_locked(): initialize aufs inode generation for a new inode, and ++ store it in xigen file. ++- destroy_inode(): increment aufs inode generation and store it in xigen ++ file. it is necessary even if it is not unlinked, because any data of ++ inode may be changed by UDBA. ++- encode_fh(): for a root dir, simply return FILEID_ROOT. otherwise ++ build file handle by ++ + branch id (4 bytes) ++ + superblock generation (4 bytes) ++ + inode number (4 or 8 bytes) ++ + parent dir inode number (4 or 8 bytes) ++ + inode generation (4 bytes)) ++ + return value of exportfs_encode_fh() for the parent on a branch (4 ++ bytes) ++ + file handle for a branch (by exportfs_encode_fh()) ++- fh_to_dentry(): ++ + find the index of a branch from its id in handle, and check it is ++ still exist in aufs. ++ + 1st level: get the inode number from handle and search it in cache. ++ + 2nd level: if not found in cache, get the parent inode number from ++ the handle and search it in cache. and then open the found parent ++ dir, find the matching inode number by vfs_readdir() and get its ++ name, and call lookup_one_len() for the target dentry. ++ + 3rd level: if the parent dir is not cached, call ++ exportfs_decode_fh() for a branch and get the parent on a branch, ++ build a pathname of it, convert it a pathname in aufs, call ++ path_lookup(). now aufs gets a parent dir dentry, then handle it as ++ the 2nd level. ++ + to open the dir, aufs needs struct vfsmount. aufs keeps vfsmount ++ for every branch, but not itself. to get this, (currently) aufs ++ searches in current->nsproxy->mnt_ns list. it may not be a good ++ idea, but I didn't get other approach. ++ + test the generation of the gotten inode. ++- every inode operation: they may get EBUSY due to UDBA. in this case, ++ convert it into ESTALE for NFSD. ++- readdir(): call lockdep_on/off() because filldir in NFSD calls ++ lookup_one_len(), vfs_getattr(), encode_fh() and others. +diff --git a/Documentation/filesystems/aufs/design/08shwh.txt b/Documentation/filesystems/aufs/design/08shwh.txt +new file mode 100644 +index 0000000..ad58ebe +--- /dev/null ++++ b/Documentation/filesystems/aufs/design/08shwh.txt +@@ -0,0 +1,39 @@ ++ ++# Copyright (C) 2005-2016 Junjiro R. Okajima ++ ++Show Whiteout Mode (shwh) ++---------------------------------------------------------------------- ++Generally aufs hides the name of whiteouts. But in some cases, to show ++them is very useful for users. For instance, creating a new middle layer ++(branch) by merging existing layers. ++ ++(borrowing aufs1 HOW-TO from a user, Michael Towers) ++When you have three branches, ++- Bottom: 'system', squashfs (underlying base system), read-only ++- Middle: 'mods', squashfs, read-only ++- Top: 'overlay', ram (tmpfs), read-write ++ ++The top layer is loaded at boot time and saved at shutdown, to preserve ++the changes made to the system during the session. ++When larger changes have been made, or smaller changes have accumulated, ++the size of the saved top layer data grows. At this point, it would be ++nice to be able to merge the two overlay branches ('mods' and 'overlay') ++and rewrite the 'mods' squashfs, clearing the top layer and thus ++restoring save and load speed. ++ ++This merging is simplified by the use of another aufs mount, of just the ++two overlay branches using the 'shwh' option. ++# mount -t aufs -o ro,shwh,br:/livesys/overlay=ro+wh:/livesys/mods=rr+wh \ ++ aufs /livesys/merge_union ++ ++A merged view of these two branches is then available at ++/livesys/merge_union, and the new feature is that the whiteouts are ++visible! ++Note that in 'shwh' mode the aufs mount must be 'ro', which will disable ++writing to all branches. Also the default mode for all branches is 'ro'. ++It is now possible to save the combined contents of the two overlay ++branches to a new squashfs, e.g.: ++# mksquashfs /livesys/merge_union /path/to/newmods.squash ++ ++This new squashfs archive can be stored on the boot device and the ++initramfs will use it to replace the old one at the next boot. +diff --git a/Documentation/filesystems/aufs/design/10dynop.txt b/Documentation/filesystems/aufs/design/10dynop.txt +new file mode 100644 +index 0000000..49afc58 +--- /dev/null ++++ b/Documentation/filesystems/aufs/design/10dynop.txt +@@ -0,0 +1,34 @@ ++ ++# Copyright (C) 2010-2016 Junjiro R. Okajima ++ ++Dynamically customizable FS operations ++---------------------------------------------------------------------- ++Generally FS operations (struct inode_operations, struct ++address_space_operations, struct file_operations, etc.) are defined as ++"static const", but it never means that FS have only one set of ++operation. Some FS have multiple sets of them. For instance, ext2 has ++three sets, one for XIP, for NOBH, and for normal. ++Since aufs overrides and redirects these operations, sometimes aufs has ++to change its behaviour according to the branch FS type. More importantly ++VFS acts differently if a function (member in the struct) is set or ++not. It means aufs should have several sets of operations and select one ++among them according to the branch FS definition. ++ ++In order to solve this problem and not to affect the behaviour of VFS, ++aufs defines these operations dynamically. For instance, aufs defines ++dummy direct_IO function for struct address_space_operations, but it may ++not be set to the address_space_operations actually. When the branch FS ++doesn't have it, aufs doesn't set it to its address_space_operations ++while the function definition itself is still alive. So the behaviour ++itself will not change, and it will return an error when direct_IO is ++not set. ++ ++The lifetime of these dynamically generated operation object is ++maintained by aufs branch object. When the branch is removed from aufs, ++the reference counter of the object is decremented. When it reaches ++zero, the dynamically generated operation object will be freed. ++ ++This approach is designed to support AIO (io_submit), Direct I/O and ++XIP (DAX) mainly. ++Currently this approach is applied to address_space_operations for ++regular files only. +diff --git a/MAINTAINERS b/MAINTAINERS +index 9c567a4..a62aea4 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -2128,6 +2128,19 @@ F: include/linux/audit.h + F: include/uapi/linux/audit.h + F: kernel/audit* + ++AUFS (advanced multi layered unification filesystem) FILESYSTEM ++M: "J. R. Okajima" ++L: linux-unionfs@vger.kernel.org ++L: aufs-users@lists.sourceforge.net (members only) ++W: http://aufs.sourceforge.net ++T: git://github.com/sfjro/aufs4-linux.git ++S: Supported ++F: Documentation/filesystems/aufs/ ++F: Documentation/ABI/testing/debugfs-aufs ++F: Documentation/ABI/testing/sysfs-aufs ++F: fs/aufs/ ++F: include/uapi/linux/aufs_type.h ++ + AUXILIARY DISPLAY DRIVERS + M: Miguel Ojeda Sandonis + W: http://miguelojeda.es/auxdisplay.htm +diff --git a/drivers/block/loop.c b/drivers/block/loop.c +index 80cf8ad..ba9e4a7 100644 +--- a/drivers/block/loop.c ++++ b/drivers/block/loop.c +@@ -712,6 +712,24 @@ static inline int is_loop_device(struct file *file) return i && S_ISBLK(i->i_mode) && MAJOR(i->i_rdev) == LOOP_MAJOR; } @@ -1721,9 +1777,265 @@ diff -Nurp linux-4.4.3/drivers/block/loop.c linux-4.4.3-aufs/drivers/block/loop. /* loop sysfs attributes */ static ssize_t loop_attr_show(struct device *dev, char *page, -diff -Nurp linux-4.4.3/fs/aufs/aufs.h linux-4.4.3-aufs/fs/aufs/aufs.h ---- linux-4.4.3/fs/aufs/aufs.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/aufs.h 2016-03-02 17:59:37.361498688 +0200 +diff --git a/fs/Kconfig b/fs/Kconfig +index 6725f59..2ced198 100644 +--- a/fs/Kconfig ++++ b/fs/Kconfig +@@ -235,6 +235,7 @@ source "fs/pstore/Kconfig" + source "fs/sysv/Kconfig" + source "fs/ufs/Kconfig" + source "fs/exofs/Kconfig" ++source "fs/aufs/Kconfig" + + endif # MISC_FILESYSTEMS + +diff --git a/fs/Makefile b/fs/Makefile +index 85b6e13..e7bb164 100644 +--- a/fs/Makefile ++++ b/fs/Makefile +@@ -128,3 +128,4 @@ obj-y += exofs/ # Multiple modules + obj-$(CONFIG_CEPH_FS) += ceph/ + obj-$(CONFIG_PSTORE) += pstore/ + obj-$(CONFIG_EFIVAR_FS) += efivarfs/ ++obj-$(CONFIG_AUFS_FS) += aufs/ +diff --git a/fs/aufs/Kconfig b/fs/aufs/Kconfig +new file mode 100644 +index 0000000..a4efb8b +--- /dev/null ++++ b/fs/aufs/Kconfig +@@ -0,0 +1,185 @@ ++config AUFS_FS ++ bool "Aufs (Advanced multi layered unification filesystem) support" ++ help ++ Aufs is a stackable unification filesystem such as Unionfs, ++ which unifies several directories and provides a merged single ++ directory. ++ In the early days, aufs was entirely re-designed and ++ re-implemented Unionfs Version 1.x series. Introducing many ++ original ideas, approaches and improvements, it becomes totally ++ different from Unionfs while keeping the basic features. ++ ++if AUFS_FS ++choice ++ prompt "Maximum number of branches" ++ default AUFS_BRANCH_MAX_127 ++ help ++ Specifies the maximum number of branches (or member directories) ++ in a single aufs. The larger value consumes more system ++ resources and has a minor impact to performance. ++config AUFS_BRANCH_MAX_127 ++ bool "127" ++ help ++ Specifies the maximum number of branches (or member directories) ++ in a single aufs. The larger value consumes more system ++ resources and has a minor impact to performance. ++config AUFS_BRANCH_MAX_511 ++ bool "511" ++ help ++ Specifies the maximum number of branches (or member directories) ++ in a single aufs. The larger value consumes more system ++ resources and has a minor impact to performance. ++config AUFS_BRANCH_MAX_1023 ++ bool "1023" ++ help ++ Specifies the maximum number of branches (or member directories) ++ in a single aufs. The larger value consumes more system ++ resources and has a minor impact to performance. ++config AUFS_BRANCH_MAX_32767 ++ bool "32767" ++ help ++ Specifies the maximum number of branches (or member directories) ++ in a single aufs. The larger value consumes more system ++ resources and has a minor impact to performance. ++endchoice ++ ++config AUFS_SBILIST ++ bool ++ depends on AUFS_MAGIC_SYSRQ || PROC_FS ++ default y ++ help ++ Automatic configuration for internal use. ++ When aufs supports Magic SysRq or /proc, enabled automatically. ++ ++config AUFS_HNOTIFY ++ bool "Detect direct branch access (bypassing aufs)" ++ help ++ If you want to modify files on branches directly, eg. bypassing aufs, ++ and want aufs to detect the changes of them fully, then enable this ++ option and use 'udba=notify' mount option. ++ Currently there is only one available configuration, "fsnotify". ++ It will have a negative impact to the performance. ++ See detail in aufs.5. ++ ++choice ++ prompt "method" if AUFS_HNOTIFY ++ default AUFS_HFSNOTIFY ++config AUFS_HFSNOTIFY ++ bool "fsnotify" ++ select FSNOTIFY ++endchoice ++ ++config AUFS_EXPORT ++ bool "NFS-exportable aufs" ++ depends on EXPORTFS = y ++ help ++ If you want to export your mounted aufs via NFS, then enable this ++ option. There are several requirements for this configuration. ++ See detail in aufs.5. ++ ++config AUFS_INO_T_64 ++ bool ++ depends on AUFS_EXPORT ++ depends on 64BIT && !(ALPHA || S390) ++ default y ++ help ++ Automatic configuration for internal use. ++ /* typedef unsigned long/int __kernel_ino_t */ ++ /* alpha and s390x are int */ ++ ++config AUFS_XATTR ++ bool "support for XATTR/EA (including Security Labels)" ++ help ++ If your branch fs supports XATTR/EA and you want to make them ++ available in aufs too, then enable this opsion and specify the ++ branch attributes for EA. ++ See detail in aufs.5. ++ ++config AUFS_FHSM ++ bool "File-based Hierarchical Storage Management" ++ help ++ Hierarchical Storage Management (or HSM) is a well-known feature ++ in the storage world. Aufs provides this feature as file-based. ++ with multiple branches. ++ These multiple branches are prioritized, ie. the topmost one ++ should be the fastest drive and be used heavily. ++ ++config AUFS_RDU ++ bool "Readdir in userspace" ++ help ++ Aufs has two methods to provide a merged view for a directory, ++ by a user-space library and by kernel-space natively. The latter ++ is always enabled but sometimes large and slow. ++ If you enable this option, install the library in aufs2-util ++ package, and set some environment variables for your readdir(3), ++ then the work will be handled in user-space which generally ++ shows better performance in most cases. ++ See detail in aufs.5. ++ ++config AUFS_SHWH ++ bool "Show whiteouts" ++ help ++ If you want to make the whiteouts in aufs visible, then enable ++ this option and specify 'shwh' mount option. Although it may ++ sounds like philosophy or something, but in technically it ++ simply shows the name of whiteout with keeping its behaviour. ++ ++config AUFS_BR_RAMFS ++ bool "Ramfs (initramfs/rootfs) as an aufs branch" ++ help ++ If you want to use ramfs as an aufs branch fs, then enable this ++ option. Generally tmpfs is recommended. ++ Aufs prohibited them to be a branch fs by default, because ++ initramfs becomes unusable after switch_root or something ++ generally. If you sets initramfs as an aufs branch and boot your ++ system by switch_root, you will meet a problem easily since the ++ files in initramfs may be inaccessible. ++ Unless you are going to use ramfs as an aufs branch fs without ++ switch_root or something, leave it N. ++ ++config AUFS_BR_FUSE ++ bool "Fuse fs as an aufs branch" ++ depends on FUSE_FS ++ select AUFS_POLL ++ help ++ If you want to use fuse-based userspace filesystem as an aufs ++ branch fs, then enable this option. ++ It implements the internal poll(2) operation which is ++ implemented by fuse only (curretnly). ++ ++config AUFS_POLL ++ bool ++ help ++ Automatic configuration for internal use. ++ ++config AUFS_BR_HFSPLUS ++ bool "Hfsplus as an aufs branch" ++ depends on HFSPLUS_FS ++ default y ++ help ++ If you want to use hfsplus fs as an aufs branch fs, then enable ++ this option. This option introduces a small overhead at ++ copying-up a file on hfsplus. ++ ++config AUFS_BDEV_LOOP ++ bool ++ depends on BLK_DEV_LOOP ++ default y ++ help ++ Automatic configuration for internal use. ++ Convert =[ym] into =y. ++ ++config AUFS_DEBUG ++ bool "Debug aufs" ++ help ++ Enable this to compile aufs internal debug code. ++ It will have a negative impact to the performance. ++ ++config AUFS_MAGIC_SYSRQ ++ bool ++ depends on AUFS_DEBUG && MAGIC_SYSRQ ++ default y ++ help ++ Automatic configuration for internal use. ++ When aufs supports Magic SysRq, enabled automatically. ++endif +diff --git a/fs/aufs/Makefile b/fs/aufs/Makefile +new file mode 100644 +index 0000000..c7efb62 +--- /dev/null ++++ b/fs/aufs/Makefile +@@ -0,0 +1,36 @@ ++ ++include ${srctree}/${src}/magic.mk ++ ++# cf. include/linux/kernel.h ++# enable pr_debug ++ccflags-y += -DDEBUG ++# sparse requires the full pathname ++ccflags-y += -include ${srctree}/include/uapi/linux/aufs_type.h ++ ++obj-$(CONFIG_AUFS_FS) += aufs.o ++aufs-y := module.o sbinfo.o super.o branch.o xino.o sysaufs.o opts.o \ ++ wkq.o vfsub.o dcsub.o \ ++ cpup.o whout.o wbr_policy.o \ ++ dinfo.o dentry.o \ ++ dynop.o \ ++ finfo.o file.o f_op.o \ ++ dir.o vdir.o \ ++ iinfo.o inode.o i_op.o i_op_add.o i_op_del.o i_op_ren.o \ ++ mvdown.o ioctl.o ++ ++# all are boolean ++aufs-$(CONFIG_PROC_FS) += procfs.o plink.o ++aufs-$(CONFIG_SYSFS) += sysfs.o ++aufs-$(CONFIG_DEBUG_FS) += dbgaufs.o ++aufs-$(CONFIG_AUFS_BDEV_LOOP) += loop.o ++aufs-$(CONFIG_AUFS_HNOTIFY) += hnotify.o ++aufs-$(CONFIG_AUFS_HFSNOTIFY) += hfsnotify.o ++aufs-$(CONFIG_AUFS_EXPORT) += export.o ++aufs-$(CONFIG_AUFS_XATTR) += xattr.o ++aufs-$(CONFIG_FS_POSIX_ACL) += posix_acl.o ++aufs-$(CONFIG_AUFS_FHSM) += fhsm.o ++aufs-$(CONFIG_AUFS_POLL) += poll.o ++aufs-$(CONFIG_AUFS_RDU) += rdu.o ++aufs-$(CONFIG_AUFS_BR_HFSPLUS) += hfsplus.o ++aufs-$(CONFIG_AUFS_DEBUG) += debug.o ++aufs-$(CONFIG_AUFS_MAGIC_SYSRQ) += sysrq.o +diff --git a/fs/aufs/aufs.h b/fs/aufs/aufs.h +new file mode 100644 +index 0000000..49f43b4 +--- /dev/null ++++ b/fs/aufs/aufs.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima @@ -1771,10 +2083,12 @@ diff -Nurp linux-4.4.3/fs/aufs/aufs.h linux-4.4.3-aufs/fs/aufs/aufs.h + +#endif /* __KERNEL__ */ +#endif /* __AUFS_H__ */ -diff -Nurp linux-4.4.3/fs/aufs/branch.c linux-4.4.3-aufs/fs/aufs/branch.c ---- linux-4.4.3/fs/aufs/branch.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/branch.c 2016-03-02 17:59:37.361498688 +0200 -@@ -0,0 +1,1394 @@ +diff --git a/fs/aufs/branch.c b/fs/aufs/branch.c +new file mode 100644 +index 0000000..5259d30 +--- /dev/null ++++ b/fs/aufs/branch.c +@@ -0,0 +1,1393 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima + */ @@ -1802,7 +2116,8 @@ diff -Nurp linux-4.4.3/fs/aufs/branch.c linux-4.4.3-aufs/fs/aufs/branch.c + fput(br->br_xino.xi_file); + mutex_destroy(&br->br_xino.xi_nondir_mtx); + -+ AuDebugOn(atomic_read(&br->br_count)); ++ AuDebugOn(au_br_count(br)); ++ au_br_count_fin(br); + + wbr = br->br_wbr; + if (wbr) { @@ -1842,7 +2157,7 @@ diff -Nurp linux-4.4.3/fs/aufs/branch.c linux-4.4.3-aufs/fs/aufs/branch.c + + AuRwMustWriteLock(&sbinfo->si_rwsem); + -+ bmax = sbinfo->si_bend + 1; ++ bmax = sbinfo->si_bbot + 1; + br = sbinfo->si_branch; + while (bmax--) + au_br_do_free(*br++); @@ -1853,10 +2168,10 @@ diff -Nurp linux-4.4.3/fs/aufs/branch.c linux-4.4.3-aufs/fs/aufs/branch.c + */ +int au_br_index(struct super_block *sb, aufs_bindex_t br_id) +{ -+ aufs_bindex_t bindex, bend; ++ aufs_bindex_t bindex, bbot; + -+ bend = au_sbend(sb); -+ for (bindex = 0; bindex <= bend; bindex++) ++ bbot = au_sbbot(sb); ++ for (bindex = 0; bindex <= bbot; bindex++) + if (au_sbr_id(sb, bindex) == br_id) + return bindex; + return -1; @@ -1921,7 +2236,7 @@ diff -Nurp linux-4.4.3/fs/aufs/branch.c linux-4.4.3-aufs/fs/aufs/branch.c + err = au_di_realloc(au_di(root), new_nbranch); + if (!err) { + inode = d_inode(root); -+ err = au_ii_realloc(au_ii(inode), new_nbranch); ++ err = au_hinode_realloc(au_ii(inode), new_nbranch); + } + if (!err) + return add_branch; /* success */ @@ -1963,13 +2278,13 @@ diff -Nurp linux-4.4.3/fs/aufs/branch.c linux-4.4.3-aufs/fs/aufs/branch.c +static int test_add(struct super_block *sb, struct au_opt_add *add, int remount) +{ + int err; -+ aufs_bindex_t bend, bindex; ++ aufs_bindex_t bbot, bindex; + struct dentry *root, *h_dentry; + struct inode *inode, *h_inode; + + root = sb->s_root; -+ bend = au_sbend(sb); -+ if (unlikely(bend >= 0 ++ bbot = au_sbbot(sb); ++ if (unlikely(bbot >= 0 + && au_find_dbindex(root, add->path.dentry) >= 0)) { + err = 1; + if (!remount) { @@ -1981,13 +2296,13 @@ diff -Nurp linux-4.4.3/fs/aufs/branch.c linux-4.4.3-aufs/fs/aufs/branch.c + + err = -ENOSPC; /* -E2BIG; */ + if (unlikely(AUFS_BRANCH_MAX <= add->bindex -+ || AUFS_BRANCH_MAX - 1 <= bend)) { ++ || AUFS_BRANCH_MAX - 1 <= bbot)) { + pr_err("number of branches exceeded %s\n", add->pathname); + goto out; + } + + err = -EDOM; -+ if (unlikely(add->bindex < 0 || bend + 1 < add->bindex)) { ++ if (unlikely(add->bindex < 0 || bbot + 1 < add->bindex)) { + pr_err("bad index %d\n", add->bindex); + goto out; + } @@ -2021,11 +2336,11 @@ diff -Nurp linux-4.4.3/fs/aufs/branch.c linux-4.4.3-aufs/fs/aufs/branch.c + if (unlikely(err)) + goto out; + -+ if (bend < 0) ++ if (bbot < 0) + return 0; /* success */ + + err = -EINVAL; -+ for (bindex = 0; bindex <= bend; bindex++) ++ for (bindex = 0; bindex <= bbot; bindex++) + if (unlikely(test_overlap(sb, add->path.dentry, + au_h_dptr(root, bindex)))) { + pr_err("%s is overlapped\n", add->pathname); @@ -2059,7 +2374,7 @@ diff -Nurp linux-4.4.3/fs/aufs/branch.c linux-4.4.3-aufs/fs/aufs/branch.c +{ + int err, old_perm; + aufs_bindex_t bindex; -+ struct mutex *h_mtx; ++ struct inode *h_inode; + struct au_wbr *wbr; + struct au_hinode *hdir; + struct dentry *h_dentry; @@ -2072,15 +2387,15 @@ diff -Nurp linux-4.4.3/fs/aufs/branch.c linux-4.4.3-aufs/fs/aufs/branch.c + old_perm = br->br_perm; + br->br_perm = new_perm; + hdir = NULL; -+ h_mtx = NULL; ++ h_inode = NULL; + bindex = au_br_index(sb, br->br_id); + if (0 <= bindex) { + hdir = au_hi(d_inode(sb->s_root), bindex); + au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); + } else { + h_dentry = au_br_dentry(br); -+ h_mtx = &d_inode(h_dentry)->i_mutex; -+ mutex_lock_nested(h_mtx, AuLsc_I_PARENT); ++ h_inode = d_inode(h_dentry); ++ inode_lock_nested(h_inode, AuLsc_I_PARENT); + } + if (!wbr) + err = au_wh_init(br, sb); @@ -2092,7 +2407,7 @@ diff -Nurp linux-4.4.3/fs/aufs/branch.c linux-4.4.3-aufs/fs/aufs/branch.c + if (hdir) + au_hn_imtx_unlock(hdir); + else -+ mutex_unlock(h_mtx); ++ inode_unlock(h_inode); + vfsub_mnt_drop_write(au_br_mnt(br)); + br->br_perm = old_perm; + @@ -2147,7 +2462,7 @@ diff -Nurp linux-4.4.3/fs/aufs/branch.c linux-4.4.3-aufs/fs/aufs/branch.c + br->br_perm = add->perm; + br->br_path = add->path; /* set first, path_get() later */ + spin_lock_init(&br->br_dykey_lock); -+ atomic_set(&br->br_count, 0); ++ au_br_count_init(br); + atomic_set(&br->br_xino_running, 0); + br->br_id = au_new_br_id(sb); + AuDebugOn(br->br_id < 0); @@ -2179,7 +2494,7 @@ diff -Nurp linux-4.4.3/fs/aufs/branch.c linux-4.4.3-aufs/fs/aufs/branch.c +} + +static void au_br_do_add_brp(struct au_sbinfo *sbinfo, aufs_bindex_t bindex, -+ struct au_branch *br, aufs_bindex_t bend, ++ struct au_branch *br, aufs_bindex_t bbot, + aufs_bindex_t amount) +{ + struct au_branch **brp; @@ -2189,13 +2504,13 @@ diff -Nurp linux-4.4.3/fs/aufs/branch.c linux-4.4.3-aufs/fs/aufs/branch.c + brp = sbinfo->si_branch + bindex; + memmove(brp + 1, brp, sizeof(*brp) * amount); + *brp = br; -+ sbinfo->si_bend++; -+ if (unlikely(bend < 0)) -+ sbinfo->si_bend = 0; ++ sbinfo->si_bbot++; ++ if (unlikely(bbot < 0)) ++ sbinfo->si_bbot = 0; +} + +static void au_br_do_add_hdp(struct au_dinfo *dinfo, aufs_bindex_t bindex, -+ aufs_bindex_t bend, aufs_bindex_t amount) ++ aufs_bindex_t bbot, aufs_bindex_t amount) +{ + struct au_hdentry *hdp; + @@ -2204,25 +2519,24 @@ diff -Nurp linux-4.4.3/fs/aufs/branch.c linux-4.4.3-aufs/fs/aufs/branch.c + hdp = dinfo->di_hdentry + bindex; + memmove(hdp + 1, hdp, sizeof(*hdp) * amount); + au_h_dentry_init(hdp); -+ dinfo->di_bend++; -+ if (unlikely(bend < 0)) -+ dinfo->di_bstart = 0; ++ dinfo->di_bbot++; ++ if (unlikely(bbot < 0)) ++ dinfo->di_btop = 0; +} + +static void au_br_do_add_hip(struct au_iinfo *iinfo, aufs_bindex_t bindex, -+ aufs_bindex_t bend, aufs_bindex_t amount) ++ aufs_bindex_t bbot, aufs_bindex_t amount) +{ + struct au_hinode *hip; + + AuRwMustWriteLock(&iinfo->ii_rwsem); + -+ hip = iinfo->ii_hinode + bindex; ++ hip = au_hinode(iinfo, bindex); + memmove(hip + 1, hip, sizeof(*hip) * amount); -+ hip->hi_inode = NULL; -+ au_hn_init(hip); -+ iinfo->ii_bend++; -+ if (unlikely(bend < 0)) -+ iinfo->ii_bstart = 0; ++ au_hinode_init(hip); ++ iinfo->ii_bbot++; ++ if (unlikely(bbot < 0)) ++ iinfo->ii_btop = 0; +} + +static void au_br_do_add(struct super_block *sb, struct au_branch *br, @@ -2230,17 +2544,17 @@ diff -Nurp linux-4.4.3/fs/aufs/branch.c linux-4.4.3-aufs/fs/aufs/branch.c +{ + struct dentry *root, *h_dentry; + struct inode *root_inode, *h_inode; -+ aufs_bindex_t bend, amount; ++ aufs_bindex_t bbot, amount; + + root = sb->s_root; + root_inode = d_inode(root); -+ bend = au_sbend(sb); -+ amount = bend + 1 - bindex; ++ bbot = au_sbbot(sb); ++ amount = bbot + 1 - bindex; + h_dentry = au_br_dentry(br); + au_sbilist_lock(); -+ au_br_do_add_brp(au_sbi(sb), bindex, br, bend, amount); -+ au_br_do_add_hdp(au_di(root), bindex, bend, amount); -+ au_br_do_add_hip(au_ii(root_inode), bindex, bend, amount); ++ au_br_do_add_brp(au_sbi(sb), bindex, br, bbot, amount); ++ au_br_do_add_hdp(au_di(root), bindex, bbot, amount); ++ au_br_do_add_hip(au_ii(root_inode), bindex, bbot, amount); + au_set_h_dptr(root, bindex, dget(h_dentry)); + h_inode = d_inode(h_dentry); + au_set_h_iptr(root_inode, bindex, au_igrab(h_inode), /*flags*/0); @@ -2250,7 +2564,7 @@ diff -Nurp linux-4.4.3/fs/aufs/branch.c linux-4.4.3-aufs/fs/aufs/branch.c +int au_br_add(struct super_block *sb, struct au_opt_add *add, int remount) +{ + int err; -+ aufs_bindex_t bend, add_bindex; ++ aufs_bindex_t bbot, add_bindex; + struct dentry *root, *h_dentry; + struct inode *root_inode; + struct au_branch *add_branch; @@ -2266,8 +2580,8 @@ diff -Nurp linux-4.4.3/fs/aufs/branch.c linux-4.4.3-aufs/fs/aufs/branch.c + goto out; /* success */ + } + -+ bend = au_sbend(sb); -+ add_branch = au_br_alloc(sb, bend + 2, add->perm); ++ bbot = au_sbbot(sb); ++ add_branch = au_br_alloc(sb, bbot + 2, add->perm); + err = PTR_ERR(add_branch); + if (IS_ERR(add_branch)) + goto out; @@ -2343,7 +2657,7 @@ diff -Nurp linux-4.4.3/fs/aufs/branch.c linux-4.4.3-aufs/fs/aufs/branch.c +static struct file **au_farray_alloc(struct super_block *sb, + unsigned long long *max) +{ -+ *max = atomic_long_read(&au_sbi(sb)->si_nfiles); ++ *max = au_nfiles(sb); + return au_array_alloc(max, au_farray_cb, sb, /*arg*/NULL); +} + @@ -2369,16 +2683,16 @@ diff -Nurp linux-4.4.3/fs/aufs/branch.c linux-4.4.3-aufs/fs/aufs/branch.c + pr_info(fmt, ##__VA_ARGS__); \ +} while (0) + -+static int au_test_ibusy(struct inode *inode, aufs_bindex_t bstart, -+ aufs_bindex_t bend) ++static int au_test_ibusy(struct inode *inode, aufs_bindex_t btop, ++ aufs_bindex_t bbot) +{ -+ return (inode && !S_ISDIR(inode->i_mode)) || bstart == bend; ++ return (inode && !S_ISDIR(inode->i_mode)) || btop == bbot; +} + -+static int au_test_dbusy(struct dentry *dentry, aufs_bindex_t bstart, -+ aufs_bindex_t bend) ++static int au_test_dbusy(struct dentry *dentry, aufs_bindex_t btop, ++ aufs_bindex_t bbot) +{ -+ return au_test_ibusy(d_inode(dentry), bstart, bend); ++ return au_test_ibusy(d_inode(dentry), btop, bbot); +} + +/* @@ -2388,7 +2702,7 @@ diff -Nurp linux-4.4.3/fs/aufs/branch.c linux-4.4.3-aufs/fs/aufs/branch.c + unsigned int sigen, const unsigned int verbose) +{ + int err, i, j, ndentry; -+ aufs_bindex_t bstart, bend; ++ aufs_bindex_t btop, bbot; + struct au_dcsub_pages dpages; + struct au_dpage *dpage; + struct dentry *d; @@ -2428,12 +2742,12 @@ diff -Nurp linux-4.4.3/fs/aufs/branch.c linux-4.4.3-aufs/fs/aufs/branch.c + } + + /* AuDbgDentry(d); */ -+ bstart = au_dbstart(d); -+ bend = au_dbend(d); -+ if (bstart <= bindex -+ && bindex <= bend ++ btop = au_dbtop(d); ++ bbot = au_dbbot(d); ++ if (btop <= bindex ++ && bindex <= bbot + && au_h_dptr(d, bindex) -+ && au_test_dbusy(d, bstart, bend)) { ++ && au_test_dbusy(d, btop, bbot)) { + err = -EBUSY; + AuVerbose(verbose, "busy %pd\n", d); + AuDbgDentry(d); @@ -2454,7 +2768,7 @@ diff -Nurp linux-4.4.3/fs/aufs/branch.c linux-4.4.3-aufs/fs/aufs/branch.c + int err; + unsigned long long max, ull; + struct inode *i, **array; -+ aufs_bindex_t bstart, bend; ++ aufs_bindex_t btop, bbot; + + array = au_iarray_alloc(sb, &max); + err = PTR_ERR(array); @@ -2485,12 +2799,12 @@ diff -Nurp linux-4.4.3/fs/aufs/branch.c linux-4.4.3-aufs/fs/aufs/branch.c + } + } + -+ bstart = au_ibstart(i); -+ bend = au_ibend(i); -+ if (bstart <= bindex -+ && bindex <= bend ++ btop = au_ibtop(i); ++ bbot = au_ibbot(i); ++ if (btop <= bindex ++ && bindex <= bbot + && au_h_iptr(i, bindex) -+ && au_test_ibusy(i, bstart, bend)) { ++ && au_test_ibusy(i, btop, bbot)) { + err = -EBUSY; + AuVerbose(verbose, "busy i%lu\n", i->i_ino); + AuDbgInode(i); @@ -2526,7 +2840,7 @@ diff -Nurp linux-4.4.3/fs/aufs/branch.c linux-4.4.3-aufs/fs/aufs/branch.c +{ + int err; + unsigned char matched, root; -+ aufs_bindex_t bindex, bend; ++ aufs_bindex_t bindex, bbot; + struct au_fidir *fidir; + struct au_hfile *hfile; + @@ -2542,8 +2856,8 @@ diff -Nurp linux-4.4.3/fs/aufs/branch.c linux-4.4.3-aufs/fs/aufs/branch.c + matched = 0; + fidir = au_fi(file)->fi_hdir; + AuDebugOn(!fidir); -+ bend = au_fbend_dir(file); -+ for (bindex = au_fbstart(file); bindex <= bend; bindex++) { ++ bbot = au_fbbot_dir(file); ++ for (bindex = au_fbtop(file); bindex <= bbot; bindex++) { + hfile = fidir->fd_hfile + bindex; + if (!hfile->hf_file) + continue; @@ -2565,7 +2879,7 @@ diff -Nurp linux-4.4.3/fs/aufs/branch.c linux-4.4.3-aufs/fs/aufs/branch.c +{ + int err, idx; + unsigned long long ull, max; -+ aufs_bindex_t bstart; ++ aufs_bindex_t btop; + struct file *file, **array; + struct dentry *root; + struct au_hfile *hfile; @@ -2586,7 +2900,7 @@ diff -Nurp linux-4.4.3/fs/aufs/branch.c linux-4.4.3-aufs/fs/aufs/branch.c + + /* AuDbg("%pD\n", file); */ + fi_read_lock(file); -+ bstart = au_fbstart(file); ++ btop = au_fbtop(file); + if (!d_is_dir(file->f_path.dentry)) { + hfile = &au_fi(file)->fi_htop; + if (hfile->hf_br->br_id == br_id) @@ -2609,7 +2923,7 @@ diff -Nurp linux-4.4.3/fs/aufs/branch.c linux-4.4.3-aufs/fs/aufs/branch.c + aufs_bindex_t br_id) +{ + unsigned long long ull; -+ aufs_bindex_t bindex, bstart, bend, bfound; ++ aufs_bindex_t bindex, btop, bbot, bfound; + struct file *file; + struct au_fidir *fidir; + struct au_hfile *hfile; @@ -2625,9 +2939,9 @@ diff -Nurp linux-4.4.3/fs/aufs/branch.c linux-4.4.3-aufs/fs/aufs/branch.c + fidir = au_fi(file)->fi_hdir; + AuDebugOn(!fidir); + fi_write_lock(file); -+ bstart = au_fbstart(file); -+ bend = au_fbend_dir(file); -+ for (bindex = bstart; bindex <= bend; bindex++) { ++ btop = au_fbtop(file); ++ bbot = au_fbbot_dir(file); ++ for (bindex = btop; bindex <= bbot; bindex++) { + hfile = fidir->fd_hfile + bindex; + if (!hfile->hf_file) + continue; @@ -2639,10 +2953,10 @@ diff -Nurp linux-4.4.3/fs/aufs/branch.c linux-4.4.3-aufs/fs/aufs/branch.c + } + AuDebugOn(bfound < 0); + au_set_h_fptr(file, bfound, NULL); -+ if (bfound == bstart) { -+ for (bstart++; bstart <= bend; bstart++) -+ if (au_hf_dir(file, bstart)) { -+ au_set_fbstart(file, bstart); ++ if (bfound == btop) { ++ for (btop++; btop <= bbot; btop++) ++ if (au_hf_dir(file, btop)) { ++ au_set_fbtop(file, btop); + break; + } + } @@ -2652,59 +2966,58 @@ diff -Nurp linux-4.4.3/fs/aufs/branch.c linux-4.4.3-aufs/fs/aufs/branch.c + +static void au_br_do_del_brp(struct au_sbinfo *sbinfo, + const aufs_bindex_t bindex, -+ const aufs_bindex_t bend) ++ const aufs_bindex_t bbot) +{ + struct au_branch **brp, **p; + + AuRwMustWriteLock(&sbinfo->si_rwsem); + + brp = sbinfo->si_branch + bindex; -+ if (bindex < bend) -+ memmove(brp, brp + 1, sizeof(*brp) * (bend - bindex)); -+ sbinfo->si_branch[0 + bend] = NULL; -+ sbinfo->si_bend--; ++ if (bindex < bbot) ++ memmove(brp, brp + 1, sizeof(*brp) * (bbot - bindex)); ++ sbinfo->si_branch[0 + bbot] = NULL; ++ sbinfo->si_bbot--; + -+ p = krealloc(sbinfo->si_branch, sizeof(*p) * bend, AuGFP_SBILIST); ++ p = krealloc(sbinfo->si_branch, sizeof(*p) * bbot, AuGFP_SBILIST); + if (p) + sbinfo->si_branch = p; + /* harmless error */ +} + +static void au_br_do_del_hdp(struct au_dinfo *dinfo, const aufs_bindex_t bindex, -+ const aufs_bindex_t bend) ++ const aufs_bindex_t bbot) +{ + struct au_hdentry *hdp, *p; + + AuRwMustWriteLock(&dinfo->di_rwsem); + + hdp = dinfo->di_hdentry; -+ if (bindex < bend) ++ if (bindex < bbot) + memmove(hdp + bindex, hdp + bindex + 1, -+ sizeof(*hdp) * (bend - bindex)); -+ hdp[0 + bend].hd_dentry = NULL; -+ dinfo->di_bend--; ++ sizeof(*hdp) * (bbot - bindex)); ++ hdp[0 + bbot].hd_dentry = NULL; ++ dinfo->di_bbot--; + -+ p = krealloc(hdp, sizeof(*p) * bend, AuGFP_SBILIST); ++ p = krealloc(hdp, sizeof(*p) * bbot, AuGFP_SBILIST); + if (p) + dinfo->di_hdentry = p; + /* harmless error */ +} + +static void au_br_do_del_hip(struct au_iinfo *iinfo, const aufs_bindex_t bindex, -+ const aufs_bindex_t bend) ++ const aufs_bindex_t bbot) +{ + struct au_hinode *hip, *p; + + AuRwMustWriteLock(&iinfo->ii_rwsem); + -+ hip = iinfo->ii_hinode + bindex; -+ if (bindex < bend) -+ memmove(hip, hip + 1, sizeof(*hip) * (bend - bindex)); -+ iinfo->ii_hinode[0 + bend].hi_inode = NULL; -+ au_hn_init(iinfo->ii_hinode + bend); -+ iinfo->ii_bend--; ++ hip = au_hinode(iinfo, bindex); ++ if (bindex < bbot) ++ memmove(hip, hip + 1, sizeof(*hip) * (bbot - bindex)); ++ /* au_hinode_init(au_hinode(iinfo, bbot)); */ ++ iinfo->ii_bbot--; + -+ p = krealloc(iinfo->ii_hinode, sizeof(*p) * bend, AuGFP_SBILIST); ++ p = krealloc(iinfo->ii_hinode, sizeof(*p) * bbot, AuGFP_SBILIST); + if (p) + iinfo->ii_hinode = p; + /* harmless error */ @@ -2713,7 +3026,7 @@ diff -Nurp linux-4.4.3/fs/aufs/branch.c linux-4.4.3-aufs/fs/aufs/branch.c +static void au_br_do_del(struct super_block *sb, aufs_bindex_t bindex, + struct au_branch *br) +{ -+ aufs_bindex_t bend; ++ aufs_bindex_t bbot; + struct au_sbinfo *sbinfo; + struct dentry *root, *h_root; + struct inode *inode, *h_inode; @@ -2724,7 +3037,7 @@ diff -Nurp linux-4.4.3/fs/aufs/branch.c linux-4.4.3-aufs/fs/aufs/branch.c + root = sb->s_root; + inode = d_inode(root); + sbinfo = au_sbi(sb); -+ bend = sbinfo->si_bend; ++ bbot = sbinfo->si_bbot; + + h_root = au_h_dptr(root, bindex); + hinode = au_hi(inode, bindex); @@ -2732,9 +3045,9 @@ diff -Nurp linux-4.4.3/fs/aufs/branch.c linux-4.4.3-aufs/fs/aufs/branch.c + au_hiput(hinode); + + au_sbilist_lock(); -+ au_br_do_del_brp(sbinfo, bindex, bend); -+ au_br_do_del_hdp(au_di(root), bindex, bend); -+ au_br_do_del_hip(au_ii(inode), bindex, bend); ++ au_br_do_del_brp(sbinfo, bindex, bbot); ++ au_br_do_del_hdp(au_di(root), bindex, bbot); ++ au_br_do_del_hip(au_ii(inode), bindex, bbot); + au_sbilist_unlock(); + + dput(h_root); @@ -2753,7 +3066,7 @@ diff -Nurp linux-4.4.3/fs/aufs/branch.c linux-4.4.3-aufs/fs/aufs/branch.c + int err, rerr, i; + unsigned long long opened; + unsigned int mnt_flags; -+ aufs_bindex_t bindex, bend, br_id; ++ aufs_bindex_t bindex, bbot, br_id; + unsigned char do_wh, verbose; + struct au_branch *br; + struct au_wbr *wbr; @@ -2777,8 +3090,8 @@ diff -Nurp linux-4.4.3/fs/aufs/branch.c linux-4.4.3-aufs/fs/aufs/branch.c + err = -EBUSY; + mnt_flags = au_mntflags(sb); + verbose = !!au_opt_test(mnt_flags, VERBOSE); -+ bend = au_sbend(sb); -+ if (unlikely(!bend)) { ++ bbot = au_sbbot(sb); ++ if (unlikely(!bbot)) { + AuVerbose(verbose, "no more branches left\n"); + goto out; + } @@ -2786,7 +3099,7 @@ diff -Nurp linux-4.4.3/fs/aufs/branch.c linux-4.4.3-aufs/fs/aufs/branch.c + AuDebugOn(!path_equal(&br->br_path, &del->h_path)); + + br_id = br->br_id; -+ opened = atomic_read(&br->br_count); ++ opened = au_br_count(br); + if (unlikely(opened)) { + to_free = au_array_alloc(&opened, empty_cb, sb, NULL); + err = PTR_ERR(to_free); @@ -2866,7 +3179,7 @@ diff -Nurp linux-4.4.3/fs/aufs/branch.c linux-4.4.3-aufs/fs/aufs/branch.c +static int au_ibusy(struct super_block *sb, struct aufs_ibusy __user *arg) +{ + int err; -+ aufs_bindex_t bstart, bend; ++ aufs_bindex_t btop, bbot; + struct aufs_ibusy ibusy; + struct inode *inode, *h_inode; + @@ -2885,7 +3198,7 @@ diff -Nurp linux-4.4.3/fs/aufs/branch.c linux-4.4.3-aufs/fs/aufs/branch.c + + err = -EINVAL; + si_read_lock(sb, AuLock_FLUSH); -+ if (unlikely(ibusy.bindex < 0 || ibusy.bindex > au_sbend(sb))) ++ if (unlikely(ibusy.bindex < 0 || ibusy.bindex > au_sbbot(sb))) + goto out_unlock; + + err = 0; @@ -2897,11 +3210,11 @@ diff -Nurp linux-4.4.3/fs/aufs/branch.c linux-4.4.3-aufs/fs/aufs/branch.c + goto out_unlock; + + ii_read_lock_child(inode); -+ bstart = au_ibstart(inode); -+ bend = au_ibend(inode); -+ if (bstart <= ibusy.bindex && ibusy.bindex <= bend) { ++ btop = au_ibtop(inode); ++ bbot = au_ibbot(inode); ++ if (btop <= ibusy.bindex && ibusy.bindex <= bbot) { + h_inode = au_h_iptr(inode, ibusy.bindex); -+ if (h_inode && au_test_ibusy(inode, bstart, bend)) ++ if (h_inode && au_test_ibusy(inode, btop, bbot)) + ibusy.h_ino = h_inode->i_ino; + } + ii_read_unlock(inode); @@ -3169,10 +3482,12 @@ diff -Nurp linux-4.4.3/fs/aufs/branch.c linux-4.4.3-aufs/fs/aufs/branch.c + + return err; +} -diff -Nurp linux-4.4.3/fs/aufs/branch.h linux-4.4.3-aufs/fs/aufs/branch.h ---- linux-4.4.3/fs/aufs/branch.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/branch.h 2016-03-02 17:59:37.361498688 +0200 -@@ -0,0 +1,266 @@ +diff --git a/fs/aufs/branch.h b/fs/aufs/branch.h +new file mode 100644 +index 0000000..32a4d8f +--- /dev/null ++++ b/fs/aufs/branch.h +@@ -0,0 +1,296 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima + */ @@ -3261,7 +3576,7 @@ diff -Nurp linux-4.4.3/fs/aufs/branch.h linux-4.4.3-aufs/fs/aufs/branch.h + struct path br_path; + spinlock_t br_dykey_lock; + struct au_dykey *br_dykey[AuBrDynOp]; -+ atomic_t br_count; ++ struct percpu_counter br_count; + + struct au_wbr *br_wbr; + struct au_br_fhsm *br_fhsm; @@ -3296,6 +3611,31 @@ diff -Nurp linux-4.4.3/fs/aufs/branch.h linux-4.4.3-aufs/fs/aufs/branch.h + return au_br_mnt(br)->mnt_sb; +} + ++static inline void au_br_get(struct au_branch *br) ++{ ++ percpu_counter_inc(&br->br_count); ++} ++ ++static inline void au_br_put(struct au_branch *br) ++{ ++ percpu_counter_dec(&br->br_count); ++} ++ ++static inline s64 au_br_count(struct au_branch *br) ++{ ++ return percpu_counter_sum(&br->br_count); ++} ++ ++static inline void au_br_count_init(struct au_branch *br) ++{ ++ percpu_counter_init(&br->br_count, 0, GFP_NOFS); ++} ++ ++static inline void au_br_count_fin(struct au_branch *br) ++{ ++ percpu_counter_destroy(&br->br_count); ++} ++ +static inline int au_br_rdonly(struct au_branch *br) +{ + return ((au_br_sb(br)->s_flags & MS_RDONLY) @@ -3391,9 +3731,14 @@ diff -Nurp linux-4.4.3/fs/aufs/branch.h linux-4.4.3-aufs/fs/aufs/branch.h + return au_br_sb(au_sbr(sb, bindex)); +} + ++static inline void au_sbr_get(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ au_br_get(au_sbr(sb, bindex)); ++} ++ +static inline void au_sbr_put(struct super_block *sb, aufs_bindex_t bindex) +{ -+ atomic_dec(&au_sbr(sb, bindex)->br_count); ++ au_br_put(au_sbr(sb, bindex)); +} + +static inline int au_sbr_perm(struct super_block *sb, aufs_bindex_t bindex) @@ -3439,9 +3784,11 @@ diff -Nurp linux-4.4.3/fs/aufs/branch.h linux-4.4.3-aufs/fs/aufs/branch.h + +#endif /* __KERNEL__ */ +#endif /* __AUFS_BRANCH_H__ */ -diff -Nurp linux-4.4.3/fs/aufs/cpup.c linux-4.4.3-aufs/fs/aufs/cpup.c ---- linux-4.4.3/fs/aufs/cpup.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/cpup.c 2016-03-02 17:59:37.362498696 +0200 +diff --git a/fs/aufs/cpup.c b/fs/aufs/cpup.c +new file mode 100644 +index 0000000..dbf96fd +--- /dev/null ++++ b/fs/aufs/cpup.c @@ -0,0 +1,1366 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima @@ -3472,7 +3819,7 @@ diff -Nurp linux-4.4.3/fs/aufs/cpup.c linux-4.4.3-aufs/fs/aufs/cpup.c +{ + struct inode *h_inode; + -+ h_inode = au_h_iptr(inode, au_ibstart(inode)); ++ h_inode = au_h_iptr(inode, au_ibtop(inode)); + fsstack_copy_attr_times(inode, h_inode); + fsstack_copy_inode_size(inode, h_inode); +} @@ -3481,10 +3828,10 @@ diff -Nurp linux-4.4.3/fs/aufs/cpup.c linux-4.4.3-aufs/fs/aufs/cpup.c +{ + struct inode *h_inode; + struct super_block *sb; -+ aufs_bindex_t bindex, bend; ++ aufs_bindex_t bindex, bbot; + + sb = inode->i_sb; -+ bindex = au_ibstart(inode); ++ bindex = au_ibtop(inode); + h_inode = au_h_iptr(inode, bindex); + if (!force + && !S_ISDIR(h_inode->i_mode) @@ -3507,8 +3854,8 @@ diff -Nurp linux-4.4.3/fs/aufs/cpup.c linux-4.4.3-aufs/fs/aufs/cpup.c + * it may includes whplink directory. + */ + if (S_ISDIR(h_inode->i_mode)) { -+ bend = au_ibend(inode); -+ for (bindex++; bindex <= bend; bindex++) { ++ bbot = au_ibbot(inode); ++ for (bindex++; bindex <= bbot; bindex++) { + h_inode = au_h_iptr(inode, bindex); + if (h_inode) + au_add_nlink(inode, h_inode); @@ -3520,7 +3867,7 @@ diff -Nurp linux-4.4.3/fs/aufs/cpup.c linux-4.4.3-aufs/fs/aufs/cpup.c +{ + struct inode *h_inode; + -+ h_inode = au_h_iptr(inode, au_ibstart(inode)); ++ h_inode = au_h_iptr(inode, au_ibtop(inode)); + inode->i_mode = h_inode->i_mode; + inode->i_uid = h_inode->i_uid; + inode->i_gid = h_inode->i_gid; @@ -3542,7 +3889,7 @@ diff -Nurp linux-4.4.3/fs/aufs/cpup.c linux-4.4.3-aufs/fs/aufs/cpup.c +{ + struct inode *h_inode; + -+ h_inode = au_h_iptr(inode, au_ibstart(inode)); ++ h_inode = au_h_iptr(inode, au_ibtop(inode)); + au_cpup_attr_changeable(inode); + if (inode->i_nlink > 0) + au_cpup_attr_nlink(inode, force); @@ -3670,7 +4017,7 @@ diff -Nurp linux-4.4.3/fs/aufs/cpup.c linux-4.4.3-aufs/fs/aufs/cpup.c + size_t sz, rbytes, wbytes; + unsigned char all_zero; + char *p, *zp; -+ struct mutex *h_mtx; ++ struct inode *h_inode; + /* reduce stack usage */ + struct iattr *ia; + @@ -3750,12 +4097,12 @@ diff -Nurp linux-4.4.3/fs/aufs/cpup.c linux-4.4.3-aufs/fs/aufs/cpup.c + ia->ia_size = dst->f_pos; + ia->ia_valid = ATTR_SIZE | ATTR_FILE; + ia->ia_file = dst; -+ h_mtx = &file_inode(dst)->i_mutex; -+ mutex_lock_nested(h_mtx, AuLsc_I_CHILD2); ++ h_inode = file_inode(dst); ++ inode_lock_nested(h_inode, AuLsc_I_CHILD2); + /* no delegation since it is just created */ + err = vfsub_notify_change(&dst->f_path, ia, + /*delegated*/NULL); -+ mutex_unlock(h_mtx); ++ inode_unlock(h_inode); + } + } + @@ -3883,7 +4230,7 @@ diff -Nurp linux-4.4.3/fs/aufs/cpup.c linux-4.4.3-aufs/fs/aufs/cpup.c + cpg->len = l; + if (cpg->len) { + /* try stopping to update while we are referencing */ -+ mutex_lock_nested(&h_src_inode->i_mutex, AuLsc_I_CHILD); ++ inode_lock_nested(h_src_inode, AuLsc_I_CHILD); + au_pin_hdir_unlock(cpg->pin); + + h_path.dentry = au_h_dptr(cpg->dentry, cpg->bsrc); @@ -3892,17 +4239,17 @@ diff -Nurp linux-4.4.3/fs/aufs/cpup.c linux-4.4.3-aufs/fs/aufs/cpup.c + if (!au_test_nfs(h_src_inode->i_sb)) + err = vfs_getattr(&h_path, &h_src_attr->st); + else { -+ mutex_unlock(&h_src_inode->i_mutex); ++ inode_unlock(h_src_inode); + err = vfs_getattr(&h_path, &h_src_attr->st); -+ mutex_lock_nested(&h_src_inode->i_mutex, AuLsc_I_CHILD); ++ inode_lock_nested(h_src_inode, AuLsc_I_CHILD); + } + if (unlikely(err)) { -+ mutex_unlock(&h_src_inode->i_mutex); ++ inode_unlock(h_src_inode); + goto out; + } + h_src_attr->valid = 1; + err = au_cp_regular(cpg); -+ mutex_unlock(&h_src_inode->i_mutex); ++ inode_unlock(h_src_inode); + rerr = au_pin_hdir_relock(cpg->pin); + if (!err && rerr) + err = rerr; @@ -3999,7 +4346,7 @@ diff -Nurp linux-4.4.3/fs/aufs/cpup.c linux-4.4.3-aufs/fs/aufs/cpup.c + * particularry setattr case + */ + dir = d_inode(dst_parent); -+ if (au_ibstart(dir) == cpg->bdst) ++ if (au_ibtop(dir) == cpg->bdst) + au_cpup_attr_nlink(dir, /*force*/1); + inode = d_inode(cpg->dentry); + au_cpup_attr_nlink(inode, /*force*/1); @@ -4160,7 +4507,7 @@ diff -Nurp linux-4.4.3/fs/aufs/cpup.c linux-4.4.3-aufs/fs/aufs/cpup.c +static int au_cpup_single(struct au_cp_generic *cpg, struct dentry *dst_parent) +{ + int err, rerr; -+ aufs_bindex_t old_ibstart; ++ aufs_bindex_t old_ibtop; + unsigned char isdir, plink; + struct dentry *h_src, *h_dst, *h_parent; + struct inode *dst_inode, *h_dir, *inode, *delegated, *src_inode; @@ -4248,12 +4595,12 @@ diff -Nurp linux-4.4.3/fs/aufs/cpup.c linux-4.4.3-aufs/fs/aufs/cpup.c + } + + isdir = S_ISDIR(inode->i_mode); -+ old_ibstart = au_ibstart(inode); ++ old_ibtop = au_ibtop(inode); + err = cpup_entry(cpg, dst_parent, &a->h_src_attr); + if (unlikely(err)) + goto out_rev; + dst_inode = d_inode(h_dst); -+ mutex_lock_nested(&dst_inode->i_mutex, AuLsc_I_CHILD2); ++ inode_lock_nested(dst_inode, AuLsc_I_CHILD2); + /* todo: necessary? */ + /* au_pin_hdir_unlock(cpg->pin); */ + @@ -4261,29 +4608,29 @@ diff -Nurp linux-4.4.3/fs/aufs/cpup.c linux-4.4.3-aufs/fs/aufs/cpup.c + if (unlikely(err)) { + /* todo: necessary? */ + /* au_pin_hdir_relock(cpg->pin); */ /* ignore an error */ -+ mutex_unlock(&dst_inode->i_mutex); ++ inode_unlock(dst_inode); + goto out_rev; + } + -+ if (cpg->bdst < old_ibstart) { ++ if (cpg->bdst < old_ibtop) { + if (S_ISREG(inode->i_mode)) { + err = au_dy_iaop(inode, cpg->bdst, dst_inode); + if (unlikely(err)) { + /* ignore an error */ + /* au_pin_hdir_relock(cpg->pin); */ -+ mutex_unlock(&dst_inode->i_mutex); ++ inode_unlock(dst_inode); + goto out_rev; + } + } -+ au_set_ibstart(inode, cpg->bdst); ++ au_set_ibtop(inode, cpg->bdst); + } else -+ au_set_ibend(inode, cpg->bdst); ++ au_set_ibbot(inode, cpg->bdst); + au_set_h_iptr(inode, cpg->bdst, au_igrab(dst_inode), + au_hi_flags(inode, isdir)); + + /* todo: necessary? */ + /* err = au_pin_hdir_relock(cpg->pin); */ -+ mutex_unlock(&dst_inode->i_mutex); ++ inode_unlock(dst_inode); + if (unlikely(err)) + goto out_rev; + @@ -4433,7 +4780,7 @@ diff -Nurp linux-4.4.3/fs/aufs/cpup.c linux-4.4.3-aufs/fs/aufs/cpup.c + + /* revert */ + au_set_h_dptr(dentry, cpg->bdst, NULL); -+ au_set_dbstart(dentry, cpg->bsrc); ++ au_set_dbtop(dentry, cpg->bsrc); + } + + return err; @@ -4495,20 +4842,20 @@ diff -Nurp linux-4.4.3/fs/aufs/cpup.c linux-4.4.3-aufs/fs/aufs/cpup.c + +int au_sio_cpup_simple(struct au_cp_generic *cpg) +{ -+ aufs_bindex_t bsrc, bend; ++ aufs_bindex_t bsrc, bbot; + struct dentry *dentry, *h_dentry; + + if (cpg->bsrc < 0) { + dentry = cpg->dentry; -+ bend = au_dbend(dentry); -+ for (bsrc = cpg->bdst + 1; bsrc <= bend; bsrc++) { ++ bbot = au_dbbot(dentry); ++ for (bsrc = cpg->bdst + 1; bsrc <= bbot; bsrc++) { + h_dentry = au_h_dptr(dentry, bsrc); + if (h_dentry) { + AuDebugOn(d_is_negative(h_dentry)); + break; + } + } -+ AuDebugOn(bsrc > bend); ++ AuDebugOn(bsrc > bbot); + cpg->bsrc = bsrc; + } + AuDebugOn(cpg->bsrc <= cpg->bdst); @@ -4540,10 +4887,10 @@ diff -Nurp linux-4.4.3/fs/aufs/cpup.c linux-4.4.3-aufs/fs/aufs/cpup.c + AuRwMustWriteLock(&dinfo->di_rwsem); + + bsrc_orig = cpg->bsrc; -+ cpg->bsrc = dinfo->di_bstart; ++ cpg->bsrc = dinfo->di_btop; + hdp = dinfo->di_hdentry; + h_d_dst = hdp[0 + cpg->bdst].hd_dentry; -+ dinfo->di_bstart = cpg->bdst; ++ dinfo->di_btop = cpg->bdst; + hdp[0 + cpg->bdst].hd_dentry = wh_dentry; + h_d_start = NULL; + if (file) { @@ -4560,7 +4907,7 @@ diff -Nurp linux-4.4.3/fs/aufs/cpup.c linux-4.4.3-aufs/fs/aufs/cpup.c + hdp[0 + cpg->bsrc].hd_dentry = h_d_start; + } + hdp[0 + cpg->bdst].hd_dentry = h_d_dst; -+ dinfo->di_bstart = cpg->bsrc; ++ dinfo->di_btop = cpg->bsrc; + cpg->bsrc = bsrc_orig; + + return err; @@ -4657,7 +5004,7 @@ diff -Nurp linux-4.4.3/fs/aufs/cpup.c linux-4.4.3-aufs/fs/aufs/cpup.c + h_tmpdir = d_inode(h_orph); + au_set_h_iptr(dir, bdst, au_igrab(h_tmpdir), /*flags*/0); + -+ mutex_lock_nested(&h_tmpdir->i_mutex, AuLsc_I_PARENT3); ++ inode_lock_nested(h_tmpdir, AuLsc_I_PARENT3); + /* todo: au_h_open_pre()? */ + + pin_orig = cpg->pin; @@ -4681,7 +5028,7 @@ diff -Nurp linux-4.4.3/fs/aufs/cpup.c linux-4.4.3-aufs/fs/aufs/cpup.c + } + + if (h_orph) { -+ mutex_unlock(&h_tmpdir->i_mutex); ++ inode_unlock(h_tmpdir); + /* todo: au_h_open_post()? */ + au_set_h_iptr(dir, bdst, au_igrab(h_dir), /*flags*/0); + au_set_h_dptr(parent, bdst, h_parent); @@ -4744,7 +5091,7 @@ diff -Nurp linux-4.4.3/fs/aufs/cpup.c linux-4.4.3-aufs/fs/aufs/cpup.c + h_dentry = au_h_dptr(d, bdst); + if (!h_dentry || d_is_negative(h_dentry)) { + if (h_dentry) -+ au_update_dbstart(d); ++ au_update_dbtop(d); + + au_pin_set_dentry(&pin, d); + err = au_do_pin(&pin); @@ -4809,9 +5156,11 @@ diff -Nurp linux-4.4.3/fs/aufs/cpup.c linux-4.4.3-aufs/fs/aufs/cpup.c + dput(parent); + return err; +} -diff -Nurp linux-4.4.3/fs/aufs/cpup.h linux-4.4.3-aufs/fs/aufs/cpup.h ---- linux-4.4.3/fs/aufs/cpup.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/cpup.h 2016-03-02 17:59:37.362498696 +0200 +diff --git a/fs/aufs/cpup.h b/fs/aufs/cpup.h +new file mode 100644 +index 0000000..ccba2c4 +--- /dev/null ++++ b/fs/aufs/cpup.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima @@ -4894,9 +5243,11 @@ diff -Nurp linux-4.4.3/fs/aufs/cpup.h linux-4.4.3-aufs/fs/aufs/cpup.h + +#endif /* __KERNEL__ */ +#endif /* __AUFS_CPUP_H__ */ -diff -Nurp linux-4.4.3/fs/aufs/dbgaufs.c linux-4.4.3-aufs/fs/aufs/dbgaufs.c ---- linux-4.4.3/fs/aufs/dbgaufs.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/dbgaufs.c 2016-03-02 17:59:37.362498696 +0200 +diff --git a/fs/aufs/dbgaufs.c b/fs/aufs/dbgaufs.c +new file mode 100644 +index 0000000..21ac773 +--- /dev/null ++++ b/fs/aufs/dbgaufs.c @@ -0,0 +1,419 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima @@ -5125,7 +5476,7 @@ diff -Nurp linux-4.4.3/fs/aufs/dbgaufs.c linux-4.4.3-aufs/fs/aufs/dbgaufs.c + sbinfo = inode->i_private; + sb = sbinfo->si_sb; + si_noflush_read_lock(sb); -+ if (l <= au_sbend(sb)) { ++ if (l <= au_sbbot(sb)) { + xf = au_sbr(sb, (aufs_bindex_t)l)->br_xino.xi_file; + err = dbgaufs_xi_open(xf, file, /*do_fcnt*/1); + } else @@ -5145,15 +5496,15 @@ diff -Nurp linux-4.4.3/fs/aufs/dbgaufs.c linux-4.4.3-aufs/fs/aufs/dbgaufs.c + +void dbgaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex) +{ -+ aufs_bindex_t bend; ++ aufs_bindex_t bbot; + struct au_branch *br; + struct au_xino_file *xi; + + if (!au_sbi(sb)->si_dbgaufs) + return; + -+ bend = au_sbend(sb); -+ for (; bindex <= bend; bindex++) { ++ bbot = au_sbbot(sb); ++ for (; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + xi = &br->br_xino; + debugfs_remove(xi->xi_dbgaufs); @@ -5167,7 +5518,7 @@ diff -Nurp linux-4.4.3/fs/aufs/dbgaufs.c linux-4.4.3-aufs/fs/aufs/dbgaufs.c + struct dentry *parent; + struct au_branch *br; + struct au_xino_file *xi; -+ aufs_bindex_t bend; ++ aufs_bindex_t bbot; + char name[sizeof(DbgaufsXi_PREFIX) + 5]; /* "xi" bindex NULL */ + + sbinfo = au_sbi(sb); @@ -5175,8 +5526,8 @@ diff -Nurp linux-4.4.3/fs/aufs/dbgaufs.c linux-4.4.3-aufs/fs/aufs/dbgaufs.c + if (!parent) + return; + -+ bend = au_sbend(sb); -+ for (; bindex <= bend; bindex++) { ++ bbot = au_sbbot(sb); ++ for (; bindex <= bbot; bindex++) { + snprintf(name, sizeof(name), DbgaufsXi_PREFIX "%d", bindex); + br = au_sbr(sb, bindex); + xi = &br->br_xino; @@ -5317,9 +5668,11 @@ diff -Nurp linux-4.4.3/fs/aufs/dbgaufs.c linux-4.4.3-aufs/fs/aufs/dbgaufs.c + err = 0; + return err; +} -diff -Nurp linux-4.4.3/fs/aufs/dbgaufs.h linux-4.4.3-aufs/fs/aufs/dbgaufs.h ---- linux-4.4.3/fs/aufs/dbgaufs.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/dbgaufs.h 2016-03-02 17:59:37.362498696 +0200 +diff --git a/fs/aufs/dbgaufs.h b/fs/aufs/dbgaufs.h +new file mode 100644 +index 0000000..81f272e +--- /dev/null ++++ b/fs/aufs/dbgaufs.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima @@ -5356,9 +5709,11 @@ diff -Nurp linux-4.4.3/fs/aufs/dbgaufs.h linux-4.4.3-aufs/fs/aufs/dbgaufs.h + +#endif /* __KERNEL__ */ +#endif /* __DBGAUFS_H__ */ -diff -Nurp linux-4.4.3/fs/aufs/dcsub.c linux-4.4.3-aufs/fs/aufs/dcsub.c ---- linux-4.4.3/fs/aufs/dcsub.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/dcsub.c 2016-03-02 17:59:37.362498696 +0200 +diff --git a/fs/aufs/dcsub.c b/fs/aufs/dcsub.c +new file mode 100644 +index 0000000..e72acce +--- /dev/null ++++ b/fs/aufs/dcsub.c @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima @@ -5571,9 +5926,11 @@ diff -Nurp linux-4.4.3/fs/aufs/dcsub.c linux-4.4.3-aufs/fs/aufs/dcsub.c + + return path_is_under(path + 0, path + 1); +} -diff -Nurp linux-4.4.3/fs/aufs/dcsub.h linux-4.4.3-aufs/fs/aufs/dcsub.h ---- linux-4.4.3/fs/aufs/dcsub.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/dcsub.h 2016-03-02 17:59:37.362498696 +0200 +diff --git a/fs/aufs/dcsub.h b/fs/aufs/dcsub.h +new file mode 100644 +index 0000000..5d2cf66 +--- /dev/null ++++ b/fs/aufs/dcsub.h @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima @@ -5698,10 +6055,12 @@ diff -Nurp linux-4.4.3/fs/aufs/dcsub.h linux-4.4.3-aufs/fs/aufs/dcsub.h + +#endif /* __KERNEL__ */ +#endif /* __AUFS_DCSUB_H__ */ -diff -Nurp linux-4.4.3/fs/aufs/debug.c linux-4.4.3-aufs/fs/aufs/debug.c ---- linux-4.4.3/fs/aufs/debug.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/debug.c 2016-03-02 17:59:37.363498703 +0200 -@@ -0,0 +1,425 @@ +diff --git a/fs/aufs/debug.c b/fs/aufs/debug.c +new file mode 100644 +index 0000000..14e69cb +--- /dev/null ++++ b/fs/aufs/debug.c +@@ -0,0 +1,429 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima + */ @@ -5832,25 +6191,24 @@ diff -Nurp linux-4.4.3/fs/aufs/debug.c linux-4.4.3-aufs/fs/aufs/debug.c +void au_dpri_inode(struct inode *inode) +{ + struct au_iinfo *iinfo; ++ struct au_hinode *hi; + aufs_bindex_t bindex; + int err, hn; + + err = do_pri_inode(-1, inode, -1, NULL); -+ if (err || !au_test_aufs(inode->i_sb)) ++ if (err || !au_test_aufs(inode->i_sb) || is_bad_inode(inode)) + return; + + iinfo = au_ii(inode); -+ if (!iinfo) -+ return; -+ dpri("i-1: bstart %d, bend %d, gen %d\n", -+ iinfo->ii_bstart, iinfo->ii_bend, au_iigen(inode, NULL)); -+ if (iinfo->ii_bstart < 0) ++ dpri("i-1: btop %d, bbot %d, gen %d\n", ++ iinfo->ii_btop, iinfo->ii_bbot, au_iigen(inode, NULL)); ++ if (iinfo->ii_btop < 0) + return; + hn = 0; -+ for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend; bindex++) { -+ hn = !!au_hn(iinfo->ii_hinode + bindex); -+ do_pri_inode(bindex, iinfo->ii_hinode[0 + bindex].hi_inode, hn, -+ iinfo->ii_hinode[0 + bindex].hi_whdentry); ++ for (bindex = iinfo->ii_btop; bindex <= iinfo->ii_bbot; bindex++) { ++ hi = au_hinode(iinfo, bindex); ++ hn = !!au_hn(hi); ++ do_pri_inode(bindex, hi->hi_inode, hn, hi->hi_whdentry); + } +} + @@ -5869,6 +6227,8 @@ diff -Nurp linux-4.4.3/fs/aufs/debug.c linux-4.4.3-aufs/fs/aufs/debug.c + struct dentry *wh = NULL; + int hn; + struct au_iinfo *iinfo; ++ struct au_hinode *hi; ++ struct inode *inode; + + if (!dentry || IS_ERR(dentry)) { + dpri("d%d: err %ld\n", bindex, PTR_ERR(dentry)); @@ -5882,16 +6242,18 @@ diff -Nurp linux-4.4.3/fs/aufs/debug.c linux-4.4.3-aufs/fs/aufs/debug.c + au_dcount(dentry), dentry->d_flags, + d_unhashed(dentry) ? "un" : ""); + hn = -1; ++ inode = NULL; + if (bindex >= 0 + && d_is_positive(dentry) -+ && au_test_aufs(dentry->d_sb)) { -+ iinfo = au_ii(d_inode(dentry)); -+ if (iinfo) { -+ hn = !!au_hn(iinfo->ii_hinode + bindex); -+ wh = iinfo->ii_hinode[0 + bindex].hi_whdentry; -+ } ++ && au_test_aufs(dentry->d_sb)) ++ inode = d_inode(dentry); ++ if (inode && !is_bad_inode(inode)) { ++ iinfo = au_ii(inode); ++ hi = au_hinode(iinfo, bindex); ++ hn = !!au_hn(hi); ++ wh = hi->hi_whdentry; + } -+ do_pri_inode(bindex, d_inode(dentry), hn, wh); ++ do_pri_inode(bindex, inode, hn, wh); + return 0; +} + @@ -5909,14 +6271,14 @@ diff -Nurp linux-4.4.3/fs/aufs/debug.c linux-4.4.3-aufs/fs/aufs/debug.c + dinfo = au_di(dentry); + if (!dinfo) + return; -+ dpri("d-1: bstart %d, bend %d, bwh %d, bdiropq %d, gen %d, tmp %d\n", -+ dinfo->di_bstart, dinfo->di_bend, ++ dpri("d-1: btop %d, bbot %d, bwh %d, bdiropq %d, gen %d, tmp %d\n", ++ dinfo->di_btop, dinfo->di_bbot, + dinfo->di_bwh, dinfo->di_bdiropq, au_digen(dentry), + dinfo->di_tmpfile); -+ if (dinfo->di_bstart < 0) ++ if (dinfo->di_btop < 0) + return; + hdp = dinfo->di_hdentry; -+ for (bindex = dinfo->di_bstart; bindex <= dinfo->di_bend; bindex++) ++ for (bindex = dinfo->di_btop; bindex <= dinfo->di_bbot; bindex++) + do_pri_dentry(bindex, hdp[0 + bindex].hd_dentry); +} + @@ -5988,10 +6350,10 @@ diff -Nurp linux-4.4.3/fs/aufs/debug.c linux-4.4.3-aufs/fs/aufs/debug.c + if (!sb || IS_ERR(sb)) + goto out; + -+ dpri("s%d: {perm 0x%x, id %d, cnt %d, wbr %p}, " ++ dpri("s%d: {perm 0x%x, id %d, cnt %lld, wbr %p}, " + "%s, dev 0x%02x%02x, flags 0x%lx, cnt %d, active %d, " + "xino %d\n", -+ bindex, br->br_perm, br->br_id, atomic_read(&br->br_count), ++ bindex, br->br_perm, br->br_id, au_br_count(br), + br->br_wbr, au_sbtype(sb), MAJOR(sb->s_dev), MINOR(sb->s_dev), + sb->s_flags, sb->s_count, + atomic_read(&sb->s_active), !!br->br_xino.xi_file); @@ -6022,9 +6384,9 @@ diff -Nurp linux-4.4.3/fs/aufs/debug.c linux-4.4.3-aufs/fs/aufs/debug.c + + a->mnt.mnt_sb = sb; + a->fake.br_path.mnt = &a->mnt; -+ atomic_set(&a->fake.br_count, 0); -+ smp_mb(); /* atomic_set */ ++ au_br_count_init(&a->fake); + err = do_pri_br(-1, &a->fake); ++ au_br_count_fin(&a->fake); + kfree(a); + dpri("dev 0x%x\n", sb->s_dev); + if (err || !au_test_aufs(sb)) @@ -6033,10 +6395,11 @@ diff -Nurp linux-4.4.3/fs/aufs/debug.c linux-4.4.3-aufs/fs/aufs/debug.c + sbinfo = au_sbi(sb); + if (!sbinfo) + return; -+ dpri("nw %d, gen %u, kobj %d\n", -+ atomic_read(&sbinfo->si_nowait.nw_len), sbinfo->si_generation, ++ dpri("nw %lld, gen %u, kobj %d\n", ++ percpu_counter_sum(&sbinfo->si_nowait.nw_len), ++ sbinfo->si_generation, + atomic_read(&sbinfo->si_kobj.kref.refcount)); -+ for (bindex = 0; bindex <= sbinfo->si_bend; bindex++) ++ for (bindex = 0; bindex <= sbinfo->si_bbot; bindex++) + do_pri_br(bindex, sbinfo->si_branch[0 + bindex]); +} + @@ -6046,21 +6409,21 @@ diff -Nurp linux-4.4.3/fs/aufs/debug.c linux-4.4.3-aufs/fs/aufs/debug.c +{ + struct inode *h_inode, *inode = d_inode(dentry); + struct dentry *h_dentry; -+ aufs_bindex_t bindex, bend, bi; ++ aufs_bindex_t bindex, bbot, bi; + + if (!inode /* || au_di(dentry)->di_lsc == AuLsc_DI_TMP */) + return; + -+ bend = au_dbend(dentry); -+ bi = au_ibend(inode); -+ if (bi < bend) -+ bend = bi; -+ bindex = au_dbstart(dentry); -+ bi = au_ibstart(inode); ++ bbot = au_dbbot(dentry); ++ bi = au_ibbot(inode); ++ if (bi < bbot) ++ bbot = bi; ++ bindex = au_dbtop(dentry); ++ bi = au_ibtop(inode); + if (bi > bindex) + bindex = bi; + -+ for (; bindex <= bend; bindex++) { ++ for (; bindex <= bbot; bindex++) { + h_dentry = au_h_dptr(dentry, bindex); + if (!h_dentry) + continue; @@ -6127,9 +6490,11 @@ diff -Nurp linux-4.4.3/fs/aufs/debug.c linux-4.4.3-aufs/fs/aufs/debug.c + + return 0; +} -diff -Nurp linux-4.4.3/fs/aufs/debug.h linux-4.4.3-aufs/fs/aufs/debug.h ---- linux-4.4.3/fs/aufs/debug.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/debug.h 2016-03-02 17:59:37.363498703 +0200 +diff --git a/fs/aufs/debug.h b/fs/aufs/debug.h +new file mode 100644 +index 0000000..0567f31 +--- /dev/null ++++ b/fs/aufs/debug.h @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima @@ -6343,9 +6708,11 @@ diff -Nurp linux-4.4.3/fs/aufs/debug.h linux-4.4.3-aufs/fs/aufs/debug.h + +#endif /* __KERNEL__ */ +#endif /* __AUFS_DEBUG_H__ */ -diff -Nurp linux-4.4.3/fs/aufs/dentry.c linux-4.4.3-aufs/fs/aufs/dentry.c ---- linux-4.4.3/fs/aufs/dentry.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/dentry.c 2016-03-02 17:59:37.363498703 +0200 +diff --git a/fs/aufs/dentry.c b/fs/aufs/dentry.c +new file mode 100644 +index 0000000..ce2a136 +--- /dev/null ++++ b/fs/aufs/dentry.c @@ -0,0 +1,1123 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima @@ -6401,7 +6768,7 @@ diff -Nurp linux-4.4.3/fs/aufs/dentry.c linux-4.4.3-aufs/fs/aufs/dentry.c + goto out; + + /* We found a whiteout */ -+ /* au_set_dbend(dentry, bindex); */ ++ /* au_set_dbbot(dentry, bindex); */ + au_set_dbwh(dentry, bindex); + if (!allow_neg) + return NULL; /* success */ @@ -6426,10 +6793,10 @@ diff -Nurp linux-4.4.3/fs/aufs/dentry.c linux-4.4.3-aufs/fs/aufs/dentry.c + || (args->type && args->type != (h_inode->i_mode & S_IFMT))) + goto out_neg; + -+ if (au_dbend(dentry) <= bindex) -+ au_set_dbend(dentry, bindex); -+ if (au_dbstart(dentry) < 0 || bindex < au_dbstart(dentry)) -+ au_set_dbstart(dentry, bindex); ++ if (au_dbbot(dentry) <= bindex) ++ au_set_dbbot(dentry, bindex); ++ if (au_dbtop(dentry) < 0 || bindex < au_dbtop(dentry)) ++ au_set_dbtop(dentry, bindex); + au_set_h_dptr(dentry, bindex, h_dentry); + + if (!d_is_dir(h_dentry) @@ -6437,9 +6804,9 @@ diff -Nurp linux-4.4.3/fs/aufs/dentry.c linux-4.4.3-aufs/fs/aufs/dentry.c + || (d_really_is_positive(dentry) && !d_is_dir(dentry))) + goto out; /* success */ + -+ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); ++ inode_lock_nested(h_inode, AuLsc_I_CHILD); + opq = au_diropq_test(h_dentry); -+ mutex_unlock(&h_inode->i_mutex); ++ inode_unlock(h_inode); + if (opq > 0) + au_set_dbdiropq(dentry, bindex); + else if (unlikely(opq < 0)) { @@ -6468,7 +6835,7 @@ diff -Nurp linux-4.4.3/fs/aufs/dentry.c linux-4.4.3-aufs/fs/aufs/dentry.c + * otherwise an error. + * can be called at unlinking with @type is zero. + */ -+int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type) ++int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t btop, mode_t type) +{ + int npositive, err; + aufs_bindex_t bindex, btail, bdiropq; @@ -6499,7 +6866,7 @@ diff -Nurp linux-4.4.3/fs/aufs/dentry.c linux-4.4.3-aufs/fs/aufs/dentry.c + npositive = 0; + parent = dget_parent(dentry); + btail = au_dbtaildir(parent); -+ for (bindex = bstart; bindex <= btail; bindex++) { ++ for (bindex = btop; bindex <= btail; bindex++) { + struct dentry *h_parent, *h_dentry; + struct inode *h_inode, *h_dir; + @@ -6516,10 +6883,10 @@ diff -Nurp linux-4.4.3/fs/aufs/dentry.c linux-4.4.3-aufs/fs/aufs/dentry.c + continue; + + h_dir = d_inode(h_parent); -+ mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT); ++ inode_lock_nested(h_dir, AuLsc_I_PARENT); + h_dentry = au_do_lookup(h_parent, dentry, bindex, &whname, + &args); -+ mutex_unlock(&h_dir->i_mutex); ++ inode_unlock(h_dir); + err = PTR_ERR(h_dentry); + if (IS_ERR(h_dentry)) + goto out_parent; @@ -6550,11 +6917,11 @@ diff -Nurp linux-4.4.3/fs/aufs/dentry.c linux-4.4.3-aufs/fs/aufs/dentry.c + + if (npositive) { + AuLabel(positive); -+ au_update_dbstart(dentry); ++ au_update_dbtop(dentry); + } + err = npositive; + if (unlikely(!au_opt_test(au_mntflags(sb), UDBA_NONE) -+ && au_dbstart(dentry) < 0)) { ++ && au_dbtop(dentry) < 0)) { + err = -EIO; + AuIOErr("both of real entry and whiteout found, %pd, err %d\n", + dentry, err); @@ -6616,10 +6983,10 @@ diff -Nurp linux-4.4.3/fs/aufs/dentry.c linux-4.4.3-aufs/fs/aufs/dentry.c + } + + err = 0; -+ if (bindex < au_dbstart(dentry)) -+ au_set_dbstart(dentry, bindex); -+ if (au_dbend(dentry) < bindex) -+ au_set_dbend(dentry, bindex); ++ if (bindex < au_dbtop(dentry)) ++ au_set_dbtop(dentry, bindex); ++ if (au_dbbot(dentry) < bindex) ++ au_set_dbbot(dentry, bindex); + au_set_h_dptr(dentry, bindex, h_dentry); + +out: @@ -6731,7 +7098,7 @@ diff -Nurp linux-4.4.3/fs/aufs/dentry.c linux-4.4.3-aufs/fs/aufs/dentry.c +static int au_do_refresh_hdentry(struct dentry *dentry, struct dentry *parent) +{ + int err; -+ aufs_bindex_t new_bindex, bindex, bend, bwh, bdiropq; ++ aufs_bindex_t new_bindex, bindex, bbot, bwh, bdiropq; + struct au_hdentry tmp, *p, *q; + struct au_dinfo *dinfo; + struct super_block *sb; @@ -6740,11 +7107,11 @@ diff -Nurp linux-4.4.3/fs/aufs/dentry.c linux-4.4.3-aufs/fs/aufs/dentry.c + + sb = dentry->d_sb; + dinfo = au_di(dentry); -+ bend = dinfo->di_bend; ++ bbot = dinfo->di_bbot; + bwh = dinfo->di_bwh; + bdiropq = dinfo->di_bdiropq; -+ p = dinfo->di_hdentry + dinfo->di_bstart; -+ for (bindex = dinfo->di_bstart; bindex <= bend; bindex++, p++) { ++ p = dinfo->di_hdentry + dinfo->di_btop; ++ for (bindex = dinfo->di_btop; bindex <= bbot; bindex++, p++) { + if (!p->hd_dentry) + continue; + @@ -6774,31 +7141,31 @@ diff -Nurp linux-4.4.3/fs/aufs/dentry.c linux-4.4.3-aufs/fs/aufs/dentry.c + } + + dinfo->di_bwh = -1; -+ if (bwh >= 0 && bwh <= au_sbend(sb) && au_sbr_whable(sb, bwh)) ++ if (bwh >= 0 && bwh <= au_sbbot(sb) && au_sbr_whable(sb, bwh)) + dinfo->di_bwh = bwh; + + dinfo->di_bdiropq = -1; + if (bdiropq >= 0 -+ && bdiropq <= au_sbend(sb) ++ && bdiropq <= au_sbbot(sb) + && au_sbr_whable(sb, bdiropq)) + dinfo->di_bdiropq = bdiropq; + + err = -EIO; -+ dinfo->di_bstart = -1; -+ dinfo->di_bend = -1; -+ bend = au_dbend(parent); ++ dinfo->di_btop = -1; ++ dinfo->di_bbot = -1; ++ bbot = au_dbbot(parent); + p = dinfo->di_hdentry; -+ for (bindex = 0; bindex <= bend; bindex++, p++) ++ for (bindex = 0; bindex <= bbot; bindex++, p++) + if (p->hd_dentry) { -+ dinfo->di_bstart = bindex; ++ dinfo->di_btop = bindex; + break; + } + -+ if (dinfo->di_bstart >= 0) { -+ p = dinfo->di_hdentry + bend; -+ for (bindex = bend; bindex >= 0; bindex--, p--) ++ if (dinfo->di_btop >= 0) { ++ p = dinfo->di_hdentry + bbot; ++ for (bindex = bbot; bindex >= 0; bindex--, p--) + if (p->hd_dentry) { -+ dinfo->di_bend = bindex; ++ dinfo->di_bbot = bindex; + err = 0; + break; + } @@ -6898,7 +7265,7 @@ diff -Nurp linux-4.4.3/fs/aufs/dentry.c linux-4.4.3-aufs/fs/aufs/dentry.c + struct au_dinfo *tmp) +{ + int err; -+ aufs_bindex_t bindex, bend; ++ aufs_bindex_t bindex, bbot; + struct { + struct dentry *dentry; + struct inode *inode; @@ -6911,16 +7278,16 @@ diff -Nurp linux-4.4.3/fs/aufs/dentry.c linux-4.4.3-aufs/fs/aufs/dentry.c + struct dentry *h_dentry; + + err = 0; -+ AuDebugOn(dinfo->di_bstart < 0); ++ AuDebugOn(dinfo->di_btop < 0); + orig_h.mode = 0; -+ orig_h.dentry = dinfo->di_hdentry[dinfo->di_bstart].hd_dentry; ++ orig_h.dentry = dinfo->di_hdentry[dinfo->di_btop].hd_dentry; + orig_h.inode = NULL; + if (d_is_positive(orig_h.dentry)) { + orig_h.inode = d_inode(orig_h.dentry); + orig_h.mode = orig_h.inode->i_mode & S_IFMT; + } -+ if (tmp->di_bstart >= 0) { -+ tmp_h.dentry = tmp->di_hdentry[tmp->di_bstart].hd_dentry; ++ if (tmp->di_btop >= 0) { ++ tmp_h.dentry = tmp->di_hdentry[tmp->di_btop].hd_dentry; + if (d_is_positive(tmp_h.dentry)) { + tmp_h.inode = d_inode(tmp_h.dentry); + tmp_h.mode = tmp_h.inode->i_mode & S_IFMT; @@ -6937,20 +7304,20 @@ diff -Nurp linux-4.4.3/fs/aufs/dentry.c linux-4.4.3-aufs/fs/aufs/dentry.c + goto out; + } + AuDebugOn(inode); -+ AuDebugOn(dinfo->di_bstart != dinfo->di_bend); ++ AuDebugOn(dinfo->di_btop != dinfo->di_bbot); + AuDebugOn(dinfo->di_bdiropq != -1); + + if (!tmp_h.inode) { + AuDbg("negative --> negative\n"); + /* should have only one negative lower */ -+ if (tmp->di_bstart >= 0 -+ && tmp->di_bstart < dinfo->di_bstart) { -+ AuDebugOn(tmp->di_bstart != tmp->di_bend); -+ AuDebugOn(dinfo->di_bstart != dinfo->di_bend); -+ au_set_h_dptr(dentry, dinfo->di_bstart, NULL); ++ if (tmp->di_btop >= 0 ++ && tmp->di_btop < dinfo->di_btop) { ++ AuDebugOn(tmp->di_btop != tmp->di_bbot); ++ AuDebugOn(dinfo->di_btop != dinfo->di_bbot); ++ au_set_h_dptr(dentry, dinfo->di_btop, NULL); + au_di_cp(dinfo, tmp); -+ hd = tmp->di_hdentry + tmp->di_bstart; -+ au_set_h_dptr(dentry, tmp->di_bstart, ++ hd = tmp->di_hdentry + tmp->di_btop; ++ au_set_h_dptr(dentry, tmp->di_btop, + dget(hd->hd_dentry)); + } + au_dbg_verify_dinode(dentry); @@ -6975,7 +7342,7 @@ diff -Nurp linux-4.4.3/fs/aufs/dentry.c linux-4.4.3-aufs/fs/aufs/dentry.c + AuDbg("positive --> negative\n"); + /* or bypassing aufs */ + au_hide(dentry); -+ if (tmp->di_bwh >= 0 && tmp->di_bwh <= dinfo->di_bstart) ++ if (tmp->di_bwh >= 0 && tmp->di_bwh <= dinfo->di_btop) + dinfo->di_bwh = tmp->di_bwh; + if (inode) + err = au_refresh_hinode_self(inode); @@ -6983,7 +7350,7 @@ diff -Nurp linux-4.4.3/fs/aufs/dentry.c linux-4.4.3-aufs/fs/aufs/dentry.c + } else if (orig_h.mode == tmp_h.mode) { + AuDbg("positive --> positive, same type\n"); + if (!S_ISDIR(orig_h.mode) -+ && dinfo->di_bstart > tmp->di_bstart) { ++ && dinfo->di_btop > tmp->di_btop) { + /* + * similar to the behaviour of removing and + * creating. @@ -6994,15 +7361,15 @@ diff -Nurp linux-4.4.3/fs/aufs/dentry.c linux-4.4.3-aufs/fs/aufs/dentry.c + au_dbg_verify_dinode(dentry); + } else { + /* fill empty slots */ -+ if (dinfo->di_bstart > tmp->di_bstart) -+ dinfo->di_bstart = tmp->di_bstart; -+ if (dinfo->di_bend < tmp->di_bend) -+ dinfo->di_bend = tmp->di_bend; ++ if (dinfo->di_btop > tmp->di_btop) ++ dinfo->di_btop = tmp->di_btop; ++ if (dinfo->di_bbot < tmp->di_bbot) ++ dinfo->di_bbot = tmp->di_bbot; + dinfo->di_bwh = tmp->di_bwh; + dinfo->di_bdiropq = tmp->di_bdiropq; + hd = tmp->di_hdentry; -+ bend = dinfo->di_bend; -+ for (bindex = tmp->di_bstart; bindex <= bend; ++ bbot = dinfo->di_bbot; ++ for (bindex = tmp->di_btop; bindex <= bbot; + bindex++) { + if (au_h_dptr(dentry, bindex)) + continue; @@ -7075,7 +7442,7 @@ diff -Nurp linux-4.4.3/fs/aufs/dentry.c linux-4.4.3-aufs/fs/aufs/dentry.c + goto out; + + dinfo = au_di(dentry); -+ err = au_di_realloc(dinfo, au_sbend(sb) + 1); ++ err = au_di_realloc(dinfo, au_sbbot(sb) + 1); + if (unlikely(err)) + goto out; + ebrange = au_dbrange_test(dentry); @@ -7083,7 +7450,7 @@ diff -Nurp linux-4.4.3/fs/aufs/dentry.c linux-4.4.3-aufs/fs/aufs/dentry.c + ebrange = au_do_refresh_hdentry(dentry, parent); + + if (d_unhashed(dentry) || ebrange /* || dinfo->di_tmpfile */) { -+ AuDebugOn(au_dbstart(dentry) < 0 && au_dbend(dentry) >= 0); ++ AuDebugOn(au_dbtop(dentry) < 0 && au_dbbot(dentry) >= 0); + if (d_really_is_positive(dentry)) { + inode = d_inode(dentry); + err = au_refresh_hinode_self(inode); @@ -7106,7 +7473,7 @@ diff -Nurp linux-4.4.3/fs/aufs/dentry.c linux-4.4.3-aufs/fs/aufs/dentry.c + * if current working dir is removed, it returns an error. + * but the dentry is legal. + */ -+ err = au_lkup_dentry(dentry, /*bstart*/0, /*type*/0); ++ err = au_lkup_dentry(dentry, /*btop*/0, /*type*/0); + AuDbgDentry(dentry); + au_di_swap(tmp, dinfo); + if (err == -ENOENT) @@ -7169,7 +7536,7 @@ diff -Nurp linux-4.4.3/fs/aufs/dentry.c linux-4.4.3-aufs/fs/aufs/dentry.c +{ + int err; + umode_t mode, h_mode; -+ aufs_bindex_t bindex, btail, bstart, ibs, ibe; ++ aufs_bindex_t bindex, btail, btop, ibs, ibe; + unsigned char plus, unhashed, is_root, h_plus, h_nfs, tmpfile; + struct inode *h_inode, *h_cached_inode; + struct dentry *h_dentry; @@ -7195,15 +7562,15 @@ diff -Nurp linux-4.4.3/fs/aufs/dentry.c linux-4.4.3-aufs/fs/aufs/dentry.c + if (do_udba && inode) { + mode = (inode->i_mode & S_IFMT); + plus = (inode->i_nlink > 0); -+ ibs = au_ibstart(inode); -+ ibe = au_ibend(inode); ++ ibs = au_ibtop(inode); ++ ibe = au_ibbot(inode); + } + -+ bstart = au_dbstart(dentry); -+ btail = bstart; ++ btop = au_dbtop(dentry); ++ btail = btop; + if (inode && S_ISDIR(inode->i_mode)) + btail = au_dbtaildir(dentry); -+ for (bindex = bstart; bindex <= btail; bindex++) { ++ for (bindex = btop; bindex <= btail; bindex++) { + h_dentry = au_h_dptr(dentry, bindex); + if (!h_dentry) + continue; @@ -7418,11 +7785,11 @@ diff -Nurp linux-4.4.3/fs/aufs/dentry.c linux-4.4.3-aufs/fs/aufs/dentry.c + + do_udba = !au_opt_test(au_mntflags(sb), UDBA_NONE); + if (do_udba && inode) { -+ aufs_bindex_t bstart = au_ibstart(inode); ++ aufs_bindex_t btop = au_ibtop(inode); + struct inode *h_inode; + -+ if (bstart >= 0) { -+ h_inode = au_h_iptr(inode, bstart); ++ if (btop >= 0) { ++ h_inode = au_h_iptr(inode, btop); + if (h_inode && au_test_higen(inode, h_inode)) { + AuTraceErr(err); + goto out_inval; @@ -7431,7 +7798,7 @@ diff -Nurp linux-4.4.3/fs/aufs/dentry.c linux-4.4.3-aufs/fs/aufs/dentry.c + } + + err = h_d_revalidate(dentry, inode, flags, do_udba); -+ if (unlikely(!err && do_udba && au_dbstart(dentry) < 0)) { ++ if (unlikely(!err && do_udba && au_dbtop(dentry) < 0)) { + err = -EIO; + AuDbg("both of real entry and whiteout found, %p, err %d\n", + dentry, err); @@ -7470,9 +7837,11 @@ diff -Nurp linux-4.4.3/fs/aufs/dentry.c linux-4.4.3-aufs/fs/aufs/dentry.c +const struct dentry_operations aufs_dop_noreval = { + .d_release = aufs_d_release +}; -diff -Nurp linux-4.4.3/fs/aufs/dentry.h linux-4.4.3-aufs/fs/aufs/dentry.h ---- linux-4.4.3/fs/aufs/dentry.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/dentry.h 2016-03-02 17:59:37.363498703 +0200 +diff --git a/fs/aufs/dentry.h b/fs/aufs/dentry.h +new file mode 100644 +index 0000000..3e48b5c +--- /dev/null ++++ b/fs/aufs/dentry.h @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima @@ -7499,7 +7868,7 @@ diff -Nurp linux-4.4.3/fs/aufs/dentry.h linux-4.4.3-aufs/fs/aufs/dentry.h + atomic_t di_generation; + + struct au_rwsem di_rwsem; -+ aufs_bindex_t di_bstart, di_bend, di_bwh, di_bdiropq; ++ aufs_bindex_t di_btop, di_bbot, di_bwh, di_bdiropq; + unsigned char di_tmpfile; /* to allow the different name */ + struct au_hdentry *di_hdentry; +} ____cacheline_aligned_in_smp; @@ -7513,7 +7882,7 @@ diff -Nurp linux-4.4.3/fs/aufs/dentry.h linux-4.4.3-aufs/fs/aufs/dentry.h +int au_h_verify(struct dentry *h_dentry, unsigned int udba, struct inode *h_dir, + struct dentry *h_parent, struct au_branch *br); + -+int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type); ++int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t btop, mode_t type); +int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex, int wh); +int au_refresh_dentry(struct dentry *dentry, struct dentry *parent); +int au_reval_dpath(struct dentry *dentry, unsigned int sigen); @@ -7549,8 +7918,8 @@ diff -Nurp linux-4.4.3/fs/aufs/dentry.h linux-4.4.3-aufs/fs/aufs/dentry.h +int au_dbrange_test(struct dentry *dentry); +void au_update_digen(struct dentry *dentry); +void au_update_dbrange(struct dentry *dentry, int do_put_zero); -+void au_update_dbstart(struct dentry *dentry); -+void au_update_dbend(struct dentry *dentry); ++void au_update_dbtop(struct dentry *dentry); ++void au_update_dbbot(struct dentry *dentry); +int au_find_dbindex(struct dentry *dentry, struct dentry *h_dentry); + +/* ---------------------------------------------------------------------- */ @@ -7627,16 +7996,16 @@ diff -Nurp linux-4.4.3/fs/aufs/dentry.h linux-4.4.3-aufs/fs/aufs/dentry.h + dput(hd->hd_dentry); +} + -+static inline aufs_bindex_t au_dbstart(struct dentry *dentry) ++static inline aufs_bindex_t au_dbtop(struct dentry *dentry) +{ + DiMustAnyLock(dentry); -+ return au_di(dentry)->di_bstart; ++ return au_di(dentry)->di_btop; +} + -+static inline aufs_bindex_t au_dbend(struct dentry *dentry) ++static inline aufs_bindex_t au_dbbot(struct dentry *dentry) +{ + DiMustAnyLock(dentry); -+ return au_di(dentry)->di_bend; ++ return au_di(dentry)->di_bbot; +} + +static inline aufs_bindex_t au_dbwh(struct dentry *dentry) @@ -7652,22 +8021,22 @@ diff -Nurp linux-4.4.3/fs/aufs/dentry.h linux-4.4.3-aufs/fs/aufs/dentry.h +} + +/* todo: hard/soft set? */ -+static inline void au_set_dbstart(struct dentry *dentry, aufs_bindex_t bindex) ++static inline void au_set_dbtop(struct dentry *dentry, aufs_bindex_t bindex) +{ + DiMustWriteLock(dentry); -+ au_di(dentry)->di_bstart = bindex; ++ au_di(dentry)->di_btop = bindex; +} + -+static inline void au_set_dbend(struct dentry *dentry, aufs_bindex_t bindex) ++static inline void au_set_dbbot(struct dentry *dentry, aufs_bindex_t bindex) +{ + DiMustWriteLock(dentry); -+ au_di(dentry)->di_bend = bindex; ++ au_di(dentry)->di_bbot = bindex; +} + +static inline void au_set_dbwh(struct dentry *dentry, aufs_bindex_t bindex) +{ + DiMustWriteLock(dentry); -+ /* dbwh can be outside of bstart - bend range */ ++ /* dbwh can be outside of btop - bbot range */ + au_di(dentry)->di_bwh = bindex; +} + @@ -7695,10 +8064,12 @@ diff -Nurp linux-4.4.3/fs/aufs/dentry.h linux-4.4.3-aufs/fs/aufs/dentry.h + +#endif /* __KERNEL__ */ +#endif /* __AUFS_DENTRY_H__ */ -diff -Nurp linux-4.4.3/fs/aufs/dinfo.c linux-4.4.3-aufs/fs/aufs/dinfo.c ---- linux-4.4.3/fs/aufs/dinfo.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/dinfo.c 2016-03-02 17:59:37.364498710 +0200 -@@ -0,0 +1,537 @@ +diff --git a/fs/aufs/dinfo.c b/fs/aufs/dinfo.c +new file mode 100644 +index 0000000..b2eb8c2 +--- /dev/null ++++ b/fs/aufs/dinfo.c +@@ -0,0 +1,535 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima + */ @@ -7712,10 +8083,8 @@ diff -Nurp linux-4.4.3/fs/aufs/dinfo.c linux-4.4.3-aufs/fs/aufs/dinfo.c +void au_di_init_once(void *_dinfo) +{ + struct au_dinfo *dinfo = _dinfo; -+ static struct lock_class_key aufs_di; + + au_rw_init(&dinfo->di_rwsem); -+ au_rw_class(&dinfo->di_rwsem, &aufs_di); +} + +struct au_dinfo *au_di_alloc(struct super_block *sb, unsigned int lsc) @@ -7727,14 +8096,14 @@ diff -Nurp linux-4.4.3/fs/aufs/dinfo.c linux-4.4.3-aufs/fs/aufs/dinfo.c + if (unlikely(!dinfo)) + goto out; + -+ nbr = au_sbend(sb) + 1; ++ nbr = au_sbbot(sb) + 1; + if (nbr <= 0) + nbr = 1; + dinfo->di_hdentry = kcalloc(nbr, sizeof(*dinfo->di_hdentry), GFP_NOFS); + if (dinfo->di_hdentry) { + au_rw_write_lock_nested(&dinfo->di_rwsem, lsc); -+ dinfo->di_bstart = -1; -+ dinfo->di_bend = -1; ++ dinfo->di_btop = -1; ++ dinfo->di_bbot = -1; + dinfo->di_bwh = -1; + dinfo->di_bdiropq = -1; + dinfo->di_tmpfile = 0; @@ -7753,14 +8122,14 @@ diff -Nurp linux-4.4.3/fs/aufs/dinfo.c linux-4.4.3-aufs/fs/aufs/dinfo.c +void au_di_free(struct au_dinfo *dinfo) +{ + struct au_hdentry *p; -+ aufs_bindex_t bend, bindex; ++ aufs_bindex_t bbot, bindex; + + /* dentry may not be revalidated */ -+ bindex = dinfo->di_bstart; ++ bindex = dinfo->di_btop; + if (bindex >= 0) { -+ bend = dinfo->di_bend; ++ bbot = dinfo->di_bbot; + p = dinfo->di_hdentry + bindex; -+ while (bindex++ <= bend) ++ while (bindex++ <= bbot) + au_hdput(p++); + } + kfree(dinfo->di_hdentry); @@ -7783,8 +8152,8 @@ diff -Nurp linux-4.4.3/fs/aufs/dinfo.c linux-4.4.3-aufs/fs/aufs/dinfo.c + } while (0) + + DiSwap(p, hdentry); -+ DiSwap(bi, bstart); -+ DiSwap(bi, bend); ++ DiSwap(bi, btop); ++ DiSwap(bi, bbot); + DiSwap(bi, bwh); + DiSwap(bi, bdiropq); + /* smp_mb(); */ @@ -7797,8 +8166,8 @@ diff -Nurp linux-4.4.3/fs/aufs/dinfo.c linux-4.4.3-aufs/fs/aufs/dinfo.c + AuRwMustWriteLock(&dst->di_rwsem); + AuRwMustWriteLock(&src->di_rwsem); + -+ dst->di_bstart = src->di_bstart; -+ dst->di_bend = src->di_bend; ++ dst->di_btop = src->di_btop; ++ dst->di_bbot = src->di_bbot; + dst->di_bwh = src->di_bwh; + dst->di_bdiropq = src->di_bdiropq; + /* smp_mb(); */ @@ -7840,7 +8209,7 @@ diff -Nurp linux-4.4.3/fs/aufs/dinfo.c linux-4.4.3-aufs/fs/aufs/dinfo.c + AuRwMustWriteLock(&dinfo->di_rwsem); + + err = -ENOMEM; -+ sz = sizeof(*hdp) * (dinfo->di_bend + 1); ++ sz = sizeof(*hdp) * (dinfo->di_bbot + 1); + if (!sz) + sz = sizeof(*hdp); + hdp = au_kzrealloc(dinfo->di_hdentry, sz, sizeof(*hdp) * nbr, GFP_NOFS); @@ -8008,7 +8377,7 @@ diff -Nurp linux-4.4.3/fs/aufs/dinfo.c linux-4.4.3-aufs/fs/aufs/dinfo.c + + DiMustAnyLock(dentry); + -+ if (au_dbstart(dentry) < 0 || bindex < au_dbstart(dentry)) ++ if (au_dbtop(dentry) < 0 || bindex < au_dbtop(dentry)) + return NULL; + AuDebugOn(bindex < 0); + d = au_di(dentry)->di_hdentry[0 + bindex].hd_dentry; @@ -8029,8 +8398,8 @@ diff -Nurp linux-4.4.3/fs/aufs/dinfo.c linux-4.4.3-aufs/fs/aufs/dinfo.c + AuDebugOn(d_really_is_negative(dentry)); + + h_dentry = NULL; -+ if (au_dbstart(dentry) <= bindex -+ && bindex <= au_dbend(dentry)) ++ if (au_dbtop(dentry) <= bindex ++ && bindex <= au_dbbot(dentry)) + h_dentry = au_h_dptr(dentry, bindex); + if (h_dentry && !au_d_linkable(h_dentry)) { + dget(h_dentry); @@ -8038,8 +8407,8 @@ diff -Nurp linux-4.4.3/fs/aufs/dinfo.c linux-4.4.3-aufs/fs/aufs/dinfo.c + } + + inode = d_inode(dentry); -+ AuDebugOn(bindex < au_ibstart(inode)); -+ AuDebugOn(au_ibend(inode) < bindex); ++ AuDebugOn(bindex < au_ibtop(inode)); ++ AuDebugOn(au_ibbot(inode) < bindex); + h_inode = au_h_iptr(inode, bindex); + h_dentry = d_find_alias(h_inode); + if (h_dentry) { @@ -8069,30 +8438,30 @@ diff -Nurp linux-4.4.3/fs/aufs/dinfo.c linux-4.4.3-aufs/fs/aufs/dinfo.c + +aufs_bindex_t au_dbtail(struct dentry *dentry) +{ -+ aufs_bindex_t bend, bwh; ++ aufs_bindex_t bbot, bwh; + -+ bend = au_dbend(dentry); -+ if (0 <= bend) { ++ bbot = au_dbbot(dentry); ++ if (0 <= bbot) { + bwh = au_dbwh(dentry); + if (!bwh) + return bwh; -+ if (0 < bwh && bwh < bend) ++ if (0 < bwh && bwh < bbot) + return bwh - 1; + } -+ return bend; ++ return bbot; +} + +aufs_bindex_t au_dbtaildir(struct dentry *dentry) +{ -+ aufs_bindex_t bend, bopq; ++ aufs_bindex_t bbot, bopq; + -+ bend = au_dbtail(dentry); -+ if (0 <= bend) { ++ bbot = au_dbtail(dentry); ++ if (0 <= bbot) { + bopq = au_dbdiropq(dentry); -+ if (0 <= bopq && bopq < bend) -+ bend = bopq; ++ if (0 <= bopq && bopq < bbot) ++ bbot = bopq; + } -+ return bend; ++ return bbot; +} + +/* ---------------------------------------------------------------------- */ @@ -8116,16 +8485,16 @@ diff -Nurp linux-4.4.3/fs/aufs/dinfo.c linux-4.4.3-aufs/fs/aufs/dinfo.c +int au_dbrange_test(struct dentry *dentry) +{ + int err; -+ aufs_bindex_t bstart, bend; ++ aufs_bindex_t btop, bbot; + + err = 0; -+ bstart = au_dbstart(dentry); -+ bend = au_dbend(dentry); -+ if (bstart >= 0) -+ AuDebugOn(bend < 0 && bstart > bend); ++ btop = au_dbtop(dentry); ++ bbot = au_dbbot(dentry); ++ if (btop >= 0) ++ AuDebugOn(bbot < 0 && btop > bbot); + else { + err = -EIO; -+ AuDebugOn(bend >= 0); ++ AuDebugOn(bbot >= 0); + } + + return err; @@ -8158,68 +8527,68 @@ diff -Nurp linux-4.4.3/fs/aufs/dinfo.c linux-4.4.3-aufs/fs/aufs/dinfo.c + DiMustWriteLock(dentry); + + dinfo = au_di(dentry); -+ if (!dinfo || dinfo->di_bstart < 0) ++ if (!dinfo || dinfo->di_btop < 0) + return; + + hdp = dinfo->di_hdentry; + if (do_put_zero) { -+ aufs_bindex_t bindex, bend; ++ aufs_bindex_t bindex, bbot; + -+ bend = dinfo->di_bend; -+ for (bindex = dinfo->di_bstart; bindex <= bend; bindex++) { ++ bbot = dinfo->di_bbot; ++ for (bindex = dinfo->di_btop; bindex <= bbot; bindex++) { + h_d = hdp[0 + bindex].hd_dentry; + if (h_d && d_is_negative(h_d)) + au_set_h_dptr(dentry, bindex, NULL); + } + } + -+ dinfo->di_bstart = -1; -+ while (++dinfo->di_bstart <= dinfo->di_bend) -+ if (hdp[0 + dinfo->di_bstart].hd_dentry) ++ dinfo->di_btop = -1; ++ while (++dinfo->di_btop <= dinfo->di_bbot) ++ if (hdp[0 + dinfo->di_btop].hd_dentry) + break; -+ if (dinfo->di_bstart > dinfo->di_bend) { -+ dinfo->di_bstart = -1; -+ dinfo->di_bend = -1; ++ if (dinfo->di_btop > dinfo->di_bbot) { ++ dinfo->di_btop = -1; ++ dinfo->di_bbot = -1; + return; + } + -+ dinfo->di_bend++; -+ while (0 <= --dinfo->di_bend) -+ if (hdp[0 + dinfo->di_bend].hd_dentry) ++ dinfo->di_bbot++; ++ while (0 <= --dinfo->di_bbot) ++ if (hdp[0 + dinfo->di_bbot].hd_dentry) + break; -+ AuDebugOn(dinfo->di_bstart > dinfo->di_bend || dinfo->di_bend < 0); ++ AuDebugOn(dinfo->di_btop > dinfo->di_bbot || dinfo->di_bbot < 0); +} + -+void au_update_dbstart(struct dentry *dentry) ++void au_update_dbtop(struct dentry *dentry) +{ -+ aufs_bindex_t bindex, bend; ++ aufs_bindex_t bindex, bbot; + struct dentry *h_dentry; + -+ bend = au_dbend(dentry); -+ for (bindex = au_dbstart(dentry); bindex <= bend; bindex++) { ++ bbot = au_dbbot(dentry); ++ for (bindex = au_dbtop(dentry); bindex <= bbot; bindex++) { + h_dentry = au_h_dptr(dentry, bindex); + if (!h_dentry) + continue; + if (d_is_positive(h_dentry)) { -+ au_set_dbstart(dentry, bindex); ++ au_set_dbtop(dentry, bindex); + return; + } + au_set_h_dptr(dentry, bindex, NULL); + } +} + -+void au_update_dbend(struct dentry *dentry) ++void au_update_dbbot(struct dentry *dentry) +{ -+ aufs_bindex_t bindex, bstart; ++ aufs_bindex_t bindex, btop; + struct dentry *h_dentry; + -+ bstart = au_dbstart(dentry); -+ for (bindex = au_dbend(dentry); bindex >= bstart; bindex--) { ++ btop = au_dbtop(dentry); ++ for (bindex = au_dbbot(dentry); bindex >= btop; bindex--) { + h_dentry = au_h_dptr(dentry, bindex); + if (!h_dentry) + continue; + if (d_is_positive(h_dentry)) { -+ au_set_dbend(dentry, bindex); ++ au_set_dbbot(dentry, bindex); + return; + } + au_set_h_dptr(dentry, bindex, NULL); @@ -8228,18 +8597,20 @@ diff -Nurp linux-4.4.3/fs/aufs/dinfo.c linux-4.4.3-aufs/fs/aufs/dinfo.c + +int au_find_dbindex(struct dentry *dentry, struct dentry *h_dentry) +{ -+ aufs_bindex_t bindex, bend; ++ aufs_bindex_t bindex, bbot; + -+ bend = au_dbend(dentry); -+ for (bindex = au_dbstart(dentry); bindex <= bend; bindex++) ++ bbot = au_dbbot(dentry); ++ for (bindex = au_dbtop(dentry); bindex <= bbot; bindex++) + if (au_h_dptr(dentry, bindex) == h_dentry) + return bindex; + return -1; +} -diff -Nurp linux-4.4.3/fs/aufs/dir.c linux-4.4.3-aufs/fs/aufs/dir.c ---- linux-4.4.3/fs/aufs/dir.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/dir.c 2016-03-02 17:59:37.364498710 +0200 -@@ -0,0 +1,745 @@ +diff --git a/fs/aufs/dir.c b/fs/aufs/dir.c +new file mode 100644 +index 0000000..4dc666e +--- /dev/null ++++ b/fs/aufs/dir.c +@@ -0,0 +1,743 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima + */ @@ -8284,7 +8655,7 @@ diff -Nurp linux-4.4.3/fs/aufs/dir.c linux-4.4.3-aufs/fs/aufs/dir.c +loff_t au_dir_size(struct file *file, struct dentry *dentry) +{ + loff_t sz; -+ aufs_bindex_t bindex, bend; ++ aufs_bindex_t bindex, bbot; + struct file *h_file; + struct dentry *h_dentry; + @@ -8292,9 +8663,9 @@ diff -Nurp linux-4.4.3/fs/aufs/dir.c linux-4.4.3-aufs/fs/aufs/dir.c + if (file) { + AuDebugOn(!d_is_dir(file->f_path.dentry)); + -+ bend = au_fbend_dir(file); -+ for (bindex = au_fbstart(file); -+ bindex <= bend && sz < KMALLOC_MAX_SIZE; ++ bbot = au_fbbot_dir(file); ++ for (bindex = au_fbtop(file); ++ bindex <= bbot && sz < KMALLOC_MAX_SIZE; + bindex++) { + h_file = au_hf_dir(file, bindex); + if (h_file && file_inode(h_file)) @@ -8304,9 +8675,9 @@ diff -Nurp linux-4.4.3/fs/aufs/dir.c linux-4.4.3-aufs/fs/aufs/dir.c + AuDebugOn(!dentry); + AuDebugOn(!d_is_dir(dentry)); + -+ bend = au_dbtaildir(dentry); -+ for (bindex = au_dbstart(dentry); -+ bindex <= bend && sz < KMALLOC_MAX_SIZE; ++ bbot = au_dbtaildir(dentry); ++ for (bindex = au_dbtop(dentry); ++ bindex <= bbot && sz < KMALLOC_MAX_SIZE; + bindex++) { + h_dentry = au_h_dptr(dentry, bindex); + if (h_dentry && d_is_positive(h_dentry)) @@ -8339,7 +8710,7 @@ diff -Nurp linux-4.4.3/fs/aufs/dir.c linux-4.4.3-aufs/fs/aufs/dir.c + struct au_branch *br; + struct au_hinode *hdir; + int err; -+ aufs_bindex_t bstart, bindex; ++ aufs_bindex_t btop, bindex; + + sb = a->dentry->d_sb; + if (d_really_is_negative(a->dentry)) @@ -8348,9 +8719,9 @@ diff -Nurp linux-4.4.3/fs/aufs/dir.c linux-4.4.3-aufs/fs/aufs/dir.c + aufs_read_lock(a->dentry, AuLock_DW); /* noflush */ + + dir = d_inode(a->dentry); -+ bstart = au_ibstart(dir); ++ btop = au_ibtop(dir); + bindex = au_br_index(sb, a->brid); -+ if (bindex < bstart) ++ if (bindex < btop) + goto out_unlock; + + br = au_sbr(sb, bindex); @@ -8360,17 +8731,17 @@ diff -Nurp linux-4.4.3/fs/aufs/dir.c linux-4.4.3-aufs/fs/aufs/dir.c + h_path.mnt = au_br_mnt(br); + au_dtime_store(&dt, a->dentry, &h_path); + -+ br = au_sbr(sb, bstart); ++ br = au_sbr(sb, btop); + if (!au_br_writable(br->br_perm)) + goto out_unlock; -+ h_path.dentry = au_h_dptr(a->dentry, bstart); ++ h_path.dentry = au_h_dptr(a->dentry, btop); + h_path.mnt = au_br_mnt(br); + err = vfsub_mnt_want_write(h_path.mnt); + if (err) + goto out_unlock; -+ hdir = au_hi(dir, bstart); ++ hdir = au_hi(dir, btop); + au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); -+ h_dir = au_h_iptr(dir, bstart); ++ h_dir = au_h_iptr(dir, btop); + if (h_dir->i_nlink + && timespec_compare(&h_dir->i_mtime, &dt.dt_mtime) < 0) { + dt.dt_h_path = h_path; @@ -8391,7 +8762,7 @@ diff -Nurp linux-4.4.3/fs/aufs/dir.c linux-4.4.3-aufs/fs/aufs/dir.c +void au_dir_ts(struct inode *dir, aufs_bindex_t bindex) +{ + int perm, wkq_err; -+ aufs_bindex_t bstart; ++ aufs_bindex_t btop; + struct au_dir_ts_arg *arg; + struct dentry *dentry; + struct super_block *sb; @@ -8401,13 +8772,13 @@ diff -Nurp linux-4.4.3/fs/aufs/dir.c linux-4.4.3-aufs/fs/aufs/dir.c + dentry = d_find_any_alias(dir); + AuDebugOn(!dentry); + sb = dentry->d_sb; -+ bstart = au_ibstart(dir); -+ if (bstart == bindex) { ++ btop = au_ibtop(dir); ++ if (btop == bindex) { + au_cpup_attr_timesizes(dir); + goto out; + } + -+ perm = au_sbr_perm(sb, bstart); ++ perm = au_sbr_perm(sb, btop); + if (!au_br_writable(perm)) + goto out; + @@ -8434,24 +8805,24 @@ diff -Nurp linux-4.4.3/fs/aufs/dir.c linux-4.4.3-aufs/fs/aufs/dir.c +{ + int err; + unsigned int flags; -+ aufs_bindex_t bindex, btail, bstart; ++ aufs_bindex_t bindex, btail, btop; + struct dentry *dentry, *h_dentry; + struct file *h_file; + + /* open all lower dirs */ + dentry = file->f_path.dentry; -+ bstart = au_dbstart(dentry); -+ for (bindex = au_fbstart(file); bindex < bstart; bindex++) ++ btop = au_dbtop(dentry); ++ for (bindex = au_fbtop(file); bindex < btop; bindex++) + au_set_h_fptr(file, bindex, NULL); -+ au_set_fbstart(file, bstart); ++ au_set_fbtop(file, btop); + + btail = au_dbtaildir(dentry); -+ for (bindex = au_fbend_dir(file); btail < bindex; bindex--) ++ for (bindex = au_fbbot_dir(file); btail < bindex; bindex--) + au_set_h_fptr(file, bindex, NULL); -+ au_set_fbend_dir(file, btail); ++ au_set_fbbot_dir(file, btail); + + flags = vfsub_file_flags(file); -+ for (bindex = bstart; bindex <= btail; bindex++) { ++ for (bindex = btop; bindex <= btail; bindex++) { + h_dentry = au_h_dptr(dentry, bindex); + if (!h_dentry) + continue; @@ -8488,10 +8859,10 @@ diff -Nurp linux-4.4.3/fs/aufs/dir.c linux-4.4.3-aufs/fs/aufs/dir.c + mnt = file->f_path.mnt; + dentry = file->f_path.dentry; + file->f_version = d_inode(dentry)->i_version; -+ bindex = au_dbstart(dentry); -+ au_set_fbstart(file, bindex); ++ bindex = au_dbtop(dentry); ++ au_set_fbtop(file, bindex); + btail = au_dbtaildir(dentry); -+ au_set_fbend_dir(file, btail); ++ au_set_fbbot_dir(file, btail); + for (; !err && bindex <= btail; bindex++) { + h_dentry = au_h_dptr(dentry, bindex); + if (!h_dentry) @@ -8514,10 +8885,10 @@ diff -Nurp linux-4.4.3/fs/aufs/dir.c linux-4.4.3-aufs/fs/aufs/dir.c + return 0; /* success */ + + /* close all */ -+ for (bindex = au_fbstart(file); bindex <= btail; bindex++) ++ for (bindex = au_fbtop(file); bindex <= btail; bindex++) + au_set_h_fptr(file, bindex, NULL); -+ au_set_fbstart(file, -1); -+ au_set_fbend_dir(file, -1); ++ au_set_fbtop(file, -1); ++ au_set_fbbot_dir(file, -1); + + return err; +} @@ -8552,7 +8923,7 @@ diff -Nurp linux-4.4.3/fs/aufs/dir.c linux-4.4.3-aufs/fs/aufs/dir.c + struct au_vdir *vdir_cache; + struct au_finfo *finfo; + struct au_fidir *fidir; -+ aufs_bindex_t bindex, bend; ++ aufs_bindex_t bindex, bbot; + + finfo = au_fi(file); + fidir = finfo->fi_hdir; @@ -8569,8 +8940,8 @@ diff -Nurp linux-4.4.3/fs/aufs/dir.c linux-4.4.3-aufs/fs/aufs/dir.c + * calls fput() instead of filp_close(), + * since no dnotify or lock for the lower file. + */ -+ bend = fidir->fd_bbot; -+ for (; bindex <= bend; bindex++) ++ bbot = fidir->fd_bbot; ++ for (; bindex <= bbot; bindex++) + au_set_h_fptr(file, bindex, NULL); + } + kfree(fidir); @@ -8585,12 +8956,12 @@ diff -Nurp linux-4.4.3/fs/aufs/dir.c linux-4.4.3-aufs/fs/aufs/dir.c +static int au_do_flush_dir(struct file *file, fl_owner_t id) +{ + int err; -+ aufs_bindex_t bindex, bend; ++ aufs_bindex_t bindex, bbot; + struct file *h_file; + + err = 0; -+ bend = au_fbend_dir(file); -+ for (bindex = au_fbstart(file); !err && bindex <= bend; bindex++) { ++ bbot = au_fbbot_dir(file); ++ for (bindex = au_fbtop(file); !err && bindex <= bbot; bindex++) { + h_file = au_hf_dir(file, bindex); + if (h_file) + err = vfsub_flush(h_file, id); @@ -8608,7 +8979,7 @@ diff -Nurp linux-4.4.3/fs/aufs/dir.c linux-4.4.3-aufs/fs/aufs/dir.c +static int au_do_fsync_dir_no_file(struct dentry *dentry, int datasync) +{ + int err; -+ aufs_bindex_t bend, bindex; ++ aufs_bindex_t bbot, bindex; + struct inode *inode; + struct super_block *sb; + @@ -8616,8 +8987,8 @@ diff -Nurp linux-4.4.3/fs/aufs/dir.c linux-4.4.3-aufs/fs/aufs/dir.c + sb = dentry->d_sb; + inode = d_inode(dentry); + IMustLock(inode); -+ bend = au_dbend(dentry); -+ for (bindex = au_dbstart(dentry); !err && bindex <= bend; bindex++) { ++ bbot = au_dbbot(dentry); ++ for (bindex = au_dbtop(dentry); !err && bindex <= bbot; bindex++) { + struct path h_path; + + if (au_test_ro(sb, bindex, inode)) @@ -8636,7 +9007,7 @@ diff -Nurp linux-4.4.3/fs/aufs/dir.c linux-4.4.3-aufs/fs/aufs/dir.c +static int au_do_fsync_dir(struct file *file, int datasync) +{ + int err; -+ aufs_bindex_t bend, bindex; ++ aufs_bindex_t bbot, bindex; + struct file *h_file; + struct super_block *sb; + struct inode *inode; @@ -8647,8 +9018,8 @@ diff -Nurp linux-4.4.3/fs/aufs/dir.c linux-4.4.3-aufs/fs/aufs/dir.c + + inode = file_inode(file); + sb = inode->i_sb; -+ bend = au_fbend_dir(file); -+ for (bindex = au_fbstart(file); !err && bindex <= bend; bindex++) { ++ bbot = au_fbbot_dir(file); ++ for (bindex = au_fbtop(file); !err && bindex <= bbot; bindex++) { + h_file = au_hf_dir(file, bindex); + if (!h_file || au_test_ro(sb, bindex, inode)) + continue; @@ -8670,13 +9041,11 @@ diff -Nurp linux-4.4.3/fs/aufs/dir.c linux-4.4.3-aufs/fs/aufs/dir.c + struct dentry *dentry; + struct inode *inode; + struct super_block *sb; -+ struct mutex *mtx; + + err = 0; + dentry = file->f_path.dentry; + inode = d_inode(dentry); -+ mtx = &inode->i_mutex; -+ mutex_lock(mtx); ++ inode_lock(inode); + sb = dentry->d_sb; + si_noflush_read_lock(sb); + if (file) @@ -8691,7 +9060,7 @@ diff -Nurp linux-4.4.3/fs/aufs/dir.c linux-4.4.3-aufs/fs/aufs/dir.c + fi_write_unlock(file); + + si_read_unlock(sb); -+ mutex_unlock(mtx); ++ inode_unlock(inode); + return err; +} + @@ -8722,7 +9091,7 @@ diff -Nurp linux-4.4.3/fs/aufs/dir.c linux-4.4.3-aufs/fs/aufs/dir.c + if (unlikely(err)) + goto out_unlock; + -+ h_inode = au_h_iptr(inode, au_ibstart(inode)); ++ h_inode = au_h_iptr(inode, au_ibtop(inode)); + if (!au_test_nfsd()) { + err = au_vdir_fill_de(file, ctx); + fsstack_copy_attr_atime(inode, h_inode); @@ -8865,9 +9234,9 @@ diff -Nurp linux-4.4.3/fs/aufs/dir.c linux-4.4.3-aufs/fs/aufs/dir.c + h_dentry = au_h_dptr(dentry, arg->bindex); + h_inode = d_inode(h_dentry); + /* todo: i_mode changes anytime? */ -+ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); ++ inode_lock_nested(h_inode, AuLsc_I_CHILD); + err = au_test_h_perm_sio(h_inode, MAY_EXEC | MAY_READ); -+ mutex_unlock(&h_inode->i_mutex); ++ inode_unlock(h_inode); + if (!err) + err = do_test_empty(dentry, arg); + else { @@ -8891,7 +9260,7 @@ diff -Nurp linux-4.4.3/fs/aufs/dir.c linux-4.4.3-aufs/fs/aufs/dir.c +{ + int err; + unsigned int rdhash; -+ aufs_bindex_t bindex, bstart, btail; ++ aufs_bindex_t bindex, btop, btail; + struct au_nhash whlist; + struct test_empty_arg arg = { + .ctx = { @@ -8911,20 +9280,20 @@ diff -Nurp linux-4.4.3/fs/aufs/dir.c linux-4.4.3-aufs/fs/aufs/dir.c + + arg.flags = 0; + arg.whlist = &whlist; -+ bstart = au_dbstart(dentry); ++ btop = au_dbtop(dentry); + if (au_opt_test(au_mntflags(dentry->d_sb), SHWH)) + au_fset_testempty(arg.flags, SHWH); + test_empty = do_test_empty; + if (au_opt_test(au_mntflags(dentry->d_sb), DIRPERM1)) + test_empty = sio_test_empty; -+ arg.bindex = bstart; ++ arg.bindex = btop; + err = test_empty(dentry, &arg); + if (unlikely(err)) + goto out_whlist; + + au_fset_testempty(arg.flags, WHONLY); + btail = au_dbtaildir(dentry); -+ for (bindex = bstart + 1; !err && bindex <= btail; bindex++) { ++ for (bindex = btop + 1; !err && bindex <= btail; bindex++) { + struct dentry *h_dentry; + + h_dentry = au_h_dptr(dentry, bindex); @@ -8956,7 +9325,7 @@ diff -Nurp linux-4.4.3/fs/aufs/dir.c linux-4.4.3-aufs/fs/aufs/dir.c + if (au_opt_test(au_mntflags(dentry->d_sb), SHWH)) + au_fset_testempty(arg.flags, SHWH); + btail = au_dbtaildir(dentry); -+ for (bindex = au_dbstart(dentry); !err && bindex <= btail; bindex++) { ++ for (bindex = au_dbtop(dentry); !err && bindex <= btail; bindex++) { + struct dentry *h_dentry; + + h_dentry = au_h_dptr(dentry, bindex); @@ -8985,9 +9354,11 @@ diff -Nurp linux-4.4.3/fs/aufs/dir.c linux-4.4.3-aufs/fs/aufs/dir.c + .flush = aufs_flush_dir, + .fsync = aufs_fsync_dir +}; -diff -Nurp linux-4.4.3/fs/aufs/dir.h linux-4.4.3-aufs/fs/aufs/dir.h ---- linux-4.4.3/fs/aufs/dir.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/dir.h 2016-03-02 17:59:37.364498710 +0200 +diff --git a/fs/aufs/dir.h b/fs/aufs/dir.h +new file mode 100644 +index 0000000..b0a79d7 +--- /dev/null ++++ b/fs/aufs/dir.h @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima @@ -9107,9 +9478,11 @@ diff -Nurp linux-4.4.3/fs/aufs/dir.h linux-4.4.3-aufs/fs/aufs/dir.h + +#endif /* __KERNEL__ */ +#endif /* __AUFS_DIR_H__ */ -diff -Nurp linux-4.4.3/fs/aufs/dynop.c linux-4.4.3-aufs/fs/aufs/dynop.c ---- linux-4.4.3/fs/aufs/dynop.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/dynop.c 2016-03-02 17:59:37.364498710 +0200 +diff --git a/fs/aufs/dynop.c b/fs/aufs/dynop.c +new file mode 100644 +index 0000000..dfb3a71 +--- /dev/null ++++ b/fs/aufs/dynop.c @@ -0,0 +1,356 @@ +/* + * Copyright (C) 2010-2016 Junjiro R. Okajima @@ -9421,14 +9794,14 @@ diff -Nurp linux-4.4.3/fs/aufs/dynop.c linux-4.4.3-aufs/fs/aufs/dynop.c +int au_dy_irefresh(struct inode *inode) +{ + int err; -+ aufs_bindex_t bstart; ++ aufs_bindex_t btop; + struct inode *h_inode; + + err = 0; + if (S_ISREG(inode->i_mode)) { -+ bstart = au_ibstart(inode); -+ h_inode = au_h_iptr(inode, bstart); -+ err = au_dy_iaop(inode, bstart, h_inode); ++ btop = au_ibtop(inode); ++ h_inode = au_h_iptr(inode, btop); ++ err = au_dy_iaop(inode, btop, h_inode); + } + return err; +} @@ -9467,9 +9840,11 @@ diff -Nurp linux-4.4.3/fs/aufs/dynop.c linux-4.4.3-aufs/fs/aufs/dynop.c + for (i = 0; i < AuDyLast; i++) + WARN_ON(!list_empty(&dynop[i].head)); +} -diff -Nurp linux-4.4.3/fs/aufs/dynop.h linux-4.4.3-aufs/fs/aufs/dynop.h ---- linux-4.4.3/fs/aufs/dynop.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/dynop.h 2016-03-02 17:59:37.364498710 +0200 +diff --git a/fs/aufs/dynop.h b/fs/aufs/dynop.h +new file mode 100644 +index 0000000..8680bfc +--- /dev/null ++++ b/fs/aufs/dynop.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2010-2016 Junjiro R. Okajima @@ -9532,10 +9907,12 @@ diff -Nurp linux-4.4.3/fs/aufs/dynop.h linux-4.4.3-aufs/fs/aufs/dynop.h + +#endif /* __KERNEL__ */ +#endif /* __AUFS_DYNOP_H__ */ -diff -Nurp linux-4.4.3/fs/aufs/export.c linux-4.4.3-aufs/fs/aufs/export.c ---- linux-4.4.3/fs/aufs/export.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/export.c 2016-03-02 17:59:37.365498717 +0200 -@@ -0,0 +1,819 @@ +diff --git a/fs/aufs/export.c b/fs/aufs/export.c +new file mode 100644 +index 0000000..8755b1e +--- /dev/null ++++ b/fs/aufs/export.c +@@ -0,0 +1,824 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima + */ @@ -9931,9 +10308,7 @@ diff -Nurp linux-4.4.3/fs/aufs/export.c linux-4.4.3-aufs/fs/aufs/export.c + + /* do not call vfsub_lkup_one() */ + dir = d_inode(parent); -+ mutex_lock(&dir->i_mutex); -+ dentry = vfsub_lookup_one_len(arg.name, parent, arg.namelen); -+ mutex_unlock(&dir->i_mutex); ++ dentry = vfsub_lookup_one_len_unlocked(arg.name, parent, arg.namelen); + AuTraceErrPtr(dentry); + if (IS_ERR(dentry)) + goto out_name; @@ -10040,9 +10415,11 @@ diff -Nurp linux-4.4.3/fs/aufs/export.c linux-4.4.3-aufs/fs/aufs/export.c + h_mnt = au_br_mnt(br); + h_sb = h_mnt->mnt_sb; + /* todo: call lower fh_to_dentry()? fh_to_parent()? */ ++ lockdep_off(); + h_parent = exportfs_decode_fh(h_mnt, (void *)(fh + Fh_tail), + fh_len - Fh_tail, fh[Fh_h_type], + h_acceptable, /*context*/NULL); ++ lockdep_on(); + dentry = h_parent; + if (unlikely(!h_parent || IS_ERR(h_parent))) { + AuWarn1("%s decode_fh failed, %ld\n", @@ -10146,7 +10523,7 @@ diff -Nurp linux-4.4.3/fs/aufs/export.c linux-4.4.3-aufs/fs/aufs/export.c + + /* is the parent dir cached? */ + br = au_sbr(sb, nsi_lock.bindex); -+ atomic_inc(&br->br_count); ++ au_br_get(br); + dentry = decode_by_dir_ino(sb, ino, dir_ino, &nsi_lock); + if (IS_ERR(dentry)) + goto out_unlock; @@ -10170,7 +10547,7 @@ diff -Nurp linux-4.4.3/fs/aufs/export.c linux-4.4.3-aufs/fs/aufs/export.c + dentry = ERR_PTR(-ESTALE); +out_unlock: + if (br) -+ atomic_dec(&br->br_count); ++ au_br_put(br); + si_read_unlock(sb); +out: + AuTraceErrPtr(dentry); @@ -10237,7 +10614,7 @@ diff -Nurp linux-4.4.3/fs/aufs/export.c linux-4.4.3-aufs/fs/aufs/export.c + err = -EIO; + parent = NULL; + ii_read_lock_child(inode); -+ bindex = au_ibstart(inode); ++ bindex = au_ibtop(inode); + if (!dir) { + dentry = d_find_any_alias(inode); + if (unlikely(!dentry)) @@ -10312,7 +10689,7 @@ diff -Nurp linux-4.4.3/fs/aufs/export.c linux-4.4.3-aufs/fs/aufs/export.c + sb = inode->i_sb; + si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); + ii_write_lock_child(inode); -+ bindex = au_ibstart(inode); ++ bindex = au_ibtop(inode); + AuDebugOn(bindex < 0); + h_inode = au_h_iptr(inode, bindex); + @@ -10348,6 +10725,11 @@ diff -Nurp linux-4.4.3/fs/aufs/export.c linux-4.4.3-aufs/fs/aufs/export.c + struct au_sbinfo *sbinfo; + __u32 u; + ++ BUILD_BUG_ON_MSG(IS_BUILTIN(CONFIG_AUFS_FS) ++ && IS_MODULE(CONFIG_EXPORTFS), ++ AUFS_NAME ": unsupported configuration " ++ "CONFIG_EXPORTFS=m and CONFIG_AUFS_FS=y"); ++ + sb->s_export_op = &aufs_export_op; + sbinfo = au_sbi(sb); + sbinfo->si_xigen = NULL; @@ -10355,9 +10737,774 @@ diff -Nurp linux-4.4.3/fs/aufs/export.c linux-4.4.3-aufs/fs/aufs/export.c + BUILD_BUG_ON(sizeof(u) != sizeof(int)); + atomic_set(&sbinfo->si_xigen_next, u); +} -diff -Nurp linux-4.4.3/fs/aufs/fhsm.c linux-4.4.3-aufs/fs/aufs/fhsm.c ---- linux-4.4.3/fs/aufs/fhsm.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/fhsm.c 2016-03-02 17:59:37.365498717 +0200 +diff --git a/fs/aufs/f_op.c b/fs/aufs/f_op.c +new file mode 100644 +index 0000000..b0b0fff +--- /dev/null ++++ b/fs/aufs/f_op.c +@@ -0,0 +1,757 @@ ++/* ++ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ */ ++ ++/* ++ * file and vm operations ++ */ ++ ++#include ++#include ++#include ++#include ++#include "aufs.h" ++ ++int au_do_open_nondir(struct file *file, int flags, struct file *h_file) ++{ ++ int err; ++ aufs_bindex_t bindex; ++ struct dentry *dentry, *h_dentry; ++ struct au_finfo *finfo; ++ struct inode *h_inode; ++ ++ FiMustWriteLock(file); ++ ++ err = 0; ++ dentry = file->f_path.dentry; ++ AuDebugOn(IS_ERR_OR_NULL(dentry)); ++ finfo = au_fi(file); ++ memset(&finfo->fi_htop, 0, sizeof(finfo->fi_htop)); ++ atomic_set(&finfo->fi_mmapped, 0); ++ bindex = au_dbtop(dentry); ++ if (!h_file) { ++ h_dentry = au_h_dptr(dentry, bindex); ++ err = vfsub_test_mntns(file->f_path.mnt, h_dentry->d_sb); ++ if (unlikely(err)) ++ goto out; ++ h_file = au_h_open(dentry, bindex, flags, file, /*force_wr*/0); ++ } else { ++ h_dentry = h_file->f_path.dentry; ++ err = vfsub_test_mntns(file->f_path.mnt, h_dentry->d_sb); ++ if (unlikely(err)) ++ goto out; ++ get_file(h_file); ++ } ++ if (IS_ERR(h_file)) ++ err = PTR_ERR(h_file); ++ else { ++ if ((flags & __O_TMPFILE) ++ && !(flags & O_EXCL)) { ++ h_inode = file_inode(h_file); ++ spin_lock(&h_inode->i_lock); ++ h_inode->i_state |= I_LINKABLE; ++ spin_unlock(&h_inode->i_lock); ++ } ++ au_set_fbtop(file, bindex); ++ au_set_h_fptr(file, bindex, h_file); ++ au_update_figen(file); ++ /* todo: necessary? */ ++ /* file->f_ra = h_file->f_ra; */ ++ } ++ ++out: ++ return err; ++} ++ ++static int aufs_open_nondir(struct inode *inode __maybe_unused, ++ struct file *file) ++{ ++ int err; ++ struct super_block *sb; ++ struct au_do_open_args args = { ++ .open = au_do_open_nondir ++ }; ++ ++ AuDbg("%pD, f_flags 0x%x, f_mode 0x%x\n", ++ file, vfsub_file_flags(file), file->f_mode); ++ ++ sb = file->f_path.dentry->d_sb; ++ si_read_lock(sb, AuLock_FLUSH); ++ err = au_do_open(file, &args); ++ si_read_unlock(sb); ++ return err; ++} ++ ++int aufs_release_nondir(struct inode *inode __maybe_unused, struct file *file) ++{ ++ struct au_finfo *finfo; ++ aufs_bindex_t bindex; ++ ++ finfo = au_fi(file); ++ au_sphl_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); ++ ++ au_finfo_fin(file); ++ return 0; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int au_do_flush_nondir(struct file *file, fl_owner_t id) ++{ ++ int err; ++ struct file *h_file; ++ ++ err = 0; ++ h_file = au_hf_top(file); ++ if (h_file) ++ err = vfsub_flush(h_file, id); ++ return err; ++} ++ ++static int aufs_flush_nondir(struct file *file, fl_owner_t id) ++{ ++ return au_do_flush(file, id, au_do_flush_nondir); ++} ++ ++/* ---------------------------------------------------------------------- */ ++/* ++ * read and write functions acquire [fdi]_rwsem once, but release before ++ * mmap_sem. This is because to stop a race condition between mmap(2). ++ * Releasing these aufs-rwsem should be safe, no branch-mamagement (by keeping ++ * si_rwsem), no harmful copy-up should happen. Actually copy-up may happen in ++ * read functions after [fdi]_rwsem are released, but it should be harmless. ++ */ ++ ++/* Callers should call au_read_post() or fput() in the end */ ++struct file *au_read_pre(struct file *file, int keep_fi) ++{ ++ struct file *h_file; ++ int err; ++ ++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/0); ++ if (!err) { ++ di_read_unlock(file->f_path.dentry, AuLock_IR); ++ h_file = au_hf_top(file); ++ get_file(h_file); ++ if (!keep_fi) ++ fi_read_unlock(file); ++ } else ++ h_file = ERR_PTR(err); ++ ++ return h_file; ++} ++ ++static void au_read_post(struct inode *inode, struct file *h_file) ++{ ++ /* update without lock, I don't think it a problem */ ++ fsstack_copy_attr_atime(inode, file_inode(h_file)); ++ fput(h_file); ++} ++ ++struct au_write_pre { ++ blkcnt_t blks; ++ aufs_bindex_t btop; ++}; ++ ++/* ++ * return with iinfo is write-locked ++ * callers should call au_write_post() or iinfo_write_unlock() + fput() in the ++ * end ++ */ ++static struct file *au_write_pre(struct file *file, int do_ready, ++ struct au_write_pre *wpre) ++{ ++ struct file *h_file; ++ struct dentry *dentry; ++ int err; ++ struct au_pin pin; ++ ++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1); ++ h_file = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out; ++ ++ dentry = file->f_path.dentry; ++ if (do_ready) { ++ err = au_ready_to_write(file, -1, &pin); ++ if (unlikely(err)) { ++ h_file = ERR_PTR(err); ++ di_write_unlock(dentry); ++ goto out_fi; ++ } ++ } ++ ++ di_downgrade_lock(dentry, /*flags*/0); ++ if (wpre) ++ wpre->btop = au_fbtop(file); ++ h_file = au_hf_top(file); ++ get_file(h_file); ++ if (wpre) ++ wpre->blks = file_inode(h_file)->i_blocks; ++ if (do_ready) ++ au_unpin(&pin); ++ di_read_unlock(dentry, /*flags*/0); ++ ++out_fi: ++ fi_write_unlock(file); ++out: ++ return h_file; ++} ++ ++static void au_write_post(struct inode *inode, struct file *h_file, ++ struct au_write_pre *wpre, ssize_t written) ++{ ++ struct inode *h_inode; ++ ++ au_cpup_attr_timesizes(inode); ++ AuDebugOn(au_ibtop(inode) != wpre->btop); ++ 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); ++} ++ ++static ssize_t aufs_read(struct file *file, char __user *buf, size_t count, ++ loff_t *ppos) ++{ ++ ssize_t err; ++ struct inode *inode; ++ struct file *h_file; ++ struct super_block *sb; ++ ++ inode = file_inode(file); ++ sb = inode->i_sb; ++ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); ++ ++ h_file = au_read_pre(file, /*keep_fi*/0); ++ err = PTR_ERR(h_file); ++ if (IS_ERR(h_file)) ++ goto out; ++ ++ /* filedata may be obsoleted by concurrent copyup, but no problem */ ++ err = vfsub_read_u(h_file, buf, count, ppos); ++ /* todo: necessary? */ ++ /* file->f_ra = h_file->f_ra; */ ++ au_read_post(inode, h_file); ++ ++out: ++ si_read_unlock(sb); ++ return err; ++} ++ ++/* ++ * todo: very ugly ++ * it locks both of i_mutex and si_rwsem for read in safe. ++ * if the plink maintenance mode continues forever (that is the problem), ++ * may loop forever. ++ */ ++static void au_mtx_and_read_lock(struct inode *inode) ++{ ++ int err; ++ struct super_block *sb = inode->i_sb; ++ ++ while (1) { ++ inode_lock(inode); ++ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); ++ if (!err) ++ break; ++ inode_unlock(inode); ++ si_read_lock(sb, AuLock_NOPLMW); ++ si_read_unlock(sb); ++ } ++} ++ ++static ssize_t aufs_write(struct file *file, const char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ ssize_t err; ++ struct au_write_pre wpre; ++ struct inode *inode; ++ struct file *h_file; ++ char __user *buf = (char __user *)ubuf; ++ ++ inode = file_inode(file); ++ au_mtx_and_read_lock(inode); ++ ++ h_file = au_write_pre(file, /*do_ready*/1, &wpre); ++ err = PTR_ERR(h_file); ++ if (IS_ERR(h_file)) ++ goto out; ++ ++ err = vfsub_write_u(h_file, buf, count, ppos); ++ au_write_post(inode, h_file, &wpre, err); ++ ++out: ++ si_read_unlock(inode->i_sb); ++ inode_unlock(inode); ++ return err; ++} ++ ++static ssize_t au_do_iter(struct file *h_file, int rw, struct kiocb *kio, ++ struct iov_iter *iov_iter) ++{ ++ ssize_t err; ++ struct file *file; ++ ssize_t (*iter)(struct kiocb *, struct iov_iter *); ++ ++ err = security_file_permission(h_file, rw); ++ if (unlikely(err)) ++ goto out; ++ ++ err = -ENOSYS; ++ iter = NULL; ++ if (rw == MAY_READ) ++ iter = h_file->f_op->read_iter; ++ else if (rw == MAY_WRITE) ++ iter = h_file->f_op->write_iter; ++ ++ file = kio->ki_filp; ++ kio->ki_filp = h_file; ++ if (iter) { ++ lockdep_off(); ++ err = iter(kio, iov_iter); ++ lockdep_on(); ++ } else ++ /* currently there is no such fs */ ++ WARN_ON_ONCE(1); ++ kio->ki_filp = file; ++ ++out: ++ return err; ++} ++ ++static ssize_t aufs_read_iter(struct kiocb *kio, struct iov_iter *iov_iter) ++{ ++ ssize_t err; ++ struct file *file, *h_file; ++ struct inode *inode; ++ struct super_block *sb; ++ ++ file = kio->ki_filp; ++ inode = file_inode(file); ++ sb = inode->i_sb; ++ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); ++ ++ h_file = au_read_pre(file, /*keep_fi*/0); ++ err = PTR_ERR(h_file); ++ if (IS_ERR(h_file)) ++ goto out; ++ ++ err = au_do_iter(h_file, MAY_READ, kio, iov_iter); ++ /* todo: necessary? */ ++ /* file->f_ra = h_file->f_ra; */ ++ au_read_post(inode, h_file); ++ ++out: ++ si_read_unlock(sb); ++ return err; ++} ++ ++static ssize_t aufs_write_iter(struct kiocb *kio, struct iov_iter *iov_iter) ++{ ++ ssize_t err; ++ struct au_write_pre wpre; ++ struct inode *inode; ++ struct file *file, *h_file; ++ ++ file = kio->ki_filp; ++ inode = file_inode(file); ++ au_mtx_and_read_lock(inode); ++ ++ h_file = au_write_pre(file, /*do_ready*/1, &wpre); ++ err = PTR_ERR(h_file); ++ if (IS_ERR(h_file)) ++ goto out; ++ ++ err = au_do_iter(h_file, MAY_WRITE, kio, iov_iter); ++ au_write_post(inode, h_file, &wpre, err); ++ ++out: ++ si_read_unlock(inode->i_sb); ++ inode_unlock(inode); ++ return err; ++} ++ ++static ssize_t aufs_splice_read(struct file *file, loff_t *ppos, ++ struct pipe_inode_info *pipe, size_t len, ++ unsigned int flags) ++{ ++ ssize_t err; ++ struct file *h_file; ++ struct inode *inode; ++ struct super_block *sb; ++ ++ inode = file_inode(file); ++ sb = inode->i_sb; ++ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); ++ ++ h_file = au_read_pre(file, /*keep_fi*/1); ++ err = PTR_ERR(h_file); ++ if (IS_ERR(h_file)) ++ goto out; ++ ++ if (au_test_loopback_kthread()) { ++ au_warn_loopback(h_file->f_path.dentry->d_sb); ++ if (file->f_mapping != h_file->f_mapping) { ++ file->f_mapping = h_file->f_mapping; ++ smp_mb(); /* unnecessary? */ ++ } ++ } ++ fi_read_unlock(file); ++ ++ err = vfsub_splice_to(h_file, ppos, pipe, len, flags); ++ /* todo: necessasry? */ ++ /* file->f_ra = h_file->f_ra; */ ++ au_read_post(inode, h_file); ++ ++out: ++ si_read_unlock(sb); ++ return err; ++} ++ ++static ssize_t ++aufs_splice_write(struct pipe_inode_info *pipe, struct file *file, loff_t *ppos, ++ size_t len, unsigned int flags) ++{ ++ ssize_t err; ++ struct au_write_pre wpre; ++ struct inode *inode; ++ struct file *h_file; ++ ++ inode = file_inode(file); ++ au_mtx_and_read_lock(inode); ++ ++ h_file = au_write_pre(file, /*do_ready*/1, &wpre); ++ err = PTR_ERR(h_file); ++ if (IS_ERR(h_file)) ++ goto out; ++ ++ err = vfsub_splice_from(pipe, h_file, ppos, len, flags); ++ au_write_post(inode, h_file, &wpre, err); ++ ++out: ++ si_read_unlock(inode->i_sb); ++ inode_unlock(inode); ++ return err; ++} ++ ++static long aufs_fallocate(struct file *file, int mode, loff_t offset, ++ loff_t len) ++{ ++ long err; ++ struct au_write_pre wpre; ++ struct inode *inode; ++ struct file *h_file; ++ ++ inode = file_inode(file); ++ au_mtx_and_read_lock(inode); ++ ++ h_file = au_write_pre(file, /*do_ready*/1, &wpre); ++ err = PTR_ERR(h_file); ++ if (IS_ERR(h_file)) ++ goto out; ++ ++ lockdep_off(); ++ err = vfs_fallocate(h_file, mode, offset, len); ++ lockdep_on(); ++ au_write_post(inode, h_file, &wpre, /*written*/1); ++ ++out: ++ si_read_unlock(inode->i_sb); ++ inode_unlock(inode); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * The locking order around current->mmap_sem. ++ * - in most and regular cases ++ * file I/O syscall -- aufs_read() or something ++ * -- si_rwsem for read -- mmap_sem ++ * (Note that [fdi]i_rwsem are released before mmap_sem). ++ * - in mmap case ++ * mmap(2) -- mmap_sem -- aufs_mmap() -- si_rwsem for read -- [fdi]i_rwsem ++ * This AB-BA order is definitly bad, but is not a problem since "si_rwsem for ++ * read" allows muliple processes to acquire it and [fdi]i_rwsem are not held in ++ * file I/O. Aufs needs to stop lockdep in aufs_mmap() though. ++ * It means that when aufs acquires si_rwsem for write, the process should never ++ * acquire mmap_sem. ++ * ++ * Actually aufs_iterate() holds [fdi]i_rwsem before mmap_sem, but this is not a ++ * problem either since any directory is not able to be mmap-ed. ++ * The similar scenario is applied to aufs_readlink() too. ++ */ ++ ++#if 0 /* stop calling security_file_mmap() */ ++/* cf. linux/include/linux/mman.h: calc_vm_prot_bits() */ ++#define AuConv_VM_PROT(f, b) _calc_vm_trans(f, VM_##b, PROT_##b) ++ ++static unsigned long au_arch_prot_conv(unsigned long flags) ++{ ++ /* currently ppc64 only */ ++#ifdef CONFIG_PPC64 ++ /* cf. linux/arch/powerpc/include/asm/mman.h */ ++ AuDebugOn(arch_calc_vm_prot_bits(-1) != VM_SAO); ++ return AuConv_VM_PROT(flags, SAO); ++#else ++ AuDebugOn(arch_calc_vm_prot_bits(-1)); ++ return 0; ++#endif ++} ++ ++static unsigned long au_prot_conv(unsigned long flags) ++{ ++ return AuConv_VM_PROT(flags, READ) ++ | AuConv_VM_PROT(flags, WRITE) ++ | AuConv_VM_PROT(flags, EXEC) ++ | au_arch_prot_conv(flags); ++} ++ ++/* cf. linux/include/linux/mman.h: calc_vm_flag_bits() */ ++#define AuConv_VM_MAP(f, b) _calc_vm_trans(f, VM_##b, MAP_##b) ++ ++static unsigned long au_flag_conv(unsigned long flags) ++{ ++ return AuConv_VM_MAP(flags, GROWSDOWN) ++ | AuConv_VM_MAP(flags, DENYWRITE) ++ | AuConv_VM_MAP(flags, LOCKED); ++} ++#endif ++ ++static int aufs_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ int err; ++ const unsigned char wlock ++ = (file->f_mode & FMODE_WRITE) && (vma->vm_flags & VM_SHARED); ++ struct super_block *sb; ++ struct file *h_file; ++ struct inode *inode; ++ ++ AuDbgVmRegion(file, vma); ++ ++ inode = file_inode(file); ++ sb = inode->i_sb; ++ lockdep_off(); ++ si_read_lock(sb, AuLock_NOPLMW); ++ ++ h_file = au_write_pre(file, wlock, /*wpre*/NULL); ++ lockdep_on(); ++ err = PTR_ERR(h_file); ++ if (IS_ERR(h_file)) ++ goto out; ++ ++ err = 0; ++ au_set_mmapped(file); ++ au_vm_file_reset(vma, h_file); ++ /* ++ * we cannot call security_mmap_file() here since it may acquire ++ * mmap_sem or i_mutex. ++ * ++ * err = security_mmap_file(h_file, au_prot_conv(vma->vm_flags), ++ * au_flag_conv(vma->vm_flags)); ++ */ ++ if (!err) ++ err = h_file->f_op->mmap(h_file, vma); ++ if (!err) { ++ au_vm_prfile_set(vma, file); ++ fsstack_copy_attr_atime(inode, file_inode(h_file)); ++ goto out_fput; /* success */ ++ } ++ au_unset_mmapped(file); ++ au_vm_file_reset(vma, file); ++ ++out_fput: ++ lockdep_off(); ++ ii_write_unlock(inode); ++ lockdep_on(); ++ fput(h_file); ++out: ++ lockdep_off(); ++ si_read_unlock(sb); ++ lockdep_on(); ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int aufs_fsync_nondir(struct file *file, loff_t start, loff_t end, ++ int datasync) ++{ ++ int err; ++ struct au_write_pre wpre; ++ struct inode *inode; ++ struct file *h_file; ++ ++ err = 0; /* -EBADF; */ /* posix? */ ++ if (unlikely(!(file->f_mode & FMODE_WRITE))) ++ goto out; ++ ++ inode = file_inode(file); ++ au_mtx_and_read_lock(inode); ++ ++ h_file = au_write_pre(file, /*do_ready*/1, &wpre); ++ err = PTR_ERR(h_file); ++ if (IS_ERR(h_file)) ++ goto out_unlock; ++ ++ err = vfsub_fsync(h_file, &h_file->f_path, datasync); ++ au_write_post(inode, h_file, &wpre, /*written*/0); ++ ++out_unlock: ++ si_read_unlock(inode->i_sb); ++ inode_unlock(inode); ++out: ++ return err; ++} ++ ++/* no one supports this operation, currently */ ++#if 0 ++static int aufs_aio_fsync_nondir(struct kiocb *kio, int datasync) ++{ ++ int err; ++ struct au_write_pre wpre; ++ struct inode *inode, *h_inode; ++ struct file *file, *h_file; ++ ++ err = 0; /* -EBADF; */ /* posix? */ ++ if (unlikely(!(file->f_mode & FMODE_WRITE))) ++ goto out; ++ ++ file = kio->ki_filp; ++ inode = file_inode(file); ++ au_mtx_and_read_lock(inode); ++ ++ h_file = au_write_pre(file, /*do_ready*/1, &wpre); ++ err = PTR_ERR(h_file); ++ if (IS_ERR(h_file)) ++ goto out_unlock; ++ ++ err = -ENOSYS; ++ h_file = au_hf_top(file); ++ if (h_file->f_op->aio_fsync) { ++ h_inode = file_inode(h_file); ++ if (!is_sync_kiocb(kio)) { ++ get_file(h_file); ++ fput(file); ++ } ++ kio->ki_filp = h_file; ++ err = h_file->f_op->aio_fsync(kio, datasync); ++ inode_lock_nested(h_inode, AuLsc_I_CHILD); ++ if (!err) ++ vfsub_update_h_iattr(&h_file->f_path, /*did*/NULL); ++ /*ignore*/ ++ inode_unlock(h_inode); ++ } ++ au_write_post(inode, h_file, &wpre, /*written*/0); ++ ++out_unlock: ++ si_read_unlock(inode->sb); ++ inode_unlock(inode); ++out: ++ return err; ++} ++#endif ++ ++static int aufs_fasync(int fd, struct file *file, int flag) ++{ ++ int err; ++ struct file *h_file; ++ struct super_block *sb; ++ ++ sb = file->f_path.dentry->d_sb; ++ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); ++ ++ h_file = au_read_pre(file, /*keep_fi*/0); ++ err = PTR_ERR(h_file); ++ if (IS_ERR(h_file)) ++ goto out; ++ ++ if (h_file->f_op->fasync) ++ err = h_file->f_op->fasync(fd, h_file, flag); ++ fput(h_file); /* instead of au_read_post() */ ++ ++out: ++ si_read_unlock(sb); ++ return err; ++} ++ ++static int aufs_setfl(struct file *file, unsigned long arg) ++{ ++ int err; ++ struct file *h_file; ++ struct super_block *sb; ++ ++ sb = file->f_path.dentry->d_sb; ++ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); ++ ++ h_file = au_read_pre(file, /*keep_fi*/0); ++ err = PTR_ERR(h_file); ++ if (IS_ERR(h_file)) ++ goto out; ++ ++ arg |= vfsub_file_flags(file) & FASYNC; /* stop calling h_file->fasync */ ++ err = setfl(/*unused fd*/-1, h_file, arg); ++ fput(h_file); /* instead of au_read_post() */ ++ ++out: ++ si_read_unlock(sb); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* no one supports this operation, currently */ ++#if 0 ++static ssize_t aufs_sendpage(struct file *file, struct page *page, int offset, ++ size_t len, loff_t *pos, int more) ++{ ++} ++#endif ++ ++/* ---------------------------------------------------------------------- */ ++ ++const struct file_operations aufs_file_fop = { ++ .owner = THIS_MODULE, ++ ++ .llseek = default_llseek, ++ ++ .read = aufs_read, ++ .write = aufs_write, ++ .read_iter = aufs_read_iter, ++ .write_iter = aufs_write_iter, ++ ++#ifdef CONFIG_AUFS_POLL ++ .poll = aufs_poll, ++#endif ++ .unlocked_ioctl = aufs_ioctl_nondir, ++#ifdef CONFIG_COMPAT ++ .compat_ioctl = aufs_compat_ioctl_nondir, ++#endif ++ .mmap = aufs_mmap, ++ .open = aufs_open_nondir, ++ .flush = aufs_flush_nondir, ++ .release = aufs_release_nondir, ++ .fsync = aufs_fsync_nondir, ++ /* .aio_fsync = aufs_aio_fsync_nondir, */ ++ .fasync = aufs_fasync, ++ /* .sendpage = aufs_sendpage, */ ++ .setfl = aufs_setfl, ++ .splice_write = aufs_splice_write, ++ .splice_read = aufs_splice_read, ++#if 0 ++ .aio_splice_write = aufs_aio_splice_write, ++ .aio_splice_read = aufs_aio_splice_read, ++#endif ++ .fallocate = aufs_fallocate ++}; +diff --git a/fs/aufs/fhsm.c b/fs/aufs/fhsm.c +new file mode 100644 +index 0000000..e3cb6ed +--- /dev/null ++++ b/fs/aufs/fhsm.c @@ -0,0 +1,412 @@ +/* + * Copyright (C) 2011-2016 Junjiro R. Okajima @@ -10502,12 +11649,12 @@ diff -Nurp linux-4.4.3/fs/aufs/fhsm.c linux-4.4.3-aufs/fs/aufs/fhsm.c + +void au_fhsm_wrote_all(struct super_block *sb, int force) +{ -+ aufs_bindex_t bindex, bend; ++ aufs_bindex_t bindex, bbot; + struct au_branch *br; + + /* exclude the bottom */ -+ bend = au_fhsm_bottom(sb); -+ for (bindex = 0; bindex < bend; bindex++) { ++ bbot = au_fhsm_bottom(sb); ++ for (bindex = 0; bindex < bbot; bindex++) { + br = au_sbr(sb, bindex); + if (au_br_fhsm(br->br_perm)) + au_fhsm_wrote(sb, bindex, force); @@ -10553,15 +11700,15 @@ diff -Nurp linux-4.4.3/fs/aufs/fhsm.c linux-4.4.3-aufs/fs/aufs/fhsm.c +{ + ssize_t err; + int nstbr; -+ aufs_bindex_t bindex, bend; ++ aufs_bindex_t bindex, bbot; + struct au_branch *br; + struct au_br_fhsm *bf; + + /* except the bottom branch */ + err = 0; + nstbr = 0; -+ bend = au_fhsm_bottom(sb); -+ for (bindex = 0; !err && bindex < bend; bindex++) { ++ bbot = au_fhsm_bottom(sb); ++ for (bindex = 0; !err && bindex < bbot; bindex++) { + br = au_sbr(sb, bindex); + if (!au_br_fhsm(br->br_perm)) + continue; @@ -10592,7 +11739,7 @@ diff -Nurp linux-4.4.3/fs/aufs/fhsm.c linux-4.4.3-aufs/fs/aufs/fhsm.c +{ + ssize_t err; + int readable; -+ aufs_bindex_t nfhsm, bindex, bend; ++ aufs_bindex_t nfhsm, bindex, bbot; + struct au_sbinfo *sbinfo; + struct au_fhsm *fhsm; + struct au_branch *br; @@ -10623,8 +11770,8 @@ diff -Nurp linux-4.4.3/fs/aufs/fhsm.c linux-4.4.3-aufs/fs/aufs/fhsm.c + AuDebugOn(!sb); + /* exclude the bottom branch */ + nfhsm = 0; -+ bend = au_fhsm_bottom(sb); -+ for (bindex = 0; bindex < bend; bindex++) { ++ bbot = au_fhsm_bottom(sb); ++ for (bindex = 0; bindex < bbot; bindex++) { + br = au_sbr(sb, bindex); + if (au_br_fhsm(br->br_perm)) + nfhsm++; @@ -10771,9 +11918,11 @@ diff -Nurp linux-4.4.3/fs/aufs/fhsm.c linux-4.4.3-aufs/fs/aufs/fhsm.c + if (u != AUFS_FHSM_CACHE_DEF_SEC) + seq_printf(seq, ",fhsm_sec=%u", u); +} -diff -Nurp linux-4.4.3/fs/aufs/file.c linux-4.4.3-aufs/fs/aufs/file.c ---- linux-4.4.3/fs/aufs/file.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/file.c 2016-03-02 17:59:37.366498724 +0200 +diff --git a/fs/aufs/file.c b/fs/aufs/file.c +new file mode 100644 +index 0000000..27c5bf8 +--- /dev/null ++++ b/fs/aufs/file.c @@ -0,0 +1,831 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima @@ -10846,7 +11995,7 @@ diff -Nurp linux-4.4.3/fs/aufs/file.c linux-4.4.3-aufs/fs/aufs/file.c + } + } + flags &= ~O_CREAT; -+ atomic_inc(&br->br_count); ++ au_br_get(br); + h_path.dentry = h_dentry; + h_path.mnt = au_br_mnt(br); + h_file = vfsub_dentry_open(&h_path, flags); @@ -10865,7 +12014,7 @@ diff -Nurp linux-4.4.3/fs/aufs/file.c linux-4.4.3-aufs/fs/aufs/file.c + goto out; /* success */ + +out_br: -+ atomic_dec(&br->br_count); ++ au_br_put(br); +out: + return h_file; +} @@ -10899,7 +12048,7 @@ diff -Nurp linux-4.4.3/fs/aufs/file.c linux-4.4.3-aufs/fs/aufs/file.c + err = 0; + if (IS_ROOT(dentry)) + goto out; -+ cpg.bsrc = au_dbstart(dentry); ++ cpg.bsrc = au_dbtop(dentry); + if (!cpg.bsrc) + goto out; + @@ -11014,7 +12163,7 @@ diff -Nurp linux-4.4.3/fs/aufs/file.c linux-4.4.3-aufs/fs/aufs/file.c + if (!err) + err = args->open(file, vfsub_file_flags(file), + args->h_file); -+ if (!err && au_fbstart(file) != au_dbstart(dentry)) ++ if (!err && au_fbtop(file) != au_dbtop(dentry)) + /* + * cmoo happens after h_file was opened. + * need to refresh file later. @@ -11047,49 +12196,49 @@ diff -Nurp linux-4.4.3/fs/aufs/file.c linux-4.4.3-aufs/fs/aufs/file.c +int au_reopen_nondir(struct file *file) +{ + int err; -+ aufs_bindex_t bstart; ++ aufs_bindex_t btop; + struct dentry *dentry; + struct file *h_file, *h_file_tmp; + + dentry = file->f_path.dentry; -+ bstart = au_dbstart(dentry); ++ btop = au_dbtop(dentry); + h_file_tmp = NULL; -+ if (au_fbstart(file) == bstart) { ++ if (au_fbtop(file) == btop) { + h_file = au_hf_top(file); + if (file->f_mode == h_file->f_mode) + return 0; /* success */ + h_file_tmp = h_file; + get_file(h_file_tmp); -+ au_set_h_fptr(file, bstart, NULL); ++ au_set_h_fptr(file, btop, NULL); + } + AuDebugOn(au_fi(file)->fi_hdir); + /* + * it can happen + * file exists on both of rw and ro -+ * open --> dbstart and fbstart are both 0 ++ * open --> dbtop and fbtop are both 0 + * prepend a branch as rw, "rw" become ro + * remove rw/file + * delete the top branch, "rw" becomes rw again -+ * --> dbstart is 1, fbstart is still 0 -+ * write --> fbstart is 0 but dbstart is 1 ++ * --> dbtop is 1, fbtop is still 0 ++ * write --> fbtop is 0 but dbtop is 1 + */ -+ /* AuDebugOn(au_fbstart(file) < bstart); */ ++ /* AuDebugOn(au_fbtop(file) < btop); */ + -+ h_file = au_h_open(dentry, bstart, vfsub_file_flags(file) & ~O_TRUNC, ++ h_file = au_h_open(dentry, btop, vfsub_file_flags(file) & ~O_TRUNC, + file, /*force_wr*/0); + err = PTR_ERR(h_file); + if (IS_ERR(h_file)) { + if (h_file_tmp) { -+ atomic_inc(&au_sbr(dentry->d_sb, bstart)->br_count); -+ au_set_h_fptr(file, bstart, h_file_tmp); ++ au_sbr_get(dentry->d_sb, btop); ++ au_set_h_fptr(file, btop, h_file_tmp); + h_file_tmp = NULL; + } + goto out; /* todo: close all? */ + } + + err = 0; -+ au_set_fbstart(file, bstart); -+ au_set_h_fptr(file, bstart, h_file); ++ au_set_fbtop(file, btop); ++ au_set_h_fptr(file, btop, h_file); + au_update_figen(file); + /* todo: necessary? */ + /* file->f_ra = h_file->f_ra; */ @@ -11106,7 +12255,7 @@ diff -Nurp linux-4.4.3/fs/aufs/file.c linux-4.4.3-aufs/fs/aufs/file.c + struct dentry *hi_wh) +{ + int err; -+ aufs_bindex_t bstart; ++ aufs_bindex_t btop; + struct au_dinfo *dinfo; + struct dentry *h_dentry; + struct au_hdentry *hdp; @@ -11114,14 +12263,14 @@ diff -Nurp linux-4.4.3/fs/aufs/file.c linux-4.4.3-aufs/fs/aufs/file.c + dinfo = au_di(file->f_path.dentry); + AuRwMustWriteLock(&dinfo->di_rwsem); + -+ bstart = dinfo->di_bstart; -+ dinfo->di_bstart = btgt; ++ btop = dinfo->di_btop; ++ dinfo->di_btop = btgt; + hdp = dinfo->di_hdentry; + h_dentry = hdp[0 + btgt].hd_dentry; + hdp[0 + btgt].hd_dentry = hi_wh; + err = au_reopen_nondir(file); + hdp[0 + btgt].hd_dentry = h_dentry; -+ dinfo->di_bstart = bstart; ++ dinfo->di_btop = btop; + + return err; +} @@ -11140,11 +12289,11 @@ diff -Nurp linux-4.4.3/fs/aufs/file.c linux-4.4.3-aufs/fs/aufs/file.c + .pin = pin + }; + -+ au_update_dbstart(cpg.dentry); ++ au_update_dbtop(cpg.dentry); + inode = d_inode(cpg.dentry); + h_inode = NULL; -+ if (au_dbstart(cpg.dentry) <= bcpup -+ && au_dbend(cpg.dentry) >= bcpup) { ++ if (au_dbtop(cpg.dentry) <= bcpup ++ && au_dbbot(cpg.dentry) >= bcpup) { + h_dentry = au_h_dptr(cpg.dentry, bcpup); + if (h_dentry && d_is_positive(h_dentry)) + h_inode = d_inode(h_dentry); @@ -11171,7 +12320,7 @@ diff -Nurp linux-4.4.3/fs/aufs/file.c linux-4.4.3-aufs/fs/aufs/file.c +int au_ready_to_write(struct file *file, loff_t len, struct au_pin *pin) +{ + int err; -+ aufs_bindex_t dbstart; ++ aufs_bindex_t dbtop; + struct dentry *parent; + struct inode *inode; + struct super_block *sb; @@ -11187,7 +12336,7 @@ diff -Nurp linux-4.4.3/fs/aufs/file.c linux-4.4.3-aufs/fs/aufs/file.c + + sb = cpg.dentry->d_sb; + inode = d_inode(cpg.dentry); -+ cpg.bsrc = au_fbstart(file); ++ cpg.bsrc = au_fbtop(file); + err = au_test_ro(sb, cpg.bsrc, inode); + if (!err && (au_hf_top(file)->f_mode & FMODE_WRITE)) { + err = au_pin(pin, cpg.dentry, cpg.bsrc, AuOpt_UDBA_NONE, @@ -11215,11 +12364,11 @@ diff -Nurp linux-4.4.3/fs/aufs/file.c linux-4.4.3-aufs/fs/aufs/file.c + if (unlikely(err)) + goto out_dgrade; + -+ dbstart = au_dbstart(cpg.dentry); -+ if (dbstart <= cpg.bdst) ++ dbtop = au_dbtop(cpg.dentry); ++ if (dbtop <= cpg.bdst) + cpg.bsrc = cpg.bdst; + -+ if (dbstart <= cpg.bdst /* just reopen */ ++ if (dbtop <= cpg.bdst /* just reopen */ + || !d_unhashed(cpg.dentry) /* copyup and reopen */ + ) { + h_file = au_h_open_pre(cpg.dentry, cpg.bsrc, /*force_wr*/0); @@ -11227,7 +12376,7 @@ diff -Nurp linux-4.4.3/fs/aufs/file.c linux-4.4.3-aufs/fs/aufs/file.c + err = PTR_ERR(h_file); + else { + di_downgrade_lock(parent, AuLock_IR); -+ if (dbstart > cpg.bdst) ++ if (dbtop > cpg.bdst) + err = au_sio_cpup_simple(&cpg); + if (!err) + err = au_reopen_nondir(file); @@ -11308,7 +12457,7 @@ diff -Nurp linux-4.4.3/fs/aufs/file.c linux-4.4.3-aufs/fs/aufs/file.c + finfo = au_fi(file); + sb = cpg.dentry->d_sb; + inode = d_inode(cpg.dentry); -+ cpg.bdst = au_ibstart(inode); ++ cpg.bdst = au_ibtop(inode); + if (cpg.bdst == finfo->fi_btop || IS_ROOT(cpg.dentry)) + goto out; + @@ -11329,7 +12478,7 @@ diff -Nurp linux-4.4.3/fs/aufs/file.c linux-4.4.3-aufs/fs/aufs/file.c + && au_opt_test(au_mntflags(sb), PLINK) + && au_plink_test(inode) + && !d_unhashed(cpg.dentry) -+ && cpg.bdst < au_dbstart(cpg.dentry)) { ++ && cpg.bdst < au_dbtop(cpg.dentry)) { + err = au_test_and_cpup_dirs(cpg.dentry, cpg.bdst); + if (unlikely(err)) + goto out_unlock; @@ -11357,7 +12506,7 @@ diff -Nurp linux-4.4.3/fs/aufs/file.c linux-4.4.3-aufs/fs/aufs/file.c + +static void au_do_refresh_dir(struct file *file) +{ -+ aufs_bindex_t bindex, bend, new_bindex, brid; ++ aufs_bindex_t bindex, bbot, new_bindex, brid; + struct au_hfile *p, tmp, *q; + struct au_finfo *finfo; + struct super_block *sb; @@ -11371,8 +12520,8 @@ diff -Nurp linux-4.4.3/fs/aufs/file.c linux-4.4.3-aufs/fs/aufs/file.c + AuDebugOn(!fidir); + p = fidir->fd_hfile + finfo->fi_btop; + brid = p->hf_br->br_id; -+ bend = fidir->fd_bbot; -+ for (bindex = finfo->fi_btop; bindex <= bend; bindex++, p++) { ++ bbot = fidir->fd_bbot; ++ for (bindex = finfo->fi_btop; bindex <= bbot; bindex++, p++) { + if (!p->hf_file) + continue; + @@ -11397,8 +12546,8 @@ diff -Nurp linux-4.4.3/fs/aufs/file.c linux-4.4.3-aufs/fs/aufs/file.c + + p = fidir->fd_hfile; + if (!au_test_mmapped(file) && !d_unlinked(file->f_path.dentry)) { -+ bend = au_sbend(sb); -+ for (finfo->fi_btop = 0; finfo->fi_btop <= bend; ++ bbot = au_sbbot(sb); ++ for (finfo->fi_btop = 0; finfo->fi_btop <= bbot; + finfo->fi_btop++, p++) + if (p->hf_file) { + if (file_inode(p->hf_file)) @@ -11406,16 +12555,16 @@ diff -Nurp linux-4.4.3/fs/aufs/file.c linux-4.4.3-aufs/fs/aufs/file.c + au_hfput(p, file); + } + } else { -+ bend = au_br_index(sb, brid); -+ for (finfo->fi_btop = 0; finfo->fi_btop < bend; ++ 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, file); -+ bend = au_sbend(sb); ++ bbot = au_sbbot(sb); + } + -+ p = fidir->fd_hfile + bend; -+ for (fidir->fd_bbot = bend; fidir->fd_bbot >= finfo->fi_btop; ++ p = fidir->fd_hfile + bbot; ++ for (fidir->fd_bbot = bbot; fidir->fd_bbot >= finfo->fi_btop; + fidir->fd_bbot--, p--) + if (p->hf_file) { + if (file_inode(p->hf_file)) @@ -11431,7 +12580,7 @@ diff -Nurp linux-4.4.3/fs/aufs/file.c linux-4.4.3-aufs/fs/aufs/file.c +static int refresh_file(struct file *file, int (*reopen)(struct file *file)) +{ + int err, need_reopen; -+ aufs_bindex_t bend, bindex; ++ aufs_bindex_t bbot, bindex; + struct dentry *dentry; + struct au_finfo *finfo; + struct au_hfile *hfile; @@ -11444,9 +12593,9 @@ diff -Nurp linux-4.4.3/fs/aufs/file.c linux-4.4.3-aufs/fs/aufs/file.c + bindex = au_br_index(dentry->d_sb, hfile->hf_br->br_id); + AuDebugOn(bindex < 0); + if (bindex != finfo->fi_btop) -+ au_set_fbstart(file, bindex); ++ au_set_fbtop(file, bindex); + } else { -+ err = au_fidir_realloc(finfo, au_sbend(dentry->d_sb) + 1); ++ err = au_fidir_realloc(finfo, au_sbbot(dentry->d_sb) + 1); + if (unlikely(err)) + goto out; + au_do_refresh_dir(file); @@ -11465,8 +12614,8 @@ diff -Nurp linux-4.4.3/fs/aufs/file.c linux-4.4.3-aufs/fs/aufs/file.c + + /* error, close all lower files */ + if (finfo->fi_hdir) { -+ bend = au_fbend_dir(file); -+ for (bindex = au_fbstart(file); bindex <= bend; bindex++) ++ bbot = au_fbbot_dir(file); ++ for (bindex = au_fbtop(file); bindex <= bbot; bindex++) + au_set_h_fptr(file, bindex, NULL); + } + @@ -11480,7 +12629,7 @@ diff -Nurp linux-4.4.3/fs/aufs/file.c linux-4.4.3-aufs/fs/aufs/file.c +{ + int err; + unsigned int sigen, figen; -+ aufs_bindex_t bstart; ++ aufs_bindex_t btop; + unsigned char pseudo_link; + struct dentry *dentry; + struct inode *inode; @@ -11492,9 +12641,9 @@ diff -Nurp linux-4.4.3/fs/aufs/file.c linux-4.4.3-aufs/fs/aufs/file.c + fi_write_lock(file); + figen = au_figen(file); + di_write_lock_child(dentry); -+ bstart = au_dbstart(dentry); -+ pseudo_link = (bstart != au_ibstart(inode)); -+ if (sigen == figen && !pseudo_link && au_fbstart(file) == bstart) { ++ btop = au_dbtop(dentry); ++ pseudo_link = (btop != au_ibtop(inode)); ++ if (sigen == figen && !pseudo_link && au_fbtop(file) == btop) { + if (!wlock) { + di_downgrade_lock(dentry, AuLock_IR); + fi_downgrade_lock(file); @@ -11606,9 +12755,11 @@ diff -Nurp linux-4.4.3/fs/aufs/file.c linux-4.4.3-aufs/fs/aufs/file.c + .swap_deactivate = aufs_swap_deactivate +#endif /* CONFIG_AUFS_DEBUG */ +}; -diff -Nurp linux-4.4.3/fs/aufs/file.h linux-4.4.3-aufs/fs/aufs/file.h ---- linux-4.4.3/fs/aufs/file.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/file.h 2016-03-02 17:59:37.366498724 +0200 +diff --git a/fs/aufs/file.h b/fs/aufs/file.h +new file mode 100644 +index 0000000..96ab0a0 +--- /dev/null ++++ b/fs/aufs/file.h @@ -0,0 +1,278 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima @@ -11756,13 +12907,13 @@ diff -Nurp linux-4.4.3/fs/aufs/file.h linux-4.4.3-aufs/fs/aufs/file.h +/* ---------------------------------------------------------------------- */ + +/* todo: hard/soft set? */ -+static inline aufs_bindex_t au_fbstart(struct file *file) ++static inline aufs_bindex_t au_fbtop(struct file *file) +{ + FiMustAnyLock(file); + return au_fi(file)->fi_btop; +} + -+static inline aufs_bindex_t au_fbend_dir(struct file *file) ++static inline aufs_bindex_t au_fbbot_dir(struct file *file) +{ + FiMustAnyLock(file); + AuDebugOn(!au_fi(file)->fi_hdir); @@ -11776,13 +12927,13 @@ diff -Nurp linux-4.4.3/fs/aufs/file.h linux-4.4.3-aufs/fs/aufs/file.h + return au_fi(file)->fi_hdir->fd_vdir_cache; +} + -+static inline void au_set_fbstart(struct file *file, aufs_bindex_t bindex) ++static inline void au_set_fbtop(struct file *file, aufs_bindex_t bindex) +{ + FiMustWriteLock(file); + au_fi(file)->fi_btop = bindex; +} + -+static inline void au_set_fbend_dir(struct file *file, aufs_bindex_t bindex) ++static inline void au_set_fbbot_dir(struct file *file, aufs_bindex_t bindex) +{ + FiMustWriteLock(file); + AuDebugOn(!au_fi(file)->fi_hdir); @@ -11888,10 +13039,12 @@ diff -Nurp linux-4.4.3/fs/aufs/file.h linux-4.4.3-aufs/fs/aufs/file.h + +#endif /* __KERNEL__ */ +#endif /* __AUFS_FILE_H__ */ -diff -Nurp linux-4.4.3/fs/aufs/finfo.c linux-4.4.3-aufs/fs/aufs/finfo.c ---- linux-4.4.3/fs/aufs/finfo.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/finfo.c 2016-03-02 17:59:37.366498724 +0200 -@@ -0,0 +1,143 @@ +diff --git a/fs/aufs/finfo.c b/fs/aufs/finfo.c +new file mode 100644 +index 0000000..07f6eb3 +--- /dev/null ++++ b/fs/aufs/finfo.c +@@ -0,0 +1,136 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima + */ @@ -11909,7 +13062,7 @@ diff -Nurp linux-4.4.3/fs/aufs/finfo.c linux-4.4.3-aufs/fs/aufs/finfo.c + allow_write_access(hf->hf_file); + fput(hf->hf_file); + hf->hf_file = NULL; -+ atomic_dec(&hf->hf_br->br_count); ++ au_br_put(hf->hf_br); + hf->hf_br = NULL; +} + @@ -11949,7 +13102,7 @@ diff -Nurp linux-4.4.3/fs/aufs/finfo.c linux-4.4.3-aufs/fs/aufs/finfo.c + struct au_fidir *fidir; + int nbr; + -+ nbr = au_sbend(sb) + 1; ++ nbr = au_sbbot(sb) + 1; + if (nbr < 2) + nbr = 2; /* initial allocate for 2 branches */ + fidir = kzalloc(au_fidir_sz(nbr), GFP_NOFS); @@ -11999,10 +13152,8 @@ diff -Nurp linux-4.4.3/fs/aufs/finfo.c linux-4.4.3-aufs/fs/aufs/finfo.c +void au_fi_init_once(void *_finfo) +{ + struct au_finfo *finfo = _finfo; -+ static struct lock_class_key aufs_fi; + + au_rw_init(&finfo->fi_rwsem); -+ au_rw_class(&finfo->fi_rwsem, &aufs_fi); +} + +int au_finfo_init(struct file *file, struct au_fidir *fidir) @@ -12019,11 +13170,6 @@ diff -Nurp linux-4.4.3/fs/aufs/finfo.c linux-4.4.3-aufs/fs/aufs/finfo.c + + err = 0; + au_nfiles_inc(dentry->d_sb); -+ /* verbose coding for lock class name */ -+ if (!fidir) -+ au_rw_class(&finfo->fi_rwsem, au_lc_key + AuLcNonDir_FIINFO); -+ else -+ au_rw_class(&finfo->fi_rwsem, au_lc_key + AuLcDir_FIINFO); + au_rw_write_lock(&finfo->fi_rwsem); + finfo->fi_btop = -1; + finfo->fi_hdir = fidir; @@ -12035,772 +13181,11 @@ diff -Nurp linux-4.4.3/fs/aufs/finfo.c linux-4.4.3-aufs/fs/aufs/finfo.c +out: + return err; +} -diff -Nurp linux-4.4.3/fs/aufs/f_op.c linux-4.4.3-aufs/fs/aufs/f_op.c ---- linux-4.4.3/fs/aufs/f_op.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/f_op.c 2016-03-02 17:59:37.365498717 +0200 -@@ -0,0 +1,759 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ */ -+ -+/* -+ * file and vm operations -+ */ -+ -+#include -+#include -+#include -+#include -+#include "aufs.h" -+ -+int au_do_open_nondir(struct file *file, int flags, struct file *h_file) -+{ -+ int err; -+ aufs_bindex_t bindex; -+ struct dentry *dentry, *h_dentry; -+ struct au_finfo *finfo; -+ struct inode *h_inode; -+ -+ FiMustWriteLock(file); -+ -+ err = 0; -+ dentry = file->f_path.dentry; -+ AuDebugOn(IS_ERR_OR_NULL(dentry)); -+ finfo = au_fi(file); -+ memset(&finfo->fi_htop, 0, sizeof(finfo->fi_htop)); -+ atomic_set(&finfo->fi_mmapped, 0); -+ bindex = au_dbstart(dentry); -+ if (!h_file) { -+ h_dentry = au_h_dptr(dentry, bindex); -+ err = vfsub_test_mntns(file->f_path.mnt, h_dentry->d_sb); -+ if (unlikely(err)) -+ goto out; -+ h_file = au_h_open(dentry, bindex, flags, file, /*force_wr*/0); -+ } else { -+ h_dentry = h_file->f_path.dentry; -+ err = vfsub_test_mntns(file->f_path.mnt, h_dentry->d_sb); -+ if (unlikely(err)) -+ goto out; -+ get_file(h_file); -+ } -+ if (IS_ERR(h_file)) -+ err = PTR_ERR(h_file); -+ else { -+ if ((flags & __O_TMPFILE) -+ && !(flags & O_EXCL)) { -+ h_inode = file_inode(h_file); -+ spin_lock(&h_inode->i_lock); -+ h_inode->i_state |= I_LINKABLE; -+ spin_unlock(&h_inode->i_lock); -+ } -+ au_set_fbstart(file, bindex); -+ au_set_h_fptr(file, bindex, h_file); -+ au_update_figen(file); -+ /* todo: necessary? */ -+ /* file->f_ra = h_file->f_ra; */ -+ } -+ -+out: -+ return err; -+} -+ -+static int aufs_open_nondir(struct inode *inode __maybe_unused, -+ struct file *file) -+{ -+ int err; -+ struct super_block *sb; -+ struct au_do_open_args args = { -+ .open = au_do_open_nondir -+ }; -+ -+ AuDbg("%pD, f_flags 0x%x, f_mode 0x%x\n", -+ file, vfsub_file_flags(file), file->f_mode); -+ -+ sb = file->f_path.dentry->d_sb; -+ si_read_lock(sb, AuLock_FLUSH); -+ err = au_do_open(file, &args); -+ si_read_unlock(sb); -+ return err; -+} -+ -+int aufs_release_nondir(struct inode *inode __maybe_unused, struct file *file) -+{ -+ struct au_finfo *finfo; -+ aufs_bindex_t bindex; -+ -+ finfo = au_fi(file); -+ au_sphl_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); -+ -+ au_finfo_fin(file); -+ return 0; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int au_do_flush_nondir(struct file *file, fl_owner_t id) -+{ -+ int err; -+ struct file *h_file; -+ -+ err = 0; -+ h_file = au_hf_top(file); -+ if (h_file) -+ err = vfsub_flush(h_file, id); -+ return err; -+} -+ -+static int aufs_flush_nondir(struct file *file, fl_owner_t id) -+{ -+ return au_do_flush(file, id, au_do_flush_nondir); -+} -+ -+/* ---------------------------------------------------------------------- */ -+/* -+ * read and write functions acquire [fdi]_rwsem once, but release before -+ * mmap_sem. This is because to stop a race condition between mmap(2). -+ * Releasing these aufs-rwsem should be safe, no branch-mamagement (by keeping -+ * si_rwsem), no harmful copy-up should happen. Actually copy-up may happen in -+ * read functions after [fdi]_rwsem are released, but it should be harmless. -+ */ -+ -+/* Callers should call au_read_post() or fput() in the end */ -+struct file *au_read_pre(struct file *file, int keep_fi) -+{ -+ struct file *h_file; -+ int err; -+ -+ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/0); -+ if (!err) { -+ di_read_unlock(file->f_path.dentry, AuLock_IR); -+ h_file = au_hf_top(file); -+ get_file(h_file); -+ if (!keep_fi) -+ fi_read_unlock(file); -+ } else -+ h_file = ERR_PTR(err); -+ -+ return h_file; -+} -+ -+static void au_read_post(struct inode *inode, struct file *h_file) -+{ -+ /* update without lock, I don't think it a problem */ -+ fsstack_copy_attr_atime(inode, file_inode(h_file)); -+ fput(h_file); -+} -+ -+struct au_write_pre { -+ blkcnt_t blks; -+ aufs_bindex_t bstart; -+}; -+ -+/* -+ * return with iinfo is write-locked -+ * callers should call au_write_post() or iinfo_write_unlock() + fput() in the -+ * end -+ */ -+static struct file *au_write_pre(struct file *file, int do_ready, -+ struct au_write_pre *wpre) -+{ -+ struct file *h_file; -+ struct dentry *dentry; -+ int err; -+ struct au_pin pin; -+ -+ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1); -+ h_file = ERR_PTR(err); -+ if (unlikely(err)) -+ goto out; -+ -+ dentry = file->f_path.dentry; -+ if (do_ready) { -+ err = au_ready_to_write(file, -1, &pin); -+ if (unlikely(err)) { -+ h_file = ERR_PTR(err); -+ di_write_unlock(dentry); -+ goto out_fi; -+ } -+ } -+ -+ di_downgrade_lock(dentry, /*flags*/0); -+ if (wpre) -+ wpre->bstart = au_fbstart(file); -+ h_file = au_hf_top(file); -+ get_file(h_file); -+ if (wpre) -+ wpre->blks = file_inode(h_file)->i_blocks; -+ if (do_ready) -+ au_unpin(&pin); -+ di_read_unlock(dentry, /*flags*/0); -+ -+out_fi: -+ fi_write_unlock(file); -+out: -+ return h_file; -+} -+ -+static void au_write_post(struct inode *inode, struct file *h_file, -+ struct au_write_pre *wpre, ssize_t written) -+{ -+ struct inode *h_inode; -+ -+ au_cpup_attr_timesizes(inode); -+ AuDebugOn(au_ibstart(inode) != wpre->bstart); -+ 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->bstart, -+ /*force*/h_inode->i_blocks > wpre->blks); -+} -+ -+static ssize_t aufs_read(struct file *file, char __user *buf, size_t count, -+ loff_t *ppos) -+{ -+ ssize_t err; -+ struct inode *inode; -+ struct file *h_file; -+ struct super_block *sb; -+ -+ inode = file_inode(file); -+ sb = inode->i_sb; -+ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); -+ -+ h_file = au_read_pre(file, /*keep_fi*/0); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out; -+ -+ /* filedata may be obsoleted by concurrent copyup, but no problem */ -+ err = vfsub_read_u(h_file, buf, count, ppos); -+ /* todo: necessary? */ -+ /* file->f_ra = h_file->f_ra; */ -+ au_read_post(inode, h_file); -+ -+out: -+ si_read_unlock(sb); -+ return err; -+} -+ -+/* -+ * todo: very ugly -+ * it locks both of i_mutex and si_rwsem for read in safe. -+ * if the plink maintenance mode continues forever (that is the problem), -+ * may loop forever. -+ */ -+static void au_mtx_and_read_lock(struct inode *inode) -+{ -+ int err; -+ struct super_block *sb = inode->i_sb; -+ -+ while (1) { -+ mutex_lock(&inode->i_mutex); -+ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); -+ if (!err) -+ break; -+ mutex_unlock(&inode->i_mutex); -+ si_read_lock(sb, AuLock_NOPLMW); -+ si_read_unlock(sb); -+ } -+} -+ -+static ssize_t aufs_write(struct file *file, const char __user *ubuf, -+ size_t count, loff_t *ppos) -+{ -+ ssize_t err; -+ struct au_write_pre wpre; -+ struct inode *inode; -+ struct file *h_file; -+ char __user *buf = (char __user *)ubuf; -+ -+ inode = file_inode(file); -+ au_mtx_and_read_lock(inode); -+ -+ h_file = au_write_pre(file, /*do_ready*/1, &wpre); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out; -+ -+ err = vfsub_write_u(h_file, buf, count, ppos); -+ au_write_post(inode, h_file, &wpre, err); -+ -+out: -+ si_read_unlock(inode->i_sb); -+ mutex_unlock(&inode->i_mutex); -+ return err; -+} -+ -+static ssize_t au_do_iter(struct file *h_file, int rw, struct kiocb *kio, -+ struct iov_iter *iov_iter) -+{ -+ ssize_t err; -+ struct file *file; -+ ssize_t (*iter)(struct kiocb *, struct iov_iter *); -+ -+ err = security_file_permission(h_file, rw); -+ if (unlikely(err)) -+ goto out; -+ -+ err = -ENOSYS; -+ iter = NULL; -+ if (rw == MAY_READ) -+ iter = h_file->f_op->read_iter; -+ else if (rw == MAY_WRITE) -+ iter = h_file->f_op->write_iter; -+ -+ file = kio->ki_filp; -+ kio->ki_filp = h_file; -+ if (iter) { -+ lockdep_off(); -+ err = iter(kio, iov_iter); -+ lockdep_on(); -+ } else -+ /* currently there is no such fs */ -+ WARN_ON_ONCE(1); -+ kio->ki_filp = file; -+ -+out: -+ return err; -+} -+ -+static ssize_t aufs_read_iter(struct kiocb *kio, struct iov_iter *iov_iter) -+{ -+ ssize_t err; -+ struct file *file, *h_file; -+ struct inode *inode; -+ struct super_block *sb; -+ -+ file = kio->ki_filp; -+ inode = file_inode(file); -+ sb = inode->i_sb; -+ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); -+ -+ h_file = au_read_pre(file, /*keep_fi*/0); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out; -+ -+ err = au_do_iter(h_file, MAY_READ, kio, iov_iter); -+ /* todo: necessary? */ -+ /* file->f_ra = h_file->f_ra; */ -+ au_read_post(inode, h_file); -+ -+out: -+ si_read_unlock(sb); -+ return err; -+} -+ -+static ssize_t aufs_write_iter(struct kiocb *kio, struct iov_iter *iov_iter) -+{ -+ ssize_t err; -+ struct au_write_pre wpre; -+ struct inode *inode; -+ struct file *file, *h_file; -+ -+ file = kio->ki_filp; -+ inode = file_inode(file); -+ au_mtx_and_read_lock(inode); -+ -+ h_file = au_write_pre(file, /*do_ready*/1, &wpre); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out; -+ -+ err = au_do_iter(h_file, MAY_WRITE, kio, iov_iter); -+ au_write_post(inode, h_file, &wpre, err); -+ -+out: -+ si_read_unlock(inode->i_sb); -+ mutex_unlock(&inode->i_mutex); -+ return err; -+} -+ -+static ssize_t aufs_splice_read(struct file *file, loff_t *ppos, -+ struct pipe_inode_info *pipe, size_t len, -+ unsigned int flags) -+{ -+ ssize_t err; -+ struct file *h_file; -+ struct inode *inode; -+ struct super_block *sb; -+ -+ inode = file_inode(file); -+ sb = inode->i_sb; -+ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); -+ -+ h_file = au_read_pre(file, /*keep_fi*/1); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out; -+ -+ if (au_test_loopback_kthread()) { -+ au_warn_loopback(h_file->f_path.dentry->d_sb); -+ if (file->f_mapping != h_file->f_mapping) { -+ file->f_mapping = h_file->f_mapping; -+ smp_mb(); /* unnecessary? */ -+ } -+ } -+ fi_read_unlock(file); -+ -+ err = vfsub_splice_to(h_file, ppos, pipe, len, flags); -+ /* todo: necessasry? */ -+ /* file->f_ra = h_file->f_ra; */ -+ au_read_post(inode, h_file); -+ -+out: -+ si_read_unlock(sb); -+ return err; -+} -+ -+static ssize_t -+aufs_splice_write(struct pipe_inode_info *pipe, struct file *file, loff_t *ppos, -+ size_t len, unsigned int flags) -+{ -+ ssize_t err; -+ struct au_write_pre wpre; -+ struct inode *inode; -+ struct file *h_file; -+ -+ inode = file_inode(file); -+ au_mtx_and_read_lock(inode); -+ -+ h_file = au_write_pre(file, /*do_ready*/1, &wpre); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out; -+ -+ err = vfsub_splice_from(pipe, h_file, ppos, len, flags); -+ au_write_post(inode, h_file, &wpre, err); -+ -+out: -+ si_read_unlock(inode->i_sb); -+ mutex_unlock(&inode->i_mutex); -+ return err; -+} -+ -+static long aufs_fallocate(struct file *file, int mode, loff_t offset, -+ loff_t len) -+{ -+ long err; -+ struct au_write_pre wpre; -+ struct inode *inode; -+ struct file *h_file; -+ -+ inode = file_inode(file); -+ au_mtx_and_read_lock(inode); -+ -+ h_file = au_write_pre(file, /*do_ready*/1, &wpre); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out; -+ -+ lockdep_off(); -+ err = vfs_fallocate(h_file, mode, offset, len); -+ lockdep_on(); -+ au_write_post(inode, h_file, &wpre, /*written*/1); -+ -+out: -+ si_read_unlock(inode->i_sb); -+ mutex_unlock(&inode->i_mutex); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * The locking order around current->mmap_sem. -+ * - in most and regular cases -+ * file I/O syscall -- aufs_read() or something -+ * -- si_rwsem for read -- mmap_sem -+ * (Note that [fdi]i_rwsem are released before mmap_sem). -+ * - in mmap case -+ * mmap(2) -- mmap_sem -- aufs_mmap() -- si_rwsem for read -- [fdi]i_rwsem -+ * This AB-BA order is definitly bad, but is not a problem since "si_rwsem for -+ * read" allows muliple processes to acquire it and [fdi]i_rwsem are not held in -+ * file I/O. Aufs needs to stop lockdep in aufs_mmap() though. -+ * It means that when aufs acquires si_rwsem for write, the process should never -+ * acquire mmap_sem. -+ * -+ * Actually aufs_iterate() holds [fdi]i_rwsem before mmap_sem, but this is not a -+ * problem either since any directory is not able to be mmap-ed. -+ * The similar scenario is applied to aufs_readlink() too. -+ */ -+ -+#if 0 /* stop calling security_file_mmap() */ -+/* cf. linux/include/linux/mman.h: calc_vm_prot_bits() */ -+#define AuConv_VM_PROT(f, b) _calc_vm_trans(f, VM_##b, PROT_##b) -+ -+static unsigned long au_arch_prot_conv(unsigned long flags) -+{ -+ /* currently ppc64 only */ -+#ifdef CONFIG_PPC64 -+ /* cf. linux/arch/powerpc/include/asm/mman.h */ -+ AuDebugOn(arch_calc_vm_prot_bits(-1) != VM_SAO); -+ return AuConv_VM_PROT(flags, SAO); -+#else -+ AuDebugOn(arch_calc_vm_prot_bits(-1)); -+ return 0; -+#endif -+} -+ -+static unsigned long au_prot_conv(unsigned long flags) -+{ -+ return AuConv_VM_PROT(flags, READ) -+ | AuConv_VM_PROT(flags, WRITE) -+ | AuConv_VM_PROT(flags, EXEC) -+ | au_arch_prot_conv(flags); -+} -+ -+/* cf. linux/include/linux/mman.h: calc_vm_flag_bits() */ -+#define AuConv_VM_MAP(f, b) _calc_vm_trans(f, VM_##b, MAP_##b) -+ -+static unsigned long au_flag_conv(unsigned long flags) -+{ -+ return AuConv_VM_MAP(flags, GROWSDOWN) -+ | AuConv_VM_MAP(flags, DENYWRITE) -+ | AuConv_VM_MAP(flags, LOCKED); -+} -+#endif -+ -+static int aufs_mmap(struct file *file, struct vm_area_struct *vma) -+{ -+ int err; -+ const unsigned char wlock -+ = (file->f_mode & FMODE_WRITE) && (vma->vm_flags & VM_SHARED); -+ struct super_block *sb; -+ struct file *h_file; -+ struct inode *inode; -+ -+ AuDbgVmRegion(file, vma); -+ -+ inode = file_inode(file); -+ sb = inode->i_sb; -+ lockdep_off(); -+ si_read_lock(sb, AuLock_NOPLMW); -+ -+ h_file = au_write_pre(file, wlock, /*wpre*/NULL); -+ lockdep_on(); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out; -+ -+ err = 0; -+ au_set_mmapped(file); -+ au_vm_file_reset(vma, h_file); -+ /* -+ * we cannot call security_mmap_file() here since it may acquire -+ * mmap_sem or i_mutex. -+ * -+ * err = security_mmap_file(h_file, au_prot_conv(vma->vm_flags), -+ * au_flag_conv(vma->vm_flags)); -+ */ -+ if (!err) -+ err = h_file->f_op->mmap(h_file, vma); -+ if (!err) { -+ au_vm_prfile_set(vma, file); -+ fsstack_copy_attr_atime(inode, file_inode(h_file)); -+ goto out_fput; /* success */ -+ } -+ au_unset_mmapped(file); -+ au_vm_file_reset(vma, file); -+ -+out_fput: -+ lockdep_off(); -+ ii_write_unlock(inode); -+ lockdep_on(); -+ fput(h_file); -+out: -+ lockdep_off(); -+ si_read_unlock(sb); -+ lockdep_on(); -+ AuTraceErr(err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int aufs_fsync_nondir(struct file *file, loff_t start, loff_t end, -+ int datasync) -+{ -+ int err; -+ struct au_write_pre wpre; -+ struct inode *inode; -+ struct file *h_file; -+ -+ err = 0; /* -EBADF; */ /* posix? */ -+ if (unlikely(!(file->f_mode & FMODE_WRITE))) -+ goto out; -+ -+ inode = file_inode(file); -+ au_mtx_and_read_lock(inode); -+ -+ h_file = au_write_pre(file, /*do_ready*/1, &wpre); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out_unlock; -+ -+ err = vfsub_fsync(h_file, &h_file->f_path, datasync); -+ au_write_post(inode, h_file, &wpre, /*written*/0); -+ -+out_unlock: -+ si_read_unlock(inode->i_sb); -+ mutex_unlock(&inode->i_mutex); -+out: -+ return err; -+} -+ -+/* no one supports this operation, currently */ -+#if 0 -+static int aufs_aio_fsync_nondir(struct kiocb *kio, int datasync) -+{ -+ int err; -+ struct au_write_pre wpre; -+ struct inode *inode; -+ struct file *file, *h_file; -+ -+ err = 0; /* -EBADF; */ /* posix? */ -+ if (unlikely(!(file->f_mode & FMODE_WRITE))) -+ goto out; -+ -+ file = kio->ki_filp; -+ inode = file_inode(file); -+ au_mtx_and_read_lock(inode); -+ -+ h_file = au_write_pre(file, /*do_ready*/1, &wpre); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out_unlock; -+ -+ err = -ENOSYS; -+ h_file = au_hf_top(file); -+ if (h_file->f_op->aio_fsync) { -+ struct mutex *h_mtx; -+ -+ h_mtx = &file_inode(h_file)->i_mutex; -+ if (!is_sync_kiocb(kio)) { -+ get_file(h_file); -+ fput(file); -+ } -+ kio->ki_filp = h_file; -+ err = h_file->f_op->aio_fsync(kio, datasync); -+ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); -+ if (!err) -+ vfsub_update_h_iattr(&h_file->f_path, /*did*/NULL); -+ /*ignore*/ -+ mutex_unlock(h_mtx); -+ } -+ au_write_post(inode, h_file, &wpre, /*written*/0); -+ -+out_unlock: -+ si_read_unlock(inode->sb); -+ mutex_unlock(&inode->i_mutex); -+out: -+ return err; -+} -+#endif -+ -+static int aufs_fasync(int fd, struct file *file, int flag) -+{ -+ int err; -+ struct file *h_file; -+ struct super_block *sb; -+ -+ sb = file->f_path.dentry->d_sb; -+ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); -+ -+ h_file = au_read_pre(file, /*keep_fi*/0); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out; -+ -+ if (h_file->f_op->fasync) -+ err = h_file->f_op->fasync(fd, h_file, flag); -+ fput(h_file); /* instead of au_read_post() */ -+ -+out: -+ si_read_unlock(sb); -+ return err; -+} -+ -+static int aufs_setfl(struct file *file, unsigned long arg) -+{ -+ int err; -+ struct file *h_file; -+ struct super_block *sb; -+ -+ sb = file->f_path.dentry->d_sb; -+ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); -+ -+ h_file = au_read_pre(file, /*keep_fi*/0); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out; -+ -+ arg |= vfsub_file_flags(file) & FASYNC; /* stop calling h_file->fasync */ -+ err = setfl(/*unused fd*/-1, h_file, arg); -+ fput(h_file); /* instead of au_read_post() */ -+ -+out: -+ si_read_unlock(sb); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* no one supports this operation, currently */ -+#if 0 -+static ssize_t aufs_sendpage(struct file *file, struct page *page, int offset, -+ size_t len, loff_t *pos, int more) -+{ -+} -+#endif -+ -+/* ---------------------------------------------------------------------- */ -+ -+const struct file_operations aufs_file_fop = { -+ .owner = THIS_MODULE, -+ -+ .llseek = default_llseek, -+ -+ .read = aufs_read, -+ .write = aufs_write, -+ .read_iter = aufs_read_iter, -+ .write_iter = aufs_write_iter, -+ -+#ifdef CONFIG_AUFS_POLL -+ .poll = aufs_poll, -+#endif -+ .unlocked_ioctl = aufs_ioctl_nondir, -+#ifdef CONFIG_COMPAT -+ .compat_ioctl = aufs_compat_ioctl_nondir, -+#endif -+ .mmap = aufs_mmap, -+ .open = aufs_open_nondir, -+ .flush = aufs_flush_nondir, -+ .release = aufs_release_nondir, -+ .fsync = aufs_fsync_nondir, -+ /* .aio_fsync = aufs_aio_fsync_nondir, */ -+ .fasync = aufs_fasync, -+ /* .sendpage = aufs_sendpage, */ -+ .setfl = aufs_setfl, -+ .splice_write = aufs_splice_write, -+ .splice_read = aufs_splice_read, -+#if 0 -+ .aio_splice_write = aufs_aio_splice_write, -+ .aio_splice_read = aufs_aio_splice_read, -+#endif -+ .fallocate = aufs_fallocate -+}; -diff -Nurp linux-4.4.3/fs/aufs/fstype.h linux-4.4.3-aufs/fs/aufs/fstype.h ---- linux-4.4.3/fs/aufs/fstype.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/fstype.h 2016-03-02 17:59:37.366498724 +0200 +diff --git a/fs/aufs/fstype.h b/fs/aufs/fstype.h +new file mode 100644 +index 0000000..725b2ff +--- /dev/null ++++ b/fs/aufs/fstype.h @@ -0,0 +1,387 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima @@ -13189,10 +13574,12 @@ diff -Nurp linux-4.4.3/fs/aufs/fstype.h linux-4.4.3-aufs/fs/aufs/fstype.h + +#endif /* __KERNEL__ */ +#endif /* __AUFS_FSTYPE_H__ */ -diff -Nurp linux-4.4.3/fs/aufs/hfsnotify.c linux-4.4.3-aufs/fs/aufs/hfsnotify.c ---- linux-4.4.3/fs/aufs/hfsnotify.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/hfsnotify.c 2016-03-02 17:59:37.366498724 +0200 -@@ -0,0 +1,275 @@ +diff --git a/fs/aufs/hfsnotify.c b/fs/aufs/hfsnotify.c +new file mode 100644 +index 0000000..07b4ecc +--- /dev/null ++++ b/fs/aufs/hfsnotify.c +@@ -0,0 +1,274 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima + */ @@ -13245,8 +13632,6 @@ diff -Nurp linux-4.4.3/fs/aufs/hfsnotify.c linux-4.4.3-aufs/fs/aufs/hfsnotify.c + lockdep_off(); + err = fsnotify_add_mark(mark, br->br_hfsn->hfsn_group, hinode->hi_inode, + /*mnt*/NULL, /*allow_dups*/1); -+ /* even if err */ -+ fsnotify_put_mark(mark); + lockdep_on(); + + return err; @@ -13268,6 +13653,7 @@ diff -Nurp linux-4.4.3/fs/aufs/hfsnotify.c linux-4.4.3-aufs/fs/aufs/hfsnotify.c + spin_unlock(&mark->lock); + lockdep_off(); + fsnotify_destroy_mark(mark, group); ++ fsnotify_put_mark(mark); + fsnotify_put_group(group); + lockdep_on(); + @@ -13468,9 +13854,11 @@ diff -Nurp linux-4.4.3/fs/aufs/hfsnotify.c linux-4.4.3-aufs/fs/aufs/hfsnotify.c + .fin_br = au_hfsn_fin_br, + .init_br = au_hfsn_init_br +}; -diff -Nurp linux-4.4.3/fs/aufs/hfsplus.c linux-4.4.3-aufs/fs/aufs/hfsplus.c ---- linux-4.4.3/fs/aufs/hfsplus.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/hfsplus.c 2016-03-02 17:59:37.366498724 +0200 +diff --git a/fs/aufs/hfsplus.c b/fs/aufs/hfsplus.c +new file mode 100644 +index 0000000..145c6ac +--- /dev/null ++++ b/fs/aufs/hfsplus.c @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2010-2016 Junjiro R. Okajima @@ -13515,9 +13903,11 @@ diff -Nurp linux-4.4.3/fs/aufs/hfsplus.c linux-4.4.3-aufs/fs/aufs/hfsplus.c + au_sbr_put(dentry->d_sb, bindex); + } +} -diff -Nurp linux-4.4.3/fs/aufs/hnotify.c linux-4.4.3-aufs/fs/aufs/hnotify.c ---- linux-4.4.3/fs/aufs/hnotify.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/hnotify.c 2016-03-02 17:59:37.367498731 +0200 +diff --git a/fs/aufs/hnotify.c b/fs/aufs/hnotify.c +new file mode 100644 +index 0000000..3016c5e +--- /dev/null ++++ b/fs/aufs/hnotify.c @@ -0,0 +1,697 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima @@ -13581,17 +13971,17 @@ diff -Nurp linux-4.4.3/fs/aufs/hnotify.c linux-4.4.3-aufs/fs/aufs/hnotify.c + +void au_hn_reset(struct inode *inode, unsigned int flags) +{ -+ aufs_bindex_t bindex, bend; ++ aufs_bindex_t bindex, bbot; + struct inode *hi; + struct dentry *iwhdentry; + -+ bend = au_ibend(inode); -+ for (bindex = au_ibstart(inode); bindex <= bend; bindex++) { ++ bbot = au_ibbot(inode); ++ for (bindex = au_ibtop(inode); bindex <= bbot; bindex++) { + hi = au_h_iptr(inode, bindex); + if (!hi) + continue; + -+ /* mutex_lock_nested(&hi->i_mutex, AuLsc_I_CHILD); */ ++ /* inode_lock_nested(hi, AuLsc_I_CHILD); */ + iwhdentry = au_hi_wh(inode, bindex); + if (iwhdentry) + dget(iwhdentry); @@ -13601,7 +13991,7 @@ diff -Nurp linux-4.4.3/fs/aufs/hnotify.c linux-4.4.3-aufs/fs/aufs/hnotify.c + flags & ~AuHi_XINO); + iput(hi); + dput(iwhdentry); -+ /* mutex_unlock(&hi->i_mutex); */ ++ /* inode_unlock(hi); */ + } +} + @@ -13610,7 +14000,7 @@ diff -Nurp linux-4.4.3/fs/aufs/hnotify.c linux-4.4.3-aufs/fs/aufs/hnotify.c +static int hn_xino(struct inode *inode, struct inode *h_inode) +{ + int err; -+ aufs_bindex_t bindex, bend, bfound, bstart; ++ aufs_bindex_t bindex, bbot, bfound, btop; + struct inode *h_i; + + err = 0; @@ -13620,15 +14010,15 @@ diff -Nurp linux-4.4.3/fs/aufs/hnotify.c linux-4.4.3-aufs/fs/aufs/hnotify.c + } + + bfound = -1; -+ bend = au_ibend(inode); -+ bstart = au_ibstart(inode); ++ bbot = au_ibbot(inode); ++ btop = au_ibtop(inode); +#if 0 /* reserved for future use */ -+ if (bindex == bend) { ++ if (bindex == bbot) { + /* keep this ino in rename case */ + goto out; + } +#endif -+ for (bindex = bstart; bindex <= bend; bindex++) ++ for (bindex = btop; bindex <= bbot; bindex++) + if (au_h_iptr(inode, bindex) == h_inode) { + bfound = bindex; + break; @@ -13636,7 +14026,7 @@ diff -Nurp linux-4.4.3/fs/aufs/hnotify.c linux-4.4.3-aufs/fs/aufs/hnotify.c + if (bfound < 0) + goto out; + -+ for (bindex = bstart; bindex <= bend; bindex++) { ++ for (bindex = btop; bindex <= bbot; bindex++) { + h_i = au_h_iptr(inode, bindex); + if (!h_i) + continue; @@ -13830,11 +14220,11 @@ diff -Nurp linux-4.4.3/fs/aufs/hnotify.c linux-4.4.3-aufs/fs/aufs/hnotify.c + if (au_ftest_hnjob(a->flags, TRYXINO0) + && a->inode + && a->h_inode) { -+ mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD); ++ inode_lock_nested(a->h_inode, AuLsc_I_CHILD); + if (!a->h_inode->i_nlink + && !(a->h_inode->i_state & I_LINKABLE)) + hn_xino(a->inode, a->h_inode); /* ignore this error */ -+ mutex_unlock(&a->h_inode->i_mutex); ++ inode_unlock(a->h_inode); + } + + /* make the generation obsolete */ @@ -13941,7 +14331,7 @@ diff -Nurp linux-4.4.3/fs/aufs/hnotify.c linux-4.4.3-aufs/fs/aufs/hnotify.c +{ + struct au_hnotify_args *a = _args; + struct super_block *sb; -+ aufs_bindex_t bindex, bend, bfound; ++ aufs_bindex_t bindex, bbot, bfound; + unsigned char xino, try_iput; + int err; + struct inode *inode; @@ -13972,8 +14362,8 @@ diff -Nurp linux-4.4.3/fs/aufs/hnotify.c linux-4.4.3-aufs/fs/aufs/hnotify.c + + ii_read_lock_parent(a->dir); + bfound = -1; -+ bend = au_ibend(a->dir); -+ for (bindex = au_ibstart(a->dir); bindex <= bend; bindex++) ++ bbot = au_ibbot(a->dir); ++ for (bindex = au_ibtop(a->dir); bindex <= bbot; bindex++) + if (au_h_iptr(a->dir, bindex) == a->h_dir) { + bfound = bindex; + break; @@ -14216,10 +14606,3840 @@ diff -Nurp linux-4.4.3/fs/aufs/hnotify.c linux-4.4.3-aufs/fs/aufs/hnotify.c + if (au_cachep[AuCache_HNOTIFY]) + au_hn_destroy_cache(); +} -diff -Nurp linux-4.4.3/fs/aufs/iinfo.c linux-4.4.3-aufs/fs/aufs/iinfo.c ---- linux-4.4.3/fs/aufs/iinfo.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/iinfo.c 2016-03-02 17:59:37.368498738 +0200 -@@ -0,0 +1,264 @@ +diff --git a/fs/aufs/i_op.c b/fs/aufs/i_op.c +new file mode 100644 +index 0000000..1572a7d +--- /dev/null ++++ b/fs/aufs/i_op.c +@@ -0,0 +1,1394 @@ ++/* ++ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ */ ++ ++/* ++ * inode operations (except add/del/rename) ++ */ ++ ++#include ++#include ++#include ++#include ++#include "aufs.h" ++ ++static int h_permission(struct inode *h_inode, int mask, ++ struct vfsmount *h_mnt, int brperm) ++{ ++ int err; ++ const unsigned char write_mask = !!(mask & (MAY_WRITE | MAY_APPEND)); ++ ++ err = -EACCES; ++ if ((write_mask && IS_IMMUTABLE(h_inode)) ++ || ((mask & MAY_EXEC) ++ && S_ISREG(h_inode->i_mode) ++ && ((h_mnt->mnt_flags & MNT_NOEXEC) ++ || !(h_inode->i_mode & S_IXUGO)))) ++ goto out; ++ ++ /* ++ * - skip the lower fs test in the case of write to ro branch. ++ * - nfs dir permission write check is optimized, but a policy for ++ * link/rename requires a real check. ++ * - nfs always sets MS_POSIXACL regardless its mount option 'noacl.' ++ * in this case, generic_permission() returns -EOPNOTSUPP. ++ */ ++ if ((write_mask && !au_br_writable(brperm)) ++ || (au_test_nfs(h_inode->i_sb) && S_ISDIR(h_inode->i_mode) ++ && write_mask && !(mask & MAY_READ)) ++ || !h_inode->i_op->permission) { ++ /* AuLabel(generic_permission); */ ++ /* AuDbg("get_acl %pf\n", h_inode->i_op->get_acl); */ ++ err = generic_permission(h_inode, mask); ++ if (err == -EOPNOTSUPP && au_test_nfs_noacl(h_inode)) ++ err = h_inode->i_op->permission(h_inode, mask); ++ AuTraceErr(err); ++ } else { ++ /* AuLabel(h_inode->permission); */ ++ err = h_inode->i_op->permission(h_inode, mask); ++ AuTraceErr(err); ++ } ++ ++ if (!err) ++ err = devcgroup_inode_permission(h_inode, mask); ++ if (!err) ++ err = security_inode_permission(h_inode, mask); ++ ++#if 0 ++ if (!err) { ++ /* todo: do we need to call ima_path_check()? */ ++ struct path h_path = { ++ .dentry = ++ .mnt = h_mnt ++ }; ++ err = ima_path_check(&h_path, ++ mask & (MAY_READ | MAY_WRITE | MAY_EXEC), ++ IMA_COUNT_LEAVE); ++ } ++#endif ++ ++out: ++ return err; ++} ++ ++static int aufs_permission(struct inode *inode, int mask) ++{ ++ int err; ++ aufs_bindex_t bindex, bbot; ++ const unsigned char isdir = !!S_ISDIR(inode->i_mode), ++ write_mask = !!(mask & (MAY_WRITE | MAY_APPEND)); ++ struct inode *h_inode; ++ struct super_block *sb; ++ struct au_branch *br; ++ ++ /* todo: support rcu-walk? */ ++ if (mask & MAY_NOT_BLOCK) ++ return -ECHILD; ++ ++ sb = inode->i_sb; ++ si_read_lock(sb, AuLock_FLUSH); ++ ii_read_lock_child(inode); ++#if 0 ++ err = au_iigen_test(inode, au_sigen(sb)); ++ if (unlikely(err)) ++ goto out; ++#endif ++ ++ if (!isdir ++ || write_mask ++ || au_opt_test(au_mntflags(sb), DIRPERM1)) { ++ err = au_busy_or_stale(); ++ h_inode = au_h_iptr(inode, au_ibtop(inode)); ++ if (unlikely(!h_inode ++ || (h_inode->i_mode & S_IFMT) ++ != (inode->i_mode & S_IFMT))) ++ goto out; ++ ++ err = 0; ++ bindex = au_ibtop(inode); ++ br = au_sbr(sb, bindex); ++ err = h_permission(h_inode, mask, au_br_mnt(br), br->br_perm); ++ if (write_mask ++ && !err ++ && !special_file(h_inode->i_mode)) { ++ /* test whether the upper writable branch exists */ ++ err = -EROFS; ++ for (; bindex >= 0; bindex--) ++ if (!au_br_rdonly(au_sbr(sb, bindex))) { ++ err = 0; ++ break; ++ } ++ } ++ goto out; ++ } ++ ++ /* non-write to dir */ ++ err = 0; ++ bbot = au_ibbot(inode); ++ for (bindex = au_ibtop(inode); !err && bindex <= bbot; bindex++) { ++ h_inode = au_h_iptr(inode, bindex); ++ if (h_inode) { ++ err = au_busy_or_stale(); ++ if (unlikely(!S_ISDIR(h_inode->i_mode))) ++ break; ++ ++ br = au_sbr(sb, bindex); ++ err = h_permission(h_inode, mask, au_br_mnt(br), ++ br->br_perm); ++ } ++ } ++ ++out: ++ ii_read_unlock(inode); ++ si_read_unlock(sb); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static struct dentry *aufs_lookup(struct inode *dir, struct dentry *dentry, ++ unsigned int flags) ++{ ++ struct dentry *ret, *parent; ++ struct inode *inode; ++ struct super_block *sb; ++ int err, npositive; ++ ++ IMustLock(dir); ++ ++ /* todo: support rcu-walk? */ ++ ret = ERR_PTR(-ECHILD); ++ if (flags & LOOKUP_RCU) ++ goto out; ++ ++ ret = ERR_PTR(-ENAMETOOLONG); ++ if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) ++ goto out; ++ ++ sb = dir->i_sb; ++ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); ++ ret = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out; ++ ++ err = au_di_init(dentry); ++ ret = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out_si; ++ ++ inode = NULL; ++ npositive = 0; /* suppress a warning */ ++ parent = dentry->d_parent; /* dir inode is locked */ ++ di_read_lock_parent(parent, AuLock_IR); ++ err = au_alive_dir(parent); ++ if (!err) ++ err = au_digen_test(parent, au_sigen(sb)); ++ if (!err) { ++ npositive = au_lkup_dentry(dentry, au_dbtop(parent), ++ /*type*/0); ++ err = npositive; ++ } ++ di_read_unlock(parent, AuLock_IR); ++ ret = ERR_PTR(err); ++ if (unlikely(err < 0)) ++ goto out_unlock; ++ ++ if (npositive) { ++ inode = au_new_inode(dentry, /*must_new*/0); ++ if (IS_ERR(inode)) { ++ ret = (void *)inode; ++ inode = NULL; ++ goto out_unlock; ++ } ++ } ++ ++ if (inode) ++ atomic_inc(&inode->i_count); ++ ret = d_splice_alias(inode, dentry); ++#if 0 ++ if (unlikely(d_need_lookup(dentry))) { ++ spin_lock(&dentry->d_lock); ++ dentry->d_flags &= ~DCACHE_NEED_LOOKUP; ++ spin_unlock(&dentry->d_lock); ++ } else ++#endif ++ if (inode) { ++ if (!IS_ERR(ret)) { ++ iput(inode); ++ if (ret && ret != dentry) ++ ii_write_unlock(inode); ++ } else { ++ ii_write_unlock(inode); ++ iput(inode); ++ inode = NULL; ++ } ++ } ++ ++out_unlock: ++ di_write_unlock(dentry); ++out_si: ++ si_read_unlock(sb); ++out: ++ return ret; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct aopen_node { ++ struct hlist_node hlist; ++ struct file *file, *h_file; ++}; ++ ++static int au_do_aopen(struct inode *inode, struct file *file) ++{ ++ struct au_sphlhead *aopen; ++ struct aopen_node *node; ++ struct au_do_open_args args = { ++ .no_lock = 1, ++ .open = au_do_open_nondir ++ }; ++ ++ aopen = &au_sbi(inode->i_sb)->si_aopen; ++ spin_lock(&aopen->spin); ++ hlist_for_each_entry(node, &aopen->head, hlist) ++ if (node->file == file) { ++ args.h_file = node->h_file; ++ break; ++ } ++ spin_unlock(&aopen->spin); ++ /* AuDebugOn(!args.h_file); */ ++ ++ return au_do_open(file, &args); ++} ++ ++static int aufs_atomic_open(struct inode *dir, struct dentry *dentry, ++ struct file *file, unsigned int open_flag, ++ umode_t create_mode, int *opened) ++{ ++ int err, h_opened = *opened; ++ struct dentry *parent; ++ struct dentry *d; ++ struct au_sphlhead *aopen; ++ struct vfsub_aopen_args args = { ++ .open_flag = open_flag, ++ .create_mode = create_mode, ++ .opened = &h_opened ++ }; ++ struct aopen_node aopen_node = { ++ .file = file ++ }; ++ ++ IMustLock(dir); ++ AuDbg("open_flag 0x%x\n", open_flag); ++ AuDbgDentry(dentry); ++ ++ err = 0; ++ if (!au_di(dentry)) { ++ d = aufs_lookup(dir, dentry, /*flags*/0); ++ if (IS_ERR(d)) { ++ err = PTR_ERR(d); ++ goto out; ++ } else if (d) { ++ /* ++ * obsoleted dentry found. ++ * another error will be returned later. ++ */ ++ d_drop(d); ++ dput(d); ++ AuDbgDentry(d); ++ } ++ AuDbgDentry(dentry); ++ } ++ ++ if (d_is_positive(dentry) ++ || d_unhashed(dentry) ++ || d_unlinked(dentry) ++ || !(open_flag & O_CREAT)) ++ goto out_no_open; ++ ++ err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH | AuLock_GEN); ++ if (unlikely(err)) ++ goto out; ++ ++ parent = dentry->d_parent; /* dir is locked */ ++ di_write_lock_parent(parent); ++ err = au_lkup_dentry(dentry, /*btop*/0, /*type*/0); ++ if (unlikely(err)) ++ goto out_unlock; ++ ++ AuDbgDentry(dentry); ++ if (d_is_positive(dentry)) ++ goto out_unlock; ++ ++ args.file = get_empty_filp(); ++ err = PTR_ERR(args.file); ++ if (IS_ERR(args.file)) ++ goto out_unlock; ++ ++ args.file->f_flags = file->f_flags; ++ err = au_aopen_or_create(dir, dentry, &args); ++ AuTraceErr(err); ++ AuDbgFile(args.file); ++ if (unlikely(err < 0)) { ++ if (h_opened & FILE_OPENED) ++ fput(args.file); ++ else ++ put_filp(args.file); ++ goto out_unlock; ++ } ++ ++ /* some filesystems don't set FILE_CREATED while succeeded? */ ++ *opened |= FILE_CREATED; ++ if (h_opened & FILE_OPENED) ++ aopen_node.h_file = args.file; ++ else { ++ put_filp(args.file); ++ args.file = NULL; ++ } ++ aopen = &au_sbi(dir->i_sb)->si_aopen; ++ au_sphl_add(&aopen_node.hlist, aopen); ++ err = finish_open(file, dentry, au_do_aopen, opened); ++ au_sphl_del(&aopen_node.hlist, aopen); ++ AuTraceErr(err); ++ AuDbgFile(file); ++ if (aopen_node.h_file) ++ fput(aopen_node.h_file); ++ ++out_unlock: ++ di_write_unlock(parent); ++ aufs_read_unlock(dentry, AuLock_DW); ++ AuDbgDentry(dentry); ++ if (unlikely(err)) ++ goto out; ++out_no_open: ++ if (!err && !(*opened & FILE_CREATED)) { ++ AuLabel(out_no_open); ++ dget(dentry); ++ err = finish_no_open(file, dentry); ++ } ++out: ++ AuDbg("%pd%s%s\n", dentry, ++ (*opened & FILE_CREATED) ? " created" : "", ++ (*opened & FILE_OPENED) ? " opened" : ""); ++ AuTraceErr(err); ++ return err; ++} ++ ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int au_wr_dir_cpup(struct dentry *dentry, struct dentry *parent, ++ const unsigned char add_entry, aufs_bindex_t bcpup, ++ aufs_bindex_t btop) ++{ ++ int err; ++ struct dentry *h_parent; ++ struct inode *h_dir; ++ ++ if (add_entry) ++ IMustLock(d_inode(parent)); ++ else ++ di_write_lock_parent(parent); ++ ++ err = 0; ++ if (!au_h_dptr(parent, bcpup)) { ++ if (btop > bcpup) ++ err = au_cpup_dirs(dentry, bcpup); ++ else if (btop < bcpup) ++ err = au_cpdown_dirs(dentry, bcpup); ++ else ++ BUG(); ++ } ++ if (!err && add_entry && !au_ftest_wrdir(add_entry, TMPFILE)) { ++ h_parent = au_h_dptr(parent, bcpup); ++ h_dir = d_inode(h_parent); ++ inode_lock_nested(h_dir, AuLsc_I_PARENT); ++ err = au_lkup_neg(dentry, bcpup, /*wh*/0); ++ /* todo: no unlock here */ ++ inode_unlock(h_dir); ++ ++ AuDbg("bcpup %d\n", bcpup); ++ if (!err) { ++ if (d_really_is_negative(dentry)) ++ au_set_h_dptr(dentry, btop, NULL); ++ au_update_dbrange(dentry, /*do_put_zero*/0); ++ } ++ } ++ ++ if (!add_entry) ++ di_write_unlock(parent); ++ if (!err) ++ err = bcpup; /* success */ ++ ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ++ * decide the branch and the parent dir where we will create a new entry. ++ * returns new bindex or an error. ++ * copyup the parent dir if needed. ++ */ ++int au_wr_dir(struct dentry *dentry, struct dentry *src_dentry, ++ struct au_wr_dir_args *args) ++{ ++ int err; ++ unsigned int flags; ++ aufs_bindex_t bcpup, btop, src_btop; ++ const unsigned char add_entry ++ = au_ftest_wrdir(args->flags, ADD_ENTRY) ++ | au_ftest_wrdir(args->flags, TMPFILE); ++ struct super_block *sb; ++ struct dentry *parent; ++ struct au_sbinfo *sbinfo; ++ ++ sb = dentry->d_sb; ++ sbinfo = au_sbi(sb); ++ parent = dget_parent(dentry); ++ btop = au_dbtop(dentry); ++ bcpup = btop; ++ if (args->force_btgt < 0) { ++ if (src_dentry) { ++ src_btop = au_dbtop(src_dentry); ++ if (src_btop < btop) ++ bcpup = src_btop; ++ } else if (add_entry) { ++ flags = 0; ++ if (au_ftest_wrdir(args->flags, ISDIR)) ++ au_fset_wbr(flags, DIR); ++ err = AuWbrCreate(sbinfo, dentry, flags); ++ bcpup = err; ++ } ++ ++ if (bcpup < 0 || au_test_ro(sb, bcpup, d_inode(dentry))) { ++ if (add_entry) ++ err = AuWbrCopyup(sbinfo, dentry); ++ else { ++ if (!IS_ROOT(dentry)) { ++ di_read_lock_parent(parent, !AuLock_IR); ++ err = AuWbrCopyup(sbinfo, dentry); ++ di_read_unlock(parent, !AuLock_IR); ++ } else ++ err = AuWbrCopyup(sbinfo, dentry); ++ } ++ bcpup = err; ++ if (unlikely(err < 0)) ++ goto out; ++ } ++ } else { ++ bcpup = args->force_btgt; ++ AuDebugOn(au_test_ro(sb, bcpup, d_inode(dentry))); ++ } ++ ++ AuDbg("btop %d, bcpup %d\n", btop, bcpup); ++ err = bcpup; ++ if (bcpup == btop) ++ goto out; /* success */ ++ ++ /* copyup the new parent into the branch we process */ ++ err = au_wr_dir_cpup(dentry, parent, add_entry, bcpup, btop); ++ if (err >= 0) { ++ if (d_really_is_negative(dentry)) { ++ au_set_h_dptr(dentry, btop, NULL); ++ au_set_dbtop(dentry, bcpup); ++ au_set_dbbot(dentry, bcpup); ++ } ++ AuDebugOn(add_entry ++ && !au_ftest_wrdir(args->flags, TMPFILE) ++ && !au_h_dptr(dentry, bcpup)); ++ } ++ ++out: ++ dput(parent); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void au_pin_hdir_unlock(struct au_pin *p) ++{ ++ if (p->hdir) ++ au_hn_imtx_unlock(p->hdir); ++} ++ ++int au_pin_hdir_lock(struct au_pin *p) ++{ ++ int err; ++ ++ err = 0; ++ if (!p->hdir) ++ goto out; ++ ++ /* even if an error happens later, keep this lock */ ++ au_hn_imtx_lock_nested(p->hdir, p->lsc_hi); ++ ++ err = -EBUSY; ++ if (unlikely(p->hdir->hi_inode != d_inode(p->h_parent))) ++ goto out; ++ ++ err = 0; ++ if (p->h_dentry) ++ err = au_h_verify(p->h_dentry, p->udba, p->hdir->hi_inode, ++ p->h_parent, p->br); ++ ++out: ++ return err; ++} ++ ++int au_pin_hdir_relock(struct au_pin *p) ++{ ++ int err, i; ++ struct inode *h_i; ++ struct dentry *h_d[] = { ++ p->h_dentry, ++ p->h_parent ++ }; ++ ++ err = au_pin_hdir_lock(p); ++ if (unlikely(err)) ++ goto out; ++ ++ for (i = 0; !err && i < sizeof(h_d)/sizeof(*h_d); i++) { ++ if (!h_d[i]) ++ continue; ++ if (d_is_positive(h_d[i])) { ++ h_i = d_inode(h_d[i]); ++ err = !h_i->i_nlink; ++ } ++ } ++ ++out: ++ return err; ++} ++ ++void au_pin_hdir_set_owner(struct au_pin *p, struct task_struct *task) ++{ ++#if defined(CONFIG_DEBUG_MUTEXES) || defined(CONFIG_SMP) ++ p->hdir->hi_inode->i_mutex.owner = task; ++#endif ++} ++ ++void au_pin_hdir_acquire_nest(struct au_pin *p) ++{ ++ if (p->hdir) { ++ mutex_acquire_nest(&p->hdir->hi_inode->i_mutex.dep_map, ++ p->lsc_hi, 0, NULL, _RET_IP_); ++ au_pin_hdir_set_owner(p, current); ++ } ++} ++ ++void au_pin_hdir_release(struct au_pin *p) ++{ ++ if (p->hdir) { ++ au_pin_hdir_set_owner(p, p->task); ++ mutex_release(&p->hdir->hi_inode->i_mutex.dep_map, 1, _RET_IP_); ++ } ++} ++ ++struct dentry *au_pinned_h_parent(struct au_pin *pin) ++{ ++ if (pin && pin->parent) ++ return au_h_dptr(pin->parent, pin->bindex); ++ return NULL; ++} ++ ++void au_unpin(struct au_pin *p) ++{ ++ if (p->hdir) ++ au_pin_hdir_unlock(p); ++ if (p->h_mnt && au_ftest_pin(p->flags, MNT_WRITE)) ++ vfsub_mnt_drop_write(p->h_mnt); ++ if (!p->hdir) ++ return; ++ ++ if (!au_ftest_pin(p->flags, DI_LOCKED)) ++ di_read_unlock(p->parent, AuLock_IR); ++ iput(p->hdir->hi_inode); ++ dput(p->parent); ++ p->parent = NULL; ++ p->hdir = NULL; ++ p->h_mnt = NULL; ++ /* do not clear p->task */ ++} ++ ++int au_do_pin(struct au_pin *p) ++{ ++ int err; ++ struct super_block *sb; ++ struct inode *h_dir; ++ ++ err = 0; ++ sb = p->dentry->d_sb; ++ p->br = au_sbr(sb, p->bindex); ++ if (IS_ROOT(p->dentry)) { ++ if (au_ftest_pin(p->flags, MNT_WRITE)) { ++ p->h_mnt = au_br_mnt(p->br); ++ err = vfsub_mnt_want_write(p->h_mnt); ++ if (unlikely(err)) { ++ au_fclr_pin(p->flags, MNT_WRITE); ++ goto out_err; ++ } ++ } ++ goto out; ++ } ++ ++ p->h_dentry = NULL; ++ if (p->bindex <= au_dbbot(p->dentry)) ++ p->h_dentry = au_h_dptr(p->dentry, p->bindex); ++ ++ p->parent = dget_parent(p->dentry); ++ if (!au_ftest_pin(p->flags, DI_LOCKED)) ++ di_read_lock(p->parent, AuLock_IR, p->lsc_di); ++ ++ h_dir = NULL; ++ p->h_parent = au_h_dptr(p->parent, p->bindex); ++ p->hdir = au_hi(d_inode(p->parent), p->bindex); ++ if (p->hdir) ++ h_dir = p->hdir->hi_inode; ++ ++ /* ++ * udba case, or ++ * if DI_LOCKED is not set, then p->parent may be different ++ * and h_parent can be NULL. ++ */ ++ if (unlikely(!p->hdir || !h_dir || !p->h_parent)) { ++ err = -EBUSY; ++ if (!au_ftest_pin(p->flags, DI_LOCKED)) ++ di_read_unlock(p->parent, AuLock_IR); ++ dput(p->parent); ++ p->parent = NULL; ++ goto out_err; ++ } ++ ++ if (au_ftest_pin(p->flags, MNT_WRITE)) { ++ p->h_mnt = au_br_mnt(p->br); ++ err = vfsub_mnt_want_write(p->h_mnt); ++ if (unlikely(err)) { ++ au_fclr_pin(p->flags, MNT_WRITE); ++ if (!au_ftest_pin(p->flags, DI_LOCKED)) ++ di_read_unlock(p->parent, AuLock_IR); ++ dput(p->parent); ++ p->parent = NULL; ++ goto out_err; ++ } ++ } ++ ++ au_igrab(h_dir); ++ err = au_pin_hdir_lock(p); ++ if (!err) ++ goto out; /* success */ ++ ++ au_unpin(p); ++ ++out_err: ++ pr_err("err %d\n", err); ++ err = au_busy_or_stale(); ++out: ++ return err; ++} ++ ++void au_pin_init(struct au_pin *p, struct dentry *dentry, ++ aufs_bindex_t bindex, int lsc_di, int lsc_hi, ++ unsigned int udba, unsigned char flags) ++{ ++ p->dentry = dentry; ++ p->udba = udba; ++ p->lsc_di = lsc_di; ++ p->lsc_hi = lsc_hi; ++ p->flags = flags; ++ p->bindex = bindex; ++ ++ p->parent = NULL; ++ p->hdir = NULL; ++ p->h_mnt = NULL; ++ ++ p->h_dentry = NULL; ++ p->h_parent = NULL; ++ p->br = NULL; ++ p->task = current; ++} ++ ++int au_pin(struct au_pin *pin, struct dentry *dentry, aufs_bindex_t bindex, ++ unsigned int udba, unsigned char flags) ++{ ++ au_pin_init(pin, dentry, bindex, AuLsc_DI_PARENT, AuLsc_I_PARENT2, ++ udba, flags); ++ return au_do_pin(pin); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * ->setattr() and ->getattr() are called in various cases. ++ * chmod, stat: dentry is revalidated. ++ * fchmod, fstat: file and dentry are not revalidated, additionally they may be ++ * unhashed. ++ * for ->setattr(), ia->ia_file is passed from ftruncate only. ++ */ ++/* todo: consolidate with do_refresh() and simple_reval_dpath() */ ++int au_reval_for_attr(struct dentry *dentry, unsigned int sigen) ++{ ++ int err; ++ struct dentry *parent; ++ ++ err = 0; ++ if (au_digen_test(dentry, sigen)) { ++ parent = dget_parent(dentry); ++ di_read_lock_parent(parent, AuLock_IR); ++ err = au_refresh_dentry(dentry, parent); ++ di_read_unlock(parent, AuLock_IR); ++ dput(parent); ++ } ++ ++ AuTraceErr(err); ++ return err; ++} ++ ++int au_pin_and_icpup(struct dentry *dentry, struct iattr *ia, ++ struct au_icpup_args *a) ++{ ++ int err; ++ loff_t sz; ++ aufs_bindex_t btop, ibtop; ++ struct dentry *hi_wh, *parent; ++ struct inode *inode; ++ struct au_wr_dir_args wr_dir_args = { ++ .force_btgt = -1, ++ .flags = 0 ++ }; ++ ++ if (d_is_dir(dentry)) ++ au_fset_wrdir(wr_dir_args.flags, ISDIR); ++ /* plink or hi_wh() case */ ++ btop = au_dbtop(dentry); ++ inode = d_inode(dentry); ++ ibtop = au_ibtop(inode); ++ if (btop != ibtop && !au_test_ro(inode->i_sb, ibtop, inode)) ++ wr_dir_args.force_btgt = ibtop; ++ err = au_wr_dir(dentry, /*src_dentry*/NULL, &wr_dir_args); ++ if (unlikely(err < 0)) ++ goto out; ++ a->btgt = err; ++ if (err != btop) ++ au_fset_icpup(a->flags, DID_CPUP); ++ ++ err = 0; ++ a->pin_flags = AuPin_MNT_WRITE; ++ parent = NULL; ++ if (!IS_ROOT(dentry)) { ++ au_fset_pin(a->pin_flags, DI_LOCKED); ++ parent = dget_parent(dentry); ++ di_write_lock_parent(parent); ++ } ++ ++ err = au_pin(&a->pin, dentry, a->btgt, a->udba, a->pin_flags); ++ if (unlikely(err)) ++ goto out_parent; ++ ++ sz = -1; ++ a->h_path.dentry = au_h_dptr(dentry, btop); ++ a->h_inode = d_inode(a->h_path.dentry); ++ if (ia && (ia->ia_valid & ATTR_SIZE)) { ++ inode_lock_nested(a->h_inode, AuLsc_I_CHILD); ++ if (ia->ia_size < i_size_read(a->h_inode)) ++ sz = ia->ia_size; ++ inode_unlock(a->h_inode); ++ } ++ ++ hi_wh = NULL; ++ if (au_ftest_icpup(a->flags, DID_CPUP) && d_unlinked(dentry)) { ++ hi_wh = au_hi_wh(inode, a->btgt); ++ if (!hi_wh) { ++ struct au_cp_generic cpg = { ++ .dentry = dentry, ++ .bdst = a->btgt, ++ .bsrc = -1, ++ .len = sz, ++ .pin = &a->pin ++ }; ++ err = au_sio_cpup_wh(&cpg, /*file*/NULL); ++ if (unlikely(err)) ++ goto out_unlock; ++ hi_wh = au_hi_wh(inode, a->btgt); ++ /* todo: revalidate hi_wh? */ ++ } ++ } ++ ++ if (parent) { ++ au_pin_set_parent_lflag(&a->pin, /*lflag*/0); ++ di_downgrade_lock(parent, AuLock_IR); ++ dput(parent); ++ parent = NULL; ++ } ++ if (!au_ftest_icpup(a->flags, DID_CPUP)) ++ goto out; /* success */ ++ ++ if (!d_unhashed(dentry)) { ++ struct au_cp_generic cpg = { ++ .dentry = dentry, ++ .bdst = a->btgt, ++ .bsrc = btop, ++ .len = sz, ++ .pin = &a->pin, ++ .flags = AuCpup_DTIME | AuCpup_HOPEN ++ }; ++ err = au_sio_cpup_simple(&cpg); ++ if (!err) ++ a->h_path.dentry = au_h_dptr(dentry, a->btgt); ++ } else if (!hi_wh) ++ a->h_path.dentry = au_h_dptr(dentry, a->btgt); ++ else ++ a->h_path.dentry = hi_wh; /* do not dget here */ ++ ++out_unlock: ++ a->h_inode = d_inode(a->h_path.dentry); ++ if (!err) ++ goto out; /* success */ ++ au_unpin(&a->pin); ++out_parent: ++ if (parent) { ++ di_write_unlock(parent); ++ dput(parent); ++ } ++out: ++ if (!err) ++ inode_lock_nested(a->h_inode, AuLsc_I_CHILD); ++ return err; ++} ++ ++static int aufs_setattr(struct dentry *dentry, struct iattr *ia) ++{ ++ int err; ++ struct inode *inode, *delegated; ++ struct super_block *sb; ++ struct file *file; ++ struct au_icpup_args *a; ++ ++ inode = d_inode(dentry); ++ IMustLock(inode); ++ ++ err = -ENOMEM; ++ a = kzalloc(sizeof(*a), GFP_NOFS); ++ if (unlikely(!a)) ++ goto out; ++ ++ if (ia->ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID)) ++ ia->ia_valid &= ~ATTR_MODE; ++ ++ file = NULL; ++ sb = dentry->d_sb; ++ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); ++ if (unlikely(err)) ++ goto out_kfree; ++ ++ if (ia->ia_valid & ATTR_FILE) { ++ /* currently ftruncate(2) only */ ++ AuDebugOn(!d_is_reg(dentry)); ++ file = ia->ia_file; ++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1); ++ if (unlikely(err)) ++ goto out_si; ++ ia->ia_file = au_hf_top(file); ++ a->udba = AuOpt_UDBA_NONE; ++ } else { ++ /* fchmod() doesn't pass ia_file */ ++ a->udba = au_opt_udba(sb); ++ di_write_lock_child(dentry); ++ /* no d_unlinked(), to set UDBA_NONE for root */ ++ if (d_unhashed(dentry)) ++ a->udba = AuOpt_UDBA_NONE; ++ if (a->udba != AuOpt_UDBA_NONE) { ++ AuDebugOn(IS_ROOT(dentry)); ++ err = au_reval_for_attr(dentry, au_sigen(sb)); ++ if (unlikely(err)) ++ goto out_dentry; ++ } ++ } ++ ++ err = au_pin_and_icpup(dentry, ia, a); ++ if (unlikely(err < 0)) ++ goto out_dentry; ++ if (au_ftest_icpup(a->flags, DID_CPUP)) { ++ ia->ia_file = NULL; ++ ia->ia_valid &= ~ATTR_FILE; ++ } ++ ++ a->h_path.mnt = au_sbr_mnt(sb, a->btgt); ++ if ((ia->ia_valid & (ATTR_MODE | ATTR_CTIME)) ++ == (ATTR_MODE | ATTR_CTIME)) { ++ err = security_path_chmod(&a->h_path, ia->ia_mode); ++ if (unlikely(err)) ++ goto out_unlock; ++ } else if ((ia->ia_valid & (ATTR_UID | ATTR_GID)) ++ && (ia->ia_valid & ATTR_CTIME)) { ++ err = security_path_chown(&a->h_path, ia->ia_uid, ia->ia_gid); ++ if (unlikely(err)) ++ goto out_unlock; ++ } ++ ++ if (ia->ia_valid & ATTR_SIZE) { ++ struct file *f; ++ ++ if (ia->ia_size < i_size_read(inode)) ++ /* unmap only */ ++ truncate_setsize(inode, ia->ia_size); ++ ++ f = NULL; ++ if (ia->ia_valid & ATTR_FILE) ++ f = ia->ia_file; ++ inode_unlock(a->h_inode); ++ err = vfsub_trunc(&a->h_path, ia->ia_size, ia->ia_valid, f); ++ inode_lock_nested(a->h_inode, AuLsc_I_CHILD); ++ } else { ++ delegated = NULL; ++ while (1) { ++ err = vfsub_notify_change(&a->h_path, ia, &delegated); ++ if (delegated) { ++ err = break_deleg_wait(&delegated); ++ if (!err) ++ continue; ++ } ++ break; ++ } ++ } ++ /* ++ * regardless aufs 'acl' option setting. ++ * why don't all acl-aware fs call this func from their ->setattr()? ++ */ ++ if (!err && (ia->ia_valid & ATTR_MODE)) ++ err = vfsub_acl_chmod(a->h_inode, ia->ia_mode); ++ if (!err) ++ au_cpup_attr_changeable(inode); ++ ++out_unlock: ++ inode_unlock(a->h_inode); ++ au_unpin(&a->pin); ++ if (unlikely(err)) ++ au_update_dbtop(dentry); ++out_dentry: ++ di_write_unlock(dentry); ++ if (file) { ++ fi_write_unlock(file); ++ ia->ia_file = file; ++ ia->ia_valid |= ATTR_FILE; ++ } ++out_si: ++ si_read_unlock(sb); ++out_kfree: ++ kfree(a); ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++#if IS_ENABLED(CONFIG_AUFS_XATTR) || IS_ENABLED(CONFIG_FS_POSIX_ACL) ++static int au_h_path_to_set_attr(struct dentry *dentry, ++ struct au_icpup_args *a, struct path *h_path) ++{ ++ int err; ++ struct super_block *sb; ++ ++ sb = dentry->d_sb; ++ a->udba = au_opt_udba(sb); ++ /* no d_unlinked(), to set UDBA_NONE for root */ ++ if (d_unhashed(dentry)) ++ a->udba = AuOpt_UDBA_NONE; ++ if (a->udba != AuOpt_UDBA_NONE) { ++ AuDebugOn(IS_ROOT(dentry)); ++ err = au_reval_for_attr(dentry, au_sigen(sb)); ++ if (unlikely(err)) ++ goto out; ++ } ++ err = au_pin_and_icpup(dentry, /*ia*/NULL, a); ++ if (unlikely(err < 0)) ++ goto out; ++ ++ h_path->dentry = a->h_path.dentry; ++ h_path->mnt = au_sbr_mnt(sb, a->btgt); ++ ++out: ++ return err; ++} ++ ++ssize_t au_srxattr(struct dentry *dentry, struct au_srxattr *arg) ++{ ++ int err; ++ struct path h_path; ++ struct super_block *sb; ++ struct au_icpup_args *a; ++ struct inode *inode, *h_inode; ++ ++ inode = d_inode(dentry); ++ IMustLock(inode); ++ ++ err = -ENOMEM; ++ a = kzalloc(sizeof(*a), GFP_NOFS); ++ if (unlikely(!a)) ++ goto out; ++ ++ sb = dentry->d_sb; ++ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); ++ if (unlikely(err)) ++ goto out_kfree; ++ ++ h_path.dentry = NULL; /* silence gcc */ ++ di_write_lock_child(dentry); ++ err = au_h_path_to_set_attr(dentry, a, &h_path); ++ if (unlikely(err)) ++ goto out_di; ++ ++ inode_unlock(a->h_inode); ++ switch (arg->type) { ++ case AU_XATTR_SET: ++ err = vfsub_setxattr(h_path.dentry, ++ arg->u.set.name, arg->u.set.value, ++ arg->u.set.size, arg->u.set.flags); ++ break; ++ case AU_XATTR_REMOVE: ++ err = vfsub_removexattr(h_path.dentry, arg->u.remove.name); ++ break; ++ case AU_ACL_SET: ++ err = -EOPNOTSUPP; ++ h_inode = d_inode(h_path.dentry); ++ if (h_inode->i_op->set_acl) ++ err = h_inode->i_op->set_acl(h_inode, ++ arg->u.acl_set.acl, ++ arg->u.acl_set.type); ++ break; ++ } ++ if (!err) ++ au_cpup_attr_timesizes(inode); ++ ++ au_unpin(&a->pin); ++ if (unlikely(err)) ++ au_update_dbtop(dentry); ++ ++out_di: ++ di_write_unlock(dentry); ++ si_read_unlock(sb); ++out_kfree: ++ kfree(a); ++out: ++ AuTraceErr(err); ++ return err; ++} ++#endif ++ ++static void au_refresh_iattr(struct inode *inode, struct kstat *st, ++ unsigned int nlink) ++{ ++ unsigned int n; ++ ++ inode->i_mode = st->mode; ++ /* don't i_[ug]id_write() here */ ++ inode->i_uid = st->uid; ++ inode->i_gid = st->gid; ++ inode->i_atime = st->atime; ++ inode->i_mtime = st->mtime; ++ inode->i_ctime = st->ctime; ++ ++ au_cpup_attr_nlink(inode, /*force*/0); ++ if (S_ISDIR(inode->i_mode)) { ++ n = inode->i_nlink; ++ n -= nlink; ++ n += st->nlink; ++ smp_mb(); /* for i_nlink */ ++ /* 0 can happen */ ++ set_nlink(inode, n); ++ } ++ ++ spin_lock(&inode->i_lock); ++ inode->i_blocks = st->blocks; ++ i_size_write(inode, st->size); ++ spin_unlock(&inode->i_lock); ++} ++ ++/* ++ * common routine for aufs_getattr() and aufs_getxattr(). ++ * returns zero or negative (an error). ++ * @dentry will be read-locked in success. ++ */ ++int au_h_path_getattr(struct dentry *dentry, int force, struct path *h_path) ++{ ++ int err; ++ unsigned int mnt_flags, sigen; ++ unsigned char udba_none; ++ aufs_bindex_t bindex; ++ struct super_block *sb, *h_sb; ++ struct inode *inode; ++ ++ h_path->mnt = NULL; ++ h_path->dentry = NULL; ++ ++ err = 0; ++ sb = dentry->d_sb; ++ mnt_flags = au_mntflags(sb); ++ udba_none = !!au_opt_test(mnt_flags, UDBA_NONE); ++ ++ /* support fstat(2) */ ++ if (!d_unlinked(dentry) && !udba_none) { ++ sigen = au_sigen(sb); ++ err = au_digen_test(dentry, sigen); ++ if (!err) { ++ di_read_lock_child(dentry, AuLock_IR); ++ err = au_dbrange_test(dentry); ++ if (unlikely(err)) { ++ di_read_unlock(dentry, AuLock_IR); ++ goto out; ++ } ++ } else { ++ AuDebugOn(IS_ROOT(dentry)); ++ di_write_lock_child(dentry); ++ err = au_dbrange_test(dentry); ++ if (!err) ++ err = au_reval_for_attr(dentry, sigen); ++ if (!err) ++ di_downgrade_lock(dentry, AuLock_IR); ++ else { ++ di_write_unlock(dentry); ++ goto out; ++ } ++ } ++ } else ++ di_read_lock_child(dentry, AuLock_IR); ++ ++ inode = d_inode(dentry); ++ bindex = au_ibtop(inode); ++ h_path->mnt = au_sbr_mnt(sb, bindex); ++ h_sb = h_path->mnt->mnt_sb; ++ if (!force ++ && !au_test_fs_bad_iattr(h_sb) ++ && udba_none) ++ goto out; /* success */ ++ ++ if (au_dbtop(dentry) == bindex) ++ h_path->dentry = au_h_dptr(dentry, bindex); ++ else if (au_opt_test(mnt_flags, PLINK) && au_plink_test(inode)) { ++ h_path->dentry = au_plink_lkup(inode, bindex); ++ if (IS_ERR(h_path->dentry)) ++ /* pretending success */ ++ h_path->dentry = NULL; ++ else ++ dput(h_path->dentry); ++ } ++ ++out: ++ return err; ++} ++ ++static int aufs_getattr(struct vfsmount *mnt __maybe_unused, ++ struct dentry *dentry, struct kstat *st) ++{ ++ int err; ++ unsigned char positive; ++ struct path h_path; ++ struct inode *inode; ++ struct super_block *sb; ++ ++ inode = d_inode(dentry); ++ sb = dentry->d_sb; ++ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); ++ if (unlikely(err)) ++ goto out; ++ err = au_h_path_getattr(dentry, /*force*/0, &h_path); ++ if (unlikely(err)) ++ goto out_si; ++ if (unlikely(!h_path.dentry)) ++ /* illegally overlapped or something */ ++ goto out_fill; /* pretending success */ ++ ++ positive = d_is_positive(h_path.dentry); ++ if (positive) ++ err = vfs_getattr(&h_path, st); ++ if (!err) { ++ if (positive) ++ au_refresh_iattr(inode, st, ++ d_inode(h_path.dentry)->i_nlink); ++ goto out_fill; /* success */ ++ } ++ AuTraceErr(err); ++ goto out_di; ++ ++out_fill: ++ generic_fillattr(inode, st); ++out_di: ++ di_read_unlock(dentry, AuLock_IR); ++out_si: ++ si_read_unlock(sb); ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static const char *aufs_get_link(struct dentry *dentry, struct inode *inode, ++ struct delayed_call *done) ++{ ++ const char *ret; ++ struct dentry *h_dentry; ++ struct inode *h_inode; ++ int err; ++ aufs_bindex_t bindex; ++ ++ ret = NULL; /* suppress a warning */ ++ err = -ECHILD; ++ if (!dentry) ++ goto out; ++ ++ err = aufs_read_lock(dentry, AuLock_IR | AuLock_GEN); ++ if (unlikely(err)) ++ goto out; ++ ++ err = au_d_hashed_positive(dentry); ++ if (unlikely(err)) ++ goto out_unlock; ++ ++ err = -EINVAL; ++ inode = d_inode(dentry); ++ bindex = au_ibtop(inode); ++ h_inode = au_h_iptr(inode, bindex); ++ if (unlikely(!h_inode->i_op->get_link)) ++ goto out_unlock; ++ ++ err = -EBUSY; ++ h_dentry = NULL; ++ if (au_dbtop(dentry) <= bindex) { ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (h_dentry) ++ dget(h_dentry); ++ } ++ if (!h_dentry) { ++ h_dentry = d_find_any_alias(h_inode); ++ if (IS_ERR(h_dentry)) { ++ err = PTR_ERR(h_dentry); ++ goto out_unlock; ++ } ++ } ++ if (unlikely(!h_dentry)) ++ goto out_unlock; ++ ++ err = 0; ++ AuDbg("%pf\n", h_inode->i_op->get_link); ++ AuDbgDentry(h_dentry); ++ ret = h_inode->i_op->get_link(h_dentry, h_inode, done); ++ dput(h_dentry); ++ if (IS_ERR(ret)) ++ err = PTR_ERR(ret); ++ ++out_unlock: ++ aufs_read_unlock(dentry, AuLock_IR); ++out: ++ if (unlikely(err)) ++ ret = ERR_PTR(err); ++ AuTraceErrPtr(ret); ++ return ret; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int aufs_update_time(struct inode *inode, struct timespec *ts, int flags) ++{ ++ int err; ++ struct super_block *sb; ++ struct inode *h_inode; ++ ++ sb = inode->i_sb; ++ /* mmap_sem might be acquired already, cf. aufs_mmap() */ ++ lockdep_off(); ++ si_read_lock(sb, AuLock_FLUSH); ++ ii_write_lock_child(inode); ++ lockdep_on(); ++ h_inode = au_h_iptr(inode, au_ibtop(inode)); ++ err = vfsub_update_time(h_inode, ts, flags); ++ lockdep_off(); ++ if (!err) ++ au_cpup_attr_timesizes(inode); ++ ii_write_unlock(inode); ++ si_read_unlock(sb); ++ lockdep_on(); ++ ++ if (!err && (flags & S_VERSION)) ++ inode_inc_iversion(inode); ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* no getattr version will be set by module.c:aufs_init() */ ++struct inode_operations aufs_iop_nogetattr[AuIop_Last], ++ aufs_iop[] = { ++ [AuIop_SYMLINK] = { ++ .permission = aufs_permission, ++#ifdef CONFIG_FS_POSIX_ACL ++ .get_acl = aufs_get_acl, ++ .set_acl = aufs_set_acl, /* unsupport for symlink? */ ++#endif ++ ++ .setattr = aufs_setattr, ++ .getattr = aufs_getattr, ++ ++#ifdef CONFIG_AUFS_XATTR ++ .setxattr = aufs_setxattr, ++ .getxattr = aufs_getxattr, ++ .listxattr = aufs_listxattr, ++ .removexattr = aufs_removexattr, ++#endif ++ ++ .readlink = generic_readlink, ++ .get_link = aufs_get_link, ++ ++ /* .update_time = aufs_update_time */ ++ }, ++ [AuIop_DIR] = { ++ .create = aufs_create, ++ .lookup = aufs_lookup, ++ .link = aufs_link, ++ .unlink = aufs_unlink, ++ .symlink = aufs_symlink, ++ .mkdir = aufs_mkdir, ++ .rmdir = aufs_rmdir, ++ .mknod = aufs_mknod, ++ .rename = aufs_rename, ++ ++ .permission = aufs_permission, ++#ifdef CONFIG_FS_POSIX_ACL ++ .get_acl = aufs_get_acl, ++ .set_acl = aufs_set_acl, ++#endif ++ ++ .setattr = aufs_setattr, ++ .getattr = aufs_getattr, ++ ++#ifdef CONFIG_AUFS_XATTR ++ .setxattr = aufs_setxattr, ++ .getxattr = aufs_getxattr, ++ .listxattr = aufs_listxattr, ++ .removexattr = aufs_removexattr, ++#endif ++ ++ .update_time = aufs_update_time, ++ .atomic_open = aufs_atomic_open, ++ .tmpfile = aufs_tmpfile ++ }, ++ [AuIop_OTHER] = { ++ .permission = aufs_permission, ++#ifdef CONFIG_FS_POSIX_ACL ++ .get_acl = aufs_get_acl, ++ .set_acl = aufs_set_acl, ++#endif ++ ++ .setattr = aufs_setattr, ++ .getattr = aufs_getattr, ++ ++#ifdef CONFIG_AUFS_XATTR ++ .setxattr = aufs_setxattr, ++ .getxattr = aufs_getxattr, ++ .listxattr = aufs_listxattr, ++ .removexattr = aufs_removexattr, ++#endif ++ ++ .update_time = aufs_update_time ++ } ++}; +diff --git a/fs/aufs/i_op_add.c b/fs/aufs/i_op_add.c +new file mode 100644 +index 0000000..8e3fb61 +--- /dev/null ++++ b/fs/aufs/i_op_add.c +@@ -0,0 +1,911 @@ ++/* ++ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ */ ++ ++/* ++ * inode operations (add entry) ++ */ ++ ++#include "aufs.h" ++ ++/* ++ * final procedure of adding a new entry, except link(2). ++ * remove whiteout, instantiate, copyup the parent dir's times and size ++ * and update version. ++ * if it failed, re-create the removed whiteout. ++ */ ++static int epilog(struct inode *dir, aufs_bindex_t bindex, ++ struct dentry *wh_dentry, struct dentry *dentry) ++{ ++ int err, rerr; ++ aufs_bindex_t bwh; ++ struct path h_path; ++ struct super_block *sb; ++ struct inode *inode, *h_dir; ++ struct dentry *wh; ++ ++ bwh = -1; ++ sb = dir->i_sb; ++ if (wh_dentry) { ++ h_dir = d_inode(wh_dentry->d_parent); /* dir inode is locked */ ++ IMustLock(h_dir); ++ AuDebugOn(au_h_iptr(dir, bindex) != h_dir); ++ bwh = au_dbwh(dentry); ++ h_path.dentry = wh_dentry; ++ h_path.mnt = au_sbr_mnt(sb, bindex); ++ err = au_wh_unlink_dentry(au_h_iptr(dir, bindex), &h_path, ++ dentry); ++ if (unlikely(err)) ++ goto out; ++ } ++ ++ inode = au_new_inode(dentry, /*must_new*/1); ++ if (!IS_ERR(inode)) { ++ d_instantiate(dentry, inode); ++ dir = d_inode(dentry->d_parent); /* dir inode is locked */ ++ IMustLock(dir); ++ au_dir_ts(dir, bindex); ++ dir->i_version++; ++ au_fhsm_wrote(sb, bindex, /*force*/0); ++ return 0; /* success */ ++ } ++ ++ err = PTR_ERR(inode); ++ if (!wh_dentry) ++ goto out; ++ ++ /* revert */ ++ /* dir inode is locked */ ++ wh = au_wh_create(dentry, bwh, wh_dentry->d_parent); ++ rerr = PTR_ERR(wh); ++ if (IS_ERR(wh)) { ++ AuIOErr("%pd reverting whiteout failed(%d, %d)\n", ++ dentry, err, rerr); ++ err = -EIO; ++ } else ++ dput(wh); ++ ++out: ++ return err; ++} ++ ++static int au_d_may_add(struct dentry *dentry) ++{ ++ int err; ++ ++ err = 0; ++ if (unlikely(d_unhashed(dentry))) ++ err = -ENOENT; ++ if (unlikely(d_really_is_positive(dentry))) ++ err = -EEXIST; ++ return err; ++} ++ ++/* ++ * simple tests for the adding inode operations. ++ * following the checks in vfs, plus the parent-child relationship. ++ */ ++int au_may_add(struct dentry *dentry, aufs_bindex_t bindex, ++ struct dentry *h_parent, int isdir) ++{ ++ int err; ++ umode_t h_mode; ++ struct dentry *h_dentry; ++ struct inode *h_inode; ++ ++ err = -ENAMETOOLONG; ++ if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) ++ goto out; ++ ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (d_really_is_negative(dentry)) { ++ err = -EEXIST; ++ if (unlikely(d_is_positive(h_dentry))) ++ goto out; ++ } else { ++ /* rename(2) case */ ++ err = -EIO; ++ if (unlikely(d_is_negative(h_dentry))) ++ goto out; ++ h_inode = d_inode(h_dentry); ++ if (unlikely(!h_inode->i_nlink)) ++ goto out; ++ ++ h_mode = h_inode->i_mode; ++ if (!isdir) { ++ err = -EISDIR; ++ if (unlikely(S_ISDIR(h_mode))) ++ goto out; ++ } else if (unlikely(!S_ISDIR(h_mode))) { ++ err = -ENOTDIR; ++ goto out; ++ } ++ } ++ ++ err = 0; ++ /* expected parent dir is locked */ ++ if (unlikely(h_parent != h_dentry->d_parent)) ++ err = -EIO; ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ++ * initial procedure of adding a new entry. ++ * prepare writable branch and the parent dir, lock it, ++ * and lookup whiteout for the new entry. ++ */ ++static struct dentry* ++lock_hdir_lkup_wh(struct dentry *dentry, struct au_dtime *dt, ++ struct dentry *src_dentry, struct au_pin *pin, ++ struct au_wr_dir_args *wr_dir_args) ++{ ++ struct dentry *wh_dentry, *h_parent; ++ struct super_block *sb; ++ struct au_branch *br; ++ int err; ++ unsigned int udba; ++ aufs_bindex_t bcpup; ++ ++ AuDbg("%pd\n", dentry); ++ ++ err = au_wr_dir(dentry, src_dentry, wr_dir_args); ++ bcpup = err; ++ wh_dentry = ERR_PTR(err); ++ if (unlikely(err < 0)) ++ goto out; ++ ++ sb = dentry->d_sb; ++ udba = au_opt_udba(sb); ++ err = au_pin(pin, dentry, bcpup, udba, ++ AuPin_DI_LOCKED | AuPin_MNT_WRITE); ++ wh_dentry = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out; ++ ++ h_parent = au_pinned_h_parent(pin); ++ if (udba != AuOpt_UDBA_NONE ++ && au_dbtop(dentry) == bcpup) ++ err = au_may_add(dentry, bcpup, h_parent, ++ au_ftest_wrdir(wr_dir_args->flags, ISDIR)); ++ else if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) ++ err = -ENAMETOOLONG; ++ wh_dentry = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out_unpin; ++ ++ br = au_sbr(sb, bcpup); ++ if (dt) { ++ struct path tmp = { ++ .dentry = h_parent, ++ .mnt = au_br_mnt(br) ++ }; ++ au_dtime_store(dt, au_pinned_parent(pin), &tmp); ++ } ++ ++ wh_dentry = NULL; ++ if (bcpup != au_dbwh(dentry)) ++ goto out; /* success */ ++ ++ /* ++ * ENAMETOOLONG here means that if we allowed create such name, then it ++ * would not be able to removed in the future. So we don't allow such ++ * name here and we don't handle ENAMETOOLONG differently here. ++ */ ++ wh_dentry = au_wh_lkup(h_parent, &dentry->d_name, br); ++ ++out_unpin: ++ if (IS_ERR(wh_dentry)) ++ au_unpin(pin); ++out: ++ return wh_dentry; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++enum { Mknod, Symlink, Creat }; ++struct simple_arg { ++ int type; ++ union { ++ struct { ++ umode_t mode; ++ bool want_excl; ++ bool try_aopen; ++ struct vfsub_aopen_args *aopen; ++ } c; ++ struct { ++ const char *symname; ++ } s; ++ struct { ++ umode_t mode; ++ dev_t dev; ++ } m; ++ } u; ++}; ++ ++static int add_simple(struct inode *dir, struct dentry *dentry, ++ struct simple_arg *arg) ++{ ++ int err, rerr; ++ aufs_bindex_t btop; ++ unsigned char created; ++ const unsigned char try_aopen ++ = (arg->type == Creat && arg->u.c.try_aopen); ++ struct dentry *wh_dentry, *parent; ++ struct inode *h_dir; ++ struct super_block *sb; ++ struct au_branch *br; ++ /* to reuduce stack size */ ++ struct { ++ struct au_dtime dt; ++ struct au_pin pin; ++ struct path h_path; ++ struct au_wr_dir_args wr_dir_args; ++ } *a; ++ ++ AuDbg("%pd\n", dentry); ++ IMustLock(dir); ++ ++ err = -ENOMEM; ++ a = kmalloc(sizeof(*a), GFP_NOFS); ++ if (unlikely(!a)) ++ goto out; ++ a->wr_dir_args.force_btgt = -1; ++ a->wr_dir_args.flags = AuWrDir_ADD_ENTRY; ++ ++ parent = dentry->d_parent; /* dir inode is locked */ ++ if (!try_aopen) { ++ err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN); ++ if (unlikely(err)) ++ goto out_free; ++ } ++ err = au_d_may_add(dentry); ++ if (unlikely(err)) ++ goto out_unlock; ++ if (!try_aopen) ++ di_write_lock_parent(parent); ++ wh_dentry = lock_hdir_lkup_wh(dentry, &a->dt, /*src_dentry*/NULL, ++ &a->pin, &a->wr_dir_args); ++ err = PTR_ERR(wh_dentry); ++ if (IS_ERR(wh_dentry)) ++ goto out_parent; ++ ++ btop = au_dbtop(dentry); ++ sb = dentry->d_sb; ++ br = au_sbr(sb, btop); ++ a->h_path.dentry = au_h_dptr(dentry, btop); ++ a->h_path.mnt = au_br_mnt(br); ++ h_dir = au_pinned_h_dir(&a->pin); ++ switch (arg->type) { ++ case Creat: ++ err = 0; ++ if (!try_aopen || !h_dir->i_op->atomic_open) ++ err = vfsub_create(h_dir, &a->h_path, arg->u.c.mode, ++ arg->u.c.want_excl); ++ else ++ err = vfsub_atomic_open(h_dir, a->h_path.dentry, ++ arg->u.c.aopen, br); ++ break; ++ case Symlink: ++ err = vfsub_symlink(h_dir, &a->h_path, arg->u.s.symname); ++ break; ++ case Mknod: ++ err = vfsub_mknod(h_dir, &a->h_path, arg->u.m.mode, ++ arg->u.m.dev); ++ break; ++ default: ++ BUG(); ++ } ++ created = !err; ++ if (!err) ++ err = epilog(dir, btop, wh_dentry, dentry); ++ ++ /* revert */ ++ if (unlikely(created && err && d_is_positive(a->h_path.dentry))) { ++ /* no delegation since it is just created */ ++ rerr = vfsub_unlink(h_dir, &a->h_path, /*delegated*/NULL, ++ /*force*/0); ++ if (rerr) { ++ AuIOErr("%pd revert failure(%d, %d)\n", ++ dentry, err, rerr); ++ err = -EIO; ++ } ++ au_dtime_revert(&a->dt); ++ } ++ ++ if (!err && try_aopen && !h_dir->i_op->atomic_open) ++ *arg->u.c.aopen->opened |= FILE_CREATED; ++ ++ au_unpin(&a->pin); ++ dput(wh_dentry); ++ ++out_parent: ++ if (!try_aopen) ++ di_write_unlock(parent); ++out_unlock: ++ if (unlikely(err)) { ++ au_update_dbtop(dentry); ++ d_drop(dentry); ++ } ++ if (!try_aopen) ++ aufs_read_unlock(dentry, AuLock_DW); ++out_free: ++ kfree(a); ++out: ++ return err; ++} ++ ++int aufs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, ++ dev_t dev) ++{ ++ struct simple_arg arg = { ++ .type = Mknod, ++ .u.m = { ++ .mode = mode, ++ .dev = dev ++ } ++ }; ++ return add_simple(dir, dentry, &arg); ++} ++ ++int aufs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) ++{ ++ struct simple_arg arg = { ++ .type = Symlink, ++ .u.s.symname = symname ++ }; ++ return add_simple(dir, dentry, &arg); ++} ++ ++int aufs_create(struct inode *dir, struct dentry *dentry, umode_t mode, ++ bool want_excl) ++{ ++ struct simple_arg arg = { ++ .type = Creat, ++ .u.c = { ++ .mode = mode, ++ .want_excl = want_excl ++ } ++ }; ++ return add_simple(dir, dentry, &arg); ++} ++ ++int au_aopen_or_create(struct inode *dir, struct dentry *dentry, ++ struct vfsub_aopen_args *aopen_args) ++{ ++ struct simple_arg arg = { ++ .type = Creat, ++ .u.c = { ++ .mode = aopen_args->create_mode, ++ .want_excl = aopen_args->open_flag & O_EXCL, ++ .try_aopen = true, ++ .aopen = aopen_args ++ } ++ }; ++ return add_simple(dir, dentry, &arg); ++} ++ ++int aufs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode) ++{ ++ int err; ++ aufs_bindex_t bindex; ++ struct super_block *sb; ++ struct dentry *parent, *h_parent, *h_dentry; ++ struct inode *h_dir, *inode; ++ struct vfsmount *h_mnt; ++ struct au_wr_dir_args wr_dir_args = { ++ .force_btgt = -1, ++ .flags = AuWrDir_TMPFILE ++ }; ++ ++ /* copy-up may happen */ ++ inode_lock(dir); ++ ++ sb = dir->i_sb; ++ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); ++ if (unlikely(err)) ++ goto out; ++ ++ err = au_di_init(dentry); ++ if (unlikely(err)) ++ goto out_si; ++ ++ err = -EBUSY; ++ parent = d_find_any_alias(dir); ++ AuDebugOn(!parent); ++ di_write_lock_parent(parent); ++ if (unlikely(d_inode(parent) != dir)) ++ goto out_parent; ++ ++ err = au_digen_test(parent, au_sigen(sb)); ++ if (unlikely(err)) ++ goto out_parent; ++ ++ bindex = au_dbtop(parent); ++ au_set_dbtop(dentry, bindex); ++ au_set_dbbot(dentry, bindex); ++ err = au_wr_dir(dentry, /*src_dentry*/NULL, &wr_dir_args); ++ bindex = err; ++ if (unlikely(err < 0)) ++ goto out_parent; ++ ++ err = -EOPNOTSUPP; ++ h_dir = au_h_iptr(dir, bindex); ++ if (unlikely(!h_dir->i_op->tmpfile)) ++ goto out_parent; ++ ++ h_mnt = au_sbr_mnt(sb, bindex); ++ err = vfsub_mnt_want_write(h_mnt); ++ if (unlikely(err)) ++ goto out_parent; ++ ++ h_parent = au_h_dptr(parent, bindex); ++ err = inode_permission(d_inode(h_parent), MAY_WRITE | MAY_EXEC); ++ if (unlikely(err)) ++ goto out_mnt; ++ ++ err = -ENOMEM; ++ h_dentry = d_alloc(h_parent, &dentry->d_name); ++ if (unlikely(!h_dentry)) ++ goto out_mnt; ++ ++ err = h_dir->i_op->tmpfile(h_dir, h_dentry, mode); ++ if (unlikely(err)) ++ goto out_dentry; ++ ++ au_set_dbtop(dentry, bindex); ++ au_set_dbbot(dentry, bindex); ++ au_set_h_dptr(dentry, bindex, dget(h_dentry)); ++ inode = au_new_inode(dentry, /*must_new*/1); ++ if (IS_ERR(inode)) { ++ err = PTR_ERR(inode); ++ au_set_h_dptr(dentry, bindex, NULL); ++ au_set_dbtop(dentry, -1); ++ au_set_dbbot(dentry, -1); ++ } else { ++ if (!inode->i_nlink) ++ set_nlink(inode, 1); ++ d_tmpfile(dentry, inode); ++ au_di(dentry)->di_tmpfile = 1; ++ ++ /* update without i_mutex */ ++ if (au_ibtop(dir) == au_dbtop(dentry)) ++ au_cpup_attr_timesizes(dir); ++ } ++ ++out_dentry: ++ dput(h_dentry); ++out_mnt: ++ vfsub_mnt_drop_write(h_mnt); ++out_parent: ++ di_write_unlock(parent); ++ dput(parent); ++ di_write_unlock(dentry); ++ if (unlikely(err)) { ++ au_di_fin(dentry); ++ dentry->d_fsdata = NULL; ++ } ++out_si: ++ si_read_unlock(sb); ++out: ++ inode_unlock(dir); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct au_link_args { ++ aufs_bindex_t bdst, bsrc; ++ struct au_pin pin; ++ struct path h_path; ++ struct dentry *src_parent, *parent; ++}; ++ ++static int au_cpup_before_link(struct dentry *src_dentry, ++ struct au_link_args *a) ++{ ++ int err; ++ struct dentry *h_src_dentry; ++ struct au_cp_generic cpg = { ++ .dentry = src_dentry, ++ .bdst = a->bdst, ++ .bsrc = a->bsrc, ++ .len = -1, ++ .pin = &a->pin, ++ .flags = AuCpup_DTIME | AuCpup_HOPEN /* | AuCpup_KEEPLINO */ ++ }; ++ ++ di_read_lock_parent(a->src_parent, AuLock_IR); ++ err = au_test_and_cpup_dirs(src_dentry, a->bdst); ++ if (unlikely(err)) ++ goto out; ++ ++ h_src_dentry = au_h_dptr(src_dentry, a->bsrc); ++ err = au_pin(&a->pin, src_dentry, a->bdst, ++ au_opt_udba(src_dentry->d_sb), ++ AuPin_DI_LOCKED | AuPin_MNT_WRITE); ++ if (unlikely(err)) ++ goto out; ++ ++ err = au_sio_cpup_simple(&cpg); ++ au_unpin(&a->pin); ++ ++out: ++ di_read_unlock(a->src_parent, AuLock_IR); ++ return err; ++} ++ ++static int au_cpup_or_link(struct dentry *src_dentry, struct dentry *dentry, ++ struct au_link_args *a) ++{ ++ int err; ++ unsigned char plink; ++ aufs_bindex_t bbot; ++ struct dentry *h_src_dentry; ++ struct inode *h_inode, *inode, *delegated; ++ struct super_block *sb; ++ struct file *h_file; ++ ++ plink = 0; ++ h_inode = NULL; ++ sb = src_dentry->d_sb; ++ inode = d_inode(src_dentry); ++ if (au_ibtop(inode) <= a->bdst) ++ h_inode = au_h_iptr(inode, a->bdst); ++ if (!h_inode || !h_inode->i_nlink) { ++ /* copyup src_dentry as the name of dentry. */ ++ bbot = au_dbbot(dentry); ++ if (bbot < a->bsrc) ++ au_set_dbbot(dentry, a->bsrc); ++ au_set_h_dptr(dentry, a->bsrc, ++ dget(au_h_dptr(src_dentry, a->bsrc))); ++ dget(a->h_path.dentry); ++ au_set_h_dptr(dentry, a->bdst, NULL); ++ AuDbg("temporary d_inode...\n"); ++ spin_lock(&dentry->d_lock); ++ dentry->d_inode = d_inode(src_dentry); /* tmp */ ++ spin_unlock(&dentry->d_lock); ++ h_file = au_h_open_pre(dentry, a->bsrc, /*force_wr*/0); ++ if (IS_ERR(h_file)) ++ err = PTR_ERR(h_file); ++ else { ++ struct au_cp_generic cpg = { ++ .dentry = dentry, ++ .bdst = a->bdst, ++ .bsrc = -1, ++ .len = -1, ++ .pin = &a->pin, ++ .flags = AuCpup_KEEPLINO ++ }; ++ err = au_sio_cpup_simple(&cpg); ++ au_h_open_post(dentry, a->bsrc, h_file); ++ if (!err) { ++ dput(a->h_path.dentry); ++ a->h_path.dentry = au_h_dptr(dentry, a->bdst); ++ } else ++ au_set_h_dptr(dentry, a->bdst, ++ a->h_path.dentry); ++ } ++ spin_lock(&dentry->d_lock); ++ dentry->d_inode = NULL; /* restore */ ++ spin_unlock(&dentry->d_lock); ++ AuDbg("temporary d_inode...done\n"); ++ au_set_h_dptr(dentry, a->bsrc, NULL); ++ au_set_dbbot(dentry, bbot); ++ } else { ++ /* the inode of src_dentry already exists on a.bdst branch */ ++ h_src_dentry = d_find_alias(h_inode); ++ if (!h_src_dentry && au_plink_test(inode)) { ++ plink = 1; ++ h_src_dentry = au_plink_lkup(inode, a->bdst); ++ err = PTR_ERR(h_src_dentry); ++ if (IS_ERR(h_src_dentry)) ++ goto out; ++ ++ if (unlikely(d_is_negative(h_src_dentry))) { ++ dput(h_src_dentry); ++ h_src_dentry = NULL; ++ } ++ ++ } ++ if (h_src_dentry) { ++ delegated = NULL; ++ err = vfsub_link(h_src_dentry, au_pinned_h_dir(&a->pin), ++ &a->h_path, &delegated); ++ if (unlikely(err == -EWOULDBLOCK)) { ++ pr_warn("cannot retry for NFSv4 delegation" ++ " for an internal link\n"); ++ iput(delegated); ++ } ++ dput(h_src_dentry); ++ } else { ++ AuIOErr("no dentry found for hi%lu on b%d\n", ++ h_inode->i_ino, a->bdst); ++ err = -EIO; ++ } ++ } ++ ++ if (!err && !plink) ++ au_plink_append(inode, a->bdst, a->h_path.dentry); ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++int aufs_link(struct dentry *src_dentry, struct inode *dir, ++ struct dentry *dentry) ++{ ++ int err, rerr; ++ struct au_dtime dt; ++ struct au_link_args *a; ++ struct dentry *wh_dentry, *h_src_dentry; ++ struct inode *inode, *delegated; ++ struct super_block *sb; ++ struct au_wr_dir_args wr_dir_args = { ++ /* .force_btgt = -1, */ ++ .flags = AuWrDir_ADD_ENTRY ++ }; ++ ++ IMustLock(dir); ++ inode = d_inode(src_dentry); ++ IMustLock(inode); ++ ++ err = -ENOMEM; ++ a = kzalloc(sizeof(*a), GFP_NOFS); ++ if (unlikely(!a)) ++ goto out; ++ ++ a->parent = dentry->d_parent; /* dir inode is locked */ ++ err = aufs_read_and_write_lock2(dentry, src_dentry, ++ AuLock_NOPLM | AuLock_GEN); ++ if (unlikely(err)) ++ goto out_kfree; ++ err = au_d_linkable(src_dentry); ++ if (unlikely(err)) ++ goto out_unlock; ++ err = au_d_may_add(dentry); ++ if (unlikely(err)) ++ goto out_unlock; ++ ++ a->src_parent = dget_parent(src_dentry); ++ wr_dir_args.force_btgt = au_ibtop(inode); ++ ++ di_write_lock_parent(a->parent); ++ wr_dir_args.force_btgt = au_wbr(dentry, wr_dir_args.force_btgt); ++ wh_dentry = lock_hdir_lkup_wh(dentry, &dt, src_dentry, &a->pin, ++ &wr_dir_args); ++ err = PTR_ERR(wh_dentry); ++ if (IS_ERR(wh_dentry)) ++ goto out_parent; ++ ++ err = 0; ++ sb = dentry->d_sb; ++ a->bdst = au_dbtop(dentry); ++ a->h_path.dentry = au_h_dptr(dentry, a->bdst); ++ a->h_path.mnt = au_sbr_mnt(sb, a->bdst); ++ a->bsrc = au_ibtop(inode); ++ h_src_dentry = au_h_d_alias(src_dentry, a->bsrc); ++ if (!h_src_dentry && au_di(src_dentry)->di_tmpfile) ++ h_src_dentry = dget(au_hi_wh(inode, a->bsrc)); ++ if (!h_src_dentry) { ++ a->bsrc = au_dbtop(src_dentry); ++ h_src_dentry = au_h_d_alias(src_dentry, a->bsrc); ++ AuDebugOn(!h_src_dentry); ++ } else if (IS_ERR(h_src_dentry)) { ++ err = PTR_ERR(h_src_dentry); ++ goto out_parent; ++ } ++ ++ if (au_opt_test(au_mntflags(sb), PLINK)) { ++ if (a->bdst < a->bsrc ++ /* && h_src_dentry->d_sb != a->h_path.dentry->d_sb */) ++ err = au_cpup_or_link(src_dentry, dentry, a); ++ else { ++ delegated = NULL; ++ err = vfsub_link(h_src_dentry, au_pinned_h_dir(&a->pin), ++ &a->h_path, &delegated); ++ if (unlikely(err == -EWOULDBLOCK)) { ++ pr_warn("cannot retry for NFSv4 delegation" ++ " for an internal link\n"); ++ iput(delegated); ++ } ++ } ++ dput(h_src_dentry); ++ } else { ++ /* ++ * copyup src_dentry to the branch we process, ++ * and then link(2) to it. ++ */ ++ dput(h_src_dentry); ++ if (a->bdst < a->bsrc ++ /* && h_src_dentry->d_sb != a->h_path.dentry->d_sb */) { ++ au_unpin(&a->pin); ++ di_write_unlock(a->parent); ++ err = au_cpup_before_link(src_dentry, a); ++ di_write_lock_parent(a->parent); ++ if (!err) ++ err = au_pin(&a->pin, dentry, a->bdst, ++ au_opt_udba(sb), ++ AuPin_DI_LOCKED | AuPin_MNT_WRITE); ++ if (unlikely(err)) ++ goto out_wh; ++ } ++ if (!err) { ++ h_src_dentry = au_h_dptr(src_dentry, a->bdst); ++ err = -ENOENT; ++ if (h_src_dentry && d_is_positive(h_src_dentry)) { ++ delegated = NULL; ++ err = vfsub_link(h_src_dentry, ++ au_pinned_h_dir(&a->pin), ++ &a->h_path, &delegated); ++ if (unlikely(err == -EWOULDBLOCK)) { ++ pr_warn("cannot retry" ++ " for NFSv4 delegation" ++ " for an internal link\n"); ++ iput(delegated); ++ } ++ } ++ } ++ } ++ if (unlikely(err)) ++ goto out_unpin; ++ ++ if (wh_dentry) { ++ a->h_path.dentry = wh_dentry; ++ err = au_wh_unlink_dentry(au_pinned_h_dir(&a->pin), &a->h_path, ++ dentry); ++ if (unlikely(err)) ++ goto out_revert; ++ } ++ ++ au_dir_ts(dir, a->bdst); ++ dir->i_version++; ++ inc_nlink(inode); ++ inode->i_ctime = dir->i_ctime; ++ d_instantiate(dentry, au_igrab(inode)); ++ if (d_unhashed(a->h_path.dentry)) ++ /* some filesystem calls d_drop() */ ++ d_drop(dentry); ++ /* some filesystems consume an inode even hardlink */ ++ au_fhsm_wrote(sb, a->bdst, /*force*/0); ++ goto out_unpin; /* success */ ++ ++out_revert: ++ /* no delegation since it is just created */ ++ rerr = vfsub_unlink(au_pinned_h_dir(&a->pin), &a->h_path, ++ /*delegated*/NULL, /*force*/0); ++ if (unlikely(rerr)) { ++ AuIOErr("%pd reverting failed(%d, %d)\n", dentry, err, rerr); ++ err = -EIO; ++ } ++ au_dtime_revert(&dt); ++out_unpin: ++ au_unpin(&a->pin); ++out_wh: ++ dput(wh_dentry); ++out_parent: ++ di_write_unlock(a->parent); ++ dput(a->src_parent); ++out_unlock: ++ if (unlikely(err)) { ++ au_update_dbtop(dentry); ++ d_drop(dentry); ++ } ++ aufs_read_and_write_unlock2(dentry, src_dentry); ++out_kfree: ++ kfree(a); ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++int aufs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) ++{ ++ int err, rerr; ++ aufs_bindex_t bindex; ++ unsigned char diropq; ++ struct path h_path; ++ struct dentry *wh_dentry, *parent, *opq_dentry; ++ struct inode *h_inode; ++ struct super_block *sb; ++ struct { ++ struct au_pin pin; ++ struct au_dtime dt; ++ } *a; /* reduce the stack usage */ ++ struct au_wr_dir_args wr_dir_args = { ++ .force_btgt = -1, ++ .flags = AuWrDir_ADD_ENTRY | AuWrDir_ISDIR ++ }; ++ ++ IMustLock(dir); ++ ++ err = -ENOMEM; ++ a = kmalloc(sizeof(*a), GFP_NOFS); ++ if (unlikely(!a)) ++ goto out; ++ ++ err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN); ++ if (unlikely(err)) ++ goto out_free; ++ err = au_d_may_add(dentry); ++ if (unlikely(err)) ++ goto out_unlock; ++ ++ parent = dentry->d_parent; /* dir inode is locked */ ++ di_write_lock_parent(parent); ++ wh_dentry = lock_hdir_lkup_wh(dentry, &a->dt, /*src_dentry*/NULL, ++ &a->pin, &wr_dir_args); ++ err = PTR_ERR(wh_dentry); ++ if (IS_ERR(wh_dentry)) ++ goto out_parent; ++ ++ sb = dentry->d_sb; ++ bindex = au_dbtop(dentry); ++ h_path.dentry = au_h_dptr(dentry, bindex); ++ h_path.mnt = au_sbr_mnt(sb, bindex); ++ err = vfsub_mkdir(au_pinned_h_dir(&a->pin), &h_path, mode); ++ if (unlikely(err)) ++ goto out_unpin; ++ ++ /* make the dir opaque */ ++ diropq = 0; ++ h_inode = d_inode(h_path.dentry); ++ if (wh_dentry ++ || au_opt_test(au_mntflags(sb), ALWAYS_DIROPQ)) { ++ inode_lock_nested(h_inode, AuLsc_I_CHILD); ++ opq_dentry = au_diropq_create(dentry, bindex); ++ inode_unlock(h_inode); ++ err = PTR_ERR(opq_dentry); ++ if (IS_ERR(opq_dentry)) ++ goto out_dir; ++ dput(opq_dentry); ++ diropq = 1; ++ } ++ ++ err = epilog(dir, bindex, wh_dentry, dentry); ++ if (!err) { ++ inc_nlink(dir); ++ goto out_unpin; /* success */ ++ } ++ ++ /* revert */ ++ if (diropq) { ++ AuLabel(revert opq); ++ inode_lock_nested(h_inode, AuLsc_I_CHILD); ++ rerr = au_diropq_remove(dentry, bindex); ++ inode_unlock(h_inode); ++ if (rerr) { ++ AuIOErr("%pd reverting diropq failed(%d, %d)\n", ++ dentry, err, rerr); ++ err = -EIO; ++ } ++ } ++ ++out_dir: ++ AuLabel(revert dir); ++ rerr = vfsub_rmdir(au_pinned_h_dir(&a->pin), &h_path); ++ if (rerr) { ++ AuIOErr("%pd reverting dir failed(%d, %d)\n", ++ dentry, err, rerr); ++ err = -EIO; ++ } ++ au_dtime_revert(&a->dt); ++out_unpin: ++ au_unpin(&a->pin); ++ dput(wh_dentry); ++out_parent: ++ di_write_unlock(parent); ++out_unlock: ++ if (unlikely(err)) { ++ au_update_dbtop(dentry); ++ d_drop(dentry); ++ } ++ aufs_read_unlock(dentry, AuLock_DW); ++out_free: ++ kfree(a); ++out: ++ return err; ++} +diff --git a/fs/aufs/i_op_del.c b/fs/aufs/i_op_del.c +new file mode 100644 +index 0000000..bda70b0 +--- /dev/null ++++ b/fs/aufs/i_op_del.c +@@ -0,0 +1,497 @@ ++/* ++ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ */ ++ ++/* ++ * inode operations (del entry) ++ */ ++ ++#include "aufs.h" ++ ++/* ++ * decide if a new whiteout for @dentry is necessary or not. ++ * when it is necessary, prepare the parent dir for the upper branch whose ++ * branch index is @bcpup for creation. the actual creation of the whiteout will ++ * be done by caller. ++ * return value: ++ * 0: wh is unnecessary ++ * plus: wh is necessary ++ * minus: error ++ */ ++int au_wr_dir_need_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup) ++{ ++ int need_wh, err; ++ aufs_bindex_t btop; ++ struct super_block *sb; ++ ++ sb = dentry->d_sb; ++ btop = au_dbtop(dentry); ++ if (*bcpup < 0) { ++ *bcpup = btop; ++ if (au_test_ro(sb, btop, d_inode(dentry))) { ++ err = AuWbrCopyup(au_sbi(sb), dentry); ++ *bcpup = err; ++ if (unlikely(err < 0)) ++ goto out; ++ } ++ } else ++ AuDebugOn(btop < *bcpup ++ || au_test_ro(sb, *bcpup, d_inode(dentry))); ++ AuDbg("bcpup %d, btop %d\n", *bcpup, btop); ++ ++ if (*bcpup != btop) { ++ err = au_cpup_dirs(dentry, *bcpup); ++ if (unlikely(err)) ++ goto out; ++ need_wh = 1; ++ } else { ++ struct au_dinfo *dinfo, *tmp; ++ ++ need_wh = -ENOMEM; ++ dinfo = au_di(dentry); ++ tmp = au_di_alloc(sb, AuLsc_DI_TMP); ++ if (tmp) { ++ au_di_cp(tmp, dinfo); ++ au_di_swap(tmp, dinfo); ++ /* returns the number of positive dentries */ ++ need_wh = au_lkup_dentry(dentry, btop + 1, /*type*/0); ++ au_di_swap(tmp, dinfo); ++ au_rw_write_unlock(&tmp->di_rwsem); ++ au_di_free(tmp); ++ } ++ } ++ AuDbg("need_wh %d\n", need_wh); ++ err = need_wh; ++ ++out: ++ return err; ++} ++ ++/* ++ * simple tests for the del-entry operations. ++ * following the checks in vfs, plus the parent-child relationship. ++ */ ++int au_may_del(struct dentry *dentry, aufs_bindex_t bindex, ++ struct dentry *h_parent, int isdir) ++{ ++ int err; ++ umode_t h_mode; ++ struct dentry *h_dentry, *h_latest; ++ struct inode *h_inode; ++ ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (d_really_is_positive(dentry)) { ++ err = -ENOENT; ++ if (unlikely(d_is_negative(h_dentry))) ++ goto out; ++ h_inode = d_inode(h_dentry); ++ if (unlikely(!h_inode->i_nlink)) ++ goto out; ++ ++ h_mode = h_inode->i_mode; ++ if (!isdir) { ++ err = -EISDIR; ++ if (unlikely(S_ISDIR(h_mode))) ++ goto out; ++ } else if (unlikely(!S_ISDIR(h_mode))) { ++ err = -ENOTDIR; ++ goto out; ++ } ++ } else { ++ /* rename(2) case */ ++ err = -EIO; ++ if (unlikely(d_is_positive(h_dentry))) ++ goto out; ++ } ++ ++ err = -ENOENT; ++ /* expected parent dir is locked */ ++ if (unlikely(h_parent != h_dentry->d_parent)) ++ goto out; ++ err = 0; ++ ++ /* ++ * rmdir a dir may break the consistency on some filesystem. ++ * let's try heavy test. ++ */ ++ err = -EACCES; ++ if (unlikely(!au_opt_test(au_mntflags(dentry->d_sb), DIRPERM1) ++ && au_test_h_perm(d_inode(h_parent), ++ MAY_EXEC | MAY_WRITE))) ++ goto out; ++ ++ h_latest = au_sio_lkup_one(&dentry->d_name, h_parent); ++ err = -EIO; ++ if (IS_ERR(h_latest)) ++ goto out; ++ if (h_latest == h_dentry) ++ err = 0; ++ dput(h_latest); ++ ++out: ++ return err; ++} ++ ++/* ++ * decide the branch where we operate for @dentry. the branch index will be set ++ * @rbcpup. after diciding it, 'pin' it and store the timestamps of the parent ++ * dir for reverting. ++ * when a new whiteout is necessary, create it. ++ */ ++static struct dentry* ++lock_hdir_create_wh(struct dentry *dentry, int isdir, aufs_bindex_t *rbcpup, ++ struct au_dtime *dt, struct au_pin *pin) ++{ ++ struct dentry *wh_dentry; ++ struct super_block *sb; ++ struct path h_path; ++ int err, need_wh; ++ unsigned int udba; ++ aufs_bindex_t bcpup; ++ ++ need_wh = au_wr_dir_need_wh(dentry, isdir, rbcpup); ++ wh_dentry = ERR_PTR(need_wh); ++ if (unlikely(need_wh < 0)) ++ goto out; ++ ++ sb = dentry->d_sb; ++ udba = au_opt_udba(sb); ++ bcpup = *rbcpup; ++ err = au_pin(pin, dentry, bcpup, udba, ++ AuPin_DI_LOCKED | AuPin_MNT_WRITE); ++ wh_dentry = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out; ++ ++ h_path.dentry = au_pinned_h_parent(pin); ++ if (udba != AuOpt_UDBA_NONE ++ && au_dbtop(dentry) == bcpup) { ++ err = au_may_del(dentry, bcpup, h_path.dentry, isdir); ++ wh_dentry = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out_unpin; ++ } ++ ++ h_path.mnt = au_sbr_mnt(sb, bcpup); ++ au_dtime_store(dt, au_pinned_parent(pin), &h_path); ++ wh_dentry = NULL; ++ if (!need_wh) ++ goto out; /* success, no need to create whiteout */ ++ ++ wh_dentry = au_wh_create(dentry, bcpup, h_path.dentry); ++ if (IS_ERR(wh_dentry)) ++ goto out_unpin; ++ ++ /* returns with the parent is locked and wh_dentry is dget-ed */ ++ goto out; /* success */ ++ ++out_unpin: ++ au_unpin(pin); ++out: ++ return wh_dentry; ++} ++ ++/* ++ * when removing a dir, rename it to a unique temporary whiteout-ed name first ++ * in order to be revertible and save time for removing many child whiteouts ++ * under the dir. ++ * returns 1 when there are too many child whiteout and caller should remove ++ * them asynchronously. returns 0 when the number of children is enough small to ++ * remove now or the branch fs is a remote fs. ++ * otherwise return an error. ++ */ ++static int renwh_and_rmdir(struct dentry *dentry, aufs_bindex_t bindex, ++ struct au_nhash *whlist, struct inode *dir) ++{ ++ int rmdir_later, err, dirwh; ++ struct dentry *h_dentry; ++ struct super_block *sb; ++ struct inode *inode; ++ ++ sb = dentry->d_sb; ++ SiMustAnyLock(sb); ++ h_dentry = au_h_dptr(dentry, bindex); ++ err = au_whtmp_ren(h_dentry, au_sbr(sb, bindex)); ++ if (unlikely(err)) ++ goto out; ++ ++ /* stop monitoring */ ++ inode = d_inode(dentry); ++ au_hn_free(au_hi(inode, bindex)); ++ ++ if (!au_test_fs_remote(h_dentry->d_sb)) { ++ dirwh = au_sbi(sb)->si_dirwh; ++ rmdir_later = (dirwh <= 1); ++ if (!rmdir_later) ++ rmdir_later = au_nhash_test_longer_wh(whlist, bindex, ++ dirwh); ++ if (rmdir_later) ++ return rmdir_later; ++ } ++ ++ err = au_whtmp_rmdir(dir, bindex, h_dentry, whlist); ++ if (unlikely(err)) { ++ AuIOErr("rmdir %pd, b%d failed, %d. ignored\n", ++ h_dentry, bindex, err); ++ err = 0; ++ } ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ++ * final procedure for deleting a entry. ++ * maintain dentry and iattr. ++ */ ++static void epilog(struct inode *dir, struct dentry *dentry, ++ aufs_bindex_t bindex) ++{ ++ struct inode *inode; ++ ++ inode = d_inode(dentry); ++ d_drop(dentry); ++ inode->i_ctime = dir->i_ctime; ++ ++ au_dir_ts(dir, bindex); ++ dir->i_version++; ++} ++ ++/* ++ * when an error happened, remove the created whiteout and revert everything. ++ */ ++static int do_revert(int err, struct inode *dir, aufs_bindex_t bindex, ++ aufs_bindex_t bwh, struct dentry *wh_dentry, ++ struct dentry *dentry, struct au_dtime *dt) ++{ ++ int rerr; ++ struct path h_path = { ++ .dentry = wh_dentry, ++ .mnt = au_sbr_mnt(dir->i_sb, bindex) ++ }; ++ ++ rerr = au_wh_unlink_dentry(au_h_iptr(dir, bindex), &h_path, dentry); ++ if (!rerr) { ++ au_set_dbwh(dentry, bwh); ++ au_dtime_revert(dt); ++ return 0; ++ } ++ ++ AuIOErr("%pd reverting whiteout failed(%d, %d)\n", dentry, err, rerr); ++ return -EIO; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int aufs_unlink(struct inode *dir, struct dentry *dentry) ++{ ++ int err; ++ aufs_bindex_t bwh, bindex, btop; ++ struct inode *inode, *h_dir, *delegated; ++ struct dentry *parent, *wh_dentry; ++ /* to reuduce stack size */ ++ struct { ++ struct au_dtime dt; ++ struct au_pin pin; ++ struct path h_path; ++ } *a; ++ ++ IMustLock(dir); ++ ++ err = -ENOMEM; ++ a = kmalloc(sizeof(*a), GFP_NOFS); ++ if (unlikely(!a)) ++ goto out; ++ ++ err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN); ++ if (unlikely(err)) ++ goto out_free; ++ err = au_d_hashed_positive(dentry); ++ if (unlikely(err)) ++ goto out_unlock; ++ inode = d_inode(dentry); ++ IMustLock(inode); ++ err = -EISDIR; ++ if (unlikely(d_is_dir(dentry))) ++ goto out_unlock; /* possible? */ ++ ++ btop = au_dbtop(dentry); ++ bwh = au_dbwh(dentry); ++ bindex = -1; ++ parent = dentry->d_parent; /* dir inode is locked */ ++ di_write_lock_parent(parent); ++ wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/0, &bindex, &a->dt, ++ &a->pin); ++ err = PTR_ERR(wh_dentry); ++ if (IS_ERR(wh_dentry)) ++ goto out_parent; ++ ++ a->h_path.mnt = au_sbr_mnt(dentry->d_sb, btop); ++ a->h_path.dentry = au_h_dptr(dentry, btop); ++ dget(a->h_path.dentry); ++ if (bindex == btop) { ++ h_dir = au_pinned_h_dir(&a->pin); ++ delegated = NULL; ++ err = vfsub_unlink(h_dir, &a->h_path, &delegated, /*force*/0); ++ if (unlikely(err == -EWOULDBLOCK)) { ++ pr_warn("cannot retry for NFSv4 delegation" ++ " for an internal unlink\n"); ++ iput(delegated); ++ } ++ } else { ++ /* dir inode is locked */ ++ h_dir = d_inode(wh_dentry->d_parent); ++ IMustLock(h_dir); ++ err = 0; ++ } ++ ++ if (!err) { ++ vfsub_drop_nlink(inode); ++ epilog(dir, dentry, bindex); ++ ++ /* update target timestamps */ ++ if (bindex == btop) { ++ vfsub_update_h_iattr(&a->h_path, /*did*/NULL); ++ /*ignore*/ ++ inode->i_ctime = d_inode(a->h_path.dentry)->i_ctime; ++ } else ++ /* todo: this timestamp may be reverted later */ ++ inode->i_ctime = h_dir->i_ctime; ++ goto out_unpin; /* success */ ++ } ++ ++ /* revert */ ++ if (wh_dentry) { ++ int rerr; ++ ++ rerr = do_revert(err, dir, bindex, bwh, wh_dentry, dentry, ++ &a->dt); ++ if (rerr) ++ err = rerr; ++ } ++ ++out_unpin: ++ au_unpin(&a->pin); ++ dput(wh_dentry); ++ dput(a->h_path.dentry); ++out_parent: ++ di_write_unlock(parent); ++out_unlock: ++ aufs_read_unlock(dentry, AuLock_DW); ++out_free: ++ kfree(a); ++out: ++ return err; ++} ++ ++int aufs_rmdir(struct inode *dir, struct dentry *dentry) ++{ ++ int err, rmdir_later; ++ aufs_bindex_t bwh, bindex, btop; ++ struct inode *inode; ++ struct dentry *parent, *wh_dentry, *h_dentry; ++ struct au_whtmp_rmdir *args; ++ /* to reuduce stack size */ ++ struct { ++ struct au_dtime dt; ++ struct au_pin pin; ++ } *a; ++ ++ IMustLock(dir); ++ ++ err = -ENOMEM; ++ a = kmalloc(sizeof(*a), GFP_NOFS); ++ if (unlikely(!a)) ++ goto out; ++ ++ err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH | AuLock_GEN); ++ if (unlikely(err)) ++ goto out_free; ++ err = au_alive_dir(dentry); ++ if (unlikely(err)) ++ goto out_unlock; ++ inode = d_inode(dentry); ++ IMustLock(inode); ++ err = -ENOTDIR; ++ if (unlikely(!d_is_dir(dentry))) ++ goto out_unlock; /* possible? */ ++ ++ err = -ENOMEM; ++ args = au_whtmp_rmdir_alloc(dir->i_sb, GFP_NOFS); ++ if (unlikely(!args)) ++ goto out_unlock; ++ ++ parent = dentry->d_parent; /* dir inode is locked */ ++ di_write_lock_parent(parent); ++ err = au_test_empty(dentry, &args->whlist); ++ if (unlikely(err)) ++ goto out_parent; ++ ++ btop = au_dbtop(dentry); ++ bwh = au_dbwh(dentry); ++ bindex = -1; ++ wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/1, &bindex, &a->dt, ++ &a->pin); ++ err = PTR_ERR(wh_dentry); ++ if (IS_ERR(wh_dentry)) ++ goto out_parent; ++ ++ h_dentry = au_h_dptr(dentry, btop); ++ dget(h_dentry); ++ rmdir_later = 0; ++ if (bindex == btop) { ++ err = renwh_and_rmdir(dentry, btop, &args->whlist, dir); ++ if (err > 0) { ++ rmdir_later = err; ++ err = 0; ++ } ++ } else { ++ /* stop monitoring */ ++ au_hn_free(au_hi(inode, btop)); ++ ++ /* dir inode is locked */ ++ IMustLock(d_inode(wh_dentry->d_parent)); ++ err = 0; ++ } ++ ++ if (!err) { ++ vfsub_dead_dir(inode); ++ au_set_dbdiropq(dentry, -1); ++ epilog(dir, dentry, bindex); ++ ++ if (rmdir_later) { ++ au_whtmp_kick_rmdir(dir, btop, h_dentry, args); ++ args = NULL; ++ } ++ ++ goto out_unpin; /* success */ ++ } ++ ++ /* revert */ ++ AuLabel(revert); ++ if (wh_dentry) { ++ int rerr; ++ ++ rerr = do_revert(err, dir, bindex, bwh, wh_dentry, dentry, ++ &a->dt); ++ if (rerr) ++ err = rerr; ++ } ++ ++out_unpin: ++ au_unpin(&a->pin); ++ dput(wh_dentry); ++ dput(h_dentry); ++out_parent: ++ di_write_unlock(parent); ++ if (args) ++ au_whtmp_rmdir_free(args); ++out_unlock: ++ aufs_read_unlock(dentry, AuLock_DW); ++out_free: ++ kfree(a); ++out: ++ AuTraceErr(err); ++ return err; ++} +diff --git a/fs/aufs/i_op_ren.c b/fs/aufs/i_op_ren.c +new file mode 100644 +index 0000000..fdc2992 +--- /dev/null ++++ b/fs/aufs/i_op_ren.c +@@ -0,0 +1,1002 @@ ++/* ++ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ */ ++ ++/* ++ * inode operation (rename entry) ++ * todo: this is crazy monster ++ */ ++ ++#include "aufs.h" ++ ++enum { AuSRC, AuDST, AuSrcDst }; ++enum { AuPARENT, AuCHILD, AuParentChild }; ++ ++#define AuRen_ISDIR 1 ++#define AuRen_ISSAMEDIR (1 << 1) ++#define AuRen_WHSRC (1 << 2) ++#define AuRen_WHDST (1 << 3) ++#define AuRen_MNT_WRITE (1 << 4) ++#define AuRen_DT_DSTDIR (1 << 5) ++#define AuRen_DIROPQ (1 << 6) ++#define au_ftest_ren(flags, name) ((flags) & AuRen_##name) ++#define au_fset_ren(flags, name) \ ++ do { (flags) |= AuRen_##name; } while (0) ++#define au_fclr_ren(flags, name) \ ++ do { (flags) &= ~AuRen_##name; } while (0) ++ ++struct au_ren_args { ++ struct { ++ struct dentry *dentry, *h_dentry, *parent, *h_parent, ++ *wh_dentry; ++ struct inode *dir, *inode; ++ struct au_hinode *hdir; ++ struct au_dtime dt[AuParentChild]; ++ aufs_bindex_t btop; ++ } sd[AuSrcDst]; ++ ++#define src_dentry sd[AuSRC].dentry ++#define src_dir sd[AuSRC].dir ++#define src_inode sd[AuSRC].inode ++#define src_h_dentry sd[AuSRC].h_dentry ++#define src_parent sd[AuSRC].parent ++#define src_h_parent sd[AuSRC].h_parent ++#define src_wh_dentry sd[AuSRC].wh_dentry ++#define src_hdir sd[AuSRC].hdir ++#define src_h_dir sd[AuSRC].hdir->hi_inode ++#define src_dt sd[AuSRC].dt ++#define src_btop sd[AuSRC].btop ++ ++#define dst_dentry sd[AuDST].dentry ++#define dst_dir sd[AuDST].dir ++#define dst_inode sd[AuDST].inode ++#define dst_h_dentry sd[AuDST].h_dentry ++#define dst_parent sd[AuDST].parent ++#define dst_h_parent sd[AuDST].h_parent ++#define dst_wh_dentry sd[AuDST].wh_dentry ++#define dst_hdir sd[AuDST].hdir ++#define dst_h_dir sd[AuDST].hdir->hi_inode ++#define dst_dt sd[AuDST].dt ++#define dst_btop sd[AuDST].btop ++ ++ struct dentry *h_trap; ++ struct au_branch *br; ++ struct au_hinode *src_hinode; ++ struct path h_path; ++ struct au_nhash whlist; ++ aufs_bindex_t btgt, src_bwh, src_bdiropq; ++ ++ unsigned int flags; ++ ++ struct au_whtmp_rmdir *thargs; ++ struct dentry *h_dst; ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * functions for reverting. ++ * when an error happened in a single rename systemcall, we should revert ++ * everything as if nothing happened. ++ * we don't need to revert the copied-up/down the parent dir since they are ++ * harmless. ++ */ ++ ++#define RevertFailure(fmt, ...) do { \ ++ AuIOErr("revert failure: " fmt " (%d, %d)\n", \ ++ ##__VA_ARGS__, err, rerr); \ ++ err = -EIO; \ ++} while (0) ++ ++static void au_ren_rev_diropq(int err, struct au_ren_args *a) ++{ ++ int rerr; ++ ++ au_hn_imtx_lock_nested(a->src_hinode, AuLsc_I_CHILD); ++ rerr = au_diropq_remove(a->src_dentry, a->btgt); ++ au_hn_imtx_unlock(a->src_hinode); ++ au_set_dbdiropq(a->src_dentry, a->src_bdiropq); ++ if (rerr) ++ RevertFailure("remove diropq %pd", a->src_dentry); ++} ++ ++static void au_ren_rev_rename(int err, struct au_ren_args *a) ++{ ++ int rerr; ++ struct inode *delegated; ++ ++ a->h_path.dentry = vfsub_lkup_one(&a->src_dentry->d_name, ++ a->src_h_parent); ++ rerr = PTR_ERR(a->h_path.dentry); ++ if (IS_ERR(a->h_path.dentry)) { ++ RevertFailure("lkup one %pd", a->src_dentry); ++ return; ++ } ++ ++ delegated = NULL; ++ rerr = vfsub_rename(a->dst_h_dir, ++ au_h_dptr(a->src_dentry, a->btgt), ++ a->src_h_dir, &a->h_path, &delegated); ++ if (unlikely(rerr == -EWOULDBLOCK)) { ++ pr_warn("cannot retry for NFSv4 delegation" ++ " for an internal rename\n"); ++ iput(delegated); ++ } ++ d_drop(a->h_path.dentry); ++ dput(a->h_path.dentry); ++ /* au_set_h_dptr(a->src_dentry, a->btgt, NULL); */ ++ if (rerr) ++ RevertFailure("rename %pd", a->src_dentry); ++} ++ ++static void au_ren_rev_whtmp(int err, struct au_ren_args *a) ++{ ++ int rerr; ++ struct inode *delegated; ++ ++ a->h_path.dentry = vfsub_lkup_one(&a->dst_dentry->d_name, ++ a->dst_h_parent); ++ rerr = PTR_ERR(a->h_path.dentry); ++ if (IS_ERR(a->h_path.dentry)) { ++ RevertFailure("lkup one %pd", a->dst_dentry); ++ return; ++ } ++ if (d_is_positive(a->h_path.dentry)) { ++ d_drop(a->h_path.dentry); ++ dput(a->h_path.dentry); ++ return; ++ } ++ ++ delegated = NULL; ++ rerr = vfsub_rename(a->dst_h_dir, a->h_dst, a->dst_h_dir, &a->h_path, ++ &delegated); ++ if (unlikely(rerr == -EWOULDBLOCK)) { ++ pr_warn("cannot retry for NFSv4 delegation" ++ " for an internal rename\n"); ++ iput(delegated); ++ } ++ d_drop(a->h_path.dentry); ++ dput(a->h_path.dentry); ++ if (!rerr) ++ au_set_h_dptr(a->dst_dentry, a->btgt, dget(a->h_dst)); ++ else ++ RevertFailure("rename %pd", a->h_dst); ++} ++ ++static void au_ren_rev_whsrc(int err, struct au_ren_args *a) ++{ ++ int rerr; ++ ++ a->h_path.dentry = a->src_wh_dentry; ++ rerr = au_wh_unlink_dentry(a->src_h_dir, &a->h_path, a->src_dentry); ++ au_set_dbwh(a->src_dentry, a->src_bwh); ++ if (rerr) ++ RevertFailure("unlink %pd", a->src_wh_dentry); ++} ++#undef RevertFailure ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * when we have to copyup the renaming entry, do it with the rename-target name ++ * in order to minimize the cost (the later actual rename is unnecessary). ++ * otherwise rename it on the target branch. ++ */ ++static int au_ren_or_cpup(struct au_ren_args *a) ++{ ++ int err; ++ struct dentry *d; ++ struct inode *delegated; ++ ++ d = a->src_dentry; ++ if (au_dbtop(d) == a->btgt) { ++ a->h_path.dentry = a->dst_h_dentry; ++ if (au_ftest_ren(a->flags, DIROPQ) ++ && au_dbdiropq(d) == a->btgt) ++ au_fclr_ren(a->flags, DIROPQ); ++ AuDebugOn(au_dbtop(d) != a->btgt); ++ delegated = NULL; ++ err = vfsub_rename(a->src_h_dir, au_h_dptr(d, a->btgt), ++ a->dst_h_dir, &a->h_path, &delegated); ++ if (unlikely(err == -EWOULDBLOCK)) { ++ pr_warn("cannot retry for NFSv4 delegation" ++ " for an internal rename\n"); ++ iput(delegated); ++ } ++ } else ++ BUG(); ++ ++ if (!err && a->h_dst) ++ /* it will be set to dinfo later */ ++ dget(a->h_dst); ++ ++ return err; ++} ++ ++/* cf. aufs_rmdir() */ ++static int au_ren_del_whtmp(struct au_ren_args *a) ++{ ++ int err; ++ struct inode *dir; ++ ++ dir = a->dst_dir; ++ SiMustAnyLock(dir->i_sb); ++ if (!au_nhash_test_longer_wh(&a->whlist, a->btgt, ++ au_sbi(dir->i_sb)->si_dirwh) ++ || au_test_fs_remote(a->h_dst->d_sb)) { ++ err = au_whtmp_rmdir(dir, a->btgt, a->h_dst, &a->whlist); ++ if (unlikely(err)) ++ pr_warn("failed removing whtmp dir %pd (%d), " ++ "ignored.\n", a->h_dst, err); ++ } else { ++ au_nhash_wh_free(&a->thargs->whlist); ++ a->thargs->whlist = a->whlist; ++ a->whlist.nh_num = 0; ++ au_whtmp_kick_rmdir(dir, a->btgt, a->h_dst, a->thargs); ++ dput(a->h_dst); ++ a->thargs = NULL; ++ } ++ ++ return 0; ++} ++ ++/* make it 'opaque' dir. */ ++static int au_ren_diropq(struct au_ren_args *a) ++{ ++ int err; ++ struct dentry *diropq; ++ ++ err = 0; ++ a->src_bdiropq = au_dbdiropq(a->src_dentry); ++ a->src_hinode = au_hi(a->src_inode, a->btgt); ++ au_hn_imtx_lock_nested(a->src_hinode, AuLsc_I_CHILD); ++ diropq = au_diropq_create(a->src_dentry, a->btgt); ++ au_hn_imtx_unlock(a->src_hinode); ++ if (IS_ERR(diropq)) ++ err = PTR_ERR(diropq); ++ else ++ dput(diropq); ++ ++ return err; ++} ++ ++static int do_rename(struct au_ren_args *a) ++{ ++ int err; ++ struct dentry *d, *h_d; ++ ++ /* prepare workqueue args for asynchronous rmdir */ ++ h_d = a->dst_h_dentry; ++ if (au_ftest_ren(a->flags, ISDIR) && d_is_positive(h_d)) { ++ err = -ENOMEM; ++ a->thargs = au_whtmp_rmdir_alloc(a->src_dentry->d_sb, GFP_NOFS); ++ if (unlikely(!a->thargs)) ++ goto out; ++ a->h_dst = dget(h_d); ++ } ++ ++ /* create whiteout for src_dentry */ ++ if (au_ftest_ren(a->flags, WHSRC)) { ++ a->src_bwh = au_dbwh(a->src_dentry); ++ AuDebugOn(a->src_bwh >= 0); ++ a->src_wh_dentry ++ = au_wh_create(a->src_dentry, a->btgt, a->src_h_parent); ++ err = PTR_ERR(a->src_wh_dentry); ++ if (IS_ERR(a->src_wh_dentry)) ++ goto out_thargs; ++ } ++ ++ /* lookup whiteout for dentry */ ++ if (au_ftest_ren(a->flags, WHDST)) { ++ h_d = au_wh_lkup(a->dst_h_parent, &a->dst_dentry->d_name, ++ a->br); ++ err = PTR_ERR(h_d); ++ if (IS_ERR(h_d)) ++ goto out_whsrc; ++ if (d_is_negative(h_d)) ++ dput(h_d); ++ else ++ a->dst_wh_dentry = h_d; ++ } ++ ++ /* rename dentry to tmpwh */ ++ if (a->thargs) { ++ err = au_whtmp_ren(a->dst_h_dentry, a->br); ++ if (unlikely(err)) ++ goto out_whdst; ++ ++ d = a->dst_dentry; ++ au_set_h_dptr(d, a->btgt, NULL); ++ err = au_lkup_neg(d, a->btgt, /*wh*/0); ++ if (unlikely(err)) ++ goto out_whtmp; ++ a->dst_h_dentry = au_h_dptr(d, a->btgt); ++ } ++ ++ BUG_ON(d_is_positive(a->dst_h_dentry) && a->src_btop != a->btgt); ++ ++ /* rename by vfs_rename or cpup */ ++ d = a->dst_dentry; ++ if (au_ftest_ren(a->flags, ISDIR) ++ && (a->dst_wh_dentry ++ || au_dbdiropq(d) == a->btgt ++ /* hide the lower to keep xino */ ++ || a->btgt < au_dbbot(d) ++ || au_opt_test(au_mntflags(d->d_sb), ALWAYS_DIROPQ))) ++ au_fset_ren(a->flags, DIROPQ); ++ err = au_ren_or_cpup(a); ++ if (unlikely(err)) ++ /* leave the copied-up one */ ++ goto out_whtmp; ++ ++ /* make dir opaque */ ++ if (au_ftest_ren(a->flags, DIROPQ)) { ++ err = au_ren_diropq(a); ++ if (unlikely(err)) ++ goto out_rename; ++ } ++ ++ /* update target timestamps */ ++ AuDebugOn(au_dbtop(a->src_dentry) != a->btgt); ++ a->h_path.dentry = au_h_dptr(a->src_dentry, a->btgt); ++ vfsub_update_h_iattr(&a->h_path, /*did*/NULL); /*ignore*/ ++ a->src_inode->i_ctime = d_inode(a->h_path.dentry)->i_ctime; ++ ++ /* remove whiteout for dentry */ ++ if (a->dst_wh_dentry) { ++ a->h_path.dentry = a->dst_wh_dentry; ++ err = au_wh_unlink_dentry(a->dst_h_dir, &a->h_path, ++ a->dst_dentry); ++ if (unlikely(err)) ++ goto out_diropq; ++ } ++ ++ /* remove whtmp */ ++ if (a->thargs) ++ au_ren_del_whtmp(a); /* ignore this error */ ++ ++ au_fhsm_wrote(a->src_dentry->d_sb, a->btgt, /*force*/0); ++ err = 0; ++ goto out_success; ++ ++out_diropq: ++ if (au_ftest_ren(a->flags, DIROPQ)) ++ au_ren_rev_diropq(err, a); ++out_rename: ++ au_ren_rev_rename(err, a); ++ dput(a->h_dst); ++out_whtmp: ++ if (a->thargs) ++ au_ren_rev_whtmp(err, a); ++out_whdst: ++ dput(a->dst_wh_dentry); ++ a->dst_wh_dentry = NULL; ++out_whsrc: ++ if (a->src_wh_dentry) ++ au_ren_rev_whsrc(err, a); ++out_success: ++ dput(a->src_wh_dentry); ++ dput(a->dst_wh_dentry); ++out_thargs: ++ if (a->thargs) { ++ dput(a->h_dst); ++ au_whtmp_rmdir_free(a->thargs); ++ a->thargs = NULL; ++ } ++out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * test if @dentry dir can be rename destination or not. ++ * success means, it is a logically empty dir. ++ */ ++static int may_rename_dstdir(struct dentry *dentry, struct au_nhash *whlist) ++{ ++ return au_test_empty(dentry, whlist); ++} ++ ++/* ++ * test if @dentry dir can be rename source or not. ++ * if it can, return 0 and @children is filled. ++ * success means, ++ * - it is a logically empty dir. ++ * - or, it exists on writable branch and has no children including whiteouts ++ * on the lower branch. ++ */ ++static int may_rename_srcdir(struct dentry *dentry, aufs_bindex_t btgt) ++{ ++ int err; ++ unsigned int rdhash; ++ aufs_bindex_t btop; ++ ++ btop = au_dbtop(dentry); ++ if (btop != btgt) { ++ struct au_nhash whlist; ++ ++ SiMustAnyLock(dentry->d_sb); ++ rdhash = au_sbi(dentry->d_sb)->si_rdhash; ++ if (!rdhash) ++ rdhash = au_rdhash_est(au_dir_size(/*file*/NULL, ++ dentry)); ++ err = au_nhash_alloc(&whlist, rdhash, GFP_NOFS); ++ if (unlikely(err)) ++ goto out; ++ err = au_test_empty(dentry, &whlist); ++ au_nhash_wh_free(&whlist); ++ goto out; ++ } ++ ++ if (btop == au_dbtaildir(dentry)) ++ return 0; /* success */ ++ ++ err = au_test_empty_lower(dentry); ++ ++out: ++ if (err == -ENOTEMPTY) { ++ AuWarn1("renaming dir who has child(ren) on multiple branches," ++ " is not supported\n"); ++ err = -EXDEV; ++ } ++ return err; ++} ++ ++/* side effect: sets whlist and h_dentry */ ++static int au_ren_may_dir(struct au_ren_args *a) ++{ ++ int err; ++ unsigned int rdhash; ++ struct dentry *d; ++ ++ d = a->dst_dentry; ++ SiMustAnyLock(d->d_sb); ++ ++ err = 0; ++ if (au_ftest_ren(a->flags, ISDIR) && a->dst_inode) { ++ rdhash = au_sbi(d->d_sb)->si_rdhash; ++ if (!rdhash) ++ rdhash = au_rdhash_est(au_dir_size(/*file*/NULL, d)); ++ err = au_nhash_alloc(&a->whlist, rdhash, GFP_NOFS); ++ if (unlikely(err)) ++ goto out; ++ ++ au_set_dbtop(d, a->dst_btop); ++ err = may_rename_dstdir(d, &a->whlist); ++ au_set_dbtop(d, a->btgt); ++ } ++ a->dst_h_dentry = au_h_dptr(d, au_dbtop(d)); ++ if (unlikely(err)) ++ goto out; ++ ++ d = a->src_dentry; ++ a->src_h_dentry = au_h_dptr(d, au_dbtop(d)); ++ if (au_ftest_ren(a->flags, ISDIR)) { ++ err = may_rename_srcdir(d, a->btgt); ++ if (unlikely(err)) { ++ au_nhash_wh_free(&a->whlist); ++ a->whlist.nh_num = 0; ++ } ++ } ++out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * simple tests for rename. ++ * following the checks in vfs, plus the parent-child relationship. ++ */ ++static int au_may_ren(struct au_ren_args *a) ++{ ++ int err, isdir; ++ struct inode *h_inode; ++ ++ if (a->src_btop == a->btgt) { ++ err = au_may_del(a->src_dentry, a->btgt, a->src_h_parent, ++ au_ftest_ren(a->flags, ISDIR)); ++ if (unlikely(err)) ++ goto out; ++ err = -EINVAL; ++ if (unlikely(a->src_h_dentry == a->h_trap)) ++ goto out; ++ } ++ ++ err = 0; ++ if (a->dst_btop != a->btgt) ++ goto out; ++ ++ err = -ENOTEMPTY; ++ if (unlikely(a->dst_h_dentry == a->h_trap)) ++ goto out; ++ ++ err = -EIO; ++ isdir = !!au_ftest_ren(a->flags, ISDIR); ++ if (d_really_is_negative(a->dst_dentry)) { ++ if (d_is_negative(a->dst_h_dentry)) ++ err = au_may_add(a->dst_dentry, a->btgt, ++ a->dst_h_parent, isdir); ++ } else { ++ if (unlikely(d_is_negative(a->dst_h_dentry))) ++ goto out; ++ h_inode = d_inode(a->dst_h_dentry); ++ if (h_inode->i_nlink) ++ err = au_may_del(a->dst_dentry, a->btgt, ++ a->dst_h_parent, isdir); ++ } ++ ++out: ++ if (unlikely(err == -ENOENT || err == -EEXIST)) ++ err = -EIO; ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * locking order ++ * (VFS) ++ * - src_dir and dir by lock_rename() ++ * - inode if exitsts ++ * (aufs) ++ * - lock all ++ * + src_dentry and dentry by aufs_read_and_write_lock2() which calls, ++ * + si_read_lock ++ * + di_write_lock2_child() ++ * + di_write_lock_child() ++ * + ii_write_lock_child() ++ * + di_write_lock_child2() ++ * + ii_write_lock_child2() ++ * + src_parent and parent ++ * + di_write_lock_parent() ++ * + ii_write_lock_parent() ++ * + di_write_lock_parent2() ++ * + ii_write_lock_parent2() ++ * + lower src_dir and dir by vfsub_lock_rename() ++ * + verify the every relationships between child and parent. if any ++ * of them failed, unlock all and return -EBUSY. ++ */ ++static void au_ren_unlock(struct au_ren_args *a) ++{ ++ vfsub_unlock_rename(a->src_h_parent, a->src_hdir, ++ a->dst_h_parent, a->dst_hdir); ++ if (au_ftest_ren(a->flags, MNT_WRITE)) ++ vfsub_mnt_drop_write(au_br_mnt(a->br)); ++} ++ ++static int au_ren_lock(struct au_ren_args *a) ++{ ++ int err; ++ unsigned int udba; ++ ++ err = 0; ++ a->src_h_parent = au_h_dptr(a->src_parent, a->btgt); ++ a->src_hdir = au_hi(a->src_dir, a->btgt); ++ a->dst_h_parent = au_h_dptr(a->dst_parent, a->btgt); ++ a->dst_hdir = au_hi(a->dst_dir, a->btgt); ++ ++ err = vfsub_mnt_want_write(au_br_mnt(a->br)); ++ if (unlikely(err)) ++ goto out; ++ au_fset_ren(a->flags, MNT_WRITE); ++ a->h_trap = vfsub_lock_rename(a->src_h_parent, a->src_hdir, ++ a->dst_h_parent, a->dst_hdir); ++ udba = au_opt_udba(a->src_dentry->d_sb); ++ if (unlikely(a->src_hdir->hi_inode != d_inode(a->src_h_parent) ++ || a->dst_hdir->hi_inode != d_inode(a->dst_h_parent))) ++ err = au_busy_or_stale(); ++ if (!err && au_dbtop(a->src_dentry) == a->btgt) ++ err = au_h_verify(a->src_h_dentry, udba, ++ d_inode(a->src_h_parent), a->src_h_parent, ++ a->br); ++ if (!err && au_dbtop(a->dst_dentry) == a->btgt) ++ err = au_h_verify(a->dst_h_dentry, udba, ++ d_inode(a->dst_h_parent), a->dst_h_parent, ++ a->br); ++ if (!err) ++ goto out; /* success */ ++ ++ err = au_busy_or_stale(); ++ au_ren_unlock(a); ++ ++out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static void au_ren_refresh_dir(struct au_ren_args *a) ++{ ++ struct inode *dir; ++ ++ dir = a->dst_dir; ++ dir->i_version++; ++ if (au_ftest_ren(a->flags, ISDIR)) { ++ /* is this updating defined in POSIX? */ ++ au_cpup_attr_timesizes(a->src_inode); ++ au_cpup_attr_nlink(dir, /*force*/1); ++ } ++ ++ au_dir_ts(dir, a->btgt); ++ ++ if (au_ftest_ren(a->flags, ISSAMEDIR)) ++ return; ++ ++ dir = a->src_dir; ++ dir->i_version++; ++ if (au_ftest_ren(a->flags, ISDIR)) ++ au_cpup_attr_nlink(dir, /*force*/1); ++ au_dir_ts(dir, a->btgt); ++} ++ ++static void au_ren_refresh(struct au_ren_args *a) ++{ ++ aufs_bindex_t bbot, bindex; ++ struct dentry *d, *h_d; ++ struct inode *i, *h_i; ++ struct super_block *sb; ++ ++ d = a->dst_dentry; ++ d_drop(d); ++ if (a->h_dst) ++ /* already dget-ed by au_ren_or_cpup() */ ++ au_set_h_dptr(d, a->btgt, a->h_dst); ++ ++ i = a->dst_inode; ++ if (i) { ++ if (!au_ftest_ren(a->flags, ISDIR)) ++ vfsub_drop_nlink(i); ++ else { ++ vfsub_dead_dir(i); ++ au_cpup_attr_timesizes(i); ++ } ++ au_update_dbrange(d, /*do_put_zero*/1); ++ } else { ++ bbot = a->btgt; ++ for (bindex = au_dbtop(d); bindex < bbot; bindex++) ++ au_set_h_dptr(d, bindex, NULL); ++ bbot = au_dbbot(d); ++ for (bindex = a->btgt + 1; bindex <= bbot; bindex++) ++ au_set_h_dptr(d, bindex, NULL); ++ au_update_dbrange(d, /*do_put_zero*/0); ++ } ++ ++ d = a->src_dentry; ++ au_set_dbwh(d, -1); ++ bbot = au_dbbot(d); ++ for (bindex = a->btgt + 1; bindex <= bbot; bindex++) { ++ h_d = au_h_dptr(d, bindex); ++ if (h_d) ++ au_set_h_dptr(d, bindex, NULL); ++ } ++ au_set_dbbot(d, a->btgt); ++ ++ sb = d->d_sb; ++ i = a->src_inode; ++ if (au_opt_test(au_mntflags(sb), PLINK) && au_plink_test(i)) ++ return; /* success */ ++ ++ bbot = au_ibbot(i); ++ for (bindex = a->btgt + 1; bindex <= bbot; bindex++) { ++ h_i = au_h_iptr(i, bindex); ++ if (h_i) { ++ au_xino_write(sb, bindex, h_i->i_ino, /*ino*/0); ++ /* ignore this error */ ++ au_set_h_iptr(i, bindex, NULL, 0); ++ } ++ } ++ au_set_ibbot(i, a->btgt); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* mainly for link(2) and rename(2) */ ++int au_wbr(struct dentry *dentry, aufs_bindex_t btgt) ++{ ++ aufs_bindex_t bdiropq, bwh; ++ struct dentry *parent; ++ struct au_branch *br; ++ ++ parent = dentry->d_parent; ++ IMustLock(d_inode(parent)); /* dir is locked */ ++ ++ bdiropq = au_dbdiropq(parent); ++ bwh = au_dbwh(dentry); ++ br = au_sbr(dentry->d_sb, btgt); ++ if (au_br_rdonly(br) ++ || (0 <= bdiropq && bdiropq < btgt) ++ || (0 <= bwh && bwh < btgt)) ++ btgt = -1; ++ ++ AuDbg("btgt %d\n", btgt); ++ return btgt; ++} ++ ++/* sets src_btop, dst_btop and btgt */ ++static int au_ren_wbr(struct au_ren_args *a) ++{ ++ int err; ++ struct au_wr_dir_args wr_dir_args = { ++ /* .force_btgt = -1, */ ++ .flags = AuWrDir_ADD_ENTRY ++ }; ++ ++ a->src_btop = au_dbtop(a->src_dentry); ++ a->dst_btop = au_dbtop(a->dst_dentry); ++ if (au_ftest_ren(a->flags, ISDIR)) ++ au_fset_wrdir(wr_dir_args.flags, ISDIR); ++ wr_dir_args.force_btgt = a->src_btop; ++ if (a->dst_inode && a->dst_btop < a->src_btop) ++ wr_dir_args.force_btgt = a->dst_btop; ++ wr_dir_args.force_btgt = au_wbr(a->dst_dentry, wr_dir_args.force_btgt); ++ err = au_wr_dir(a->dst_dentry, a->src_dentry, &wr_dir_args); ++ a->btgt = err; ++ ++ return err; ++} ++ ++static void au_ren_dt(struct au_ren_args *a) ++{ ++ a->h_path.dentry = a->src_h_parent; ++ au_dtime_store(a->src_dt + AuPARENT, a->src_parent, &a->h_path); ++ if (!au_ftest_ren(a->flags, ISSAMEDIR)) { ++ a->h_path.dentry = a->dst_h_parent; ++ au_dtime_store(a->dst_dt + AuPARENT, a->dst_parent, &a->h_path); ++ } ++ ++ au_fclr_ren(a->flags, DT_DSTDIR); ++ if (!au_ftest_ren(a->flags, ISDIR)) ++ return; ++ ++ a->h_path.dentry = a->src_h_dentry; ++ au_dtime_store(a->src_dt + AuCHILD, a->src_dentry, &a->h_path); ++ if (d_is_positive(a->dst_h_dentry)) { ++ au_fset_ren(a->flags, DT_DSTDIR); ++ a->h_path.dentry = a->dst_h_dentry; ++ au_dtime_store(a->dst_dt + AuCHILD, a->dst_dentry, &a->h_path); ++ } ++} ++ ++static void au_ren_rev_dt(int err, struct au_ren_args *a) ++{ ++ struct dentry *h_d; ++ struct inode *h_inode; ++ ++ au_dtime_revert(a->src_dt + AuPARENT); ++ if (!au_ftest_ren(a->flags, ISSAMEDIR)) ++ au_dtime_revert(a->dst_dt + AuPARENT); ++ ++ if (au_ftest_ren(a->flags, ISDIR) && err != -EIO) { ++ h_d = a->src_dt[AuCHILD].dt_h_path.dentry; ++ h_inode = d_inode(h_d); ++ inode_lock_nested(h_inode, AuLsc_I_CHILD); ++ au_dtime_revert(a->src_dt + AuCHILD); ++ inode_unlock(h_inode); ++ ++ if (au_ftest_ren(a->flags, DT_DSTDIR)) { ++ h_d = a->dst_dt[AuCHILD].dt_h_path.dentry; ++ h_inode = d_inode(h_d); ++ inode_lock_nested(h_inode, AuLsc_I_CHILD); ++ au_dtime_revert(a->dst_dt + AuCHILD); ++ inode_unlock(h_inode); ++ } ++ } ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int aufs_rename(struct inode *_src_dir, struct dentry *_src_dentry, ++ struct inode *_dst_dir, struct dentry *_dst_dentry) ++{ ++ int err, flags; ++ /* reduce stack space */ ++ struct au_ren_args *a; ++ ++ AuDbg("%pd, %pd\n", _src_dentry, _dst_dentry); ++ IMustLock(_src_dir); ++ IMustLock(_dst_dir); ++ ++ err = -ENOMEM; ++ BUILD_BUG_ON(sizeof(*a) > PAGE_SIZE); ++ a = kzalloc(sizeof(*a), GFP_NOFS); ++ if (unlikely(!a)) ++ goto out; ++ ++ a->src_dir = _src_dir; ++ a->src_dentry = _src_dentry; ++ a->src_inode = NULL; ++ if (d_really_is_positive(a->src_dentry)) ++ a->src_inode = d_inode(a->src_dentry); ++ a->src_parent = a->src_dentry->d_parent; /* dir inode is locked */ ++ a->dst_dir = _dst_dir; ++ a->dst_dentry = _dst_dentry; ++ a->dst_inode = NULL; ++ if (d_really_is_positive(a->dst_dentry)) ++ a->dst_inode = d_inode(a->dst_dentry); ++ a->dst_parent = a->dst_dentry->d_parent; /* dir inode is locked */ ++ if (a->dst_inode) { ++ IMustLock(a->dst_inode); ++ au_igrab(a->dst_inode); ++ } ++ ++ err = -ENOTDIR; ++ flags = AuLock_FLUSH | AuLock_NOPLM | AuLock_GEN; ++ if (d_is_dir(a->src_dentry)) { ++ au_fset_ren(a->flags, ISDIR); ++ if (unlikely(d_really_is_positive(a->dst_dentry) ++ && !d_is_dir(a->dst_dentry))) ++ goto out_free; ++ flags |= AuLock_DIRS; ++ } ++ err = aufs_read_and_write_lock2(a->dst_dentry, a->src_dentry, flags); ++ if (unlikely(err)) ++ goto out_free; ++ ++ err = au_d_hashed_positive(a->src_dentry); ++ if (unlikely(err)) ++ goto out_unlock; ++ err = -ENOENT; ++ if (a->dst_inode) { ++ /* ++ * If it is a dir, VFS unhash dst_dentry before this ++ * function. It means we cannot rely upon d_unhashed(). ++ */ ++ if (unlikely(!a->dst_inode->i_nlink)) ++ goto out_unlock; ++ if (!S_ISDIR(a->dst_inode->i_mode)) { ++ err = au_d_hashed_positive(a->dst_dentry); ++ if (unlikely(err)) ++ goto out_unlock; ++ } else if (unlikely(IS_DEADDIR(a->dst_inode))) ++ goto out_unlock; ++ } else if (unlikely(d_unhashed(a->dst_dentry))) ++ goto out_unlock; ++ ++ /* ++ * is it possible? ++ * yes, it happened (in linux-3.3-rcN) but I don't know why. ++ * there may exist a problem somewhere else. ++ */ ++ err = -EINVAL; ++ if (unlikely(d_inode(a->dst_parent) == d_inode(a->src_dentry))) ++ goto out_unlock; ++ ++ au_fset_ren(a->flags, ISSAMEDIR); /* temporary */ ++ di_write_lock_parent(a->dst_parent); ++ ++ /* which branch we process */ ++ err = au_ren_wbr(a); ++ if (unlikely(err < 0)) ++ goto out_parent; ++ a->br = au_sbr(a->dst_dentry->d_sb, a->btgt); ++ a->h_path.mnt = au_br_mnt(a->br); ++ ++ /* are they available to be renamed */ ++ err = au_ren_may_dir(a); ++ if (unlikely(err)) ++ goto out_children; ++ ++ /* prepare the writable parent dir on the same branch */ ++ if (a->dst_btop == a->btgt) { ++ au_fset_ren(a->flags, WHDST); ++ } else { ++ err = au_cpup_dirs(a->dst_dentry, a->btgt); ++ if (unlikely(err)) ++ goto out_children; ++ } ++ ++ if (a->src_dir != a->dst_dir) { ++ /* ++ * this temporary unlock is safe, ++ * because both dir->i_mutex are locked. ++ */ ++ di_write_unlock(a->dst_parent); ++ di_write_lock_parent(a->src_parent); ++ err = au_wr_dir_need_wh(a->src_dentry, ++ au_ftest_ren(a->flags, ISDIR), ++ &a->btgt); ++ di_write_unlock(a->src_parent); ++ di_write_lock2_parent(a->src_parent, a->dst_parent, /*isdir*/1); ++ au_fclr_ren(a->flags, ISSAMEDIR); ++ } else ++ err = au_wr_dir_need_wh(a->src_dentry, ++ au_ftest_ren(a->flags, ISDIR), ++ &a->btgt); ++ if (unlikely(err < 0)) ++ goto out_children; ++ if (err) ++ au_fset_ren(a->flags, WHSRC); ++ ++ /* cpup src */ ++ if (a->src_btop != a->btgt) { ++ struct au_pin pin; ++ ++ err = au_pin(&pin, a->src_dentry, a->btgt, ++ au_opt_udba(a->src_dentry->d_sb), ++ AuPin_DI_LOCKED | AuPin_MNT_WRITE); ++ if (!err) { ++ struct au_cp_generic cpg = { ++ .dentry = a->src_dentry, ++ .bdst = a->btgt, ++ .bsrc = a->src_btop, ++ .len = -1, ++ .pin = &pin, ++ .flags = AuCpup_DTIME | AuCpup_HOPEN ++ }; ++ AuDebugOn(au_dbtop(a->src_dentry) != a->src_btop); ++ err = au_sio_cpup_simple(&cpg); ++ au_unpin(&pin); ++ } ++ if (unlikely(err)) ++ goto out_children; ++ a->src_btop = a->btgt; ++ a->src_h_dentry = au_h_dptr(a->src_dentry, a->btgt); ++ au_fset_ren(a->flags, WHSRC); ++ } ++ ++ /* lock them all */ ++ err = au_ren_lock(a); ++ if (unlikely(err)) ++ /* leave the copied-up one */ ++ goto out_children; ++ ++ if (!au_opt_test(au_mntflags(a->dst_dir->i_sb), UDBA_NONE)) ++ err = au_may_ren(a); ++ else if (unlikely(a->dst_dentry->d_name.len > AUFS_MAX_NAMELEN)) ++ err = -ENAMETOOLONG; ++ if (unlikely(err)) ++ goto out_hdir; ++ ++ /* store timestamps to be revertible */ ++ au_ren_dt(a); ++ ++ /* here we go */ ++ err = do_rename(a); ++ if (unlikely(err)) ++ goto out_dt; ++ ++ /* update dir attributes */ ++ au_ren_refresh_dir(a); ++ ++ /* dput/iput all lower dentries */ ++ au_ren_refresh(a); ++ ++ goto out_hdir; /* success */ ++ ++out_dt: ++ au_ren_rev_dt(err, a); ++out_hdir: ++ au_ren_unlock(a); ++out_children: ++ au_nhash_wh_free(&a->whlist); ++ if (err && a->dst_inode && a->dst_btop != a->btgt) { ++ AuDbg("btop %d, btgt %d\n", a->dst_btop, a->btgt); ++ au_set_h_dptr(a->dst_dentry, a->btgt, NULL); ++ au_set_dbtop(a->dst_dentry, a->dst_btop); ++ } ++out_parent: ++ if (!err) ++ d_move(a->src_dentry, a->dst_dentry); ++ else { ++ au_update_dbtop(a->dst_dentry); ++ if (!a->dst_inode) ++ d_drop(a->dst_dentry); ++ } ++ if (au_ftest_ren(a->flags, ISSAMEDIR)) ++ di_write_unlock(a->dst_parent); ++ else ++ di_write_unlock2(a->src_parent, a->dst_parent); ++out_unlock: ++ aufs_read_and_write_unlock2(a->dst_dentry, a->src_dentry); ++out_free: ++ iput(a->dst_inode); ++ if (a->thargs) ++ au_whtmp_rmdir_free(a->thargs); ++ kfree(a); ++out: ++ AuTraceErr(err); ++ return err; ++} +diff --git a/fs/aufs/iinfo.c b/fs/aufs/iinfo.c +new file mode 100644 +index 0000000..3a10d8f +--- /dev/null ++++ b/fs/aufs/iinfo.c +@@ -0,0 +1,267 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima + */ @@ -14233,10 +18453,12 @@ diff -Nurp linux-4.4.3/fs/aufs/iinfo.c linux-4.4.3-aufs/fs/aufs/iinfo.c +struct inode *au_h_iptr(struct inode *inode, aufs_bindex_t bindex) +{ + struct inode *h_inode; ++ struct au_hinode *hinode; + + IiMustAnyLock(inode); + -+ h_inode = au_ii(inode)->ii_hinode[0 + bindex].hi_inode; ++ hinode = au_hinode(au_ii(inode), bindex); ++ h_inode = hinode->hi_inode; + AuDebugOn(h_inode && atomic_read(&h_inode->i_count) <= 0); + return h_inode; +} @@ -14271,7 +18493,7 @@ diff -Nurp linux-4.4.3/fs/aufs/iinfo.c linux-4.4.3-aufs/fs/aufs/iinfo.c + + IiMustWriteLock(inode); + -+ hinode = iinfo->ii_hinode + bindex; ++ hinode = au_hinode(iinfo, bindex); + hi = hinode->hi_inode; + AuDebugOn(h_inode && atomic_read(&h_inode->i_count) <= 0); + @@ -14286,7 +18508,7 @@ diff -Nurp linux-4.4.3/fs/aufs/iinfo.c linux-4.4.3-aufs/fs/aufs/iinfo.c + AuDebugOn(inode->i_mode + && (h_inode->i_mode & S_IFMT) + != (inode->i_mode & S_IFMT)); -+ if (bindex == iinfo->ii_bstart) ++ if (bindex == iinfo->ii_btop) + au_cpup_igen(inode, h_inode); + br = au_sbr(sb, bindex); + hinode->hi_id = br->br_id; @@ -14313,7 +18535,7 @@ diff -Nurp linux-4.4.3/fs/aufs/iinfo.c linux-4.4.3-aufs/fs/aufs/iinfo.c + + IiMustWriteLock(inode); + -+ hinode = au_ii(inode)->ii_hinode + bindex; ++ hinode = au_hinode(au_ii(inode), bindex); + AuDebugOn(hinode->hi_whdentry); + hinode->hi_whdentry = h_wh; +} @@ -14340,20 +18562,18 @@ diff -Nurp linux-4.4.3/fs/aufs/iinfo.c linux-4.4.3-aufs/fs/aufs/iinfo.c +void au_update_ibrange(struct inode *inode, int do_put_zero) +{ + struct au_iinfo *iinfo; -+ aufs_bindex_t bindex, bend; -+ -+ iinfo = au_ii(inode); -+ if (!iinfo) -+ return; ++ aufs_bindex_t bindex, bbot; + ++ AuDebugOn(is_bad_inode(inode)); + IiMustWriteLock(inode); + -+ if (do_put_zero && iinfo->ii_bstart >= 0) { -+ for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend; ++ iinfo = au_ii(inode); ++ if (do_put_zero && iinfo->ii_btop >= 0) { ++ for (bindex = iinfo->ii_btop; bindex <= iinfo->ii_bbot; + bindex++) { + struct inode *h_i; + -+ h_i = iinfo->ii_hinode[0 + bindex].hi_inode; ++ h_i = au_hinode(iinfo, bindex)->hi_inode; + if (h_i + && !h_i->i_nlink + && !(h_i->i_state & I_LINKABLE)) @@ -14361,21 +18581,21 @@ diff -Nurp linux-4.4.3/fs/aufs/iinfo.c linux-4.4.3-aufs/fs/aufs/iinfo.c + } + } + -+ iinfo->ii_bstart = -1; -+ iinfo->ii_bend = -1; -+ bend = au_sbend(inode->i_sb); -+ for (bindex = 0; bindex <= bend; bindex++) -+ if (iinfo->ii_hinode[0 + bindex].hi_inode) { -+ iinfo->ii_bstart = bindex; ++ iinfo->ii_btop = -1; ++ iinfo->ii_bbot = -1; ++ bbot = au_sbbot(inode->i_sb); ++ for (bindex = 0; bindex <= bbot; bindex++) ++ if (au_hinode(iinfo, bindex)->hi_inode) { ++ iinfo->ii_btop = bindex; + break; + } -+ if (iinfo->ii_bstart >= 0) -+ for (bindex = bend; bindex >= iinfo->ii_bstart; bindex--) -+ if (iinfo->ii_hinode[0 + bindex].hi_inode) { -+ iinfo->ii_bend = bindex; ++ if (iinfo->ii_btop >= 0) ++ for (bindex = bbot; bindex >= iinfo->ii_btop; bindex--) ++ if (au_hinode(iinfo, bindex)->hi_inode) { ++ iinfo->ii_bbot = bindex; + break; + } -+ AuDebugOn(iinfo->ii_bstart > iinfo->ii_bend); ++ AuDebugOn(iinfo->ii_btop > iinfo->ii_bbot); +} + +/* ---------------------------------------------------------------------- */ @@ -14384,14 +18604,20 @@ diff -Nurp linux-4.4.3/fs/aufs/iinfo.c linux-4.4.3-aufs/fs/aufs/iinfo.c +{ + struct au_icntnr *c = _c; + struct au_iinfo *iinfo = &c->iinfo; -+ static struct lock_class_key aufs_ii; + + spin_lock_init(&iinfo->ii_generation.ig_spin); + au_rw_init(&iinfo->ii_rwsem); -+ au_rw_class(&iinfo->ii_rwsem, &aufs_ii); + inode_init_once(&c->vfs_inode); +} + ++void au_hinode_init(struct au_hinode *hinode) ++{ ++ hinode->hi_inode = NULL; ++ hinode->hi_id = -1; ++ au_hn_init(hinode); ++ hinode->hi_whdentry = NULL; ++} ++ +int au_iinfo_init(struct inode *inode) +{ + struct au_iinfo *iinfo; @@ -14400,37 +18626,37 @@ diff -Nurp linux-4.4.3/fs/aufs/iinfo.c linux-4.4.3-aufs/fs/aufs/iinfo.c + + sb = inode->i_sb; + iinfo = &(container_of(inode, struct au_icntnr, vfs_inode)->iinfo); -+ nbr = au_sbend(sb) + 1; ++ nbr = au_sbbot(sb) + 1; + if (unlikely(nbr <= 0)) + nbr = 1; -+ iinfo->ii_hinode = kcalloc(nbr, sizeof(*iinfo->ii_hinode), GFP_NOFS); ++ iinfo->ii_hinode = kmalloc_array(nbr, sizeof(*iinfo->ii_hinode), ++ GFP_NOFS); + if (iinfo->ii_hinode) { + au_ninodes_inc(sb); + for (i = 0; i < nbr; i++) -+ iinfo->ii_hinode[i].hi_id = -1; ++ au_hinode_init(iinfo->ii_hinode + i); + + iinfo->ii_generation.ig_generation = au_sigen(sb); -+ iinfo->ii_bstart = -1; -+ iinfo->ii_bend = -1; ++ iinfo->ii_btop = -1; ++ iinfo->ii_bbot = -1; + iinfo->ii_vdir = NULL; + return 0; + } + return -ENOMEM; +} + -+int au_ii_realloc(struct au_iinfo *iinfo, int nbr) ++int au_hinode_realloc(struct au_iinfo *iinfo, int nbr) +{ -+ int err, sz; ++ int err, i; + struct au_hinode *hip; + + AuRwMustWriteLock(&iinfo->ii_rwsem); + + err = -ENOMEM; -+ sz = sizeof(*hip) * (iinfo->ii_bend + 1); -+ if (!sz) -+ sz = sizeof(*hip); -+ hip = au_kzrealloc(iinfo->ii_hinode, sz, sizeof(*hip) * nbr, GFP_NOFS); ++ hip = krealloc(iinfo->ii_hinode, sizeof(*hip) * nbr, GFP_NOFS); + if (hip) { ++ for (i = iinfo->ii_bbot + 1; i < nbr; i++) ++ au_hinode_init(hip + i); + iinfo->ii_hinode = hip; + err = 0; + } @@ -14443,13 +18669,10 @@ diff -Nurp linux-4.4.3/fs/aufs/iinfo.c linux-4.4.3-aufs/fs/aufs/iinfo.c + struct au_iinfo *iinfo; + struct au_hinode *hi; + struct super_block *sb; -+ aufs_bindex_t bindex, bend; ++ aufs_bindex_t bindex, bbot; + const unsigned char unlinked = !inode->i_nlink; + -+ iinfo = au_ii(inode); -+ /* bad_inode case */ -+ if (!iinfo) -+ return; ++ AuDebugOn(is_bad_inode(inode)); + + sb = inode->i_sb; + au_ninodes_dec(sb); @@ -14467,27 +18690,29 @@ diff -Nurp linux-4.4.3/fs/aufs/iinfo.c linux-4.4.3-aufs/fs/aufs/iinfo.c + lockdep_on(); + } + ++ iinfo = au_ii(inode); + if (iinfo->ii_vdir) + au_vdir_free(iinfo->ii_vdir); + -+ bindex = iinfo->ii_bstart; ++ bindex = iinfo->ii_btop; + if (bindex >= 0) { -+ hi = iinfo->ii_hinode + bindex; -+ bend = iinfo->ii_bend; -+ while (bindex++ <= bend) { ++ hi = au_hinode(iinfo, bindex); ++ bbot = iinfo->ii_bbot; ++ while (bindex++ <= bbot) { + if (hi->hi_inode) + au_hiput(hi); + hi++; + } + } + kfree(iinfo->ii_hinode); -+ iinfo->ii_hinode = NULL; + AuRwDestroy(&iinfo->ii_rwsem); +} -diff -Nurp linux-4.4.3/fs/aufs/inode.c linux-4.4.3-aufs/fs/aufs/inode.c ---- linux-4.4.3/fs/aufs/inode.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/inode.c 2016-03-02 17:59:37.368498738 +0200 -@@ -0,0 +1,514 @@ +diff --git a/fs/aufs/inode.c b/fs/aufs/inode.c +new file mode 100644 +index 0000000..d599b4c +--- /dev/null ++++ b/fs/aufs/inode.c +@@ -0,0 +1,504 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima + */ @@ -14524,19 +18749,20 @@ diff -Nurp linux-4.4.3/fs/aufs/inode.c linux-4.4.3-aufs/fs/aufs/inode.c + struct au_iinfo *iinfo; + struct au_hinode *p, *q, tmp; + ++ AuDebugOn(is_bad_inode(inode)); + IiMustWriteLock(inode); + + *update = 0; + sb = inode->i_sb; + type = inode->i_mode & S_IFMT; + iinfo = au_ii(inode); -+ err = au_ii_realloc(iinfo, au_sbend(sb) + 1); ++ err = au_hinode_realloc(iinfo, au_sbbot(sb) + 1); + if (unlikely(err)) + goto out; + -+ AuDebugOn(iinfo->ii_bstart < 0); -+ p = iinfo->ii_hinode + iinfo->ii_bstart; -+ for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend; ++ AuDebugOn(iinfo->ii_btop < 0); ++ p = au_hinode(iinfo, iinfo->ii_btop); ++ for (bindex = iinfo->ii_btop; bindex <= iinfo->ii_bbot; + bindex++, p++) { + if (!p->hi_inode) + continue; @@ -14553,12 +18779,12 @@ diff -Nurp linux-4.4.3/fs/aufs/inode.c linux-4.4.3-aufs/fs/aufs/inode.c + continue; + } + -+ if (new_bindex < iinfo->ii_bstart) -+ iinfo->ii_bstart = new_bindex; -+ if (iinfo->ii_bend < new_bindex) -+ iinfo->ii_bend = new_bindex; ++ if (new_bindex < iinfo->ii_btop) ++ iinfo->ii_btop = new_bindex; ++ if (iinfo->ii_bbot < new_bindex) ++ iinfo->ii_bbot = new_bindex; + /* swap two lower inode, and loop again */ -+ q = iinfo->ii_hinode + new_bindex; ++ q = au_hinode(iinfo, new_bindex); + tmp = *q; + *q = *p; + *p = tmp; @@ -14620,7 +18846,7 @@ diff -Nurp linux-4.4.3/fs/aufs/inode.c linux-4.4.3-aufs/fs/aufs/inode.c + int err, e, update; + unsigned int flags; + umode_t mode; -+ aufs_bindex_t bindex, bend; ++ aufs_bindex_t bindex, bbot; + unsigned char isdir; + struct au_hinode *p; + struct au_iinfo *iinfo; @@ -14631,12 +18857,12 @@ diff -Nurp linux-4.4.3/fs/aufs/inode.c linux-4.4.3-aufs/fs/aufs/inode.c + + update = 0; + iinfo = au_ii(inode); -+ p = iinfo->ii_hinode + iinfo->ii_bstart; ++ p = au_hinode(iinfo, iinfo->ii_btop); + mode = (inode->i_mode & S_IFMT); + isdir = S_ISDIR(mode); + flags = au_hi_flags(inode, isdir); -+ bend = au_dbend(dentry); -+ for (bindex = au_dbstart(dentry); bindex <= bend; bindex++) { ++ bbot = au_dbbot(dentry); ++ for (bindex = au_dbtop(dentry); bindex <= bbot; bindex++) { + struct inode *h_i, *h_inode; + struct dentry *h_d; + @@ -14646,7 +18872,7 @@ diff -Nurp linux-4.4.3/fs/aufs/inode.c linux-4.4.3-aufs/fs/aufs/inode.c + + h_inode = d_inode(h_d); + AuDebugOn(mode != (h_inode->i_mode & S_IFMT)); -+ if (iinfo->ii_bstart <= bindex && bindex <= iinfo->ii_bend) { ++ if (iinfo->ii_btop <= bindex && bindex <= iinfo->ii_bbot) { + h_i = au_h_iptr(inode, bindex); + if (h_i) { + if (h_i == h_inode) @@ -14655,10 +18881,10 @@ diff -Nurp linux-4.4.3/fs/aufs/inode.c linux-4.4.3-aufs/fs/aufs/inode.c + break; + } + } -+ if (bindex < iinfo->ii_bstart) -+ iinfo->ii_bstart = bindex; -+ if (iinfo->ii_bend < bindex) -+ iinfo->ii_bend = bindex; ++ if (bindex < iinfo->ii_btop) ++ iinfo->ii_btop = bindex; ++ if (iinfo->ii_bbot < bindex) ++ iinfo->ii_bbot = bindex; + au_set_h_iptr(inode, bindex, au_igrab(h_inode), flags); + update = 1; + } @@ -14679,7 +18905,7 @@ diff -Nurp linux-4.4.3/fs/aufs/inode.c linux-4.4.3-aufs/fs/aufs/inode.c + int err; + unsigned int flags; + umode_t mode; -+ aufs_bindex_t bindex, bstart, btail; ++ aufs_bindex_t bindex, btop, btail; + unsigned char isdir; + struct dentry *h_dentry; + struct inode *h_inode; @@ -14691,8 +18917,8 @@ diff -Nurp linux-4.4.3/fs/aufs/inode.c linux-4.4.3-aufs/fs/aufs/inode.c + err = 0; + isdir = 0; + iop = au_sbi(inode->i_sb)->si_iop_array; -+ bstart = au_dbstart(dentry); -+ h_dentry = au_h_dptr(dentry, bstart); ++ btop = au_dbtop(dentry); ++ h_dentry = au_h_dptr(dentry, btop); + h_inode = d_inode(h_dentry); + mode = h_inode->i_mode; + switch (mode & S_IFMT) { @@ -14700,7 +18926,7 @@ diff -Nurp linux-4.4.3/fs/aufs/inode.c linux-4.4.3-aufs/fs/aufs/inode.c + btail = au_dbtail(dentry); + inode->i_op = iop + AuIop_OTHER; + inode->i_fop = &aufs_file_fop; -+ err = au_dy_iaop(inode, bstart, h_inode); ++ err = au_dy_iaop(inode, btop, h_inode); + if (unlikely(err)) + goto out; + break; @@ -14736,9 +18962,9 @@ diff -Nurp linux-4.4.3/fs/aufs/inode.c linux-4.4.3-aufs/fs/aufs/inode.c + && !memcmp(dentry->d_name.name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) + au_fclr_hi(flags, HNOTIFY); + iinfo = au_ii(inode); -+ iinfo->ii_bstart = bstart; -+ iinfo->ii_bend = btail; -+ for (bindex = bstart; bindex <= btail; bindex++) { ++ iinfo->ii_btop = btop; ++ iinfo->ii_bbot = btail; ++ for (bindex = btop; bindex <= btail; bindex++) { + h_dentry = au_h_dptr(dentry, bindex); + if (h_dentry) + au_set_h_iptr(inode, bindex, @@ -14764,7 +18990,7 @@ diff -Nurp linux-4.4.3/fs/aufs/inode.c linux-4.4.3-aufs/fs/aufs/inode.c +{ + int err; + unsigned int gen, igflags; -+ aufs_bindex_t bindex, bend; ++ aufs_bindex_t bindex, bbot; + struct inode *h_inode, *h_dinode; + struct dentry *h_dentry; + @@ -14779,10 +19005,10 @@ diff -Nurp linux-4.4.3/fs/aufs/inode.c linux-4.4.3-aufs/fs/aufs/inode.c + + err = 1; + ii_write_lock_new_child(inode); -+ h_dentry = au_h_dptr(dentry, au_dbstart(dentry)); ++ h_dentry = au_h_dptr(dentry, au_dbtop(dentry)); + h_dinode = d_inode(h_dentry); -+ bend = au_ibend(inode); -+ for (bindex = au_ibstart(inode); bindex <= bend; bindex++) { ++ bbot = au_ibbot(inode); ++ for (bindex = au_ibtop(inode); bindex <= bbot; bindex++) { + h_inode = au_h_iptr(inode, bindex); + if (!h_inode || h_inode != h_dinode) + continue; @@ -14848,11 +19074,11 @@ diff -Nurp linux-4.4.3/fs/aufs/inode.c linux-4.4.3-aufs/fs/aufs/inode.c + struct mutex *mtx; + ino_t h_ino, ino; + int err; -+ aufs_bindex_t bstart; ++ aufs_bindex_t btop; + + sb = dentry->d_sb; -+ bstart = au_dbstart(dentry); -+ h_dentry = au_h_dptr(dentry, bstart); ++ btop = au_dbtop(dentry); ++ h_dentry = au_h_dptr(dentry, btop); + h_inode = d_inode(h_dentry); + h_ino = h_inode->i_ino; + @@ -14862,12 +19088,12 @@ diff -Nurp linux-4.4.3/fs/aufs/inode.c linux-4.4.3-aufs/fs/aufs/inode.c + */ + mtx = NULL; + if (!d_is_dir(h_dentry)) -+ mtx = &au_sbr(sb, bstart)->br_xino.xi_nondir_mtx; ++ mtx = &au_sbr(sb, btop)->br_xino.xi_nondir_mtx; + +new_ino: + if (mtx) + mutex_lock(mtx); -+ err = au_xino_read(sb, bstart, h_ino, &ino); ++ err = au_xino_read(sb, btop, h_ino, &ino); + inode = ERR_PTR(err); + if (unlikely(err)) + goto out; @@ -14888,17 +19114,6 @@ diff -Nurp linux-4.4.3/fs/aufs/inode.c linux-4.4.3-aufs/fs/aufs/inode.c + + AuDbg("%lx, new %d\n", inode->i_state, !!(inode->i_state & I_NEW)); + if (inode->i_state & I_NEW) { -+ /* verbose coding for lock class name */ -+ if (unlikely(d_is_symlink(h_dentry))) -+ au_rw_class(&au_ii(inode)->ii_rwsem, -+ au_lc_key + AuLcSymlink_IIINFO); -+ else if (unlikely(d_is_dir(h_dentry))) -+ au_rw_class(&au_ii(inode)->ii_rwsem, -+ au_lc_key + AuLcDir_IIINFO); -+ else /* likely */ -+ au_rw_class(&au_ii(inode)->ii_rwsem, -+ au_lc_key + AuLcNonDir_IIINFO); -+ + ii_write_lock_new_child(inode); + err = set_inode(inode, dentry); + if (!err) { @@ -14914,7 +19129,7 @@ diff -Nurp linux-4.4.3/fs/aufs/inode.c linux-4.4.3-aufs/fs/aufs/inode.c + atomic_inc(&inode->i_count); + iget_failed(inode); + ii_write_unlock(inode); -+ au_xino_write(sb, bstart, h_ino, /*ino*/0); ++ au_xino_write(sb, btop, h_ino, /*ino*/0); + /* ignore this error */ + goto out_iput; + } else if (!must_new && !IS_DEADDIR(inode) && inode->i_nlink) { @@ -14940,10 +19155,10 @@ diff -Nurp linux-4.4.3/fs/aufs/inode.c linux-4.4.3-aufs/fs/aufs/inode.c + if (unlikely(au_test_fs_unique_ino(h_inode))) + AuWarn1("Warning: Un-notified UDBA or repeatedly renamed dir," + " b%d, %s, %pd, hi%lu, i%lu.\n", -+ bstart, au_sbtype(h_dentry->d_sb), dentry, ++ btop, au_sbtype(h_dentry->d_sb), dentry, + (unsigned long)h_ino, (unsigned long)ino); + ino = 0; -+ err = au_xino_write(sb, bstart, h_ino, /*ino*/0); ++ err = au_xino_write(sb, btop, h_ino, /*ino*/0); + if (!err) { + iput(inode); + if (mtx) @@ -14973,8 +19188,8 @@ diff -Nurp linux-4.4.3/fs/aufs/inode.c linux-4.4.3-aufs/fs/aufs/inode.c + /* pseudo-link after flushed may happen out of bounds */ + if (!err + && inode -+ && au_ibstart(inode) <= bindex -+ && bindex <= au_ibend(inode)) { ++ && au_ibtop(inode) <= bindex ++ && bindex <= au_ibbot(inode)) { + /* + * permission check is unnecessary since vfsub routine + * will be called later @@ -15002,10 +19217,12 @@ diff -Nurp linux-4.4.3/fs/aufs/inode.c linux-4.4.3-aufs/fs/aufs/inode.c + mask |= MAY_READ; /* force permission check */ + return au_test_h_perm(h_inode, mask); +} -diff -Nurp linux-4.4.3/fs/aufs/inode.h linux-4.4.3-aufs/fs/aufs/inode.h ---- linux-4.4.3/fs/aufs/inode.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/inode.h 2016-03-02 17:59:37.369498746 +0200 -@@ -0,0 +1,672 @@ +diff --git a/fs/aufs/inode.h b/fs/aufs/inode.h +new file mode 100644 +index 0000000..0d654e8 +--- /dev/null ++++ b/fs/aufs/inode.h +@@ -0,0 +1,676 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima + */ @@ -15064,7 +19281,7 @@ diff -Nurp linux-4.4.3/fs/aufs/inode.h linux-4.4.3-aufs/fs/aufs/inode.h + struct super_block *ii_hsb1; /* no get/put */ + + struct au_rwsem ii_rwsem; -+ aufs_bindex_t ii_bstart, ii_bend; ++ aufs_bindex_t ii_btop, ii_bbot; + __u32 ii_higen; + struct au_hinode *ii_hinode; + struct au_vdir *ii_vdir; @@ -15073,6 +19290,7 @@ diff -Nurp linux-4.4.3/fs/aufs/inode.h linux-4.4.3-aufs/fs/aufs/inode.h +struct au_icntnr { + struct au_iinfo iinfo; + struct inode vfs_inode; ++ struct hlist_node plink; +} ____cacheline_aligned_in_smp; + +/* au_pin flags */ @@ -15113,12 +19331,8 @@ diff -Nurp linux-4.4.3/fs/aufs/inode.h linux-4.4.3-aufs/fs/aufs/inode.h + +static inline struct au_iinfo *au_ii(struct inode *inode) +{ -+ struct au_iinfo *iinfo; -+ -+ iinfo = &(container_of(inode, struct au_icntnr, vfs_inode)->iinfo); -+ if (iinfo->ii_hinode) -+ return iinfo; -+ return NULL; /* debugging bad_inode case */ ++ BUG_ON(is_bad_inode(inode)); ++ return &(container_of(inode, struct au_icntnr, vfs_inode)->iinfo); +} + +/* ---------------------------------------------------------------------- */ @@ -15261,9 +19475,10 @@ diff -Nurp linux-4.4.3/fs/aufs/inode.h linux-4.4.3-aufs/fs/aufs/inode.h +void au_update_ibrange(struct inode *inode, int do_put_zero); + +void au_icntnr_init_once(void *_c); ++void au_hinode_init(struct au_hinode *hinode); +int au_iinfo_init(struct inode *inode); +void au_iinfo_fin(struct inode *inode); -+int au_ii_realloc(struct au_iinfo *iinfo, int nbr); ++int au_hinode_realloc(struct au_iinfo *iinfo, int nbr); + +#ifdef CONFIG_PROC_FS +/* plink.c */ @@ -15471,23 +19686,29 @@ diff -Nurp linux-4.4.3/fs/aufs/inode.h linux-4.4.3-aufs/fs/aufs/inode.h + +/* ---------------------------------------------------------------------- */ + ++static inline struct au_hinode *au_hinode(struct au_iinfo *iinfo, ++ aufs_bindex_t bindex) ++{ ++ return iinfo->ii_hinode + bindex; ++} ++ +static inline aufs_bindex_t au_ii_br_id(struct inode *inode, + aufs_bindex_t bindex) +{ + IiMustAnyLock(inode); -+ return au_ii(inode)->ii_hinode[0 + bindex].hi_id; ++ return au_hinode(au_ii(inode), bindex)->hi_id; +} + -+static inline aufs_bindex_t au_ibstart(struct inode *inode) ++static inline aufs_bindex_t au_ibtop(struct inode *inode) +{ + IiMustAnyLock(inode); -+ return au_ii(inode)->ii_bstart; ++ return au_ii(inode)->ii_btop; +} + -+static inline aufs_bindex_t au_ibend(struct inode *inode) ++static inline aufs_bindex_t au_ibbot(struct inode *inode) +{ + IiMustAnyLock(inode); -+ return au_ii(inode)->ii_bend; ++ return au_ii(inode)->ii_bbot; +} + +static inline struct au_vdir *au_ivdir(struct inode *inode) @@ -15499,19 +19720,19 @@ diff -Nurp linux-4.4.3/fs/aufs/inode.h linux-4.4.3-aufs/fs/aufs/inode.h +static inline struct dentry *au_hi_wh(struct inode *inode, aufs_bindex_t bindex) +{ + IiMustAnyLock(inode); -+ return au_ii(inode)->ii_hinode[0 + bindex].hi_whdentry; ++ return au_hinode(au_ii(inode), bindex)->hi_whdentry; +} + -+static inline void au_set_ibstart(struct inode *inode, aufs_bindex_t bindex) ++static inline void au_set_ibtop(struct inode *inode, aufs_bindex_t bindex) +{ + IiMustWriteLock(inode); -+ au_ii(inode)->ii_bstart = bindex; ++ au_ii(inode)->ii_btop = bindex; +} + -+static inline void au_set_ibend(struct inode *inode, aufs_bindex_t bindex) ++static inline void au_set_ibbot(struct inode *inode, aufs_bindex_t bindex) +{ + IiMustWriteLock(inode); -+ au_ii(inode)->ii_bend = bindex; ++ au_ii(inode)->ii_bbot = bindex; +} + +static inline void au_set_ivdir(struct inode *inode, struct au_vdir *vdir) @@ -15523,7 +19744,7 @@ diff -Nurp linux-4.4.3/fs/aufs/inode.h linux-4.4.3-aufs/fs/aufs/inode.h +static inline struct au_hinode *au_hi(struct inode *inode, aufs_bindex_t bindex) +{ + IiMustAnyLock(inode); -+ return au_ii(inode)->ii_hinode + bindex; ++ return au_hinode(au_ii(inode), bindex); +} + +/* ---------------------------------------------------------------------- */ @@ -15659,28 +19880,30 @@ diff -Nurp linux-4.4.3/fs/aufs/inode.h linux-4.4.3-aufs/fs/aufs/inode.h + +static inline void au_hn_imtx_lock(struct au_hinode *hdir) +{ -+ mutex_lock(&hdir->hi_inode->i_mutex); ++ inode_lock(hdir->hi_inode); + au_hn_suspend(hdir); +} + +static inline void au_hn_imtx_lock_nested(struct au_hinode *hdir, + unsigned int sc __maybe_unused) +{ -+ mutex_lock_nested(&hdir->hi_inode->i_mutex, sc); ++ inode_lock_nested(hdir->hi_inode, sc); + au_hn_suspend(hdir); +} + +static inline void au_hn_imtx_unlock(struct au_hinode *hdir) +{ + au_hn_resume(hdir); -+ mutex_unlock(&hdir->hi_inode->i_mutex); ++ inode_unlock(hdir->hi_inode); +} + +#endif /* __KERNEL__ */ +#endif /* __AUFS_INODE_H__ */ -diff -Nurp linux-4.4.3/fs/aufs/ioctl.c linux-4.4.3-aufs/fs/aufs/ioctl.c ---- linux-4.4.3/fs/aufs/ioctl.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/ioctl.c 2016-03-02 17:59:37.369498746 +0200 +diff --git a/fs/aufs/ioctl.c b/fs/aufs/ioctl.c +new file mode 100644 +index 0000000..2ebfdc4 +--- /dev/null ++++ b/fs/aufs/ioctl.c @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima @@ -15701,7 +19924,7 @@ diff -Nurp linux-4.4.3/fs/aufs/ioctl.c linux-4.4.3-aufs/fs/aufs/ioctl.c +static int au_wbr_fd(struct path *path, struct aufs_wbr_fd __user *arg) +{ + int err, fd; -+ aufs_bindex_t wbi, bindex, bend; ++ aufs_bindex_t wbi, bindex, bbot; + struct file *h_file; + struct super_block *sb; + struct dentry *root; @@ -15741,10 +19964,10 @@ diff -Nurp linux-4.4.3/fs/aufs/ioctl.c linux-4.4.3-aufs/fs/aufs/ioctl.c + sb = path->dentry->d_sb; + root = sb->s_root; + aufs_read_lock(root, AuLock_IR); -+ bend = au_sbend(sb); ++ bbot = au_sbbot(sb); + if (wbrfd.brid >= 0) { + wbi = au_br_index(sb, wbrfd.brid); -+ if (unlikely(wbi < 0 || wbi > bend)) ++ if (unlikely(wbi < 0 || wbi > bbot)) + goto out_unlock; + } + @@ -15756,7 +19979,7 @@ diff -Nurp linux-4.4.3/fs/aufs/ioctl.c linux-4.4.3-aufs/fs/aufs/ioctl.c + + bindex = wbi + 1; + wbi = -1; -+ for (; bindex <= bend; bindex++) { ++ for (; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + if (au_br_writable(br->br_perm)) { + wbi = bindex; @@ -15776,7 +19999,7 @@ diff -Nurp linux-4.4.3/fs/aufs/ioctl.c linux-4.4.3-aufs/fs/aufs/ioctl.c + if (IS_ERR(h_file)) + goto out_fd; + -+ atomic_dec(&br->br_count); /* cf. au_h_open() */ ++ au_br_put(br); /* cf. au_h_open() */ + fd_install(fd, h_file); + err = fd; + goto out; /* success */ @@ -15888,4109 +20111,11 @@ diff -Nurp linux-4.4.3/fs/aufs/ioctl.c linux-4.4.3-aufs/fs/aufs/ioctl.c + return aufs_ioctl_nondir(file, cmd, (unsigned long)compat_ptr(arg)); +} +#endif -diff -Nurp linux-4.4.3/fs/aufs/i_op_add.c linux-4.4.3-aufs/fs/aufs/i_op_add.c ---- linux-4.4.3/fs/aufs/i_op_add.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/i_op_add.c 2016-03-02 17:59:37.367498731 +0200 -@@ -0,0 +1,919 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ */ -+ -+/* -+ * inode operations (add entry) -+ */ -+ -+#include "aufs.h" -+ -+/* -+ * final procedure of adding a new entry, except link(2). -+ * remove whiteout, instantiate, copyup the parent dir's times and size -+ * and update version. -+ * if it failed, re-create the removed whiteout. -+ */ -+static int epilog(struct inode *dir, aufs_bindex_t bindex, -+ struct dentry *wh_dentry, struct dentry *dentry) -+{ -+ int err, rerr; -+ aufs_bindex_t bwh; -+ struct path h_path; -+ struct super_block *sb; -+ struct inode *inode, *h_dir; -+ struct dentry *wh; -+ -+ bwh = -1; -+ sb = dir->i_sb; -+ if (wh_dentry) { -+ h_dir = d_inode(wh_dentry->d_parent); /* dir inode is locked */ -+ IMustLock(h_dir); -+ AuDebugOn(au_h_iptr(dir, bindex) != h_dir); -+ bwh = au_dbwh(dentry); -+ h_path.dentry = wh_dentry; -+ h_path.mnt = au_sbr_mnt(sb, bindex); -+ err = au_wh_unlink_dentry(au_h_iptr(dir, bindex), &h_path, -+ dentry); -+ if (unlikely(err)) -+ goto out; -+ } -+ -+ inode = au_new_inode(dentry, /*must_new*/1); -+ if (!IS_ERR(inode)) { -+ d_instantiate(dentry, inode); -+ dir = d_inode(dentry->d_parent); /* dir inode is locked */ -+ IMustLock(dir); -+ au_dir_ts(dir, bindex); -+ dir->i_version++; -+ au_fhsm_wrote(sb, bindex, /*force*/0); -+ return 0; /* success */ -+ } -+ -+ err = PTR_ERR(inode); -+ if (!wh_dentry) -+ goto out; -+ -+ /* revert */ -+ /* dir inode is locked */ -+ wh = au_wh_create(dentry, bwh, wh_dentry->d_parent); -+ rerr = PTR_ERR(wh); -+ if (IS_ERR(wh)) { -+ AuIOErr("%pd reverting whiteout failed(%d, %d)\n", -+ dentry, err, rerr); -+ err = -EIO; -+ } else -+ dput(wh); -+ -+out: -+ return err; -+} -+ -+static int au_d_may_add(struct dentry *dentry) -+{ -+ int err; -+ -+ err = 0; -+ if (unlikely(d_unhashed(dentry))) -+ err = -ENOENT; -+ if (unlikely(d_really_is_positive(dentry))) -+ err = -EEXIST; -+ return err; -+} -+ -+/* -+ * simple tests for the adding inode operations. -+ * following the checks in vfs, plus the parent-child relationship. -+ */ -+int au_may_add(struct dentry *dentry, aufs_bindex_t bindex, -+ struct dentry *h_parent, int isdir) -+{ -+ int err; -+ umode_t h_mode; -+ struct dentry *h_dentry; -+ struct inode *h_inode; -+ -+ err = -ENAMETOOLONG; -+ if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) -+ goto out; -+ -+ h_dentry = au_h_dptr(dentry, bindex); -+ if (d_really_is_negative(dentry)) { -+ err = -EEXIST; -+ if (unlikely(d_is_positive(h_dentry))) -+ goto out; -+ } else { -+ /* rename(2) case */ -+ err = -EIO; -+ if (unlikely(d_is_negative(h_dentry))) -+ goto out; -+ h_inode = d_inode(h_dentry); -+ if (unlikely(!h_inode->i_nlink)) -+ goto out; -+ -+ h_mode = h_inode->i_mode; -+ if (!isdir) { -+ err = -EISDIR; -+ if (unlikely(S_ISDIR(h_mode))) -+ goto out; -+ } else if (unlikely(!S_ISDIR(h_mode))) { -+ err = -ENOTDIR; -+ goto out; -+ } -+ } -+ -+ err = 0; -+ /* expected parent dir is locked */ -+ if (unlikely(h_parent != h_dentry->d_parent)) -+ err = -EIO; -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+/* -+ * initial procedure of adding a new entry. -+ * prepare writable branch and the parent dir, lock it, -+ * and lookup whiteout for the new entry. -+ */ -+static struct dentry* -+lock_hdir_lkup_wh(struct dentry *dentry, struct au_dtime *dt, -+ struct dentry *src_dentry, struct au_pin *pin, -+ struct au_wr_dir_args *wr_dir_args) -+{ -+ struct dentry *wh_dentry, *h_parent; -+ struct super_block *sb; -+ struct au_branch *br; -+ int err; -+ unsigned int udba; -+ aufs_bindex_t bcpup; -+ -+ AuDbg("%pd\n", dentry); -+ -+ err = au_wr_dir(dentry, src_dentry, wr_dir_args); -+ bcpup = err; -+ wh_dentry = ERR_PTR(err); -+ if (unlikely(err < 0)) -+ goto out; -+ -+ sb = dentry->d_sb; -+ udba = au_opt_udba(sb); -+ err = au_pin(pin, dentry, bcpup, udba, -+ AuPin_DI_LOCKED | AuPin_MNT_WRITE); -+ wh_dentry = ERR_PTR(err); -+ if (unlikely(err)) -+ goto out; -+ -+ h_parent = au_pinned_h_parent(pin); -+ if (udba != AuOpt_UDBA_NONE -+ && au_dbstart(dentry) == bcpup) -+ err = au_may_add(dentry, bcpup, h_parent, -+ au_ftest_wrdir(wr_dir_args->flags, ISDIR)); -+ else if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) -+ err = -ENAMETOOLONG; -+ wh_dentry = ERR_PTR(err); -+ if (unlikely(err)) -+ goto out_unpin; -+ -+ br = au_sbr(sb, bcpup); -+ if (dt) { -+ struct path tmp = { -+ .dentry = h_parent, -+ .mnt = au_br_mnt(br) -+ }; -+ au_dtime_store(dt, au_pinned_parent(pin), &tmp); -+ } -+ -+ wh_dentry = NULL; -+ if (bcpup != au_dbwh(dentry)) -+ goto out; /* success */ -+ -+ /* -+ * ENAMETOOLONG here means that if we allowed create such name, then it -+ * would not be able to removed in the future. So we don't allow such -+ * name here and we don't handle ENAMETOOLONG differently here. -+ */ -+ wh_dentry = au_wh_lkup(h_parent, &dentry->d_name, br); -+ -+out_unpin: -+ if (IS_ERR(wh_dentry)) -+ au_unpin(pin); -+out: -+ return wh_dentry; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+enum { Mknod, Symlink, Creat }; -+struct simple_arg { -+ int type; -+ union { -+ struct { -+ umode_t mode; -+ bool want_excl; -+ bool try_aopen; -+ struct vfsub_aopen_args *aopen; -+ } c; -+ struct { -+ const char *symname; -+ } s; -+ struct { -+ umode_t mode; -+ dev_t dev; -+ } m; -+ } u; -+}; -+ -+static int add_simple(struct inode *dir, struct dentry *dentry, -+ struct simple_arg *arg) -+{ -+ int err, rerr; -+ aufs_bindex_t bstart; -+ unsigned char created; -+ const unsigned char try_aopen -+ = (arg->type == Creat && arg->u.c.try_aopen); -+ struct dentry *wh_dentry, *parent; -+ struct inode *h_dir; -+ struct super_block *sb; -+ struct au_branch *br; -+ /* to reuduce stack size */ -+ struct { -+ struct au_dtime dt; -+ struct au_pin pin; -+ struct path h_path; -+ struct au_wr_dir_args wr_dir_args; -+ } *a; -+ -+ AuDbg("%pd\n", dentry); -+ IMustLock(dir); -+ -+ err = -ENOMEM; -+ a = kmalloc(sizeof(*a), GFP_NOFS); -+ if (unlikely(!a)) -+ goto out; -+ a->wr_dir_args.force_btgt = -1; -+ a->wr_dir_args.flags = AuWrDir_ADD_ENTRY; -+ -+ parent = dentry->d_parent; /* dir inode is locked */ -+ if (!try_aopen) { -+ err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN); -+ if (unlikely(err)) -+ goto out_free; -+ } -+ err = au_d_may_add(dentry); -+ if (unlikely(err)) -+ goto out_unlock; -+ if (!try_aopen) -+ di_write_lock_parent(parent); -+ wh_dentry = lock_hdir_lkup_wh(dentry, &a->dt, /*src_dentry*/NULL, -+ &a->pin, &a->wr_dir_args); -+ err = PTR_ERR(wh_dentry); -+ if (IS_ERR(wh_dentry)) -+ goto out_parent; -+ -+ bstart = au_dbstart(dentry); -+ sb = dentry->d_sb; -+ br = au_sbr(sb, bstart); -+ a->h_path.dentry = au_h_dptr(dentry, bstart); -+ a->h_path.mnt = au_br_mnt(br); -+ h_dir = au_pinned_h_dir(&a->pin); -+ switch (arg->type) { -+ case Creat: -+ err = 0; -+ if (!try_aopen || !h_dir->i_op->atomic_open) -+ err = vfsub_create(h_dir, &a->h_path, arg->u.c.mode, -+ arg->u.c.want_excl); -+ else -+ err = vfsub_atomic_open(h_dir, a->h_path.dentry, -+ arg->u.c.aopen, br); -+ break; -+ case Symlink: -+ err = vfsub_symlink(h_dir, &a->h_path, arg->u.s.symname); -+ break; -+ case Mknod: -+ err = vfsub_mknod(h_dir, &a->h_path, arg->u.m.mode, -+ arg->u.m.dev); -+ break; -+ default: -+ BUG(); -+ } -+ created = !err; -+ if (!err) -+ err = epilog(dir, bstart, wh_dentry, dentry); -+ -+ /* revert */ -+ if (unlikely(created && err && d_is_positive(a->h_path.dentry))) { -+ /* no delegation since it is just created */ -+ rerr = vfsub_unlink(h_dir, &a->h_path, /*delegated*/NULL, -+ /*force*/0); -+ if (rerr) { -+ AuIOErr("%pd revert failure(%d, %d)\n", -+ dentry, err, rerr); -+ err = -EIO; -+ } -+ au_dtime_revert(&a->dt); -+ } -+ -+ if (!err && try_aopen && !h_dir->i_op->atomic_open) -+ *arg->u.c.aopen->opened |= FILE_CREATED; -+ -+ au_unpin(&a->pin); -+ dput(wh_dentry); -+ -+out_parent: -+ if (!try_aopen) -+ di_write_unlock(parent); -+out_unlock: -+ if (unlikely(err)) { -+ au_update_dbstart(dentry); -+ d_drop(dentry); -+ } -+ if (!try_aopen) -+ aufs_read_unlock(dentry, AuLock_DW); -+out_free: -+ kfree(a); -+out: -+ return err; -+} -+ -+int aufs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, -+ dev_t dev) -+{ -+ struct simple_arg arg = { -+ .type = Mknod, -+ .u.m = { -+ .mode = mode, -+ .dev = dev -+ } -+ }; -+ return add_simple(dir, dentry, &arg); -+} -+ -+int aufs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) -+{ -+ struct simple_arg arg = { -+ .type = Symlink, -+ .u.s.symname = symname -+ }; -+ return add_simple(dir, dentry, &arg); -+} -+ -+int aufs_create(struct inode *dir, struct dentry *dentry, umode_t mode, -+ bool want_excl) -+{ -+ struct simple_arg arg = { -+ .type = Creat, -+ .u.c = { -+ .mode = mode, -+ .want_excl = want_excl -+ } -+ }; -+ return add_simple(dir, dentry, &arg); -+} -+ -+int au_aopen_or_create(struct inode *dir, struct dentry *dentry, -+ struct vfsub_aopen_args *aopen_args) -+{ -+ struct simple_arg arg = { -+ .type = Creat, -+ .u.c = { -+ .mode = aopen_args->create_mode, -+ .want_excl = aopen_args->open_flag & O_EXCL, -+ .try_aopen = true, -+ .aopen = aopen_args -+ } -+ }; -+ return add_simple(dir, dentry, &arg); -+} -+ -+int aufs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode) -+{ -+ int err; -+ aufs_bindex_t bindex; -+ struct super_block *sb; -+ struct dentry *parent, *h_parent, *h_dentry; -+ struct inode *h_dir, *inode; -+ struct vfsmount *h_mnt; -+ struct au_wr_dir_args wr_dir_args = { -+ .force_btgt = -1, -+ .flags = AuWrDir_TMPFILE -+ }; -+ -+ /* copy-up may happen */ -+ mutex_lock(&dir->i_mutex); -+ -+ sb = dir->i_sb; -+ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); -+ if (unlikely(err)) -+ goto out; -+ -+ err = au_di_init(dentry); -+ if (unlikely(err)) -+ goto out_si; -+ -+ err = -EBUSY; -+ parent = d_find_any_alias(dir); -+ AuDebugOn(!parent); -+ di_write_lock_parent(parent); -+ if (unlikely(d_inode(parent) != dir)) -+ goto out_parent; -+ -+ err = au_digen_test(parent, au_sigen(sb)); -+ if (unlikely(err)) -+ goto out_parent; -+ -+ bindex = au_dbstart(parent); -+ au_set_dbstart(dentry, bindex); -+ au_set_dbend(dentry, bindex); -+ err = au_wr_dir(dentry, /*src_dentry*/NULL, &wr_dir_args); -+ bindex = err; -+ if (unlikely(err < 0)) -+ goto out_parent; -+ -+ err = -EOPNOTSUPP; -+ h_dir = au_h_iptr(dir, bindex); -+ if (unlikely(!h_dir->i_op->tmpfile)) -+ goto out_parent; -+ -+ h_mnt = au_sbr_mnt(sb, bindex); -+ err = vfsub_mnt_want_write(h_mnt); -+ if (unlikely(err)) -+ goto out_parent; -+ -+ h_parent = au_h_dptr(parent, bindex); -+ err = inode_permission(d_inode(h_parent), MAY_WRITE | MAY_EXEC); -+ if (unlikely(err)) -+ goto out_mnt; -+ -+ err = -ENOMEM; -+ h_dentry = d_alloc(h_parent, &dentry->d_name); -+ if (unlikely(!h_dentry)) -+ goto out_mnt; -+ -+ err = h_dir->i_op->tmpfile(h_dir, h_dentry, mode); -+ if (unlikely(err)) -+ goto out_dentry; -+ -+ au_set_dbstart(dentry, bindex); -+ au_set_dbend(dentry, bindex); -+ au_set_h_dptr(dentry, bindex, dget(h_dentry)); -+ inode = au_new_inode(dentry, /*must_new*/1); -+ if (IS_ERR(inode)) { -+ err = PTR_ERR(inode); -+ au_set_h_dptr(dentry, bindex, NULL); -+ au_set_dbstart(dentry, -1); -+ au_set_dbend(dentry, -1); -+ } else { -+ if (!inode->i_nlink) -+ set_nlink(inode, 1); -+ d_tmpfile(dentry, inode); -+ au_di(dentry)->di_tmpfile = 1; -+ -+ /* update without i_mutex */ -+ if (au_ibstart(dir) == au_dbstart(dentry)) -+ au_cpup_attr_timesizes(dir); -+ } -+ -+out_dentry: -+ dput(h_dentry); -+out_mnt: -+ vfsub_mnt_drop_write(h_mnt); -+out_parent: -+ di_write_unlock(parent); -+ dput(parent); -+ di_write_unlock(dentry); -+ if (!err) -+#if 0 -+ /* verbose coding for lock class name */ -+ au_rw_class(&au_di(dentry)->di_rwsem, -+ au_lc_key + AuLcNonDir_DIINFO); -+#else -+ ; -+#endif -+ else { -+ au_di_fin(dentry); -+ dentry->d_fsdata = NULL; -+ } -+out_si: -+ si_read_unlock(sb); -+out: -+ mutex_unlock(&dir->i_mutex); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct au_link_args { -+ aufs_bindex_t bdst, bsrc; -+ struct au_pin pin; -+ struct path h_path; -+ struct dentry *src_parent, *parent; -+}; -+ -+static int au_cpup_before_link(struct dentry *src_dentry, -+ struct au_link_args *a) -+{ -+ int err; -+ struct dentry *h_src_dentry; -+ struct au_cp_generic cpg = { -+ .dentry = src_dentry, -+ .bdst = a->bdst, -+ .bsrc = a->bsrc, -+ .len = -1, -+ .pin = &a->pin, -+ .flags = AuCpup_DTIME | AuCpup_HOPEN /* | AuCpup_KEEPLINO */ -+ }; -+ -+ di_read_lock_parent(a->src_parent, AuLock_IR); -+ err = au_test_and_cpup_dirs(src_dentry, a->bdst); -+ if (unlikely(err)) -+ goto out; -+ -+ h_src_dentry = au_h_dptr(src_dentry, a->bsrc); -+ err = au_pin(&a->pin, src_dentry, a->bdst, -+ au_opt_udba(src_dentry->d_sb), -+ AuPin_DI_LOCKED | AuPin_MNT_WRITE); -+ if (unlikely(err)) -+ goto out; -+ -+ err = au_sio_cpup_simple(&cpg); -+ au_unpin(&a->pin); -+ -+out: -+ di_read_unlock(a->src_parent, AuLock_IR); -+ return err; -+} -+ -+static int au_cpup_or_link(struct dentry *src_dentry, struct dentry *dentry, -+ struct au_link_args *a) -+{ -+ int err; -+ unsigned char plink; -+ aufs_bindex_t bend; -+ struct dentry *h_src_dentry; -+ struct inode *h_inode, *inode, *delegated; -+ struct super_block *sb; -+ struct file *h_file; -+ -+ plink = 0; -+ h_inode = NULL; -+ sb = src_dentry->d_sb; -+ inode = d_inode(src_dentry); -+ if (au_ibstart(inode) <= a->bdst) -+ h_inode = au_h_iptr(inode, a->bdst); -+ if (!h_inode || !h_inode->i_nlink) { -+ /* copyup src_dentry as the name of dentry. */ -+ bend = au_dbend(dentry); -+ if (bend < a->bsrc) -+ au_set_dbend(dentry, a->bsrc); -+ au_set_h_dptr(dentry, a->bsrc, -+ dget(au_h_dptr(src_dentry, a->bsrc))); -+ dget(a->h_path.dentry); -+ au_set_h_dptr(dentry, a->bdst, NULL); -+ AuDbg("temporary d_inode...\n"); -+ spin_lock(&dentry->d_lock); -+ dentry->d_inode = d_inode(src_dentry); /* tmp */ -+ spin_unlock(&dentry->d_lock); -+ h_file = au_h_open_pre(dentry, a->bsrc, /*force_wr*/0); -+ if (IS_ERR(h_file)) -+ err = PTR_ERR(h_file); -+ else { -+ struct au_cp_generic cpg = { -+ .dentry = dentry, -+ .bdst = a->bdst, -+ .bsrc = -1, -+ .len = -1, -+ .pin = &a->pin, -+ .flags = AuCpup_KEEPLINO -+ }; -+ err = au_sio_cpup_simple(&cpg); -+ au_h_open_post(dentry, a->bsrc, h_file); -+ if (!err) { -+ dput(a->h_path.dentry); -+ a->h_path.dentry = au_h_dptr(dentry, a->bdst); -+ } else -+ au_set_h_dptr(dentry, a->bdst, -+ a->h_path.dentry); -+ } -+ spin_lock(&dentry->d_lock); -+ dentry->d_inode = NULL; /* restore */ -+ spin_unlock(&dentry->d_lock); -+ AuDbg("temporary d_inode...done\n"); -+ au_set_h_dptr(dentry, a->bsrc, NULL); -+ au_set_dbend(dentry, bend); -+ } else { -+ /* the inode of src_dentry already exists on a.bdst branch */ -+ h_src_dentry = d_find_alias(h_inode); -+ if (!h_src_dentry && au_plink_test(inode)) { -+ plink = 1; -+ h_src_dentry = au_plink_lkup(inode, a->bdst); -+ err = PTR_ERR(h_src_dentry); -+ if (IS_ERR(h_src_dentry)) -+ goto out; -+ -+ if (unlikely(d_is_negative(h_src_dentry))) { -+ dput(h_src_dentry); -+ h_src_dentry = NULL; -+ } -+ -+ } -+ if (h_src_dentry) { -+ delegated = NULL; -+ err = vfsub_link(h_src_dentry, au_pinned_h_dir(&a->pin), -+ &a->h_path, &delegated); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal link\n"); -+ iput(delegated); -+ } -+ dput(h_src_dentry); -+ } else { -+ AuIOErr("no dentry found for hi%lu on b%d\n", -+ h_inode->i_ino, a->bdst); -+ err = -EIO; -+ } -+ } -+ -+ if (!err && !plink) -+ au_plink_append(inode, a->bdst, a->h_path.dentry); -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+int aufs_link(struct dentry *src_dentry, struct inode *dir, -+ struct dentry *dentry) -+{ -+ int err, rerr; -+ struct au_dtime dt; -+ struct au_link_args *a; -+ struct dentry *wh_dentry, *h_src_dentry; -+ struct inode *inode, *delegated; -+ struct super_block *sb; -+ struct au_wr_dir_args wr_dir_args = { -+ /* .force_btgt = -1, */ -+ .flags = AuWrDir_ADD_ENTRY -+ }; -+ -+ IMustLock(dir); -+ inode = d_inode(src_dentry); -+ IMustLock(inode); -+ -+ err = -ENOMEM; -+ a = kzalloc(sizeof(*a), GFP_NOFS); -+ if (unlikely(!a)) -+ goto out; -+ -+ a->parent = dentry->d_parent; /* dir inode is locked */ -+ err = aufs_read_and_write_lock2(dentry, src_dentry, -+ AuLock_NOPLM | AuLock_GEN); -+ if (unlikely(err)) -+ goto out_kfree; -+ err = au_d_linkable(src_dentry); -+ if (unlikely(err)) -+ goto out_unlock; -+ err = au_d_may_add(dentry); -+ if (unlikely(err)) -+ goto out_unlock; -+ -+ a->src_parent = dget_parent(src_dentry); -+ wr_dir_args.force_btgt = au_ibstart(inode); -+ -+ di_write_lock_parent(a->parent); -+ wr_dir_args.force_btgt = au_wbr(dentry, wr_dir_args.force_btgt); -+ wh_dentry = lock_hdir_lkup_wh(dentry, &dt, src_dentry, &a->pin, -+ &wr_dir_args); -+ err = PTR_ERR(wh_dentry); -+ if (IS_ERR(wh_dentry)) -+ goto out_parent; -+ -+ err = 0; -+ sb = dentry->d_sb; -+ a->bdst = au_dbstart(dentry); -+ a->h_path.dentry = au_h_dptr(dentry, a->bdst); -+ a->h_path.mnt = au_sbr_mnt(sb, a->bdst); -+ a->bsrc = au_ibstart(inode); -+ h_src_dentry = au_h_d_alias(src_dentry, a->bsrc); -+ if (!h_src_dentry && au_di(src_dentry)->di_tmpfile) -+ h_src_dentry = dget(au_hi_wh(inode, a->bsrc)); -+ if (!h_src_dentry) { -+ a->bsrc = au_dbstart(src_dentry); -+ h_src_dentry = au_h_d_alias(src_dentry, a->bsrc); -+ AuDebugOn(!h_src_dentry); -+ } else if (IS_ERR(h_src_dentry)) { -+ err = PTR_ERR(h_src_dentry); -+ goto out_parent; -+ } -+ -+ if (au_opt_test(au_mntflags(sb), PLINK)) { -+ if (a->bdst < a->bsrc -+ /* && h_src_dentry->d_sb != a->h_path.dentry->d_sb */) -+ err = au_cpup_or_link(src_dentry, dentry, a); -+ else { -+ delegated = NULL; -+ err = vfsub_link(h_src_dentry, au_pinned_h_dir(&a->pin), -+ &a->h_path, &delegated); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal link\n"); -+ iput(delegated); -+ } -+ } -+ dput(h_src_dentry); -+ } else { -+ /* -+ * copyup src_dentry to the branch we process, -+ * and then link(2) to it. -+ */ -+ dput(h_src_dentry); -+ if (a->bdst < a->bsrc -+ /* && h_src_dentry->d_sb != a->h_path.dentry->d_sb */) { -+ au_unpin(&a->pin); -+ di_write_unlock(a->parent); -+ err = au_cpup_before_link(src_dentry, a); -+ di_write_lock_parent(a->parent); -+ if (!err) -+ err = au_pin(&a->pin, dentry, a->bdst, -+ au_opt_udba(sb), -+ AuPin_DI_LOCKED | AuPin_MNT_WRITE); -+ if (unlikely(err)) -+ goto out_wh; -+ } -+ if (!err) { -+ h_src_dentry = au_h_dptr(src_dentry, a->bdst); -+ err = -ENOENT; -+ if (h_src_dentry && d_is_positive(h_src_dentry)) { -+ delegated = NULL; -+ err = vfsub_link(h_src_dentry, -+ au_pinned_h_dir(&a->pin), -+ &a->h_path, &delegated); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry" -+ " for NFSv4 delegation" -+ " for an internal link\n"); -+ iput(delegated); -+ } -+ } -+ } -+ } -+ if (unlikely(err)) -+ goto out_unpin; -+ -+ if (wh_dentry) { -+ a->h_path.dentry = wh_dentry; -+ err = au_wh_unlink_dentry(au_pinned_h_dir(&a->pin), &a->h_path, -+ dentry); -+ if (unlikely(err)) -+ goto out_revert; -+ } -+ -+ au_dir_ts(dir, a->bdst); -+ dir->i_version++; -+ inc_nlink(inode); -+ inode->i_ctime = dir->i_ctime; -+ d_instantiate(dentry, au_igrab(inode)); -+ if (d_unhashed(a->h_path.dentry)) -+ /* some filesystem calls d_drop() */ -+ d_drop(dentry); -+ /* some filesystems consume an inode even hardlink */ -+ au_fhsm_wrote(sb, a->bdst, /*force*/0); -+ goto out_unpin; /* success */ -+ -+out_revert: -+ /* no delegation since it is just created */ -+ rerr = vfsub_unlink(au_pinned_h_dir(&a->pin), &a->h_path, -+ /*delegated*/NULL, /*force*/0); -+ if (unlikely(rerr)) { -+ AuIOErr("%pd reverting failed(%d, %d)\n", dentry, err, rerr); -+ err = -EIO; -+ } -+ au_dtime_revert(&dt); -+out_unpin: -+ au_unpin(&a->pin); -+out_wh: -+ dput(wh_dentry); -+out_parent: -+ di_write_unlock(a->parent); -+ dput(a->src_parent); -+out_unlock: -+ if (unlikely(err)) { -+ au_update_dbstart(dentry); -+ d_drop(dentry); -+ } -+ aufs_read_and_write_unlock2(dentry, src_dentry); -+out_kfree: -+ kfree(a); -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+int aufs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) -+{ -+ int err, rerr; -+ aufs_bindex_t bindex; -+ unsigned char diropq; -+ struct path h_path; -+ struct dentry *wh_dentry, *parent, *opq_dentry; -+ struct mutex *h_mtx; -+ struct super_block *sb; -+ struct { -+ struct au_pin pin; -+ struct au_dtime dt; -+ } *a; /* reduce the stack usage */ -+ struct au_wr_dir_args wr_dir_args = { -+ .force_btgt = -1, -+ .flags = AuWrDir_ADD_ENTRY | AuWrDir_ISDIR -+ }; -+ -+ IMustLock(dir); -+ -+ err = -ENOMEM; -+ a = kmalloc(sizeof(*a), GFP_NOFS); -+ if (unlikely(!a)) -+ goto out; -+ -+ err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN); -+ if (unlikely(err)) -+ goto out_free; -+ err = au_d_may_add(dentry); -+ if (unlikely(err)) -+ goto out_unlock; -+ -+ parent = dentry->d_parent; /* dir inode is locked */ -+ di_write_lock_parent(parent); -+ wh_dentry = lock_hdir_lkup_wh(dentry, &a->dt, /*src_dentry*/NULL, -+ &a->pin, &wr_dir_args); -+ err = PTR_ERR(wh_dentry); -+ if (IS_ERR(wh_dentry)) -+ goto out_parent; -+ -+ sb = dentry->d_sb; -+ bindex = au_dbstart(dentry); -+ h_path.dentry = au_h_dptr(dentry, bindex); -+ h_path.mnt = au_sbr_mnt(sb, bindex); -+ err = vfsub_mkdir(au_pinned_h_dir(&a->pin), &h_path, mode); -+ if (unlikely(err)) -+ goto out_unpin; -+ -+ /* make the dir opaque */ -+ diropq = 0; -+ h_mtx = &d_inode(h_path.dentry)->i_mutex; -+ if (wh_dentry -+ || au_opt_test(au_mntflags(sb), ALWAYS_DIROPQ)) { -+ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); -+ opq_dentry = au_diropq_create(dentry, bindex); -+ mutex_unlock(h_mtx); -+ err = PTR_ERR(opq_dentry); -+ if (IS_ERR(opq_dentry)) -+ goto out_dir; -+ dput(opq_dentry); -+ diropq = 1; -+ } -+ -+ err = epilog(dir, bindex, wh_dentry, dentry); -+ if (!err) { -+ inc_nlink(dir); -+ goto out_unpin; /* success */ -+ } -+ -+ /* revert */ -+ if (diropq) { -+ AuLabel(revert opq); -+ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); -+ rerr = au_diropq_remove(dentry, bindex); -+ mutex_unlock(h_mtx); -+ if (rerr) { -+ AuIOErr("%pd reverting diropq failed(%d, %d)\n", -+ dentry, err, rerr); -+ err = -EIO; -+ } -+ } -+ -+out_dir: -+ AuLabel(revert dir); -+ rerr = vfsub_rmdir(au_pinned_h_dir(&a->pin), &h_path); -+ if (rerr) { -+ AuIOErr("%pd reverting dir failed(%d, %d)\n", -+ dentry, err, rerr); -+ err = -EIO; -+ } -+ au_dtime_revert(&a->dt); -+out_unpin: -+ au_unpin(&a->pin); -+ dput(wh_dentry); -+out_parent: -+ di_write_unlock(parent); -+out_unlock: -+ if (unlikely(err)) { -+ au_update_dbstart(dentry); -+ d_drop(dentry); -+ } -+ aufs_read_unlock(dentry, AuLock_DW); -+out_free: -+ kfree(a); -+out: -+ return err; -+} -diff -Nurp linux-4.4.3/fs/aufs/i_op.c linux-4.4.3-aufs/fs/aufs/i_op.c ---- linux-4.4.3/fs/aufs/i_op.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/i_op.c 2016-03-02 17:59:37.367498731 +0200 -@@ -0,0 +1,1477 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ */ -+ -+/* -+ * inode operations (except add/del/rename) -+ */ -+ -+#include -+#include -+#include -+#include -+#include "aufs.h" -+ -+static int h_permission(struct inode *h_inode, int mask, -+ struct vfsmount *h_mnt, int brperm) -+{ -+ int err; -+ const unsigned char write_mask = !!(mask & (MAY_WRITE | MAY_APPEND)); -+ -+ err = -EACCES; -+ if ((write_mask && IS_IMMUTABLE(h_inode)) -+ || ((mask & MAY_EXEC) -+ && S_ISREG(h_inode->i_mode) -+ && ((h_mnt->mnt_flags & MNT_NOEXEC) -+ || !(h_inode->i_mode & S_IXUGO)))) -+ goto out; -+ -+ /* -+ * - skip the lower fs test in the case of write to ro branch. -+ * - nfs dir permission write check is optimized, but a policy for -+ * link/rename requires a real check. -+ * - nfs always sets MS_POSIXACL regardless its mount option 'noacl.' -+ * in this case, generic_permission() returns -EOPNOTSUPP. -+ */ -+ if ((write_mask && !au_br_writable(brperm)) -+ || (au_test_nfs(h_inode->i_sb) && S_ISDIR(h_inode->i_mode) -+ && write_mask && !(mask & MAY_READ)) -+ || !h_inode->i_op->permission) { -+ /* AuLabel(generic_permission); */ -+ /* AuDbg("get_acl %pf\n", h_inode->i_op->get_acl); */ -+ err = generic_permission(h_inode, mask); -+ if (err == -EOPNOTSUPP && au_test_nfs_noacl(h_inode)) -+ err = h_inode->i_op->permission(h_inode, mask); -+ AuTraceErr(err); -+ } else { -+ /* AuLabel(h_inode->permission); */ -+ err = h_inode->i_op->permission(h_inode, mask); -+ AuTraceErr(err); -+ } -+ -+ if (!err) -+ err = devcgroup_inode_permission(h_inode, mask); -+ if (!err) -+ err = security_inode_permission(h_inode, mask); -+ -+#if 0 -+ if (!err) { -+ /* todo: do we need to call ima_path_check()? */ -+ struct path h_path = { -+ .dentry = -+ .mnt = h_mnt -+ }; -+ err = ima_path_check(&h_path, -+ mask & (MAY_READ | MAY_WRITE | MAY_EXEC), -+ IMA_COUNT_LEAVE); -+ } -+#endif -+ -+out: -+ return err; -+} -+ -+static int aufs_permission(struct inode *inode, int mask) -+{ -+ int err; -+ aufs_bindex_t bindex, bend; -+ const unsigned char isdir = !!S_ISDIR(inode->i_mode), -+ write_mask = !!(mask & (MAY_WRITE | MAY_APPEND)); -+ struct inode *h_inode; -+ struct super_block *sb; -+ struct au_branch *br; -+ -+ /* todo: support rcu-walk? */ -+ if (mask & MAY_NOT_BLOCK) -+ return -ECHILD; -+ -+ sb = inode->i_sb; -+ si_read_lock(sb, AuLock_FLUSH); -+ ii_read_lock_child(inode); -+#if 0 -+ err = au_iigen_test(inode, au_sigen(sb)); -+ if (unlikely(err)) -+ goto out; -+#endif -+ -+ if (!isdir -+ || write_mask -+ || au_opt_test(au_mntflags(sb), DIRPERM1)) { -+ err = au_busy_or_stale(); -+ h_inode = au_h_iptr(inode, au_ibstart(inode)); -+ if (unlikely(!h_inode -+ || (h_inode->i_mode & S_IFMT) -+ != (inode->i_mode & S_IFMT))) -+ goto out; -+ -+ err = 0; -+ bindex = au_ibstart(inode); -+ br = au_sbr(sb, bindex); -+ err = h_permission(h_inode, mask, au_br_mnt(br), br->br_perm); -+ if (write_mask -+ && !err -+ && !special_file(h_inode->i_mode)) { -+ /* test whether the upper writable branch exists */ -+ err = -EROFS; -+ for (; bindex >= 0; bindex--) -+ if (!au_br_rdonly(au_sbr(sb, bindex))) { -+ err = 0; -+ break; -+ } -+ } -+ goto out; -+ } -+ -+ /* non-write to dir */ -+ err = 0; -+ bend = au_ibend(inode); -+ for (bindex = au_ibstart(inode); !err && bindex <= bend; bindex++) { -+ h_inode = au_h_iptr(inode, bindex); -+ if (h_inode) { -+ err = au_busy_or_stale(); -+ if (unlikely(!S_ISDIR(h_inode->i_mode))) -+ break; -+ -+ br = au_sbr(sb, bindex); -+ err = h_permission(h_inode, mask, au_br_mnt(br), -+ br->br_perm); -+ } -+ } -+ -+out: -+ ii_read_unlock(inode); -+ si_read_unlock(sb); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static struct dentry *aufs_lookup(struct inode *dir, struct dentry *dentry, -+ unsigned int flags) -+{ -+ struct dentry *ret, *parent; -+ struct inode *inode; -+ struct super_block *sb; -+ int err, npositive; -+ -+ IMustLock(dir); -+ -+ /* todo: support rcu-walk? */ -+ ret = ERR_PTR(-ECHILD); -+ if (flags & LOOKUP_RCU) -+ goto out; -+ -+ ret = ERR_PTR(-ENAMETOOLONG); -+ if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) -+ goto out; -+ -+ sb = dir->i_sb; -+ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); -+ ret = ERR_PTR(err); -+ if (unlikely(err)) -+ goto out; -+ -+ err = au_di_init(dentry); -+ ret = ERR_PTR(err); -+ if (unlikely(err)) -+ goto out_si; -+ -+ inode = NULL; -+ npositive = 0; /* suppress a warning */ -+ parent = dentry->d_parent; /* dir inode is locked */ -+ di_read_lock_parent(parent, AuLock_IR); -+ err = au_alive_dir(parent); -+ if (!err) -+ err = au_digen_test(parent, au_sigen(sb)); -+ if (!err) { -+ npositive = au_lkup_dentry(dentry, au_dbstart(parent), -+ /*type*/0); -+ err = npositive; -+ } -+ di_read_unlock(parent, AuLock_IR); -+ ret = ERR_PTR(err); -+ if (unlikely(err < 0)) -+ goto out_unlock; -+ -+ if (npositive) { -+ inode = au_new_inode(dentry, /*must_new*/0); -+ if (IS_ERR(inode)) { -+ ret = (void *)inode; -+ inode = NULL; -+ goto out_unlock; -+ } -+ } -+ -+ if (inode) -+ atomic_inc(&inode->i_count); -+ ret = d_splice_alias(inode, dentry); -+#if 0 -+ if (unlikely(d_need_lookup(dentry))) { -+ spin_lock(&dentry->d_lock); -+ dentry->d_flags &= ~DCACHE_NEED_LOOKUP; -+ spin_unlock(&dentry->d_lock); -+ } else -+#endif -+ if (inode) { -+ if (!IS_ERR(ret)) { -+ iput(inode); -+ if (ret && ret != dentry) -+ ii_write_unlock(inode); -+ } else { -+ ii_write_unlock(inode); -+ iput(inode); -+ inode = NULL; -+ } -+ } -+ -+out_unlock: -+ di_write_unlock(dentry); -+ if (inode) { -+ /* verbose coding for lock class name */ -+ if (unlikely(S_ISLNK(inode->i_mode))) -+ au_rw_class(&au_di(dentry)->di_rwsem, -+ au_lc_key + AuLcSymlink_DIINFO); -+ else if (unlikely(S_ISDIR(inode->i_mode))) -+ au_rw_class(&au_di(dentry)->di_rwsem, -+ au_lc_key + AuLcDir_DIINFO); -+ else /* likely */ -+ au_rw_class(&au_di(dentry)->di_rwsem, -+ au_lc_key + AuLcNonDir_DIINFO); -+ } -+out_si: -+ si_read_unlock(sb); -+out: -+ return ret; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct aopen_node { -+ struct hlist_node hlist; -+ struct file *file, *h_file; -+}; -+ -+static int au_do_aopen(struct inode *inode, struct file *file) -+{ -+ struct au_sphlhead *aopen; -+ struct aopen_node *node; -+ struct au_do_open_args args = { -+ .no_lock = 1, -+ .open = au_do_open_nondir -+ }; -+ -+ aopen = &au_sbi(inode->i_sb)->si_aopen; -+ spin_lock(&aopen->spin); -+ hlist_for_each_entry(node, &aopen->head, hlist) -+ if (node->file == file) { -+ args.h_file = node->h_file; -+ break; -+ } -+ spin_unlock(&aopen->spin); -+ /* AuDebugOn(!args.h_file); */ -+ -+ return au_do_open(file, &args); -+} -+ -+static int aufs_atomic_open(struct inode *dir, struct dentry *dentry, -+ struct file *file, unsigned int open_flag, -+ umode_t create_mode, int *opened) -+{ -+ int err, h_opened = *opened; -+ struct dentry *parent; -+ struct dentry *d; -+ struct au_sphlhead *aopen; -+ struct vfsub_aopen_args args = { -+ .open_flag = open_flag, -+ .create_mode = create_mode, -+ .opened = &h_opened -+ }; -+ struct aopen_node aopen_node = { -+ .file = file -+ }; -+ -+ IMustLock(dir); -+ AuDbg("open_flag 0x%x\n", open_flag); -+ AuDbgDentry(dentry); -+ -+ err = 0; -+ if (!au_di(dentry)) { -+ d = aufs_lookup(dir, dentry, /*flags*/0); -+ if (IS_ERR(d)) { -+ err = PTR_ERR(d); -+ goto out; -+ } else if (d) { -+ /* -+ * obsoleted dentry found. -+ * another error will be returned later. -+ */ -+ d_drop(d); -+ dput(d); -+ AuDbgDentry(d); -+ } -+ AuDbgDentry(dentry); -+ } -+ -+ if (d_is_positive(dentry) -+ || d_unhashed(dentry) -+ || d_unlinked(dentry) -+ || !(open_flag & O_CREAT)) -+ goto out_no_open; -+ -+ err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH | AuLock_GEN); -+ if (unlikely(err)) -+ goto out; -+ -+ parent = dentry->d_parent; /* dir is locked */ -+ di_write_lock_parent(parent); -+ err = au_lkup_dentry(dentry, /*bstart*/0, /*type*/0); -+ if (unlikely(err)) -+ goto out_unlock; -+ -+ AuDbgDentry(dentry); -+ if (d_is_positive(dentry)) -+ goto out_unlock; -+ -+ args.file = get_empty_filp(); -+ err = PTR_ERR(args.file); -+ if (IS_ERR(args.file)) -+ goto out_unlock; -+ -+ args.file->f_flags = file->f_flags; -+ err = au_aopen_or_create(dir, dentry, &args); -+ AuTraceErr(err); -+ AuDbgFile(args.file); -+ if (unlikely(err < 0)) { -+ if (h_opened & FILE_OPENED) -+ fput(args.file); -+ else -+ put_filp(args.file); -+ goto out_unlock; -+ } -+ -+ /* some filesystems don't set FILE_CREATED while succeeded? */ -+ *opened |= FILE_CREATED; -+ if (h_opened & FILE_OPENED) -+ aopen_node.h_file = args.file; -+ else { -+ put_filp(args.file); -+ args.file = NULL; -+ } -+ aopen = &au_sbi(dir->i_sb)->si_aopen; -+ au_sphl_add(&aopen_node.hlist, aopen); -+ err = finish_open(file, dentry, au_do_aopen, opened); -+ au_sphl_del(&aopen_node.hlist, aopen); -+ AuTraceErr(err); -+ AuDbgFile(file); -+ if (aopen_node.h_file) -+ fput(aopen_node.h_file); -+ -+out_unlock: -+ di_write_unlock(parent); -+ aufs_read_unlock(dentry, AuLock_DW); -+ AuDbgDentry(dentry); -+ if (unlikely(err)) -+ goto out; -+out_no_open: -+ if (!err && !(*opened & FILE_CREATED)) { -+ AuLabel(out_no_open); -+ dget(dentry); -+ err = finish_no_open(file, dentry); -+ } -+out: -+ AuDbg("%pd%s%s\n", dentry, -+ (*opened & FILE_CREATED) ? " created" : "", -+ (*opened & FILE_OPENED) ? " opened" : ""); -+ AuTraceErr(err); -+ return err; -+} -+ -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int au_wr_dir_cpup(struct dentry *dentry, struct dentry *parent, -+ const unsigned char add_entry, aufs_bindex_t bcpup, -+ aufs_bindex_t bstart) -+{ -+ int err; -+ struct dentry *h_parent; -+ struct inode *h_dir; -+ -+ if (add_entry) -+ IMustLock(d_inode(parent)); -+ else -+ di_write_lock_parent(parent); -+ -+ err = 0; -+ if (!au_h_dptr(parent, bcpup)) { -+ if (bstart > bcpup) -+ err = au_cpup_dirs(dentry, bcpup); -+ else if (bstart < bcpup) -+ err = au_cpdown_dirs(dentry, bcpup); -+ else -+ BUG(); -+ } -+ if (!err && add_entry && !au_ftest_wrdir(add_entry, TMPFILE)) { -+ h_parent = au_h_dptr(parent, bcpup); -+ h_dir = d_inode(h_parent); -+ mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT); -+ err = au_lkup_neg(dentry, bcpup, /*wh*/0); -+ /* todo: no unlock here */ -+ mutex_unlock(&h_dir->i_mutex); -+ -+ AuDbg("bcpup %d\n", bcpup); -+ if (!err) { -+ if (d_really_is_negative(dentry)) -+ au_set_h_dptr(dentry, bstart, NULL); -+ au_update_dbrange(dentry, /*do_put_zero*/0); -+ } -+ } -+ -+ if (!add_entry) -+ di_write_unlock(parent); -+ if (!err) -+ err = bcpup; /* success */ -+ -+ AuTraceErr(err); -+ return err; -+} -+ -+/* -+ * decide the branch and the parent dir where we will create a new entry. -+ * returns new bindex or an error. -+ * copyup the parent dir if needed. -+ */ -+int au_wr_dir(struct dentry *dentry, struct dentry *src_dentry, -+ struct au_wr_dir_args *args) -+{ -+ int err; -+ unsigned int flags; -+ aufs_bindex_t bcpup, bstart, src_bstart; -+ const unsigned char add_entry -+ = au_ftest_wrdir(args->flags, ADD_ENTRY) -+ | au_ftest_wrdir(args->flags, TMPFILE); -+ struct super_block *sb; -+ struct dentry *parent; -+ struct au_sbinfo *sbinfo; -+ -+ sb = dentry->d_sb; -+ sbinfo = au_sbi(sb); -+ parent = dget_parent(dentry); -+ bstart = au_dbstart(dentry); -+ bcpup = bstart; -+ if (args->force_btgt < 0) { -+ if (src_dentry) { -+ src_bstart = au_dbstart(src_dentry); -+ if (src_bstart < bstart) -+ bcpup = src_bstart; -+ } else if (add_entry) { -+ flags = 0; -+ if (au_ftest_wrdir(args->flags, ISDIR)) -+ au_fset_wbr(flags, DIR); -+ err = AuWbrCreate(sbinfo, dentry, flags); -+ bcpup = err; -+ } -+ -+ if (bcpup < 0 || au_test_ro(sb, bcpup, d_inode(dentry))) { -+ if (add_entry) -+ err = AuWbrCopyup(sbinfo, dentry); -+ else { -+ if (!IS_ROOT(dentry)) { -+ di_read_lock_parent(parent, !AuLock_IR); -+ err = AuWbrCopyup(sbinfo, dentry); -+ di_read_unlock(parent, !AuLock_IR); -+ } else -+ err = AuWbrCopyup(sbinfo, dentry); -+ } -+ bcpup = err; -+ if (unlikely(err < 0)) -+ goto out; -+ } -+ } else { -+ bcpup = args->force_btgt; -+ AuDebugOn(au_test_ro(sb, bcpup, d_inode(dentry))); -+ } -+ -+ AuDbg("bstart %d, bcpup %d\n", bstart, bcpup); -+ err = bcpup; -+ if (bcpup == bstart) -+ goto out; /* success */ -+ -+ /* copyup the new parent into the branch we process */ -+ err = au_wr_dir_cpup(dentry, parent, add_entry, bcpup, bstart); -+ if (err >= 0) { -+ if (d_really_is_negative(dentry)) { -+ au_set_h_dptr(dentry, bstart, NULL); -+ au_set_dbstart(dentry, bcpup); -+ au_set_dbend(dentry, bcpup); -+ } -+ AuDebugOn(add_entry -+ && !au_ftest_wrdir(args->flags, TMPFILE) -+ && !au_h_dptr(dentry, bcpup)); -+ } -+ -+out: -+ dput(parent); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+void au_pin_hdir_unlock(struct au_pin *p) -+{ -+ if (p->hdir) -+ au_hn_imtx_unlock(p->hdir); -+} -+ -+int au_pin_hdir_lock(struct au_pin *p) -+{ -+ int err; -+ -+ err = 0; -+ if (!p->hdir) -+ goto out; -+ -+ /* even if an error happens later, keep this lock */ -+ au_hn_imtx_lock_nested(p->hdir, p->lsc_hi); -+ -+ err = -EBUSY; -+ if (unlikely(p->hdir->hi_inode != d_inode(p->h_parent))) -+ goto out; -+ -+ err = 0; -+ if (p->h_dentry) -+ err = au_h_verify(p->h_dentry, p->udba, p->hdir->hi_inode, -+ p->h_parent, p->br); -+ -+out: -+ return err; -+} -+ -+int au_pin_hdir_relock(struct au_pin *p) -+{ -+ int err, i; -+ struct inode *h_i; -+ struct dentry *h_d[] = { -+ p->h_dentry, -+ p->h_parent -+ }; -+ -+ err = au_pin_hdir_lock(p); -+ if (unlikely(err)) -+ goto out; -+ -+ for (i = 0; !err && i < sizeof(h_d)/sizeof(*h_d); i++) { -+ if (!h_d[i]) -+ continue; -+ if (d_is_positive(h_d[i])) { -+ h_i = d_inode(h_d[i]); -+ err = !h_i->i_nlink; -+ } -+ } -+ -+out: -+ return err; -+} -+ -+void au_pin_hdir_set_owner(struct au_pin *p, struct task_struct *task) -+{ -+#if defined(CONFIG_DEBUG_MUTEXES) || defined(CONFIG_SMP) -+ p->hdir->hi_inode->i_mutex.owner = task; -+#endif -+} -+ -+void au_pin_hdir_acquire_nest(struct au_pin *p) -+{ -+ if (p->hdir) { -+ mutex_acquire_nest(&p->hdir->hi_inode->i_mutex.dep_map, -+ p->lsc_hi, 0, NULL, _RET_IP_); -+ au_pin_hdir_set_owner(p, current); -+ } -+} -+ -+void au_pin_hdir_release(struct au_pin *p) -+{ -+ if (p->hdir) { -+ au_pin_hdir_set_owner(p, p->task); -+ mutex_release(&p->hdir->hi_inode->i_mutex.dep_map, 1, _RET_IP_); -+ } -+} -+ -+struct dentry *au_pinned_h_parent(struct au_pin *pin) -+{ -+ if (pin && pin->parent) -+ return au_h_dptr(pin->parent, pin->bindex); -+ return NULL; -+} -+ -+void au_unpin(struct au_pin *p) -+{ -+ if (p->hdir) -+ au_pin_hdir_unlock(p); -+ if (p->h_mnt && au_ftest_pin(p->flags, MNT_WRITE)) -+ vfsub_mnt_drop_write(p->h_mnt); -+ if (!p->hdir) -+ return; -+ -+ if (!au_ftest_pin(p->flags, DI_LOCKED)) -+ di_read_unlock(p->parent, AuLock_IR); -+ iput(p->hdir->hi_inode); -+ dput(p->parent); -+ p->parent = NULL; -+ p->hdir = NULL; -+ p->h_mnt = NULL; -+ /* do not clear p->task */ -+} -+ -+int au_do_pin(struct au_pin *p) -+{ -+ int err; -+ struct super_block *sb; -+ struct inode *h_dir; -+ -+ err = 0; -+ sb = p->dentry->d_sb; -+ p->br = au_sbr(sb, p->bindex); -+ if (IS_ROOT(p->dentry)) { -+ if (au_ftest_pin(p->flags, MNT_WRITE)) { -+ p->h_mnt = au_br_mnt(p->br); -+ err = vfsub_mnt_want_write(p->h_mnt); -+ if (unlikely(err)) { -+ au_fclr_pin(p->flags, MNT_WRITE); -+ goto out_err; -+ } -+ } -+ goto out; -+ } -+ -+ p->h_dentry = NULL; -+ if (p->bindex <= au_dbend(p->dentry)) -+ p->h_dentry = au_h_dptr(p->dentry, p->bindex); -+ -+ p->parent = dget_parent(p->dentry); -+ if (!au_ftest_pin(p->flags, DI_LOCKED)) -+ di_read_lock(p->parent, AuLock_IR, p->lsc_di); -+ -+ h_dir = NULL; -+ p->h_parent = au_h_dptr(p->parent, p->bindex); -+ p->hdir = au_hi(d_inode(p->parent), p->bindex); -+ if (p->hdir) -+ h_dir = p->hdir->hi_inode; -+ -+ /* -+ * udba case, or -+ * if DI_LOCKED is not set, then p->parent may be different -+ * and h_parent can be NULL. -+ */ -+ if (unlikely(!p->hdir || !h_dir || !p->h_parent)) { -+ err = -EBUSY; -+ if (!au_ftest_pin(p->flags, DI_LOCKED)) -+ di_read_unlock(p->parent, AuLock_IR); -+ dput(p->parent); -+ p->parent = NULL; -+ goto out_err; -+ } -+ -+ if (au_ftest_pin(p->flags, MNT_WRITE)) { -+ p->h_mnt = au_br_mnt(p->br); -+ err = vfsub_mnt_want_write(p->h_mnt); -+ if (unlikely(err)) { -+ au_fclr_pin(p->flags, MNT_WRITE); -+ if (!au_ftest_pin(p->flags, DI_LOCKED)) -+ di_read_unlock(p->parent, AuLock_IR); -+ dput(p->parent); -+ p->parent = NULL; -+ goto out_err; -+ } -+ } -+ -+ au_igrab(h_dir); -+ err = au_pin_hdir_lock(p); -+ if (!err) -+ goto out; /* success */ -+ -+ au_unpin(p); -+ -+out_err: -+ pr_err("err %d\n", err); -+ err = au_busy_or_stale(); -+out: -+ return err; -+} -+ -+void au_pin_init(struct au_pin *p, struct dentry *dentry, -+ aufs_bindex_t bindex, int lsc_di, int lsc_hi, -+ unsigned int udba, unsigned char flags) -+{ -+ p->dentry = dentry; -+ p->udba = udba; -+ p->lsc_di = lsc_di; -+ p->lsc_hi = lsc_hi; -+ p->flags = flags; -+ p->bindex = bindex; -+ -+ p->parent = NULL; -+ p->hdir = NULL; -+ p->h_mnt = NULL; -+ -+ p->h_dentry = NULL; -+ p->h_parent = NULL; -+ p->br = NULL; -+ p->task = current; -+} -+ -+int au_pin(struct au_pin *pin, struct dentry *dentry, aufs_bindex_t bindex, -+ unsigned int udba, unsigned char flags) -+{ -+ au_pin_init(pin, dentry, bindex, AuLsc_DI_PARENT, AuLsc_I_PARENT2, -+ udba, flags); -+ return au_do_pin(pin); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * ->setattr() and ->getattr() are called in various cases. -+ * chmod, stat: dentry is revalidated. -+ * fchmod, fstat: file and dentry are not revalidated, additionally they may be -+ * unhashed. -+ * for ->setattr(), ia->ia_file is passed from ftruncate only. -+ */ -+/* todo: consolidate with do_refresh() and simple_reval_dpath() */ -+int au_reval_for_attr(struct dentry *dentry, unsigned int sigen) -+{ -+ int err; -+ struct dentry *parent; -+ -+ err = 0; -+ if (au_digen_test(dentry, sigen)) { -+ parent = dget_parent(dentry); -+ di_read_lock_parent(parent, AuLock_IR); -+ err = au_refresh_dentry(dentry, parent); -+ di_read_unlock(parent, AuLock_IR); -+ dput(parent); -+ } -+ -+ AuTraceErr(err); -+ return err; -+} -+ -+int au_pin_and_icpup(struct dentry *dentry, struct iattr *ia, -+ struct au_icpup_args *a) -+{ -+ int err; -+ loff_t sz; -+ aufs_bindex_t bstart, ibstart; -+ struct dentry *hi_wh, *parent; -+ struct inode *inode; -+ struct au_wr_dir_args wr_dir_args = { -+ .force_btgt = -1, -+ .flags = 0 -+ }; -+ -+ if (d_is_dir(dentry)) -+ au_fset_wrdir(wr_dir_args.flags, ISDIR); -+ /* plink or hi_wh() case */ -+ bstart = au_dbstart(dentry); -+ inode = d_inode(dentry); -+ ibstart = au_ibstart(inode); -+ if (bstart != ibstart && !au_test_ro(inode->i_sb, ibstart, inode)) -+ wr_dir_args.force_btgt = ibstart; -+ err = au_wr_dir(dentry, /*src_dentry*/NULL, &wr_dir_args); -+ if (unlikely(err < 0)) -+ goto out; -+ a->btgt = err; -+ if (err != bstart) -+ au_fset_icpup(a->flags, DID_CPUP); -+ -+ err = 0; -+ a->pin_flags = AuPin_MNT_WRITE; -+ parent = NULL; -+ if (!IS_ROOT(dentry)) { -+ au_fset_pin(a->pin_flags, DI_LOCKED); -+ parent = dget_parent(dentry); -+ di_write_lock_parent(parent); -+ } -+ -+ err = au_pin(&a->pin, dentry, a->btgt, a->udba, a->pin_flags); -+ if (unlikely(err)) -+ goto out_parent; -+ -+ a->h_path.dentry = au_h_dptr(dentry, bstart); -+ sz = -1; -+ a->h_inode = d_inode(a->h_path.dentry); -+ if (ia && (ia->ia_valid & ATTR_SIZE)) { -+ mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD); -+ if (ia->ia_size < i_size_read(a->h_inode)) -+ sz = ia->ia_size; -+ mutex_unlock(&a->h_inode->i_mutex); -+ } -+ -+ hi_wh = NULL; -+ if (au_ftest_icpup(a->flags, DID_CPUP) && d_unlinked(dentry)) { -+ hi_wh = au_hi_wh(inode, a->btgt); -+ if (!hi_wh) { -+ struct au_cp_generic cpg = { -+ .dentry = dentry, -+ .bdst = a->btgt, -+ .bsrc = -1, -+ .len = sz, -+ .pin = &a->pin -+ }; -+ err = au_sio_cpup_wh(&cpg, /*file*/NULL); -+ if (unlikely(err)) -+ goto out_unlock; -+ hi_wh = au_hi_wh(inode, a->btgt); -+ /* todo: revalidate hi_wh? */ -+ } -+ } -+ -+ if (parent) { -+ au_pin_set_parent_lflag(&a->pin, /*lflag*/0); -+ di_downgrade_lock(parent, AuLock_IR); -+ dput(parent); -+ parent = NULL; -+ } -+ if (!au_ftest_icpup(a->flags, DID_CPUP)) -+ goto out; /* success */ -+ -+ if (!d_unhashed(dentry)) { -+ struct au_cp_generic cpg = { -+ .dentry = dentry, -+ .bdst = a->btgt, -+ .bsrc = bstart, -+ .len = sz, -+ .pin = &a->pin, -+ .flags = AuCpup_DTIME | AuCpup_HOPEN -+ }; -+ err = au_sio_cpup_simple(&cpg); -+ if (!err) -+ a->h_path.dentry = au_h_dptr(dentry, a->btgt); -+ } else if (!hi_wh) -+ a->h_path.dentry = au_h_dptr(dentry, a->btgt); -+ else -+ a->h_path.dentry = hi_wh; /* do not dget here */ -+ -+out_unlock: -+ a->h_inode = d_inode(a->h_path.dentry); -+ if (!err) -+ goto out; /* success */ -+ au_unpin(&a->pin); -+out_parent: -+ if (parent) { -+ di_write_unlock(parent); -+ dput(parent); -+ } -+out: -+ if (!err) -+ mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD); -+ return err; -+} -+ -+static int aufs_setattr(struct dentry *dentry, struct iattr *ia) -+{ -+ int err; -+ struct inode *inode, *delegated; -+ struct super_block *sb; -+ struct file *file; -+ struct au_icpup_args *a; -+ -+ inode = d_inode(dentry); -+ IMustLock(inode); -+ -+ err = -ENOMEM; -+ a = kzalloc(sizeof(*a), GFP_NOFS); -+ if (unlikely(!a)) -+ goto out; -+ -+ if (ia->ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID)) -+ ia->ia_valid &= ~ATTR_MODE; -+ -+ file = NULL; -+ sb = dentry->d_sb; -+ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); -+ if (unlikely(err)) -+ goto out_kfree; -+ -+ if (ia->ia_valid & ATTR_FILE) { -+ /* currently ftruncate(2) only */ -+ AuDebugOn(!d_is_reg(dentry)); -+ file = ia->ia_file; -+ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1); -+ if (unlikely(err)) -+ goto out_si; -+ ia->ia_file = au_hf_top(file); -+ a->udba = AuOpt_UDBA_NONE; -+ } else { -+ /* fchmod() doesn't pass ia_file */ -+ a->udba = au_opt_udba(sb); -+ di_write_lock_child(dentry); -+ /* no d_unlinked(), to set UDBA_NONE for root */ -+ if (d_unhashed(dentry)) -+ a->udba = AuOpt_UDBA_NONE; -+ if (a->udba != AuOpt_UDBA_NONE) { -+ AuDebugOn(IS_ROOT(dentry)); -+ err = au_reval_for_attr(dentry, au_sigen(sb)); -+ if (unlikely(err)) -+ goto out_dentry; -+ } -+ } -+ -+ err = au_pin_and_icpup(dentry, ia, a); -+ if (unlikely(err < 0)) -+ goto out_dentry; -+ if (au_ftest_icpup(a->flags, DID_CPUP)) { -+ ia->ia_file = NULL; -+ ia->ia_valid &= ~ATTR_FILE; -+ } -+ -+ a->h_path.mnt = au_sbr_mnt(sb, a->btgt); -+ if ((ia->ia_valid & (ATTR_MODE | ATTR_CTIME)) -+ == (ATTR_MODE | ATTR_CTIME)) { -+ err = security_path_chmod(&a->h_path, ia->ia_mode); -+ if (unlikely(err)) -+ goto out_unlock; -+ } else if ((ia->ia_valid & (ATTR_UID | ATTR_GID)) -+ && (ia->ia_valid & ATTR_CTIME)) { -+ err = security_path_chown(&a->h_path, ia->ia_uid, ia->ia_gid); -+ if (unlikely(err)) -+ goto out_unlock; -+ } -+ -+ if (ia->ia_valid & ATTR_SIZE) { -+ struct file *f; -+ -+ if (ia->ia_size < i_size_read(inode)) -+ /* unmap only */ -+ truncate_setsize(inode, ia->ia_size); -+ -+ f = NULL; -+ if (ia->ia_valid & ATTR_FILE) -+ f = ia->ia_file; -+ mutex_unlock(&a->h_inode->i_mutex); -+ err = vfsub_trunc(&a->h_path, ia->ia_size, ia->ia_valid, f); -+ mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD); -+ } else { -+ delegated = NULL; -+ while (1) { -+ err = vfsub_notify_change(&a->h_path, ia, &delegated); -+ if (delegated) { -+ err = break_deleg_wait(&delegated); -+ if (!err) -+ continue; -+ } -+ break; -+ } -+ } -+ /* -+ * regardless aufs 'acl' option setting. -+ * why don't all acl-aware fs call this func from their ->setattr()? -+ */ -+ if (!err && (ia->ia_valid & ATTR_MODE)) -+ err = vfsub_acl_chmod(a->h_inode, ia->ia_mode); -+ if (!err) -+ au_cpup_attr_changeable(inode); -+ -+out_unlock: -+ mutex_unlock(&a->h_inode->i_mutex); -+ au_unpin(&a->pin); -+ if (unlikely(err)) -+ au_update_dbstart(dentry); -+out_dentry: -+ di_write_unlock(dentry); -+ if (file) { -+ fi_write_unlock(file); -+ ia->ia_file = file; -+ ia->ia_valid |= ATTR_FILE; -+ } -+out_si: -+ si_read_unlock(sb); -+out_kfree: -+ kfree(a); -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+#if IS_ENABLED(CONFIG_AUFS_XATTR) || IS_ENABLED(CONFIG_FS_POSIX_ACL) -+static int au_h_path_to_set_attr(struct dentry *dentry, -+ struct au_icpup_args *a, struct path *h_path) -+{ -+ int err; -+ struct super_block *sb; -+ -+ sb = dentry->d_sb; -+ a->udba = au_opt_udba(sb); -+ /* no d_unlinked(), to set UDBA_NONE for root */ -+ if (d_unhashed(dentry)) -+ a->udba = AuOpt_UDBA_NONE; -+ if (a->udba != AuOpt_UDBA_NONE) { -+ AuDebugOn(IS_ROOT(dentry)); -+ err = au_reval_for_attr(dentry, au_sigen(sb)); -+ if (unlikely(err)) -+ goto out; -+ } -+ err = au_pin_and_icpup(dentry, /*ia*/NULL, a); -+ if (unlikely(err < 0)) -+ goto out; -+ -+ h_path->dentry = a->h_path.dentry; -+ h_path->mnt = au_sbr_mnt(sb, a->btgt); -+ -+out: -+ return err; -+} -+ -+ssize_t au_srxattr(struct dentry *dentry, struct au_srxattr *arg) -+{ -+ int err; -+ struct path h_path; -+ struct super_block *sb; -+ struct au_icpup_args *a; -+ struct inode *inode, *h_inode; -+ -+ inode = d_inode(dentry); -+ IMustLock(inode); -+ -+ err = -ENOMEM; -+ a = kzalloc(sizeof(*a), GFP_NOFS); -+ if (unlikely(!a)) -+ goto out; -+ -+ sb = dentry->d_sb; -+ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); -+ if (unlikely(err)) -+ goto out_kfree; -+ -+ h_path.dentry = NULL; /* silence gcc */ -+ di_write_lock_child(dentry); -+ err = au_h_path_to_set_attr(dentry, a, &h_path); -+ if (unlikely(err)) -+ goto out_di; -+ -+ mutex_unlock(&a->h_inode->i_mutex); -+ switch (arg->type) { -+ case AU_XATTR_SET: -+ err = vfsub_setxattr(h_path.dentry, -+ arg->u.set.name, arg->u.set.value, -+ arg->u.set.size, arg->u.set.flags); -+ break; -+ case AU_XATTR_REMOVE: -+ err = vfsub_removexattr(h_path.dentry, arg->u.remove.name); -+ break; -+ case AU_ACL_SET: -+ err = -EOPNOTSUPP; -+ h_inode = d_inode(h_path.dentry); -+ if (h_inode->i_op->set_acl) -+ err = h_inode->i_op->set_acl(h_inode, -+ arg->u.acl_set.acl, -+ arg->u.acl_set.type); -+ break; -+ } -+ if (!err) -+ au_cpup_attr_timesizes(inode); -+ -+ au_unpin(&a->pin); -+ if (unlikely(err)) -+ au_update_dbstart(dentry); -+ -+out_di: -+ di_write_unlock(dentry); -+ si_read_unlock(sb); -+out_kfree: -+ kfree(a); -+out: -+ AuTraceErr(err); -+ return err; -+} -+#endif -+ -+static void au_refresh_iattr(struct inode *inode, struct kstat *st, -+ unsigned int nlink) -+{ -+ unsigned int n; -+ -+ inode->i_mode = st->mode; -+ /* don't i_[ug]id_write() here */ -+ inode->i_uid = st->uid; -+ inode->i_gid = st->gid; -+ inode->i_atime = st->atime; -+ inode->i_mtime = st->mtime; -+ inode->i_ctime = st->ctime; -+ -+ au_cpup_attr_nlink(inode, /*force*/0); -+ if (S_ISDIR(inode->i_mode)) { -+ n = inode->i_nlink; -+ n -= nlink; -+ n += st->nlink; -+ smp_mb(); /* for i_nlink */ -+ /* 0 can happen */ -+ set_nlink(inode, n); -+ } -+ -+ spin_lock(&inode->i_lock); -+ inode->i_blocks = st->blocks; -+ i_size_write(inode, st->size); -+ spin_unlock(&inode->i_lock); -+} -+ -+/* -+ * common routine for aufs_getattr() and aufs_getxattr(). -+ * returns zero or negative (an error). -+ * @dentry will be read-locked in success. -+ */ -+int au_h_path_getattr(struct dentry *dentry, int force, struct path *h_path) -+{ -+ int err; -+ unsigned int mnt_flags, sigen; -+ unsigned char udba_none; -+ aufs_bindex_t bindex; -+ struct super_block *sb, *h_sb; -+ struct inode *inode; -+ -+ h_path->mnt = NULL; -+ h_path->dentry = NULL; -+ -+ err = 0; -+ sb = dentry->d_sb; -+ mnt_flags = au_mntflags(sb); -+ udba_none = !!au_opt_test(mnt_flags, UDBA_NONE); -+ -+ /* support fstat(2) */ -+ if (!d_unlinked(dentry) && !udba_none) { -+ sigen = au_sigen(sb); -+ err = au_digen_test(dentry, sigen); -+ if (!err) { -+ di_read_lock_child(dentry, AuLock_IR); -+ err = au_dbrange_test(dentry); -+ if (unlikely(err)) { -+ di_read_unlock(dentry, AuLock_IR); -+ goto out; -+ } -+ } else { -+ AuDebugOn(IS_ROOT(dentry)); -+ di_write_lock_child(dentry); -+ err = au_dbrange_test(dentry); -+ if (!err) -+ err = au_reval_for_attr(dentry, sigen); -+ if (!err) -+ di_downgrade_lock(dentry, AuLock_IR); -+ else { -+ di_write_unlock(dentry); -+ goto out; -+ } -+ } -+ } else -+ di_read_lock_child(dentry, AuLock_IR); -+ -+ inode = d_inode(dentry); -+ bindex = au_ibstart(inode); -+ h_path->mnt = au_sbr_mnt(sb, bindex); -+ h_sb = h_path->mnt->mnt_sb; -+ if (!force -+ && !au_test_fs_bad_iattr(h_sb) -+ && udba_none) -+ goto out; /* success */ -+ -+ if (au_dbstart(dentry) == bindex) -+ h_path->dentry = au_h_dptr(dentry, bindex); -+ else if (au_opt_test(mnt_flags, PLINK) && au_plink_test(inode)) { -+ h_path->dentry = au_plink_lkup(inode, bindex); -+ if (IS_ERR(h_path->dentry)) -+ /* pretending success */ -+ h_path->dentry = NULL; -+ else -+ dput(h_path->dentry); -+ } -+ -+out: -+ return err; -+} -+ -+static int aufs_getattr(struct vfsmount *mnt __maybe_unused, -+ struct dentry *dentry, struct kstat *st) -+{ -+ int err; -+ unsigned char positive; -+ struct path h_path; -+ struct inode *inode; -+ struct super_block *sb; -+ -+ inode = d_inode(dentry); -+ sb = dentry->d_sb; -+ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); -+ if (unlikely(err)) -+ goto out; -+ err = au_h_path_getattr(dentry, /*force*/0, &h_path); -+ if (unlikely(err)) -+ goto out_si; -+ if (unlikely(!h_path.dentry)) -+ /* illegally overlapped or something */ -+ goto out_fill; /* pretending success */ -+ -+ positive = d_is_positive(h_path.dentry); -+ if (positive) -+ err = vfs_getattr(&h_path, st); -+ if (!err) { -+ if (positive) -+ au_refresh_iattr(inode, st, -+ d_inode(h_path.dentry)->i_nlink); -+ goto out_fill; /* success */ -+ } -+ AuTraceErr(err); -+ goto out_di; -+ -+out_fill: -+ generic_fillattr(inode, st); -+out_di: -+ di_read_unlock(dentry, AuLock_IR); -+out_si: -+ si_read_unlock(sb); -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * Assumption: -+ * - the number of symlinks is not so many. -+ * -+ * Structure: -+ * - sbinfo (instead of iinfo) contains an hlist of struct au_symlink. -+ * If iinfo contained the hlist, then it would be rather large waste of memory -+ * I am afraid. -+ * - struct au_symlink contains the necessary info for h_inode follow_link() and -+ * put_link(). -+ */ -+ -+struct au_symlink { -+ union { -+ struct hlist_node hlist; -+ struct rcu_head rcu; -+ }; -+ -+ struct inode *h_inode; -+ void *h_cookie; -+}; -+ -+static void au_symlink_add(struct super_block *sb, struct au_symlink *slink, -+ struct inode *h_inode, void *cookie) -+{ -+ struct au_sbinfo *sbinfo; -+ -+ ihold(h_inode); -+ slink->h_inode = h_inode; -+ slink->h_cookie = cookie; -+ sbinfo = au_sbi(sb); -+ au_sphl_add(&slink->hlist, &sbinfo->si_symlink); -+} -+ -+static void au_symlink_del(struct super_block *sb, struct au_symlink *slink) -+{ -+ struct au_sbinfo *sbinfo; -+ -+ /* do not iput() within rcu */ -+ iput(slink->h_inode); -+ slink->h_inode = NULL; -+ sbinfo = au_sbi(sb); -+ au_sphl_del_rcu(&slink->hlist, &sbinfo->si_symlink); -+ kfree_rcu(slink, rcu); -+} -+ -+static const char *aufs_follow_link(struct dentry *dentry, void **cookie) -+{ -+ const char *ret; -+ struct inode *inode, *h_inode; -+ struct dentry *h_dentry; -+ struct au_symlink *slink; -+ int err; -+ aufs_bindex_t bindex; -+ -+ ret = NULL; /* suppress a warning */ -+ err = aufs_read_lock(dentry, AuLock_IR | AuLock_GEN); -+ if (unlikely(err)) -+ goto out; -+ -+ err = au_d_hashed_positive(dentry); -+ if (unlikely(err)) -+ goto out_unlock; -+ -+ err = -EINVAL; -+ inode = d_inode(dentry); -+ bindex = au_ibstart(inode); -+ h_inode = au_h_iptr(inode, bindex); -+ if (unlikely(!h_inode->i_op->follow_link)) -+ goto out_unlock; -+ -+ err = -ENOMEM; -+ slink = kmalloc(sizeof(*slink), GFP_NOFS); -+ if (unlikely(!slink)) -+ goto out_unlock; -+ -+ err = -EBUSY; -+ h_dentry = NULL; -+ if (au_dbstart(dentry) <= bindex) { -+ h_dentry = au_h_dptr(dentry, bindex); -+ if (h_dentry) -+ dget(h_dentry); -+ } -+ if (!h_dentry) { -+ h_dentry = d_find_any_alias(h_inode); -+ if (IS_ERR(h_dentry)) { -+ err = PTR_ERR(h_dentry); -+ goto out_free; -+ } -+ } -+ if (unlikely(!h_dentry)) -+ goto out_free; -+ -+ err = 0; -+ AuDbg("%pf\n", h_inode->i_op->follow_link); -+ AuDbgDentry(h_dentry); -+ ret = h_inode->i_op->follow_link(h_dentry, cookie); -+ dput(h_dentry); -+ -+ if (!IS_ERR_OR_NULL(ret)) { -+ au_symlink_add(inode->i_sb, slink, h_inode, *cookie); -+ *cookie = slink; -+ AuDbg("slink %p\n", slink); -+ goto out_unlock; /* success */ -+ } -+ -+out_free: -+ slink->h_inode = NULL; -+ kfree_rcu(slink, rcu); -+out_unlock: -+ aufs_read_unlock(dentry, AuLock_IR); -+out: -+ if (unlikely(err)) -+ ret = ERR_PTR(err); -+ AuTraceErrPtr(ret); -+ return ret; -+} -+ -+static void aufs_put_link(struct inode *inode, void *cookie) -+{ -+ struct au_symlink *slink; -+ struct inode *h_inode; -+ -+ slink = cookie; -+ AuDbg("slink %p\n", slink); -+ h_inode = slink->h_inode; -+ AuDbg("%pf\n", h_inode->i_op->put_link); -+ AuDbgInode(h_inode); -+ if (h_inode->i_op->put_link) -+ h_inode->i_op->put_link(h_inode, slink->h_cookie); -+ au_symlink_del(inode->i_sb, slink); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int aufs_update_time(struct inode *inode, struct timespec *ts, int flags) -+{ -+ int err; -+ struct super_block *sb; -+ struct inode *h_inode; -+ -+ sb = inode->i_sb; -+ /* mmap_sem might be acquired already, cf. aufs_mmap() */ -+ lockdep_off(); -+ si_read_lock(sb, AuLock_FLUSH); -+ ii_write_lock_child(inode); -+ lockdep_on(); -+ h_inode = au_h_iptr(inode, au_ibstart(inode)); -+ err = vfsub_update_time(h_inode, ts, flags); -+ lockdep_off(); -+ if (!err) -+ au_cpup_attr_timesizes(inode); -+ ii_write_unlock(inode); -+ si_read_unlock(sb); -+ lockdep_on(); -+ -+ if (!err && (flags & S_VERSION)) -+ inode_inc_iversion(inode); -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* no getattr version will be set by module.c:aufs_init() */ -+struct inode_operations aufs_iop_nogetattr[AuIop_Last], -+ aufs_iop[] = { -+ [AuIop_SYMLINK] = { -+ .permission = aufs_permission, -+#ifdef CONFIG_FS_POSIX_ACL -+ .get_acl = aufs_get_acl, -+ .set_acl = aufs_set_acl, /* unsupport for symlink? */ -+#endif -+ -+ .setattr = aufs_setattr, -+ .getattr = aufs_getattr, -+ -+#ifdef CONFIG_AUFS_XATTR -+ .setxattr = aufs_setxattr, -+ .getxattr = aufs_getxattr, -+ .listxattr = aufs_listxattr, -+ .removexattr = aufs_removexattr, -+#endif -+ -+ .readlink = generic_readlink, -+ .follow_link = aufs_follow_link, -+ .put_link = aufs_put_link, -+ -+ /* .update_time = aufs_update_time */ -+ }, -+ [AuIop_DIR] = { -+ .create = aufs_create, -+ .lookup = aufs_lookup, -+ .link = aufs_link, -+ .unlink = aufs_unlink, -+ .symlink = aufs_symlink, -+ .mkdir = aufs_mkdir, -+ .rmdir = aufs_rmdir, -+ .mknod = aufs_mknod, -+ .rename = aufs_rename, -+ -+ .permission = aufs_permission, -+#ifdef CONFIG_FS_POSIX_ACL -+ .get_acl = aufs_get_acl, -+ .set_acl = aufs_set_acl, -+#endif -+ -+ .setattr = aufs_setattr, -+ .getattr = aufs_getattr, -+ -+#ifdef CONFIG_AUFS_XATTR -+ .setxattr = aufs_setxattr, -+ .getxattr = aufs_getxattr, -+ .listxattr = aufs_listxattr, -+ .removexattr = aufs_removexattr, -+#endif -+ -+ .update_time = aufs_update_time, -+ .atomic_open = aufs_atomic_open, -+ .tmpfile = aufs_tmpfile -+ }, -+ [AuIop_OTHER] = { -+ .permission = aufs_permission, -+#ifdef CONFIG_FS_POSIX_ACL -+ .get_acl = aufs_get_acl, -+ .set_acl = aufs_set_acl, -+#endif -+ -+ .setattr = aufs_setattr, -+ .getattr = aufs_getattr, -+ -+#ifdef CONFIG_AUFS_XATTR -+ .setxattr = aufs_setxattr, -+ .getxattr = aufs_getxattr, -+ .listxattr = aufs_listxattr, -+ .removexattr = aufs_removexattr, -+#endif -+ -+ .update_time = aufs_update_time -+ } -+}; -diff -Nurp linux-4.4.3/fs/aufs/i_op_del.c linux-4.4.3-aufs/fs/aufs/i_op_del.c ---- linux-4.4.3/fs/aufs/i_op_del.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/i_op_del.c 2016-03-02 17:59:37.368498738 +0200 -@@ -0,0 +1,497 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ */ -+ -+/* -+ * inode operations (del entry) -+ */ -+ -+#include "aufs.h" -+ -+/* -+ * decide if a new whiteout for @dentry is necessary or not. -+ * when it is necessary, prepare the parent dir for the upper branch whose -+ * branch index is @bcpup for creation. the actual creation of the whiteout will -+ * be done by caller. -+ * return value: -+ * 0: wh is unnecessary -+ * plus: wh is necessary -+ * minus: error -+ */ -+int au_wr_dir_need_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup) -+{ -+ int need_wh, err; -+ aufs_bindex_t bstart; -+ struct super_block *sb; -+ -+ sb = dentry->d_sb; -+ bstart = au_dbstart(dentry); -+ if (*bcpup < 0) { -+ *bcpup = bstart; -+ if (au_test_ro(sb, bstart, d_inode(dentry))) { -+ err = AuWbrCopyup(au_sbi(sb), dentry); -+ *bcpup = err; -+ if (unlikely(err < 0)) -+ goto out; -+ } -+ } else -+ AuDebugOn(bstart < *bcpup -+ || au_test_ro(sb, *bcpup, d_inode(dentry))); -+ AuDbg("bcpup %d, bstart %d\n", *bcpup, bstart); -+ -+ if (*bcpup != bstart) { -+ err = au_cpup_dirs(dentry, *bcpup); -+ if (unlikely(err)) -+ goto out; -+ need_wh = 1; -+ } else { -+ struct au_dinfo *dinfo, *tmp; -+ -+ need_wh = -ENOMEM; -+ dinfo = au_di(dentry); -+ tmp = au_di_alloc(sb, AuLsc_DI_TMP); -+ if (tmp) { -+ au_di_cp(tmp, dinfo); -+ au_di_swap(tmp, dinfo); -+ /* returns the number of positive dentries */ -+ need_wh = au_lkup_dentry(dentry, bstart + 1, /*type*/0); -+ au_di_swap(tmp, dinfo); -+ au_rw_write_unlock(&tmp->di_rwsem); -+ au_di_free(tmp); -+ } -+ } -+ AuDbg("need_wh %d\n", need_wh); -+ err = need_wh; -+ -+out: -+ return err; -+} -+ -+/* -+ * simple tests for the del-entry operations. -+ * following the checks in vfs, plus the parent-child relationship. -+ */ -+int au_may_del(struct dentry *dentry, aufs_bindex_t bindex, -+ struct dentry *h_parent, int isdir) -+{ -+ int err; -+ umode_t h_mode; -+ struct dentry *h_dentry, *h_latest; -+ struct inode *h_inode; -+ -+ h_dentry = au_h_dptr(dentry, bindex); -+ if (d_really_is_positive(dentry)) { -+ err = -ENOENT; -+ if (unlikely(d_is_negative(h_dentry))) -+ goto out; -+ h_inode = d_inode(h_dentry); -+ if (unlikely(!h_inode->i_nlink)) -+ goto out; -+ -+ h_mode = h_inode->i_mode; -+ if (!isdir) { -+ err = -EISDIR; -+ if (unlikely(S_ISDIR(h_mode))) -+ goto out; -+ } else if (unlikely(!S_ISDIR(h_mode))) { -+ err = -ENOTDIR; -+ goto out; -+ } -+ } else { -+ /* rename(2) case */ -+ err = -EIO; -+ if (unlikely(d_is_positive(h_dentry))) -+ goto out; -+ } -+ -+ err = -ENOENT; -+ /* expected parent dir is locked */ -+ if (unlikely(h_parent != h_dentry->d_parent)) -+ goto out; -+ err = 0; -+ -+ /* -+ * rmdir a dir may break the consistency on some filesystem. -+ * let's try heavy test. -+ */ -+ err = -EACCES; -+ if (unlikely(!au_opt_test(au_mntflags(dentry->d_sb), DIRPERM1) -+ && au_test_h_perm(d_inode(h_parent), -+ MAY_EXEC | MAY_WRITE))) -+ goto out; -+ -+ h_latest = au_sio_lkup_one(&dentry->d_name, h_parent); -+ err = -EIO; -+ if (IS_ERR(h_latest)) -+ goto out; -+ if (h_latest == h_dentry) -+ err = 0; -+ dput(h_latest); -+ -+out: -+ return err; -+} -+ -+/* -+ * decide the branch where we operate for @dentry. the branch index will be set -+ * @rbcpup. after diciding it, 'pin' it and store the timestamps of the parent -+ * dir for reverting. -+ * when a new whiteout is necessary, create it. -+ */ -+static struct dentry* -+lock_hdir_create_wh(struct dentry *dentry, int isdir, aufs_bindex_t *rbcpup, -+ struct au_dtime *dt, struct au_pin *pin) -+{ -+ struct dentry *wh_dentry; -+ struct super_block *sb; -+ struct path h_path; -+ int err, need_wh; -+ unsigned int udba; -+ aufs_bindex_t bcpup; -+ -+ need_wh = au_wr_dir_need_wh(dentry, isdir, rbcpup); -+ wh_dentry = ERR_PTR(need_wh); -+ if (unlikely(need_wh < 0)) -+ goto out; -+ -+ sb = dentry->d_sb; -+ udba = au_opt_udba(sb); -+ bcpup = *rbcpup; -+ err = au_pin(pin, dentry, bcpup, udba, -+ AuPin_DI_LOCKED | AuPin_MNT_WRITE); -+ wh_dentry = ERR_PTR(err); -+ if (unlikely(err)) -+ goto out; -+ -+ h_path.dentry = au_pinned_h_parent(pin); -+ if (udba != AuOpt_UDBA_NONE -+ && au_dbstart(dentry) == bcpup) { -+ err = au_may_del(dentry, bcpup, h_path.dentry, isdir); -+ wh_dentry = ERR_PTR(err); -+ if (unlikely(err)) -+ goto out_unpin; -+ } -+ -+ h_path.mnt = au_sbr_mnt(sb, bcpup); -+ au_dtime_store(dt, au_pinned_parent(pin), &h_path); -+ wh_dentry = NULL; -+ if (!need_wh) -+ goto out; /* success, no need to create whiteout */ -+ -+ wh_dentry = au_wh_create(dentry, bcpup, h_path.dentry); -+ if (IS_ERR(wh_dentry)) -+ goto out_unpin; -+ -+ /* returns with the parent is locked and wh_dentry is dget-ed */ -+ goto out; /* success */ -+ -+out_unpin: -+ au_unpin(pin); -+out: -+ return wh_dentry; -+} -+ -+/* -+ * when removing a dir, rename it to a unique temporary whiteout-ed name first -+ * in order to be revertible and save time for removing many child whiteouts -+ * under the dir. -+ * returns 1 when there are too many child whiteout and caller should remove -+ * them asynchronously. returns 0 when the number of children is enough small to -+ * remove now or the branch fs is a remote fs. -+ * otherwise return an error. -+ */ -+static int renwh_and_rmdir(struct dentry *dentry, aufs_bindex_t bindex, -+ struct au_nhash *whlist, struct inode *dir) -+{ -+ int rmdir_later, err, dirwh; -+ struct dentry *h_dentry; -+ struct super_block *sb; -+ struct inode *inode; -+ -+ sb = dentry->d_sb; -+ SiMustAnyLock(sb); -+ h_dentry = au_h_dptr(dentry, bindex); -+ err = au_whtmp_ren(h_dentry, au_sbr(sb, bindex)); -+ if (unlikely(err)) -+ goto out; -+ -+ /* stop monitoring */ -+ inode = d_inode(dentry); -+ au_hn_free(au_hi(inode, bindex)); -+ -+ if (!au_test_fs_remote(h_dentry->d_sb)) { -+ dirwh = au_sbi(sb)->si_dirwh; -+ rmdir_later = (dirwh <= 1); -+ if (!rmdir_later) -+ rmdir_later = au_nhash_test_longer_wh(whlist, bindex, -+ dirwh); -+ if (rmdir_later) -+ return rmdir_later; -+ } -+ -+ err = au_whtmp_rmdir(dir, bindex, h_dentry, whlist); -+ if (unlikely(err)) { -+ AuIOErr("rmdir %pd, b%d failed, %d. ignored\n", -+ h_dentry, bindex, err); -+ err = 0; -+ } -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+/* -+ * final procedure for deleting a entry. -+ * maintain dentry and iattr. -+ */ -+static void epilog(struct inode *dir, struct dentry *dentry, -+ aufs_bindex_t bindex) -+{ -+ struct inode *inode; -+ -+ inode = d_inode(dentry); -+ d_drop(dentry); -+ inode->i_ctime = dir->i_ctime; -+ -+ au_dir_ts(dir, bindex); -+ dir->i_version++; -+} -+ -+/* -+ * when an error happened, remove the created whiteout and revert everything. -+ */ -+static int do_revert(int err, struct inode *dir, aufs_bindex_t bindex, -+ aufs_bindex_t bwh, struct dentry *wh_dentry, -+ struct dentry *dentry, struct au_dtime *dt) -+{ -+ int rerr; -+ struct path h_path = { -+ .dentry = wh_dentry, -+ .mnt = au_sbr_mnt(dir->i_sb, bindex) -+ }; -+ -+ rerr = au_wh_unlink_dentry(au_h_iptr(dir, bindex), &h_path, dentry); -+ if (!rerr) { -+ au_set_dbwh(dentry, bwh); -+ au_dtime_revert(dt); -+ return 0; -+ } -+ -+ AuIOErr("%pd reverting whiteout failed(%d, %d)\n", dentry, err, rerr); -+ return -EIO; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+int aufs_unlink(struct inode *dir, struct dentry *dentry) -+{ -+ int err; -+ aufs_bindex_t bwh, bindex, bstart; -+ struct inode *inode, *h_dir, *delegated; -+ struct dentry *parent, *wh_dentry; -+ /* to reuduce stack size */ -+ struct { -+ struct au_dtime dt; -+ struct au_pin pin; -+ struct path h_path; -+ } *a; -+ -+ IMustLock(dir); -+ -+ err = -ENOMEM; -+ a = kmalloc(sizeof(*a), GFP_NOFS); -+ if (unlikely(!a)) -+ goto out; -+ -+ err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN); -+ if (unlikely(err)) -+ goto out_free; -+ err = au_d_hashed_positive(dentry); -+ if (unlikely(err)) -+ goto out_unlock; -+ inode = d_inode(dentry); -+ IMustLock(inode); -+ err = -EISDIR; -+ if (unlikely(d_is_dir(dentry))) -+ goto out_unlock; /* possible? */ -+ -+ bstart = au_dbstart(dentry); -+ bwh = au_dbwh(dentry); -+ bindex = -1; -+ parent = dentry->d_parent; /* dir inode is locked */ -+ di_write_lock_parent(parent); -+ wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/0, &bindex, &a->dt, -+ &a->pin); -+ err = PTR_ERR(wh_dentry); -+ if (IS_ERR(wh_dentry)) -+ goto out_parent; -+ -+ a->h_path.mnt = au_sbr_mnt(dentry->d_sb, bstart); -+ a->h_path.dentry = au_h_dptr(dentry, bstart); -+ dget(a->h_path.dentry); -+ if (bindex == bstart) { -+ h_dir = au_pinned_h_dir(&a->pin); -+ delegated = NULL; -+ err = vfsub_unlink(h_dir, &a->h_path, &delegated, /*force*/0); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal unlink\n"); -+ iput(delegated); -+ } -+ } else { -+ /* dir inode is locked */ -+ h_dir = d_inode(wh_dentry->d_parent); -+ IMustLock(h_dir); -+ err = 0; -+ } -+ -+ if (!err) { -+ vfsub_drop_nlink(inode); -+ epilog(dir, dentry, bindex); -+ -+ /* update target timestamps */ -+ if (bindex == bstart) { -+ vfsub_update_h_iattr(&a->h_path, /*did*/NULL); -+ /*ignore*/ -+ inode->i_ctime = d_inode(a->h_path.dentry)->i_ctime; -+ } else -+ /* todo: this timestamp may be reverted later */ -+ inode->i_ctime = h_dir->i_ctime; -+ goto out_unpin; /* success */ -+ } -+ -+ /* revert */ -+ if (wh_dentry) { -+ int rerr; -+ -+ rerr = do_revert(err, dir, bindex, bwh, wh_dentry, dentry, -+ &a->dt); -+ if (rerr) -+ err = rerr; -+ } -+ -+out_unpin: -+ au_unpin(&a->pin); -+ dput(wh_dentry); -+ dput(a->h_path.dentry); -+out_parent: -+ di_write_unlock(parent); -+out_unlock: -+ aufs_read_unlock(dentry, AuLock_DW); -+out_free: -+ kfree(a); -+out: -+ return err; -+} -+ -+int aufs_rmdir(struct inode *dir, struct dentry *dentry) -+{ -+ int err, rmdir_later; -+ aufs_bindex_t bwh, bindex, bstart; -+ struct inode *inode; -+ struct dentry *parent, *wh_dentry, *h_dentry; -+ struct au_whtmp_rmdir *args; -+ /* to reuduce stack size */ -+ struct { -+ struct au_dtime dt; -+ struct au_pin pin; -+ } *a; -+ -+ IMustLock(dir); -+ -+ err = -ENOMEM; -+ a = kmalloc(sizeof(*a), GFP_NOFS); -+ if (unlikely(!a)) -+ goto out; -+ -+ err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH | AuLock_GEN); -+ if (unlikely(err)) -+ goto out_free; -+ err = au_alive_dir(dentry); -+ if (unlikely(err)) -+ goto out_unlock; -+ inode = d_inode(dentry); -+ IMustLock(inode); -+ err = -ENOTDIR; -+ if (unlikely(!d_is_dir(dentry))) -+ goto out_unlock; /* possible? */ -+ -+ err = -ENOMEM; -+ args = au_whtmp_rmdir_alloc(dir->i_sb, GFP_NOFS); -+ if (unlikely(!args)) -+ goto out_unlock; -+ -+ parent = dentry->d_parent; /* dir inode is locked */ -+ di_write_lock_parent(parent); -+ err = au_test_empty(dentry, &args->whlist); -+ if (unlikely(err)) -+ goto out_parent; -+ -+ bstart = au_dbstart(dentry); -+ bwh = au_dbwh(dentry); -+ bindex = -1; -+ wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/1, &bindex, &a->dt, -+ &a->pin); -+ err = PTR_ERR(wh_dentry); -+ if (IS_ERR(wh_dentry)) -+ goto out_parent; -+ -+ h_dentry = au_h_dptr(dentry, bstart); -+ dget(h_dentry); -+ rmdir_later = 0; -+ if (bindex == bstart) { -+ err = renwh_and_rmdir(dentry, bstart, &args->whlist, dir); -+ if (err > 0) { -+ rmdir_later = err; -+ err = 0; -+ } -+ } else { -+ /* stop monitoring */ -+ au_hn_free(au_hi(inode, bstart)); -+ -+ /* dir inode is locked */ -+ IMustLock(d_inode(wh_dentry->d_parent)); -+ err = 0; -+ } -+ -+ if (!err) { -+ vfsub_dead_dir(inode); -+ au_set_dbdiropq(dentry, -1); -+ epilog(dir, dentry, bindex); -+ -+ if (rmdir_later) { -+ au_whtmp_kick_rmdir(dir, bstart, h_dentry, args); -+ args = NULL; -+ } -+ -+ goto out_unpin; /* success */ -+ } -+ -+ /* revert */ -+ AuLabel(revert); -+ if (wh_dentry) { -+ int rerr; -+ -+ rerr = do_revert(err, dir, bindex, bwh, wh_dentry, dentry, -+ &a->dt); -+ if (rerr) -+ err = rerr; -+ } -+ -+out_unpin: -+ au_unpin(&a->pin); -+ dput(wh_dentry); -+ dput(h_dentry); -+out_parent: -+ di_write_unlock(parent); -+ if (args) -+ au_whtmp_rmdir_free(args); -+out_unlock: -+ aufs_read_unlock(dentry, AuLock_DW); -+out_free: -+ kfree(a); -+out: -+ AuTraceErr(err); -+ return err; -+} -diff -Nurp linux-4.4.3/fs/aufs/i_op_ren.c linux-4.4.3-aufs/fs/aufs/i_op_ren.c ---- linux-4.4.3/fs/aufs/i_op_ren.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/i_op_ren.c 2016-03-02 17:59:37.368498738 +0200 -@@ -0,0 +1,1002 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ */ -+ -+/* -+ * inode operation (rename entry) -+ * todo: this is crazy monster -+ */ -+ -+#include "aufs.h" -+ -+enum { AuSRC, AuDST, AuSrcDst }; -+enum { AuPARENT, AuCHILD, AuParentChild }; -+ -+#define AuRen_ISDIR 1 -+#define AuRen_ISSAMEDIR (1 << 1) -+#define AuRen_WHSRC (1 << 2) -+#define AuRen_WHDST (1 << 3) -+#define AuRen_MNT_WRITE (1 << 4) -+#define AuRen_DT_DSTDIR (1 << 5) -+#define AuRen_DIROPQ (1 << 6) -+#define au_ftest_ren(flags, name) ((flags) & AuRen_##name) -+#define au_fset_ren(flags, name) \ -+ do { (flags) |= AuRen_##name; } while (0) -+#define au_fclr_ren(flags, name) \ -+ do { (flags) &= ~AuRen_##name; } while (0) -+ -+struct au_ren_args { -+ struct { -+ struct dentry *dentry, *h_dentry, *parent, *h_parent, -+ *wh_dentry; -+ struct inode *dir, *inode; -+ struct au_hinode *hdir; -+ struct au_dtime dt[AuParentChild]; -+ aufs_bindex_t bstart; -+ } sd[AuSrcDst]; -+ -+#define src_dentry sd[AuSRC].dentry -+#define src_dir sd[AuSRC].dir -+#define src_inode sd[AuSRC].inode -+#define src_h_dentry sd[AuSRC].h_dentry -+#define src_parent sd[AuSRC].parent -+#define src_h_parent sd[AuSRC].h_parent -+#define src_wh_dentry sd[AuSRC].wh_dentry -+#define src_hdir sd[AuSRC].hdir -+#define src_h_dir sd[AuSRC].hdir->hi_inode -+#define src_dt sd[AuSRC].dt -+#define src_bstart sd[AuSRC].bstart -+ -+#define dst_dentry sd[AuDST].dentry -+#define dst_dir sd[AuDST].dir -+#define dst_inode sd[AuDST].inode -+#define dst_h_dentry sd[AuDST].h_dentry -+#define dst_parent sd[AuDST].parent -+#define dst_h_parent sd[AuDST].h_parent -+#define dst_wh_dentry sd[AuDST].wh_dentry -+#define dst_hdir sd[AuDST].hdir -+#define dst_h_dir sd[AuDST].hdir->hi_inode -+#define dst_dt sd[AuDST].dt -+#define dst_bstart sd[AuDST].bstart -+ -+ struct dentry *h_trap; -+ struct au_branch *br; -+ struct au_hinode *src_hinode; -+ struct path h_path; -+ struct au_nhash whlist; -+ aufs_bindex_t btgt, src_bwh, src_bdiropq; -+ -+ unsigned int flags; -+ -+ struct au_whtmp_rmdir *thargs; -+ struct dentry *h_dst; -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * functions for reverting. -+ * when an error happened in a single rename systemcall, we should revert -+ * everything as if nothing happened. -+ * we don't need to revert the copied-up/down the parent dir since they are -+ * harmless. -+ */ -+ -+#define RevertFailure(fmt, ...) do { \ -+ AuIOErr("revert failure: " fmt " (%d, %d)\n", \ -+ ##__VA_ARGS__, err, rerr); \ -+ err = -EIO; \ -+} while (0) -+ -+static void au_ren_rev_diropq(int err, struct au_ren_args *a) -+{ -+ int rerr; -+ -+ au_hn_imtx_lock_nested(a->src_hinode, AuLsc_I_CHILD); -+ rerr = au_diropq_remove(a->src_dentry, a->btgt); -+ au_hn_imtx_unlock(a->src_hinode); -+ au_set_dbdiropq(a->src_dentry, a->src_bdiropq); -+ if (rerr) -+ RevertFailure("remove diropq %pd", a->src_dentry); -+} -+ -+static void au_ren_rev_rename(int err, struct au_ren_args *a) -+{ -+ int rerr; -+ struct inode *delegated; -+ -+ a->h_path.dentry = vfsub_lkup_one(&a->src_dentry->d_name, -+ a->src_h_parent); -+ rerr = PTR_ERR(a->h_path.dentry); -+ if (IS_ERR(a->h_path.dentry)) { -+ RevertFailure("lkup one %pd", a->src_dentry); -+ return; -+ } -+ -+ delegated = NULL; -+ rerr = vfsub_rename(a->dst_h_dir, -+ au_h_dptr(a->src_dentry, a->btgt), -+ a->src_h_dir, &a->h_path, &delegated); -+ if (unlikely(rerr == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal rename\n"); -+ iput(delegated); -+ } -+ d_drop(a->h_path.dentry); -+ dput(a->h_path.dentry); -+ /* au_set_h_dptr(a->src_dentry, a->btgt, NULL); */ -+ if (rerr) -+ RevertFailure("rename %pd", a->src_dentry); -+} -+ -+static void au_ren_rev_whtmp(int err, struct au_ren_args *a) -+{ -+ int rerr; -+ struct inode *delegated; -+ -+ a->h_path.dentry = vfsub_lkup_one(&a->dst_dentry->d_name, -+ a->dst_h_parent); -+ rerr = PTR_ERR(a->h_path.dentry); -+ if (IS_ERR(a->h_path.dentry)) { -+ RevertFailure("lkup one %pd", a->dst_dentry); -+ return; -+ } -+ if (d_is_positive(a->h_path.dentry)) { -+ d_drop(a->h_path.dentry); -+ dput(a->h_path.dentry); -+ return; -+ } -+ -+ delegated = NULL; -+ rerr = vfsub_rename(a->dst_h_dir, a->h_dst, a->dst_h_dir, &a->h_path, -+ &delegated); -+ if (unlikely(rerr == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal rename\n"); -+ iput(delegated); -+ } -+ d_drop(a->h_path.dentry); -+ dput(a->h_path.dentry); -+ if (!rerr) -+ au_set_h_dptr(a->dst_dentry, a->btgt, dget(a->h_dst)); -+ else -+ RevertFailure("rename %pd", a->h_dst); -+} -+ -+static void au_ren_rev_whsrc(int err, struct au_ren_args *a) -+{ -+ int rerr; -+ -+ a->h_path.dentry = a->src_wh_dentry; -+ rerr = au_wh_unlink_dentry(a->src_h_dir, &a->h_path, a->src_dentry); -+ au_set_dbwh(a->src_dentry, a->src_bwh); -+ if (rerr) -+ RevertFailure("unlink %pd", a->src_wh_dentry); -+} -+#undef RevertFailure -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * when we have to copyup the renaming entry, do it with the rename-target name -+ * in order to minimize the cost (the later actual rename is unnecessary). -+ * otherwise rename it on the target branch. -+ */ -+static int au_ren_or_cpup(struct au_ren_args *a) -+{ -+ int err; -+ struct dentry *d; -+ struct inode *delegated; -+ -+ d = a->src_dentry; -+ if (au_dbstart(d) == a->btgt) { -+ a->h_path.dentry = a->dst_h_dentry; -+ if (au_ftest_ren(a->flags, DIROPQ) -+ && au_dbdiropq(d) == a->btgt) -+ au_fclr_ren(a->flags, DIROPQ); -+ AuDebugOn(au_dbstart(d) != a->btgt); -+ delegated = NULL; -+ err = vfsub_rename(a->src_h_dir, au_h_dptr(d, a->btgt), -+ a->dst_h_dir, &a->h_path, &delegated); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal rename\n"); -+ iput(delegated); -+ } -+ } else -+ BUG(); -+ -+ if (!err && a->h_dst) -+ /* it will be set to dinfo later */ -+ dget(a->h_dst); -+ -+ return err; -+} -+ -+/* cf. aufs_rmdir() */ -+static int au_ren_del_whtmp(struct au_ren_args *a) -+{ -+ int err; -+ struct inode *dir; -+ -+ dir = a->dst_dir; -+ SiMustAnyLock(dir->i_sb); -+ if (!au_nhash_test_longer_wh(&a->whlist, a->btgt, -+ au_sbi(dir->i_sb)->si_dirwh) -+ || au_test_fs_remote(a->h_dst->d_sb)) { -+ err = au_whtmp_rmdir(dir, a->btgt, a->h_dst, &a->whlist); -+ if (unlikely(err)) -+ pr_warn("failed removing whtmp dir %pd (%d), " -+ "ignored.\n", a->h_dst, err); -+ } else { -+ au_nhash_wh_free(&a->thargs->whlist); -+ a->thargs->whlist = a->whlist; -+ a->whlist.nh_num = 0; -+ au_whtmp_kick_rmdir(dir, a->btgt, a->h_dst, a->thargs); -+ dput(a->h_dst); -+ a->thargs = NULL; -+ } -+ -+ return 0; -+} -+ -+/* make it 'opaque' dir. */ -+static int au_ren_diropq(struct au_ren_args *a) -+{ -+ int err; -+ struct dentry *diropq; -+ -+ err = 0; -+ a->src_bdiropq = au_dbdiropq(a->src_dentry); -+ a->src_hinode = au_hi(a->src_inode, a->btgt); -+ au_hn_imtx_lock_nested(a->src_hinode, AuLsc_I_CHILD); -+ diropq = au_diropq_create(a->src_dentry, a->btgt); -+ au_hn_imtx_unlock(a->src_hinode); -+ if (IS_ERR(diropq)) -+ err = PTR_ERR(diropq); -+ else -+ dput(diropq); -+ -+ return err; -+} -+ -+static int do_rename(struct au_ren_args *a) -+{ -+ int err; -+ struct dentry *d, *h_d; -+ -+ /* prepare workqueue args for asynchronous rmdir */ -+ h_d = a->dst_h_dentry; -+ if (au_ftest_ren(a->flags, ISDIR) && d_is_positive(h_d)) { -+ err = -ENOMEM; -+ a->thargs = au_whtmp_rmdir_alloc(a->src_dentry->d_sb, GFP_NOFS); -+ if (unlikely(!a->thargs)) -+ goto out; -+ a->h_dst = dget(h_d); -+ } -+ -+ /* create whiteout for src_dentry */ -+ if (au_ftest_ren(a->flags, WHSRC)) { -+ a->src_bwh = au_dbwh(a->src_dentry); -+ AuDebugOn(a->src_bwh >= 0); -+ a->src_wh_dentry -+ = au_wh_create(a->src_dentry, a->btgt, a->src_h_parent); -+ err = PTR_ERR(a->src_wh_dentry); -+ if (IS_ERR(a->src_wh_dentry)) -+ goto out_thargs; -+ } -+ -+ /* lookup whiteout for dentry */ -+ if (au_ftest_ren(a->flags, WHDST)) { -+ h_d = au_wh_lkup(a->dst_h_parent, &a->dst_dentry->d_name, -+ a->br); -+ err = PTR_ERR(h_d); -+ if (IS_ERR(h_d)) -+ goto out_whsrc; -+ if (d_is_negative(h_d)) -+ dput(h_d); -+ else -+ a->dst_wh_dentry = h_d; -+ } -+ -+ /* rename dentry to tmpwh */ -+ if (a->thargs) { -+ err = au_whtmp_ren(a->dst_h_dentry, a->br); -+ if (unlikely(err)) -+ goto out_whdst; -+ -+ d = a->dst_dentry; -+ au_set_h_dptr(d, a->btgt, NULL); -+ err = au_lkup_neg(d, a->btgt, /*wh*/0); -+ if (unlikely(err)) -+ goto out_whtmp; -+ a->dst_h_dentry = au_h_dptr(d, a->btgt); -+ } -+ -+ BUG_ON(d_is_positive(a->dst_h_dentry) && a->src_bstart != a->btgt); -+ -+ /* rename by vfs_rename or cpup */ -+ d = a->dst_dentry; -+ if (au_ftest_ren(a->flags, ISDIR) -+ && (a->dst_wh_dentry -+ || au_dbdiropq(d) == a->btgt -+ /* hide the lower to keep xino */ -+ || a->btgt < au_dbend(d) -+ || au_opt_test(au_mntflags(d->d_sb), ALWAYS_DIROPQ))) -+ au_fset_ren(a->flags, DIROPQ); -+ err = au_ren_or_cpup(a); -+ if (unlikely(err)) -+ /* leave the copied-up one */ -+ goto out_whtmp; -+ -+ /* make dir opaque */ -+ if (au_ftest_ren(a->flags, DIROPQ)) { -+ err = au_ren_diropq(a); -+ if (unlikely(err)) -+ goto out_rename; -+ } -+ -+ /* update target timestamps */ -+ AuDebugOn(au_dbstart(a->src_dentry) != a->btgt); -+ a->h_path.dentry = au_h_dptr(a->src_dentry, a->btgt); -+ vfsub_update_h_iattr(&a->h_path, /*did*/NULL); /*ignore*/ -+ a->src_inode->i_ctime = d_inode(a->h_path.dentry)->i_ctime; -+ -+ /* remove whiteout for dentry */ -+ if (a->dst_wh_dentry) { -+ a->h_path.dentry = a->dst_wh_dentry; -+ err = au_wh_unlink_dentry(a->dst_h_dir, &a->h_path, -+ a->dst_dentry); -+ if (unlikely(err)) -+ goto out_diropq; -+ } -+ -+ /* remove whtmp */ -+ if (a->thargs) -+ au_ren_del_whtmp(a); /* ignore this error */ -+ -+ au_fhsm_wrote(a->src_dentry->d_sb, a->btgt, /*force*/0); -+ err = 0; -+ goto out_success; -+ -+out_diropq: -+ if (au_ftest_ren(a->flags, DIROPQ)) -+ au_ren_rev_diropq(err, a); -+out_rename: -+ au_ren_rev_rename(err, a); -+ dput(a->h_dst); -+out_whtmp: -+ if (a->thargs) -+ au_ren_rev_whtmp(err, a); -+out_whdst: -+ dput(a->dst_wh_dentry); -+ a->dst_wh_dentry = NULL; -+out_whsrc: -+ if (a->src_wh_dentry) -+ au_ren_rev_whsrc(err, a); -+out_success: -+ dput(a->src_wh_dentry); -+ dput(a->dst_wh_dentry); -+out_thargs: -+ if (a->thargs) { -+ dput(a->h_dst); -+ au_whtmp_rmdir_free(a->thargs); -+ a->thargs = NULL; -+ } -+out: -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * test if @dentry dir can be rename destination or not. -+ * success means, it is a logically empty dir. -+ */ -+static int may_rename_dstdir(struct dentry *dentry, struct au_nhash *whlist) -+{ -+ return au_test_empty(dentry, whlist); -+} -+ -+/* -+ * test if @dentry dir can be rename source or not. -+ * if it can, return 0 and @children is filled. -+ * success means, -+ * - it is a logically empty dir. -+ * - or, it exists on writable branch and has no children including whiteouts -+ * on the lower branch. -+ */ -+static int may_rename_srcdir(struct dentry *dentry, aufs_bindex_t btgt) -+{ -+ int err; -+ unsigned int rdhash; -+ aufs_bindex_t bstart; -+ -+ bstart = au_dbstart(dentry); -+ if (bstart != btgt) { -+ struct au_nhash whlist; -+ -+ SiMustAnyLock(dentry->d_sb); -+ rdhash = au_sbi(dentry->d_sb)->si_rdhash; -+ if (!rdhash) -+ rdhash = au_rdhash_est(au_dir_size(/*file*/NULL, -+ dentry)); -+ err = au_nhash_alloc(&whlist, rdhash, GFP_NOFS); -+ if (unlikely(err)) -+ goto out; -+ err = au_test_empty(dentry, &whlist); -+ au_nhash_wh_free(&whlist); -+ goto out; -+ } -+ -+ if (bstart == au_dbtaildir(dentry)) -+ return 0; /* success */ -+ -+ err = au_test_empty_lower(dentry); -+ -+out: -+ if (err == -ENOTEMPTY) { -+ AuWarn1("renaming dir who has child(ren) on multiple branches," -+ " is not supported\n"); -+ err = -EXDEV; -+ } -+ return err; -+} -+ -+/* side effect: sets whlist and h_dentry */ -+static int au_ren_may_dir(struct au_ren_args *a) -+{ -+ int err; -+ unsigned int rdhash; -+ struct dentry *d; -+ -+ d = a->dst_dentry; -+ SiMustAnyLock(d->d_sb); -+ -+ err = 0; -+ if (au_ftest_ren(a->flags, ISDIR) && a->dst_inode) { -+ rdhash = au_sbi(d->d_sb)->si_rdhash; -+ if (!rdhash) -+ rdhash = au_rdhash_est(au_dir_size(/*file*/NULL, d)); -+ err = au_nhash_alloc(&a->whlist, rdhash, GFP_NOFS); -+ if (unlikely(err)) -+ goto out; -+ -+ au_set_dbstart(d, a->dst_bstart); -+ err = may_rename_dstdir(d, &a->whlist); -+ au_set_dbstart(d, a->btgt); -+ } -+ a->dst_h_dentry = au_h_dptr(d, au_dbstart(d)); -+ if (unlikely(err)) -+ goto out; -+ -+ d = a->src_dentry; -+ a->src_h_dentry = au_h_dptr(d, au_dbstart(d)); -+ if (au_ftest_ren(a->flags, ISDIR)) { -+ err = may_rename_srcdir(d, a->btgt); -+ if (unlikely(err)) { -+ au_nhash_wh_free(&a->whlist); -+ a->whlist.nh_num = 0; -+ } -+ } -+out: -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * simple tests for rename. -+ * following the checks in vfs, plus the parent-child relationship. -+ */ -+static int au_may_ren(struct au_ren_args *a) -+{ -+ int err, isdir; -+ struct inode *h_inode; -+ -+ if (a->src_bstart == a->btgt) { -+ err = au_may_del(a->src_dentry, a->btgt, a->src_h_parent, -+ au_ftest_ren(a->flags, ISDIR)); -+ if (unlikely(err)) -+ goto out; -+ err = -EINVAL; -+ if (unlikely(a->src_h_dentry == a->h_trap)) -+ goto out; -+ } -+ -+ err = 0; -+ if (a->dst_bstart != a->btgt) -+ goto out; -+ -+ err = -ENOTEMPTY; -+ if (unlikely(a->dst_h_dentry == a->h_trap)) -+ goto out; -+ -+ err = -EIO; -+ isdir = !!au_ftest_ren(a->flags, ISDIR); -+ if (d_really_is_negative(a->dst_dentry)) { -+ if (d_is_negative(a->dst_h_dentry)) -+ err = au_may_add(a->dst_dentry, a->btgt, -+ a->dst_h_parent, isdir); -+ } else { -+ if (unlikely(d_is_negative(a->dst_h_dentry))) -+ goto out; -+ h_inode = d_inode(a->dst_h_dentry); -+ if (h_inode->i_nlink) -+ err = au_may_del(a->dst_dentry, a->btgt, -+ a->dst_h_parent, isdir); -+ } -+ -+out: -+ if (unlikely(err == -ENOENT || err == -EEXIST)) -+ err = -EIO; -+ AuTraceErr(err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * locking order -+ * (VFS) -+ * - src_dir and dir by lock_rename() -+ * - inode if exitsts -+ * (aufs) -+ * - lock all -+ * + src_dentry and dentry by aufs_read_and_write_lock2() which calls, -+ * + si_read_lock -+ * + di_write_lock2_child() -+ * + di_write_lock_child() -+ * + ii_write_lock_child() -+ * + di_write_lock_child2() -+ * + ii_write_lock_child2() -+ * + src_parent and parent -+ * + di_write_lock_parent() -+ * + ii_write_lock_parent() -+ * + di_write_lock_parent2() -+ * + ii_write_lock_parent2() -+ * + lower src_dir and dir by vfsub_lock_rename() -+ * + verify the every relationships between child and parent. if any -+ * of them failed, unlock all and return -EBUSY. -+ */ -+static void au_ren_unlock(struct au_ren_args *a) -+{ -+ vfsub_unlock_rename(a->src_h_parent, a->src_hdir, -+ a->dst_h_parent, a->dst_hdir); -+ if (au_ftest_ren(a->flags, MNT_WRITE)) -+ vfsub_mnt_drop_write(au_br_mnt(a->br)); -+} -+ -+static int au_ren_lock(struct au_ren_args *a) -+{ -+ int err; -+ unsigned int udba; -+ -+ err = 0; -+ a->src_h_parent = au_h_dptr(a->src_parent, a->btgt); -+ a->src_hdir = au_hi(a->src_dir, a->btgt); -+ a->dst_h_parent = au_h_dptr(a->dst_parent, a->btgt); -+ a->dst_hdir = au_hi(a->dst_dir, a->btgt); -+ -+ err = vfsub_mnt_want_write(au_br_mnt(a->br)); -+ if (unlikely(err)) -+ goto out; -+ au_fset_ren(a->flags, MNT_WRITE); -+ a->h_trap = vfsub_lock_rename(a->src_h_parent, a->src_hdir, -+ a->dst_h_parent, a->dst_hdir); -+ udba = au_opt_udba(a->src_dentry->d_sb); -+ if (unlikely(a->src_hdir->hi_inode != d_inode(a->src_h_parent) -+ || a->dst_hdir->hi_inode != d_inode(a->dst_h_parent))) -+ err = au_busy_or_stale(); -+ if (!err && au_dbstart(a->src_dentry) == a->btgt) -+ err = au_h_verify(a->src_h_dentry, udba, -+ d_inode(a->src_h_parent), a->src_h_parent, -+ a->br); -+ if (!err && au_dbstart(a->dst_dentry) == a->btgt) -+ err = au_h_verify(a->dst_h_dentry, udba, -+ d_inode(a->dst_h_parent), a->dst_h_parent, -+ a->br); -+ if (!err) -+ goto out; /* success */ -+ -+ err = au_busy_or_stale(); -+ au_ren_unlock(a); -+ -+out: -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static void au_ren_refresh_dir(struct au_ren_args *a) -+{ -+ struct inode *dir; -+ -+ dir = a->dst_dir; -+ dir->i_version++; -+ if (au_ftest_ren(a->flags, ISDIR)) { -+ /* is this updating defined in POSIX? */ -+ au_cpup_attr_timesizes(a->src_inode); -+ au_cpup_attr_nlink(dir, /*force*/1); -+ } -+ -+ au_dir_ts(dir, a->btgt); -+ -+ if (au_ftest_ren(a->flags, ISSAMEDIR)) -+ return; -+ -+ dir = a->src_dir; -+ dir->i_version++; -+ if (au_ftest_ren(a->flags, ISDIR)) -+ au_cpup_attr_nlink(dir, /*force*/1); -+ au_dir_ts(dir, a->btgt); -+} -+ -+static void au_ren_refresh(struct au_ren_args *a) -+{ -+ aufs_bindex_t bend, bindex; -+ struct dentry *d, *h_d; -+ struct inode *i, *h_i; -+ struct super_block *sb; -+ -+ d = a->dst_dentry; -+ d_drop(d); -+ if (a->h_dst) -+ /* already dget-ed by au_ren_or_cpup() */ -+ au_set_h_dptr(d, a->btgt, a->h_dst); -+ -+ i = a->dst_inode; -+ if (i) { -+ if (!au_ftest_ren(a->flags, ISDIR)) -+ vfsub_drop_nlink(i); -+ else { -+ vfsub_dead_dir(i); -+ au_cpup_attr_timesizes(i); -+ } -+ au_update_dbrange(d, /*do_put_zero*/1); -+ } else { -+ bend = a->btgt; -+ for (bindex = au_dbstart(d); bindex < bend; bindex++) -+ au_set_h_dptr(d, bindex, NULL); -+ bend = au_dbend(d); -+ for (bindex = a->btgt + 1; bindex <= bend; bindex++) -+ au_set_h_dptr(d, bindex, NULL); -+ au_update_dbrange(d, /*do_put_zero*/0); -+ } -+ -+ d = a->src_dentry; -+ au_set_dbwh(d, -1); -+ bend = au_dbend(d); -+ for (bindex = a->btgt + 1; bindex <= bend; bindex++) { -+ h_d = au_h_dptr(d, bindex); -+ if (h_d) -+ au_set_h_dptr(d, bindex, NULL); -+ } -+ au_set_dbend(d, a->btgt); -+ -+ sb = d->d_sb; -+ i = a->src_inode; -+ if (au_opt_test(au_mntflags(sb), PLINK) && au_plink_test(i)) -+ return; /* success */ -+ -+ bend = au_ibend(i); -+ for (bindex = a->btgt + 1; bindex <= bend; bindex++) { -+ h_i = au_h_iptr(i, bindex); -+ if (h_i) { -+ au_xino_write(sb, bindex, h_i->i_ino, /*ino*/0); -+ /* ignore this error */ -+ au_set_h_iptr(i, bindex, NULL, 0); -+ } -+ } -+ au_set_ibend(i, a->btgt); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* mainly for link(2) and rename(2) */ -+int au_wbr(struct dentry *dentry, aufs_bindex_t btgt) -+{ -+ aufs_bindex_t bdiropq, bwh; -+ struct dentry *parent; -+ struct au_branch *br; -+ -+ parent = dentry->d_parent; -+ IMustLock(d_inode(parent)); /* dir is locked */ -+ -+ bdiropq = au_dbdiropq(parent); -+ bwh = au_dbwh(dentry); -+ br = au_sbr(dentry->d_sb, btgt); -+ if (au_br_rdonly(br) -+ || (0 <= bdiropq && bdiropq < btgt) -+ || (0 <= bwh && bwh < btgt)) -+ btgt = -1; -+ -+ AuDbg("btgt %d\n", btgt); -+ return btgt; -+} -+ -+/* sets src_bstart, dst_bstart and btgt */ -+static int au_ren_wbr(struct au_ren_args *a) -+{ -+ int err; -+ struct au_wr_dir_args wr_dir_args = { -+ /* .force_btgt = -1, */ -+ .flags = AuWrDir_ADD_ENTRY -+ }; -+ -+ a->src_bstart = au_dbstart(a->src_dentry); -+ a->dst_bstart = au_dbstart(a->dst_dentry); -+ if (au_ftest_ren(a->flags, ISDIR)) -+ au_fset_wrdir(wr_dir_args.flags, ISDIR); -+ wr_dir_args.force_btgt = a->src_bstart; -+ if (a->dst_inode && a->dst_bstart < a->src_bstart) -+ wr_dir_args.force_btgt = a->dst_bstart; -+ wr_dir_args.force_btgt = au_wbr(a->dst_dentry, wr_dir_args.force_btgt); -+ err = au_wr_dir(a->dst_dentry, a->src_dentry, &wr_dir_args); -+ a->btgt = err; -+ -+ return err; -+} -+ -+static void au_ren_dt(struct au_ren_args *a) -+{ -+ a->h_path.dentry = a->src_h_parent; -+ au_dtime_store(a->src_dt + AuPARENT, a->src_parent, &a->h_path); -+ if (!au_ftest_ren(a->flags, ISSAMEDIR)) { -+ a->h_path.dentry = a->dst_h_parent; -+ au_dtime_store(a->dst_dt + AuPARENT, a->dst_parent, &a->h_path); -+ } -+ -+ au_fclr_ren(a->flags, DT_DSTDIR); -+ if (!au_ftest_ren(a->flags, ISDIR)) -+ return; -+ -+ a->h_path.dentry = a->src_h_dentry; -+ au_dtime_store(a->src_dt + AuCHILD, a->src_dentry, &a->h_path); -+ if (d_is_positive(a->dst_h_dentry)) { -+ au_fset_ren(a->flags, DT_DSTDIR); -+ a->h_path.dentry = a->dst_h_dentry; -+ au_dtime_store(a->dst_dt + AuCHILD, a->dst_dentry, &a->h_path); -+ } -+} -+ -+static void au_ren_rev_dt(int err, struct au_ren_args *a) -+{ -+ struct dentry *h_d; -+ struct mutex *h_mtx; -+ -+ au_dtime_revert(a->src_dt + AuPARENT); -+ if (!au_ftest_ren(a->flags, ISSAMEDIR)) -+ au_dtime_revert(a->dst_dt + AuPARENT); -+ -+ if (au_ftest_ren(a->flags, ISDIR) && err != -EIO) { -+ h_d = a->src_dt[AuCHILD].dt_h_path.dentry; -+ h_mtx = &d_inode(h_d)->i_mutex; -+ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); -+ au_dtime_revert(a->src_dt + AuCHILD); -+ mutex_unlock(h_mtx); -+ -+ if (au_ftest_ren(a->flags, DT_DSTDIR)) { -+ h_d = a->dst_dt[AuCHILD].dt_h_path.dentry; -+ h_mtx = &d_inode(h_d)->i_mutex; -+ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); -+ au_dtime_revert(a->dst_dt + AuCHILD); -+ mutex_unlock(h_mtx); -+ } -+ } -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+int aufs_rename(struct inode *_src_dir, struct dentry *_src_dentry, -+ struct inode *_dst_dir, struct dentry *_dst_dentry) -+{ -+ int err, flags; -+ /* reduce stack space */ -+ struct au_ren_args *a; -+ -+ AuDbg("%pd, %pd\n", _src_dentry, _dst_dentry); -+ IMustLock(_src_dir); -+ IMustLock(_dst_dir); -+ -+ err = -ENOMEM; -+ BUILD_BUG_ON(sizeof(*a) > PAGE_SIZE); -+ a = kzalloc(sizeof(*a), GFP_NOFS); -+ if (unlikely(!a)) -+ goto out; -+ -+ a->src_dir = _src_dir; -+ a->src_dentry = _src_dentry; -+ a->src_inode = NULL; -+ if (d_really_is_positive(a->src_dentry)) -+ a->src_inode = d_inode(a->src_dentry); -+ a->src_parent = a->src_dentry->d_parent; /* dir inode is locked */ -+ a->dst_dir = _dst_dir; -+ a->dst_dentry = _dst_dentry; -+ a->dst_inode = NULL; -+ if (d_really_is_positive(a->dst_dentry)) -+ a->dst_inode = d_inode(a->dst_dentry); -+ a->dst_parent = a->dst_dentry->d_parent; /* dir inode is locked */ -+ if (a->dst_inode) { -+ IMustLock(a->dst_inode); -+ au_igrab(a->dst_inode); -+ } -+ -+ err = -ENOTDIR; -+ flags = AuLock_FLUSH | AuLock_NOPLM | AuLock_GEN; -+ if (d_is_dir(a->src_dentry)) { -+ au_fset_ren(a->flags, ISDIR); -+ if (unlikely(d_really_is_positive(a->dst_dentry) -+ && !d_is_dir(a->dst_dentry))) -+ goto out_free; -+ flags |= AuLock_DIRS; -+ } -+ err = aufs_read_and_write_lock2(a->dst_dentry, a->src_dentry, flags); -+ if (unlikely(err)) -+ goto out_free; -+ -+ err = au_d_hashed_positive(a->src_dentry); -+ if (unlikely(err)) -+ goto out_unlock; -+ err = -ENOENT; -+ if (a->dst_inode) { -+ /* -+ * If it is a dir, VFS unhash dst_dentry before this -+ * function. It means we cannot rely upon d_unhashed(). -+ */ -+ if (unlikely(!a->dst_inode->i_nlink)) -+ goto out_unlock; -+ if (!S_ISDIR(a->dst_inode->i_mode)) { -+ err = au_d_hashed_positive(a->dst_dentry); -+ if (unlikely(err)) -+ goto out_unlock; -+ } else if (unlikely(IS_DEADDIR(a->dst_inode))) -+ goto out_unlock; -+ } else if (unlikely(d_unhashed(a->dst_dentry))) -+ goto out_unlock; -+ -+ /* -+ * is it possible? -+ * yes, it happened (in linux-3.3-rcN) but I don't know why. -+ * there may exist a problem somewhere else. -+ */ -+ err = -EINVAL; -+ if (unlikely(d_inode(a->dst_parent) == d_inode(a->src_dentry))) -+ goto out_unlock; -+ -+ au_fset_ren(a->flags, ISSAMEDIR); /* temporary */ -+ di_write_lock_parent(a->dst_parent); -+ -+ /* which branch we process */ -+ err = au_ren_wbr(a); -+ if (unlikely(err < 0)) -+ goto out_parent; -+ a->br = au_sbr(a->dst_dentry->d_sb, a->btgt); -+ a->h_path.mnt = au_br_mnt(a->br); -+ -+ /* are they available to be renamed */ -+ err = au_ren_may_dir(a); -+ if (unlikely(err)) -+ goto out_children; -+ -+ /* prepare the writable parent dir on the same branch */ -+ if (a->dst_bstart == a->btgt) { -+ au_fset_ren(a->flags, WHDST); -+ } else { -+ err = au_cpup_dirs(a->dst_dentry, a->btgt); -+ if (unlikely(err)) -+ goto out_children; -+ } -+ -+ if (a->src_dir != a->dst_dir) { -+ /* -+ * this temporary unlock is safe, -+ * because both dir->i_mutex are locked. -+ */ -+ di_write_unlock(a->dst_parent); -+ di_write_lock_parent(a->src_parent); -+ err = au_wr_dir_need_wh(a->src_dentry, -+ au_ftest_ren(a->flags, ISDIR), -+ &a->btgt); -+ di_write_unlock(a->src_parent); -+ di_write_lock2_parent(a->src_parent, a->dst_parent, /*isdir*/1); -+ au_fclr_ren(a->flags, ISSAMEDIR); -+ } else -+ err = au_wr_dir_need_wh(a->src_dentry, -+ au_ftest_ren(a->flags, ISDIR), -+ &a->btgt); -+ if (unlikely(err < 0)) -+ goto out_children; -+ if (err) -+ au_fset_ren(a->flags, WHSRC); -+ -+ /* cpup src */ -+ if (a->src_bstart != a->btgt) { -+ struct au_pin pin; -+ -+ err = au_pin(&pin, a->src_dentry, a->btgt, -+ au_opt_udba(a->src_dentry->d_sb), -+ AuPin_DI_LOCKED | AuPin_MNT_WRITE); -+ if (!err) { -+ struct au_cp_generic cpg = { -+ .dentry = a->src_dentry, -+ .bdst = a->btgt, -+ .bsrc = a->src_bstart, -+ .len = -1, -+ .pin = &pin, -+ .flags = AuCpup_DTIME | AuCpup_HOPEN -+ }; -+ AuDebugOn(au_dbstart(a->src_dentry) != a->src_bstart); -+ err = au_sio_cpup_simple(&cpg); -+ au_unpin(&pin); -+ } -+ if (unlikely(err)) -+ goto out_children; -+ a->src_bstart = a->btgt; -+ a->src_h_dentry = au_h_dptr(a->src_dentry, a->btgt); -+ au_fset_ren(a->flags, WHSRC); -+ } -+ -+ /* lock them all */ -+ err = au_ren_lock(a); -+ if (unlikely(err)) -+ /* leave the copied-up one */ -+ goto out_children; -+ -+ if (!au_opt_test(au_mntflags(a->dst_dir->i_sb), UDBA_NONE)) -+ err = au_may_ren(a); -+ else if (unlikely(a->dst_dentry->d_name.len > AUFS_MAX_NAMELEN)) -+ err = -ENAMETOOLONG; -+ if (unlikely(err)) -+ goto out_hdir; -+ -+ /* store timestamps to be revertible */ -+ au_ren_dt(a); -+ -+ /* here we go */ -+ err = do_rename(a); -+ if (unlikely(err)) -+ goto out_dt; -+ -+ /* update dir attributes */ -+ au_ren_refresh_dir(a); -+ -+ /* dput/iput all lower dentries */ -+ au_ren_refresh(a); -+ -+ goto out_hdir; /* success */ -+ -+out_dt: -+ au_ren_rev_dt(err, a); -+out_hdir: -+ au_ren_unlock(a); -+out_children: -+ au_nhash_wh_free(&a->whlist); -+ if (err && a->dst_inode && a->dst_bstart != a->btgt) { -+ AuDbg("bstart %d, btgt %d\n", a->dst_bstart, a->btgt); -+ au_set_h_dptr(a->dst_dentry, a->btgt, NULL); -+ au_set_dbstart(a->dst_dentry, a->dst_bstart); -+ } -+out_parent: -+ if (!err) -+ d_move(a->src_dentry, a->dst_dentry); -+ else { -+ au_update_dbstart(a->dst_dentry); -+ if (!a->dst_inode) -+ d_drop(a->dst_dentry); -+ } -+ if (au_ftest_ren(a->flags, ISSAMEDIR)) -+ di_write_unlock(a->dst_parent); -+ else -+ di_write_unlock2(a->src_parent, a->dst_parent); -+out_unlock: -+ aufs_read_and_write_unlock2(a->dst_dentry, a->src_dentry); -+out_free: -+ iput(a->dst_inode); -+ if (a->thargs) -+ au_whtmp_rmdir_free(a->thargs); -+ kfree(a); -+out: -+ AuTraceErr(err); -+ return err; -+} -diff -Nurp linux-4.4.3/fs/aufs/Kconfig linux-4.4.3-aufs/fs/aufs/Kconfig ---- linux-4.4.3/fs/aufs/Kconfig 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/Kconfig 2016-03-02 17:59:37.360498681 +0200 -@@ -0,0 +1,185 @@ -+config AUFS_FS -+ bool "Aufs (Advanced multi layered unification filesystem) support" -+ help -+ Aufs is a stackable unification filesystem such as Unionfs, -+ which unifies several directories and provides a merged single -+ directory. -+ In the early days, aufs was entirely re-designed and -+ re-implemented Unionfs Version 1.x series. Introducing many -+ original ideas, approaches and improvements, it becomes totally -+ different from Unionfs while keeping the basic features. -+ -+if AUFS_FS -+choice -+ prompt "Maximum number of branches" -+ default AUFS_BRANCH_MAX_127 -+ help -+ Specifies the maximum number of branches (or member directories) -+ in a single aufs. The larger value consumes more system -+ resources and has a minor impact to performance. -+config AUFS_BRANCH_MAX_127 -+ bool "127" -+ help -+ Specifies the maximum number of branches (or member directories) -+ in a single aufs. The larger value consumes more system -+ resources and has a minor impact to performance. -+config AUFS_BRANCH_MAX_511 -+ bool "511" -+ help -+ Specifies the maximum number of branches (or member directories) -+ in a single aufs. The larger value consumes more system -+ resources and has a minor impact to performance. -+config AUFS_BRANCH_MAX_1023 -+ bool "1023" -+ help -+ Specifies the maximum number of branches (or member directories) -+ in a single aufs. The larger value consumes more system -+ resources and has a minor impact to performance. -+config AUFS_BRANCH_MAX_32767 -+ bool "32767" -+ help -+ Specifies the maximum number of branches (or member directories) -+ in a single aufs. The larger value consumes more system -+ resources and has a minor impact to performance. -+endchoice -+ -+config AUFS_SBILIST -+ bool -+ depends on AUFS_MAGIC_SYSRQ || PROC_FS -+ default y -+ help -+ Automatic configuration for internal use. -+ When aufs supports Magic SysRq or /proc, enabled automatically. -+ -+config AUFS_HNOTIFY -+ bool "Detect direct branch access (bypassing aufs)" -+ help -+ If you want to modify files on branches directly, eg. bypassing aufs, -+ and want aufs to detect the changes of them fully, then enable this -+ option and use 'udba=notify' mount option. -+ Currently there is only one available configuration, "fsnotify". -+ It will have a negative impact to the performance. -+ See detail in aufs.5. -+ -+choice -+ prompt "method" if AUFS_HNOTIFY -+ default AUFS_HFSNOTIFY -+config AUFS_HFSNOTIFY -+ bool "fsnotify" -+ select FSNOTIFY -+endchoice -+ -+config AUFS_EXPORT -+ bool "NFS-exportable aufs" -+ depends on EXPORTFS = y -+ help -+ If you want to export your mounted aufs via NFS, then enable this -+ option. There are several requirements for this configuration. -+ See detail in aufs.5. -+ -+config AUFS_INO_T_64 -+ bool -+ depends on AUFS_EXPORT -+ depends on 64BIT && !(ALPHA || S390) -+ default y -+ help -+ Automatic configuration for internal use. -+ /* typedef unsigned long/int __kernel_ino_t */ -+ /* alpha and s390x are int */ -+ -+config AUFS_XATTR -+ bool "support for XATTR/EA (including Security Labels)" -+ help -+ If your branch fs supports XATTR/EA and you want to make them -+ available in aufs too, then enable this opsion and specify the -+ branch attributes for EA. -+ See detail in aufs.5. -+ -+config AUFS_FHSM -+ bool "File-based Hierarchical Storage Management" -+ help -+ Hierarchical Storage Management (or HSM) is a well-known feature -+ in the storage world. Aufs provides this feature as file-based. -+ with multiple branches. -+ These multiple branches are prioritized, ie. the topmost one -+ should be the fastest drive and be used heavily. -+ -+config AUFS_RDU -+ bool "Readdir in userspace" -+ help -+ Aufs has two methods to provide a merged view for a directory, -+ by a user-space library and by kernel-space natively. The latter -+ is always enabled but sometimes large and slow. -+ If you enable this option, install the library in aufs2-util -+ package, and set some environment variables for your readdir(3), -+ then the work will be handled in user-space which generally -+ shows better performance in most cases. -+ See detail in aufs.5. -+ -+config AUFS_SHWH -+ bool "Show whiteouts" -+ help -+ If you want to make the whiteouts in aufs visible, then enable -+ this option and specify 'shwh' mount option. Although it may -+ sounds like philosophy or something, but in technically it -+ simply shows the name of whiteout with keeping its behaviour. -+ -+config AUFS_BR_RAMFS -+ bool "Ramfs (initramfs/rootfs) as an aufs branch" -+ help -+ If you want to use ramfs as an aufs branch fs, then enable this -+ option. Generally tmpfs is recommended. -+ Aufs prohibited them to be a branch fs by default, because -+ initramfs becomes unusable after switch_root or something -+ generally. If you sets initramfs as an aufs branch and boot your -+ system by switch_root, you will meet a problem easily since the -+ files in initramfs may be inaccessible. -+ Unless you are going to use ramfs as an aufs branch fs without -+ switch_root or something, leave it N. -+ -+config AUFS_BR_FUSE -+ bool "Fuse fs as an aufs branch" -+ depends on FUSE_FS -+ select AUFS_POLL -+ help -+ If you want to use fuse-based userspace filesystem as an aufs -+ branch fs, then enable this option. -+ It implements the internal poll(2) operation which is -+ implemented by fuse only (curretnly). -+ -+config AUFS_POLL -+ bool -+ help -+ Automatic configuration for internal use. -+ -+config AUFS_BR_HFSPLUS -+ bool "Hfsplus as an aufs branch" -+ depends on HFSPLUS_FS -+ default y -+ help -+ If you want to use hfsplus fs as an aufs branch fs, then enable -+ this option. This option introduces a small overhead at -+ copying-up a file on hfsplus. -+ -+config AUFS_BDEV_LOOP -+ bool -+ depends on BLK_DEV_LOOP -+ default y -+ help -+ Automatic configuration for internal use. -+ Convert =[ym] into =y. -+ -+config AUFS_DEBUG -+ bool "Debug aufs" -+ help -+ Enable this to compile aufs internal debug code. -+ It will have a negative impact to the performance. -+ -+config AUFS_MAGIC_SYSRQ -+ bool -+ depends on AUFS_DEBUG && MAGIC_SYSRQ -+ default y -+ help -+ Automatic configuration for internal use. -+ When aufs supports Magic SysRq, enabled automatically. -+endif -diff -Nurp linux-4.4.3/fs/aufs/loop.c linux-4.4.3-aufs/fs/aufs/loop.c ---- linux-4.4.3/fs/aufs/loop.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/loop.c 2016-03-02 17:59:37.369498746 +0200 +diff --git a/fs/aufs/loop.c b/fs/aufs/loop.c +new file mode 100644 +index 0000000..5711e7a +--- /dev/null ++++ b/fs/aufs/loop.c @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima @@ -20125,9 +20250,11 @@ diff -Nurp linux-4.4.3/fs/aufs/loop.c linux-4.4.3-aufs/fs/aufs/loop.c + symbol_put(loop_backing_file); + kfree(au_warn_loopback_array); +} -diff -Nurp linux-4.4.3/fs/aufs/loop.h linux-4.4.3-aufs/fs/aufs/loop.h ---- linux-4.4.3/fs/aufs/loop.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/loop.h 2016-03-02 17:59:37.369498746 +0200 +diff --git a/fs/aufs/loop.h b/fs/aufs/loop.h +new file mode 100644 +index 0000000..48bf070 +--- /dev/null ++++ b/fs/aufs/loop.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima @@ -20168,9 +20295,11 @@ diff -Nurp linux-4.4.3/fs/aufs/loop.h linux-4.4.3-aufs/fs/aufs/loop.h + +#endif /* __KERNEL__ */ +#endif /* __AUFS_LOOP_H__ */ -diff -Nurp linux-4.4.3/fs/aufs/magic.mk linux-4.4.3-aufs/fs/aufs/magic.mk ---- linux-4.4.3/fs/aufs/magic.mk 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/magic.mk 2016-03-02 17:59:37.369498746 +0200 +diff --git a/fs/aufs/magic.mk b/fs/aufs/magic.mk +new file mode 100644 +index 0000000..4f83bdf +--- /dev/null ++++ b/fs/aufs/magic.mk @@ -0,0 +1,30 @@ + +# defined in ${srctree}/fs/fuse/inode.c @@ -20202,50 +20331,12 @@ diff -Nurp linux-4.4.3/fs/aufs/magic.mk linux-4.4.3-aufs/fs/aufs/magic.mk +ifdef CONFIG_HFSPLUS_FS +ccflags-y += -DHFSPLUS_SUPER_MAGIC=0x482b +endif -diff -Nurp linux-4.4.3/fs/aufs/Makefile linux-4.4.3-aufs/fs/aufs/Makefile ---- linux-4.4.3/fs/aufs/Makefile 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/Makefile 2016-03-02 17:59:37.361498688 +0200 -@@ -0,0 +1,36 @@ -+ -+include ${srctree}/${src}/magic.mk -+ -+# cf. include/linux/kernel.h -+# enable pr_debug -+ccflags-y += -DDEBUG -+# sparse requires the full pathname -+ccflags-y += -include ${srctree}/include/uapi/linux/aufs_type.h -+ -+obj-$(CONFIG_AUFS_FS) += aufs.o -+aufs-y := module.o sbinfo.o super.o branch.o xino.o sysaufs.o opts.o \ -+ wkq.o vfsub.o dcsub.o \ -+ cpup.o whout.o wbr_policy.o \ -+ dinfo.o dentry.o \ -+ dynop.o \ -+ finfo.o file.o f_op.o \ -+ dir.o vdir.o \ -+ iinfo.o inode.o i_op.o i_op_add.o i_op_del.o i_op_ren.o \ -+ mvdown.o ioctl.o -+ -+# all are boolean -+aufs-$(CONFIG_PROC_FS) += procfs.o plink.o -+aufs-$(CONFIG_SYSFS) += sysfs.o -+aufs-$(CONFIG_DEBUG_FS) += dbgaufs.o -+aufs-$(CONFIG_AUFS_BDEV_LOOP) += loop.o -+aufs-$(CONFIG_AUFS_HNOTIFY) += hnotify.o -+aufs-$(CONFIG_AUFS_HFSNOTIFY) += hfsnotify.o -+aufs-$(CONFIG_AUFS_EXPORT) += export.o -+aufs-$(CONFIG_AUFS_XATTR) += xattr.o -+aufs-$(CONFIG_FS_POSIX_ACL) += posix_acl.o -+aufs-$(CONFIG_AUFS_FHSM) += fhsm.o -+aufs-$(CONFIG_AUFS_POLL) += poll.o -+aufs-$(CONFIG_AUFS_RDU) += rdu.o -+aufs-$(CONFIG_AUFS_BR_HFSPLUS) += hfsplus.o -+aufs-$(CONFIG_AUFS_DEBUG) += debug.o -+aufs-$(CONFIG_AUFS_MAGIC_SYSRQ) += sysrq.o -diff -Nurp linux-4.4.3/fs/aufs/module.c linux-4.4.3-aufs/fs/aufs/module.c ---- linux-4.4.3/fs/aufs/module.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/module.c 2016-03-02 17:59:37.369498746 +0200 -@@ -0,0 +1,207 @@ +diff --git a/fs/aufs/module.c b/fs/aufs/module.c +new file mode 100644 +index 0000000..88f8f41 +--- /dev/null ++++ b/fs/aufs/module.c +@@ -0,0 +1,209 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima + */ @@ -20274,26 +20365,9 @@ diff -Nurp linux-4.4.3/fs/aufs/module.c linux-4.4.3-aufs/fs/aufs/module.c +/* + * aufs caches + */ -+struct kmem_cache *au_cachep[AuCache_Last]; -+static int __init au_cache_init(void) -+{ -+ au_cachep[AuCache_DINFO] = AuCacheCtor(au_dinfo, au_di_init_once); -+ if (au_cachep[AuCache_DINFO]) -+ /* SLAB_DESTROY_BY_RCU */ -+ au_cachep[AuCache_ICNTNR] = AuCacheCtor(au_icntnr, -+ au_icntnr_init_once); -+ if (au_cachep[AuCache_ICNTNR]) -+ au_cachep[AuCache_FINFO] = AuCacheCtor(au_finfo, -+ au_fi_init_once); -+ if (au_cachep[AuCache_FINFO]) -+ au_cachep[AuCache_VDIR] = AuCache(au_vdir); -+ if (au_cachep[AuCache_VDIR]) -+ au_cachep[AuCache_DEHSTR] = AuCache(au_vdir_dehstr); -+ if (au_cachep[AuCache_DEHSTR]) -+ return 0; -+ -+ return -ENOMEM; -+} ++struct kmem_cache *au_cachep[AuCache_Last] = { ++ [0] = NULL ++}; + +static void au_cache_fin(void) +{ @@ -20313,6 +20387,27 @@ diff -Nurp linux-4.4.3/fs/aufs/module.c linux-4.4.3-aufs/fs/aufs/module.c + } +} + ++static int __init au_cache_init(void) ++{ ++ au_cachep[AuCache_DINFO] = AuCacheCtor(au_dinfo, au_di_init_once); ++ if (au_cachep[AuCache_DINFO]) ++ /* SLAB_DESTROY_BY_RCU */ ++ au_cachep[AuCache_ICNTNR] = AuCacheCtor(au_icntnr, ++ au_icntnr_init_once); ++ if (au_cachep[AuCache_ICNTNR]) ++ au_cachep[AuCache_FINFO] = AuCacheCtor(au_finfo, ++ au_fi_init_once); ++ if (au_cachep[AuCache_FINFO]) ++ au_cachep[AuCache_VDIR] = AuCache(au_vdir); ++ if (au_cachep[AuCache_VDIR]) ++ au_cachep[AuCache_DEHSTR] = AuCache(au_vdir_dehstr); ++ if (au_cachep[AuCache_DEHSTR]) ++ return 0; ++ ++ au_cache_fin(); ++ return -ENOMEM; ++} ++ +/* ---------------------------------------------------------------------- */ + +int au_dir_roflags; @@ -20322,11 +20417,9 @@ diff -Nurp linux-4.4.3/fs/aufs/module.c linux-4.4.3-aufs/fs/aufs/module.c + * iterate_supers_type() doesn't protect us from + * remounting (branch management) + */ -+struct au_splhead au_sbilist; ++struct au_sphlhead au_sbilist; +#endif + -+struct lock_class_key au_lc_key[AuLcKey_Last]; -+ +/* + * functions for module interface. + */ @@ -20453,10 +20546,12 @@ diff -Nurp linux-4.4.3/fs/aufs/module.c linux-4.4.3-aufs/fs/aufs/module.c + +module_init(aufs_init); +module_exit(aufs_exit); -diff -Nurp linux-4.4.3/fs/aufs/module.h linux-4.4.3-aufs/fs/aufs/module.h ---- linux-4.4.3/fs/aufs/module.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/module.h 2016-03-02 17:59:37.369498746 +0200 -@@ -0,0 +1,92 @@ +diff --git a/fs/aufs/module.h b/fs/aufs/module.h +new file mode 100644 +index 0000000..1383e3d +--- /dev/null ++++ b/fs/aufs/module.h +@@ -0,0 +1,76 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima + */ @@ -20483,22 +20578,6 @@ diff -Nurp linux-4.4.3/fs/aufs/module.h linux-4.4.3-aufs/fs/aufs/module.h + +extern int au_dir_roflags; + -+enum { -+ AuLcNonDir_FIINFO, -+ AuLcNonDir_DIINFO, -+ AuLcNonDir_IIINFO, -+ -+ AuLcDir_FIINFO, -+ AuLcDir_DIINFO, -+ AuLcDir_IIINFO, -+ -+ AuLcSymlink_DIINFO, -+ AuLcSymlink_IIINFO, -+ -+ AuLcKey_Last -+}; -+extern struct lock_class_key au_lc_key[AuLcKey_Last]; -+ +void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp); +int au_seq_path(struct seq_file *seq, struct path *path); + @@ -20549,9 +20628,11 @@ diff -Nurp linux-4.4.3/fs/aufs/module.h linux-4.4.3-aufs/fs/aufs/module.h + +#endif /* __KERNEL__ */ +#endif /* __AUFS_MODULE_H__ */ -diff -Nurp linux-4.4.3/fs/aufs/mvdown.c linux-4.4.3-aufs/fs/aufs/mvdown.c ---- linux-4.4.3/fs/aufs/mvdown.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/mvdown.c 2016-03-02 17:59:37.370498753 +0200 +diff --git a/fs/aufs/mvdown.c b/fs/aufs/mvdown.c +new file mode 100644 +index 0000000..334f25c +--- /dev/null ++++ b/fs/aufs/mvdown.c @@ -0,0 +1,690 @@ +/* + * Copyright (C) 2011-2016 Junjiro R. Okajima @@ -20608,27 +20689,27 @@ diff -Nurp linux-4.4.3/fs/aufs/mvdown.c linux-4.4.3-aufs/fs/aufs/mvdown.c +static int find_lower_writable(struct au_mvd_args *a) +{ + struct super_block *sb; -+ aufs_bindex_t bindex, bend; ++ aufs_bindex_t bindex, bbot; + struct au_branch *br; + + sb = a->sb; + bindex = a->mvd_bsrc; -+ bend = au_sbend(sb); ++ bbot = au_sbbot(sb); + if (a->mvdown.flags & AUFS_MVDOWN_FHSM_LOWER) -+ for (bindex++; bindex <= bend; bindex++) { ++ for (bindex++; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + if (au_br_fhsm(br->br_perm) + && (!(au_br_sb(br)->s_flags & MS_RDONLY))) + return bindex; + } + else if (!(a->mvdown.flags & AUFS_MVDOWN_ROLOWER)) -+ for (bindex++; bindex <= bend; bindex++) { ++ for (bindex++; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + if (!au_br_rdonly(br)) + return bindex; + } + else -+ for (bindex++; bindex <= bend; bindex++) { ++ for (bindex++; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + if (!(au_br_sb(br)->s_flags & MS_RDONLY)) { + if (au_br_rdonly(br)) @@ -20651,7 +20732,7 @@ diff -Nurp linux-4.4.3/fs/aufs/mvdown.c linux-4.4.3-aufs/fs/aufs/mvdown.c + a->mvd_hdir_dst = au_hi(a->dir, a->mvd_bdst); + a->mvd_h_src_parent = au_h_dptr(a->parent, a->mvd_bsrc); + a->mvd_h_dst_parent = NULL; -+ if (au_dbend(a->parent) >= a->mvd_bdst) ++ if (au_dbbot(a->parent) >= a->mvd_bdst) + a->mvd_h_dst_parent = au_h_dptr(a->parent, a->mvd_bdst); + if (!a->mvd_h_dst_parent) { + err = au_cpdown_dirs(a->dentry, a->mvd_bdst); @@ -20905,20 +20986,20 @@ diff -Nurp linux-4.4.3/fs/aufs/mvdown.c linux-4.4.3-aufs/fs/aufs/mvdown.c + /* maintain internal array */ + if (!(a->mvdown.flags & AUFS_MVDOWN_KUPPER)) { + au_set_h_dptr(a->dentry, a->mvd_bsrc, NULL); -+ au_set_dbstart(a->dentry, a->mvd_bdst); ++ au_set_dbtop(a->dentry, a->mvd_bdst); + au_set_h_iptr(a->inode, a->mvd_bsrc, NULL, /*flags*/0); -+ au_set_ibstart(a->inode, a->mvd_bdst); ++ au_set_ibtop(a->inode, a->mvd_bdst); + } else { + /* hide the lower */ + au_set_h_dptr(a->dentry, a->mvd_bdst, NULL); -+ au_set_dbend(a->dentry, a->mvd_bsrc); ++ au_set_dbbot(a->dentry, a->mvd_bsrc); + au_set_h_iptr(a->inode, a->mvd_bdst, NULL, /*flags*/0); -+ au_set_ibend(a->inode, a->mvd_bsrc); ++ au_set_ibbot(a->inode, a->mvd_bsrc); + } -+ if (au_dbend(a->dentry) < a->mvd_bdst) -+ au_set_dbend(a->dentry, a->mvd_bdst); -+ if (au_ibend(a->inode) < a->mvd_bdst) -+ au_set_ibend(a->inode, a->mvd_bdst); ++ if (au_dbbot(a->dentry) < a->mvd_bdst) ++ au_set_dbbot(a->dentry, a->mvd_bdst); ++ if (au_ibbot(a->inode) < a->mvd_bdst) ++ au_set_ibbot(a->inode, a->mvd_bdst); + +out_unlock: + au_do_unlock(dmsg, a); @@ -20936,7 +21017,7 @@ diff -Nurp linux-4.4.3/fs/aufs/mvdown.c linux-4.4.3-aufs/fs/aufs/mvdown.c + + err = 0; + plinked = !!au_opt_test(au_mntflags(a->sb), PLINK); -+ if (au_dbstart(a->dentry) == a->mvd_bsrc ++ if (au_dbtop(a->dentry) == a->mvd_bsrc + && au_dcount(a->dentry) == 1 + && atomic_read(&a->inode->i_count) == 1 + /* && a->mvd_h_src_inode->i_nlink == 1 */ @@ -20947,7 +21028,7 @@ diff -Nurp linux-4.4.3/fs/aufs/mvdown.c linux-4.4.3-aufs/fs/aufs/mvdown.c + err = -EBUSY; + AU_MVD_PR(dmsg, + "b%d, d{b%d, c%d?}, i{c%d?, l%u}, hi{l%u}, p{%d, %d}\n", -+ a->mvd_bsrc, au_dbstart(a->dentry), au_dcount(a->dentry), ++ a->mvd_bsrc, au_dbtop(a->dentry), au_dcount(a->dentry), + atomic_read(&a->inode->i_count), a->inode->i_nlink, + a->mvd_h_src_inode->i_nlink, + plinked, plinked ? au_plink_test(a->inode) : 0); @@ -21010,7 +21091,7 @@ diff -Nurp linux-4.4.3/fs/aufs/mvdown.c linux-4.4.3-aufs/fs/aufs/mvdown.c + if (!err) + a->bwh = au_dbwh(a->dentry); + else if (err > 0) -+ a->bfound = au_dbstart(a->dentry); ++ a->bfound = au_dbtop(a->dentry); + + au_di_swap(tmp, dinfo); + au_rw_write_unlock(&tmp->di_rwsem); @@ -21097,22 +21178,22 @@ diff -Nurp linux-4.4.3/fs/aufs/mvdown.c linux-4.4.3-aufs/fs/aufs/mvdown.c + + err = -EINVAL; + if (!(a->mvdown.flags & AUFS_MVDOWN_BRID_UPPER)) -+ a->mvd_bsrc = au_ibstart(a->inode); ++ a->mvd_bsrc = au_ibtop(a->inode); + else { + a->mvd_bsrc = au_br_index(a->sb, a->mvd_src_brid); + if (unlikely(a->mvd_bsrc < 0 -+ || (a->mvd_bsrc < au_dbstart(a->dentry) -+ || au_dbend(a->dentry) < a->mvd_bsrc ++ || (a->mvd_bsrc < au_dbtop(a->dentry) ++ || au_dbbot(a->dentry) < a->mvd_bsrc + || !au_h_dptr(a->dentry, a->mvd_bsrc)) -+ || (a->mvd_bsrc < au_ibstart(a->inode) -+ || au_ibend(a->inode) < a->mvd_bsrc ++ || (a->mvd_bsrc < au_ibtop(a->inode) ++ || au_ibbot(a->inode) < a->mvd_bsrc + || !au_h_iptr(a->inode, a->mvd_bsrc)))) { + a->mvd_errno = EAU_MVDOWN_NOUPPER; + AU_MVD_PR(dmsg, "no upper\n"); + goto out; + } + } -+ if (unlikely(a->mvd_bsrc == au_sbend(a->sb))) { ++ if (unlikely(a->mvd_bsrc == au_sbbot(a->sb))) { + a->mvd_errno = EAU_MVDOWN_BOTTOM; + AU_MVD_PR(dmsg, "on the bottom\n"); + goto out; @@ -21142,7 +21223,7 @@ diff -Nurp linux-4.4.3/fs/aufs/mvdown.c linux-4.4.3-aufs/fs/aufs/mvdown.c + } else { + a->mvd_bdst = au_br_index(a->sb, a->mvd_dst_brid); + if (unlikely(a->mvd_bdst < 0 -+ || au_sbend(a->sb) < a->mvd_bdst)) { ++ || au_sbbot(a->sb) < a->mvd_bdst)) { + a->mvd_errno = EAU_MVDOWN_NOLOWERBR; + AU_MVD_PR(dmsg, "no lower brid\n"); + goto out; @@ -21200,14 +21281,14 @@ diff -Nurp linux-4.4.3/fs/aufs/mvdown.c linux-4.4.3-aufs/fs/aufs/mvdown.c + dmsg = !!(args->mvdown.flags & AUFS_MVDOWN_DMSG); + args->parent = dget_parent(dentry); + args->dir = d_inode(args->parent); -+ mutex_lock_nested(&args->dir->i_mutex, I_MUTEX_PARENT); ++ inode_lock_nested(args->dir, I_MUTEX_PARENT); + dput(args->parent); + if (unlikely(args->parent != dentry->d_parent)) { + AU_MVD_PR(dmsg, "parent dir is moved\n"); + goto out_dir; + } + -+ mutex_lock_nested(&inode->i_mutex, I_MUTEX_CHILD); ++ inode_lock_nested(inode, I_MUTEX_CHILD); + err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH | AuLock_NOPLMW); + if (unlikely(err)) + goto out_inode; @@ -21231,9 +21312,9 @@ diff -Nurp linux-4.4.3/fs/aufs/mvdown.c linux-4.4.3-aufs/fs/aufs/mvdown.c + di_write_unlock(args->parent); + aufs_read_unlock(dentry, AuLock_DW); +out_inode: -+ mutex_unlock(&inode->i_mutex); ++ inode_unlock(inode); +out_dir: -+ mutex_unlock(&args->dir->i_mutex); ++ inode_unlock(args->dir); +out_free: + e = copy_to_user(uarg, &args->mvdown, sizeof(args->mvdown)); + if (unlikely(e)) @@ -21243,9 +21324,11 @@ diff -Nurp linux-4.4.3/fs/aufs/mvdown.c linux-4.4.3-aufs/fs/aufs/mvdown.c + AuTraceErr(err); + return err; +} -diff -Nurp linux-4.4.3/fs/aufs/opts.c linux-4.4.3-aufs/fs/aufs/opts.c ---- linux-4.4.3/fs/aufs/opts.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/opts.c 2016-03-02 17:59:37.370498753 +0200 +diff --git a/fs/aufs/opts.c b/fs/aufs/opts.c +new file mode 100644 +index 0000000..23a146d +--- /dev/null ++++ b/fs/aufs/opts.c @@ -0,0 +1,1846 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima @@ -22073,7 +22156,7 @@ diff -Nurp linux-4.4.3/fs/aufs/opts.c linux-4.4.3-aufs/fs/aufs/opts.c + err = -EINVAL; + root = sb->s_root; + aufs_read_lock(root, AuLock_FLUSH); -+ if (bindex < 0 || au_sbend(sb) < bindex) { ++ if (bindex < 0 || au_sbbot(sb) < bindex) { + pr_err("out of bounds, %d\n", bindex); + goto out; + } @@ -22129,7 +22212,7 @@ diff -Nurp linux-4.4.3/fs/aufs/opts.c linux-4.4.3-aufs/fs/aufs/opts.c + err = -EINVAL; + root = sb->s_root; + aufs_read_lock(root, AuLock_FLUSH); -+ if (bindex < 0 || au_sbend(sb) < bindex) { ++ if (bindex < 0 || au_sbbot(sb) < bindex) { + pr_err("out of bounds, %d\n", bindex); + goto out; + } @@ -22178,7 +22261,7 @@ diff -Nurp linux-4.4.3/fs/aufs/opts.c linux-4.4.3-aufs/fs/aufs/opts.c + substring_t args[]) +{ + int err; -+ aufs_bindex_t bend, bindex; ++ aufs_bindex_t bbot, bindex; + struct path path; + struct dentry *root; + @@ -22191,8 +22274,8 @@ diff -Nurp linux-4.4.3/fs/aufs/opts.c linux-4.4.3-aufs/fs/aufs/opts.c + xino_itrunc->bindex = -1; + root = sb->s_root; + aufs_read_lock(root, AuLock_FLUSH); -+ bend = au_sbend(sb); -+ for (bindex = 0; bindex <= bend; bindex++) { ++ bbot = au_sbbot(sb); ++ for (bindex = 0; bindex <= bbot; bindex++) { + if (au_h_dptr(root, bindex) == path.dentry) { + xino_itrunc->bindex = bindex; + break; @@ -22336,7 +22419,7 @@ diff -Nurp linux-4.4.3/fs/aufs/opts.c linux-4.4.3-aufs/fs/aufs/opts.c + } + u.xino_itrunc->bindex = n; + aufs_read_lock(root, AuLock_FLUSH); -+ if (n < 0 || au_sbend(sb) < n) { ++ if (n < 0 || au_sbbot(sb) < n) { + pr_err("out of bounds, %d\n", n); + aufs_read_unlock(root, !AuLock_IR); + break; @@ -22714,7 +22797,7 @@ diff -Nurp linux-4.4.3/fs/aufs/opts.c linux-4.4.3-aufs/fs/aufs/opts.c + err = 0; + switch (opt->type) { + case Opt_append: -+ opt->add.bindex = au_sbend(sb) + 1; ++ opt->add.bindex = au_sbbot(sb) + 1; + if (opt->add.bindex < 0) + opt->add.bindex = 0; + goto add; @@ -22762,7 +22845,7 @@ diff -Nurp linux-4.4.3/fs/aufs/opts.c linux-4.4.3-aufs/fs/aufs/opts.c + struct au_opts *opts) +{ + int err; -+ aufs_bindex_t bend, bindex; ++ aufs_bindex_t bbot, bindex; + struct dentry *root, *parent, *h_root; + + err = 0; @@ -22779,8 +22862,8 @@ diff -Nurp linux-4.4.3/fs/aufs/opts.c linux-4.4.3-aufs/fs/aufs/opts.c + /* safe d_parent access */ + parent = opt->xino.file->f_path.dentry->d_parent; + root = sb->s_root; -+ bend = au_sbend(sb); -+ for (bindex = 0; bindex <= bend; bindex++) { ++ bbot = au_sbbot(sb); ++ for (bindex = 0; bindex <= bbot; bindex++) { + h_root = au_h_dptr(root, bindex); + if (h_root == parent) { + au_xino_brid_set(sb, au_sbr_id(sb, bindex)); @@ -22803,7 +22886,7 @@ diff -Nurp linux-4.4.3/fs/aufs/opts.c linux-4.4.3-aufs/fs/aufs/opts.c + unsigned int pending) +{ + int err, fhsm; -+ aufs_bindex_t bindex, bend; ++ aufs_bindex_t bindex, bbot; + unsigned char do_plink, skip, do_free, can_no_dreval; + struct au_branch *br; + struct au_wbr *wbr; @@ -22821,16 +22904,16 @@ diff -Nurp linux-4.4.3/fs/aufs/opts.c linux-4.4.3-aufs/fs/aufs/opts.c + if (unlikely(!au_br_writable(au_sbr_perm(sb, 0)))) + pr_warn("first branch should be rw\n"); + if (unlikely(au_opt_test(sbinfo->si_mntflags, SHWH))) -+ pr_warn("shwh should be used with ro\n"); ++ pr_warn_once("shwh should be used with ro\n"); + } + + if (au_opt_test((sbinfo->si_mntflags | pending), UDBA_HNOTIFY) + && !au_opt_test(sbinfo->si_mntflags, XINO)) -+ pr_warn("udba=*notify requires xino\n"); ++ pr_warn_once("udba=*notify requires xino\n"); + + if (au_opt_test(sbinfo->si_mntflags, DIRPERM1)) -+ pr_warn("dirperm1 breaks the protection" -+ " by the permission bits on the lower branch\n"); ++ pr_warn_once("dirperm1 breaks the protection" ++ " by the permission bits on the lower branch\n"); + + err = 0; + fhsm = 0; @@ -22839,8 +22922,8 @@ diff -Nurp linux-4.4.3/fs/aufs/opts.c linux-4.4.3-aufs/fs/aufs/opts.c + do_plink = !!au_opt_test(sbinfo->si_mntflags, PLINK); + can_no_dreval = !!au_opt_test((sbinfo->si_mntflags | pending), + UDBA_NONE); -+ bend = au_sbend(sb); -+ for (bindex = 0; !err && bindex <= bend; bindex++) { ++ bbot = au_sbbot(sb); ++ for (bindex = 0; !err && bindex <= bbot; bindex++) { + skip = 0; + h_dir = au_h_iptr(dir, bindex); + br = au_sbr(sb, bindex); @@ -22926,7 +23009,7 @@ diff -Nurp linux-4.4.3/fs/aufs/opts.c linux-4.4.3-aufs/fs/aufs/opts.c + + if (fhsm >= 2) { + au_fset_si(sbinfo, FHSM); -+ for (bindex = bend; bindex >= 0; bindex--) { ++ for (bindex = bbot; bindex >= 0; bindex--) { + br = au_sbr(sb, bindex); + if (au_br_fhsm(br->br_perm)) { + au_fhsm_set_bottom(sb, bindex); @@ -22945,7 +23028,7 @@ diff -Nurp linux-4.4.3/fs/aufs/opts.c linux-4.4.3-aufs/fs/aufs/opts.c +{ + int err; + unsigned int tmp; -+ aufs_bindex_t bindex, bend; ++ aufs_bindex_t bindex, bbot; + struct au_opt *opt; + struct au_opt_xino *opt_xino, xino; + struct au_sbinfo *sbinfo; @@ -22978,8 +23061,8 @@ diff -Nurp linux-4.4.3/fs/aufs/opts.c linux-4.4.3-aufs/fs/aufs/opts.c + else if (unlikely(err < 0)) + goto out; + -+ bend = au_sbend(sb); -+ if (unlikely(bend < 0)) { ++ bbot = au_sbbot(sb); ++ if (unlikely(bbot < 0)) { + err = -EINVAL; + pr_err("no branches\n"); + goto out; @@ -23014,8 +23097,8 @@ diff -Nurp linux-4.4.3/fs/aufs/opts.c linux-4.4.3-aufs/fs/aufs/opts.c + tmp &= AuOptMask_UDBA; + sbinfo->si_mntflags &= ~AuOptMask_UDBA; + sbinfo->si_mntflags |= tmp; -+ bend = au_sbend(sb); -+ for (bindex = 0; bindex <= bend; bindex++) { ++ bbot = au_sbbot(sb); ++ for (bindex = 0; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + err = au_hnotify_reset_br(tmp, br, br->br_perm); + if (unlikely(err)) @@ -23093,9 +23176,11 @@ diff -Nurp linux-4.4.3/fs/aufs/opts.c linux-4.4.3-aufs/fs/aufs/opts.c +{ + return au_mntflags(sb) & AuOptMask_UDBA; +} -diff -Nurp linux-4.4.3/fs/aufs/opts.h linux-4.4.3-aufs/fs/aufs/opts.h ---- linux-4.4.3/fs/aufs/opts.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/opts.h 2016-03-02 17:59:37.370498753 +0200 +diff --git a/fs/aufs/opts.h b/fs/aufs/opts.h +new file mode 100644 +index 0000000..0d6c2e1 +--- /dev/null ++++ b/fs/aufs/opts.h @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima @@ -23295,10 +23380,12 @@ diff -Nurp linux-4.4.3/fs/aufs/opts.h linux-4.4.3-aufs/fs/aufs/opts.h + +#endif /* __KERNEL__ */ +#endif /* __AUFS_OPTS_H__ */ -diff -Nurp linux-4.4.3/fs/aufs/plink.c linux-4.4.3-aufs/fs/aufs/plink.c ---- linux-4.4.3/fs/aufs/plink.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/plink.c 2016-03-02 17:59:37.370498753 +0200 -@@ -0,0 +1,515 @@ +diff --git a/fs/aufs/plink.c b/fs/aufs/plink.c +new file mode 100644 +index 0000000..c42734d +--- /dev/null ++++ b/fs/aufs/plink.c +@@ -0,0 +1,489 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima + */ @@ -23412,7 +23499,7 @@ diff -Nurp linux-4.4.3/fs/aufs/plink.c linux-4.4.3-aufs/fs/aufs/plink.c + int i; + struct au_sbinfo *sbinfo; + struct hlist_head *plink_hlist; -+ struct pseudo_link *plink; ++ struct au_icntnr *icntnr; + + SiMustAnyLock(sb); + @@ -23423,8 +23510,8 @@ diff -Nurp linux-4.4.3/fs/aufs/plink.c linux-4.4.3-aufs/fs/aufs/plink.c + for (i = 0; i < AuPlink_NHASH; i++) { + plink_hlist = &sbinfo->si_plink[i].head; + rcu_read_lock(); -+ hlist_for_each_entry_rcu(plink, plink_hlist, hlist) -+ AuDbg("%lu\n", plink->inode->i_ino); ++ hlist_for_each_entry_rcu(icntnr, plink_hlist, plink) ++ AuDbg("%lu\n", icntnr->vfs_inode.i_ino); + rcu_read_unlock(); + } +} @@ -23436,7 +23523,7 @@ diff -Nurp linux-4.4.3/fs/aufs/plink.c linux-4.4.3-aufs/fs/aufs/plink.c + int found, i; + struct au_sbinfo *sbinfo; + struct hlist_head *plink_hlist; -+ struct pseudo_link *plink; ++ struct au_icntnr *icntnr; + + sbinfo = au_sbi(inode->i_sb); + AuRwMustAnyLock(&sbinfo->si_rwsem); @@ -23447,8 +23534,8 @@ diff -Nurp linux-4.4.3/fs/aufs/plink.c linux-4.4.3-aufs/fs/aufs/plink.c + i = au_plink_hash(inode->i_ino); + plink_hlist = &sbinfo->si_plink[i].head; + rcu_read_lock(); -+ hlist_for_each_entry_rcu(plink, plink_hlist, hlist) -+ if (plink->inode == inode) { ++ hlist_for_each_entry_rcu(icntnr, plink_hlist, plink) ++ if (&icntnr->vfs_inode == inode) { + found = 1; + break; + } @@ -23488,12 +23575,12 @@ diff -Nurp linux-4.4.3/fs/aufs/plink.c linux-4.4.3-aufs/fs/aufs/plink.c + struct au_branch *br) +{ + struct dentry *h_dentry; -+ struct mutex *h_mtx; ++ struct inode *h_inode; + -+ h_mtx = &d_inode(h_parent)->i_mutex; -+ mutex_lock_nested(h_mtx, AuLsc_I_CHILD2); ++ h_inode = d_inode(h_parent); ++ inode_lock_nested(h_inode, AuLsc_I_CHILD2); + h_dentry = vfsub_lkup_one(tgtname, h_parent); -+ mutex_unlock(h_mtx); ++ inode_unlock(h_inode); + return h_dentry; +} + @@ -23546,7 +23633,7 @@ diff -Nurp linux-4.4.3/fs/aufs/plink.c linux-4.4.3-aufs/fs/aufs/plink.c + struct inode *h_dir, *delegated; + + h_dir = d_inode(h_parent); -+ mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_CHILD2); ++ inode_lock_nested(h_dir, AuLsc_I_CHILD2); +again: + h_path.dentry = vfsub_lkup_one(tgt, h_parent); + err = PTR_ERR(h_path.dentry); @@ -23582,7 +23669,7 @@ diff -Nurp linux-4.4.3/fs/aufs/plink.c linux-4.4.3-aufs/fs/aufs/plink.c + dput(h_path.dentry); + +out: -+ mutex_unlock(&h_dir->i_mutex); ++ inode_unlock(h_dir); + return err; +} + @@ -23631,24 +23718,6 @@ diff -Nurp linux-4.4.3/fs/aufs/plink.c linux-4.4.3-aufs/fs/aufs/plink.c + return err; +} + -+/* free a single plink */ -+static void do_put_plink(struct pseudo_link *plink, int do_del) -+{ -+ if (do_del) -+ hlist_del(&plink->hlist); -+ iput(plink->inode); -+ kfree(plink); -+} -+ -+static void do_put_plink_rcu(struct rcu_head *rcu) -+{ -+ struct pseudo_link *plink; -+ -+ plink = container_of(rcu, struct pseudo_link, rcu); -+ iput(plink->inode); -+ kfree(plink); -+} -+ +/* + * create a new pseudo-link for @h_dentry on @bindex. + * the linked inode is held in aufs @inode. @@ -23659,7 +23728,7 @@ diff -Nurp linux-4.4.3/fs/aufs/plink.c linux-4.4.3-aufs/fs/aufs/plink.c + struct super_block *sb; + struct au_sbinfo *sbinfo; + struct hlist_head *plink_hlist; -+ struct pseudo_link *plink, *tmp; ++ struct au_icntnr *icntnr; + struct au_sphlhead *sphl; + int found, err, cnt, i; + @@ -23675,23 +23744,19 @@ diff -Nurp linux-4.4.3/fs/aufs/plink.c linux-4.4.3-aufs/fs/aufs/plink.c + i = au_plink_hash(inode->i_ino); + sphl = sbinfo->si_plink + i; + plink_hlist = &sphl->head; -+ tmp = kmalloc(sizeof(*plink), GFP_NOFS); -+ if (tmp) -+ tmp->inode = au_igrab(inode); -+ else { -+ err = -ENOMEM; -+ goto out; -+ } ++ au_igrab(inode); + + spin_lock(&sphl->spin); -+ hlist_for_each_entry(plink, plink_hlist, hlist) { -+ if (plink->inode == inode) { ++ hlist_for_each_entry(icntnr, plink_hlist, plink) { ++ if (&icntnr->vfs_inode == inode) { + found = 1; + break; + } + } -+ if (!found) -+ hlist_add_head_rcu(&tmp->hlist, plink_hlist); ++ if (!found) { ++ icntnr = container_of(inode, struct au_icntnr, vfs_inode); ++ hlist_add_head_rcu(&icntnr->plink, plink_hlist); ++ } + spin_unlock(&sphl->spin); + if (!found) { + cnt = au_sphl_count(sphl); @@ -23700,19 +23765,13 @@ diff -Nurp linux-4.4.3/fs/aufs/plink.c linux-4.4.3-aufs/fs/aufs/plink.c + AuWarn1(msg ", %d\n", cnt); +#undef msg + err = whplink(h_dentry, inode, bindex, au_sbr(sb, bindex)); -+ } else { -+ do_put_plink(tmp, 0); -+ return; -+ } -+ -+out: -+ if (unlikely(err)) { -+ pr_warn("err %d, damaged pseudo link.\n", err); -+ if (tmp) { -+ au_sphl_del_rcu(&tmp->hlist, sphl); -+ call_rcu(&tmp->rcu, do_put_plink_rcu); ++ if (unlikely(err)) { ++ pr_warn("err %d, damaged pseudo link.\n", err); ++ au_sphl_del_rcu(&icntnr->plink, sphl); ++ iput(&icntnr->vfs_inode); + } -+ } ++ } else ++ iput(&icntnr->vfs_inode); +} + +/* free all plinks */ @@ -23722,7 +23781,7 @@ diff -Nurp linux-4.4.3/fs/aufs/plink.c linux-4.4.3-aufs/fs/aufs/plink.c + struct au_sbinfo *sbinfo; + struct hlist_head *plink_hlist; + struct hlist_node *tmp; -+ struct pseudo_link *plink; ++ struct au_icntnr *icntnr; + + SiMustWriteLock(sb); + @@ -23738,8 +23797,8 @@ diff -Nurp linux-4.4.3/fs/aufs/plink.c linux-4.4.3-aufs/fs/aufs/plink.c + pr_warn("pseudo-link is not flushed"); + warned = 1; + } -+ hlist_for_each_entry_safe(plink, tmp, plink_hlist, hlist) -+ do_put_plink(plink, 0); ++ hlist_for_each_entry_safe(icntnr, tmp, plink_hlist, plink) ++ iput(&icntnr->vfs_inode); + INIT_HLIST_HEAD(plink_hlist); + } +} @@ -23758,13 +23817,13 @@ diff -Nurp linux-4.4.3/fs/aufs/plink.c linux-4.4.3-aufs/fs/aufs/plink.c +static int au_plink_do_half_refresh(struct inode *inode, aufs_bindex_t br_id) +{ + int do_put; -+ aufs_bindex_t bstart, bend, bindex; ++ aufs_bindex_t btop, bbot, bindex; + + do_put = 0; -+ bstart = au_ibstart(inode); -+ bend = au_ibend(inode); -+ if (bstart >= 0) { -+ for (bindex = bstart; bindex <= bend; bindex++) { ++ btop = au_ibtop(inode); ++ bbot = au_ibbot(inode); ++ if (btop >= 0) { ++ for (bindex = btop; bindex <= bbot; bindex++) { + if (!au_h_iptr(inode, bindex) + || au_ii_br_id(inode, bindex) != br_id) + continue; @@ -23773,7 +23832,7 @@ diff -Nurp linux-4.4.3/fs/aufs/plink.c linux-4.4.3-aufs/fs/aufs/plink.c + break; + } + if (do_put) -+ for (bindex = bstart; bindex <= bend; bindex++) ++ for (bindex = btop; bindex <= bbot; bindex++) + if (au_h_iptr(inode, bindex)) { + do_put = 0; + break; @@ -23790,7 +23849,7 @@ diff -Nurp linux-4.4.3/fs/aufs/plink.c linux-4.4.3-aufs/fs/aufs/plink.c + struct au_sbinfo *sbinfo; + struct hlist_head *plink_hlist; + struct hlist_node *tmp; -+ struct pseudo_link *plink; ++ struct au_icntnr *icntnr; + struct inode *inode; + int i, do_put; + @@ -23803,20 +23862,24 @@ diff -Nurp linux-4.4.3/fs/aufs/plink.c linux-4.4.3-aufs/fs/aufs/plink.c + /* no spin_lock since sbinfo is write-locked */ + for (i = 0; i < AuPlink_NHASH; i++) { + plink_hlist = &sbinfo->si_plink[i].head; -+ hlist_for_each_entry_safe(plink, tmp, plink_hlist, hlist) { -+ inode = au_igrab(plink->inode); ++ hlist_for_each_entry_safe(icntnr, tmp, plink_hlist, plink) { ++ inode = au_igrab(&icntnr->vfs_inode); + ii_write_lock_child(inode); + do_put = au_plink_do_half_refresh(inode, br_id); -+ if (do_put) -+ do_put_plink(plink, 1); ++ if (do_put) { ++ hlist_del(&icntnr->plink); ++ iput(inode); ++ } + ii_write_unlock(inode); + iput(inode); + } + } +} -diff -Nurp linux-4.4.3/fs/aufs/poll.c linux-4.4.3-aufs/fs/aufs/poll.c ---- linux-4.4.3/fs/aufs/poll.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/poll.c 2016-03-02 17:59:37.370498753 +0200 +diff --git a/fs/aufs/poll.c b/fs/aufs/poll.c +new file mode 100644 +index 0000000..dd2baf5 +--- /dev/null ++++ b/fs/aufs/poll.c @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima @@ -23857,9 +23920,11 @@ diff -Nurp linux-4.4.3/fs/aufs/poll.c linux-4.4.3-aufs/fs/aufs/poll.c + AuTraceErr((int)mask); + return mask; +} -diff -Nurp linux-4.4.3/fs/aufs/posix_acl.c linux-4.4.3-aufs/fs/aufs/posix_acl.c ---- linux-4.4.3/fs/aufs/posix_acl.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/posix_acl.c 2016-03-02 17:59:37.371498760 +0200 +diff --git a/fs/aufs/posix_acl.c b/fs/aufs/posix_acl.c +new file mode 100644 +index 0000000..0ddd469 +--- /dev/null ++++ b/fs/aufs/posix_acl.c @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2014-2016 Junjiro R. Okajima @@ -23887,7 +23952,7 @@ diff -Nurp linux-4.4.3/fs/aufs/posix_acl.c linux-4.4.3-aufs/fs/aufs/posix_acl.c + if (!(sb->s_flags & MS_POSIXACL)) + goto out; + -+ bindex = au_ibstart(inode); ++ bindex = au_ibtop(inode); + h_inode = au_h_iptr(inode, bindex); + if (unlikely(!h_inode + || ((h_inode->i_mode & S_IFMT) @@ -23921,7 +23986,7 @@ diff -Nurp linux-4.4.3/fs/aufs/posix_acl.c linux-4.4.3-aufs/fs/aufs/posix_acl.c + }, + }; + -+ mutex_lock(&inode->i_mutex); ++ inode_lock(inode); + if (inode->i_ino == AUFS_ROOT_INO) + dentry = dget(inode->i_sb->s_root); + else { @@ -23943,12 +24008,14 @@ diff -Nurp linux-4.4.3/fs/aufs/posix_acl.c linux-4.4.3-aufs/fs/aufs/posix_acl.c + err = 0; + +out: -+ mutex_unlock(&inode->i_mutex); ++ inode_unlock(inode); + return err; +} -diff -Nurp linux-4.4.3/fs/aufs/procfs.c linux-4.4.3-aufs/fs/aufs/procfs.c ---- linux-4.4.3/fs/aufs/procfs.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/procfs.c 2016-03-02 17:59:37.371498760 +0200 +diff --git a/fs/aufs/procfs.c b/fs/aufs/procfs.c +new file mode 100644 +index 0000000..e5a4e37 +--- /dev/null ++++ b/fs/aufs/procfs.c @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2010-2016 Junjiro R. Okajima @@ -23996,7 +24063,7 @@ diff -Nurp linux-4.4.3/fs/aufs/procfs.c linux-4.4.3-aufs/fs/aufs/procfs.c + sb = NULL; + /* don't use au_sbilist_lock() here */ + spin_lock(&au_sbilist.spin); -+ list_for_each_entry(sbinfo, &au_sbilist.head, si_list) ++ hlist_for_each_entry(sbinfo, &au_sbilist.head, si_list) + if (id == sysaufs_si_id(sbinfo)) { + kobject_get(&sbinfo->si_kobj); + sb = sbinfo->si_sb; @@ -24106,10 +24173,12 @@ diff -Nurp linux-4.4.3/fs/aufs/procfs.c linux-4.4.3-aufs/fs/aufs/procfs.c +out: + return err; +} -diff -Nurp linux-4.4.3/fs/aufs/rdu.c linux-4.4.3-aufs/fs/aufs/rdu.c ---- linux-4.4.3/fs/aufs/rdu.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/rdu.c 2016-03-02 17:59:37.371498760 +0200 -@@ -0,0 +1,375 @@ +diff --git a/fs/aufs/rdu.c b/fs/aufs/rdu.c +new file mode 100644 +index 0000000..7180f18 +--- /dev/null ++++ b/fs/aufs/rdu.c +@@ -0,0 +1,376 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima + */ @@ -24221,7 +24290,7 @@ diff -Nurp linux-4.4.3/fs/aufs/rdu.c linux-4.4.3-aufs/fs/aufs/rdu.c +static int au_rdu(struct file *file, struct aufs_rdu *rdu) +{ + int err; -+ aufs_bindex_t bend; ++ aufs_bindex_t bbot; + struct au_rdu_arg arg = { + .ctx = { + .actor = au_rdu_fill @@ -24258,8 +24327,9 @@ diff -Nurp linux-4.4.3/fs/aufs/rdu.c linux-4.4.3-aufs/fs/aufs/rdu.c + dentry = file->f_path.dentry; + inode = d_inode(dentry); +#if 1 -+ mutex_lock(&inode->i_mutex); ++ inode_lock(inode); +#else ++ /* todo: create a new inline func inode_lock_killable() */ + err = mutex_lock_killable(&inode->i_mutex); + AuTraceErr(err); + if (unlikely(err)) @@ -24287,12 +24357,12 @@ diff -Nurp linux-4.4.3/fs/aufs/rdu.c linux-4.4.3-aufs/fs/aufs/rdu.c + if (!rdu->blk) + rdu->blk = au_dir_size(file, /*dentry*/NULL); + } -+ bend = au_fbstart(file); -+ if (cookie->bindex < bend) -+ cookie->bindex = bend; -+ bend = au_fbend_dir(file); -+ /* AuDbg("b%d, b%d\n", cookie->bindex, bend); */ -+ for (; !err && cookie->bindex <= bend; ++ bbot = au_fbtop(file); ++ if (cookie->bindex < bbot) ++ cookie->bindex = bbot; ++ bbot = au_fbbot_dir(file); ++ /* AuDbg("b%d, b%d\n", cookie->bindex, bbot); */ ++ for (; !err && cookie->bindex <= bbot; + cookie->bindex++, cookie->h_pos = 0) { + h_file = au_hf_dir(file, cookie->bindex); + if (!h_file) @@ -24313,7 +24383,7 @@ diff -Nurp linux-4.4.3/fs/aufs/rdu.c linux-4.4.3-aufs/fs/aufs/rdu.c + } + + ii_read_lock_child(inode); -+ fsstack_copy_attr_atime(inode, au_h_iptr(inode, au_ibstart(inode))); ++ fsstack_copy_attr_atime(inode, au_h_iptr(inode, au_ibtop(inode))); + ii_read_unlock(inode); + +out_unlock: @@ -24321,7 +24391,7 @@ diff -Nurp linux-4.4.3/fs/aufs/rdu.c linux-4.4.3-aufs/fs/aufs/rdu.c +out_si: + si_read_unlock(arg.sb); +out_mtx: -+ mutex_unlock(&inode->i_mutex); ++ inode_unlock(inode); +out: + AuTraceErr(err); + return err; @@ -24485,10 +24555,12 @@ diff -Nurp linux-4.4.3/fs/aufs/rdu.c linux-4.4.3-aufs/fs/aufs/rdu.c + return err; +} +#endif -diff -Nurp linux-4.4.3/fs/aufs/rwsem.h linux-4.4.3-aufs/fs/aufs/rwsem.h ---- linux-4.4.3/fs/aufs/rwsem.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/rwsem.h 2016-03-02 17:59:37.371498760 +0200 -@@ -0,0 +1,178 @@ +diff --git a/fs/aufs/rwsem.h b/fs/aufs/rwsem.h +new file mode 100644 +index 0000000..6c0d5a9 +--- /dev/null ++++ b/fs/aufs/rwsem.h +@@ -0,0 +1,185 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima + */ @@ -24512,6 +24584,15 @@ diff -Nurp linux-4.4.3/fs/aufs/rwsem.h linux-4.4.3-aufs/fs/aufs/rwsem.h +#endif +}; + ++#ifdef CONFIG_LOCKDEP ++#define au_lockdep_set_name(rw) \ ++ lockdep_set_class_and_name(&(rw)->rwsem, \ ++ /*original key*/(rw)->rwsem.dep_map.key, \ ++ /*name*/#rw) ++#else ++#define au_lockdep_set_name(rw) do {} while (0) ++#endif ++ +#ifdef CONFIG_AUFS_DEBUG +#define AuDbgCntInit(rw) do { \ + atomic_set(&(rw)->rcnt, 0); \ @@ -24519,11 +24600,15 @@ diff -Nurp linux-4.4.3/fs/aufs/rwsem.h linux-4.4.3-aufs/fs/aufs/rwsem.h + smp_mb(); /* atomic set */ \ +} while (0) + -+#define AuDbgRcntInc(rw) atomic_inc(&(rw)->rcnt) -+#define AuDbgRcntDec(rw) WARN_ON(atomic_dec_return(&(rw)->rcnt) < 0) -+#define AuDbgWcntInc(rw) atomic_inc(&(rw)->wcnt) -+#define AuDbgWcntDec(rw) WARN_ON(atomic_dec_return(&(rw)->wcnt) < 0) ++#define AuDbgCnt(rw, cnt) atomic_read(&(rw)->cnt) ++#define AuDbgCntInc(rw, cnt) atomic_inc(&(rw)->cnt) ++#define AuDbgCntDec(rw, cnt) WARN_ON(atomic_dec_return(&(rw)->cnt) < 0) ++#define AuDbgRcntInc(rw) AuDbgCntInc(rw, rcnt) ++#define AuDbgRcntDec(rw) AuDbgCntDec(rw, rcnt) ++#define AuDbgWcntInc(rw) AuDbgCntInc(rw, wcnt) ++#define AuDbgWcntDec(rw) AuDbgCntDec(rw, wcnt) +#else ++#define AuDbgCnt(rw, cnt) 0 +#define AuDbgCntInit(rw) do {} while (0) +#define AuDbgRcntInc(rw) do {} while (0) +#define AuDbgRcntDec(rw) do {} while (0) @@ -24532,37 +24617,32 @@ diff -Nurp linux-4.4.3/fs/aufs/rwsem.h linux-4.4.3-aufs/fs/aufs/rwsem.h +#endif /* CONFIG_AUFS_DEBUG */ + +/* to debug easier, do not make them inlined functions */ -+#define AuRwMustNoWaiters(rw) AuDebugOn(!list_empty(&(rw)->rwsem.wait_list)) ++#define AuRwMustNoWaiters(rw) AuDebugOn(rwsem_is_contended(&(rw)->rwsem)) +/* rwsem_is_locked() is unusable */ -+#define AuRwMustReadLock(rw) AuDebugOn(atomic_read(&(rw)->rcnt) <= 0) -+#define AuRwMustWriteLock(rw) AuDebugOn(atomic_read(&(rw)->wcnt) <= 0) -+#define AuRwMustAnyLock(rw) AuDebugOn(atomic_read(&(rw)->rcnt) <= 0 \ -+ && atomic_read(&(rw)->wcnt) <= 0) -+#define AuRwDestroy(rw) AuDebugOn(atomic_read(&(rw)->rcnt) \ -+ || atomic_read(&(rw)->wcnt)) ++#define AuRwMustReadLock(rw) AuDebugOn(AuDbgCnt(rw, rcnt) <= 0) ++#define AuRwMustWriteLock(rw) AuDebugOn(AuDbgCnt(rw, wcnt) <= 0) ++#define AuRwMustAnyLock(rw) AuDebugOn(AuDbgCnt(rw, rcnt) <= 0 \ ++ && AuDbgCnt(rw, wcnt) <= 0) ++#define AuRwDestroy(rw) AuDebugOn(AuDbgCnt(rw, rcnt) \ ++ || AuDbgCnt(rw, wcnt)) + -+#define au_rw_class(rw, key) lockdep_set_class(&(rw)->rwsem, key) ++#define au_rw_init(rw) do { \ ++ AuDbgCntInit(rw); \ ++ init_rwsem(&(rw)->rwsem); \ ++ au_lockdep_set_name(rw); \ ++ } while (0) + -+static inline void au_rw_init(struct au_rwsem *rw) -+{ -+ AuDbgCntInit(rw); -+ init_rwsem(&rw->rwsem); -+} ++#define au_rw_init_wlock(rw) do { \ ++ au_rw_init(rw); \ ++ down_write(&(rw)->rwsem); \ ++ AuDbgWcntInc(rw); \ ++ } while (0) + -+static inline void au_rw_init_wlock(struct au_rwsem *rw) -+{ -+ au_rw_init(rw); -+ down_write(&rw->rwsem); -+ AuDbgWcntInc(rw); -+} -+ -+static inline void au_rw_init_wlock_nested(struct au_rwsem *rw, -+ unsigned int lsc) -+{ -+ au_rw_init(rw); -+ down_write_nested(&rw->rwsem, lsc); -+ AuDbgWcntInc(rw); -+} ++#define au_rw_init_wlock_nested(rw, lsc) do { \ ++ au_rw_init(rw); \ ++ down_write_nested(&(rw)->rwsem, lsc); \ ++ AuDbgWcntInc(rw); \ ++ } while (0) + +static inline void au_rw_read_lock(struct au_rwsem *rw) +{ @@ -24632,10 +24712,9 @@ diff -Nurp linux-4.4.3/fs/aufs/rwsem.h linux-4.4.3-aufs/fs/aufs/rwsem.h + return ret; +} + -+#undef AuDbgCntInit ++#undef AuDbgCntDec +#undef AuDbgRcntInc +#undef AuDbgRcntDec -+#undef AuDbgWcntInc +#undef AuDbgWcntDec + +#define AuSimpleLockRwsemFuncs(prefix, param, rwsem) \ @@ -24667,10 +24746,12 @@ diff -Nurp linux-4.4.3/fs/aufs/rwsem.h linux-4.4.3-aufs/fs/aufs/rwsem.h + +#endif /* __KERNEL__ */ +#endif /* __AUFS_RWSEM_H__ */ -diff -Nurp linux-4.4.3/fs/aufs/sbinfo.c linux-4.4.3-aufs/fs/aufs/sbinfo.c ---- linux-4.4.3/fs/aufs/sbinfo.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/sbinfo.c 2016-03-02 17:59:37.371498760 +0200 -@@ -0,0 +1,353 @@ +diff --git a/fs/aufs/sbinfo.c b/fs/aufs/sbinfo.c +new file mode 100644 +index 0000000..aa97059 +--- /dev/null ++++ b/fs/aufs/sbinfo.c +@@ -0,0 +1,340 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima + */ @@ -24693,21 +24774,21 @@ diff -Nurp linux-4.4.3/fs/aufs/sbinfo.c linux-4.4.3-aufs/fs/aufs/sbinfo.c + sbinfo = container_of(kobj, struct au_sbinfo, si_kobj); + for (i = 0; i < AuPlink_NHASH; i++) + AuDebugOn(!hlist_empty(&sbinfo->si_plink[i].head)); -+ AuDebugOn(atomic_read(&sbinfo->si_nowait.nw_len)); ++ au_nwt_fin(&sbinfo->si_nowait); + -+ AuDebugOn(!hlist_empty(&sbinfo->si_symlink.head)); ++ AuDebugOn(percpu_counter_sum(&sbinfo->si_ninodes)); ++ percpu_counter_destroy(&sbinfo->si_ninodes); ++ AuDebugOn(percpu_counter_sum(&sbinfo->si_nfiles)); ++ percpu_counter_destroy(&sbinfo->si_nfiles); + + au_rw_write_lock(&sbinfo->si_rwsem); + au_br_free(sbinfo); + au_rw_write_unlock(&sbinfo->si_rwsem); + -+ AuDebugOn(radix_tree_gang_lookup -+ (&sbinfo->au_si_pid.tree, (void **)&locked, -+ /*first_index*/PID_MAX_DEFAULT - 1, -+ /*max_items*/sizeof(locked)/sizeof(*locked))); -+ + kfree(sbinfo->si_branch); -+ kfree(sbinfo->au_si_pid.bitmap); ++ for (i = 0; i < AU_NPIDMAP; i++) ++ kfree(sbinfo->au_si_pid.pid_bitmap[i]); ++ mutex_destroy(&sbinfo->au_si_pid.pid_mtx); + mutex_destroy(&sbinfo->si_xib_mtx); + AuRwDestroy(&sbinfo->si_rwsem); + @@ -24718,25 +24799,16 @@ diff -Nurp linux-4.4.3/fs/aufs/sbinfo.c linux-4.4.3-aufs/fs/aufs/sbinfo.c +{ + int err, i; + struct au_sbinfo *sbinfo; -+ static struct lock_class_key aufs_si; + + err = -ENOMEM; + sbinfo = kzalloc(sizeof(*sbinfo), GFP_NOFS); + if (unlikely(!sbinfo)) + goto out; + -+ BUILD_BUG_ON(sizeof(unsigned long) != -+ sizeof(*sbinfo->au_si_pid.bitmap)); -+ sbinfo->au_si_pid.bitmap = kcalloc(BITS_TO_LONGS(PID_MAX_DEFAULT), -+ sizeof(*sbinfo->au_si_pid.bitmap), -+ GFP_NOFS); -+ if (unlikely(!sbinfo->au_si_pid.bitmap)) -+ goto out_sbinfo; -+ + /* will be reallocated separately */ + sbinfo->si_branch = kzalloc(sizeof(*sbinfo->si_branch), GFP_NOFS); + if (unlikely(!sbinfo->si_branch)) -+ goto out_pidmap; ++ goto out_sbinfo; + + err = sysaufs_si_init(sbinfo); + if (unlikely(err)) @@ -24744,14 +24816,12 @@ diff -Nurp linux-4.4.3/fs/aufs/sbinfo.c linux-4.4.3-aufs/fs/aufs/sbinfo.c + + au_nwt_init(&sbinfo->si_nowait); + au_rw_init_wlock(&sbinfo->si_rwsem); -+ au_rw_class(&sbinfo->si_rwsem, &aufs_si); -+ spin_lock_init(&sbinfo->au_si_pid.tree_lock); -+ INIT_RADIX_TREE(&sbinfo->au_si_pid.tree, GFP_ATOMIC | __GFP_NOFAIL); ++ mutex_init(&sbinfo->au_si_pid.pid_mtx); + -+ atomic_long_set(&sbinfo->si_ninodes, 0); -+ atomic_long_set(&sbinfo->si_nfiles, 0); ++ percpu_counter_init(&sbinfo->si_ninodes, 0, GFP_NOFS); ++ percpu_counter_init(&sbinfo->si_nfiles, 0, GFP_NOFS); + -+ sbinfo->si_bend = -1; ++ sbinfo->si_bbot = -1; + sbinfo->si_last_br_id = AUFS_BRANCH_MAX / 2; + + sbinfo->si_wbr_copyup = AuWbrCopyup_Def; @@ -24763,8 +24833,6 @@ diff -Nurp linux-4.4.3/fs/aufs/sbinfo.c linux-4.4.3-aufs/fs/aufs/sbinfo.c + + sbinfo->si_mntflags = au_opts_plink(AuOpt_Def); + -+ au_sphl_init(&sbinfo->si_symlink); -+ + sbinfo->si_xino_jiffy = jiffies; + sbinfo->si_xino_expire + = msecs_to_jiffies(AUFS_XINO_DEF_SEC * MSEC_PER_SEC); @@ -24797,8 +24865,6 @@ diff -Nurp linux-4.4.3/fs/aufs/sbinfo.c linux-4.4.3-aufs/fs/aufs/sbinfo.c + +out_br: + kfree(sbinfo->si_branch); -+out_pidmap: -+ kfree(sbinfo->au_si_pid.bitmap); +out_sbinfo: + kfree(sbinfo); +out: @@ -24813,7 +24879,7 @@ diff -Nurp linux-4.4.3/fs/aufs/sbinfo.c linux-4.4.3-aufs/fs/aufs/sbinfo.c + AuRwMustWriteLock(&sbinfo->si_rwsem); + + err = -ENOMEM; -+ sz = sizeof(*brp) * (sbinfo->si_bend + 1); ++ sz = sizeof(*brp) * (sbinfo->si_bbot + 1); + if (unlikely(!sz)) + sz = sizeof(*brp); + brp = au_kzrealloc(sbinfo->si_branch, sz, sizeof(*brp) * nbr, GFP_NOFS); @@ -24983,50 +25049,54 @@ diff -Nurp linux-4.4.3/fs/aufs/sbinfo.c linux-4.4.3-aufs/fs/aufs/sbinfo.c + +/* ---------------------------------------------------------------------- */ + -+int si_pid_test_slow(struct super_block *sb) ++static void si_pid_alloc(struct au_si_pid *au_si_pid, int idx) +{ -+ void *p; ++ unsigned long *p; + -+ rcu_read_lock(); -+ p = radix_tree_lookup(&au_sbi(sb)->au_si_pid.tree, current->pid); -+ rcu_read_unlock(); ++ BUILD_BUG_ON(sizeof(unsigned long) != ++ sizeof(*au_si_pid->pid_bitmap)); + -+ return (long)!!p; ++ mutex_lock(&au_si_pid->pid_mtx); ++ p = au_si_pid->pid_bitmap[idx]; ++ while (!p) { ++ /* ++ * bad approach. ++ * but keeping 'si_pid_set()' void is more important. ++ */ ++ p = kcalloc(BITS_TO_LONGS(AU_PIDSTEP), ++ sizeof(*au_si_pid->pid_bitmap), ++ GFP_NOFS); ++ if (p) ++ break; ++ cond_resched(); ++ } ++ au_si_pid->pid_bitmap[idx] = p; ++ mutex_unlock(&au_si_pid->pid_mtx); +} + -+void si_pid_set_slow(struct super_block *sb) ++void si_pid_set(struct super_block *sb) +{ -+ int err; -+ struct au_sbinfo *sbinfo; ++ pid_t bit; ++ int idx; ++ unsigned long *bitmap; ++ struct au_si_pid *au_si_pid; + -+ AuDebugOn(si_pid_test_slow(sb)); -+ -+ sbinfo = au_sbi(sb); -+ err = radix_tree_preload(GFP_NOFS | __GFP_NOFAIL); -+ AuDebugOn(err); -+ spin_lock(&sbinfo->au_si_pid.tree_lock); -+ err = radix_tree_insert(&sbinfo->au_si_pid.tree, current->pid, -+ /*any valid ptr*/sb); -+ spin_unlock(&sbinfo->au_si_pid.tree_lock); -+ AuDebugOn(err); -+ radix_tree_preload_end(); ++ si_pid_idx_bit(&idx, &bit); ++ au_si_pid = &au_sbi(sb)->au_si_pid; ++ bitmap = au_si_pid->pid_bitmap[idx]; ++ if (!bitmap) { ++ si_pid_alloc(au_si_pid, idx); ++ bitmap = au_si_pid->pid_bitmap[idx]; ++ } ++ AuDebugOn(test_bit(bit, bitmap)); ++ set_bit(bit, bitmap); ++ /* smp_mb(); */ +} -+ -+void si_pid_clr_slow(struct super_block *sb) -+{ -+ void *p; -+ struct au_sbinfo *sbinfo; -+ -+ AuDebugOn(!si_pid_test_slow(sb)); -+ -+ sbinfo = au_sbi(sb); -+ spin_lock(&sbinfo->au_si_pid.tree_lock); -+ p = radix_tree_delete(&sbinfo->au_si_pid.tree, current->pid); -+ spin_unlock(&sbinfo->au_si_pid.tree_lock); -+} -diff -Nurp linux-4.4.3/fs/aufs/spl.h linux-4.4.3-aufs/fs/aufs/spl.h ---- linux-4.4.3/fs/aufs/spl.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/spl.h 2016-03-02 17:59:37.371498760 +0200 +diff --git a/fs/aufs/spl.h b/fs/aufs/spl.h +new file mode 100644 +index 0000000..f9b5288 +--- /dev/null ++++ b/fs/aufs/spl.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima @@ -25126,9 +25196,11 @@ diff -Nurp linux-4.4.3/fs/aufs/spl.h linux-4.4.3-aufs/fs/aufs/spl.h + +#endif /* __KERNEL__ */ +#endif /* __AUFS_SPL_H__ */ -diff -Nurp linux-4.4.3/fs/aufs/super.c linux-4.4.3-aufs/fs/aufs/super.c ---- linux-4.4.3/fs/aufs/super.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/super.c 2016-03-02 17:59:37.372498767 +0200 +diff --git a/fs/aufs/super.c b/fs/aufs/super.c +new file mode 100644 +index 0000000..18190aa +--- /dev/null ++++ b/fs/aufs/super.c @@ -0,0 +1,1026 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima @@ -25155,7 +25227,6 @@ diff -Nurp linux-4.4.3/fs/aufs/super.c linux-4.4.3-aufs/fs/aufs/super.c + if (c) { + au_icntnr_init(c); + c->vfs_inode.i_version = 1; /* sigen(sb); */ -+ c->iinfo.ii_hinode = NULL; + return &c->vfs_inode; + } + return NULL; @@ -25171,7 +25242,8 @@ diff -Nurp linux-4.4.3/fs/aufs/super.c linux-4.4.3-aufs/fs/aufs/super.c + +static void aufs_destroy_inode(struct inode *inode) +{ -+ au_iinfo_fin(inode); ++ if (!is_bad_inode(inode)) ++ au_iinfo_fin(inode); + call_rcu(&inode->i_rcu, aufs_destroy_inode_cb); +} + @@ -25209,16 +25281,16 @@ diff -Nurp linux-4.4.3/fs/aufs/super.c linux-4.4.3-aufs/fs/aufs/super.c +static int au_show_brs(struct seq_file *seq, struct super_block *sb) +{ + int err; -+ aufs_bindex_t bindex, bend; ++ aufs_bindex_t bindex, bbot; + struct path path; + struct au_hdentry *hdp; + struct au_branch *br; + au_br_perm_str_t perm; + + err = 0; -+ bend = au_sbend(sb); ++ bbot = au_sbbot(sb); + hdp = au_di(sb->s_root)->di_hdentry; -+ for (bindex = 0; !err && bindex <= bend; bindex++) { ++ for (bindex = 0; !err && bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + path.mnt = au_br_mnt(br); + path.dentry = hdp[bindex].hd_dentry; @@ -25226,7 +25298,7 @@ diff -Nurp linux-4.4.3/fs/aufs/super.c linux-4.4.3-aufs/fs/aufs/super.c + if (!err) { + au_optstr_br_perm(&perm, br->br_perm); + seq_printf(seq, "=%s", perm.a); -+ if (bindex != bend) ++ if (bindex != bbot) + seq_putc(seq, ':'); + } + } @@ -25450,7 +25522,7 @@ diff -Nurp linux-4.4.3/fs/aufs/super.c linux-4.4.3-aufs/fs/aufs/super.c + int err; + long bsize, factor; + u64 blocks, bfree, bavail, files, ffree; -+ aufs_bindex_t bend, bindex, i; ++ aufs_bindex_t bbot, bindex, i; + unsigned char shared; + struct path h_path; + struct super_block *h_sb; @@ -25462,8 +25534,8 @@ diff -Nurp linux-4.4.3/fs/aufs/super.c linux-4.4.3-aufs/fs/aufs/super.c + blocks = 0; + bfree = 0; + bavail = 0; -+ bend = au_sbend(sb); -+ for (bindex = 0; bindex <= bend; bindex++) { ++ bbot = au_sbbot(sb); ++ for (bindex = 0; bindex <= bbot; bindex++) { + h_path.mnt = au_sbr_mnt(sb, bindex); + h_sb = h_path.mnt->mnt_sb; + shared = 0; @@ -25546,14 +25618,14 @@ diff -Nurp linux-4.4.3/fs/aufs/super.c linux-4.4.3-aufs/fs/aufs/super.c +static int aufs_sync_fs(struct super_block *sb, int wait) +{ + int err, e; -+ aufs_bindex_t bend, bindex; ++ aufs_bindex_t bbot, bindex; + struct au_branch *br; + struct super_block *h_sb; + + err = 0; + si_noflush_read_lock(sb); -+ bend = au_sbend(sb); -+ for (bindex = 0; bindex <= bend; bindex++) { ++ bbot = au_sbbot(sb); ++ for (bindex = 0; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + if (!au_br_writable(br->br_perm)) + continue; @@ -25636,7 +25708,7 @@ diff -Nurp linux-4.4.3/fs/aufs/super.c linux-4.4.3-aufs/fs/aufs/super.c + spin_lock(&sb->s_inode_list_lock); + list_for_each_entry(inode, head, i_sb_list) { + if (!is_bad_inode(inode) -+ && au_ii(inode)->ii_bstart >= 0) { ++ && au_ii(inode)->ii_btop >= 0) { + spin_lock(&inode->i_lock); + if (atomic_read(&inode->i_count)) { + au_igrab(inode); @@ -25654,7 +25726,7 @@ diff -Nurp linux-4.4.3/fs/aufs/super.c linux-4.4.3-aufs/fs/aufs/super.c + +struct inode **au_iarray_alloc(struct super_block *sb, unsigned long long *max) +{ -+ *max = atomic_long_read(&au_sbi(sb)->si_ninodes); ++ *max = au_ninodes(sb); + return au_array_alloc(max, au_iarray_cb, sb, &sb->s_inodes); +} + @@ -25813,7 +25885,7 @@ diff -Nurp linux-4.4.3/fs/aufs/super.c linux-4.4.3-aufs/fs/aufs/super.c +{ + int err, e; + unsigned int udba; -+ aufs_bindex_t bindex, bend; ++ aufs_bindex_t bindex, bbot; + struct dentry *root; + struct inode *inode; + struct au_branch *br; @@ -25829,8 +25901,8 @@ diff -Nurp linux-4.4.3/fs/aufs/super.c linux-4.4.3-aufs/fs/aufs/super.c + IiMustNoWaiters(inode); + + udba = au_opt_udba(sb); -+ bend = au_sbend(sb); -+ for (bindex = 0; bindex <= bend; bindex++) { ++ bbot = au_sbbot(sb); ++ for (bindex = 0; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + err = au_hnotify_reset_br(udba, br, br->br_perm); + if (unlikely(err)) @@ -25923,7 +25995,7 @@ diff -Nurp linux-4.4.3/fs/aufs/super.c linux-4.4.3-aufs/fs/aufs/super.c + + sbinfo = au_sbi(sb); + inode = d_inode(root); -+ mutex_lock(&inode->i_mutex); ++ inode_lock(inode); + err = si_write_lock(sb, AuLock_FLUSH | AuLock_NOPLM); + if (unlikely(err)) + goto out_mtx; @@ -25946,7 +26018,7 @@ diff -Nurp linux-4.4.3/fs/aufs/super.c linux-4.4.3-aufs/fs/aufs/super.c + aufs_write_unlock(root); + +out_mtx: -+ mutex_unlock(&inode->i_mutex); ++ inode_unlock(inode); +out_opts: + free_page((unsigned long)opts.opt); +out: @@ -26065,7 +26137,7 @@ diff -Nurp linux-4.4.3/fs/aufs/super.c linux-4.4.3-aufs/fs/aufs/super.c + goto out_root; + + /* lock vfs_inode first, then aufs. */ -+ mutex_lock(&inode->i_mutex); ++ inode_lock(inode); + aufs_write_lock(root); + err = au_opts_mount(sb, &opts); + au_opts_free(&opts); @@ -26077,7 +26149,7 @@ diff -Nurp linux-4.4.3/fs/aufs/super.c linux-4.4.3-aufs/fs/aufs/super.c + au_refresh_iop(inode, /*force_getattr*/0); + } + aufs_write_unlock(root); -+ mutex_unlock(&inode->i_mutex); ++ inode_unlock(inode); + if (!err) + goto out_opts; /* success */ + @@ -26156,10 +26228,12 @@ diff -Nurp linux-4.4.3/fs/aufs/super.c linux-4.4.3-aufs/fs/aufs/super.c + /* no need to __module_get() and module_put(). */ + .owner = THIS_MODULE, +}; -diff -Nurp linux-4.4.3/fs/aufs/super.h linux-4.4.3-aufs/fs/aufs/super.h ---- linux-4.4.3/fs/aufs/super.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/super.h 2016-03-02 17:59:37.372498767 +0200 -@@ -0,0 +1,628 @@ +diff --git a/fs/aufs/super.h b/fs/aufs/super.h +new file mode 100644 +index 0000000..b8ca149 +--- /dev/null ++++ b/fs/aufs/super.h +@@ -0,0 +1,625 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima + */ @@ -26207,14 +26281,6 @@ diff -Nurp linux-4.4.3/fs/aufs/super.h linux-4.4.3-aufs/fs/aufs/super.h + unsigned long long mfsrr_watermark; +}; + -+struct pseudo_link { -+ union { -+ struct hlist_node hlist; -+ struct rcu_head rcu; -+ }; -+ struct inode *inode; -+}; -+ +#define AuPlink_NHASH 100 +static inline int au_plink_hash(ino_t ino) +{ @@ -26236,6 +26302,13 @@ diff -Nurp linux-4.4.3/fs/aufs/super.h linux-4.4.3-aufs/fs/aufs/super.h +#endif +}; + ++#define AU_PIDSTEP (int)(BITS_TO_LONGS(PID_MAX_DEFAULT) * BITS_PER_LONG) ++#define AU_NPIDMAP (int)DIV_ROUND_UP(PID_MAX_LIMIT, AU_PIDSTEP) ++struct au_si_pid { ++ unsigned long *pid_bitmap[AU_NPIDMAP]; ++ struct mutex pid_mtx; ++}; ++ +struct au_branch; +struct au_sbinfo { + /* nowait tasks in the system-wide workqueue */ @@ -26248,17 +26321,13 @@ diff -Nurp linux-4.4.3/fs/aufs/super.h linux-4.4.3-aufs/fs/aufs/super.h + struct au_rwsem si_rwsem; + + /* prevent recursive locking in deleting inode */ -+ struct { -+ unsigned long *bitmap; -+ spinlock_t tree_lock; -+ struct radix_tree_root tree; -+ } au_si_pid; ++ struct au_si_pid au_si_pid; + + /* + * dirty approach to protect sb->sb_inodes and ->s_files (gone) from + * remount. + */ -+ atomic_long_t si_ninodes, si_nfiles; ++ struct percpu_counter si_ninodes, si_nfiles; + + /* branch management */ + unsigned int si_generation; @@ -26266,7 +26335,7 @@ diff -Nurp linux-4.4.3/fs/aufs/super.h linux-4.4.3-aufs/fs/aufs/super.h + /* see AuSi_ flags */ + unsigned char au_si_status; + -+ aufs_bindex_t si_bend; ++ aufs_bindex_t si_bbot; + + /* dirty trick to keep br_id plus */ + unsigned int si_last_br_id : @@ -26292,9 +26361,6 @@ diff -Nurp linux-4.4.3/fs/aufs/super.h linux-4.4.3-aufs/fs/aufs/super.h + /* include/asm-ia64/siginfo.h defines a macro named si_flags */ + unsigned int si_mntflags; + -+ /* symlink to follow_link() and put_link() */ -+ struct au_sphlhead si_symlink; -+ + /* external inode number (bitmap and translation table) */ + vfs_readf_t si_xread; + vfs_writef_t si_xwrite; @@ -26360,7 +26426,7 @@ diff -Nurp linux-4.4.3/fs/aufs/super.h linux-4.4.3-aufs/fs/aufs/super.h +#endif + +#ifdef CONFIG_AUFS_SBILIST -+ struct list_head si_list; ++ struct hlist_node si_list; +#endif + + /* dirty, necessary for unmounting, sysfs and sysrq */ @@ -26450,16 +26516,12 @@ diff -Nurp linux-4.4.3/fs/aufs/super.h linux-4.4.3-aufs/fs/aufs/super.h +int aufs_read_and_write_lock2(struct dentry *d1, struct dentry *d2, int flags); +void aufs_read_and_write_unlock2(struct dentry *d1, struct dentry *d2); + -+int si_pid_test_slow(struct super_block *sb); -+void si_pid_set_slow(struct super_block *sb); -+void si_pid_clr_slow(struct super_block *sb); -+ +/* wbr_policy.c */ +extern struct au_wbr_copyup_operations au_wbr_copyup_ops[]; +extern struct au_wbr_create_operations au_wbr_create_ops[]; +int au_cpdown_dirs(struct dentry *dentry, aufs_bindex_t bdst); +int au_wbr_nonopq(struct dentry *dentry, aufs_bindex_t bindex); -+int au_wbr_do_copyup_bu(struct dentry *dentry, aufs_bindex_t bstart); ++int au_wbr_do_copyup_bu(struct dentry *dentry, aufs_bindex_t btop); + +/* mvdown.c */ +int au_mvdown(struct dentry *dentry, struct aufs_mvdown __user *arg); @@ -26538,21 +26600,21 @@ diff -Nurp linux-4.4.3/fs/aufs/super.h linux-4.4.3-aufs/fs/aufs/super.h + +#ifdef CONFIG_AUFS_SBILIST +/* module.c */ -+extern struct au_splhead au_sbilist; ++extern struct au_sphlhead au_sbilist; + +static inline void au_sbilist_init(void) +{ -+ au_spl_init(&au_sbilist); ++ au_sphl_init(&au_sbilist); +} + +static inline void au_sbilist_add(struct super_block *sb) +{ -+ au_spl_add(&au_sbi(sb)->si_list, &au_sbilist); ++ au_sphl_add(&au_sbi(sb)->si_list, &au_sbilist); +} + +static inline void au_sbilist_del(struct super_block *sb) +{ -+ au_spl_del(&au_sbi(sb)->si_list, &au_sbilist); ++ au_sphl_del(&au_sbi(sb)->si_list, &au_sbilist); +} + +#ifdef CONFIG_AUFS_MAGIC_SYSRQ @@ -26601,48 +26663,43 @@ diff -Nurp linux-4.4.3/fs/aufs/super.h linux-4.4.3-aufs/fs/aufs/super.h + +/* ---------------------------------------------------------------------- */ + -+static inline pid_t si_pid_bit(void) ++static inline void si_pid_idx_bit(int *idx, pid_t *bit) +{ + /* the origin of pid is 1, but the bitmap's is 0 */ -+ return current->pid - 1; ++ *bit = current->pid - 1; ++ *idx = *bit / AU_PIDSTEP; ++ *bit %= AU_PIDSTEP; +} + +static inline int si_pid_test(struct super_block *sb) +{ + pid_t bit; ++ int idx; ++ unsigned long *bitmap; + -+ bit = si_pid_bit(); -+ if (bit < PID_MAX_DEFAULT) -+ return test_bit(bit, au_sbi(sb)->au_si_pid.bitmap); -+ return si_pid_test_slow(sb); -+} -+ -+static inline void si_pid_set(struct super_block *sb) -+{ -+ pid_t bit; -+ -+ bit = si_pid_bit(); -+ if (bit < PID_MAX_DEFAULT) { -+ AuDebugOn(test_bit(bit, au_sbi(sb)->au_si_pid.bitmap)); -+ set_bit(bit, au_sbi(sb)->au_si_pid.bitmap); -+ /* smp_mb(); */ -+ } else -+ si_pid_set_slow(sb); ++ si_pid_idx_bit(&idx, &bit); ++ bitmap = au_sbi(sb)->au_si_pid.pid_bitmap[idx]; ++ if (bitmap) ++ return test_bit(bit, bitmap); ++ return 0; +} + +static inline void si_pid_clr(struct super_block *sb) +{ + pid_t bit; ++ int idx; ++ unsigned long *bitmap; + -+ bit = si_pid_bit(); -+ if (bit < PID_MAX_DEFAULT) { -+ AuDebugOn(!test_bit(bit, au_sbi(sb)->au_si_pid.bitmap)); -+ clear_bit(bit, au_sbi(sb)->au_si_pid.bitmap); -+ /* smp_mb(); */ -+ } else -+ si_pid_clr_slow(sb); ++ si_pid_idx_bit(&idx, &bit); ++ bitmap = au_sbi(sb)->au_si_pid.pid_bitmap[idx]; ++ BUG_ON(!bitmap); ++ AuDebugOn(!test_bit(bit, bitmap)); ++ clear_bit(bit, bitmap); ++ /* smp_mb(); */ +} + ++void si_pid_set(struct super_block *sb); ++ +/* ---------------------------------------------------------------------- */ + +/* lock superblock. mainly for entry point functions */ @@ -26727,10 +26784,10 @@ diff -Nurp linux-4.4.3/fs/aufs/super.h linux-4.4.3-aufs/fs/aufs/super.h + +/* ---------------------------------------------------------------------- */ + -+static inline aufs_bindex_t au_sbend(struct super_block *sb) ++static inline aufs_bindex_t au_sbbot(struct super_block *sb) +{ + SiMustAnyLock(sb); -+ return au_sbi(sb)->si_bend; ++ return au_sbi(sb)->si_bbot; +} + +static inline unsigned int au_mntflags(struct super_block *sb) @@ -26745,26 +26802,40 @@ diff -Nurp linux-4.4.3/fs/aufs/super.h linux-4.4.3-aufs/fs/aufs/super.h + return au_sbi(sb)->si_generation; +} + ++static inline unsigned long long au_ninodes(struct super_block *sb) ++{ ++ s64 n = percpu_counter_sum(&au_sbi(sb)->si_ninodes); ++ ++ BUG_ON(n < 0); ++ return n; ++} ++ +static inline void au_ninodes_inc(struct super_block *sb) +{ -+ atomic_long_inc(&au_sbi(sb)->si_ninodes); ++ percpu_counter_inc(&au_sbi(sb)->si_ninodes); +} + +static inline void au_ninodes_dec(struct super_block *sb) +{ -+ AuDebugOn(!atomic_long_read(&au_sbi(sb)->si_ninodes)); -+ atomic_long_dec(&au_sbi(sb)->si_ninodes); ++ percpu_counter_dec(&au_sbi(sb)->si_ninodes); ++} ++ ++static inline unsigned long long au_nfiles(struct super_block *sb) ++{ ++ s64 n = percpu_counter_sum(&au_sbi(sb)->si_nfiles); ++ ++ BUG_ON(n < 0); ++ return n; +} + +static inline void au_nfiles_inc(struct super_block *sb) +{ -+ atomic_long_inc(&au_sbi(sb)->si_nfiles); ++ percpu_counter_inc(&au_sbi(sb)->si_nfiles); +} + +static inline void au_nfiles_dec(struct super_block *sb) +{ -+ AuDebugOn(!atomic_long_read(&au_sbi(sb)->si_nfiles)); -+ atomic_long_dec(&au_sbi(sb)->si_nfiles); ++ percpu_counter_dec(&au_sbi(sb)->si_nfiles); +} + +static inline struct au_branch *au_sbr(struct super_block *sb, @@ -26788,9 +26859,11 @@ diff -Nurp linux-4.4.3/fs/aufs/super.h linux-4.4.3-aufs/fs/aufs/super.h + +#endif /* __KERNEL__ */ +#endif /* __AUFS_SUPER_H__ */ -diff -Nurp linux-4.4.3/fs/aufs/sysaufs.c linux-4.4.3-aufs/fs/aufs/sysaufs.c ---- linux-4.4.3/fs/aufs/sysaufs.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/sysaufs.c 2016-03-02 17:59:37.372498767 +0200 +diff --git a/fs/aufs/sysaufs.c b/fs/aufs/sysaufs.c +new file mode 100644 +index 0000000..8ec10fb3 +--- /dev/null ++++ b/fs/aufs/sysaufs.c @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima @@ -26883,9 +26956,11 @@ diff -Nurp linux-4.4.3/fs/aufs/sysaufs.c linux-4.4.3-aufs/fs/aufs/sysaufs.c +out: + return err; +} -diff -Nurp linux-4.4.3/fs/aufs/sysaufs.h linux-4.4.3-aufs/fs/aufs/sysaufs.h ---- linux-4.4.3/fs/aufs/sysaufs.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/sysaufs.h 2016-03-02 17:59:37.372498767 +0200 +diff --git a/fs/aufs/sysaufs.h b/fs/aufs/sysaufs.h +new file mode 100644 +index 0000000..1f79983 +--- /dev/null ++++ b/fs/aufs/sysaufs.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima @@ -26975,9 +27050,11 @@ diff -Nurp linux-4.4.3/fs/aufs/sysaufs.h linux-4.4.3-aufs/fs/aufs/sysaufs.h + +#endif /* __KERNEL__ */ +#endif /* __SYSAUFS_H__ */ -diff -Nurp linux-4.4.3/fs/aufs/sysfs.c linux-4.4.3-aufs/fs/aufs/sysfs.c ---- linux-4.4.3/fs/aufs/sysfs.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/sysfs.c 2016-03-02 17:59:37.372498767 +0200 +diff --git a/fs/aufs/sysfs.c b/fs/aufs/sysfs.c +new file mode 100644 +index 0000000..0efb77a +--- /dev/null ++++ b/fs/aufs/sysfs.c @@ -0,0 +1,340 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima @@ -27087,7 +27164,7 @@ diff -Nurp linux-4.4.3/fs/aufs/sysfs.c linux-4.4.3-aufs/fs/aufs/sysfs.c + ssize_t err; + int idx; + long l; -+ aufs_bindex_t bend; ++ aufs_bindex_t bbot; + struct au_sbinfo *sbinfo; + struct super_block *sb; + struct seq_file *seq; @@ -27140,8 +27217,8 @@ diff -Nurp linux-4.4.3/fs/aufs/sysfs.c linux-4.4.3-aufs/fs/aufs/sysfs.c + + err = kstrtol(name, 10, &l); + if (!err) { -+ bend = au_sbend(sb); -+ if (l <= bend) ++ bbot = au_sbbot(sb); ++ if (l <= bbot) + err = sysaufs_si_br(seq, sb, (aufs_bindex_t)l, idx); + else + err = -ENOENT; @@ -27167,15 +27244,15 @@ diff -Nurp linux-4.4.3/fs/aufs/sysfs.c linux-4.4.3-aufs/fs/aufs/sysfs.c +{ + int err; + int16_t brid; -+ aufs_bindex_t bindex, bend; ++ aufs_bindex_t bindex, bbot; + size_t sz; + char *buf; + struct seq_file *seq; + struct au_branch *br; + + si_read_lock(sb, AuLock_FLUSH); -+ bend = au_sbend(sb); -+ err = bend + 1; ++ bbot = au_sbbot(sb); ++ err = bbot + 1; + if (!arg) + goto out; + @@ -27190,7 +27267,7 @@ diff -Nurp linux-4.4.3/fs/aufs/sysfs.c linux-4.4.3-aufs/fs/aufs/sysfs.c + goto out_buf; + + sz = sizeof(*arg) - offsetof(union aufs_brinfo, path); -+ for (bindex = 0; bindex <= bend; bindex++, arg++) { ++ for (bindex = 0; bindex <= bbot; bindex++, arg++) { + err = !access_ok(VERIFY_WRITE, arg, sizeof(*arg)); + if (unlikely(err)) + break; @@ -27269,7 +27346,7 @@ diff -Nurp linux-4.4.3/fs/aufs/sysfs.c linux-4.4.3-aufs/fs/aufs/sysfs.c + struct kobject *kobj; + struct au_brsysfs *br_sysfs; + int i; -+ aufs_bindex_t bend; ++ aufs_bindex_t bbot; + + dbgaufs_brs_del(sb, bindex); + @@ -27277,8 +27354,8 @@ diff -Nurp linux-4.4.3/fs/aufs/sysfs.c linux-4.4.3-aufs/fs/aufs/sysfs.c + return; + + kobj = &au_sbi(sb)->si_kobj; -+ bend = au_sbend(sb); -+ for (; bindex <= bend; bindex++) { ++ bbot = au_sbbot(sb); ++ for (; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + br_sysfs = br->br_sysfs; + for (i = 0; i < ARRAY_SIZE(br->br_sysfs); i++) { @@ -27291,7 +27368,7 @@ diff -Nurp linux-4.4.3/fs/aufs/sysfs.c linux-4.4.3-aufs/fs/aufs/sysfs.c +void sysaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex) +{ + int err, i; -+ aufs_bindex_t bend; ++ aufs_bindex_t bbot; + struct kobject *kobj; + struct au_branch *br; + struct au_brsysfs *br_sysfs; @@ -27302,8 +27379,8 @@ diff -Nurp linux-4.4.3/fs/aufs/sysfs.c linux-4.4.3-aufs/fs/aufs/sysfs.c + return; + + kobj = &au_sbi(sb)->si_kobj; -+ bend = au_sbend(sb); -+ for (; bindex <= bend; bindex++) { ++ bbot = au_sbbot(sb); ++ for (; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + br_sysfs = br->br_sysfs; + snprintf(br_sysfs[AuBrSysfs_BR].name, sizeof(br_sysfs->name), @@ -27319,9 +27396,11 @@ diff -Nurp linux-4.4.3/fs/aufs/sysfs.c linux-4.4.3-aufs/fs/aufs/sysfs.c + } + } +} -diff -Nurp linux-4.4.3/fs/aufs/sysrq.c linux-4.4.3-aufs/fs/aufs/sysrq.c ---- linux-4.4.3/fs/aufs/sysrq.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/sysrq.c 2016-03-02 17:59:37.372498767 +0200 +diff --git a/fs/aufs/sysrq.c b/fs/aufs/sysrq.c +new file mode 100644 +index 0000000..7d22979 +--- /dev/null ++++ b/fs/aufs/sysrq.c @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima @@ -27430,7 +27509,7 @@ diff -Nurp linux-4.4.3/fs/aufs/sysrq.c linux-4.4.3-aufs/fs/aufs/sysrq.c + + lockdep_off(); + au_sbilist_lock(); -+ list_for_each_entry(sbinfo, &au_sbilist.head, si_list) ++ hlist_for_each_entry(sbinfo, &au_sbilist.head, si_list) + sysrq_sb(sbinfo->si_sb); + au_sbilist_unlock(); + lockdep_on(); @@ -27467,9 +27546,11 @@ diff -Nurp linux-4.4.3/fs/aufs/sysrq.c linux-4.4.3-aufs/fs/aufs/sysrq.c + if (unlikely(err)) + pr_err("err %d (ignored)\n", err); +} -diff -Nurp linux-4.4.3/fs/aufs/vdir.c linux-4.4.3-aufs/fs/aufs/vdir.c ---- linux-4.4.3/fs/aufs/vdir.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/vdir.c 2016-03-02 17:59:37.372498767 +0200 +diff --git a/fs/aufs/vdir.c b/fs/aufs/vdir.c +new file mode 100644 +index 0000000..feddcc2 +--- /dev/null ++++ b/fs/aufs/vdir.c @@ -0,0 +1,875 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima @@ -28011,7 +28092,7 @@ diff -Nurp linux-4.4.3/fs/aufs/vdir.c linux-4.4.3-aufs/fs/aufs/vdir.c + int err; + unsigned int rdhash; + loff_t offset; -+ aufs_bindex_t bend, bindex, bstart; ++ aufs_bindex_t bbot, bindex, btop; + unsigned char shwh; + struct file *hf, *file; + struct super_block *sb; @@ -28037,9 +28118,9 @@ diff -Nurp linux-4.4.3/fs/aufs/vdir.c linux-4.4.3-aufs/fs/aufs/vdir.c + shwh = 1; + au_fset_fillvdir(arg->flags, SHWH); + } -+ bstart = au_fbstart(file); -+ bend = au_fbend_dir(file); -+ for (bindex = bstart; !err && bindex <= bend; bindex++) { ++ btop = au_fbtop(file); ++ bbot = au_fbbot_dir(file); ++ for (bindex = btop; !err && bindex <= bbot; bindex++) { + hf = au_hf_dir(file, bindex); + if (!hf) + continue; @@ -28052,7 +28133,7 @@ diff -Nurp linux-4.4.3/fs/aufs/vdir.c linux-4.4.3-aufs/fs/aufs/vdir.c + arg->bindex = bindex; + au_fclr_fillvdir(arg->flags, WHABLE); + if (shwh -+ || (bindex != bend ++ || (bindex != bbot + && au_br_whable(au_sbr_perm(sb, bindex)))) + au_fset_fillvdir(arg->flags, WHABLE); + do { @@ -28346,10 +28427,12 @@ diff -Nurp linux-4.4.3/fs/aufs/vdir.c linux-4.4.3-aufs/fs/aufs/vdir.c + /* smp_mb(); */ + return 0; +} -diff -Nurp linux-4.4.3/fs/aufs/vfsub.c linux-4.4.3-aufs/fs/aufs/vfsub.c ---- linux-4.4.3/fs/aufs/vfsub.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/vfsub.c 2016-03-02 17:59:37.373498774 +0200 -@@ -0,0 +1,853 @@ +diff --git a/fs/aufs/vfsub.c b/fs/aufs/vfsub.c +new file mode 100644 +index 0000000..2e54bad +--- /dev/null ++++ b/fs/aufs/vfsub.c +@@ -0,0 +1,871 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima + */ @@ -28477,7 +28560,7 @@ diff -Nurp linux-4.4.3/fs/aufs/vfsub.c linux-4.4.3-aufs/fs/aufs/vfsub.c + goto out; + } + -+ atomic_inc(&br->br_count); ++ au_br_get(br); + fsnotify_open(file); + +out: @@ -28494,6 +28577,24 @@ diff -Nurp linux-4.4.3/fs/aufs/vfsub.c linux-4.4.3-aufs/fs/aufs/vfsub.c + return err; +} + ++struct dentry *vfsub_lookup_one_len_unlocked(const char *name, ++ struct dentry *parent, int len) ++{ ++ struct path path = { ++ .mnt = NULL ++ }; ++ ++ path.dentry = lookup_one_len_unlocked(name, parent, len); ++ if (IS_ERR(path.dentry)) ++ goto out; ++ if (d_is_positive(path.dentry)) ++ vfsub_update_h_iattr(&path, /*did*/NULL); /*ignore*/ ++ ++out: ++ AuTraceErrPtr(path.dentry); ++ return path.dentry; ++} ++ +struct dentry *vfsub_lookup_one_len(const char *name, struct dentry *parent, + int len) +{ @@ -29203,10 +29304,12 @@ diff -Nurp linux-4.4.3/fs/aufs/vfsub.c linux-4.4.3-aufs/fs/aufs/vfsub.c + + return err; +} -diff -Nurp linux-4.4.3/fs/aufs/vfsub.h linux-4.4.3-aufs/fs/aufs/vfsub.h ---- linux-4.4.3/fs/aufs/vfsub.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/vfsub.h 2016-03-02 17:59:37.373498774 +0200 -@@ -0,0 +1,295 @@ +diff --git a/fs/aufs/vfsub.h b/fs/aufs/vfsub.h +new file mode 100644 +index 0000000..4e08d33 +--- /dev/null ++++ b/fs/aufs/vfsub.h +@@ -0,0 +1,297 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima + */ @@ -29248,7 +29351,7 @@ diff -Nurp linux-4.4.3/fs/aufs/vfsub.h linux-4.4.3-aufs/fs/aufs/vfsub.h + +/* to debug easier, do not make them inlined functions */ +#define MtxMustLock(mtx) AuDebugOn(!mutex_is_locked(mtx)) -+#define IMustLock(i) MtxMustLock(&(i)->i_mutex) ++#define IMustLock(i) AuDebugOn(!inode_is_locked(i)) + +/* ---------------------------------------------------------------------- */ + @@ -29295,6 +29398,8 @@ diff -Nurp linux-4.4.3/fs/aufs/vfsub.h linux-4.4.3-aufs/fs/aufs/vfsub.h + struct vfsub_aopen_args *args, struct au_branch *br); +int vfsub_kern_path(const char *name, unsigned int flags, struct path *path); + ++struct dentry *vfsub_lookup_one_len_unlocked(const char *name, ++ struct dentry *parent, int len); +struct dentry *vfsub_lookup_one_len(const char *name, struct dentry *parent, + int len); + @@ -29502,9 +29607,11 @@ diff -Nurp linux-4.4.3/fs/aufs/vfsub.h linux-4.4.3-aufs/fs/aufs/vfsub.h + +#endif /* __KERNEL__ */ +#endif /* __AUFS_VFSUB_H__ */ -diff -Nurp linux-4.4.3/fs/aufs/wbr_policy.c linux-4.4.3-aufs/fs/aufs/wbr_policy.c ---- linux-4.4.3/fs/aufs/wbr_policy.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/wbr_policy.c 2016-03-02 17:59:37.373498774 +0200 +diff --git a/fs/aufs/wbr_policy.c b/fs/aufs/wbr_policy.c +new file mode 100644 +index 0000000..91a010a +--- /dev/null ++++ b/fs/aufs/wbr_policy.c @@ -0,0 +1,752 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima @@ -29602,13 +29709,13 @@ diff -Nurp linux-4.4.3/fs/aufs/wbr_policy.c linux-4.4.3-aufs/fs/aufs/wbr_policy. + struct dentry *h_parent, void *arg) +{ + int err, rerr; -+ aufs_bindex_t bopq, bstart; ++ aufs_bindex_t bopq, btop; + struct path h_path; + struct dentry *parent; + struct inode *h_dir, *h_inode, *inode, *dir; + unsigned int *flags = arg; + -+ bstart = au_dbstart(dentry); ++ btop = au_dbtop(dentry); + /* dentry is di-locked */ + parent = dget_parent(dentry); + dir = d_inode(parent); @@ -29635,17 +29742,17 @@ diff -Nurp linux-4.4.3/fs/aufs/wbr_policy.c linux-4.4.3-aufs/fs/aufs/wbr_policy. + if (!au_ftest_cpdown(*flags, PARENT_OPQ) && bopq <= bdst) + au_fset_cpdown(*flags, PARENT_OPQ); + h_inode = d_inode(h_path.dentry); -+ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); ++ inode_lock_nested(h_inode, AuLsc_I_CHILD); + if (au_ftest_cpdown(*flags, WHED)) { + err = au_cpdown_dir_opq(dentry, bdst, flags); + if (unlikely(err)) { -+ mutex_unlock(&h_inode->i_mutex); ++ inode_unlock(h_inode); + goto out_dir; + } + } + -+ err = au_cpdown_attr(&h_path, au_h_dptr(dentry, bstart)); -+ mutex_unlock(&h_inode->i_mutex); ++ err = au_cpdown_attr(&h_path, au_h_dptr(dentry, btop)); ++ inode_unlock(h_inode); + if (unlikely(err)) + goto out_opq; + @@ -29656,8 +29763,8 @@ diff -Nurp linux-4.4.3/fs/aufs/wbr_policy.c linux-4.4.3-aufs/fs/aufs/wbr_policy. + } + + inode = d_inode(dentry); -+ if (au_ibend(inode) < bdst) -+ au_set_ibend(inode, bdst); ++ if (au_ibbot(inode) < bdst) ++ au_set_ibbot(inode, bdst); + au_set_h_iptr(inode, bdst, au_igrab(h_inode), + au_hi_flags(inode, /*isdir*/1)); + au_fhsm_wrote(dentry->d_sb, bdst, /*force*/0); @@ -29666,9 +29773,9 @@ diff -Nurp linux-4.4.3/fs/aufs/wbr_policy.c linux-4.4.3-aufs/fs/aufs/wbr_policy. + /* revert */ +out_opq: + if (au_ftest_cpdown(*flags, DIROPQ)) { -+ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); ++ inode_lock_nested(h_inode, AuLsc_I_CHILD); + rerr = au_diropq_remove(dentry, bdst); -+ mutex_unlock(&h_inode->i_mutex); ++ inode_unlock(h_inode); + if (unlikely(rerr)) { + AuIOErr("failed removing diropq for %pd b%d (%d)\n", + dentry, bdst, rerr); @@ -29687,8 +29794,8 @@ diff -Nurp linux-4.4.3/fs/aufs/wbr_policy.c linux-4.4.3-aufs/fs/aufs/wbr_policy. + } +out_put: + au_set_h_dptr(dentry, bdst, NULL); -+ if (au_dbend(dentry) == bdst) -+ au_update_dbend(dentry); ++ if (au_dbbot(dentry) == bdst) ++ au_update_dbbot(dentry); +out: + dput(parent); + return err; @@ -29760,19 +29867,19 @@ diff -Nurp linux-4.4.3/fs/aufs/wbr_policy.c linux-4.4.3-aufs/fs/aufs/wbr_policy. + unsigned int flags __maybe_unused) +{ + int err; -+ aufs_bindex_t bstart, bindex; ++ aufs_bindex_t btop, bindex; + struct super_block *sb; + struct dentry *parent, *h_parent; + + sb = dentry->d_sb; -+ bstart = au_dbstart(dentry); -+ err = bstart; -+ if (!au_br_rdonly(au_sbr(sb, bstart))) ++ btop = au_dbtop(dentry); ++ err = btop; ++ if (!au_br_rdonly(au_sbr(sb, btop))) + goto out; + + err = -EROFS; + parent = dget_parent(dentry); -+ for (bindex = au_dbstart(parent); bindex < bstart; bindex++) { ++ for (bindex = au_dbtop(parent); bindex < btop; bindex++) { + h_parent = au_h_dptr(parent, bindex); + if (!h_parent || d_is_negative(h_parent)) + continue; @@ -29786,7 +29893,7 @@ diff -Nurp linux-4.4.3/fs/aufs/wbr_policy.c linux-4.4.3-aufs/fs/aufs/wbr_policy. + + /* bottom up here */ + if (unlikely(err < 0)) { -+ err = au_wbr_bu(sb, bstart - 1); ++ err = au_wbr_bu(sb, btop - 1); + if (err >= 0) + err = au_wbr_nonopq(dentry, err); + } @@ -29838,7 +29945,7 @@ diff -Nurp linux-4.4.3/fs/aufs/wbr_policy.c linux-4.4.3-aufs/fs/aufs/wbr_policy. +{ + int err; + -+ err = au_wbr_bu(sb, au_sbend(sb)); ++ err = au_wbr_bu(sb, au_sbbot(sb)); + atomic_set(&au_sbi(sb)->si_wbr_rr_next, -err); /* less important */ + /* smp_mb(); */ + @@ -29850,7 +29957,7 @@ diff -Nurp linux-4.4.3/fs/aufs/wbr_policy.c linux-4.4.3-aufs/fs/aufs/wbr_policy. +{ + int err, nbr; + unsigned int u; -+ aufs_bindex_t bindex, bend; ++ aufs_bindex_t bindex, bbot; + struct super_block *sb; + atomic_t *next; + @@ -29860,9 +29967,9 @@ diff -Nurp linux-4.4.3/fs/aufs/wbr_policy.c linux-4.4.3-aufs/fs/aufs/wbr_policy. + + sb = dentry->d_sb; + next = &au_sbi(sb)->si_wbr_rr_next; -+ bend = au_sbend(sb); -+ nbr = bend + 1; -+ for (bindex = 0; bindex <= bend; bindex++) { ++ bbot = au_sbbot(sb); ++ nbr = bbot + 1; ++ for (bindex = 0; bindex <= bbot; bindex++) { + if (!au_ftest_wbr(flags, DIR)) { + err = atomic_dec_return(next) + 1; + /* modulo for 0 is meaningless */ @@ -29896,7 +30003,7 @@ diff -Nurp linux-4.4.3/fs/aufs/wbr_policy.c linux-4.4.3-aufs/fs/aufs/wbr_policy. + struct au_branch *br; + struct au_wbr_mfs *mfs; + struct dentry *h_parent; -+ aufs_bindex_t bindex, bend; ++ aufs_bindex_t bindex, bbot; + int err; + unsigned long long b, bavail; + struct path h_path; @@ -29917,13 +30024,13 @@ diff -Nurp linux-4.4.3/fs/aufs/wbr_policy.c linux-4.4.3-aufs/fs/aufs/wbr_policy. + mfs->mfsrr_bytes = 0; + if (!parent) { + bindex = 0; -+ bend = au_sbend(sb); ++ bbot = au_sbbot(sb); + } else { -+ bindex = au_dbstart(parent); -+ bend = au_dbtaildir(parent); ++ bindex = au_dbtop(parent); ++ bbot = au_dbtaildir(parent); + } + -+ for (; bindex <= bend; bindex++) { ++ for (; bindex <= bbot; bindex++) { + if (parent) { + h_parent = au_h_dptr(parent, bindex); + if (!h_parent || d_is_negative(h_parent)) @@ -30048,7 +30155,7 @@ diff -Nurp linux-4.4.3/fs/aufs/wbr_policy.c linux-4.4.3-aufs/fs/aufs/wbr_policy. +{ + int err, e2; + unsigned long long b; -+ aufs_bindex_t bindex, bstart, bend; ++ aufs_bindex_t bindex, btop, bbot; + struct super_block *sb; + struct dentry *parent, *h_parent; + struct au_branch *br; @@ -30057,9 +30164,9 @@ diff -Nurp linux-4.4.3/fs/aufs/wbr_policy.c linux-4.4.3-aufs/fs/aufs/wbr_policy. + if (unlikely(err < 0)) + goto out; + parent = dget_parent(dentry); -+ bstart = au_dbstart(parent); -+ bend = au_dbtaildir(parent); -+ if (bstart == bend) ++ btop = au_dbtop(parent); ++ bbot = au_dbtaildir(parent); ++ if (btop == bbot) + goto out_parent; /* success */ + + e2 = au_wbr_create_mfs(dentry, flags); @@ -30072,7 +30179,7 @@ diff -Nurp linux-4.4.3/fs/aufs/wbr_policy.c linux-4.4.3-aufs/fs/aufs/wbr_policy. + b = br->br_wbr->wbr_bytes; + AuDbg("b%d, %llu\n", err, b); + -+ for (bindex = bstart; bindex <= bend; bindex++) { ++ for (bindex = btop; bindex <= bbot; bindex++) { + h_parent = au_h_dptr(parent, bindex); + if (!h_parent || d_is_negative(h_parent)) + continue; @@ -30143,15 +30250,15 @@ diff -Nurp linux-4.4.3/fs/aufs/wbr_policy.c linux-4.4.3-aufs/fs/aufs/wbr_policy. +static int au_wbr_copyup_bup(struct dentry *dentry) +{ + int err; -+ aufs_bindex_t bindex, bstart; ++ aufs_bindex_t bindex, btop; + struct dentry *parent, *h_parent; + struct super_block *sb; + + err = -EROFS; + sb = dentry->d_sb; + parent = dget_parent(dentry); -+ bstart = au_dbstart(parent); -+ for (bindex = au_dbstart(dentry); bindex >= bstart; bindex--) { ++ btop = au_dbtop(parent); ++ for (bindex = au_dbtop(dentry); bindex >= btop; bindex--) { + h_parent = au_h_dptr(parent, bindex); + if (!h_parent || d_is_negative(h_parent)) + continue; @@ -30165,20 +30272,20 @@ diff -Nurp linux-4.4.3/fs/aufs/wbr_policy.c linux-4.4.3-aufs/fs/aufs/wbr_policy. + + /* bottom up here */ + if (unlikely(err < 0)) -+ err = au_wbr_bu(sb, bstart - 1); ++ err = au_wbr_bu(sb, btop - 1); + + AuDbg("b%d\n", err); + return err; +} + +/* bottom up */ -+int au_wbr_do_copyup_bu(struct dentry *dentry, aufs_bindex_t bstart) ++int au_wbr_do_copyup_bu(struct dentry *dentry, aufs_bindex_t btop) +{ + int err; + -+ err = au_wbr_bu(dentry->d_sb, bstart); ++ err = au_wbr_bu(dentry->d_sb, btop); + AuDbg("b%d\n", err); -+ if (err > bstart) ++ if (err > btop) + err = au_wbr_nonopq(dentry, err); + + AuDbg("b%d\n", err); @@ -30188,10 +30295,10 @@ diff -Nurp linux-4.4.3/fs/aufs/wbr_policy.c linux-4.4.3-aufs/fs/aufs/wbr_policy. +static int au_wbr_copyup_bu(struct dentry *dentry) +{ + int err; -+ aufs_bindex_t bstart; ++ aufs_bindex_t btop; + -+ bstart = au_dbstart(dentry); -+ err = au_wbr_do_copyup_bu(dentry, bstart); ++ btop = au_dbtop(dentry); ++ err = au_wbr_do_copyup_bu(dentry, btop); + return err; +} + @@ -30258,9 +30365,11 @@ diff -Nurp linux-4.4.3/fs/aufs/wbr_policy.c linux-4.4.3-aufs/fs/aufs/wbr_policy. + .fin = au_wbr_create_fin_mfs + } +}; -diff -Nurp linux-4.4.3/fs/aufs/whout.c linux-4.4.3-aufs/fs/aufs/whout.c ---- linux-4.4.3/fs/aufs/whout.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/whout.c 2016-03-02 17:59:37.374498781 +0200 +diff --git a/fs/aufs/whout.c b/fs/aufs/whout.c +new file mode 100644 +index 0000000..78f97ed +--- /dev/null ++++ b/fs/aufs/whout.c @@ -0,0 +1,1047 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima @@ -30849,7 +30958,7 @@ diff -Nurp linux-4.4.3/fs/aufs/whout.c linux-4.4.3-aufs/fs/aufs/whout.c +out: + if (wbr) + atomic_dec(&wbr->wbr_wh_running); -+ atomic_dec(&a->br->br_count); ++ au_br_put(a->br); + si_write_unlock(a->sb); + au_nwt_done(&au_sbi(a->sb)->si_nowait); + kfree(arg); @@ -30875,11 +30984,11 @@ diff -Nurp linux-4.4.3/fs/aufs/whout.c linux-4.4.3-aufs/fs/aufs/whout.c + */ + arg->sb = sb; + arg->br = br; -+ atomic_inc(&br->br_count); ++ au_br_get(br); + wkq_err = au_wkq_nowait(reinit_br_wh, arg, sb, /*flags*/0); + if (unlikely(wkq_err)) { + atomic_dec(&br->br_wbr->wbr_wh_running); -+ atomic_dec(&br->br_count); ++ au_br_put(br); + kfree(arg); + } + do_dec = 0; @@ -31168,7 +31277,7 @@ diff -Nurp linux-4.4.3/fs/aufs/whout.c linux-4.4.3-aufs/fs/aufs/whout.c +void au_whtmp_rmdir_free(struct au_whtmp_rmdir *whtmp) +{ + if (whtmp->br) -+ atomic_dec(&whtmp->br->br_count); ++ au_br_put(whtmp->br); + dput(whtmp->wh_dentry); + iput(whtmp->dir); + au_nhash_wh_free(&whtmp->whlist); @@ -31193,7 +31302,7 @@ diff -Nurp linux-4.4.3/fs/aufs/whout.c linux-4.4.3-aufs/fs/aufs/whout.c + + br = au_sbr(dir->i_sb, bindex); + wh_inode = d_inode(wh_dentry); -+ mutex_lock_nested(&wh_inode->i_mutex, AuLsc_I_CHILD); ++ inode_lock_nested(wh_inode, AuLsc_I_CHILD); + + /* + * someone else might change some whiteouts while we were sleeping. @@ -31215,7 +31324,7 @@ diff -Nurp linux-4.4.3/fs/aufs/whout.c linux-4.4.3-aufs/fs/aufs/whout.c + if (unlikely(wkq_err)) + err = wkq_err; + } -+ mutex_unlock(&wh_inode->i_mutex); ++ inode_unlock(wh_inode); + + if (!err) { + h_tmp.dentry = wh_dentry; @@ -31227,7 +31336,7 @@ diff -Nurp linux-4.4.3/fs/aufs/whout.c linux-4.4.3-aufs/fs/aufs/whout.c + } + + if (!err) { -+ if (au_ibstart(dir) == bindex) { ++ if (au_ibtop(dir) == bindex) { + /* todo: dir->i_mutex is necessary */ + au_cpup_attr_timesizes(dir); + if (h_nlink) @@ -31251,7 +31360,7 @@ diff -Nurp linux-4.4.3/fs/aufs/whout.c linux-4.4.3-aufs/fs/aufs/whout.c + struct au_hinode *hdir; + + /* rmdir by nfsd may cause deadlock with this i_mutex */ -+ /* mutex_lock(&a->dir->i_mutex); */ ++ /* inode_lock(a->dir); */ + err = -EROFS; + sb = a->dir->i_sb; + si_read_lock(sb, !AuLock_FLUSH); @@ -31281,7 +31390,7 @@ diff -Nurp linux-4.4.3/fs/aufs/whout.c linux-4.4.3-aufs/fs/aufs/whout.c + dput(h_parent); + ii_write_unlock(a->dir); +out: -+ /* mutex_unlock(&a->dir->i_mutex); */ ++ /* inode_unlock(a->dir); */ + au_whtmp_rmdir_free(a); + si_read_unlock(sb); + au_nwt_done(&au_sbi(sb)->si_nowait); @@ -31301,7 +31410,7 @@ diff -Nurp linux-4.4.3/fs/aufs/whout.c linux-4.4.3-aufs/fs/aufs/whout.c + sb = dir->i_sb; + args->dir = au_igrab(dir); + args->br = au_sbr(sb, bindex); -+ atomic_inc(&args->br->br_count); ++ au_br_get(args->br); + args->wh_dentry = dget(wh_dentry); + wkq_err = au_wkq_nowait(call_rmdir_whtmp, args, sb, /*flags*/0); + if (unlikely(wkq_err)) { @@ -31309,9 +31418,11 @@ diff -Nurp linux-4.4.3/fs/aufs/whout.c linux-4.4.3-aufs/fs/aufs/whout.c + au_whtmp_rmdir_free(args); + } +} -diff -Nurp linux-4.4.3/fs/aufs/whout.h linux-4.4.3-aufs/fs/aufs/whout.h ---- linux-4.4.3/fs/aufs/whout.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/whout.h 2016-03-02 17:59:37.374498781 +0200 +diff --git a/fs/aufs/whout.h b/fs/aufs/whout.h +new file mode 100644 +index 0000000..4077dd1 +--- /dev/null ++++ b/fs/aufs/whout.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima @@ -31385,10 +31496,12 @@ diff -Nurp linux-4.4.3/fs/aufs/whout.h linux-4.4.3-aufs/fs/aufs/whout.h + +#endif /* __KERNEL__ */ +#endif /* __AUFS_WHOUT_H__ */ -diff -Nurp linux-4.4.3/fs/aufs/wkq.c linux-4.4.3-aufs/fs/aufs/wkq.c ---- linux-4.4.3/fs/aufs/wkq.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/wkq.c 2016-03-02 17:59:37.374498781 +0200 -@@ -0,0 +1,200 @@ +diff --git a/fs/aufs/wkq.c b/fs/aufs/wkq.c +new file mode 100644 +index 0000000..65c0137 +--- /dev/null ++++ b/fs/aufs/wkq.c +@@ -0,0 +1,205 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima + */ @@ -31536,7 +31649,7 @@ diff -Nurp linux-4.4.3/fs/aufs/wkq.c linux-4.4.3-aufs/fs/aufs/wkq.c + int err; + struct au_wkinfo *wkinfo; + -+ atomic_inc(&au_sbi(sb)->si_nowait.nw_len); ++ percpu_counter_inc(&au_sbi(sb)->si_nowait.nw_len); + + /* + * wkq_func() must free this wkinfo. @@ -31566,11 +31679,16 @@ diff -Nurp linux-4.4.3/fs/aufs/wkq.c linux-4.4.3-aufs/fs/aufs/wkq.c + +void au_nwt_init(struct au_nowait_tasks *nwt) +{ -+ atomic_set(&nwt->nw_len, 0); -+ /* smp_mb(); */ /* atomic_set */ ++ percpu_counter_init(&nwt->nw_len, 0, GFP_NOFS); + init_waitqueue_head(&nwt->nw_wq); +} + ++void au_nwt_fin(struct au_nowait_tasks *nwt) ++{ ++ AuDebugOn(percpu_counter_sum(&nwt->nw_len)); ++ percpu_counter_destroy(&nwt->nw_len); ++} ++ +void au_wkq_fin(void) +{ + destroy_workqueue(au_wkq); @@ -31589,10 +31707,12 @@ diff -Nurp linux-4.4.3/fs/aufs/wkq.c linux-4.4.3-aufs/fs/aufs/wkq.c + + return err; +} -diff -Nurp linux-4.4.3/fs/aufs/wkq.h linux-4.4.3-aufs/fs/aufs/wkq.h ---- linux-4.4.3/fs/aufs/wkq.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/wkq.h 2016-03-02 17:59:37.374498781 +0200 -@@ -0,0 +1,78 @@ +diff --git a/fs/aufs/wkq.h b/fs/aufs/wkq.h +new file mode 100644 +index 0000000..752b9c5 +--- /dev/null ++++ b/fs/aufs/wkq.h +@@ -0,0 +1,80 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima + */ @@ -31615,7 +31735,7 @@ diff -Nurp linux-4.4.3/fs/aufs/wkq.h linux-4.4.3-aufs/fs/aufs/wkq.h + * in the next operation, wait for the 'nowait' tasks in system-wide workqueue + */ +struct au_nowait_tasks { -+ atomic_t nw_len; ++ struct percpu_counter nw_len; + wait_queue_head_t nw_wq; +}; + @@ -31642,6 +31762,7 @@ diff -Nurp linux-4.4.3/fs/aufs/wkq.h linux-4.4.3-aufs/fs/aufs/wkq.h +int au_wkq_nowait(au_wkq_func_t func, void *args, struct super_block *sb, + unsigned int flags); +void au_nwt_init(struct au_nowait_tasks *nwt); ++void au_nwt_fin(struct au_nowait_tasks *nwt); +int __init au_wkq_init(void); +void au_wkq_fin(void); + @@ -31659,21 +31780,24 @@ diff -Nurp linux-4.4.3/fs/aufs/wkq.h linux-4.4.3-aufs/fs/aufs/wkq.h + +static inline void au_nwt_done(struct au_nowait_tasks *nwt) +{ -+ if (atomic_dec_and_test(&nwt->nw_len)) ++ percpu_counter_dec(&nwt->nw_len); ++ if (!percpu_counter_sum(&nwt->nw_len)) + wake_up_all(&nwt->nw_wq); +} + +static inline int au_nwt_flush(struct au_nowait_tasks *nwt) +{ -+ wait_event(nwt->nw_wq, !atomic_read(&nwt->nw_len)); ++ wait_event(nwt->nw_wq, !percpu_counter_sum(&nwt->nw_len)); + return 0; +} + +#endif /* __KERNEL__ */ +#endif /* __AUFS_WKQ_H__ */ -diff -Nurp linux-4.4.3/fs/aufs/xattr.c linux-4.4.3-aufs/fs/aufs/xattr.c ---- linux-4.4.3/fs/aufs/xattr.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/xattr.c 2016-03-02 17:59:37.375498789 +0200 +diff --git a/fs/aufs/xattr.c b/fs/aufs/xattr.c +new file mode 100644 +index 0000000..890c107 +--- /dev/null ++++ b/fs/aufs/xattr.c @@ -0,0 +1,331 @@ +/* + * Copyright (C) 2014-2016 Junjiro R. Okajima @@ -31752,9 +31876,9 @@ diff -Nurp linux-4.4.3/fs/aufs/xattr.c linux-4.4.3-aufs/fs/aufs/xattr.c + + /* unlock it temporary */ + h_idst = d_inode(h_dst); -+ mutex_unlock(&h_idst->i_mutex); ++ inode_unlock(h_idst); + err = vfsub_setxattr(h_dst, name, *buf, ssz, /*flags*/0); -+ mutex_lock_nested(&h_idst->i_mutex, AuLsc_I_CHILD2); ++ inode_lock_nested(h_idst, AuLsc_I_CHILD2); + if (unlikely(err)) { + if (verbose || au_debug_test()) + pr_err("%s, err %d\n", name, err); @@ -31777,9 +31901,9 @@ diff -Nurp linux-4.4.3/fs/aufs/xattr.c linux-4.4.3-aufs/fs/aufs/xattr.c + /* there should not be the parent-child relationship between them */ + h_isrc = d_inode(h_src); + h_idst = d_inode(h_dst); -+ mutex_unlock(&h_idst->i_mutex); -+ mutex_lock_nested(&h_isrc->i_mutex, AuLsc_I_CHILD); -+ mutex_lock_nested(&h_idst->i_mutex, AuLsc_I_CHILD2); ++ inode_unlock(h_idst); ++ inode_lock_nested(h_isrc, AuLsc_I_CHILD); ++ inode_lock_nested(h_idst, AuLsc_I_CHILD2); + unlocked = 0; + + /* some filesystems don't list POSIX ACL, for example tmpfs */ @@ -31804,7 +31928,7 @@ diff -Nurp linux-4.4.3/fs/aufs/xattr.c linux-4.4.3-aufs/fs/aufs/xattr.c + goto out; + err = vfs_listxattr(h_src, p, ssz); + } -+ mutex_unlock(&h_isrc->i_mutex); ++ inode_unlock(h_isrc); + unlocked = 1; + AuDbg("err %d, ssz %zd\n", err, ssz); + if (unlikely(err < 0)) @@ -31846,7 +31970,7 @@ diff -Nurp linux-4.4.3/fs/aufs/xattr.c linux-4.4.3-aufs/fs/aufs/xattr.c + kfree(o); +out: + if (!unlocked) -+ mutex_unlock(&h_isrc->i_mutex); ++ inode_unlock(h_isrc); + AuTraceErr(err); + return err; +} @@ -32006,10 +32130,12 @@ diff -Nurp linux-4.4.3/fs/aufs/xattr.c linux-4.4.3-aufs/fs/aufs/xattr.c + /* sb->s_xattr = au_xattr_handlers; */ +} +#endif -diff -Nurp linux-4.4.3/fs/aufs/xino.c linux-4.4.3-aufs/fs/aufs/xino.c ---- linux-4.4.3/fs/aufs/xino.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/fs/aufs/xino.c 2016-03-02 17:59:37.376498796 +0200 -@@ -0,0 +1,1305 @@ +diff --git a/fs/aufs/xino.c b/fs/aufs/xino.c +new file mode 100644 +index 0000000..c0eb51f +--- /dev/null ++++ b/fs/aufs/xino.c +@@ -0,0 +1,1304 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima + */ @@ -32229,7 +32355,7 @@ diff -Nurp linux-4.4.3/fs/aufs/xino.c linux-4.4.3-aufs/fs/aufs/xino.c +struct au_xino_lock_dir { + struct au_hinode *hdir; + struct dentry *parent; -+ struct mutex *mtx; ++ struct inode *dir; +}; + +static void au_xino_lock_dir(struct super_block *sb, struct file *xino, @@ -32247,8 +32373,8 @@ diff -Nurp linux-4.4.3/fs/aufs/xino.c linux-4.4.3-aufs/fs/aufs/xino.c + au_hn_imtx_lock_nested(ldir->hdir, AuLsc_I_PARENT); + } else { + ldir->parent = dget_parent(xino->f_path.dentry); -+ ldir->mtx = &d_inode(ldir->parent)->i_mutex; -+ mutex_lock_nested(ldir->mtx, AuLsc_I_PARENT); ++ ldir->dir = d_inode(ldir->parent); ++ inode_lock_nested(ldir->dir, AuLsc_I_PARENT); + } +} + @@ -32257,7 +32383,7 @@ diff -Nurp linux-4.4.3/fs/aufs/xino.c linux-4.4.3-aufs/fs/aufs/xino.c + if (ldir->hdir) + au_hn_imtx_unlock(ldir->hdir); + else { -+ mutex_unlock(ldir->mtx); ++ inode_unlock(ldir->dir); + dput(ldir->parent); + } +} @@ -32271,7 +32397,7 @@ diff -Nurp linux-4.4.3/fs/aufs/xino.c linux-4.4.3-aufs/fs/aufs/xino.c + int err; + unsigned long jiffy; + blkcnt_t blocks; -+ aufs_bindex_t bi, bend; ++ aufs_bindex_t bi, bbot; + struct kstatfs *st; + struct au_branch *br; + struct file *new_xino, *file; @@ -32284,8 +32410,8 @@ diff -Nurp linux-4.4.3/fs/aufs/xino.c linux-4.4.3-aufs/fs/aufs/xino.c + goto out; + + err = -EINVAL; -+ bend = au_sbend(sb); -+ if (unlikely(bindex < 0 || bend < bindex)) ++ bbot = au_sbbot(sb); ++ if (unlikely(bindex < 0 || bbot < bindex)) + goto out_st; + br = au_sbr(sb, bindex); + file = br->br_xino.xi_file; @@ -32314,7 +32440,7 @@ diff -Nurp linux-4.4.3/fs/aufs/xino.c linux-4.4.3-aufs/fs/aufs/xino.c + br->br_xino.xi_file = new_xino; + + h_sb = au_br_sb(br); -+ for (bi = 0; bi <= bend; bi++) { ++ for (bi = 0; bi <= bbot; bi++) { + if (unlikely(bi == bindex)) + continue; + br = au_sbr(sb, bi); @@ -32369,7 +32495,7 @@ diff -Nurp linux-4.4.3/fs/aufs/xino.c linux-4.4.3-aufs/fs/aufs/xino.c + if (unlikely(err)) + pr_warn("err b%d, (%d)\n", bindex, err); + atomic_dec(&br->br_xino_running); -+ atomic_dec(&br->br_count); ++ au_br_put(br); + si_write_unlock(sb); + au_nwt_done(&au_sbi(sb)->si_nowait); + kfree(args); @@ -32417,7 +32543,7 @@ diff -Nurp linux-4.4.3/fs/aufs/xino.c linux-4.4.3-aufs/fs/aufs/xino.c + goto out_args; + } + -+ atomic_inc(&br->br_count); ++ au_br_get(br); + args->sb = sb; + args->br = br; + wkq_err = au_wkq_nowait(xino_do_trunc, args, sb, /*flags*/0); @@ -32425,7 +32551,7 @@ diff -Nurp linux-4.4.3/fs/aufs/xino.c linux-4.4.3-aufs/fs/aufs/xino.c + return; /* success */ + + pr_err("wkq %d\n", wkq_err); -+ atomic_dec(&br->br_count); ++ au_br_put(br); + +out_args: + kfree(args); @@ -32588,7 +32714,7 @@ diff -Nurp linux-4.4.3/fs/aufs/xino.c linux-4.4.3-aufs/fs/aufs/xino.c +{ + int err; + unsigned int mnt_flags; -+ aufs_bindex_t bindex, bend, bi; ++ aufs_bindex_t bindex, bbot, bi; + unsigned char try_trunc; + struct au_iinfo *iinfo; + struct super_block *sb; @@ -32597,6 +32723,8 @@ diff -Nurp linux-4.4.3/fs/aufs/xino.c linux-4.4.3-aufs/fs/aufs/xino.c + struct au_branch *br; + vfs_writef_t xwrite; + ++ AuDebugOn(is_bad_inode(inode)); ++ + sb = inode->i_sb; + mnt_flags = au_mntflags(sb); + if (!au_opt_test(mnt_flags, XINO) @@ -32609,18 +32737,15 @@ diff -Nurp linux-4.4.3/fs/aufs/xino.c linux-4.4.3-aufs/fs/aufs/xino.c + } + + iinfo = au_ii(inode); -+ if (!iinfo) -+ return; -+ -+ bindex = iinfo->ii_bstart; ++ bindex = iinfo->ii_btop; + if (bindex < 0) + return; + + xwrite = au_sbi(sb)->si_xwrite; + try_trunc = !!au_opt_test(mnt_flags, TRUNC_XINO); -+ hi = iinfo->ii_hinode + bindex; -+ bend = iinfo->ii_bend; -+ for (; bindex <= bend; bindex++, hi++) { ++ hi = au_hinode(iinfo, bindex); ++ bbot = iinfo->ii_bbot; ++ for (; bindex <= bbot; bindex++, hi++) { + h_inode = hi->hi_inode; + if (!h_inode + || (!unlinked && h_inode->i_nlink)) @@ -32772,13 +32897,13 @@ diff -Nurp linux-4.4.3/fs/aufs/xino.c linux-4.4.3-aufs/fs/aufs/xino.c + inode = file_inode(file); + h_parent = dget_parent(file->f_path.dentry); + h_dir = d_inode(h_parent); -+ mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT); ++ inode_lock_nested(h_dir, AuLsc_I_PARENT); + /* mnt_want_write() is unnecessary here */ + /* no delegation since it is just created */ + if (inode->i_nlink) + err = vfsub_unlink(h_dir, &file->f_path, /*delegated*/NULL, + /*force*/0); -+ mutex_unlock(&h_dir->i_mutex); ++ inode_unlock(h_dir); + dput(h_parent); + if (unlikely(err)) { + if (!silent) @@ -32809,10 +32934,10 @@ diff -Nurp linux-4.4.3/fs/aufs/xino.c linux-4.4.3-aufs/fs/aufs/xino.c + +/* + * find another branch who is on the same filesystem of the specified -+ * branch{@btgt}. search until @bend. ++ * branch{@btgt}. search until @bbot. + */ +static int is_sb_shared(struct super_block *sb, aufs_bindex_t btgt, -+ aufs_bindex_t bend) ++ aufs_bindex_t bbot) +{ + aufs_bindex_t bindex; + struct super_block *tgt_sb = au_sbr_sb(sb, btgt); @@ -32820,7 +32945,7 @@ diff -Nurp linux-4.4.3/fs/aufs/xino.c linux-4.4.3-aufs/fs/aufs/xino.c + for (bindex = 0; bindex < btgt; bindex++) + if (unlikely(tgt_sb == au_sbr_sb(sb, bindex))) + return bindex; -+ for (bindex++; bindex <= bend; bindex++) ++ for (bindex++; bindex <= bbot; bindex++) + if (unlikely(tgt_sb == au_sbr_sb(sb, bindex))) + return bindex; + return -1; @@ -32839,16 +32964,16 @@ diff -Nurp linux-4.4.3/fs/aufs/xino.c linux-4.4.3-aufs/fs/aufs/xino.c +{ + int err; + ino_t ino; -+ aufs_bindex_t bend, bindex; ++ aufs_bindex_t bbot, bindex; + struct au_branch *shared_br, *b; + struct file *file; + struct super_block *tgt_sb; + + shared_br = NULL; -+ bend = au_sbend(sb); ++ bbot = au_sbbot(sb); + if (do_test) { + tgt_sb = au_br_sb(br); -+ for (bindex = 0; bindex <= bend; bindex++) { ++ for (bindex = 0; bindex <= bbot; bindex++) { + b = au_sbr(sb, bindex); + if (tgt_sb == au_br_sb(b)) { + shared_br = b; @@ -32936,7 +33061,7 @@ diff -Nurp linux-4.4.3/fs/aufs/xino.c linux-4.4.3-aufs/fs/aufs/xino.c +static int xib_restore(struct super_block *sb) +{ + int err; -+ aufs_bindex_t bindex, bend; ++ aufs_bindex_t bindex, bbot; + void *page; + + err = -ENOMEM; @@ -32945,8 +33070,8 @@ diff -Nurp linux-4.4.3/fs/aufs/xino.c linux-4.4.3-aufs/fs/aufs/xino.c + goto out; + + err = 0; -+ bend = au_sbend(sb); -+ for (bindex = 0; !err && bindex <= bend; bindex++) ++ bbot = au_sbbot(sb); ++ for (bindex = 0; !err && bindex <= bbot; bindex++) + if (!bindex || is_sb_shared(sb, bindex, bindex - 1) < 0) + err = do_xib_restore + (sb, au_sbr(sb, bindex)->br_xino.xi_file, page); @@ -33088,11 +33213,11 @@ diff -Nurp linux-4.4.3/fs/aufs/xino.c linux-4.4.3-aufs/fs/aufs/xino.c +/* xino for each branch */ +static void xino_clear_br(struct super_block *sb) +{ -+ aufs_bindex_t bindex, bend; ++ aufs_bindex_t bindex, bbot; + struct au_branch *br; + -+ bend = au_sbend(sb); -+ for (bindex = 0; bindex <= bend; bindex++) { ++ bbot = au_sbbot(sb); ++ for (bindex = 0; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + if (!br || !br->br_xino.xi_file) + continue; @@ -33106,7 +33231,7 @@ diff -Nurp linux-4.4.3/fs/aufs/xino.c linux-4.4.3-aufs/fs/aufs/xino.c +{ + int err; + ino_t ino; -+ aufs_bindex_t bindex, bend, bshared; ++ aufs_bindex_t bindex, bbot, bshared; + struct { + struct file *old, *new; + } *fpair, *p; @@ -33117,15 +33242,15 @@ diff -Nurp linux-4.4.3/fs/aufs/xino.c linux-4.4.3-aufs/fs/aufs/xino.c + SiMustWriteLock(sb); + + err = -ENOMEM; -+ bend = au_sbend(sb); -+ fpair = kcalloc(bend + 1, sizeof(*fpair), GFP_NOFS); ++ bbot = au_sbbot(sb); ++ fpair = kcalloc(bbot + 1, sizeof(*fpair), GFP_NOFS); + if (unlikely(!fpair)) + goto out; + + inode = d_inode(sb->s_root); + ino = AUFS_ROOT_INO; + writef = au_sbi(sb)->si_xwrite; -+ for (bindex = 0, p = fpair; bindex <= bend; bindex++, p++) { ++ for (bindex = 0, p = fpair; bindex <= bbot; bindex++, p++) { + br = au_sbr(sb, bindex); + bshared = is_sb_shared(sb, bindex, bindex - 1); + if (bshared >= 0) { @@ -33151,7 +33276,7 @@ diff -Nurp linux-4.4.3/fs/aufs/xino.c linux-4.4.3-aufs/fs/aufs/xino.c + goto out_pair; + } + -+ for (bindex = 0, p = fpair; bindex <= bend; bindex++, p++) { ++ for (bindex = 0, p = fpair; bindex <= bbot; bindex++, p++) { + br = au_sbr(sb, bindex); + if (br->br_xino.xi_file) + fput(br->br_xino.xi_file); @@ -33160,7 +33285,7 @@ diff -Nurp linux-4.4.3/fs/aufs/xino.c linux-4.4.3-aufs/fs/aufs/xino.c + } + +out_pair: -+ for (bindex = 0, p = fpair; bindex <= bend; bindex++, p++) ++ for (bindex = 0, p = fpair; bindex <= bbot; bindex++, p++) + if (p->new) + fput(p->new); + else @@ -33213,14 +33338,14 @@ diff -Nurp linux-4.4.3/fs/aufs/xino.c linux-4.4.3-aufs/fs/aufs/xino.c + + au_opt_set(sbinfo->si_mntflags, XINO); + dir = d_inode(parent); -+ mutex_lock_nested(&dir->i_mutex, AuLsc_I_PARENT); ++ inode_lock_nested(dir, AuLsc_I_PARENT); + /* mnt_want_write() is unnecessary here */ + err = au_xino_set_xib(sb, xino->file); + if (!err) + err = au_xigen_set(sb, xino->file); + if (!err) + err = au_xino_set_br(sb, xino->file); -+ mutex_unlock(&dir->i_mutex); ++ inode_unlock(dir); + if (!err) + goto out; /* success */ + @@ -33246,12 +33371,12 @@ diff -Nurp linux-4.4.3/fs/aufs/xino.c linux-4.4.3-aufs/fs/aufs/xino.c + struct au_branch *br; + struct super_block *h_sb; + struct path path; -+ aufs_bindex_t bend, bindex, bwr; ++ aufs_bindex_t bbot, bindex, bwr; + + br = NULL; -+ bend = au_sbend(sb); ++ bbot = au_sbbot(sb); + bwr = -1; -+ for (bindex = 0; bindex <= bend; bindex++) { ++ for (bindex = 0; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + if (au_br_writable(br->br_perm) + && !au_test_fs_bad_xino(au_br_sb(br))) { @@ -33315,10 +33440,11 @@ diff -Nurp linux-4.4.3/fs/aufs/xino.c linux-4.4.3-aufs/fs/aufs/xino.c +out: + return err; +} -diff -Nurp linux-4.4.3/fs/dcache.c linux-4.4.3-aufs/fs/dcache.c ---- linux-4.4.3/fs/dcache.c 2016-01-11 01:01:32.000000000 +0200 -+++ linux-4.4.3-aufs/fs/dcache.c 2016-03-02 17:59:37.376498796 +0200 -@@ -1167,7 +1167,7 @@ enum d_walk_ret { +diff --git a/fs/dcache.c b/fs/dcache.c +index d5ecc6e..4de75b2 100644 +--- a/fs/dcache.c ++++ b/fs/dcache.c +@@ -1156,7 +1156,7 @@ enum d_walk_ret { * * The @enter() and @finish() callbacks are called with d_lock held. */ @@ -33327,9 +33453,10 @@ diff -Nurp linux-4.4.3/fs/dcache.c linux-4.4.3-aufs/fs/dcache.c enum d_walk_ret (*enter)(void *, struct dentry *), void (*finish)(void *)) { -diff -Nurp linux-4.4.3/fs/fcntl.c linux-4.4.3-aufs/fs/fcntl.c ---- linux-4.4.3/fs/fcntl.c 2016-01-11 01:01:32.000000000 +0200 -+++ linux-4.4.3-aufs/fs/fcntl.c 2016-03-02 17:59:37.376498796 +0200 +diff --git a/fs/fcntl.c b/fs/fcntl.c +index 350a2c8..6f42279 100644 +--- a/fs/fcntl.c ++++ b/fs/fcntl.c @@ -29,7 +29,7 @@ #define SETFL_MASK (O_APPEND | O_NONBLOCK | O_NDELAY | O_DIRECT | O_NOATIME) @@ -33339,7 +33466,7 @@ diff -Nurp linux-4.4.3/fs/fcntl.c linux-4.4.3-aufs/fs/fcntl.c { struct inode * inode = file_inode(filp); int error = 0; -@@ -59,6 +59,8 @@ static int setfl(int fd, struct file * f +@@ -60,6 +60,8 @@ static int setfl(int fd, struct file * filp, unsigned long arg) if (filp->f_op->check_flags) error = filp->f_op->check_flags(arg); @@ -33348,29 +33475,11 @@ diff -Nurp linux-4.4.3/fs/fcntl.c linux-4.4.3-aufs/fs/fcntl.c if (error) return error; -diff -Nurp linux-4.4.3/fs/Kconfig linux-4.4.3-aufs/fs/Kconfig ---- linux-4.4.3/fs/Kconfig 2016-01-11 01:01:32.000000000 +0200 -+++ linux-4.4.3-aufs/fs/Kconfig 2016-03-02 17:59:37.360498681 +0200 -@@ -221,6 +221,7 @@ source "fs/pstore/Kconfig" - source "fs/sysv/Kconfig" - source "fs/ufs/Kconfig" - source "fs/exofs/Kconfig" -+source "fs/aufs/Kconfig" - - endif # MISC_FILESYSTEMS - -diff -Nurp linux-4.4.3/fs/Makefile linux-4.4.3-aufs/fs/Makefile ---- linux-4.4.3/fs/Makefile 2016-01-11 01:01:32.000000000 +0200 -+++ linux-4.4.3-aufs/fs/Makefile 2016-03-02 17:59:37.360498681 +0200 -@@ -126,3 +126,4 @@ obj-y += exofs/ # Multiple modules - obj-$(CONFIG_CEPH_FS) += ceph/ - obj-$(CONFIG_PSTORE) += pstore/ - obj-$(CONFIG_EFIVAR_FS) += efivarfs/ -+obj-$(CONFIG_AUFS_FS) += aufs/ -diff -Nurp linux-4.4.3/fs/proc/base.c linux-4.4.3-aufs/fs/proc/base.c ---- linux-4.4.3/fs/proc/base.c 2016-03-02 17:42:59.444713286 +0200 -+++ linux-4.4.3-aufs/fs/proc/base.c 2016-03-02 17:59:37.376498796 +0200 -@@ -1922,7 +1922,7 @@ static int proc_map_files_get_link(struc +diff --git a/fs/proc/base.c b/fs/proc/base.c +index 0d163a8..b958f79 100644 +--- a/fs/proc/base.c ++++ b/fs/proc/base.c +@@ -1934,7 +1934,7 @@ static int map_files_get_link(struct dentry *dentry, struct path *path) down_read(&mm->mmap_sem); vma = find_exact_vma(mm, vm_start, vm_end); if (vma && vma->vm_file) { @@ -33379,10 +33488,11 @@ diff -Nurp linux-4.4.3/fs/proc/base.c linux-4.4.3-aufs/fs/proc/base.c path_get(path); rc = 0; } -diff -Nurp linux-4.4.3/fs/proc/nommu.c linux-4.4.3-aufs/fs/proc/nommu.c ---- linux-4.4.3/fs/proc/nommu.c 2016-01-11 01:01:32.000000000 +0200 -+++ linux-4.4.3-aufs/fs/proc/nommu.c 2016-03-02 17:59:37.377498803 +0200 -@@ -45,7 +45,10 @@ static int nommu_region_show(struct seq_ +diff --git a/fs/proc/nommu.c b/fs/proc/nommu.c +index f8595e8..cb8eda0 100644 +--- a/fs/proc/nommu.c ++++ b/fs/proc/nommu.c +@@ -45,7 +45,10 @@ static int nommu_region_show(struct seq_file *m, struct vm_region *region) file = region->vm_file; if (file) { @@ -33394,10 +33504,11 @@ diff -Nurp linux-4.4.3/fs/proc/nommu.c linux-4.4.3-aufs/fs/proc/nommu.c dev = inode->i_sb->s_dev; ino = inode->i_ino; } -diff -Nurp linux-4.4.3/fs/proc/task_mmu.c linux-4.4.3-aufs/fs/proc/task_mmu.c ---- linux-4.4.3/fs/proc/task_mmu.c 2016-03-02 17:42:59.444713286 +0200 -+++ linux-4.4.3-aufs/fs/proc/task_mmu.c 2016-03-02 17:59:37.377498803 +0200 -@@ -281,7 +281,10 @@ show_map_vma(struct seq_file *m, struct +diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c +index 5415835..c41eb73 100644 +--- a/fs/proc/task_mmu.c ++++ b/fs/proc/task_mmu.c +@@ -298,7 +298,10 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma, int is_pid) const char *name = NULL; if (file) { @@ -33409,7 +33520,7 @@ diff -Nurp linux-4.4.3/fs/proc/task_mmu.c linux-4.4.3-aufs/fs/proc/task_mmu.c dev = inode->i_sb->s_dev; ino = inode->i_ino; pgoff = ((loff_t)vma->vm_pgoff) << PAGE_SHIFT; -@@ -1506,7 +1509,7 @@ static int show_numa_map(struct seq_file +@@ -1617,7 +1620,7 @@ static int show_numa_map(struct seq_file *m, void *v, int is_pid) struct proc_maps_private *proc_priv = &numa_priv->proc_maps; struct vm_area_struct *vma = v; struct numa_maps *md = &numa_priv->md; @@ -33418,10 +33529,11 @@ diff -Nurp linux-4.4.3/fs/proc/task_mmu.c linux-4.4.3-aufs/fs/proc/task_mmu.c struct mm_struct *mm = vma->vm_mm; struct mm_walk walk = { .hugetlb_entry = gather_hugetlb_stats, -diff -Nurp linux-4.4.3/fs/proc/task_nommu.c linux-4.4.3-aufs/fs/proc/task_nommu.c ---- linux-4.4.3/fs/proc/task_nommu.c 2016-01-11 01:01:32.000000000 +0200 -+++ linux-4.4.3-aufs/fs/proc/task_nommu.c 2016-03-02 17:59:37.377498803 +0200 -@@ -160,7 +160,10 @@ static int nommu_vma_show(struct seq_fil +diff --git a/fs/proc/task_nommu.c b/fs/proc/task_nommu.c +index faacb0c..17b43be 100644 +--- a/fs/proc/task_nommu.c ++++ b/fs/proc/task_nommu.c +@@ -163,7 +163,10 @@ static int nommu_vma_show(struct seq_file *m, struct vm_area_struct *vma, file = vma->vm_file; if (file) { @@ -33433,10 +33545,11 @@ diff -Nurp linux-4.4.3/fs/proc/task_nommu.c linux-4.4.3-aufs/fs/proc/task_nommu. dev = inode->i_sb->s_dev; ino = inode->i_ino; pgoff = (loff_t)vma->vm_pgoff << PAGE_SHIFT; -diff -Nurp linux-4.4.3/fs/read_write.c linux-4.4.3-aufs/fs/read_write.c ---- linux-4.4.3/fs/read_write.c 2016-01-11 01:01:32.000000000 +0200 -+++ linux-4.4.3-aufs/fs/read_write.c 2016-03-02 17:59:37.377498803 +0200 -@@ -494,6 +494,28 @@ ssize_t __vfs_write(struct file *file, c +diff --git a/fs/read_write.c b/fs/read_write.c +index cf377cf..0ebcdc5 100644 +--- a/fs/read_write.c ++++ b/fs/read_write.c +@@ -534,6 +534,28 @@ ssize_t __vfs_write(struct file *file, const char __user *p, size_t count, } EXPORT_SYMBOL(__vfs_write); @@ -33465,10 +33578,11 @@ diff -Nurp linux-4.4.3/fs/read_write.c linux-4.4.3-aufs/fs/read_write.c ssize_t __kernel_write(struct file *file, const char *buf, size_t count, loff_t *pos) { mm_segment_t old_fs; -diff -Nurp linux-4.4.3/fs/splice.c linux-4.4.3-aufs/fs/splice.c ---- linux-4.4.3/fs/splice.c 2016-01-11 01:01:32.000000000 +0200 -+++ linux-4.4.3-aufs/fs/splice.c 2016-03-02 17:59:37.377498803 +0200 -@@ -1110,8 +1110,8 @@ EXPORT_SYMBOL(generic_splice_sendpage); +diff --git a/fs/splice.c b/fs/splice.c +index dd9bf7e..9326c2a 100644 +--- a/fs/splice.c ++++ b/fs/splice.c +@@ -1111,8 +1111,8 @@ EXPORT_SYMBOL(generic_splice_sendpage); /* * Attempt to initiate a splice from pipe to file. */ @@ -33479,7 +33593,7 @@ diff -Nurp linux-4.4.3/fs/splice.c linux-4.4.3-aufs/fs/splice.c { ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int); -@@ -1127,9 +1127,9 @@ static long do_splice_from(struct pipe_i +@@ -1128,9 +1128,9 @@ static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, /* * Attempt to initiate a splice from a file to a pipe. */ @@ -33492,9 +33606,10 @@ diff -Nurp linux-4.4.3/fs/splice.c linux-4.4.3-aufs/fs/splice.c { ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); -diff -Nurp linux-4.4.3/include/linux/file.h linux-4.4.3-aufs/include/linux/file.h ---- linux-4.4.3/include/linux/file.h 2016-01-11 01:01:32.000000000 +0200 -+++ linux-4.4.3-aufs/include/linux/file.h 2016-03-02 17:59:37.377498803 +0200 +diff --git a/include/linux/file.h b/include/linux/file.h +index f87d308..9a290b3 100644 +--- a/include/linux/file.h ++++ b/include/linux/file.h @@ -19,6 +19,7 @@ struct dentry; struct path; extern struct file *alloc_file(struct path *, fmode_t mode, @@ -33503,10 +33618,11 @@ diff -Nurp linux-4.4.3/include/linux/file.h linux-4.4.3-aufs/include/linux/file. static inline void fput_light(struct file *file, int fput_needed) { -diff -Nurp linux-4.4.3/include/linux/fs.h linux-4.4.3-aufs/include/linux/fs.h ---- linux-4.4.3/include/linux/fs.h 2016-01-11 01:01:32.000000000 +0200 -+++ linux-4.4.3-aufs/include/linux/fs.h 2016-03-02 17:59:37.378498810 +0200 -@@ -1233,6 +1233,7 @@ extern void fasync_free(struct fasync_st +diff --git a/include/linux/fs.h b/include/linux/fs.h +index 70e61b5..351bb05 100644 +--- a/include/linux/fs.h ++++ b/include/linux/fs.h +@@ -1277,6 +1277,7 @@ extern void fasync_free(struct fasync_struct *); /* can be called from interrupts */ extern void kill_fasync(struct fasync_struct **, int, int); @@ -33514,7 +33630,7 @@ diff -Nurp linux-4.4.3/include/linux/fs.h linux-4.4.3-aufs/include/linux/fs.h extern void __f_setown(struct file *filp, struct pid *, enum pid_type, int force); extern void f_setown(struct file *filp, unsigned long arg, int force); extern void f_delown(struct file *filp); -@@ -1619,6 +1620,7 @@ struct file_operations { +@@ -1660,6 +1661,7 @@ struct file_operations { ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); int (*check_flags)(int); @@ -33522,7 +33638,7 @@ diff -Nurp linux-4.4.3/include/linux/fs.h linux-4.4.3-aufs/include/linux/fs.h int (*flock) (struct file *, int, struct file_lock *); ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int); ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); -@@ -1672,6 +1674,12 @@ ssize_t rw_copy_check_uvector(int type, +@@ -1718,6 +1720,12 @@ ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector, struct iovec *fast_pointer, struct iovec **ret_pointer); @@ -33535,10 +33651,11 @@ diff -Nurp linux-4.4.3/include/linux/fs.h linux-4.4.3-aufs/include/linux/fs.h 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 *); -diff -Nurp linux-4.4.3/include/linux/mm.h linux-4.4.3-aufs/include/linux/mm.h ---- linux-4.4.3/include/linux/mm.h 2016-01-11 01:01:32.000000000 +0200 -+++ linux-4.4.3-aufs/include/linux/mm.h 2016-03-02 17:59:37.378498810 +0200 -@@ -1183,6 +1183,28 @@ static inline int fixup_user_fault(struc +diff --git a/include/linux/mm.h b/include/linux/mm.h +index 8f468e0..4ab3095 100644 +--- a/include/linux/mm.h ++++ b/include/linux/mm.h +@@ -1251,6 +1251,28 @@ static inline int fixup_user_fault(struct task_struct *tsk, } #endif @@ -33567,10 +33684,11 @@ diff -Nurp linux-4.4.3/include/linux/mm.h linux-4.4.3-aufs/include/linux/mm.h extern int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, int len, int write); extern int access_remote_vm(struct mm_struct *mm, unsigned long addr, void *buf, int len, int write); -diff -Nurp linux-4.4.3/include/linux/mm_types.h linux-4.4.3-aufs/include/linux/mm_types.h ---- linux-4.4.3/include/linux/mm_types.h 2016-01-11 01:01:32.000000000 +0200 -+++ linux-4.4.3-aufs/include/linux/mm_types.h 2016-03-02 17:59:37.378498810 +0200 -@@ -272,6 +272,7 @@ struct vm_region { +diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h +index c2d75b4..9e324fe 100644 +--- a/include/linux/mm_types.h ++++ b/include/linux/mm_types.h +@@ -269,6 +269,7 @@ struct vm_region { unsigned long vm_top; /* region allocated to here */ unsigned long vm_pgoff; /* the offset in vm_file corresponding to vm_start */ struct file *vm_file; /* the backing file or NULL */ @@ -33578,18 +33696,19 @@ diff -Nurp linux-4.4.3/include/linux/mm_types.h linux-4.4.3-aufs/include/linux/m int vm_usage; /* region usage count (access under nommu_region_sem) */ bool vm_icache_flushed : 1; /* true if the icache has been flushed for -@@ -346,6 +347,7 @@ struct vm_area_struct { +@@ -343,6 +344,7 @@ struct vm_area_struct { unsigned long vm_pgoff; /* Offset (within vm_file) in PAGE_SIZE - units, *not* PAGE_CACHE_SIZE */ + units */ struct file * vm_file; /* File we map to (can be NULL). */ + struct file *vm_prfile; /* shadow of vm_file */ void * vm_private_data; /* was vm_pte (shared mem) */ #ifndef CONFIG_MMU -diff -Nurp linux-4.4.3/include/linux/splice.h linux-4.4.3-aufs/include/linux/splice.h ---- linux-4.4.3/include/linux/splice.h 2016-01-11 01:01:32.000000000 +0200 -+++ linux-4.4.3-aufs/include/linux/splice.h 2016-03-02 17:59:37.378498810 +0200 -@@ -83,4 +83,10 @@ extern void splice_shrink_spd(struct spl +diff --git a/include/linux/splice.h b/include/linux/splice.h +index da2751d..2e0fca6 100644 +--- a/include/linux/splice.h ++++ b/include/linux/splice.h +@@ -83,4 +83,10 @@ extern void splice_shrink_spd(struct splice_pipe_desc *); extern void spd_release_page(struct splice_pipe_desc *, unsigned int); extern const struct pipe_buf_operations page_cache_pipe_buf_ops; @@ -33600,9 +33719,23 @@ diff -Nurp linux-4.4.3/include/linux/splice.h linux-4.4.3-aufs/include/linux/spl + struct pipe_inode_info *pipe, size_t len, + unsigned int flags); #endif -diff -Nurp linux-4.4.3/include/uapi/linux/aufs_type.h linux-4.4.3-aufs/include/uapi/linux/aufs_type.h ---- linux-4.4.3/include/uapi/linux/aufs_type.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/include/uapi/linux/aufs_type.h 2016-03-02 17:59:37.378498810 +0200 +diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild +index 813ffb2e..ac2202e 100644 +--- a/include/uapi/linux/Kbuild ++++ b/include/uapi/linux/Kbuild +@@ -59,6 +59,7 @@ header-y += atmsvc.h + header-y += atm_tcp.h + header-y += atm_zatm.h + header-y += audit.h ++header-y += aufs_type.h + header-y += auto_fs4.h + header-y += auto_fs.h + header-y += auxvec.h +diff --git a/include/uapi/linux/aufs_type.h b/include/uapi/linux/aufs_type.h +new file mode 100644 +index 0000000..2e0c537 +--- /dev/null ++++ b/include/uapi/linux/aufs_type.h @@ -0,0 +1,406 @@ +/* + * Copyright (C) 2005-2016 Junjiro R. Okajima @@ -33632,7 +33765,7 @@ diff -Nurp linux-4.4.3/include/uapi/linux/aufs_type.h linux-4.4.3-aufs/include/u + +#include + -+#define AUFS_VERSION "4.4" ++#define AUFS_VERSION "4.6" + +/* todo? move this to linux-2.6.19/include/magic.h */ +#define AUFS_SUPER_MAGIC ('a' << 24 | 'u' << 16 | 'f' << 8 | 's') @@ -34010,21 +34143,11 @@ diff -Nurp linux-4.4.3/include/uapi/linux/aufs_type.h linux-4.4.3-aufs/include/u +#define AUFS_CTL_FHSM_FD _IOW(AuCtlType, AuCtl_FHSM_FD, int) + +#endif /* __AUFS_TYPE_H__ */ -diff -Nurp linux-4.4.3/include/uapi/linux/Kbuild linux-4.4.3-aufs/include/uapi/linux/Kbuild ---- linux-4.4.3/include/uapi/linux/Kbuild 2016-01-11 01:01:32.000000000 +0200 -+++ linux-4.4.3-aufs/include/uapi/linux/Kbuild 2016-03-02 17:59:37.378498810 +0200 -@@ -59,6 +59,7 @@ header-y += atmsvc.h - header-y += atm_tcp.h - header-y += atm_zatm.h - header-y += audit.h -+header-y += aufs_type.h - header-y += auto_fs4.h - header-y += auto_fs.h - header-y += auxvec.h -diff -Nurp linux-4.4.3/kernel/fork.c linux-4.4.3-aufs/kernel/fork.c ---- linux-4.4.3/kernel/fork.c 2016-01-11 01:01:32.000000000 +0200 -+++ linux-4.4.3-aufs/kernel/fork.c 2016-03-02 17:59:37.379498817 +0200 -@@ -465,7 +465,7 @@ static int dup_mmap(struct mm_struct *mm +diff --git a/kernel/fork.c b/kernel/fork.c +index d277e83..683b8a2 100644 +--- a/kernel/fork.c ++++ b/kernel/fork.c +@@ -475,7 +475,7 @@ static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm) struct inode *inode = file_inode(file); struct address_space *mapping = file->f_mapping; @@ -34033,45 +34156,11 @@ diff -Nurp linux-4.4.3/kernel/fork.c linux-4.4.3-aufs/kernel/fork.c if (tmp->vm_flags & VM_DENYWRITE) atomic_dec(&inode->i_writecount); i_mmap_lock_write(mapping); -diff -Nurp linux-4.4.3/MAINTAINERS linux-4.4.3-aufs/MAINTAINERS ---- linux-4.4.3/MAINTAINERS 2016-03-02 17:42:59.404713024 +0200 -+++ linux-4.4.3-aufs/MAINTAINERS 2016-03-02 17:59:37.360498681 +0200 -@@ -2029,6 +2029,19 @@ F: include/linux/audit.h - F: include/uapi/linux/audit.h - F: kernel/audit* - -+AUFS (advanced multi layered unification filesystem) FILESYSTEM -+M: "J. R. Okajima" -+L: linux-unionfs@vger.kernel.org -+L: aufs-users@lists.sourceforge.net (members only) -+W: http://aufs.sourceforge.net -+T: git://github.com/sfjro/aufs4-linux.git -+S: Supported -+F: Documentation/filesystems/aufs/ -+F: Documentation/ABI/testing/debugfs-aufs -+F: Documentation/ABI/testing/sysfs-aufs -+F: fs/aufs/ -+F: include/uapi/linux/aufs_type.h -+ - AUXILIARY DISPLAY DRIVERS - M: Miguel Ojeda Sandonis - W: http://miguelojeda.es/auxdisplay.htm -diff -Nurp linux-4.4.3/mm/filemap.c linux-4.4.3-aufs/mm/filemap.c ---- linux-4.4.3/mm/filemap.c 2016-01-11 01:01:32.000000000 +0200 -+++ linux-4.4.3-aufs/mm/filemap.c 2016-03-02 17:59:37.379498817 +0200 -@@ -2128,7 +2128,7 @@ int filemap_page_mkwrite(struct vm_area_ - int ret = VM_FAULT_LOCKED; - - sb_start_pagefault(inode->i_sb); -- file_update_time(vma->vm_file); -+ vma_file_update_time(vma); - lock_page(page); - if (page->mapping != inode->i_mapping) { - unlock_page(page); -diff -Nurp linux-4.4.3/mm/Makefile linux-4.4.3-aufs/mm/Makefile ---- linux-4.4.3/mm/Makefile 2016-01-11 01:01:32.000000000 +0200 -+++ linux-4.4.3-aufs/mm/Makefile 2016-03-02 17:59:37.379498817 +0200 -@@ -21,7 +21,7 @@ obj-y := filemap.o mempool.o oom_kill. +diff --git a/mm/Makefile b/mm/Makefile +index deb467e..0f6ae63 100644 +--- a/mm/Makefile ++++ b/mm/Makefile +@@ -37,7 +37,7 @@ obj-y := filemap.o mempool.o oom_kill.o \ mm_init.o mmu_context.o percpu.o slab_common.o \ compaction.o vmacache.o \ interval_tree.o list_lru.o workingset.o \ @@ -34080,10 +34169,24 @@ diff -Nurp linux-4.4.3/mm/Makefile linux-4.4.3-aufs/mm/Makefile obj-y += init-mm.o -diff -Nurp linux-4.4.3/mm/memory.c linux-4.4.3-aufs/mm/memory.c ---- linux-4.4.3/mm/memory.c 2016-01-11 01:01:32.000000000 +0200 -+++ linux-4.4.3-aufs/mm/memory.c 2016-03-02 17:59:37.379498817 +0200 -@@ -2035,7 +2035,7 @@ static inline int wp_page_reuse(struct m +diff --git a/mm/filemap.c b/mm/filemap.c +index f2479af..31f4b0d 100644 +--- a/mm/filemap.c ++++ b/mm/filemap.c +@@ -2211,7 +2211,7 @@ int filemap_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) + int ret = VM_FAULT_LOCKED; + + sb_start_pagefault(inode->i_sb); +- file_update_time(vma->vm_file); ++ vma_file_update_time(vma); + lock_page(page); + if (page->mapping != inode->i_mapping) { + unlock_page(page); +diff --git a/mm/memory.c b/mm/memory.c +index 07493e3..dc696bc 100644 +--- a/mm/memory.c ++++ b/mm/memory.c +@@ -2098,7 +2098,7 @@ static inline int wp_page_reuse(struct mm_struct *mm, } if (!page_mkwrite) @@ -34092,10 +34195,11 @@ diff -Nurp linux-4.4.3/mm/memory.c linux-4.4.3-aufs/mm/memory.c } return VM_FAULT_WRITE; -diff -Nurp linux-4.4.3/mm/mmap.c linux-4.4.3-aufs/mm/mmap.c ---- linux-4.4.3/mm/mmap.c 2016-03-02 17:42:59.457713370 +0200 -+++ linux-4.4.3-aufs/mm/mmap.c 2016-03-02 17:59:37.380498824 +0200 -@@ -275,7 +275,7 @@ static struct vm_area_struct *remove_vma +diff --git a/mm/mmap.c b/mm/mmap.c +index bd2e1a53..7328b74 100644 +--- a/mm/mmap.c ++++ b/mm/mmap.c +@@ -166,7 +166,7 @@ static struct vm_area_struct *remove_vma(struct vm_area_struct *vma) if (vma->vm_ops && vma->vm_ops->close) vma->vm_ops->close(vma); if (vma->vm_file) @@ -34104,7 +34208,7 @@ diff -Nurp linux-4.4.3/mm/mmap.c linux-4.4.3-aufs/mm/mmap.c mpol_put(vma_policy(vma)); kmem_cache_free(vm_area_cachep, vma); return next; -@@ -891,7 +891,7 @@ again: remove_next = 1 + (end > next-> +@@ -785,7 +785,7 @@ again: remove_next = 1 + (end > next->vm_end); if (remove_next) { if (file) { uprobe_munmap(next, next->vm_start, next->vm_end); @@ -34113,7 +34217,7 @@ diff -Nurp linux-4.4.3/mm/mmap.c linux-4.4.3-aufs/mm/mmap.c } if (next->anon_vma) anon_vma_merge(vma, next); -@@ -1685,8 +1685,8 @@ out: +@@ -1566,8 +1566,8 @@ out: return addr; unmap_and_free_vma: @@ -34123,7 +34227,7 @@ diff -Nurp linux-4.4.3/mm/mmap.c linux-4.4.3-aufs/mm/mmap.c /* Undo any partial mapping done by a device driver. */ unmap_region(mm, vma, prev, vma->vm_start, vma->vm_end); -@@ -2483,7 +2483,7 @@ static int __split_vma(struct mm_struct +@@ -2362,7 +2362,7 @@ static int __split_vma(struct mm_struct *mm, struct vm_area_struct *vma, goto out_free_mpol; if (new->vm_file) @@ -34132,7 +34236,7 @@ diff -Nurp linux-4.4.3/mm/mmap.c linux-4.4.3-aufs/mm/mmap.c if (new->vm_ops && new->vm_ops->open) new->vm_ops->open(new); -@@ -2502,7 +2502,7 @@ static int __split_vma(struct mm_struct +@@ -2381,7 +2381,7 @@ static int __split_vma(struct mm_struct *mm, struct vm_area_struct *vma, if (new->vm_ops && new->vm_ops->close) new->vm_ops->close(new); if (new->vm_file) @@ -34141,28 +34245,45 @@ diff -Nurp linux-4.4.3/mm/mmap.c linux-4.4.3-aufs/mm/mmap.c unlink_anon_vmas(new); out_free_mpol: mpol_put(vma_policy(new)); -@@ -2644,7 +2644,6 @@ SYSCALL_DEFINE5(remap_file_pages, unsign +@@ -2523,7 +2523,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; - struct file *file; ++ struct file *file, *prfile; - pr_warn_once("%s (%d) uses deprecated remap_file_pages() syscall. " - "See Documentation/vm/remap_file_pages.txt.\n", -@@ -2712,10 +2711,10 @@ SYSCALL_DEFINE5(remap_file_pages, unsign + pr_warn_once("%s (%d) uses deprecated remap_file_pages() syscall. See Documentation/vm/remap_file_pages.txt.\n", + current->comm, current->pid); +@@ -2590,10 +2590,27 @@ SYSCALL_DEFINE5(remap_file_pages, unsigned long, start, unsigned long, size, } } - file = get_file(vma->vm_file); + vma_get_file(vma); ++ file = vma->vm_file; ++ prfile = vma->vm_prfile; ret = do_mmap_pgoff(vma->vm_file, start, size, prot, flags, pgoff, &populate); -- fput(file); -+ vma_fput(vma); ++ if (!IS_ERR_VALUE(ret) && file && prfile) { ++ struct vm_area_struct *new_vma; ++ ++ new_vma = find_vma(mm, ret); ++ if (!new_vma->vm_prfile) ++ new_vma->vm_prfile = prfile; ++ if (new_vma != vma) ++ get_file(prfile); ++ } ++ /* ++ * two fput()s instead of vma_fput(vma), ++ * coz vma may not be available anymore. ++ */ + fput(file); ++ if (prfile) ++ fput(prfile); out: up_write(&mm->mmap_sem); if (populate) -@@ -2985,7 +2984,7 @@ struct vm_area_struct *copy_vma(struct v +@@ -2864,7 +2881,7 @@ struct vm_area_struct *copy_vma(struct vm_area_struct **vmap, if (anon_vma_clone(new_vma, vma)) goto out_free_mempol; if (new_vma->vm_file) @@ -34171,10 +34292,11 @@ diff -Nurp linux-4.4.3/mm/mmap.c linux-4.4.3-aufs/mm/mmap.c if (new_vma->vm_ops && new_vma->vm_ops->open) new_vma->vm_ops->open(new_vma); vma_link(mm, new_vma, prev, rb_link, rb_parent); -diff -Nurp linux-4.4.3/mm/nommu.c linux-4.4.3-aufs/mm/nommu.c ---- linux-4.4.3/mm/nommu.c 2016-01-11 01:01:32.000000000 +0200 -+++ linux-4.4.3-aufs/mm/nommu.c 2016-03-02 17:59:37.380498824 +0200 -@@ -671,7 +671,7 @@ static void __put_nommu_region(struct vm +diff --git a/mm/nommu.c b/mm/nommu.c +index c8bd59a..4cfc2fc 100644 +--- a/mm/nommu.c ++++ b/mm/nommu.c +@@ -644,7 +644,7 @@ static void __put_nommu_region(struct vm_region *region) up_write(&nommu_region_sem); if (region->vm_file) @@ -34183,7 +34305,7 @@ diff -Nurp linux-4.4.3/mm/nommu.c linux-4.4.3-aufs/mm/nommu.c /* IO memory and memory shared directly out of the pagecache * from ramfs/tmpfs mustn't be released here */ -@@ -829,7 +829,7 @@ static void delete_vma(struct mm_struct +@@ -802,7 +802,7 @@ static void delete_vma(struct mm_struct *mm, struct vm_area_struct *vma) if (vma->vm_ops && vma->vm_ops->close) vma->vm_ops->close(vma); if (vma->vm_file) @@ -34192,7 +34314,7 @@ diff -Nurp linux-4.4.3/mm/nommu.c linux-4.4.3-aufs/mm/nommu.c put_nommu_region(vma->vm_region); kmem_cache_free(vm_area_cachep, vma); } -@@ -1355,7 +1355,7 @@ unsigned long do_mmap(struct file *file, +@@ -1328,7 +1328,7 @@ unsigned long do_mmap(struct file *file, goto error_just_free; } } @@ -34201,7 +34323,7 @@ diff -Nurp linux-4.4.3/mm/nommu.c linux-4.4.3-aufs/mm/nommu.c kmem_cache_free(vm_region_jar, region); region = pregion; result = start; -@@ -1430,10 +1430,10 @@ error_just_free: +@@ -1403,10 +1403,10 @@ error_just_free: up_write(&nommu_region_sem); error: if (region->vm_file) @@ -34214,9 +34336,11 @@ diff -Nurp linux-4.4.3/mm/nommu.c linux-4.4.3-aufs/mm/nommu.c kmem_cache_free(vm_area_cachep, vma); return ret; -diff -Nurp linux-4.4.3/mm/prfile.c linux-4.4.3-aufs/mm/prfile.c ---- linux-4.4.3/mm/prfile.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-4.4.3-aufs/mm/prfile.c 2016-03-02 17:59:37.380498824 +0200 +diff --git a/mm/prfile.c b/mm/prfile.c +new file mode 100644 +index 0000000..b323b8a +--- /dev/null ++++ b/mm/prfile.c @@ -0,0 +1,86 @@ +/* + * Mainly for aufs which mmap(2) diffrent file and wants to print different path diff --git a/kernel/kernel/files/patches/mageia/fs-ovl-move-super-block-magic-number-to-magic.h.patch b/kernel/kernel/files/patches/mageia/fs-ovl-move-super-block-magic-number-to-magic.h.patch deleted file mode 100644 index 87d5c18f..00000000 --- a/kernel/kernel/files/patches/mageia/fs-ovl-move-super-block-magic-number-to-magic.h.patch +++ /dev/null @@ -1,46 +0,0 @@ -From 257f871993474e2bde6c497b54022c362cf398e1 Mon Sep 17 00:00:00 2001 -From: Stephen Hemminger -Date: Wed, 4 Nov 2015 10:59:52 -0800 -Subject: [PATCH 3/6] ovl: move super block magic number to magic.h - -The overlayfs file system is not recognized by programs -like tail because the magic number is not in standard header location. - -Move it so that the value will propagate on for the GNU library -and utilities. Needs to go in the fstatfs manual page as well. - -Signed-off-by: Stephen Hemminger -Signed-off-by: Miklos Szeredi ---- - fs/overlayfs/super.c | 2 -- - include/uapi/linux/magic.h | 1 + - 2 files changed, 1 insertion(+), 2 deletions(-) - -diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c -index 97cacb5..32f3124 100644 ---- a/fs/overlayfs/super.c -+++ b/fs/overlayfs/super.c -@@ -24,8 +24,6 @@ MODULE_AUTHOR("Miklos Szeredi "); - MODULE_DESCRIPTION("Overlay filesystem"); - MODULE_LICENSE("GPL"); - --#define OVERLAYFS_SUPER_MAGIC 0x794c7630 -- - struct ovl_config { - char *lowerdir; - char *upperdir; -diff --git a/include/uapi/linux/magic.h b/include/uapi/linux/magic.h -index 7b1425a..eec4389 100644 ---- a/include/uapi/linux/magic.h -+++ b/include/uapi/linux/magic.h -@@ -31,6 +31,7 @@ - #define PSTOREFS_MAGIC 0x6165676C - #define EFIVARFS_MAGIC 0xde5e81e4 - #define HOSTFS_SUPER_MAGIC 0x00c0ffee -+#define OVERLAYFS_SUPER_MAGIC 0x794c7630 - - #define MINIX_SUPER_MAGIC 0x137F /* minix v1 fs, 14 char names */ - #define MINIX_SUPER_MAGIC2 0x138F /* minix v1 fs, 30 char names */ --- -2.7.0 - diff --git a/kernel/kernel/files/patches/mageia/gpu-drm-0001-devicetree-add-vendor-prefix-for-Vivante-Corporation.patch b/kernel/kernel/files/patches/mageia/gpu-drm-0001-devicetree-add-vendor-prefix-for-Vivante-Corporation.patch deleted file mode 100644 index 63166e4f..00000000 --- a/kernel/kernel/files/patches/mageia/gpu-drm-0001-devicetree-add-vendor-prefix-for-Vivante-Corporation.patch +++ /dev/null @@ -1,30 +0,0 @@ -From a6351c68fe1ea89ebb39e8c405d622464bcb339e Mon Sep 17 00:00:00 2001 -From: Philipp Zabel -Date: Thu, 2 Apr 2015 17:29:03 +0200 -Subject: [PATCH 1/4] devicetree: add vendor prefix for Vivante Corporation - -Trivial patch to add Vivante Corporation to the list of -devicetree vendor prefixes. - -Signed-off-by: Philipp Zabel -Signed-off-by: Lucas Stach -Acked-by: Rob Herring ---- - Documentation/devicetree/bindings/vendor-prefixes.txt | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt -index 55df1d4..c2767a7 100644 ---- a/Documentation/devicetree/bindings/vendor-prefixes.txt -+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt -@@ -238,6 +238,7 @@ v3 V3 Semiconductor - variscite Variscite Ltd. - via VIA Technologies, Inc. - virtio Virtual I/O Device Specification, developed by the OASIS consortium -+vivante Vivante Corporation - voipac Voipac Technologies s.r.o. - wexler Wexler - winbond Winbond Electronics corp. --- -2.6.4 - diff --git a/kernel/kernel/files/patches/mageia/gpu-drm-0002-drm-etnaviv-add-devicetree-bindings.patch b/kernel/kernel/files/patches/mageia/gpu-drm-0002-drm-etnaviv-add-devicetree-bindings.patch deleted file mode 100644 index afe6d580..00000000 --- a/kernel/kernel/files/patches/mageia/gpu-drm-0002-drm-etnaviv-add-devicetree-bindings.patch +++ /dev/null @@ -1,79 +0,0 @@ -From f04b205ac143413831b193f39fd9592665111d4b Mon Sep 17 00:00:00 2001 -From: Lucas Stach -Date: Thu, 2 Apr 2015 17:29:04 +0200 -Subject: [PATCH 2/4] drm/etnaviv: add devicetree bindings - -Etnaviv follows the same priciple as imx-drm to have a virtual -master device node to bind all the individual GPU cores together -into one DRM device. - -Signed-off-by: Lucas Stach -Acked-by: Rob Herring ---- - .../bindings/display/etnaviv/etnaviv-drm.txt | 54 ++++++++++++++++++++++ - 1 file changed, 54 insertions(+) - create mode 100644 Documentation/devicetree/bindings/display/etnaviv/etnaviv-drm.txt - -diff --git a/Documentation/devicetree/bindings/display/etnaviv/etnaviv-drm.txt b/Documentation/devicetree/bindings/display/etnaviv/etnaviv-drm.txt -new file mode 100644 -index 0000000..ed5e0a7 ---- /dev/null -+++ b/Documentation/devicetree/bindings/display/etnaviv/etnaviv-drm.txt -@@ -0,0 +1,54 @@ -+Etnaviv DRM master device -+========================= -+ -+The Etnaviv DRM master device is a virtual device needed to list all -+Vivante GPU cores that comprise the GPU subsystem. -+ -+Required properties: -+- compatible: Should be one of -+ "fsl,imx-gpu-subsystem" -+ "marvell,dove-gpu-subsystem" -+- cores: Should contain a list of phandles pointing to Vivante GPU devices -+ -+example: -+ -+gpu-subsystem { -+ compatible = "fsl,imx-gpu-subsystem"; -+ cores = <&gpu_2d>, <&gpu_3d>; -+}; -+ -+ -+Vivante GPU core devices -+======================== -+ -+Required properties: -+- compatible: Should be "vivante,gc" -+ A more specific compatible is not needed, as the cores contain chip -+ identification registers at fixed locations, which provide all the -+ necessary information to the driver. -+- reg: should be register base and length as documented in the -+ datasheet -+- interrupts: Should contain the cores interrupt line -+- clocks: should contain one clock for entry in clock-names -+ see Documentation/devicetree/bindings/clock/clock-bindings.txt -+- clock-names: -+ - "bus": AXI/register clock -+ - "core": GPU core clock -+ - "shader": Shader clock (only required if GPU has feature PIPE_3D) -+ -+Optional properties: -+- power-domains: a power domain consumer specifier according to -+ Documentation/devicetree/bindings/power/power_domain.txt -+ -+example: -+ -+gpu_3d: gpu@00130000 { -+ compatible = "vivante,gc"; -+ reg = <0x00130000 0x4000>; -+ interrupts = <0 9 IRQ_TYPE_LEVEL_HIGH>; -+ clocks = <&clks IMX6QDL_CLK_GPU3D_AXI>, -+ <&clks IMX6QDL_CLK_GPU3D_CORE>, -+ <&clks IMX6QDL_CLK_GPU3D_SHADER>; -+ clock-names = "bus", "core", "shader"; -+ power-domains = <&gpc 1>; -+}; --- -2.6.4 - diff --git a/kernel/kernel/files/patches/mageia/gpu-drm-0003-drm-etnaviv-add-initial-etnaviv-DRM-driver.patch b/kernel/kernel/files/patches/mageia/gpu-drm-0003-drm-etnaviv-add-initial-etnaviv-DRM-driver.patch deleted file mode 100644 index d50562e0..00000000 --- a/kernel/kernel/files/patches/mageia/gpu-drm-0003-drm-etnaviv-add-initial-etnaviv-DRM-driver.patch +++ /dev/null @@ -1,7474 +0,0 @@ -From a8c21a5451d831e67b7a6fb910f9ca8bc7b43554 Mon Sep 17 00:00:00 2001 -From: The etnaviv authors -Date: Thu, 3 Dec 2015 18:21:29 +0100 -Subject: [PATCH 3/4] drm/etnaviv: add initial etnaviv DRM driver - -This adds the etnaviv DRM driver and hooks it up in Makefiles -and Kconfig. - -Signed-off-by: Christian Gmeiner -Signed-off-by: Russell King -Signed-off-by: Lucas Stach -Acked-by: Daniel Vetter ---- - drivers/gpu/drm/Kconfig | 2 + - drivers/gpu/drm/Makefile | 1 + - drivers/gpu/drm/etnaviv/Kconfig | 20 + - drivers/gpu/drm/etnaviv/Makefile | 14 + - drivers/gpu/drm/etnaviv/cmdstream.xml.h | 218 ++++ - drivers/gpu/drm/etnaviv/common.xml.h | 249 ++++ - drivers/gpu/drm/etnaviv/etnaviv_buffer.c | 268 +++++ - drivers/gpu/drm/etnaviv/etnaviv_cmd_parser.c | 209 ++++ - drivers/gpu/drm/etnaviv/etnaviv_drv.c | 707 +++++++++++ - drivers/gpu/drm/etnaviv/etnaviv_drv.h | 161 +++ - drivers/gpu/drm/etnaviv/etnaviv_dump.c | 227 ++++ - drivers/gpu/drm/etnaviv/etnaviv_dump.h | 54 + - drivers/gpu/drm/etnaviv/etnaviv_gem.c | 897 ++++++++++++++ - drivers/gpu/drm/etnaviv/etnaviv_gem.h | 117 ++ - drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c | 122 ++ - drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c | 443 +++++++ - drivers/gpu/drm/etnaviv/etnaviv_gpu.c | 1644 ++++++++++++++++++++++++++ - drivers/gpu/drm/etnaviv/etnaviv_gpu.h | 209 ++++ - drivers/gpu/drm/etnaviv/etnaviv_iommu.c | 240 ++++ - drivers/gpu/drm/etnaviv/etnaviv_iommu.h | 28 + - drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c | 33 + - drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.h | 25 + - drivers/gpu/drm/etnaviv/etnaviv_mmu.c | 299 +++++ - drivers/gpu/drm/etnaviv/etnaviv_mmu.h | 71 ++ - drivers/gpu/drm/etnaviv/state.xml.h | 351 ++++++ - drivers/gpu/drm/etnaviv/state_hi.xml.h | 407 +++++++ - include/uapi/drm/etnaviv_drm.h | 222 ++++ - 27 files changed, 7238 insertions(+) - create mode 100644 drivers/gpu/drm/etnaviv/Kconfig - create mode 100644 drivers/gpu/drm/etnaviv/Makefile - create mode 100644 drivers/gpu/drm/etnaviv/cmdstream.xml.h - create mode 100644 drivers/gpu/drm/etnaviv/common.xml.h - create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_buffer.c - create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_cmd_parser.c - create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_drv.c - create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_drv.h - create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_dump.c - create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_dump.h - create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_gem.c - create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_gem.h - create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c - create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c - create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_gpu.c - create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_gpu.h - create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_iommu.c - create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_iommu.h - create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c - create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.h - create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_mmu.c - create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_mmu.h - create mode 100644 drivers/gpu/drm/etnaviv/state.xml.h - create mode 100644 drivers/gpu/drm/etnaviv/state_hi.xml.h - create mode 100644 include/uapi/drm/etnaviv_drm.h - -diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig -index c4bf9a1..b02ac62 100644 ---- a/drivers/gpu/drm/Kconfig -+++ b/drivers/gpu/drm/Kconfig -@@ -266,3 +266,5 @@ source "drivers/gpu/drm/amd/amdkfd/Kconfig" - source "drivers/gpu/drm/imx/Kconfig" - - source "drivers/gpu/drm/vc4/Kconfig" -+ -+source "drivers/gpu/drm/etnaviv/Kconfig" -diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile -index 1e9ff4c..f858aa2 100644 ---- a/drivers/gpu/drm/Makefile -+++ b/drivers/gpu/drm/Makefile -@@ -75,3 +75,4 @@ obj-y += i2c/ - obj-y += panel/ - obj-y += bridge/ - obj-$(CONFIG_DRM_FSL_DCU) += fsl-dcu/ -+obj-$(CONFIG_DRM_ETNAVIV) += etnaviv/ -diff --git a/drivers/gpu/drm/etnaviv/Kconfig b/drivers/gpu/drm/etnaviv/Kconfig -new file mode 100644 -index 0000000..2cde7a5 ---- /dev/null -+++ b/drivers/gpu/drm/etnaviv/Kconfig -@@ -0,0 +1,20 @@ -+ -+config DRM_ETNAVIV -+ tristate "ETNAVIV (DRM support for Vivante GPU IP cores)" -+ depends on DRM -+ depends on ARCH_MXC || ARCH_DOVE -+ select SHMEM -+ select TMPFS -+ select IOMMU_API -+ select IOMMU_SUPPORT -+ select WANT_DEV_COREDUMP -+ help -+ DRM driver for Vivante GPUs. -+ -+config DRM_ETNAVIV_REGISTER_LOGGING -+ bool "enable ETNAVIV register logging" -+ depends on DRM_ETNAVIV -+ help -+ Compile in support for logging register reads/writes in a format -+ that can be parsed by envytools demsm tool. If enabled, register -+ logging can be switched on via etnaviv.reglog=y module param. -diff --git a/drivers/gpu/drm/etnaviv/Makefile b/drivers/gpu/drm/etnaviv/Makefile -new file mode 100644 -index 0000000..1086e98 ---- /dev/null -+++ b/drivers/gpu/drm/etnaviv/Makefile -@@ -0,0 +1,14 @@ -+etnaviv-y := \ -+ etnaviv_buffer.o \ -+ etnaviv_cmd_parser.o \ -+ etnaviv_drv.o \ -+ etnaviv_dump.o \ -+ etnaviv_gem_prime.o \ -+ etnaviv_gem_submit.o \ -+ etnaviv_gem.o \ -+ etnaviv_gpu.o \ -+ etnaviv_iommu_v2.o \ -+ etnaviv_iommu.o \ -+ etnaviv_mmu.o -+ -+obj-$(CONFIG_DRM_ETNAVIV) += etnaviv.o -diff --git a/drivers/gpu/drm/etnaviv/cmdstream.xml.h b/drivers/gpu/drm/etnaviv/cmdstream.xml.h -new file mode 100644 -index 0000000..8c44ba9 ---- /dev/null -+++ b/drivers/gpu/drm/etnaviv/cmdstream.xml.h -@@ -0,0 +1,218 @@ -+#ifndef CMDSTREAM_XML -+#define CMDSTREAM_XML -+ -+/* Autogenerated file, DO NOT EDIT manually! -+ -+This file was generated by the rules-ng-ng headergen tool in this git repository: -+http://0x04.net/cgit/index.cgi/rules-ng-ng -+git clone git://0x04.net/rules-ng-ng -+ -+The rules-ng-ng source files this header was generated from are: -+- cmdstream.xml ( 12589 bytes, from 2014-02-17 14:57:56) -+- common.xml ( 18437 bytes, from 2015-03-25 11:27:41) -+ -+Copyright (C) 2014 -+*/ -+ -+ -+#define FE_OPCODE_LOAD_STATE 0x00000001 -+#define FE_OPCODE_END 0x00000002 -+#define FE_OPCODE_NOP 0x00000003 -+#define FE_OPCODE_DRAW_2D 0x00000004 -+#define FE_OPCODE_DRAW_PRIMITIVES 0x00000005 -+#define FE_OPCODE_DRAW_INDEXED_PRIMITIVES 0x00000006 -+#define FE_OPCODE_WAIT 0x00000007 -+#define FE_OPCODE_LINK 0x00000008 -+#define FE_OPCODE_STALL 0x00000009 -+#define FE_OPCODE_CALL 0x0000000a -+#define FE_OPCODE_RETURN 0x0000000b -+#define FE_OPCODE_CHIP_SELECT 0x0000000d -+#define PRIMITIVE_TYPE_POINTS 0x00000001 -+#define PRIMITIVE_TYPE_LINES 0x00000002 -+#define PRIMITIVE_TYPE_LINE_STRIP 0x00000003 -+#define PRIMITIVE_TYPE_TRIANGLES 0x00000004 -+#define PRIMITIVE_TYPE_TRIANGLE_STRIP 0x00000005 -+#define PRIMITIVE_TYPE_TRIANGLE_FAN 0x00000006 -+#define PRIMITIVE_TYPE_LINE_LOOP 0x00000007 -+#define PRIMITIVE_TYPE_QUADS 0x00000008 -+#define VIV_FE_LOAD_STATE 0x00000000 -+ -+#define VIV_FE_LOAD_STATE_HEADER 0x00000000 -+#define VIV_FE_LOAD_STATE_HEADER_OP__MASK 0xf8000000 -+#define VIV_FE_LOAD_STATE_HEADER_OP__SHIFT 27 -+#define VIV_FE_LOAD_STATE_HEADER_OP_LOAD_STATE 0x08000000 -+#define VIV_FE_LOAD_STATE_HEADER_FIXP 0x04000000 -+#define VIV_FE_LOAD_STATE_HEADER_COUNT__MASK 0x03ff0000 -+#define VIV_FE_LOAD_STATE_HEADER_COUNT__SHIFT 16 -+#define VIV_FE_LOAD_STATE_HEADER_COUNT(x) (((x) << VIV_FE_LOAD_STATE_HEADER_COUNT__SHIFT) & VIV_FE_LOAD_STATE_HEADER_COUNT__MASK) -+#define VIV_FE_LOAD_STATE_HEADER_OFFSET__MASK 0x0000ffff -+#define VIV_FE_LOAD_STATE_HEADER_OFFSET__SHIFT 0 -+#define VIV_FE_LOAD_STATE_HEADER_OFFSET(x) (((x) << VIV_FE_LOAD_STATE_HEADER_OFFSET__SHIFT) & VIV_FE_LOAD_STATE_HEADER_OFFSET__MASK) -+#define VIV_FE_LOAD_STATE_HEADER_OFFSET__SHR 2 -+ -+#define VIV_FE_END 0x00000000 -+ -+#define VIV_FE_END_HEADER 0x00000000 -+#define VIV_FE_END_HEADER_EVENT_ID__MASK 0x0000001f -+#define VIV_FE_END_HEADER_EVENT_ID__SHIFT 0 -+#define VIV_FE_END_HEADER_EVENT_ID(x) (((x) << VIV_FE_END_HEADER_EVENT_ID__SHIFT) & VIV_FE_END_HEADER_EVENT_ID__MASK) -+#define VIV_FE_END_HEADER_EVENT_ENABLE 0x00000100 -+#define VIV_FE_END_HEADER_OP__MASK 0xf8000000 -+#define VIV_FE_END_HEADER_OP__SHIFT 27 -+#define VIV_FE_END_HEADER_OP_END 0x10000000 -+ -+#define VIV_FE_NOP 0x00000000 -+ -+#define VIV_FE_NOP_HEADER 0x00000000 -+#define VIV_FE_NOP_HEADER_OP__MASK 0xf8000000 -+#define VIV_FE_NOP_HEADER_OP__SHIFT 27 -+#define VIV_FE_NOP_HEADER_OP_NOP 0x18000000 -+ -+#define VIV_FE_DRAW_2D 0x00000000 -+ -+#define VIV_FE_DRAW_2D_HEADER 0x00000000 -+#define VIV_FE_DRAW_2D_HEADER_COUNT__MASK 0x0000ff00 -+#define VIV_FE_DRAW_2D_HEADER_COUNT__SHIFT 8 -+#define VIV_FE_DRAW_2D_HEADER_COUNT(x) (((x) << VIV_FE_DRAW_2D_HEADER_COUNT__SHIFT) & VIV_FE_DRAW_2D_HEADER_COUNT__MASK) -+#define VIV_FE_DRAW_2D_HEADER_DATA_COUNT__MASK 0x07ff0000 -+#define VIV_FE_DRAW_2D_HEADER_DATA_COUNT__SHIFT 16 -+#define VIV_FE_DRAW_2D_HEADER_DATA_COUNT(x) (((x) << VIV_FE_DRAW_2D_HEADER_DATA_COUNT__SHIFT) & VIV_FE_DRAW_2D_HEADER_DATA_COUNT__MASK) -+#define VIV_FE_DRAW_2D_HEADER_OP__MASK 0xf8000000 -+#define VIV_FE_DRAW_2D_HEADER_OP__SHIFT 27 -+#define VIV_FE_DRAW_2D_HEADER_OP_DRAW_2D 0x20000000 -+ -+#define VIV_FE_DRAW_2D_TOP_LEFT 0x00000008 -+#define VIV_FE_DRAW_2D_TOP_LEFT_X__MASK 0x0000ffff -+#define VIV_FE_DRAW_2D_TOP_LEFT_X__SHIFT 0 -+#define VIV_FE_DRAW_2D_TOP_LEFT_X(x) (((x) << VIV_FE_DRAW_2D_TOP_LEFT_X__SHIFT) & VIV_FE_DRAW_2D_TOP_LEFT_X__MASK) -+#define VIV_FE_DRAW_2D_TOP_LEFT_Y__MASK 0xffff0000 -+#define VIV_FE_DRAW_2D_TOP_LEFT_Y__SHIFT 16 -+#define VIV_FE_DRAW_2D_TOP_LEFT_Y(x) (((x) << VIV_FE_DRAW_2D_TOP_LEFT_Y__SHIFT) & VIV_FE_DRAW_2D_TOP_LEFT_Y__MASK) -+ -+#define VIV_FE_DRAW_2D_BOTTOM_RIGHT 0x0000000c -+#define VIV_FE_DRAW_2D_BOTTOM_RIGHT_X__MASK 0x0000ffff -+#define VIV_FE_DRAW_2D_BOTTOM_RIGHT_X__SHIFT 0 -+#define VIV_FE_DRAW_2D_BOTTOM_RIGHT_X(x) (((x) << VIV_FE_DRAW_2D_BOTTOM_RIGHT_X__SHIFT) & VIV_FE_DRAW_2D_BOTTOM_RIGHT_X__MASK) -+#define VIV_FE_DRAW_2D_BOTTOM_RIGHT_Y__MASK 0xffff0000 -+#define VIV_FE_DRAW_2D_BOTTOM_RIGHT_Y__SHIFT 16 -+#define VIV_FE_DRAW_2D_BOTTOM_RIGHT_Y(x) (((x) << VIV_FE_DRAW_2D_BOTTOM_RIGHT_Y__SHIFT) & VIV_FE_DRAW_2D_BOTTOM_RIGHT_Y__MASK) -+ -+#define VIV_FE_DRAW_PRIMITIVES 0x00000000 -+ -+#define VIV_FE_DRAW_PRIMITIVES_HEADER 0x00000000 -+#define VIV_FE_DRAW_PRIMITIVES_HEADER_OP__MASK 0xf8000000 -+#define VIV_FE_DRAW_PRIMITIVES_HEADER_OP__SHIFT 27 -+#define VIV_FE_DRAW_PRIMITIVES_HEADER_OP_DRAW_PRIMITIVES 0x28000000 -+ -+#define VIV_FE_DRAW_PRIMITIVES_COMMAND 0x00000004 -+#define VIV_FE_DRAW_PRIMITIVES_COMMAND_TYPE__MASK 0x000000ff -+#define VIV_FE_DRAW_PRIMITIVES_COMMAND_TYPE__SHIFT 0 -+#define VIV_FE_DRAW_PRIMITIVES_COMMAND_TYPE(x) (((x) << VIV_FE_DRAW_PRIMITIVES_COMMAND_TYPE__SHIFT) & VIV_FE_DRAW_PRIMITIVES_COMMAND_TYPE__MASK) -+ -+#define VIV_FE_DRAW_PRIMITIVES_START 0x00000008 -+ -+#define VIV_FE_DRAW_PRIMITIVES_COUNT 0x0000000c -+ -+#define VIV_FE_DRAW_INDEXED_PRIMITIVES 0x00000000 -+ -+#define VIV_FE_DRAW_INDEXED_PRIMITIVES_HEADER 0x00000000 -+#define VIV_FE_DRAW_INDEXED_PRIMITIVES_HEADER_OP__MASK 0xf8000000 -+#define VIV_FE_DRAW_INDEXED_PRIMITIVES_HEADER_OP__SHIFT 27 -+#define VIV_FE_DRAW_INDEXED_PRIMITIVES_HEADER_OP_DRAW_INDEXED_PRIMITIVES 0x30000000 -+ -+#define VIV_FE_DRAW_INDEXED_PRIMITIVES_COMMAND 0x00000004 -+#define VIV_FE_DRAW_INDEXED_PRIMITIVES_COMMAND_TYPE__MASK 0x000000ff -+#define VIV_FE_DRAW_INDEXED_PRIMITIVES_COMMAND_TYPE__SHIFT 0 -+#define VIV_FE_DRAW_INDEXED_PRIMITIVES_COMMAND_TYPE(x) (((x) << VIV_FE_DRAW_INDEXED_PRIMITIVES_COMMAND_TYPE__SHIFT) & VIV_FE_DRAW_INDEXED_PRIMITIVES_COMMAND_TYPE__MASK) -+ -+#define VIV_FE_DRAW_INDEXED_PRIMITIVES_START 0x00000008 -+ -+#define VIV_FE_DRAW_INDEXED_PRIMITIVES_COUNT 0x0000000c -+ -+#define VIV_FE_DRAW_INDEXED_PRIMITIVES_OFFSET 0x00000010 -+ -+#define VIV_FE_WAIT 0x00000000 -+ -+#define VIV_FE_WAIT_HEADER 0x00000000 -+#define VIV_FE_WAIT_HEADER_DELAY__MASK 0x0000ffff -+#define VIV_FE_WAIT_HEADER_DELAY__SHIFT 0 -+#define VIV_FE_WAIT_HEADER_DELAY(x) (((x) << VIV_FE_WAIT_HEADER_DELAY__SHIFT) & VIV_FE_WAIT_HEADER_DELAY__MASK) -+#define VIV_FE_WAIT_HEADER_OP__MASK 0xf8000000 -+#define VIV_FE_WAIT_HEADER_OP__SHIFT 27 -+#define VIV_FE_WAIT_HEADER_OP_WAIT 0x38000000 -+ -+#define VIV_FE_LINK 0x00000000 -+ -+#define VIV_FE_LINK_HEADER 0x00000000 -+#define VIV_FE_LINK_HEADER_PREFETCH__MASK 0x0000ffff -+#define VIV_FE_LINK_HEADER_PREFETCH__SHIFT 0 -+#define VIV_FE_LINK_HEADER_PREFETCH(x) (((x) << VIV_FE_LINK_HEADER_PREFETCH__SHIFT) & VIV_FE_LINK_HEADER_PREFETCH__MASK) -+#define VIV_FE_LINK_HEADER_OP__MASK 0xf8000000 -+#define VIV_FE_LINK_HEADER_OP__SHIFT 27 -+#define VIV_FE_LINK_HEADER_OP_LINK 0x40000000 -+ -+#define VIV_FE_LINK_ADDRESS 0x00000004 -+ -+#define VIV_FE_STALL 0x00000000 -+ -+#define VIV_FE_STALL_HEADER 0x00000000 -+#define VIV_FE_STALL_HEADER_OP__MASK 0xf8000000 -+#define VIV_FE_STALL_HEADER_OP__SHIFT 27 -+#define VIV_FE_STALL_HEADER_OP_STALL 0x48000000 -+ -+#define VIV_FE_STALL_TOKEN 0x00000004 -+#define VIV_FE_STALL_TOKEN_FROM__MASK 0x0000001f -+#define VIV_FE_STALL_TOKEN_FROM__SHIFT 0 -+#define VIV_FE_STALL_TOKEN_FROM(x) (((x) << VIV_FE_STALL_TOKEN_FROM__SHIFT) & VIV_FE_STALL_TOKEN_FROM__MASK) -+#define VIV_FE_STALL_TOKEN_TO__MASK 0x00001f00 -+#define VIV_FE_STALL_TOKEN_TO__SHIFT 8 -+#define VIV_FE_STALL_TOKEN_TO(x) (((x) << VIV_FE_STALL_TOKEN_TO__SHIFT) & VIV_FE_STALL_TOKEN_TO__MASK) -+ -+#define VIV_FE_CALL 0x00000000 -+ -+#define VIV_FE_CALL_HEADER 0x00000000 -+#define VIV_FE_CALL_HEADER_PREFETCH__MASK 0x0000ffff -+#define VIV_FE_CALL_HEADER_PREFETCH__SHIFT 0 -+#define VIV_FE_CALL_HEADER_PREFETCH(x) (((x) << VIV_FE_CALL_HEADER_PREFETCH__SHIFT) & VIV_FE_CALL_HEADER_PREFETCH__MASK) -+#define VIV_FE_CALL_HEADER_OP__MASK 0xf8000000 -+#define VIV_FE_CALL_HEADER_OP__SHIFT 27 -+#define VIV_FE_CALL_HEADER_OP_CALL 0x50000000 -+ -+#define VIV_FE_CALL_ADDRESS 0x00000004 -+ -+#define VIV_FE_CALL_RETURN_PREFETCH 0x00000008 -+ -+#define VIV_FE_CALL_RETURN_ADDRESS 0x0000000c -+ -+#define VIV_FE_RETURN 0x00000000 -+ -+#define VIV_FE_RETURN_HEADER 0x00000000 -+#define VIV_FE_RETURN_HEADER_OP__MASK 0xf8000000 -+#define VIV_FE_RETURN_HEADER_OP__SHIFT 27 -+#define VIV_FE_RETURN_HEADER_OP_RETURN 0x58000000 -+ -+#define VIV_FE_CHIP_SELECT 0x00000000 -+ -+#define VIV_FE_CHIP_SELECT_HEADER 0x00000000 -+#define VIV_FE_CHIP_SELECT_HEADER_OP__MASK 0xf8000000 -+#define VIV_FE_CHIP_SELECT_HEADER_OP__SHIFT 27 -+#define VIV_FE_CHIP_SELECT_HEADER_OP_CHIP_SELECT 0x68000000 -+#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP15 0x00008000 -+#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP14 0x00004000 -+#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP13 0x00002000 -+#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP12 0x00001000 -+#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP11 0x00000800 -+#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP10 0x00000400 -+#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP9 0x00000200 -+#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP8 0x00000100 -+#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP7 0x00000080 -+#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP6 0x00000040 -+#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP5 0x00000020 -+#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP4 0x00000010 -+#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP3 0x00000008 -+#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP2 0x00000004 -+#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP1 0x00000002 -+#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP0 0x00000001 -+ -+ -+#endif /* CMDSTREAM_XML */ -diff --git a/drivers/gpu/drm/etnaviv/common.xml.h b/drivers/gpu/drm/etnaviv/common.xml.h -new file mode 100644 -index 0000000..9e585d5 ---- /dev/null -+++ b/drivers/gpu/drm/etnaviv/common.xml.h -@@ -0,0 +1,249 @@ -+#ifndef COMMON_XML -+#define COMMON_XML -+ -+/* Autogenerated file, DO NOT EDIT manually! -+ -+This file was generated by the rules-ng-ng headergen tool in this git repository: -+http://0x04.net/cgit/index.cgi/rules-ng-ng -+git clone git://0x04.net/rules-ng-ng -+ -+The rules-ng-ng source files this header was generated from are: -+- state_vg.xml ( 5973 bytes, from 2015-03-25 11:26:01) -+- common.xml ( 18437 bytes, from 2015-03-25 11:27:41) -+ -+Copyright (C) 2015 -+*/ -+ -+ -+#define PIPE_ID_PIPE_3D 0x00000000 -+#define PIPE_ID_PIPE_2D 0x00000001 -+#define SYNC_RECIPIENT_FE 0x00000001 -+#define SYNC_RECIPIENT_RA 0x00000005 -+#define SYNC_RECIPIENT_PE 0x00000007 -+#define SYNC_RECIPIENT_DE 0x0000000b -+#define SYNC_RECIPIENT_VG 0x0000000f -+#define SYNC_RECIPIENT_TESSELATOR 0x00000010 -+#define SYNC_RECIPIENT_VG2 0x00000011 -+#define SYNC_RECIPIENT_TESSELATOR2 0x00000012 -+#define SYNC_RECIPIENT_VG3 0x00000013 -+#define SYNC_RECIPIENT_TESSELATOR3 0x00000014 -+#define ENDIAN_MODE_NO_SWAP 0x00000000 -+#define ENDIAN_MODE_SWAP_16 0x00000001 -+#define ENDIAN_MODE_SWAP_32 0x00000002 -+#define chipModel_GC300 0x00000300 -+#define chipModel_GC320 0x00000320 -+#define chipModel_GC350 0x00000350 -+#define chipModel_GC355 0x00000355 -+#define chipModel_GC400 0x00000400 -+#define chipModel_GC410 0x00000410 -+#define chipModel_GC420 0x00000420 -+#define chipModel_GC450 0x00000450 -+#define chipModel_GC500 0x00000500 -+#define chipModel_GC530 0x00000530 -+#define chipModel_GC600 0x00000600 -+#define chipModel_GC700 0x00000700 -+#define chipModel_GC800 0x00000800 -+#define chipModel_GC860 0x00000860 -+#define chipModel_GC880 0x00000880 -+#define chipModel_GC1000 0x00001000 -+#define chipModel_GC2000 0x00002000 -+#define chipModel_GC2100 0x00002100 -+#define chipModel_GC4000 0x00004000 -+#define RGBA_BITS_R 0x00000001 -+#define RGBA_BITS_G 0x00000002 -+#define RGBA_BITS_B 0x00000004 -+#define RGBA_BITS_A 0x00000008 -+#define chipFeatures_FAST_CLEAR 0x00000001 -+#define chipFeatures_SPECIAL_ANTI_ALIASING 0x00000002 -+#define chipFeatures_PIPE_3D 0x00000004 -+#define chipFeatures_DXT_TEXTURE_COMPRESSION 0x00000008 -+#define chipFeatures_DEBUG_MODE 0x00000010 -+#define chipFeatures_Z_COMPRESSION 0x00000020 -+#define chipFeatures_YUV420_SCALER 0x00000040 -+#define chipFeatures_MSAA 0x00000080 -+#define chipFeatures_DC 0x00000100 -+#define chipFeatures_PIPE_2D 0x00000200 -+#define chipFeatures_ETC1_TEXTURE_COMPRESSION 0x00000400 -+#define chipFeatures_FAST_SCALER 0x00000800 -+#define chipFeatures_HIGH_DYNAMIC_RANGE 0x00001000 -+#define chipFeatures_YUV420_TILER 0x00002000 -+#define chipFeatures_MODULE_CG 0x00004000 -+#define chipFeatures_MIN_AREA 0x00008000 -+#define chipFeatures_NO_EARLY_Z 0x00010000 -+#define chipFeatures_NO_422_TEXTURE 0x00020000 -+#define chipFeatures_BUFFER_INTERLEAVING 0x00040000 -+#define chipFeatures_BYTE_WRITE_2D 0x00080000 -+#define chipFeatures_NO_SCALER 0x00100000 -+#define chipFeatures_YUY2_AVERAGING 0x00200000 -+#define chipFeatures_HALF_PE_CACHE 0x00400000 -+#define chipFeatures_HALF_TX_CACHE 0x00800000 -+#define chipFeatures_YUY2_RENDER_TARGET 0x01000000 -+#define chipFeatures_MEM32 0x02000000 -+#define chipFeatures_PIPE_VG 0x04000000 -+#define chipFeatures_VGTS 0x08000000 -+#define chipFeatures_FE20 0x10000000 -+#define chipFeatures_BYTE_WRITE_3D 0x20000000 -+#define chipFeatures_RS_YUV_TARGET 0x40000000 -+#define chipFeatures_32_BIT_INDICES 0x80000000 -+#define chipMinorFeatures0_FLIP_Y 0x00000001 -+#define chipMinorFeatures0_DUAL_RETURN_BUS 0x00000002 -+#define chipMinorFeatures0_ENDIANNESS_CONFIG 0x00000004 -+#define chipMinorFeatures0_TEXTURE_8K 0x00000008 -+#define chipMinorFeatures0_CORRECT_TEXTURE_CONVERTER 0x00000010 -+#define chipMinorFeatures0_SPECIAL_MSAA_LOD 0x00000020 -+#define chipMinorFeatures0_FAST_CLEAR_FLUSH 0x00000040 -+#define chipMinorFeatures0_2DPE20 0x00000080 -+#define chipMinorFeatures0_CORRECT_AUTO_DISABLE 0x00000100 -+#define chipMinorFeatures0_RENDERTARGET_8K 0x00000200 -+#define chipMinorFeatures0_2BITPERTILE 0x00000400 -+#define chipMinorFeatures0_SEPARATE_TILE_STATUS_WHEN_INTERLEAVED 0x00000800 -+#define chipMinorFeatures0_SUPER_TILED 0x00001000 -+#define chipMinorFeatures0_VG_20 0x00002000 -+#define chipMinorFeatures0_TS_EXTENDED_COMMANDS 0x00004000 -+#define chipMinorFeatures0_COMPRESSION_FIFO_FIXED 0x00008000 -+#define chipMinorFeatures0_HAS_SIGN_FLOOR_CEIL 0x00010000 -+#define chipMinorFeatures0_VG_FILTER 0x00020000 -+#define chipMinorFeatures0_VG_21 0x00040000 -+#define chipMinorFeatures0_SHADER_HAS_W 0x00080000 -+#define chipMinorFeatures0_HAS_SQRT_TRIG 0x00100000 -+#define chipMinorFeatures0_MORE_MINOR_FEATURES 0x00200000 -+#define chipMinorFeatures0_MC20 0x00400000 -+#define chipMinorFeatures0_MSAA_SIDEBAND 0x00800000 -+#define chipMinorFeatures0_BUG_FIXES0 0x01000000 -+#define chipMinorFeatures0_VAA 0x02000000 -+#define chipMinorFeatures0_BYPASS_IN_MSAA 0x04000000 -+#define chipMinorFeatures0_HZ 0x08000000 -+#define chipMinorFeatures0_NEW_TEXTURE 0x10000000 -+#define chipMinorFeatures0_2D_A8_TARGET 0x20000000 -+#define chipMinorFeatures0_CORRECT_STENCIL 0x40000000 -+#define chipMinorFeatures0_ENHANCE_VR 0x80000000 -+#define chipMinorFeatures1_RSUV_SWIZZLE 0x00000001 -+#define chipMinorFeatures1_V2_COMPRESSION 0x00000002 -+#define chipMinorFeatures1_VG_DOUBLE_BUFFER 0x00000004 -+#define chipMinorFeatures1_EXTRA_EVENT_STATES 0x00000008 -+#define chipMinorFeatures1_NO_STRIPING_NEEDED 0x00000010 -+#define chipMinorFeatures1_TEXTURE_STRIDE 0x00000020 -+#define chipMinorFeatures1_BUG_FIXES3 0x00000040 -+#define chipMinorFeatures1_AUTO_DISABLE 0x00000080 -+#define chipMinorFeatures1_AUTO_RESTART_TS 0x00000100 -+#define chipMinorFeatures1_DISABLE_PE_GATING 0x00000200 -+#define chipMinorFeatures1_L2_WINDOWING 0x00000400 -+#define chipMinorFeatures1_HALF_FLOAT 0x00000800 -+#define chipMinorFeatures1_PIXEL_DITHER 0x00001000 -+#define chipMinorFeatures1_TWO_STENCIL_REFERENCE 0x00002000 -+#define chipMinorFeatures1_EXTENDED_PIXEL_FORMAT 0x00004000 -+#define chipMinorFeatures1_CORRECT_MIN_MAX_DEPTH 0x00008000 -+#define chipMinorFeatures1_2D_DITHER 0x00010000 -+#define chipMinorFeatures1_BUG_FIXES5 0x00020000 -+#define chipMinorFeatures1_NEW_2D 0x00040000 -+#define chipMinorFeatures1_NEW_FP 0x00080000 -+#define chipMinorFeatures1_TEXTURE_HALIGN 0x00100000 -+#define chipMinorFeatures1_NON_POWER_OF_TWO 0x00200000 -+#define chipMinorFeatures1_LINEAR_TEXTURE_SUPPORT 0x00400000 -+#define chipMinorFeatures1_HALTI0 0x00800000 -+#define chipMinorFeatures1_CORRECT_OVERFLOW_VG 0x01000000 -+#define chipMinorFeatures1_NEGATIVE_LOG_FIX 0x02000000 -+#define chipMinorFeatures1_RESOLVE_OFFSET 0x04000000 -+#define chipMinorFeatures1_OK_TO_GATE_AXI_CLOCK 0x08000000 -+#define chipMinorFeatures1_MMU_VERSION 0x10000000 -+#define chipMinorFeatures1_WIDE_LINE 0x20000000 -+#define chipMinorFeatures1_BUG_FIXES6 0x40000000 -+#define chipMinorFeatures1_FC_FLUSH_STALL 0x80000000 -+#define chipMinorFeatures2_LINE_LOOP 0x00000001 -+#define chipMinorFeatures2_LOGIC_OP 0x00000002 -+#define chipMinorFeatures2_UNK2 0x00000004 -+#define chipMinorFeatures2_SUPERTILED_TEXTURE 0x00000008 -+#define chipMinorFeatures2_UNK4 0x00000010 -+#define chipMinorFeatures2_RECT_PRIMITIVE 0x00000020 -+#define chipMinorFeatures2_COMPOSITION 0x00000040 -+#define chipMinorFeatures2_CORRECT_AUTO_DISABLE_COUNT 0x00000080 -+#define chipMinorFeatures2_UNK8 0x00000100 -+#define chipMinorFeatures2_UNK9 0x00000200 -+#define chipMinorFeatures2_UNK10 0x00000400 -+#define chipMinorFeatures2_SAMPLERBASE_16 0x00000800 -+#define chipMinorFeatures2_UNK12 0x00001000 -+#define chipMinorFeatures2_UNK13 0x00002000 -+#define chipMinorFeatures2_UNK14 0x00004000 -+#define chipMinorFeatures2_EXTRA_TEXTURE_STATE 0x00008000 -+#define chipMinorFeatures2_FULL_DIRECTFB 0x00010000 -+#define chipMinorFeatures2_2D_TILING 0x00020000 -+#define chipMinorFeatures2_THREAD_WALKER_IN_PS 0x00040000 -+#define chipMinorFeatures2_TILE_FILLER 0x00080000 -+#define chipMinorFeatures2_UNK20 0x00100000 -+#define chipMinorFeatures2_2D_MULTI_SOURCE_BLIT 0x00200000 -+#define chipMinorFeatures2_UNK22 0x00400000 -+#define chipMinorFeatures2_UNK23 0x00800000 -+#define chipMinorFeatures2_UNK24 0x01000000 -+#define chipMinorFeatures2_MIXED_STREAMS 0x02000000 -+#define chipMinorFeatures2_2D_420_L2CACHE 0x04000000 -+#define chipMinorFeatures2_UNK27 0x08000000 -+#define chipMinorFeatures2_2D_NO_INDEX8_BRUSH 0x10000000 -+#define chipMinorFeatures2_TEXTURE_TILED_READ 0x20000000 -+#define chipMinorFeatures2_UNK30 0x40000000 -+#define chipMinorFeatures2_UNK31 0x80000000 -+#define chipMinorFeatures3_ROTATION_STALL_FIX 0x00000001 -+#define chipMinorFeatures3_UNK1 0x00000002 -+#define chipMinorFeatures3_2D_MULTI_SOURCE_BLT_EX 0x00000004 -+#define chipMinorFeatures3_UNK3 0x00000008 -+#define chipMinorFeatures3_UNK4 0x00000010 -+#define chipMinorFeatures3_UNK5 0x00000020 -+#define chipMinorFeatures3_UNK6 0x00000040 -+#define chipMinorFeatures3_UNK7 0x00000080 -+#define chipMinorFeatures3_UNK8 0x00000100 -+#define chipMinorFeatures3_UNK9 0x00000200 -+#define chipMinorFeatures3_BUG_FIXES10 0x00000400 -+#define chipMinorFeatures3_UNK11 0x00000800 -+#define chipMinorFeatures3_BUG_FIXES11 0x00001000 -+#define chipMinorFeatures3_UNK13 0x00002000 -+#define chipMinorFeatures3_UNK14 0x00004000 -+#define chipMinorFeatures3_UNK15 0x00008000 -+#define chipMinorFeatures3_UNK16 0x00010000 -+#define chipMinorFeatures3_UNK17 0x00020000 -+#define chipMinorFeatures3_UNK18 0x00040000 -+#define chipMinorFeatures3_UNK19 0x00080000 -+#define chipMinorFeatures3_UNK20 0x00100000 -+#define chipMinorFeatures3_UNK21 0x00200000 -+#define chipMinorFeatures3_UNK22 0x00400000 -+#define chipMinorFeatures3_UNK23 0x00800000 -+#define chipMinorFeatures3_UNK24 0x01000000 -+#define chipMinorFeatures3_UNK25 0x02000000 -+#define chipMinorFeatures3_UNK26 0x04000000 -+#define chipMinorFeatures3_UNK27 0x08000000 -+#define chipMinorFeatures3_UNK28 0x10000000 -+#define chipMinorFeatures3_UNK29 0x20000000 -+#define chipMinorFeatures3_UNK30 0x40000000 -+#define chipMinorFeatures3_UNK31 0x80000000 -+#define chipMinorFeatures4_UNK0 0x00000001 -+#define chipMinorFeatures4_UNK1 0x00000002 -+#define chipMinorFeatures4_UNK2 0x00000004 -+#define chipMinorFeatures4_UNK3 0x00000008 -+#define chipMinorFeatures4_UNK4 0x00000010 -+#define chipMinorFeatures4_UNK5 0x00000020 -+#define chipMinorFeatures4_UNK6 0x00000040 -+#define chipMinorFeatures4_UNK7 0x00000080 -+#define chipMinorFeatures4_UNK8 0x00000100 -+#define chipMinorFeatures4_UNK9 0x00000200 -+#define chipMinorFeatures4_UNK10 0x00000400 -+#define chipMinorFeatures4_UNK11 0x00000800 -+#define chipMinorFeatures4_UNK12 0x00001000 -+#define chipMinorFeatures4_UNK13 0x00002000 -+#define chipMinorFeatures4_UNK14 0x00004000 -+#define chipMinorFeatures4_UNK15 0x00008000 -+#define chipMinorFeatures4_UNK16 0x00010000 -+#define chipMinorFeatures4_UNK17 0x00020000 -+#define chipMinorFeatures4_UNK18 0x00040000 -+#define chipMinorFeatures4_UNK19 0x00080000 -+#define chipMinorFeatures4_UNK20 0x00100000 -+#define chipMinorFeatures4_UNK21 0x00200000 -+#define chipMinorFeatures4_UNK22 0x00400000 -+#define chipMinorFeatures4_UNK23 0x00800000 -+#define chipMinorFeatures4_UNK24 0x01000000 -+#define chipMinorFeatures4_UNK25 0x02000000 -+#define chipMinorFeatures4_UNK26 0x04000000 -+#define chipMinorFeatures4_UNK27 0x08000000 -+#define chipMinorFeatures4_UNK28 0x10000000 -+#define chipMinorFeatures4_UNK29 0x20000000 -+#define chipMinorFeatures4_UNK30 0x40000000 -+#define chipMinorFeatures4_UNK31 0x80000000 -+ -+#endif /* COMMON_XML */ -diff --git a/drivers/gpu/drm/etnaviv/etnaviv_buffer.c b/drivers/gpu/drm/etnaviv/etnaviv_buffer.c -new file mode 100644 -index 0000000..332c55e ---- /dev/null -+++ b/drivers/gpu/drm/etnaviv/etnaviv_buffer.c -@@ -0,0 +1,268 @@ -+/* -+ * Copyright (C) 2014 Etnaviv Project -+ * Author: Christian Gmeiner -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License version 2 as published by -+ * the Free Software Foundation. -+ * -+ * 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 . -+ */ -+ -+#include "etnaviv_gpu.h" -+#include "etnaviv_gem.h" -+#include "etnaviv_mmu.h" -+ -+#include "common.xml.h" -+#include "state.xml.h" -+#include "cmdstream.xml.h" -+ -+/* -+ * Command Buffer helper: -+ */ -+ -+ -+static inline void OUT(struct etnaviv_cmdbuf *buffer, u32 data) -+{ -+ u32 *vaddr = (u32 *)buffer->vaddr; -+ -+ BUG_ON(buffer->user_size >= buffer->size); -+ -+ vaddr[buffer->user_size / 4] = data; -+ buffer->user_size += 4; -+} -+ -+static inline void CMD_LOAD_STATE(struct etnaviv_cmdbuf *buffer, -+ u32 reg, u32 value) -+{ -+ u32 index = reg >> VIV_FE_LOAD_STATE_HEADER_OFFSET__SHR; -+ -+ buffer->user_size = ALIGN(buffer->user_size, 8); -+ -+ /* write a register via cmd stream */ -+ OUT(buffer, VIV_FE_LOAD_STATE_HEADER_OP_LOAD_STATE | -+ VIV_FE_LOAD_STATE_HEADER_COUNT(1) | -+ VIV_FE_LOAD_STATE_HEADER_OFFSET(index)); -+ OUT(buffer, value); -+} -+ -+static inline void CMD_END(struct etnaviv_cmdbuf *buffer) -+{ -+ buffer->user_size = ALIGN(buffer->user_size, 8); -+ -+ OUT(buffer, VIV_FE_END_HEADER_OP_END); -+} -+ -+static inline void CMD_WAIT(struct etnaviv_cmdbuf *buffer) -+{ -+ buffer->user_size = ALIGN(buffer->user_size, 8); -+ -+ OUT(buffer, VIV_FE_WAIT_HEADER_OP_WAIT | 200); -+} -+ -+static inline void CMD_LINK(struct etnaviv_cmdbuf *buffer, -+ u16 prefetch, u32 address) -+{ -+ buffer->user_size = ALIGN(buffer->user_size, 8); -+ -+ OUT(buffer, VIV_FE_LINK_HEADER_OP_LINK | -+ VIV_FE_LINK_HEADER_PREFETCH(prefetch)); -+ OUT(buffer, address); -+} -+ -+static inline void CMD_STALL(struct etnaviv_cmdbuf *buffer, -+ u32 from, u32 to) -+{ -+ buffer->user_size = ALIGN(buffer->user_size, 8); -+ -+ OUT(buffer, VIV_FE_STALL_HEADER_OP_STALL); -+ OUT(buffer, VIV_FE_STALL_TOKEN_FROM(from) | VIV_FE_STALL_TOKEN_TO(to)); -+} -+ -+static void etnaviv_cmd_select_pipe(struct etnaviv_cmdbuf *buffer, u8 pipe) -+{ -+ u32 flush; -+ u32 stall; -+ -+ /* -+ * This assumes that if we're switching to 2D, we're switching -+ * away from 3D, and vice versa. Hence, if we're switching to -+ * the 2D core, we need to flush the 3D depth and color caches, -+ * otherwise we need to flush the 2D pixel engine cache. -+ */ -+ if (pipe == ETNA_PIPE_2D) -+ flush = VIVS_GL_FLUSH_CACHE_DEPTH | VIVS_GL_FLUSH_CACHE_COLOR; -+ else -+ flush = VIVS_GL_FLUSH_CACHE_PE2D; -+ -+ stall = VIVS_GL_SEMAPHORE_TOKEN_FROM(SYNC_RECIPIENT_FE) | -+ VIVS_GL_SEMAPHORE_TOKEN_TO(SYNC_RECIPIENT_PE); -+ -+ CMD_LOAD_STATE(buffer, VIVS_GL_FLUSH_CACHE, flush); -+ CMD_LOAD_STATE(buffer, VIVS_GL_SEMAPHORE_TOKEN, stall); -+ -+ CMD_STALL(buffer, SYNC_RECIPIENT_FE, SYNC_RECIPIENT_PE); -+ -+ CMD_LOAD_STATE(buffer, VIVS_GL_PIPE_SELECT, -+ VIVS_GL_PIPE_SELECT_PIPE(pipe)); -+} -+ -+static u32 gpu_va(struct etnaviv_gpu *gpu, struct etnaviv_cmdbuf *buf) -+{ -+ return buf->paddr - gpu->memory_base; -+} -+ -+static void etnaviv_buffer_dump(struct etnaviv_gpu *gpu, -+ struct etnaviv_cmdbuf *buf, u32 off, u32 len) -+{ -+ u32 size = buf->size; -+ u32 *ptr = buf->vaddr + off; -+ -+ dev_info(gpu->dev, "virt %p phys 0x%08x free 0x%08x\n", -+ ptr, gpu_va(gpu, buf) + off, size - len * 4 - off); -+ -+ print_hex_dump(KERN_INFO, "cmd ", DUMP_PREFIX_OFFSET, 16, 4, -+ ptr, len * 4, 0); -+} -+ -+u16 etnaviv_buffer_init(struct etnaviv_gpu *gpu) -+{ -+ struct etnaviv_cmdbuf *buffer = gpu->buffer; -+ -+ /* initialize buffer */ -+ buffer->user_size = 0; -+ -+ CMD_WAIT(buffer); -+ CMD_LINK(buffer, 2, gpu_va(gpu, buffer) + buffer->user_size - 4); -+ -+ return buffer->user_size / 8; -+} -+ -+void etnaviv_buffer_end(struct etnaviv_gpu *gpu) -+{ -+ struct etnaviv_cmdbuf *buffer = gpu->buffer; -+ -+ /* Replace the last WAIT with an END */ -+ buffer->user_size -= 16; -+ -+ CMD_END(buffer); -+ mb(); -+} -+ -+void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event, -+ struct etnaviv_cmdbuf *cmdbuf) -+{ -+ struct etnaviv_cmdbuf *buffer = gpu->buffer; -+ u32 *lw = buffer->vaddr + buffer->user_size - 16; -+ u32 back, link_target, link_size, reserve_size, extra_size = 0; -+ -+ if (drm_debug & DRM_UT_DRIVER) -+ etnaviv_buffer_dump(gpu, buffer, 0, 0x50); -+ -+ /* -+ * If we need to flush the MMU prior to submitting this buffer, we -+ * will need to append a mmu flush load state, followed by a new -+ * link to this buffer - a total of four additional words. -+ */ -+ if (gpu->mmu->need_flush || gpu->switch_context) { -+ /* link command */ -+ extra_size += 2; -+ /* flush command */ -+ if (gpu->mmu->need_flush) -+ extra_size += 2; -+ /* pipe switch commands */ -+ if (gpu->switch_context) -+ extra_size += 8; -+ } -+ -+ reserve_size = (6 + extra_size) * 4; -+ -+ /* -+ * if we are going to completely overflow the buffer, we need to wrap. -+ */ -+ if (buffer->user_size + reserve_size > buffer->size) -+ buffer->user_size = 0; -+ -+ /* save offset back into main buffer */ -+ back = buffer->user_size + reserve_size - 6 * 4; -+ link_target = gpu_va(gpu, buffer) + buffer->user_size; -+ link_size = 6; -+ -+ /* Skip over any extra instructions */ -+ link_target += extra_size * sizeof(u32); -+ -+ if (drm_debug & DRM_UT_DRIVER) -+ pr_info("stream link to 0x%08x @ 0x%08x %p\n", -+ link_target, gpu_va(gpu, cmdbuf), cmdbuf->vaddr); -+ -+ /* jump back from cmd to main buffer */ -+ CMD_LINK(cmdbuf, link_size, link_target); -+ -+ link_target = gpu_va(gpu, cmdbuf); -+ link_size = cmdbuf->size / 8; -+ -+ -+ -+ if (drm_debug & DRM_UT_DRIVER) { -+ print_hex_dump(KERN_INFO, "cmd ", DUMP_PREFIX_OFFSET, 16, 4, -+ cmdbuf->vaddr, cmdbuf->size, 0); -+ -+ pr_info("link op: %p\n", lw); -+ pr_info("link addr: %p\n", lw + 1); -+ pr_info("addr: 0x%08x\n", link_target); -+ pr_info("back: 0x%08x\n", gpu_va(gpu, buffer) + back); -+ pr_info("event: %d\n", event); -+ } -+ -+ if (gpu->mmu->need_flush || gpu->switch_context) { -+ u32 new_target = gpu_va(gpu, buffer) + buffer->user_size; -+ -+ if (gpu->mmu->need_flush) { -+ /* Add the MMU flush */ -+ CMD_LOAD_STATE(buffer, VIVS_GL_FLUSH_MMU, -+ VIVS_GL_FLUSH_MMU_FLUSH_FEMMU | -+ VIVS_GL_FLUSH_MMU_FLUSH_UNK1 | -+ VIVS_GL_FLUSH_MMU_FLUSH_UNK2 | -+ VIVS_GL_FLUSH_MMU_FLUSH_PEMMU | -+ VIVS_GL_FLUSH_MMU_FLUSH_UNK4); -+ -+ gpu->mmu->need_flush = false; -+ } -+ -+ if (gpu->switch_context) { -+ etnaviv_cmd_select_pipe(buffer, cmdbuf->exec_state); -+ gpu->switch_context = false; -+ } -+ -+ /* And the link to the first buffer */ -+ CMD_LINK(buffer, link_size, link_target); -+ -+ /* Update the link target to point to above instructions */ -+ link_target = new_target; -+ link_size = extra_size; -+ } -+ -+ /* trigger event */ -+ CMD_LOAD_STATE(buffer, VIVS_GL_EVENT, VIVS_GL_EVENT_EVENT_ID(event) | -+ VIVS_GL_EVENT_FROM_PE); -+ -+ /* append WAIT/LINK to main buffer */ -+ CMD_WAIT(buffer); -+ CMD_LINK(buffer, 2, gpu_va(gpu, buffer) + (buffer->user_size - 4)); -+ -+ /* Change WAIT into a LINK command; write the address first. */ -+ *(lw + 1) = link_target; -+ mb(); -+ *(lw) = VIV_FE_LINK_HEADER_OP_LINK | -+ VIV_FE_LINK_HEADER_PREFETCH(link_size); -+ mb(); -+ -+ if (drm_debug & DRM_UT_DRIVER) -+ etnaviv_buffer_dump(gpu, buffer, 0, 0x50); -+} -diff --git a/drivers/gpu/drm/etnaviv/etnaviv_cmd_parser.c b/drivers/gpu/drm/etnaviv/etnaviv_cmd_parser.c -new file mode 100644 -index 0000000..dcfd565 ---- /dev/null -+++ b/drivers/gpu/drm/etnaviv/etnaviv_cmd_parser.c -@@ -0,0 +1,209 @@ -+/* -+ * Copyright (C) 2015 Etnaviv Project -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License version 2 as published by -+ * the Free Software Foundation. -+ * -+ * 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 . -+ */ -+ -+#include -+ -+#include "etnaviv_gem.h" -+#include "etnaviv_gpu.h" -+ -+#include "cmdstream.xml.h" -+ -+#define EXTRACT(val, field) (((val) & field##__MASK) >> field##__SHIFT) -+ -+struct etna_validation_state { -+ struct etnaviv_gpu *gpu; -+ const struct drm_etnaviv_gem_submit_reloc *relocs; -+ unsigned int num_relocs; -+ u32 *start; -+}; -+ -+static const struct { -+ u16 offset; -+ u16 size; -+} etnaviv_sensitive_states[] __initconst = { -+#define ST(start, num) { (start) >> 2, (num) } -+ /* 2D */ -+ ST(0x1200, 1), -+ ST(0x1228, 1), -+ ST(0x1238, 1), -+ ST(0x1284, 1), -+ ST(0x128c, 1), -+ ST(0x1304, 1), -+ ST(0x1310, 1), -+ ST(0x1318, 1), -+ ST(0x12800, 4), -+ ST(0x128a0, 4), -+ ST(0x128c0, 4), -+ ST(0x12970, 4), -+ ST(0x12a00, 8), -+ ST(0x12b40, 8), -+ ST(0x12b80, 8), -+ ST(0x12ce0, 8), -+ /* 3D */ -+ ST(0x0644, 1), -+ ST(0x064c, 1), -+ ST(0x0680, 8), -+ ST(0x1410, 1), -+ ST(0x1430, 1), -+ ST(0x1458, 1), -+ ST(0x1460, 8), -+ ST(0x1480, 8), -+ ST(0x1500, 8), -+ ST(0x1520, 8), -+ ST(0x1608, 1), -+ ST(0x1610, 1), -+ ST(0x1658, 1), -+ ST(0x165c, 1), -+ ST(0x1664, 1), -+ ST(0x1668, 1), -+ ST(0x16a4, 1), -+ ST(0x16c0, 8), -+ ST(0x16e0, 8), -+ ST(0x1740, 8), -+ ST(0x2400, 14 * 16), -+ ST(0x10800, 32 * 16), -+#undef ST -+}; -+ -+#define ETNAVIV_STATES_SIZE (VIV_FE_LOAD_STATE_HEADER_OFFSET__MASK + 1u) -+static DECLARE_BITMAP(etnaviv_states, ETNAVIV_STATES_SIZE); -+ -+void __init etnaviv_validate_init(void) -+{ -+ unsigned int i; -+ -+ for (i = 0; i < ARRAY_SIZE(etnaviv_sensitive_states); i++) -+ bitmap_set(etnaviv_states, etnaviv_sensitive_states[i].offset, -+ etnaviv_sensitive_states[i].size); -+} -+ -+static void etnaviv_warn_if_non_sensitive(struct etna_validation_state *state, -+ unsigned int buf_offset, unsigned int state_addr) -+{ -+ if (state->num_relocs && state->relocs->submit_offset < buf_offset) { -+ dev_warn_once(state->gpu->dev, -+ "%s: relocation for non-sensitive state 0x%x at offset %u\n", -+ __func__, state_addr, -+ state->relocs->submit_offset); -+ while (state->num_relocs && -+ state->relocs->submit_offset < buf_offset) { -+ state->relocs++; -+ state->num_relocs--; -+ } -+ } -+} -+ -+static bool etnaviv_validate_load_state(struct etna_validation_state *state, -+ u32 *ptr, unsigned int state_offset, unsigned int num) -+{ -+ unsigned int size = min(ETNAVIV_STATES_SIZE, state_offset + num); -+ unsigned int st_offset = state_offset, buf_offset; -+ -+ for_each_set_bit_from(st_offset, etnaviv_states, size) { -+ buf_offset = (ptr - state->start + -+ st_offset - state_offset) * 4; -+ -+ etnaviv_warn_if_non_sensitive(state, buf_offset, st_offset * 4); -+ if (state->num_relocs && -+ state->relocs->submit_offset == buf_offset) { -+ state->relocs++; -+ state->num_relocs--; -+ continue; -+ } -+ -+ dev_warn_ratelimited(state->gpu->dev, -+ "%s: load state touches restricted state 0x%x at offset %u\n", -+ __func__, st_offset * 4, buf_offset); -+ return false; -+ } -+ -+ if (state->num_relocs) { -+ buf_offset = (ptr - state->start + num) * 4; -+ etnaviv_warn_if_non_sensitive(state, buf_offset, st_offset * 4 + -+ state->relocs->submit_offset - -+ buf_offset); -+ } -+ -+ return true; -+} -+ -+static uint8_t cmd_length[32] = { -+ [FE_OPCODE_DRAW_PRIMITIVES] = 4, -+ [FE_OPCODE_DRAW_INDEXED_PRIMITIVES] = 6, -+ [FE_OPCODE_NOP] = 2, -+ [FE_OPCODE_STALL] = 2, -+}; -+ -+bool etnaviv_cmd_validate_one(struct etnaviv_gpu *gpu, u32 *stream, -+ unsigned int size, -+ struct drm_etnaviv_gem_submit_reloc *relocs, -+ unsigned int reloc_size) -+{ -+ struct etna_validation_state state; -+ u32 *buf = stream; -+ u32 *end = buf + size; -+ -+ state.gpu = gpu; -+ state.relocs = relocs; -+ state.num_relocs = reloc_size; -+ state.start = stream; -+ -+ while (buf < end) { -+ u32 cmd = *buf; -+ unsigned int len, n, off; -+ unsigned int op = cmd >> 27; -+ -+ switch (op) { -+ case FE_OPCODE_LOAD_STATE: -+ n = EXTRACT(cmd, VIV_FE_LOAD_STATE_HEADER_COUNT); -+ len = ALIGN(1 + n, 2); -+ if (buf + len > end) -+ break; -+ -+ off = EXTRACT(cmd, VIV_FE_LOAD_STATE_HEADER_OFFSET); -+ if (!etnaviv_validate_load_state(&state, buf + 1, -+ off, n)) -+ return false; -+ break; -+ -+ case FE_OPCODE_DRAW_2D: -+ n = EXTRACT(cmd, VIV_FE_DRAW_2D_HEADER_COUNT); -+ if (n == 0) -+ n = 256; -+ len = 2 + n * 2; -+ break; -+ -+ default: -+ len = cmd_length[op]; -+ if (len == 0) { -+ dev_err(gpu->dev, "%s: op %u not permitted at offset %tu\n", -+ __func__, op, buf - state.start); -+ return false; -+ } -+ break; -+ } -+ -+ buf += len; -+ } -+ -+ if (buf > end) { -+ dev_err(gpu->dev, "%s: commands overflow end of buffer: %tu > %u\n", -+ __func__, buf - state.start, size); -+ return false; -+ } -+ -+ return true; -+} -diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.c b/drivers/gpu/drm/etnaviv/etnaviv_drv.c -new file mode 100644 -index 0000000..5c89ebb ---- /dev/null -+++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.c -@@ -0,0 +1,707 @@ -+/* -+ * Copyright (C) 2015 Etnaviv Project -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License version 2 as published by -+ * the Free Software Foundation. -+ * -+ * 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 . -+ */ -+ -+#include -+#include -+ -+#include "etnaviv_drv.h" -+#include "etnaviv_gpu.h" -+#include "etnaviv_gem.h" -+#include "etnaviv_mmu.h" -+#include "etnaviv_gem.h" -+ -+#ifdef CONFIG_DRM_ETNAVIV_REGISTER_LOGGING -+static bool reglog; -+MODULE_PARM_DESC(reglog, "Enable register read/write logging"); -+module_param(reglog, bool, 0600); -+#else -+#define reglog 0 -+#endif -+ -+void __iomem *etnaviv_ioremap(struct platform_device *pdev, const char *name, -+ const char *dbgname) -+{ -+ struct resource *res; -+ void __iomem *ptr; -+ -+ if (name) -+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); -+ else -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ -+ ptr = devm_ioremap_resource(&pdev->dev, res); -+ if (IS_ERR(ptr)) { -+ dev_err(&pdev->dev, "failed to ioremap %s: %ld\n", name, -+ PTR_ERR(ptr)); -+ return ptr; -+ } -+ -+ if (reglog) -+ dev_printk(KERN_DEBUG, &pdev->dev, "IO:region %s 0x%p %08zx\n", -+ dbgname, ptr, (size_t)resource_size(res)); -+ -+ return ptr; -+} -+ -+void etnaviv_writel(u32 data, void __iomem *addr) -+{ -+ if (reglog) -+ printk(KERN_DEBUG "IO:W %p %08x\n", addr, data); -+ -+ writel(data, addr); -+} -+ -+u32 etnaviv_readl(const void __iomem *addr) -+{ -+ u32 val = readl(addr); -+ -+ if (reglog) -+ printk(KERN_DEBUG "IO:R %p %08x\n", addr, val); -+ -+ return val; -+} -+ -+/* -+ * DRM operations: -+ */ -+ -+ -+static void load_gpu(struct drm_device *dev) -+{ -+ struct etnaviv_drm_private *priv = dev->dev_private; -+ unsigned int i; -+ -+ for (i = 0; i < ETNA_MAX_PIPES; i++) { -+ struct etnaviv_gpu *g = priv->gpu[i]; -+ -+ if (g) { -+ int ret; -+ -+ ret = etnaviv_gpu_init(g); -+ if (ret) { -+ dev_err(g->dev, "hw init failed: %d\n", ret); -+ priv->gpu[i] = NULL; -+ } -+ } -+ } -+} -+ -+static int etnaviv_open(struct drm_device *dev, struct drm_file *file) -+{ -+ struct etnaviv_file_private *ctx; -+ -+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); -+ if (!ctx) -+ return -ENOMEM; -+ -+ file->driver_priv = ctx; -+ -+ return 0; -+} -+ -+static void etnaviv_preclose(struct drm_device *dev, struct drm_file *file) -+{ -+ struct etnaviv_drm_private *priv = dev->dev_private; -+ struct etnaviv_file_private *ctx = file->driver_priv; -+ unsigned int i; -+ -+ for (i = 0; i < ETNA_MAX_PIPES; i++) { -+ struct etnaviv_gpu *gpu = priv->gpu[i]; -+ -+ if (gpu) { -+ mutex_lock(&gpu->lock); -+ if (gpu->lastctx == ctx) -+ gpu->lastctx = NULL; -+ mutex_unlock(&gpu->lock); -+ } -+ } -+ -+ kfree(ctx); -+} -+ -+/* -+ * DRM debugfs: -+ */ -+ -+#ifdef CONFIG_DEBUG_FS -+static int etnaviv_gem_show(struct drm_device *dev, struct seq_file *m) -+{ -+ struct etnaviv_drm_private *priv = dev->dev_private; -+ -+ etnaviv_gem_describe_objects(priv, m); -+ -+ return 0; -+} -+ -+static int etnaviv_mm_show(struct drm_device *dev, struct seq_file *m) -+{ -+ int ret; -+ -+ read_lock(&dev->vma_offset_manager->vm_lock); -+ ret = drm_mm_dump_table(m, &dev->vma_offset_manager->vm_addr_space_mm); -+ read_unlock(&dev->vma_offset_manager->vm_lock); -+ -+ return ret; -+} -+ -+static int etnaviv_mmu_show(struct etnaviv_gpu *gpu, struct seq_file *m) -+{ -+ seq_printf(m, "Active Objects (%s):\n", dev_name(gpu->dev)); -+ -+ mutex_lock(&gpu->mmu->lock); -+ drm_mm_dump_table(m, &gpu->mmu->mm); -+ mutex_unlock(&gpu->mmu->lock); -+ -+ return 0; -+} -+ -+static void etnaviv_buffer_dump(struct etnaviv_gpu *gpu, struct seq_file *m) -+{ -+ struct etnaviv_cmdbuf *buf = gpu->buffer; -+ u32 size = buf->size; -+ u32 *ptr = buf->vaddr; -+ u32 i; -+ -+ seq_printf(m, "virt %p - phys 0x%llx - free 0x%08x\n", -+ buf->vaddr, (u64)buf->paddr, size - buf->user_size); -+ -+ for (i = 0; i < size / 4; i++) { -+ if (i && !(i % 4)) -+ seq_puts(m, "\n"); -+ if (i % 4 == 0) -+ seq_printf(m, "\t0x%p: ", ptr + i); -+ seq_printf(m, "%08x ", *(ptr + i)); -+ } -+ seq_puts(m, "\n"); -+} -+ -+static int etnaviv_ring_show(struct etnaviv_gpu *gpu, struct seq_file *m) -+{ -+ seq_printf(m, "Ring Buffer (%s): ", dev_name(gpu->dev)); -+ -+ mutex_lock(&gpu->lock); -+ etnaviv_buffer_dump(gpu, m); -+ mutex_unlock(&gpu->lock); -+ -+ return 0; -+} -+ -+static int show_unlocked(struct seq_file *m, void *arg) -+{ -+ struct drm_info_node *node = (struct drm_info_node *) m->private; -+ struct drm_device *dev = node->minor->dev; -+ int (*show)(struct drm_device *dev, struct seq_file *m) = -+ node->info_ent->data; -+ -+ return show(dev, m); -+} -+ -+static int show_each_gpu(struct seq_file *m, void *arg) -+{ -+ struct drm_info_node *node = (struct drm_info_node *) m->private; -+ struct drm_device *dev = node->minor->dev; -+ struct etnaviv_drm_private *priv = dev->dev_private; -+ struct etnaviv_gpu *gpu; -+ int (*show)(struct etnaviv_gpu *gpu, struct seq_file *m) = -+ node->info_ent->data; -+ unsigned int i; -+ int ret = 0; -+ -+ for (i = 0; i < ETNA_MAX_PIPES; i++) { -+ gpu = priv->gpu[i]; -+ if (!gpu) -+ continue; -+ -+ ret = show(gpu, m); -+ if (ret < 0) -+ break; -+ } -+ -+ return ret; -+} -+ -+static struct drm_info_list etnaviv_debugfs_list[] = { -+ {"gpu", show_each_gpu, 0, etnaviv_gpu_debugfs}, -+ {"gem", show_unlocked, 0, etnaviv_gem_show}, -+ { "mm", show_unlocked, 0, etnaviv_mm_show }, -+ {"mmu", show_each_gpu, 0, etnaviv_mmu_show}, -+ {"ring", show_each_gpu, 0, etnaviv_ring_show}, -+}; -+ -+static int etnaviv_debugfs_init(struct drm_minor *minor) -+{ -+ struct drm_device *dev = minor->dev; -+ int ret; -+ -+ ret = drm_debugfs_create_files(etnaviv_debugfs_list, -+ ARRAY_SIZE(etnaviv_debugfs_list), -+ minor->debugfs_root, minor); -+ -+ if (ret) { -+ dev_err(dev->dev, "could not install etnaviv_debugfs_list\n"); -+ return ret; -+ } -+ -+ return ret; -+} -+ -+static void etnaviv_debugfs_cleanup(struct drm_minor *minor) -+{ -+ drm_debugfs_remove_files(etnaviv_debugfs_list, -+ ARRAY_SIZE(etnaviv_debugfs_list), minor); -+} -+#endif -+ -+/* -+ * DRM ioctls: -+ */ -+ -+static int etnaviv_ioctl_get_param(struct drm_device *dev, void *data, -+ struct drm_file *file) -+{ -+ struct etnaviv_drm_private *priv = dev->dev_private; -+ struct drm_etnaviv_param *args = data; -+ struct etnaviv_gpu *gpu; -+ -+ if (args->pipe >= ETNA_MAX_PIPES) -+ return -EINVAL; -+ -+ gpu = priv->gpu[args->pipe]; -+ if (!gpu) -+ return -ENXIO; -+ -+ return etnaviv_gpu_get_param(gpu, args->param, &args->value); -+} -+ -+static int etnaviv_ioctl_gem_new(struct drm_device *dev, void *data, -+ struct drm_file *file) -+{ -+ struct drm_etnaviv_gem_new *args = data; -+ -+ if (args->flags & ~(ETNA_BO_CACHED | ETNA_BO_WC | ETNA_BO_UNCACHED | -+ ETNA_BO_FORCE_MMU)) -+ return -EINVAL; -+ -+ return etnaviv_gem_new_handle(dev, file, args->size, -+ args->flags, &args->handle); -+} -+ -+#define TS(t) ((struct timespec){ \ -+ .tv_sec = (t).tv_sec, \ -+ .tv_nsec = (t).tv_nsec \ -+}) -+ -+static int etnaviv_ioctl_gem_cpu_prep(struct drm_device *dev, void *data, -+ struct drm_file *file) -+{ -+ struct drm_etnaviv_gem_cpu_prep *args = data; -+ struct drm_gem_object *obj; -+ int ret; -+ -+ if (args->op & ~(ETNA_PREP_READ | ETNA_PREP_WRITE | ETNA_PREP_NOSYNC)) -+ return -EINVAL; -+ -+ obj = drm_gem_object_lookup(dev, file, args->handle); -+ if (!obj) -+ return -ENOENT; -+ -+ ret = etnaviv_gem_cpu_prep(obj, args->op, &TS(args->timeout)); -+ -+ drm_gem_object_unreference_unlocked(obj); -+ -+ return ret; -+} -+ -+static int etnaviv_ioctl_gem_cpu_fini(struct drm_device *dev, void *data, -+ struct drm_file *file) -+{ -+ struct drm_etnaviv_gem_cpu_fini *args = data; -+ struct drm_gem_object *obj; -+ int ret; -+ -+ if (args->flags) -+ return -EINVAL; -+ -+ obj = drm_gem_object_lookup(dev, file, args->handle); -+ if (!obj) -+ return -ENOENT; -+ -+ ret = etnaviv_gem_cpu_fini(obj); -+ -+ drm_gem_object_unreference_unlocked(obj); -+ -+ return ret; -+} -+ -+static int etnaviv_ioctl_gem_info(struct drm_device *dev, void *data, -+ struct drm_file *file) -+{ -+ struct drm_etnaviv_gem_info *args = data; -+ struct drm_gem_object *obj; -+ int ret; -+ -+ if (args->pad) -+ return -EINVAL; -+ -+ obj = drm_gem_object_lookup(dev, file, args->handle); -+ if (!obj) -+ return -ENOENT; -+ -+ ret = etnaviv_gem_mmap_offset(obj, &args->offset); -+ drm_gem_object_unreference_unlocked(obj); -+ -+ return ret; -+} -+ -+static int etnaviv_ioctl_wait_fence(struct drm_device *dev, void *data, -+ struct drm_file *file) -+{ -+ struct drm_etnaviv_wait_fence *args = data; -+ struct etnaviv_drm_private *priv = dev->dev_private; -+ struct timespec *timeout = &TS(args->timeout); -+ struct etnaviv_gpu *gpu; -+ -+ if (args->flags & ~(ETNA_WAIT_NONBLOCK)) -+ return -EINVAL; -+ -+ if (args->pipe >= ETNA_MAX_PIPES) -+ return -EINVAL; -+ -+ gpu = priv->gpu[args->pipe]; -+ if (!gpu) -+ return -ENXIO; -+ -+ if (args->flags & ETNA_WAIT_NONBLOCK) -+ timeout = NULL; -+ -+ return etnaviv_gpu_wait_fence_interruptible(gpu, args->fence, -+ timeout); -+} -+ -+static int etnaviv_ioctl_gem_userptr(struct drm_device *dev, void *data, -+ struct drm_file *file) -+{ -+ struct drm_etnaviv_gem_userptr *args = data; -+ int access; -+ -+ if (args->flags & ~(ETNA_USERPTR_READ|ETNA_USERPTR_WRITE) || -+ args->flags == 0) -+ return -EINVAL; -+ -+ if (offset_in_page(args->user_ptr | args->user_size) || -+ (uintptr_t)args->user_ptr != args->user_ptr || -+ (u32)args->user_size != args->user_size || -+ args->user_ptr & ~PAGE_MASK) -+ return -EINVAL; -+ -+ if (args->flags & ETNA_USERPTR_WRITE) -+ access = VERIFY_WRITE; -+ else -+ access = VERIFY_READ; -+ -+ if (!access_ok(access, (void __user *)(unsigned long)args->user_ptr, -+ args->user_size)) -+ return -EFAULT; -+ -+ return etnaviv_gem_new_userptr(dev, file, args->user_ptr, -+ args->user_size, args->flags, -+ &args->handle); -+} -+ -+static int etnaviv_ioctl_gem_wait(struct drm_device *dev, void *data, -+ struct drm_file *file) -+{ -+ struct etnaviv_drm_private *priv = dev->dev_private; -+ struct drm_etnaviv_gem_wait *args = data; -+ struct timespec *timeout = &TS(args->timeout); -+ struct drm_gem_object *obj; -+ struct etnaviv_gpu *gpu; -+ int ret; -+ -+ if (args->flags & ~(ETNA_WAIT_NONBLOCK)) -+ return -EINVAL; -+ -+ if (args->pipe >= ETNA_MAX_PIPES) -+ return -EINVAL; -+ -+ gpu = priv->gpu[args->pipe]; -+ if (!gpu) -+ return -ENXIO; -+ -+ obj = drm_gem_object_lookup(dev, file, args->handle); -+ if (!obj) -+ return -ENOENT; -+ -+ if (args->flags & ETNA_WAIT_NONBLOCK) -+ timeout = NULL; -+ -+ ret = etnaviv_gem_wait_bo(gpu, obj, timeout); -+ -+ drm_gem_object_unreference_unlocked(obj); -+ -+ return ret; -+} -+ -+static const struct drm_ioctl_desc etnaviv_ioctls[] = { -+#define ETNA_IOCTL(n, func, flags) \ -+ DRM_IOCTL_DEF_DRV(ETNAVIV_##n, etnaviv_ioctl_##func, flags) -+ ETNA_IOCTL(GET_PARAM, get_param, DRM_AUTH|DRM_RENDER_ALLOW), -+ ETNA_IOCTL(GEM_NEW, gem_new, DRM_AUTH|DRM_RENDER_ALLOW), -+ ETNA_IOCTL(GEM_INFO, gem_info, DRM_AUTH|DRM_RENDER_ALLOW), -+ ETNA_IOCTL(GEM_CPU_PREP, gem_cpu_prep, DRM_AUTH|DRM_RENDER_ALLOW), -+ ETNA_IOCTL(GEM_CPU_FINI, gem_cpu_fini, DRM_AUTH|DRM_RENDER_ALLOW), -+ ETNA_IOCTL(GEM_SUBMIT, gem_submit, DRM_AUTH|DRM_RENDER_ALLOW), -+ ETNA_IOCTL(WAIT_FENCE, wait_fence, DRM_AUTH|DRM_RENDER_ALLOW), -+ ETNA_IOCTL(GEM_USERPTR, gem_userptr, DRM_AUTH|DRM_RENDER_ALLOW), -+ ETNA_IOCTL(GEM_WAIT, gem_wait, DRM_AUTH|DRM_RENDER_ALLOW), -+}; -+ -+static const struct vm_operations_struct vm_ops = { -+ .fault = etnaviv_gem_fault, -+ .open = drm_gem_vm_open, -+ .close = drm_gem_vm_close, -+}; -+ -+static const struct file_operations fops = { -+ .owner = THIS_MODULE, -+ .open = drm_open, -+ .release = drm_release, -+ .unlocked_ioctl = drm_ioctl, -+#ifdef CONFIG_COMPAT -+ .compat_ioctl = drm_compat_ioctl, -+#endif -+ .poll = drm_poll, -+ .read = drm_read, -+ .llseek = no_llseek, -+ .mmap = etnaviv_gem_mmap, -+}; -+ -+static struct drm_driver etnaviv_drm_driver = { -+ .driver_features = DRIVER_HAVE_IRQ | -+ DRIVER_GEM | -+ DRIVER_PRIME | -+ DRIVER_RENDER, -+ .open = etnaviv_open, -+ .preclose = etnaviv_preclose, -+ .set_busid = drm_platform_set_busid, -+ .gem_free_object = etnaviv_gem_free_object, -+ .gem_vm_ops = &vm_ops, -+ .prime_handle_to_fd = drm_gem_prime_handle_to_fd, -+ .prime_fd_to_handle = drm_gem_prime_fd_to_handle, -+ .gem_prime_export = drm_gem_prime_export, -+ .gem_prime_import = drm_gem_prime_import, -+ .gem_prime_pin = etnaviv_gem_prime_pin, -+ .gem_prime_unpin = etnaviv_gem_prime_unpin, -+ .gem_prime_get_sg_table = etnaviv_gem_prime_get_sg_table, -+ .gem_prime_import_sg_table = etnaviv_gem_prime_import_sg_table, -+ .gem_prime_vmap = etnaviv_gem_prime_vmap, -+ .gem_prime_vunmap = etnaviv_gem_prime_vunmap, -+#ifdef CONFIG_DEBUG_FS -+ .debugfs_init = etnaviv_debugfs_init, -+ .debugfs_cleanup = etnaviv_debugfs_cleanup, -+#endif -+ .ioctls = etnaviv_ioctls, -+ .num_ioctls = DRM_ETNAVIV_NUM_IOCTLS, -+ .fops = &fops, -+ .name = "etnaviv", -+ .desc = "etnaviv DRM", -+ .date = "20151214", -+ .major = 1, -+ .minor = 0, -+}; -+ -+/* -+ * Platform driver: -+ */ -+static int etnaviv_bind(struct device *dev) -+{ -+ struct etnaviv_drm_private *priv; -+ struct drm_device *drm; -+ int ret; -+ -+ drm = drm_dev_alloc(&etnaviv_drm_driver, dev); -+ if (!drm) -+ return -ENOMEM; -+ -+ drm->platformdev = to_platform_device(dev); -+ -+ priv = kzalloc(sizeof(*priv), GFP_KERNEL); -+ if (!priv) { -+ dev_err(dev, "failed to allocate private data\n"); -+ ret = -ENOMEM; -+ goto out_unref; -+ } -+ drm->dev_private = priv; -+ -+ priv->wq = alloc_ordered_workqueue("etnaviv", 0); -+ if (!priv->wq) { -+ ret = -ENOMEM; -+ goto out_wq; -+ } -+ -+ mutex_init(&priv->gem_lock); -+ INIT_LIST_HEAD(&priv->gem_list); -+ priv->num_gpus = 0; -+ -+ dev_set_drvdata(dev, drm); -+ -+ ret = component_bind_all(dev, drm); -+ if (ret < 0) -+ goto out_bind; -+ -+ load_gpu(drm); -+ -+ ret = drm_dev_register(drm, 0); -+ if (ret) -+ goto out_register; -+ -+ return 0; -+ -+out_register: -+ component_unbind_all(dev, drm); -+out_bind: -+ flush_workqueue(priv->wq); -+ destroy_workqueue(priv->wq); -+out_wq: -+ kfree(priv); -+out_unref: -+ drm_dev_unref(drm); -+ -+ return ret; -+} -+ -+static void etnaviv_unbind(struct device *dev) -+{ -+ struct drm_device *drm = dev_get_drvdata(dev); -+ struct etnaviv_drm_private *priv = drm->dev_private; -+ -+ drm_dev_unregister(drm); -+ -+ flush_workqueue(priv->wq); -+ destroy_workqueue(priv->wq); -+ -+ component_unbind_all(dev, drm); -+ -+ drm->dev_private = NULL; -+ kfree(priv); -+ -+ drm_put_dev(drm); -+} -+ -+static const struct component_master_ops etnaviv_master_ops = { -+ .bind = etnaviv_bind, -+ .unbind = etnaviv_unbind, -+}; -+ -+static int compare_of(struct device *dev, void *data) -+{ -+ struct device_node *np = data; -+ -+ return dev->of_node == np; -+} -+ -+static int compare_str(struct device *dev, void *data) -+{ -+ return !strcmp(dev_name(dev), data); -+} -+ -+static int etnaviv_pdev_probe(struct platform_device *pdev) -+{ -+ struct device *dev = &pdev->dev; -+ struct device_node *node = dev->of_node; -+ struct component_match *match = NULL; -+ -+ dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); -+ -+ if (node) { -+ struct device_node *core_node; -+ int i; -+ -+ for (i = 0; ; i++) { -+ core_node = of_parse_phandle(node, "cores", i); -+ if (!core_node) -+ break; -+ -+ component_match_add(&pdev->dev, &match, compare_of, -+ core_node); -+ of_node_put(core_node); -+ } -+ } else if (dev->platform_data) { -+ char **names = dev->platform_data; -+ unsigned i; -+ -+ for (i = 0; names[i]; i++) -+ component_match_add(dev, &match, compare_str, names[i]); -+ } -+ -+ return component_master_add_with_match(dev, &etnaviv_master_ops, match); -+} -+ -+static int etnaviv_pdev_remove(struct platform_device *pdev) -+{ -+ component_master_del(&pdev->dev, &etnaviv_master_ops); -+ -+ return 0; -+} -+ -+static const struct of_device_id dt_match[] = { -+ { .compatible = "fsl,imx-gpu-subsystem" }, -+ { .compatible = "marvell,dove-gpu-subsystem" }, -+ {} -+}; -+MODULE_DEVICE_TABLE(of, dt_match); -+ -+static struct platform_driver etnaviv_platform_driver = { -+ .probe = etnaviv_pdev_probe, -+ .remove = etnaviv_pdev_remove, -+ .driver = { -+ .owner = THIS_MODULE, -+ .name = "etnaviv", -+ .of_match_table = dt_match, -+ }, -+}; -+ -+static int __init etnaviv_init(void) -+{ -+ int ret; -+ -+ etnaviv_validate_init(); -+ -+ ret = platform_driver_register(&etnaviv_gpu_driver); -+ if (ret != 0) -+ return ret; -+ -+ ret = platform_driver_register(&etnaviv_platform_driver); -+ if (ret != 0) -+ platform_driver_unregister(&etnaviv_gpu_driver); -+ -+ return ret; -+} -+module_init(etnaviv_init); -+ -+static void __exit etnaviv_exit(void) -+{ -+ platform_driver_unregister(&etnaviv_gpu_driver); -+ platform_driver_unregister(&etnaviv_platform_driver); -+} -+module_exit(etnaviv_exit); -+ -+MODULE_AUTHOR("Christian Gmeiner "); -+MODULE_AUTHOR("Russell King "); -+MODULE_AUTHOR("Lucas Stach "); -+MODULE_DESCRIPTION("etnaviv DRM Driver"); -+MODULE_LICENSE("GPL v2"); -+MODULE_ALIAS("platform:etnaviv"); -diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.h b/drivers/gpu/drm/etnaviv/etnaviv_drv.h -new file mode 100644 -index 0000000..d6bd438 ---- /dev/null -+++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.h -@@ -0,0 +1,161 @@ -+/* -+ * Copyright (C) 2015 Etnaviv Project -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License version 2 as published by -+ * the Free Software Foundation. -+ * -+ * 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 . -+ */ -+ -+#ifndef __ETNAVIV_DRV_H__ -+#define __ETNAVIV_DRV_H__ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+ -+struct etnaviv_cmdbuf; -+struct etnaviv_gpu; -+struct etnaviv_mmu; -+struct etnaviv_gem_object; -+struct etnaviv_gem_submit; -+ -+struct etnaviv_file_private { -+ /* currently we don't do anything useful with this.. but when -+ * per-context address spaces are supported we'd keep track of -+ * the context's page-tables here. -+ */ -+ int dummy; -+}; -+ -+struct etnaviv_drm_private { -+ int num_gpus; -+ struct etnaviv_gpu *gpu[ETNA_MAX_PIPES]; -+ -+ /* list of GEM objects: */ -+ struct mutex gem_lock; -+ struct list_head gem_list; -+ -+ struct workqueue_struct *wq; -+}; -+ -+static inline void etnaviv_queue_work(struct drm_device *dev, -+ struct work_struct *w) -+{ -+ struct etnaviv_drm_private *priv = dev->dev_private; -+ -+ queue_work(priv->wq, w); -+} -+ -+int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data, -+ struct drm_file *file); -+ -+int etnaviv_gem_mmap(struct file *filp, struct vm_area_struct *vma); -+int etnaviv_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf); -+int etnaviv_gem_mmap_offset(struct drm_gem_object *obj, u64 *offset); -+int etnaviv_gem_get_iova(struct etnaviv_gpu *gpu, -+ struct drm_gem_object *obj, u32 *iova); -+void etnaviv_gem_put_iova(struct etnaviv_gpu *gpu, struct drm_gem_object *obj); -+struct sg_table *etnaviv_gem_prime_get_sg_table(struct drm_gem_object *obj); -+void *etnaviv_gem_prime_vmap(struct drm_gem_object *obj); -+void etnaviv_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr); -+struct drm_gem_object *etnaviv_gem_prime_import_sg_table(struct drm_device *dev, -+ struct dma_buf_attachment *attach, struct sg_table *sg); -+int etnaviv_gem_prime_pin(struct drm_gem_object *obj); -+void etnaviv_gem_prime_unpin(struct drm_gem_object *obj); -+void *etnaviv_gem_vaddr(struct drm_gem_object *obj); -+int etnaviv_gem_cpu_prep(struct drm_gem_object *obj, u32 op, -+ struct timespec *timeout); -+int etnaviv_gem_cpu_fini(struct drm_gem_object *obj); -+void etnaviv_gem_free_object(struct drm_gem_object *obj); -+int etnaviv_gem_new_handle(struct drm_device *dev, struct drm_file *file, -+ u32 size, u32 flags, u32 *handle); -+struct drm_gem_object *etnaviv_gem_new_locked(struct drm_device *dev, -+ u32 size, u32 flags); -+struct drm_gem_object *etnaviv_gem_new(struct drm_device *dev, -+ u32 size, u32 flags); -+int etnaviv_gem_new_userptr(struct drm_device *dev, struct drm_file *file, -+ uintptr_t ptr, u32 size, u32 flags, u32 *handle); -+u16 etnaviv_buffer_init(struct etnaviv_gpu *gpu); -+void etnaviv_buffer_end(struct etnaviv_gpu *gpu); -+void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event, -+ struct etnaviv_cmdbuf *cmdbuf); -+void etnaviv_validate_init(void); -+bool etnaviv_cmd_validate_one(struct etnaviv_gpu *gpu, -+ u32 *stream, unsigned int size, -+ struct drm_etnaviv_gem_submit_reloc *relocs, unsigned int reloc_size); -+ -+#ifdef CONFIG_DEBUG_FS -+void etnaviv_gem_describe_objects(struct etnaviv_drm_private *priv, -+ struct seq_file *m); -+#endif -+ -+void __iomem *etnaviv_ioremap(struct platform_device *pdev, const char *name, -+ const char *dbgname); -+void etnaviv_writel(u32 data, void __iomem *addr); -+u32 etnaviv_readl(const void __iomem *addr); -+ -+#define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__) -+#define VERB(fmt, ...) if (0) DRM_DEBUG(fmt"\n", ##__VA_ARGS__) -+ -+/* -+ * Return the storage size of a structure with a variable length array. -+ * The array is nelem elements of elem_size, where the base structure -+ * is defined by base. If the size overflows size_t, return zero. -+ */ -+static inline size_t size_vstruct(size_t nelem, size_t elem_size, size_t base) -+{ -+ if (elem_size && nelem > (SIZE_MAX - base) / elem_size) -+ return 0; -+ return base + nelem * elem_size; -+} -+ -+/* returns true if fence a comes after fence b */ -+static inline bool fence_after(u32 a, u32 b) -+{ -+ return (s32)(a - b) > 0; -+} -+ -+static inline bool fence_after_eq(u32 a, u32 b) -+{ -+ return (s32)(a - b) >= 0; -+} -+ -+static inline unsigned long etnaviv_timeout_to_jiffies( -+ const struct timespec *timeout) -+{ -+ unsigned long timeout_jiffies = timespec_to_jiffies(timeout); -+ unsigned long start_jiffies = jiffies; -+ unsigned long remaining_jiffies; -+ -+ if (time_after(start_jiffies, timeout_jiffies)) -+ remaining_jiffies = 0; -+ else -+ remaining_jiffies = timeout_jiffies - start_jiffies; -+ -+ return remaining_jiffies; -+} -+ -+#endif /* __ETNAVIV_DRV_H__ */ -diff --git a/drivers/gpu/drm/etnaviv/etnaviv_dump.c b/drivers/gpu/drm/etnaviv/etnaviv_dump.c -new file mode 100644 -index 0000000..bf8fa85 ---- /dev/null -+++ b/drivers/gpu/drm/etnaviv/etnaviv_dump.c -@@ -0,0 +1,227 @@ -+/* -+ * Copyright (C) 2015 Etnaviv Project -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License version 2 as published by -+ * the Free Software Foundation. -+ * -+ * 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 . -+ */ -+ -+#include -+#include "etnaviv_dump.h" -+#include "etnaviv_gem.h" -+#include "etnaviv_gpu.h" -+#include "etnaviv_mmu.h" -+#include "state.xml.h" -+#include "state_hi.xml.h" -+ -+struct core_dump_iterator { -+ void *start; -+ struct etnaviv_dump_object_header *hdr; -+ void *data; -+}; -+ -+static const unsigned short etnaviv_dump_registers[] = { -+ VIVS_HI_AXI_STATUS, -+ VIVS_HI_CLOCK_CONTROL, -+ VIVS_HI_IDLE_STATE, -+ VIVS_HI_AXI_CONFIG, -+ VIVS_HI_INTR_ENBL, -+ VIVS_HI_CHIP_IDENTITY, -+ VIVS_HI_CHIP_FEATURE, -+ VIVS_HI_CHIP_MODEL, -+ VIVS_HI_CHIP_REV, -+ VIVS_HI_CHIP_DATE, -+ VIVS_HI_CHIP_TIME, -+ VIVS_HI_CHIP_MINOR_FEATURE_0, -+ VIVS_HI_CACHE_CONTROL, -+ VIVS_HI_AXI_CONTROL, -+ VIVS_PM_POWER_CONTROLS, -+ VIVS_PM_MODULE_CONTROLS, -+ VIVS_PM_MODULE_STATUS, -+ VIVS_PM_PULSE_EATER, -+ VIVS_MC_MMU_FE_PAGE_TABLE, -+ VIVS_MC_MMU_TX_PAGE_TABLE, -+ VIVS_MC_MMU_PE_PAGE_TABLE, -+ VIVS_MC_MMU_PEZ_PAGE_TABLE, -+ VIVS_MC_MMU_RA_PAGE_TABLE, -+ VIVS_MC_DEBUG_MEMORY, -+ VIVS_MC_MEMORY_BASE_ADDR_RA, -+ VIVS_MC_MEMORY_BASE_ADDR_FE, -+ VIVS_MC_MEMORY_BASE_ADDR_TX, -+ VIVS_MC_MEMORY_BASE_ADDR_PEZ, -+ VIVS_MC_MEMORY_BASE_ADDR_PE, -+ VIVS_MC_MEMORY_TIMING_CONTROL, -+ VIVS_MC_BUS_CONFIG, -+ VIVS_FE_DMA_STATUS, -+ VIVS_FE_DMA_DEBUG_STATE, -+ VIVS_FE_DMA_ADDRESS, -+ VIVS_FE_DMA_LOW, -+ VIVS_FE_DMA_HIGH, -+ VIVS_FE_AUTO_FLUSH, -+}; -+ -+static void etnaviv_core_dump_header(struct core_dump_iterator *iter, -+ u32 type, void *data_end) -+{ -+ struct etnaviv_dump_object_header *hdr = iter->hdr; -+ -+ hdr->magic = cpu_to_le32(ETDUMP_MAGIC); -+ hdr->type = cpu_to_le32(type); -+ hdr->file_offset = cpu_to_le32(iter->data - iter->start); -+ hdr->file_size = cpu_to_le32(data_end - iter->data); -+ -+ iter->hdr++; -+ iter->data += hdr->file_size; -+} -+ -+static void etnaviv_core_dump_registers(struct core_dump_iterator *iter, -+ struct etnaviv_gpu *gpu) -+{ -+ struct etnaviv_dump_registers *reg = iter->data; -+ unsigned int i; -+ -+ for (i = 0; i < ARRAY_SIZE(etnaviv_dump_registers); i++, reg++) { -+ reg->reg = etnaviv_dump_registers[i]; -+ reg->value = gpu_read(gpu, etnaviv_dump_registers[i]); -+ } -+ -+ etnaviv_core_dump_header(iter, ETDUMP_BUF_REG, reg); -+} -+ -+static void etnaviv_core_dump_mmu(struct core_dump_iterator *iter, -+ struct etnaviv_gpu *gpu, size_t mmu_size) -+{ -+ etnaviv_iommu_dump(gpu->mmu, iter->data); -+ -+ etnaviv_core_dump_header(iter, ETDUMP_BUF_MMU, iter->data + mmu_size); -+} -+ -+static void etnaviv_core_dump_mem(struct core_dump_iterator *iter, u32 type, -+ void *ptr, size_t size, u64 iova) -+{ -+ memcpy(iter->data, ptr, size); -+ -+ iter->hdr->iova = cpu_to_le64(iova); -+ -+ etnaviv_core_dump_header(iter, type, iter->data + size); -+} -+ -+void etnaviv_core_dump(struct etnaviv_gpu *gpu) -+{ -+ struct core_dump_iterator iter; -+ struct etnaviv_vram_mapping *vram; -+ struct etnaviv_gem_object *obj; -+ struct etnaviv_cmdbuf *cmd; -+ unsigned int n_obj, n_bomap_pages; -+ size_t file_size, mmu_size; -+ __le64 *bomap, *bomap_start; -+ -+ mmu_size = etnaviv_iommu_dump_size(gpu->mmu); -+ -+ /* We always dump registers, mmu, ring and end marker */ -+ n_obj = 4; -+ n_bomap_pages = 0; -+ file_size = ARRAY_SIZE(etnaviv_dump_registers) * -+ sizeof(struct etnaviv_dump_registers) + -+ mmu_size + gpu->buffer->size; -+ -+ /* Add in the active command buffers */ -+ list_for_each_entry(cmd, &gpu->active_cmd_list, node) { -+ file_size += cmd->size; -+ n_obj++; -+ } -+ -+ /* Add in the active buffer objects */ -+ list_for_each_entry(vram, &gpu->mmu->mappings, mmu_node) { -+ if (!vram->use) -+ continue; -+ -+ obj = vram->object; -+ file_size += obj->base.size; -+ n_bomap_pages += obj->base.size >> PAGE_SHIFT; -+ n_obj++; -+ } -+ -+ /* If we have any buffer objects, add a bomap object */ -+ if (n_bomap_pages) { -+ file_size += n_bomap_pages * sizeof(__le64); -+ n_obj++; -+ } -+ -+ /* Add the size of the headers */ -+ file_size += sizeof(*iter.hdr) * n_obj; -+ -+ /* Allocate the file in vmalloc memory, it's likely to be big */ -+ iter.start = vmalloc(file_size); -+ if (!iter.start) { -+ dev_warn(gpu->dev, "failed to allocate devcoredump file\n"); -+ return; -+ } -+ -+ /* Point the data member after the headers */ -+ iter.hdr = iter.start; -+ iter.data = &iter.hdr[n_obj]; -+ -+ memset(iter.hdr, 0, iter.data - iter.start); -+ -+ etnaviv_core_dump_registers(&iter, gpu); -+ etnaviv_core_dump_mmu(&iter, gpu, mmu_size); -+ etnaviv_core_dump_mem(&iter, ETDUMP_BUF_RING, gpu->buffer->vaddr, -+ gpu->buffer->size, gpu->buffer->paddr); -+ -+ list_for_each_entry(cmd, &gpu->active_cmd_list, node) -+ etnaviv_core_dump_mem(&iter, ETDUMP_BUF_CMD, cmd->vaddr, -+ cmd->size, cmd->paddr); -+ -+ /* Reserve space for the bomap */ -+ if (n_bomap_pages) { -+ bomap_start = bomap = iter.data; -+ memset(bomap, 0, sizeof(*bomap) * n_bomap_pages); -+ etnaviv_core_dump_header(&iter, ETDUMP_BUF_BOMAP, -+ bomap + n_bomap_pages); -+ } else { -+ /* Silence warning */ -+ bomap_start = bomap = NULL; -+ } -+ -+ list_for_each_entry(vram, &gpu->mmu->mappings, mmu_node) { -+ struct page **pages; -+ void *vaddr; -+ -+ if (vram->use == 0) -+ continue; -+ -+ obj = vram->object; -+ -+ pages = etnaviv_gem_get_pages(obj); -+ if (pages) { -+ int j; -+ -+ iter.hdr->data[0] = bomap - bomap_start; -+ -+ for (j = 0; j < obj->base.size >> PAGE_SHIFT; j++) -+ *bomap++ = cpu_to_le64(page_to_phys(*pages++)); -+ } -+ -+ iter.hdr->iova = cpu_to_le64(vram->iova); -+ -+ vaddr = etnaviv_gem_vaddr(&obj->base); -+ if (vaddr && !IS_ERR(vaddr)) -+ memcpy(iter.data, vaddr, obj->base.size); -+ -+ etnaviv_core_dump_header(&iter, ETDUMP_BUF_BO, iter.data + -+ obj->base.size); -+ } -+ -+ etnaviv_core_dump_header(&iter, ETDUMP_BUF_END, iter.data); -+ -+ dev_coredumpv(gpu->dev, iter.start, iter.data - iter.start, GFP_KERNEL); -+} -diff --git a/drivers/gpu/drm/etnaviv/etnaviv_dump.h b/drivers/gpu/drm/etnaviv/etnaviv_dump.h -new file mode 100644 -index 0000000..97f2f8d ---- /dev/null -+++ b/drivers/gpu/drm/etnaviv/etnaviv_dump.h -@@ -0,0 +1,54 @@ -+/* -+ * Copyright (C) 2015 Etnaviv Project -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License version 2 as published by -+ * the Free Software Foundation. -+ * -+ * 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 . -+ * -+ * Etnaviv devcoredump file definitions -+ */ -+#ifndef ETNAVIV_DUMP_H -+#define ETNAVIV_DUMP_H -+ -+#include -+ -+enum { -+ ETDUMP_MAGIC = 0x414e5445, -+ ETDUMP_BUF_REG = 0, -+ ETDUMP_BUF_MMU, -+ ETDUMP_BUF_RING, -+ ETDUMP_BUF_CMD, -+ ETDUMP_BUF_BOMAP, -+ ETDUMP_BUF_BO, -+ ETDUMP_BUF_END, -+}; -+ -+struct etnaviv_dump_object_header { -+ __le32 magic; -+ __le32 type; -+ __le32 file_offset; -+ __le32 file_size; -+ __le64 iova; -+ __le32 data[2]; -+}; -+ -+/* Registers object, an array of these */ -+struct etnaviv_dump_registers { -+ __le32 reg; -+ __le32 value; -+}; -+ -+#ifdef __KERNEL__ -+struct etnaviv_gpu; -+void etnaviv_core_dump(struct etnaviv_gpu *gpu); -+#endif -+ -+#endif -diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.c b/drivers/gpu/drm/etnaviv/etnaviv_gem.c -new file mode 100644 -index 0000000..8d6f859 ---- /dev/null -+++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.c -@@ -0,0 +1,897 @@ -+/* -+ * Copyright (C) 2015 Etnaviv Project -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License version 2 as published by -+ * the Free Software Foundation. -+ * -+ * 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 . -+ */ -+ -+#include -+#include -+ -+#include "etnaviv_drv.h" -+#include "etnaviv_gem.h" -+#include "etnaviv_gpu.h" -+#include "etnaviv_mmu.h" -+ -+static void etnaviv_gem_scatter_map(struct etnaviv_gem_object *etnaviv_obj) -+{ -+ struct drm_device *dev = etnaviv_obj->base.dev; -+ struct sg_table *sgt = etnaviv_obj->sgt; -+ -+ /* -+ * For non-cached buffers, ensure the new pages are clean -+ * because display controller, GPU, etc. are not coherent. -+ */ -+ if (etnaviv_obj->flags & ETNA_BO_CACHE_MASK) -+ dma_map_sg(dev->dev, sgt->sgl, sgt->nents, DMA_BIDIRECTIONAL); -+} -+ -+static void etnaviv_gem_scatterlist_unmap(struct etnaviv_gem_object *etnaviv_obj) -+{ -+ struct drm_device *dev = etnaviv_obj->base.dev; -+ struct sg_table *sgt = etnaviv_obj->sgt; -+ -+ /* -+ * For non-cached buffers, ensure the new pages are clean -+ * because display controller, GPU, etc. are not coherent: -+ * -+ * WARNING: The DMA API does not support concurrent CPU -+ * and device access to the memory area. With BIDIRECTIONAL, -+ * we will clean the cache lines which overlap the region, -+ * and invalidate all cache lines (partially) contained in -+ * the region. -+ * -+ * If you have dirty data in the overlapping cache lines, -+ * that will corrupt the GPU-written data. If you have -+ * written into the remainder of the region, this can -+ * discard those writes. -+ */ -+ if (etnaviv_obj->flags & ETNA_BO_CACHE_MASK) -+ dma_unmap_sg(dev->dev, sgt->sgl, sgt->nents, DMA_BIDIRECTIONAL); -+} -+ -+/* called with etnaviv_obj->lock held */ -+static int etnaviv_gem_shmem_get_pages(struct etnaviv_gem_object *etnaviv_obj) -+{ -+ struct drm_device *dev = etnaviv_obj->base.dev; -+ struct page **p = drm_gem_get_pages(&etnaviv_obj->base); -+ -+ if (IS_ERR(p)) { -+ dev_err(dev->dev, "could not get pages: %ld\n", PTR_ERR(p)); -+ return PTR_ERR(p); -+ } -+ -+ etnaviv_obj->pages = p; -+ -+ return 0; -+} -+ -+static void put_pages(struct etnaviv_gem_object *etnaviv_obj) -+{ -+ if (etnaviv_obj->sgt) { -+ etnaviv_gem_scatterlist_unmap(etnaviv_obj); -+ sg_free_table(etnaviv_obj->sgt); -+ kfree(etnaviv_obj->sgt); -+ etnaviv_obj->sgt = NULL; -+ } -+ if (etnaviv_obj->pages) { -+ drm_gem_put_pages(&etnaviv_obj->base, etnaviv_obj->pages, -+ true, false); -+ -+ etnaviv_obj->pages = NULL; -+ } -+} -+ -+struct page **etnaviv_gem_get_pages(struct etnaviv_gem_object *etnaviv_obj) -+{ -+ int ret; -+ -+ lockdep_assert_held(&etnaviv_obj->lock); -+ -+ if (!etnaviv_obj->pages) { -+ ret = etnaviv_obj->ops->get_pages(etnaviv_obj); -+ if (ret < 0) -+ return ERR_PTR(ret); -+ } -+ -+ if (!etnaviv_obj->sgt) { -+ struct drm_device *dev = etnaviv_obj->base.dev; -+ int npages = etnaviv_obj->base.size >> PAGE_SHIFT; -+ struct sg_table *sgt; -+ -+ sgt = drm_prime_pages_to_sg(etnaviv_obj->pages, npages); -+ if (IS_ERR(sgt)) { -+ dev_err(dev->dev, "failed to allocate sgt: %ld\n", -+ PTR_ERR(sgt)); -+ return ERR_CAST(sgt); -+ } -+ -+ etnaviv_obj->sgt = sgt; -+ -+ etnaviv_gem_scatter_map(etnaviv_obj); -+ } -+ -+ return etnaviv_obj->pages; -+} -+ -+void etnaviv_gem_put_pages(struct etnaviv_gem_object *etnaviv_obj) -+{ -+ lockdep_assert_held(&etnaviv_obj->lock); -+ /* when we start tracking the pin count, then do something here */ -+} -+ -+static int etnaviv_gem_mmap_obj(struct drm_gem_object *obj, -+ struct vm_area_struct *vma) -+{ -+ struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj); -+ pgprot_t vm_page_prot; -+ -+ vma->vm_flags &= ~VM_PFNMAP; -+ vma->vm_flags |= VM_MIXEDMAP; -+ -+ vm_page_prot = vm_get_page_prot(vma->vm_flags); -+ -+ if (etnaviv_obj->flags & ETNA_BO_WC) { -+ vma->vm_page_prot = pgprot_writecombine(vm_page_prot); -+ } else if (etnaviv_obj->flags & ETNA_BO_UNCACHED) { -+ vma->vm_page_prot = pgprot_noncached(vm_page_prot); -+ } else { -+ /* -+ * Shunt off cached objs to shmem file so they have their own -+ * address_space (so unmap_mapping_range does what we want, -+ * in particular in the case of mmap'd dmabufs) -+ */ -+ fput(vma->vm_file); -+ get_file(obj->filp); -+ vma->vm_pgoff = 0; -+ vma->vm_file = obj->filp; -+ -+ vma->vm_page_prot = vm_page_prot; -+ } -+ -+ return 0; -+} -+ -+int etnaviv_gem_mmap(struct file *filp, struct vm_area_struct *vma) -+{ -+ struct etnaviv_gem_object *obj; -+ int ret; -+ -+ ret = drm_gem_mmap(filp, vma); -+ if (ret) { -+ DBG("mmap failed: %d", ret); -+ return ret; -+ } -+ -+ obj = to_etnaviv_bo(vma->vm_private_data); -+ return etnaviv_gem_mmap_obj(vma->vm_private_data, vma); -+} -+ -+int etnaviv_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) -+{ -+ struct drm_gem_object *obj = vma->vm_private_data; -+ struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj); -+ struct page **pages, *page; -+ pgoff_t pgoff; -+ int ret; -+ -+ /* -+ * Make sure we don't parallel update on a fault, nor move or remove -+ * something from beneath our feet. Note that vm_insert_page() is -+ * specifically coded to take care of this, so we don't have to. -+ */ -+ ret = mutex_lock_interruptible(&etnaviv_obj->lock); -+ if (ret) -+ goto out; -+ -+ /* make sure we have pages attached now */ -+ pages = etnaviv_gem_get_pages(etnaviv_obj); -+ mutex_unlock(&etnaviv_obj->lock); -+ -+ if (IS_ERR(pages)) { -+ ret = PTR_ERR(pages); -+ goto out; -+ } -+ -+ /* We don't use vmf->pgoff since that has the fake offset: */ -+ pgoff = ((unsigned long)vmf->virtual_address - -+ vma->vm_start) >> PAGE_SHIFT; -+ -+ page = pages[pgoff]; -+ -+ VERB("Inserting %p pfn %lx, pa %lx", vmf->virtual_address, -+ page_to_pfn(page), page_to_pfn(page) << PAGE_SHIFT); -+ -+ ret = vm_insert_page(vma, (unsigned long)vmf->virtual_address, page); -+ -+out: -+ switch (ret) { -+ case -EAGAIN: -+ case 0: -+ case -ERESTARTSYS: -+ case -EINTR: -+ case -EBUSY: -+ /* -+ * EBUSY is ok: this just means that another thread -+ * already did the job. -+ */ -+ return VM_FAULT_NOPAGE; -+ case -ENOMEM: -+ return VM_FAULT_OOM; -+ default: -+ return VM_FAULT_SIGBUS; -+ } -+} -+ -+int etnaviv_gem_mmap_offset(struct drm_gem_object *obj, u64 *offset) -+{ -+ int ret; -+ -+ /* Make it mmapable */ -+ ret = drm_gem_create_mmap_offset(obj); -+ if (ret) -+ dev_err(obj->dev->dev, "could not allocate mmap offset\n"); -+ else -+ *offset = drm_vma_node_offset_addr(&obj->vma_node); -+ -+ return ret; -+} -+ -+static struct etnaviv_vram_mapping * -+etnaviv_gem_get_vram_mapping(struct etnaviv_gem_object *obj, -+ struct etnaviv_iommu *mmu) -+{ -+ struct etnaviv_vram_mapping *mapping; -+ -+ list_for_each_entry(mapping, &obj->vram_list, obj_node) { -+ if (mapping->mmu == mmu) -+ return mapping; -+ } -+ -+ return NULL; -+} -+ -+int etnaviv_gem_get_iova(struct etnaviv_gpu *gpu, -+ struct drm_gem_object *obj, u32 *iova) -+{ -+ struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj); -+ struct etnaviv_vram_mapping *mapping; -+ struct page **pages; -+ int ret = 0; -+ -+ mutex_lock(&etnaviv_obj->lock); -+ mapping = etnaviv_gem_get_vram_mapping(etnaviv_obj, gpu->mmu); -+ if (mapping) { -+ /* -+ * Holding the object lock prevents the use count changing -+ * beneath us. If the use count is zero, the MMU might be -+ * reaping this object, so take the lock and re-check that -+ * the MMU owns this mapping to close this race. -+ */ -+ if (mapping->use == 0) { -+ mutex_lock(&gpu->mmu->lock); -+ if (mapping->mmu == gpu->mmu) -+ mapping->use += 1; -+ else -+ mapping = NULL; -+ mutex_unlock(&gpu->mmu->lock); -+ if (mapping) -+ goto out; -+ } else { -+ mapping->use += 1; -+ goto out; -+ } -+ } -+ -+ pages = etnaviv_gem_get_pages(etnaviv_obj); -+ if (IS_ERR(pages)) { -+ ret = PTR_ERR(pages); -+ goto out; -+ } -+ -+ /* -+ * See if we have a reaped vram mapping we can re-use before -+ * allocating a fresh mapping. -+ */ -+ mapping = etnaviv_gem_get_vram_mapping(etnaviv_obj, NULL); -+ if (!mapping) { -+ mapping = kzalloc(sizeof(*mapping), GFP_KERNEL); -+ if (!mapping) -+ return -ENOMEM; -+ -+ INIT_LIST_HEAD(&mapping->scan_node); -+ mapping->object = etnaviv_obj; -+ } else { -+ list_del(&mapping->obj_node); -+ } -+ -+ mapping->mmu = gpu->mmu; -+ mapping->use = 1; -+ -+ ret = etnaviv_iommu_map_gem(gpu->mmu, etnaviv_obj, gpu->memory_base, -+ mapping); -+ if (ret < 0) -+ kfree(mapping); -+ else -+ list_add_tail(&mapping->obj_node, &etnaviv_obj->vram_list); -+ -+out: -+ mutex_unlock(&etnaviv_obj->lock); -+ -+ if (!ret) { -+ /* Take a reference on the object */ -+ drm_gem_object_reference(obj); -+ *iova = mapping->iova; -+ } -+ -+ return ret; -+} -+ -+void etnaviv_gem_put_iova(struct etnaviv_gpu *gpu, struct drm_gem_object *obj) -+{ -+ struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj); -+ struct etnaviv_vram_mapping *mapping; -+ -+ mutex_lock(&etnaviv_obj->lock); -+ mapping = etnaviv_gem_get_vram_mapping(etnaviv_obj, gpu->mmu); -+ -+ WARN_ON(mapping->use == 0); -+ mapping->use -= 1; -+ mutex_unlock(&etnaviv_obj->lock); -+ -+ drm_gem_object_unreference_unlocked(obj); -+} -+ -+void *etnaviv_gem_vaddr(struct drm_gem_object *obj) -+{ -+ struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj); -+ -+ mutex_lock(&etnaviv_obj->lock); -+ if (!etnaviv_obj->vaddr) { -+ struct page **pages = etnaviv_gem_get_pages(etnaviv_obj); -+ -+ if (IS_ERR(pages)) -+ return ERR_CAST(pages); -+ -+ etnaviv_obj->vaddr = vmap(pages, obj->size >> PAGE_SHIFT, -+ VM_MAP, pgprot_writecombine(PAGE_KERNEL)); -+ } -+ mutex_unlock(&etnaviv_obj->lock); -+ -+ return etnaviv_obj->vaddr; -+} -+ -+static inline enum dma_data_direction etnaviv_op_to_dma_dir(u32 op) -+{ -+ if (op & ETNA_PREP_READ) -+ return DMA_FROM_DEVICE; -+ else if (op & ETNA_PREP_WRITE) -+ return DMA_TO_DEVICE; -+ else -+ return DMA_BIDIRECTIONAL; -+} -+ -+int etnaviv_gem_cpu_prep(struct drm_gem_object *obj, u32 op, -+ struct timespec *timeout) -+{ -+ struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj); -+ struct drm_device *dev = obj->dev; -+ bool write = !!(op & ETNA_PREP_WRITE); -+ int ret; -+ -+ if (op & ETNA_PREP_NOSYNC) { -+ if (!reservation_object_test_signaled_rcu(etnaviv_obj->resv, -+ write)) -+ return -EBUSY; -+ } else { -+ unsigned long remain = etnaviv_timeout_to_jiffies(timeout); -+ -+ ret = reservation_object_wait_timeout_rcu(etnaviv_obj->resv, -+ write, true, remain); -+ if (ret <= 0) -+ return ret == 0 ? -ETIMEDOUT : ret; -+ } -+ -+ if (etnaviv_obj->flags & ETNA_BO_CACHED) { -+ if (!etnaviv_obj->sgt) { -+ void *ret; -+ -+ mutex_lock(&etnaviv_obj->lock); -+ ret = etnaviv_gem_get_pages(etnaviv_obj); -+ mutex_unlock(&etnaviv_obj->lock); -+ if (IS_ERR(ret)) -+ return PTR_ERR(ret); -+ } -+ -+ dma_sync_sg_for_cpu(dev->dev, etnaviv_obj->sgt->sgl, -+ etnaviv_obj->sgt->nents, -+ etnaviv_op_to_dma_dir(op)); -+ etnaviv_obj->last_cpu_prep_op = op; -+ } -+ -+ return 0; -+} -+ -+int etnaviv_gem_cpu_fini(struct drm_gem_object *obj) -+{ -+ struct drm_device *dev = obj->dev; -+ struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj); -+ -+ if (etnaviv_obj->flags & ETNA_BO_CACHED) { -+ /* fini without a prep is almost certainly a userspace error */ -+ WARN_ON(etnaviv_obj->last_cpu_prep_op == 0); -+ dma_sync_sg_for_device(dev->dev, etnaviv_obj->sgt->sgl, -+ etnaviv_obj->sgt->nents, -+ etnaviv_op_to_dma_dir(etnaviv_obj->last_cpu_prep_op)); -+ etnaviv_obj->last_cpu_prep_op = 0; -+ } -+ -+ return 0; -+} -+ -+int etnaviv_gem_wait_bo(struct etnaviv_gpu *gpu, struct drm_gem_object *obj, -+ struct timespec *timeout) -+{ -+ struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj); -+ -+ return etnaviv_gpu_wait_obj_inactive(gpu, etnaviv_obj, timeout); -+} -+ -+#ifdef CONFIG_DEBUG_FS -+static void etnaviv_gem_describe_fence(struct fence *fence, -+ const char *type, struct seq_file *m) -+{ -+ if (!test_bit(FENCE_FLAG_SIGNALED_BIT, &fence->flags)) -+ seq_printf(m, "\t%9s: %s %s seq %u\n", -+ type, -+ fence->ops->get_driver_name(fence), -+ fence->ops->get_timeline_name(fence), -+ fence->seqno); -+} -+ -+static void etnaviv_gem_describe(struct drm_gem_object *obj, struct seq_file *m) -+{ -+ struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj); -+ struct reservation_object *robj = etnaviv_obj->resv; -+ struct reservation_object_list *fobj; -+ struct fence *fence; -+ unsigned long off = drm_vma_node_start(&obj->vma_node); -+ -+ seq_printf(m, "%08x: %c %2d (%2d) %08lx %p %zd\n", -+ etnaviv_obj->flags, is_active(etnaviv_obj) ? 'A' : 'I', -+ obj->name, obj->refcount.refcount.counter, -+ off, etnaviv_obj->vaddr, obj->size); -+ -+ rcu_read_lock(); -+ fobj = rcu_dereference(robj->fence); -+ if (fobj) { -+ unsigned int i, shared_count = fobj->shared_count; -+ -+ for (i = 0; i < shared_count; i++) { -+ fence = rcu_dereference(fobj->shared[i]); -+ etnaviv_gem_describe_fence(fence, "Shared", m); -+ } -+ } -+ -+ fence = rcu_dereference(robj->fence_excl); -+ if (fence) -+ etnaviv_gem_describe_fence(fence, "Exclusive", m); -+ rcu_read_unlock(); -+} -+ -+void etnaviv_gem_describe_objects(struct etnaviv_drm_private *priv, -+ struct seq_file *m) -+{ -+ struct etnaviv_gem_object *etnaviv_obj; -+ int count = 0; -+ size_t size = 0; -+ -+ mutex_lock(&priv->gem_lock); -+ list_for_each_entry(etnaviv_obj, &priv->gem_list, gem_node) { -+ struct drm_gem_object *obj = &etnaviv_obj->base; -+ -+ seq_puts(m, " "); -+ etnaviv_gem_describe(obj, m); -+ count++; -+ size += obj->size; -+ } -+ mutex_unlock(&priv->gem_lock); -+ -+ seq_printf(m, "Total %d objects, %zu bytes\n", count, size); -+} -+#endif -+ -+static void etnaviv_gem_shmem_release(struct etnaviv_gem_object *etnaviv_obj) -+{ -+ if (etnaviv_obj->vaddr) -+ vunmap(etnaviv_obj->vaddr); -+ put_pages(etnaviv_obj); -+} -+ -+static const struct etnaviv_gem_ops etnaviv_gem_shmem_ops = { -+ .get_pages = etnaviv_gem_shmem_get_pages, -+ .release = etnaviv_gem_shmem_release, -+}; -+ -+void etnaviv_gem_free_object(struct drm_gem_object *obj) -+{ -+ struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj); -+ struct etnaviv_vram_mapping *mapping, *tmp; -+ -+ /* object should not be active */ -+ WARN_ON(is_active(etnaviv_obj)); -+ -+ list_del(&etnaviv_obj->gem_node); -+ -+ list_for_each_entry_safe(mapping, tmp, &etnaviv_obj->vram_list, -+ obj_node) { -+ struct etnaviv_iommu *mmu = mapping->mmu; -+ -+ WARN_ON(mapping->use); -+ -+ if (mmu) -+ etnaviv_iommu_unmap_gem(mmu, mapping); -+ -+ list_del(&mapping->obj_node); -+ kfree(mapping); -+ } -+ -+ drm_gem_free_mmap_offset(obj); -+ etnaviv_obj->ops->release(etnaviv_obj); -+ if (etnaviv_obj->resv == &etnaviv_obj->_resv) -+ reservation_object_fini(&etnaviv_obj->_resv); -+ drm_gem_object_release(obj); -+ -+ kfree(etnaviv_obj); -+} -+ -+int etnaviv_gem_obj_add(struct drm_device *dev, struct drm_gem_object *obj) -+{ -+ struct etnaviv_drm_private *priv = dev->dev_private; -+ struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj); -+ -+ mutex_lock(&priv->gem_lock); -+ list_add_tail(&etnaviv_obj->gem_node, &priv->gem_list); -+ mutex_unlock(&priv->gem_lock); -+ -+ return 0; -+} -+ -+static int etnaviv_gem_new_impl(struct drm_device *dev, u32 size, u32 flags, -+ struct reservation_object *robj, const struct etnaviv_gem_ops *ops, -+ struct drm_gem_object **obj) -+{ -+ struct etnaviv_gem_object *etnaviv_obj; -+ unsigned sz = sizeof(*etnaviv_obj); -+ bool valid = true; -+ -+ /* validate flags */ -+ switch (flags & ETNA_BO_CACHE_MASK) { -+ case ETNA_BO_UNCACHED: -+ case ETNA_BO_CACHED: -+ case ETNA_BO_WC: -+ break; -+ default: -+ valid = false; -+ } -+ -+ if (!valid) { -+ dev_err(dev->dev, "invalid cache flag: %x\n", -+ (flags & ETNA_BO_CACHE_MASK)); -+ return -EINVAL; -+ } -+ -+ etnaviv_obj = kzalloc(sz, GFP_KERNEL); -+ if (!etnaviv_obj) -+ return -ENOMEM; -+ -+ etnaviv_obj->flags = flags; -+ etnaviv_obj->ops = ops; -+ if (robj) { -+ etnaviv_obj->resv = robj; -+ } else { -+ etnaviv_obj->resv = &etnaviv_obj->_resv; -+ reservation_object_init(&etnaviv_obj->_resv); -+ } -+ -+ mutex_init(&etnaviv_obj->lock); -+ INIT_LIST_HEAD(&etnaviv_obj->vram_list); -+ -+ *obj = &etnaviv_obj->base; -+ -+ return 0; -+} -+ -+static struct drm_gem_object *__etnaviv_gem_new(struct drm_device *dev, -+ u32 size, u32 flags) -+{ -+ struct drm_gem_object *obj = NULL; -+ int ret; -+ -+ size = PAGE_ALIGN(size); -+ -+ ret = etnaviv_gem_new_impl(dev, size, flags, NULL, -+ &etnaviv_gem_shmem_ops, &obj); -+ if (ret) -+ goto fail; -+ -+ ret = drm_gem_object_init(dev, obj, size); -+ if (ret == 0) { -+ struct address_space *mapping; -+ -+ /* -+ * Our buffers are kept pinned, so allocating them -+ * from the MOVABLE zone is a really bad idea, and -+ * conflicts with CMA. See coments above new_inode() -+ * why this is required _and_ expected if you're -+ * going to pin these pages. -+ */ -+ mapping = file_inode(obj->filp)->i_mapping; -+ mapping_set_gfp_mask(mapping, GFP_HIGHUSER); -+ } -+ -+ if (ret) -+ goto fail; -+ -+ return obj; -+ -+fail: -+ if (obj) -+ drm_gem_object_unreference_unlocked(obj); -+ -+ return ERR_PTR(ret); -+} -+ -+/* convenience method to construct a GEM buffer object, and userspace handle */ -+int etnaviv_gem_new_handle(struct drm_device *dev, struct drm_file *file, -+ u32 size, u32 flags, u32 *handle) -+{ -+ struct drm_gem_object *obj; -+ int ret; -+ -+ obj = __etnaviv_gem_new(dev, size, flags); -+ if (IS_ERR(obj)) -+ return PTR_ERR(obj); -+ -+ ret = etnaviv_gem_obj_add(dev, obj); -+ if (ret < 0) { -+ drm_gem_object_unreference_unlocked(obj); -+ return ret; -+ } -+ -+ ret = drm_gem_handle_create(file, obj, handle); -+ -+ /* drop reference from allocate - handle holds it now */ -+ drm_gem_object_unreference_unlocked(obj); -+ -+ return ret; -+} -+ -+struct drm_gem_object *etnaviv_gem_new(struct drm_device *dev, -+ u32 size, u32 flags) -+{ -+ struct drm_gem_object *obj; -+ int ret; -+ -+ obj = __etnaviv_gem_new(dev, size, flags); -+ if (IS_ERR(obj)) -+ return obj; -+ -+ ret = etnaviv_gem_obj_add(dev, obj); -+ if (ret < 0) { -+ drm_gem_object_unreference_unlocked(obj); -+ return ERR_PTR(ret); -+ } -+ -+ return obj; -+} -+ -+int etnaviv_gem_new_private(struct drm_device *dev, size_t size, u32 flags, -+ struct reservation_object *robj, const struct etnaviv_gem_ops *ops, -+ struct etnaviv_gem_object **res) -+{ -+ struct drm_gem_object *obj; -+ int ret; -+ -+ ret = etnaviv_gem_new_impl(dev, size, flags, robj, ops, &obj); -+ if (ret) -+ return ret; -+ -+ drm_gem_private_object_init(dev, obj, size); -+ -+ *res = to_etnaviv_bo(obj); -+ -+ return 0; -+} -+ -+struct get_pages_work { -+ struct work_struct work; -+ struct mm_struct *mm; -+ struct task_struct *task; -+ struct etnaviv_gem_object *etnaviv_obj; -+}; -+ -+static struct page **etnaviv_gem_userptr_do_get_pages( -+ struct etnaviv_gem_object *etnaviv_obj, struct mm_struct *mm, struct task_struct *task) -+{ -+ int ret = 0, pinned, npages = etnaviv_obj->base.size >> PAGE_SHIFT; -+ struct page **pvec; -+ uintptr_t ptr; -+ -+ pvec = drm_malloc_ab(npages, sizeof(struct page *)); -+ if (!pvec) -+ return ERR_PTR(-ENOMEM); -+ -+ pinned = 0; -+ ptr = etnaviv_obj->userptr.ptr; -+ -+ down_read(&mm->mmap_sem); -+ while (pinned < npages) { -+ ret = get_user_pages(task, mm, ptr, npages - pinned, -+ !etnaviv_obj->userptr.ro, 0, -+ pvec + pinned, NULL); -+ if (ret < 0) -+ break; -+ -+ ptr += ret * PAGE_SIZE; -+ pinned += ret; -+ } -+ up_read(&mm->mmap_sem); -+ -+ if (ret < 0) { -+ release_pages(pvec, pinned, 0); -+ drm_free_large(pvec); -+ return ERR_PTR(ret); -+ } -+ -+ return pvec; -+} -+ -+static void __etnaviv_gem_userptr_get_pages(struct work_struct *_work) -+{ -+ struct get_pages_work *work = container_of(_work, typeof(*work), work); -+ struct etnaviv_gem_object *etnaviv_obj = work->etnaviv_obj; -+ struct page **pvec; -+ -+ pvec = etnaviv_gem_userptr_do_get_pages(etnaviv_obj, work->mm, work->task); -+ -+ mutex_lock(&etnaviv_obj->lock); -+ if (IS_ERR(pvec)) { -+ etnaviv_obj->userptr.work = ERR_CAST(pvec); -+ } else { -+ etnaviv_obj->userptr.work = NULL; -+ etnaviv_obj->pages = pvec; -+ } -+ -+ mutex_unlock(&etnaviv_obj->lock); -+ drm_gem_object_unreference_unlocked(&etnaviv_obj->base); -+ -+ mmput(work->mm); -+ put_task_struct(work->task); -+ kfree(work); -+} -+ -+static int etnaviv_gem_userptr_get_pages(struct etnaviv_gem_object *etnaviv_obj) -+{ -+ struct page **pvec = NULL; -+ struct get_pages_work *work; -+ struct mm_struct *mm; -+ int ret, pinned, npages = etnaviv_obj->base.size >> PAGE_SHIFT; -+ -+ if (etnaviv_obj->userptr.work) { -+ if (IS_ERR(etnaviv_obj->userptr.work)) { -+ ret = PTR_ERR(etnaviv_obj->userptr.work); -+ etnaviv_obj->userptr.work = NULL; -+ } else { -+ ret = -EAGAIN; -+ } -+ return ret; -+ } -+ -+ mm = get_task_mm(etnaviv_obj->userptr.task); -+ pinned = 0; -+ if (mm == current->mm) { -+ pvec = drm_malloc_ab(npages, sizeof(struct page *)); -+ if (!pvec) { -+ mmput(mm); -+ return -ENOMEM; -+ } -+ -+ pinned = __get_user_pages_fast(etnaviv_obj->userptr.ptr, npages, -+ !etnaviv_obj->userptr.ro, pvec); -+ if (pinned < 0) { -+ drm_free_large(pvec); -+ mmput(mm); -+ return pinned; -+ } -+ -+ if (pinned == npages) { -+ etnaviv_obj->pages = pvec; -+ mmput(mm); -+ return 0; -+ } -+ } -+ -+ release_pages(pvec, pinned, 0); -+ drm_free_large(pvec); -+ -+ work = kmalloc(sizeof(*work), GFP_KERNEL); -+ if (!work) { -+ mmput(mm); -+ return -ENOMEM; -+ } -+ -+ get_task_struct(current); -+ drm_gem_object_reference(&etnaviv_obj->base); -+ -+ work->mm = mm; -+ work->task = current; -+ work->etnaviv_obj = etnaviv_obj; -+ -+ etnaviv_obj->userptr.work = &work->work; -+ INIT_WORK(&work->work, __etnaviv_gem_userptr_get_pages); -+ -+ etnaviv_queue_work(etnaviv_obj->base.dev, &work->work); -+ -+ return -EAGAIN; -+} -+ -+static void etnaviv_gem_userptr_release(struct etnaviv_gem_object *etnaviv_obj) -+{ -+ if (etnaviv_obj->sgt) { -+ etnaviv_gem_scatterlist_unmap(etnaviv_obj); -+ sg_free_table(etnaviv_obj->sgt); -+ kfree(etnaviv_obj->sgt); -+ } -+ if (etnaviv_obj->pages) { -+ int npages = etnaviv_obj->base.size >> PAGE_SHIFT; -+ -+ release_pages(etnaviv_obj->pages, npages, 0); -+ drm_free_large(etnaviv_obj->pages); -+ } -+ put_task_struct(etnaviv_obj->userptr.task); -+} -+ -+static const struct etnaviv_gem_ops etnaviv_gem_userptr_ops = { -+ .get_pages = etnaviv_gem_userptr_get_pages, -+ .release = etnaviv_gem_userptr_release, -+}; -+ -+int etnaviv_gem_new_userptr(struct drm_device *dev, struct drm_file *file, -+ uintptr_t ptr, u32 size, u32 flags, u32 *handle) -+{ -+ struct etnaviv_gem_object *etnaviv_obj; -+ int ret; -+ -+ ret = etnaviv_gem_new_private(dev, size, ETNA_BO_CACHED, NULL, -+ &etnaviv_gem_userptr_ops, &etnaviv_obj); -+ if (ret) -+ return ret; -+ -+ etnaviv_obj->userptr.ptr = ptr; -+ etnaviv_obj->userptr.task = current; -+ etnaviv_obj->userptr.ro = !(flags & ETNA_USERPTR_WRITE); -+ get_task_struct(current); -+ -+ ret = etnaviv_gem_obj_add(dev, &etnaviv_obj->base); -+ if (ret) { -+ drm_gem_object_unreference_unlocked(&etnaviv_obj->base); -+ return ret; -+ } -+ -+ ret = drm_gem_handle_create(file, &etnaviv_obj->base, handle); -+ -+ /* drop reference from allocate - handle holds it now */ -+ drm_gem_object_unreference_unlocked(&etnaviv_obj->base); -+ -+ return ret; -+} -diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.h b/drivers/gpu/drm/etnaviv/etnaviv_gem.h -new file mode 100644 -index 0000000..a300b4b ---- /dev/null -+++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.h -@@ -0,0 +1,117 @@ -+/* -+ * Copyright (C) 2015 Etnaviv Project -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License version 2 as published by -+ * the Free Software Foundation. -+ * -+ * 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 . -+ */ -+ -+#ifndef __ETNAVIV_GEM_H__ -+#define __ETNAVIV_GEM_H__ -+ -+#include -+#include "etnaviv_drv.h" -+ -+struct etnaviv_gem_ops; -+struct etnaviv_gem_object; -+ -+struct etnaviv_gem_userptr { -+ uintptr_t ptr; -+ struct task_struct *task; -+ struct work_struct *work; -+ bool ro; -+}; -+ -+struct etnaviv_vram_mapping { -+ struct list_head obj_node; -+ struct list_head scan_node; -+ struct list_head mmu_node; -+ struct etnaviv_gem_object *object; -+ struct etnaviv_iommu *mmu; -+ struct drm_mm_node vram_node; -+ unsigned int use; -+ u32 iova; -+}; -+ -+struct etnaviv_gem_object { -+ struct drm_gem_object base; -+ const struct etnaviv_gem_ops *ops; -+ struct mutex lock; -+ -+ u32 flags; -+ -+ struct list_head gem_node; -+ struct etnaviv_gpu *gpu; /* non-null if active */ -+ atomic_t gpu_active; -+ u32 access; -+ -+ struct page **pages; -+ struct sg_table *sgt; -+ void *vaddr; -+ -+ /* normally (resv == &_resv) except for imported bo's */ -+ struct reservation_object *resv; -+ struct reservation_object _resv; -+ -+ struct list_head vram_list; -+ -+ /* cache maintenance */ -+ u32 last_cpu_prep_op; -+ -+ struct etnaviv_gem_userptr userptr; -+}; -+ -+static inline -+struct etnaviv_gem_object *to_etnaviv_bo(struct drm_gem_object *obj) -+{ -+ return container_of(obj, struct etnaviv_gem_object, base); -+} -+ -+struct etnaviv_gem_ops { -+ int (*get_pages)(struct etnaviv_gem_object *); -+ void (*release)(struct etnaviv_gem_object *); -+}; -+ -+static inline bool is_active(struct etnaviv_gem_object *etnaviv_obj) -+{ -+ return atomic_read(&etnaviv_obj->gpu_active) != 0; -+} -+ -+#define MAX_CMDS 4 -+ -+/* Created per submit-ioctl, to track bo's and cmdstream bufs, etc, -+ * associated with the cmdstream submission for synchronization (and -+ * make it easier to unwind when things go wrong, etc). This only -+ * lasts for the duration of the submit-ioctl. -+ */ -+struct etnaviv_gem_submit { -+ struct drm_device *dev; -+ struct etnaviv_gpu *gpu; -+ struct ww_acquire_ctx ticket; -+ u32 fence; -+ unsigned int nr_bos; -+ struct { -+ u32 flags; -+ struct etnaviv_gem_object *obj; -+ u32 iova; -+ } bos[0]; -+}; -+ -+int etnaviv_gem_wait_bo(struct etnaviv_gpu *gpu, struct drm_gem_object *obj, -+ struct timespec *timeout); -+int etnaviv_gem_new_private(struct drm_device *dev, size_t size, u32 flags, -+ struct reservation_object *robj, const struct etnaviv_gem_ops *ops, -+ struct etnaviv_gem_object **res); -+int etnaviv_gem_obj_add(struct drm_device *dev, struct drm_gem_object *obj); -+struct page **etnaviv_gem_get_pages(struct etnaviv_gem_object *obj); -+void etnaviv_gem_put_pages(struct etnaviv_gem_object *obj); -+ -+#endif /* __ETNAVIV_GEM_H__ */ -diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c b/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c -new file mode 100644 -index 0000000..e94db4f ---- /dev/null -+++ b/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c -@@ -0,0 +1,122 @@ -+/* -+ * Copyright (C) 2013 Red Hat -+ * Author: Rob Clark -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License version 2 as published by -+ * the Free Software Foundation. -+ * -+ * 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 . -+ */ -+ -+#include -+#include "etnaviv_drv.h" -+#include "etnaviv_gem.h" -+ -+ -+struct sg_table *etnaviv_gem_prime_get_sg_table(struct drm_gem_object *obj) -+{ -+ struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj); -+ -+ BUG_ON(!etnaviv_obj->sgt); /* should have already pinned! */ -+ -+ return etnaviv_obj->sgt; -+} -+ -+void *etnaviv_gem_prime_vmap(struct drm_gem_object *obj) -+{ -+ return etnaviv_gem_vaddr(obj); -+} -+ -+void etnaviv_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr) -+{ -+ /* TODO msm_gem_vunmap() */ -+} -+ -+int etnaviv_gem_prime_pin(struct drm_gem_object *obj) -+{ -+ if (!obj->import_attach) { -+ struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj); -+ -+ mutex_lock(&etnaviv_obj->lock); -+ etnaviv_gem_get_pages(etnaviv_obj); -+ mutex_unlock(&etnaviv_obj->lock); -+ } -+ return 0; -+} -+ -+void etnaviv_gem_prime_unpin(struct drm_gem_object *obj) -+{ -+ if (!obj->import_attach) { -+ struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj); -+ -+ mutex_lock(&etnaviv_obj->lock); -+ etnaviv_gem_put_pages(to_etnaviv_bo(obj)); -+ mutex_unlock(&etnaviv_obj->lock); -+ } -+} -+ -+static void etnaviv_gem_prime_release(struct etnaviv_gem_object *etnaviv_obj) -+{ -+ if (etnaviv_obj->vaddr) -+ dma_buf_vunmap(etnaviv_obj->base.import_attach->dmabuf, -+ etnaviv_obj->vaddr); -+ -+ /* Don't drop the pages for imported dmabuf, as they are not -+ * ours, just free the array we allocated: -+ */ -+ if (etnaviv_obj->pages) -+ drm_free_large(etnaviv_obj->pages); -+ -+ drm_prime_gem_destroy(&etnaviv_obj->base, etnaviv_obj->sgt); -+} -+ -+static const struct etnaviv_gem_ops etnaviv_gem_prime_ops = { -+ /* .get_pages should never be called */ -+ .release = etnaviv_gem_prime_release, -+}; -+ -+struct drm_gem_object *etnaviv_gem_prime_import_sg_table(struct drm_device *dev, -+ struct dma_buf_attachment *attach, struct sg_table *sgt) -+{ -+ struct etnaviv_gem_object *etnaviv_obj; -+ size_t size = PAGE_ALIGN(attach->dmabuf->size); -+ int ret, npages; -+ -+ ret = etnaviv_gem_new_private(dev, size, ETNA_BO_WC, -+ attach->dmabuf->resv, -+ &etnaviv_gem_prime_ops, &etnaviv_obj); -+ if (ret < 0) -+ return ERR_PTR(ret); -+ -+ npages = size / PAGE_SIZE; -+ -+ etnaviv_obj->sgt = sgt; -+ etnaviv_obj->pages = drm_malloc_ab(npages, sizeof(struct page *)); -+ if (!etnaviv_obj->pages) { -+ ret = -ENOMEM; -+ goto fail; -+ } -+ -+ ret = drm_prime_sg_to_page_addr_arrays(sgt, etnaviv_obj->pages, -+ NULL, npages); -+ if (ret) -+ goto fail; -+ -+ ret = etnaviv_gem_obj_add(dev, &etnaviv_obj->base); -+ if (ret) -+ goto fail; -+ -+ return &etnaviv_obj->base; -+ -+fail: -+ drm_gem_object_unreference_unlocked(&etnaviv_obj->base); -+ -+ return ERR_PTR(ret); -+} -diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c b/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c -new file mode 100644 -index 0000000..1aba01a ---- /dev/null -+++ b/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c -@@ -0,0 +1,443 @@ -+/* -+ * Copyright (C) 2015 Etnaviv Project -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License version 2 as published by -+ * the Free Software Foundation. -+ * -+ * 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 . -+ */ -+ -+#include -+#include "etnaviv_drv.h" -+#include "etnaviv_gpu.h" -+#include "etnaviv_gem.h" -+ -+/* -+ * Cmdstream submission: -+ */ -+ -+#define BO_INVALID_FLAGS ~(ETNA_SUBMIT_BO_READ | ETNA_SUBMIT_BO_WRITE) -+/* make sure these don't conflict w/ ETNAVIV_SUBMIT_BO_x */ -+#define BO_LOCKED 0x4000 -+#define BO_PINNED 0x2000 -+ -+static inline void __user *to_user_ptr(u64 address) -+{ -+ return (void __user *)(uintptr_t)address; -+} -+ -+static struct etnaviv_gem_submit *submit_create(struct drm_device *dev, -+ struct etnaviv_gpu *gpu, size_t nr) -+{ -+ struct etnaviv_gem_submit *submit; -+ size_t sz = size_vstruct(nr, sizeof(submit->bos[0]), sizeof(*submit)); -+ -+ submit = kmalloc(sz, GFP_TEMPORARY | __GFP_NOWARN | __GFP_NORETRY); -+ if (submit) { -+ submit->dev = dev; -+ submit->gpu = gpu; -+ -+ /* initially, until copy_from_user() and bo lookup succeeds: */ -+ submit->nr_bos = 0; -+ -+ ww_acquire_init(&submit->ticket, &reservation_ww_class); -+ } -+ -+ return submit; -+} -+ -+static int submit_lookup_objects(struct etnaviv_gem_submit *submit, -+ struct drm_file *file, struct drm_etnaviv_gem_submit_bo *submit_bos, -+ unsigned nr_bos) -+{ -+ struct drm_etnaviv_gem_submit_bo *bo; -+ unsigned i; -+ int ret = 0; -+ -+ spin_lock(&file->table_lock); -+ -+ for (i = 0, bo = submit_bos; i < nr_bos; i++, bo++) { -+ struct drm_gem_object *obj; -+ -+ if (bo->flags & BO_INVALID_FLAGS) { -+ DRM_ERROR("invalid flags: %x\n", bo->flags); -+ ret = -EINVAL; -+ goto out_unlock; -+ } -+ -+ submit->bos[i].flags = bo->flags; -+ -+ /* normally use drm_gem_object_lookup(), but for bulk lookup -+ * all under single table_lock just hit object_idr directly: -+ */ -+ obj = idr_find(&file->object_idr, bo->handle); -+ if (!obj) { -+ DRM_ERROR("invalid handle %u at index %u\n", -+ bo->handle, i); -+ ret = -EINVAL; -+ goto out_unlock; -+ } -+ -+ /* -+ * Take a refcount on the object. The file table lock -+ * prevents the object_idr's refcount on this being dropped. -+ */ -+ drm_gem_object_reference(obj); -+ -+ submit->bos[i].obj = to_etnaviv_bo(obj); -+ } -+ -+out_unlock: -+ submit->nr_bos = i; -+ spin_unlock(&file->table_lock); -+ -+ return ret; -+} -+ -+static void submit_unlock_object(struct etnaviv_gem_submit *submit, int i) -+{ -+ if (submit->bos[i].flags & BO_LOCKED) { -+ struct etnaviv_gem_object *etnaviv_obj = submit->bos[i].obj; -+ -+ ww_mutex_unlock(&etnaviv_obj->resv->lock); -+ submit->bos[i].flags &= ~BO_LOCKED; -+ } -+} -+ -+static int submit_lock_objects(struct etnaviv_gem_submit *submit) -+{ -+ int contended, slow_locked = -1, i, ret = 0; -+ -+retry: -+ for (i = 0; i < submit->nr_bos; i++) { -+ struct etnaviv_gem_object *etnaviv_obj = submit->bos[i].obj; -+ -+ if (slow_locked == i) -+ slow_locked = -1; -+ -+ contended = i; -+ -+ if (!(submit->bos[i].flags & BO_LOCKED)) { -+ ret = ww_mutex_lock_interruptible(&etnaviv_obj->resv->lock, -+ &submit->ticket); -+ if (ret == -EALREADY) -+ DRM_ERROR("BO at index %u already on submit list\n", -+ i); -+ if (ret) -+ goto fail; -+ submit->bos[i].flags |= BO_LOCKED; -+ } -+ } -+ -+ ww_acquire_done(&submit->ticket); -+ -+ return 0; -+ -+fail: -+ for (; i >= 0; i--) -+ submit_unlock_object(submit, i); -+ -+ if (slow_locked > 0) -+ submit_unlock_object(submit, slow_locked); -+ -+ if (ret == -EDEADLK) { -+ struct etnaviv_gem_object *etnaviv_obj; -+ -+ etnaviv_obj = submit->bos[contended].obj; -+ -+ /* we lost out in a seqno race, lock and retry.. */ -+ ret = ww_mutex_lock_slow_interruptible(&etnaviv_obj->resv->lock, -+ &submit->ticket); -+ if (!ret) { -+ submit->bos[contended].flags |= BO_LOCKED; -+ slow_locked = contended; -+ goto retry; -+ } -+ } -+ -+ return ret; -+} -+ -+static int submit_fence_sync(const struct etnaviv_gem_submit *submit) -+{ -+ unsigned int context = submit->gpu->fence_context; -+ int i, ret = 0; -+ -+ for (i = 0; i < submit->nr_bos; i++) { -+ struct etnaviv_gem_object *etnaviv_obj = submit->bos[i].obj; -+ bool write = submit->bos[i].flags & ETNA_SUBMIT_BO_WRITE; -+ -+ ret = etnaviv_gpu_fence_sync_obj(etnaviv_obj, context, write); -+ if (ret) -+ break; -+ } -+ -+ return ret; -+} -+ -+static void submit_unpin_objects(struct etnaviv_gem_submit *submit) -+{ -+ int i; -+ -+ for (i = 0; i < submit->nr_bos; i++) { -+ struct etnaviv_gem_object *etnaviv_obj = submit->bos[i].obj; -+ -+ if (submit->bos[i].flags & BO_PINNED) -+ etnaviv_gem_put_iova(submit->gpu, &etnaviv_obj->base); -+ -+ submit->bos[i].iova = 0; -+ submit->bos[i].flags &= ~BO_PINNED; -+ } -+} -+ -+static int submit_pin_objects(struct etnaviv_gem_submit *submit) -+{ -+ int i, ret = 0; -+ -+ for (i = 0; i < submit->nr_bos; i++) { -+ struct etnaviv_gem_object *etnaviv_obj = submit->bos[i].obj; -+ u32 iova; -+ -+ ret = etnaviv_gem_get_iova(submit->gpu, &etnaviv_obj->base, -+ &iova); -+ if (ret) -+ break; -+ -+ submit->bos[i].flags |= BO_PINNED; -+ submit->bos[i].iova = iova; -+ } -+ -+ return ret; -+} -+ -+static int submit_bo(struct etnaviv_gem_submit *submit, u32 idx, -+ struct etnaviv_gem_object **obj, u32 *iova) -+{ -+ if (idx >= submit->nr_bos) { -+ DRM_ERROR("invalid buffer index: %u (out of %u)\n", -+ idx, submit->nr_bos); -+ return -EINVAL; -+ } -+ -+ if (obj) -+ *obj = submit->bos[idx].obj; -+ if (iova) -+ *iova = submit->bos[idx].iova; -+ -+ return 0; -+} -+ -+/* process the reloc's and patch up the cmdstream as needed: */ -+static int submit_reloc(struct etnaviv_gem_submit *submit, void *stream, -+ u32 size, const struct drm_etnaviv_gem_submit_reloc *relocs, -+ u32 nr_relocs) -+{ -+ u32 i, last_offset = 0; -+ u32 *ptr = stream; -+ int ret; -+ -+ for (i = 0; i < nr_relocs; i++) { -+ const struct drm_etnaviv_gem_submit_reloc *r = relocs + i; -+ struct etnaviv_gem_object *bobj; -+ u32 iova, off; -+ -+ if (unlikely(r->flags)) { -+ DRM_ERROR("invalid reloc flags\n"); -+ return -EINVAL; -+ } -+ -+ if (r->submit_offset % 4) { -+ DRM_ERROR("non-aligned reloc offset: %u\n", -+ r->submit_offset); -+ return -EINVAL; -+ } -+ -+ /* offset in dwords: */ -+ off = r->submit_offset / 4; -+ -+ if ((off >= size ) || -+ (off < last_offset)) { -+ DRM_ERROR("invalid offset %u at reloc %u\n", off, i); -+ return -EINVAL; -+ } -+ -+ ret = submit_bo(submit, r->reloc_idx, &bobj, &iova); -+ if (ret) -+ return ret; -+ -+ if (r->reloc_offset >= -+ bobj->base.size - sizeof(*ptr)) { -+ DRM_ERROR("relocation %u outside object", i); -+ return -EINVAL; -+ } -+ -+ ptr[off] = iova + r->reloc_offset; -+ -+ last_offset = off; -+ } -+ -+ return 0; -+} -+ -+static void submit_cleanup(struct etnaviv_gem_submit *submit) -+{ -+ unsigned i; -+ -+ for (i = 0; i < submit->nr_bos; i++) { -+ struct etnaviv_gem_object *etnaviv_obj = submit->bos[i].obj; -+ -+ submit_unlock_object(submit, i); -+ drm_gem_object_unreference_unlocked(&etnaviv_obj->base); -+ } -+ -+ ww_acquire_fini(&submit->ticket); -+ kfree(submit); -+} -+ -+int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data, -+ struct drm_file *file) -+{ -+ struct etnaviv_drm_private *priv = dev->dev_private; -+ struct drm_etnaviv_gem_submit *args = data; -+ struct drm_etnaviv_gem_submit_reloc *relocs; -+ struct drm_etnaviv_gem_submit_bo *bos; -+ struct etnaviv_gem_submit *submit; -+ struct etnaviv_cmdbuf *cmdbuf; -+ struct etnaviv_gpu *gpu; -+ void *stream; -+ int ret; -+ -+ if (args->pipe >= ETNA_MAX_PIPES) -+ return -EINVAL; -+ -+ gpu = priv->gpu[args->pipe]; -+ if (!gpu) -+ return -ENXIO; -+ -+ if (args->stream_size % 4) { -+ DRM_ERROR("non-aligned cmdstream buffer size: %u\n", -+ args->stream_size); -+ return -EINVAL; -+ } -+ -+ if (args->exec_state != ETNA_PIPE_3D && -+ args->exec_state != ETNA_PIPE_2D && -+ args->exec_state != ETNA_PIPE_VG) { -+ DRM_ERROR("invalid exec_state: 0x%x\n", args->exec_state); -+ return -EINVAL; -+ } -+ -+ /* -+ * Copy the command submission and bo array to kernel space in -+ * one go, and do this outside of any locks. -+ */ -+ bos = drm_malloc_ab(args->nr_bos, sizeof(*bos)); -+ relocs = drm_malloc_ab(args->nr_relocs, sizeof(*relocs)); -+ stream = drm_malloc_ab(1, args->stream_size); -+ cmdbuf = etnaviv_gpu_cmdbuf_new(gpu, ALIGN(args->stream_size, 8) + 8, -+ args->nr_bos); -+ if (!bos || !relocs || !stream || !cmdbuf) { -+ ret = -ENOMEM; -+ goto err_submit_cmds; -+ } -+ -+ cmdbuf->exec_state = args->exec_state; -+ cmdbuf->ctx = file->driver_priv; -+ -+ ret = copy_from_user(bos, to_user_ptr(args->bos), -+ args->nr_bos * sizeof(*bos)); -+ if (ret) { -+ ret = -EFAULT; -+ goto err_submit_cmds; -+ } -+ -+ ret = copy_from_user(relocs, to_user_ptr(args->relocs), -+ args->nr_relocs * sizeof(*relocs)); -+ if (ret) { -+ ret = -EFAULT; -+ goto err_submit_cmds; -+ } -+ -+ ret = copy_from_user(stream, to_user_ptr(args->stream), -+ args->stream_size); -+ if (ret) { -+ ret = -EFAULT; -+ goto err_submit_cmds; -+ } -+ -+ submit = submit_create(dev, gpu, args->nr_bos); -+ if (!submit) { -+ ret = -ENOMEM; -+ goto err_submit_cmds; -+ } -+ -+ ret = submit_lookup_objects(submit, file, bos, args->nr_bos); -+ if (ret) -+ goto err_submit_objects; -+ -+ ret = submit_lock_objects(submit); -+ if (ret) -+ goto err_submit_objects; -+ -+ if (!etnaviv_cmd_validate_one(gpu, stream, args->stream_size / 4, -+ relocs, args->nr_relocs)) { -+ ret = -EINVAL; -+ goto err_submit_objects; -+ } -+ -+ ret = submit_fence_sync(submit); -+ if (ret) -+ goto err_submit_objects; -+ -+ ret = submit_pin_objects(submit); -+ if (ret) -+ goto out; -+ -+ ret = submit_reloc(submit, stream, args->stream_size / 4, -+ relocs, args->nr_relocs); -+ if (ret) -+ goto out; -+ -+ memcpy(cmdbuf->vaddr, stream, args->stream_size); -+ cmdbuf->user_size = ALIGN(args->stream_size, 8); -+ -+ ret = etnaviv_gpu_submit(gpu, submit, cmdbuf); -+ if (ret == 0) -+ cmdbuf = NULL; -+ -+ args->fence = submit->fence; -+ -+out: -+ submit_unpin_objects(submit); -+ -+ /* -+ * If we're returning -EAGAIN, it may be due to the userptr code -+ * wanting to run its workqueue outside of any locks. Flush our -+ * workqueue to ensure that it is run in a timely manner. -+ */ -+ if (ret == -EAGAIN) -+ flush_workqueue(priv->wq); -+ -+err_submit_objects: -+ submit_cleanup(submit); -+ -+err_submit_cmds: -+ /* if we still own the cmdbuf */ -+ if (cmdbuf) -+ etnaviv_gpu_cmdbuf_free(cmdbuf); -+ if (stream) -+ drm_free_large(stream); -+ if (bos) -+ drm_free_large(bos); -+ if (relocs) -+ drm_free_large(relocs); -+ -+ return ret; -+} -diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c -new file mode 100644 -index 0000000..d39093d ---- /dev/null -+++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c -@@ -0,0 +1,1644 @@ -+/* -+ * Copyright (C) 2015 Etnaviv Project -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License version 2 as published by -+ * the Free Software Foundation. -+ * -+ * 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 . -+ */ -+ -+#include -+#include -+#include -+#include -+#include "etnaviv_dump.h" -+#include "etnaviv_gpu.h" -+#include "etnaviv_gem.h" -+#include "etnaviv_mmu.h" -+#include "etnaviv_iommu.h" -+#include "etnaviv_iommu_v2.h" -+#include "common.xml.h" -+#include "state.xml.h" -+#include "state_hi.xml.h" -+#include "cmdstream.xml.h" -+ -+static const struct platform_device_id gpu_ids[] = { -+ { .name = "etnaviv-gpu,2d" }, -+ { }, -+}; -+ -+static bool etnaviv_dump_core = true; -+module_param_named(dump_core, etnaviv_dump_core, bool, 0600); -+ -+/* -+ * Driver functions: -+ */ -+ -+int etnaviv_gpu_get_param(struct etnaviv_gpu *gpu, u32 param, u64 *value) -+{ -+ switch (param) { -+ case ETNAVIV_PARAM_GPU_MODEL: -+ *value = gpu->identity.model; -+ break; -+ -+ case ETNAVIV_PARAM_GPU_REVISION: -+ *value = gpu->identity.revision; -+ break; -+ -+ case ETNAVIV_PARAM_GPU_FEATURES_0: -+ *value = gpu->identity.features; -+ break; -+ -+ case ETNAVIV_PARAM_GPU_FEATURES_1: -+ *value = gpu->identity.minor_features0; -+ break; -+ -+ case ETNAVIV_PARAM_GPU_FEATURES_2: -+ *value = gpu->identity.minor_features1; -+ break; -+ -+ case ETNAVIV_PARAM_GPU_FEATURES_3: -+ *value = gpu->identity.minor_features2; -+ break; -+ -+ case ETNAVIV_PARAM_GPU_FEATURES_4: -+ *value = gpu->identity.minor_features3; -+ break; -+ -+ case ETNAVIV_PARAM_GPU_STREAM_COUNT: -+ *value = gpu->identity.stream_count; -+ break; -+ -+ case ETNAVIV_PARAM_GPU_REGISTER_MAX: -+ *value = gpu->identity.register_max; -+ break; -+ -+ case ETNAVIV_PARAM_GPU_THREAD_COUNT: -+ *value = gpu->identity.thread_count; -+ break; -+ -+ case ETNAVIV_PARAM_GPU_VERTEX_CACHE_SIZE: -+ *value = gpu->identity.vertex_cache_size; -+ break; -+ -+ case ETNAVIV_PARAM_GPU_SHADER_CORE_COUNT: -+ *value = gpu->identity.shader_core_count; -+ break; -+ -+ case ETNAVIV_PARAM_GPU_PIXEL_PIPES: -+ *value = gpu->identity.pixel_pipes; -+ break; -+ -+ case ETNAVIV_PARAM_GPU_VERTEX_OUTPUT_BUFFER_SIZE: -+ *value = gpu->identity.vertex_output_buffer_size; -+ break; -+ -+ case ETNAVIV_PARAM_GPU_BUFFER_SIZE: -+ *value = gpu->identity.buffer_size; -+ break; -+ -+ case ETNAVIV_PARAM_GPU_INSTRUCTION_COUNT: -+ *value = gpu->identity.instruction_count; -+ break; -+ -+ case ETNAVIV_PARAM_GPU_NUM_CONSTANTS: -+ *value = gpu->identity.num_constants; -+ break; -+ -+ default: -+ DBG("%s: invalid param: %u", dev_name(gpu->dev), param); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static void etnaviv_hw_specs(struct etnaviv_gpu *gpu) -+{ -+ if (gpu->identity.minor_features0 & -+ chipMinorFeatures0_MORE_MINOR_FEATURES) { -+ u32 specs[2]; -+ -+ specs[0] = gpu_read(gpu, VIVS_HI_CHIP_SPECS); -+ specs[1] = gpu_read(gpu, VIVS_HI_CHIP_SPECS_2); -+ -+ gpu->identity.stream_count = -+ (specs[0] & VIVS_HI_CHIP_SPECS_STREAM_COUNT__MASK) -+ >> VIVS_HI_CHIP_SPECS_STREAM_COUNT__SHIFT; -+ gpu->identity.register_max = -+ (specs[0] & VIVS_HI_CHIP_SPECS_REGISTER_MAX__MASK) -+ >> VIVS_HI_CHIP_SPECS_REGISTER_MAX__SHIFT; -+ gpu->identity.thread_count = -+ (specs[0] & VIVS_HI_CHIP_SPECS_THREAD_COUNT__MASK) -+ >> VIVS_HI_CHIP_SPECS_THREAD_COUNT__SHIFT; -+ gpu->identity.vertex_cache_size = -+ (specs[0] & VIVS_HI_CHIP_SPECS_VERTEX_CACHE_SIZE__MASK) -+ >> VIVS_HI_CHIP_SPECS_VERTEX_CACHE_SIZE__SHIFT; -+ gpu->identity.shader_core_count = -+ (specs[0] & VIVS_HI_CHIP_SPECS_SHADER_CORE_COUNT__MASK) -+ >> VIVS_HI_CHIP_SPECS_SHADER_CORE_COUNT__SHIFT; -+ gpu->identity.pixel_pipes = -+ (specs[0] & VIVS_HI_CHIP_SPECS_PIXEL_PIPES__MASK) -+ >> VIVS_HI_CHIP_SPECS_PIXEL_PIPES__SHIFT; -+ gpu->identity.vertex_output_buffer_size = -+ (specs[0] & VIVS_HI_CHIP_SPECS_VERTEX_OUTPUT_BUFFER_SIZE__MASK) -+ >> VIVS_HI_CHIP_SPECS_VERTEX_OUTPUT_BUFFER_SIZE__SHIFT; -+ -+ gpu->identity.buffer_size = -+ (specs[1] & VIVS_HI_CHIP_SPECS_2_BUFFER_SIZE__MASK) -+ >> VIVS_HI_CHIP_SPECS_2_BUFFER_SIZE__SHIFT; -+ gpu->identity.instruction_count = -+ (specs[1] & VIVS_HI_CHIP_SPECS_2_INSTRUCTION_COUNT__MASK) -+ >> VIVS_HI_CHIP_SPECS_2_INSTRUCTION_COUNT__SHIFT; -+ gpu->identity.num_constants = -+ (specs[1] & VIVS_HI_CHIP_SPECS_2_NUM_CONSTANTS__MASK) -+ >> VIVS_HI_CHIP_SPECS_2_NUM_CONSTANTS__SHIFT; -+ } -+ -+ /* Fill in the stream count if not specified */ -+ if (gpu->identity.stream_count == 0) { -+ if (gpu->identity.model >= 0x1000) -+ gpu->identity.stream_count = 4; -+ else -+ gpu->identity.stream_count = 1; -+ } -+ -+ /* Convert the register max value */ -+ if (gpu->identity.register_max) -+ gpu->identity.register_max = 1 << gpu->identity.register_max; -+ else if (gpu->identity.model == 0x0400) -+ gpu->identity.register_max = 32; -+ else -+ gpu->identity.register_max = 64; -+ -+ /* Convert thread count */ -+ if (gpu->identity.thread_count) -+ gpu->identity.thread_count = 1 << gpu->identity.thread_count; -+ else if (gpu->identity.model == 0x0400) -+ gpu->identity.thread_count = 64; -+ else if (gpu->identity.model == 0x0500 || -+ gpu->identity.model == 0x0530) -+ gpu->identity.thread_count = 128; -+ else -+ gpu->identity.thread_count = 256; -+ -+ if (gpu->identity.vertex_cache_size == 0) -+ gpu->identity.vertex_cache_size = 8; -+ -+ if (gpu->identity.shader_core_count == 0) { -+ if (gpu->identity.model >= 0x1000) -+ gpu->identity.shader_core_count = 2; -+ else -+ gpu->identity.shader_core_count = 1; -+ } -+ -+ if (gpu->identity.pixel_pipes == 0) -+ gpu->identity.pixel_pipes = 1; -+ -+ /* Convert virtex buffer size */ -+ if (gpu->identity.vertex_output_buffer_size) { -+ gpu->identity.vertex_output_buffer_size = -+ 1 << gpu->identity.vertex_output_buffer_size; -+ } else if (gpu->identity.model == 0x0400) { -+ if (gpu->identity.revision < 0x4000) -+ gpu->identity.vertex_output_buffer_size = 512; -+ else if (gpu->identity.revision < 0x4200) -+ gpu->identity.vertex_output_buffer_size = 256; -+ else -+ gpu->identity.vertex_output_buffer_size = 128; -+ } else { -+ gpu->identity.vertex_output_buffer_size = 512; -+ } -+ -+ switch (gpu->identity.instruction_count) { -+ case 0: -+ if ((gpu->identity.model == 0x2000 && -+ gpu->identity.revision == 0x5108) || -+ gpu->identity.model == 0x880) -+ gpu->identity.instruction_count = 512; -+ else -+ gpu->identity.instruction_count = 256; -+ break; -+ -+ case 1: -+ gpu->identity.instruction_count = 1024; -+ break; -+ -+ case 2: -+ gpu->identity.instruction_count = 2048; -+ break; -+ -+ default: -+ gpu->identity.instruction_count = 256; -+ break; -+ } -+ -+ if (gpu->identity.num_constants == 0) -+ gpu->identity.num_constants = 168; -+} -+ -+static void etnaviv_hw_identify(struct etnaviv_gpu *gpu) -+{ -+ u32 chipIdentity; -+ -+ chipIdentity = gpu_read(gpu, VIVS_HI_CHIP_IDENTITY); -+ -+ /* Special case for older graphic cores. */ -+ if (VIVS_HI_CHIP_IDENTITY_FAMILY(chipIdentity) == 0x01) { -+ gpu->identity.model = 0x500; /* gc500 */ -+ gpu->identity.revision = VIVS_HI_CHIP_IDENTITY_REVISION(chipIdentity); -+ } else { -+ -+ gpu->identity.model = gpu_read(gpu, VIVS_HI_CHIP_MODEL); -+ gpu->identity.revision = gpu_read(gpu, VIVS_HI_CHIP_REV); -+ -+ /* -+ * !!!! HACK ALERT !!!! -+ * Because people change device IDs without letting software -+ * know about it - here is the hack to make it all look the -+ * same. Only for GC400 family. -+ */ -+ if ((gpu->identity.model & 0xff00) == 0x0400 && -+ gpu->identity.model != 0x0420) { -+ gpu->identity.model = gpu->identity.model & 0x0400; -+ } -+ -+ /* Another special case */ -+ if (gpu->identity.model == 0x300 && -+ gpu->identity.revision == 0x2201) { -+ u32 chipDate = gpu_read(gpu, VIVS_HI_CHIP_DATE); -+ u32 chipTime = gpu_read(gpu, VIVS_HI_CHIP_TIME); -+ -+ if (chipDate == 0x20080814 && chipTime == 0x12051100) { -+ /* -+ * This IP has an ECO; put the correct -+ * revision in it. -+ */ -+ gpu->identity.revision = 0x1051; -+ } -+ } -+ } -+ -+ dev_info(gpu->dev, "model: GC%x, revision: %x\n", -+ gpu->identity.model, gpu->identity.revision); -+ -+ gpu->identity.features = gpu_read(gpu, VIVS_HI_CHIP_FEATURE); -+ -+ /* Disable fast clear on GC700. */ -+ if (gpu->identity.model == 0x700) -+ gpu->identity.features &= ~chipFeatures_FAST_CLEAR; -+ -+ if ((gpu->identity.model == 0x500 && gpu->identity.revision < 2) || -+ (gpu->identity.model == 0x300 && gpu->identity.revision < 0x2000)) { -+ -+ /* -+ * GC500 rev 1.x and GC300 rev < 2.0 doesn't have these -+ * registers. -+ */ -+ gpu->identity.minor_features0 = 0; -+ gpu->identity.minor_features1 = 0; -+ gpu->identity.minor_features2 = 0; -+ gpu->identity.minor_features3 = 0; -+ } else -+ gpu->identity.minor_features0 = -+ gpu_read(gpu, VIVS_HI_CHIP_MINOR_FEATURE_0); -+ -+ if (gpu->identity.minor_features0 & -+ chipMinorFeatures0_MORE_MINOR_FEATURES) { -+ gpu->identity.minor_features1 = -+ gpu_read(gpu, VIVS_HI_CHIP_MINOR_FEATURE_1); -+ gpu->identity.minor_features2 = -+ gpu_read(gpu, VIVS_HI_CHIP_MINOR_FEATURE_2); -+ gpu->identity.minor_features3 = -+ gpu_read(gpu, VIVS_HI_CHIP_MINOR_FEATURE_3); -+ } -+ -+ /* GC600 idle register reports zero bits where modules aren't present */ -+ if (gpu->identity.model == chipModel_GC600) { -+ gpu->idle_mask = VIVS_HI_IDLE_STATE_TX | -+ VIVS_HI_IDLE_STATE_RA | -+ VIVS_HI_IDLE_STATE_SE | -+ VIVS_HI_IDLE_STATE_PA | -+ VIVS_HI_IDLE_STATE_SH | -+ VIVS_HI_IDLE_STATE_PE | -+ VIVS_HI_IDLE_STATE_DE | -+ VIVS_HI_IDLE_STATE_FE; -+ } else { -+ gpu->idle_mask = ~VIVS_HI_IDLE_STATE_AXI_LP; -+ } -+ -+ etnaviv_hw_specs(gpu); -+} -+ -+static void etnaviv_gpu_load_clock(struct etnaviv_gpu *gpu, u32 clock) -+{ -+ gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, clock | -+ VIVS_HI_CLOCK_CONTROL_FSCALE_CMD_LOAD); -+ gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, clock); -+} -+ -+static int etnaviv_hw_reset(struct etnaviv_gpu *gpu) -+{ -+ u32 control, idle; -+ unsigned long timeout; -+ bool failed = true; -+ -+ /* TODO -+ * -+ * - clock gating -+ * - puls eater -+ * - what about VG? -+ */ -+ -+ /* We hope that the GPU resets in under one second */ -+ timeout = jiffies + msecs_to_jiffies(1000); -+ -+ while (time_is_after_jiffies(timeout)) { -+ control = VIVS_HI_CLOCK_CONTROL_DISABLE_DEBUG_REGISTERS | -+ VIVS_HI_CLOCK_CONTROL_FSCALE_VAL(0x40); -+ -+ /* enable clock */ -+ etnaviv_gpu_load_clock(gpu, control); -+ -+ /* Wait for stable clock. Vivante's code waited for 1ms */ -+ usleep_range(1000, 10000); -+ -+ /* isolate the GPU. */ -+ control |= VIVS_HI_CLOCK_CONTROL_ISOLATE_GPU; -+ gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, control); -+ -+ /* set soft reset. */ -+ control |= VIVS_HI_CLOCK_CONTROL_SOFT_RESET; -+ gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, control); -+ -+ /* wait for reset. */ -+ msleep(1); -+ -+ /* reset soft reset bit. */ -+ control &= ~VIVS_HI_CLOCK_CONTROL_SOFT_RESET; -+ gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, control); -+ -+ /* reset GPU isolation. */ -+ control &= ~VIVS_HI_CLOCK_CONTROL_ISOLATE_GPU; -+ gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, control); -+ -+ /* read idle register. */ -+ idle = gpu_read(gpu, VIVS_HI_IDLE_STATE); -+ -+ /* try reseting again if FE it not idle */ -+ if ((idle & VIVS_HI_IDLE_STATE_FE) == 0) { -+ dev_dbg(gpu->dev, "FE is not idle\n"); -+ continue; -+ } -+ -+ /* read reset register. */ -+ control = gpu_read(gpu, VIVS_HI_CLOCK_CONTROL); -+ -+ /* is the GPU idle? */ -+ if (((control & VIVS_HI_CLOCK_CONTROL_IDLE_3D) == 0) || -+ ((control & VIVS_HI_CLOCK_CONTROL_IDLE_2D) == 0)) { -+ dev_dbg(gpu->dev, "GPU is not idle\n"); -+ continue; -+ } -+ -+ failed = false; -+ break; -+ } -+ -+ if (failed) { -+ idle = gpu_read(gpu, VIVS_HI_IDLE_STATE); -+ control = gpu_read(gpu, VIVS_HI_CLOCK_CONTROL); -+ -+ dev_err(gpu->dev, "GPU failed to reset: FE %sidle, 3D %sidle, 2D %sidle\n", -+ idle & VIVS_HI_IDLE_STATE_FE ? "" : "not ", -+ control & VIVS_HI_CLOCK_CONTROL_IDLE_3D ? "" : "not ", -+ control & VIVS_HI_CLOCK_CONTROL_IDLE_2D ? "" : "not "); -+ -+ return -EBUSY; -+ } -+ -+ /* We rely on the GPU running, so program the clock */ -+ control = VIVS_HI_CLOCK_CONTROL_DISABLE_DEBUG_REGISTERS | -+ VIVS_HI_CLOCK_CONTROL_FSCALE_VAL(0x40); -+ -+ /* enable clock */ -+ etnaviv_gpu_load_clock(gpu, control); -+ -+ return 0; -+} -+ -+static void etnaviv_gpu_hw_init(struct etnaviv_gpu *gpu) -+{ -+ u16 prefetch; -+ -+ if (gpu->identity.model == chipModel_GC320 && -+ gpu_read(gpu, VIVS_HI_CHIP_TIME) != 0x2062400 && -+ (gpu->identity.revision == 0x5007 || -+ gpu->identity.revision == 0x5220)) { -+ u32 mc_memory_debug; -+ -+ mc_memory_debug = gpu_read(gpu, VIVS_MC_DEBUG_MEMORY) & ~0xff; -+ -+ if (gpu->identity.revision == 0x5007) -+ mc_memory_debug |= 0x0c; -+ else -+ mc_memory_debug |= 0x08; -+ -+ gpu_write(gpu, VIVS_MC_DEBUG_MEMORY, mc_memory_debug); -+ } -+ -+ /* -+ * Update GPU AXI cache atttribute to "cacheable, no allocate". -+ * This is necessary to prevent the iMX6 SoC locking up. -+ */ -+ gpu_write(gpu, VIVS_HI_AXI_CONFIG, -+ VIVS_HI_AXI_CONFIG_AWCACHE(2) | -+ VIVS_HI_AXI_CONFIG_ARCACHE(2)); -+ -+ /* GC2000 rev 5108 needs a special bus config */ -+ if (gpu->identity.model == 0x2000 && gpu->identity.revision == 0x5108) { -+ u32 bus_config = gpu_read(gpu, VIVS_MC_BUS_CONFIG); -+ bus_config &= ~(VIVS_MC_BUS_CONFIG_FE_BUS_CONFIG__MASK | -+ VIVS_MC_BUS_CONFIG_TX_BUS_CONFIG__MASK); -+ bus_config |= VIVS_MC_BUS_CONFIG_FE_BUS_CONFIG(1) | -+ VIVS_MC_BUS_CONFIG_TX_BUS_CONFIG(0); -+ gpu_write(gpu, VIVS_MC_BUS_CONFIG, bus_config); -+ } -+ -+ /* set base addresses */ -+ gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_RA, gpu->memory_base); -+ gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_FE, gpu->memory_base); -+ gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_TX, gpu->memory_base); -+ gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_PEZ, gpu->memory_base); -+ gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_PE, gpu->memory_base); -+ -+ /* setup the MMU page table pointers */ -+ etnaviv_iommu_domain_restore(gpu, gpu->mmu->domain); -+ -+ /* Start command processor */ -+ prefetch = etnaviv_buffer_init(gpu); -+ -+ gpu_write(gpu, VIVS_HI_INTR_ENBL, ~0U); -+ gpu_write(gpu, VIVS_FE_COMMAND_ADDRESS, -+ gpu->buffer->paddr - gpu->memory_base); -+ gpu_write(gpu, VIVS_FE_COMMAND_CONTROL, -+ VIVS_FE_COMMAND_CONTROL_ENABLE | -+ VIVS_FE_COMMAND_CONTROL_PREFETCH(prefetch)); -+} -+ -+int etnaviv_gpu_init(struct etnaviv_gpu *gpu) -+{ -+ int ret, i; -+ struct iommu_domain *iommu; -+ enum etnaviv_iommu_version version; -+ bool mmuv2; -+ -+ ret = pm_runtime_get_sync(gpu->dev); -+ if (ret < 0) -+ return ret; -+ -+ etnaviv_hw_identify(gpu); -+ -+ if (gpu->identity.model == 0) { -+ dev_err(gpu->dev, "Unknown GPU model\n"); -+ pm_runtime_put_autosuspend(gpu->dev); -+ return -ENXIO; -+ } -+ -+ ret = etnaviv_hw_reset(gpu); -+ if (ret) -+ goto fail; -+ -+ /* Setup IOMMU.. eventually we will (I think) do this once per context -+ * and have separate page tables per context. For now, to keep things -+ * simple and to get something working, just use a single address space: -+ */ -+ mmuv2 = gpu->identity.minor_features1 & chipMinorFeatures1_MMU_VERSION; -+ dev_dbg(gpu->dev, "mmuv2: %d\n", mmuv2); -+ -+ if (!mmuv2) { -+ iommu = etnaviv_iommu_domain_alloc(gpu); -+ version = ETNAVIV_IOMMU_V1; -+ } else { -+ iommu = etnaviv_iommu_v2_domain_alloc(gpu); -+ version = ETNAVIV_IOMMU_V2; -+ } -+ -+ if (!iommu) { -+ ret = -ENOMEM; -+ goto fail; -+ } -+ -+ /* TODO: we will leak here memory - fix it! */ -+ -+ gpu->mmu = etnaviv_iommu_new(gpu, iommu, version); -+ if (!gpu->mmu) { -+ ret = -ENOMEM; -+ goto fail; -+ } -+ -+ /* Create buffer: */ -+ gpu->buffer = etnaviv_gpu_cmdbuf_new(gpu, PAGE_SIZE, 0); -+ if (!gpu->buffer) { -+ ret = -ENOMEM; -+ dev_err(gpu->dev, "could not create command buffer\n"); -+ goto fail; -+ } -+ if (gpu->buffer->paddr - gpu->memory_base > 0x80000000) { -+ ret = -EINVAL; -+ dev_err(gpu->dev, -+ "command buffer outside valid memory window\n"); -+ goto free_buffer; -+ } -+ -+ /* Setup event management */ -+ spin_lock_init(&gpu->event_spinlock); -+ init_completion(&gpu->event_free); -+ for (i = 0; i < ARRAY_SIZE(gpu->event); i++) { -+ gpu->event[i].used = false; -+ complete(&gpu->event_free); -+ } -+ -+ /* Now program the hardware */ -+ mutex_lock(&gpu->lock); -+ etnaviv_gpu_hw_init(gpu); -+ mutex_unlock(&gpu->lock); -+ -+ pm_runtime_mark_last_busy(gpu->dev); -+ pm_runtime_put_autosuspend(gpu->dev); -+ -+ return 0; -+ -+free_buffer: -+ etnaviv_gpu_cmdbuf_free(gpu->buffer); -+ gpu->buffer = NULL; -+fail: -+ pm_runtime_mark_last_busy(gpu->dev); -+ pm_runtime_put_autosuspend(gpu->dev); -+ -+ return ret; -+} -+ -+#ifdef CONFIG_DEBUG_FS -+struct dma_debug { -+ u32 address[2]; -+ u32 state[2]; -+}; -+ -+static void verify_dma(struct etnaviv_gpu *gpu, struct dma_debug *debug) -+{ -+ u32 i; -+ -+ debug->address[0] = gpu_read(gpu, VIVS_FE_DMA_ADDRESS); -+ debug->state[0] = gpu_read(gpu, VIVS_FE_DMA_DEBUG_STATE); -+ -+ for (i = 0; i < 500; i++) { -+ debug->address[1] = gpu_read(gpu, VIVS_FE_DMA_ADDRESS); -+ debug->state[1] = gpu_read(gpu, VIVS_FE_DMA_DEBUG_STATE); -+ -+ if (debug->address[0] != debug->address[1]) -+ break; -+ -+ if (debug->state[0] != debug->state[1]) -+ break; -+ } -+} -+ -+int etnaviv_gpu_debugfs(struct etnaviv_gpu *gpu, struct seq_file *m) -+{ -+ struct dma_debug debug; -+ u32 dma_lo, dma_hi, axi, idle; -+ int ret; -+ -+ seq_printf(m, "%s Status:\n", dev_name(gpu->dev)); -+ -+ ret = pm_runtime_get_sync(gpu->dev); -+ if (ret < 0) -+ return ret; -+ -+ dma_lo = gpu_read(gpu, VIVS_FE_DMA_LOW); -+ dma_hi = gpu_read(gpu, VIVS_FE_DMA_HIGH); -+ axi = gpu_read(gpu, VIVS_HI_AXI_STATUS); -+ idle = gpu_read(gpu, VIVS_HI_IDLE_STATE); -+ -+ verify_dma(gpu, &debug); -+ -+ seq_puts(m, "\tfeatures\n"); -+ seq_printf(m, "\t minor_features0: 0x%08x\n", -+ gpu->identity.minor_features0); -+ seq_printf(m, "\t minor_features1: 0x%08x\n", -+ gpu->identity.minor_features1); -+ seq_printf(m, "\t minor_features2: 0x%08x\n", -+ gpu->identity.minor_features2); -+ seq_printf(m, "\t minor_features3: 0x%08x\n", -+ gpu->identity.minor_features3); -+ -+ seq_puts(m, "\tspecs\n"); -+ seq_printf(m, "\t stream_count: %d\n", -+ gpu->identity.stream_count); -+ seq_printf(m, "\t register_max: %d\n", -+ gpu->identity.register_max); -+ seq_printf(m, "\t thread_count: %d\n", -+ gpu->identity.thread_count); -+ seq_printf(m, "\t vertex_cache_size: %d\n", -+ gpu->identity.vertex_cache_size); -+ seq_printf(m, "\t shader_core_count: %d\n", -+ gpu->identity.shader_core_count); -+ seq_printf(m, "\t pixel_pipes: %d\n", -+ gpu->identity.pixel_pipes); -+ seq_printf(m, "\t vertex_output_buffer_size: %d\n", -+ gpu->identity.vertex_output_buffer_size); -+ seq_printf(m, "\t buffer_size: %d\n", -+ gpu->identity.buffer_size); -+ seq_printf(m, "\t instruction_count: %d\n", -+ gpu->identity.instruction_count); -+ seq_printf(m, "\t num_constants: %d\n", -+ gpu->identity.num_constants); -+ -+ seq_printf(m, "\taxi: 0x%08x\n", axi); -+ seq_printf(m, "\tidle: 0x%08x\n", idle); -+ idle |= ~gpu->idle_mask & ~VIVS_HI_IDLE_STATE_AXI_LP; -+ if ((idle & VIVS_HI_IDLE_STATE_FE) == 0) -+ seq_puts(m, "\t FE is not idle\n"); -+ if ((idle & VIVS_HI_IDLE_STATE_DE) == 0) -+ seq_puts(m, "\t DE is not idle\n"); -+ if ((idle & VIVS_HI_IDLE_STATE_PE) == 0) -+ seq_puts(m, "\t PE is not idle\n"); -+ if ((idle & VIVS_HI_IDLE_STATE_SH) == 0) -+ seq_puts(m, "\t SH is not idle\n"); -+ if ((idle & VIVS_HI_IDLE_STATE_PA) == 0) -+ seq_puts(m, "\t PA is not idle\n"); -+ if ((idle & VIVS_HI_IDLE_STATE_SE) == 0) -+ seq_puts(m, "\t SE is not idle\n"); -+ if ((idle & VIVS_HI_IDLE_STATE_RA) == 0) -+ seq_puts(m, "\t RA is not idle\n"); -+ if ((idle & VIVS_HI_IDLE_STATE_TX) == 0) -+ seq_puts(m, "\t TX is not idle\n"); -+ if ((idle & VIVS_HI_IDLE_STATE_VG) == 0) -+ seq_puts(m, "\t VG is not idle\n"); -+ if ((idle & VIVS_HI_IDLE_STATE_IM) == 0) -+ seq_puts(m, "\t IM is not idle\n"); -+ if ((idle & VIVS_HI_IDLE_STATE_FP) == 0) -+ seq_puts(m, "\t FP is not idle\n"); -+ if ((idle & VIVS_HI_IDLE_STATE_TS) == 0) -+ seq_puts(m, "\t TS is not idle\n"); -+ if (idle & VIVS_HI_IDLE_STATE_AXI_LP) -+ seq_puts(m, "\t AXI low power mode\n"); -+ -+ if (gpu->identity.features & chipFeatures_DEBUG_MODE) { -+ u32 read0 = gpu_read(gpu, VIVS_MC_DEBUG_READ0); -+ u32 read1 = gpu_read(gpu, VIVS_MC_DEBUG_READ1); -+ u32 write = gpu_read(gpu, VIVS_MC_DEBUG_WRITE); -+ -+ seq_puts(m, "\tMC\n"); -+ seq_printf(m, "\t read0: 0x%08x\n", read0); -+ seq_printf(m, "\t read1: 0x%08x\n", read1); -+ seq_printf(m, "\t write: 0x%08x\n", write); -+ } -+ -+ seq_puts(m, "\tDMA "); -+ -+ if (debug.address[0] == debug.address[1] && -+ debug.state[0] == debug.state[1]) { -+ seq_puts(m, "seems to be stuck\n"); -+ } else if (debug.address[0] == debug.address[1]) { -+ seq_puts(m, "adress is constant\n"); -+ } else { -+ seq_puts(m, "is runing\n"); -+ } -+ -+ seq_printf(m, "\t address 0: 0x%08x\n", debug.address[0]); -+ seq_printf(m, "\t address 1: 0x%08x\n", debug.address[1]); -+ seq_printf(m, "\t state 0: 0x%08x\n", debug.state[0]); -+ seq_printf(m, "\t state 1: 0x%08x\n", debug.state[1]); -+ seq_printf(m, "\t last fetch 64 bit word: 0x%08x 0x%08x\n", -+ dma_lo, dma_hi); -+ -+ ret = 0; -+ -+ pm_runtime_mark_last_busy(gpu->dev); -+ pm_runtime_put_autosuspend(gpu->dev); -+ -+ return ret; -+} -+#endif -+ -+/* -+ * Power Management: -+ */ -+static int enable_clk(struct etnaviv_gpu *gpu) -+{ -+ if (gpu->clk_core) -+ clk_prepare_enable(gpu->clk_core); -+ if (gpu->clk_shader) -+ clk_prepare_enable(gpu->clk_shader); -+ -+ return 0; -+} -+ -+static int disable_clk(struct etnaviv_gpu *gpu) -+{ -+ if (gpu->clk_core) -+ clk_disable_unprepare(gpu->clk_core); -+ if (gpu->clk_shader) -+ clk_disable_unprepare(gpu->clk_shader); -+ -+ return 0; -+} -+ -+static int enable_axi(struct etnaviv_gpu *gpu) -+{ -+ if (gpu->clk_bus) -+ clk_prepare_enable(gpu->clk_bus); -+ -+ return 0; -+} -+ -+static int disable_axi(struct etnaviv_gpu *gpu) -+{ -+ if (gpu->clk_bus) -+ clk_disable_unprepare(gpu->clk_bus); -+ -+ return 0; -+} -+ -+/* -+ * Hangcheck detection for locked gpu: -+ */ -+static void recover_worker(struct work_struct *work) -+{ -+ struct etnaviv_gpu *gpu = container_of(work, struct etnaviv_gpu, -+ recover_work); -+ unsigned long flags; -+ unsigned int i; -+ -+ dev_err(gpu->dev, "hangcheck recover!\n"); -+ -+ if (pm_runtime_get_sync(gpu->dev) < 0) -+ return; -+ -+ mutex_lock(&gpu->lock); -+ -+ /* Only catch the first event, or when manually re-armed */ -+ if (etnaviv_dump_core) { -+ etnaviv_core_dump(gpu); -+ etnaviv_dump_core = false; -+ } -+ -+ etnaviv_hw_reset(gpu); -+ -+ /* complete all events, the GPU won't do it after the reset */ -+ spin_lock_irqsave(&gpu->event_spinlock, flags); -+ for (i = 0; i < ARRAY_SIZE(gpu->event); i++) { -+ if (!gpu->event[i].used) -+ continue; -+ fence_signal(gpu->event[i].fence); -+ gpu->event[i].fence = NULL; -+ gpu->event[i].used = false; -+ complete(&gpu->event_free); -+ /* -+ * Decrement the PM count for each stuck event. This is safe -+ * even in atomic context as we use ASYNC RPM here. -+ */ -+ pm_runtime_put_autosuspend(gpu->dev); -+ } -+ spin_unlock_irqrestore(&gpu->event_spinlock, flags); -+ gpu->completed_fence = gpu->active_fence; -+ -+ etnaviv_gpu_hw_init(gpu); -+ gpu->switch_context = true; -+ -+ mutex_unlock(&gpu->lock); -+ pm_runtime_mark_last_busy(gpu->dev); -+ pm_runtime_put_autosuspend(gpu->dev); -+ -+ /* Retire the buffer objects in a work */ -+ etnaviv_queue_work(gpu->drm, &gpu->retire_work); -+} -+ -+static void hangcheck_timer_reset(struct etnaviv_gpu *gpu) -+{ -+ DBG("%s", dev_name(gpu->dev)); -+ mod_timer(&gpu->hangcheck_timer, -+ round_jiffies_up(jiffies + DRM_ETNAVIV_HANGCHECK_JIFFIES)); -+} -+ -+static void hangcheck_handler(unsigned long data) -+{ -+ struct etnaviv_gpu *gpu = (struct etnaviv_gpu *)data; -+ u32 fence = gpu->completed_fence; -+ bool progress = false; -+ -+ if (fence != gpu->hangcheck_fence) { -+ gpu->hangcheck_fence = fence; -+ progress = true; -+ } -+ -+ if (!progress) { -+ u32 dma_addr = gpu_read(gpu, VIVS_FE_DMA_ADDRESS); -+ int change = dma_addr - gpu->hangcheck_dma_addr; -+ -+ if (change < 0 || change > 16) { -+ gpu->hangcheck_dma_addr = dma_addr; -+ progress = true; -+ } -+ } -+ -+ if (!progress && fence_after(gpu->active_fence, fence)) { -+ dev_err(gpu->dev, "hangcheck detected gpu lockup!\n"); -+ dev_err(gpu->dev, " completed fence: %u\n", fence); -+ dev_err(gpu->dev, " active fence: %u\n", -+ gpu->active_fence); -+ etnaviv_queue_work(gpu->drm, &gpu->recover_work); -+ } -+ -+ /* if still more pending work, reset the hangcheck timer: */ -+ if (fence_after(gpu->active_fence, gpu->hangcheck_fence)) -+ hangcheck_timer_reset(gpu); -+} -+ -+static void hangcheck_disable(struct etnaviv_gpu *gpu) -+{ -+ del_timer_sync(&gpu->hangcheck_timer); -+ cancel_work_sync(&gpu->recover_work); -+} -+ -+/* fence object management */ -+struct etnaviv_fence { -+ struct etnaviv_gpu *gpu; -+ struct fence base; -+}; -+ -+static inline struct etnaviv_fence *to_etnaviv_fence(struct fence *fence) -+{ -+ return container_of(fence, struct etnaviv_fence, base); -+} -+ -+static const char *etnaviv_fence_get_driver_name(struct fence *fence) -+{ -+ return "etnaviv"; -+} -+ -+static const char *etnaviv_fence_get_timeline_name(struct fence *fence) -+{ -+ struct etnaviv_fence *f = to_etnaviv_fence(fence); -+ -+ return dev_name(f->gpu->dev); -+} -+ -+static bool etnaviv_fence_enable_signaling(struct fence *fence) -+{ -+ return true; -+} -+ -+static bool etnaviv_fence_signaled(struct fence *fence) -+{ -+ struct etnaviv_fence *f = to_etnaviv_fence(fence); -+ -+ return fence_completed(f->gpu, f->base.seqno); -+} -+ -+static void etnaviv_fence_release(struct fence *fence) -+{ -+ struct etnaviv_fence *f = to_etnaviv_fence(fence); -+ -+ kfree_rcu(f, base.rcu); -+} -+ -+static const struct fence_ops etnaviv_fence_ops = { -+ .get_driver_name = etnaviv_fence_get_driver_name, -+ .get_timeline_name = etnaviv_fence_get_timeline_name, -+ .enable_signaling = etnaviv_fence_enable_signaling, -+ .signaled = etnaviv_fence_signaled, -+ .wait = fence_default_wait, -+ .release = etnaviv_fence_release, -+}; -+ -+static struct fence *etnaviv_gpu_fence_alloc(struct etnaviv_gpu *gpu) -+{ -+ struct etnaviv_fence *f; -+ -+ f = kzalloc(sizeof(*f), GFP_KERNEL); -+ if (!f) -+ return NULL; -+ -+ f->gpu = gpu; -+ -+ fence_init(&f->base, &etnaviv_fence_ops, &gpu->fence_spinlock, -+ gpu->fence_context, ++gpu->next_fence); -+ -+ return &f->base; -+} -+ -+int etnaviv_gpu_fence_sync_obj(struct etnaviv_gem_object *etnaviv_obj, -+ unsigned int context, bool exclusive) -+{ -+ struct reservation_object *robj = etnaviv_obj->resv; -+ struct reservation_object_list *fobj; -+ struct fence *fence; -+ int i, ret; -+ -+ if (!exclusive) { -+ ret = reservation_object_reserve_shared(robj); -+ if (ret) -+ return ret; -+ } -+ -+ /* -+ * If we have any shared fences, then the exclusive fence -+ * should be ignored as it will already have been signalled. -+ */ -+ fobj = reservation_object_get_list(robj); -+ if (!fobj || fobj->shared_count == 0) { -+ /* Wait on any existing exclusive fence which isn't our own */ -+ fence = reservation_object_get_excl(robj); -+ if (fence && fence->context != context) { -+ ret = fence_wait(fence, true); -+ if (ret) -+ return ret; -+ } -+ } -+ -+ if (!exclusive || !fobj) -+ return 0; -+ -+ for (i = 0; i < fobj->shared_count; i++) { -+ fence = rcu_dereference_protected(fobj->shared[i], -+ reservation_object_held(robj)); -+ if (fence->context != context) { -+ ret = fence_wait(fence, true); -+ if (ret) -+ return ret; -+ } -+ } -+ -+ return 0; -+} -+ -+/* -+ * event management: -+ */ -+ -+static unsigned int event_alloc(struct etnaviv_gpu *gpu) -+{ -+ unsigned long ret, flags; -+ unsigned int i, event = ~0U; -+ -+ ret = wait_for_completion_timeout(&gpu->event_free, -+ msecs_to_jiffies(10 * 10000)); -+ if (!ret) -+ dev_err(gpu->dev, "wait_for_completion_timeout failed"); -+ -+ spin_lock_irqsave(&gpu->event_spinlock, flags); -+ -+ /* find first free event */ -+ for (i = 0; i < ARRAY_SIZE(gpu->event); i++) { -+ if (gpu->event[i].used == false) { -+ gpu->event[i].used = true; -+ event = i; -+ break; -+ } -+ } -+ -+ spin_unlock_irqrestore(&gpu->event_spinlock, flags); -+ -+ return event; -+} -+ -+static void event_free(struct etnaviv_gpu *gpu, unsigned int event) -+{ -+ unsigned long flags; -+ -+ spin_lock_irqsave(&gpu->event_spinlock, flags); -+ -+ if (gpu->event[event].used == false) { -+ dev_warn(gpu->dev, "event %u is already marked as free", -+ event); -+ spin_unlock_irqrestore(&gpu->event_spinlock, flags); -+ } else { -+ gpu->event[event].used = false; -+ spin_unlock_irqrestore(&gpu->event_spinlock, flags); -+ -+ complete(&gpu->event_free); -+ } -+} -+ -+/* -+ * Cmdstream submission/retirement: -+ */ -+ -+struct etnaviv_cmdbuf *etnaviv_gpu_cmdbuf_new(struct etnaviv_gpu *gpu, u32 size, -+ size_t nr_bos) -+{ -+ struct etnaviv_cmdbuf *cmdbuf; -+ size_t sz = size_vstruct(nr_bos, sizeof(cmdbuf->bo[0]), -+ sizeof(*cmdbuf)); -+ -+ cmdbuf = kzalloc(sz, GFP_KERNEL); -+ if (!cmdbuf) -+ return NULL; -+ -+ cmdbuf->vaddr = dma_alloc_writecombine(gpu->dev, size, &cmdbuf->paddr, -+ GFP_KERNEL); -+ if (!cmdbuf->vaddr) { -+ kfree(cmdbuf); -+ return NULL; -+ } -+ -+ cmdbuf->gpu = gpu; -+ cmdbuf->size = size; -+ -+ return cmdbuf; -+} -+ -+void etnaviv_gpu_cmdbuf_free(struct etnaviv_cmdbuf *cmdbuf) -+{ -+ dma_free_writecombine(cmdbuf->gpu->dev, cmdbuf->size, -+ cmdbuf->vaddr, cmdbuf->paddr); -+ kfree(cmdbuf); -+} -+ -+static void retire_worker(struct work_struct *work) -+{ -+ struct etnaviv_gpu *gpu = container_of(work, struct etnaviv_gpu, -+ retire_work); -+ u32 fence = gpu->completed_fence; -+ struct etnaviv_cmdbuf *cmdbuf, *tmp; -+ unsigned int i; -+ -+ mutex_lock(&gpu->lock); -+ list_for_each_entry_safe(cmdbuf, tmp, &gpu->active_cmd_list, node) { -+ if (!fence_is_signaled(cmdbuf->fence)) -+ break; -+ -+ list_del(&cmdbuf->node); -+ fence_put(cmdbuf->fence); -+ -+ for (i = 0; i < cmdbuf->nr_bos; i++) { -+ struct etnaviv_gem_object *etnaviv_obj = cmdbuf->bo[i]; -+ -+ atomic_dec(&etnaviv_obj->gpu_active); -+ /* drop the refcount taken in etnaviv_gpu_submit */ -+ etnaviv_gem_put_iova(gpu, &etnaviv_obj->base); -+ } -+ -+ etnaviv_gpu_cmdbuf_free(cmdbuf); -+ } -+ -+ gpu->retired_fence = fence; -+ -+ mutex_unlock(&gpu->lock); -+ -+ wake_up_all(&gpu->fence_event); -+} -+ -+int etnaviv_gpu_wait_fence_interruptible(struct etnaviv_gpu *gpu, -+ u32 fence, struct timespec *timeout) -+{ -+ int ret; -+ -+ if (fence_after(fence, gpu->next_fence)) { -+ DRM_ERROR("waiting on invalid fence: %u (of %u)\n", -+ fence, gpu->next_fence); -+ return -EINVAL; -+ } -+ -+ if (!timeout) { -+ /* No timeout was requested: just test for completion */ -+ ret = fence_completed(gpu, fence) ? 0 : -EBUSY; -+ } else { -+ unsigned long remaining = etnaviv_timeout_to_jiffies(timeout); -+ -+ ret = wait_event_interruptible_timeout(gpu->fence_event, -+ fence_completed(gpu, fence), -+ remaining); -+ if (ret == 0) { -+ DBG("timeout waiting for fence: %u (retired: %u completed: %u)", -+ fence, gpu->retired_fence, -+ gpu->completed_fence); -+ ret = -ETIMEDOUT; -+ } else if (ret != -ERESTARTSYS) { -+ ret = 0; -+ } -+ } -+ -+ return ret; -+} -+ -+/* -+ * Wait for an object to become inactive. This, on it's own, is not race -+ * free: the object is moved by the retire worker off the active list, and -+ * then the iova is put. Moreover, the object could be re-submitted just -+ * after we notice that it's become inactive. -+ * -+ * Although the retirement happens under the gpu lock, we don't want to hold -+ * that lock in this function while waiting. -+ */ -+int etnaviv_gpu_wait_obj_inactive(struct etnaviv_gpu *gpu, -+ struct etnaviv_gem_object *etnaviv_obj, struct timespec *timeout) -+{ -+ unsigned long remaining; -+ long ret; -+ -+ if (!timeout) -+ return !is_active(etnaviv_obj) ? 0 : -EBUSY; -+ -+ remaining = etnaviv_timeout_to_jiffies(timeout); -+ -+ ret = wait_event_interruptible_timeout(gpu->fence_event, -+ !is_active(etnaviv_obj), -+ remaining); -+ if (ret > 0) { -+ struct etnaviv_drm_private *priv = gpu->drm->dev_private; -+ -+ /* Synchronise with the retire worker */ -+ flush_workqueue(priv->wq); -+ return 0; -+ } else if (ret == -ERESTARTSYS) { -+ return -ERESTARTSYS; -+ } else { -+ return -ETIMEDOUT; -+ } -+} -+ -+int etnaviv_gpu_pm_get_sync(struct etnaviv_gpu *gpu) -+{ -+ return pm_runtime_get_sync(gpu->dev); -+} -+ -+void etnaviv_gpu_pm_put(struct etnaviv_gpu *gpu) -+{ -+ pm_runtime_mark_last_busy(gpu->dev); -+ pm_runtime_put_autosuspend(gpu->dev); -+} -+ -+/* add bo's to gpu's ring, and kick gpu: */ -+int etnaviv_gpu_submit(struct etnaviv_gpu *gpu, -+ struct etnaviv_gem_submit *submit, struct etnaviv_cmdbuf *cmdbuf) -+{ -+ struct fence *fence; -+ unsigned int event, i; -+ int ret; -+ -+ ret = etnaviv_gpu_pm_get_sync(gpu); -+ if (ret < 0) -+ return ret; -+ -+ mutex_lock(&gpu->lock); -+ -+ /* -+ * TODO -+ * -+ * - flush -+ * - data endian -+ * - prefetch -+ * -+ */ -+ -+ event = event_alloc(gpu); -+ if (unlikely(event == ~0U)) { -+ DRM_ERROR("no free event\n"); -+ ret = -EBUSY; -+ goto out_unlock; -+ } -+ -+ fence = etnaviv_gpu_fence_alloc(gpu); -+ if (!fence) { -+ event_free(gpu, event); -+ ret = -ENOMEM; -+ goto out_unlock; -+ } -+ -+ gpu->event[event].fence = fence; -+ submit->fence = fence->seqno; -+ gpu->active_fence = submit->fence; -+ -+ if (gpu->lastctx != cmdbuf->ctx) { -+ gpu->mmu->need_flush = true; -+ gpu->switch_context = true; -+ gpu->lastctx = cmdbuf->ctx; -+ } -+ -+ etnaviv_buffer_queue(gpu, event, cmdbuf); -+ -+ cmdbuf->fence = fence; -+ list_add_tail(&cmdbuf->node, &gpu->active_cmd_list); -+ -+ /* We're committed to adding this command buffer, hold a PM reference */ -+ pm_runtime_get_noresume(gpu->dev); -+ -+ for (i = 0; i < submit->nr_bos; i++) { -+ struct etnaviv_gem_object *etnaviv_obj = submit->bos[i].obj; -+ u32 iova; -+ -+ /* Each cmdbuf takes a refcount on the iova */ -+ etnaviv_gem_get_iova(gpu, &etnaviv_obj->base, &iova); -+ cmdbuf->bo[i] = etnaviv_obj; -+ atomic_inc(&etnaviv_obj->gpu_active); -+ -+ if (submit->bos[i].flags & ETNA_SUBMIT_BO_WRITE) -+ reservation_object_add_excl_fence(etnaviv_obj->resv, -+ fence); -+ else -+ reservation_object_add_shared_fence(etnaviv_obj->resv, -+ fence); -+ } -+ cmdbuf->nr_bos = submit->nr_bos; -+ hangcheck_timer_reset(gpu); -+ ret = 0; -+ -+out_unlock: -+ mutex_unlock(&gpu->lock); -+ -+ etnaviv_gpu_pm_put(gpu); -+ -+ return ret; -+} -+ -+/* -+ * Init/Cleanup: -+ */ -+static irqreturn_t irq_handler(int irq, void *data) -+{ -+ struct etnaviv_gpu *gpu = data; -+ irqreturn_t ret = IRQ_NONE; -+ -+ u32 intr = gpu_read(gpu, VIVS_HI_INTR_ACKNOWLEDGE); -+ -+ if (intr != 0) { -+ int event; -+ -+ pm_runtime_mark_last_busy(gpu->dev); -+ -+ dev_dbg(gpu->dev, "intr 0x%08x\n", intr); -+ -+ if (intr & VIVS_HI_INTR_ACKNOWLEDGE_AXI_BUS_ERROR) { -+ dev_err(gpu->dev, "AXI bus error\n"); -+ intr &= ~VIVS_HI_INTR_ACKNOWLEDGE_AXI_BUS_ERROR; -+ } -+ -+ while ((event = ffs(intr)) != 0) { -+ struct fence *fence; -+ -+ event -= 1; -+ -+ intr &= ~(1 << event); -+ -+ dev_dbg(gpu->dev, "event %u\n", event); -+ -+ fence = gpu->event[event].fence; -+ gpu->event[event].fence = NULL; -+ fence_signal(fence); -+ -+ /* -+ * Events can be processed out of order. Eg, -+ * - allocate and queue event 0 -+ * - allocate event 1 -+ * - event 0 completes, we process it -+ * - allocate and queue event 0 -+ * - event 1 and event 0 complete -+ * we can end up processing event 0 first, then 1. -+ */ -+ if (fence_after(fence->seqno, gpu->completed_fence)) -+ gpu->completed_fence = fence->seqno; -+ -+ event_free(gpu, event); -+ -+ /* -+ * We need to balance the runtime PM count caused by -+ * each submission. Upon submission, we increment -+ * the runtime PM counter, and allocate one event. -+ * So here, we put the runtime PM count for each -+ * completed event. -+ */ -+ pm_runtime_put_autosuspend(gpu->dev); -+ } -+ -+ /* Retire the buffer objects in a work */ -+ etnaviv_queue_work(gpu->drm, &gpu->retire_work); -+ -+ ret = IRQ_HANDLED; -+ } -+ -+ return ret; -+} -+ -+static int etnaviv_gpu_clk_enable(struct etnaviv_gpu *gpu) -+{ -+ int ret; -+ -+ ret = enable_clk(gpu); -+ if (ret) -+ return ret; -+ -+ ret = enable_axi(gpu); -+ if (ret) { -+ disable_clk(gpu); -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static int etnaviv_gpu_clk_disable(struct etnaviv_gpu *gpu) -+{ -+ int ret; -+ -+ ret = disable_axi(gpu); -+ if (ret) -+ return ret; -+ -+ ret = disable_clk(gpu); -+ if (ret) -+ return ret; -+ -+ return 0; -+} -+ -+static int etnaviv_gpu_hw_suspend(struct etnaviv_gpu *gpu) -+{ -+ if (gpu->buffer) { -+ unsigned long timeout; -+ -+ /* Replace the last WAIT with END */ -+ etnaviv_buffer_end(gpu); -+ -+ /* -+ * We know that only the FE is busy here, this should -+ * happen quickly (as the WAIT is only 200 cycles). If -+ * we fail, just warn and continue. -+ */ -+ timeout = jiffies + msecs_to_jiffies(100); -+ do { -+ u32 idle = gpu_read(gpu, VIVS_HI_IDLE_STATE); -+ -+ if ((idle & gpu->idle_mask) == gpu->idle_mask) -+ break; -+ -+ if (time_is_before_jiffies(timeout)) { -+ dev_warn(gpu->dev, -+ "timed out waiting for idle: idle=0x%x\n", -+ idle); -+ break; -+ } -+ -+ udelay(5); -+ } while (1); -+ } -+ -+ return etnaviv_gpu_clk_disable(gpu); -+} -+ -+#ifdef CONFIG_PM -+static int etnaviv_gpu_hw_resume(struct etnaviv_gpu *gpu) -+{ -+ u32 clock; -+ int ret; -+ -+ ret = mutex_lock_killable(&gpu->lock); -+ if (ret) -+ return ret; -+ -+ clock = VIVS_HI_CLOCK_CONTROL_DISABLE_DEBUG_REGISTERS | -+ VIVS_HI_CLOCK_CONTROL_FSCALE_VAL(0x40); -+ -+ etnaviv_gpu_load_clock(gpu, clock); -+ etnaviv_gpu_hw_init(gpu); -+ -+ gpu->switch_context = true; -+ -+ mutex_unlock(&gpu->lock); -+ -+ return 0; -+} -+#endif -+ -+static int etnaviv_gpu_bind(struct device *dev, struct device *master, -+ void *data) -+{ -+ struct drm_device *drm = data; -+ struct etnaviv_drm_private *priv = drm->dev_private; -+ struct etnaviv_gpu *gpu = dev_get_drvdata(dev); -+ int ret; -+ -+#ifdef CONFIG_PM -+ ret = pm_runtime_get_sync(gpu->dev); -+#else -+ ret = etnaviv_gpu_clk_enable(gpu); -+#endif -+ if (ret < 0) -+ return ret; -+ -+ gpu->drm = drm; -+ gpu->fence_context = fence_context_alloc(1); -+ spin_lock_init(&gpu->fence_spinlock); -+ -+ INIT_LIST_HEAD(&gpu->active_cmd_list); -+ INIT_WORK(&gpu->retire_work, retire_worker); -+ INIT_WORK(&gpu->recover_work, recover_worker); -+ init_waitqueue_head(&gpu->fence_event); -+ -+ setup_timer(&gpu->hangcheck_timer, hangcheck_handler, -+ (unsigned long)gpu); -+ -+ priv->gpu[priv->num_gpus++] = gpu; -+ -+ pm_runtime_mark_last_busy(gpu->dev); -+ pm_runtime_put_autosuspend(gpu->dev); -+ -+ return 0; -+} -+ -+static void etnaviv_gpu_unbind(struct device *dev, struct device *master, -+ void *data) -+{ -+ struct etnaviv_gpu *gpu = dev_get_drvdata(dev); -+ -+ DBG("%s", dev_name(gpu->dev)); -+ -+ hangcheck_disable(gpu); -+ -+#ifdef CONFIG_PM -+ pm_runtime_get_sync(gpu->dev); -+ pm_runtime_put_sync_suspend(gpu->dev); -+#else -+ etnaviv_gpu_hw_suspend(gpu); -+#endif -+ -+ if (gpu->buffer) { -+ etnaviv_gpu_cmdbuf_free(gpu->buffer); -+ gpu->buffer = NULL; -+ } -+ -+ if (gpu->mmu) { -+ etnaviv_iommu_destroy(gpu->mmu); -+ gpu->mmu = NULL; -+ } -+ -+ gpu->drm = NULL; -+} -+ -+static const struct component_ops gpu_ops = { -+ .bind = etnaviv_gpu_bind, -+ .unbind = etnaviv_gpu_unbind, -+}; -+ -+static const struct of_device_id etnaviv_gpu_match[] = { -+ { -+ .compatible = "vivante,gc" -+ }, -+ { /* sentinel */ } -+}; -+ -+static int etnaviv_gpu_platform_probe(struct platform_device *pdev) -+{ -+ struct device *dev = &pdev->dev; -+ struct etnaviv_gpu *gpu; -+ int err = 0; -+ -+ gpu = devm_kzalloc(dev, sizeof(*gpu), GFP_KERNEL); -+ if (!gpu) -+ return -ENOMEM; -+ -+ gpu->dev = &pdev->dev; -+ mutex_init(&gpu->lock); -+ -+ /* -+ * Set the GPU base address to the start of physical memory. This -+ * ensures that if we have up to 2GB, the v1 MMU can address the -+ * highest memory. This is important as command buffers may be -+ * allocated outside of this limit. -+ */ -+ gpu->memory_base = PHYS_OFFSET; -+ -+ /* Map registers: */ -+ gpu->mmio = etnaviv_ioremap(pdev, NULL, dev_name(gpu->dev)); -+ if (IS_ERR(gpu->mmio)) -+ return PTR_ERR(gpu->mmio); -+ -+ /* Get Interrupt: */ -+ gpu->irq = platform_get_irq(pdev, 0); -+ if (gpu->irq < 0) { -+ err = gpu->irq; -+ dev_err(dev, "failed to get irq: %d\n", err); -+ goto fail; -+ } -+ -+ err = devm_request_irq(&pdev->dev, gpu->irq, irq_handler, 0, -+ dev_name(gpu->dev), gpu); -+ if (err) { -+ dev_err(dev, "failed to request IRQ%u: %d\n", gpu->irq, err); -+ goto fail; -+ } -+ -+ /* Get Clocks: */ -+ gpu->clk_bus = devm_clk_get(&pdev->dev, "bus"); -+ DBG("clk_bus: %p", gpu->clk_bus); -+ if (IS_ERR(gpu->clk_bus)) -+ gpu->clk_bus = NULL; -+ -+ gpu->clk_core = devm_clk_get(&pdev->dev, "core"); -+ DBG("clk_core: %p", gpu->clk_core); -+ if (IS_ERR(gpu->clk_core)) -+ gpu->clk_core = NULL; -+ -+ gpu->clk_shader = devm_clk_get(&pdev->dev, "shader"); -+ DBG("clk_shader: %p", gpu->clk_shader); -+ if (IS_ERR(gpu->clk_shader)) -+ gpu->clk_shader = NULL; -+ -+ /* TODO: figure out max mapped size */ -+ dev_set_drvdata(dev, gpu); -+ -+ /* -+ * We treat the device as initially suspended. The runtime PM -+ * autosuspend delay is rather arbitary: no measurements have -+ * yet been performed to determine an appropriate value. -+ */ -+ pm_runtime_use_autosuspend(gpu->dev); -+ pm_runtime_set_autosuspend_delay(gpu->dev, 200); -+ pm_runtime_enable(gpu->dev); -+ -+ err = component_add(&pdev->dev, &gpu_ops); -+ if (err < 0) { -+ dev_err(&pdev->dev, "failed to register component: %d\n", err); -+ goto fail; -+ } -+ -+ return 0; -+ -+fail: -+ return err; -+} -+ -+static int etnaviv_gpu_platform_remove(struct platform_device *pdev) -+{ -+ component_del(&pdev->dev, &gpu_ops); -+ pm_runtime_disable(&pdev->dev); -+ return 0; -+} -+ -+#ifdef CONFIG_PM -+static int etnaviv_gpu_rpm_suspend(struct device *dev) -+{ -+ struct etnaviv_gpu *gpu = dev_get_drvdata(dev); -+ u32 idle, mask; -+ -+ /* If we have outstanding fences, we're not idle */ -+ if (gpu->completed_fence != gpu->active_fence) -+ return -EBUSY; -+ -+ /* Check whether the hardware (except FE) is idle */ -+ mask = gpu->idle_mask & ~VIVS_HI_IDLE_STATE_FE; -+ idle = gpu_read(gpu, VIVS_HI_IDLE_STATE) & mask; -+ if (idle != mask) -+ return -EBUSY; -+ -+ return etnaviv_gpu_hw_suspend(gpu); -+} -+ -+static int etnaviv_gpu_rpm_resume(struct device *dev) -+{ -+ struct etnaviv_gpu *gpu = dev_get_drvdata(dev); -+ int ret; -+ -+ ret = etnaviv_gpu_clk_enable(gpu); -+ if (ret) -+ return ret; -+ -+ /* Re-initialise the basic hardware state */ -+ if (gpu->drm && gpu->buffer) { -+ ret = etnaviv_gpu_hw_resume(gpu); -+ if (ret) { -+ etnaviv_gpu_clk_disable(gpu); -+ return ret; -+ } -+ } -+ -+ return 0; -+} -+#endif -+ -+static const struct dev_pm_ops etnaviv_gpu_pm_ops = { -+ SET_RUNTIME_PM_OPS(etnaviv_gpu_rpm_suspend, etnaviv_gpu_rpm_resume, -+ NULL) -+}; -+ -+struct platform_driver etnaviv_gpu_driver = { -+ .driver = { -+ .name = "etnaviv-gpu", -+ .owner = THIS_MODULE, -+ .pm = &etnaviv_gpu_pm_ops, -+ .of_match_table = etnaviv_gpu_match, -+ }, -+ .probe = etnaviv_gpu_platform_probe, -+ .remove = etnaviv_gpu_platform_remove, -+ .id_table = gpu_ids, -+}; -diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.h b/drivers/gpu/drm/etnaviv/etnaviv_gpu.h -new file mode 100644 -index 0000000..c75d503 ---- /dev/null -+++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.h -@@ -0,0 +1,209 @@ -+/* -+ * Copyright (C) 2015 Etnaviv Project -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License version 2 as published by -+ * the Free Software Foundation. -+ * -+ * 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 . -+ */ -+ -+#ifndef __ETNAVIV_GPU_H__ -+#define __ETNAVIV_GPU_H__ -+ -+#include -+#include -+ -+#include "etnaviv_drv.h" -+ -+struct etnaviv_gem_submit; -+ -+struct etnaviv_chip_identity { -+ /* Chip model. */ -+ u32 model; -+ -+ /* Revision value.*/ -+ u32 revision; -+ -+ /* Supported feature fields. */ -+ u32 features; -+ -+ /* Supported minor feature fields. */ -+ u32 minor_features0; -+ -+ /* Supported minor feature 1 fields. */ -+ u32 minor_features1; -+ -+ /* Supported minor feature 2 fields. */ -+ u32 minor_features2; -+ -+ /* Supported minor feature 3 fields. */ -+ u32 minor_features3; -+ -+ /* Number of streams supported. */ -+ u32 stream_count; -+ -+ /* Total number of temporary registers per thread. */ -+ u32 register_max; -+ -+ /* Maximum number of threads. */ -+ u32 thread_count; -+ -+ /* Number of shader cores. */ -+ u32 shader_core_count; -+ -+ /* Size of the vertex cache. */ -+ u32 vertex_cache_size; -+ -+ /* Number of entries in the vertex output buffer. */ -+ u32 vertex_output_buffer_size; -+ -+ /* Number of pixel pipes. */ -+ u32 pixel_pipes; -+ -+ /* Number of instructions. */ -+ u32 instruction_count; -+ -+ /* Number of constants. */ -+ u32 num_constants; -+ -+ /* Buffer size */ -+ u32 buffer_size; -+}; -+ -+struct etnaviv_event { -+ bool used; -+ struct fence *fence; -+}; -+ -+struct etnaviv_cmdbuf; -+ -+struct etnaviv_gpu { -+ struct drm_device *drm; -+ struct device *dev; -+ struct mutex lock; -+ struct etnaviv_chip_identity identity; -+ struct etnaviv_file_private *lastctx; -+ bool switch_context; -+ -+ /* 'ring'-buffer: */ -+ struct etnaviv_cmdbuf *buffer; -+ -+ /* bus base address of memory */ -+ u32 memory_base; -+ -+ /* event management: */ -+ struct etnaviv_event event[30]; -+ struct completion event_free; -+ spinlock_t event_spinlock; -+ -+ /* list of currently in-flight command buffers */ -+ struct list_head active_cmd_list; -+ -+ u32 idle_mask; -+ -+ /* Fencing support */ -+ u32 next_fence; -+ u32 active_fence; -+ u32 completed_fence; -+ u32 retired_fence; -+ wait_queue_head_t fence_event; -+ unsigned int fence_context; -+ spinlock_t fence_spinlock; -+ -+ /* worker for handling active-list retiring: */ -+ struct work_struct retire_work; -+ -+ void __iomem *mmio; -+ int irq; -+ -+ struct etnaviv_iommu *mmu; -+ -+ /* Power Control: */ -+ struct clk *clk_bus; -+ struct clk *clk_core; -+ struct clk *clk_shader; -+ -+ /* Hang Detction: */ -+#define DRM_ETNAVIV_HANGCHECK_PERIOD 500 /* in ms */ -+#define DRM_ETNAVIV_HANGCHECK_JIFFIES msecs_to_jiffies(DRM_ETNAVIV_HANGCHECK_PERIOD) -+ struct timer_list hangcheck_timer; -+ u32 hangcheck_fence; -+ u32 hangcheck_dma_addr; -+ struct work_struct recover_work; -+}; -+ -+struct etnaviv_cmdbuf { -+ /* device this cmdbuf is allocated for */ -+ struct etnaviv_gpu *gpu; -+ /* user context key, must be unique between all active users */ -+ struct etnaviv_file_private *ctx; -+ /* cmdbuf properties */ -+ void *vaddr; -+ dma_addr_t paddr; -+ u32 size; -+ u32 user_size; -+ /* fence after which this buffer is to be disposed */ -+ struct fence *fence; -+ /* target exec state */ -+ u32 exec_state; -+ /* per GPU in-flight list */ -+ struct list_head node; -+ /* BOs attached to this command buffer */ -+ unsigned int nr_bos; -+ struct etnaviv_gem_object *bo[0]; -+}; -+ -+static inline void gpu_write(struct etnaviv_gpu *gpu, u32 reg, u32 data) -+{ -+ etnaviv_writel(data, gpu->mmio + reg); -+} -+ -+static inline u32 gpu_read(struct etnaviv_gpu *gpu, u32 reg) -+{ -+ return etnaviv_readl(gpu->mmio + reg); -+} -+ -+static inline bool fence_completed(struct etnaviv_gpu *gpu, u32 fence) -+{ -+ return fence_after_eq(gpu->completed_fence, fence); -+} -+ -+static inline bool fence_retired(struct etnaviv_gpu *gpu, u32 fence) -+{ -+ return fence_after_eq(gpu->retired_fence, fence); -+} -+ -+int etnaviv_gpu_get_param(struct etnaviv_gpu *gpu, u32 param, u64 *value); -+ -+int etnaviv_gpu_init(struct etnaviv_gpu *gpu); -+ -+#ifdef CONFIG_DEBUG_FS -+int etnaviv_gpu_debugfs(struct etnaviv_gpu *gpu, struct seq_file *m); -+#endif -+ -+int etnaviv_gpu_fence_sync_obj(struct etnaviv_gem_object *etnaviv_obj, -+ unsigned int context, bool exclusive); -+ -+void etnaviv_gpu_retire(struct etnaviv_gpu *gpu); -+int etnaviv_gpu_wait_fence_interruptible(struct etnaviv_gpu *gpu, -+ u32 fence, struct timespec *timeout); -+int etnaviv_gpu_wait_obj_inactive(struct etnaviv_gpu *gpu, -+ struct etnaviv_gem_object *etnaviv_obj, struct timespec *timeout); -+int etnaviv_gpu_submit(struct etnaviv_gpu *gpu, -+ struct etnaviv_gem_submit *submit, struct etnaviv_cmdbuf *cmdbuf); -+struct etnaviv_cmdbuf *etnaviv_gpu_cmdbuf_new(struct etnaviv_gpu *gpu, -+ u32 size, size_t nr_bos); -+void etnaviv_gpu_cmdbuf_free(struct etnaviv_cmdbuf *cmdbuf); -+int etnaviv_gpu_pm_get_sync(struct etnaviv_gpu *gpu); -+void etnaviv_gpu_pm_put(struct etnaviv_gpu *gpu); -+ -+extern struct platform_driver etnaviv_gpu_driver; -+ -+#endif /* __ETNAVIV_GPU_H__ */ -diff --git a/drivers/gpu/drm/etnaviv/etnaviv_iommu.c b/drivers/gpu/drm/etnaviv/etnaviv_iommu.c -new file mode 100644 -index 0000000..522cfd4 ---- /dev/null -+++ b/drivers/gpu/drm/etnaviv/etnaviv_iommu.c -@@ -0,0 +1,240 @@ -+/* -+ * Copyright (C) 2014 Christian Gmeiner -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License version 2 as published by -+ * the Free Software Foundation. -+ * -+ * 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 . -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "etnaviv_gpu.h" -+#include "etnaviv_mmu.h" -+#include "etnaviv_iommu.h" -+#include "state_hi.xml.h" -+ -+#define PT_SIZE SZ_2M -+#define PT_ENTRIES (PT_SIZE / sizeof(u32)) -+ -+#define GPU_MEM_START 0x80000000 -+ -+struct etnaviv_iommu_domain_pgtable { -+ u32 *pgtable; -+ dma_addr_t paddr; -+}; -+ -+struct etnaviv_iommu_domain { -+ struct iommu_domain domain; -+ struct device *dev; -+ void *bad_page_cpu; -+ dma_addr_t bad_page_dma; -+ struct etnaviv_iommu_domain_pgtable pgtable; -+ spinlock_t map_lock; -+}; -+ -+static struct etnaviv_iommu_domain *to_etnaviv_domain(struct iommu_domain *domain) -+{ -+ return container_of(domain, struct etnaviv_iommu_domain, domain); -+} -+ -+static int pgtable_alloc(struct etnaviv_iommu_domain_pgtable *pgtable, -+ size_t size) -+{ -+ pgtable->pgtable = dma_alloc_coherent(NULL, size, &pgtable->paddr, GFP_KERNEL); -+ if (!pgtable->pgtable) -+ return -ENOMEM; -+ -+ return 0; -+} -+ -+static void pgtable_free(struct etnaviv_iommu_domain_pgtable *pgtable, -+ size_t size) -+{ -+ dma_free_coherent(NULL, size, pgtable->pgtable, pgtable->paddr); -+} -+ -+static u32 pgtable_read(struct etnaviv_iommu_domain_pgtable *pgtable, -+ unsigned long iova) -+{ -+ /* calcuate index into page table */ -+ unsigned int index = (iova - GPU_MEM_START) / SZ_4K; -+ phys_addr_t paddr; -+ -+ paddr = pgtable->pgtable[index]; -+ -+ return paddr; -+} -+ -+static void pgtable_write(struct etnaviv_iommu_domain_pgtable *pgtable, -+ unsigned long iova, phys_addr_t paddr) -+{ -+ /* calcuate index into page table */ -+ unsigned int index = (iova - GPU_MEM_START) / SZ_4K; -+ -+ pgtable->pgtable[index] = paddr; -+} -+ -+static int __etnaviv_iommu_init(struct etnaviv_iommu_domain *etnaviv_domain) -+{ -+ u32 *p; -+ int ret, i; -+ -+ etnaviv_domain->bad_page_cpu = dma_alloc_coherent(etnaviv_domain->dev, -+ SZ_4K, -+ &etnaviv_domain->bad_page_dma, -+ GFP_KERNEL); -+ if (!etnaviv_domain->bad_page_cpu) -+ return -ENOMEM; -+ -+ p = etnaviv_domain->bad_page_cpu; -+ for (i = 0; i < SZ_4K / 4; i++) -+ *p++ = 0xdead55aa; -+ -+ ret = pgtable_alloc(&etnaviv_domain->pgtable, PT_SIZE); -+ if (ret < 0) { -+ dma_free_coherent(etnaviv_domain->dev, SZ_4K, -+ etnaviv_domain->bad_page_cpu, -+ etnaviv_domain->bad_page_dma); -+ return ret; -+ } -+ -+ for (i = 0; i < PT_ENTRIES; i++) -+ etnaviv_domain->pgtable.pgtable[i] = -+ etnaviv_domain->bad_page_dma; -+ -+ spin_lock_init(&etnaviv_domain->map_lock); -+ -+ return 0; -+} -+ -+static void etnaviv_domain_free(struct iommu_domain *domain) -+{ -+ struct etnaviv_iommu_domain *etnaviv_domain = to_etnaviv_domain(domain); -+ -+ pgtable_free(&etnaviv_domain->pgtable, PT_SIZE); -+ -+ dma_free_coherent(etnaviv_domain->dev, SZ_4K, -+ etnaviv_domain->bad_page_cpu, -+ etnaviv_domain->bad_page_dma); -+ -+ kfree(etnaviv_domain); -+} -+ -+static int etnaviv_iommuv1_map(struct iommu_domain *domain, unsigned long iova, -+ phys_addr_t paddr, size_t size, int prot) -+{ -+ struct etnaviv_iommu_domain *etnaviv_domain = to_etnaviv_domain(domain); -+ -+ if (size != SZ_4K) -+ return -EINVAL; -+ -+ spin_lock(&etnaviv_domain->map_lock); -+ pgtable_write(&etnaviv_domain->pgtable, iova, paddr); -+ spin_unlock(&etnaviv_domain->map_lock); -+ -+ return 0; -+} -+ -+static size_t etnaviv_iommuv1_unmap(struct iommu_domain *domain, -+ unsigned long iova, size_t size) -+{ -+ struct etnaviv_iommu_domain *etnaviv_domain = to_etnaviv_domain(domain); -+ -+ if (size != SZ_4K) -+ return -EINVAL; -+ -+ spin_lock(&etnaviv_domain->map_lock); -+ pgtable_write(&etnaviv_domain->pgtable, iova, -+ etnaviv_domain->bad_page_dma); -+ spin_unlock(&etnaviv_domain->map_lock); -+ -+ return SZ_4K; -+} -+ -+static phys_addr_t etnaviv_iommu_iova_to_phys(struct iommu_domain *domain, -+ dma_addr_t iova) -+{ -+ struct etnaviv_iommu_domain *etnaviv_domain = to_etnaviv_domain(domain); -+ -+ return pgtable_read(&etnaviv_domain->pgtable, iova); -+} -+ -+static size_t etnaviv_iommuv1_dump_size(struct iommu_domain *domain) -+{ -+ return PT_SIZE; -+} -+ -+static void etnaviv_iommuv1_dump(struct iommu_domain *domain, void *buf) -+{ -+ struct etnaviv_iommu_domain *etnaviv_domain = to_etnaviv_domain(domain); -+ -+ memcpy(buf, etnaviv_domain->pgtable.pgtable, PT_SIZE); -+} -+ -+static struct etnaviv_iommu_ops etnaviv_iommu_ops = { -+ .ops = { -+ .domain_free = etnaviv_domain_free, -+ .map = etnaviv_iommuv1_map, -+ .unmap = etnaviv_iommuv1_unmap, -+ .iova_to_phys = etnaviv_iommu_iova_to_phys, -+ .pgsize_bitmap = SZ_4K, -+ }, -+ .dump_size = etnaviv_iommuv1_dump_size, -+ .dump = etnaviv_iommuv1_dump, -+}; -+ -+void etnaviv_iommu_domain_restore(struct etnaviv_gpu *gpu, -+ struct iommu_domain *domain) -+{ -+ struct etnaviv_iommu_domain *etnaviv_domain = to_etnaviv_domain(domain); -+ u32 pgtable; -+ -+ /* set page table address in MC */ -+ pgtable = (u32)etnaviv_domain->pgtable.paddr; -+ -+ gpu_write(gpu, VIVS_MC_MMU_FE_PAGE_TABLE, pgtable); -+ gpu_write(gpu, VIVS_MC_MMU_TX_PAGE_TABLE, pgtable); -+ gpu_write(gpu, VIVS_MC_MMU_PE_PAGE_TABLE, pgtable); -+ gpu_write(gpu, VIVS_MC_MMU_PEZ_PAGE_TABLE, pgtable); -+ gpu_write(gpu, VIVS_MC_MMU_RA_PAGE_TABLE, pgtable); -+} -+ -+struct iommu_domain *etnaviv_iommu_domain_alloc(struct etnaviv_gpu *gpu) -+{ -+ struct etnaviv_iommu_domain *etnaviv_domain; -+ int ret; -+ -+ etnaviv_domain = kzalloc(sizeof(*etnaviv_domain), GFP_KERNEL); -+ if (!etnaviv_domain) -+ return NULL; -+ -+ etnaviv_domain->dev = gpu->dev; -+ -+ etnaviv_domain->domain.type = __IOMMU_DOMAIN_PAGING; -+ etnaviv_domain->domain.ops = &etnaviv_iommu_ops.ops; -+ etnaviv_domain->domain.geometry.aperture_start = GPU_MEM_START; -+ etnaviv_domain->domain.geometry.aperture_end = GPU_MEM_START + PT_ENTRIES * SZ_4K - 1; -+ -+ ret = __etnaviv_iommu_init(etnaviv_domain); -+ if (ret) -+ goto out_free; -+ -+ return &etnaviv_domain->domain; -+ -+out_free: -+ kfree(etnaviv_domain); -+ return NULL; -+} -diff --git a/drivers/gpu/drm/etnaviv/etnaviv_iommu.h b/drivers/gpu/drm/etnaviv/etnaviv_iommu.h -new file mode 100644 -index 0000000..cf45503 ---- /dev/null -+++ b/drivers/gpu/drm/etnaviv/etnaviv_iommu.h -@@ -0,0 +1,28 @@ -+/* -+ * Copyright (C) 2014 Christian Gmeiner -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License version 2 as published by -+ * the Free Software Foundation. -+ * -+ * 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 . -+ */ -+ -+#ifndef __ETNAVIV_IOMMU_H__ -+#define __ETNAVIV_IOMMU_H__ -+ -+#include -+struct etnaviv_gpu; -+ -+struct iommu_domain *etnaviv_iommu_domain_alloc(struct etnaviv_gpu *gpu); -+void etnaviv_iommu_domain_restore(struct etnaviv_gpu *gpu, -+ struct iommu_domain *domain); -+struct iommu_domain *etnaviv_iommu_v2_domain_alloc(struct etnaviv_gpu *gpu); -+ -+#endif /* __ETNAVIV_IOMMU_H__ */ -diff --git a/drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c b/drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c -new file mode 100644 -index 0000000..fbb4aed ---- /dev/null -+++ b/drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c -@@ -0,0 +1,33 @@ -+/* -+ * Copyright (C) 2014 Christian Gmeiner -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License version 2 as published by -+ * the Free Software Foundation. -+ * -+ * 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 . -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "etnaviv_gpu.h" -+#include "etnaviv_iommu.h" -+#include "state_hi.xml.h" -+ -+ -+struct iommu_domain *etnaviv_iommu_v2_domain_alloc(struct etnaviv_gpu *gpu) -+{ -+ /* TODO */ -+ return NULL; -+} -diff --git a/drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.h b/drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.h -new file mode 100644 -index 0000000..603ea41 ---- /dev/null -+++ b/drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.h -@@ -0,0 +1,25 @@ -+/* -+ * Copyright (C) 2014 Christian Gmeiner -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License version 2 as published by -+ * the Free Software Foundation. -+ * -+ * 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 . -+ */ -+ -+#ifndef __ETNAVIV_IOMMU_V2_H__ -+#define __ETNAVIV_IOMMU_V2_H__ -+ -+#include -+struct etnaviv_gpu; -+ -+struct iommu_domain *etnaviv_iommu_v2_domain_alloc(struct etnaviv_gpu *gpu); -+ -+#endif /* __ETNAVIV_IOMMU_V2_H__ */ -diff --git a/drivers/gpu/drm/etnaviv/etnaviv_mmu.c b/drivers/gpu/drm/etnaviv/etnaviv_mmu.c -new file mode 100644 -index 0000000..6743bc6 ---- /dev/null -+++ b/drivers/gpu/drm/etnaviv/etnaviv_mmu.c -@@ -0,0 +1,299 @@ -+/* -+ * Copyright (C) 2015 Etnaviv Project -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License version 2 as published by -+ * the Free Software Foundation. -+ * -+ * 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 . -+ */ -+ -+#include "etnaviv_drv.h" -+#include "etnaviv_gem.h" -+#include "etnaviv_gpu.h" -+#include "etnaviv_mmu.h" -+ -+static int etnaviv_fault_handler(struct iommu_domain *iommu, struct device *dev, -+ unsigned long iova, int flags, void *arg) -+{ -+ DBG("*** fault: iova=%08lx, flags=%d", iova, flags); -+ return 0; -+} -+ -+int etnaviv_iommu_map(struct etnaviv_iommu *iommu, u32 iova, -+ struct sg_table *sgt, unsigned len, int prot) -+{ -+ struct iommu_domain *domain = iommu->domain; -+ struct scatterlist *sg; -+ unsigned int da = iova; -+ unsigned int i, j; -+ int ret; -+ -+ if (!domain || !sgt) -+ return -EINVAL; -+ -+ for_each_sg(sgt->sgl, sg, sgt->nents, i) { -+ u32 pa = sg_dma_address(sg) - sg->offset; -+ size_t bytes = sg_dma_len(sg) + sg->offset; -+ -+ VERB("map[%d]: %08x %08x(%zx)", i, iova, pa, bytes); -+ -+ ret = iommu_map(domain, da, pa, bytes, prot); -+ if (ret) -+ goto fail; -+ -+ da += bytes; -+ } -+ -+ return 0; -+ -+fail: -+ da = iova; -+ -+ for_each_sg(sgt->sgl, sg, i, j) { -+ size_t bytes = sg_dma_len(sg) + sg->offset; -+ -+ iommu_unmap(domain, da, bytes); -+ da += bytes; -+ } -+ return ret; -+} -+ -+int etnaviv_iommu_unmap(struct etnaviv_iommu *iommu, u32 iova, -+ struct sg_table *sgt, unsigned len) -+{ -+ struct iommu_domain *domain = iommu->domain; -+ struct scatterlist *sg; -+ unsigned int da = iova; -+ int i; -+ -+ for_each_sg(sgt->sgl, sg, sgt->nents, i) { -+ size_t bytes = sg_dma_len(sg) + sg->offset; -+ size_t unmapped; -+ -+ unmapped = iommu_unmap(domain, da, bytes); -+ if (unmapped < bytes) -+ return unmapped; -+ -+ VERB("unmap[%d]: %08x(%zx)", i, iova, bytes); -+ -+ BUG_ON(!PAGE_ALIGNED(bytes)); -+ -+ da += bytes; -+ } -+ -+ return 0; -+} -+ -+static void etnaviv_iommu_remove_mapping(struct etnaviv_iommu *mmu, -+ struct etnaviv_vram_mapping *mapping) -+{ -+ struct etnaviv_gem_object *etnaviv_obj = mapping->object; -+ -+ etnaviv_iommu_unmap(mmu, mapping->vram_node.start, -+ etnaviv_obj->sgt, etnaviv_obj->base.size); -+ drm_mm_remove_node(&mapping->vram_node); -+} -+ -+int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu, -+ struct etnaviv_gem_object *etnaviv_obj, u32 memory_base, -+ struct etnaviv_vram_mapping *mapping) -+{ -+ struct etnaviv_vram_mapping *free = NULL; -+ struct sg_table *sgt = etnaviv_obj->sgt; -+ struct drm_mm_node *node; -+ int ret; -+ -+ lockdep_assert_held(&etnaviv_obj->lock); -+ -+ mutex_lock(&mmu->lock); -+ -+ /* v1 MMU can optimize single entry (contiguous) scatterlists */ -+ if (sgt->nents == 1 && !(etnaviv_obj->flags & ETNA_BO_FORCE_MMU)) { -+ u32 iova; -+ -+ iova = sg_dma_address(sgt->sgl) - memory_base; -+ if (iova < 0x80000000 - sg_dma_len(sgt->sgl)) { -+ mapping->iova = iova; -+ list_add_tail(&mapping->mmu_node, &mmu->mappings); -+ mutex_unlock(&mmu->lock); -+ return 0; -+ } -+ } -+ -+ node = &mapping->vram_node; -+ while (1) { -+ struct etnaviv_vram_mapping *m, *n; -+ struct list_head list; -+ bool found; -+ -+ ret = drm_mm_insert_node_in_range(&mmu->mm, node, -+ etnaviv_obj->base.size, 0, mmu->last_iova, ~0UL, -+ DRM_MM_SEARCH_DEFAULT); -+ -+ if (ret != -ENOSPC) -+ break; -+ -+ /* -+ * If we did not search from the start of the MMU region, -+ * try again in case there are free slots. -+ */ -+ if (mmu->last_iova) { -+ mmu->last_iova = 0; -+ mmu->need_flush = true; -+ continue; -+ } -+ -+ /* Try to retire some entries */ -+ drm_mm_init_scan(&mmu->mm, etnaviv_obj->base.size, 0, 0); -+ -+ found = 0; -+ INIT_LIST_HEAD(&list); -+ list_for_each_entry(free, &mmu->mappings, mmu_node) { -+ /* If this vram node has not been used, skip this. */ -+ if (!free->vram_node.mm) -+ continue; -+ -+ /* -+ * If the iova is pinned, then it's in-use, -+ * so we must keep its mapping. -+ */ -+ if (free->use) -+ continue; -+ -+ list_add(&free->scan_node, &list); -+ if (drm_mm_scan_add_block(&free->vram_node)) { -+ found = true; -+ break; -+ } -+ } -+ -+ if (!found) { -+ /* Nothing found, clean up and fail */ -+ list_for_each_entry_safe(m, n, &list, scan_node) -+ BUG_ON(drm_mm_scan_remove_block(&m->vram_node)); -+ break; -+ } -+ -+ /* -+ * drm_mm does not allow any other operations while -+ * scanning, so we have to remove all blocks first. -+ * If drm_mm_scan_remove_block() returns false, we -+ * can leave the block pinned. -+ */ -+ list_for_each_entry_safe(m, n, &list, scan_node) -+ if (!drm_mm_scan_remove_block(&m->vram_node)) -+ list_del_init(&m->scan_node); -+ -+ /* -+ * Unmap the blocks which need to be reaped from the MMU. -+ * Clear the mmu pointer to prevent the get_iova finding -+ * this mapping. -+ */ -+ list_for_each_entry_safe(m, n, &list, scan_node) { -+ etnaviv_iommu_remove_mapping(mmu, m); -+ m->mmu = NULL; -+ list_del_init(&m->mmu_node); -+ list_del_init(&m->scan_node); -+ } -+ -+ /* -+ * We removed enough mappings so that the new allocation will -+ * succeed. Ensure that the MMU will be flushed before the -+ * associated commit requesting this mapping, and retry the -+ * allocation one more time. -+ */ -+ mmu->need_flush = true; -+ } -+ -+ if (ret < 0) { -+ mutex_unlock(&mmu->lock); -+ return ret; -+ } -+ -+ mmu->last_iova = node->start + etnaviv_obj->base.size; -+ mapping->iova = node->start; -+ ret = etnaviv_iommu_map(mmu, node->start, sgt, etnaviv_obj->base.size, -+ IOMMU_READ | IOMMU_WRITE); -+ -+ if (ret < 0) { -+ drm_mm_remove_node(node); -+ mutex_unlock(&mmu->lock); -+ return ret; -+ } -+ -+ list_add_tail(&mapping->mmu_node, &mmu->mappings); -+ mutex_unlock(&mmu->lock); -+ -+ return ret; -+} -+ -+void etnaviv_iommu_unmap_gem(struct etnaviv_iommu *mmu, -+ struct etnaviv_vram_mapping *mapping) -+{ -+ WARN_ON(mapping->use); -+ -+ mutex_lock(&mmu->lock); -+ -+ /* If the vram node is on the mm, unmap and remove the node */ -+ if (mapping->vram_node.mm == &mmu->mm) -+ etnaviv_iommu_remove_mapping(mmu, mapping); -+ -+ list_del(&mapping->mmu_node); -+ mutex_unlock(&mmu->lock); -+} -+ -+void etnaviv_iommu_destroy(struct etnaviv_iommu *mmu) -+{ -+ drm_mm_takedown(&mmu->mm); -+ iommu_domain_free(mmu->domain); -+ kfree(mmu); -+} -+ -+struct etnaviv_iommu *etnaviv_iommu_new(struct etnaviv_gpu *gpu, -+ struct iommu_domain *domain, enum etnaviv_iommu_version version) -+{ -+ struct etnaviv_iommu *mmu; -+ -+ mmu = kzalloc(sizeof(*mmu), GFP_KERNEL); -+ if (!mmu) -+ return ERR_PTR(-ENOMEM); -+ -+ mmu->domain = domain; -+ mmu->gpu = gpu; -+ mmu->version = version; -+ mutex_init(&mmu->lock); -+ INIT_LIST_HEAD(&mmu->mappings); -+ -+ drm_mm_init(&mmu->mm, domain->geometry.aperture_start, -+ domain->geometry.aperture_end - -+ domain->geometry.aperture_start + 1); -+ -+ iommu_set_fault_handler(domain, etnaviv_fault_handler, gpu->dev); -+ -+ return mmu; -+} -+ -+size_t etnaviv_iommu_dump_size(struct etnaviv_iommu *iommu) -+{ -+ struct etnaviv_iommu_ops *ops; -+ -+ ops = container_of(iommu->domain->ops, struct etnaviv_iommu_ops, ops); -+ -+ return ops->dump_size(iommu->domain); -+} -+ -+void etnaviv_iommu_dump(struct etnaviv_iommu *iommu, void *buf) -+{ -+ struct etnaviv_iommu_ops *ops; -+ -+ ops = container_of(iommu->domain->ops, struct etnaviv_iommu_ops, ops); -+ -+ ops->dump(iommu->domain, buf); -+} -diff --git a/drivers/gpu/drm/etnaviv/etnaviv_mmu.h b/drivers/gpu/drm/etnaviv/etnaviv_mmu.h -new file mode 100644 -index 0000000..fff215a ---- /dev/null -+++ b/drivers/gpu/drm/etnaviv/etnaviv_mmu.h -@@ -0,0 +1,71 @@ -+/* -+ * Copyright (C) 2015 Etnaviv Project -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License version 2 as published by -+ * the Free Software Foundation. -+ * -+ * 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 . -+ */ -+ -+#ifndef __ETNAVIV_MMU_H__ -+#define __ETNAVIV_MMU_H__ -+ -+#include -+ -+enum etnaviv_iommu_version { -+ ETNAVIV_IOMMU_V1 = 0, -+ ETNAVIV_IOMMU_V2, -+}; -+ -+struct etnaviv_gpu; -+struct etnaviv_vram_mapping; -+ -+struct etnaviv_iommu_ops { -+ struct iommu_ops ops; -+ size_t (*dump_size)(struct iommu_domain *); -+ void (*dump)(struct iommu_domain *, void *); -+}; -+ -+struct etnaviv_iommu { -+ struct etnaviv_gpu *gpu; -+ struct iommu_domain *domain; -+ -+ enum etnaviv_iommu_version version; -+ -+ /* memory manager for GPU address area */ -+ struct mutex lock; -+ struct list_head mappings; -+ struct drm_mm mm; -+ u32 last_iova; -+ bool need_flush; -+}; -+ -+struct etnaviv_gem_object; -+ -+int etnaviv_iommu_attach(struct etnaviv_iommu *iommu, const char **names, -+ int cnt); -+int etnaviv_iommu_map(struct etnaviv_iommu *iommu, u32 iova, -+ struct sg_table *sgt, unsigned len, int prot); -+int etnaviv_iommu_unmap(struct etnaviv_iommu *iommu, u32 iova, -+ struct sg_table *sgt, unsigned len); -+int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu, -+ struct etnaviv_gem_object *etnaviv_obj, u32 memory_base, -+ struct etnaviv_vram_mapping *mapping); -+void etnaviv_iommu_unmap_gem(struct etnaviv_iommu *mmu, -+ struct etnaviv_vram_mapping *mapping); -+void etnaviv_iommu_destroy(struct etnaviv_iommu *iommu); -+ -+size_t etnaviv_iommu_dump_size(struct etnaviv_iommu *iommu); -+void etnaviv_iommu_dump(struct etnaviv_iommu *iommu, void *buf); -+ -+struct etnaviv_iommu *etnaviv_iommu_new(struct etnaviv_gpu *gpu, -+ struct iommu_domain *domain, enum etnaviv_iommu_version version); -+ -+#endif /* __ETNAVIV_MMU_H__ */ -diff --git a/drivers/gpu/drm/etnaviv/state.xml.h b/drivers/gpu/drm/etnaviv/state.xml.h -new file mode 100644 -index 0000000..3682183 ---- /dev/null -+++ b/drivers/gpu/drm/etnaviv/state.xml.h -@@ -0,0 +1,351 @@ -+#ifndef STATE_XML -+#define STATE_XML -+ -+/* Autogenerated file, DO NOT EDIT manually! -+ -+This file was generated by the rules-ng-ng headergen tool in this git repository: -+http://0x04.net/cgit/index.cgi/rules-ng-ng -+git clone git://0x04.net/rules-ng-ng -+ -+The rules-ng-ng source files this header was generated from are: -+- state.xml ( 18882 bytes, from 2015-03-25 11:42:32) -+- common.xml ( 18437 bytes, from 2015-03-25 11:27:41) -+- state_hi.xml ( 23420 bytes, from 2015-03-25 11:47:21) -+- state_2d.xml ( 51549 bytes, from 2015-03-25 11:25:06) -+- state_3d.xml ( 54600 bytes, from 2015-03-25 11:25:19) -+- state_vg.xml ( 5973 bytes, from 2015-03-25 11:26:01) -+ -+Copyright (C) 2015 -+*/ -+ -+ -+#define VARYING_COMPONENT_USE_UNUSED 0x00000000 -+#define VARYING_COMPONENT_USE_USED 0x00000001 -+#define VARYING_COMPONENT_USE_POINTCOORD_X 0x00000002 -+#define VARYING_COMPONENT_USE_POINTCOORD_Y 0x00000003 -+#define FE_VERTEX_STREAM_CONTROL_VERTEX_STRIDE__MASK 0x000000ff -+#define FE_VERTEX_STREAM_CONTROL_VERTEX_STRIDE__SHIFT 0 -+#define FE_VERTEX_STREAM_CONTROL_VERTEX_STRIDE(x) (((x) << FE_VERTEX_STREAM_CONTROL_VERTEX_STRIDE__SHIFT) & FE_VERTEX_STREAM_CONTROL_VERTEX_STRIDE__MASK) -+#define VIVS_FE 0x00000000 -+ -+#define VIVS_FE_VERTEX_ELEMENT_CONFIG(i0) (0x00000600 + 0x4*(i0)) -+#define VIVS_FE_VERTEX_ELEMENT_CONFIG__ESIZE 0x00000004 -+#define VIVS_FE_VERTEX_ELEMENT_CONFIG__LEN 0x00000010 -+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_TYPE__MASK 0x0000000f -+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_TYPE__SHIFT 0 -+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_TYPE_BYTE 0x00000000 -+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_TYPE_UNSIGNED_BYTE 0x00000001 -+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_TYPE_SHORT 0x00000002 -+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_TYPE_UNSIGNED_SHORT 0x00000003 -+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_TYPE_INT 0x00000004 -+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_TYPE_UNSIGNED_INT 0x00000005 -+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_TYPE_FLOAT 0x00000008 -+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_TYPE_HALF_FLOAT 0x00000009 -+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_TYPE_FIXED 0x0000000b -+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_TYPE_INT_10_10_10_2 0x0000000c -+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_TYPE_UNSIGNED_INT_10_10_10_2 0x0000000d -+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_ENDIAN__MASK 0x00000030 -+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_ENDIAN__SHIFT 4 -+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_ENDIAN(x) (((x) << VIVS_FE_VERTEX_ELEMENT_CONFIG_ENDIAN__SHIFT) & VIVS_FE_VERTEX_ELEMENT_CONFIG_ENDIAN__MASK) -+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_NONCONSECUTIVE 0x00000080 -+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_STREAM__MASK 0x00000700 -+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_STREAM__SHIFT 8 -+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_STREAM(x) (((x) << VIVS_FE_VERTEX_ELEMENT_CONFIG_STREAM__SHIFT) & VIVS_FE_VERTEX_ELEMENT_CONFIG_STREAM__MASK) -+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_NUM__MASK 0x00003000 -+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_NUM__SHIFT 12 -+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_NUM(x) (((x) << VIVS_FE_VERTEX_ELEMENT_CONFIG_NUM__SHIFT) & VIVS_FE_VERTEX_ELEMENT_CONFIG_NUM__MASK) -+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_NORMALIZE__MASK 0x0000c000 -+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_NORMALIZE__SHIFT 14 -+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_NORMALIZE_OFF 0x00000000 -+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_NORMALIZE_ON 0x00008000 -+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_START__MASK 0x00ff0000 -+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_START__SHIFT 16 -+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_START(x) (((x) << VIVS_FE_VERTEX_ELEMENT_CONFIG_START__SHIFT) & VIVS_FE_VERTEX_ELEMENT_CONFIG_START__MASK) -+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_END__MASK 0xff000000 -+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_END__SHIFT 24 -+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_END(x) (((x) << VIVS_FE_VERTEX_ELEMENT_CONFIG_END__SHIFT) & VIVS_FE_VERTEX_ELEMENT_CONFIG_END__MASK) -+ -+#define VIVS_FE_CMD_STREAM_BASE_ADDR 0x00000640 -+ -+#define VIVS_FE_INDEX_STREAM_BASE_ADDR 0x00000644 -+ -+#define VIVS_FE_INDEX_STREAM_CONTROL 0x00000648 -+#define VIVS_FE_INDEX_STREAM_CONTROL_TYPE__MASK 0x00000003 -+#define VIVS_FE_INDEX_STREAM_CONTROL_TYPE__SHIFT 0 -+#define VIVS_FE_INDEX_STREAM_CONTROL_TYPE_UNSIGNED_CHAR 0x00000000 -+#define VIVS_FE_INDEX_STREAM_CONTROL_TYPE_UNSIGNED_SHORT 0x00000001 -+#define VIVS_FE_INDEX_STREAM_CONTROL_TYPE_UNSIGNED_INT 0x00000002 -+ -+#define VIVS_FE_VERTEX_STREAM_BASE_ADDR 0x0000064c -+ -+#define VIVS_FE_VERTEX_STREAM_CONTROL 0x00000650 -+ -+#define VIVS_FE_COMMAND_ADDRESS 0x00000654 -+ -+#define VIVS_FE_COMMAND_CONTROL 0x00000658 -+#define VIVS_FE_COMMAND_CONTROL_PREFETCH__MASK 0x0000ffff -+#define VIVS_FE_COMMAND_CONTROL_PREFETCH__SHIFT 0 -+#define VIVS_FE_COMMAND_CONTROL_PREFETCH(x) (((x) << VIVS_FE_COMMAND_CONTROL_PREFETCH__SHIFT) & VIVS_FE_COMMAND_CONTROL_PREFETCH__MASK) -+#define VIVS_FE_COMMAND_CONTROL_ENABLE 0x00010000 -+ -+#define VIVS_FE_DMA_STATUS 0x0000065c -+ -+#define VIVS_FE_DMA_DEBUG_STATE 0x00000660 -+#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE__MASK 0x0000001f -+#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE__SHIFT 0 -+#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_IDLE 0x00000000 -+#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_DEC 0x00000001 -+#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_ADR0 0x00000002 -+#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_LOAD0 0x00000003 -+#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_ADR1 0x00000004 -+#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_LOAD1 0x00000005 -+#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_3DADR 0x00000006 -+#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_3DCMD 0x00000007 -+#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_3DCNTL 0x00000008 -+#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_3DIDXCNTL 0x00000009 -+#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_INITREQDMA 0x0000000a -+#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_DRAWIDX 0x0000000b -+#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_DRAW 0x0000000c -+#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_2DRECT0 0x0000000d -+#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_2DRECT1 0x0000000e -+#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_2DDATA0 0x0000000f -+#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_2DDATA1 0x00000010 -+#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_WAITFIFO 0x00000011 -+#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_WAIT 0x00000012 -+#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_LINK 0x00000013 -+#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_END 0x00000014 -+#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_STALL 0x00000015 -+#define VIVS_FE_DMA_DEBUG_STATE_CMD_DMA_STATE__MASK 0x00000300 -+#define VIVS_FE_DMA_DEBUG_STATE_CMD_DMA_STATE__SHIFT 8 -+#define VIVS_FE_DMA_DEBUG_STATE_CMD_DMA_STATE_IDLE 0x00000000 -+#define VIVS_FE_DMA_DEBUG_STATE_CMD_DMA_STATE_START 0x00000100 -+#define VIVS_FE_DMA_DEBUG_STATE_CMD_DMA_STATE_REQ 0x00000200 -+#define VIVS_FE_DMA_DEBUG_STATE_CMD_DMA_STATE_END 0x00000300 -+#define VIVS_FE_DMA_DEBUG_STATE_CMD_FETCH_STATE__MASK 0x00000c00 -+#define VIVS_FE_DMA_DEBUG_STATE_CMD_FETCH_STATE__SHIFT 10 -+#define VIVS_FE_DMA_DEBUG_STATE_CMD_FETCH_STATE_IDLE 0x00000000 -+#define VIVS_FE_DMA_DEBUG_STATE_CMD_FETCH_STATE_RAMVALID 0x00000400 -+#define VIVS_FE_DMA_DEBUG_STATE_CMD_FETCH_STATE_VALID 0x00000800 -+#define VIVS_FE_DMA_DEBUG_STATE_REQ_DMA_STATE__MASK 0x00003000 -+#define VIVS_FE_DMA_DEBUG_STATE_REQ_DMA_STATE__SHIFT 12 -+#define VIVS_FE_DMA_DEBUG_STATE_REQ_DMA_STATE_IDLE 0x00000000 -+#define VIVS_FE_DMA_DEBUG_STATE_REQ_DMA_STATE_WAITIDX 0x00001000 -+#define VIVS_FE_DMA_DEBUG_STATE_REQ_DMA_STATE_CAL 0x00002000 -+#define VIVS_FE_DMA_DEBUG_STATE_CAL_STATE__MASK 0x0000c000 -+#define VIVS_FE_DMA_DEBUG_STATE_CAL_STATE__SHIFT 14 -+#define VIVS_FE_DMA_DEBUG_STATE_CAL_STATE_IDLE 0x00000000 -+#define VIVS_FE_DMA_DEBUG_STATE_CAL_STATE_LDADR 0x00004000 -+#define VIVS_FE_DMA_DEBUG_STATE_CAL_STATE_IDXCALC 0x00008000 -+#define VIVS_FE_DMA_DEBUG_STATE_VE_REQ_STATE__MASK 0x00030000 -+#define VIVS_FE_DMA_DEBUG_STATE_VE_REQ_STATE__SHIFT 16 -+#define VIVS_FE_DMA_DEBUG_STATE_VE_REQ_STATE_IDLE 0x00000000 -+#define VIVS_FE_DMA_DEBUG_STATE_VE_REQ_STATE_CKCACHE 0x00010000 -+#define VIVS_FE_DMA_DEBUG_STATE_VE_REQ_STATE_MISS 0x00020000 -+ -+#define VIVS_FE_DMA_ADDRESS 0x00000664 -+ -+#define VIVS_FE_DMA_LOW 0x00000668 -+ -+#define VIVS_FE_DMA_HIGH 0x0000066c -+ -+#define VIVS_FE_AUTO_FLUSH 0x00000670 -+ -+#define VIVS_FE_UNK00678 0x00000678 -+ -+#define VIVS_FE_UNK0067C 0x0000067c -+ -+#define VIVS_FE_VERTEX_STREAMS(i0) (0x00000000 + 0x4*(i0)) -+#define VIVS_FE_VERTEX_STREAMS__ESIZE 0x00000004 -+#define VIVS_FE_VERTEX_STREAMS__LEN 0x00000008 -+ -+#define VIVS_FE_VERTEX_STREAMS_BASE_ADDR(i0) (0x00000680 + 0x4*(i0)) -+ -+#define VIVS_FE_VERTEX_STREAMS_CONTROL(i0) (0x000006a0 + 0x4*(i0)) -+ -+#define VIVS_FE_UNK00700(i0) (0x00000700 + 0x4*(i0)) -+#define VIVS_FE_UNK00700__ESIZE 0x00000004 -+#define VIVS_FE_UNK00700__LEN 0x00000010 -+ -+#define VIVS_FE_UNK00740(i0) (0x00000740 + 0x4*(i0)) -+#define VIVS_FE_UNK00740__ESIZE 0x00000004 -+#define VIVS_FE_UNK00740__LEN 0x00000010 -+ -+#define VIVS_FE_UNK00780(i0) (0x00000780 + 0x4*(i0)) -+#define VIVS_FE_UNK00780__ESIZE 0x00000004 -+#define VIVS_FE_UNK00780__LEN 0x00000010 -+ -+#define VIVS_GL 0x00000000 -+ -+#define VIVS_GL_PIPE_SELECT 0x00003800 -+#define VIVS_GL_PIPE_SELECT_PIPE__MASK 0x00000001 -+#define VIVS_GL_PIPE_SELECT_PIPE__SHIFT 0 -+#define VIVS_GL_PIPE_SELECT_PIPE(x) (((x) << VIVS_GL_PIPE_SELECT_PIPE__SHIFT) & VIVS_GL_PIPE_SELECT_PIPE__MASK) -+ -+#define VIVS_GL_EVENT 0x00003804 -+#define VIVS_GL_EVENT_EVENT_ID__MASK 0x0000001f -+#define VIVS_GL_EVENT_EVENT_ID__SHIFT 0 -+#define VIVS_GL_EVENT_EVENT_ID(x) (((x) << VIVS_GL_EVENT_EVENT_ID__SHIFT) & VIVS_GL_EVENT_EVENT_ID__MASK) -+#define VIVS_GL_EVENT_FROM_FE 0x00000020 -+#define VIVS_GL_EVENT_FROM_PE 0x00000040 -+#define VIVS_GL_EVENT_SOURCE__MASK 0x00001f00 -+#define VIVS_GL_EVENT_SOURCE__SHIFT 8 -+#define VIVS_GL_EVENT_SOURCE(x) (((x) << VIVS_GL_EVENT_SOURCE__SHIFT) & VIVS_GL_EVENT_SOURCE__MASK) -+ -+#define VIVS_GL_SEMAPHORE_TOKEN 0x00003808 -+#define VIVS_GL_SEMAPHORE_TOKEN_FROM__MASK 0x0000001f -+#define VIVS_GL_SEMAPHORE_TOKEN_FROM__SHIFT 0 -+#define VIVS_GL_SEMAPHORE_TOKEN_FROM(x) (((x) << VIVS_GL_SEMAPHORE_TOKEN_FROM__SHIFT) & VIVS_GL_SEMAPHORE_TOKEN_FROM__MASK) -+#define VIVS_GL_SEMAPHORE_TOKEN_TO__MASK 0x00001f00 -+#define VIVS_GL_SEMAPHORE_TOKEN_TO__SHIFT 8 -+#define VIVS_GL_SEMAPHORE_TOKEN_TO(x) (((x) << VIVS_GL_SEMAPHORE_TOKEN_TO__SHIFT) & VIVS_GL_SEMAPHORE_TOKEN_TO__MASK) -+ -+#define VIVS_GL_FLUSH_CACHE 0x0000380c -+#define VIVS_GL_FLUSH_CACHE_DEPTH 0x00000001 -+#define VIVS_GL_FLUSH_CACHE_COLOR 0x00000002 -+#define VIVS_GL_FLUSH_CACHE_TEXTURE 0x00000004 -+#define VIVS_GL_FLUSH_CACHE_PE2D 0x00000008 -+#define VIVS_GL_FLUSH_CACHE_TEXTUREVS 0x00000010 -+#define VIVS_GL_FLUSH_CACHE_SHADER_L1 0x00000020 -+#define VIVS_GL_FLUSH_CACHE_SHADER_L2 0x00000040 -+ -+#define VIVS_GL_FLUSH_MMU 0x00003810 -+#define VIVS_GL_FLUSH_MMU_FLUSH_FEMMU 0x00000001 -+#define VIVS_GL_FLUSH_MMU_FLUSH_UNK1 0x00000002 -+#define VIVS_GL_FLUSH_MMU_FLUSH_UNK2 0x00000004 -+#define VIVS_GL_FLUSH_MMU_FLUSH_PEMMU 0x00000008 -+#define VIVS_GL_FLUSH_MMU_FLUSH_UNK4 0x00000010 -+ -+#define VIVS_GL_VERTEX_ELEMENT_CONFIG 0x00003814 -+ -+#define VIVS_GL_MULTI_SAMPLE_CONFIG 0x00003818 -+#define VIVS_GL_MULTI_SAMPLE_CONFIG_MSAA_SAMPLES__MASK 0x00000003 -+#define VIVS_GL_MULTI_SAMPLE_CONFIG_MSAA_SAMPLES__SHIFT 0 -+#define VIVS_GL_MULTI_SAMPLE_CONFIG_MSAA_SAMPLES_NONE 0x00000000 -+#define VIVS_GL_MULTI_SAMPLE_CONFIG_MSAA_SAMPLES_2X 0x00000001 -+#define VIVS_GL_MULTI_SAMPLE_CONFIG_MSAA_SAMPLES_4X 0x00000002 -+#define VIVS_GL_MULTI_SAMPLE_CONFIG_MSAA_SAMPLES_MASK 0x00000008 -+#define VIVS_GL_MULTI_SAMPLE_CONFIG_MSAA_ENABLES__MASK 0x000000f0 -+#define VIVS_GL_MULTI_SAMPLE_CONFIG_MSAA_ENABLES__SHIFT 4 -+#define VIVS_GL_MULTI_SAMPLE_CONFIG_MSAA_ENABLES(x) (((x) << VIVS_GL_MULTI_SAMPLE_CONFIG_MSAA_ENABLES__SHIFT) & VIVS_GL_MULTI_SAMPLE_CONFIG_MSAA_ENABLES__MASK) -+#define VIVS_GL_MULTI_SAMPLE_CONFIG_MSAA_ENABLES_MASK 0x00000100 -+#define VIVS_GL_MULTI_SAMPLE_CONFIG_UNK12__MASK 0x00007000 -+#define VIVS_GL_MULTI_SAMPLE_CONFIG_UNK12__SHIFT 12 -+#define VIVS_GL_MULTI_SAMPLE_CONFIG_UNK12(x) (((x) << VIVS_GL_MULTI_SAMPLE_CONFIG_UNK12__SHIFT) & VIVS_GL_MULTI_SAMPLE_CONFIG_UNK12__MASK) -+#define VIVS_GL_MULTI_SAMPLE_CONFIG_UNK12_MASK 0x00008000 -+#define VIVS_GL_MULTI_SAMPLE_CONFIG_UNK16__MASK 0x00030000 -+#define VIVS_GL_MULTI_SAMPLE_CONFIG_UNK16__SHIFT 16 -+#define VIVS_GL_MULTI_SAMPLE_CONFIG_UNK16(x) (((x) << VIVS_GL_MULTI_SAMPLE_CONFIG_UNK16__SHIFT) & VIVS_GL_MULTI_SAMPLE_CONFIG_UNK16__MASK) -+#define VIVS_GL_MULTI_SAMPLE_CONFIG_UNK16_MASK 0x00080000 -+ -+#define VIVS_GL_VARYING_TOTAL_COMPONENTS 0x0000381c -+#define VIVS_GL_VARYING_TOTAL_COMPONENTS_NUM__MASK 0x000000ff -+#define VIVS_GL_VARYING_TOTAL_COMPONENTS_NUM__SHIFT 0 -+#define VIVS_GL_VARYING_TOTAL_COMPONENTS_NUM(x) (((x) << VIVS_GL_VARYING_TOTAL_COMPONENTS_NUM__SHIFT) & VIVS_GL_VARYING_TOTAL_COMPONENTS_NUM__MASK) -+ -+#define VIVS_GL_VARYING_NUM_COMPONENTS 0x00003820 -+#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR0__MASK 0x00000007 -+#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR0__SHIFT 0 -+#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR0(x) (((x) << VIVS_GL_VARYING_NUM_COMPONENTS_VAR0__SHIFT) & VIVS_GL_VARYING_NUM_COMPONENTS_VAR0__MASK) -+#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR1__MASK 0x00000070 -+#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR1__SHIFT 4 -+#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR1(x) (((x) << VIVS_GL_VARYING_NUM_COMPONENTS_VAR1__SHIFT) & VIVS_GL_VARYING_NUM_COMPONENTS_VAR1__MASK) -+#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR2__MASK 0x00000700 -+#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR2__SHIFT 8 -+#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR2(x) (((x) << VIVS_GL_VARYING_NUM_COMPONENTS_VAR2__SHIFT) & VIVS_GL_VARYING_NUM_COMPONENTS_VAR2__MASK) -+#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR3__MASK 0x00007000 -+#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR3__SHIFT 12 -+#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR3(x) (((x) << VIVS_GL_VARYING_NUM_COMPONENTS_VAR3__SHIFT) & VIVS_GL_VARYING_NUM_COMPONENTS_VAR3__MASK) -+#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR4__MASK 0x00070000 -+#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR4__SHIFT 16 -+#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR4(x) (((x) << VIVS_GL_VARYING_NUM_COMPONENTS_VAR4__SHIFT) & VIVS_GL_VARYING_NUM_COMPONENTS_VAR4__MASK) -+#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR5__MASK 0x00700000 -+#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR5__SHIFT 20 -+#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR5(x) (((x) << VIVS_GL_VARYING_NUM_COMPONENTS_VAR5__SHIFT) & VIVS_GL_VARYING_NUM_COMPONENTS_VAR5__MASK) -+#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR6__MASK 0x07000000 -+#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR6__SHIFT 24 -+#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR6(x) (((x) << VIVS_GL_VARYING_NUM_COMPONENTS_VAR6__SHIFT) & VIVS_GL_VARYING_NUM_COMPONENTS_VAR6__MASK) -+#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR7__MASK 0x70000000 -+#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR7__SHIFT 28 -+#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR7(x) (((x) << VIVS_GL_VARYING_NUM_COMPONENTS_VAR7__SHIFT) & VIVS_GL_VARYING_NUM_COMPONENTS_VAR7__MASK) -+ -+#define VIVS_GL_VARYING_COMPONENT_USE(i0) (0x00003828 + 0x4*(i0)) -+#define VIVS_GL_VARYING_COMPONENT_USE__ESIZE 0x00000004 -+#define VIVS_GL_VARYING_COMPONENT_USE__LEN 0x00000002 -+#define VIVS_GL_VARYING_COMPONENT_USE_COMP0__MASK 0x00000003 -+#define VIVS_GL_VARYING_COMPONENT_USE_COMP0__SHIFT 0 -+#define VIVS_GL_VARYING_COMPONENT_USE_COMP0(x) (((x) << VIVS_GL_VARYING_COMPONENT_USE_COMP0__SHIFT) & VIVS_GL_VARYING_COMPONENT_USE_COMP0__MASK) -+#define VIVS_GL_VARYING_COMPONENT_USE_COMP1__MASK 0x0000000c -+#define VIVS_GL_VARYING_COMPONENT_USE_COMP1__SHIFT 2 -+#define VIVS_GL_VARYING_COMPONENT_USE_COMP1(x) (((x) << VIVS_GL_VARYING_COMPONENT_USE_COMP1__SHIFT) & VIVS_GL_VARYING_COMPONENT_USE_COMP1__MASK) -+#define VIVS_GL_VARYING_COMPONENT_USE_COMP2__MASK 0x00000030 -+#define VIVS_GL_VARYING_COMPONENT_USE_COMP2__SHIFT 4 -+#define VIVS_GL_VARYING_COMPONENT_USE_COMP2(x) (((x) << VIVS_GL_VARYING_COMPONENT_USE_COMP2__SHIFT) & VIVS_GL_VARYING_COMPONENT_USE_COMP2__MASK) -+#define VIVS_GL_VARYING_COMPONENT_USE_COMP3__MASK 0x000000c0 -+#define VIVS_GL_VARYING_COMPONENT_USE_COMP3__SHIFT 6 -+#define VIVS_GL_VARYING_COMPONENT_USE_COMP3(x) (((x) << VIVS_GL_VARYING_COMPONENT_USE_COMP3__SHIFT) & VIVS_GL_VARYING_COMPONENT_USE_COMP3__MASK) -+#define VIVS_GL_VARYING_COMPONENT_USE_COMP4__MASK 0x00000300 -+#define VIVS_GL_VARYING_COMPONENT_USE_COMP4__SHIFT 8 -+#define VIVS_GL_VARYING_COMPONENT_USE_COMP4(x) (((x) << VIVS_GL_VARYING_COMPONENT_USE_COMP4__SHIFT) & VIVS_GL_VARYING_COMPONENT_USE_COMP4__MASK) -+#define VIVS_GL_VARYING_COMPONENT_USE_COMP5__MASK 0x00000c00 -+#define VIVS_GL_VARYING_COMPONENT_USE_COMP5__SHIFT 10 -+#define VIVS_GL_VARYING_COMPONENT_USE_COMP5(x) (((x) << VIVS_GL_VARYING_COMPONENT_USE_COMP5__SHIFT) & VIVS_GL_VARYING_COMPONENT_USE_COMP5__MASK) -+#define VIVS_GL_VARYING_COMPONENT_USE_COMP6__MASK 0x00003000 -+#define VIVS_GL_VARYING_COMPONENT_USE_COMP6__SHIFT 12 -+#define VIVS_GL_VARYING_COMPONENT_USE_COMP6(x) (((x) << VIVS_GL_VARYING_COMPONENT_USE_COMP6__SHIFT) & VIVS_GL_VARYING_COMPONENT_USE_COMP6__MASK) -+#define VIVS_GL_VARYING_COMPONENT_USE_COMP7__MASK 0x0000c000 -+#define VIVS_GL_VARYING_COMPONENT_USE_COMP7__SHIFT 14 -+#define VIVS_GL_VARYING_COMPONENT_USE_COMP7(x) (((x) << VIVS_GL_VARYING_COMPONENT_USE_COMP7__SHIFT) & VIVS_GL_VARYING_COMPONENT_USE_COMP7__MASK) -+#define VIVS_GL_VARYING_COMPONENT_USE_COMP8__MASK 0x00030000 -+#define VIVS_GL_VARYING_COMPONENT_USE_COMP8__SHIFT 16 -+#define VIVS_GL_VARYING_COMPONENT_USE_COMP8(x) (((x) << VIVS_GL_VARYING_COMPONENT_USE_COMP8__SHIFT) & VIVS_GL_VARYING_COMPONENT_USE_COMP8__MASK) -+#define VIVS_GL_VARYING_COMPONENT_USE_COMP9__MASK 0x000c0000 -+#define VIVS_GL_VARYING_COMPONENT_USE_COMP9__SHIFT 18 -+#define VIVS_GL_VARYING_COMPONENT_USE_COMP9(x) (((x) << VIVS_GL_VARYING_COMPONENT_USE_COMP9__SHIFT) & VIVS_GL_VARYING_COMPONENT_USE_COMP9__MASK) -+#define VIVS_GL_VARYING_COMPONENT_USE_COMP10__MASK 0x00300000 -+#define VIVS_GL_VARYING_COMPONENT_USE_COMP10__SHIFT 20 -+#define VIVS_GL_VARYING_COMPONENT_USE_COMP10(x) (((x) << VIVS_GL_VARYING_COMPONENT_USE_COMP10__SHIFT) & VIVS_GL_VARYING_COMPONENT_USE_COMP10__MASK) -+#define VIVS_GL_VARYING_COMPONENT_USE_COMP11__MASK 0x00c00000 -+#define VIVS_GL_VARYING_COMPONENT_USE_COMP11__SHIFT 22 -+#define VIVS_GL_VARYING_COMPONENT_USE_COMP11(x) (((x) << VIVS_GL_VARYING_COMPONENT_USE_COMP11__SHIFT) & VIVS_GL_VARYING_COMPONENT_USE_COMP11__MASK) -+#define VIVS_GL_VARYING_COMPONENT_USE_COMP12__MASK 0x03000000 -+#define VIVS_GL_VARYING_COMPONENT_USE_COMP12__SHIFT 24 -+#define VIVS_GL_VARYING_COMPONENT_USE_COMP12(x) (((x) << VIVS_GL_VARYING_COMPONENT_USE_COMP12__SHIFT) & VIVS_GL_VARYING_COMPONENT_USE_COMP12__MASK) -+#define VIVS_GL_VARYING_COMPONENT_USE_COMP13__MASK 0x0c000000 -+#define VIVS_GL_VARYING_COMPONENT_USE_COMP13__SHIFT 26 -+#define VIVS_GL_VARYING_COMPONENT_USE_COMP13(x) (((x) << VIVS_GL_VARYING_COMPONENT_USE_COMP13__SHIFT) & VIVS_GL_VARYING_COMPONENT_USE_COMP13__MASK) -+#define VIVS_GL_VARYING_COMPONENT_USE_COMP14__MASK 0x30000000 -+#define VIVS_GL_VARYING_COMPONENT_USE_COMP14__SHIFT 28 -+#define VIVS_GL_VARYING_COMPONENT_USE_COMP14(x) (((x) << VIVS_GL_VARYING_COMPONENT_USE_COMP14__SHIFT) & VIVS_GL_VARYING_COMPONENT_USE_COMP14__MASK) -+#define VIVS_GL_VARYING_COMPONENT_USE_COMP15__MASK 0xc0000000 -+#define VIVS_GL_VARYING_COMPONENT_USE_COMP15__SHIFT 30 -+#define VIVS_GL_VARYING_COMPONENT_USE_COMP15(x) (((x) << VIVS_GL_VARYING_COMPONENT_USE_COMP15__SHIFT) & VIVS_GL_VARYING_COMPONENT_USE_COMP15__MASK) -+ -+#define VIVS_GL_UNK03834 0x00003834 -+ -+#define VIVS_GL_UNK03838 0x00003838 -+ -+#define VIVS_GL_API_MODE 0x0000384c -+#define VIVS_GL_API_MODE_OPENGL 0x00000000 -+#define VIVS_GL_API_MODE_OPENVG 0x00000001 -+#define VIVS_GL_API_MODE_OPENCL 0x00000002 -+ -+#define VIVS_GL_CONTEXT_POINTER 0x00003850 -+ -+#define VIVS_GL_UNK03A00 0x00003a00 -+ -+#define VIVS_GL_STALL_TOKEN 0x00003c00 -+#define VIVS_GL_STALL_TOKEN_FROM__MASK 0x0000001f -+#define VIVS_GL_STALL_TOKEN_FROM__SHIFT 0 -+#define VIVS_GL_STALL_TOKEN_FROM(x) (((x) << VIVS_GL_STALL_TOKEN_FROM__SHIFT) & VIVS_GL_STALL_TOKEN_FROM__MASK) -+#define VIVS_GL_STALL_TOKEN_TO__MASK 0x00001f00 -+#define VIVS_GL_STALL_TOKEN_TO__SHIFT 8 -+#define VIVS_GL_STALL_TOKEN_TO(x) (((x) << VIVS_GL_STALL_TOKEN_TO__SHIFT) & VIVS_GL_STALL_TOKEN_TO__MASK) -+#define VIVS_GL_STALL_TOKEN_FLIP0 0x40000000 -+#define VIVS_GL_STALL_TOKEN_FLIP1 0x80000000 -+ -+#define VIVS_DUMMY 0x00000000 -+ -+#define VIVS_DUMMY_DUMMY 0x0003fffc -+ -+ -+#endif /* STATE_XML */ -diff --git a/drivers/gpu/drm/etnaviv/state_hi.xml.h b/drivers/gpu/drm/etnaviv/state_hi.xml.h -new file mode 100644 -index 0000000..0064f26 ---- /dev/null -+++ b/drivers/gpu/drm/etnaviv/state_hi.xml.h -@@ -0,0 +1,407 @@ -+#ifndef STATE_HI_XML -+#define STATE_HI_XML -+ -+/* Autogenerated file, DO NOT EDIT manually! -+ -+This file was generated by the rules-ng-ng headergen tool in this git repository: -+http://0x04.net/cgit/index.cgi/rules-ng-ng -+git clone git://0x04.net/rules-ng-ng -+ -+The rules-ng-ng source files this header was generated from are: -+- state_hi.xml ( 23420 bytes, from 2015-03-25 11:47:21) -+- common.xml ( 18437 bytes, from 2015-03-25 11:27:41) -+ -+Copyright (C) 2015 -+*/ -+ -+ -+#define MMU_EXCEPTION_SLAVE_NOT_PRESENT 0x00000001 -+#define MMU_EXCEPTION_PAGE_NOT_PRESENT 0x00000002 -+#define MMU_EXCEPTION_WRITE_VIOLATION 0x00000003 -+#define VIVS_HI 0x00000000 -+ -+#define VIVS_HI_CLOCK_CONTROL 0x00000000 -+#define VIVS_HI_CLOCK_CONTROL_CLK3D_DIS 0x00000001 -+#define VIVS_HI_CLOCK_CONTROL_CLK2D_DIS 0x00000002 -+#define VIVS_HI_CLOCK_CONTROL_FSCALE_VAL__MASK 0x000001fc -+#define VIVS_HI_CLOCK_CONTROL_FSCALE_VAL__SHIFT 2 -+#define VIVS_HI_CLOCK_CONTROL_FSCALE_VAL(x) (((x) << VIVS_HI_CLOCK_CONTROL_FSCALE_VAL__SHIFT) & VIVS_HI_CLOCK_CONTROL_FSCALE_VAL__MASK) -+#define VIVS_HI_CLOCK_CONTROL_FSCALE_CMD_LOAD 0x00000200 -+#define VIVS_HI_CLOCK_CONTROL_DISABLE_RAM_CLK_GATING 0x00000400 -+#define VIVS_HI_CLOCK_CONTROL_DISABLE_DEBUG_REGISTERS 0x00000800 -+#define VIVS_HI_CLOCK_CONTROL_SOFT_RESET 0x00001000 -+#define VIVS_HI_CLOCK_CONTROL_IDLE_3D 0x00010000 -+#define VIVS_HI_CLOCK_CONTROL_IDLE_2D 0x00020000 -+#define VIVS_HI_CLOCK_CONTROL_IDLE_VG 0x00040000 -+#define VIVS_HI_CLOCK_CONTROL_ISOLATE_GPU 0x00080000 -+#define VIVS_HI_CLOCK_CONTROL_DEBUG_PIXEL_PIPE__MASK 0x00f00000 -+#define VIVS_HI_CLOCK_CONTROL_DEBUG_PIXEL_PIPE__SHIFT 20 -+#define VIVS_HI_CLOCK_CONTROL_DEBUG_PIXEL_PIPE(x) (((x) << VIVS_HI_CLOCK_CONTROL_DEBUG_PIXEL_PIPE__SHIFT) & VIVS_HI_CLOCK_CONTROL_DEBUG_PIXEL_PIPE__MASK) -+ -+#define VIVS_HI_IDLE_STATE 0x00000004 -+#define VIVS_HI_IDLE_STATE_FE 0x00000001 -+#define VIVS_HI_IDLE_STATE_DE 0x00000002 -+#define VIVS_HI_IDLE_STATE_PE 0x00000004 -+#define VIVS_HI_IDLE_STATE_SH 0x00000008 -+#define VIVS_HI_IDLE_STATE_PA 0x00000010 -+#define VIVS_HI_IDLE_STATE_SE 0x00000020 -+#define VIVS_HI_IDLE_STATE_RA 0x00000040 -+#define VIVS_HI_IDLE_STATE_TX 0x00000080 -+#define VIVS_HI_IDLE_STATE_VG 0x00000100 -+#define VIVS_HI_IDLE_STATE_IM 0x00000200 -+#define VIVS_HI_IDLE_STATE_FP 0x00000400 -+#define VIVS_HI_IDLE_STATE_TS 0x00000800 -+#define VIVS_HI_IDLE_STATE_AXI_LP 0x80000000 -+ -+#define VIVS_HI_AXI_CONFIG 0x00000008 -+#define VIVS_HI_AXI_CONFIG_AWID__MASK 0x0000000f -+#define VIVS_HI_AXI_CONFIG_AWID__SHIFT 0 -+#define VIVS_HI_AXI_CONFIG_AWID(x) (((x) << VIVS_HI_AXI_CONFIG_AWID__SHIFT) & VIVS_HI_AXI_CONFIG_AWID__MASK) -+#define VIVS_HI_AXI_CONFIG_ARID__MASK 0x000000f0 -+#define VIVS_HI_AXI_CONFIG_ARID__SHIFT 4 -+#define VIVS_HI_AXI_CONFIG_ARID(x) (((x) << VIVS_HI_AXI_CONFIG_ARID__SHIFT) & VIVS_HI_AXI_CONFIG_ARID__MASK) -+#define VIVS_HI_AXI_CONFIG_AWCACHE__MASK 0x00000f00 -+#define VIVS_HI_AXI_CONFIG_AWCACHE__SHIFT 8 -+#define VIVS_HI_AXI_CONFIG_AWCACHE(x) (((x) << VIVS_HI_AXI_CONFIG_AWCACHE__SHIFT) & VIVS_HI_AXI_CONFIG_AWCACHE__MASK) -+#define VIVS_HI_AXI_CONFIG_ARCACHE__MASK 0x0000f000 -+#define VIVS_HI_AXI_CONFIG_ARCACHE__SHIFT 12 -+#define VIVS_HI_AXI_CONFIG_ARCACHE(x) (((x) << VIVS_HI_AXI_CONFIG_ARCACHE__SHIFT) & VIVS_HI_AXI_CONFIG_ARCACHE__MASK) -+ -+#define VIVS_HI_AXI_STATUS 0x0000000c -+#define VIVS_HI_AXI_STATUS_WR_ERR_ID__MASK 0x0000000f -+#define VIVS_HI_AXI_STATUS_WR_ERR_ID__SHIFT 0 -+#define VIVS_HI_AXI_STATUS_WR_ERR_ID(x) (((x) << VIVS_HI_AXI_STATUS_WR_ERR_ID__SHIFT) & VIVS_HI_AXI_STATUS_WR_ERR_ID__MASK) -+#define VIVS_HI_AXI_STATUS_RD_ERR_ID__MASK 0x000000f0 -+#define VIVS_HI_AXI_STATUS_RD_ERR_ID__SHIFT 4 -+#define VIVS_HI_AXI_STATUS_RD_ERR_ID(x) (((x) << VIVS_HI_AXI_STATUS_RD_ERR_ID__SHIFT) & VIVS_HI_AXI_STATUS_RD_ERR_ID__MASK) -+#define VIVS_HI_AXI_STATUS_DET_WR_ERR 0x00000100 -+#define VIVS_HI_AXI_STATUS_DET_RD_ERR 0x00000200 -+ -+#define VIVS_HI_INTR_ACKNOWLEDGE 0x00000010 -+#define VIVS_HI_INTR_ACKNOWLEDGE_INTR_VEC__MASK 0x7fffffff -+#define VIVS_HI_INTR_ACKNOWLEDGE_INTR_VEC__SHIFT 0 -+#define VIVS_HI_INTR_ACKNOWLEDGE_INTR_VEC(x) (((x) << VIVS_HI_INTR_ACKNOWLEDGE_INTR_VEC__SHIFT) & VIVS_HI_INTR_ACKNOWLEDGE_INTR_VEC__MASK) -+#define VIVS_HI_INTR_ACKNOWLEDGE_AXI_BUS_ERROR 0x80000000 -+ -+#define VIVS_HI_INTR_ENBL 0x00000014 -+#define VIVS_HI_INTR_ENBL_INTR_ENBL_VEC__MASK 0xffffffff -+#define VIVS_HI_INTR_ENBL_INTR_ENBL_VEC__SHIFT 0 -+#define VIVS_HI_INTR_ENBL_INTR_ENBL_VEC(x) (((x) << VIVS_HI_INTR_ENBL_INTR_ENBL_VEC__SHIFT) & VIVS_HI_INTR_ENBL_INTR_ENBL_VEC__MASK) -+ -+#define VIVS_HI_CHIP_IDENTITY 0x00000018 -+#define VIVS_HI_CHIP_IDENTITY_FAMILY__MASK 0xff000000 -+#define VIVS_HI_CHIP_IDENTITY_FAMILY__SHIFT 24 -+#define VIVS_HI_CHIP_IDENTITY_FAMILY(x) (((x) << VIVS_HI_CHIP_IDENTITY_FAMILY__SHIFT) & VIVS_HI_CHIP_IDENTITY_FAMILY__MASK) -+#define VIVS_HI_CHIP_IDENTITY_PRODUCT__MASK 0x00ff0000 -+#define VIVS_HI_CHIP_IDENTITY_PRODUCT__SHIFT 16 -+#define VIVS_HI_CHIP_IDENTITY_PRODUCT(x) (((x) << VIVS_HI_CHIP_IDENTITY_PRODUCT__SHIFT) & VIVS_HI_CHIP_IDENTITY_PRODUCT__MASK) -+#define VIVS_HI_CHIP_IDENTITY_REVISION__MASK 0x0000f000 -+#define VIVS_HI_CHIP_IDENTITY_REVISION__SHIFT 12 -+#define VIVS_HI_CHIP_IDENTITY_REVISION(x) (((x) << VIVS_HI_CHIP_IDENTITY_REVISION__SHIFT) & VIVS_HI_CHIP_IDENTITY_REVISION__MASK) -+ -+#define VIVS_HI_CHIP_FEATURE 0x0000001c -+ -+#define VIVS_HI_CHIP_MODEL 0x00000020 -+ -+#define VIVS_HI_CHIP_REV 0x00000024 -+ -+#define VIVS_HI_CHIP_DATE 0x00000028 -+ -+#define VIVS_HI_CHIP_TIME 0x0000002c -+ -+#define VIVS_HI_CHIP_MINOR_FEATURE_0 0x00000034 -+ -+#define VIVS_HI_CACHE_CONTROL 0x00000038 -+ -+#define VIVS_HI_MEMORY_COUNTER_RESET 0x0000003c -+ -+#define VIVS_HI_PROFILE_READ_BYTES8 0x00000040 -+ -+#define VIVS_HI_PROFILE_WRITE_BYTES8 0x00000044 -+ -+#define VIVS_HI_CHIP_SPECS 0x00000048 -+#define VIVS_HI_CHIP_SPECS_STREAM_COUNT__MASK 0x0000000f -+#define VIVS_HI_CHIP_SPECS_STREAM_COUNT__SHIFT 0 -+#define VIVS_HI_CHIP_SPECS_STREAM_COUNT(x) (((x) << VIVS_HI_CHIP_SPECS_STREAM_COUNT__SHIFT) & VIVS_HI_CHIP_SPECS_STREAM_COUNT__MASK) -+#define VIVS_HI_CHIP_SPECS_REGISTER_MAX__MASK 0x000000f0 -+#define VIVS_HI_CHIP_SPECS_REGISTER_MAX__SHIFT 4 -+#define VIVS_HI_CHIP_SPECS_REGISTER_MAX(x) (((x) << VIVS_HI_CHIP_SPECS_REGISTER_MAX__SHIFT) & VIVS_HI_CHIP_SPECS_REGISTER_MAX__MASK) -+#define VIVS_HI_CHIP_SPECS_THREAD_COUNT__MASK 0x00000f00 -+#define VIVS_HI_CHIP_SPECS_THREAD_COUNT__SHIFT 8 -+#define VIVS_HI_CHIP_SPECS_THREAD_COUNT(x) (((x) << VIVS_HI_CHIP_SPECS_THREAD_COUNT__SHIFT) & VIVS_HI_CHIP_SPECS_THREAD_COUNT__MASK) -+#define VIVS_HI_CHIP_SPECS_VERTEX_CACHE_SIZE__MASK 0x0001f000 -+#define VIVS_HI_CHIP_SPECS_VERTEX_CACHE_SIZE__SHIFT 12 -+#define VIVS_HI_CHIP_SPECS_VERTEX_CACHE_SIZE(x) (((x) << VIVS_HI_CHIP_SPECS_VERTEX_CACHE_SIZE__SHIFT) & VIVS_HI_CHIP_SPECS_VERTEX_CACHE_SIZE__MASK) -+#define VIVS_HI_CHIP_SPECS_SHADER_CORE_COUNT__MASK 0x01f00000 -+#define VIVS_HI_CHIP_SPECS_SHADER_CORE_COUNT__SHIFT 20 -+#define VIVS_HI_CHIP_SPECS_SHADER_CORE_COUNT(x) (((x) << VIVS_HI_CHIP_SPECS_SHADER_CORE_COUNT__SHIFT) & VIVS_HI_CHIP_SPECS_SHADER_CORE_COUNT__MASK) -+#define VIVS_HI_CHIP_SPECS_PIXEL_PIPES__MASK 0x0e000000 -+#define VIVS_HI_CHIP_SPECS_PIXEL_PIPES__SHIFT 25 -+#define VIVS_HI_CHIP_SPECS_PIXEL_PIPES(x) (((x) << VIVS_HI_CHIP_SPECS_PIXEL_PIPES__SHIFT) & VIVS_HI_CHIP_SPECS_PIXEL_PIPES__MASK) -+#define VIVS_HI_CHIP_SPECS_VERTEX_OUTPUT_BUFFER_SIZE__MASK 0xf0000000 -+#define VIVS_HI_CHIP_SPECS_VERTEX_OUTPUT_BUFFER_SIZE__SHIFT 28 -+#define VIVS_HI_CHIP_SPECS_VERTEX_OUTPUT_BUFFER_SIZE(x) (((x) << VIVS_HI_CHIP_SPECS_VERTEX_OUTPUT_BUFFER_SIZE__SHIFT) & VIVS_HI_CHIP_SPECS_VERTEX_OUTPUT_BUFFER_SIZE__MASK) -+ -+#define VIVS_HI_PROFILE_WRITE_BURSTS 0x0000004c -+ -+#define VIVS_HI_PROFILE_WRITE_REQUESTS 0x00000050 -+ -+#define VIVS_HI_PROFILE_READ_BURSTS 0x00000058 -+ -+#define VIVS_HI_PROFILE_READ_REQUESTS 0x0000005c -+ -+#define VIVS_HI_PROFILE_READ_LASTS 0x00000060 -+ -+#define VIVS_HI_GP_OUT0 0x00000064 -+ -+#define VIVS_HI_GP_OUT1 0x00000068 -+ -+#define VIVS_HI_GP_OUT2 0x0000006c -+ -+#define VIVS_HI_AXI_CONTROL 0x00000070 -+#define VIVS_HI_AXI_CONTROL_WR_FULL_BURST_MODE 0x00000001 -+ -+#define VIVS_HI_CHIP_MINOR_FEATURE_1 0x00000074 -+ -+#define VIVS_HI_PROFILE_TOTAL_CYCLES 0x00000078 -+ -+#define VIVS_HI_PROFILE_IDLE_CYCLES 0x0000007c -+ -+#define VIVS_HI_CHIP_SPECS_2 0x00000080 -+#define VIVS_HI_CHIP_SPECS_2_BUFFER_SIZE__MASK 0x000000ff -+#define VIVS_HI_CHIP_SPECS_2_BUFFER_SIZE__SHIFT 0 -+#define VIVS_HI_CHIP_SPECS_2_BUFFER_SIZE(x) (((x) << VIVS_HI_CHIP_SPECS_2_BUFFER_SIZE__SHIFT) & VIVS_HI_CHIP_SPECS_2_BUFFER_SIZE__MASK) -+#define VIVS_HI_CHIP_SPECS_2_INSTRUCTION_COUNT__MASK 0x0000ff00 -+#define VIVS_HI_CHIP_SPECS_2_INSTRUCTION_COUNT__SHIFT 8 -+#define VIVS_HI_CHIP_SPECS_2_INSTRUCTION_COUNT(x) (((x) << VIVS_HI_CHIP_SPECS_2_INSTRUCTION_COUNT__SHIFT) & VIVS_HI_CHIP_SPECS_2_INSTRUCTION_COUNT__MASK) -+#define VIVS_HI_CHIP_SPECS_2_NUM_CONSTANTS__MASK 0xffff0000 -+#define VIVS_HI_CHIP_SPECS_2_NUM_CONSTANTS__SHIFT 16 -+#define VIVS_HI_CHIP_SPECS_2_NUM_CONSTANTS(x) (((x) << VIVS_HI_CHIP_SPECS_2_NUM_CONSTANTS__SHIFT) & VIVS_HI_CHIP_SPECS_2_NUM_CONSTANTS__MASK) -+ -+#define VIVS_HI_CHIP_MINOR_FEATURE_2 0x00000084 -+ -+#define VIVS_HI_CHIP_MINOR_FEATURE_3 0x00000088 -+ -+#define VIVS_HI_CHIP_MINOR_FEATURE_4 0x00000094 -+ -+#define VIVS_PM 0x00000000 -+ -+#define VIVS_PM_POWER_CONTROLS 0x00000100 -+#define VIVS_PM_POWER_CONTROLS_ENABLE_MODULE_CLOCK_GATING 0x00000001 -+#define VIVS_PM_POWER_CONTROLS_DISABLE_STALL_MODULE_CLOCK_GATING 0x00000002 -+#define VIVS_PM_POWER_CONTROLS_DISABLE_STARVE_MODULE_CLOCK_GATING 0x00000004 -+#define VIVS_PM_POWER_CONTROLS_TURN_ON_COUNTER__MASK 0x000000f0 -+#define VIVS_PM_POWER_CONTROLS_TURN_ON_COUNTER__SHIFT 4 -+#define VIVS_PM_POWER_CONTROLS_TURN_ON_COUNTER(x) (((x) << VIVS_PM_POWER_CONTROLS_TURN_ON_COUNTER__SHIFT) & VIVS_PM_POWER_CONTROLS_TURN_ON_COUNTER__MASK) -+#define VIVS_PM_POWER_CONTROLS_TURN_OFF_COUNTER__MASK 0xffff0000 -+#define VIVS_PM_POWER_CONTROLS_TURN_OFF_COUNTER__SHIFT 16 -+#define VIVS_PM_POWER_CONTROLS_TURN_OFF_COUNTER(x) (((x) << VIVS_PM_POWER_CONTROLS_TURN_OFF_COUNTER__SHIFT) & VIVS_PM_POWER_CONTROLS_TURN_OFF_COUNTER__MASK) -+ -+#define VIVS_PM_MODULE_CONTROLS 0x00000104 -+#define VIVS_PM_MODULE_CONTROLS_DISABLE_MODULE_CLOCK_GATING_FE 0x00000001 -+#define VIVS_PM_MODULE_CONTROLS_DISABLE_MODULE_CLOCK_GATING_DE 0x00000002 -+#define VIVS_PM_MODULE_CONTROLS_DISABLE_MODULE_CLOCK_GATING_PE 0x00000004 -+ -+#define VIVS_PM_MODULE_STATUS 0x00000108 -+#define VIVS_PM_MODULE_STATUS_MODULE_CLOCK_GATED_FE 0x00000001 -+#define VIVS_PM_MODULE_STATUS_MODULE_CLOCK_GATED_DE 0x00000002 -+#define VIVS_PM_MODULE_STATUS_MODULE_CLOCK_GATED_PE 0x00000004 -+ -+#define VIVS_PM_PULSE_EATER 0x0000010c -+ -+#define VIVS_MMUv2 0x00000000 -+ -+#define VIVS_MMUv2_SAFE_ADDRESS 0x00000180 -+ -+#define VIVS_MMUv2_CONFIGURATION 0x00000184 -+#define VIVS_MMUv2_CONFIGURATION_MODE__MASK 0x00000001 -+#define VIVS_MMUv2_CONFIGURATION_MODE__SHIFT 0 -+#define VIVS_MMUv2_CONFIGURATION_MODE_MODE4_K 0x00000000 -+#define VIVS_MMUv2_CONFIGURATION_MODE_MODE1_K 0x00000001 -+#define VIVS_MMUv2_CONFIGURATION_MODE_MASK 0x00000008 -+#define VIVS_MMUv2_CONFIGURATION_FLUSH__MASK 0x00000010 -+#define VIVS_MMUv2_CONFIGURATION_FLUSH__SHIFT 4 -+#define VIVS_MMUv2_CONFIGURATION_FLUSH_FLUSH 0x00000010 -+#define VIVS_MMUv2_CONFIGURATION_FLUSH_MASK 0x00000080 -+#define VIVS_MMUv2_CONFIGURATION_ADDRESS_MASK 0x00000100 -+#define VIVS_MMUv2_CONFIGURATION_ADDRESS__MASK 0xfffffc00 -+#define VIVS_MMUv2_CONFIGURATION_ADDRESS__SHIFT 10 -+#define VIVS_MMUv2_CONFIGURATION_ADDRESS(x) (((x) << VIVS_MMUv2_CONFIGURATION_ADDRESS__SHIFT) & VIVS_MMUv2_CONFIGURATION_ADDRESS__MASK) -+ -+#define VIVS_MMUv2_STATUS 0x00000188 -+#define VIVS_MMUv2_STATUS_EXCEPTION0__MASK 0x00000003 -+#define VIVS_MMUv2_STATUS_EXCEPTION0__SHIFT 0 -+#define VIVS_MMUv2_STATUS_EXCEPTION0(x) (((x) << VIVS_MMUv2_STATUS_EXCEPTION0__SHIFT) & VIVS_MMUv2_STATUS_EXCEPTION0__MASK) -+#define VIVS_MMUv2_STATUS_EXCEPTION1__MASK 0x00000030 -+#define VIVS_MMUv2_STATUS_EXCEPTION1__SHIFT 4 -+#define VIVS_MMUv2_STATUS_EXCEPTION1(x) (((x) << VIVS_MMUv2_STATUS_EXCEPTION1__SHIFT) & VIVS_MMUv2_STATUS_EXCEPTION1__MASK) -+#define VIVS_MMUv2_STATUS_EXCEPTION2__MASK 0x00000300 -+#define VIVS_MMUv2_STATUS_EXCEPTION2__SHIFT 8 -+#define VIVS_MMUv2_STATUS_EXCEPTION2(x) (((x) << VIVS_MMUv2_STATUS_EXCEPTION2__SHIFT) & VIVS_MMUv2_STATUS_EXCEPTION2__MASK) -+#define VIVS_MMUv2_STATUS_EXCEPTION3__MASK 0x00003000 -+#define VIVS_MMUv2_STATUS_EXCEPTION3__SHIFT 12 -+#define VIVS_MMUv2_STATUS_EXCEPTION3(x) (((x) << VIVS_MMUv2_STATUS_EXCEPTION3__SHIFT) & VIVS_MMUv2_STATUS_EXCEPTION3__MASK) -+ -+#define VIVS_MMUv2_CONTROL 0x0000018c -+#define VIVS_MMUv2_CONTROL_ENABLE 0x00000001 -+ -+#define VIVS_MMUv2_EXCEPTION_ADDR(i0) (0x00000190 + 0x4*(i0)) -+#define VIVS_MMUv2_EXCEPTION_ADDR__ESIZE 0x00000004 -+#define VIVS_MMUv2_EXCEPTION_ADDR__LEN 0x00000004 -+ -+#define VIVS_MC 0x00000000 -+ -+#define VIVS_MC_MMU_FE_PAGE_TABLE 0x00000400 -+ -+#define VIVS_MC_MMU_TX_PAGE_TABLE 0x00000404 -+ -+#define VIVS_MC_MMU_PE_PAGE_TABLE 0x00000408 -+ -+#define VIVS_MC_MMU_PEZ_PAGE_TABLE 0x0000040c -+ -+#define VIVS_MC_MMU_RA_PAGE_TABLE 0x00000410 -+ -+#define VIVS_MC_DEBUG_MEMORY 0x00000414 -+#define VIVS_MC_DEBUG_MEMORY_SPECIAL_PATCH_GC320 0x00000008 -+#define VIVS_MC_DEBUG_MEMORY_FAST_CLEAR_BYPASS 0x00100000 -+#define VIVS_MC_DEBUG_MEMORY_COMPRESSION_BYPASS 0x00200000 -+ -+#define VIVS_MC_MEMORY_BASE_ADDR_RA 0x00000418 -+ -+#define VIVS_MC_MEMORY_BASE_ADDR_FE 0x0000041c -+ -+#define VIVS_MC_MEMORY_BASE_ADDR_TX 0x00000420 -+ -+#define VIVS_MC_MEMORY_BASE_ADDR_PEZ 0x00000424 -+ -+#define VIVS_MC_MEMORY_BASE_ADDR_PE 0x00000428 -+ -+#define VIVS_MC_MEMORY_TIMING_CONTROL 0x0000042c -+ -+#define VIVS_MC_MEMORY_FLUSH 0x00000430 -+ -+#define VIVS_MC_PROFILE_CYCLE_COUNTER 0x00000438 -+ -+#define VIVS_MC_DEBUG_READ0 0x0000043c -+ -+#define VIVS_MC_DEBUG_READ1 0x00000440 -+ -+#define VIVS_MC_DEBUG_WRITE 0x00000444 -+ -+#define VIVS_MC_PROFILE_RA_READ 0x00000448 -+ -+#define VIVS_MC_PROFILE_TX_READ 0x0000044c -+ -+#define VIVS_MC_PROFILE_FE_READ 0x00000450 -+ -+#define VIVS_MC_PROFILE_PE_READ 0x00000454 -+ -+#define VIVS_MC_PROFILE_DE_READ 0x00000458 -+ -+#define VIVS_MC_PROFILE_SH_READ 0x0000045c -+ -+#define VIVS_MC_PROFILE_PA_READ 0x00000460 -+ -+#define VIVS_MC_PROFILE_SE_READ 0x00000464 -+ -+#define VIVS_MC_PROFILE_MC_READ 0x00000468 -+ -+#define VIVS_MC_PROFILE_HI_READ 0x0000046c -+ -+#define VIVS_MC_PROFILE_CONFIG0 0x00000470 -+#define VIVS_MC_PROFILE_CONFIG0_FE__MASK 0x0000000f -+#define VIVS_MC_PROFILE_CONFIG0_FE__SHIFT 0 -+#define VIVS_MC_PROFILE_CONFIG0_FE_RESET 0x0000000f -+#define VIVS_MC_PROFILE_CONFIG0_DE__MASK 0x00000f00 -+#define VIVS_MC_PROFILE_CONFIG0_DE__SHIFT 8 -+#define VIVS_MC_PROFILE_CONFIG0_DE_RESET 0x00000f00 -+#define VIVS_MC_PROFILE_CONFIG0_PE__MASK 0x000f0000 -+#define VIVS_MC_PROFILE_CONFIG0_PE__SHIFT 16 -+#define VIVS_MC_PROFILE_CONFIG0_PE_PIXEL_COUNT_KILLED_BY_COLOR_PIPE 0x00000000 -+#define VIVS_MC_PROFILE_CONFIG0_PE_PIXEL_COUNT_KILLED_BY_DEPTH_PIPE 0x00010000 -+#define VIVS_MC_PROFILE_CONFIG0_PE_PIXEL_COUNT_DRAWN_BY_COLOR_PIPE 0x00020000 -+#define VIVS_MC_PROFILE_CONFIG0_PE_PIXEL_COUNT_DRAWN_BY_DEPTH_PIPE 0x00030000 -+#define VIVS_MC_PROFILE_CONFIG0_PE_PIXELS_RENDERED_2D 0x000b0000 -+#define VIVS_MC_PROFILE_CONFIG0_PE_RESET 0x000f0000 -+#define VIVS_MC_PROFILE_CONFIG0_SH__MASK 0x0f000000 -+#define VIVS_MC_PROFILE_CONFIG0_SH__SHIFT 24 -+#define VIVS_MC_PROFILE_CONFIG0_SH_SHADER_CYCLES 0x04000000 -+#define VIVS_MC_PROFILE_CONFIG0_SH_PS_INST_COUNTER 0x07000000 -+#define VIVS_MC_PROFILE_CONFIG0_SH_RENDERED_PIXEL_COUNTER 0x08000000 -+#define VIVS_MC_PROFILE_CONFIG0_SH_VS_INST_COUNTER 0x09000000 -+#define VIVS_MC_PROFILE_CONFIG0_SH_RENDERED_VERTICE_COUNTER 0x0a000000 -+#define VIVS_MC_PROFILE_CONFIG0_SH_VTX_BRANCH_INST_COUNTER 0x0b000000 -+#define VIVS_MC_PROFILE_CONFIG0_SH_VTX_TEXLD_INST_COUNTER 0x0c000000 -+#define VIVS_MC_PROFILE_CONFIG0_SH_PXL_BRANCH_INST_COUNTER 0x0d000000 -+#define VIVS_MC_PROFILE_CONFIG0_SH_PXL_TEXLD_INST_COUNTER 0x0e000000 -+#define VIVS_MC_PROFILE_CONFIG0_SH_RESET 0x0f000000 -+ -+#define VIVS_MC_PROFILE_CONFIG1 0x00000474 -+#define VIVS_MC_PROFILE_CONFIG1_PA__MASK 0x0000000f -+#define VIVS_MC_PROFILE_CONFIG1_PA__SHIFT 0 -+#define VIVS_MC_PROFILE_CONFIG1_PA_INPUT_VTX_COUNTER 0x00000003 -+#define VIVS_MC_PROFILE_CONFIG1_PA_INPUT_PRIM_COUNTER 0x00000004 -+#define VIVS_MC_PROFILE_CONFIG1_PA_OUTPUT_PRIM_COUNTER 0x00000005 -+#define VIVS_MC_PROFILE_CONFIG1_PA_DEPTH_CLIPPED_COUNTER 0x00000006 -+#define VIVS_MC_PROFILE_CONFIG1_PA_TRIVIAL_REJECTED_COUNTER 0x00000007 -+#define VIVS_MC_PROFILE_CONFIG1_PA_CULLED_COUNTER 0x00000008 -+#define VIVS_MC_PROFILE_CONFIG1_PA_RESET 0x0000000f -+#define VIVS_MC_PROFILE_CONFIG1_SE__MASK 0x00000f00 -+#define VIVS_MC_PROFILE_CONFIG1_SE__SHIFT 8 -+#define VIVS_MC_PROFILE_CONFIG1_SE_CULLED_TRIANGLE_COUNT 0x00000000 -+#define VIVS_MC_PROFILE_CONFIG1_SE_CULLED_LINES_COUNT 0x00000100 -+#define VIVS_MC_PROFILE_CONFIG1_SE_RESET 0x00000f00 -+#define VIVS_MC_PROFILE_CONFIG1_RA__MASK 0x000f0000 -+#define VIVS_MC_PROFILE_CONFIG1_RA__SHIFT 16 -+#define VIVS_MC_PROFILE_CONFIG1_RA_VALID_PIXEL_COUNT 0x00000000 -+#define VIVS_MC_PROFILE_CONFIG1_RA_TOTAL_QUAD_COUNT 0x00010000 -+#define VIVS_MC_PROFILE_CONFIG1_RA_VALID_QUAD_COUNT_AFTER_EARLY_Z 0x00020000 -+#define VIVS_MC_PROFILE_CONFIG1_RA_TOTAL_PRIMITIVE_COUNT 0x00030000 -+#define VIVS_MC_PROFILE_CONFIG1_RA_PIPE_CACHE_MISS_COUNTER 0x00090000 -+#define VIVS_MC_PROFILE_CONFIG1_RA_PREFETCH_CACHE_MISS_COUNTER 0x000a0000 -+#define VIVS_MC_PROFILE_CONFIG1_RA_CULLED_QUAD_COUNT 0x000b0000 -+#define VIVS_MC_PROFILE_CONFIG1_RA_RESET 0x000f0000 -+#define VIVS_MC_PROFILE_CONFIG1_TX__MASK 0x0f000000 -+#define VIVS_MC_PROFILE_CONFIG1_TX__SHIFT 24 -+#define VIVS_MC_PROFILE_CONFIG1_TX_TOTAL_BILINEAR_REQUESTS 0x00000000 -+#define VIVS_MC_PROFILE_CONFIG1_TX_TOTAL_TRILINEAR_REQUESTS 0x01000000 -+#define VIVS_MC_PROFILE_CONFIG1_TX_TOTAL_DISCARDED_TEXTURE_REQUESTS 0x02000000 -+#define VIVS_MC_PROFILE_CONFIG1_TX_TOTAL_TEXTURE_REQUESTS 0x03000000 -+#define VIVS_MC_PROFILE_CONFIG1_TX_UNKNOWN 0x04000000 -+#define VIVS_MC_PROFILE_CONFIG1_TX_MEM_READ_COUNT 0x05000000 -+#define VIVS_MC_PROFILE_CONFIG1_TX_MEM_READ_IN_8B_COUNT 0x06000000 -+#define VIVS_MC_PROFILE_CONFIG1_TX_CACHE_MISS_COUNT 0x07000000 -+#define VIVS_MC_PROFILE_CONFIG1_TX_CACHE_HIT_TEXEL_COUNT 0x08000000 -+#define VIVS_MC_PROFILE_CONFIG1_TX_CACHE_MISS_TEXEL_COUNT 0x09000000 -+#define VIVS_MC_PROFILE_CONFIG1_TX_RESET 0x0f000000 -+ -+#define VIVS_MC_PROFILE_CONFIG2 0x00000478 -+#define VIVS_MC_PROFILE_CONFIG2_MC__MASK 0x0000000f -+#define VIVS_MC_PROFILE_CONFIG2_MC__SHIFT 0 -+#define VIVS_MC_PROFILE_CONFIG2_MC_TOTAL_READ_REQ_8B_FROM_PIPELINE 0x00000001 -+#define VIVS_MC_PROFILE_CONFIG2_MC_TOTAL_READ_REQ_8B_FROM_IP 0x00000002 -+#define VIVS_MC_PROFILE_CONFIG2_MC_TOTAL_WRITE_REQ_8B_FROM_PIPELINE 0x00000003 -+#define VIVS_MC_PROFILE_CONFIG2_MC_RESET 0x0000000f -+#define VIVS_MC_PROFILE_CONFIG2_HI__MASK 0x00000f00 -+#define VIVS_MC_PROFILE_CONFIG2_HI__SHIFT 8 -+#define VIVS_MC_PROFILE_CONFIG2_HI_AXI_CYCLES_READ_REQUEST_STALLED 0x00000000 -+#define VIVS_MC_PROFILE_CONFIG2_HI_AXI_CYCLES_WRITE_REQUEST_STALLED 0x00000100 -+#define VIVS_MC_PROFILE_CONFIG2_HI_AXI_CYCLES_WRITE_DATA_STALLED 0x00000200 -+#define VIVS_MC_PROFILE_CONFIG2_HI_RESET 0x00000f00 -+ -+#define VIVS_MC_PROFILE_CONFIG3 0x0000047c -+ -+#define VIVS_MC_BUS_CONFIG 0x00000480 -+#define VIVS_MC_BUS_CONFIG_FE_BUS_CONFIG__MASK 0x0000000f -+#define VIVS_MC_BUS_CONFIG_FE_BUS_CONFIG__SHIFT 0 -+#define VIVS_MC_BUS_CONFIG_FE_BUS_CONFIG(x) (((x) << VIVS_MC_BUS_CONFIG_FE_BUS_CONFIG__SHIFT) & VIVS_MC_BUS_CONFIG_FE_BUS_CONFIG__MASK) -+#define VIVS_MC_BUS_CONFIG_TX_BUS_CONFIG__MASK 0x000000f0 -+#define VIVS_MC_BUS_CONFIG_TX_BUS_CONFIG__SHIFT 4 -+#define VIVS_MC_BUS_CONFIG_TX_BUS_CONFIG(x) (((x) << VIVS_MC_BUS_CONFIG_TX_BUS_CONFIG__SHIFT) & VIVS_MC_BUS_CONFIG_TX_BUS_CONFIG__MASK) -+ -+#define VIVS_MC_START_COMPOSITION 0x00000554 -+ -+#define VIVS_MC_128B_MERGE 0x00000558 -+ -+ -+#endif /* STATE_HI_XML */ -diff --git a/include/uapi/drm/etnaviv_drm.h b/include/uapi/drm/etnaviv_drm.h -new file mode 100644 -index 0000000..4cc989a ---- /dev/null -+++ b/include/uapi/drm/etnaviv_drm.h -@@ -0,0 +1,222 @@ -+/* -+ * Copyright (C) 2015 Etnaviv Project -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License version 2 as published by -+ * the Free Software Foundation. -+ * -+ * 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 . -+ */ -+ -+#ifndef __ETNAVIV_DRM_H__ -+#define __ETNAVIV_DRM_H__ -+ -+#include "drm.h" -+ -+/* Please note that modifications to all structs defined here are -+ * subject to backwards-compatibility constraints: -+ * 1) Do not use pointers, use __u64 instead for 32 bit / 64 bit -+ * user/kernel compatibility -+ * 2) Keep fields aligned to their size -+ * 3) Because of how drm_ioctl() works, we can add new fields at -+ * the end of an ioctl if some care is taken: drm_ioctl() will -+ * zero out the new fields at the tail of the ioctl, so a zero -+ * value should have a backwards compatible meaning. And for -+ * output params, userspace won't see the newly added output -+ * fields.. so that has to be somehow ok. -+ */ -+ -+/* timeouts are specified in clock-monotonic absolute times (to simplify -+ * restarting interrupted ioctls). The following struct is logically the -+ * same as 'struct timespec' but 32/64b ABI safe. -+ */ -+struct drm_etnaviv_timespec { -+ __s64 tv_sec; /* seconds */ -+ __s64 tv_nsec; /* nanoseconds */ -+}; -+ -+#define ETNAVIV_PARAM_GPU_MODEL 0x01 -+#define ETNAVIV_PARAM_GPU_REVISION 0x02 -+#define ETNAVIV_PARAM_GPU_FEATURES_0 0x03 -+#define ETNAVIV_PARAM_GPU_FEATURES_1 0x04 -+#define ETNAVIV_PARAM_GPU_FEATURES_2 0x05 -+#define ETNAVIV_PARAM_GPU_FEATURES_3 0x06 -+#define ETNAVIV_PARAM_GPU_FEATURES_4 0x07 -+ -+#define ETNAVIV_PARAM_GPU_STREAM_COUNT 0x10 -+#define ETNAVIV_PARAM_GPU_REGISTER_MAX 0x11 -+#define ETNAVIV_PARAM_GPU_THREAD_COUNT 0x12 -+#define ETNAVIV_PARAM_GPU_VERTEX_CACHE_SIZE 0x13 -+#define ETNAVIV_PARAM_GPU_SHADER_CORE_COUNT 0x14 -+#define ETNAVIV_PARAM_GPU_PIXEL_PIPES 0x15 -+#define ETNAVIV_PARAM_GPU_VERTEX_OUTPUT_BUFFER_SIZE 0x16 -+#define ETNAVIV_PARAM_GPU_BUFFER_SIZE 0x17 -+#define ETNAVIV_PARAM_GPU_INSTRUCTION_COUNT 0x18 -+#define ETNAVIV_PARAM_GPU_NUM_CONSTANTS 0x19 -+ -+#define ETNA_MAX_PIPES 4 -+ -+struct drm_etnaviv_param { -+ __u32 pipe; /* in */ -+ __u32 param; /* in, ETNAVIV_PARAM_x */ -+ __u64 value; /* out (get_param) or in (set_param) */ -+}; -+ -+/* -+ * GEM buffers: -+ */ -+ -+#define ETNA_BO_CACHE_MASK 0x000f0000 -+/* cache modes */ -+#define ETNA_BO_CACHED 0x00010000 -+#define ETNA_BO_WC 0x00020000 -+#define ETNA_BO_UNCACHED 0x00040000 -+/* map flags */ -+#define ETNA_BO_FORCE_MMU 0x00100000 -+ -+struct drm_etnaviv_gem_new { -+ __u64 size; /* in */ -+ __u32 flags; /* in, mask of ETNA_BO_x */ -+ __u32 handle; /* out */ -+}; -+ -+struct drm_etnaviv_gem_info { -+ __u32 handle; /* in */ -+ __u32 pad; -+ __u64 offset; /* out, offset to pass to mmap() */ -+}; -+ -+#define ETNA_PREP_READ 0x01 -+#define ETNA_PREP_WRITE 0x02 -+#define ETNA_PREP_NOSYNC 0x04 -+ -+struct drm_etnaviv_gem_cpu_prep { -+ __u32 handle; /* in */ -+ __u32 op; /* in, mask of ETNA_PREP_x */ -+ struct drm_etnaviv_timespec timeout; /* in */ -+}; -+ -+struct drm_etnaviv_gem_cpu_fini { -+ __u32 handle; /* in */ -+ __u32 flags; /* in, placeholder for now, no defined values */ -+}; -+ -+/* -+ * Cmdstream Submission: -+ */ -+ -+/* The value written into the cmdstream is logically: -+ * relocbuf->gpuaddr + reloc_offset -+ * -+ * NOTE that reloc's must be sorted by order of increasing submit_offset, -+ * otherwise EINVAL. -+ */ -+struct drm_etnaviv_gem_submit_reloc { -+ __u32 submit_offset; /* in, offset from submit_bo */ -+ __u32 reloc_idx; /* in, index of reloc_bo buffer */ -+ __u64 reloc_offset; /* in, offset from start of reloc_bo */ -+ __u32 flags; /* in, placeholder for now, no defined values */ -+}; -+ -+/* Each buffer referenced elsewhere in the cmdstream submit (ie. the -+ * cmdstream buffer(s) themselves or reloc entries) has one (and only -+ * one) entry in the submit->bos[] table. -+ * -+ * As a optimization, the current buffer (gpu virtual address) can be -+ * passed back through the 'presumed' field. If on a subsequent reloc, -+ * userspace passes back a 'presumed' address that is still valid, -+ * then patching the cmdstream for this entry is skipped. This can -+ * avoid kernel needing to map/access the cmdstream bo in the common -+ * case. -+ */ -+#define ETNA_SUBMIT_BO_READ 0x0001 -+#define ETNA_SUBMIT_BO_WRITE 0x0002 -+struct drm_etnaviv_gem_submit_bo { -+ __u32 flags; /* in, mask of ETNA_SUBMIT_BO_x */ -+ __u32 handle; /* in, GEM handle */ -+ __u64 presumed; /* in/out, presumed buffer address */ -+}; -+ -+/* Each cmdstream submit consists of a table of buffers involved, and -+ * one or more cmdstream buffers. This allows for conditional execution -+ * (context-restore), and IB buffers needed for per tile/bin draw cmds. -+ */ -+#define ETNA_PIPE_3D 0x00 -+#define ETNA_PIPE_2D 0x01 -+#define ETNA_PIPE_VG 0x02 -+struct drm_etnaviv_gem_submit { -+ __u32 fence; /* out */ -+ __u32 pipe; /* in */ -+ __u32 exec_state; /* in, initial execution state (ETNA_PIPE_x) */ -+ __u32 nr_bos; /* in, number of submit_bo's */ -+ __u32 nr_relocs; /* in, number of submit_reloc's */ -+ __u32 stream_size; /* in, cmdstream size */ -+ __u64 bos; /* in, ptr to array of submit_bo's */ -+ __u64 relocs; /* in, ptr to array of submit_reloc's */ -+ __u64 stream; /* in, ptr to cmdstream */ -+}; -+ -+/* The normal way to synchronize with the GPU is just to CPU_PREP on -+ * a buffer if you need to access it from the CPU (other cmdstream -+ * submission from same or other contexts, PAGE_FLIP ioctl, etc, all -+ * handle the required synchronization under the hood). This ioctl -+ * mainly just exists as a way to implement the gallium pipe_fence -+ * APIs without requiring a dummy bo to synchronize on. -+ */ -+#define ETNA_WAIT_NONBLOCK 0x01 -+struct drm_etnaviv_wait_fence { -+ __u32 pipe; /* in */ -+ __u32 fence; /* in */ -+ __u32 flags; /* in, mask of ETNA_WAIT_x */ -+ __u32 pad; -+ struct drm_etnaviv_timespec timeout; /* in */ -+}; -+ -+#define ETNA_USERPTR_READ 0x01 -+#define ETNA_USERPTR_WRITE 0x02 -+struct drm_etnaviv_gem_userptr { -+ __u64 user_ptr; /* in, page aligned user pointer */ -+ __u64 user_size; /* in, page aligned user size */ -+ __u32 flags; /* in, flags */ -+ __u32 handle; /* out, non-zero handle */ -+}; -+ -+struct drm_etnaviv_gem_wait { -+ __u32 pipe; /* in */ -+ __u32 handle; /* in, bo to be waited for */ -+ __u32 flags; /* in, mask of ETNA_WAIT_x */ -+ __u32 pad; -+ struct drm_etnaviv_timespec timeout; /* in */ -+}; -+ -+#define DRM_ETNAVIV_GET_PARAM 0x00 -+/* placeholder: -+#define DRM_ETNAVIV_SET_PARAM 0x01 -+ */ -+#define DRM_ETNAVIV_GEM_NEW 0x02 -+#define DRM_ETNAVIV_GEM_INFO 0x03 -+#define DRM_ETNAVIV_GEM_CPU_PREP 0x04 -+#define DRM_ETNAVIV_GEM_CPU_FINI 0x05 -+#define DRM_ETNAVIV_GEM_SUBMIT 0x06 -+#define DRM_ETNAVIV_WAIT_FENCE 0x07 -+#define DRM_ETNAVIV_GEM_USERPTR 0x08 -+#define DRM_ETNAVIV_GEM_WAIT 0x09 -+#define DRM_ETNAVIV_NUM_IOCTLS 0x0a -+ -+#define DRM_IOCTL_ETNAVIV_GET_PARAM DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_GET_PARAM, struct drm_etnaviv_param) -+#define DRM_IOCTL_ETNAVIV_GEM_NEW DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_NEW, struct drm_etnaviv_gem_new) -+#define DRM_IOCTL_ETNAVIV_GEM_INFO DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_INFO, struct drm_etnaviv_gem_info) -+#define DRM_IOCTL_ETNAVIV_GEM_CPU_PREP DRM_IOW(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_CPU_PREP, struct drm_etnaviv_gem_cpu_prep) -+#define DRM_IOCTL_ETNAVIV_GEM_CPU_FINI DRM_IOW(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_CPU_FINI, struct drm_etnaviv_gem_cpu_fini) -+#define DRM_IOCTL_ETNAVIV_GEM_SUBMIT DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_SUBMIT, struct drm_etnaviv_gem_submit) -+#define DRM_IOCTL_ETNAVIV_WAIT_FENCE DRM_IOW(DRM_COMMAND_BASE + DRM_ETNAVIV_WAIT_FENCE, struct drm_etnaviv_wait_fence) -+#define DRM_IOCTL_ETNAVIV_GEM_USERPTR DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_USERPTR, struct drm_etnaviv_gem_userptr) -+#define DRM_IOCTL_ETNAVIV_GEM_WAIT DRM_IOW(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_WAIT, struct drm_etnaviv_gem_wait) -+ -+#endif /* __ETNAVIV_DRM_H__ */ --- -2.6.4 - diff --git a/kernel/kernel/files/patches/mageia/gpu-drm-0004-MAINTAINERS-add-maintainer-and-reviewers-for-the-etn.patch b/kernel/kernel/files/patches/mageia/gpu-drm-0004-MAINTAINERS-add-maintainer-and-reviewers-for-the-etn.patch deleted file mode 100644 index b16230df..00000000 --- a/kernel/kernel/files/patches/mageia/gpu-drm-0004-MAINTAINERS-add-maintainer-and-reviewers-for-the-etn.patch +++ /dev/null @@ -1,36 +0,0 @@ -From 8bb0bce92ec9330b0ea931df90f719fb5c4a5224 Mon Sep 17 00:00:00 2001 -From: Lucas Stach -Date: Thu, 3 Dec 2015 17:12:07 +0100 -Subject: [PATCH 4/4] MAINTAINERS: add maintainer and reviewers for the etnaviv - DRM driver - -Signed-off-by: Lucas Stach -Acked-by: Russell King -Acked-by: Christian Gmeiner ---- - MAINTAINERS | 9 +++++++++ - 1 file changed, 9 insertions(+) - -diff --git a/MAINTAINERS b/MAINTAINERS -index e9caa4b..9990a3b 100644 ---- a/MAINTAINERS -+++ b/MAINTAINERS -@@ -3741,6 +3741,15 @@ S: Maintained - F: drivers/gpu/drm/sti - F: Documentation/devicetree/bindings/display/st,stih4xx.txt - -+DRM DRIVERS FOR VIVANTE GPU IP -+M: Lucas Stach -+R: Russell King -+R: Christian Gmeiner -+L: dri-devel@lists.freedesktop.org -+S: Maintained -+F: drivers/gpu/drm/etnaviv -+F: Documentation/devicetree/bindings/display/etnaviv -+ - DSBR100 USB FM RADIO DRIVER - M: Alexey Klimov - L: linux-media@vger.kernel.org --- -2.6.4 - diff --git a/kernel/kernel/files/patches/mageia/gpu-drm-0005-drm-etnaviv-unlock-on-error-in-etnaviv_gem_get_iova.patch b/kernel/kernel/files/patches/mageia/gpu-drm-0005-drm-etnaviv-unlock-on-error-in-etnaviv_gem_get_iova.patch deleted file mode 100644 index de12f1fd..00000000 --- a/kernel/kernel/files/patches/mageia/gpu-drm-0005-drm-etnaviv-unlock-on-error-in-etnaviv_gem_get_iova.patch +++ /dev/null @@ -1,34 +0,0 @@ -From ed94add00e290e675c36cef6767d7d1f51a02f28 Mon Sep 17 00:00:00 2001 -From: Dan Carpenter -Date: Mon, 4 Jan 2016 16:10:24 +0300 -Subject: [PATCH 1/2] drm/etnaviv: unlock on error in etnaviv_gem_get_iova() - -We have to drop a lock before returning -ENOMEM here. - -Fixes: a8c21a5451d8 ('drm/etnaviv: add initial etnaviv DRM driver') -Signed-off-by: Dan Carpenter -Acked-by: Russell King ---- - drivers/gpu/drm/etnaviv/etnaviv_gem.c | 6 ++++-- - 1 file changed, 4 insertions(+), 2 deletions(-) - -diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.c b/drivers/gpu/drm/etnaviv/etnaviv_gem.c -index 8d6f859..9f77c3b 100644 ---- a/drivers/gpu/drm/etnaviv/etnaviv_gem.c -+++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.c -@@ -305,8 +305,10 @@ int etnaviv_gem_get_iova(struct etnaviv_gpu *gpu, - mapping = etnaviv_gem_get_vram_mapping(etnaviv_obj, NULL); - if (!mapping) { - mapping = kzalloc(sizeof(*mapping), GFP_KERNEL); -- if (!mapping) -- return -ENOMEM; -+ if (!mapping) { -+ ret = -ENOMEM; -+ goto out; -+ } - - INIT_LIST_HEAD(&mapping->scan_node); - mapping->object = etnaviv_obj; --- -2.7.0 - diff --git a/kernel/kernel/files/patches/mageia/gpu-drm-0006-drm-etnaviv-fix-workaround-for-GC500.patch b/kernel/kernel/files/patches/mageia/gpu-drm-0006-drm-etnaviv-fix-workaround-for-GC500.patch deleted file mode 100644 index ae189b54..00000000 --- a/kernel/kernel/files/patches/mageia/gpu-drm-0006-drm-etnaviv-fix-workaround-for-GC500.patch +++ /dev/null @@ -1,38 +0,0 @@ -From c33246d793b5bb9d8be7c67918136c310185c23d Mon Sep 17 00:00:00 2001 -From: Lucas Stach -Date: Wed, 6 Jan 2016 14:36:40 +0100 -Subject: [PATCH 2/2] drm/etnaviv: fix workaround for GC500 - -The hardware description macros define the mask and shifts the wrong -way around for the intended use, leading to the condition never being -true and the chip revision ending up with the wrong value. - -Reported-by: Dan Carpenter -Signed-off-by: Lucas Stach -Acked-by: Christian Gmeiner ---- - drivers/gpu/drm/etnaviv/etnaviv_gpu.c | 7 +++++-- - 1 file changed, 5 insertions(+), 2 deletions(-) - -diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c -index d39093d..056a72e 100644 ---- a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c -+++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c -@@ -251,9 +251,12 @@ static void etnaviv_hw_identify(struct etnaviv_gpu *gpu) - chipIdentity = gpu_read(gpu, VIVS_HI_CHIP_IDENTITY); - - /* Special case for older graphic cores. */ -- if (VIVS_HI_CHIP_IDENTITY_FAMILY(chipIdentity) == 0x01) { -+ if (((chipIdentity & VIVS_HI_CHIP_IDENTITY_FAMILY__MASK) -+ >> VIVS_HI_CHIP_IDENTITY_FAMILY__SHIFT) == 0x01) { - gpu->identity.model = 0x500; /* gc500 */ -- gpu->identity.revision = VIVS_HI_CHIP_IDENTITY_REVISION(chipIdentity); -+ gpu->identity.revision = -+ (chipIdentity & VIVS_HI_CHIP_IDENTITY_REVISION__MASK) -+ >> VIVS_HI_CHIP_IDENTITY_REVISION__SHIFT; - } else { - - gpu->identity.model = gpu_read(gpu, VIVS_HI_CHIP_MODEL); --- -2.7.0 - diff --git a/kernel/kernel/files/patches/mageia/gpu-drm-0101-drm-Create-a-driver-hook-for-allocating-GEM-object-s.patch b/kernel/kernel/files/patches/mageia/gpu-drm-0101-drm-Create-a-driver-hook-for-allocating-GEM-object-s.patch deleted file mode 100644 index 2ccda340..00000000 --- a/kernel/kernel/files/patches/mageia/gpu-drm-0101-drm-Create-a-driver-hook-for-allocating-GEM-object-s.patch +++ /dev/null @@ -1,62 +0,0 @@ -From 10028c5ab107d3765c7fc282b6c45324d1602155 Mon Sep 17 00:00:00 2001 -From: Eric Anholt -Date: Mon, 30 Nov 2015 10:55:13 -0800 -Subject: [PATCH 1/9] drm: Create a driver hook for allocating GEM object - structs. - -The CMA helpers had no way for a driver to extend the struct with its -own fields. Since the CMA helpers are mostly "Allocate a -drm_gem_cma_object, then fill in a few fields", it's hard to write as -pure helpers without passing in a driver callback for the allocate -step. - -Signed-off-by: Eric Anholt -Reviewed-by: Daniel Vetter ---- - drivers/gpu/drm/drm_gem_cma_helper.c | 10 ++++++---- - include/drm/drmP.h | 7 +++++++ - 2 files changed, 13 insertions(+), 4 deletions(-) - -diff --git a/drivers/gpu/drm/drm_gem_cma_helper.c b/drivers/gpu/drm/drm_gem_cma_helper.c -index e109b49..0f7b00b 100644 ---- a/drivers/gpu/drm/drm_gem_cma_helper.c -+++ b/drivers/gpu/drm/drm_gem_cma_helper.c -@@ -59,11 +59,13 @@ __drm_gem_cma_create(struct drm_device *drm, size_t size) - struct drm_gem_object *gem_obj; - int ret; - -- cma_obj = kzalloc(sizeof(*cma_obj), GFP_KERNEL); -- if (!cma_obj) -+ if (drm->driver->gem_create_object) -+ gem_obj = drm->driver->gem_create_object(drm, size); -+ else -+ gem_obj = kzalloc(sizeof(*cma_obj), GFP_KERNEL); -+ if (!gem_obj) - return ERR_PTR(-ENOMEM); -- -- gem_obj = &cma_obj->base; -+ cma_obj = container_of(gem_obj, struct drm_gem_cma_object, base); - - ret = drm_gem_object_init(drm, gem_obj, size); - if (ret) -diff --git a/include/drm/drmP.h b/include/drm/drmP.h -index 0b921ae..22ff162 100644 ---- a/include/drm/drmP.h -+++ b/include/drm/drmP.h -@@ -580,6 +580,13 @@ struct drm_driver { - int (*gem_open_object) (struct drm_gem_object *, struct drm_file *); - void (*gem_close_object) (struct drm_gem_object *, struct drm_file *); - -+ /** -+ * Hook for allocating the GEM object struct, for use by core -+ * helpers. -+ */ -+ struct drm_gem_object *(*gem_create_object)(struct drm_device *dev, -+ size_t size); -+ - /* prime: */ - /* export handle -> fd (see drm_gem_prime_handle_to_fd() helper) */ - int (*prime_handle_to_fd)(struct drm_device *dev, struct drm_file *file_priv, --- -2.6.4 - diff --git a/kernel/kernel/files/patches/mageia/gpu-drm-0102-drm-vc4-Add-a-BO-cache.patch b/kernel/kernel/files/patches/mageia/gpu-drm-0102-drm-vc4-Add-a-BO-cache.patch deleted file mode 100644 index 8b8ecc8d..00000000 --- a/kernel/kernel/files/patches/mageia/gpu-drm-0102-drm-vc4-Add-a-BO-cache.patch +++ /dev/null @@ -1,512 +0,0 @@ -From c826a6e1064419f78855463cf29ce9e8b9d25bf4 Mon Sep 17 00:00:00 2001 -From: Eric Anholt -Date: Fri, 9 Oct 2015 20:25:07 -0700 -Subject: [PATCH 2/9] drm/vc4: Add a BO cache. - -We need to allocate new BOs in the kernel as part of each frame, but -the CMA allocator is way too slow for that. As an optimization, keep -track of recently-freed BOs and reuse them, with a 1 second timeout to -fully free them back to the system. - -This improves 3D performance by about 15%. - -Signed-off-by: Eric Anholt ---- - drivers/gpu/drm/vc4/vc4_bo.c | 336 +++++++++++++++++++++++++++++++++++++- - drivers/gpu/drm/vc4/vc4_debugfs.c | 1 + - drivers/gpu/drm/vc4/vc4_drv.c | 6 +- - drivers/gpu/drm/vc4/vc4_drv.h | 49 +++++- - 4 files changed, 384 insertions(+), 8 deletions(-) - -diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c -index ab9f510..18faa5b 100644 ---- a/drivers/gpu/drm/vc4/vc4_bo.c -+++ b/drivers/gpu/drm/vc4/vc4_bo.c -@@ -12,19 +12,229 @@ - * access to system memory with no MMU in between. To support it, we - * use the GEM CMA helper functions to allocate contiguous ranges of - * physical memory for our BOs. -+ * -+ * Since the CMA allocator is very slow, we keep a cache of recently -+ * freed BOs around so that the kernel's allocation of objects for 3D -+ * rendering can return quickly. - */ - - #include "vc4_drv.h" - --struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t size) -+static void vc4_bo_stats_dump(struct vc4_dev *vc4) -+{ -+ DRM_INFO("num bos allocated: %d\n", -+ vc4->bo_stats.num_allocated); -+ DRM_INFO("size bos allocated: %dkb\n", -+ vc4->bo_stats.size_allocated / 1024); -+ DRM_INFO("num bos used: %d\n", -+ vc4->bo_stats.num_allocated - vc4->bo_stats.num_cached); -+ DRM_INFO("size bos used: %dkb\n", -+ (vc4->bo_stats.size_allocated - -+ vc4->bo_stats.size_cached) / 1024); -+ DRM_INFO("num bos cached: %d\n", -+ vc4->bo_stats.num_cached); -+ DRM_INFO("size bos cached: %dkb\n", -+ vc4->bo_stats.size_cached / 1024); -+} -+ -+#ifdef CONFIG_DEBUG_FS -+int vc4_bo_stats_debugfs(struct seq_file *m, void *unused) -+{ -+ struct drm_info_node *node = (struct drm_info_node *)m->private; -+ struct drm_device *dev = node->minor->dev; -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ struct vc4_bo_stats stats; -+ -+ /* Take a snapshot of the current stats with the lock held. */ -+ mutex_lock(&vc4->bo_lock); -+ stats = vc4->bo_stats; -+ mutex_unlock(&vc4->bo_lock); -+ -+ seq_printf(m, "num bos allocated: %d\n", -+ stats.num_allocated); -+ seq_printf(m, "size bos allocated: %dkb\n", -+ stats.size_allocated / 1024); -+ seq_printf(m, "num bos used: %d\n", -+ stats.num_allocated - stats.num_cached); -+ seq_printf(m, "size bos used: %dkb\n", -+ (stats.size_allocated - stats.size_cached) / 1024); -+ seq_printf(m, "num bos cached: %d\n", -+ stats.num_cached); -+ seq_printf(m, "size bos cached: %dkb\n", -+ stats.size_cached / 1024); -+ -+ return 0; -+} -+#endif -+ -+static uint32_t bo_page_index(size_t size) -+{ -+ return (size / PAGE_SIZE) - 1; -+} -+ -+/* Must be called with bo_lock held. */ -+static void vc4_bo_destroy(struct vc4_bo *bo) - { -+ struct drm_gem_object *obj = &bo->base.base; -+ struct vc4_dev *vc4 = to_vc4_dev(obj->dev); -+ -+ vc4->bo_stats.num_allocated--; -+ vc4->bo_stats.size_allocated -= obj->size; -+ drm_gem_cma_free_object(obj); -+} -+ -+/* Must be called with bo_lock held. */ -+static void vc4_bo_remove_from_cache(struct vc4_bo *bo) -+{ -+ struct drm_gem_object *obj = &bo->base.base; -+ struct vc4_dev *vc4 = to_vc4_dev(obj->dev); -+ -+ vc4->bo_stats.num_cached--; -+ vc4->bo_stats.size_cached -= obj->size; -+ -+ list_del(&bo->unref_head); -+ list_del(&bo->size_head); -+} -+ -+static struct list_head *vc4_get_cache_list_for_size(struct drm_device *dev, -+ size_t size) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ uint32_t page_index = bo_page_index(size); -+ -+ if (vc4->bo_cache.size_list_size <= page_index) { -+ uint32_t new_size = max(vc4->bo_cache.size_list_size * 2, -+ page_index + 1); -+ struct list_head *new_list; -+ uint32_t i; -+ -+ new_list = kmalloc_array(new_size, sizeof(struct list_head), -+ GFP_KERNEL); -+ if (!new_list) -+ return NULL; -+ -+ /* Rebase the old cached BO lists to their new list -+ * head locations. -+ */ -+ for (i = 0; i < vc4->bo_cache.size_list_size; i++) { -+ struct list_head *old_list = -+ &vc4->bo_cache.size_list[i]; -+ -+ if (list_empty(old_list)) -+ INIT_LIST_HEAD(&new_list[i]); -+ else -+ list_replace(old_list, &new_list[i]); -+ } -+ /* And initialize the brand new BO list heads. */ -+ for (i = vc4->bo_cache.size_list_size; i < new_size; i++) -+ INIT_LIST_HEAD(&new_list[i]); -+ -+ kfree(vc4->bo_cache.size_list); -+ vc4->bo_cache.size_list = new_list; -+ vc4->bo_cache.size_list_size = new_size; -+ } -+ -+ return &vc4->bo_cache.size_list[page_index]; -+} -+ -+void vc4_bo_cache_purge(struct drm_device *dev) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ -+ mutex_lock(&vc4->bo_lock); -+ while (!list_empty(&vc4->bo_cache.time_list)) { -+ struct vc4_bo *bo = list_last_entry(&vc4->bo_cache.time_list, -+ struct vc4_bo, unref_head); -+ vc4_bo_remove_from_cache(bo); -+ vc4_bo_destroy(bo); -+ } -+ mutex_unlock(&vc4->bo_lock); -+} -+ -+static struct vc4_bo *vc4_bo_get_from_cache(struct drm_device *dev, -+ uint32_t size) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ uint32_t page_index = bo_page_index(size); -+ struct vc4_bo *bo = NULL; -+ -+ size = roundup(size, PAGE_SIZE); -+ -+ mutex_lock(&vc4->bo_lock); -+ if (page_index >= vc4->bo_cache.size_list_size) -+ goto out; -+ -+ if (list_empty(&vc4->bo_cache.size_list[page_index])) -+ goto out; -+ -+ bo = list_first_entry(&vc4->bo_cache.size_list[page_index], -+ struct vc4_bo, size_head); -+ vc4_bo_remove_from_cache(bo); -+ kref_init(&bo->base.base.refcount); -+ -+out: -+ mutex_unlock(&vc4->bo_lock); -+ return bo; -+} -+ -+/** -+ * vc4_gem_create_object - Implementation of driver->gem_create_object. -+ * -+ * This lets the CMA helpers allocate object structs for us, and keep -+ * our BO stats correct. -+ */ -+struct drm_gem_object *vc4_create_object(struct drm_device *dev, size_t size) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ struct vc4_bo *bo; -+ -+ bo = kzalloc(sizeof(*bo), GFP_KERNEL); -+ if (!bo) -+ return ERR_PTR(-ENOMEM); -+ -+ mutex_lock(&vc4->bo_lock); -+ vc4->bo_stats.num_allocated++; -+ vc4->bo_stats.size_allocated += size; -+ mutex_unlock(&vc4->bo_lock); -+ -+ return &bo->base.base; -+} -+ -+struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t unaligned_size, -+ bool from_cache) -+{ -+ size_t size = roundup(unaligned_size, PAGE_SIZE); -+ struct vc4_dev *vc4 = to_vc4_dev(dev); - struct drm_gem_cma_object *cma_obj; - -- cma_obj = drm_gem_cma_create(dev, size); -- if (IS_ERR(cma_obj)) -+ if (size == 0) - return NULL; -- else -- return to_vc4_bo(&cma_obj->base); -+ -+ /* First, try to get a vc4_bo from the kernel BO cache. */ -+ if (from_cache) { -+ struct vc4_bo *bo = vc4_bo_get_from_cache(dev, size); -+ -+ if (bo) -+ return bo; -+ } -+ -+ cma_obj = drm_gem_cma_create(dev, size); -+ if (IS_ERR(cma_obj)) { -+ /* -+ * If we've run out of CMA memory, kill the cache of -+ * CMA allocations we've got laying around and try again. -+ */ -+ vc4_bo_cache_purge(dev); -+ -+ cma_obj = drm_gem_cma_create(dev, size); -+ if (IS_ERR(cma_obj)) { -+ DRM_ERROR("Failed to allocate from CMA:\n"); -+ vc4_bo_stats_dump(vc4); -+ return NULL; -+ } -+ } -+ -+ return to_vc4_bo(&cma_obj->base); - } - - int vc4_dumb_create(struct drm_file *file_priv, -@@ -41,7 +251,7 @@ int vc4_dumb_create(struct drm_file *file_priv, - if (args->size < args->pitch * args->height) - args->size = args->pitch * args->height; - -- bo = vc4_bo_create(dev, roundup(args->size, PAGE_SIZE)); -+ bo = vc4_bo_create(dev, args->size, false); - if (!bo) - return -ENOMEM; - -@@ -50,3 +260,117 @@ int vc4_dumb_create(struct drm_file *file_priv, - - return ret; - } -+ -+/* Must be called with bo_lock held. */ -+static void vc4_bo_cache_free_old(struct drm_device *dev) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ unsigned long expire_time = jiffies - msecs_to_jiffies(1000); -+ -+ while (!list_empty(&vc4->bo_cache.time_list)) { -+ struct vc4_bo *bo = list_last_entry(&vc4->bo_cache.time_list, -+ struct vc4_bo, unref_head); -+ if (time_before(expire_time, bo->free_time)) { -+ mod_timer(&vc4->bo_cache.time_timer, -+ round_jiffies_up(jiffies + -+ msecs_to_jiffies(1000))); -+ return; -+ } -+ -+ vc4_bo_remove_from_cache(bo); -+ vc4_bo_destroy(bo); -+ } -+} -+ -+/* Called on the last userspace/kernel unreference of the BO. Returns -+ * it to the BO cache if possible, otherwise frees it. -+ * -+ * Note that this is called with the struct_mutex held. -+ */ -+void vc4_free_object(struct drm_gem_object *gem_bo) -+{ -+ struct drm_device *dev = gem_bo->dev; -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ struct vc4_bo *bo = to_vc4_bo(gem_bo); -+ struct list_head *cache_list; -+ -+ mutex_lock(&vc4->bo_lock); -+ /* If the object references someone else's memory, we can't cache it. -+ */ -+ if (gem_bo->import_attach) { -+ vc4_bo_destroy(bo); -+ goto out; -+ } -+ -+ /* Don't cache if it was publicly named. */ -+ if (gem_bo->name) { -+ vc4_bo_destroy(bo); -+ goto out; -+ } -+ -+ cache_list = vc4_get_cache_list_for_size(dev, gem_bo->size); -+ if (!cache_list) { -+ vc4_bo_destroy(bo); -+ goto out; -+ } -+ -+ bo->free_time = jiffies; -+ list_add(&bo->size_head, cache_list); -+ list_add(&bo->unref_head, &vc4->bo_cache.time_list); -+ -+ vc4->bo_stats.num_cached++; -+ vc4->bo_stats.size_cached += gem_bo->size; -+ -+ vc4_bo_cache_free_old(dev); -+ -+out: -+ mutex_unlock(&vc4->bo_lock); -+} -+ -+static void vc4_bo_cache_time_work(struct work_struct *work) -+{ -+ struct vc4_dev *vc4 = -+ container_of(work, struct vc4_dev, bo_cache.time_work); -+ struct drm_device *dev = vc4->dev; -+ -+ mutex_lock(&vc4->bo_lock); -+ vc4_bo_cache_free_old(dev); -+ mutex_unlock(&vc4->bo_lock); -+} -+ -+static void vc4_bo_cache_time_timer(unsigned long data) -+{ -+ struct drm_device *dev = (struct drm_device *)data; -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ -+ schedule_work(&vc4->bo_cache.time_work); -+} -+ -+void vc4_bo_cache_init(struct drm_device *dev) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ -+ mutex_init(&vc4->bo_lock); -+ -+ INIT_LIST_HEAD(&vc4->bo_cache.time_list); -+ -+ INIT_WORK(&vc4->bo_cache.time_work, vc4_bo_cache_time_work); -+ setup_timer(&vc4->bo_cache.time_timer, -+ vc4_bo_cache_time_timer, -+ (unsigned long)dev); -+} -+ -+void vc4_bo_cache_destroy(struct drm_device *dev) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ -+ del_timer(&vc4->bo_cache.time_timer); -+ cancel_work_sync(&vc4->bo_cache.time_work); -+ -+ vc4_bo_cache_purge(dev); -+ -+ if (vc4->bo_stats.num_allocated) { -+ DRM_ERROR("Destroying BO cache while BOs still allocated:\n"); -+ vc4_bo_stats_dump(vc4); -+ } -+} -diff --git a/drivers/gpu/drm/vc4/vc4_debugfs.c b/drivers/gpu/drm/vc4/vc4_debugfs.c -index 4297b0a5..6bcf96e 100644 ---- a/drivers/gpu/drm/vc4/vc4_debugfs.c -+++ b/drivers/gpu/drm/vc4/vc4_debugfs.c -@@ -16,6 +16,7 @@ - #include "vc4_regs.h" - - static const struct drm_info_list vc4_debugfs_list[] = { -+ {"bo_stats", vc4_bo_stats_debugfs, 0}, - {"hdmi_regs", vc4_hdmi_debugfs_regs, 0}, - {"hvs_regs", vc4_hvs_debugfs_regs, 0}, - {"crtc0_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)0}, -diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c -index 6e73060..da041fa 100644 ---- a/drivers/gpu/drm/vc4/vc4_drv.c -+++ b/drivers/gpu/drm/vc4/vc4_drv.c -@@ -92,7 +92,8 @@ static struct drm_driver vc4_drm_driver = { - .debugfs_cleanup = vc4_debugfs_cleanup, - #endif - -- .gem_free_object = drm_gem_cma_free_object, -+ .gem_create_object = vc4_create_object, -+ .gem_free_object = vc4_free_object, - .gem_vm_ops = &drm_gem_cma_vm_ops, - - .prime_handle_to_fd = drm_gem_prime_handle_to_fd, -@@ -170,6 +171,8 @@ static int vc4_drm_bind(struct device *dev) - - drm_dev_set_unique(drm, dev_name(dev)); - -+ vc4_bo_cache_init(drm); -+ - drm_mode_config_init(drm); - if (ret) - goto unref; -@@ -202,6 +205,7 @@ unbind_all: - component_unbind_all(dev, drm); - unref: - drm_dev_unref(drm); -+ vc4_bo_cache_destroy(drm); - return ret; - } - -diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h -index fd8319f..39a1ff5 100644 ---- a/drivers/gpu/drm/vc4/vc4_drv.h -+++ b/drivers/gpu/drm/vc4/vc4_drv.h -@@ -17,6 +17,37 @@ struct vc4_dev { - struct vc4_crtc *crtc[3]; - - struct drm_fbdev_cma *fbdev; -+ -+ /* The kernel-space BO cache. Tracks buffers that have been -+ * unreferenced by all other users (refcounts of 0!) but not -+ * yet freed, so we can do cheap allocations. -+ */ -+ struct vc4_bo_cache { -+ /* Array of list heads for entries in the BO cache, -+ * based on number of pages, so we can do O(1) lookups -+ * in the cache when allocating. -+ */ -+ struct list_head *size_list; -+ uint32_t size_list_size; -+ -+ /* List of all BOs in the cache, ordered by age, so we -+ * can do O(1) lookups when trying to free old -+ * buffers. -+ */ -+ struct list_head time_list; -+ struct work_struct time_work; -+ struct timer_list time_timer; -+ } bo_cache; -+ -+ struct vc4_bo_stats { -+ u32 num_allocated; -+ u32 size_allocated; -+ u32 num_cached; -+ u32 size_cached; -+ } bo_stats; -+ -+ /* Protects bo_cache and the BO stats. */ -+ struct mutex bo_lock; - }; - - static inline struct vc4_dev * -@@ -27,6 +58,17 @@ to_vc4_dev(struct drm_device *dev) - - struct vc4_bo { - struct drm_gem_cma_object base; -+ -+ /* List entry for the BO's position in either -+ * vc4_exec_info->unref_list or vc4_dev->bo_cache.time_list -+ */ -+ struct list_head unref_head; -+ -+ /* Time in jiffies when the BO was put in vc4->bo_cache. */ -+ unsigned long free_time; -+ -+ /* List entry for the BO's position in vc4_dev->bo_cache.size_list */ -+ struct list_head size_head; - }; - - static inline struct vc4_bo * -@@ -104,13 +146,18 @@ to_vc4_encoder(struct drm_encoder *encoder) - #define wait_for(COND, MS) _wait_for(COND, MS, 1) - - /* vc4_bo.c */ -+struct drm_gem_object *vc4_create_object(struct drm_device *dev, size_t size); - void vc4_free_object(struct drm_gem_object *gem_obj); --struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t size); -+struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t size, -+ bool from_cache); - int vc4_dumb_create(struct drm_file *file_priv, - struct drm_device *dev, - struct drm_mode_create_dumb *args); - struct dma_buf *vc4_prime_export(struct drm_device *dev, - struct drm_gem_object *obj, int flags); -+void vc4_bo_cache_init(struct drm_device *dev); -+void vc4_bo_cache_destroy(struct drm_device *dev); -+int vc4_bo_stats_debugfs(struct seq_file *m, void *arg); - - /* vc4_crtc.c */ - extern struct platform_driver vc4_crtc_driver; --- -2.6.4 - diff --git a/kernel/kernel/files/patches/mageia/gpu-drm-0103-drm-vc4-Add-create-and-map-BO-ioctls.patch b/kernel/kernel/files/patches/mageia/gpu-drm-0103-drm-vc4-Add-create-and-map-BO-ioctls.patch deleted file mode 100644 index 53df377e..00000000 --- a/kernel/kernel/files/patches/mageia/gpu-drm-0103-drm-vc4-Add-create-and-map-BO-ioctls.patch +++ /dev/null @@ -1,204 +0,0 @@ -From d5bc60f6ad05b3c676b057bec662cfafc3ee24dd Mon Sep 17 00:00:00 2001 -From: Eric Anholt -Date: Sun, 18 Jan 2015 09:33:17 +1300 -Subject: [PATCH 3/9] drm/vc4: Add create and map BO ioctls. - -While there exist dumb APIs for creating and mapping BOs, one of the -rules is that drivers doing 3D acceleration have to provide their own -APIs for buffer allocation (besides, the pitch/height parameters of -the dumb alloc don't really make sense for a lot of 3D allocations). - -v2: Use __u32-style types, use "drm.h" instead of . - -Signed-off-by: Eric Anholt ---- - drivers/gpu/drm/vc4/vc4_bo.c | 41 ++++++++++++++++++++++++++ - drivers/gpu/drm/vc4/vc4_drv.c | 3 ++ - drivers/gpu/drm/vc4/vc4_drv.h | 4 +++ - include/uapi/drm/Kbuild | 1 + - include/uapi/drm/vc4_drm.h | 68 +++++++++++++++++++++++++++++++++++++++++++ - 5 files changed, 117 insertions(+) - create mode 100644 include/uapi/drm/vc4_drm.h - -diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c -index 18faa5b..06cba26 100644 ---- a/drivers/gpu/drm/vc4/vc4_bo.c -+++ b/drivers/gpu/drm/vc4/vc4_bo.c -@@ -19,6 +19,7 @@ - */ - - #include "vc4_drv.h" -+#include "uapi/drm/vc4_drm.h" - - static void vc4_bo_stats_dump(struct vc4_dev *vc4) - { -@@ -346,6 +347,46 @@ static void vc4_bo_cache_time_timer(unsigned long data) - schedule_work(&vc4->bo_cache.time_work); - } - -+int vc4_create_bo_ioctl(struct drm_device *dev, void *data, -+ struct drm_file *file_priv) -+{ -+ struct drm_vc4_create_bo *args = data; -+ struct vc4_bo *bo = NULL; -+ int ret; -+ -+ /* -+ * We can't allocate from the BO cache, because the BOs don't -+ * get zeroed, and that might leak data between users. -+ */ -+ bo = vc4_bo_create(dev, args->size, false); -+ if (!bo) -+ return -ENOMEM; -+ -+ ret = drm_gem_handle_create(file_priv, &bo->base.base, &args->handle); -+ drm_gem_object_unreference_unlocked(&bo->base.base); -+ -+ return ret; -+} -+ -+int vc4_mmap_bo_ioctl(struct drm_device *dev, void *data, -+ struct drm_file *file_priv) -+{ -+ struct drm_vc4_mmap_bo *args = data; -+ struct drm_gem_object *gem_obj; -+ -+ gem_obj = drm_gem_object_lookup(dev, file_priv, args->handle); -+ if (!gem_obj) { -+ DRM_ERROR("Failed to look up GEM BO %d\n", args->handle); -+ return -EINVAL; -+ } -+ -+ /* The mmap offset was set up at BO allocation time. */ -+ args->offset = drm_vma_node_offset_addr(&gem_obj->vma_node); -+ -+ drm_gem_object_unreference_unlocked(gem_obj); -+ return 0; -+} -+ - void vc4_bo_cache_init(struct drm_device *dev) - { - struct vc4_dev *vc4 = to_vc4_dev(dev); -diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c -index da041fa..5fa4688 100644 ---- a/drivers/gpu/drm/vc4/vc4_drv.c -+++ b/drivers/gpu/drm/vc4/vc4_drv.c -@@ -16,6 +16,7 @@ - #include - #include "drm_fb_cma_helper.h" - -+#include "uapi/drm/vc4_drm.h" - #include "vc4_drv.h" - #include "vc4_regs.h" - -@@ -73,6 +74,8 @@ static const struct file_operations vc4_drm_fops = { - }; - - static const struct drm_ioctl_desc vc4_drm_ioctls[] = { -+ DRM_IOCTL_DEF_DRV(VC4_CREATE_BO, vc4_create_bo_ioctl, 0), -+ DRM_IOCTL_DEF_DRV(VC4_MMAP_BO, vc4_mmap_bo_ioctl, 0), - }; - - static struct drm_driver vc4_drm_driver = { -diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h -index 39a1ff5..fddb0a0 100644 ---- a/drivers/gpu/drm/vc4/vc4_drv.h -+++ b/drivers/gpu/drm/vc4/vc4_drv.h -@@ -155,6 +155,10 @@ int vc4_dumb_create(struct drm_file *file_priv, - struct drm_mode_create_dumb *args); - struct dma_buf *vc4_prime_export(struct drm_device *dev, - struct drm_gem_object *obj, int flags); -+int vc4_create_bo_ioctl(struct drm_device *dev, void *data, -+ struct drm_file *file_priv); -+int vc4_mmap_bo_ioctl(struct drm_device *dev, void *data, -+ struct drm_file *file_priv); - void vc4_bo_cache_init(struct drm_device *dev); - void vc4_bo_cache_destroy(struct drm_device *dev); - int vc4_bo_stats_debugfs(struct seq_file *m, void *arg); -diff --git a/include/uapi/drm/Kbuild b/include/uapi/drm/Kbuild -index 38d4370..974fcd5 100644 ---- a/include/uapi/drm/Kbuild -+++ b/include/uapi/drm/Kbuild -@@ -17,4 +17,5 @@ header-y += tegra_drm.h - header-y += via_drm.h - header-y += vmwgfx_drm.h - header-y += msm_drm.h -+header-y += vc4_drm.h - header-y += virtgpu_drm.h -diff --git a/include/uapi/drm/vc4_drm.h b/include/uapi/drm/vc4_drm.h -new file mode 100644 -index 0000000..219d34c ---- /dev/null -+++ b/include/uapi/drm/vc4_drm.h -@@ -0,0 +1,68 @@ -+/* -+ * Copyright © 2014-2015 Broadcom -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice (including the next -+ * paragraph) shall be included in all copies or substantial portions of the -+ * Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -+ * IN THE SOFTWARE. -+ */ -+ -+#ifndef _UAPI_VC4_DRM_H_ -+#define _UAPI_VC4_DRM_H_ -+ -+#include "drm.h" -+ -+#define DRM_VC4_CREATE_BO 0x03 -+#define DRM_VC4_MMAP_BO 0x04 -+ -+#define DRM_IOCTL_VC4_CREATE_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_CREATE_BO, struct drm_vc4_create_bo) -+#define DRM_IOCTL_VC4_MMAP_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_MMAP_BO, struct drm_vc4_mmap_bo) -+ -+/** -+ * struct drm_vc4_create_bo - ioctl argument for creating VC4 BOs. -+ * -+ * There are currently no values for the flags argument, but it may be -+ * used in a future extension. -+ */ -+struct drm_vc4_create_bo { -+ __u32 size; -+ __u32 flags; -+ /** Returned GEM handle for the BO. */ -+ __u32 handle; -+ __u32 pad; -+}; -+ -+/** -+ * struct drm_vc4_mmap_bo - ioctl argument for mapping VC4 BOs. -+ * -+ * This doesn't actually perform an mmap. Instead, it returns the -+ * offset you need to use in an mmap on the DRM device node. This -+ * means that tools like valgrind end up knowing about the mapped -+ * memory. -+ * -+ * There are currently no values for the flags argument, but it may be -+ * used in a future extension. -+ */ -+struct drm_vc4_mmap_bo { -+ /** Handle for the object being mapped. */ -+ __u32 handle; -+ __u32 flags; -+ /** offset into the drm node to use for subsequent mmap call. */ -+ __u64 offset; -+}; -+ -+#endif /* _UAPI_VC4_DRM_H_ */ --- -2.6.4 - diff --git a/kernel/kernel/files/patches/mageia/gpu-drm-0104-drm-vc4-Add-an-API-for-creating-GPU-shaders-in-GEM-B.patch b/kernel/kernel/files/patches/mageia/gpu-drm-0104-drm-vc4-Add-an-API-for-creating-GPU-shaders-in-GEM-B.patch deleted file mode 100644 index f06c0a10..00000000 --- a/kernel/kernel/files/patches/mageia/gpu-drm-0104-drm-vc4-Add-an-API-for-creating-GPU-shaders-in-GEM-B.patch +++ /dev/null @@ -1,1168 +0,0 @@ -From 463873d5701427f2964a0b4b72c45f1f14b6df87 Mon Sep 17 00:00:00 2001 -From: Eric Anholt -Date: Mon, 30 Nov 2015 11:41:40 -0800 -Subject: [PATCH 4/9] drm/vc4: Add an API for creating GPU shaders in GEM BOs. - -Since we have no MMU, the kernel needs to validate that the submitted -shader code won't make any accesses to memory that the user doesn't -control, which involves banning some operations (general purpose DMA -writes), and tracking where we need to write out pointers for other -operations (texture sampling). Once it's validated, we return a GEM -BO containing the shader, which doesn't allow mapping for write or -exporting to other subsystems. - -v2: Use __u32-style types. - -Signed-off-by: Eric Anholt ---- - drivers/gpu/drm/vc4/Makefile | 3 +- - drivers/gpu/drm/vc4/vc4_bo.c | 140 ++++++++ - drivers/gpu/drm/vc4/vc4_drv.c | 9 +- - drivers/gpu/drm/vc4/vc4_drv.h | 50 +++ - drivers/gpu/drm/vc4/vc4_qpu_defines.h | 264 +++++++++++++++ - drivers/gpu/drm/vc4/vc4_validate_shaders.c | 513 +++++++++++++++++++++++++++++ - include/uapi/drm/vc4_drm.h | 25 ++ - 7 files changed, 999 insertions(+), 5 deletions(-) - create mode 100644 drivers/gpu/drm/vc4/vc4_qpu_defines.h - create mode 100644 drivers/gpu/drm/vc4/vc4_validate_shaders.c - -diff --git a/drivers/gpu/drm/vc4/Makefile b/drivers/gpu/drm/vc4/Makefile -index 32b4f9c..eb776a6 100644 ---- a/drivers/gpu/drm/vc4/Makefile -+++ b/drivers/gpu/drm/vc4/Makefile -@@ -10,7 +10,8 @@ vc4-y := \ - vc4_kms.o \ - vc4_hdmi.o \ - vc4_hvs.o \ -- vc4_plane.o -+ vc4_plane.o \ -+ vc4_validate_shaders.o - - vc4-$(CONFIG_DEBUG_FS) += vc4_debugfs.o - -diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c -index 06cba26..18dfe3e 100644 ---- a/drivers/gpu/drm/vc4/vc4_bo.c -+++ b/drivers/gpu/drm/vc4/vc4_bo.c -@@ -79,6 +79,12 @@ static void vc4_bo_destroy(struct vc4_bo *bo) - struct drm_gem_object *obj = &bo->base.base; - struct vc4_dev *vc4 = to_vc4_dev(obj->dev); - -+ if (bo->validated_shader) { -+ kfree(bo->validated_shader->texture_samples); -+ kfree(bo->validated_shader); -+ bo->validated_shader = NULL; -+ } -+ - vc4->bo_stats.num_allocated--; - vc4->bo_stats.size_allocated -= obj->size; - drm_gem_cma_free_object(obj); -@@ -315,6 +321,12 @@ void vc4_free_object(struct drm_gem_object *gem_bo) - goto out; - } - -+ if (bo->validated_shader) { -+ kfree(bo->validated_shader->texture_samples); -+ kfree(bo->validated_shader); -+ bo->validated_shader = NULL; -+ } -+ - bo->free_time = jiffies; - list_add(&bo->size_head, cache_list); - list_add(&bo->unref_head, &vc4->bo_cache.time_list); -@@ -347,6 +359,78 @@ static void vc4_bo_cache_time_timer(unsigned long data) - schedule_work(&vc4->bo_cache.time_work); - } - -+struct dma_buf * -+vc4_prime_export(struct drm_device *dev, struct drm_gem_object *obj, int flags) -+{ -+ struct vc4_bo *bo = to_vc4_bo(obj); -+ -+ if (bo->validated_shader) { -+ DRM_ERROR("Attempting to export shader BO\n"); -+ return ERR_PTR(-EINVAL); -+ } -+ -+ return drm_gem_prime_export(dev, obj, flags); -+} -+ -+int vc4_mmap(struct file *filp, struct vm_area_struct *vma) -+{ -+ struct drm_gem_object *gem_obj; -+ struct vc4_bo *bo; -+ int ret; -+ -+ ret = drm_gem_mmap(filp, vma); -+ if (ret) -+ return ret; -+ -+ gem_obj = vma->vm_private_data; -+ bo = to_vc4_bo(gem_obj); -+ -+ if (bo->validated_shader && (vma->vm_flags & VM_WRITE)) { -+ DRM_ERROR("mmaping of shader BOs for writing not allowed.\n"); -+ return -EINVAL; -+ } -+ -+ /* -+ * Clear the VM_PFNMAP flag that was set by drm_gem_mmap(), and set the -+ * vm_pgoff (used as a fake buffer offset by DRM) to 0 as we want to map -+ * the whole buffer. -+ */ -+ vma->vm_flags &= ~VM_PFNMAP; -+ vma->vm_pgoff = 0; -+ -+ ret = dma_mmap_writecombine(bo->base.base.dev->dev, vma, -+ bo->base.vaddr, bo->base.paddr, -+ vma->vm_end - vma->vm_start); -+ if (ret) -+ drm_gem_vm_close(vma); -+ -+ return ret; -+} -+ -+int vc4_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma) -+{ -+ struct vc4_bo *bo = to_vc4_bo(obj); -+ -+ if (bo->validated_shader && (vma->vm_flags & VM_WRITE)) { -+ DRM_ERROR("mmaping of shader BOs for writing not allowed.\n"); -+ return -EINVAL; -+ } -+ -+ return drm_gem_cma_prime_mmap(obj, vma); -+} -+ -+void *vc4_prime_vmap(struct drm_gem_object *obj) -+{ -+ struct vc4_bo *bo = to_vc4_bo(obj); -+ -+ if (bo->validated_shader) { -+ DRM_ERROR("mmaping of shader BOs not allowed.\n"); -+ return ERR_PTR(-EINVAL); -+ } -+ -+ return drm_gem_cma_prime_vmap(obj); -+} -+ - int vc4_create_bo_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) - { -@@ -387,6 +471,62 @@ int vc4_mmap_bo_ioctl(struct drm_device *dev, void *data, - return 0; - } - -+int -+vc4_create_shader_bo_ioctl(struct drm_device *dev, void *data, -+ struct drm_file *file_priv) -+{ -+ struct drm_vc4_create_shader_bo *args = data; -+ struct vc4_bo *bo = NULL; -+ int ret; -+ -+ if (args->size == 0) -+ return -EINVAL; -+ -+ if (args->size % sizeof(u64) != 0) -+ return -EINVAL; -+ -+ if (args->flags != 0) { -+ DRM_INFO("Unknown flags set: 0x%08x\n", args->flags); -+ return -EINVAL; -+ } -+ -+ if (args->pad != 0) { -+ DRM_INFO("Pad set: 0x%08x\n", args->pad); -+ return -EINVAL; -+ } -+ -+ bo = vc4_bo_create(dev, args->size, true); -+ if (!bo) -+ return -ENOMEM; -+ -+ ret = copy_from_user(bo->base.vaddr, -+ (void __user *)(uintptr_t)args->data, -+ args->size); -+ if (ret != 0) -+ goto fail; -+ /* Clear the rest of the memory from allocating from the BO -+ * cache. -+ */ -+ memset(bo->base.vaddr + args->size, 0, -+ bo->base.base.size - args->size); -+ -+ bo->validated_shader = vc4_validate_shader(&bo->base); -+ if (!bo->validated_shader) { -+ ret = -EINVAL; -+ goto fail; -+ } -+ -+ /* We have to create the handle after validation, to avoid -+ * races for users to do doing things like mmap the shader BO. -+ */ -+ ret = drm_gem_handle_create(file_priv, &bo->base.base, &args->handle); -+ -+ fail: -+ drm_gem_object_unreference_unlocked(&bo->base.base); -+ -+ return ret; -+} -+ - void vc4_bo_cache_init(struct drm_device *dev) - { - struct vc4_dev *vc4 = to_vc4_dev(dev); -diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c -index 5fa4688..da4be9c 100644 ---- a/drivers/gpu/drm/vc4/vc4_drv.c -+++ b/drivers/gpu/drm/vc4/vc4_drv.c -@@ -64,7 +64,7 @@ static const struct file_operations vc4_drm_fops = { - .open = drm_open, - .release = drm_release, - .unlocked_ioctl = drm_ioctl, -- .mmap = drm_gem_cma_mmap, -+ .mmap = vc4_mmap, - .poll = drm_poll, - .read = drm_read, - #ifdef CONFIG_COMPAT -@@ -76,6 +76,7 @@ static const struct file_operations vc4_drm_fops = { - static const struct drm_ioctl_desc vc4_drm_ioctls[] = { - DRM_IOCTL_DEF_DRV(VC4_CREATE_BO, vc4_create_bo_ioctl, 0), - DRM_IOCTL_DEF_DRV(VC4_MMAP_BO, vc4_mmap_bo_ioctl, 0), -+ DRM_IOCTL_DEF_DRV(VC4_CREATE_SHADER_BO, vc4_create_shader_bo_ioctl, 0), - }; - - static struct drm_driver vc4_drm_driver = { -@@ -102,12 +103,12 @@ static struct drm_driver vc4_drm_driver = { - .prime_handle_to_fd = drm_gem_prime_handle_to_fd, - .prime_fd_to_handle = drm_gem_prime_fd_to_handle, - .gem_prime_import = drm_gem_prime_import, -- .gem_prime_export = drm_gem_prime_export, -+ .gem_prime_export = vc4_prime_export, - .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, - .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, -- .gem_prime_vmap = drm_gem_cma_prime_vmap, -+ .gem_prime_vmap = vc4_prime_vmap, - .gem_prime_vunmap = drm_gem_cma_prime_vunmap, -- .gem_prime_mmap = drm_gem_cma_prime_mmap, -+ .gem_prime_mmap = vc4_prime_mmap, - - .dumb_create = vc4_dumb_create, - .dumb_map_offset = drm_gem_cma_dumb_map_offset, -diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h -index fddb0a0..bd77d55 100644 ---- a/drivers/gpu/drm/vc4/vc4_drv.h -+++ b/drivers/gpu/drm/vc4/vc4_drv.h -@@ -69,6 +69,11 @@ struct vc4_bo { - - /* List entry for the BO's position in vc4_dev->bo_cache.size_list */ - struct list_head size_head; -+ -+ /* Struct for shader validation state, if created by -+ * DRM_IOCTL_VC4_CREATE_SHADER_BO. -+ */ -+ struct vc4_validated_shader_info *validated_shader; - }; - - static inline struct vc4_bo * -@@ -118,6 +123,42 @@ to_vc4_encoder(struct drm_encoder *encoder) - #define HVS_WRITE(offset, val) writel(val, vc4->hvs->regs + offset) - - /** -+ * struct vc4_texture_sample_info - saves the offsets into the UBO for texture -+ * setup parameters. -+ * -+ * This will be used at draw time to relocate the reference to the texture -+ * contents in p0, and validate that the offset combined with -+ * width/height/stride/etc. from p1 and p2/p3 doesn't sample outside the BO. -+ * Note that the hardware treats unprovided config parameters as 0, so not all -+ * of them need to be set up for every texure sample, and we'll store ~0 as -+ * the offset to mark the unused ones. -+ * -+ * See the VC4 3D architecture guide page 41 ("Texture and Memory Lookup Unit -+ * Setup") for definitions of the texture parameters. -+ */ -+struct vc4_texture_sample_info { -+ bool is_direct; -+ uint32_t p_offset[4]; -+}; -+ -+/** -+ * struct vc4_validated_shader_info - information about validated shaders that -+ * needs to be used from command list validation. -+ * -+ * For a given shader, each time a shader state record references it, we need -+ * to verify that the shader doesn't read more uniforms than the shader state -+ * record's uniform BO pointer can provide, and we need to apply relocations -+ * and validate the shader state record's uniforms that define the texture -+ * samples. -+ */ -+struct vc4_validated_shader_info { -+ uint32_t uniforms_size; -+ uint32_t uniforms_src_size; -+ uint32_t num_texture_samples; -+ struct vc4_texture_sample_info *texture_samples; -+}; -+ -+/** - * _wait_for - magic (register) wait macro - * - * Does the right thing for modeset paths when run under kdgb or similar atomic -@@ -157,8 +198,13 @@ struct dma_buf *vc4_prime_export(struct drm_device *dev, - struct drm_gem_object *obj, int flags); - int vc4_create_bo_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); -+int vc4_create_shader_bo_ioctl(struct drm_device *dev, void *data, -+ struct drm_file *file_priv); - int vc4_mmap_bo_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); -+int vc4_mmap(struct file *filp, struct vm_area_struct *vma); -+int vc4_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma); -+void *vc4_prime_vmap(struct drm_gem_object *obj); - void vc4_bo_cache_init(struct drm_device *dev); - void vc4_bo_cache_destroy(struct drm_device *dev); - int vc4_bo_stats_debugfs(struct seq_file *m, void *arg); -@@ -194,3 +240,7 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev, - enum drm_plane_type type); - u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist); - u32 vc4_plane_dlist_size(struct drm_plane_state *state); -+ -+/* vc4_validate_shader.c */ -+struct vc4_validated_shader_info * -+vc4_validate_shader(struct drm_gem_cma_object *shader_obj); -diff --git a/drivers/gpu/drm/vc4/vc4_qpu_defines.h b/drivers/gpu/drm/vc4/vc4_qpu_defines.h -new file mode 100644 -index 0000000..d5c2f3c ---- /dev/null -+++ b/drivers/gpu/drm/vc4/vc4_qpu_defines.h -@@ -0,0 +1,264 @@ -+/* -+ * Copyright © 2014 Broadcom -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice (including the next -+ * paragraph) shall be included in all copies or substantial portions of the -+ * Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -+ * IN THE SOFTWARE. -+ */ -+ -+#ifndef VC4_QPU_DEFINES_H -+#define VC4_QPU_DEFINES_H -+ -+enum qpu_op_add { -+ QPU_A_NOP, -+ QPU_A_FADD, -+ QPU_A_FSUB, -+ QPU_A_FMIN, -+ QPU_A_FMAX, -+ QPU_A_FMINABS, -+ QPU_A_FMAXABS, -+ QPU_A_FTOI, -+ QPU_A_ITOF, -+ QPU_A_ADD = 12, -+ QPU_A_SUB, -+ QPU_A_SHR, -+ QPU_A_ASR, -+ QPU_A_ROR, -+ QPU_A_SHL, -+ QPU_A_MIN, -+ QPU_A_MAX, -+ QPU_A_AND, -+ QPU_A_OR, -+ QPU_A_XOR, -+ QPU_A_NOT, -+ QPU_A_CLZ, -+ QPU_A_V8ADDS = 30, -+ QPU_A_V8SUBS = 31, -+}; -+ -+enum qpu_op_mul { -+ QPU_M_NOP, -+ QPU_M_FMUL, -+ QPU_M_MUL24, -+ QPU_M_V8MULD, -+ QPU_M_V8MIN, -+ QPU_M_V8MAX, -+ QPU_M_V8ADDS, -+ QPU_M_V8SUBS, -+}; -+ -+enum qpu_raddr { -+ QPU_R_FRAG_PAYLOAD_ZW = 15, /* W for A file, Z for B file */ -+ /* 0-31 are the plain regfile a or b fields */ -+ QPU_R_UNIF = 32, -+ QPU_R_VARY = 35, -+ QPU_R_ELEM_QPU = 38, -+ QPU_R_NOP, -+ QPU_R_XY_PIXEL_COORD = 41, -+ QPU_R_MS_REV_FLAGS = 41, -+ QPU_R_VPM = 48, -+ QPU_R_VPM_LD_BUSY, -+ QPU_R_VPM_LD_WAIT, -+ QPU_R_MUTEX_ACQUIRE, -+}; -+ -+enum qpu_waddr { -+ /* 0-31 are the plain regfile a or b fields */ -+ QPU_W_ACC0 = 32, /* aka r0 */ -+ QPU_W_ACC1, -+ QPU_W_ACC2, -+ QPU_W_ACC3, -+ QPU_W_TMU_NOSWAP, -+ QPU_W_ACC5, -+ QPU_W_HOST_INT, -+ QPU_W_NOP, -+ QPU_W_UNIFORMS_ADDRESS, -+ QPU_W_QUAD_XY, /* X for regfile a, Y for regfile b */ -+ QPU_W_MS_FLAGS = 42, -+ QPU_W_REV_FLAG = 42, -+ QPU_W_TLB_STENCIL_SETUP = 43, -+ QPU_W_TLB_Z, -+ QPU_W_TLB_COLOR_MS, -+ QPU_W_TLB_COLOR_ALL, -+ QPU_W_TLB_ALPHA_MASK, -+ QPU_W_VPM, -+ QPU_W_VPMVCD_SETUP, /* LD for regfile a, ST for regfile b */ -+ QPU_W_VPM_ADDR, /* LD for regfile a, ST for regfile b */ -+ QPU_W_MUTEX_RELEASE, -+ QPU_W_SFU_RECIP, -+ QPU_W_SFU_RECIPSQRT, -+ QPU_W_SFU_EXP, -+ QPU_W_SFU_LOG, -+ QPU_W_TMU0_S, -+ QPU_W_TMU0_T, -+ QPU_W_TMU0_R, -+ QPU_W_TMU0_B, -+ QPU_W_TMU1_S, -+ QPU_W_TMU1_T, -+ QPU_W_TMU1_R, -+ QPU_W_TMU1_B, -+}; -+ -+enum qpu_sig_bits { -+ QPU_SIG_SW_BREAKPOINT, -+ QPU_SIG_NONE, -+ QPU_SIG_THREAD_SWITCH, -+ QPU_SIG_PROG_END, -+ QPU_SIG_WAIT_FOR_SCOREBOARD, -+ QPU_SIG_SCOREBOARD_UNLOCK, -+ QPU_SIG_LAST_THREAD_SWITCH, -+ QPU_SIG_COVERAGE_LOAD, -+ QPU_SIG_COLOR_LOAD, -+ QPU_SIG_COLOR_LOAD_END, -+ QPU_SIG_LOAD_TMU0, -+ QPU_SIG_LOAD_TMU1, -+ QPU_SIG_ALPHA_MASK_LOAD, -+ QPU_SIG_SMALL_IMM, -+ QPU_SIG_LOAD_IMM, -+ QPU_SIG_BRANCH -+}; -+ -+enum qpu_mux { -+ /* hardware mux values */ -+ QPU_MUX_R0, -+ QPU_MUX_R1, -+ QPU_MUX_R2, -+ QPU_MUX_R3, -+ QPU_MUX_R4, -+ QPU_MUX_R5, -+ QPU_MUX_A, -+ QPU_MUX_B, -+ -+ /* non-hardware mux values */ -+ QPU_MUX_IMM, -+}; -+ -+enum qpu_cond { -+ QPU_COND_NEVER, -+ QPU_COND_ALWAYS, -+ QPU_COND_ZS, -+ QPU_COND_ZC, -+ QPU_COND_NS, -+ QPU_COND_NC, -+ QPU_COND_CS, -+ QPU_COND_CC, -+}; -+ -+enum qpu_pack_mul { -+ QPU_PACK_MUL_NOP, -+ /* replicated to each 8 bits of the 32-bit dst. */ -+ QPU_PACK_MUL_8888 = 3, -+ QPU_PACK_MUL_8A, -+ QPU_PACK_MUL_8B, -+ QPU_PACK_MUL_8C, -+ QPU_PACK_MUL_8D, -+}; -+ -+enum qpu_pack_a { -+ QPU_PACK_A_NOP, -+ /* convert to 16 bit float if float input, or to int16. */ -+ QPU_PACK_A_16A, -+ QPU_PACK_A_16B, -+ /* replicated to each 8 bits of the 32-bit dst. */ -+ QPU_PACK_A_8888, -+ /* Convert to 8-bit unsigned int. */ -+ QPU_PACK_A_8A, -+ QPU_PACK_A_8B, -+ QPU_PACK_A_8C, -+ QPU_PACK_A_8D, -+ -+ /* Saturating variants of the previous instructions. */ -+ QPU_PACK_A_32_SAT, /* int-only */ -+ QPU_PACK_A_16A_SAT, /* int or float */ -+ QPU_PACK_A_16B_SAT, -+ QPU_PACK_A_8888_SAT, -+ QPU_PACK_A_8A_SAT, -+ QPU_PACK_A_8B_SAT, -+ QPU_PACK_A_8C_SAT, -+ QPU_PACK_A_8D_SAT, -+}; -+ -+enum qpu_unpack_r4 { -+ QPU_UNPACK_R4_NOP, -+ QPU_UNPACK_R4_F16A_TO_F32, -+ QPU_UNPACK_R4_F16B_TO_F32, -+ QPU_UNPACK_R4_8D_REP, -+ QPU_UNPACK_R4_8A, -+ QPU_UNPACK_R4_8B, -+ QPU_UNPACK_R4_8C, -+ QPU_UNPACK_R4_8D, -+}; -+ -+#define QPU_MASK(high, low) \ -+ ((((uint64_t)1 << ((high) - (low) + 1)) - 1) << (low)) -+ -+#define QPU_GET_FIELD(word, field) \ -+ ((uint32_t)(((word) & field ## _MASK) >> field ## _SHIFT)) -+ -+#define QPU_SIG_SHIFT 60 -+#define QPU_SIG_MASK QPU_MASK(63, 60) -+ -+#define QPU_UNPACK_SHIFT 57 -+#define QPU_UNPACK_MASK QPU_MASK(59, 57) -+ -+/** -+ * If set, the pack field means PACK_MUL or R4 packing, instead of normal -+ * regfile a packing. -+ */ -+#define QPU_PM ((uint64_t)1 << 56) -+ -+#define QPU_PACK_SHIFT 52 -+#define QPU_PACK_MASK QPU_MASK(55, 52) -+ -+#define QPU_COND_ADD_SHIFT 49 -+#define QPU_COND_ADD_MASK QPU_MASK(51, 49) -+#define QPU_COND_MUL_SHIFT 46 -+#define QPU_COND_MUL_MASK QPU_MASK(48, 46) -+ -+#define QPU_SF ((uint64_t)1 << 45) -+ -+#define QPU_WADDR_ADD_SHIFT 38 -+#define QPU_WADDR_ADD_MASK QPU_MASK(43, 38) -+#define QPU_WADDR_MUL_SHIFT 32 -+#define QPU_WADDR_MUL_MASK QPU_MASK(37, 32) -+ -+#define QPU_OP_MUL_SHIFT 29 -+#define QPU_OP_MUL_MASK QPU_MASK(31, 29) -+ -+#define QPU_RADDR_A_SHIFT 18 -+#define QPU_RADDR_A_MASK QPU_MASK(23, 18) -+#define QPU_RADDR_B_SHIFT 12 -+#define QPU_RADDR_B_MASK QPU_MASK(17, 12) -+#define QPU_SMALL_IMM_SHIFT 12 -+#define QPU_SMALL_IMM_MASK QPU_MASK(17, 12) -+ -+#define QPU_ADD_A_SHIFT 9 -+#define QPU_ADD_A_MASK QPU_MASK(11, 9) -+#define QPU_ADD_B_SHIFT 6 -+#define QPU_ADD_B_MASK QPU_MASK(8, 6) -+#define QPU_MUL_A_SHIFT 3 -+#define QPU_MUL_A_MASK QPU_MASK(5, 3) -+#define QPU_MUL_B_SHIFT 0 -+#define QPU_MUL_B_MASK QPU_MASK(2, 0) -+ -+#define QPU_WS ((uint64_t)1 << 44) -+ -+#define QPU_OP_ADD_SHIFT 24 -+#define QPU_OP_ADD_MASK QPU_MASK(28, 24) -+ -+#endif /* VC4_QPU_DEFINES_H */ -diff --git a/drivers/gpu/drm/vc4/vc4_validate_shaders.c b/drivers/gpu/drm/vc4/vc4_validate_shaders.c -new file mode 100644 -index 0000000..f67124b ---- /dev/null -+++ b/drivers/gpu/drm/vc4/vc4_validate_shaders.c -@@ -0,0 +1,513 @@ -+/* -+ * Copyright © 2014 Broadcom -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice (including the next -+ * paragraph) shall be included in all copies or substantial portions of the -+ * Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -+ * IN THE SOFTWARE. -+ */ -+ -+/** -+ * DOC: Shader validator for VC4. -+ * -+ * The VC4 has no IOMMU between it and system memory, so a user with -+ * access to execute shaders could escalate privilege by overwriting -+ * system memory (using the VPM write address register in the -+ * general-purpose DMA mode) or reading system memory it shouldn't -+ * (reading it as a texture, or uniform data, or vertex data). -+ * -+ * This walks over a shader BO, ensuring that its accesses are -+ * appropriately bounded, and recording how many texture accesses are -+ * made and where so that we can do relocations for them in the -+ * uniform stream. -+ */ -+ -+#include "vc4_drv.h" -+#include "vc4_qpu_defines.h" -+ -+struct vc4_shader_validation_state { -+ struct vc4_texture_sample_info tmu_setup[2]; -+ int tmu_write_count[2]; -+ -+ /* For registers that were last written to by a MIN instruction with -+ * one argument being a uniform, the address of the uniform. -+ * Otherwise, ~0. -+ * -+ * This is used for the validation of direct address memory reads. -+ */ -+ uint32_t live_min_clamp_offsets[32 + 32 + 4]; -+ bool live_max_clamp_regs[32 + 32 + 4]; -+}; -+ -+static uint32_t -+waddr_to_live_reg_index(uint32_t waddr, bool is_b) -+{ -+ if (waddr < 32) { -+ if (is_b) -+ return 32 + waddr; -+ else -+ return waddr; -+ } else if (waddr <= QPU_W_ACC3) { -+ return 64 + waddr - QPU_W_ACC0; -+ } else { -+ return ~0; -+ } -+} -+ -+static uint32_t -+raddr_add_a_to_live_reg_index(uint64_t inst) -+{ -+ uint32_t sig = QPU_GET_FIELD(inst, QPU_SIG); -+ uint32_t add_a = QPU_GET_FIELD(inst, QPU_ADD_A); -+ uint32_t raddr_a = QPU_GET_FIELD(inst, QPU_RADDR_A); -+ uint32_t raddr_b = QPU_GET_FIELD(inst, QPU_RADDR_B); -+ -+ if (add_a == QPU_MUX_A) -+ return raddr_a; -+ else if (add_a == QPU_MUX_B && sig != QPU_SIG_SMALL_IMM) -+ return 32 + raddr_b; -+ else if (add_a <= QPU_MUX_R3) -+ return 64 + add_a; -+ else -+ return ~0; -+} -+ -+static bool -+is_tmu_submit(uint32_t waddr) -+{ -+ return (waddr == QPU_W_TMU0_S || -+ waddr == QPU_W_TMU1_S); -+} -+ -+static bool -+is_tmu_write(uint32_t waddr) -+{ -+ return (waddr >= QPU_W_TMU0_S && -+ waddr <= QPU_W_TMU1_B); -+} -+ -+static bool -+record_texture_sample(struct vc4_validated_shader_info *validated_shader, -+ struct vc4_shader_validation_state *validation_state, -+ int tmu) -+{ -+ uint32_t s = validated_shader->num_texture_samples; -+ int i; -+ struct vc4_texture_sample_info *temp_samples; -+ -+ temp_samples = krealloc(validated_shader->texture_samples, -+ (s + 1) * sizeof(*temp_samples), -+ GFP_KERNEL); -+ if (!temp_samples) -+ return false; -+ -+ memcpy(&temp_samples[s], -+ &validation_state->tmu_setup[tmu], -+ sizeof(*temp_samples)); -+ -+ validated_shader->num_texture_samples = s + 1; -+ validated_shader->texture_samples = temp_samples; -+ -+ for (i = 0; i < 4; i++) -+ validation_state->tmu_setup[tmu].p_offset[i] = ~0; -+ -+ return true; -+} -+ -+static bool -+check_tmu_write(uint64_t inst, -+ struct vc4_validated_shader_info *validated_shader, -+ struct vc4_shader_validation_state *validation_state, -+ bool is_mul) -+{ -+ uint32_t waddr = (is_mul ? -+ QPU_GET_FIELD(inst, QPU_WADDR_MUL) : -+ QPU_GET_FIELD(inst, QPU_WADDR_ADD)); -+ uint32_t raddr_a = QPU_GET_FIELD(inst, QPU_RADDR_A); -+ uint32_t raddr_b = QPU_GET_FIELD(inst, QPU_RADDR_B); -+ int tmu = waddr > QPU_W_TMU0_B; -+ bool submit = is_tmu_submit(waddr); -+ bool is_direct = submit && validation_state->tmu_write_count[tmu] == 0; -+ uint32_t sig = QPU_GET_FIELD(inst, QPU_SIG); -+ -+ if (is_direct) { -+ uint32_t add_b = QPU_GET_FIELD(inst, QPU_ADD_B); -+ uint32_t clamp_reg, clamp_offset; -+ -+ if (sig == QPU_SIG_SMALL_IMM) { -+ DRM_ERROR("direct TMU read used small immediate\n"); -+ return false; -+ } -+ -+ /* Make sure that this texture load is an add of the base -+ * address of the UBO to a clamped offset within the UBO. -+ */ -+ if (is_mul || -+ QPU_GET_FIELD(inst, QPU_OP_ADD) != QPU_A_ADD) { -+ DRM_ERROR("direct TMU load wasn't an add\n"); -+ return false; -+ } -+ -+ /* We assert that the the clamped address is the first -+ * argument, and the UBO base address is the second argument. -+ * This is arbitrary, but simpler than supporting flipping the -+ * two either way. -+ */ -+ clamp_reg = raddr_add_a_to_live_reg_index(inst); -+ if (clamp_reg == ~0) { -+ DRM_ERROR("direct TMU load wasn't clamped\n"); -+ return false; -+ } -+ -+ clamp_offset = validation_state->live_min_clamp_offsets[clamp_reg]; -+ if (clamp_offset == ~0) { -+ DRM_ERROR("direct TMU load wasn't clamped\n"); -+ return false; -+ } -+ -+ /* Store the clamp value's offset in p1 (see reloc_tex() in -+ * vc4_validate.c). -+ */ -+ validation_state->tmu_setup[tmu].p_offset[1] = -+ clamp_offset; -+ -+ if (!(add_b == QPU_MUX_A && raddr_a == QPU_R_UNIF) && -+ !(add_b == QPU_MUX_B && raddr_b == QPU_R_UNIF)) { -+ DRM_ERROR("direct TMU load didn't add to a uniform\n"); -+ return false; -+ } -+ -+ validation_state->tmu_setup[tmu].is_direct = true; -+ } else { -+ if (raddr_a == QPU_R_UNIF || (sig != QPU_SIG_SMALL_IMM && -+ raddr_b == QPU_R_UNIF)) { -+ DRM_ERROR("uniform read in the same instruction as " -+ "texture setup.\n"); -+ return false; -+ } -+ } -+ -+ if (validation_state->tmu_write_count[tmu] >= 4) { -+ DRM_ERROR("TMU%d got too many parameters before dispatch\n", -+ tmu); -+ return false; -+ } -+ validation_state->tmu_setup[tmu].p_offset[validation_state->tmu_write_count[tmu]] = -+ validated_shader->uniforms_size; -+ validation_state->tmu_write_count[tmu]++; -+ /* Since direct uses a RADDR uniform reference, it will get counted in -+ * check_instruction_reads() -+ */ -+ if (!is_direct) -+ validated_shader->uniforms_size += 4; -+ -+ if (submit) { -+ if (!record_texture_sample(validated_shader, -+ validation_state, tmu)) { -+ return false; -+ } -+ -+ validation_state->tmu_write_count[tmu] = 0; -+ } -+ -+ return true; -+} -+ -+static bool -+check_reg_write(uint64_t inst, -+ struct vc4_validated_shader_info *validated_shader, -+ struct vc4_shader_validation_state *validation_state, -+ bool is_mul) -+{ -+ uint32_t waddr = (is_mul ? -+ QPU_GET_FIELD(inst, QPU_WADDR_MUL) : -+ QPU_GET_FIELD(inst, QPU_WADDR_ADD)); -+ -+ switch (waddr) { -+ case QPU_W_UNIFORMS_ADDRESS: -+ /* XXX: We'll probably need to support this for reladdr, but -+ * it's definitely a security-related one. -+ */ -+ DRM_ERROR("uniforms address load unsupported\n"); -+ return false; -+ -+ case QPU_W_TLB_COLOR_MS: -+ case QPU_W_TLB_COLOR_ALL: -+ case QPU_W_TLB_Z: -+ /* These only interact with the tile buffer, not main memory, -+ * so they're safe. -+ */ -+ return true; -+ -+ case QPU_W_TMU0_S: -+ case QPU_W_TMU0_T: -+ case QPU_W_TMU0_R: -+ case QPU_W_TMU0_B: -+ case QPU_W_TMU1_S: -+ case QPU_W_TMU1_T: -+ case QPU_W_TMU1_R: -+ case QPU_W_TMU1_B: -+ return check_tmu_write(inst, validated_shader, validation_state, -+ is_mul); -+ -+ case QPU_W_HOST_INT: -+ case QPU_W_TMU_NOSWAP: -+ case QPU_W_TLB_ALPHA_MASK: -+ case QPU_W_MUTEX_RELEASE: -+ /* XXX: I haven't thought about these, so don't support them -+ * for now. -+ */ -+ DRM_ERROR("Unsupported waddr %d\n", waddr); -+ return false; -+ -+ case QPU_W_VPM_ADDR: -+ DRM_ERROR("General VPM DMA unsupported\n"); -+ return false; -+ -+ case QPU_W_VPM: -+ case QPU_W_VPMVCD_SETUP: -+ /* We allow VPM setup in general, even including VPM DMA -+ * configuration setup, because the (unsafe) DMA can only be -+ * triggered by QPU_W_VPM_ADDR writes. -+ */ -+ return true; -+ -+ case QPU_W_TLB_STENCIL_SETUP: -+ return true; -+ } -+ -+ return true; -+} -+ -+static void -+track_live_clamps(uint64_t inst, -+ struct vc4_validated_shader_info *validated_shader, -+ struct vc4_shader_validation_state *validation_state) -+{ -+ uint32_t op_add = QPU_GET_FIELD(inst, QPU_OP_ADD); -+ uint32_t waddr_add = QPU_GET_FIELD(inst, QPU_WADDR_ADD); -+ uint32_t waddr_mul = QPU_GET_FIELD(inst, QPU_WADDR_MUL); -+ uint32_t cond_add = QPU_GET_FIELD(inst, QPU_COND_ADD); -+ uint32_t add_a = QPU_GET_FIELD(inst, QPU_ADD_A); -+ uint32_t add_b = QPU_GET_FIELD(inst, QPU_ADD_B); -+ uint32_t raddr_a = QPU_GET_FIELD(inst, QPU_RADDR_A); -+ uint32_t raddr_b = QPU_GET_FIELD(inst, QPU_RADDR_B); -+ uint32_t sig = QPU_GET_FIELD(inst, QPU_SIG); -+ bool ws = inst & QPU_WS; -+ uint32_t lri_add_a, lri_add, lri_mul; -+ bool add_a_is_min_0; -+ -+ /* Check whether OP_ADD's A argumennt comes from a live MAX(x, 0), -+ * before we clear previous live state. -+ */ -+ lri_add_a = raddr_add_a_to_live_reg_index(inst); -+ add_a_is_min_0 = (lri_add_a != ~0 && -+ validation_state->live_max_clamp_regs[lri_add_a]); -+ -+ /* Clear live state for registers written by our instruction. */ -+ lri_add = waddr_to_live_reg_index(waddr_add, ws); -+ lri_mul = waddr_to_live_reg_index(waddr_mul, !ws); -+ if (lri_mul != ~0) { -+ validation_state->live_max_clamp_regs[lri_mul] = false; -+ validation_state->live_min_clamp_offsets[lri_mul] = ~0; -+ } -+ if (lri_add != ~0) { -+ validation_state->live_max_clamp_regs[lri_add] = false; -+ validation_state->live_min_clamp_offsets[lri_add] = ~0; -+ } else { -+ /* Nothing further to do for live tracking, since only ADDs -+ * generate new live clamp registers. -+ */ -+ return; -+ } -+ -+ /* Now, handle remaining live clamp tracking for the ADD operation. */ -+ -+ if (cond_add != QPU_COND_ALWAYS) -+ return; -+ -+ if (op_add == QPU_A_MAX) { -+ /* Track live clamps of a value to a minimum of 0 (in either -+ * arg). -+ */ -+ if (sig != QPU_SIG_SMALL_IMM || raddr_b != 0 || -+ (add_a != QPU_MUX_B && add_b != QPU_MUX_B)) { -+ return; -+ } -+ -+ validation_state->live_max_clamp_regs[lri_add] = true; -+ } else if (op_add == QPU_A_MIN) { -+ /* Track live clamps of a value clamped to a minimum of 0 and -+ * a maximum of some uniform's offset. -+ */ -+ if (!add_a_is_min_0) -+ return; -+ -+ if (!(add_b == QPU_MUX_A && raddr_a == QPU_R_UNIF) && -+ !(add_b == QPU_MUX_B && raddr_b == QPU_R_UNIF && -+ sig != QPU_SIG_SMALL_IMM)) { -+ return; -+ } -+ -+ validation_state->live_min_clamp_offsets[lri_add] = -+ validated_shader->uniforms_size; -+ } -+} -+ -+static bool -+check_instruction_writes(uint64_t inst, -+ struct vc4_validated_shader_info *validated_shader, -+ struct vc4_shader_validation_state *validation_state) -+{ -+ uint32_t waddr_add = QPU_GET_FIELD(inst, QPU_WADDR_ADD); -+ uint32_t waddr_mul = QPU_GET_FIELD(inst, QPU_WADDR_MUL); -+ bool ok; -+ -+ if (is_tmu_write(waddr_add) && is_tmu_write(waddr_mul)) { -+ DRM_ERROR("ADD and MUL both set up textures\n"); -+ return false; -+ } -+ -+ ok = (check_reg_write(inst, validated_shader, validation_state, -+ false) && -+ check_reg_write(inst, validated_shader, validation_state, -+ true)); -+ -+ track_live_clamps(inst, validated_shader, validation_state); -+ -+ return ok; -+} -+ -+static bool -+check_instruction_reads(uint64_t inst, -+ struct vc4_validated_shader_info *validated_shader) -+{ -+ uint32_t raddr_a = QPU_GET_FIELD(inst, QPU_RADDR_A); -+ uint32_t raddr_b = QPU_GET_FIELD(inst, QPU_RADDR_B); -+ uint32_t sig = QPU_GET_FIELD(inst, QPU_SIG); -+ -+ if (raddr_a == QPU_R_UNIF || -+ (raddr_b == QPU_R_UNIF && sig != QPU_SIG_SMALL_IMM)) { -+ /* This can't overflow the uint32_t, because we're reading 8 -+ * bytes of instruction to increment by 4 here, so we'd -+ * already be OOM. -+ */ -+ validated_shader->uniforms_size += 4; -+ } -+ -+ return true; -+} -+ -+struct vc4_validated_shader_info * -+vc4_validate_shader(struct drm_gem_cma_object *shader_obj) -+{ -+ bool found_shader_end = false; -+ int shader_end_ip = 0; -+ uint32_t ip, max_ip; -+ uint64_t *shader; -+ struct vc4_validated_shader_info *validated_shader; -+ struct vc4_shader_validation_state validation_state; -+ int i; -+ -+ memset(&validation_state, 0, sizeof(validation_state)); -+ -+ for (i = 0; i < 8; i++) -+ validation_state.tmu_setup[i / 4].p_offset[i % 4] = ~0; -+ for (i = 0; i < ARRAY_SIZE(validation_state.live_min_clamp_offsets); i++) -+ validation_state.live_min_clamp_offsets[i] = ~0; -+ -+ shader = shader_obj->vaddr; -+ max_ip = shader_obj->base.size / sizeof(uint64_t); -+ -+ validated_shader = kcalloc(1, sizeof(*validated_shader), GFP_KERNEL); -+ if (!validated_shader) -+ return NULL; -+ -+ for (ip = 0; ip < max_ip; ip++) { -+ uint64_t inst = shader[ip]; -+ uint32_t sig = QPU_GET_FIELD(inst, QPU_SIG); -+ -+ switch (sig) { -+ case QPU_SIG_NONE: -+ case QPU_SIG_WAIT_FOR_SCOREBOARD: -+ case QPU_SIG_SCOREBOARD_UNLOCK: -+ case QPU_SIG_COLOR_LOAD: -+ case QPU_SIG_LOAD_TMU0: -+ case QPU_SIG_LOAD_TMU1: -+ case QPU_SIG_PROG_END: -+ case QPU_SIG_SMALL_IMM: -+ if (!check_instruction_writes(inst, validated_shader, -+ &validation_state)) { -+ DRM_ERROR("Bad write at ip %d\n", ip); -+ goto fail; -+ } -+ -+ if (!check_instruction_reads(inst, validated_shader)) -+ goto fail; -+ -+ if (sig == QPU_SIG_PROG_END) { -+ found_shader_end = true; -+ shader_end_ip = ip; -+ } -+ -+ break; -+ -+ case QPU_SIG_LOAD_IMM: -+ if (!check_instruction_writes(inst, validated_shader, -+ &validation_state)) { -+ DRM_ERROR("Bad LOAD_IMM write at ip %d\n", ip); -+ goto fail; -+ } -+ break; -+ -+ default: -+ DRM_ERROR("Unsupported QPU signal %d at " -+ "instruction %d\n", sig, ip); -+ goto fail; -+ } -+ -+ /* There are two delay slots after program end is signaled -+ * that are still executed, then we're finished. -+ */ -+ if (found_shader_end && ip == shader_end_ip + 2) -+ break; -+ } -+ -+ if (ip == max_ip) { -+ DRM_ERROR("shader failed to terminate before " -+ "shader BO end at %zd\n", -+ shader_obj->base.size); -+ goto fail; -+ } -+ -+ /* Again, no chance of integer overflow here because the worst case -+ * scenario is 8 bytes of uniforms plus handles per 8-byte -+ * instruction. -+ */ -+ validated_shader->uniforms_src_size = -+ (validated_shader->uniforms_size + -+ 4 * validated_shader->num_texture_samples); -+ -+ return validated_shader; -+ -+fail: -+ if (validated_shader) { -+ kfree(validated_shader->texture_samples); -+ kfree(validated_shader); -+ } -+ return NULL; -+} -diff --git a/include/uapi/drm/vc4_drm.h b/include/uapi/drm/vc4_drm.h -index 219d34c..74de184 100644 ---- a/include/uapi/drm/vc4_drm.h -+++ b/include/uapi/drm/vc4_drm.h -@@ -28,9 +28,11 @@ - - #define DRM_VC4_CREATE_BO 0x03 - #define DRM_VC4_MMAP_BO 0x04 -+#define DRM_VC4_CREATE_SHADER_BO 0x05 - - #define DRM_IOCTL_VC4_CREATE_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_CREATE_BO, struct drm_vc4_create_bo) - #define DRM_IOCTL_VC4_MMAP_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_MMAP_BO, struct drm_vc4_mmap_bo) -+#define DRM_IOCTL_VC4_CREATE_SHADER_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_CREATE_SHADER_BO, struct drm_vc4_create_shader_bo) - - /** - * struct drm_vc4_create_bo - ioctl argument for creating VC4 BOs. -@@ -65,4 +67,27 @@ struct drm_vc4_mmap_bo { - __u64 offset; - }; - -+/** -+ * struct drm_vc4_create_shader_bo - ioctl argument for creating VC4 -+ * shader BOs. -+ * -+ * Since allowing a shader to be overwritten while it's also being -+ * executed from would allow privlege escalation, shaders must be -+ * created using this ioctl, and they can't be mmapped later. -+ */ -+struct drm_vc4_create_shader_bo { -+ /* Size of the data argument. */ -+ __u32 size; -+ /* Flags, currently must be 0. */ -+ __u32 flags; -+ -+ /* Pointer to the data. */ -+ __u64 data; -+ -+ /** Returned GEM handle for the BO. */ -+ __u32 handle; -+ /* Pad, must be 0. */ -+ __u32 pad; -+}; -+ - #endif /* _UAPI_VC4_DRM_H_ */ --- -2.6.4 - diff --git a/kernel/kernel/files/patches/mageia/gpu-drm-0105-drm-vc4-Fix-a-typo-in-a-V3D-debug-register.patch b/kernel/kernel/files/patches/mageia/gpu-drm-0105-drm-vc4-Fix-a-typo-in-a-V3D-debug-register.patch deleted file mode 100644 index e084f368..00000000 --- a/kernel/kernel/files/patches/mageia/gpu-drm-0105-drm-vc4-Fix-a-typo-in-a-V3D-debug-register.patch +++ /dev/null @@ -1,26 +0,0 @@ -From 1fa81589bbac16af6baf153ccc9b3f38fb16a498 Mon Sep 17 00:00:00 2001 -From: Eric Anholt -Date: Fri, 23 Oct 2015 14:57:22 +0100 -Subject: [PATCH 5/9] drm/vc4: Fix a typo in a V3D debug register. - -Signed-off-by: Eric Anholt ---- - drivers/gpu/drm/vc4/vc4_regs.h | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/gpu/drm/vc4/vc4_regs.h b/drivers/gpu/drm/vc4/vc4_regs.h -index 9e4e904..4e52a0a 100644 ---- a/drivers/gpu/drm/vc4/vc4_regs.h -+++ b/drivers/gpu/drm/vc4/vc4_regs.h -@@ -154,7 +154,7 @@ - #define V3D_PCTRS14 0x006f4 - #define V3D_PCTR15 0x006f8 - #define V3D_PCTRS15 0x006fc --#define V3D_BGE 0x00f00 -+#define V3D_DBGE 0x00f00 - #define V3D_FDBGO 0x00f04 - #define V3D_FDBGB 0x00f08 - #define V3D_FDBGR 0x00f0c --- -2.6.4 - diff --git a/kernel/kernel/files/patches/mageia/gpu-drm-0106-drm-vc4-Bind-and-initialize-the-V3D-engine.patch b/kernel/kernel/files/patches/mageia/gpu-drm-0106-drm-vc4-Bind-and-initialize-the-V3D-engine.patch deleted file mode 100644 index c24a21a6..00000000 --- a/kernel/kernel/files/patches/mageia/gpu-drm-0106-drm-vc4-Bind-and-initialize-the-V3D-engine.patch +++ /dev/null @@ -1,333 +0,0 @@ -From d3f5168a0810005920e7a3d5ba83e249bd9a750c Mon Sep 17 00:00:00 2001 -From: Eric Anholt -Date: Mon, 2 Mar 2015 13:01:12 -0800 -Subject: [PATCH 6/9] drm/vc4: Bind and initialize the V3D engine. - -This is the component of the GPU that does 3D rendering. - -Signed-off-by: Eric Anholt ---- - drivers/gpu/drm/vc4/Makefile | 1 + - drivers/gpu/drm/vc4/vc4_debugfs.c | 2 + - drivers/gpu/drm/vc4/vc4_drv.c | 1 + - drivers/gpu/drm/vc4/vc4_drv.h | 13 +++ - drivers/gpu/drm/vc4/vc4_v3d.c | 225 ++++++++++++++++++++++++++++++++++++++ - 5 files changed, 242 insertions(+) - create mode 100644 drivers/gpu/drm/vc4/vc4_v3d.c - -diff --git a/drivers/gpu/drm/vc4/Makefile b/drivers/gpu/drm/vc4/Makefile -index eb776a6..e87a6f2 100644 ---- a/drivers/gpu/drm/vc4/Makefile -+++ b/drivers/gpu/drm/vc4/Makefile -@@ -11,6 +11,7 @@ vc4-y := \ - vc4_hdmi.o \ - vc4_hvs.o \ - vc4_plane.o \ -+ vc4_v3d.o \ - vc4_validate_shaders.o - - vc4-$(CONFIG_DEBUG_FS) += vc4_debugfs.o -diff --git a/drivers/gpu/drm/vc4/vc4_debugfs.c b/drivers/gpu/drm/vc4/vc4_debugfs.c -index 6bcf96e..d76ad10 100644 ---- a/drivers/gpu/drm/vc4/vc4_debugfs.c -+++ b/drivers/gpu/drm/vc4/vc4_debugfs.c -@@ -22,6 +22,8 @@ static const struct drm_info_list vc4_debugfs_list[] = { - {"crtc0_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)0}, - {"crtc1_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)1}, - {"crtc2_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)2}, -+ {"v3d_ident", vc4_v3d_debugfs_ident, 0}, -+ {"v3d_regs", vc4_v3d_debugfs_regs, 0}, - }; - - #define VC4_DEBUGFS_ENTRIES ARRAY_SIZE(vc4_debugfs_list) -diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c -index da4be9c..db58d74 100644 ---- a/drivers/gpu/drm/vc4/vc4_drv.c -+++ b/drivers/gpu/drm/vc4/vc4_drv.c -@@ -236,6 +236,7 @@ static struct platform_driver *const component_drivers[] = { - &vc4_hdmi_driver, - &vc4_crtc_driver, - &vc4_hvs_driver, -+ &vc4_v3d_driver, - }; - - static int vc4_platform_drm_probe(struct platform_device *pdev) -diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h -index bd77d55..8945463 100644 ---- a/drivers/gpu/drm/vc4/vc4_drv.h -+++ b/drivers/gpu/drm/vc4/vc4_drv.h -@@ -15,6 +15,7 @@ struct vc4_dev { - struct vc4_hdmi *hdmi; - struct vc4_hvs *hvs; - struct vc4_crtc *crtc[3]; -+ struct vc4_v3d *v3d; - - struct drm_fbdev_cma *fbdev; - -@@ -82,6 +83,11 @@ to_vc4_bo(struct drm_gem_object *bo) - return (struct vc4_bo *)bo; - } - -+struct vc4_v3d { -+ struct platform_device *pdev; -+ void __iomem *regs; -+}; -+ - struct vc4_hvs { - struct platform_device *pdev; - void __iomem *regs; -@@ -119,6 +125,8 @@ to_vc4_encoder(struct drm_encoder *encoder) - return container_of(encoder, struct vc4_encoder, base); - } - -+#define V3D_READ(offset) readl(vc4->v3d->regs + offset) -+#define V3D_WRITE(offset, val) writel(val, vc4->v3d->regs + offset) - #define HVS_READ(offset) readl(vc4->hvs->regs + offset) - #define HVS_WRITE(offset, val) writel(val, vc4->hvs->regs + offset) - -@@ -241,6 +249,11 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev, - u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist); - u32 vc4_plane_dlist_size(struct drm_plane_state *state); - -+/* vc4_v3d.c */ -+extern struct platform_driver vc4_v3d_driver; -+int vc4_v3d_debugfs_ident(struct seq_file *m, void *unused); -+int vc4_v3d_debugfs_regs(struct seq_file *m, void *unused); -+ - /* vc4_validate_shader.c */ - struct vc4_validated_shader_info * - vc4_validate_shader(struct drm_gem_cma_object *shader_obj); -diff --git a/drivers/gpu/drm/vc4/vc4_v3d.c b/drivers/gpu/drm/vc4/vc4_v3d.c -new file mode 100644 -index 0000000..040ad0d ---- /dev/null -+++ b/drivers/gpu/drm/vc4/vc4_v3d.c -@@ -0,0 +1,225 @@ -+/* -+ * Copyright (c) 2014 The Linux Foundation. All rights reserved. -+ * Copyright (C) 2013 Red Hat -+ * Author: Rob Clark -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License version 2 as published by -+ * the Free Software Foundation. -+ * -+ * 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 . -+ */ -+ -+#include "linux/component.h" -+#include "vc4_drv.h" -+#include "vc4_regs.h" -+ -+#ifdef CONFIG_DEBUG_FS -+#define REGDEF(reg) { reg, #reg } -+static const struct { -+ uint32_t reg; -+ const char *name; -+} vc4_reg_defs[] = { -+ REGDEF(V3D_IDENT0), -+ REGDEF(V3D_IDENT1), -+ REGDEF(V3D_IDENT2), -+ REGDEF(V3D_SCRATCH), -+ REGDEF(V3D_L2CACTL), -+ REGDEF(V3D_SLCACTL), -+ REGDEF(V3D_INTCTL), -+ REGDEF(V3D_INTENA), -+ REGDEF(V3D_INTDIS), -+ REGDEF(V3D_CT0CS), -+ REGDEF(V3D_CT1CS), -+ REGDEF(V3D_CT0EA), -+ REGDEF(V3D_CT1EA), -+ REGDEF(V3D_CT0CA), -+ REGDEF(V3D_CT1CA), -+ REGDEF(V3D_CT00RA0), -+ REGDEF(V3D_CT01RA0), -+ REGDEF(V3D_CT0LC), -+ REGDEF(V3D_CT1LC), -+ REGDEF(V3D_CT0PC), -+ REGDEF(V3D_CT1PC), -+ REGDEF(V3D_PCS), -+ REGDEF(V3D_BFC), -+ REGDEF(V3D_RFC), -+ REGDEF(V3D_BPCA), -+ REGDEF(V3D_BPCS), -+ REGDEF(V3D_BPOA), -+ REGDEF(V3D_BPOS), -+ REGDEF(V3D_BXCF), -+ REGDEF(V3D_SQRSV0), -+ REGDEF(V3D_SQRSV1), -+ REGDEF(V3D_SQCNTL), -+ REGDEF(V3D_SRQPC), -+ REGDEF(V3D_SRQUA), -+ REGDEF(V3D_SRQUL), -+ REGDEF(V3D_SRQCS), -+ REGDEF(V3D_VPACNTL), -+ REGDEF(V3D_VPMBASE), -+ REGDEF(V3D_PCTRC), -+ REGDEF(V3D_PCTRE), -+ REGDEF(V3D_PCTR0), -+ REGDEF(V3D_PCTRS0), -+ REGDEF(V3D_PCTR1), -+ REGDEF(V3D_PCTRS1), -+ REGDEF(V3D_PCTR2), -+ REGDEF(V3D_PCTRS2), -+ REGDEF(V3D_PCTR3), -+ REGDEF(V3D_PCTRS3), -+ REGDEF(V3D_PCTR4), -+ REGDEF(V3D_PCTRS4), -+ REGDEF(V3D_PCTR5), -+ REGDEF(V3D_PCTRS5), -+ REGDEF(V3D_PCTR6), -+ REGDEF(V3D_PCTRS6), -+ REGDEF(V3D_PCTR7), -+ REGDEF(V3D_PCTRS7), -+ REGDEF(V3D_PCTR8), -+ REGDEF(V3D_PCTRS8), -+ REGDEF(V3D_PCTR9), -+ REGDEF(V3D_PCTRS9), -+ REGDEF(V3D_PCTR10), -+ REGDEF(V3D_PCTRS10), -+ REGDEF(V3D_PCTR11), -+ REGDEF(V3D_PCTRS11), -+ REGDEF(V3D_PCTR12), -+ REGDEF(V3D_PCTRS12), -+ REGDEF(V3D_PCTR13), -+ REGDEF(V3D_PCTRS13), -+ REGDEF(V3D_PCTR14), -+ REGDEF(V3D_PCTRS14), -+ REGDEF(V3D_PCTR15), -+ REGDEF(V3D_PCTRS15), -+ REGDEF(V3D_DBGE), -+ REGDEF(V3D_FDBGO), -+ REGDEF(V3D_FDBGB), -+ REGDEF(V3D_FDBGR), -+ REGDEF(V3D_FDBGS), -+ REGDEF(V3D_ERRSTAT), -+}; -+ -+int vc4_v3d_debugfs_regs(struct seq_file *m, void *unused) -+{ -+ struct drm_info_node *node = (struct drm_info_node *)m->private; -+ struct drm_device *dev = node->minor->dev; -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ int i; -+ -+ for (i = 0; i < ARRAY_SIZE(vc4_reg_defs); i++) { -+ seq_printf(m, "%s (0x%04x): 0x%08x\n", -+ vc4_reg_defs[i].name, vc4_reg_defs[i].reg, -+ V3D_READ(vc4_reg_defs[i].reg)); -+ } -+ -+ return 0; -+} -+ -+int vc4_v3d_debugfs_ident(struct seq_file *m, void *unused) -+{ -+ struct drm_info_node *node = (struct drm_info_node *)m->private; -+ struct drm_device *dev = node->minor->dev; -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ uint32_t ident1 = V3D_READ(V3D_IDENT1); -+ uint32_t nslc = VC4_GET_FIELD(ident1, V3D_IDENT1_NSLC); -+ uint32_t tups = VC4_GET_FIELD(ident1, V3D_IDENT1_TUPS); -+ uint32_t qups = VC4_GET_FIELD(ident1, V3D_IDENT1_QUPS); -+ -+ seq_printf(m, "Revision: %d\n", -+ VC4_GET_FIELD(ident1, V3D_IDENT1_REV)); -+ seq_printf(m, "Slices: %d\n", nslc); -+ seq_printf(m, "TMUs: %d\n", nslc * tups); -+ seq_printf(m, "QPUs: %d\n", nslc * qups); -+ seq_printf(m, "Semaphores: %d\n", -+ VC4_GET_FIELD(ident1, V3D_IDENT1_NSEM)); -+ -+ return 0; -+} -+#endif /* CONFIG_DEBUG_FS */ -+ -+static void vc4_v3d_init_hw(struct drm_device *dev) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ -+ /* Take all the memory that would have been reserved for user -+ * QPU programs, since we don't have an interface for running -+ * them, anyway. -+ */ -+ V3D_WRITE(V3D_VPMBASE, 0); -+} -+ -+static int vc4_v3d_bind(struct device *dev, struct device *master, void *data) -+{ -+ struct platform_device *pdev = to_platform_device(dev); -+ struct drm_device *drm = dev_get_drvdata(master); -+ struct vc4_dev *vc4 = to_vc4_dev(drm); -+ struct vc4_v3d *v3d = NULL; -+ -+ v3d = devm_kzalloc(&pdev->dev, sizeof(*v3d), GFP_KERNEL); -+ if (!v3d) -+ return -ENOMEM; -+ -+ v3d->pdev = pdev; -+ -+ v3d->regs = vc4_ioremap_regs(pdev, 0); -+ if (IS_ERR(v3d->regs)) -+ return PTR_ERR(v3d->regs); -+ -+ vc4->v3d = v3d; -+ -+ if (V3D_READ(V3D_IDENT0) != V3D_EXPECTED_IDENT0) { -+ DRM_ERROR("V3D_IDENT0 read 0x%08x instead of 0x%08x\n", -+ V3D_READ(V3D_IDENT0), V3D_EXPECTED_IDENT0); -+ return -EINVAL; -+ } -+ -+ vc4_v3d_init_hw(drm); -+ -+ return 0; -+} -+ -+static void vc4_v3d_unbind(struct device *dev, struct device *master, -+ void *data) -+{ -+ struct drm_device *drm = dev_get_drvdata(master); -+ struct vc4_dev *vc4 = to_vc4_dev(drm); -+ -+ vc4->v3d = NULL; -+} -+ -+static const struct component_ops vc4_v3d_ops = { -+ .bind = vc4_v3d_bind, -+ .unbind = vc4_v3d_unbind, -+}; -+ -+static int vc4_v3d_dev_probe(struct platform_device *pdev) -+{ -+ return component_add(&pdev->dev, &vc4_v3d_ops); -+} -+ -+static int vc4_v3d_dev_remove(struct platform_device *pdev) -+{ -+ component_del(&pdev->dev, &vc4_v3d_ops); -+ return 0; -+} -+ -+static const struct of_device_id vc4_v3d_dt_match[] = { -+ { .compatible = "brcm,vc4-v3d" }, -+ {} -+}; -+ -+struct platform_driver vc4_v3d_driver = { -+ .probe = vc4_v3d_dev_probe, -+ .remove = vc4_v3d_dev_remove, -+ .driver = { -+ .name = "vc4_v3d", -+ .of_match_table = vc4_v3d_dt_match, -+ }, -+}; --- -2.6.4 - diff --git a/kernel/kernel/files/patches/mageia/gpu-drm-0107-drm-vc4-Add-support-for-drawing-3D-frames.patch b/kernel/kernel/files/patches/mageia/gpu-drm-0107-drm-vc4-Add-support-for-drawing-3D-frames.patch deleted file mode 100644 index 565f2c1e..00000000 --- a/kernel/kernel/files/patches/mageia/gpu-drm-0107-drm-vc4-Add-support-for-drawing-3D-frames.patch +++ /dev/null @@ -1,3477 +0,0 @@ -From d5b1a78a772f1e31a94f8babfa964152ec5e9aa5 Mon Sep 17 00:00:00 2001 -From: Eric Anholt -Date: Mon, 30 Nov 2015 12:13:37 -0800 -Subject: [PATCH 7/9] drm/vc4: Add support for drawing 3D frames. - -The user submission is basically a pointer to a command list and a -pointer to uniforms. We copy those in to the kernel, validate and -relocate them, and store the result in a GPU BO which we queue for -execution. - -v2: Drop support for NV shader recs (not necessary for GL), simplify - vc4_use_bo(), improve bin flush/semaphore checks, use __u32 style - types. - -Signed-off-by: Eric Anholt ---- - drivers/gpu/drm/vc4/Makefile | 7 + - drivers/gpu/drm/vc4/vc4_drv.c | 15 +- - drivers/gpu/drm/vc4/vc4_drv.h | 182 +++++++ - drivers/gpu/drm/vc4/vc4_gem.c | 642 +++++++++++++++++++++++ - drivers/gpu/drm/vc4/vc4_irq.c | 210 ++++++++ - drivers/gpu/drm/vc4/vc4_packet.h | 399 +++++++++++++++ - drivers/gpu/drm/vc4/vc4_render_cl.c | 634 +++++++++++++++++++++++ - drivers/gpu/drm/vc4/vc4_trace.h | 63 +++ - drivers/gpu/drm/vc4/vc4_trace_points.c | 14 + - drivers/gpu/drm/vc4/vc4_v3d.c | 37 ++ - drivers/gpu/drm/vc4/vc4_validate.c | 900 +++++++++++++++++++++++++++++++++ - include/uapi/drm/vc4_drm.h | 141 ++++++ - 12 files changed, 3243 insertions(+), 1 deletion(-) - create mode 100644 drivers/gpu/drm/vc4/vc4_gem.c - create mode 100644 drivers/gpu/drm/vc4/vc4_irq.c - create mode 100644 drivers/gpu/drm/vc4/vc4_packet.h - create mode 100644 drivers/gpu/drm/vc4/vc4_render_cl.c - create mode 100644 drivers/gpu/drm/vc4/vc4_trace.h - create mode 100644 drivers/gpu/drm/vc4/vc4_trace_points.c - create mode 100644 drivers/gpu/drm/vc4/vc4_validate.c - -diff --git a/drivers/gpu/drm/vc4/Makefile b/drivers/gpu/drm/vc4/Makefile -index e87a6f2..4c6a99f 100644 ---- a/drivers/gpu/drm/vc4/Makefile -+++ b/drivers/gpu/drm/vc4/Makefile -@@ -8,12 +8,19 @@ vc4-y := \ - vc4_crtc.o \ - vc4_drv.o \ - vc4_kms.o \ -+ vc4_gem.o \ - vc4_hdmi.o \ - vc4_hvs.o \ -+ vc4_irq.o \ - vc4_plane.o \ -+ vc4_render_cl.o \ -+ vc4_trace_points.o \ - vc4_v3d.o \ -+ vc4_validate.o \ - vc4_validate_shaders.o - - vc4-$(CONFIG_DEBUG_FS) += vc4_debugfs.o - - obj-$(CONFIG_DRM_VC4) += vc4.o -+ -+CFLAGS_vc4_trace_points.o := -I$(src) -diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c -index db58d74..2cfee59 100644 ---- a/drivers/gpu/drm/vc4/vc4_drv.c -+++ b/drivers/gpu/drm/vc4/vc4_drv.c -@@ -74,6 +74,9 @@ static const struct file_operations vc4_drm_fops = { - }; - - static const struct drm_ioctl_desc vc4_drm_ioctls[] = { -+ DRM_IOCTL_DEF_DRV(VC4_SUBMIT_CL, vc4_submit_cl_ioctl, 0), -+ DRM_IOCTL_DEF_DRV(VC4_WAIT_SEQNO, vc4_wait_seqno_ioctl, 0), -+ DRM_IOCTL_DEF_DRV(VC4_WAIT_BO, vc4_wait_bo_ioctl, 0), - DRM_IOCTL_DEF_DRV(VC4_CREATE_BO, vc4_create_bo_ioctl, 0), - DRM_IOCTL_DEF_DRV(VC4_MMAP_BO, vc4_mmap_bo_ioctl, 0), - DRM_IOCTL_DEF_DRV(VC4_CREATE_SHADER_BO, vc4_create_shader_bo_ioctl, 0), -@@ -83,10 +86,16 @@ static struct drm_driver vc4_drm_driver = { - .driver_features = (DRIVER_MODESET | - DRIVER_ATOMIC | - DRIVER_GEM | -+ DRIVER_HAVE_IRQ | - DRIVER_PRIME), - .lastclose = vc4_lastclose, - .preclose = vc4_drm_preclose, - -+ .irq_handler = vc4_irq, -+ .irq_preinstall = vc4_irq_preinstall, -+ .irq_postinstall = vc4_irq_postinstall, -+ .irq_uninstall = vc4_irq_uninstall, -+ - .enable_vblank = vc4_enable_vblank, - .disable_vblank = vc4_disable_vblank, - .get_vblank_counter = drm_vblank_count, -@@ -181,9 +190,11 @@ static int vc4_drm_bind(struct device *dev) - if (ret) - goto unref; - -+ vc4_gem_init(drm); -+ - ret = component_bind_all(dev, drm); - if (ret) -- goto unref; -+ goto gem_destroy; - - ret = drm_dev_register(drm, 0); - if (ret < 0) -@@ -207,6 +218,8 @@ unregister: - drm_dev_unregister(drm); - unbind_all: - component_unbind_all(dev, drm); -+gem_destroy: -+ vc4_gem_destroy(drm); - unref: - drm_dev_unref(drm); - vc4_bo_cache_destroy(drm); -diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h -index 8945463..0bc8c57 100644 ---- a/drivers/gpu/drm/vc4/vc4_drv.h -+++ b/drivers/gpu/drm/vc4/vc4_drv.h -@@ -49,6 +49,48 @@ struct vc4_dev { - - /* Protects bo_cache and the BO stats. */ - struct mutex bo_lock; -+ -+ /* Sequence number for the last job queued in job_list. -+ * Starts at 0 (no jobs emitted). -+ */ -+ uint64_t emit_seqno; -+ -+ /* Sequence number for the last completed job on the GPU. -+ * Starts at 0 (no jobs completed). -+ */ -+ uint64_t finished_seqno; -+ -+ /* List of all struct vc4_exec_info for jobs to be executed. -+ * The first job in the list is the one currently programmed -+ * into ct0ca/ct1ca for execution. -+ */ -+ struct list_head job_list; -+ /* List of the finished vc4_exec_infos waiting to be freed by -+ * job_done_work. -+ */ -+ struct list_head job_done_list; -+ /* Spinlock used to synchronize the job_list and seqno -+ * accesses between the IRQ handler and GEM ioctls. -+ */ -+ spinlock_t job_lock; -+ wait_queue_head_t job_wait_queue; -+ struct work_struct job_done_work; -+ -+ /* The binner overflow memory that's currently set up in -+ * BPOA/BPOS registers. When overflow occurs and a new one is -+ * allocated, the previous one will be moved to -+ * vc4->current_exec's free list. -+ */ -+ struct vc4_bo *overflow_mem; -+ struct work_struct overflow_mem_work; -+ -+ struct { -+ uint32_t last_ct0ca, last_ct1ca; -+ struct timer_list timer; -+ struct work_struct reset_work; -+ } hangcheck; -+ -+ struct semaphore async_modeset; - }; - - static inline struct vc4_dev * -@@ -60,6 +102,9 @@ to_vc4_dev(struct drm_device *dev) - struct vc4_bo { - struct drm_gem_cma_object base; - -+ /* seqno of the last job to render to this BO. */ -+ uint64_t seqno; -+ - /* List entry for the BO's position in either - * vc4_exec_info->unref_list or vc4_dev->bo_cache.time_list - */ -@@ -130,6 +175,101 @@ to_vc4_encoder(struct drm_encoder *encoder) - #define HVS_READ(offset) readl(vc4->hvs->regs + offset) - #define HVS_WRITE(offset, val) writel(val, vc4->hvs->regs + offset) - -+struct vc4_exec_info { -+ /* Sequence number for this bin/render job. */ -+ uint64_t seqno; -+ -+ /* Kernel-space copy of the ioctl arguments */ -+ struct drm_vc4_submit_cl *args; -+ -+ /* This is the array of BOs that were looked up at the start of exec. -+ * Command validation will use indices into this array. -+ */ -+ struct drm_gem_cma_object **bo; -+ uint32_t bo_count; -+ -+ /* Pointers for our position in vc4->job_list */ -+ struct list_head head; -+ -+ /* List of other BOs used in the job that need to be released -+ * once the job is complete. -+ */ -+ struct list_head unref_list; -+ -+ /* Current unvalidated indices into @bo loaded by the non-hardware -+ * VC4_PACKET_GEM_HANDLES. -+ */ -+ uint32_t bo_index[2]; -+ -+ /* This is the BO where we store the validated command lists, shader -+ * records, and uniforms. -+ */ -+ struct drm_gem_cma_object *exec_bo; -+ -+ /** -+ * This tracks the per-shader-record state (packet 64) that -+ * determines the length of the shader record and the offset -+ * it's expected to be found at. It gets read in from the -+ * command lists. -+ */ -+ struct vc4_shader_state { -+ uint32_t addr; -+ /* Maximum vertex index referenced by any primitive using this -+ * shader state. -+ */ -+ uint32_t max_index; -+ } *shader_state; -+ -+ /** How many shader states the user declared they were using. */ -+ uint32_t shader_state_size; -+ /** How many shader state records the validator has seen. */ -+ uint32_t shader_state_count; -+ -+ bool found_tile_binning_mode_config_packet; -+ bool found_start_tile_binning_packet; -+ bool found_increment_semaphore_packet; -+ bool found_flush; -+ uint8_t bin_tiles_x, bin_tiles_y; -+ struct drm_gem_cma_object *tile_bo; -+ uint32_t tile_alloc_offset; -+ -+ /** -+ * Computed addresses pointing into exec_bo where we start the -+ * bin thread (ct0) and render thread (ct1). -+ */ -+ uint32_t ct0ca, ct0ea; -+ uint32_t ct1ca, ct1ea; -+ -+ /* Pointer to the unvalidated bin CL (if present). */ -+ void *bin_u; -+ -+ /* Pointers to the shader recs. These paddr gets incremented as CL -+ * packets are relocated in validate_gl_shader_state, and the vaddrs -+ * (u and v) get incremented and size decremented as the shader recs -+ * themselves are validated. -+ */ -+ void *shader_rec_u; -+ void *shader_rec_v; -+ uint32_t shader_rec_p; -+ uint32_t shader_rec_size; -+ -+ /* Pointers to the uniform data. These pointers are incremented, and -+ * size decremented, as each batch of uniforms is uploaded. -+ */ -+ void *uniforms_u; -+ void *uniforms_v; -+ uint32_t uniforms_p; -+ uint32_t uniforms_size; -+}; -+ -+static inline struct vc4_exec_info * -+vc4_first_job(struct vc4_dev *vc4) -+{ -+ if (list_empty(&vc4->job_list)) -+ return NULL; -+ return list_first_entry(&vc4->job_list, struct vc4_exec_info, head); -+} -+ - /** - * struct vc4_texture_sample_info - saves the offsets into the UBO for texture - * setup parameters. -@@ -231,10 +371,31 @@ void vc4_debugfs_cleanup(struct drm_minor *minor); - /* vc4_drv.c */ - void __iomem *vc4_ioremap_regs(struct platform_device *dev, int index); - -+/* vc4_gem.c */ -+void vc4_gem_init(struct drm_device *dev); -+void vc4_gem_destroy(struct drm_device *dev); -+int vc4_submit_cl_ioctl(struct drm_device *dev, void *data, -+ struct drm_file *file_priv); -+int vc4_wait_seqno_ioctl(struct drm_device *dev, void *data, -+ struct drm_file *file_priv); -+int vc4_wait_bo_ioctl(struct drm_device *dev, void *data, -+ struct drm_file *file_priv); -+void vc4_submit_next_job(struct drm_device *dev); -+int vc4_wait_for_seqno(struct drm_device *dev, uint64_t seqno, -+ uint64_t timeout_ns, bool interruptible); -+void vc4_job_handle_completed(struct vc4_dev *vc4); -+ - /* vc4_hdmi.c */ - extern struct platform_driver vc4_hdmi_driver; - int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused); - -+/* vc4_irq.c */ -+irqreturn_t vc4_irq(int irq, void *arg); -+void vc4_irq_preinstall(struct drm_device *dev); -+int vc4_irq_postinstall(struct drm_device *dev); -+void vc4_irq_uninstall(struct drm_device *dev); -+void vc4_irq_reset(struct drm_device *dev); -+ - /* vc4_hvs.c */ - extern struct platform_driver vc4_hvs_driver; - void vc4_hvs_dump_state(struct drm_device *dev); -@@ -253,6 +414,27 @@ u32 vc4_plane_dlist_size(struct drm_plane_state *state); - extern struct platform_driver vc4_v3d_driver; - int vc4_v3d_debugfs_ident(struct seq_file *m, void *unused); - int vc4_v3d_debugfs_regs(struct seq_file *m, void *unused); -+int vc4_v3d_set_power(struct vc4_dev *vc4, bool on); -+ -+/* vc4_validate.c */ -+int -+vc4_validate_bin_cl(struct drm_device *dev, -+ void *validated, -+ void *unvalidated, -+ struct vc4_exec_info *exec); -+ -+int -+vc4_validate_shader_recs(struct drm_device *dev, struct vc4_exec_info *exec); -+ -+struct drm_gem_cma_object *vc4_use_bo(struct vc4_exec_info *exec, -+ uint32_t hindex); -+ -+int vc4_get_rcl(struct drm_device *dev, struct vc4_exec_info *exec); -+ -+bool vc4_check_tex_size(struct vc4_exec_info *exec, -+ struct drm_gem_cma_object *fbo, -+ uint32_t offset, uint8_t tiling_format, -+ uint32_t width, uint32_t height, uint8_t cpp); - - /* vc4_validate_shader.c */ - struct vc4_validated_shader_info * -diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c -new file mode 100644 -index 0000000..936dddf ---- /dev/null -+++ b/drivers/gpu/drm/vc4/vc4_gem.c -@@ -0,0 +1,642 @@ -+/* -+ * Copyright © 2014 Broadcom -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice (including the next -+ * paragraph) shall be included in all copies or substantial portions of the -+ * Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -+ * IN THE SOFTWARE. -+ */ -+ -+#include -+#include -+#include -+#include -+ -+#include "uapi/drm/vc4_drm.h" -+#include "vc4_drv.h" -+#include "vc4_regs.h" -+#include "vc4_trace.h" -+ -+static void -+vc4_queue_hangcheck(struct drm_device *dev) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ -+ mod_timer(&vc4->hangcheck.timer, -+ round_jiffies_up(jiffies + msecs_to_jiffies(100))); -+} -+ -+static void -+vc4_reset(struct drm_device *dev) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ -+ DRM_INFO("Resetting GPU.\n"); -+ vc4_v3d_set_power(vc4, false); -+ vc4_v3d_set_power(vc4, true); -+ -+ vc4_irq_reset(dev); -+ -+ /* Rearm the hangcheck -- another job might have been waiting -+ * for our hung one to get kicked off, and vc4_irq_reset() -+ * would have started it. -+ */ -+ vc4_queue_hangcheck(dev); -+} -+ -+static void -+vc4_reset_work(struct work_struct *work) -+{ -+ struct vc4_dev *vc4 = -+ container_of(work, struct vc4_dev, hangcheck.reset_work); -+ -+ vc4_reset(vc4->dev); -+} -+ -+static void -+vc4_hangcheck_elapsed(unsigned long data) -+{ -+ struct drm_device *dev = (struct drm_device *)data; -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ uint32_t ct0ca, ct1ca; -+ -+ /* If idle, we can stop watching for hangs. */ -+ if (list_empty(&vc4->job_list)) -+ return; -+ -+ ct0ca = V3D_READ(V3D_CTNCA(0)); -+ ct1ca = V3D_READ(V3D_CTNCA(1)); -+ -+ /* If we've made any progress in execution, rearm the timer -+ * and wait. -+ */ -+ if (ct0ca != vc4->hangcheck.last_ct0ca || -+ ct1ca != vc4->hangcheck.last_ct1ca) { -+ vc4->hangcheck.last_ct0ca = ct0ca; -+ vc4->hangcheck.last_ct1ca = ct1ca; -+ vc4_queue_hangcheck(dev); -+ return; -+ } -+ -+ /* We've gone too long with no progress, reset. This has to -+ * be done from a work struct, since resetting can sleep and -+ * this timer hook isn't allowed to. -+ */ -+ schedule_work(&vc4->hangcheck.reset_work); -+} -+ -+static void -+submit_cl(struct drm_device *dev, uint32_t thread, uint32_t start, uint32_t end) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ -+ /* Set the current and end address of the control list. -+ * Writing the end register is what starts the job. -+ */ -+ V3D_WRITE(V3D_CTNCA(thread), start); -+ V3D_WRITE(V3D_CTNEA(thread), end); -+} -+ -+int -+vc4_wait_for_seqno(struct drm_device *dev, uint64_t seqno, uint64_t timeout_ns, -+ bool interruptible) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ int ret = 0; -+ unsigned long timeout_expire; -+ DEFINE_WAIT(wait); -+ -+ if (vc4->finished_seqno >= seqno) -+ return 0; -+ -+ if (timeout_ns == 0) -+ return -ETIME; -+ -+ timeout_expire = jiffies + nsecs_to_jiffies(timeout_ns); -+ -+ trace_vc4_wait_for_seqno_begin(dev, seqno, timeout_ns); -+ for (;;) { -+ prepare_to_wait(&vc4->job_wait_queue, &wait, -+ interruptible ? TASK_INTERRUPTIBLE : -+ TASK_UNINTERRUPTIBLE); -+ -+ if (interruptible && signal_pending(current)) { -+ ret = -ERESTARTSYS; -+ break; -+ } -+ -+ if (vc4->finished_seqno >= seqno) -+ break; -+ -+ if (timeout_ns != ~0ull) { -+ if (time_after_eq(jiffies, timeout_expire)) { -+ ret = -ETIME; -+ break; -+ } -+ schedule_timeout(timeout_expire - jiffies); -+ } else { -+ schedule(); -+ } -+ } -+ -+ finish_wait(&vc4->job_wait_queue, &wait); -+ trace_vc4_wait_for_seqno_end(dev, seqno); -+ -+ if (ret && ret != -ERESTARTSYS) { -+ DRM_ERROR("timeout waiting for render thread idle\n"); -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static void -+vc4_flush_caches(struct drm_device *dev) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ -+ /* Flush the GPU L2 caches. These caches sit on top of system -+ * L3 (the 128kb or so shared with the CPU), and are -+ * non-allocating in the L3. -+ */ -+ V3D_WRITE(V3D_L2CACTL, -+ V3D_L2CACTL_L2CCLR); -+ -+ V3D_WRITE(V3D_SLCACTL, -+ VC4_SET_FIELD(0xf, V3D_SLCACTL_T1CC) | -+ VC4_SET_FIELD(0xf, V3D_SLCACTL_T0CC) | -+ VC4_SET_FIELD(0xf, V3D_SLCACTL_UCC) | -+ VC4_SET_FIELD(0xf, V3D_SLCACTL_ICC)); -+} -+ -+/* Sets the registers for the next job to be actually be executed in -+ * the hardware. -+ * -+ * The job_lock should be held during this. -+ */ -+void -+vc4_submit_next_job(struct drm_device *dev) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ struct vc4_exec_info *exec = vc4_first_job(vc4); -+ -+ if (!exec) -+ return; -+ -+ vc4_flush_caches(dev); -+ -+ /* Disable the binner's pre-loaded overflow memory address */ -+ V3D_WRITE(V3D_BPOA, 0); -+ V3D_WRITE(V3D_BPOS, 0); -+ -+ if (exec->ct0ca != exec->ct0ea) -+ submit_cl(dev, 0, exec->ct0ca, exec->ct0ea); -+ submit_cl(dev, 1, exec->ct1ca, exec->ct1ea); -+} -+ -+static void -+vc4_update_bo_seqnos(struct vc4_exec_info *exec, uint64_t seqno) -+{ -+ struct vc4_bo *bo; -+ unsigned i; -+ -+ for (i = 0; i < exec->bo_count; i++) { -+ bo = to_vc4_bo(&exec->bo[i]->base); -+ bo->seqno = seqno; -+ } -+ -+ list_for_each_entry(bo, &exec->unref_list, unref_head) { -+ bo->seqno = seqno; -+ } -+} -+ -+/* Queues a struct vc4_exec_info for execution. If no job is -+ * currently executing, then submits it. -+ * -+ * Unlike most GPUs, our hardware only handles one command list at a -+ * time. To queue multiple jobs at once, we'd need to edit the -+ * previous command list to have a jump to the new one at the end, and -+ * then bump the end address. That's a change for a later date, -+ * though. -+ */ -+static void -+vc4_queue_submit(struct drm_device *dev, struct vc4_exec_info *exec) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ uint64_t seqno; -+ unsigned long irqflags; -+ -+ spin_lock_irqsave(&vc4->job_lock, irqflags); -+ -+ seqno = ++vc4->emit_seqno; -+ exec->seqno = seqno; -+ vc4_update_bo_seqnos(exec, seqno); -+ -+ list_add_tail(&exec->head, &vc4->job_list); -+ -+ /* If no job was executing, kick ours off. Otherwise, it'll -+ * get started when the previous job's frame done interrupt -+ * occurs. -+ */ -+ if (vc4_first_job(vc4) == exec) { -+ vc4_submit_next_job(dev); -+ vc4_queue_hangcheck(dev); -+ } -+ -+ spin_unlock_irqrestore(&vc4->job_lock, irqflags); -+} -+ -+/** -+ * Looks up a bunch of GEM handles for BOs and stores the array for -+ * use in the command validator that actually writes relocated -+ * addresses pointing to them. -+ */ -+static int -+vc4_cl_lookup_bos(struct drm_device *dev, -+ struct drm_file *file_priv, -+ struct vc4_exec_info *exec) -+{ -+ struct drm_vc4_submit_cl *args = exec->args; -+ uint32_t *handles; -+ int ret = 0; -+ int i; -+ -+ exec->bo_count = args->bo_handle_count; -+ -+ if (!exec->bo_count) { -+ /* See comment on bo_index for why we have to check -+ * this. -+ */ -+ DRM_ERROR("Rendering requires BOs to validate\n"); -+ return -EINVAL; -+ } -+ -+ exec->bo = kcalloc(exec->bo_count, sizeof(struct drm_gem_cma_object *), -+ GFP_KERNEL); -+ if (!exec->bo) { -+ DRM_ERROR("Failed to allocate validated BO pointers\n"); -+ return -ENOMEM; -+ } -+ -+ handles = drm_malloc_ab(exec->bo_count, sizeof(uint32_t)); -+ if (!handles) { -+ DRM_ERROR("Failed to allocate incoming GEM handles\n"); -+ goto fail; -+ } -+ -+ ret = copy_from_user(handles, -+ (void __user *)(uintptr_t)args->bo_handles, -+ exec->bo_count * sizeof(uint32_t)); -+ if (ret) { -+ DRM_ERROR("Failed to copy in GEM handles\n"); -+ goto fail; -+ } -+ -+ spin_lock(&file_priv->table_lock); -+ for (i = 0; i < exec->bo_count; i++) { -+ struct drm_gem_object *bo = idr_find(&file_priv->object_idr, -+ handles[i]); -+ if (!bo) { -+ DRM_ERROR("Failed to look up GEM BO %d: %d\n", -+ i, handles[i]); -+ ret = -EINVAL; -+ spin_unlock(&file_priv->table_lock); -+ goto fail; -+ } -+ drm_gem_object_reference(bo); -+ exec->bo[i] = (struct drm_gem_cma_object *)bo; -+ } -+ spin_unlock(&file_priv->table_lock); -+ -+fail: -+ kfree(handles); -+ return 0; -+} -+ -+static int -+vc4_get_bcl(struct drm_device *dev, struct vc4_exec_info *exec) -+{ -+ struct drm_vc4_submit_cl *args = exec->args; -+ void *temp = NULL; -+ void *bin; -+ int ret = 0; -+ uint32_t bin_offset = 0; -+ uint32_t shader_rec_offset = roundup(bin_offset + args->bin_cl_size, -+ 16); -+ uint32_t uniforms_offset = shader_rec_offset + args->shader_rec_size; -+ uint32_t exec_size = uniforms_offset + args->uniforms_size; -+ uint32_t temp_size = exec_size + (sizeof(struct vc4_shader_state) * -+ args->shader_rec_count); -+ struct vc4_bo *bo; -+ -+ if (uniforms_offset < shader_rec_offset || -+ exec_size < uniforms_offset || -+ args->shader_rec_count >= (UINT_MAX / -+ sizeof(struct vc4_shader_state)) || -+ temp_size < exec_size) { -+ DRM_ERROR("overflow in exec arguments\n"); -+ goto fail; -+ } -+ -+ /* Allocate space where we'll store the copied in user command lists -+ * and shader records. -+ * -+ * We don't just copy directly into the BOs because we need to -+ * read the contents back for validation, and I think the -+ * bo->vaddr is uncached access. -+ */ -+ temp = kmalloc(temp_size, GFP_KERNEL); -+ if (!temp) { -+ DRM_ERROR("Failed to allocate storage for copying " -+ "in bin/render CLs.\n"); -+ ret = -ENOMEM; -+ goto fail; -+ } -+ bin = temp + bin_offset; -+ exec->shader_rec_u = temp + shader_rec_offset; -+ exec->uniforms_u = temp + uniforms_offset; -+ exec->shader_state = temp + exec_size; -+ exec->shader_state_size = args->shader_rec_count; -+ -+ ret = copy_from_user(bin, -+ (void __user *)(uintptr_t)args->bin_cl, -+ args->bin_cl_size); -+ if (ret) { -+ DRM_ERROR("Failed to copy in bin cl\n"); -+ goto fail; -+ } -+ -+ ret = copy_from_user(exec->shader_rec_u, -+ (void __user *)(uintptr_t)args->shader_rec, -+ args->shader_rec_size); -+ if (ret) { -+ DRM_ERROR("Failed to copy in shader recs\n"); -+ goto fail; -+ } -+ -+ ret = copy_from_user(exec->uniforms_u, -+ (void __user *)(uintptr_t)args->uniforms, -+ args->uniforms_size); -+ if (ret) { -+ DRM_ERROR("Failed to copy in uniforms cl\n"); -+ goto fail; -+ } -+ -+ bo = vc4_bo_create(dev, exec_size, true); -+ if (!bo) { -+ DRM_ERROR("Couldn't allocate BO for binning\n"); -+ ret = PTR_ERR(exec->exec_bo); -+ goto fail; -+ } -+ exec->exec_bo = &bo->base; -+ -+ list_add_tail(&to_vc4_bo(&exec->exec_bo->base)->unref_head, -+ &exec->unref_list); -+ -+ exec->ct0ca = exec->exec_bo->paddr + bin_offset; -+ -+ exec->bin_u = bin; -+ -+ exec->shader_rec_v = exec->exec_bo->vaddr + shader_rec_offset; -+ exec->shader_rec_p = exec->exec_bo->paddr + shader_rec_offset; -+ exec->shader_rec_size = args->shader_rec_size; -+ -+ exec->uniforms_v = exec->exec_bo->vaddr + uniforms_offset; -+ exec->uniforms_p = exec->exec_bo->paddr + uniforms_offset; -+ exec->uniforms_size = args->uniforms_size; -+ -+ ret = vc4_validate_bin_cl(dev, -+ exec->exec_bo->vaddr + bin_offset, -+ bin, -+ exec); -+ if (ret) -+ goto fail; -+ -+ ret = vc4_validate_shader_recs(dev, exec); -+ -+fail: -+ kfree(temp); -+ return ret; -+} -+ -+static void -+vc4_complete_exec(struct drm_device *dev, struct vc4_exec_info *exec) -+{ -+ unsigned i; -+ -+ /* Need the struct lock for drm_gem_object_unreference(). */ -+ mutex_lock(&dev->struct_mutex); -+ if (exec->bo) { -+ for (i = 0; i < exec->bo_count; i++) -+ drm_gem_object_unreference(&exec->bo[i]->base); -+ kfree(exec->bo); -+ } -+ -+ while (!list_empty(&exec->unref_list)) { -+ struct vc4_bo *bo = list_first_entry(&exec->unref_list, -+ struct vc4_bo, unref_head); -+ list_del(&bo->unref_head); -+ drm_gem_object_unreference(&bo->base.base); -+ } -+ mutex_unlock(&dev->struct_mutex); -+ -+ kfree(exec); -+} -+ -+void -+vc4_job_handle_completed(struct vc4_dev *vc4) -+{ -+ unsigned long irqflags; -+ -+ spin_lock_irqsave(&vc4->job_lock, irqflags); -+ while (!list_empty(&vc4->job_done_list)) { -+ struct vc4_exec_info *exec = -+ list_first_entry(&vc4->job_done_list, -+ struct vc4_exec_info, head); -+ list_del(&exec->head); -+ -+ spin_unlock_irqrestore(&vc4->job_lock, irqflags); -+ vc4_complete_exec(vc4->dev, exec); -+ spin_lock_irqsave(&vc4->job_lock, irqflags); -+ } -+ spin_unlock_irqrestore(&vc4->job_lock, irqflags); -+} -+ -+/* Scheduled when any job has been completed, this walks the list of -+ * jobs that had completed and unrefs their BOs and frees their exec -+ * structs. -+ */ -+static void -+vc4_job_done_work(struct work_struct *work) -+{ -+ struct vc4_dev *vc4 = -+ container_of(work, struct vc4_dev, job_done_work); -+ -+ vc4_job_handle_completed(vc4); -+} -+ -+static int -+vc4_wait_for_seqno_ioctl_helper(struct drm_device *dev, -+ uint64_t seqno, -+ uint64_t *timeout_ns) -+{ -+ unsigned long start = jiffies; -+ int ret = vc4_wait_for_seqno(dev, seqno, *timeout_ns, true); -+ -+ if ((ret == -EINTR || ret == -ERESTARTSYS) && *timeout_ns != ~0ull) { -+ uint64_t delta = jiffies_to_nsecs(jiffies - start); -+ -+ if (*timeout_ns >= delta) -+ *timeout_ns -= delta; -+ } -+ -+ return ret; -+} -+ -+int -+vc4_wait_seqno_ioctl(struct drm_device *dev, void *data, -+ struct drm_file *file_priv) -+{ -+ struct drm_vc4_wait_seqno *args = data; -+ -+ return vc4_wait_for_seqno_ioctl_helper(dev, args->seqno, -+ &args->timeout_ns); -+} -+ -+int -+vc4_wait_bo_ioctl(struct drm_device *dev, void *data, -+ struct drm_file *file_priv) -+{ -+ int ret; -+ struct drm_vc4_wait_bo *args = data; -+ struct drm_gem_object *gem_obj; -+ struct vc4_bo *bo; -+ -+ gem_obj = drm_gem_object_lookup(dev, file_priv, args->handle); -+ if (!gem_obj) { -+ DRM_ERROR("Failed to look up GEM BO %d\n", args->handle); -+ return -EINVAL; -+ } -+ bo = to_vc4_bo(gem_obj); -+ -+ ret = vc4_wait_for_seqno_ioctl_helper(dev, bo->seqno, -+ &args->timeout_ns); -+ -+ drm_gem_object_unreference_unlocked(gem_obj); -+ return ret; -+} -+ -+/** -+ * Submits a command list to the VC4. -+ * -+ * This is what is called batchbuffer emitting on other hardware. -+ */ -+int -+vc4_submit_cl_ioctl(struct drm_device *dev, void *data, -+ struct drm_file *file_priv) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ struct drm_vc4_submit_cl *args = data; -+ struct vc4_exec_info *exec; -+ int ret; -+ -+ if ((args->flags & ~VC4_SUBMIT_CL_USE_CLEAR_COLOR) != 0) { -+ DRM_ERROR("Unknown flags: 0x%02x\n", args->flags); -+ return -EINVAL; -+ } -+ -+ exec = kcalloc(1, sizeof(*exec), GFP_KERNEL); -+ if (!exec) { -+ DRM_ERROR("malloc failure on exec struct\n"); -+ return -ENOMEM; -+ } -+ -+ exec->args = args; -+ INIT_LIST_HEAD(&exec->unref_list); -+ -+ ret = vc4_cl_lookup_bos(dev, file_priv, exec); -+ if (ret) -+ goto fail; -+ -+ if (exec->args->bin_cl_size != 0) { -+ ret = vc4_get_bcl(dev, exec); -+ if (ret) -+ goto fail; -+ } else { -+ exec->ct0ca = 0; -+ exec->ct0ea = 0; -+ } -+ -+ ret = vc4_get_rcl(dev, exec); -+ if (ret) -+ goto fail; -+ -+ /* Clear this out of the struct we'll be putting in the queue, -+ * since it's part of our stack. -+ */ -+ exec->args = NULL; -+ -+ vc4_queue_submit(dev, exec); -+ -+ /* Return the seqno for our job. */ -+ args->seqno = vc4->emit_seqno; -+ -+ return 0; -+ -+fail: -+ vc4_complete_exec(vc4->dev, exec); -+ -+ return ret; -+} -+ -+void -+vc4_gem_init(struct drm_device *dev) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ -+ INIT_LIST_HEAD(&vc4->job_list); -+ INIT_LIST_HEAD(&vc4->job_done_list); -+ spin_lock_init(&vc4->job_lock); -+ -+ INIT_WORK(&vc4->hangcheck.reset_work, vc4_reset_work); -+ setup_timer(&vc4->hangcheck.timer, -+ vc4_hangcheck_elapsed, -+ (unsigned long)dev); -+ -+ INIT_WORK(&vc4->job_done_work, vc4_job_done_work); -+} -+ -+void -+vc4_gem_destroy(struct drm_device *dev) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ -+ /* Waiting for exec to finish would need to be done before -+ * unregistering V3D. -+ */ -+ WARN_ON(vc4->emit_seqno != vc4->finished_seqno); -+ -+ /* V3D should already have disabled its interrupt and cleared -+ * the overflow allocation registers. Now free the object. -+ */ -+ if (vc4->overflow_mem) { -+ drm_gem_object_unreference_unlocked(&vc4->overflow_mem->base.base); -+ vc4->overflow_mem = NULL; -+ } -+ -+ vc4_bo_cache_destroy(dev); -+} -diff --git a/drivers/gpu/drm/vc4/vc4_irq.c b/drivers/gpu/drm/vc4/vc4_irq.c -new file mode 100644 -index 0000000..b68060e ---- /dev/null -+++ b/drivers/gpu/drm/vc4/vc4_irq.c -@@ -0,0 +1,210 @@ -+/* -+ * Copyright © 2014 Broadcom -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice (including the next -+ * paragraph) shall be included in all copies or substantial portions of the -+ * Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -+ * IN THE SOFTWARE. -+ */ -+ -+/** DOC: Interrupt management for the V3D engine. -+ * -+ * We have an interrupt status register (V3D_INTCTL) which reports -+ * interrupts, and where writing 1 bits clears those interrupts. -+ * There are also a pair of interrupt registers -+ * (V3D_INTENA/V3D_INTDIS) where writing a 1 to their bits enables or -+ * disables that specific interrupt, and 0s written are ignored -+ * (reading either one returns the set of enabled interrupts). -+ * -+ * When we take a render frame interrupt, we need to wake the -+ * processes waiting for some frame to be done, and get the next frame -+ * submitted ASAP (so the hardware doesn't sit idle when there's work -+ * to do). -+ * -+ * When we take the binner out of memory interrupt, we need to -+ * allocate some new memory and pass it to the binner so that the -+ * current job can make progress. -+ */ -+ -+#include "vc4_drv.h" -+#include "vc4_regs.h" -+ -+#define V3D_DRIVER_IRQS (V3D_INT_OUTOMEM | \ -+ V3D_INT_FRDONE) -+ -+DECLARE_WAIT_QUEUE_HEAD(render_wait); -+ -+static void -+vc4_overflow_mem_work(struct work_struct *work) -+{ -+ struct vc4_dev *vc4 = -+ container_of(work, struct vc4_dev, overflow_mem_work); -+ struct drm_device *dev = vc4->dev; -+ struct vc4_bo *bo; -+ -+ bo = vc4_bo_create(dev, 256 * 1024, true); -+ if (!bo) { -+ DRM_ERROR("Couldn't allocate binner overflow mem\n"); -+ return; -+ } -+ -+ /* If there's a job executing currently, then our previous -+ * overflow allocation is getting used in that job and we need -+ * to queue it to be released when the job is done. But if no -+ * job is executing at all, then we can free the old overflow -+ * object direcctly. -+ * -+ * No lock necessary for this pointer since we're the only -+ * ones that update the pointer, and our workqueue won't -+ * reenter. -+ */ -+ if (vc4->overflow_mem) { -+ struct vc4_exec_info *current_exec; -+ unsigned long irqflags; -+ -+ spin_lock_irqsave(&vc4->job_lock, irqflags); -+ current_exec = vc4_first_job(vc4); -+ if (current_exec) { -+ vc4->overflow_mem->seqno = vc4->finished_seqno + 1; -+ list_add_tail(&vc4->overflow_mem->unref_head, -+ ¤t_exec->unref_list); -+ vc4->overflow_mem = NULL; -+ } -+ spin_unlock_irqrestore(&vc4->job_lock, irqflags); -+ } -+ -+ if (vc4->overflow_mem) -+ drm_gem_object_unreference_unlocked(&vc4->overflow_mem->base.base); -+ vc4->overflow_mem = bo; -+ -+ V3D_WRITE(V3D_BPOA, bo->base.paddr); -+ V3D_WRITE(V3D_BPOS, bo->base.base.size); -+ V3D_WRITE(V3D_INTCTL, V3D_INT_OUTOMEM); -+ V3D_WRITE(V3D_INTENA, V3D_INT_OUTOMEM); -+} -+ -+static void -+vc4_irq_finish_job(struct drm_device *dev) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ struct vc4_exec_info *exec = vc4_first_job(vc4); -+ -+ if (!exec) -+ return; -+ -+ vc4->finished_seqno++; -+ list_move_tail(&exec->head, &vc4->job_done_list); -+ vc4_submit_next_job(dev); -+ -+ wake_up_all(&vc4->job_wait_queue); -+ schedule_work(&vc4->job_done_work); -+} -+ -+irqreturn_t -+vc4_irq(int irq, void *arg) -+{ -+ struct drm_device *dev = arg; -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ uint32_t intctl; -+ irqreturn_t status = IRQ_NONE; -+ -+ barrier(); -+ intctl = V3D_READ(V3D_INTCTL); -+ -+ /* Acknowledge the interrupts we're handling here. The render -+ * frame done interrupt will be cleared, while OUTOMEM will -+ * stay high until the underlying cause is cleared. -+ */ -+ V3D_WRITE(V3D_INTCTL, intctl); -+ -+ if (intctl & V3D_INT_OUTOMEM) { -+ /* Disable OUTOMEM until the work is done. */ -+ V3D_WRITE(V3D_INTDIS, V3D_INT_OUTOMEM); -+ schedule_work(&vc4->overflow_mem_work); -+ status = IRQ_HANDLED; -+ } -+ -+ if (intctl & V3D_INT_FRDONE) { -+ spin_lock(&vc4->job_lock); -+ vc4_irq_finish_job(dev); -+ spin_unlock(&vc4->job_lock); -+ status = IRQ_HANDLED; -+ } -+ -+ return status; -+} -+ -+void -+vc4_irq_preinstall(struct drm_device *dev) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ -+ init_waitqueue_head(&vc4->job_wait_queue); -+ INIT_WORK(&vc4->overflow_mem_work, vc4_overflow_mem_work); -+ -+ /* Clear any pending interrupts someone might have left around -+ * for us. -+ */ -+ V3D_WRITE(V3D_INTCTL, V3D_DRIVER_IRQS); -+} -+ -+int -+vc4_irq_postinstall(struct drm_device *dev) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ -+ /* Enable both the render done and out of memory interrupts. */ -+ V3D_WRITE(V3D_INTENA, V3D_DRIVER_IRQS); -+ -+ return 0; -+} -+ -+void -+vc4_irq_uninstall(struct drm_device *dev) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ -+ /* Disable sending interrupts for our driver's IRQs. */ -+ V3D_WRITE(V3D_INTDIS, V3D_DRIVER_IRQS); -+ -+ /* Clear any pending interrupts we might have left. */ -+ V3D_WRITE(V3D_INTCTL, V3D_DRIVER_IRQS); -+ -+ cancel_work_sync(&vc4->overflow_mem_work); -+} -+ -+/** Reinitializes interrupt registers when a GPU reset is performed. */ -+void vc4_irq_reset(struct drm_device *dev) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ unsigned long irqflags; -+ -+ /* Acknowledge any stale IRQs. */ -+ V3D_WRITE(V3D_INTCTL, V3D_DRIVER_IRQS); -+ -+ /* -+ * Turn all our interrupts on. Binner out of memory is the -+ * only one we expect to trigger at this point, since we've -+ * just come from poweron and haven't supplied any overflow -+ * memory yet. -+ */ -+ V3D_WRITE(V3D_INTENA, V3D_DRIVER_IRQS); -+ -+ spin_lock_irqsave(&vc4->job_lock, irqflags); -+ vc4_irq_finish_job(dev); -+ spin_unlock_irqrestore(&vc4->job_lock, irqflags); -+} -diff --git a/drivers/gpu/drm/vc4/vc4_packet.h b/drivers/gpu/drm/vc4/vc4_packet.h -new file mode 100644 -index 0000000..0f31cc0 ---- /dev/null -+++ b/drivers/gpu/drm/vc4/vc4_packet.h -@@ -0,0 +1,399 @@ -+/* -+ * Copyright © 2014 Broadcom -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice (including the next -+ * paragraph) shall be included in all copies or substantial portions of the -+ * Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -+ * IN THE SOFTWARE. -+ */ -+ -+#ifndef VC4_PACKET_H -+#define VC4_PACKET_H -+ -+#include "vc4_regs.h" /* for VC4_MASK, VC4_GET_FIELD, VC4_SET_FIELD */ -+ -+enum vc4_packet { -+ VC4_PACKET_HALT = 0, -+ VC4_PACKET_NOP = 1, -+ -+ VC4_PACKET_FLUSH = 4, -+ VC4_PACKET_FLUSH_ALL = 5, -+ VC4_PACKET_START_TILE_BINNING = 6, -+ VC4_PACKET_INCREMENT_SEMAPHORE = 7, -+ VC4_PACKET_WAIT_ON_SEMAPHORE = 8, -+ -+ VC4_PACKET_BRANCH = 16, -+ VC4_PACKET_BRANCH_TO_SUB_LIST = 17, -+ -+ VC4_PACKET_STORE_MS_TILE_BUFFER = 24, -+ VC4_PACKET_STORE_MS_TILE_BUFFER_AND_EOF = 25, -+ VC4_PACKET_STORE_FULL_RES_TILE_BUFFER = 26, -+ VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER = 27, -+ VC4_PACKET_STORE_TILE_BUFFER_GENERAL = 28, -+ VC4_PACKET_LOAD_TILE_BUFFER_GENERAL = 29, -+ -+ VC4_PACKET_GL_INDEXED_PRIMITIVE = 32, -+ VC4_PACKET_GL_ARRAY_PRIMITIVE = 33, -+ -+ VC4_PACKET_COMPRESSED_PRIMITIVE = 48, -+ VC4_PACKET_CLIPPED_COMPRESSED_PRIMITIVE = 49, -+ -+ VC4_PACKET_PRIMITIVE_LIST_FORMAT = 56, -+ -+ VC4_PACKET_GL_SHADER_STATE = 64, -+ VC4_PACKET_NV_SHADER_STATE = 65, -+ VC4_PACKET_VG_SHADER_STATE = 66, -+ -+ VC4_PACKET_CONFIGURATION_BITS = 96, -+ VC4_PACKET_FLAT_SHADE_FLAGS = 97, -+ VC4_PACKET_POINT_SIZE = 98, -+ VC4_PACKET_LINE_WIDTH = 99, -+ VC4_PACKET_RHT_X_BOUNDARY = 100, -+ VC4_PACKET_DEPTH_OFFSET = 101, -+ VC4_PACKET_CLIP_WINDOW = 102, -+ VC4_PACKET_VIEWPORT_OFFSET = 103, -+ VC4_PACKET_Z_CLIPPING = 104, -+ VC4_PACKET_CLIPPER_XY_SCALING = 105, -+ VC4_PACKET_CLIPPER_Z_SCALING = 106, -+ -+ VC4_PACKET_TILE_BINNING_MODE_CONFIG = 112, -+ VC4_PACKET_TILE_RENDERING_MODE_CONFIG = 113, -+ VC4_PACKET_CLEAR_COLORS = 114, -+ VC4_PACKET_TILE_COORDINATES = 115, -+ -+ /* Not an actual hardware packet -- this is what we use to put -+ * references to GEM bos in the command stream, since we need the u32 -+ * int the actual address packet in order to store the offset from the -+ * start of the BO. -+ */ -+ VC4_PACKET_GEM_HANDLES = 254, -+} __attribute__ ((__packed__)); -+ -+#define VC4_PACKET_HALT_SIZE 1 -+#define VC4_PACKET_NOP_SIZE 1 -+#define VC4_PACKET_FLUSH_SIZE 1 -+#define VC4_PACKET_FLUSH_ALL_SIZE 1 -+#define VC4_PACKET_START_TILE_BINNING_SIZE 1 -+#define VC4_PACKET_INCREMENT_SEMAPHORE_SIZE 1 -+#define VC4_PACKET_WAIT_ON_SEMAPHORE_SIZE 1 -+#define VC4_PACKET_BRANCH_SIZE 5 -+#define VC4_PACKET_BRANCH_TO_SUB_LIST_SIZE 5 -+#define VC4_PACKET_STORE_MS_TILE_BUFFER_SIZE 1 -+#define VC4_PACKET_STORE_MS_TILE_BUFFER_AND_EOF_SIZE 1 -+#define VC4_PACKET_STORE_FULL_RES_TILE_BUFFER_SIZE 5 -+#define VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER_SIZE 5 -+#define VC4_PACKET_STORE_TILE_BUFFER_GENERAL_SIZE 7 -+#define VC4_PACKET_LOAD_TILE_BUFFER_GENERAL_SIZE 7 -+#define VC4_PACKET_GL_INDEXED_PRIMITIVE_SIZE 14 -+#define VC4_PACKET_GL_ARRAY_PRIMITIVE_SIZE 10 -+#define VC4_PACKET_COMPRESSED_PRIMITIVE_SIZE 1 -+#define VC4_PACKET_CLIPPED_COMPRESSED_PRIMITIVE_SIZE 1 -+#define VC4_PACKET_PRIMITIVE_LIST_FORMAT_SIZE 2 -+#define VC4_PACKET_GL_SHADER_STATE_SIZE 5 -+#define VC4_PACKET_NV_SHADER_STATE_SIZE 5 -+#define VC4_PACKET_VG_SHADER_STATE_SIZE 5 -+#define VC4_PACKET_CONFIGURATION_BITS_SIZE 4 -+#define VC4_PACKET_FLAT_SHADE_FLAGS_SIZE 5 -+#define VC4_PACKET_POINT_SIZE_SIZE 5 -+#define VC4_PACKET_LINE_WIDTH_SIZE 5 -+#define VC4_PACKET_RHT_X_BOUNDARY_SIZE 3 -+#define VC4_PACKET_DEPTH_OFFSET_SIZE 5 -+#define VC4_PACKET_CLIP_WINDOW_SIZE 9 -+#define VC4_PACKET_VIEWPORT_OFFSET_SIZE 5 -+#define VC4_PACKET_Z_CLIPPING_SIZE 9 -+#define VC4_PACKET_CLIPPER_XY_SCALING_SIZE 9 -+#define VC4_PACKET_CLIPPER_Z_SCALING_SIZE 9 -+#define VC4_PACKET_TILE_BINNING_MODE_CONFIG_SIZE 16 -+#define VC4_PACKET_TILE_RENDERING_MODE_CONFIG_SIZE 11 -+#define VC4_PACKET_CLEAR_COLORS_SIZE 14 -+#define VC4_PACKET_TILE_COORDINATES_SIZE 3 -+#define VC4_PACKET_GEM_HANDLES_SIZE 9 -+ -+/* Number of multisamples supported. */ -+#define VC4_MAX_SAMPLES 4 -+/* Size of a full resolution color or Z tile buffer load/store. */ -+#define VC4_TILE_BUFFER_SIZE (64 * 64 * 4) -+ -+/** @{ -+ * Bits used by packets like VC4_PACKET_STORE_TILE_BUFFER_GENERAL and -+ * VC4_PACKET_TILE_RENDERING_MODE_CONFIG. -+*/ -+#define VC4_TILING_FORMAT_LINEAR 0 -+#define VC4_TILING_FORMAT_T 1 -+#define VC4_TILING_FORMAT_LT 2 -+/** @} */ -+ -+/** @{ -+ * -+ * low bits of VC4_PACKET_STORE_FULL_RES_TILE_BUFFER and -+ * VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER. -+ */ -+#define VC4_LOADSTORE_FULL_RES_EOF BIT(3) -+#define VC4_LOADSTORE_FULL_RES_DISABLE_CLEAR_ALL BIT(2) -+#define VC4_LOADSTORE_FULL_RES_DISABLE_ZS BIT(1) -+#define VC4_LOADSTORE_FULL_RES_DISABLE_COLOR BIT(0) -+ -+/** @{ -+ * -+ * low bits of VC4_PACKET_STORE_FULL_RES_TILE_BUFFER and -+ * VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER. -+ */ -+#define VC4_LOADSTORE_FULL_RES_EOF BIT(3) -+#define VC4_LOADSTORE_FULL_RES_DISABLE_CLEAR_ALL BIT(2) -+#define VC4_LOADSTORE_FULL_RES_DISABLE_ZS BIT(1) -+#define VC4_LOADSTORE_FULL_RES_DISABLE_COLOR BIT(0) -+ -+/** @{ -+ * -+ * byte 2 of VC4_PACKET_STORE_TILE_BUFFER_GENERAL and -+ * VC4_PACKET_LOAD_TILE_BUFFER_GENERAL (low bits of the address) -+ */ -+ -+#define VC4_LOADSTORE_TILE_BUFFER_EOF BIT(3) -+#define VC4_LOADSTORE_TILE_BUFFER_DISABLE_FULL_VG_MASK BIT(2) -+#define VC4_LOADSTORE_TILE_BUFFER_DISABLE_FULL_ZS BIT(1) -+#define VC4_LOADSTORE_TILE_BUFFER_DISABLE_FULL_COLOR BIT(0) -+ -+/** @} */ -+ -+/** @{ -+ * -+ * byte 0-1 of VC4_PACKET_STORE_TILE_BUFFER_GENERAL and -+ * VC4_PACKET_LOAD_TILE_BUFFER_GENERAL -+ */ -+#define VC4_STORE_TILE_BUFFER_DISABLE_VG_MASK_CLEAR BIT(15) -+#define VC4_STORE_TILE_BUFFER_DISABLE_ZS_CLEAR BIT(14) -+#define VC4_STORE_TILE_BUFFER_DISABLE_COLOR_CLEAR BIT(13) -+#define VC4_STORE_TILE_BUFFER_DISABLE_SWAP BIT(12) -+ -+#define VC4_LOADSTORE_TILE_BUFFER_FORMAT_MASK VC4_MASK(9, 8) -+#define VC4_LOADSTORE_TILE_BUFFER_FORMAT_SHIFT 8 -+#define VC4_LOADSTORE_TILE_BUFFER_RGBA8888 0 -+#define VC4_LOADSTORE_TILE_BUFFER_BGR565_DITHER 1 -+#define VC4_LOADSTORE_TILE_BUFFER_BGR565 2 -+/** @} */ -+ -+/** @{ -+ * -+ * byte 0 of VC4_PACKET_STORE_TILE_BUFFER_GENERAL and -+ * VC4_PACKET_LOAD_TILE_BUFFER_GENERAL -+ */ -+#define VC4_STORE_TILE_BUFFER_MODE_MASK VC4_MASK(7, 6) -+#define VC4_STORE_TILE_BUFFER_MODE_SHIFT 6 -+#define VC4_STORE_TILE_BUFFER_MODE_SAMPLE0 (0 << 6) -+#define VC4_STORE_TILE_BUFFER_MODE_DECIMATE_X4 (1 << 6) -+#define VC4_STORE_TILE_BUFFER_MODE_DECIMATE_X16 (2 << 6) -+ -+/** The values of the field are VC4_TILING_FORMAT_* */ -+#define VC4_LOADSTORE_TILE_BUFFER_TILING_MASK VC4_MASK(5, 4) -+#define VC4_LOADSTORE_TILE_BUFFER_TILING_SHIFT 4 -+ -+#define VC4_LOADSTORE_TILE_BUFFER_BUFFER_MASK VC4_MASK(2, 0) -+#define VC4_LOADSTORE_TILE_BUFFER_BUFFER_SHIFT 0 -+#define VC4_LOADSTORE_TILE_BUFFER_NONE 0 -+#define VC4_LOADSTORE_TILE_BUFFER_COLOR 1 -+#define VC4_LOADSTORE_TILE_BUFFER_ZS 2 -+#define VC4_LOADSTORE_TILE_BUFFER_Z 3 -+#define VC4_LOADSTORE_TILE_BUFFER_VG_MASK 4 -+#define VC4_LOADSTORE_TILE_BUFFER_FULL 5 -+/** @} */ -+ -+#define VC4_INDEX_BUFFER_U8 (0 << 4) -+#define VC4_INDEX_BUFFER_U16 (1 << 4) -+ -+/* This flag is only present in NV shader state. */ -+#define VC4_SHADER_FLAG_SHADED_CLIP_COORDS BIT(3) -+#define VC4_SHADER_FLAG_ENABLE_CLIPPING BIT(2) -+#define VC4_SHADER_FLAG_VS_POINT_SIZE BIT(1) -+#define VC4_SHADER_FLAG_FS_SINGLE_THREAD BIT(0) -+ -+/** @{ byte 2 of config bits. */ -+#define VC4_CONFIG_BITS_EARLY_Z_UPDATE BIT(1) -+#define VC4_CONFIG_BITS_EARLY_Z BIT(0) -+/** @} */ -+ -+/** @{ byte 1 of config bits. */ -+#define VC4_CONFIG_BITS_Z_UPDATE BIT(7) -+/** same values in this 3-bit field as PIPE_FUNC_* */ -+#define VC4_CONFIG_BITS_DEPTH_FUNC_SHIFT 4 -+#define VC4_CONFIG_BITS_COVERAGE_READ_LEAVE BIT(3) -+ -+#define VC4_CONFIG_BITS_COVERAGE_UPDATE_NONZERO (0 << 1) -+#define VC4_CONFIG_BITS_COVERAGE_UPDATE_ODD (1 << 1) -+#define VC4_CONFIG_BITS_COVERAGE_UPDATE_OR (2 << 1) -+#define VC4_CONFIG_BITS_COVERAGE_UPDATE_ZERO (3 << 1) -+ -+#define VC4_CONFIG_BITS_COVERAGE_PIPE_SELECT BIT(0) -+/** @} */ -+ -+/** @{ byte 0 of config bits. */ -+#define VC4_CONFIG_BITS_RASTERIZER_OVERSAMPLE_NONE (0 << 6) -+#define VC4_CONFIG_BITS_RASTERIZER_OVERSAMPLE_4X (1 << 6) -+#define VC4_CONFIG_BITS_RASTERIZER_OVERSAMPLE_16X (2 << 6) -+ -+#define VC4_CONFIG_BITS_AA_POINTS_AND_LINES BIT(4) -+#define VC4_CONFIG_BITS_ENABLE_DEPTH_OFFSET BIT(3) -+#define VC4_CONFIG_BITS_CW_PRIMITIVES BIT(2) -+#define VC4_CONFIG_BITS_ENABLE_PRIM_BACK BIT(1) -+#define VC4_CONFIG_BITS_ENABLE_PRIM_FRONT BIT(0) -+/** @} */ -+ -+/** @{ bits in the last u8 of VC4_PACKET_TILE_BINNING_MODE_CONFIG */ -+#define VC4_BIN_CONFIG_DB_NON_MS BIT(7) -+ -+#define VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_MASK VC4_MASK(6, 5) -+#define VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_SHIFT 5 -+#define VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_32 0 -+#define VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_64 1 -+#define VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_128 2 -+#define VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_256 3 -+ -+#define VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_MASK VC4_MASK(4, 3) -+#define VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_SHIFT 3 -+#define VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_32 0 -+#define VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_64 1 -+#define VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_128 2 -+#define VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_256 3 -+ -+#define VC4_BIN_CONFIG_AUTO_INIT_TSDA BIT(2) -+#define VC4_BIN_CONFIG_TILE_BUFFER_64BIT BIT(1) -+#define VC4_BIN_CONFIG_MS_MODE_4X BIT(0) -+/** @} */ -+ -+/** @{ bits in the last u16 of VC4_PACKET_TILE_RENDERING_MODE_CONFIG */ -+#define VC4_RENDER_CONFIG_DB_NON_MS BIT(12) -+#define VC4_RENDER_CONFIG_EARLY_Z_COVERAGE_DISABLE BIT(11) -+#define VC4_RENDER_CONFIG_EARLY_Z_DIRECTION_G BIT(10) -+#define VC4_RENDER_CONFIG_COVERAGE_MODE BIT(9) -+#define VC4_RENDER_CONFIG_ENABLE_VG_MASK BIT(8) -+ -+/** The values of the field are VC4_TILING_FORMAT_* */ -+#define VC4_RENDER_CONFIG_MEMORY_FORMAT_MASK VC4_MASK(7, 6) -+#define VC4_RENDER_CONFIG_MEMORY_FORMAT_SHIFT 6 -+ -+#define VC4_RENDER_CONFIG_DECIMATE_MODE_1X (0 << 4) -+#define VC4_RENDER_CONFIG_DECIMATE_MODE_4X (1 << 4) -+#define VC4_RENDER_CONFIG_DECIMATE_MODE_16X (2 << 4) -+ -+#define VC4_RENDER_CONFIG_FORMAT_MASK VC4_MASK(3, 2) -+#define VC4_RENDER_CONFIG_FORMAT_SHIFT 2 -+#define VC4_RENDER_CONFIG_FORMAT_BGR565_DITHERED 0 -+#define VC4_RENDER_CONFIG_FORMAT_RGBA8888 1 -+#define VC4_RENDER_CONFIG_FORMAT_BGR565 2 -+ -+#define VC4_RENDER_CONFIG_TILE_BUFFER_64BIT BIT(1) -+#define VC4_RENDER_CONFIG_MS_MODE_4X BIT(0) -+ -+#define VC4_PRIMITIVE_LIST_FORMAT_16_INDEX (1 << 4) -+#define VC4_PRIMITIVE_LIST_FORMAT_32_XY (3 << 4) -+#define VC4_PRIMITIVE_LIST_FORMAT_TYPE_POINTS (0 << 0) -+#define VC4_PRIMITIVE_LIST_FORMAT_TYPE_LINES (1 << 0) -+#define VC4_PRIMITIVE_LIST_FORMAT_TYPE_TRIANGLES (2 << 0) -+#define VC4_PRIMITIVE_LIST_FORMAT_TYPE_RHT (3 << 0) -+ -+enum vc4_texture_data_type { -+ VC4_TEXTURE_TYPE_RGBA8888 = 0, -+ VC4_TEXTURE_TYPE_RGBX8888 = 1, -+ VC4_TEXTURE_TYPE_RGBA4444 = 2, -+ VC4_TEXTURE_TYPE_RGBA5551 = 3, -+ VC4_TEXTURE_TYPE_RGB565 = 4, -+ VC4_TEXTURE_TYPE_LUMINANCE = 5, -+ VC4_TEXTURE_TYPE_ALPHA = 6, -+ VC4_TEXTURE_TYPE_LUMALPHA = 7, -+ VC4_TEXTURE_TYPE_ETC1 = 8, -+ VC4_TEXTURE_TYPE_S16F = 9, -+ VC4_TEXTURE_TYPE_S8 = 10, -+ VC4_TEXTURE_TYPE_S16 = 11, -+ VC4_TEXTURE_TYPE_BW1 = 12, -+ VC4_TEXTURE_TYPE_A4 = 13, -+ VC4_TEXTURE_TYPE_A1 = 14, -+ VC4_TEXTURE_TYPE_RGBA64 = 15, -+ VC4_TEXTURE_TYPE_RGBA32R = 16, -+ VC4_TEXTURE_TYPE_YUV422R = 17, -+}; -+ -+#define VC4_TEX_P0_OFFSET_MASK VC4_MASK(31, 12) -+#define VC4_TEX_P0_OFFSET_SHIFT 12 -+#define VC4_TEX_P0_CSWIZ_MASK VC4_MASK(11, 10) -+#define VC4_TEX_P0_CSWIZ_SHIFT 10 -+#define VC4_TEX_P0_CMMODE_MASK VC4_MASK(9, 9) -+#define VC4_TEX_P0_CMMODE_SHIFT 9 -+#define VC4_TEX_P0_FLIPY_MASK VC4_MASK(8, 8) -+#define VC4_TEX_P0_FLIPY_SHIFT 8 -+#define VC4_TEX_P0_TYPE_MASK VC4_MASK(7, 4) -+#define VC4_TEX_P0_TYPE_SHIFT 4 -+#define VC4_TEX_P0_MIPLVLS_MASK VC4_MASK(3, 0) -+#define VC4_TEX_P0_MIPLVLS_SHIFT 0 -+ -+#define VC4_TEX_P1_TYPE4_MASK VC4_MASK(31, 31) -+#define VC4_TEX_P1_TYPE4_SHIFT 31 -+#define VC4_TEX_P1_HEIGHT_MASK VC4_MASK(30, 20) -+#define VC4_TEX_P1_HEIGHT_SHIFT 20 -+#define VC4_TEX_P1_ETCFLIP_MASK VC4_MASK(19, 19) -+#define VC4_TEX_P1_ETCFLIP_SHIFT 19 -+#define VC4_TEX_P1_WIDTH_MASK VC4_MASK(18, 8) -+#define VC4_TEX_P1_WIDTH_SHIFT 8 -+ -+#define VC4_TEX_P1_MAGFILT_MASK VC4_MASK(7, 7) -+#define VC4_TEX_P1_MAGFILT_SHIFT 7 -+# define VC4_TEX_P1_MAGFILT_LINEAR 0 -+# define VC4_TEX_P1_MAGFILT_NEAREST 1 -+ -+#define VC4_TEX_P1_MINFILT_MASK VC4_MASK(6, 4) -+#define VC4_TEX_P1_MINFILT_SHIFT 4 -+# define VC4_TEX_P1_MINFILT_LINEAR 0 -+# define VC4_TEX_P1_MINFILT_NEAREST 1 -+# define VC4_TEX_P1_MINFILT_NEAR_MIP_NEAR 2 -+# define VC4_TEX_P1_MINFILT_NEAR_MIP_LIN 3 -+# define VC4_TEX_P1_MINFILT_LIN_MIP_NEAR 4 -+# define VC4_TEX_P1_MINFILT_LIN_MIP_LIN 5 -+ -+#define VC4_TEX_P1_WRAP_T_MASK VC4_MASK(3, 2) -+#define VC4_TEX_P1_WRAP_T_SHIFT 2 -+#define VC4_TEX_P1_WRAP_S_MASK VC4_MASK(1, 0) -+#define VC4_TEX_P1_WRAP_S_SHIFT 0 -+# define VC4_TEX_P1_WRAP_REPEAT 0 -+# define VC4_TEX_P1_WRAP_CLAMP 1 -+# define VC4_TEX_P1_WRAP_MIRROR 2 -+# define VC4_TEX_P1_WRAP_BORDER 3 -+ -+#define VC4_TEX_P2_PTYPE_MASK VC4_MASK(31, 30) -+#define VC4_TEX_P2_PTYPE_SHIFT 30 -+# define VC4_TEX_P2_PTYPE_IGNORED 0 -+# define VC4_TEX_P2_PTYPE_CUBE_MAP_STRIDE 1 -+# define VC4_TEX_P2_PTYPE_CHILD_IMAGE_DIMENSIONS 2 -+# define VC4_TEX_P2_PTYPE_CHILD_IMAGE_OFFSETS 3 -+ -+/* VC4_TEX_P2_PTYPE_CUBE_MAP_STRIDE bits */ -+#define VC4_TEX_P2_CMST_MASK VC4_MASK(29, 12) -+#define VC4_TEX_P2_CMST_SHIFT 12 -+#define VC4_TEX_P2_BSLOD_MASK VC4_MASK(0, 0) -+#define VC4_TEX_P2_BSLOD_SHIFT 0 -+ -+/* VC4_TEX_P2_PTYPE_CHILD_IMAGE_DIMENSIONS */ -+#define VC4_TEX_P2_CHEIGHT_MASK VC4_MASK(22, 12) -+#define VC4_TEX_P2_CHEIGHT_SHIFT 12 -+#define VC4_TEX_P2_CWIDTH_MASK VC4_MASK(10, 0) -+#define VC4_TEX_P2_CWIDTH_SHIFT 0 -+ -+/* VC4_TEX_P2_PTYPE_CHILD_IMAGE_OFFSETS */ -+#define VC4_TEX_P2_CYOFF_MASK VC4_MASK(22, 12) -+#define VC4_TEX_P2_CYOFF_SHIFT 12 -+#define VC4_TEX_P2_CXOFF_MASK VC4_MASK(10, 0) -+#define VC4_TEX_P2_CXOFF_SHIFT 0 -+ -+#endif /* VC4_PACKET_H */ -diff --git a/drivers/gpu/drm/vc4/vc4_render_cl.c b/drivers/gpu/drm/vc4/vc4_render_cl.c -new file mode 100644 -index 0000000..8a2a312 ---- /dev/null -+++ b/drivers/gpu/drm/vc4/vc4_render_cl.c -@@ -0,0 +1,634 @@ -+/* -+ * Copyright © 2014-2015 Broadcom -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice (including the next -+ * paragraph) shall be included in all copies or substantial portions of the -+ * Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -+ * IN THE SOFTWARE. -+ */ -+ -+/** -+ * DOC: Render command list generation -+ * -+ * In the VC4 driver, render command list generation is performed by the -+ * kernel instead of userspace. We do this because validating a -+ * user-submitted command list is hard to get right and has high CPU overhead, -+ * while the number of valid configurations for render command lists is -+ * actually fairly low. -+ */ -+ -+#include "uapi/drm/vc4_drm.h" -+#include "vc4_drv.h" -+#include "vc4_packet.h" -+ -+struct vc4_rcl_setup { -+ struct drm_gem_cma_object *color_read; -+ struct drm_gem_cma_object *color_write; -+ struct drm_gem_cma_object *zs_read; -+ struct drm_gem_cma_object *zs_write; -+ struct drm_gem_cma_object *msaa_color_write; -+ struct drm_gem_cma_object *msaa_zs_write; -+ -+ struct drm_gem_cma_object *rcl; -+ u32 next_offset; -+}; -+ -+static inline void rcl_u8(struct vc4_rcl_setup *setup, u8 val) -+{ -+ *(u8 *)(setup->rcl->vaddr + setup->next_offset) = val; -+ setup->next_offset += 1; -+} -+ -+static inline void rcl_u16(struct vc4_rcl_setup *setup, u16 val) -+{ -+ *(u16 *)(setup->rcl->vaddr + setup->next_offset) = val; -+ setup->next_offset += 2; -+} -+ -+static inline void rcl_u32(struct vc4_rcl_setup *setup, u32 val) -+{ -+ *(u32 *)(setup->rcl->vaddr + setup->next_offset) = val; -+ setup->next_offset += 4; -+} -+ -+/* -+ * Emits a no-op STORE_TILE_BUFFER_GENERAL. -+ * -+ * If we emit a PACKET_TILE_COORDINATES, it must be followed by a store of -+ * some sort before another load is triggered. -+ */ -+static void vc4_store_before_load(struct vc4_rcl_setup *setup) -+{ -+ rcl_u8(setup, VC4_PACKET_STORE_TILE_BUFFER_GENERAL); -+ rcl_u16(setup, -+ VC4_SET_FIELD(VC4_LOADSTORE_TILE_BUFFER_NONE, -+ VC4_LOADSTORE_TILE_BUFFER_BUFFER) | -+ VC4_STORE_TILE_BUFFER_DISABLE_COLOR_CLEAR | -+ VC4_STORE_TILE_BUFFER_DISABLE_ZS_CLEAR | -+ VC4_STORE_TILE_BUFFER_DISABLE_VG_MASK_CLEAR); -+ rcl_u32(setup, 0); /* no address, since we're in None mode */ -+} -+ -+/* -+ * Calculates the physical address of the start of a tile in a RCL surface. -+ * -+ * Unlike the other load/store packets, -+ * VC4_PACKET_LOAD/STORE_FULL_RES_TILE_BUFFER don't look at the tile -+ * coordinates packet, and instead just store to the address given. -+ */ -+static uint32_t vc4_full_res_offset(struct vc4_exec_info *exec, -+ struct drm_gem_cma_object *bo, -+ struct drm_vc4_submit_rcl_surface *surf, -+ uint8_t x, uint8_t y) -+{ -+ return bo->paddr + surf->offset + VC4_TILE_BUFFER_SIZE * -+ (DIV_ROUND_UP(exec->args->width, 32) * y + x); -+} -+ -+/* -+ * Emits a PACKET_TILE_COORDINATES if one isn't already pending. -+ * -+ * The tile coordinates packet triggers a pending load if there is one, are -+ * used for clipping during rendering, and determine where loads/stores happen -+ * relative to their base address. -+ */ -+static void vc4_tile_coordinates(struct vc4_rcl_setup *setup, -+ uint32_t x, uint32_t y) -+{ -+ rcl_u8(setup, VC4_PACKET_TILE_COORDINATES); -+ rcl_u8(setup, x); -+ rcl_u8(setup, y); -+} -+ -+static void emit_tile(struct vc4_exec_info *exec, -+ struct vc4_rcl_setup *setup, -+ uint8_t x, uint8_t y, bool first, bool last) -+{ -+ struct drm_vc4_submit_cl *args = exec->args; -+ bool has_bin = args->bin_cl_size != 0; -+ -+ /* Note that the load doesn't actually occur until the -+ * tile coords packet is processed, and only one load -+ * may be outstanding at a time. -+ */ -+ if (setup->color_read) { -+ if (args->color_read.flags & -+ VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES) { -+ rcl_u8(setup, VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER); -+ rcl_u32(setup, -+ vc4_full_res_offset(exec, setup->color_read, -+ &args->color_read, x, y) | -+ VC4_LOADSTORE_FULL_RES_DISABLE_ZS); -+ } else { -+ rcl_u8(setup, VC4_PACKET_LOAD_TILE_BUFFER_GENERAL); -+ rcl_u16(setup, args->color_read.bits); -+ rcl_u32(setup, setup->color_read->paddr + -+ args->color_read.offset); -+ } -+ } -+ -+ if (setup->zs_read) { -+ if (args->zs_read.flags & -+ VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES) { -+ rcl_u8(setup, VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER); -+ rcl_u32(setup, -+ vc4_full_res_offset(exec, setup->zs_read, -+ &args->zs_read, x, y) | -+ VC4_LOADSTORE_FULL_RES_DISABLE_COLOR); -+ } else { -+ if (setup->color_read) { -+ /* Exec previous load. */ -+ vc4_tile_coordinates(setup, x, y); -+ vc4_store_before_load(setup); -+ } -+ -+ rcl_u8(setup, VC4_PACKET_LOAD_TILE_BUFFER_GENERAL); -+ rcl_u16(setup, args->zs_read.bits); -+ rcl_u32(setup, setup->zs_read->paddr + -+ args->zs_read.offset); -+ } -+ } -+ -+ /* Clipping depends on tile coordinates having been -+ * emitted, so we always need one here. -+ */ -+ vc4_tile_coordinates(setup, x, y); -+ -+ /* Wait for the binner before jumping to the first -+ * tile's lists. -+ */ -+ if (first && has_bin) -+ rcl_u8(setup, VC4_PACKET_WAIT_ON_SEMAPHORE); -+ -+ if (has_bin) { -+ rcl_u8(setup, VC4_PACKET_BRANCH_TO_SUB_LIST); -+ rcl_u32(setup, (exec->tile_bo->paddr + -+ exec->tile_alloc_offset + -+ (y * exec->bin_tiles_x + x) * 32)); -+ } -+ -+ if (setup->msaa_color_write) { -+ bool last_tile_write = (!setup->msaa_zs_write && -+ !setup->zs_write && -+ !setup->color_write); -+ uint32_t bits = VC4_LOADSTORE_FULL_RES_DISABLE_ZS; -+ -+ if (!last_tile_write) -+ bits |= VC4_LOADSTORE_FULL_RES_DISABLE_CLEAR_ALL; -+ else if (last) -+ bits |= VC4_LOADSTORE_FULL_RES_EOF; -+ rcl_u8(setup, VC4_PACKET_STORE_FULL_RES_TILE_BUFFER); -+ rcl_u32(setup, -+ vc4_full_res_offset(exec, setup->msaa_color_write, -+ &args->msaa_color_write, x, y) | -+ bits); -+ } -+ -+ if (setup->msaa_zs_write) { -+ bool last_tile_write = (!setup->zs_write && -+ !setup->color_write); -+ uint32_t bits = VC4_LOADSTORE_FULL_RES_DISABLE_COLOR; -+ -+ if (setup->msaa_color_write) -+ vc4_tile_coordinates(setup, x, y); -+ if (!last_tile_write) -+ bits |= VC4_LOADSTORE_FULL_RES_DISABLE_CLEAR_ALL; -+ else if (last) -+ bits |= VC4_LOADSTORE_FULL_RES_EOF; -+ rcl_u8(setup, VC4_PACKET_STORE_FULL_RES_TILE_BUFFER); -+ rcl_u32(setup, -+ vc4_full_res_offset(exec, setup->msaa_zs_write, -+ &args->msaa_zs_write, x, y) | -+ bits); -+ } -+ -+ if (setup->zs_write) { -+ bool last_tile_write = !setup->color_write; -+ -+ if (setup->msaa_color_write || setup->msaa_zs_write) -+ vc4_tile_coordinates(setup, x, y); -+ -+ rcl_u8(setup, VC4_PACKET_STORE_TILE_BUFFER_GENERAL); -+ rcl_u16(setup, args->zs_write.bits | -+ (last_tile_write ? -+ 0 : VC4_STORE_TILE_BUFFER_DISABLE_COLOR_CLEAR)); -+ rcl_u32(setup, -+ (setup->zs_write->paddr + args->zs_write.offset) | -+ ((last && last_tile_write) ? -+ VC4_LOADSTORE_TILE_BUFFER_EOF : 0)); -+ } -+ -+ if (setup->color_write) { -+ if (setup->msaa_color_write || setup->msaa_zs_write || -+ setup->zs_write) { -+ vc4_tile_coordinates(setup, x, y); -+ } -+ -+ if (last) -+ rcl_u8(setup, VC4_PACKET_STORE_MS_TILE_BUFFER_AND_EOF); -+ else -+ rcl_u8(setup, VC4_PACKET_STORE_MS_TILE_BUFFER); -+ } -+} -+ -+static int vc4_create_rcl_bo(struct drm_device *dev, struct vc4_exec_info *exec, -+ struct vc4_rcl_setup *setup) -+{ -+ struct drm_vc4_submit_cl *args = exec->args; -+ bool has_bin = args->bin_cl_size != 0; -+ uint8_t min_x_tile = args->min_x_tile; -+ uint8_t min_y_tile = args->min_y_tile; -+ uint8_t max_x_tile = args->max_x_tile; -+ uint8_t max_y_tile = args->max_y_tile; -+ uint8_t xtiles = max_x_tile - min_x_tile + 1; -+ uint8_t ytiles = max_y_tile - min_y_tile + 1; -+ uint8_t x, y; -+ uint32_t size, loop_body_size; -+ -+ size = VC4_PACKET_TILE_RENDERING_MODE_CONFIG_SIZE; -+ loop_body_size = VC4_PACKET_TILE_COORDINATES_SIZE; -+ -+ if (args->flags & VC4_SUBMIT_CL_USE_CLEAR_COLOR) { -+ size += VC4_PACKET_CLEAR_COLORS_SIZE + -+ VC4_PACKET_TILE_COORDINATES_SIZE + -+ VC4_PACKET_STORE_TILE_BUFFER_GENERAL_SIZE; -+ } -+ -+ if (setup->color_read) { -+ if (args->color_read.flags & -+ VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES) { -+ loop_body_size += VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER_SIZE; -+ } else { -+ loop_body_size += VC4_PACKET_LOAD_TILE_BUFFER_GENERAL_SIZE; -+ } -+ } -+ if (setup->zs_read) { -+ if (args->zs_read.flags & -+ VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES) { -+ loop_body_size += VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER_SIZE; -+ } else { -+ if (setup->color_read && -+ !(args->color_read.flags & -+ VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES)) { -+ loop_body_size += VC4_PACKET_TILE_COORDINATES_SIZE; -+ loop_body_size += VC4_PACKET_STORE_TILE_BUFFER_GENERAL_SIZE; -+ } -+ loop_body_size += VC4_PACKET_LOAD_TILE_BUFFER_GENERAL_SIZE; -+ } -+ } -+ -+ if (has_bin) { -+ size += VC4_PACKET_WAIT_ON_SEMAPHORE_SIZE; -+ loop_body_size += VC4_PACKET_BRANCH_TO_SUB_LIST_SIZE; -+ } -+ -+ if (setup->msaa_color_write) -+ loop_body_size += VC4_PACKET_STORE_FULL_RES_TILE_BUFFER_SIZE; -+ if (setup->msaa_zs_write) -+ loop_body_size += VC4_PACKET_STORE_FULL_RES_TILE_BUFFER_SIZE; -+ -+ if (setup->zs_write) -+ loop_body_size += VC4_PACKET_STORE_TILE_BUFFER_GENERAL_SIZE; -+ if (setup->color_write) -+ loop_body_size += VC4_PACKET_STORE_MS_TILE_BUFFER_SIZE; -+ -+ /* We need a VC4_PACKET_TILE_COORDINATES in between each store. */ -+ loop_body_size += VC4_PACKET_TILE_COORDINATES_SIZE * -+ ((setup->msaa_color_write != NULL) + -+ (setup->msaa_zs_write != NULL) + -+ (setup->color_write != NULL) + -+ (setup->zs_write != NULL) - 1); -+ -+ size += xtiles * ytiles * loop_body_size; -+ -+ setup->rcl = &vc4_bo_create(dev, size, true)->base; -+ if (!setup->rcl) -+ return -ENOMEM; -+ list_add_tail(&to_vc4_bo(&setup->rcl->base)->unref_head, -+ &exec->unref_list); -+ -+ rcl_u8(setup, VC4_PACKET_TILE_RENDERING_MODE_CONFIG); -+ rcl_u32(setup, -+ (setup->color_write ? (setup->color_write->paddr + -+ args->color_write.offset) : -+ 0)); -+ rcl_u16(setup, args->width); -+ rcl_u16(setup, args->height); -+ rcl_u16(setup, args->color_write.bits); -+ -+ /* The tile buffer gets cleared when the previous tile is stored. If -+ * the clear values changed between frames, then the tile buffer has -+ * stale clear values in it, so we have to do a store in None mode (no -+ * writes) so that we trigger the tile buffer clear. -+ */ -+ if (args->flags & VC4_SUBMIT_CL_USE_CLEAR_COLOR) { -+ rcl_u8(setup, VC4_PACKET_CLEAR_COLORS); -+ rcl_u32(setup, args->clear_color[0]); -+ rcl_u32(setup, args->clear_color[1]); -+ rcl_u32(setup, args->clear_z); -+ rcl_u8(setup, args->clear_s); -+ -+ vc4_tile_coordinates(setup, 0, 0); -+ -+ rcl_u8(setup, VC4_PACKET_STORE_TILE_BUFFER_GENERAL); -+ rcl_u16(setup, VC4_LOADSTORE_TILE_BUFFER_NONE); -+ rcl_u32(setup, 0); /* no address, since we're in None mode */ -+ } -+ -+ for (y = min_y_tile; y <= max_y_tile; y++) { -+ for (x = min_x_tile; x <= max_x_tile; x++) { -+ bool first = (x == min_x_tile && y == min_y_tile); -+ bool last = (x == max_x_tile && y == max_y_tile); -+ -+ emit_tile(exec, setup, x, y, first, last); -+ } -+ } -+ -+ BUG_ON(setup->next_offset != size); -+ exec->ct1ca = setup->rcl->paddr; -+ exec->ct1ea = setup->rcl->paddr + setup->next_offset; -+ -+ return 0; -+} -+ -+static int vc4_full_res_bounds_check(struct vc4_exec_info *exec, -+ struct drm_gem_cma_object *obj, -+ struct drm_vc4_submit_rcl_surface *surf) -+{ -+ struct drm_vc4_submit_cl *args = exec->args; -+ u32 render_tiles_stride = DIV_ROUND_UP(exec->args->width, 32); -+ -+ if (surf->offset > obj->base.size) { -+ DRM_ERROR("surface offset %d > BO size %zd\n", -+ surf->offset, obj->base.size); -+ return -EINVAL; -+ } -+ -+ if ((obj->base.size - surf->offset) / VC4_TILE_BUFFER_SIZE < -+ render_tiles_stride * args->max_y_tile + args->max_x_tile) { -+ DRM_ERROR("MSAA tile %d, %d out of bounds " -+ "(bo size %zd, offset %d).\n", -+ args->max_x_tile, args->max_y_tile, -+ obj->base.size, -+ surf->offset); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static int vc4_rcl_msaa_surface_setup(struct vc4_exec_info *exec, -+ struct drm_gem_cma_object **obj, -+ struct drm_vc4_submit_rcl_surface *surf) -+{ -+ if (surf->flags != 0 || surf->bits != 0) { -+ DRM_ERROR("MSAA surface had nonzero flags/bits\n"); -+ return -EINVAL; -+ } -+ -+ if (surf->hindex == ~0) -+ return 0; -+ -+ *obj = vc4_use_bo(exec, surf->hindex); -+ if (!*obj) -+ return -EINVAL; -+ -+ if (surf->offset & 0xf) { -+ DRM_ERROR("MSAA write must be 16b aligned.\n"); -+ return -EINVAL; -+ } -+ -+ return vc4_full_res_bounds_check(exec, *obj, surf); -+} -+ -+static int vc4_rcl_surface_setup(struct vc4_exec_info *exec, -+ struct drm_gem_cma_object **obj, -+ struct drm_vc4_submit_rcl_surface *surf) -+{ -+ uint8_t tiling = VC4_GET_FIELD(surf->bits, -+ VC4_LOADSTORE_TILE_BUFFER_TILING); -+ uint8_t buffer = VC4_GET_FIELD(surf->bits, -+ VC4_LOADSTORE_TILE_BUFFER_BUFFER); -+ uint8_t format = VC4_GET_FIELD(surf->bits, -+ VC4_LOADSTORE_TILE_BUFFER_FORMAT); -+ int cpp; -+ int ret; -+ -+ if (surf->flags & ~VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES) { -+ DRM_ERROR("Extra flags set\n"); -+ return -EINVAL; -+ } -+ -+ if (surf->hindex == ~0) -+ return 0; -+ -+ *obj = vc4_use_bo(exec, surf->hindex); -+ if (!*obj) -+ return -EINVAL; -+ -+ if (surf->flags & VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES) { -+ if (surf == &exec->args->zs_write) { -+ DRM_ERROR("general zs write may not be a full-res.\n"); -+ return -EINVAL; -+ } -+ -+ if (surf->bits != 0) { -+ DRM_ERROR("load/store general bits set with " -+ "full res load/store.\n"); -+ return -EINVAL; -+ } -+ -+ ret = vc4_full_res_bounds_check(exec, *obj, surf); -+ if (!ret) -+ return ret; -+ -+ return 0; -+ } -+ -+ if (surf->bits & ~(VC4_LOADSTORE_TILE_BUFFER_TILING_MASK | -+ VC4_LOADSTORE_TILE_BUFFER_BUFFER_MASK | -+ VC4_LOADSTORE_TILE_BUFFER_FORMAT_MASK)) { -+ DRM_ERROR("Unknown bits in load/store: 0x%04x\n", -+ surf->bits); -+ return -EINVAL; -+ } -+ -+ if (tiling > VC4_TILING_FORMAT_LT) { -+ DRM_ERROR("Bad tiling format\n"); -+ return -EINVAL; -+ } -+ -+ if (buffer == VC4_LOADSTORE_TILE_BUFFER_ZS) { -+ if (format != 0) { -+ DRM_ERROR("No color format should be set for ZS\n"); -+ return -EINVAL; -+ } -+ cpp = 4; -+ } else if (buffer == VC4_LOADSTORE_TILE_BUFFER_COLOR) { -+ switch (format) { -+ case VC4_LOADSTORE_TILE_BUFFER_BGR565: -+ case VC4_LOADSTORE_TILE_BUFFER_BGR565_DITHER: -+ cpp = 2; -+ break; -+ case VC4_LOADSTORE_TILE_BUFFER_RGBA8888: -+ cpp = 4; -+ break; -+ default: -+ DRM_ERROR("Bad tile buffer format\n"); -+ return -EINVAL; -+ } -+ } else { -+ DRM_ERROR("Bad load/store buffer %d.\n", buffer); -+ return -EINVAL; -+ } -+ -+ if (surf->offset & 0xf) { -+ DRM_ERROR("load/store buffer must be 16b aligned.\n"); -+ return -EINVAL; -+ } -+ -+ if (!vc4_check_tex_size(exec, *obj, surf->offset, tiling, -+ exec->args->width, exec->args->height, cpp)) { -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static int -+vc4_rcl_render_config_surface_setup(struct vc4_exec_info *exec, -+ struct vc4_rcl_setup *setup, -+ struct drm_gem_cma_object **obj, -+ struct drm_vc4_submit_rcl_surface *surf) -+{ -+ uint8_t tiling = VC4_GET_FIELD(surf->bits, -+ VC4_RENDER_CONFIG_MEMORY_FORMAT); -+ uint8_t format = VC4_GET_FIELD(surf->bits, -+ VC4_RENDER_CONFIG_FORMAT); -+ int cpp; -+ -+ if (surf->flags != 0) { -+ DRM_ERROR("No flags supported on render config.\n"); -+ return -EINVAL; -+ } -+ -+ if (surf->bits & ~(VC4_RENDER_CONFIG_MEMORY_FORMAT_MASK | -+ VC4_RENDER_CONFIG_FORMAT_MASK | -+ VC4_RENDER_CONFIG_MS_MODE_4X | -+ VC4_RENDER_CONFIG_DECIMATE_MODE_4X)) { -+ DRM_ERROR("Unknown bits in render config: 0x%04x\n", -+ surf->bits); -+ return -EINVAL; -+ } -+ -+ if (surf->hindex == ~0) -+ return 0; -+ -+ *obj = vc4_use_bo(exec, surf->hindex); -+ if (!*obj) -+ return -EINVAL; -+ -+ if (tiling > VC4_TILING_FORMAT_LT) { -+ DRM_ERROR("Bad tiling format\n"); -+ return -EINVAL; -+ } -+ -+ switch (format) { -+ case VC4_RENDER_CONFIG_FORMAT_BGR565_DITHERED: -+ case VC4_RENDER_CONFIG_FORMAT_BGR565: -+ cpp = 2; -+ break; -+ case VC4_RENDER_CONFIG_FORMAT_RGBA8888: -+ cpp = 4; -+ break; -+ default: -+ DRM_ERROR("Bad tile buffer format\n"); -+ return -EINVAL; -+ } -+ -+ if (!vc4_check_tex_size(exec, *obj, surf->offset, tiling, -+ exec->args->width, exec->args->height, cpp)) { -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+int vc4_get_rcl(struct drm_device *dev, struct vc4_exec_info *exec) -+{ -+ struct vc4_rcl_setup setup = {0}; -+ struct drm_vc4_submit_cl *args = exec->args; -+ bool has_bin = args->bin_cl_size != 0; -+ int ret; -+ -+ if (args->min_x_tile > args->max_x_tile || -+ args->min_y_tile > args->max_y_tile) { -+ DRM_ERROR("Bad render tile set (%d,%d)-(%d,%d)\n", -+ args->min_x_tile, args->min_y_tile, -+ args->max_x_tile, args->max_y_tile); -+ return -EINVAL; -+ } -+ -+ if (has_bin && -+ (args->max_x_tile > exec->bin_tiles_x || -+ args->max_y_tile > exec->bin_tiles_y)) { -+ DRM_ERROR("Render tiles (%d,%d) outside of bin config " -+ "(%d,%d)\n", -+ args->max_x_tile, args->max_y_tile, -+ exec->bin_tiles_x, exec->bin_tiles_y); -+ return -EINVAL; -+ } -+ -+ ret = vc4_rcl_render_config_surface_setup(exec, &setup, -+ &setup.color_write, -+ &args->color_write); -+ if (ret) -+ return ret; -+ -+ ret = vc4_rcl_surface_setup(exec, &setup.color_read, &args->color_read); -+ if (ret) -+ return ret; -+ -+ ret = vc4_rcl_surface_setup(exec, &setup.zs_read, &args->zs_read); -+ if (ret) -+ return ret; -+ -+ ret = vc4_rcl_surface_setup(exec, &setup.zs_write, &args->zs_write); -+ if (ret) -+ return ret; -+ -+ ret = vc4_rcl_msaa_surface_setup(exec, &setup.msaa_color_write, -+ &args->msaa_color_write); -+ if (ret) -+ return ret; -+ -+ ret = vc4_rcl_msaa_surface_setup(exec, &setup.msaa_zs_write, -+ &args->msaa_zs_write); -+ if (ret) -+ return ret; -+ -+ /* We shouldn't even have the job submitted to us if there's no -+ * surface to write out. -+ */ -+ if (!setup.color_write && !setup.zs_write && -+ !setup.msaa_color_write && !setup.msaa_zs_write) { -+ DRM_ERROR("RCL requires color or Z/S write\n"); -+ return -EINVAL; -+ } -+ -+ return vc4_create_rcl_bo(dev, exec, &setup); -+} -diff --git a/drivers/gpu/drm/vc4/vc4_trace.h b/drivers/gpu/drm/vc4/vc4_trace.h -new file mode 100644 -index 0000000..ad7b1ea ---- /dev/null -+++ b/drivers/gpu/drm/vc4/vc4_trace.h -@@ -0,0 +1,63 @@ -+/* -+ * Copyright (C) 2015 Broadcom -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#if !defined(_VC4_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ) -+#define _VC4_TRACE_H_ -+ -+#include -+#include -+#include -+ -+#undef TRACE_SYSTEM -+#define TRACE_SYSTEM vc4 -+#define TRACE_INCLUDE_FILE vc4_trace -+ -+TRACE_EVENT(vc4_wait_for_seqno_begin, -+ TP_PROTO(struct drm_device *dev, uint64_t seqno, uint64_t timeout), -+ TP_ARGS(dev, seqno, timeout), -+ -+ TP_STRUCT__entry( -+ __field(u32, dev) -+ __field(u64, seqno) -+ __field(u64, timeout) -+ ), -+ -+ TP_fast_assign( -+ __entry->dev = dev->primary->index; -+ __entry->seqno = seqno; -+ __entry->timeout = timeout; -+ ), -+ -+ TP_printk("dev=%u, seqno=%llu, timeout=%llu", -+ __entry->dev, __entry->seqno, __entry->timeout) -+); -+ -+TRACE_EVENT(vc4_wait_for_seqno_end, -+ TP_PROTO(struct drm_device *dev, uint64_t seqno), -+ TP_ARGS(dev, seqno), -+ -+ TP_STRUCT__entry( -+ __field(u32, dev) -+ __field(u64, seqno) -+ ), -+ -+ TP_fast_assign( -+ __entry->dev = dev->primary->index; -+ __entry->seqno = seqno; -+ ), -+ -+ TP_printk("dev=%u, seqno=%llu", -+ __entry->dev, __entry->seqno) -+); -+ -+#endif /* _VC4_TRACE_H_ */ -+ -+/* This part must be outside protection */ -+#undef TRACE_INCLUDE_PATH -+#define TRACE_INCLUDE_PATH . -+#include -diff --git a/drivers/gpu/drm/vc4/vc4_trace_points.c b/drivers/gpu/drm/vc4/vc4_trace_points.c -new file mode 100644 -index 0000000..e6278f2 ---- /dev/null -+++ b/drivers/gpu/drm/vc4/vc4_trace_points.c -@@ -0,0 +1,14 @@ -+/* -+ * Copyright (C) 2015 Broadcom -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "vc4_drv.h" -+ -+#ifndef __CHECKER__ -+#define CREATE_TRACE_POINTS -+#include "vc4_trace.h" -+#endif -diff --git a/drivers/gpu/drm/vc4/vc4_v3d.c b/drivers/gpu/drm/vc4/vc4_v3d.c -index 040ad0d..424d515 100644 ---- a/drivers/gpu/drm/vc4/vc4_v3d.c -+++ b/drivers/gpu/drm/vc4/vc4_v3d.c -@@ -144,6 +144,21 @@ int vc4_v3d_debugfs_ident(struct seq_file *m, void *unused) - } - #endif /* CONFIG_DEBUG_FS */ - -+/* -+ * Asks the firmware to turn on power to the V3D engine. -+ * -+ * This may be doable with just the clocks interface, though this -+ * packet does some other register setup from the firmware, too. -+ */ -+int -+vc4_v3d_set_power(struct vc4_dev *vc4, bool on) -+{ -+ if (on) -+ return pm_generic_poweroff(&vc4->v3d->pdev->dev); -+ else -+ return pm_generic_resume(&vc4->v3d->pdev->dev); -+} -+ - static void vc4_v3d_init_hw(struct drm_device *dev) - { - struct vc4_dev *vc4 = to_vc4_dev(dev); -@@ -161,6 +176,7 @@ static int vc4_v3d_bind(struct device *dev, struct device *master, void *data) - struct drm_device *drm = dev_get_drvdata(master); - struct vc4_dev *vc4 = to_vc4_dev(drm); - struct vc4_v3d *v3d = NULL; -+ int ret; - - v3d = devm_kzalloc(&pdev->dev, sizeof(*v3d), GFP_KERNEL); - if (!v3d) -@@ -180,8 +196,20 @@ static int vc4_v3d_bind(struct device *dev, struct device *master, void *data) - return -EINVAL; - } - -+ /* Reset the binner overflow address/size at setup, to be sure -+ * we don't reuse an old one. -+ */ -+ V3D_WRITE(V3D_BPOA, 0); -+ V3D_WRITE(V3D_BPOS, 0); -+ - vc4_v3d_init_hw(drm); - -+ ret = drm_irq_install(drm, platform_get_irq(pdev, 0)); -+ if (ret) { -+ DRM_ERROR("Failed to install IRQ handler\n"); -+ return ret; -+ } -+ - return 0; - } - -@@ -191,6 +219,15 @@ static void vc4_v3d_unbind(struct device *dev, struct device *master, - struct drm_device *drm = dev_get_drvdata(master); - struct vc4_dev *vc4 = to_vc4_dev(drm); - -+ drm_irq_uninstall(drm); -+ -+ /* Disable the binner's overflow memory address, so the next -+ * driver probe (if any) doesn't try to reuse our old -+ * allocation. -+ */ -+ V3D_WRITE(V3D_BPOA, 0); -+ V3D_WRITE(V3D_BPOS, 0); -+ - vc4->v3d = NULL; - } - -diff --git a/drivers/gpu/drm/vc4/vc4_validate.c b/drivers/gpu/drm/vc4/vc4_validate.c -new file mode 100644 -index 0000000..0fb5b99 ---- /dev/null -+++ b/drivers/gpu/drm/vc4/vc4_validate.c -@@ -0,0 +1,900 @@ -+/* -+ * Copyright © 2014 Broadcom -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice (including the next -+ * paragraph) shall be included in all copies or substantial portions of the -+ * Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -+ * IN THE SOFTWARE. -+ */ -+ -+/** -+ * Command list validator for VC4. -+ * -+ * The VC4 has no IOMMU between it and system memory. So, a user with -+ * access to execute command lists could escalate privilege by -+ * overwriting system memory (drawing to it as a framebuffer) or -+ * reading system memory it shouldn't (reading it as a texture, or -+ * uniform data, or vertex data). -+ * -+ * This validates command lists to ensure that all accesses are within -+ * the bounds of the GEM objects referenced. It explicitly whitelists -+ * packets, and looks at the offsets in any address fields to make -+ * sure they're constrained within the BOs they reference. -+ * -+ * Note that because of the validation that's happening anyway, this -+ * is where GEM relocation processing happens. -+ */ -+ -+#include "uapi/drm/vc4_drm.h" -+#include "vc4_drv.h" -+#include "vc4_packet.h" -+ -+#define VALIDATE_ARGS \ -+ struct vc4_exec_info *exec, \ -+ void *validated, \ -+ void *untrusted -+ -+/** Return the width in pixels of a 64-byte microtile. */ -+static uint32_t -+utile_width(int cpp) -+{ -+ switch (cpp) { -+ case 1: -+ case 2: -+ return 8; -+ case 4: -+ return 4; -+ case 8: -+ return 2; -+ default: -+ DRM_ERROR("unknown cpp: %d\n", cpp); -+ return 1; -+ } -+} -+ -+/** Return the height in pixels of a 64-byte microtile. */ -+static uint32_t -+utile_height(int cpp) -+{ -+ switch (cpp) { -+ case 1: -+ return 8; -+ case 2: -+ case 4: -+ case 8: -+ return 4; -+ default: -+ DRM_ERROR("unknown cpp: %d\n", cpp); -+ return 1; -+ } -+} -+ -+/** -+ * The texture unit decides what tiling format a particular miplevel is using -+ * this function, so we lay out our miptrees accordingly. -+ */ -+static bool -+size_is_lt(uint32_t width, uint32_t height, int cpp) -+{ -+ return (width <= 4 * utile_width(cpp) || -+ height <= 4 * utile_height(cpp)); -+} -+ -+struct drm_gem_cma_object * -+vc4_use_bo(struct vc4_exec_info *exec, uint32_t hindex) -+{ -+ struct drm_gem_cma_object *obj; -+ struct vc4_bo *bo; -+ -+ if (hindex >= exec->bo_count) { -+ DRM_ERROR("BO index %d greater than BO count %d\n", -+ hindex, exec->bo_count); -+ return NULL; -+ } -+ obj = exec->bo[hindex]; -+ bo = to_vc4_bo(&obj->base); -+ -+ if (bo->validated_shader) { -+ DRM_ERROR("Trying to use shader BO as something other than " -+ "a shader\n"); -+ return NULL; -+ } -+ -+ return obj; -+} -+ -+static struct drm_gem_cma_object * -+vc4_use_handle(struct vc4_exec_info *exec, uint32_t gem_handles_packet_index) -+{ -+ return vc4_use_bo(exec, exec->bo_index[gem_handles_packet_index]); -+} -+ -+static bool -+validate_bin_pos(struct vc4_exec_info *exec, void *untrusted, uint32_t pos) -+{ -+ /* Note that the untrusted pointer passed to these functions is -+ * incremented past the packet byte. -+ */ -+ return (untrusted - 1 == exec->bin_u + pos); -+} -+ -+static uint32_t -+gl_shader_rec_size(uint32_t pointer_bits) -+{ -+ uint32_t attribute_count = pointer_bits & 7; -+ bool extended = pointer_bits & 8; -+ -+ if (attribute_count == 0) -+ attribute_count = 8; -+ -+ if (extended) -+ return 100 + attribute_count * 4; -+ else -+ return 36 + attribute_count * 8; -+} -+ -+bool -+vc4_check_tex_size(struct vc4_exec_info *exec, struct drm_gem_cma_object *fbo, -+ uint32_t offset, uint8_t tiling_format, -+ uint32_t width, uint32_t height, uint8_t cpp) -+{ -+ uint32_t aligned_width, aligned_height, stride, size; -+ uint32_t utile_w = utile_width(cpp); -+ uint32_t utile_h = utile_height(cpp); -+ -+ /* The shaded vertex format stores signed 12.4 fixed point -+ * (-2048,2047) offsets from the viewport center, so we should -+ * never have a render target larger than 4096. The texture -+ * unit can only sample from 2048x2048, so it's even more -+ * restricted. This lets us avoid worrying about overflow in -+ * our math. -+ */ -+ if (width > 4096 || height > 4096) { -+ DRM_ERROR("Surface dimesions (%d,%d) too large", width, height); -+ return false; -+ } -+ -+ switch (tiling_format) { -+ case VC4_TILING_FORMAT_LINEAR: -+ aligned_width = round_up(width, utile_w); -+ aligned_height = height; -+ break; -+ case VC4_TILING_FORMAT_T: -+ aligned_width = round_up(width, utile_w * 8); -+ aligned_height = round_up(height, utile_h * 8); -+ break; -+ case VC4_TILING_FORMAT_LT: -+ aligned_width = round_up(width, utile_w); -+ aligned_height = round_up(height, utile_h); -+ break; -+ default: -+ DRM_ERROR("buffer tiling %d unsupported\n", tiling_format); -+ return false; -+ } -+ -+ stride = aligned_width * cpp; -+ size = stride * aligned_height; -+ -+ if (size + offset < size || -+ size + offset > fbo->base.size) { -+ DRM_ERROR("Overflow in %dx%d (%dx%d) fbo size (%d + %d > %zd)\n", -+ width, height, -+ aligned_width, aligned_height, -+ size, offset, fbo->base.size); -+ return false; -+ } -+ -+ return true; -+} -+ -+static int -+validate_flush(VALIDATE_ARGS) -+{ -+ if (!validate_bin_pos(exec, untrusted, exec->args->bin_cl_size - 1)) { -+ DRM_ERROR("Bin CL must end with VC4_PACKET_FLUSH\n"); -+ return -EINVAL; -+ } -+ exec->found_flush = true; -+ -+ return 0; -+} -+ -+static int -+validate_start_tile_binning(VALIDATE_ARGS) -+{ -+ if (exec->found_start_tile_binning_packet) { -+ DRM_ERROR("Duplicate VC4_PACKET_START_TILE_BINNING\n"); -+ return -EINVAL; -+ } -+ exec->found_start_tile_binning_packet = true; -+ -+ if (!exec->found_tile_binning_mode_config_packet) { -+ DRM_ERROR("missing VC4_PACKET_TILE_BINNING_MODE_CONFIG\n"); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static int -+validate_increment_semaphore(VALIDATE_ARGS) -+{ -+ if (!validate_bin_pos(exec, untrusted, exec->args->bin_cl_size - 2)) { -+ DRM_ERROR("Bin CL must end with " -+ "VC4_PACKET_INCREMENT_SEMAPHORE\n"); -+ return -EINVAL; -+ } -+ exec->found_increment_semaphore_packet = true; -+ -+ return 0; -+} -+ -+static int -+validate_indexed_prim_list(VALIDATE_ARGS) -+{ -+ struct drm_gem_cma_object *ib; -+ uint32_t length = *(uint32_t *)(untrusted + 1); -+ uint32_t offset = *(uint32_t *)(untrusted + 5); -+ uint32_t max_index = *(uint32_t *)(untrusted + 9); -+ uint32_t index_size = (*(uint8_t *)(untrusted + 0) >> 4) ? 2 : 1; -+ struct vc4_shader_state *shader_state; -+ -+ /* Check overflow condition */ -+ if (exec->shader_state_count == 0) { -+ DRM_ERROR("shader state must precede primitives\n"); -+ return -EINVAL; -+ } -+ shader_state = &exec->shader_state[exec->shader_state_count - 1]; -+ -+ if (max_index > shader_state->max_index) -+ shader_state->max_index = max_index; -+ -+ ib = vc4_use_handle(exec, 0); -+ if (!ib) -+ return -EINVAL; -+ -+ if (offset > ib->base.size || -+ (ib->base.size - offset) / index_size < length) { -+ DRM_ERROR("IB access overflow (%d + %d*%d > %zd)\n", -+ offset, length, index_size, ib->base.size); -+ return -EINVAL; -+ } -+ -+ *(uint32_t *)(validated + 5) = ib->paddr + offset; -+ -+ return 0; -+} -+ -+static int -+validate_gl_array_primitive(VALIDATE_ARGS) -+{ -+ uint32_t length = *(uint32_t *)(untrusted + 1); -+ uint32_t base_index = *(uint32_t *)(untrusted + 5); -+ uint32_t max_index; -+ struct vc4_shader_state *shader_state; -+ -+ /* Check overflow condition */ -+ if (exec->shader_state_count == 0) { -+ DRM_ERROR("shader state must precede primitives\n"); -+ return -EINVAL; -+ } -+ shader_state = &exec->shader_state[exec->shader_state_count - 1]; -+ -+ if (length + base_index < length) { -+ DRM_ERROR("primitive vertex count overflow\n"); -+ return -EINVAL; -+ } -+ max_index = length + base_index - 1; -+ -+ if (max_index > shader_state->max_index) -+ shader_state->max_index = max_index; -+ -+ return 0; -+} -+ -+static int -+validate_gl_shader_state(VALIDATE_ARGS) -+{ -+ uint32_t i = exec->shader_state_count++; -+ -+ if (i >= exec->shader_state_size) { -+ DRM_ERROR("More requests for shader states than declared\n"); -+ return -EINVAL; -+ } -+ -+ exec->shader_state[i].addr = *(uint32_t *)untrusted; -+ exec->shader_state[i].max_index = 0; -+ -+ if (exec->shader_state[i].addr & ~0xf) { -+ DRM_ERROR("high bits set in GL shader rec reference\n"); -+ return -EINVAL; -+ } -+ -+ *(uint32_t *)validated = (exec->shader_rec_p + -+ exec->shader_state[i].addr); -+ -+ exec->shader_rec_p += -+ roundup(gl_shader_rec_size(exec->shader_state[i].addr), 16); -+ -+ return 0; -+} -+ -+static int -+validate_tile_binning_config(VALIDATE_ARGS) -+{ -+ struct drm_device *dev = exec->exec_bo->base.dev; -+ struct vc4_bo *tile_bo; -+ uint8_t flags; -+ uint32_t tile_state_size, tile_alloc_size; -+ uint32_t tile_count; -+ -+ if (exec->found_tile_binning_mode_config_packet) { -+ DRM_ERROR("Duplicate VC4_PACKET_TILE_BINNING_MODE_CONFIG\n"); -+ return -EINVAL; -+ } -+ exec->found_tile_binning_mode_config_packet = true; -+ -+ exec->bin_tiles_x = *(uint8_t *)(untrusted + 12); -+ exec->bin_tiles_y = *(uint8_t *)(untrusted + 13); -+ tile_count = exec->bin_tiles_x * exec->bin_tiles_y; -+ flags = *(uint8_t *)(untrusted + 14); -+ -+ if (exec->bin_tiles_x == 0 || -+ exec->bin_tiles_y == 0) { -+ DRM_ERROR("Tile binning config of %dx%d too small\n", -+ exec->bin_tiles_x, exec->bin_tiles_y); -+ return -EINVAL; -+ } -+ -+ if (flags & (VC4_BIN_CONFIG_DB_NON_MS | -+ VC4_BIN_CONFIG_TILE_BUFFER_64BIT)) { -+ DRM_ERROR("unsupported binning config flags 0x%02x\n", flags); -+ return -EINVAL; -+ } -+ -+ /* The tile state data array is 48 bytes per tile, and we put it at -+ * the start of a BO containing both it and the tile alloc. -+ */ -+ tile_state_size = 48 * tile_count; -+ -+ /* Since the tile alloc array will follow us, align. */ -+ exec->tile_alloc_offset = roundup(tile_state_size, 4096); -+ -+ *(uint8_t *)(validated + 14) = -+ ((flags & ~(VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_MASK | -+ VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_MASK)) | -+ VC4_BIN_CONFIG_AUTO_INIT_TSDA | -+ VC4_SET_FIELD(VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_32, -+ VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE) | -+ VC4_SET_FIELD(VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_128, -+ VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE)); -+ -+ /* Initial block size. */ -+ tile_alloc_size = 32 * tile_count; -+ -+ /* -+ * The initial allocation gets rounded to the next 256 bytes before -+ * the hardware starts fulfilling further allocations. -+ */ -+ tile_alloc_size = roundup(tile_alloc_size, 256); -+ -+ /* Add space for the extra allocations. This is what gets used first, -+ * before overflow memory. It must have at least 4096 bytes, but we -+ * want to avoid overflow memory usage if possible. -+ */ -+ tile_alloc_size += 1024 * 1024; -+ -+ tile_bo = vc4_bo_create(dev, exec->tile_alloc_offset + tile_alloc_size, -+ true); -+ exec->tile_bo = &tile_bo->base; -+ if (!exec->tile_bo) -+ return -ENOMEM; -+ list_add_tail(&tile_bo->unref_head, &exec->unref_list); -+ -+ /* tile alloc address. */ -+ *(uint32_t *)(validated + 0) = (exec->tile_bo->paddr + -+ exec->tile_alloc_offset); -+ /* tile alloc size. */ -+ *(uint32_t *)(validated + 4) = tile_alloc_size; -+ /* tile state address. */ -+ *(uint32_t *)(validated + 8) = exec->tile_bo->paddr; -+ -+ return 0; -+} -+ -+static int -+validate_gem_handles(VALIDATE_ARGS) -+{ -+ memcpy(exec->bo_index, untrusted, sizeof(exec->bo_index)); -+ return 0; -+} -+ -+#define VC4_DEFINE_PACKET(packet, func) \ -+ [packet] = { packet ## _SIZE, #packet, func } -+ -+static const struct cmd_info { -+ uint16_t len; -+ const char *name; -+ int (*func)(struct vc4_exec_info *exec, void *validated, -+ void *untrusted); -+} cmd_info[] = { -+ VC4_DEFINE_PACKET(VC4_PACKET_HALT, NULL), -+ VC4_DEFINE_PACKET(VC4_PACKET_NOP, NULL), -+ VC4_DEFINE_PACKET(VC4_PACKET_FLUSH, validate_flush), -+ VC4_DEFINE_PACKET(VC4_PACKET_FLUSH_ALL, NULL), -+ VC4_DEFINE_PACKET(VC4_PACKET_START_TILE_BINNING, -+ validate_start_tile_binning), -+ VC4_DEFINE_PACKET(VC4_PACKET_INCREMENT_SEMAPHORE, -+ validate_increment_semaphore), -+ -+ VC4_DEFINE_PACKET(VC4_PACKET_GL_INDEXED_PRIMITIVE, -+ validate_indexed_prim_list), -+ VC4_DEFINE_PACKET(VC4_PACKET_GL_ARRAY_PRIMITIVE, -+ validate_gl_array_primitive), -+ -+ VC4_DEFINE_PACKET(VC4_PACKET_PRIMITIVE_LIST_FORMAT, NULL), -+ -+ VC4_DEFINE_PACKET(VC4_PACKET_GL_SHADER_STATE, validate_gl_shader_state), -+ -+ VC4_DEFINE_PACKET(VC4_PACKET_CONFIGURATION_BITS, NULL), -+ VC4_DEFINE_PACKET(VC4_PACKET_FLAT_SHADE_FLAGS, NULL), -+ VC4_DEFINE_PACKET(VC4_PACKET_POINT_SIZE, NULL), -+ VC4_DEFINE_PACKET(VC4_PACKET_LINE_WIDTH, NULL), -+ VC4_DEFINE_PACKET(VC4_PACKET_RHT_X_BOUNDARY, NULL), -+ VC4_DEFINE_PACKET(VC4_PACKET_DEPTH_OFFSET, NULL), -+ VC4_DEFINE_PACKET(VC4_PACKET_CLIP_WINDOW, NULL), -+ VC4_DEFINE_PACKET(VC4_PACKET_VIEWPORT_OFFSET, NULL), -+ VC4_DEFINE_PACKET(VC4_PACKET_CLIPPER_XY_SCALING, NULL), -+ /* Note: The docs say this was also 105, but it was 106 in the -+ * initial userland code drop. -+ */ -+ VC4_DEFINE_PACKET(VC4_PACKET_CLIPPER_Z_SCALING, NULL), -+ -+ VC4_DEFINE_PACKET(VC4_PACKET_TILE_BINNING_MODE_CONFIG, -+ validate_tile_binning_config), -+ -+ VC4_DEFINE_PACKET(VC4_PACKET_GEM_HANDLES, validate_gem_handles), -+}; -+ -+int -+vc4_validate_bin_cl(struct drm_device *dev, -+ void *validated, -+ void *unvalidated, -+ struct vc4_exec_info *exec) -+{ -+ uint32_t len = exec->args->bin_cl_size; -+ uint32_t dst_offset = 0; -+ uint32_t src_offset = 0; -+ -+ while (src_offset < len) { -+ void *dst_pkt = validated + dst_offset; -+ void *src_pkt = unvalidated + src_offset; -+ u8 cmd = *(uint8_t *)src_pkt; -+ const struct cmd_info *info; -+ -+ if (cmd >= ARRAY_SIZE(cmd_info)) { -+ DRM_ERROR("0x%08x: packet %d out of bounds\n", -+ src_offset, cmd); -+ return -EINVAL; -+ } -+ -+ info = &cmd_info[cmd]; -+ if (!info->name) { -+ DRM_ERROR("0x%08x: packet %d invalid\n", -+ src_offset, cmd); -+ return -EINVAL; -+ } -+ -+ if (src_offset + info->len > len) { -+ DRM_ERROR("0x%08x: packet %d (%s) length 0x%08x " -+ "exceeds bounds (0x%08x)\n", -+ src_offset, cmd, info->name, info->len, -+ src_offset + len); -+ return -EINVAL; -+ } -+ -+ if (cmd != VC4_PACKET_GEM_HANDLES) -+ memcpy(dst_pkt, src_pkt, info->len); -+ -+ if (info->func && info->func(exec, -+ dst_pkt + 1, -+ src_pkt + 1)) { -+ DRM_ERROR("0x%08x: packet %d (%s) failed to validate\n", -+ src_offset, cmd, info->name); -+ return -EINVAL; -+ } -+ -+ src_offset += info->len; -+ /* GEM handle loading doesn't produce HW packets. */ -+ if (cmd != VC4_PACKET_GEM_HANDLES) -+ dst_offset += info->len; -+ -+ /* When the CL hits halt, it'll stop reading anything else. */ -+ if (cmd == VC4_PACKET_HALT) -+ break; -+ } -+ -+ exec->ct0ea = exec->ct0ca + dst_offset; -+ -+ if (!exec->found_start_tile_binning_packet) { -+ DRM_ERROR("Bin CL missing VC4_PACKET_START_TILE_BINNING\n"); -+ return -EINVAL; -+ } -+ -+ /* The bin CL must be ended with INCREMENT_SEMAPHORE and FLUSH. The -+ * semaphore is used to trigger the render CL to start up, and the -+ * FLUSH is what caps the bin lists with -+ * VC4_PACKET_RETURN_FROM_SUB_LIST (so they jump back to the main -+ * render CL when they get called to) and actually triggers the queued -+ * semaphore increment. -+ */ -+ if (!exec->found_increment_semaphore_packet || !exec->found_flush) { -+ DRM_ERROR("Bin CL missing VC4_PACKET_INCREMENT_SEMAPHORE + " -+ "VC4_PACKET_FLUSH\n"); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static bool -+reloc_tex(struct vc4_exec_info *exec, -+ void *uniform_data_u, -+ struct vc4_texture_sample_info *sample, -+ uint32_t texture_handle_index) -+ -+{ -+ struct drm_gem_cma_object *tex; -+ uint32_t p0 = *(uint32_t *)(uniform_data_u + sample->p_offset[0]); -+ uint32_t p1 = *(uint32_t *)(uniform_data_u + sample->p_offset[1]); -+ uint32_t p2 = (sample->p_offset[2] != ~0 ? -+ *(uint32_t *)(uniform_data_u + sample->p_offset[2]) : 0); -+ uint32_t p3 = (sample->p_offset[3] != ~0 ? -+ *(uint32_t *)(uniform_data_u + sample->p_offset[3]) : 0); -+ uint32_t *validated_p0 = exec->uniforms_v + sample->p_offset[0]; -+ uint32_t offset = p0 & VC4_TEX_P0_OFFSET_MASK; -+ uint32_t miplevels = VC4_GET_FIELD(p0, VC4_TEX_P0_MIPLVLS); -+ uint32_t width = VC4_GET_FIELD(p1, VC4_TEX_P1_WIDTH); -+ uint32_t height = VC4_GET_FIELD(p1, VC4_TEX_P1_HEIGHT); -+ uint32_t cpp, tiling_format, utile_w, utile_h; -+ uint32_t i; -+ uint32_t cube_map_stride = 0; -+ enum vc4_texture_data_type type; -+ -+ tex = vc4_use_bo(exec, texture_handle_index); -+ if (!tex) -+ return false; -+ -+ if (sample->is_direct) { -+ uint32_t remaining_size = tex->base.size - p0; -+ -+ if (p0 > tex->base.size - 4) { -+ DRM_ERROR("UBO offset greater than UBO size\n"); -+ goto fail; -+ } -+ if (p1 > remaining_size - 4) { -+ DRM_ERROR("UBO clamp would allow reads " -+ "outside of UBO\n"); -+ goto fail; -+ } -+ *validated_p0 = tex->paddr + p0; -+ return true; -+ } -+ -+ if (width == 0) -+ width = 2048; -+ if (height == 0) -+ height = 2048; -+ -+ if (p0 & VC4_TEX_P0_CMMODE_MASK) { -+ if (VC4_GET_FIELD(p2, VC4_TEX_P2_PTYPE) == -+ VC4_TEX_P2_PTYPE_CUBE_MAP_STRIDE) -+ cube_map_stride = p2 & VC4_TEX_P2_CMST_MASK; -+ if (VC4_GET_FIELD(p3, VC4_TEX_P2_PTYPE) == -+ VC4_TEX_P2_PTYPE_CUBE_MAP_STRIDE) { -+ if (cube_map_stride) { -+ DRM_ERROR("Cube map stride set twice\n"); -+ goto fail; -+ } -+ -+ cube_map_stride = p3 & VC4_TEX_P2_CMST_MASK; -+ } -+ if (!cube_map_stride) { -+ DRM_ERROR("Cube map stride not set\n"); -+ goto fail; -+ } -+ } -+ -+ type = (VC4_GET_FIELD(p0, VC4_TEX_P0_TYPE) | -+ (VC4_GET_FIELD(p1, VC4_TEX_P1_TYPE4) << 4)); -+ -+ switch (type) { -+ case VC4_TEXTURE_TYPE_RGBA8888: -+ case VC4_TEXTURE_TYPE_RGBX8888: -+ case VC4_TEXTURE_TYPE_RGBA32R: -+ cpp = 4; -+ break; -+ case VC4_TEXTURE_TYPE_RGBA4444: -+ case VC4_TEXTURE_TYPE_RGBA5551: -+ case VC4_TEXTURE_TYPE_RGB565: -+ case VC4_TEXTURE_TYPE_LUMALPHA: -+ case VC4_TEXTURE_TYPE_S16F: -+ case VC4_TEXTURE_TYPE_S16: -+ cpp = 2; -+ break; -+ case VC4_TEXTURE_TYPE_LUMINANCE: -+ case VC4_TEXTURE_TYPE_ALPHA: -+ case VC4_TEXTURE_TYPE_S8: -+ cpp = 1; -+ break; -+ case VC4_TEXTURE_TYPE_ETC1: -+ case VC4_TEXTURE_TYPE_BW1: -+ case VC4_TEXTURE_TYPE_A4: -+ case VC4_TEXTURE_TYPE_A1: -+ case VC4_TEXTURE_TYPE_RGBA64: -+ case VC4_TEXTURE_TYPE_YUV422R: -+ default: -+ DRM_ERROR("Texture format %d unsupported\n", type); -+ goto fail; -+ } -+ utile_w = utile_width(cpp); -+ utile_h = utile_height(cpp); -+ -+ if (type == VC4_TEXTURE_TYPE_RGBA32R) { -+ tiling_format = VC4_TILING_FORMAT_LINEAR; -+ } else { -+ if (size_is_lt(width, height, cpp)) -+ tiling_format = VC4_TILING_FORMAT_LT; -+ else -+ tiling_format = VC4_TILING_FORMAT_T; -+ } -+ -+ if (!vc4_check_tex_size(exec, tex, offset + cube_map_stride * 5, -+ tiling_format, width, height, cpp)) { -+ goto fail; -+ } -+ -+ /* The mipmap levels are stored before the base of the texture. Make -+ * sure there is actually space in the BO. -+ */ -+ for (i = 1; i <= miplevels; i++) { -+ uint32_t level_width = max(width >> i, 1u); -+ uint32_t level_height = max(height >> i, 1u); -+ uint32_t aligned_width, aligned_height; -+ uint32_t level_size; -+ -+ /* Once the levels get small enough, they drop from T to LT. */ -+ if (tiling_format == VC4_TILING_FORMAT_T && -+ size_is_lt(level_width, level_height, cpp)) { -+ tiling_format = VC4_TILING_FORMAT_LT; -+ } -+ -+ switch (tiling_format) { -+ case VC4_TILING_FORMAT_T: -+ aligned_width = round_up(level_width, utile_w * 8); -+ aligned_height = round_up(level_height, utile_h * 8); -+ break; -+ case VC4_TILING_FORMAT_LT: -+ aligned_width = round_up(level_width, utile_w); -+ aligned_height = round_up(level_height, utile_h); -+ break; -+ default: -+ aligned_width = round_up(level_width, utile_w); -+ aligned_height = level_height; -+ break; -+ } -+ -+ level_size = aligned_width * cpp * aligned_height; -+ -+ if (offset < level_size) { -+ DRM_ERROR("Level %d (%dx%d -> %dx%d) size %db " -+ "overflowed buffer bounds (offset %d)\n", -+ i, level_width, level_height, -+ aligned_width, aligned_height, -+ level_size, offset); -+ goto fail; -+ } -+ -+ offset -= level_size; -+ } -+ -+ *validated_p0 = tex->paddr + p0; -+ -+ return true; -+ fail: -+ DRM_INFO("Texture p0 at %d: 0x%08x\n", sample->p_offset[0], p0); -+ DRM_INFO("Texture p1 at %d: 0x%08x\n", sample->p_offset[1], p1); -+ DRM_INFO("Texture p2 at %d: 0x%08x\n", sample->p_offset[2], p2); -+ DRM_INFO("Texture p3 at %d: 0x%08x\n", sample->p_offset[3], p3); -+ return false; -+} -+ -+static int -+validate_gl_shader_rec(struct drm_device *dev, -+ struct vc4_exec_info *exec, -+ struct vc4_shader_state *state) -+{ -+ uint32_t *src_handles; -+ void *pkt_u, *pkt_v; -+ static const uint32_t shader_reloc_offsets[] = { -+ 4, /* fs */ -+ 16, /* vs */ -+ 28, /* cs */ -+ }; -+ uint32_t shader_reloc_count = ARRAY_SIZE(shader_reloc_offsets); -+ struct drm_gem_cma_object *bo[shader_reloc_count + 8]; -+ uint32_t nr_attributes, nr_relocs, packet_size; -+ int i; -+ -+ nr_attributes = state->addr & 0x7; -+ if (nr_attributes == 0) -+ nr_attributes = 8; -+ packet_size = gl_shader_rec_size(state->addr); -+ -+ nr_relocs = ARRAY_SIZE(shader_reloc_offsets) + nr_attributes; -+ if (nr_relocs * 4 > exec->shader_rec_size) { -+ DRM_ERROR("overflowed shader recs reading %d handles " -+ "from %d bytes left\n", -+ nr_relocs, exec->shader_rec_size); -+ return -EINVAL; -+ } -+ src_handles = exec->shader_rec_u; -+ exec->shader_rec_u += nr_relocs * 4; -+ exec->shader_rec_size -= nr_relocs * 4; -+ -+ if (packet_size > exec->shader_rec_size) { -+ DRM_ERROR("overflowed shader recs copying %db packet " -+ "from %d bytes left\n", -+ packet_size, exec->shader_rec_size); -+ return -EINVAL; -+ } -+ pkt_u = exec->shader_rec_u; -+ pkt_v = exec->shader_rec_v; -+ memcpy(pkt_v, pkt_u, packet_size); -+ exec->shader_rec_u += packet_size; -+ /* Shader recs have to be aligned to 16 bytes (due to the attribute -+ * flags being in the low bytes), so round the next validated shader -+ * rec address up. This should be safe, since we've got so many -+ * relocations in a shader rec packet. -+ */ -+ BUG_ON(roundup(packet_size, 16) - packet_size > nr_relocs * 4); -+ exec->shader_rec_v += roundup(packet_size, 16); -+ exec->shader_rec_size -= packet_size; -+ -+ if (!(*(uint16_t *)pkt_u & VC4_SHADER_FLAG_FS_SINGLE_THREAD)) { -+ DRM_ERROR("Multi-threaded fragment shaders not supported.\n"); -+ return -EINVAL; -+ } -+ -+ for (i = 0; i < shader_reloc_count; i++) { -+ if (src_handles[i] > exec->bo_count) { -+ DRM_ERROR("Shader handle %d too big\n", src_handles[i]); -+ return -EINVAL; -+ } -+ -+ bo[i] = exec->bo[src_handles[i]]; -+ if (!bo[i]) -+ return -EINVAL; -+ } -+ for (i = shader_reloc_count; i < nr_relocs; i++) { -+ bo[i] = vc4_use_bo(exec, src_handles[i]); -+ if (!bo[i]) -+ return -EINVAL; -+ } -+ -+ for (i = 0; i < shader_reloc_count; i++) { -+ struct vc4_validated_shader_info *validated_shader; -+ uint32_t o = shader_reloc_offsets[i]; -+ uint32_t src_offset = *(uint32_t *)(pkt_u + o); -+ uint32_t *texture_handles_u; -+ void *uniform_data_u; -+ uint32_t tex; -+ -+ *(uint32_t *)(pkt_v + o) = bo[i]->paddr + src_offset; -+ -+ if (src_offset != 0) { -+ DRM_ERROR("Shaders must be at offset 0 of " -+ "the BO.\n"); -+ return -EINVAL; -+ } -+ -+ validated_shader = to_vc4_bo(&bo[i]->base)->validated_shader; -+ if (!validated_shader) -+ return -EINVAL; -+ -+ if (validated_shader->uniforms_src_size > -+ exec->uniforms_size) { -+ DRM_ERROR("Uniforms src buffer overflow\n"); -+ return -EINVAL; -+ } -+ -+ texture_handles_u = exec->uniforms_u; -+ uniform_data_u = (texture_handles_u + -+ validated_shader->num_texture_samples); -+ -+ memcpy(exec->uniforms_v, uniform_data_u, -+ validated_shader->uniforms_size); -+ -+ for (tex = 0; -+ tex < validated_shader->num_texture_samples; -+ tex++) { -+ if (!reloc_tex(exec, -+ uniform_data_u, -+ &validated_shader->texture_samples[tex], -+ texture_handles_u[tex])) { -+ return -EINVAL; -+ } -+ } -+ -+ *(uint32_t *)(pkt_v + o + 4) = exec->uniforms_p; -+ -+ exec->uniforms_u += validated_shader->uniforms_src_size; -+ exec->uniforms_v += validated_shader->uniforms_size; -+ exec->uniforms_p += validated_shader->uniforms_size; -+ } -+ -+ for (i = 0; i < nr_attributes; i++) { -+ struct drm_gem_cma_object *vbo = -+ bo[ARRAY_SIZE(shader_reloc_offsets) + i]; -+ uint32_t o = 36 + i * 8; -+ uint32_t offset = *(uint32_t *)(pkt_u + o + 0); -+ uint32_t attr_size = *(uint8_t *)(pkt_u + o + 4) + 1; -+ uint32_t stride = *(uint8_t *)(pkt_u + o + 5); -+ uint32_t max_index; -+ -+ if (state->addr & 0x8) -+ stride |= (*(uint32_t *)(pkt_u + 100 + i * 4)) & ~0xff; -+ -+ if (vbo->base.size < offset || -+ vbo->base.size - offset < attr_size) { -+ DRM_ERROR("BO offset overflow (%d + %d > %d)\n", -+ offset, attr_size, vbo->base.size); -+ return -EINVAL; -+ } -+ -+ if (stride != 0) { -+ max_index = ((vbo->base.size - offset - attr_size) / -+ stride); -+ if (state->max_index > max_index) { -+ DRM_ERROR("primitives use index %d out of " -+ "supplied %d\n", -+ state->max_index, max_index); -+ return -EINVAL; -+ } -+ } -+ -+ *(uint32_t *)(pkt_v + o) = vbo->paddr + offset; -+ } -+ -+ return 0; -+} -+ -+int -+vc4_validate_shader_recs(struct drm_device *dev, -+ struct vc4_exec_info *exec) -+{ -+ uint32_t i; -+ int ret = 0; -+ -+ for (i = 0; i < exec->shader_state_count; i++) { -+ ret = validate_gl_shader_rec(dev, exec, &exec->shader_state[i]); -+ if (ret) -+ return ret; -+ } -+ -+ return ret; -+} -diff --git a/include/uapi/drm/vc4_drm.h b/include/uapi/drm/vc4_drm.h -index 74de184..fe4161b 100644 ---- a/include/uapi/drm/vc4_drm.h -+++ b/include/uapi/drm/vc4_drm.h -@@ -26,14 +26,155 @@ - - #include "drm.h" - -+#define DRM_VC4_SUBMIT_CL 0x00 -+#define DRM_VC4_WAIT_SEQNO 0x01 -+#define DRM_VC4_WAIT_BO 0x02 - #define DRM_VC4_CREATE_BO 0x03 - #define DRM_VC4_MMAP_BO 0x04 - #define DRM_VC4_CREATE_SHADER_BO 0x05 - -+#define DRM_IOCTL_VC4_SUBMIT_CL DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_SUBMIT_CL, struct drm_vc4_submit_cl) -+#define DRM_IOCTL_VC4_WAIT_SEQNO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_WAIT_SEQNO, struct drm_vc4_wait_seqno) -+#define DRM_IOCTL_VC4_WAIT_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_WAIT_BO, struct drm_vc4_wait_bo) - #define DRM_IOCTL_VC4_CREATE_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_CREATE_BO, struct drm_vc4_create_bo) - #define DRM_IOCTL_VC4_MMAP_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_MMAP_BO, struct drm_vc4_mmap_bo) - #define DRM_IOCTL_VC4_CREATE_SHADER_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_CREATE_SHADER_BO, struct drm_vc4_create_shader_bo) - -+struct drm_vc4_submit_rcl_surface { -+ __u32 hindex; /* Handle index, or ~0 if not present. */ -+ __u32 offset; /* Offset to start of buffer. */ -+ /* -+ * Bits for either render config (color_write) or load/store packet. -+ * Bits should all be 0 for MSAA load/stores. -+ */ -+ __u16 bits; -+ -+#define VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES (1 << 0) -+ __u16 flags; -+}; -+ -+/** -+ * struct drm_vc4_submit_cl - ioctl argument for submitting commands to the 3D -+ * engine. -+ * -+ * Drivers typically use GPU BOs to store batchbuffers / command lists and -+ * their associated state. However, because the VC4 lacks an MMU, we have to -+ * do validation of memory accesses by the GPU commands. If we were to store -+ * our commands in BOs, we'd need to do uncached readback from them to do the -+ * validation process, which is too expensive. Instead, userspace accumulates -+ * commands and associated state in plain memory, then the kernel copies the -+ * data to its own address space, and then validates and stores it in a GPU -+ * BO. -+ */ -+struct drm_vc4_submit_cl { -+ /* Pointer to the binner command list. -+ * -+ * This is the first set of commands executed, which runs the -+ * coordinate shader to determine where primitives land on the screen, -+ * then writes out the state updates and draw calls necessary per tile -+ * to the tile allocation BO. -+ */ -+ __u64 bin_cl; -+ -+ /* Pointer to the shader records. -+ * -+ * Shader records are the structures read by the hardware that contain -+ * pointers to uniforms, shaders, and vertex attributes. The -+ * reference to the shader record has enough information to determine -+ * how many pointers are necessary (fixed number for shaders/uniforms, -+ * and an attribute count), so those BO indices into bo_handles are -+ * just stored as __u32s before each shader record passed in. -+ */ -+ __u64 shader_rec; -+ -+ /* Pointer to uniform data and texture handles for the textures -+ * referenced by the shader. -+ * -+ * For each shader state record, there is a set of uniform data in the -+ * order referenced by the record (FS, VS, then CS). Each set of -+ * uniform data has a __u32 index into bo_handles per texture -+ * sample operation, in the order the QPU_W_TMUn_S writes appear in -+ * the program. Following the texture BO handle indices is the actual -+ * uniform data. -+ * -+ * The individual uniform state blocks don't have sizes passed in, -+ * because the kernel has to determine the sizes anyway during shader -+ * code validation. -+ */ -+ __u64 uniforms; -+ __u64 bo_handles; -+ -+ /* Size in bytes of the binner command list. */ -+ __u32 bin_cl_size; -+ /* Size in bytes of the set of shader records. */ -+ __u32 shader_rec_size; -+ /* Number of shader records. -+ * -+ * This could just be computed from the contents of shader_records and -+ * the address bits of references to them from the bin CL, but it -+ * keeps the kernel from having to resize some allocations it makes. -+ */ -+ __u32 shader_rec_count; -+ /* Size in bytes of the uniform state. */ -+ __u32 uniforms_size; -+ -+ /* Number of BO handles passed in (size is that times 4). */ -+ __u32 bo_handle_count; -+ -+ /* RCL setup: */ -+ __u16 width; -+ __u16 height; -+ __u8 min_x_tile; -+ __u8 min_y_tile; -+ __u8 max_x_tile; -+ __u8 max_y_tile; -+ struct drm_vc4_submit_rcl_surface color_read; -+ struct drm_vc4_submit_rcl_surface color_write; -+ struct drm_vc4_submit_rcl_surface zs_read; -+ struct drm_vc4_submit_rcl_surface zs_write; -+ struct drm_vc4_submit_rcl_surface msaa_color_write; -+ struct drm_vc4_submit_rcl_surface msaa_zs_write; -+ __u32 clear_color[2]; -+ __u32 clear_z; -+ __u8 clear_s; -+ -+ __u32 pad:24; -+ -+#define VC4_SUBMIT_CL_USE_CLEAR_COLOR (1 << 0) -+ __u32 flags; -+ -+ /* Returned value of the seqno of this render job (for the -+ * wait ioctl). -+ */ -+ __u64 seqno; -+}; -+ -+/** -+ * struct drm_vc4_wait_seqno - ioctl argument for waiting for -+ * DRM_VC4_SUBMIT_CL completion using its returned seqno. -+ * -+ * timeout_ns is the timeout in nanoseconds, where "0" means "don't -+ * block, just return the status." -+ */ -+struct drm_vc4_wait_seqno { -+ __u64 seqno; -+ __u64 timeout_ns; -+}; -+ -+/** -+ * struct drm_vc4_wait_bo - ioctl argument for waiting for -+ * completion of the last DRM_VC4_SUBMIT_CL on a BO. -+ * -+ * This is useful for cases where multiple processes might be -+ * rendering to a BO and you want to wait for all rendering to be -+ * completed. -+ */ -+struct drm_vc4_wait_bo { -+ __u32 handle; -+ __u32 pad; -+ __u64 timeout_ns; -+}; -+ - /** - * struct drm_vc4_create_bo - ioctl argument for creating VC4 BOs. - * --- -2.6.4 - diff --git a/kernel/kernel/files/patches/mageia/gpu-drm-0108-drm-vc4-Add-support-for-async-pageflips.patch b/kernel/kernel/files/patches/mageia/gpu-drm-0108-drm-vc4-Add-support-for-async-pageflips.patch deleted file mode 100644 index ff86f47c..00000000 --- a/kernel/kernel/files/patches/mageia/gpu-drm-0108-drm-vc4-Add-support-for-async-pageflips.patch +++ /dev/null @@ -1,511 +0,0 @@ -From b501bacc6060fd62654b756469cc3091eb53de3a Mon Sep 17 00:00:00 2001 -From: Eric Anholt -Date: Mon, 30 Nov 2015 12:34:01 -0800 -Subject: [PATCH 8/9] drm/vc4: Add support for async pageflips. - -An async pageflip stores the modeset to be done and executes it once -the BOs are ready to be displayed. This gets us about 3x performance -in full screen rendering with pageflipping. - -Signed-off-by: Eric Anholt ---- - drivers/gpu/drm/vc4/vc4_crtc.c | 99 +++++++++++++++++++++++++- - drivers/gpu/drm/vc4/vc4_drv.h | 16 +++++ - drivers/gpu/drm/vc4/vc4_gem.c | 40 +++++++++++ - drivers/gpu/drm/vc4/vc4_kms.c | 149 +++++++++++++++++++++++++++++++++++++++- - drivers/gpu/drm/vc4/vc4_plane.c | 40 +++++++++++ - 5 files changed, 342 insertions(+), 2 deletions(-) - -diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c -index 7a9f476..a319332 100644 ---- a/drivers/gpu/drm/vc4/vc4_crtc.c -+++ b/drivers/gpu/drm/vc4/vc4_crtc.c -@@ -35,6 +35,7 @@ - #include "drm_atomic_helper.h" - #include "drm_crtc_helper.h" - #include "linux/clk.h" -+#include "drm_fb_cma_helper.h" - #include "linux/component.h" - #include "linux/of_device.h" - #include "vc4_drv.h" -@@ -475,10 +476,106 @@ static irqreturn_t vc4_crtc_irq_handler(int irq, void *data) - return ret; - } - -+struct vc4_async_flip_state { -+ struct drm_crtc *crtc; -+ struct drm_framebuffer *fb; -+ struct drm_pending_vblank_event *event; -+ -+ struct vc4_seqno_cb cb; -+}; -+ -+/* Called when the V3D execution for the BO being flipped to is done, so that -+ * we can actually update the plane's address to point to it. -+ */ -+static void -+vc4_async_page_flip_complete(struct vc4_seqno_cb *cb) -+{ -+ struct vc4_async_flip_state *flip_state = -+ container_of(cb, struct vc4_async_flip_state, cb); -+ struct drm_crtc *crtc = flip_state->crtc; -+ struct drm_device *dev = crtc->dev; -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ struct drm_plane *plane = crtc->primary; -+ -+ vc4_plane_async_set_fb(plane, flip_state->fb); -+ if (flip_state->event) { -+ unsigned long flags; -+ -+ spin_lock_irqsave(&dev->event_lock, flags); -+ drm_crtc_send_vblank_event(crtc, flip_state->event); -+ spin_unlock_irqrestore(&dev->event_lock, flags); -+ } -+ -+ drm_framebuffer_unreference(flip_state->fb); -+ kfree(flip_state); -+ -+ up(&vc4->async_modeset); -+} -+ -+/* Implements async (non-vblank-synced) page flips. -+ * -+ * The page flip ioctl needs to return immediately, so we grab the -+ * modeset semaphore on the pipe, and queue the address update for -+ * when V3D is done with the BO being flipped to. -+ */ -+static int vc4_async_page_flip(struct drm_crtc *crtc, -+ struct drm_framebuffer *fb, -+ struct drm_pending_vblank_event *event, -+ uint32_t flags) -+{ -+ struct drm_device *dev = crtc->dev; -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ struct drm_plane *plane = crtc->primary; -+ int ret = 0; -+ struct vc4_async_flip_state *flip_state; -+ struct drm_gem_cma_object *cma_bo = drm_fb_cma_get_gem_obj(fb, 0); -+ struct vc4_bo *bo = to_vc4_bo(&cma_bo->base); -+ -+ flip_state = kzalloc(sizeof(*flip_state), GFP_KERNEL); -+ if (!flip_state) -+ return -ENOMEM; -+ -+ drm_framebuffer_reference(fb); -+ flip_state->fb = fb; -+ flip_state->crtc = crtc; -+ flip_state->event = event; -+ -+ /* Make sure all other async modesetes have landed. */ -+ ret = down_interruptible(&vc4->async_modeset); -+ if (ret) { -+ kfree(flip_state); -+ return ret; -+ } -+ -+ /* Immediately update the plane's legacy fb pointer, so that later -+ * modeset prep sees the state that will be present when the semaphore -+ * is released. -+ */ -+ drm_atomic_set_fb_for_plane(plane->state, fb); -+ plane->fb = fb; -+ -+ vc4_queue_seqno_cb(dev, &flip_state->cb, bo->seqno, -+ vc4_async_page_flip_complete); -+ -+ /* Driver takes ownership of state on successful async commit. */ -+ return 0; -+} -+ -+static int vc4_page_flip(struct drm_crtc *crtc, -+ struct drm_framebuffer *fb, -+ struct drm_pending_vblank_event *event, -+ uint32_t flags) -+{ -+ if (flags & DRM_MODE_PAGE_FLIP_ASYNC) -+ return vc4_async_page_flip(crtc, fb, event, flags); -+ else -+ return drm_atomic_helper_page_flip(crtc, fb, event, flags); -+} -+ - static const struct drm_crtc_funcs vc4_crtc_funcs = { - .set_config = drm_atomic_helper_set_config, - .destroy = vc4_crtc_destroy, -- .page_flip = drm_atomic_helper_page_flip, -+ .page_flip = vc4_page_flip, - .set_property = NULL, - .cursor_set = NULL, /* handled by drm_mode_cursor_universal */ - .cursor_move = NULL, /* handled by drm_mode_cursor_universal */ -diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h -index 0bc8c57..f9927d8 100644 ---- a/drivers/gpu/drm/vc4/vc4_drv.h -+++ b/drivers/gpu/drm/vc4/vc4_drv.h -@@ -76,6 +76,11 @@ struct vc4_dev { - wait_queue_head_t job_wait_queue; - struct work_struct job_done_work; - -+ /* List of struct vc4_seqno_cb for callbacks to be made from a -+ * workqueue when the given seqno is passed. -+ */ -+ struct list_head seqno_cb_list; -+ - /* The binner overflow memory that's currently set up in - * BPOA/BPOS registers. When overflow occurs and a new one is - * allocated, the previous one will be moved to -@@ -128,6 +133,12 @@ to_vc4_bo(struct drm_gem_object *bo) - return (struct vc4_bo *)bo; - } - -+struct vc4_seqno_cb { -+ struct work_struct work; -+ uint64_t seqno; -+ void (*func)(struct vc4_seqno_cb *cb); -+}; -+ - struct vc4_v3d { - struct platform_device *pdev; - void __iomem *regs; -@@ -384,6 +395,9 @@ void vc4_submit_next_job(struct drm_device *dev); - int vc4_wait_for_seqno(struct drm_device *dev, uint64_t seqno, - uint64_t timeout_ns, bool interruptible); - void vc4_job_handle_completed(struct vc4_dev *vc4); -+int vc4_queue_seqno_cb(struct drm_device *dev, -+ struct vc4_seqno_cb *cb, uint64_t seqno, -+ void (*func)(struct vc4_seqno_cb *cb)); - - /* vc4_hdmi.c */ - extern struct platform_driver vc4_hdmi_driver; -@@ -409,6 +423,8 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev, - enum drm_plane_type type); - u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist); - u32 vc4_plane_dlist_size(struct drm_plane_state *state); -+void vc4_plane_async_set_fb(struct drm_plane *plane, -+ struct drm_framebuffer *fb); - - /* vc4_v3d.c */ - extern struct platform_driver vc4_v3d_driver; -diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c -index 936dddf..5fb0556 100644 ---- a/drivers/gpu/drm/vc4/vc4_gem.c -+++ b/drivers/gpu/drm/vc4/vc4_gem.c -@@ -461,6 +461,7 @@ void - vc4_job_handle_completed(struct vc4_dev *vc4) - { - unsigned long irqflags; -+ struct vc4_seqno_cb *cb, *cb_temp; - - spin_lock_irqsave(&vc4->job_lock, irqflags); - while (!list_empty(&vc4->job_done_list)) { -@@ -473,7 +474,45 @@ vc4_job_handle_completed(struct vc4_dev *vc4) - vc4_complete_exec(vc4->dev, exec); - spin_lock_irqsave(&vc4->job_lock, irqflags); - } -+ -+ list_for_each_entry_safe(cb, cb_temp, &vc4->seqno_cb_list, work.entry) { -+ if (cb->seqno <= vc4->finished_seqno) { -+ list_del_init(&cb->work.entry); -+ schedule_work(&cb->work); -+ } -+ } -+ -+ spin_unlock_irqrestore(&vc4->job_lock, irqflags); -+} -+ -+static void vc4_seqno_cb_work(struct work_struct *work) -+{ -+ struct vc4_seqno_cb *cb = container_of(work, struct vc4_seqno_cb, work); -+ -+ cb->func(cb); -+} -+ -+int vc4_queue_seqno_cb(struct drm_device *dev, -+ struct vc4_seqno_cb *cb, uint64_t seqno, -+ void (*func)(struct vc4_seqno_cb *cb)) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ int ret = 0; -+ unsigned long irqflags; -+ -+ cb->func = func; -+ INIT_WORK(&cb->work, vc4_seqno_cb_work); -+ -+ spin_lock_irqsave(&vc4->job_lock, irqflags); -+ if (seqno > vc4->finished_seqno) { -+ cb->seqno = seqno; -+ list_add_tail(&cb->work.entry, &vc4->seqno_cb_list); -+ } else { -+ schedule_work(&cb->work); -+ } - spin_unlock_irqrestore(&vc4->job_lock, irqflags); -+ -+ return ret; - } - - /* Scheduled when any job has been completed, this walks the list of -@@ -610,6 +649,7 @@ vc4_gem_init(struct drm_device *dev) - - INIT_LIST_HEAD(&vc4->job_list); - INIT_LIST_HEAD(&vc4->job_done_list); -+ INIT_LIST_HEAD(&vc4->seqno_cb_list); - spin_lock_init(&vc4->job_lock); - - INIT_WORK(&vc4->hangcheck.reset_work, vc4_reset_work); -diff --git a/drivers/gpu/drm/vc4/vc4_kms.c b/drivers/gpu/drm/vc4/vc4_kms.c -index 2e5597d..f95f2df 100644 ---- a/drivers/gpu/drm/vc4/vc4_kms.c -+++ b/drivers/gpu/drm/vc4/vc4_kms.c -@@ -15,6 +15,7 @@ - */ - - #include "drm_crtc.h" -+#include "drm_atomic.h" - #include "drm_atomic_helper.h" - #include "drm_crtc_helper.h" - #include "drm_plane_helper.h" -@@ -29,10 +30,152 @@ static void vc4_output_poll_changed(struct drm_device *dev) - drm_fbdev_cma_hotplug_event(vc4->fbdev); - } - -+struct vc4_commit { -+ struct drm_device *dev; -+ struct drm_atomic_state *state; -+ struct vc4_seqno_cb cb; -+}; -+ -+static void -+vc4_atomic_complete_commit(struct vc4_commit *c) -+{ -+ struct drm_atomic_state *state = c->state; -+ struct drm_device *dev = state->dev; -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ -+ drm_atomic_helper_commit_modeset_disables(dev, state); -+ -+ drm_atomic_helper_commit_planes(dev, state, false); -+ -+ drm_atomic_helper_commit_modeset_enables(dev, state); -+ -+ drm_atomic_helper_wait_for_vblanks(dev, state); -+ -+ drm_atomic_helper_cleanup_planes(dev, state); -+ -+ drm_atomic_state_free(state); -+ -+ up(&vc4->async_modeset); -+ -+ kfree(c); -+} -+ -+static void -+vc4_atomic_complete_commit_seqno_cb(struct vc4_seqno_cb *cb) -+{ -+ struct vc4_commit *c = container_of(cb, struct vc4_commit, cb); -+ -+ vc4_atomic_complete_commit(c); -+} -+ -+static struct vc4_commit *commit_init(struct drm_atomic_state *state) -+{ -+ struct vc4_commit *c = kzalloc(sizeof(*c), GFP_KERNEL); -+ -+ if (!c) -+ return NULL; -+ c->dev = state->dev; -+ c->state = state; -+ -+ return c; -+} -+ -+/** -+ * vc4_atomic_commit - commit validated state object -+ * @dev: DRM device -+ * @state: the driver state object -+ * @async: asynchronous commit -+ * -+ * This function commits a with drm_atomic_helper_check() pre-validated state -+ * object. This can still fail when e.g. the framebuffer reservation fails. For -+ * now this doesn't implement asynchronous commits. -+ * -+ * RETURNS -+ * Zero for success or -errno. -+ */ -+static int vc4_atomic_commit(struct drm_device *dev, -+ struct drm_atomic_state *state, -+ bool async) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ int ret; -+ int i; -+ uint64_t wait_seqno = 0; -+ struct vc4_commit *c; -+ -+ c = commit_init(state); -+ if (!c) -+ return -ENOMEM; -+ -+ /* Make sure that any outstanding modesets have finished. */ -+ ret = down_interruptible(&vc4->async_modeset); -+ if (ret) { -+ kfree(c); -+ return ret; -+ } -+ -+ ret = drm_atomic_helper_prepare_planes(dev, state); -+ if (ret) { -+ kfree(c); -+ up(&vc4->async_modeset); -+ return ret; -+ } -+ -+ for (i = 0; i < dev->mode_config.num_total_plane; i++) { -+ struct drm_plane *plane = state->planes[i]; -+ struct drm_plane_state *new_state = state->plane_states[i]; -+ -+ if (!plane) -+ continue; -+ -+ if ((plane->state->fb != new_state->fb) && new_state->fb) { -+ struct drm_gem_cma_object *cma_bo = -+ drm_fb_cma_get_gem_obj(new_state->fb, 0); -+ struct vc4_bo *bo = to_vc4_bo(&cma_bo->base); -+ -+ wait_seqno = max(bo->seqno, wait_seqno); -+ } -+ } -+ -+ /* -+ * This is the point of no return - everything below never fails except -+ * when the hw goes bonghits. Which means we can commit the new state on -+ * the software side now. -+ */ -+ -+ drm_atomic_helper_swap_state(dev, state); -+ -+ /* -+ * Everything below can be run asynchronously without the need to grab -+ * any modeset locks at all under one condition: It must be guaranteed -+ * that the asynchronous work has either been cancelled (if the driver -+ * supports it, which at least requires that the framebuffers get -+ * cleaned up with drm_atomic_helper_cleanup_planes()) or completed -+ * before the new state gets committed on the software side with -+ * drm_atomic_helper_swap_state(). -+ * -+ * This scheme allows new atomic state updates to be prepared and -+ * checked in parallel to the asynchronous completion of the previous -+ * update. Which is important since compositors need to figure out the -+ * composition of the next frame right after having submitted the -+ * current layout. -+ */ -+ -+ if (async) { -+ vc4_queue_seqno_cb(dev, &c->cb, wait_seqno, -+ vc4_atomic_complete_commit_seqno_cb); -+ } else { -+ vc4_wait_for_seqno(dev, wait_seqno, ~0ull, false); -+ vc4_atomic_complete_commit(c); -+ } -+ -+ return 0; -+} -+ - static const struct drm_mode_config_funcs vc4_mode_funcs = { - .output_poll_changed = vc4_output_poll_changed, - .atomic_check = drm_atomic_helper_check, -- .atomic_commit = drm_atomic_helper_commit, -+ .atomic_commit = vc4_atomic_commit, - .fb_create = drm_fb_cma_create, - }; - -@@ -41,6 +184,8 @@ int vc4_kms_load(struct drm_device *dev) - struct vc4_dev *vc4 = to_vc4_dev(dev); - int ret; - -+ sema_init(&vc4->async_modeset, 1); -+ - ret = drm_vblank_init(dev, dev->mode_config.num_crtc); - if (ret < 0) { - dev_err(dev->dev, "failed to initialize vblank\n"); -@@ -51,6 +196,8 @@ int vc4_kms_load(struct drm_device *dev) - dev->mode_config.max_height = 2048; - dev->mode_config.funcs = &vc4_mode_funcs; - dev->mode_config.preferred_depth = 24; -+ dev->mode_config.async_page_flip = true; -+ - dev->vblank_disable_allowed = true; - - drm_mode_config_reset(dev); -diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c -index cdd8b10..db32c373 100644 ---- a/drivers/gpu/drm/vc4/vc4_plane.c -+++ b/drivers/gpu/drm/vc4/vc4_plane.c -@@ -29,6 +29,14 @@ struct vc4_plane_state { - u32 *dlist; - u32 dlist_size; /* Number of dwords in allocated for the display list */ - u32 dlist_count; /* Number of used dwords in the display list. */ -+ -+ /* Offset in the dlist to pointer word 0. */ -+ u32 pw0_offset; -+ -+ /* Offset where the plane's dlist was last stored in the -+ hardware at vc4_crtc_atomic_flush() time. -+ */ -+ u32 *hw_dlist; - }; - - static inline struct vc4_plane_state * -@@ -197,6 +205,8 @@ static int vc4_plane_mode_set(struct drm_plane *plane, - /* Position Word 3: Context. Written by the HVS. */ - vc4_dlist_write(vc4_state, 0xc0c0c0c0); - -+ vc4_state->pw0_offset = vc4_state->dlist_count; -+ - /* Pointer Word 0: RGB / Y Pointer */ - vc4_dlist_write(vc4_state, bo->paddr + offset); - -@@ -248,6 +258,8 @@ u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist) - struct vc4_plane_state *vc4_state = to_vc4_plane_state(plane->state); - int i; - -+ vc4_state->hw_dlist = dlist; -+ - /* Can't memcpy_toio() because it needs to be 32-bit writes. */ - for (i = 0; i < vc4_state->dlist_count; i++) - writel(vc4_state->dlist[i], &dlist[i]); -@@ -262,6 +274,34 @@ u32 vc4_plane_dlist_size(struct drm_plane_state *state) - return vc4_state->dlist_count; - } - -+/* Updates the plane to immediately (well, once the FIFO needs -+ * refilling) scan out from at a new framebuffer. -+ */ -+void vc4_plane_async_set_fb(struct drm_plane *plane, struct drm_framebuffer *fb) -+{ -+ struct vc4_plane_state *vc4_state = to_vc4_plane_state(plane->state); -+ struct drm_gem_cma_object *bo = drm_fb_cma_get_gem_obj(fb, 0); -+ uint32_t addr; -+ -+ /* We're skipping the address adjustment for negative origin, -+ * because this is only called on the primary plane. -+ */ -+ WARN_ON_ONCE(plane->state->crtc_x < 0 || plane->state->crtc_y < 0); -+ addr = bo->paddr + fb->offsets[0]; -+ -+ /* Write the new address into the hardware immediately. The -+ * scanout will start from this address as soon as the FIFO -+ * needs to refill with pixels. -+ */ -+ writel(addr, &vc4_state->hw_dlist[vc4_state->pw0_offset]); -+ -+ /* Also update the CPU-side dlist copy, so that any later -+ * atomic updates that don't do a new modeset on our plane -+ * also use our updated address. -+ */ -+ vc4_state->dlist[vc4_state->pw0_offset] = addr; -+} -+ - static const struct drm_plane_helper_funcs vc4_plane_helper_funcs = { - .prepare_fb = NULL, - .cleanup_fb = NULL, --- -2.6.4 - diff --git a/kernel/kernel/files/patches/mageia/gpu-drm-0109-drm-vc4-Add-an-interface-for-capturing-the-GPU-state.patch b/kernel/kernel/files/patches/mageia/gpu-drm-0109-drm-vc4-Add-an-interface-for-capturing-the-GPU-state.patch deleted file mode 100644 index 1b6c6514..00000000 --- a/kernel/kernel/files/patches/mageia/gpu-drm-0109-drm-vc4-Add-an-interface-for-capturing-the-GPU-state.patch +++ /dev/null @@ -1,333 +0,0 @@ -From 214613656b5179f0daab6e0a080814b5100d45f0 Mon Sep 17 00:00:00 2001 -From: Eric Anholt -Date: Fri, 30 Oct 2015 10:09:02 -0700 -Subject: [PATCH 9/9] drm/vc4: Add an interface for capturing the GPU state - after a hang. - -This can be parsed with vc4-gpu-tools tools for trying to figure out -what was going on. - -v2: Use __u32-style types. - -Signed-off-by: Eric Anholt ---- - drivers/gpu/drm/vc4/vc4_drv.c | 2 + - drivers/gpu/drm/vc4/vc4_drv.h | 4 + - drivers/gpu/drm/vc4/vc4_gem.c | 185 ++++++++++++++++++++++++++++++++++++++++++ - include/uapi/drm/vc4_drm.h | 45 ++++++++++ - 4 files changed, 236 insertions(+) - -diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c -index 2cfee59..97226b6 100644 ---- a/drivers/gpu/drm/vc4/vc4_drv.c -+++ b/drivers/gpu/drm/vc4/vc4_drv.c -@@ -80,6 +80,8 @@ static const struct drm_ioctl_desc vc4_drm_ioctls[] = { - DRM_IOCTL_DEF_DRV(VC4_CREATE_BO, vc4_create_bo_ioctl, 0), - DRM_IOCTL_DEF_DRV(VC4_MMAP_BO, vc4_mmap_bo_ioctl, 0), - DRM_IOCTL_DEF_DRV(VC4_CREATE_SHADER_BO, vc4_create_shader_bo_ioctl, 0), -+ DRM_IOCTL_DEF_DRV(VC4_GET_HANG_STATE, vc4_get_hang_state_ioctl, -+ DRM_ROOT_ONLY), - }; - - static struct drm_driver vc4_drm_driver = { -diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h -index f9927d8..080865e 100644 ---- a/drivers/gpu/drm/vc4/vc4_drv.h -+++ b/drivers/gpu/drm/vc4/vc4_drv.h -@@ -19,6 +19,8 @@ struct vc4_dev { - - struct drm_fbdev_cma *fbdev; - -+ struct vc4_hang_state *hang_state; -+ - /* The kernel-space BO cache. Tracks buffers that have been - * unreferenced by all other users (refcounts of 0!) but not - * yet freed, so we can do cheap allocations. -@@ -361,6 +363,8 @@ int vc4_create_shader_bo_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); - int vc4_mmap_bo_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); -+int vc4_get_hang_state_ioctl(struct drm_device *dev, void *data, -+ struct drm_file *file_priv); - int vc4_mmap(struct file *filp, struct vm_area_struct *vma); - int vc4_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma); - void *vc4_prime_vmap(struct drm_gem_object *obj); -diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c -index 5fb0556..39f29e7 100644 ---- a/drivers/gpu/drm/vc4/vc4_gem.c -+++ b/drivers/gpu/drm/vc4/vc4_gem.c -@@ -40,6 +40,186 @@ vc4_queue_hangcheck(struct drm_device *dev) - round_jiffies_up(jiffies + msecs_to_jiffies(100))); - } - -+struct vc4_hang_state { -+ struct drm_vc4_get_hang_state user_state; -+ -+ u32 bo_count; -+ struct drm_gem_object **bo; -+}; -+ -+static void -+vc4_free_hang_state(struct drm_device *dev, struct vc4_hang_state *state) -+{ -+ unsigned int i; -+ -+ mutex_lock(&dev->struct_mutex); -+ for (i = 0; i < state->user_state.bo_count; i++) -+ drm_gem_object_unreference(state->bo[i]); -+ mutex_unlock(&dev->struct_mutex); -+ -+ kfree(state); -+} -+ -+int -+vc4_get_hang_state_ioctl(struct drm_device *dev, void *data, -+ struct drm_file *file_priv) -+{ -+ struct drm_vc4_get_hang_state *get_state = data; -+ struct drm_vc4_get_hang_state_bo *bo_state; -+ struct vc4_hang_state *kernel_state; -+ struct drm_vc4_get_hang_state *state; -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ unsigned long irqflags; -+ u32 i; -+ int ret; -+ -+ spin_lock_irqsave(&vc4->job_lock, irqflags); -+ kernel_state = vc4->hang_state; -+ if (!kernel_state) { -+ spin_unlock_irqrestore(&vc4->job_lock, irqflags); -+ return -ENOENT; -+ } -+ state = &kernel_state->user_state; -+ -+ /* If the user's array isn't big enough, just return the -+ * required array size. -+ */ -+ if (get_state->bo_count < state->bo_count) { -+ get_state->bo_count = state->bo_count; -+ spin_unlock_irqrestore(&vc4->job_lock, irqflags); -+ return 0; -+ } -+ -+ vc4->hang_state = NULL; -+ spin_unlock_irqrestore(&vc4->job_lock, irqflags); -+ -+ /* Save the user's BO pointer, so we don't stomp it with the memcpy. */ -+ state->bo = get_state->bo; -+ memcpy(get_state, state, sizeof(*state)); -+ -+ bo_state = kcalloc(state->bo_count, sizeof(*bo_state), GFP_KERNEL); -+ if (!bo_state) { -+ ret = -ENOMEM; -+ goto err_free; -+ } -+ -+ for (i = 0; i < state->bo_count; i++) { -+ struct vc4_bo *vc4_bo = to_vc4_bo(kernel_state->bo[i]); -+ u32 handle; -+ -+ ret = drm_gem_handle_create(file_priv, kernel_state->bo[i], -+ &handle); -+ -+ if (ret) { -+ state->bo_count = i - 1; -+ goto err; -+ } -+ bo_state[i].handle = handle; -+ bo_state[i].paddr = vc4_bo->base.paddr; -+ bo_state[i].size = vc4_bo->base.base.size; -+ } -+ -+ ret = copy_to_user((void __user *)(uintptr_t)get_state->bo, -+ bo_state, -+ state->bo_count * sizeof(*bo_state)); -+ kfree(bo_state); -+ -+err_free: -+ -+ vc4_free_hang_state(dev, kernel_state); -+ -+err: -+ return ret; -+} -+ -+static void -+vc4_save_hang_state(struct drm_device *dev) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ struct drm_vc4_get_hang_state *state; -+ struct vc4_hang_state *kernel_state; -+ struct vc4_exec_info *exec; -+ struct vc4_bo *bo; -+ unsigned long irqflags; -+ unsigned int i, unref_list_count; -+ -+ kernel_state = kcalloc(1, sizeof(*state), GFP_KERNEL); -+ if (!kernel_state) -+ return; -+ -+ state = &kernel_state->user_state; -+ -+ spin_lock_irqsave(&vc4->job_lock, irqflags); -+ exec = vc4_first_job(vc4); -+ if (!exec) { -+ spin_unlock_irqrestore(&vc4->job_lock, irqflags); -+ return; -+ } -+ -+ unref_list_count = 0; -+ list_for_each_entry(bo, &exec->unref_list, unref_head) -+ unref_list_count++; -+ -+ state->bo_count = exec->bo_count + unref_list_count; -+ kernel_state->bo = kcalloc(state->bo_count, sizeof(*kernel_state->bo), -+ GFP_ATOMIC); -+ if (!kernel_state->bo) { -+ spin_unlock_irqrestore(&vc4->job_lock, irqflags); -+ return; -+ } -+ -+ for (i = 0; i < exec->bo_count; i++) { -+ drm_gem_object_reference(&exec->bo[i]->base); -+ kernel_state->bo[i] = &exec->bo[i]->base; -+ } -+ -+ list_for_each_entry(bo, &exec->unref_list, unref_head) { -+ drm_gem_object_reference(&bo->base.base); -+ kernel_state->bo[i] = &bo->base.base; -+ i++; -+ } -+ -+ state->start_bin = exec->ct0ca; -+ state->start_render = exec->ct1ca; -+ -+ spin_unlock_irqrestore(&vc4->job_lock, irqflags); -+ -+ state->ct0ca = V3D_READ(V3D_CTNCA(0)); -+ state->ct0ea = V3D_READ(V3D_CTNEA(0)); -+ -+ state->ct1ca = V3D_READ(V3D_CTNCA(1)); -+ state->ct1ea = V3D_READ(V3D_CTNEA(1)); -+ -+ state->ct0cs = V3D_READ(V3D_CTNCS(0)); -+ state->ct1cs = V3D_READ(V3D_CTNCS(1)); -+ -+ state->ct0ra0 = V3D_READ(V3D_CT00RA0); -+ state->ct1ra0 = V3D_READ(V3D_CT01RA0); -+ -+ state->bpca = V3D_READ(V3D_BPCA); -+ state->bpcs = V3D_READ(V3D_BPCS); -+ state->bpoa = V3D_READ(V3D_BPOA); -+ state->bpos = V3D_READ(V3D_BPOS); -+ -+ state->vpmbase = V3D_READ(V3D_VPMBASE); -+ -+ state->dbge = V3D_READ(V3D_DBGE); -+ state->fdbgo = V3D_READ(V3D_FDBGO); -+ state->fdbgb = V3D_READ(V3D_FDBGB); -+ state->fdbgr = V3D_READ(V3D_FDBGR); -+ state->fdbgs = V3D_READ(V3D_FDBGS); -+ state->errstat = V3D_READ(V3D_ERRSTAT); -+ -+ spin_lock_irqsave(&vc4->job_lock, irqflags); -+ if (vc4->hang_state) { -+ spin_unlock_irqrestore(&vc4->job_lock, irqflags); -+ vc4_free_hang_state(dev, kernel_state); -+ } else { -+ vc4->hang_state = kernel_state; -+ spin_unlock_irqrestore(&vc4->job_lock, irqflags); -+ } -+} -+ - static void - vc4_reset(struct drm_device *dev) - { -@@ -64,6 +244,8 @@ vc4_reset_work(struct work_struct *work) - struct vc4_dev *vc4 = - container_of(work, struct vc4_dev, hangcheck.reset_work); - -+ vc4_save_hang_state(vc4->dev); -+ - vc4_reset(vc4->dev); - } - -@@ -679,4 +861,7 @@ vc4_gem_destroy(struct drm_device *dev) - } - - vc4_bo_cache_destroy(dev); -+ -+ if (vc4->hang_state) -+ vc4_free_hang_state(dev, vc4->hang_state); - } -diff --git a/include/uapi/drm/vc4_drm.h b/include/uapi/drm/vc4_drm.h -index fe4161b..eeb37e3 100644 ---- a/include/uapi/drm/vc4_drm.h -+++ b/include/uapi/drm/vc4_drm.h -@@ -32,6 +32,7 @@ - #define DRM_VC4_CREATE_BO 0x03 - #define DRM_VC4_MMAP_BO 0x04 - #define DRM_VC4_CREATE_SHADER_BO 0x05 -+#define DRM_VC4_GET_HANG_STATE 0x06 - - #define DRM_IOCTL_VC4_SUBMIT_CL DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_SUBMIT_CL, struct drm_vc4_submit_cl) - #define DRM_IOCTL_VC4_WAIT_SEQNO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_WAIT_SEQNO, struct drm_vc4_wait_seqno) -@@ -39,6 +40,7 @@ - #define DRM_IOCTL_VC4_CREATE_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_CREATE_BO, struct drm_vc4_create_bo) - #define DRM_IOCTL_VC4_MMAP_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_MMAP_BO, struct drm_vc4_mmap_bo) - #define DRM_IOCTL_VC4_CREATE_SHADER_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_CREATE_SHADER_BO, struct drm_vc4_create_shader_bo) -+#define DRM_IOCTL_VC4_GET_HANG_STATE DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_GET_HANG_STATE, struct drm_vc4_get_hang_state) - - struct drm_vc4_submit_rcl_surface { - __u32 hindex; /* Handle index, or ~0 if not present. */ -@@ -231,4 +233,47 @@ struct drm_vc4_create_shader_bo { - __u32 pad; - }; - -+struct drm_vc4_get_hang_state_bo { -+ __u32 handle; -+ __u32 paddr; -+ __u32 size; -+ __u32 pad; -+}; -+ -+/** -+ * struct drm_vc4_hang_state - ioctl argument for collecting state -+ * from a GPU hang for analysis. -+*/ -+struct drm_vc4_get_hang_state { -+ /** Pointer to array of struct drm_vc4_get_hang_state_bo. */ -+ __u64 bo; -+ /** -+ * On input, the size of the bo array. Output is the number -+ * of bos to be returned. -+ */ -+ __u32 bo_count; -+ -+ __u32 start_bin, start_render; -+ -+ __u32 ct0ca, ct0ea; -+ __u32 ct1ca, ct1ea; -+ __u32 ct0cs, ct1cs; -+ __u32 ct0ra0, ct1ra0; -+ -+ __u32 bpca, bpcs; -+ __u32 bpoa, bpos; -+ -+ __u32 vpmbase; -+ -+ __u32 dbge; -+ __u32 fdbgo; -+ __u32 fdbgb; -+ __u32 fdbgr; -+ __u32 fdbgs; -+ __u32 errstat; -+ -+ /* Pad that we may save more registers into in the future. */ -+ __u32 pad[16]; -+}; -+ - #endif /* _UAPI_VC4_DRM_H_ */ --- -2.6.4 - diff --git a/kernel/kernel/files/patches/mageia/gpu-drm-i915-Fix-RPS-pointer-passed-from-wait_ioctl-to-i915_wait_request.patch b/kernel/kernel/files/patches/mageia/gpu-drm-i915-Fix-RPS-pointer-passed-from-wait_ioctl-to-i915_wait_request.patch deleted file mode 100644 index a082801a..00000000 --- a/kernel/kernel/files/patches/mageia/gpu-drm-i915-Fix-RPS-pointer-passed-from-wait_ioctl-to-i915_wait_request.patch +++ /dev/null @@ -1,35 +0,0 @@ -From: Chris Wilson -Subject: [PATCH] drm/i915: Fix RPS pointer passed from wait_ioctl to i915_wait_request -Date: Wed, 2 Dec 2015 09:13:46 +0000 - -In commit 2e1b873072dfe3bbcc158a9c21acde1ab0d36c55 [v4.2] -Author: Chris Wilson -Date: Mon Apr 27 13:41:22 2015 +0100 - - drm/i915: Convert RPS tracking to a intel_rps_client struct - -we converted the __i915_wait_request() to take a new intel_rps_client -struct (rather than having to pass fake drm_i915_file_private structs). -However, due to use of passing a void pointer, I didn't spot one -callsite in wait-ioctl was passing the wrong pointer. - -Signed-off-by: Chris Wilson -Cc: Daniel Vetter -Cc: stable@vger.kernel.org ---- - drivers/gpu/drm/i915/i915_gem.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c -index f5aef48b93db..cd8c4024af92 100644 ---- a/drivers/gpu/drm/i915/i915_gem.c -+++ b/drivers/gpu/drm/i915/i915_gem.c -@@ -3045,7 +3045,7 @@ i915_gem_wait_ioctl(struct drm_device *d - if (ret == 0) - ret = __i915_wait_request(req[i], reset_counter, true, - args->timeout_ns > 0 ? &args->timeout_ns : NULL, -- file->driver_priv); -+ to_rps_client(file)); - i915_gem_request_unreference__unlocked(req[i]); - } - return ret; diff --git a/kernel/kernel/files/patches/mageia/gpu-drm-i915-fbc-Disable-on-HSW-by-default-for-now.patch b/kernel/kernel/files/patches/mageia/gpu-drm-i915-fbc-Disable-on-HSW-by-default-for-now.patch new file mode 100644 index 00000000..c5434317 --- /dev/null +++ b/kernel/kernel/files/patches/mageia/gpu-drm-i915-fbc-Disable-on-HSW-by-default-for-now.patch @@ -0,0 +1,63 @@ +From 1e3fa0acfec677e915d7de5ac6e1f18cfa4f805b Mon Sep 17 00:00:00 2001 +From: Lyude +Date: Thu, 9 Jun 2016 11:58:15 -0400 +Subject: [PATCH] drm/i915/fbc: Disable on HSW by default for now + +>From https://bugs.freedesktop.org/show_bug.cgi?id=96461 : + +This was kind of a difficult bug to track down. If you're using a +Haswell system running GNOME and you have fbc completely enabled and +working, playing videos can result in video artifacts. Steps to +reproduce: + +- Run GNOME +- Ensure FBC is enabled and active +- Download a movie, I used the ogg version of Big Buck Bunny for this +- Run `gst-launch-1.0 filesrc location='some_movie.ogg' ! decodebin ! + glimagesink` in a terminal +- Watch for about over a minute, you'll see small horizontal lines go + down the screen. + +For the time being, disable FBC for Haswell by default. + +Stefan Richter reported kernel freezes (no video artifacts) when fbc +is on. (E3-1245 v3 with HD P4600; openbox and some KDE and LXDE +applications, thread begins at https://lkml.org/lkml/2016/4/26/813). +We also got reports from Steven Honeyman on openbox+roxterm. + +v2 (From Paulo): + - Add extra information to the commit message + - Add Fixes tag + - Rebase + +Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=96461 +Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=96464 +Fixes: a98ee79317b4 ("drm/i915/fbc: enable FBC by default on HSW and BDW") +Cc: stable@vger.kernel.org +Reviewed-by: Paulo Zanoni +Signed-off-by: Lyude +Signed-off-by: Paulo Zanoni +Link: http://patchwork.freedesktop.org/patch/msgid/1465487895-7401-1-git-send-email-cpaul@redhat.com +(cherry picked from commit c7f7e2feffb0294302041507dfd5fc15f01afccc) +Signed-off-by: Jani Nikula +--- + drivers/gpu/drm/i915/intel_fbc.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/drivers/gpu/drm/i915/intel_fbc.c b/drivers/gpu/drm/i915/intel_fbc.c +index d5a7cfe..647127f 100644 +--- a/drivers/gpu/drm/i915/intel_fbc.c ++++ b/drivers/gpu/drm/i915/intel_fbc.c +@@ -824,8 +824,7 @@ static bool intel_fbc_can_choose(struct intel_crtc *crtc) + { + struct drm_i915_private *dev_priv = crtc->base.dev->dev_private; + struct intel_fbc *fbc = &dev_priv->fbc; +- bool enable_by_default = IS_HASWELL(dev_priv) || +- IS_BROADWELL(dev_priv); ++ bool enable_by_default = IS_BROADWELL(dev_priv); + + if (intel_vgpu_active(dev_priv->dev)) { + fbc->no_fbc_reason = "VGPU is active"; +-- +2.9.0 + diff --git a/kernel/kernel/files/patches/mageia/gpu-drm-radeon-Drop-unnecessary-unsigned-int-0-check.patch b/kernel/kernel/files/patches/mageia/gpu-drm-radeon-Drop-unnecessary-unsigned-int-0-check.patch deleted file mode 100644 index 3303a75a..00000000 --- a/kernel/kernel/files/patches/mageia/gpu-drm-radeon-Drop-unnecessary-unsigned-int-0-check.patch +++ /dev/null @@ -1,34 +0,0 @@ -From 85a21eafbc2218ffba59b1dda2ce9d7148bf43d2 Mon Sep 17 00:00:00 2001 -From: Thierry Reding -Date: Mon, 4 Jan 2016 18:19:12 +0100 -Subject: [PATCH] drm/radeon: Drop unnecessary unsigned int < 0 check -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Unsigned integers can never be negative, so drop this check. - -Cc: Christian König -Cc: Alex Deucher -Signed-off-by: Thierry Reding -Signed-off-by: Alex Deucher ---- - drivers/gpu/drm/radeon/radeon_kms.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c -index 4fab44e..414953c 100644 ---- a/drivers/gpu/drm/radeon/radeon_kms.c -+++ b/drivers/gpu/drm/radeon/radeon_kms.c -@@ -759,7 +759,7 @@ u32 radeon_get_vblank_counter_kms(struct drm_device *dev, unsigned int pipe) - u32 count; - struct radeon_device *rdev = dev->dev_private; - -- if (pipe < 0 || pipe >= rdev->num_crtc) { -+ if (pipe >= rdev->num_crtc) { - DRM_ERROR("Invalid crtc %u\n", pipe); - return -EINVAL; - } --- -2.3.10 - diff --git a/kernel/kernel/files/patches/mageia/gpu-drm-radeon-Update-radeon_get_vblank_counter_kms.patch b/kernel/kernel/files/patches/mageia/gpu-drm-radeon-Update-radeon_get_vblank_counter_kms.patch deleted file mode 100644 index 28fe862a..00000000 --- a/kernel/kernel/files/patches/mageia/gpu-drm-radeon-Update-radeon_get_vblank_counter_kms.patch +++ /dev/null @@ -1,106 +0,0 @@ -From 4e926d2db58244fca9845c78a5d6f873ac73795b Mon Sep 17 00:00:00 2001 -From: Thierry Reding -Date: Wed, 16 Dec 2015 15:31:47 +0100 -Subject: [PATCH] drm/radeon: Update radeon_get_vblank_counter_kms() -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Commit 88e72717c2de ("drm/irq: Use unsigned int pipe in public API") -updated the prototype of this function but not the implementation. This -wasn't noticed even through compile tests because the prototype is part -of the source file that uses it and hence the compiler won't know the -prototype when it compiles the implementation. - -The right thing would've been to move the prototype to a header that's -included in radeon_kms.c so that the implementation signature could be -checked against it, but the closest thing would've been radeon_drv.h -and including that results in a lot of build errors, so we'll leave it -as is for now. - -Cc: Christian König -Cc: Alex Deucher -Reviewed-by: Christian König -Signed-off-by: Thierry Reding -Signed-off-by: Alex Deucher ---- - drivers/gpu/drm/radeon/radeon_kms.c | 24 ++++++++++++------------ - 1 file changed, 12 insertions(+), 12 deletions(-) - -diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c -index d290a8a..4fab44e 100644 ---- a/drivers/gpu/drm/radeon/radeon_kms.c -+++ b/drivers/gpu/drm/radeon/radeon_kms.c -@@ -748,19 +748,19 @@ void radeon_driver_preclose_kms(struct drm_device *dev, - * radeon_get_vblank_counter_kms - get frame count - * - * @dev: drm dev pointer -- * @crtc: crtc to get the frame count from -+ * @pipe: crtc to get the frame count from - * - * Gets the frame count on the requested crtc (all asics). - * Returns frame count on success, -EINVAL on failure. - */ --u32 radeon_get_vblank_counter_kms(struct drm_device *dev, int crtc) -+u32 radeon_get_vblank_counter_kms(struct drm_device *dev, unsigned int pipe) - { - int vpos, hpos, stat; - u32 count; - struct radeon_device *rdev = dev->dev_private; - -- if (crtc < 0 || crtc >= rdev->num_crtc) { -- DRM_ERROR("Invalid crtc %d\n", crtc); -+ if (pipe < 0 || pipe >= rdev->num_crtc) { -+ DRM_ERROR("Invalid crtc %u\n", pipe); - return -EINVAL; - } - -@@ -772,29 +772,29 @@ u32 radeon_get_vblank_counter_kms(struct drm_device *dev, int crtc) - * and start of vsync, so vpos >= 0 means to bump the hw frame counter - * result by 1 to give the proper appearance to caller. - */ -- if (rdev->mode_info.crtcs[crtc]) { -+ if (rdev->mode_info.crtcs[pipe]) { - /* Repeat readout if needed to provide stable result if - * we cross start of vsync during the queries. - */ - do { -- count = radeon_get_vblank_counter(rdev, crtc); -+ count = radeon_get_vblank_counter(rdev, pipe); - /* Ask radeon_get_crtc_scanoutpos to return vpos as - * distance to start of vblank, instead of regular - * vertical scanout pos. - */ - stat = radeon_get_crtc_scanoutpos( -- dev, crtc, GET_DISTANCE_TO_VBLANKSTART, -+ dev, pipe, GET_DISTANCE_TO_VBLANKSTART, - &vpos, &hpos, NULL, NULL, -- &rdev->mode_info.crtcs[crtc]->base.hwmode); -- } while (count != radeon_get_vblank_counter(rdev, crtc)); -+ &rdev->mode_info.crtcs[pipe]->base.hwmode); -+ } while (count != radeon_get_vblank_counter(rdev, pipe)); - - if (((stat & (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE)) != - (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE))) { - DRM_DEBUG_VBL("Query failed! stat %d\n", stat); - } - else { -- DRM_DEBUG_VBL("crtc %d: dist from vblank start %d\n", -- crtc, vpos); -+ DRM_DEBUG_VBL("crtc %u: dist from vblank start %d\n", -+ pipe, vpos); - - /* Bump counter if we are at >= leading edge of vblank, - * but before vsync where vpos would turn negative and -@@ -806,7 +806,7 @@ u32 radeon_get_vblank_counter_kms(struct drm_device *dev, int crtc) - } - else { - /* Fallback to use value as is. */ -- count = radeon_get_vblank_counter(rdev, crtc); -+ count = radeon_get_vblank_counter(rdev, pipe); - DRM_DEBUG_VBL("NULL mode info! Returned count may be wrong.\n"); - } - --- -2.3.10 - diff --git a/kernel/kernel/files/patches/mageia/mm-sl-au-b-add-__GFP_ATOMIC-to-the-GFP-reclaim-mask.patch b/kernel/kernel/files/patches/mageia/mm-sl-au-b-add-__GFP_ATOMIC-to-the-GFP-reclaim-mask.patch new file mode 100644 index 00000000..8e23c847 --- /dev/null +++ b/kernel/kernel/files/patches/mageia/mm-sl-au-b-add-__GFP_ATOMIC-to-the-GFP-reclaim-mask.patch @@ -0,0 +1,49 @@ +From e838a45f9392a5bd2be1cd3ab0b16ae85857461c Mon Sep 17 00:00:00 2001 +From: Mel Gorman +Date: Fri, 24 Jun 2016 14:49:37 -0700 +Subject: [PATCH] mm, sl[au]b: add __GFP_ATOMIC to the GFP reclaim mask + +Commit d0164adc89f6 ("mm, page_alloc: distinguish between being unable +to sleep, unwilling to sleep and avoiding waking kswapd") modified +__GFP_WAIT to explicitly identify the difference between atomic callers +and those that were unwilling to sleep. Later the definition was +removed entirely. + +The GFP_RECLAIM_MASK is the set of flags that affect watermark checking +and reclaim behaviour but __GFP_ATOMIC was never added. Without it, +atomic users of the slab allocator strip the __GFP_ATOMIC flag and +cannot access the page allocator atomic reserves. This patch addresses +the problem. + +The user-visible impact depends on the workload but potentially atomic +allocations unnecessarily fail without this path. + +Link: http://lkml.kernel.org/r/20160610093832.GK2527@techsingularity.net +Signed-off-by: Mel Gorman +Reported-by: Marcin Wojtas +Acked-by: Vlastimil Babka +Acked-by: Michal Hocko +Cc: [4.4+] +Signed-off-by: Andrew Morton +Signed-off-by: Linus Torvalds +--- + mm/internal.h | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/mm/internal.h b/mm/internal.h +index a37e5b6..2524ec8 100644 +--- a/mm/internal.h ++++ b/mm/internal.h +@@ -24,7 +24,8 @@ + */ + #define GFP_RECLAIM_MASK (__GFP_RECLAIM|__GFP_HIGH|__GFP_IO|__GFP_FS|\ + __GFP_NOWARN|__GFP_REPEAT|__GFP_NOFAIL|\ +- __GFP_NORETRY|__GFP_MEMALLOC|__GFP_NOMEMALLOC) ++ __GFP_NORETRY|__GFP_MEMALLOC|__GFP_NOMEMALLOC|\ ++ __GFP_ATOMIC) + + /* The GFP flags allowed during early boot */ + #define GFP_BOOT_MASK (__GFP_BITS_MASK & ~(__GFP_RECLAIM|__GFP_IO|__GFP_FS)) +-- +2.9.0 + diff --git a/kernel/kernel/files/patches/mageia/mm-tmpfs-don-t-undo-fallocate-past-its-last-page.patch b/kernel/kernel/files/patches/mageia/mm-tmpfs-don-t-undo-fallocate-past-its-last-page.patch new file mode 100644 index 00000000..73eb40a2 --- /dev/null +++ b/kernel/kernel/files/patches/mageia/mm-tmpfs-don-t-undo-fallocate-past-its-last-page.patch @@ -0,0 +1,39 @@ +From b9b4bb26af017dbe930cd4df7f9b2fc3a0497bfe Mon Sep 17 00:00:00 2001 +From: Anthony Romano +Date: Fri, 24 Jun 2016 14:48:43 -0700 +Subject: [PATCH] tmpfs: don't undo fallocate past its last page + +When fallocate is interrupted it will undo a range that extends one byte +past its range of allocated pages. This can corrupt an in-use page by +zeroing out its first byte. Instead, undo using the inclusive byte +range. + +Fixes: 1635f6a74152f1d ("tmpfs: undo fallocation on failure") +Link: http://lkml.kernel.org/r/1462713387-16724-1-git-send-email-anthony.romano@coreos.com +Signed-off-by: Anthony Romano +Cc: Vlastimil Babka +Cc: Hugh Dickins +Cc: Brandon Philips +Cc: +Signed-off-by: Andrew Morton +Signed-off-by: Linus Torvalds +--- + mm/shmem.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/mm/shmem.c b/mm/shmem.c +index a361449..24463b6 100644 +--- a/mm/shmem.c ++++ b/mm/shmem.c +@@ -2227,7 +2227,7 @@ static long shmem_fallocate(struct file *file, int mode, loff_t offset, + /* Remove the !PageUptodate pages we added */ + shmem_undo_range(inode, + (loff_t)start << PAGE_SHIFT, +- (loff_t)index << PAGE_SHIFT, true); ++ ((loff_t)index << PAGE_SHIFT) - 1, true); + goto undone; + } + +-- +2.9.0 + diff --git a/kernel/kernel/files/patches/mageia/net-netfilter-x_tables-check-for-size-overflow.patch b/kernel/kernel/files/patches/mageia/net-netfilter-x_tables-check-for-size-overflow.patch deleted file mode 100644 index 0eb09157..00000000 --- a/kernel/kernel/files/patches/mageia/net-netfilter-x_tables-check-for-size-overflow.patch +++ /dev/null @@ -1,32 +0,0 @@ -List: netfilter-devel -Subject: [PATCH nf] netfilter: x_tables: check for size overflow -From: Florian Westphal -Date: 2016-03-10 0:56:23 -Message-ID: 1457571383-25520-1-git-send-email-fw () strlen ! de -Ben Hawkes says: - integer overflow in xt_alloc_table_info, which on 32-bit systems can - lead to small structure allocation and a copy_from_user based heap - corruption. - -Reported-by: Ben Hawkes -Signed-off-by: Florian Westphal ---- - net/netfilter/x_tables.c | 3 +++ - 1 file changed, 3 insertions(+) - -diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c -index c8a0b7d..17a9a9f 100644 ---- a/net/netfilter/x_tables.c -+++ b/net/netfilter/x_tables.c -@@ -659,6 +659,9 @@ struct xt_table_info *xt_alloc_table_info(unsigned int size) - struct xt_table_info *info = NULL; - size_t sz = sizeof(*info) + size; - -+ if (sz < size || sz < sizeof(*info)) -+ return NULL; -+ - /* Pedantry: prevent them from hitting BUG() in vmalloc.c --RR */ - if ((SMP_ALIGN(size) >> PAGE_SHIFT) + 2 > totalram_pages) - return NULL; --- -2.4.10 diff --git a/kernel/kernel/files/patches/mageia/net-netfilter-x_tables-deal-with-bogus-nextoffset-values.patch b/kernel/kernel/files/patches/mageia/net-netfilter-x_tables-deal-with-bogus-nextoffset-values.patch deleted file mode 100644 index 540f5bc3..00000000 --- a/kernel/kernel/files/patches/mageia/net-netfilter-x_tables-deal-with-bogus-nextoffset-values.patch +++ /dev/null @@ -1,152 +0,0 @@ -List: netfilter-devel -Subject: [PATCH nf] netfilter: x_tables: deal with bogus nextoffset values -From: Florian Westphal -Date: 2016-03-10 0:56:02 -Message-ID: 1457571362-25441-1-git-send-email-fw () strlen ! de - -Ben Hawkes says: - - In the mark_source_chains function (net/ipv4/netfilter/ip_tables.c) it - is possible for a user-supplied ipt_entry structure to have a large - next_offset field. This field is not bounds checked prior to writing a - counter value at the supplied offset. - -Problem is that xt_entry_foreach() macro stops iterating once e->next_offset -is out of bounds, assuming this is the last entry. - -With malformed data thats not necessarily the case so we can -write outside of allocated area later as we might not have walked the -entire blob. - -Fix this by simplifying mark_source_chains -- it already has to check -if nextoff is in range to catch invalid jumps, so just do the check -when we move to a next entry as well. - -Signed-off-by: Florian Westphal ---- - net/ipv4/netfilter/arp_tables.c | 16 ++++++++-------- - net/ipv4/netfilter/ip_tables.c | 15 ++++++++------- - net/ipv6/netfilter/ip6_tables.c | 13 ++++++------- - 3 files changed, 22 insertions(+), 22 deletions(-) - -diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c -index b488cac..5a0b591 100644 ---- a/net/ipv4/netfilter/arp_tables.c -+++ b/net/ipv4/netfilter/arp_tables.c -@@ -437,6 +437,10 @@ static int mark_source_chains(const struct xt_table_info *newinfo, - - /* Move along one */ - size = e->next_offset; -+ -+ if (pos + size > newinfo->size - sizeof(*e)) -+ return 0; -+ - e = (struct arpt_entry *) - (entry0 + pos + size); - e->counters.pcnt = pos; -@@ -447,14 +451,6 @@ static int mark_source_chains(const struct xt_table_info *newinfo, - if (strcmp(t->target.u.user.name, - XT_STANDARD_TARGET) == 0 && - newpos >= 0) { -- if (newpos > newinfo->size - -- sizeof(struct arpt_entry)) { -- duprintf("mark_source_chains: " -- "bad verdict (%i)\n", -- newpos); -- return 0; -- } -- - /* This a jump; chase it. */ - duprintf("Jump rule %u -> %u\n", - pos, newpos); -@@ -462,6 +458,10 @@ static int mark_source_chains(const struct xt_table_info *newinfo, - /* ... this is a fallthru */ - newpos = pos + e->next_offset; - } -+ -+ if (newpos > newinfo->size - sizeof(*e)) -+ return 0; -+ - e = (struct arpt_entry *) - (entry0 + newpos); - e->counters.pcnt = pos; -diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c -index b99affa..ceb995f 100644 ---- a/net/ipv4/netfilter/ip_tables.c -+++ b/net/ipv4/netfilter/ip_tables.c -@@ -519,6 +519,10 @@ mark_source_chains(const struct xt_table_info *newinfo, - - /* Move along one */ - size = e->next_offset; -+ -+ if (pos + size > newinfo->size - sizeof(*e)) -+ return 0; -+ - e = (struct ipt_entry *) - (entry0 + pos + size); - e->counters.pcnt = pos; -@@ -529,13 +533,6 @@ mark_source_chains(const struct xt_table_info *newinfo, - if (strcmp(t->target.u.user.name, - XT_STANDARD_TARGET) == 0 && - newpos >= 0) { -- if (newpos > newinfo->size - -- sizeof(struct ipt_entry)) { -- duprintf("mark_source_chains: " -- "bad verdict (%i)\n", -- newpos); -- return 0; -- } - /* This a jump; chase it. */ - duprintf("Jump rule %u -> %u\n", - pos, newpos); -@@ -543,6 +540,10 @@ mark_source_chains(const struct xt_table_info *newinfo, - /* ... this is a fallthru */ - newpos = pos + e->next_offset; - } -+ -+ if (newpos > newinfo->size - sizeof(*e)) -+ return 0; -+ - e = (struct ipt_entry *) - (entry0 + newpos); - e->counters.pcnt = pos; -diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c -index 99425cf..d88a794 100644 ---- a/net/ipv6/netfilter/ip6_tables.c -+++ b/net/ipv6/netfilter/ip6_tables.c -@@ -531,6 +531,8 @@ mark_source_chains(const struct xt_table_info *newinfo, - - /* Move along one */ - size = e->next_offset; -+ if (pos + size > newinfo->size - sizeof(*e)) -+ return 0; - e = (struct ip6t_entry *) - (entry0 + pos + size); - e->counters.pcnt = pos; -@@ -541,13 +543,6 @@ mark_source_chains(const struct xt_table_info *newinfo, - if (strcmp(t->target.u.user.name, - XT_STANDARD_TARGET) == 0 && - newpos >= 0) { -- if (newpos > newinfo->size - -- sizeof(struct ip6t_entry)) { -- duprintf("mark_source_chains: " -- "bad verdict (%i)\n", -- newpos); -- return 0; -- } - /* This a jump; chase it. */ - duprintf("Jump rule %u -> %u\n", - pos, newpos); -@@ -555,6 +550,10 @@ mark_source_chains(const struct xt_table_info *newinfo, - /* ... this is a fallthru */ - newpos = pos + e->next_offset; - } -+ -+ if (newpos > newinfo->size - sizeof(*e)) -+ return 0; -+ - e = (struct ip6t_entry *) - (entry0 + newpos); - e->counters.pcnt = pos; --- -2.4.10 diff --git a/kernel/kernel/files/patches/mageia/pipe-limit-the-per-user-amount-of-pages-allocated-in-pipes_CVE-2016-2847.patch b/kernel/kernel/files/patches/mageia/pipe-limit-the-per-user-amount-of-pages-allocated-in-pipes_CVE-2016-2847.patch deleted file mode 100644 index 8067901f..00000000 --- a/kernel/kernel/files/patches/mageia/pipe-limit-the-per-user-amount-of-pages-allocated-in-pipes_CVE-2016-2847.patch +++ /dev/null @@ -1,250 +0,0 @@ -From 759c01142a5d0f364a462346168a56de28a80f52 Mon Sep 17 00:00:00 2001 -From: Willy Tarreau -Date: Mon, 18 Jan 2016 16:36:09 +0100 -Subject: [PATCH] pipe: limit the per-user amount of pages allocated in pipes - -On no-so-small systems, it is possible for a single process to cause an -OOM condition by filling large pipes with data that are never read. A -typical process filling 4000 pipes with 1 MB of data will use 4 GB of -memory. On small systems it may be tricky to set the pipe max size to -prevent this from happening. - -This patch makes it possible to enforce a per-user soft limit above -which new pipes will be limited to a single page, effectively limiting -them to 4 kB each, as well as a hard limit above which no new pipes may -be created for this user. This has the effect of protecting the system -against memory abuse without hurting other users, and still allowing -pipes to work correctly though with less data at once. - -The limit are controlled by two new sysctls : pipe-user-pages-soft, and -pipe-user-pages-hard. Both may be disabled by setting them to zero. The -default soft limit allows the default number of FDs per process (1024) -to create pipes of the default size (64kB), thus reaching a limit of 64MB -before starting to create only smaller pipes. With 256 processes limited -to 1024 FDs each, this results in 1024*64kB + (256*1024 - 1024) * 4kB = -1084 MB of memory allocated for a user. The hard limit is disabled by -default to avoid breaking existing applications that make intensive use -of pipes (eg: for splicing). - -Reported-by: socketpair@gmail.com -Reported-by: Tetsuo Handa -Mitigates: CVE-2013-4312 (Linux 2.0+) -Suggested-by: Linus Torvalds -Signed-off-by: Willy Tarreau -Signed-off-by: Al Viro ---- - Documentation/sysctl/fs.txt | 23 ++++++++++++++++++++++ - fs/pipe.c | 47 +++++++++++++++++++++++++++++++++++++++++++-- - include/linux/pipe_fs_i.h | 4 ++++ - include/linux/sched.h | 1 + - kernel/sysctl.c | 14 ++++++++++++++ - 5 files changed, 87 insertions(+), 2 deletions(-) - -diff --git a/Documentation/sysctl/fs.txt b/Documentation/sysctl/fs.txt -index 88152f2..302b5ed 100644 ---- a/Documentation/sysctl/fs.txt -+++ b/Documentation/sysctl/fs.txt -@@ -32,6 +32,8 @@ Currently, these files are in /proc/sys/fs: - - nr_open - - overflowuid - - overflowgid -+- pipe-user-pages-hard -+- pipe-user-pages-soft - - protected_hardlinks - - protected_symlinks - - suid_dumpable -@@ -159,6 +161,27 @@ The default is 65534. - - ============================================================== - -+pipe-user-pages-hard: -+ -+Maximum total number of pages a non-privileged user may allocate for pipes. -+Once this limit is reached, no new pipes may be allocated until usage goes -+below the limit again. When set to 0, no limit is applied, which is the default -+setting. -+ -+============================================================== -+ -+pipe-user-pages-soft: -+ -+Maximum total number of pages a non-privileged user may allocate for pipes -+before the pipe size gets limited to a single page. Once this limit is reached, -+new pipes will be limited to a single page in size for this user in order to -+limit total memory usage, and trying to increase them using fcntl() will be -+denied until usage goes below the limit again. The default value allows to -+allocate up to 1024 pipes at their default size. When set to 0, no limit is -+applied. -+ -+============================================================== -+ - protected_hardlinks: - - A long-standing class of security issues is the hardlink-based -diff --git a/fs/pipe.c b/fs/pipe.c -index 42cf8dd..ab8dad3 100644 ---- a/fs/pipe.c -+++ b/fs/pipe.c -@@ -38,6 +38,12 @@ unsigned int pipe_max_size = 1048576; - */ - unsigned int pipe_min_size = PAGE_SIZE; - -+/* Maximum allocatable pages per user. Hard limit is unset by default, soft -+ * matches default values. -+ */ -+unsigned long pipe_user_pages_hard; -+unsigned long pipe_user_pages_soft = PIPE_DEF_BUFFERS * INR_OPEN_CUR; -+ - /* - * We use a start+len construction, which provides full use of the - * allocated memory. -@@ -583,20 +589,49 @@ pipe_fasync(int fd, struct file *filp, int on) - return retval; - } - -+static void account_pipe_buffers(struct pipe_inode_info *pipe, -+ unsigned long old, unsigned long new) -+{ -+ atomic_long_add(new - old, &pipe->user->pipe_bufs); -+} -+ -+static bool too_many_pipe_buffers_soft(struct user_struct *user) -+{ -+ return pipe_user_pages_soft && -+ atomic_long_read(&user->pipe_bufs) >= pipe_user_pages_soft; -+} -+ -+static bool too_many_pipe_buffers_hard(struct user_struct *user) -+{ -+ return pipe_user_pages_hard && -+ atomic_long_read(&user->pipe_bufs) >= pipe_user_pages_hard; -+} -+ - struct pipe_inode_info *alloc_pipe_info(void) - { - struct pipe_inode_info *pipe; - - pipe = kzalloc(sizeof(struct pipe_inode_info), GFP_KERNEL); - if (pipe) { -- pipe->bufs = kzalloc(sizeof(struct pipe_buffer) * PIPE_DEF_BUFFERS, GFP_KERNEL); -+ unsigned long pipe_bufs = PIPE_DEF_BUFFERS; -+ struct user_struct *user = get_current_user(); -+ -+ if (!too_many_pipe_buffers_hard(user)) { -+ if (too_many_pipe_buffers_soft(user)) -+ pipe_bufs = 1; -+ pipe->bufs = kzalloc(sizeof(struct pipe_buffer) * pipe_bufs, GFP_KERNEL); -+ } -+ - if (pipe->bufs) { - init_waitqueue_head(&pipe->wait); - pipe->r_counter = pipe->w_counter = 1; -- pipe->buffers = PIPE_DEF_BUFFERS; -+ pipe->buffers = pipe_bufs; -+ pipe->user = user; -+ account_pipe_buffers(pipe, 0, pipe_bufs); - mutex_init(&pipe->mutex); - return pipe; - } -+ free_uid(user); - kfree(pipe); - } - -@@ -607,6 +642,8 @@ void free_pipe_info(struct pipe_inode_info *pipe) - { - int i; - -+ account_pipe_buffers(pipe, pipe->buffers, 0); -+ free_uid(pipe->user); - for (i = 0; i < pipe->buffers; i++) { - struct pipe_buffer *buf = pipe->bufs + i; - if (buf->ops) -@@ -998,6 +1035,7 @@ static long pipe_set_size(struct pipe_inode_info *pipe, unsigned long nr_pages) - memcpy(bufs + head, pipe->bufs, tail * sizeof(struct pipe_buffer)); - } - -+ account_pipe_buffers(pipe, pipe->buffers, nr_pages); - pipe->curbuf = 0; - kfree(pipe->bufs); - pipe->bufs = bufs; -@@ -1069,6 +1107,11 @@ long pipe_fcntl(struct file *file, unsigned int cmd, unsigned long arg) - if (!capable(CAP_SYS_RESOURCE) && size > pipe_max_size) { - ret = -EPERM; - goto out; -+ } else if ((too_many_pipe_buffers_hard(pipe->user) || -+ too_many_pipe_buffers_soft(pipe->user)) && -+ !capable(CAP_SYS_RESOURCE) && !capable(CAP_SYS_ADMIN)) { -+ ret = -EPERM; -+ goto out; - } - ret = pipe_set_size(pipe, nr_pages); - break; -diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h -index eb8b8ac..24f5470 100644 ---- a/include/linux/pipe_fs_i.h -+++ b/include/linux/pipe_fs_i.h -@@ -42,6 +42,7 @@ struct pipe_buffer { - * @fasync_readers: reader side fasync - * @fasync_writers: writer side fasync - * @bufs: the circular array of pipe buffers -+ * @user: the user who created this pipe - **/ - struct pipe_inode_info { - struct mutex mutex; -@@ -57,6 +58,7 @@ struct pipe_inode_info { - struct fasync_struct *fasync_readers; - struct fasync_struct *fasync_writers; - struct pipe_buffer *bufs; -+ struct user_struct *user; - }; - - /* -@@ -123,6 +125,8 @@ void pipe_unlock(struct pipe_inode_info *); - void pipe_double_lock(struct pipe_inode_info *, struct pipe_inode_info *); - - extern unsigned int pipe_max_size, pipe_min_size; -+extern unsigned long pipe_user_pages_hard; -+extern unsigned long pipe_user_pages_soft; - int pipe_proc_fn(struct ctl_table *, int, void __user *, size_t *, loff_t *); - - -diff --git a/include/linux/sched.h b/include/linux/sched.h -index 61aa9bb..1589ddc8 100644 ---- a/include/linux/sched.h -+++ b/include/linux/sched.h -@@ -835,6 +835,7 @@ struct user_struct { - #endif - unsigned long locked_shm; /* How many pages of mlocked shm ? */ - unsigned long unix_inflight; /* How many files in flight in unix sockets */ -+ atomic_long_t pipe_bufs; /* how many pages are allocated in pipe buffers */ - - #ifdef CONFIG_KEYS - struct key *uid_keyring; /* UID specific keyring */ -diff --git a/kernel/sysctl.c b/kernel/sysctl.c -index c810f8a..f6fd236 100644 ---- a/kernel/sysctl.c -+++ b/kernel/sysctl.c -@@ -1757,6 +1757,20 @@ static struct ctl_table fs_table[] = { - .proc_handler = &pipe_proc_fn, - .extra1 = &pipe_min_size, - }, -+ { -+ .procname = "pipe-user-pages-hard", -+ .data = &pipe_user_pages_hard, -+ .maxlen = sizeof(pipe_user_pages_hard), -+ .mode = 0644, -+ .proc_handler = proc_doulongvec_minmax, -+ }, -+ { -+ .procname = "pipe-user-pages-soft", -+ .data = &pipe_user_pages_soft, -+ .maxlen = sizeof(pipe_user_pages_soft), -+ .mode = 0644, -+ .proc_handler = proc_doulongvec_minmax, -+ }, - { } - }; - --- -2.7.2 - diff --git a/kernel/kernel/files/patches/mageia/platform-x86-add-shuttle-wmi-driver.patch b/kernel/kernel/files/patches/mageia/platform-x86-add-shuttle-wmi-driver.patch index 2361b737..02f89791 100644 --- a/kernel/kernel/files/patches/mageia/platform-x86-add-shuttle-wmi-driver.patch +++ b/kernel/kernel/files/patches/mageia/platform-x86-add-shuttle-wmi-driver.patch @@ -1419,12 +1419,12 @@ diff -Nurp linux-3.14.2/drivers/platform/x86/shuttle-wmi.c linux-3.14.2-shuttle/ + +module_init(shuttle_wmi_init); +module_exit(shuttle_wmi_exit); -diff -Nurp linux-3.14.2/MAINTAINERS linux-3.14.2-shuttle/MAINTAINERS ---- linux-3.14.2/MAINTAINERS 2014-03-31 06:40:15.000000000 +0300 -+++ linux-3.14.2-shuttle/MAINTAINERS 2014-04-26 18:33:26.869761100 +0300 -@@ -7880,6 +7880,12 @@ S: Odd Fixes +diff -Nurp linux-4.6/MAINTAINERS linux-4.6-shuttle/MAINTAINERS +--- linux-4.6/MAINTAINERS ++++ linux-4.6-shuttle/MAINTAINERS +@@ -10130,6 +10130,12 @@ S: Orphan F: drivers/media/platform/sh_vou.c - F: include/media/sh_vou.h + F: include/media/drv-intf/sh_vou.h +SHUTTLE WMI EXTRAS DRIVER +M: Herton Ronaldo Krzesinski diff --git a/kernel/kernel/pspec.xml b/kernel/kernel/pspec.xml index 3e1be3aa..884dbda9 100644 --- a/kernel/kernel/pspec.xml +++ b/kernel/kernel/pspec.xml @@ -12,7 +12,7 @@ kernel The Linux kernel (the core of the Linux operating system) for Pisi Linux kernel contains the Linux kernel, the core of any Linux operating system. The kernel handles the basic functions of the operating system: memory allocation, process allocation, device input and output, etc. - https://www.kernel.org/pub/linux/kernel/v4.x/linux-4.4.tar.gz + https://www.kernel.org/pub/linux/kernel/v4.x/linux-4.6.tar.gz configs/kernel-i686-config configs/kernel-x86_64-config @@ -28,8 +28,8 @@ - patches/linux/patch-4.4.8.xz - + patches/linux/patch-4.6.3.xz + patches/mageia/Revert-ipmi-Start-the-timer-and-thread-on-internal-m.patch @@ -45,6 +45,7 @@ patches/mageia/acpi-CLEVO-M360S-disable_acpi_irq.patch patches/mageia/acpi-processor-M720SR-limit-to-C2.patch patches/mageia/ACPI-video-Add-a-quirk-to-force-acpi-video-backlight.patch + patches/mageia/acpi-ACPICA-Namespace-Fix-deadlock-triggered-by-MLC-suppo.patch - patches/mageia/3rd-ndiswrapper-1.59.patch + patches/mageia/3rd-ndiswrapper-1.60.patch patches/mageia/3rd-ndiswrapper-Kconfig.patch patches/mageia/3rd-ndiswrapper-Makefile-build-fix.patch - patches/mageia/3rd-ndiswrapper-linux-3.14-buildfix.patch - patches/mageia/3rd-ndiswrapper-4.0-buildfix.patch - patches/mageia/3rd-ndiswrapper-4.2-buildfix.patch patches/mageia/3rd-rfswitch-1.3.patch patches/mageia/3rd-rfswitch-build-fix.patch patches/mageia/3rd-rfswitch-3.0-buildfix.patch @@ -149,18 +128,8 @@ patches/mageia/3rd-viahss-3.0-buildfix.patch patches/mageia/3rd-rtl8723bs.patch patches/mageia/ahci-add-new-Intel-device-IDs.patch - patches/mageia/arm-0001-dt-bindings-Add-root-properties-for-Raspberry-Pi-2.patch - patches/mageia/arm-0002-ARM-bcm2835-Add-a-compat-string-for-bcm2836-machine-.patch - patches/mageia/arm-0003-ARM-bcm2835-Add-Kconfig-support-for-bcm2836.patch - patches/mageia/arm-0011-ARM-bcm2835-Define-two-new-packets-from-the-latest-f.patch - patches/mageia/arm-0012-dt-bindings-add-rpi-power-domain-driver-bindings.patch - patches/mageia/arm-0013-ARM-bcm2835-add-rpi-power-domain-driver.patch - patches/mageia/arm-0021-ARM-bcm2835-Split-the-DT-for-peripherals-from-the-DT.patch - patches/mageia/arm-0022-ARM-bcm2835-Move-the-CPU-peripheral-include-out-of-c.patch - patches/mageia/arm-0023-ARM-bcm2835-Add-devicetree-for-bcm2836-and-Raspberry.patch - patches/mageia/arm-0024-ARM-bcm2835-Add-the-auxiliary-clocks-to-the-device-t.patch - patches/mageia/arm-0031-ARM-bcm2835-enable-all-bcm2835-relevant-in-defconfig.patch - patches/mageia/arm-0032-ARM-bcm2835-enable-auxiliary-spi-driver-in-defconfig.patch + patches/mageia/block-sd-Fix-rw_max-for-devices-that-report-an-optimal-xfe.patch + patches/mageia/arm-0001-ARM-bcm2835-dt-Add-the-ethernet-to-the-device-trees.patch @@ -216,6 +185,17 @@ + + 2016-06-26 + 4.6.3 + Version Bump. + security + + systemRestart + + Ertuğrul Erata + ertugrulerata@gmail.com + 2016-04-30 4.4.8