From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from lists.gentoo.org (pigeon.gentoo.org [208.92.234.80]) by finch.gentoo.org (Postfix) with ESMTP id CB052138247 for ; Thu, 19 Dec 2013 12:54:22 +0000 (UTC) Received: from pigeon.gentoo.org (localhost [127.0.0.1]) by pigeon.gentoo.org (Postfix) with SMTP id 2829AE08E1; Thu, 19 Dec 2013 12:54:21 +0000 (UTC) Received: from smtp.gentoo.org (smtp.gentoo.org [140.211.166.183]) (using TLSv1 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by pigeon.gentoo.org (Postfix) with ESMTPS id 5A89AE08E1 for ; Thu, 19 Dec 2013 12:54:20 +0000 (UTC) Received: from hornbill.gentoo.org (hornbill.gentoo.org [94.100.119.163]) (using TLSv1 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by smtp.gentoo.org (Postfix) with ESMTPS id 0D0D533F42F for ; Thu, 19 Dec 2013 12:54:19 +0000 (UTC) Received: from localhost.localdomain (localhost [127.0.0.1]) by hornbill.gentoo.org (Postfix) with ESMTP id A82051102E6 for ; Thu, 19 Dec 2013 12:54:17 +0000 (UTC) From: "Alexey Shvetsov" To: gentoo-commits@lists.gentoo.org Content-Transfer-Encoding: 8bit Content-type: text/plain; charset=UTF-8 Reply-To: gentoo-dev@lists.gentoo.org, "Alexey Shvetsov" Message-ID: <1387457636.2f65d05125f10b139f2538ff9c2e692479808f62.alexxy@gentoo> Subject: [gentoo-commits] proj/sci:master commit in: sys-cluster/lustre/, sys-cluster/lustre/files/ X-VCS-Repository: proj/sci X-VCS-Files: sys-cluster/lustre/ChangeLog sys-cluster/lustre/files/0001-LU-2982-build-make-AC-check-for-linux-arch-sandbox-f.patch sys-cluster/lustre/files/0002-LU-2686-kernel-Kernel-update-for-3.7.2-201.fc18.patch sys-cluster/lustre/files/0002-LU-3373-ldiskfs-ldiskfs-patches-for-3.11.1-fc19.patch sys-cluster/lustre/files/0003-LU-3079-kernel-3.9-hlist_for_each_entry-uses-3-args.patch sys-cluster/lustre/files/0003-LU-3974-llite-dentry-d_compare-changes-in-3.11.patch sys-cluster/lustre/files/0004-LU-3079-kernel-f_vfsmnt-replaced-by-f_path.mnt.patch sys-cluster/lustre/files/0004-LU-3974-llite-use-new-struct-dir_context.patch sys-cluster/lustre/files/0005-LU-3974-llite-invalidatepage-api-changed.patch sys-cluster/lustre/lustre-9999.ebuild X-VCS-Directories: sys-cluster/lustre/ sys-cluster/lustre/files/ X-VCS-Committer: alexxy X-VCS-Committer-Name: Alexey Shvetsov X-VCS-Revision: 2f65d05125f10b139f2538ff9c2e692479808f62 X-VCS-Branch: master Date: Thu, 19 Dec 2013 12:54:17 +0000 (UTC) Precedence: bulk List-Post: List-Help: List-Unsubscribe: List-Subscribe: List-Id: Gentoo Linux mail X-BeenThere: gentoo-commits@lists.gentoo.org X-Archives-Salt: 484ddf5f-5a56-4a71-9861-bdf0623c70ac X-Archives-Hash: e9ad2fac9f1eab54bbf41350bee03c6f commit: 2f65d05125f10b139f2538ff9c2e692479808f62 Author: Alexey Shvetsov gentoo org> AuthorDate: Thu Dec 19 12:53:56 2013 +0000 Commit: Alexey Shvetsov gentoo org> CommitDate: Thu Dec 19 12:53:56 2013 +0000 URL: http://git.overlays.gentoo.org/gitweb/?p=proj/sci.git;a=commit;h=2f65d051 Update lustre build for 3.11 for workstations =D Package-Manager: portage-2.2.7 RepoMan-Options: --force --- sys-cluster/lustre/ChangeLog | 12 + ...ld-make-AC-check-for-linux-arch-sandbox-f.patch | 10 +- ...6-kernel-Kernel-update-for-3.7.2-201.fc18.patch | 169 - ...3-ldiskfs-ldiskfs-patches-for-3.11.1-fc19.patch | 5997 ++++++++++++++++++++ ...rnel-3.9-hlist_for_each_entry-uses-3-args.patch | 117 - ...74-llite-dentry-d_compare-changes-in-3.11.patch | 132 + ...79-kernel-f_vfsmnt-replaced-by-f_path.mnt.patch | 82 - ...-LU-3974-llite-use-new-struct-dir_context.patch | 276 + ...-LU-3974-llite-invalidatepage-api-changed.patch | 134 + sys-cluster/lustre/lustre-9999.ebuild | 20 +- 10 files changed, 6560 insertions(+), 389 deletions(-) diff --git a/sys-cluster/lustre/ChangeLog b/sys-cluster/lustre/ChangeLog index b769d9b..69315ef 100644 --- a/sys-cluster/lustre/ChangeLog +++ b/sys-cluster/lustre/ChangeLog @@ -2,6 +2,18 @@ # Copyright 1999-2013 Gentoo Foundation; Distributed under the GPL v2 # $Header: $ + 19 Dec 2013; Alexey Shvetsov + +files/0002-LU-3373-ldiskfs-ldiskfs-patches-for-3.11.1-fc19.patch, + +files/0003-LU-3974-llite-dentry-d_compare-changes-in-3.11.patch, + +files/0004-LU-3974-llite-use-new-struct-dir_context.patch, + +files/0005-LU-3974-llite-invalidatepage-api-changed.patch, + -files/0002-LU-2686-kernel-Kernel-update-for-3.7.2-201.fc18.patch, + -files/0003-LU-3079-kernel-3.9-hlist_for_each_entry-uses-3-args.patch, + -files/0004-LU-3079-kernel-f_vfsmnt-replaced-by-f_path.mnt.patch, + files/0001-LU-2982-build-make-AC-check-for-linux-arch-sandbox-f.patch, + lustre-9999.ebuild: + Update lustre build for 3.11 for workstations =D + 11 Jul 2013; Alexey Shvetsov +files/0001-LU-2982-build-make-AC-check-for-linux-arch-sandbox-f.patch, +files/0002-LU-2686-kernel-Kernel-update-for-3.7.2-201.fc18.patch, diff --git a/sys-cluster/lustre/files/0001-LU-2982-build-make-AC-check-for-linux-arch-sandbox-f.patch b/sys-cluster/lustre/files/0001-LU-2982-build-make-AC-check-for-linux-arch-sandbox-f.patch index a5af8be..2b3fa1a 100644 --- a/sys-cluster/lustre/files/0001-LU-2982-build-make-AC-check-for-linux-arch-sandbox-f.patch +++ b/sys-cluster/lustre/files/0001-LU-2982-build-make-AC-check-for-linux-arch-sandbox-f.patch @@ -1,7 +1,7 @@ -From e4b7000ba2dde158a990130bcaee91256255c280 Mon Sep 17 00:00:00 2001 +From a607b37a64f797b766825ccb6f41176685cd843f Mon Sep 17 00:00:00 2001 From: Alexey Shvetsov Date: Mon, 18 Mar 2013 16:22:27 +0400 -Subject: [PATCH 1/4] LU-2982 build: make AC check for linux arch sandbox +Subject: [PATCH 1/5] LU-2982 build: make AC check for linux arch sandbox friendly this commit makes AC check for linux kernel arch sandbox friendly @@ -14,10 +14,10 @@ Signed-off-by: Alexey Shvetsov 1 file changed, 58 insertions(+), 8 deletions(-) diff --git a/config/lustre-build-linux.m4 b/config/lustre-build-linux.m4 -index 95d20e2..de9df76 100644 +index b1ec10e..4a835ea 100644 --- a/config/lustre-build-linux.m4 +++ b/config/lustre-build-linux.m4 -@@ -365,14 +365,64 @@ rm -f build/conftest.o build/conftest.mod.c build/conftest.mod.o build/conftest. +@@ -394,14 +394,64 @@ rm -f build/conftest.o build/conftest.mod.c build/conftest.mod.o build/conftest. # Determine the kernel's idea of the current architecture # AC_DEFUN([LB_LINUX_ARCH], @@ -91,5 +91,5 @@ index 95d20e2..de9df76 100644 # # LB_LINUX_TRY_COMPILE -- -1.8.2.1 +1.8.5.1 diff --git a/sys-cluster/lustre/files/0002-LU-2686-kernel-Kernel-update-for-3.7.2-201.fc18.patch b/sys-cluster/lustre/files/0002-LU-2686-kernel-Kernel-update-for-3.7.2-201.fc18.patch deleted file mode 100644 index 8e02095..0000000 --- a/sys-cluster/lustre/files/0002-LU-2686-kernel-Kernel-update-for-3.7.2-201.fc18.patch +++ /dev/null @@ -1,169 +0,0 @@ -From 1359b3ff29d39aae651457e39942b5ff61092d6c Mon Sep 17 00:00:00 2001 -From: yangsheng -Date: Wed, 10 Jul 2013 13:52:41 +0800 -Subject: [PATCH 2/4] LU-2686 kernel: Kernel update for 3.7.2-201.fc18 - -Add uapi header to include search path - -Signed-off-by: yang sheng -Signed-off-by: James Simmons -Change-Id: Id90e091fef63ebf5bb8e7765969aa3148cc31aa2 ---- - autoMakefile.am | 2 +- - config/lustre-build-linux.m4 | 19 ++++++++++++------- - ldiskfs/config/ldiskfs-build.m4 | 20 +++++++++++++------- - lustre/scripts/version_tag.pl | 4 +++- - 4 files changed, 29 insertions(+), 16 deletions(-) - -diff --git a/autoMakefile.am b/autoMakefile.am -index 9b6dcfd..71d8db4 100644 ---- a/autoMakefile.am -+++ b/autoMakefile.am -@@ -84,7 +84,7 @@ all-am: modules - modules: $(DEP) all-sources - $(MAKE) CC="$(CC)" $(CROSS_VARS) -C $(LINUX_OBJ) \ - -f $(PWD)/build/Makefile LUSTRE_LINUX_CONFIG=$(LINUX_CONFIG) \ -- LINUXINCLUDE='$(EXTRA_LNET_INCLUDE) -I$$(srctree)/arch/$$(SRCARCH)/include -I$$(srctree)/arch/$$(SRCARCH)/include/generated -Iinclude $$(if $$(KBUILD_SRC),-Iinclude2 -I$$(srctree)/include) -include $(CONFIG_INCLUDE)' \ -+ LINUXINCLUDE='$(EXTRA_LNET_INCLUDE) -I$$(srctree)/arch/$$(SRCARCH)/include -I$$(srctree)/arch/$$(SRCARCH)/include/generated -Iinclude $$(if $$(KBUILD_SRC),-Iinclude2 -I$$(srctree)/include) -I$$(srctree)/arch/$$(SRCARCH)/include/uapi -Iarch/$$(SRCARCH)/include/generated/uapi -I$$(srctree)/include/uapi -Iinclude/generated/uapi -include $(CONFIG_INCLUDE)' \ - $(MODULE_TARGET)=$(PWD) -o tmp_include_depends -o scripts -o \ - include/config/MARKER $@ - endif # LINUX -diff --git a/config/lustre-build-linux.m4 b/config/lustre-build-linux.m4 -index de9df76..8849f14 100644 ---- a/config/lustre-build-linux.m4 -+++ b/config/lustre-build-linux.m4 -@@ -44,7 +44,7 @@ AC_MSG_CHECKING([for Linux release]) - if test -s $LINUX_OBJ/include/$AUTOCONF_HDIR/utsrelease.h ; then - LINUXRELEASEHEADER=$AUTOCONF_HDIR/utsrelease.h - else -- LINUXRELEASEHEADER=linux/version.h -+ LINUXRELEASEHEADER=$VERSION_HDIR/version.h - fi - LB_LINUX_TRY_MAKE([ - #include <$LINUXRELEASEHEADER> -@@ -65,7 +65,7 @@ LB_LINUX_TRY_MAKE([ - rm -f build/conftest.i - if test x$LINUXRELEASE = x ; then - AC_MSG_RESULT([unknown]) -- AC_MSG_ERROR([Could not determine Linux release version from linux/version.h.]) -+ AC_MSG_ERROR([Could not determine Linux release version from $LINUXRELEASEHEADER.]) - fi - AC_MSG_RESULT([$LINUXRELEASE]) - AC_SUBST(LINUXRELEASE) -@@ -96,7 +96,7 @@ AC_SUBST(RELEASE) - # check is redhat/suse kernels - AC_MSG_CHECKING([that RedHat kernel]) - LB_LINUX_TRY_COMPILE([ -- #include -+ #include <$VERSION_HDIR/version.h> - ],[ - #ifndef RHEL_RELEASE_CODE - #error "not redhat kernel" -@@ -222,8 +222,12 @@ LB_CHECK_FILE([$LINUX_OBJ/include/generated/autoconf.h],[AUTOCONF_HDIR=generated - [LB_CHECK_FILE([$LINUX_OBJ/include/linux/autoconf.h],[AUTOCONF_HDIR=linux], - [AC_MSG_ERROR([Run make config in $LINUX.])])]) - AC_SUBST(AUTOCONF_HDIR) --LB_CHECK_FILE([$LINUX_OBJ/include/linux/version.h],[], -- [AC_MSG_ERROR([Run make config in $LINUX.])]) -+LB_CHECK_FILE([$LINUX_OBJ/include/linux/version.h], [VERSION_HDIR=linux], -+ [LB_CHECK_FILE([$LINUX_OBJ/include/generated/uapi/linux/version.h], -+ [VERSION_HDIR=generated/uapi/linux], -+ [AC_MSG_ERROR([Run make config in $LINUX.])]) -+ ]) -+ AC_SUBST(VERSION_HDIR) - - # ----------- kconfig.h exists --------------- - # kernel 3.1, $LINUX/include/linux/kconfig.h is added -@@ -240,7 +244,7 @@ LB_CHECK_FILE([$LINUX_OBJ/include/linux/kconfig.h], - # tarred up the tree and ran make dep etc. in it, then - # version.h gets overwritten with a standard linux one. - --if grep rhconfig $LINUX_OBJ/include/linux/version.h >/dev/null ; then -+if grep rhconfig $LINUX_OBJ/include/$VERSION_HDIR/version.h >/dev/null ; then - # This is a clean kernel-source tree, we need to - # enable extensive workarounds to get this to build - # modules -@@ -352,7 +356,8 @@ $2 - AC_DEFUN([LB_LINUX_COMPILE_IFELSE], - [m4_ifvaln([$1], [AC_LANG_CONFTEST([$1])])dnl - rm -f build/conftest.o build/conftest.mod.c build/conftest.ko --AS_IF([AC_TRY_COMMAND(cp conftest.c build && make -d [$2] ${LD:+"LD=$LD"} CC="$CC" -f $PWD/build/Makefile LUSTRE_LINUX_CONFIG=$LINUX_CONFIG LINUXINCLUDE="$EXTRA_LNET_INCLUDE -I$LINUX/arch/`echo $target_cpu|sed -e 's/powerpc64/powerpc/' -e 's/x86_64/x86/' -e 's/i.86/x86/'`/include -I$LINUX/arch/`echo $target_cpu|sed -e 's/ppc.*/powerpc/' -e 's/x86_64/x86/' -e 's/i.86/x86/'`/include/generated -I$LINUX_OBJ/include -I$LINUX/include -I$LINUX_OBJ/include2 -include $CONFIG_INCLUDE" -o tmp_include_depends -o scripts -o include/config/MARKER -C $LINUX_OBJ EXTRA_CFLAGS="-Werror-implicit-function-declaration $EXTRA_KCFLAGS" $CROSS_VARS $MODULE_TARGET=$PWD/build) >/dev/null && AC_TRY_COMMAND([$3])], -+SUBARCH=$(echo $target_cpu | sed -e 's/powerpc64/powerpc/' -e 's/x86_64/x86/' -e 's/i.86/x86/') -+AS_IF([AC_TRY_COMMAND(cp conftest.c build && make -d [$2] ${LD:+"LD=$LD"} CC="$CC" -f $PWD/build/Makefile LUSTRE_LINUX_CONFIG=$LINUX_CONFIG LINUXINCLUDE="$EXTRA_LNET_INCLUDE -I$LINUX/arch/$SUBARCH/include -I$LINUX/arch/$SUBARCH/include/generated -Iinclude -I$LINUX/include -Iinclude2 -I$LINUX/include/uapi -I$LINUX/include/generated -I$LINUX/arch/$SUBARCH/include/uapi -Iarch/$SUBARCH/include/generated/uapi -I$LINUX/include/uapi -Iinclude/generated/uapi -include $CONFIG_INCLUDE" -o tmp_include_depends -o scripts -o include/config/MARKER -C $LINUX_OBJ EXTRA_CFLAGS="-Werror-implicit-function-declaration $EXTRA_KCFLAGS" $CROSS_VARS $MODULE_TARGET=$PWD/build) >/dev/null && AC_TRY_COMMAND([$3])], - [$4], - [_AC_MSG_LOG_CONFTEST - m4_ifvaln([$5],[$5])dnl]) -diff --git a/ldiskfs/config/ldiskfs-build.m4 b/ldiskfs/config/ldiskfs-build.m4 -index 38fce59..8a00872 100644 ---- a/ldiskfs/config/ldiskfs-build.m4 -+++ b/ldiskfs/config/ldiskfs-build.m4 -@@ -1,9 +1,9 @@ - AC_DEFUN([LDISKFS_AC_LINUX_VERSION], [ - AC_MSG_CHECKING([kernel source version]) - -- utsrelease1=${LINUX_OBJ}/include/linux/version.h -+ utsrelease1=${LINUX_OBJ}/include/generated/utsrelease.h - utsrelease2=${LINUX_OBJ}/include/linux/utsrelease.h -- utsrelease3=${LINUX_OBJ}/include/generated/utsrelease.h -+ utsrelease3=${LINUX_OBJ}/include/linux/version.h - AS_IF([test -r ${utsrelease1} && fgrep -q UTS_RELEASE ${utsrelease1}], [ - utsrelease=${utsrelease1} - ], [test -r ${utsrelease2} && fgrep -q UTS_RELEASE ${utsrelease2}], [ -@@ -60,10 +60,10 @@ AC_SUBST(RELEASE) - - # check is redhat/suse kernels - AC_MSG_CHECKING([for RedHat kernel version]) -- AS_IF([fgrep -q RHEL_RELEASE ${LINUX_OBJ}/include/linux/version.h], [ -+ AS_IF([fgrep -q RHEL_RELEASE ${LINUX_OBJ}/include/$VERSION_HDIR/version.h], [ - RHEL_KERNEL="yes" - RHEL_RELEASE=$(expr 0$(awk -F \" '/ RHEL_RELEASE / { print [$]2 }' \ -- ${LINUX_OBJ}/include/linux/version.h) + 1) -+ ${LINUX_OBJ}/include/$VERSION_HDIR/version.h) + 1) - KERNEL_VERSION=$(sed -e 's/\(@<:@23@:>@\.@<:@0-9@:>@*\.@<:@0-9@:>@*\).*/\1/' <<< ${LINUXRELEASE}) - RHEL_KERNEL_VERSION=${KERNEL_VERSION}-${RHEL_RELEASE} - AC_SUBST(RHEL_KERNEL_VERSION) -@@ -184,8 +184,14 @@ LB_CHECK_FILE([$LINUX_OBJ/include/generated/autoconf.h],[AUTOCONF_HDIR=generated - [LB_CHECK_FILE([$LINUX_OBJ/include/linux/autoconf.h],[AUTOCONF_HDIR=linux], - [AC_MSG_ERROR([Run make config in $LINUX.])])]) - AC_SUBST(AUTOCONF_HDIR) --LB_CHECK_FILE([$LINUX_OBJ/include/linux/version.h],[], -- [AC_MSG_ERROR([Run make config in $LINUX.])]) -+LB_CHECK_FILE([$LINUX_OBJ/include/linux/version.h], [VERSION_HDIR=linux], -+ [LB_CHECK_FILE([$LINUX_OBJ/include/generated/uapi/linux/version.h], -+ [VERSION_HDIR=generated/uapi/linux], -+ [AC_MSG_ERROR([Run make config in $LINUX.])]) -+ ]) -+ AC_SUBST(VERSION_HDIR) -+ -+ - - # ----------- kconfig.h exists --------------- - # kernel 3.1, $LINUX/include/linux/kconfig.h is added -@@ -202,7 +208,7 @@ LB_CHECK_FILE([$LINUX_OBJ/include/linux/kconfig.h], - # tarred up the tree and ran make dep etc. in it, then - # version.h gets overwritten with a standard linux one. - --if grep rhconfig $LINUX_OBJ/include/linux/version.h >/dev/null ; then -+if grep rhconfig $LINUX_OBJ/include/$VERSION_HDIR/version.h >/dev/null ; then - # This is a clean kernel-source tree, we need to - # enable extensive workarounds to get this to build - # modules -diff --git a/lustre/scripts/version_tag.pl b/lustre/scripts/version_tag.pl -index 61796c9..4d2bb4a 100644 ---- a/lustre/scripts/version_tag.pl -+++ b/lustre/scripts/version_tag.pl -@@ -43,8 +43,10 @@ sub get_kernver($$) - my $objdir = shift; - - my $ver = new IO::File; -- if (!$ver->open("$objdir/include/linux/utsrelease.h") && -+ if (!$ver->open("$objdir/include/generated/utsrelease.h") && -+ !$ver->open("$objdir/include/linux/utsrelease.h") && - !$ver->open("$objdir/include/linux/version.h") && -+ !$ver->open("$dir/include/generated/utsrelease.h") && - !$ver->open("$dir/include/linux/utsrelease.h") && - !$ver->open("$dir/include/linux/version.h")) { - die "Run make dep on '$dir'\n"; --- -1.8.2.1 - diff --git a/sys-cluster/lustre/files/0002-LU-3373-ldiskfs-ldiskfs-patches-for-3.11.1-fc19.patch b/sys-cluster/lustre/files/0002-LU-3373-ldiskfs-ldiskfs-patches-for-3.11.1-fc19.patch new file mode 100644 index 0000000..473d378 --- /dev/null +++ b/sys-cluster/lustre/files/0002-LU-3373-ldiskfs-ldiskfs-patches-for-3.11.1-fc19.patch @@ -0,0 +1,5997 @@ +From e53207df22261a635315a62f1405eb8c7b700963 Mon Sep 17 00:00:00 2001 +From: James Simmons +Date: Thu, 5 Dec 2013 09:05:22 -0500 +Subject: [PATCH 2/5] LU-3373 ldiskfs: ldiskfs patches for 3.11.1 fc19 + +ldiskfs patches + +Signed-off-by: yang sheng +Signed-off-by: James Simmons +Change-Id: I59a8e7086c4567f1fe493ac7f0086365b5a6535c +--- + config/lustre-build-ldiskfs.m4 | 15 +- + config/lustre-build-linux.m4 | 12 +- + .../fc19/ext4-change-entry-avoid-conflict.patch | 71 + + .../patches/fc19/ext4-disable-mb-cache.patch | 150 ++ + .../kernel_patches/patches/fc19/ext4-fiemap.patch | 111 + + .../patches/fc19/ext4-force_over_128tb.patch | 57 + + .../patches/fc19/ext4-inode-version.patch | 59 + + .../patches/fc19/ext4-kill-dx_root.patch | 235 ++ + .../patches/fc19/ext4-large-eas.patch | 785 +++++++ + .../patches/fc19/ext4-lookup-dotdot.patch | 37 + + .../patches/fc19/ext4-max-dir-size.patch | 44 + + .../patches/fc19/ext4-mballoc-extra-checks.patch | 315 +++ + .../fc19/ext4-mballoc-pa_free-mismatch.patch | 109 + + .../kernel_patches/patches/fc19/ext4-misc.patch | 193 ++ + .../patches/fc19/ext4-nocmtime.patch | 28 + + .../patches/fc19/ext4-osd-iam-exports.patch | 56 + + .../patches/fc19/ext4-osd-iop-common.patch | 135 ++ + .../patches/fc19/ext4-pdir-fix.patch | 61 + + .../patches/fc19/ext4-prealloc.patch | 387 ++++ + .../patches/fc19/ext4_data_in_dirent.patch | 649 ++++++ + .../kernel_patches/patches/fc19/ext4_pdirop.patch | 2252 ++++++++++++++++++++ + .../kernel_patches/series/ldiskfs-3.x-fc19.series | 22 + + 22 files changed, 5781 insertions(+), 2 deletions(-) + create mode 100644 ldiskfs/kernel_patches/patches/fc19/ext4-change-entry-avoid-conflict.patch + create mode 100644 ldiskfs/kernel_patches/patches/fc19/ext4-disable-mb-cache.patch + create mode 100644 ldiskfs/kernel_patches/patches/fc19/ext4-fiemap.patch + create mode 100644 ldiskfs/kernel_patches/patches/fc19/ext4-force_over_128tb.patch + create mode 100644 ldiskfs/kernel_patches/patches/fc19/ext4-inode-version.patch + create mode 100644 ldiskfs/kernel_patches/patches/fc19/ext4-kill-dx_root.patch + create mode 100644 ldiskfs/kernel_patches/patches/fc19/ext4-large-eas.patch + create mode 100644 ldiskfs/kernel_patches/patches/fc19/ext4-lookup-dotdot.patch + create mode 100644 ldiskfs/kernel_patches/patches/fc19/ext4-max-dir-size.patch + create mode 100644 ldiskfs/kernel_patches/patches/fc19/ext4-mballoc-extra-checks.patch + create mode 100644 ldiskfs/kernel_patches/patches/fc19/ext4-mballoc-pa_free-mismatch.patch + create mode 100644 ldiskfs/kernel_patches/patches/fc19/ext4-misc.patch + create mode 100644 ldiskfs/kernel_patches/patches/fc19/ext4-nocmtime.patch + create mode 100644 ldiskfs/kernel_patches/patches/fc19/ext4-osd-iam-exports.patch + create mode 100644 ldiskfs/kernel_patches/patches/fc19/ext4-osd-iop-common.patch + create mode 100644 ldiskfs/kernel_patches/patches/fc19/ext4-pdir-fix.patch + create mode 100644 ldiskfs/kernel_patches/patches/fc19/ext4-prealloc.patch + create mode 100644 ldiskfs/kernel_patches/patches/fc19/ext4_data_in_dirent.patch + create mode 100644 ldiskfs/kernel_patches/patches/fc19/ext4_pdirop.patch + create mode 100644 ldiskfs/kernel_patches/series/ldiskfs-3.x-fc19.series + +diff --git a/config/lustre-build-ldiskfs.m4 b/config/lustre-build-ldiskfs.m4 +index 4b15de3..c979bab 100644 +--- a/config/lustre-build-ldiskfs.m4 ++++ b/config/lustre-build-ldiskfs.m4 +@@ -9,7 +9,7 @@ AS_IF([test x$RHEL_KERNEL = xyes], [ + AS_VERSION_COMPARE([$RHEL_KERNEL_VERSION],[2.6.32],[], + [SER="2.6-rhel6.series"],[SER="2.6-rhel6.series"])], + [SER="2.6-rhel6.4.series"],[SER="2.6-rhel6.4.series"]) +-], [test x$SUSE_KERNEL = xyes], [ ++], [ AS_IF([test x$SUSE_KERNEL = xyes], [ + AS_VERSION_COMPARE([$LINUXRELEASE],[3.0.0],[ + AS_VERSION_COMPARE([$LINUXRELEASE],[2.6.32],[], + [SER="2.6-sles11.series"],[SER="2.6-sles11.series"])], +@@ -22,6 +22,19 @@ AS_IF([test x$RHEL_KERNEL = xyes], [ + ;; + esac + ]) ++], [ AS_IF([test x$FEDORA_KERNEL = xyes], [ ++ AS_VERSION_COMPARE([$LINUXRELEASE],[3.11],[], ++ [SER="3.x-fc19.series"],[SER="3.x-fc19.series"]) ++]) ++]) ++]) ++ ++# ++# Handle the case were someone uses their own kernel ++# ++AS_IF([test -z "$SER"], [ ++ AS_VERSION_COMPARE([$LINUXRELEASE],[3.11],[], ++ [SER="3.x-fc19.series"],[SER="3.x-fc19.series"]) + ]) + LDISKFS_SERIES=$SER + +diff --git a/config/lustre-build-linux.m4 b/config/lustre-build-linux.m4 +index 4a835ea..9afda9c 100644 +--- a/config/lustre-build-linux.m4 ++++ b/config/lustre-build-linux.m4 +@@ -109,7 +109,16 @@ AC_MSG_CHECKING([for RedHat kernel version]) + AC_MSG_RESULT([${RHEL_KERNEL_VERSION}]) + ], [ + AC_MSG_RESULT([not found]) +- LB_LINUX_CONFIG([SUSE_KERNEL],[SUSE_KERNEL="yes"],[]) ++ LB_LINUX_CONFIG([SUSE_KERNEL],[SUSE_KERNEL="yes"],[ ++ AC_MSG_CHECKING([for Fedora 19 kernel]) ++ AS_IF([test "$(echo $LINUXRELEASE | grep fc19)" == "$LINUXRELEASE" ],[ ++ AC_MSG_RESULT([yes]) ++ FEDORA_KERNEL="yes" ++ ], [ ++ FEDORA_KERNEL="no" ++ AC_MSG_RESULT([no]) ++ ]) ++ ]) + ]) + + AC_MSG_CHECKING([for kernel module package directory]) +@@ -118,6 +127,7 @@ AC_ARG_WITH([kmp-moddir], + [set the kmod updates or extra directory]), + [KMP_MODDIR=$withval],[ + AS_IF([test x$RHEL_KERNEL = xyes], [KMP_MODDIR="extra"], ++ [test x$FEDORA_KERNEL = xyes], [KMP_MODDIR="extra"], + [test x$SUSE_KERNEL = xyes], [KMP_MODDIR="updates"])]) + + AC_MSG_RESULT($KMP_MODDIR) +diff --git a/ldiskfs/kernel_patches/patches/fc19/ext4-change-entry-avoid-conflict.patch b/ldiskfs/kernel_patches/patches/fc19/ext4-change-entry-avoid-conflict.patch +new file mode 100644 +index 0000000..b1e4b9f +--- /dev/null ++++ b/ldiskfs/kernel_patches/patches/fc19/ext4-change-entry-avoid-conflict.patch +@@ -0,0 +1,71 @@ ++Index: linux-3.10.9-200.fc17.x86_64/fs/ext4/xattr.c ++=================================================================== ++--- linux-3.10.9-200.fc17.x86_64.orig/fs/ext4/xattr.c +++++ linux-3.10.9-200.fc17.x86_64/fs/ext4/xattr.c ++@@ -945,7 +945,7 @@ ext4_xattr_set_entry(struct ext4_xattr_i ++ if (!i->value) { ++ /* Remove the old name. */ ++ size_t size = EXT4_XATTR_LEN(name_len); ++- last = ENTRY((void *)last - size); +++ last = XA_ENTRY((void *)last - size); ++ memmove(s->here, (void *)s->here + size, ++ (void *)last - (void *)s->here + sizeof(__u32)); ++ memset(last, 0, size); ++@@ -1086,9 +1086,9 @@ ext4_xattr_block_set(handle_t *handle, s ++ if (s->base == NULL) ++ goto cleanup; ++ memcpy(s->base, BHDR(bs->bh), bs->bh->b_size); ++- s->first = ENTRY(header(s->base)+1); +++ s->first = XA_ENTRY(header(s->base)+1); ++ header(s->base)->h_refcount = cpu_to_le32(1); ++- s->here = ENTRY(s->base + offset); +++ s->here = XA_ENTRY(s->base + offset); ++ s->end = s->base + bs->bh->b_size; ++ } ++ } else { ++@@ -1101,8 +1101,8 @@ ext4_xattr_block_set(handle_t *handle, s ++ header(s->base)->h_magic = cpu_to_le32(EXT4_XATTR_MAGIC); ++ header(s->base)->h_blocks = cpu_to_le32(1); ++ header(s->base)->h_refcount = cpu_to_le32(1); ++- s->first = ENTRY(header(s->base)+1); ++- s->here = ENTRY(header(s->base)+1); +++ s->first = XA_ENTRY(header(s->base)+1); +++ s->here = XA_ENTRY(header(s->base)+1); ++ s->end = s->base + sb->s_blocksize; ++ } ++ ++@@ -1884,8 +1884,8 @@ ext4_xattr_cmp(struct ext4_xattr_header ++ { ++ struct ext4_xattr_entry *entry1, *entry2; ++ ++- entry1 = ENTRY(header1+1); ++- entry2 = ENTRY(header2+1); +++ entry1 = XA_ENTRY(header1+1); +++ entry2 = XA_ENTRY(header2+1); ++ while (!IS_LAST_ENTRY(entry1)) { ++ if (IS_LAST_ENTRY(entry2)) ++ return 1; ++@@ -2011,7 +2011,7 @@ static void ext4_xattr_rehash(struct ext ++ __u32 hash = 0; ++ ++ ext4_xattr_hash_entry(header, entry); ++- here = ENTRY(header+1); +++ here = XA_ENTRY(header+1); ++ while (!IS_LAST_ENTRY(here)) { ++ if (!here->e_hash) { ++ /* Block is not shared if an entry's hash value == 0 */ ++Index: linux-3.10.9-200.fc17.x86_64/fs/ext4/xattr.h ++=================================================================== ++--- linux-3.10.9-200.fc17.x86_64.orig/fs/ext4/xattr.h +++++ linux-3.10.9-200.fc17.x86_64/fs/ext4/xattr.h ++@@ -77,8 +77,8 @@ struct ext4_xattr_entry { ++ ((b) - EXT4_XATTR_LEN(3) - sizeof(struct ext4_xattr_header) - 4) ++ ++ #define BHDR(bh) ((struct ext4_xattr_header *)((bh)->b_data)) ++-#define ENTRY(ptr) ((struct ext4_xattr_entry *)(ptr)) ++-#define BFIRST(bh) ENTRY(BHDR(bh)+1) +++#define XA_ENTRY(ptr) ((struct ext4_xattr_entry *)(ptr)) +++#define BFIRST(bh) XA_ENTRY(BHDR(bh)+1) ++ #define IS_LAST_ENTRY(entry) (*(__u32 *)(entry) == 0) ++ ++ #define EXT4_ZERO_XATTR_VALUE ((void *)-1) +diff --git a/ldiskfs/kernel_patches/patches/fc19/ext4-disable-mb-cache.patch b/ldiskfs/kernel_patches/patches/fc19/ext4-disable-mb-cache.patch +new file mode 100644 +index 0000000..93a9022 +--- /dev/null ++++ b/ldiskfs/kernel_patches/patches/fc19/ext4-disable-mb-cache.patch +@@ -0,0 +1,150 @@ ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/ext4.h ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/ext4.h +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/ext4.h ++@@ -941,6 +941,7 @@ struct ext4_inode_info { ++ /* ++ * Mount flags set via mount options or defaults ++ */ +++#define EXT4_MOUNT_NO_MBCACHE 0x00001 /* Disable mbcache */ ++ #define EXT4_MOUNT_GRPID 0x00004 /* Create files with directory's group */ ++ #define EXT4_MOUNT_DEBUG 0x00008 /* Some debugging messages */ ++ #define EXT4_MOUNT_ERRORS_CONT 0x00010 /* Continue on errors */ ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/super.c ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/super.c +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/super.c ++@@ -1152,6 +1152,7 @@ enum { ++ Opt_inode_readahead_blks, Opt_journal_ioprio, ++ Opt_mballoc, Opt_force_over_128tb, ++ Opt_dioread_nolock, Opt_dioread_lock, +++ Opt_no_mbcache, ++ Opt_discard, Opt_nodiscard, Opt_init_itable, Opt_noinit_itable, ++ Opt_max_dir_size_kb, ++ }; ++@@ -1230,6 +1231,7 @@ static const match_table_t tokens = { ++ {Opt_discard, "discard"}, ++ {Opt_nodiscard, "nodiscard"}, ++ {Opt_init_itable, "init_itable=%u"}, +++ {Opt_no_mbcache, "no_mbcache"}, ++ {Opt_init_itable, "init_itable"}, ++ {Opt_noinit_itable, "noinit_itable"}, ++ {Opt_max_dir_size_kb, "max_dir_size_kb=%u"}, ++@@ -1389,6 +1391,7 @@ static const struct mount_opts { ++ {Opt_noauto_da_alloc, EXT4_MOUNT_NO_AUTO_DA_ALLOC, MOPT_SET}, ++ {Opt_auto_da_alloc, EXT4_MOUNT_NO_AUTO_DA_ALLOC, MOPT_CLEAR}, ++ {Opt_noinit_itable, EXT4_MOUNT_INIT_INODE_TABLE, MOPT_CLEAR}, +++ {Opt_no_mbcache, EXT4_MOUNT_NO_MBCACHE, MOPT_SET}, ++ {Opt_commit, 0, MOPT_GTE0}, ++ {Opt_max_batch_time, 0, MOPT_GTE0}, ++ {Opt_min_batch_time, 0, MOPT_GTE0}, ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/xattr.c ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/xattr.c +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/xattr.c ++@@ -81,7 +81,8 @@ ++ # define ea_bdebug(bh, fmt, ...) no_printk(fmt, ##__VA_ARGS__) ++ #endif ++ ++-static void ext4_xattr_cache_insert(struct buffer_head *); +++static void ext4_xattr_cache_insert(struct super_block *, +++ struct buffer_head *); ++ static struct buffer_head *ext4_xattr_cache_find(struct inode *, ++ struct ext4_xattr_header *, ++ struct mb_cache_entry **); ++@@ -385,7 +386,7 @@ bad_block: ++ error = -EIO; ++ goto cleanup; ++ } ++- ext4_xattr_cache_insert(bh); +++ ext4_xattr_cache_insert(inode->i_sb, bh); ++ entry = BFIRST(bh); ++ error = ext4_xattr_find_entry(&entry, name_index, name, bh->b_size, 1, ++ inode); ++@@ -546,7 +547,7 @@ ext4_xattr_block_list(struct dentry *den ++ error = -EIO; ++ goto cleanup; ++ } ++- ext4_xattr_cache_insert(bh); +++ ext4_xattr_cache_insert(inode->i_sb, bh); ++ error = ext4_xattr_list_entries(dentry, BFIRST(bh), buffer, buffer_size); ++ ++ cleanup: ++@@ -643,7 +644,9 @@ ext4_xattr_release_block(handle_t *handl ++ struct mb_cache_entry *ce = NULL; ++ int error = 0; ++ ++- ce = mb_cache_entry_get(ext4_xattr_cache, bh->b_bdev, bh->b_blocknr); +++ if (!test_opt(inode->i_sb, NO_MBCACHE)) +++ ce = mb_cache_entry_get(ext4_xattr_cache, bh->b_bdev, +++ bh->b_blocknr); ++ error = ext4_journal_get_write_access(handle, bh); ++ if (error) ++ goto out; ++@@ -1037,8 +1040,10 @@ ext4_xattr_block_set(handle_t *handle, s ++ #define header(x) ((struct ext4_xattr_header *)(x)) ++ ++ if (s->base) { ++- ce = mb_cache_entry_get(ext4_xattr_cache, bs->bh->b_bdev, ++- bs->bh->b_blocknr); +++ if (!test_opt(inode->i_sb, NO_MBCACHE)) +++ ce = mb_cache_entry_get(ext4_xattr_cache, +++ bs->bh->b_bdev, +++ bs->bh->b_blocknr); ++ error = ext4_journal_get_write_access(handle, bs->bh); ++ if (error) ++ goto cleanup; ++@@ -1055,7 +1060,7 @@ ext4_xattr_block_set(handle_t *handle, s ++ if (!IS_LAST_ENTRY(s->first)) ++ ext4_xattr_rehash(header(s->base), ++ s->here); ++- ext4_xattr_cache_insert(bs->bh); +++ ext4_xattr_cache_insert(sb, bs->bh); ++ } ++ unlock_buffer(bs->bh); ++ if (error == -EIO) ++@@ -1138,7 +1143,8 @@ inserted: ++ if (error) ++ goto cleanup_dquot; ++ } ++- mb_cache_entry_release(ce); +++ if (ce) +++ mb_cache_entry_release(ce); ++ ce = NULL; ++ } else if (bs->bh && s->base == bs->bh->b_data) { ++ /* We were modifying this block in-place. */ ++@@ -1191,7 +1197,7 @@ getblk_failed: ++ memcpy(new_bh->b_data, s->base, new_bh->b_size); ++ set_buffer_uptodate(new_bh); ++ unlock_buffer(new_bh); ++- ext4_xattr_cache_insert(new_bh); +++ ext4_xattr_cache_insert(sb, new_bh); ++ error = ext4_handle_dirty_xattr_block(handle, ++ inode, new_bh); ++ if (error) ++@@ -1837,12 +1843,15 @@ ext4_xattr_put_super(struct super_block ++ * Returns 0, or a negative error number on failure. ++ */ ++ static void ++-ext4_xattr_cache_insert(struct buffer_head *bh) +++ext4_xattr_cache_insert(struct super_block *sb, struct buffer_head *bh) ++ { ++ __u32 hash = le32_to_cpu(BHDR(bh)->h_hash); ++ struct mb_cache_entry *ce; ++ int error; ++ +++ if (test_opt(sb, NO_MBCACHE)) +++ return; +++ ++ ce = mb_cache_entry_alloc(ext4_xattr_cache, GFP_NOFS); ++ if (!ce) { ++ ea_bdebug(bh, "out of memory"); ++@@ -1915,6 +1924,8 @@ ext4_xattr_cache_find(struct inode *inod ++ __u32 hash = le32_to_cpu(header->h_hash); ++ struct mb_cache_entry *ce; ++ +++ if (test_opt(inode->i_sb, NO_MBCACHE)) +++ return NULL; ++ if (!header->h_hash) ++ return NULL; /* never share */ ++ ea_idebug(inode, "looking for cached blocks [%x]", (int)hash); +diff --git a/ldiskfs/kernel_patches/patches/fc19/ext4-fiemap.patch b/ldiskfs/kernel_patches/patches/fc19/ext4-fiemap.patch +new file mode 100644 +index 0000000..11d6d93 +--- /dev/null ++++ b/ldiskfs/kernel_patches/patches/fc19/ext4-fiemap.patch +@@ -0,0 +1,111 @@ ++This patch adds direct EXT4_IOC_FIEMAP support to ldiskfs, for Lustre to call ++without having to go through do_vfs_ioctl() (which isn't exported, and has a ++number of other ioctls which are not suitable for Lustre). The actual FIEMAP ++support is already in the kernel/ext4 for normal usage. ++ ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/ext4.h ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/ext4.h +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/ext4.h ++@@ -585,7 +585,7 @@ enum { ++ #define EXT4_IOC_GROUP_ADD _IOW('f', 8, struct ext4_new_group_input) ++ #define EXT4_IOC_MIGRATE _IO('f', 9) ++ /* note ioctl 10 reserved for an early version of the FIEMAP ioctl */ ++- /* note ioctl 11 reserved for filesystem-independent FIEMAP ioctl */ +++#define EXT4_IOC_FIEMAP _IOWR('f', 11, struct fiemap) ++ #define EXT4_IOC_ALLOC_DA_BLKS _IO('f', 12) ++ #define EXT4_IOC_MOVE_EXT _IOWR('f', 15, struct move_extent) ++ #define EXT4_IOC_RESIZE_FS _IOW('f', 16, __u64) ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/ioctl.c ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/ioctl.c +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/ioctl.c ++@@ -214,6 +214,71 @@ swap_boot_out: ++ return err; ++ } ++ +++/* So that the fiemap access checks can't overflow on 32 bit machines. */ +++#define FIEMAP_MAX_EXTENTS (UINT_MAX / sizeof(struct fiemap_extent)) +++ +++static int fiemap_check_ranges(struct super_block *sb, +++ u64 start, u64 len, u64 *new_len) +++{ +++ *new_len = len; +++ +++ if (len == 0) +++ return -EINVAL; +++ +++ if (start > sb->s_maxbytes) +++ return -EFBIG; +++ +++ /* +++ * Shrink request scope to what the fs can actually handle. +++ */ +++ if ((len > sb->s_maxbytes) || +++ (sb->s_maxbytes - len) < start) +++ *new_len = sb->s_maxbytes - start; +++ +++ return 0; +++} +++ +++int ioctl_fiemap(struct inode *inode, struct file *filp, unsigned long arg) +++{ +++ struct fiemap fiemap; +++ u64 len; +++ struct fiemap_extent_info fieinfo = {0, }; +++ struct super_block *sb = inode->i_sb; +++ int error = 0; +++ +++ if (copy_from_user(&fiemap, (struct fiemap __user *) arg, +++ sizeof(struct fiemap))) +++ return -EFAULT; +++ +++ if (fiemap.fm_extent_count > FIEMAP_MAX_EXTENTS) +++ return -EINVAL; +++ +++ error = fiemap_check_ranges(sb, fiemap.fm_start, fiemap.fm_length, +++ &len); +++ if (error) +++ return error; +++ +++ fieinfo.fi_flags = fiemap.fm_flags; +++ fieinfo.fi_extents_max = fiemap.fm_extent_count; +++ fieinfo.fi_extents_start = (struct fiemap_extent *)(arg + sizeof(fiemap)); +++ +++ if (fiemap.fm_extent_count != 0 && +++ !access_ok(VERIFY_WRITE, (void *)arg, +++ offsetof(typeof(fiemap), fm_extents[fiemap.fm_extent_count]))) +++ return -EFAULT; +++ +++ if (fieinfo.fi_flags & FIEMAP_FLAG_SYNC) +++ filemap_write_and_wait(inode->i_mapping); +++ +++ error = ext4_fiemap(inode, &fieinfo, fiemap.fm_start, len); +++ fiemap.fm_flags = fieinfo.fi_flags; +++ fiemap.fm_mapped_extents = fieinfo.fi_extents_mapped; +++ if (copy_to_user((char *)arg, &fiemap, sizeof(fiemap))) +++ error = -EFAULT; +++ +++ return error; +++} +++ ++ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) ++ { ++ struct inode *inode = file_inode(filp); ++@@ -532,6 +597,9 @@ group_add_out: ++ mnt_drop_write_file(filp); ++ return err; ++ } +++ case EXT4_IOC_FIEMAP: { +++ return ioctl_fiemap(inode, filp, arg); +++ } ++ ++ case EXT4_IOC_ALLOC_DA_BLKS: ++ { ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/fiemap.h ++=================================================================== ++--- /dev/null +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/fiemap.h ++@@ -0,0 +1,2 @@ +++ +++#include_next +diff --git a/ldiskfs/kernel_patches/patches/fc19/ext4-force_over_128tb.patch b/ldiskfs/kernel_patches/patches/fc19/ext4-force_over_128tb.patch +new file mode 100644 +index 0000000..84e75e5 +--- /dev/null ++++ b/ldiskfs/kernel_patches/patches/fc19/ext4-force_over_128tb.patch +@@ -0,0 +1,57 @@ ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/super.c ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/super.c +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/super.c ++@@ -60,6 +60,8 @@ static struct ext4_lazy_init *ext4_li_in ++ static struct mutex ext4_li_mtx; ++ static struct ext4_features *ext4_feat; ++ +++static int force_over_128tb; +++ ++ static int ext4_load_journal(struct super_block *, struct ext4_super_block *, ++ unsigned long journal_devnum); ++ static int ext4_show_options(struct seq_file *seq, struct dentry *root); ++@@ -1146,7 +1148,7 @@ enum { ++ Opt_stripe, Opt_delalloc, Opt_nodelalloc, Opt_mblk_io_submit, ++ Opt_nomblk_io_submit, Opt_block_validity, Opt_noblock_validity, ++ Opt_inode_readahead_blks, Opt_journal_ioprio, ++- Opt_mballoc, +++ Opt_mballoc, Opt_force_over_128tb, ++ Opt_dioread_nolock, Opt_dioread_lock, ++ Opt_discard, Opt_nodiscard, Opt_init_itable, Opt_noinit_itable, ++ Opt_max_dir_size_kb, ++@@ -1222,6 +1224,7 @@ static const match_table_t tokens = { ++ {Opt_noauto_da_alloc, "noauto_da_alloc"}, ++ {Opt_dioread_nolock, "dioread_nolock"}, ++ {Opt_dioread_lock, "dioread_lock"}, +++ {Opt_force_over_128tb, "force_over_128tb"}, ++ {Opt_discard, "discard"}, ++ {Opt_nodiscard, "nodiscard"}, ++ {Opt_init_itable, "init_itable=%u"}, ++@@ -1468,6 +1471,9 @@ static int handle_mount_opt(struct super ++ case Opt_iopen_nopriv: ++ case Opt_mballoc: ++ return 1; +++ case Opt_force_over_128tb: +++ force_over_128tb = 1; +++ break; ++ } ++ ++ for (m = ext4_mount_opts; m->token != Opt_err; m++) ++@@ -3718,6 +3724,16 @@ static int ext4_fill_super(struct super_ ++ goto failed_mount; ++ } ++ +++ if (ext4_blocks_count(es) > (8ULL << 32)) { +++ if (force_over_128tb == 0) { +++ printk(KERN_ERR "EXT4-fs does not support filesystems " +++ "greater than 128TB and can cause data corruption." +++ "Use \"force_over_128tb\" mount option to override." +++ "\n"); +++ goto failed_mount; +++ } +++ } +++ ++ if (EXT4_BLOCKS_PER_GROUP(sb) == 0) ++ goto cantfind_ext4; ++ +diff --git a/ldiskfs/kernel_patches/patches/fc19/ext4-inode-version.patch b/ldiskfs/kernel_patches/patches/fc19/ext4-inode-version.patch +new file mode 100644 +index 0000000..2cae2f0 +--- /dev/null ++++ b/ldiskfs/kernel_patches/patches/fc19/ext4-inode-version.patch +@@ -0,0 +1,59 @@ ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/inode.c ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/inode.c +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/inode.c ++@@ -4145,11 +4145,11 @@ struct inode *ext4_iget(struct super_blo ++ EXT4_INODE_GET_XTIME(i_atime, inode, raw_inode); ++ EXT4_EINODE_GET_XTIME(i_crtime, ei, raw_inode); ++ ++- inode->i_version = le32_to_cpu(raw_inode->i_disk_version); +++ ei->i_fs_version = le32_to_cpu(raw_inode->i_disk_version); ++ if (EXT4_INODE_SIZE(inode->i_sb) > EXT4_GOOD_OLD_INODE_SIZE) { ++ if (EXT4_FITS_IN_INODE(raw_inode, ei, i_version_hi)) ++- inode->i_version |= ++- (__u64)(le32_to_cpu(raw_inode->i_version_hi)) << 32; +++ ei->i_fs_version |= (__u64)(le32_to_cpu(raw_inode->i_version_hi)) +++ << 32; ++ } ++ ++ ret = 0; ++@@ -4365,11 +4365,11 @@ static int ext4_do_update_inode(handle_t ++ raw_inode->i_block[block] = ei->i_data[block]; ++ } ++ ++- raw_inode->i_disk_version = cpu_to_le32(inode->i_version); +++ raw_inode->i_disk_version = cpu_to_le32(ei->i_fs_version); ++ if (ei->i_extra_isize) { ++ if (EXT4_FITS_IN_INODE(raw_inode, ei, i_version_hi)) ++- raw_inode->i_version_hi = ++- cpu_to_le32(inode->i_version >> 32); +++ raw_inode->i_version_hi = cpu_to_le32(ei->i_fs_version +++ >> 32); ++ raw_inode->i_extra_isize = cpu_to_le16(ei->i_extra_isize); ++ } ++ ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/ialloc.c ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/ialloc.c +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/ialloc.c ++@@ -899,6 +899,7 @@ got: ++ ei->i_dtime = 0; ++ ei->i_block_group = group; ++ ei->i_last_alloc_group = ~0; +++ ei->i_fs_version = 0; ++ ++ ext4_set_inode_flags(inode); ++ if (IS_DIRSYNC(inode)) ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/ext4.h ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/ext4.h +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/ext4.h ++@@ -915,6 +915,8 @@ struct ext4_inode_info { ++ tid_t i_sync_tid; ++ tid_t i_datasync_tid; ++ +++ __u64 i_fs_version; +++ ++ /* Precomputed uuid+inum+igen checksum for seeding inode checksums */ ++ __u32 i_csum_seed; ++ }; +diff --git a/ldiskfs/kernel_patches/patches/fc19/ext4-kill-dx_root.patch b/ldiskfs/kernel_patches/patches/fc19/ext4-kill-dx_root.patch +new file mode 100644 +index 0000000..f9c65d0 +--- /dev/null ++++ b/ldiskfs/kernel_patches/patches/fc19/ext4-kill-dx_root.patch +@@ -0,0 +1,235 @@ ++removes static definition of dx_root struct. so that "." and ".." dirent can ++have extra data. This patch does not change any functionality but is required for ++ext4_data_in_dirent patch. ++ ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/namei.c ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/namei.c +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/namei.c ++@@ -201,22 +201,13 @@ struct dx_entry ++ * hash version mod 4 should never be 0. Sincerely, the paranoia department. ++ */ ++ ++-struct dx_root +++struct dx_root_info ++ { ++- struct fake_dirent dot; ++- char dot_name[4]; ++- struct fake_dirent dotdot; ++- char dotdot_name[4]; ++- struct dx_root_info ++- { ++- __le32 reserved_zero; ++- u8 hash_version; ++- u8 info_length; /* 8 */ ++- u8 indirect_levels; ++- u8 unused_flags; ++- } ++- info; ++- struct dx_entry entries[0]; +++ __le32 reserved_zero; +++ u8 hash_version; +++ u8 info_length; /* 8 */ +++ u8 indirect_levels; +++ u8 unused_flags; ++ }; ++ ++ struct dx_node ++@@ -519,6 +510,16 @@ ext4_next_entry(struct ext4_dir_entry_2 ++ * Future: use high four bits of block for coalesce-on-delete flags ++ * Mask them off for now. ++ */ +++struct dx_root_info * dx_get_dx_info(struct ext4_dir_entry_2 *de) +++{ +++ /* get dotdot first */ +++ de = (struct ext4_dir_entry_2 *)((char *)de + EXT4_DIR_REC_LEN(1)); +++ +++ /* dx root info is after dotdot entry */ +++ de = (struct ext4_dir_entry_2 *)((char *)de + EXT4_DIR_REC_LEN(2)); +++ +++ return (struct dx_root_info *) de; +++} ++ ++ static inline ext4_lblk_t dx_get_block(struct dx_entry *entry) ++ { ++@@ -681,7 +682,7 @@ dx_probe(const struct qstr *d_name, stru ++ { ++ unsigned count, indirect; ++ struct dx_entry *at, *entries, *p, *q, *m; ++- struct dx_root *root; +++ struct dx_root_info * info; ++ struct buffer_head *bh; ++ struct dx_frame *frame = frame_in; ++ u32 hash; ++@@ -692,17 +693,18 @@ dx_probe(const struct qstr *d_name, stru ++ *err = PTR_ERR(bh); ++ goto fail; ++ } ++- root = (struct dx_root *) bh->b_data; ++- if (root->info.hash_version != DX_HASH_TEA && ++- root->info.hash_version != DX_HASH_HALF_MD4 && ++- root->info.hash_version != DX_HASH_LEGACY) { +++ +++ info = dx_get_dx_info((struct ext4_dir_entry_2*)bh->b_data); +++ if (info->hash_version != DX_HASH_TEA && +++ info->hash_version != DX_HASH_HALF_MD4 && +++ info->hash_version != DX_HASH_LEGACY) { ++ ext4_warning(dir->i_sb, "Unrecognised inode hash code %d for directory " ++- "#%lu", root->info.hash_version, dir->i_ino); +++ "#%lu", info->hash_version, dir->i_ino); ++ brelse(bh); ++ *err = ERR_BAD_DX_DIR; ++ goto fail; ++ } ++- hinfo->hash_version = root->info.hash_version; +++ hinfo->hash_version = info->hash_version; ++ if (hinfo->hash_version <= DX_HASH_TEA) ++ hinfo->hash_version += EXT4_SB(dir->i_sb)->s_hash_unsigned; ++ hinfo->seed = EXT4_SB(dir->i_sb)->s_hash_seed; ++@@ -710,27 +712,26 @@ dx_probe(const struct qstr *d_name, stru ++ ext4fs_dirhash(d_name->name, d_name->len, hinfo); ++ hash = hinfo->hash; ++ ++- if (root->info.unused_flags & 1) { +++ if (info->unused_flags & 1) { ++ ext4_warning(dir->i_sb, "Unimplemented inode hash flags: %#06x", ++- root->info.unused_flags); +++ info->unused_flags); ++ brelse(bh); ++ *err = ERR_BAD_DX_DIR; ++ goto fail; ++ } ++ ++- if ((indirect = root->info.indirect_levels) > 1) { +++ if ((indirect = info->indirect_levels) > 1) { ++ ext4_warning(dir->i_sb, "Unimplemented inode hash depth: %#06x", ++- root->info.indirect_levels); +++ info->indirect_levels); ++ brelse(bh); ++ *err = ERR_BAD_DX_DIR; ++ goto fail; ++ } ++ ++- entries = (struct dx_entry *) (((char *)&root->info) + ++- root->info.info_length); +++ entries = (struct dx_entry *) (((char *)info) + info->info_length); ++ ++ if (dx_get_limit(entries) != dx_root_limit(dir, ++- root->info.info_length)) { +++ info->info_length)) { ++ ext4_warning(dir->i_sb, "dx entry: limit != root limit"); ++ brelse(bh); ++ *err = ERR_BAD_DX_DIR; ++@@ -815,10 +816,12 @@ fail: ++ ++ static void dx_release (struct dx_frame *frames) ++ { +++ struct dx_root_info *info; ++ if (frames[0].bh == NULL) ++ return; ++ ++- if (((struct dx_root *) frames[0].bh->b_data)->info.indirect_levels) +++ info = dx_get_dx_info((struct ext4_dir_entry_2*)frames[0].bh->b_data); +++ if (info->indirect_levels) ++ brelse(frames[1].bh); ++ brelse(frames[0].bh); ++ } ++@@ -1795,10 +1798,9 @@ static int make_indexed_dir(handle_t *ha ++ const char *name = dentry->d_name.name; ++ int namelen = dentry->d_name.len; ++ struct buffer_head *bh2; ++- struct dx_root *root; ++ struct dx_frame frames[2], *frame; ++ struct dx_entry *entries; ++- struct ext4_dir_entry_2 *de, *de2; +++ struct ext4_dir_entry_2 *de, *de2, *dot_de, *dotdot_de; ++ struct ext4_dir_entry_tail *t; ++ char *data1, *top; ++ unsigned len; ++@@ -1806,7 +1808,7 @@ static int make_indexed_dir(handle_t *ha ++ unsigned blocksize; ++ struct dx_hash_info hinfo; ++ ext4_lblk_t block; ++- struct fake_dirent *fde; +++ struct dx_root_info *dx_info; ++ int csum_size = 0; ++ ++ if (EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb, ++@@ -1821,18 +1823,19 @@ static int make_indexed_dir(handle_t *ha ++ brelse(bh); ++ return retval; ++ } ++- root = (struct dx_root *) bh->b_data; +++ +++ dot_de = (struct ext4_dir_entry_2 *) bh->b_data; +++ dotdot_de = ext4_next_entry(dot_de, blocksize); ++ ++ /* The 0th block becomes the root, move the dirents out */ ++- fde = &root->dotdot; ++- de = (struct ext4_dir_entry_2 *)((char *)fde + ++- ext4_rec_len_from_disk(fde->rec_len, blocksize)); ++- if ((char *) de >= (((char *) root) + blocksize)) { +++ de = (struct ext4_dir_entry_2 *)((char *)dotdot_de + +++ ext4_rec_len_from_disk(dotdot_de->rec_len, blocksize)); +++ if ((char *) de >= (((char *) dot_de) + blocksize)) { ++ EXT4_ERROR_INODE(dir, "invalid rec_len for '..'"); ++ brelse(bh); ++ return -EIO; ++ } ++- len = ((char *) root) + (blocksize - csum_size) - (char *) de; +++ len = ((char *) dot_de) + (blocksize - csum_size) - (char *) de; ++ ++ /* Allocate new block for the 0th block's dirents */ ++ bh2 = ext4_append(handle, dir, &block); ++@@ -1858,19 +1861,23 @@ static int make_indexed_dir(handle_t *ha ++ } ++ ++ /* Initialize the root; the dot dirents already exist */ ++- de = (struct ext4_dir_entry_2 *) (&root->dotdot); ++- de->rec_len = ext4_rec_len_to_disk(blocksize - EXT4_DIR_REC_LEN(2), ++- blocksize); ++- memset (&root->info, 0, sizeof(root->info)); ++- root->info.info_length = sizeof(root->info); ++- root->info.hash_version = EXT4_SB(dir->i_sb)->s_def_hash_version; ++- entries = root->entries; +++ dotdot_de->rec_len = ext4_rec_len_to_disk(blocksize - +++ le16_to_cpu(dot_de->rec_len), blocksize); +++ +++ /* initialize hashing info */ +++ dx_info = dx_get_dx_info(dot_de); +++ memset (dx_info, 0, sizeof(*dx_info)); +++ dx_info->info_length = sizeof(*dx_info); +++ dx_info->hash_version = EXT4_SB(dir->i_sb)->s_def_hash_version; +++ +++ entries = (void *)dx_info + sizeof(*dx_info); +++ ++ dx_set_block(entries, 1); ++ dx_set_count(entries, 1); ++- dx_set_limit(entries, dx_root_limit(dir, sizeof(root->info))); +++ dx_set_limit(entries, dx_root_limit(dir, sizeof(*dx_info))); ++ ++ /* Initialize as for dx_probe */ ++- hinfo.hash_version = root->info.hash_version; +++ hinfo.hash_version = dx_info->hash_version; ++ if (hinfo.hash_version <= DX_HASH_TEA) ++ hinfo.hash_version += EXT4_SB(dir->i_sb)->s_hash_unsigned; ++ hinfo.seed = EXT4_SB(dir->i_sb)->s_hash_seed; ++@@ -2160,6 +2167,7 @@ static int ext4_dx_add_entry(handle_t *h ++ goto journal_error; ++ brelse (bh2); ++ } else { +++ struct dx_root_info * info; ++ dxtrace(printk(KERN_DEBUG ++ "Creating second level index...\n")); ++ memcpy((char *) entries2, (char *) entries, ++@@ -2169,7 +2177,9 @@ static int ext4_dx_add_entry(handle_t *h ++ /* Set up root */ ++ dx_set_count(entries, 1); ++ dx_set_block(entries + 0, newblock); ++- ((struct dx_root *) frames[0].bh->b_data)->info.indirect_levels = 1; +++ info = dx_get_dx_info((struct ext4_dir_entry_2*) +++ frames[0].bh->b_data); +++ info->indirect_levels = 1; ++ ++ /* Add new access path frame */ ++ frame = frames + 1; +diff --git a/ldiskfs/kernel_patches/patches/fc19/ext4-large-eas.patch b/ldiskfs/kernel_patches/patches/fc19/ext4-large-eas.patch +new file mode 100644 +index 0000000..4bbb6f5 +--- /dev/null ++++ b/ldiskfs/kernel_patches/patches/fc19/ext4-large-eas.patch +@@ -0,0 +1,785 @@ ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/ext4.h ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/ext4.h +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/ext4.h ++@@ -1533,6 +1533,7 @@ static inline void ext4_clear_state_flag ++ EXT4_FEATURE_INCOMPAT_EXTENTS| \ ++ EXT4_FEATURE_INCOMPAT_64BIT| \ ++ EXT4_FEATURE_INCOMPAT_FLEX_BG| \ +++ EXT4_FEATURE_INCOMPAT_EA_INODE| \ ++ EXT4_FEATURE_INCOMPAT_MMP | \ ++ EXT4_FEATURE_INCOMPAT_DIRDATA| \ ++ EXT4_FEATURE_INCOMPAT_INLINE_DATA) ++@@ -1940,6 +1941,12 @@ struct mmpd_data { ++ #endif ++ ++ /* +++ * Maximum size of xattr attributes for FEATURE_INCOMPAT_EA_INODE 1Mb +++ * This limit is arbitrary, but is reasonable for the xattr API. +++ */ +++#define EXT4_XATTR_MAX_LARGE_EA_SIZE (1024 * 1024) +++ +++/* ++ * Function prototypes ++ */ ++ ++@@ -2163,6 +2170,7 @@ extern void ext4_set_inode_flags(struct ++ extern void ext4_get_inode_flags(struct ext4_inode_info *); ++ extern int ext4_alloc_da_blocks(struct inode *inode); ++ extern void ext4_set_aops(struct inode *inode); +++extern int ext4_meta_trans_blocks(struct inode *, int nrblocks, int chunk); ++ extern int ext4_writepage_trans_blocks(struct inode *); ++ extern int ext4_chunk_trans_blocks(struct inode *, int nrblocks); ++ extern int ext4_block_truncate_page(handle_t *handle, ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/inode.c ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/inode.c +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/inode.c ++@@ -136,8 +136,6 @@ static void ext4_invalidatepage(struct p ++ unsigned int length); ++ static int __ext4_journalled_writepage(struct page *page, unsigned int len); ++ static int ext4_bh_delay_or_unwritten(handle_t *handle, struct buffer_head *bh); ++-static int ext4_meta_trans_blocks(struct inode *inode, int lblocks, ++- int pextents); ++ ++ /* ++ * Test whether an inode is a fast symlink. ++@@ -4716,7 +4714,7 @@ static int ext4_index_trans_blocks(struc ++ * ++ * Also account for superblock, inode, quota and xattr blocks ++ */ ++-static int ext4_meta_trans_blocks(struct inode *inode, int lblocks, +++int ext4_meta_trans_blocks(struct inode *inode, int lblocks, ++ int pextents) ++ { ++ ext4_group_t groups, ngroups = ext4_get_groups_count(inode->i_sb); ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/xattr.c ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/xattr.c +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/xattr.c ++@@ -220,19 +220,26 @@ ext4_xattr_check_block(struct inode *ino ++ } ++ ++ static inline int ++-ext4_xattr_check_entry(struct ext4_xattr_entry *entry, size_t size) +++ext4_xattr_check_entry(struct ext4_xattr_entry *entry, size_t size, +++ struct inode *inode) ++ { ++ size_t value_size = le32_to_cpu(entry->e_value_size); ++ ++- if (entry->e_value_block != 0 || value_size > size || ++- le16_to_cpu(entry->e_value_offs) + value_size > size) +++ if ((entry->e_value_inum == 0) && +++ (le16_to_cpu(entry->e_value_offs) + value_size > size)) +++ return -EIO; +++ if (entry->e_value_inum != 0 && +++ (le32_to_cpu(entry->e_value_inum) < EXT4_FIRST_INO(inode->i_sb) || +++ le32_to_cpu(entry->e_value_inum) > +++ le32_to_cpu(EXT4_SB(inode->i_sb)->s_es->s_inodes_count))) ++ return -EIO; ++ return 0; ++ } ++ ++ static int ++ ext4_xattr_find_entry(struct ext4_xattr_entry **pentry, int name_index, ++- const char *name, size_t size, int sorted) +++ const char *name, size_t size, int sorted, +++ struct inode *inode) ++ { ++ struct ext4_xattr_entry *entry; ++ size_t name_len; ++@@ -252,11 +259,103 @@ ext4_xattr_find_entry(struct ext4_xattr_ ++ break; ++ } ++ *pentry = entry; ++- if (!cmp && ext4_xattr_check_entry(entry, size)) +++ if (!cmp && ext4_xattr_check_entry(entry, size, inode)) ++ return -EIO; ++ return cmp ? -ENODATA : 0; ++ } ++ +++/* +++ * Read the EA value from an inode. +++ */ +++static int +++ext4_xattr_inode_read(struct inode *ea_inode, void *buf, size_t *size) +++{ +++ unsigned long block = 0; +++ struct buffer_head *bh = NULL; +++ int err, blocksize; +++ size_t csize, ret_size = 0; +++ +++ if (*size == 0) +++ return 0; +++ +++ blocksize = ea_inode->i_sb->s_blocksize; +++ +++ while (ret_size < *size) { +++ csize = (*size - ret_size) > blocksize ? blocksize : +++ *size - ret_size; +++ bh = ext4_bread(NULL, ea_inode, block, 0, &err); +++ if (!bh) { +++ *size = ret_size; +++ return err; +++ } +++ memcpy(buf, bh->b_data, csize); +++ brelse(bh); +++ +++ buf += csize; +++ block += 1; +++ ret_size += csize; +++ } +++ +++ *size = ret_size; +++ +++ return err; +++} +++ +++struct inode *ext4_xattr_inode_iget(struct inode *parent, int ea_ino, int *err) +++{ +++ struct inode *ea_inode = NULL; +++ +++ ea_inode = ext4_iget(parent->i_sb, ea_ino); +++ if (IS_ERR(ea_inode) || is_bad_inode(ea_inode)) { +++ ext4_error(parent->i_sb, "error while reading EA inode %d", +++ ea_ino); +++ *err = -EIO; +++ return NULL; +++ } +++ +++ if (ea_inode->i_xattr_inode_parent != parent->i_ino || +++ ea_inode->i_generation != parent->i_generation) { +++ ext4_error(parent->i_sb, "Backpointer from EA inode %d " +++ "to parent invalid.", ea_ino); +++ *err = -EINVAL; +++ goto error; +++ } +++ +++ if (!(EXT4_I(ea_inode)->i_flags & EXT4_EA_INODE_FL)) { +++ ext4_error(parent->i_sb, "EA inode %d does not have " +++ "EXT4_EA_INODE_FL flag set.\n", ea_ino); +++ *err = -EINVAL; +++ goto error; +++ } +++ +++ *err = 0; +++ return ea_inode; +++ +++error: +++ iput(ea_inode); +++ return NULL; +++} +++ +++/* +++ * Read the value from the EA inode. +++ */ +++static int +++ext4_xattr_inode_get(struct inode *inode, int ea_ino, void *buffer, +++ size_t *size) +++{ +++ struct inode *ea_inode = NULL; +++ int err; +++ +++ ea_inode = ext4_xattr_inode_iget(inode, ea_ino, &err); +++ if (err) +++ return err; +++ +++ err = ext4_xattr_inode_read(ea_inode, buffer, size); +++ iput(ea_inode); +++ +++ return err; +++} +++ ++ static int ++ ext4_xattr_block_get(struct inode *inode, int name_index, const char *name, ++ void *buffer, size_t buffer_size) ++@@ -288,7 +387,8 @@ bad_block: ++ } ++ ext4_xattr_cache_insert(bh); ++ entry = BFIRST(bh); ++- error = ext4_xattr_find_entry(&entry, name_index, name, bh->b_size, 1); +++ error = ext4_xattr_find_entry(&entry, name_index, name, bh->b_size, 1, +++ inode); ++ if (error == -EIO) ++ goto bad_block; ++ if (error) ++@@ -298,8 +398,16 @@ bad_block: ++ error = -ERANGE; ++ if (size > buffer_size) ++ goto cleanup; ++- memcpy(buffer, bh->b_data + le16_to_cpu(entry->e_value_offs), ++- size); +++ if (entry->e_value_inum != 0) { +++ error = ext4_xattr_inode_get(inode, +++ le32_to_cpu(entry->e_value_inum), +++ buffer, &size); +++ if (error) +++ goto cleanup; +++ } else { +++ memcpy(buffer, bh->b_data + +++ le16_to_cpu(entry->e_value_offs), size); +++ } ++ } ++ error = size; ++ ++@@ -333,7 +441,7 @@ ext4_xattr_ibody_get(struct inode *inode ++ if (error) ++ goto cleanup; ++ error = ext4_xattr_find_entry(&entry, name_index, name, ++- end - (void *)entry, 0); +++ end - (void *)entry, 0, inode); ++ if (error) ++ goto cleanup; ++ size = le32_to_cpu(entry->e_value_size); ++@@ -341,8 +449,16 @@ ext4_xattr_ibody_get(struct inode *inode ++ error = -ERANGE; ++ if (size > buffer_size) ++ goto cleanup; ++- memcpy(buffer, (void *)IFIRST(header) + ++- le16_to_cpu(entry->e_value_offs), size); +++ if (entry->e_value_inum != 0) { +++ error = ext4_xattr_inode_get(inode, +++ le32_to_cpu(entry->e_value_inum), +++ buffer, &size); +++ if (error) +++ goto cleanup; +++ } else { +++ memcpy(buffer, (void *)IFIRST(header) + +++ le16_to_cpu(entry->e_value_offs), size); +++ } ++ } ++ error = size; ++ ++@@ -568,7 +684,7 @@ static size_t ext4_xattr_free_space(stru ++ { ++ for (; !IS_LAST_ENTRY(last); last = EXT4_XATTR_NEXT(last)) { ++ *total += EXT4_XATTR_LEN(last->e_name_len); ++- if (!last->e_value_block && last->e_value_size) { +++ if (last->e_value_inum == 0 && last->e_value_size > 0) { ++ size_t offs = le16_to_cpu(last->e_value_offs); ++ if (offs < *min_offs) ++ *min_offs = offs; ++@@ -577,16 +693,171 @@ static size_t ext4_xattr_free_space(stru ++ return (*min_offs - ((void *)last - base) - sizeof(__u32)); ++ } ++ +++/* +++ * Write the value of the EA in an inode. +++ */ +++static int +++ext4_xattr_inode_write(handle_t *handle, struct inode *ea_inode, +++ const void *buf, int bufsize) +++{ +++ struct buffer_head *bh = NULL; +++ unsigned long block = 0; +++ unsigned blocksize = ea_inode->i_sb->s_blocksize; +++ unsigned max_blocks = (bufsize + blocksize - 1) >> ea_inode->i_blkbits; +++ int csize, wsize = 0; +++ int ret = 0; +++ int retries = 0; +++ +++retry: +++ while (ret >= 0 && ret < max_blocks) { +++ struct ext4_map_blocks map; +++ map.m_lblk = block += ret; +++ map.m_len = max_blocks -= ret; +++ +++ ret = ext4_map_blocks(handle, ea_inode, &map, EXT4_GET_BLOCKS_CREATE); +++ if (ret <= 0) { +++ ext4_mark_inode_dirty(handle, ea_inode); +++ if (ret == -ENOSPC && +++ ext4_should_retry_alloc(ea_inode->i_sb, &retries)) { +++ ret = 0; +++ goto retry; +++ } +++ break; +++ } +++ } +++ +++ if (ret < 0) +++ return ret; +++ +++ block = 0; +++ while (wsize < bufsize) { +++ if (bh != NULL) +++ brelse(bh); +++ csize = (bufsize - wsize) > blocksize ? blocksize : +++ bufsize - wsize; +++ bh = ext4_getblk(handle, ea_inode, block, 0, &ret); +++ if (!bh) +++ goto out; +++ ret = ext4_journal_get_write_access(handle, bh); +++ if (ret) +++ goto out; +++ +++ memcpy(bh->b_data, buf, csize); +++ set_buffer_uptodate(bh); +++ ext4_journal_dirty_metadata(handle, bh); +++ +++ buf += csize; +++ wsize += csize; +++ block += 1; +++ } +++ +++ i_size_write(ea_inode, wsize); +++ ext4_update_i_disksize(ea_inode, wsize); +++ +++ ext4_mark_inode_dirty(handle, ea_inode); +++ +++out: +++ brelse(bh); +++ +++ return ret; +++} +++ +++/* +++ * Create an inode to store the value of a large EA. +++ */ +++static struct inode * +++ext4_xattr_inode_create(handle_t *handle, struct inode *inode) +++{ +++ struct inode *ea_inode = NULL; +++ +++ /* +++ * Let the next inode be the goal, so we try and allocate the EA inode +++ * in the same group, or nearby one. +++ */ +++ ea_inode = ext4_new_inode(handle, inode->i_sb->s_root->d_inode, +++ S_IFREG|0600, NULL, inode->i_ino + 1, NULL); +++ +++ if (!IS_ERR(ea_inode)) { +++ ea_inode->i_op = &ext4_file_inode_operations; +++ ea_inode->i_fop = &ext4_file_operations; +++ ext4_set_aops(ea_inode); +++ ea_inode->i_generation = inode->i_generation; +++ EXT4_I(ea_inode)->i_flags |= EXT4_EA_INODE_FL; +++ +++ /* +++ * A back-pointer from EA inode to parent inode will be useful +++ * for e2fsck. +++ */ +++ ea_inode->i_xattr_inode_parent = inode->i_ino; +++ unlock_new_inode(ea_inode); +++ } +++ +++ return ea_inode; +++} +++ +++/* +++ * Unlink the inode storing the value of the EA. +++ */ ++ static int ++-ext4_xattr_set_entry(struct ext4_xattr_info *i, struct ext4_xattr_search *s) +++ext4_xattr_inode_unlink(struct inode *inode, int ea_ino) +++{ +++ struct inode *ea_inode = NULL; +++ int err; +++ +++ ea_inode = ext4_xattr_inode_iget(inode, ea_ino, &err); +++ if (err) +++ return err; +++ +++ clear_nlink(ea_inode); +++ iput(ea_inode); +++ +++ return 0; +++} +++ +++/* +++ * Add value of the EA in an inode. +++ */ +++static int +++ext4_xattr_inode_set(handle_t *handle, struct inode *inode, int *ea_ino, +++ const void *value, size_t value_len) +++{ +++ struct inode *ea_inode = NULL; +++ int err; +++ +++ /* Create an inode for the EA value */ +++ ea_inode = ext4_xattr_inode_create(handle, inode); +++ if (IS_ERR(ea_inode)) +++ return -1; +++ +++ err = ext4_xattr_inode_write(handle, ea_inode, value, value_len); +++ if (err) +++ clear_nlink(ea_inode); +++ else +++ *ea_ino = ea_inode->i_ino; +++ +++ iput(ea_inode); +++ +++ return err; +++} +++ +++static int +++ext4_xattr_set_entry(struct ext4_xattr_info *i, struct ext4_xattr_search *s, +++ handle_t *handle, struct inode *inode) ++ { ++ struct ext4_xattr_entry *last; ++ size_t free, min_offs = s->end - s->base, name_len = strlen(i->name); +++ int in_inode = i->in_inode; +++ +++ if (EXT4_HAS_INCOMPAT_FEATURE(inode->i_sb, +++ EXT4_FEATURE_INCOMPAT_EA_INODE) && +++ (EXT4_XATTR_SIZE(i->value_len) > +++ EXT4_XATTR_MIN_LARGE_EA_SIZE(inode->i_sb->s_blocksize))) +++ in_inode = 1; ++ ++ /* Compute min_offs and last. */ ++ last = s->first; ++ for (; !IS_LAST_ENTRY(last); last = EXT4_XATTR_NEXT(last)) { ++- if (!last->e_value_block && last->e_value_size) { +++ if (last->e_value_inum == 0 && last->e_value_size > 0) { ++ size_t offs = le16_to_cpu(last->e_value_offs); ++ if (offs < min_offs) ++ min_offs = offs; ++@@ -594,16 +865,21 @@ ext4_xattr_set_entry(struct ext4_xattr_i ++ } ++ free = min_offs - ((void *)last - s->base) - sizeof(__u32); ++ if (!s->not_found) { ++- if (!s->here->e_value_block && s->here->e_value_size) { +++ if (!in_inode && s->here->e_value_inum == 0 && +++ s->here->e_value_size > 0) { ++ size_t size = le32_to_cpu(s->here->e_value_size); ++ free += EXT4_XATTR_SIZE(size); ++ } ++ free += EXT4_XATTR_LEN(name_len); ++ } ++ if (i->value) { ++- if (free < EXT4_XATTR_SIZE(i->value_len) || ++- free < EXT4_XATTR_LEN(name_len) + ++- EXT4_XATTR_SIZE(i->value_len)) +++ size_t value_len = EXT4_XATTR_SIZE(i->value_len); +++ +++ if (in_inode) +++ value_len = 0; +++ +++ if (free < value_len || +++ free < EXT4_XATTR_LEN(name_len) + value_len) ++ return -ENOSPC; ++ } ++ ++@@ -617,7 +893,8 @@ ext4_xattr_set_entry(struct ext4_xattr_i ++ s->here->e_name_len = name_len; ++ memcpy(s->here->e_name, i->name, name_len); ++ } else { ++- if (!s->here->e_value_block && s->here->e_value_size) { +++ if (s->here->e_value_offs > 0 && s->here->e_value_inum == 0 && +++ s->here->e_value_size > 0) { ++ void *first_val = s->base + min_offs; ++ size_t offs = le16_to_cpu(s->here->e_value_offs); ++ void *val = s->base + offs; ++@@ -651,13 +928,17 @@ ext4_xattr_set_entry(struct ext4_xattr_i ++ last = s->first; ++ while (!IS_LAST_ENTRY(last)) { ++ size_t o = le16_to_cpu(last->e_value_offs); ++- if (!last->e_value_block && ++- last->e_value_size && o < offs) +++ if (last->e_value_size > 0 && o < offs) ++ last->e_value_offs = ++ cpu_to_le16(o + size); ++ last = EXT4_XATTR_NEXT(last); ++ } ++ } +++ if (s->here->e_value_inum != 0) { +++ ext4_xattr_inode_unlink(inode, +++ le32_to_cpu(s->here->e_value_inum)); +++ s->here->e_value_inum = 0; +++ } ++ if (!i->value) { ++ /* Remove the old name. */ ++ size_t size = EXT4_XATTR_LEN(name_len); ++@@ -671,10 +952,17 @@ ext4_xattr_set_entry(struct ext4_xattr_i ++ if (i->value) { ++ /* Insert the new value. */ ++ s->here->e_value_size = cpu_to_le32(i->value_len); ++- if (i->value_len) { +++ if (in_inode) { +++ int ea_ino = le32_to_cpu(s->here->e_value_inum); +++ ext4_xattr_inode_set(handle, inode, &ea_ino, i->value, +++ i->value_len); +++ s->here->e_value_inum = cpu_to_le32(ea_ino); +++ s->here->e_value_offs = 0; +++ } else if (i->value_len) { ++ size_t size = EXT4_XATTR_SIZE(i->value_len); ++ void *val = s->base + min_offs - size; ++ s->here->e_value_offs = cpu_to_le16(min_offs - size); +++ s->here->e_value_inum = 0; ++ if (i->value == EXT4_ZERO_XATTR_VALUE) { ++ memset(val, 0, size); ++ } else { ++@@ -724,7 +1012,7 @@ ext4_xattr_block_find(struct inode *inod ++ bs->s.end = bs->bh->b_data + bs->bh->b_size; ++ bs->s.here = bs->s.first; ++ error = ext4_xattr_find_entry(&bs->s.here, i->name_index, ++- i->name, bs->bh->b_size, 1); +++ i->name, bs->bh->b_size, 1, inode); ++ if (error && error != -ENODATA) ++ goto cleanup; ++ bs->s.not_found = error; ++@@ -748,8 +1036,6 @@ ext4_xattr_block_set(handle_t *handle, s ++ ++ #define header(x) ((struct ext4_xattr_header *)(x)) ++ ++- if (i->value && i->value_len > sb->s_blocksize) ++- return -ENOSPC; ++ if (s->base) { ++ ce = mb_cache_entry_get(ext4_xattr_cache, bs->bh->b_bdev, ++ bs->bh->b_blocknr); ++@@ -764,7 +1050,7 @@ ext4_xattr_block_set(handle_t *handle, s ++ ce = NULL; ++ } ++ ea_bdebug(bs->bh, "modifying in-place"); ++- error = ext4_xattr_set_entry(i, s); +++ error = ext4_xattr_set_entry(i, s, handle, inode); ++ if (!error) { ++ if (!IS_LAST_ENTRY(s->first)) ++ ext4_xattr_rehash(header(s->base), ++@@ -815,7 +1101,7 @@ ext4_xattr_block_set(handle_t *handle, s ++ s->end = s->base + sb->s_blocksize; ++ } ++ ++- error = ext4_xattr_set_entry(i, s); +++ error = ext4_xattr_set_entry(i, s, handle, inode); ++ if (error == -EIO) ++ goto bad_block; ++ if (error) ++@@ -963,7 +1249,7 @@ int ext4_xattr_ibody_find(struct inode * ++ /* Find the named attribute. */ ++ error = ext4_xattr_find_entry(&is->s.here, i->name_index, ++ i->name, is->s.end - ++- (void *)is->s.base, 0); +++ (void *)is->s.base, 0, inode); ++ if (error && error != -ENODATA) ++ return error; ++ is->s.not_found = error; ++@@ -981,7 +1267,7 @@ int ext4_xattr_ibody_inline_set(handle_t ++ ++ if (EXT4_I(inode)->i_extra_isize == 0) ++ return -ENOSPC; ++- error = ext4_xattr_set_entry(i, s); +++ error = ext4_xattr_set_entry(i, s, handle, inode); ++ if (error) { ++ if (error == -ENOSPC && ++ ext4_has_inline_data(inode)) { ++@@ -993,7 +1279,7 @@ int ext4_xattr_ibody_inline_set(handle_t ++ error = ext4_xattr_ibody_find(inode, i, is); ++ if (error) ++ return error; ++- error = ext4_xattr_set_entry(i, s); +++ error = ext4_xattr_set_entry(i, s, handle, inode); ++ } ++ if (error) ++ return error; ++@@ -1019,7 +1305,7 @@ static int ext4_xattr_ibody_set(handle_t ++ ++ if (EXT4_I(inode)->i_extra_isize == 0) ++ return -ENOSPC; ++- error = ext4_xattr_set_entry(i, s); +++ error = ext4_xattr_set_entry(i, s, handle, inode); ++ if (error) ++ return error; ++ header = IHDR(inode, ext4_raw_inode(&is->iloc)); ++@@ -1055,7 +1341,7 @@ ext4_xattr_set_handle(handle_t *handle, ++ .name = name, ++ .value = value, ++ .value_len = value_len, ++- +++ .in_inode = 0, ++ }; ++ struct ext4_xattr_ibody_find is = { ++ .s = { .not_found = -ENODATA, }, ++@@ -1120,6 +1406,15 @@ ext4_xattr_set_handle(handle_t *handle, ++ goto cleanup; ++ } ++ error = ext4_xattr_block_set(handle, inode, &i, &bs); +++ if (EXT4_HAS_INCOMPAT_FEATURE(inode->i_sb, +++ EXT4_FEATURE_INCOMPAT_EA_INODE) && +++ error == -ENOSPC) { +++ /* xattr not fit to block, store at external +++ * inode */ +++ i.in_inode = 1; +++ error = ext4_xattr_ibody_set(handle, inode, +++ &i, &is); +++ } ++ if (error) ++ goto cleanup; ++ if (!is.s.not_found) { ++@@ -1167,9 +1462,22 @@ ext4_xattr_set(struct inode *inode, int ++ const void *value, size_t value_len, int flags) ++ { ++ handle_t *handle; +++ struct super_block *sb = inode->i_sb; ++ int error, retries = 0; ++ int credits = ext4_jbd2_credits_xattr(inode); ++ +++ if ((value_len >= EXT4_XATTR_MIN_LARGE_EA_SIZE(sb->s_blocksize)) && +++ EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_EA_INODE)) { +++ int nrblocks = (value_len + sb->s_blocksize - 1) >> +++ sb->s_blocksize_bits; +++ +++ /* For new inode */ +++ credits += EXT4_SINGLEDATA_TRANS_BLOCKS(sb) + 3; +++ +++ /* For data blocks of EA inode */ +++ credits += ext4_meta_trans_blocks(inode, nrblocks, 0); +++ } +++ ++ retry: ++ handle = ext4_journal_start(inode, EXT4_HT_XATTR, credits); ++ if (IS_ERR(handle)) { ++@@ -1181,7 +1489,7 @@ retry: ++ value, value_len, flags); ++ error2 = ext4_journal_stop(handle); ++ if (error == -ENOSPC && ++- ext4_should_retry_alloc(inode->i_sb, &retries)) +++ ext4_should_retry_alloc(sb, &retries)) ++ goto retry; ++ if (error == 0) ++ error = error2; ++@@ -1203,7 +1511,7 @@ static void ext4_xattr_shift_entries(str ++ ++ /* Adjust the value offsets of the entries */ ++ for (; !IS_LAST_ENTRY(last); last = EXT4_XATTR_NEXT(last)) { ++- if (!last->e_value_block && last->e_value_size) { +++ if (last->e_value_inum == 0 && last->e_value_size > 0) { ++ new_offs = le16_to_cpu(last->e_value_offs) + ++ value_offs_shift; ++ BUG_ON(new_offs + le32_to_cpu(last->e_value_size) ++@@ -1443,15 +1751,41 @@ cleanup: ++ /* ++ * ext4_xattr_delete_inode() ++ * ++- * Free extended attribute resources associated with this inode. This +++ * Free extended attribute resources associated with this inode. Traverse +++ * all entries and unlink any xattr inodes associated with this inode. This ++ * is called immediately before an inode is freed. We have exclusive ++- * access to the inode. +++ * access to the inode. If an orphan inode is deleted it will also delete any +++ * xattr block and all xattr inodes. They are checked by ext4_xattr_inode_iget() +++ * to ensure they belong to the parent inode and were not deleted already. ++ */ ++ void ++ ext4_xattr_delete_inode(handle_t *handle, struct inode *inode) ++ { ++ struct buffer_head *bh = NULL; +++ struct ext4_xattr_ibody_header *header; +++ struct ext4_inode *raw_inode; +++ struct ext4_iloc iloc; +++ struct ext4_xattr_entry *entry; +++ int error; +++ +++ if (!ext4_test_inode_state(inode, EXT4_STATE_XATTR)) +++ goto delete_external_ea; +++ +++ error = ext4_get_inode_loc(inode, &iloc); +++ if (error) +++ goto cleanup; +++ raw_inode = ext4_raw_inode(&iloc); +++ header = IHDR(inode, raw_inode); +++ entry = IFIRST(header); +++ for (; !IS_LAST_ENTRY(entry); entry = EXT4_XATTR_NEXT(entry)) { +++ if (entry->e_value_inum != 0) { +++ ext4_xattr_inode_unlink(inode, +++ le32_to_cpu(entry->e_value_inum)); +++ entry->e_value_inum = 0; +++ } +++ } ++ +++delete_external_ea: ++ if (!EXT4_I(inode)->i_file_acl) ++ goto cleanup; ++ bh = sb_bread(inode->i_sb, EXT4_I(inode)->i_file_acl); ++@@ -1466,6 +1800,16 @@ ext4_xattr_delete_inode(handle_t *handle ++ EXT4_I(inode)->i_file_acl); ++ goto cleanup; ++ } +++ +++ entry = BFIRST(bh); +++ for (; !IS_LAST_ENTRY(entry); entry = EXT4_XATTR_NEXT(entry)) { +++ if (entry->e_value_inum != 0) { +++ ext4_xattr_inode_unlink(inode, +++ le32_to_cpu(entry->e_value_inum)); +++ entry->e_value_inum = 0; +++ } +++ } +++ ++ ext4_xattr_release_block(handle, inode, bh); ++ EXT4_I(inode)->i_file_acl = 0; ++ ++@@ -1540,10 +1884,9 @@ ext4_xattr_cmp(struct ext4_xattr_header ++ entry1->e_name_index != entry2->e_name_index || ++ entry1->e_name_len != entry2->e_name_len || ++ entry1->e_value_size != entry2->e_value_size || +++ entry1->e_value_inum != entry2->e_value_inum || ++ memcmp(entry1->e_name, entry2->e_name, entry1->e_name_len)) ++ return 1; ++- if (entry1->e_value_block != 0 || entry2->e_value_block != 0) ++- return -EIO; ++ if (memcmp((char *)header1 + le16_to_cpu(entry1->e_value_offs), ++ (char *)header2 + le16_to_cpu(entry2->e_value_offs), ++ le32_to_cpu(entry1->e_value_size))) ++@@ -1627,7 +1970,7 @@ static inline void ext4_xattr_hash_entry ++ *name++; ++ } ++ ++- if (entry->e_value_block == 0 && entry->e_value_size != 0) { +++ if (entry->e_value_inum == 0 && entry->e_value_size != 0) { ++ __le32 *value = (__le32 *)((char *)header + ++ le16_to_cpu(entry->e_value_offs)); ++ for (n = (le32_to_cpu(entry->e_value_size) + ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/xattr.h ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/xattr.h +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/xattr.h ++@@ -42,7 +42,7 @@ struct ext4_xattr_entry { ++ __u8 e_name_len; /* length of name */ ++ __u8 e_name_index; /* attribute name index */ ++ __le16 e_value_offs; /* offset in disk block of value */ ++- __le32 e_value_block; /* disk block attribute is stored on (n/i) */ +++ __le32 e_value_inum; /* inode in which the value is stored */ ++ __le32 e_value_size; /* size of attribute value */ ++ __le32 e_hash; /* hash value of name and value */ ++ char e_name[0]; /* attribute name */ ++@@ -67,6 +67,15 @@ struct ext4_xattr_entry { ++ EXT4_I(inode)->i_extra_isize)) ++ #define IFIRST(hdr) ((struct ext4_xattr_entry *)((hdr)+1)) ++ +++#define i_xattr_inode_parent i_mtime.tv_sec +++ +++/* +++ * The minimum size of EA value when you start storing it in an external inode +++ * size of block - size of header - size of 1 entry - 4 null bytes +++*/ +++#define EXT4_XATTR_MIN_LARGE_EA_SIZE(b) \ +++ ((b) - EXT4_XATTR_LEN(3) - sizeof(struct ext4_xattr_header) - 4) +++ ++ #define BHDR(bh) ((struct ext4_xattr_header *)((bh)->b_data)) ++ #define ENTRY(ptr) ((struct ext4_xattr_entry *)(ptr)) ++ #define BFIRST(bh) ENTRY(BHDR(bh)+1) ++@@ -75,10 +84,11 @@ struct ext4_xattr_entry { ++ #define EXT4_ZERO_XATTR_VALUE ((void *)-1) ++ ++ struct ext4_xattr_info { ++- int name_index; ++ const char *name; ++ const void *value; ++ size_t value_len; +++ int name_index; +++ int in_inode; ++ }; ++ ++ struct ext4_xattr_search { ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/inline.c ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/inline.c +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/inline.c ++@@ -59,7 +59,7 @@ static int get_max_inline_xattr_value_si ++ ++ /* Compute min_offs. */ ++ for (; !IS_LAST_ENTRY(entry); entry = EXT4_XATTR_NEXT(entry)) { ++- if (!entry->e_value_block && entry->e_value_size) { +++ if (!entry->e_value_inum && entry->e_value_size) { ++ size_t offs = le16_to_cpu(entry->e_value_offs); ++ if (offs < min_offs) ++ min_offs = offs; +diff --git a/ldiskfs/kernel_patches/patches/fc19/ext4-lookup-dotdot.patch b/ldiskfs/kernel_patches/patches/fc19/ext4-lookup-dotdot.patch +new file mode 100644 +index 0000000..f4318c5 +--- /dev/null ++++ b/ldiskfs/kernel_patches/patches/fc19/ext4-lookup-dotdot.patch +@@ -0,0 +1,37 @@ ++Index: linux-3.10.9-200.fc17.x86_64/fs/ext4/namei.c ++=================================================================== ++--- linux-3.10.9-200.fc17.x86_64.orig/fs/ext4/namei.c +++++ linux-3.10.9-200.fc17.x86_64/fs/ext4/namei.c ++@@ -1438,6 +1438,32 @@ static struct dentry *ext4_lookup(struct ++ return ERR_PTR(-EIO); ++ } ++ } +++ /* ".." shouldn't go into dcache to preserve dcache hierarchy +++ * otherwise we'll get parent being a child of actual child. +++ * see bug 10458 for details -bzzz */ +++ if (inode && (dentry->d_name.name[0] == '.' && (dentry->d_name.len == 1 || +++ (dentry->d_name.len == 2 && dentry->d_name.name[1] == '.')))) { +++ struct dentry *goal = NULL; +++ +++ /* first, look for an existing dentry - any one is good */ +++ goal = d_find_any_alias(inode); +++ if (goal == NULL) { +++ spin_lock(&dentry->d_lock); +++ /* there is no alias, we need to make current dentry: +++ * a) inaccessible for __d_lookup() +++ * b) inaccessible for iopen */ +++ J_ASSERT(hlist_unhashed(&dentry->d_alias)); +++ dentry->d_flags |= DCACHE_NFSFS_RENAMED; +++ /* this is d_instantiate() ... */ +++ hlist_add_head(&dentry->d_alias, &inode->i_dentry); +++ dentry->d_inode = inode; +++ spin_unlock(&dentry->d_lock); +++ } +++ if (goal) +++ iput(inode); +++ return goal; +++ } +++ ++ return d_splice_alias(inode, dentry); ++ } ++ +diff --git a/ldiskfs/kernel_patches/patches/fc19/ext4-max-dir-size.patch b/ldiskfs/kernel_patches/patches/fc19/ext4-max-dir-size.patch +new file mode 100644 +index 0000000..a1e12a8 +--- /dev/null ++++ b/ldiskfs/kernel_patches/patches/fc19/ext4-max-dir-size.patch +@@ -0,0 +1,44 @@ ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/super.c ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/super.c +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/super.c ++@@ -2485,8 +2485,11 @@ static ssize_t sbi_ui_show(struct ext4_a ++ struct ext4_sb_info *sbi, char *buf) ++ { ++ unsigned int *ui = (unsigned int *) (((char *) sbi) + a->u.offset); +++ unsigned int v = *ui; ++ ++- return snprintf(buf, PAGE_SIZE, "%u\n", *ui); +++ if (strcmp("max_dir_size", a->attr.name) == 0) +++ v <<= 10; +++ return snprintf(buf, PAGE_SIZE, "%u\n", v); ++ } ++ ++ static ssize_t sbi_ui_store(struct ext4_attr *a, ++@@ -2500,6 +2503,8 @@ static ssize_t sbi_ui_store(struct ext4_ ++ ret = kstrtoul(skip_spaces(buf), 0, &t); ++ if (ret) ++ return ret; +++ if (strcmp("max_dir_size", a->attr.name) == 0) +++ t >>= 10; ++ *ui = t; ++ return count; ++ } ++@@ -2582,6 +2587,8 @@ EXT4_RW_ATTR(reserved_clusters); ++ EXT4_ATTR_OFFSET(inode_readahead_blks, 0644, sbi_ui_show, ++ inode_readahead_blks_store, s_inode_readahead_blks); ++ EXT4_RW_ATTR_SBI_UI(inode_goal, s_inode_goal); +++EXT4_RW_ATTR_SBI_UI(max_dir_size, s_max_dir_size_kb); +++EXT4_RW_ATTR_SBI_UI(max_dir_size_kb, s_max_dir_size_kb); ++ EXT4_RW_ATTR_SBI_UI(mb_stats, s_mb_stats); ++ EXT4_RW_ATTR_SBI_UI(mb_max_to_scan, s_mb_max_to_scan); ++ EXT4_RW_ATTR_SBI_UI(mb_min_to_scan, s_mb_min_to_scan); ++@@ -2600,6 +2607,8 @@ static struct attribute *ext4_attrs[] = ++ ATTR_LIST(reserved_clusters), ++ ATTR_LIST(inode_readahead_blks), ++ ATTR_LIST(inode_goal), +++ ATTR_LIST(max_dir_size), +++ ATTR_LIST(max_dir_size_kb), ++ ATTR_LIST(mb_stats), ++ ATTR_LIST(mb_max_to_scan), ++ ATTR_LIST(mb_min_to_scan), +diff --git a/ldiskfs/kernel_patches/patches/fc19/ext4-mballoc-extra-checks.patch b/ldiskfs/kernel_patches/patches/fc19/ext4-mballoc-extra-checks.patch +new file mode 100644 +index 0000000..a0eb883 +--- /dev/null ++++ b/ldiskfs/kernel_patches/patches/fc19/ext4-mballoc-extra-checks.patch +@@ -0,0 +1,315 @@ ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/ext4.h ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/ext4.h +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/ext4.h ++@@ -2449,6 +2449,7 @@ struct ext4_group_info { ++ ext4_grpblk_t bb_fragments; /* nr of freespace fragments */ ++ ext4_grpblk_t bb_largest_free_order;/* order of largest frag in BG */ ++ struct list_head bb_prealloc_list; +++ unsigned long bb_prealloc_nr; ++ #ifdef DOUBLE_CHECK ++ void *bb_bitmap; ++ #endif ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/mballoc.c ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/mballoc.c +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/mballoc.c ++@@ -362,7 +362,7 @@ static const char *ext4_groupinfo_slab_n ++ "ext4_groupinfo_64k", "ext4_groupinfo_128k" ++ }; ++ ++-static void ext4_mb_generate_from_pa(struct super_block *sb, void *bitmap, +++static int ext4_mb_generate_from_pa(struct super_block *sb, void *bitmap, ++ ext4_group_t group); ++ static void ext4_mb_generate_from_freelist(struct super_block *sb, void *bitmap, ++ ext4_group_t group); ++@@ -718,7 +718,7 @@ mb_set_largest_free_order(struct super_b ++ } ++ ++ static noinline_for_stack ++-void ext4_mb_generate_buddy(struct super_block *sb, +++int ext4_mb_generate_buddy(struct super_block *sb, ++ void *buddy, void *bitmap, ext4_group_t group) ++ { ++ struct ext4_group_info *grp = ext4_get_group_info(sb, group); ++@@ -750,14 +750,13 @@ void ext4_mb_generate_buddy(struct super ++ grp->bb_fragments = fragments; ++ ++ if (free != grp->bb_free) { ++- ext4_grp_locked_error(sb, group, 0, 0, ++- "%u clusters in bitmap, %u in gd", ++- free, grp->bb_free); ++- /* ++- * If we intent to continue, we consider group descritor ++- * corrupt and update bb_free using bitmap value ++- */ ++- grp->bb_free = free; +++ struct ext4_group_desc *gdp; +++ gdp = ext4_get_group_desc (sb, group, NULL); +++ ext4_error(sb, "group %lu: %u blocks in bitmap, %u in bb, " +++ "%u in gd, %lu pa's\n", (long unsigned int)group, +++ free, grp->bb_free, ext4_free_group_clusters(sb, gdp), +++ grp->bb_prealloc_nr); +++ return -EIO; ++ } ++ mb_set_largest_free_order(sb, grp); ++ ++@@ -768,6 +767,8 @@ void ext4_mb_generate_buddy(struct super ++ EXT4_SB(sb)->s_mb_buddies_generated++; ++ EXT4_SB(sb)->s_mb_generation_time += period; ++ spin_unlock(&EXT4_SB(sb)->s_bal_lock); +++ +++ return 0; ++ } ++ ++ static void mb_regenerate_buddy(struct ext4_buddy *e4b) ++@@ -883,7 +884,7 @@ static int ext4_mb_init_cache(struct pag ++ } ++ ++ first_block = page->index * blocks_per_page; ++- for (i = 0; i < blocks_per_page; i++) { +++ for (i = 0; i < blocks_per_page && err == 0; i++) { ++ group = (first_block + i) >> 1; ++ if (group >= ngroups) ++ break; ++@@ -922,7 +923,7 @@ static int ext4_mb_init_cache(struct pag ++ ext4_lock_group(sb, group); ++ /* init the buddy */ ++ memset(data, 0xff, blocksize); ++- ext4_mb_generate_buddy(sb, data, incore, group); +++ err = ext4_mb_generate_buddy(sb, data, incore, group); ++ ext4_unlock_group(sb, group); ++ incore = NULL; ++ } else { ++@@ -937,7 +938,7 @@ static int ext4_mb_init_cache(struct pag ++ memcpy(data, bitmap, blocksize); ++ ++ /* mark all preallocated blks used in in-core bitmap */ ++- ext4_mb_generate_from_pa(sb, data, group); +++ err = ext4_mb_generate_from_pa(sb, data, group); ++ ext4_mb_generate_from_freelist(sb, data, group); ++ ext4_unlock_group(sb, group); ++ ++@@ -947,7 +948,8 @@ static int ext4_mb_init_cache(struct pag ++ incore = data; ++ } ++ } ++- SetPageUptodate(page); +++ if (likely(err == 0)) +++ SetPageUptodate(page); ++ ++ out: ++ if (bh) { ++@@ -2224,9 +2226,11 @@ static void *ext4_mb_seq_groups_next(str ++ static int ext4_mb_seq_groups_show(struct seq_file *seq, void *v) ++ { ++ struct super_block *sb = seq->private; +++ struct ext4_group_desc *gdp; ++ ext4_group_t group = (ext4_group_t) ((unsigned long) v); ++ int i; ++ int err, buddy_loaded = 0; +++ int free = 0; ++ struct ext4_buddy e4b; ++ struct ext4_group_info *grinfo; ++ struct sg { ++@@ -2236,10 +2240,10 @@ static int ext4_mb_seq_groups_show(struc ++ ++ group--; ++ if (group == 0) ++- seq_printf(seq, "#%-5s: %-5s %-5s %-5s " +++ seq_printf(seq, "#%-5s: %-5s %-5s %-5s %-5s %-5s" ++ "[ %-5s %-5s %-5s %-5s %-5s %-5s %-5s " ++ "%-5s %-5s %-5s %-5s %-5s %-5s %-5s ]\n", ++- "group", "free", "frags", "first", +++ "group", "free", "frags", "first", "first", "pa", ++ "2^0", "2^1", "2^2", "2^3", "2^4", "2^5", "2^6", ++ "2^7", "2^8", "2^9", "2^10", "2^11", "2^12", "2^13"); ++ ++@@ -2256,13 +2260,19 @@ static int ext4_mb_seq_groups_show(struc ++ buddy_loaded = 1; ++ } ++ +++ gdp = ext4_get_group_desc(sb, group, NULL); +++ if (gdp != NULL) +++ free = ext4_free_group_clusters(sb, gdp); +++ ++ memcpy(&sg, ext4_get_group_info(sb, group), i); ++ ++ if (buddy_loaded) ++ ext4_mb_unload_buddy(&e4b); ++ ++- seq_printf(seq, "#%-5u: %-5u %-5u %-5u [", group, sg.info.bb_free, ++- sg.info.bb_fragments, sg.info.bb_first_free); +++ seq_printf(seq, "#%-5lu: %-5u %-5u %-5u %-5u %-5lu [", +++ (long unsigned int)group, sg.info.bb_free, free, +++ sg.info.bb_fragments, sg.info.bb_first_free, +++ sg.info.bb_prealloc_nr); ++ for (i = 0; i <= 13; i++) ++ seq_printf(seq, " %-5u", i <= sb->s_blocksize_bits + 1 ? ++ sg.info.bb_counters[i] : 0); ++@@ -3507,22 +3517,67 @@ static void ext4_mb_generate_from_freeli ++ } ++ ++ /* +++ * check free blocks in bitmap match free block in group descriptor +++ * do this before taking preallocated blocks into account to be able +++ * to detect on-disk corruptions. The group lock should be hold by the +++ * caller. +++ */ +++int ext4_mb_check_ondisk_bitmap(struct super_block *sb, void *bitmap, +++ struct ext4_group_desc *gdp, int group) +++{ +++ unsigned short max = EXT4_BLOCKS_PER_GROUP(sb); +++ unsigned short i, first, free = 0; +++ +++ i = mb_find_next_zero_bit(bitmap, max, 0); +++ +++ while (i < max) { +++ first = i; +++ i = mb_find_next_bit(bitmap, max, i); +++ if (i > max) +++ i = max; +++ free += i - first; +++ if (i < max) +++ i = mb_find_next_zero_bit(bitmap, max, i); +++ } +++ +++ if (free != ext4_free_group_clusters(sb, gdp)) { +++ ext4_error(sb, "on-disk bitmap for group %d" +++ "corrupted: %u blocks free in bitmap, %u - in gd\n", +++ group, free, ext4_free_group_clusters(sb, gdp)); +++ return -EIO; +++ } +++ return 0; +++} +++ +++/* ++ * the function goes through all preallocation in this group and marks them ++ * used in in-core bitmap. buddy must be generated from this bitmap ++ * Need to be called with ext4 group lock held ++ */ ++ static noinline_for_stack ++-void ext4_mb_generate_from_pa(struct super_block *sb, void *bitmap, +++int ext4_mb_generate_from_pa(struct super_block *sb, void *bitmap, ++ ext4_group_t group) ++ { ++ struct ext4_group_info *grp = ext4_get_group_info(sb, group); ++ struct ext4_prealloc_space *pa; +++ struct ext4_group_desc *gdp; ++ struct list_head *cur; ++ ext4_group_t groupnr; ++ ext4_grpblk_t start; ++ int preallocated = 0; +++ int skip = 0, count = 0; +++ int err; ++ int len; ++ +++ gdp = ext4_get_group_desc (sb, group, NULL); +++ if (gdp == NULL) +++ return -EIO; +++ +++ /* before applying preallocations, check bitmap consistency */ +++ err = ext4_mb_check_ondisk_bitmap(sb, bitmap, gdp, group); +++ if (err) +++ return err; +++ ++ /* all form of preallocation discards first load group, ++ * so the only competing code is preallocation use. ++ * we don't need any locking here ++@@ -3538,13 +3593,23 @@ void ext4_mb_generate_from_pa(struct sup ++ &groupnr, &start); ++ len = pa->pa_len; ++ spin_unlock(&pa->pa_lock); ++- if (unlikely(len == 0)) +++ if (unlikely(len == 0)) { +++ skip++; ++ continue; +++ } ++ BUG_ON(groupnr != group); ++ ext4_set_bits(bitmap, start, len); ++ preallocated += len; +++ count ++; +++ } +++ if (count + skip != grp->bb_prealloc_nr) { +++ ext4_error(sb, "lost preallocations: " +++ "count %d, bb_prealloc_nr %lu, skip %d\n", +++ count, grp->bb_prealloc_nr, skip); +++ return -EIO; ++ } ++ mb_debug(1, "prellocated %u for group %u\n", preallocated, group); +++ return 0; ++ } ++ ++ static void ext4_mb_pa_callback(struct rcu_head *head) ++@@ -3603,6 +3668,7 @@ static void ext4_mb_put_pa(struct ext4_a ++ */ ++ ext4_lock_group(sb, grp); ++ list_del(&pa->pa_group_list); +++ ext4_get_group_info(sb, grp)->bb_prealloc_nr--; ++ ext4_unlock_group(sb, grp); ++ ++ spin_lock(pa->pa_obj_lock); ++@@ -3697,6 +3763,7 @@ ext4_mb_new_inode_pa(struct ext4_allocat ++ ++ ext4_lock_group(sb, ac->ac_b_ex.fe_group); ++ list_add(&pa->pa_group_list, &grp->bb_prealloc_list); +++ grp->bb_prealloc_nr++; ++ ext4_unlock_group(sb, ac->ac_b_ex.fe_group); ++ ++ spin_lock(pa->pa_obj_lock); ++@@ -3758,6 +3825,7 @@ ext4_mb_new_group_pa(struct ext4_allocat ++ ++ ext4_lock_group(sb, ac->ac_b_ex.fe_group); ++ list_add(&pa->pa_group_list, &grp->bb_prealloc_list); +++ grp->bb_prealloc_nr++; ++ ext4_unlock_group(sb, ac->ac_b_ex.fe_group); ++ ++ /* ++@@ -3927,6 +3995,8 @@ repeat: ++ ++ spin_unlock(&pa->pa_lock); ++ +++ BUG_ON(grp->bb_prealloc_nr == 0); +++ grp->bb_prealloc_nr--; ++ list_del(&pa->pa_group_list); ++ list_add(&pa->u.pa_tmp_list, &list); ++ } ++@@ -4056,7 +4126,7 @@ repeat: ++ if (err) { ++ ext4_error(sb, "Error loading buddy information for %u", ++ group); ++- continue; +++ return; ++ } ++ ++ bitmap_bh = ext4_read_block_bitmap(sb, group); ++@@ -4068,6 +4138,8 @@ repeat: ++ } ++ ++ ext4_lock_group(sb, group); +++ BUG_ON(e4b.bd_info->bb_prealloc_nr == 0); +++ e4b.bd_info->bb_prealloc_nr--; ++ list_del(&pa->pa_group_list); ++ ext4_mb_release_inode_pa(&e4b, bitmap_bh, pa); ++ ext4_unlock_group(sb, group); ++@@ -4328,6 +4400,7 @@ ext4_mb_discard_lg_preallocations(struct ++ } ++ ext4_lock_group(sb, group); ++ list_del(&pa->pa_group_list); +++ ext4_get_group_info(sb, group)->bb_prealloc_nr--; ++ ext4_mb_release_group_pa(&e4b, pa); ++ ext4_unlock_group(sb, group); ++ ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/mballoc.h ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/mballoc.h +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/mballoc.h ++@@ -82,7 +82,7 @@ extern ushort ext4_mballoc_debug; ++ /* ++ * for which requests use 2^N search using buddies ++ */ ++-#define MB_DEFAULT_ORDER2_REQS 2 +++#define MB_DEFAULT_ORDER2_REQS 8 ++ ++ /* ++ * default group prealloc size 512 blocks +diff --git a/ldiskfs/kernel_patches/patches/fc19/ext4-mballoc-pa_free-mismatch.patch b/ldiskfs/kernel_patches/patches/fc19/ext4-mballoc-pa_free-mismatch.patch +new file mode 100644 +index 0000000..df69372 +--- /dev/null ++++ b/ldiskfs/kernel_patches/patches/fc19/ext4-mballoc-pa_free-mismatch.patch +@@ -0,0 +1,109 @@ ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/mballoc.c ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/mballoc.c +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/mballoc.c ++@@ -3747,6 +3747,7 @@ ext4_mb_new_inode_pa(struct ext4_allocat ++ INIT_LIST_HEAD(&pa->pa_group_list); ++ pa->pa_deleted = 0; ++ pa->pa_type = MB_INODE_PA; +++ pa->pa_error = 0; ++ ++ mb_debug(1, "new inode pa %p: %llu/%u for %u\n", pa, ++ pa->pa_pstart, pa->pa_len, pa->pa_lstart); ++@@ -3808,6 +3809,7 @@ ext4_mb_new_group_pa(struct ext4_allocat ++ INIT_LIST_HEAD(&pa->pa_group_list); ++ pa->pa_deleted = 0; ++ pa->pa_type = MB_GROUP_PA; +++ pa->pa_error = 0; ++ ++ mb_debug(1, "new group pa %p: %llu/%u for %u\n", pa, ++ pa->pa_pstart, pa->pa_len, pa->pa_lstart); ++@@ -3868,7 +3870,9 @@ ext4_mb_release_inode_pa(struct ext4_bud ++ int err = 0; ++ int free = 0; ++ +++ assert_spin_locked(ext4_group_lock_ptr(sb, e4b->bd_group)); ++ BUG_ON(pa->pa_deleted == 0); +++ BUG_ON(pa->pa_inode == NULL); ++ ext4_get_group_no_and_offset(sb, pa->pa_pstart, &group, &bit); ++ grp_blk_start = pa->pa_pstart - EXT4_C2B(sbi, bit); ++ BUG_ON(group != e4b->bd_group && pa->pa_len != 0); ++@@ -3891,12 +3895,18 @@ ext4_mb_release_inode_pa(struct ext4_bud ++ mb_free_blocks(pa->pa_inode, e4b, bit, next - bit); ++ bit = next + 1; ++ } ++- if (free != pa->pa_free) { ++- ext4_msg(e4b->bd_sb, KERN_CRIT, ++- "pa %p: logic %lu, phys. %lu, len %lu", ++- pa, (unsigned long) pa->pa_lstart, ++- (unsigned long) pa->pa_pstart, ++- (unsigned long) pa->pa_len); +++ +++ /* "free < pa->pa_free" means we maybe double alloc the same blocks, +++ * otherwise maybe leave some free blocks unavailable, no need to BUG.*/ +++ if ((free > pa->pa_free && !pa->pa_error) || (free < pa->pa_free)) { +++ ext4_error(sb, "pa free mismatch: [pa %p] " +++ "[phy %lu] [logic %lu] [len %u] [free %u] " +++ "[error %u] [inode %lu] [freed %u]", pa, +++ (unsigned long)pa->pa_pstart, +++ (unsigned long)pa->pa_lstart, +++ (unsigned)pa->pa_len, (unsigned)pa->pa_free, +++ (unsigned)pa->pa_error, pa->pa_inode->i_ino, +++ free); ++ ext4_grp_locked_error(sb, group, 0, 0, "free %u, pa_free %u", ++ free, pa->pa_free); ++ /* ++@@ -3904,6 +3914,7 @@ ext4_mb_release_inode_pa(struct ext4_bud ++ * from the bitmap and continue. ++ */ ++ } +++ BUG_ON(pa->pa_free != free); ++ atomic_add(free, &sbi->s_mb_discarded); ++ ++ return err; ++@@ -4661,6 +4672,25 @@ errout: ++ ac->ac_b_ex.fe_len = 0; ++ ar->len = 0; ++ ext4_mb_show_ac(ac); +++ if (ac->ac_pa) { +++ struct ext4_prealloc_space *pa = ac->ac_pa; +++ +++ /* We can not make sure whether the bitmap has +++ * been updated or not when fail case. So can +++ * not revert pa_free back, just mark pa_error*/ +++ pa->pa_error++; +++ ext4_error(sb, +++ "Updating bitmap error: [err %d] " +++ "[pa %p] [phy %lu] [logic %lu] " +++ "[len %u] [free %u] [error %u] " +++ "[inode %lu]", *errp, pa, +++ (unsigned long)pa->pa_pstart, +++ (unsigned long)pa->pa_lstart, +++ (unsigned)pa->pa_len, +++ (unsigned)pa->pa_free, +++ (unsigned)pa->pa_error, +++ pa->pa_inode ? pa->pa_inode->i_ino : 0); +++ } ++ } ++ ext4_mb_release_context(ac); ++ out: ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/mballoc.h ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/mballoc.h +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/mballoc.h ++@@ -19,6 +19,7 @@ ++ #include ++ #include ++ #include +++#include ++ #include "ext4_jbd2.h" ++ #include "ext4.h" ++ ++@@ -126,6 +127,7 @@ struct ext4_prealloc_space { ++ ext4_grpblk_t pa_free; /* how many blocks are free */ ++ unsigned short pa_type; /* pa type. inode or group */ ++ spinlock_t *pa_obj_lock; +++ unsigned short pa_error; ++ struct inode *pa_inode; /* hack, for history only */ ++ }; ++ +diff --git a/ldiskfs/kernel_patches/patches/fc19/ext4-misc.patch b/ldiskfs/kernel_patches/patches/fc19/ext4-misc.patch +new file mode 100644 +index 0000000..1f0ab31 +--- /dev/null ++++ b/ldiskfs/kernel_patches/patches/fc19/ext4-misc.patch +@@ -0,0 +1,193 @@ ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/ext4.h ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/ext4.h +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/ext4.h ++@@ -1421,6 +1421,11 @@ static inline void ext4_clear_state_flag ++ ++ #define NEXT_ORPHAN(inode) EXT4_I(inode)->i_dtime ++ +++/* Has been moved to linux/magic.h but we need it for Lustre */ +++#define EXT4_SUPER_MAGIC 0xEF53 +++#define JOURNAL_START_HAS_3ARGS 1 +++#define HAVE_LDISKFS_MAP_BLOCKS 1 +++ ++ /* ++ * Codes for operating systems ++ */ ++@@ -2670,6 +2675,8 @@ struct ext4_extent; ++ ++ extern int ext4_ext_tree_init(handle_t *handle, struct inode *); ++ extern int ext4_ext_writepage_trans_blocks(struct inode *, int); +++extern struct buffer_head *ext4_read_inode_bitmap(struct super_block *sb, +++ ext4_group_t block_group); ++ extern int ext4_ext_index_trans_blocks(struct inode *inode, int extents); ++ extern int ext4_ext_map_blocks(handle_t *handle, struct inode *inode, ++ struct ext4_map_blocks *map, int flags); ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/ext4_jbd2.c ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/ext4_jbd2.c +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/ext4_jbd2.c ++@@ -34,6 +34,8 @@ static void ext4_put_nojournal(handle_t ++ ++ current->journal_info = handle; ++ } +++EXPORT_SYMBOL(__ext4_journal_get_write_access); +++EXPORT_SYMBOL(__ext4_journal_start_sb); ++ ++ /* ++ * Wrappers for jbd2_journal_start/end. ++@@ -299,3 +301,4 @@ int __ext4_handle_dirty_super(const char ++ mark_buffer_dirty(bh); ++ return err; ++ } +++EXPORT_SYMBOL(__ext4_handle_dirty_metadata); ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/ext4_jbd2.h ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/ext4_jbd2.h +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/ext4_jbd2.h ++@@ -37,6 +37,8 @@ ++ (EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_EXTENTS) \ ++ ? 20U : 8U) ++ +++#define ext4_journal_dirty_metadata(handle, bh) \ +++ ext4_handle_dirty_metadata(handle, NULL, bh) ++ /* Extended attribute operations touch at most two data buffers, ++ * two bitmap buffers, and two group summaries, in addition to the inode ++ * and the superblock, which are already accounted for. */ ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/inode.c ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/inode.c +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/inode.c ++@@ -4222,6 +4222,9 @@ bad_inode: ++ iget_failed(inode); ++ return ERR_PTR(ret); ++ } +++EXPORT_SYMBOL(ext4_iget); +++EXPORT_SYMBOL(ext4_map_blocks); +++EXPORT_SYMBOL(ext4_truncate); ++ ++ static int ext4_inode_blocks_set(handle_t *handle, ++ struct ext4_inode *raw_inode, ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/super.c ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/super.c +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/super.c ++@@ -1141,10 +1141,12 @@ enum { ++ Opt_usrjquota, Opt_grpjquota, Opt_offusrjquota, Opt_offgrpjquota, ++ Opt_jqfmt_vfsold, Opt_jqfmt_vfsv0, Opt_jqfmt_vfsv1, Opt_quota, ++ Opt_noquota, Opt_barrier, Opt_nobarrier, Opt_err, +++ Opt_iopen, Opt_noiopen, Opt_iopen_nopriv, ++ Opt_usrquota, Opt_grpquota, Opt_i_version, ++ Opt_stripe, Opt_delalloc, Opt_nodelalloc, Opt_mblk_io_submit, ++ Opt_nomblk_io_submit, Opt_block_validity, Opt_noblock_validity, ++ Opt_inode_readahead_blks, Opt_journal_ioprio, +++ Opt_mballoc, ++ Opt_dioread_nolock, Opt_dioread_lock, ++ Opt_discard, Opt_nodiscard, Opt_init_itable, Opt_noinit_itable, ++ Opt_max_dir_size_kb, ++@@ -1198,6 +1200,10 @@ static const match_table_t tokens = { ++ {Opt_noquota, "noquota"}, ++ {Opt_quota, "quota"}, ++ {Opt_usrquota, "usrquota"}, +++ {Opt_iopen, "iopen"}, +++ {Opt_noiopen, "noiopen"}, +++ {Opt_iopen_nopriv, "iopen_nopriv"}, +++ {Opt_mballoc, "mballoc"}, ++ {Opt_barrier, "barrier=%u"}, ++ {Opt_barrier, "barrier"}, ++ {Opt_nobarrier, "nobarrier"}, ++@@ -1457,6 +1463,11 @@ static int handle_mount_opt(struct super ++ case Opt_i_version: ++ sb->s_flags |= MS_I_VERSION; ++ return 1; +++ case Opt_iopen: +++ case Opt_noiopen: +++ case Opt_iopen_nopriv: +++ case Opt_mballoc: +++ return 1; ++ } ++ ++ for (m = ext4_mount_opts; m->token != Opt_err; m++) ++@@ -4162,6 +4173,8 @@ out_free_orig: ++ return err ? err : ret; ++ } ++ +++EXPORT_SYMBOL(ext4_force_commit); +++ ++ /* ++ * Setup any per-fs journal parameters now. We'll do this both on ++ * initial mount, once the journal has been initialised but before we've ++@@ -5501,6 +5514,9 @@ static void __exit ext4_exit_fs(void) ++ ext4_exit_es(); ++ } ++ +++EXPORT_SYMBOL(ext4_bread); +++EXPORT_SYMBOL(__ext4_journal_stop); +++ ++ MODULE_AUTHOR("Remy Card, Stephen Tweedie, Andrew Morton, Andreas Dilger, Theodore Ts'o and others"); ++ MODULE_DESCRIPTION("Fourth Extended Filesystem"); ++ MODULE_LICENSE("GPL"); ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/namei.c ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/namei.c +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/namei.c ++@@ -2210,7 +2210,7 @@ out: ++ * DIR_NLINK feature is set if 1) nlinks > EXT4_LINK_MAX or 2) nlinks == 2, ++ * since this indicates that nlinks count was previously 1. ++ */ ++-static void ext4_inc_count(handle_t *handle, struct inode *inode) +++void ext4_inc_count(handle_t *handle, struct inode *inode) ++ { ++ inc_nlink(inode); ++ if (is_dx(inode) && inode->i_nlink > 1) { ++@@ -2222,16 +2222,18 @@ static void ext4_inc_count(handle_t *han ++ } ++ } ++ } +++EXPORT_SYMBOL(ext4_inc_count); ++ ++ /* ++ * If a directory had nlink == 1, then we should let it be 1. This indicates ++ * directory has >EXT4_LINK_MAX subdirs. ++ */ ++-static void ext4_dec_count(handle_t *handle, struct inode *inode) +++void ext4_dec_count(handle_t *handle, struct inode *inode) ++ { ++ if (!S_ISDIR(inode->i_mode) || inode->i_nlink > 2) ++ drop_nlink(inode); ++ } +++EXPORT_SYMBOL(ext4_dec_count); ++ ++ ++ static int ext4_add_nondir(handle_t *handle, ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/ialloc.c ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/ialloc.c +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/ialloc.c ++@@ -111,7 +111,7 @@ void ext4_end_bitmap_read(struct buffer_ ++ * ++ * Return buffer_head of bitmap on success or NULL. ++ */ ++-static struct buffer_head * +++struct buffer_head * ++ ext4_read_inode_bitmap(struct super_block *sb, ext4_group_t block_group) ++ { ++ struct ext4_group_desc *desc; ++@@ -191,6 +191,7 @@ verify: ++ set_buffer_verified(bh); ++ return bh; ++ } +++EXPORT_SYMBOL(ext4_read_inode_bitmap); ++ ++ /* ++ * NOTE! When we get the inode, we're the only people ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/extents.c ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/extents.c +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/extents.c ++@@ -4774,3 +4774,5 @@ int ext4_fiemap(struct inode *inode, str ++ ++ return error; ++ } +++ +++EXPORT_SYMBOL(ext4_mark_inode_dirty); +diff --git a/ldiskfs/kernel_patches/patches/fc19/ext4-nocmtime.patch b/ldiskfs/kernel_patches/patches/fc19/ext4-nocmtime.patch +new file mode 100644 +index 0000000..0717d15 +--- /dev/null ++++ b/ldiskfs/kernel_patches/patches/fc19/ext4-nocmtime.patch +@@ -0,0 +1,28 @@ ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/xattr.c ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/xattr.c +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/xattr.c ++@@ -1432,7 +1432,8 @@ ext4_xattr_set_handle(handle_t *handle, ++ } ++ if (!error) { ++ ext4_xattr_update_super_block(handle, inode->i_sb); ++- inode->i_ctime = ext4_current_time(inode); +++ if (!IS_NOCMTIME(inode)) +++ inode->i_ctime = ext4_current_time(inode); ++ if (!value) ++ ext4_clear_inode_state(inode, EXT4_STATE_NO_EXPAND); ++ error = ext4_mark_iloc_dirty(handle, inode, &is.iloc); ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/namei.c ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/namei.c +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/namei.c ++@@ -1797,7 +1797,8 @@ static int add_dirent_to_buf(handle_t *h ++ * happen is that the times are slightly out of date ++ * and/or different from the directory change time. ++ */ ++- dir->i_mtime = dir->i_ctime = ext4_current_time(dir); +++ if (!IS_NOCMTIME(dir)) +++ dir->i_mtime = dir->i_ctime = ext4_current_time(dir); ++ ext4_update_dx_flag(dir); ++ dir->i_version++; ++ ext4_mark_inode_dirty(handle, dir); +diff --git a/ldiskfs/kernel_patches/patches/fc19/ext4-osd-iam-exports.patch b/ldiskfs/kernel_patches/patches/fc19/ext4-osd-iam-exports.patch +new file mode 100644 +index 0000000..1f2cf1a +--- /dev/null ++++ b/ldiskfs/kernel_patches/patches/fc19/ext4-osd-iam-exports.patch +@@ -0,0 +1,56 @@ ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/ext4.h ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/ext4.h +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/ext4.h ++@@ -2167,6 +2167,9 @@ extern struct buffer_head * ext4_find_en ++ struct ext4_dir_entry_2 ** res_dir, ++ int *inlined); ++ #define ll_ext4_find_entry(inode, dentry, res_dir) ext4_find_entry(inode, &(dentry)->d_name, res_dir, NULL) +++extern struct buffer_head *ext4_append(handle_t *handle, +++ struct inode *inode, +++ ext4_lblk_t *block); ++ extern int ext4_add_dot_dotdot(handle_t *handle, struct inode *dir, ++ struct inode *inode); ++ extern int search_dir(struct buffer_head *bh, ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/hash.c ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/hash.c +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/hash.c ++@@ -206,3 +206,4 @@ int ext4fs_dirhash(const char *name, int ++ hinfo->minor_hash = minor_hash; ++ return 0; ++ } +++EXPORT_SYMBOL(ext4fs_dirhash); ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/namei.c ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/namei.c +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/namei.c ++@@ -48,7 +48,7 @@ ++ #define NAMEI_RA_BLOCKS 4 ++ #define NAMEI_RA_SIZE (NAMEI_RA_CHUNKS * NAMEI_RA_BLOCKS) ++ ++-static struct buffer_head *ext4_append(handle_t *handle, +++struct buffer_head *ext4_append(handle_t *handle, ++ struct inode *inode, ++ ext4_lblk_t *block) ++ { ++@@ -163,6 +163,7 @@ static struct buffer_head *__ext4_read_d ++ } ++ return bh; ++ } +++EXPORT_SYMBOL(ext4_append); ++ ++ #ifndef assert ++ #define assert(test) J_ASSERT(test) ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/super.c ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/super.c +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/super.c ++@@ -552,6 +552,7 @@ void __ext4_std_error(struct super_block ++ ++ ext4_handle_error(sb); ++ } +++EXPORT_SYMBOL(__ext4_std_error); ++ ++ /* ++ * ext4_abort is a much stronger failure handler than ext4_error. The +diff --git a/ldiskfs/kernel_patches/patches/fc19/ext4-osd-iop-common.patch b/ldiskfs/kernel_patches/patches/fc19/ext4-osd-iop-common.patch +new file mode 100644 +index 0000000..64e82d7 +--- /dev/null ++++ b/ldiskfs/kernel_patches/patches/fc19/ext4-osd-iop-common.patch +@@ -0,0 +1,135 @@ ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/ext4.h ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/ext4.h +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/ext4.h ++@@ -2155,6 +2155,20 @@ extern int ext4_orphan_add(handle_t *, s ++ extern int ext4_orphan_del(handle_t *, struct inode *); ++ extern int ext4_htree_fill_tree(struct file *dir_file, __u32 start_hash, ++ __u32 start_minor_hash, __u32 *next_hash); +++extern struct inode *ext4_create_inode(handle_t *handle, +++ struct inode * dir, int mode); +++extern int ext4_add_entry(handle_t *handle, struct dentry *dentry, +++ struct inode *inode); +++extern int ext4_delete_entry(handle_t *handle, struct inode * dir, +++ struct ext4_dir_entry_2 * de_del, +++ struct buffer_head * bh); +++extern struct buffer_head * ext4_find_entry(struct inode *dir, +++ const struct qstr *d_name, +++ struct ext4_dir_entry_2 ** res_dir, +++ int *inlined); +++#define ll_ext4_find_entry(inode, dentry, res_dir) ext4_find_entry(inode, &(dentry)->d_name, res_dir, NULL) +++extern int ext4_add_dot_dotdot(handle_t *handle, struct inode *dir, +++ struct inode *inode); ++ extern int search_dir(struct buffer_head *bh, ++ char *search_buf, ++ int buf_size, ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/namei.c ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/namei.c +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/namei.c ++@@ -1218,7 +1218,7 @@ static int is_dx_internal_node(struct in ++ * The returned buffer_head has ->b_count elevated. The caller is expected ++ * to brelse() it when appropriate. ++ */ ++-static struct buffer_head * ext4_find_entry (struct inode *dir, +++struct buffer_head * ext4_find_entry(struct inode *dir, ++ const struct qstr *d_name, ++ struct ext4_dir_entry_2 **res_dir, ++ int *inlined) ++@@ -1362,6 +1362,7 @@ cleanup_and_exit: ++ brelse(bh_use[ra_ptr]); ++ return ret; ++ } +++EXPORT_SYMBOL(ext4_find_entry); ++ ++ static struct buffer_head * ext4_dx_find_entry(struct inode *dir, const struct qstr *d_name, ++ struct ext4_dir_entry_2 **res_dir, int *err) ++@@ -1910,8 +1911,8 @@ static int make_indexed_dir(handle_t *ha ++ * may not sleep between calling this and putting something into ++ * the entry, as someone else might have used it while you slept. ++ */ ++-static int ext4_add_entry(handle_t *handle, struct dentry *dentry, ++- struct inode *inode) +++int ext4_add_entry(handle_t *handle, struct dentry *dentry, +++ struct inode *inode) ++ { ++ struct inode *dir = dentry->d_parent->d_inode; ++ struct buffer_head *bh; ++@@ -1986,6 +1987,7 @@ static int ext4_add_entry(handle_t *hand ++ ext4_set_inode_state(inode, EXT4_STATE_NEWENTRY); ++ return retval; ++ } +++EXPORT_SYMBOL(ext4_add_entry); ++ ++ /* ++ * Returns 0 for success, or a negative error value ++@@ -2172,7 +2174,7 @@ int ext4_generic_delete_entry(handle_t * ++ return -ENOENT; ++ } ++ ++-static int ext4_delete_entry(handle_t *handle, +++int ext4_delete_entry(handle_t *handle, ++ struct inode *dir, ++ struct ext4_dir_entry_2 *de_del, ++ struct buffer_head *bh) ++@@ -2213,7 +2215,7 @@ out: ++ ext4_std_error(dir->i_sb, err); ++ return err; ++ } ++- +++EXPORT_SYMBOL(ext4_delete_entry); ++ /* ++ * DIR_NLINK feature is set if 1) nlinks > EXT4_LINK_MAX or 2) nlinks == 2, ++ * since this indicates that nlinks count was previously 1. ++@@ -2260,6 +2262,27 @@ static int ext4_add_nondir(handle_t *han ++ return err; ++ } ++ +++struct inode * ext4_create_inode(handle_t *handle, struct inode * dir, int mode) +++{ +++ struct inode *inode; +++ +++ inode = ext4_new_inode(handle, dir, mode, NULL, 0, NULL); +++ if (!IS_ERR(inode)) { +++ if (S_ISCHR(mode) || S_ISBLK(mode) || S_ISFIFO(mode)) { +++#ifdef CONFIG_LDISKFS_FS_XATTR +++ inode->i_op = &ext4_special_inode_operations; +++#endif +++ } else { +++ inode->i_op = &ext4_file_inode_operations; +++ inode->i_fop = &ext4_file_operations; +++ ext4_set_aops(inode); +++ } +++ unlock_new_inode(inode); +++ } +++ return inode; +++} +++EXPORT_SYMBOL(ext4_create_inode); +++ ++ /* ++ * By the time this is called, we already have created ++ * the directory cache entry for the new file, but it ++@@ -2448,6 +2471,23 @@ out: ++ return err; ++ } ++ +++/* Initialize @inode as a subdirectory of @dir, and add the +++ * "." and ".." entries into the first directory block. */ +++int ext4_add_dot_dotdot(handle_t *handle, struct inode * dir, +++ struct inode *inode) +++{ +++ if (IS_ERR(handle)) +++ return PTR_ERR(handle); +++ +++ if (IS_DIRSYNC(dir)) +++ ext4_handle_sync(handle); +++ +++ inode->i_op = &ext4_dir_inode_operations; +++ inode->i_fop = &ext4_dir_operations; +++ return ext4_init_new_dir(handle, dir, inode); +++} +++EXPORT_SYMBOL(ext4_add_dot_dotdot); +++ ++ static int ext4_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) ++ { ++ handle_t *handle; +diff --git a/ldiskfs/kernel_patches/patches/fc19/ext4-pdir-fix.patch b/ldiskfs/kernel_patches/patches/fc19/ext4-pdir-fix.patch +new file mode 100644 +index 0000000..db24d01 +--- /dev/null ++++ b/ldiskfs/kernel_patches/patches/fc19/ext4-pdir-fix.patch +@@ -0,0 +1,61 @@ ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/ext4.h ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/ext4.h +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/ext4.h ++@@ -793,6 +793,9 @@ struct ext4_inode_info { ++ __u32 i_dtime; ++ ext4_fsblk_t i_file_acl; ++ +++ /* following fields for parallel directory operations -bzzz */ +++ struct semaphore i_append_sem; +++ ++ /* ++ * i_block_group is the number of the block group which contains ++ * this file's inode. Constant across the lifetime of the inode, ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/namei.c ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/namei.c +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/namei.c ++@@ -53,6 +53,7 @@ static struct buffer_head *ext4_append(h ++ ext4_lblk_t *block) ++ { ++ struct buffer_head *bh; +++ struct ext4_inode_info *ei = EXT4_I(inode); ++ int err = 0; ++ ++ if (unlikely(EXT4_SB(inode->i_sb)->s_max_dir_size_kb && ++@@ -60,14 +61,21 @@ static struct buffer_head *ext4_append(h ++ EXT4_SB(inode->i_sb)->s_max_dir_size_kb))) ++ return ERR_PTR(-ENOSPC); ++ +++ /* with parallel dir operations all appends +++ * have to be serialized -bzzz */ +++ down(&ei->i_append_sem); +++ ++ *block = inode->i_size >> inode->i_sb->s_blocksize_bits; ++ ++ bh = ext4_bread(handle, inode, *block, 1, &err); ++- if (!bh) +++ if (!bh) { +++ up(&ei->i_append_sem); ++ return ERR_PTR(err); +++ } ++ inode->i_size += inode->i_sb->s_blocksize; ++ EXT4_I(inode)->i_disksize = inode->i_size; ++ err = ext4_journal_get_write_access(handle, bh); +++ up(&ei->i_append_sem); ++ if (err) { ++ brelse(bh); ++ ext4_std_error(inode->i_sb, err); ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/super.c ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/super.c +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/super.c ++@@ -859,6 +859,7 @@ static struct inode *ext4_alloc_inode(st ++ return NULL; ++ ++ ei->vfs_inode.i_version = 1; +++ sema_init(&ei->i_append_sem, 1); ++ INIT_LIST_HEAD(&ei->i_prealloc_list); ++ spin_lock_init(&ei->i_prealloc_lock); ++ ext4_es_init_tree(&ei->i_es_tree); +diff --git a/ldiskfs/kernel_patches/patches/fc19/ext4-prealloc.patch b/ldiskfs/kernel_patches/patches/fc19/ext4-prealloc.patch +new file mode 100644 +index 0000000..cb54844 +--- /dev/null ++++ b/ldiskfs/kernel_patches/patches/fc19/ext4-prealloc.patch +@@ -0,0 +1,387 @@ ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/ext4.h ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/ext4.h +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/ext4.h ++@@ -1237,11 +1237,14 @@ struct ext4_sb_info { ++ ++ /* tunables */ ++ unsigned long s_stripe; ++- unsigned int s_mb_stream_request; +++ unsigned long s_mb_small_req; +++ unsigned long s_mb_large_req; ++ unsigned int s_mb_max_to_scan; ++ unsigned int s_mb_min_to_scan; ++ unsigned int s_mb_stats; ++ unsigned int s_mb_order2_reqs; +++ unsigned long *s_mb_prealloc_table; +++ unsigned long s_mb_prealloc_table_size; ++ unsigned int s_mb_group_prealloc; ++ unsigned int s_max_dir_size_kb; ++ /* where last allocation was done - for stream allocation */ ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/mballoc.c ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/mballoc.c +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/mballoc.c ++@@ -1828,6 +1828,25 @@ int ext4_mb_find_by_goal(struct ext4_all ++ return 0; ++ } ++ +++static void ext4_mb_prealloc_table_add(struct ext4_sb_info *sbi, int value) +++{ +++ int i; +++ +++ if (value > (sbi->s_blocks_per_group - 1 - 1 - sbi->s_itb_per_group)) +++ return; +++ +++ for (i = 0; i < sbi->s_mb_prealloc_table_size; i++) { +++ if (sbi->s_mb_prealloc_table[i] == 0) { +++ sbi->s_mb_prealloc_table[i] = value; +++ return; +++ } +++ +++ /* they should add values in order */ +++ if (value <= sbi->s_mb_prealloc_table[i]) +++ return; +++ } +++} +++ ++ /* ++ * The routine scans buddy structures (not bitmap!) from given order ++ * to max order and tries to find big enough chunk to satisfy the req ++@@ -2263,6 +2282,86 @@ static const struct seq_operations ext4_ ++ .show = ext4_mb_seq_groups_show, ++ }; ++ +++#define EXT4_MB_PREALLOC_TABLE "prealloc_table" +++ +++static ssize_t ext4_mb_prealloc_table_proc_write(struct file *file, +++ const char __user *buf, +++ size_t cnt, loff_t *pos) +++{ +++ struct ext4_sb_info *sbi = EXT4_SB(PDE_DATA(file_inode(file))); +++ unsigned long value; +++ unsigned long prev = 0; +++ char str[128]; +++ char *cur; +++ char *end; +++ unsigned long *new_table; +++ int num = 0; +++ int i = 0; +++ +++ if (cnt >= sizeof(str)) +++ return -EINVAL; +++ if (copy_from_user(str, buf, cnt)) +++ return -EFAULT; +++ +++ num = 0; +++ cur = str; +++ end = str + cnt; +++ while (cur < end) { +++ while ((cur < end) && (*cur == ' ')) cur++; +++ value = simple_strtol(cur, &cur, 0); +++ if (value == 0) +++ break; +++ if (value <= prev) +++ return -EINVAL; +++ prev = value; +++ num++; +++ } +++ +++ new_table = kmalloc(num * sizeof(*new_table), GFP_KERNEL); +++ if (new_table == NULL) +++ return -ENOMEM; +++ kfree(sbi->s_mb_prealloc_table); +++ memset(new_table, 0, num * sizeof(*new_table)); +++ sbi->s_mb_prealloc_table = new_table; +++ sbi->s_mb_prealloc_table_size = num; +++ cur = str; +++ end = str + cnt; +++ while (cur < end && i < num) { +++ while ((cur < end) && (*cur == ' ')) cur++; +++ value = simple_strtol(cur, &cur, 0); +++ ext4_mb_prealloc_table_add(sbi, value); +++ i++; +++ } +++ +++ return cnt; +++} +++ +++static int mb_prealloc_table_seq_show(struct seq_file *m, void *v) +++{ +++ struct ext4_sb_info *sbi = EXT4_SB(m->private); +++ int i; +++ +++ for (i = 0; i < sbi->s_mb_prealloc_table_size; i++) +++ seq_printf(m, "%ld ", sbi->s_mb_prealloc_table[i]); +++ seq_printf(m, "\n"); +++ +++ return 0; +++} +++ +++static int mb_prealloc_table_seq_open(struct inode *inode, struct file *file) +++{ +++ return single_open(file, mb_prealloc_table_seq_show, PDE_DATA(inode)); +++} +++ +++struct file_operations ext4_mb_prealloc_seq_fops = { +++ .owner = THIS_MODULE, +++ .open = mb_prealloc_table_seq_open, +++ .read = seq_read, +++ .llseek = seq_lseek, +++ .release = single_release, +++ .write = ext4_mb_prealloc_table_proc_write, +++}; +++ ++ static int ext4_mb_seq_groups_open(struct inode *inode, struct file *file) ++ { ++ struct super_block *sb = PDE_DATA(inode); ++@@ -2557,7 +2656,6 @@ int ext4_mb_init(struct super_block *sb) ++ sbi->s_mb_max_to_scan = MB_DEFAULT_MAX_TO_SCAN; ++ sbi->s_mb_min_to_scan = MB_DEFAULT_MIN_TO_SCAN; ++ sbi->s_mb_stats = MB_DEFAULT_STATS; ++- sbi->s_mb_stream_request = MB_DEFAULT_STREAM_THRESHOLD; ++ sbi->s_mb_order2_reqs = MB_DEFAULT_ORDER2_REQS; ++ /* ++ * The default group preallocation is 512, which for 4k block ++@@ -2581,9 +2679,48 @@ int ext4_mb_init(struct super_block *sb) ++ * RAID stripe size so that preallocations don't fragment ++ * the stripes. ++ */ ++- if (sbi->s_stripe > 1) { ++- sbi->s_mb_group_prealloc = roundup( ++- sbi->s_mb_group_prealloc, sbi->s_stripe); +++ +++ if (sbi->s_stripe == 0) { +++ sbi->s_mb_prealloc_table_size = 10; +++ i = sbi->s_mb_prealloc_table_size * sizeof(unsigned long); +++ sbi->s_mb_prealloc_table = kmalloc(i, GFP_NOFS); +++ if (sbi->s_mb_prealloc_table == NULL) { +++ ret = -ENOMEM; +++ goto out; +++ } +++ memset(sbi->s_mb_prealloc_table, 0, i); +++ +++ ext4_mb_prealloc_table_add(sbi, 4); +++ ext4_mb_prealloc_table_add(sbi, 8); +++ ext4_mb_prealloc_table_add(sbi, 16); +++ ext4_mb_prealloc_table_add(sbi, 32); +++ ext4_mb_prealloc_table_add(sbi, 64); +++ ext4_mb_prealloc_table_add(sbi, 128); +++ ext4_mb_prealloc_table_add(sbi, 256); +++ ext4_mb_prealloc_table_add(sbi, 512); +++ ext4_mb_prealloc_table_add(sbi, 1024); +++ ext4_mb_prealloc_table_add(sbi, 2048); +++ +++ sbi->s_mb_small_req = 256; +++ sbi->s_mb_large_req = 1024; +++ sbi->s_mb_group_prealloc = 512; +++ } else { +++ sbi->s_mb_prealloc_table_size = 3; +++ i = sbi->s_mb_prealloc_table_size * sizeof(unsigned long); +++ sbi->s_mb_prealloc_table = kmalloc(i, GFP_NOFS); +++ if (sbi->s_mb_prealloc_table == NULL) { +++ ret = -ENOMEM; +++ goto out; +++ } +++ memset(sbi->s_mb_prealloc_table, 0, i); +++ +++ ext4_mb_prealloc_table_add(sbi, sbi->s_stripe); +++ ext4_mb_prealloc_table_add(sbi, sbi->s_stripe * 2); +++ ext4_mb_prealloc_table_add(sbi, sbi->s_stripe * 4); +++ +++ sbi->s_mb_small_req = sbi->s_stripe; +++ sbi->s_mb_large_req = sbi->s_stripe * 8; +++ sbi->s_mb_group_prealloc = sbi->s_stripe * 4; ++ } ++ ++ sbi->s_locality_groups = alloc_percpu(struct ext4_locality_group); ++@@ -2605,9 +2742,13 @@ int ext4_mb_init(struct super_block *sb) ++ if (ret != 0) ++ goto out_free_locality_groups; ++ ++- if (sbi->s_proc) +++ if (sbi->s_proc) { ++ proc_create_data("mb_groups", S_IRUGO, sbi->s_proc, ++ &ext4_mb_seq_groups_fops, sb); +++ proc_create_data(EXT4_MB_PREALLOC_TABLE, S_IFREG | S_IRUGO | +++ S_IWUSR, sbi->s_proc, +++ &ext4_mb_prealloc_seq_fops, sb); +++ } ++ ++ return 0; ++ ++@@ -2615,6 +2756,7 @@ out_free_locality_groups: ++ free_percpu(sbi->s_locality_groups); ++ sbi->s_locality_groups = NULL; ++ out_free_groupinfo_slab: +++ kfree(sbi->s_mb_prealloc_table); ++ ext4_groupinfo_destroy_slabs(); ++ out: ++ kfree(sbi->s_mb_offsets); ++@@ -2651,8 +2793,10 @@ int ext4_mb_release(struct super_block * ++ struct ext4_sb_info *sbi = EXT4_SB(sb); ++ struct kmem_cache *cachep = get_groupinfo_cache(sb->s_blocksize_bits); ++ ++- if (sbi->s_proc) +++ if (sbi->s_proc) { ++ remove_proc_entry("mb_groups", sbi->s_proc); +++ remove_proc_entry(EXT4_MB_PREALLOC_TABLE, sbi->s_proc); +++ } ++ ++ if (sbi->s_group_info) { ++ for (i = 0; i < ngroups; i++) { ++@@ -2963,9 +3107,9 @@ ext4_mb_normalize_request(struct ext4_al ++ struct ext4_allocation_request *ar) ++ { ++ struct ext4_sb_info *sbi = EXT4_SB(ac->ac_sb); ++- int bsbits, max; +++ int bsbits, i, wind; ++ ext4_lblk_t end; ++- loff_t size, start_off; +++ loff_t size; ++ loff_t orig_size __maybe_unused; ++ ext4_lblk_t start; ++ struct ext4_inode_info *ei = EXT4_I(ac->ac_inode); ++@@ -2998,50 +3142,34 @@ ext4_mb_normalize_request(struct ext4_al ++ size = size << bsbits; ++ if (size < i_size_read(ac->ac_inode)) ++ size = i_size_read(ac->ac_inode); ++- orig_size = size; +++ size = (size + ac->ac_sb->s_blocksize - 1) >> bsbits; ++ ++- /* max size of free chunks */ ++- max = 2 << bsbits; +++ start = wind = 0; ++ ++-#define NRL_CHECK_SIZE(req, size, max, chunk_size) \ ++- (req <= (size) || max <= (chunk_size)) +++ /* let's choose preallocation window depending on file size */ +++ for (i = 0; i < sbi->s_mb_prealloc_table_size; i++) { +++ if (size <= sbi->s_mb_prealloc_table[i]) { +++ wind = sbi->s_mb_prealloc_table[i]; +++ break; +++ } +++ } +++ size = wind; ++ ++- /* first, try to predict filesize */ ++- /* XXX: should this table be tunable? */ ++- start_off = 0; ++- if (size <= 16 * 1024) { ++- size = 16 * 1024; ++- } else if (size <= 32 * 1024) { ++- size = 32 * 1024; ++- } else if (size <= 64 * 1024) { ++- size = 64 * 1024; ++- } else if (size <= 128 * 1024) { ++- size = 128 * 1024; ++- } else if (size <= 256 * 1024) { ++- size = 256 * 1024; ++- } else if (size <= 512 * 1024) { ++- size = 512 * 1024; ++- } else if (size <= 1024 * 1024) { ++- size = 1024 * 1024; ++- } else if (NRL_CHECK_SIZE(size, 4 * 1024 * 1024, max, 2 * 1024)) { ++- start_off = ((loff_t)ac->ac_o_ex.fe_logical >> ++- (21 - bsbits)) << 21; ++- size = 2 * 1024 * 1024; ++- } else if (NRL_CHECK_SIZE(size, 8 * 1024 * 1024, max, 4 * 1024)) { ++- start_off = ((loff_t)ac->ac_o_ex.fe_logical >> ++- (22 - bsbits)) << 22; ++- size = 4 * 1024 * 1024; ++- } else if (NRL_CHECK_SIZE(ac->ac_o_ex.fe_len, ++- (8<<20)>>bsbits, max, 8 * 1024)) { ++- start_off = ((loff_t)ac->ac_o_ex.fe_logical >> ++- (23 - bsbits)) << 23; ++- size = 8 * 1024 * 1024; ++- } else { ++- start_off = (loff_t)ac->ac_o_ex.fe_logical << bsbits; ++- size = ac->ac_o_ex.fe_len << bsbits; +++ if (wind == 0) { +++ __u64 tstart, tend; +++ /* file is quite large, we now preallocate with +++ * the biggest configured window with regart to +++ * logical offset */ +++ wind = sbi->s_mb_prealloc_table[i - 1]; +++ tstart = ac->ac_o_ex.fe_logical; +++ do_div(tstart, wind); +++ start = tstart * wind; +++ tend = ac->ac_o_ex.fe_logical + ac->ac_o_ex.fe_len - 1; +++ do_div(tend, wind); +++ tend = tend * wind + wind; +++ size = tend - start; ++ } ++- size = size >> bsbits; ++- start = start_off >> bsbits; +++ orig_size = size; ++ ++ /* don't cover already allocated blocks in selected range */ ++ if (ar->pleft && start <= ar->lleft) { ++@@ -3117,7 +3245,6 @@ ext4_mb_normalize_request(struct ext4_al ++ } ++ BUG_ON(start + size <= ac->ac_o_ex.fe_logical && ++ start > ac->ac_o_ex.fe_logical); ++- BUG_ON(size <= 0 || size > EXT4_CLUSTERS_PER_GROUP(ac->ac_sb)); ++ ++ /* now prepare goal request */ ++ ++@@ -4056,11 +4183,19 @@ static void ext4_mb_group_or_file(struct ++ ++ /* don't use group allocation for large files */ ++ size = max(size, isize); ++- if (size > sbi->s_mb_stream_request) { +++ if ((ac->ac_o_ex.fe_len >= sbi->s_mb_small_req) || +++ (size >= sbi->s_mb_large_req)) { ++ ac->ac_flags |= EXT4_MB_STREAM_ALLOC; ++ return; ++ } ++ +++ /* +++ * request is so large that we don't care about +++ * streaming - it overweights any possible seek +++ */ +++ if (ac->ac_o_ex.fe_len >= sbi->s_mb_large_req) +++ return; +++ ++ BUG_ON(ac->ac_lg != NULL); ++ /* ++ * locality group prealloc space are per cpu. The reason for having ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/super.c ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/super.c +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/super.c ++@@ -2564,7 +2564,8 @@ EXT4_RW_ATTR_SBI_UI(mb_stats, s_mb_stats ++ EXT4_RW_ATTR_SBI_UI(mb_max_to_scan, s_mb_max_to_scan); ++ EXT4_RW_ATTR_SBI_UI(mb_min_to_scan, s_mb_min_to_scan); ++ EXT4_RW_ATTR_SBI_UI(mb_order2_req, s_mb_order2_reqs); ++-EXT4_RW_ATTR_SBI_UI(mb_stream_req, s_mb_stream_request); +++EXT4_RW_ATTR_SBI_UI(mb_small_req, s_mb_small_req); +++EXT4_RW_ATTR_SBI_UI(mb_large_req, s_mb_large_req); ++ EXT4_RW_ATTR_SBI_UI(mb_group_prealloc, s_mb_group_prealloc); ++ EXT4_DEPRECATED_ATTR(max_writeback_mb_bump, 128); ++ EXT4_RW_ATTR_SBI_UI(extent_max_zeroout_kb, s_extent_max_zeroout_kb); ++@@ -2581,7 +2582,8 @@ static struct attribute *ext4_attrs[] = ++ ATTR_LIST(mb_max_to_scan), ++ ATTR_LIST(mb_min_to_scan), ++ ATTR_LIST(mb_order2_req), ++- ATTR_LIST(mb_stream_req), +++ ATTR_LIST(mb_small_req), +++ ATTR_LIST(mb_large_req), ++ ATTR_LIST(mb_group_prealloc), ++ ATTR_LIST(max_writeback_mb_bump), ++ ATTR_LIST(extent_max_zeroout_kb), ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/inode.c ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/inode.c +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/inode.c ++@@ -2434,6 +2434,10 @@ static int ext4_writepages(struct addres ++ ext4_journal_stop(handle); ++ } ++ +++ if (wbc->nr_to_write < sbi->s_mb_small_req) { +++ wbc->nr_to_write = sbi->s_mb_small_req; +++ } +++ ++ if (wbc->range_start == 0 && wbc->range_end == LLONG_MAX) ++ range_whole = 1; ++ +diff --git a/ldiskfs/kernel_patches/patches/fc19/ext4_data_in_dirent.patch b/ldiskfs/kernel_patches/patches/fc19/ext4_data_in_dirent.patch +new file mode 100644 +index 0000000..7e8e94d +--- /dev/null ++++ b/ldiskfs/kernel_patches/patches/fc19/ext4_data_in_dirent.patch +@@ -0,0 +1,649 @@ ++this patch implements feature which allows ext4 fs users (e.g. Lustre) ++to store data in ext4 dirent. ++data is stored in ext4 dirent after file-name, this space is accounted ++in de->rec_len. flag EXT4_DIRENT_LUFID added to d_type if extra data ++is present. ++ ++make use of dentry->d_fsdata to pass fid to ext4. so no ++changes in ext4_add_entry() interface required. ++ ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/dir.c ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/dir.c +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/dir.c ++@@ -70,11 +70,11 @@ int __ext4_check_dir_entry(const char *f ++ const int rlen = ext4_rec_len_from_disk(de->rec_len, ++ dir->i_sb->s_blocksize); ++ ++- if (unlikely(rlen < EXT4_DIR_REC_LEN(1))) +++ if (unlikely(rlen < __EXT4_DIR_REC_LEN(1))) ++ error_msg = "rec_len is smaller than minimal"; ++ else if (unlikely(rlen % 4 != 0)) ++ error_msg = "rec_len % 4 != 0"; ++- else if (unlikely(rlen < EXT4_DIR_REC_LEN(de->name_len))) +++ else if (unlikely(rlen < EXT4_DIR_REC_LEN(de))) ++ error_msg = "rec_len is too small for name_len"; ++ else if (unlikely(((char *) de - buf) + rlen > size)) ++ error_msg = "directory entry across range"; ++@@ -202,7 +202,7 @@ static int ext4_readdir(struct file *fil ++ * failure will be detected in the ++ * dirent test below. */ ++ if (ext4_rec_len_from_disk(de->rec_len, ++- sb->s_blocksize) < EXT4_DIR_REC_LEN(1)) +++ sb->s_blocksize) < __EXT4_DIR_REC_LEN(1)) ++ break; ++ i += ext4_rec_len_from_disk(de->rec_len, ++ sb->s_blocksize); ++@@ -421,12 +421,17 @@ int ext4_htree_store_dirent(struct file ++ struct fname *fname, *new_fn; ++ struct dir_private_info *info; ++ int len; +++ int extra_data = 1; ++ ++ info = dir_file->private_data; ++ p = &info->root.rb_node; ++ ++ /* Create and allocate the fname structure */ ++- len = sizeof(struct fname) + dirent->name_len + 1; +++ if (dirent->file_type & EXT4_DIRENT_LUFID) +++ extra_data = ext4_get_dirent_data_len(dirent); +++ +++ len = sizeof(struct fname) + dirent->name_len + extra_data; +++ ++ new_fn = kzalloc(len, GFP_KERNEL); ++ if (!new_fn) ++ return -ENOMEM; ++@@ -435,7 +440,7 @@ int ext4_htree_store_dirent(struct file ++ new_fn->inode = le32_to_cpu(dirent->inode); ++ new_fn->name_len = dirent->name_len; ++ new_fn->file_type = dirent->file_type; ++- memcpy(new_fn->name, dirent->name, dirent->name_len); +++ memcpy(new_fn->name, dirent->name, dirent->name_len + extra_data); ++ new_fn->name[dirent->name_len] = 0; ++ ++ while (*p) { ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/ext4.h ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/ext4.h +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/ext4.h ++@@ -1534,6 +1534,7 @@ static inline void ext4_clear_state_flag ++ EXT4_FEATURE_INCOMPAT_64BIT| \ ++ EXT4_FEATURE_INCOMPAT_FLEX_BG| \ ++ EXT4_FEATURE_INCOMPAT_MMP | \ +++ EXT4_FEATURE_INCOMPAT_DIRDATA| \ ++ EXT4_FEATURE_INCOMPAT_INLINE_DATA) ++ #define EXT4_FEATURE_RO_COMPAT_SUPP (EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER| \ ++ EXT4_FEATURE_RO_COMPAT_LARGE_FILE| \ ++@@ -1640,6 +1641,43 @@ struct ext4_dir_entry_tail { ++ #define EXT4_FT_SYMLINK 7 ++ ++ #define EXT4_FT_MAX 8 +++#define EXT4_FT_MASK 0xf +++ +++#if EXT4_FT_MAX > EXT4_FT_MASK +++#error "conflicting EXT4_FT_MAX and EXT4_FT_MASK" +++#endif +++ +++/* +++ * d_type has 4 unused bits, so it can hold four types data. these different +++ * type of data (e.g. lustre data, high 32 bits of 64-bit inode number) can be +++ * stored, in flag order, after file-name in ext4 dirent. +++*/ +++/* +++ * this flag is added to d_type if ext4 dirent has extra data after +++ * filename. this data length is variable and length is stored in first byte +++ * of data. data start after filename NUL byte. +++ * This is used by Lustre FS. +++ */ +++#define EXT4_DIRENT_LUFID 0x10 +++ +++#define EXT4_LUFID_MAGIC 0xAD200907UL +++struct ext4_dentry_param { +++ __u32 edp_magic; /* EXT4_LUFID_MAGIC */ +++ char edp_len; /* size of edp_data in bytes */ +++ char edp_data[0]; /* packed array of data */ +++} __attribute__((packed)); +++ +++static inline unsigned char *ext4_dentry_get_data(struct super_block *sb, +++ struct ext4_dentry_param* p) +++ +++{ +++ if (!EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_DIRDATA)) +++ return NULL; +++ if (p && p->edp_magic == EXT4_LUFID_MAGIC) +++ return &p->edp_len; +++ else +++ return NULL; +++} ++ ++ #define EXT4_FT_DIR_CSUM 0xDE ++ ++@@ -1650,8 +1688,11 @@ struct ext4_dir_entry_tail { ++ */ ++ #define EXT4_DIR_PAD 4 ++ #define EXT4_DIR_ROUND (EXT4_DIR_PAD - 1) ++-#define EXT4_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT4_DIR_ROUND) & \ +++#define __EXT4_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT4_DIR_ROUND) & \ ++ ~EXT4_DIR_ROUND) +++#define EXT4_DIR_REC_LEN(de) (__EXT4_DIR_REC_LEN((de)->name_len +\ +++ ext4_get_dirent_data_len(de))) +++ ++ #define EXT4_MAX_REC_LEN ((1<<16)-1) ++ ++ /* ++@@ -1998,7 +2039,7 @@ extern int ext4_find_dest_de(struct inod ++ void ext4_insert_dentry(struct inode *inode, ++ struct ext4_dir_entry_2 *de, ++ int buf_size, ++- const char *name, int namelen); +++ const char *name, int namelen, void *data); ++ static inline void ext4_update_dx_flag(struct inode *inode) ++ { ++ if (!EXT4_HAS_COMPAT_FEATURE(inode->i_sb, ++@@ -2011,11 +2052,18 @@ static unsigned char ext4_filetype_table ++ ++ static inline unsigned char get_dtype(struct super_block *sb, int filetype) ++ { +++ int fl_index = filetype & EXT4_FT_MASK; +++ ++ if (!EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_FILETYPE) || ++- (filetype >= EXT4_FT_MAX)) +++ (fl_index >= EXT4_FT_MAX)) ++ return DT_UNKNOWN; ++ ++- return ext4_filetype_table[filetype]; +++ if (!EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_DIRDATA)) +++ return (ext4_filetype_table[fl_index]); +++ +++ return (ext4_filetype_table[fl_index]) | +++ (filetype & EXT4_DIRENT_LUFID); +++ ++ } ++ ++ /* fsync.c */ ++@@ -2171,7 +2219,7 @@ extern struct buffer_head *ext4_append(h ++ struct inode *inode, ++ ext4_lblk_t *block); ++ extern int ext4_add_dot_dotdot(handle_t *handle, struct inode *dir, ++- struct inode *inode); +++ struct inode *inode, const void *, const void *); ++ extern int search_dir(struct buffer_head *bh, ++ char *search_buf, ++ int buf_size, ++@@ -2827,6 +2875,28 @@ extern struct mutex ext4__aio_mutex[EXT4 ++ extern int ext4_resize_begin(struct super_block *sb); ++ extern void ext4_resize_end(struct super_block *sb); ++ +++/* +++ * Compute the total directory entry data length. +++ * This includes the filename and an implicit NUL terminator (always present), +++ * and optional extensions. Each extension has a bit set in the high 4 bits of +++ * de->file_type, and the extension length is the first byte in each entry. +++ */ +++static inline int ext4_get_dirent_data_len(struct ext4_dir_entry_2 *de) +++{ +++ char *len = de->name + de->name_len + 1 /* NUL terminator */; +++ int dlen = 0; +++ __u8 extra_data_flags = (de->file_type & ~EXT4_FT_MASK) >> 4; +++ +++ while (extra_data_flags) { +++ if (extra_data_flags & 1) { +++ dlen += *len + (dlen == 0); +++ len += *len; +++ } +++ extra_data_flags >>= 1; +++ } +++ return dlen; +++} +++ ++ #endif /* __KERNEL__ */ ++ ++ #endif /* _EXT4_H */ ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/namei.c ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/namei.c +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/namei.c ++@@ -247,7 +247,8 @@ static unsigned dx_get_count(struct dx_e ++ static unsigned dx_get_limit(struct dx_entry *entries); ++ static void dx_set_count(struct dx_entry *entries, unsigned value); ++ static void dx_set_limit(struct dx_entry *entries, unsigned value); ++-static unsigned dx_root_limit(struct inode *dir, unsigned infosize); +++static inline unsigned dx_root_limit(struct inode *dir, +++ struct ext4_dir_entry_2 *dot_de, unsigned infosize); ++ static unsigned dx_node_limit(struct inode *dir); ++ static struct dx_frame *dx_probe(const struct qstr *d_name, ++ struct inode *dir, ++@@ -512,11 +513,12 @@ ext4_next_entry(struct ext4_dir_entry_2 ++ */ ++ struct dx_root_info * dx_get_dx_info(struct ext4_dir_entry_2 *de) ++ { ++- /* get dotdot first */ ++- de = (struct ext4_dir_entry_2 *)((char *)de + EXT4_DIR_REC_LEN(1)); +++ BUG_ON(de->name_len != 1); +++ /* get dotdot first */ +++ de = (struct ext4_dir_entry_2 *)((char *)de + EXT4_DIR_REC_LEN(de)); ++ ++- /* dx root info is after dotdot entry */ ++- de = (struct ext4_dir_entry_2 *)((char *)de + EXT4_DIR_REC_LEN(2)); +++ /* dx root info is after dotdot entry */ +++ de = (struct ext4_dir_entry_2 *)((char *)de + EXT4_DIR_REC_LEN(de)); ++ ++ return (struct dx_root_info *) de; ++ } ++@@ -561,10 +563,16 @@ static inline void dx_set_limit(struct d ++ ((struct dx_countlimit *) entries)->limit = cpu_to_le16(value); ++ } ++ ++-static inline unsigned dx_root_limit(struct inode *dir, unsigned infosize) +++static inline unsigned dx_root_limit(struct inode *dir, +++ struct ext4_dir_entry_2 *dot_de, unsigned infosize) ++ { ++- unsigned entry_space = dir->i_sb->s_blocksize - EXT4_DIR_REC_LEN(1) - ++- EXT4_DIR_REC_LEN(2) - infosize; +++ struct ext4_dir_entry_2 *dotdot_de; +++ unsigned entry_space; +++ +++ BUG_ON(dot_de->name_len != 1); +++ dotdot_de = ext4_next_entry(dot_de, dir->i_sb->s_blocksize); +++ entry_space = dir->i_sb->s_blocksize - EXT4_DIR_REC_LEN(dot_de) - +++ EXT4_DIR_REC_LEN(dotdot_de) - infosize; ++ ++ if (EXT4_HAS_RO_COMPAT_FEATURE(dir->i_sb, ++ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) ++@@ -574,7 +582,7 @@ static inline unsigned dx_root_limit(str ++ ++ static inline unsigned dx_node_limit(struct inode *dir) ++ { ++- unsigned entry_space = dir->i_sb->s_blocksize - EXT4_DIR_REC_LEN(0); +++ unsigned entry_space = dir->i_sb->s_blocksize - __EXT4_DIR_REC_LEN(0); ++ ++ if (EXT4_HAS_RO_COMPAT_FEATURE(dir->i_sb, ++ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) ++@@ -625,7 +633,7 @@ static struct stats dx_show_leaf(struct ++ printk(":%x.%u ", h.hash, ++ (unsigned) ((char *) de - base)); ++ } ++- space += EXT4_DIR_REC_LEN(de->name_len); +++ space += EXT4_DIR_REC_LEN(de); ++ names++; ++ } ++ de = ext4_next_entry(de, size); ++@@ -731,6 +739,7 @@ dx_probe(const struct qstr *d_name, stru ++ entries = (struct dx_entry *) (((char *)info) + info->info_length); ++ ++ if (dx_get_limit(entries) != dx_root_limit(dir, +++ (struct ext4_dir_entry_2*)bh->b_data, ++ info->info_length)) { ++ ext4_warning(dir->i_sb, "dx entry: limit != root limit"); ++ brelse(bh); ++@@ -924,7 +933,7 @@ static int htree_dirblock_to_tree(struct ++ de = (struct ext4_dir_entry_2 *) bh->b_data; ++ top = (struct ext4_dir_entry_2 *) ((char *) de + ++ dir->i_sb->s_blocksize - ++- EXT4_DIR_REC_LEN(0)); +++ __EXT4_DIR_REC_LEN(0)); ++ for (; de < top; de = ext4_next_entry(de, dir->i_sb->s_blocksize)) { ++ if (ext4_check_dir_entry(dir, NULL, de, bh, ++ bh->b_data, bh->b_size, ++@@ -1516,7 +1525,7 @@ dx_move_dirents(char *from, char *to, st ++ while (count--) { ++ struct ext4_dir_entry_2 *de = (struct ext4_dir_entry_2 *) ++ (from + (map->offs<<2)); ++- rec_len = EXT4_DIR_REC_LEN(de->name_len); +++ rec_len = EXT4_DIR_REC_LEN(de); ++ memcpy (to, de, rec_len); ++ ((struct ext4_dir_entry_2 *) to)->rec_len = ++ ext4_rec_len_to_disk(rec_len, blocksize); ++@@ -1540,7 +1549,7 @@ static struct ext4_dir_entry_2* dx_pack_ ++ while ((char*)de < base + blocksize) { ++ next = ext4_next_entry(de, blocksize); ++ if (de->inode && de->name_len) { ++- rec_len = EXT4_DIR_REC_LEN(de->name_len); +++ rec_len = EXT4_DIR_REC_LEN(de); ++ if (de > to) ++ memmove(to, de, rec_len); ++ to->rec_len = ext4_rec_len_to_disk(rec_len, blocksize); ++@@ -1675,11 +1684,12 @@ int ext4_find_dest_de(struct inode *dir, ++ struct ext4_dir_entry_2 **dest_de) ++ { ++ struct ext4_dir_entry_2 *de; ++- unsigned short reclen = EXT4_DIR_REC_LEN(namelen); +++ unsigned short reclen = __EXT4_DIR_REC_LEN((namelen & 0xffff) + (namelen >> 16)); ++ int nlen, rlen; ++ unsigned int offset = 0; ++ char *top; ++ +++ namelen &= 0xffff; ++ de = (struct ext4_dir_entry_2 *)buf; ++ top = buf + buf_size - reclen; ++ while ((char *) de <= top) { ++@@ -1688,7 +1698,7 @@ int ext4_find_dest_de(struct inode *dir, ++ return -EIO; ++ if (ext4_match(namelen, name, de)) ++ return -EEXIST; ++- nlen = EXT4_DIR_REC_LEN(de->name_len); +++ nlen = EXT4_DIR_REC_LEN(de); ++ rlen = ext4_rec_len_from_disk(de->rec_len, buf_size); ++ if ((de->inode ? rlen - nlen : rlen) >= reclen) ++ break; ++@@ -1705,12 +1715,12 @@ int ext4_find_dest_de(struct inode *dir, ++ void ext4_insert_dentry(struct inode *inode, ++ struct ext4_dir_entry_2 *de, ++ int buf_size, ++- const char *name, int namelen) +++ const char *name, int namelen, void *data) ++ { ++ ++ int nlen, rlen; ++ ++- nlen = EXT4_DIR_REC_LEN(de->name_len); +++ nlen = EXT4_DIR_REC_LEN(de); ++ rlen = ext4_rec_len_from_disk(de->rec_len, buf_size); ++ if (de->inode) { ++ struct ext4_dir_entry_2 *de1 = ++@@ -1724,6 +1734,11 @@ void ext4_insert_dentry(struct inode *in ++ ext4_set_de_type(inode->i_sb, de, inode->i_mode); ++ de->name_len = namelen; ++ memcpy(de->name, name, namelen); +++ if (data) { +++ de->name[namelen] = 0; +++ memcpy(&de->name[namelen + 1], data, *(char *)data); +++ de->file_type |= EXT4_DIRENT_LUFID; +++ } ++ } ++ /* ++ * Add a new entry into a directory (leaf) block. If de is non-NULL, ++@@ -1743,15 +1758,21 @@ static int add_dirent_to_buf(handle_t *h ++ unsigned int blocksize = dir->i_sb->s_blocksize; ++ int csum_size = 0; ++ int err; +++ unsigned char *data; ++ +++ data = ext4_dentry_get_data(inode->i_sb, (struct ext4_dentry_param *) +++ dentry->d_fsdata); ++ if (EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb, ++ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) ++ csum_size = sizeof(struct ext4_dir_entry_tail); ++ ++ if (!de) { +++ int dlen = 0; +++ if (data) +++ dlen = (*data) + 1; ++ err = ext4_find_dest_de(dir, inode, ++ bh, bh->b_data, blocksize - csum_size, ++- name, namelen, &de); +++ name, namelen + (dlen << 16), &de); ++ if (err) ++ return err; ++ } ++@@ -1763,7 +1784,7 @@ static int add_dirent_to_buf(handle_t *h ++ } ++ ++ /* By now the buffer is marked for journaling */ ++- ext4_insert_dentry(inode, de, blocksize, name, namelen); +++ ext4_insert_dentry(inode, de, blocksize, name, namelen, data); ++ ++ /* ++ * XXX shouldn't update any times until successful ++@@ -1874,7 +1895,8 @@ static int make_indexed_dir(handle_t *ha ++ ++ dx_set_block(entries, 1); ++ dx_set_count(entries, 1); ++- dx_set_limit(entries, dx_root_limit(dir, sizeof(*dx_info))); +++ dx_set_limit(entries, dx_root_limit(dir, +++ dot_de, sizeof(*dx_info))); ++ ++ /* Initialize as for dx_probe */ ++ hinfo.hash_version = dx_info->hash_version; ++@@ -1917,6 +1939,8 @@ static int ext4_update_dotdot(handle_t * ++ struct buffer_head * dir_block; ++ struct ext4_dir_entry_2 * de; ++ int len, journal = 0, err = 0; +++ int dlen = 0; +++ char *data; ++ ++ if (IS_ERR(handle)) ++ return PTR_ERR(handle); ++@@ -1932,19 +1956,24 @@ static int ext4_update_dotdot(handle_t * ++ /* the first item must be "." */ ++ assert(de->name_len == 1 && de->name[0] == '.'); ++ len = le16_to_cpu(de->rec_len); ++- assert(len >= EXT4_DIR_REC_LEN(1)); ++- if (len > EXT4_DIR_REC_LEN(1)) { +++ assert(len >= __EXT4_DIR_REC_LEN(1)); +++ if (len > __EXT4_DIR_REC_LEN(1)) { ++ BUFFER_TRACE(dir_block, "get_write_access"); ++ err = ext4_journal_get_write_access(handle, dir_block); ++ if (err) ++ goto out_journal; ++ ++ journal = 1; ++- de->rec_len = cpu_to_le16(EXT4_DIR_REC_LEN(1)); +++ de->rec_len = cpu_to_le16(EXT4_DIR_REC_LEN(de)); ++ } ++ ++- len -= EXT4_DIR_REC_LEN(1); ++- assert(len == 0 || len >= EXT4_DIR_REC_LEN(2)); +++ len -= EXT4_DIR_REC_LEN(de); +++ data = ext4_dentry_get_data(dir->i_sb, +++ (struct ext4_dentry_param *) dentry->d_fsdata); +++ if (data) +++ dlen = *data + 1; +++ assert(len == 0 || len >= __EXT4_DIR_REC_LEN(2 + dlen)); +++ ++ de = (struct ext4_dir_entry_2 *) ++ ((char *) de + le16_to_cpu(de->rec_len)); ++ if (!journal) { ++@@ -1958,10 +1987,15 @@ static int ext4_update_dotdot(handle_t * ++ if (len > 0) ++ de->rec_len = cpu_to_le16(len); ++ else ++- assert(le16_to_cpu(de->rec_len) >= EXT4_DIR_REC_LEN(2)); +++ assert(le16_to_cpu(de->rec_len) >= __EXT4_DIR_REC_LEN(2)); ++ de->name_len = 2; ++ strcpy (de->name, ".."); ++- ext4_set_de_type(dir->i_sb, de, S_IFDIR); +++ if (data != NULL && ext4_get_dirent_data_len(de) >= dlen) { +++ de->name[2] = 0; +++ memcpy(&de->name[2 + 1], data, *data); +++ ext4_set_de_type(dir->i_sb, de, S_IFDIR); +++ de->file_type |= EXT4_DIRENT_LUFID; +++ } ++ ++ out_journal: ++ if (journal) { ++@@ -2474,37 +2508,70 @@ err_drop_inode: ++ return err; ++ } ++ +++struct tp_block { +++ struct inode *inode; +++ void *data1; +++ void *data2; +++}; +++ ++ struct ext4_dir_entry_2 *ext4_init_dot_dotdot(struct inode *inode, ++ struct ext4_dir_entry_2 *de, ++ int blocksize, int csum_size, ++ unsigned int parent_ino, int dotdot_real_len) ++ { +++ void *data1 = NULL, *data2 = NULL; +++ int dot_reclen = 0; +++ +++ if (dotdot_real_len == 10) { +++ struct tp_block *tpb = (struct tp_block*)inode; +++ data1 = tpb->data1; +++ data2 = tpb->data2; +++ inode = tpb->inode; +++ dotdot_real_len = 0; +++ } ++ de->inode = cpu_to_le32(inode->i_ino); ++ de->name_len = 1; ++- de->rec_len = ext4_rec_len_to_disk(EXT4_DIR_REC_LEN(de->name_len), ++- blocksize); ++ strcpy(de->name, "."); ++ ext4_set_de_type(inode->i_sb, de, S_IFDIR); ++ +++ /* get packed fid data*/ +++ data1 = ext4_dentry_get_data(inode->i_sb, +++ (struct ext4_dentry_param *) data1); +++ if (data1) { +++ de->name[1] = 0; +++ memcpy(&de->name[2], data1, *(char *) data1); +++ de->file_type |= EXT4_DIRENT_LUFID; +++ } +++ de->rec_len = cpu_to_le16(EXT4_DIR_REC_LEN(de)); +++ dot_reclen = cpu_to_le16(de->rec_len); ++ de = ext4_next_entry(de, blocksize); ++ de->inode = cpu_to_le32(parent_ino); ++ de->name_len = 2; ++ if (!dotdot_real_len) ++ de->rec_len = ext4_rec_len_to_disk(blocksize - ++- (csum_size + EXT4_DIR_REC_LEN(1)), +++ (csum_size + dot_reclen), ++ blocksize); ++ else ++ de->rec_len = ext4_rec_len_to_disk( ++- EXT4_DIR_REC_LEN(de->name_len), blocksize); +++ EXT4_DIR_REC_LEN(de), blocksize); ++ strcpy(de->name, ".."); ++ ext4_set_de_type(inode->i_sb, de, S_IFDIR); +++ data2 = ext4_dentry_get_data(inode->i_sb, +++ (struct ext4_dentry_param *) data2); +++ if (data2) { +++ de->name[2] = 0; +++ memcpy(&de->name[3], data2, *(char *) data2); +++ de->file_type |= EXT4_DIRENT_LUFID; +++ } ++ ++ return ext4_next_entry(de, blocksize); ++ } ++ ++ static int ext4_init_new_dir(handle_t *handle, struct inode *dir, ++- struct inode *inode) +++ struct inode *inode, +++ const void *data1, const void *data2) ++ { +++ struct tp_block param; ++ struct buffer_head *dir_block = NULL; ++ struct ext4_dir_entry_2 *de; ++ struct ext4_dir_entry_tail *t; ++@@ -2534,7 +2601,11 @@ static int ext4_init_new_dir(handle_t *h ++ if (err) ++ goto out; ++ de = (struct ext4_dir_entry_2 *)dir_block->b_data; ++- ext4_init_dot_dotdot(inode, de, blocksize, csum_size, dir->i_ino, 0); +++ param.inode = inode; +++ param.data1 = (void *)data1; +++ param.data2 = (void *)data2; +++ ext4_init_dot_dotdot((struct inode *)(¶m), de, blocksize, +++ csum_size, dir->i_ino, 10); ++ set_nlink(inode, 2); ++ if (csum_size) { ++ t = EXT4_DIRENT_TAIL(dir_block->b_data, blocksize); ++@@ -2554,7 +2625,8 @@ out: ++ /* Initialize @inode as a subdirectory of @dir, and add the ++ * "." and ".." entries into the first directory block. */ ++ int ext4_add_dot_dotdot(handle_t *handle, struct inode * dir, ++- struct inode *inode) +++ struct inode *inode, +++ const void *data1, const void *data2) ++ { ++ if (IS_ERR(handle)) ++ return PTR_ERR(handle); ++@@ -2564,7 +2636,7 @@ int ext4_add_dot_dotdot(handle_t *handle ++ ++ inode->i_op = &ext4_dir_inode_operations; ++ inode->i_fop = &ext4_dir_operations; ++- return ext4_init_new_dir(handle, dir, inode); +++ return ext4_init_new_dir(handle, dir, inode, data1, data2); ++ } ++ EXPORT_SYMBOL(ext4_add_dot_dotdot); ++ ++@@ -2592,7 +2664,7 @@ retry: ++ ++ inode->i_op = &ext4_dir_inode_operations; ++ inode->i_fop = &ext4_dir_operations; ++- err = ext4_init_new_dir(handle, dir, inode); +++ err = ext4_init_new_dir(handle, dir, inode, NULL, NULL); ++ if (err) ++ goto out_clear_inode; ++ err = ext4_mark_inode_dirty(handle, inode); ++@@ -2644,7 +2716,7 @@ static int empty_dir(struct inode *inode ++ } ++ ++ sb = inode->i_sb; ++- if (inode->i_size < EXT4_DIR_REC_LEN(1) + EXT4_DIR_REC_LEN(2)) { +++ if (inode->i_size < __EXT4_DIR_REC_LEN(1) + __EXT4_DIR_REC_LEN(2)) { ++ EXT4_ERROR_INODE(inode, "invalid size"); ++ return 1; ++ } ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/inline.c ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/inline.c +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/inline.c ++@@ -998,7 +998,7 @@ static int ext4_add_dirent_to_inline(han ++ int err; ++ struct ext4_dir_entry_2 *de; ++ ++- reclen = EXT4_DIR_REC_LEN(namelen); +++ reclen = __EXT4_DIR_REC_LEN(namelen); ++ err = ext4_find_dest_de(dir, inode, iloc->bh, ++ inline_start, inline_size, ++ name, namelen, &de); ++@@ -1008,7 +1008,7 @@ static int ext4_add_dirent_to_inline(han ++ err = ext4_journal_get_write_access(handle, iloc->bh); ++ if (err) ++ return err; ++- ext4_insert_dentry(inode, de, inline_size, name, namelen); +++ ext4_insert_dentry(inode, de, inline_size, name, namelen, NULL); ++ ++ ext4_show_inline_dir(dir, iloc->bh, inline_start, inline_size); ++ ++@@ -1078,7 +1078,7 @@ static int ext4_update_inline_dir(handle ++ int old_size = EXT4_I(dir)->i_inline_size - EXT4_MIN_INLINE_DATA_SIZE; ++ int new_size = get_max_inline_xattr_value_size(dir, iloc); ++ ++- if (new_size - old_size <= EXT4_DIR_REC_LEN(1)) +++ if (new_size - old_size <= __EXT4_DIR_REC_LEN(1)) ++ return -ENOSPC; ++ ++ ret = ext4_update_inline_data(handle, dir, ++@@ -1348,7 +1348,7 @@ int htree_inlinedir_to_tree(struct file ++ fake.name_len = 1; ++ strcpy(fake.name, "."); ++ fake.rec_len = ext4_rec_len_to_disk( ++- EXT4_DIR_REC_LEN(fake.name_len), +++ EXT4_DIR_REC_LEN(&fake), ++ inline_size); ++ ext4_set_de_type(inode->i_sb, &fake, S_IFDIR); ++ de = &fake; ++@@ -1358,7 +1358,7 @@ int htree_inlinedir_to_tree(struct file ++ fake.name_len = 2; ++ strcpy(fake.name, ".."); ++ fake.rec_len = ext4_rec_len_to_disk( ++- EXT4_DIR_REC_LEN(fake.name_len), +++ EXT4_DIR_REC_LEN(&fake), ++ inline_size); ++ ext4_set_de_type(inode->i_sb, &fake, S_IFDIR); ++ de = &fake; ++@@ -1453,8 +1453,8 @@ int ext4_read_inline_dir(struct file *fi ++ * So we will use extra_offset and extra_size to indicate them ++ * during the inline dir iteration. ++ */ ++- dotdot_offset = EXT4_DIR_REC_LEN(1); ++- dotdot_size = dotdot_offset + EXT4_DIR_REC_LEN(2); +++ dotdot_offset = __EXT4_DIR_REC_LEN(1); +++ dotdot_size = dotdot_offset + __EXT4_DIR_REC_LEN(2); ++ extra_offset = dotdot_size - EXT4_INLINE_DOTDOT_SIZE; ++ extra_size = extra_offset + inline_size; ++ ++@@ -1489,7 +1489,7 @@ int ext4_read_inline_dir(struct file *fi ++ * failure will be detected in the ++ * dirent test below. */ ++ if (ext4_rec_len_from_disk(de->rec_len, extra_size) ++- < EXT4_DIR_REC_LEN(1)) +++ < __EXT4_DIR_REC_LEN(1)) ++ break; ++ i += ext4_rec_len_from_disk(de->rec_len, ++ extra_size); +diff --git a/ldiskfs/kernel_patches/patches/fc19/ext4_pdirop.patch b/ldiskfs/kernel_patches/patches/fc19/ext4_pdirop.patch +new file mode 100644 +index 0000000..3af9392 +--- /dev/null ++++ b/ldiskfs/kernel_patches/patches/fc19/ext4_pdirop.patch +@@ -0,0 +1,2252 @@ ++Index: linux-3.11.1-200.fc19.x86_64/include/linux/htree_lock.h ++=================================================================== ++--- /dev/null +++++ linux-3.11.1-200.fc19.x86_64/include/linux/htree_lock.h ++@@ -0,0 +1,187 @@ +++/* +++ * include/linux/htree_lock.h +++ * +++ * Copyright (c) 2011, 2012, Intel Corporation. +++ * +++ * Author: Liang Zhen +++ */ +++ +++/* +++ * htree lock +++ * +++ * htree_lock is an advanced lock, it can support five lock modes (concept is +++ * taken from DLM) and it's a sleeping lock. +++ * +++ * most common use case is: +++ * - create a htree_lock_head for data +++ * - each thread (contender) creates it's own htree_lock +++ * - contender needs to call htree_lock(lock_node, mode) to protect data and +++ * call htree_unlock to release lock +++ * +++ * Also, there is advanced use-case which is more complex, user can have +++ * PW/PR lock on particular key, it's mostly used while user holding shared +++ * lock on the htree (CW, CR) +++ * +++ * htree_lock(lock_node, HTREE_LOCK_CR); lock the htree with CR +++ * htree_node_lock(lock_node, HTREE_LOCK_PR, key...); lock @key with PR +++ * ... +++ * htree_node_unlock(lock_node);; unlock the key +++ * +++ * Another tip is, we can have N-levels of this kind of keys, all we need to +++ * do is specifying N-levels while creating htree_lock_head, then we can +++ * lock/unlock a specific level by: +++ * htree_node_lock(lock_node, mode1, key1, level1...); +++ * do something; +++ * htree_node_lock(lock_node, mode1, key2, level2...); +++ * do something; +++ * htree_node_unlock(lock_node, level2); +++ * htree_node_unlock(lock_node, level1); +++ * +++ * NB: for multi-level, should be careful about locking order to avoid deadlock +++ */ +++ +++#ifndef _LINUX_HTREE_LOCK_H +++#define _LINUX_HTREE_LOCK_H +++ +++#include +++#include +++#include +++ +++/* +++ * Lock Modes +++ * more details can be found here: +++ * http://en.wikipedia.org/wiki/Distributed_lock_manager +++ */ +++typedef enum { +++ HTREE_LOCK_EX = 0, /* exclusive lock: incompatible with all others */ +++ HTREE_LOCK_PW, /* protected write: allows only CR users */ +++ HTREE_LOCK_PR, /* protected read: allow PR, CR users */ +++ HTREE_LOCK_CW, /* concurrent write: allow CR, CW users */ +++ HTREE_LOCK_CR, /* concurrent read: allow all but EX users */ +++ HTREE_LOCK_MAX, /* number of lock modes */ +++} htree_lock_mode_t; +++ +++#define HTREE_LOCK_NL HTREE_LOCK_MAX +++#define HTREE_LOCK_INVAL 0xdead10c +++ +++enum { +++ HTREE_HBITS_MIN = 2, +++ HTREE_HBITS_DEF = 14, +++ HTREE_HBITS_MAX = 32, +++}; +++ +++enum { +++ HTREE_EVENT_DISABLE = (0), +++ HTREE_EVENT_RD = (1 << HTREE_LOCK_PR), +++ HTREE_EVENT_WR = (1 << HTREE_LOCK_PW), +++ HTREE_EVENT_RDWR = (HTREE_EVENT_RD | HTREE_EVENT_WR), +++}; +++ +++struct htree_lock; +++ +++typedef void (*htree_event_cb_t)(void *target, void *event); +++ +++struct htree_lock_child { +++ struct list_head lc_list; /* granted list */ +++ htree_event_cb_t lc_callback; /* event callback */ +++ unsigned lc_events; /* event types */ +++}; +++ +++struct htree_lock_head { +++ unsigned long lh_lock; /* bits lock */ +++ /* blocked lock list (htree_lock) */ +++ struct list_head lh_blocked_list; +++ /* # key levels */ +++ u16 lh_depth; +++ /* hash bits for key and limit number of locks */ +++ u16 lh_hbits; +++ /* counters for blocked locks */ +++ u16 lh_nblocked[HTREE_LOCK_MAX]; +++ /* counters for granted locks */ +++ u16 lh_ngranted[HTREE_LOCK_MAX]; +++ /* private data */ +++ void *lh_private; +++ /* array of children locks */ +++ struct htree_lock_child lh_children[0]; +++}; +++ +++/* htree_lock_node_t is child-lock for a specific key (ln_value) */ +++struct htree_lock_node { +++ htree_lock_mode_t ln_mode; +++ /* major hash key */ +++ u16 ln_major_key; +++ /* minor hash key */ +++ u16 ln_minor_key; +++ struct list_head ln_major_list; +++ struct list_head ln_minor_list; +++ /* alive list, all locks (granted, blocked, listening) are on it */ +++ struct list_head ln_alive_list; +++ /* blocked list */ +++ struct list_head ln_blocked_list; +++ /* granted list */ +++ struct list_head ln_granted_list; +++ void *ln_ev_target; +++}; +++ +++struct htree_lock { +++ struct task_struct *lk_task; +++ struct htree_lock_head *lk_head; +++ void *lk_private; +++ unsigned lk_depth; +++ htree_lock_mode_t lk_mode; +++ struct list_head lk_blocked_list; +++ struct htree_lock_node lk_nodes[0]; +++}; +++ +++/* create a lock head, which stands for a resource */ +++struct htree_lock_head *htree_lock_head_alloc(unsigned depth, +++ unsigned hbits, unsigned priv); +++/* free a lock head */ +++void htree_lock_head_free(struct htree_lock_head *lhead); +++/* register event callback for child lock at level @depth */ +++void htree_lock_event_attach(struct htree_lock_head *lhead, unsigned depth, +++ unsigned events, htree_event_cb_t callback); +++/* create a lock handle, which stands for a thread */ +++struct htree_lock *htree_lock_alloc(unsigned depth, unsigned pbytes); +++/* free a lock handle */ +++void htree_lock_free(struct htree_lock *lck); +++/* lock htree, when @wait is true, 0 is returned if the lock can't +++ * be granted immediately */ +++int htree_lock_try(struct htree_lock *lck, struct htree_lock_head *lhead, +++ htree_lock_mode_t mode, int wait); +++/* unlock htree */ +++void htree_unlock(struct htree_lock *lck); +++/* unlock and relock htree with @new_mode */ +++int htree_change_lock_try(struct htree_lock *lck, +++ htree_lock_mode_t new_mode, int wait); +++void htree_change_mode(struct htree_lock *lck, htree_lock_mode_t mode); +++/* require child lock (key) of htree at level @dep, @event will be sent to all +++ * listeners on this @key while lock being granted */ +++int htree_node_lock_try(struct htree_lock *lck, htree_lock_mode_t mode, +++ u32 key, unsigned dep, int wait, void *event); +++/* release child lock at level @dep, this lock will listen on it's key +++ * if @event isn't NULL, event_cb will be called against @lck while granting +++ * any other lock at level @dep with the same key */ +++void htree_node_unlock(struct htree_lock *lck, unsigned dep, void *event); +++/* stop listening on child lock at level @dep */ +++void htree_node_stop_listen(struct htree_lock *lck, unsigned dep); +++/* for debug */ +++void htree_lock_stat_print(int depth); +++void htree_lock_stat_reset(void); +++ +++#define htree_lock(lck, lh, mode) htree_lock_try(lck, lh, mode, 1) +++#define htree_change_lock(lck, mode) htree_change_lock_try(lck, mode, 1) +++ +++#define htree_lock_mode(lck) ((lck)->lk_mode) +++ +++#define htree_node_lock(lck, mode, key, dep) \ +++ htree_node_lock_try(lck, mode, key, dep, 1, NULL) +++/* this is only safe in thread context of lock owner */ +++#define htree_node_is_granted(lck, dep) \ +++ ((lck)->lk_nodes[dep].ln_mode != HTREE_LOCK_INVAL && \ +++ (lck)->lk_nodes[dep].ln_mode != HTREE_LOCK_NL) +++/* this is only safe in thread context of lock owner */ +++#define htree_node_is_listening(lck, dep) \ +++ ((lck)->lk_nodes[dep].ln_mode == HTREE_LOCK_NL) +++ +++#endif ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/htree_lock.c ++=================================================================== ++--- /dev/null +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/htree_lock.c ++@@ -0,0 +1,880 @@ +++/* +++ * fs/ext4/htree_lock.c +++ * +++ * Copyright (c) 2011, 2012, Intel Corporation. +++ * +++ * Author: Liang Zhen +++ */ +++#include +++#include +++#include +++#include +++ +++enum { +++ HTREE_LOCK_BIT_EX = (1 << HTREE_LOCK_EX), +++ HTREE_LOCK_BIT_PW = (1 << HTREE_LOCK_PW), +++ HTREE_LOCK_BIT_PR = (1 << HTREE_LOCK_PR), +++ HTREE_LOCK_BIT_CW = (1 << HTREE_LOCK_CW), +++ HTREE_LOCK_BIT_CR = (1 << HTREE_LOCK_CR), +++}; +++ +++enum { +++ HTREE_LOCK_COMPAT_EX = 0, +++ HTREE_LOCK_COMPAT_PW = HTREE_LOCK_COMPAT_EX | HTREE_LOCK_BIT_CR, +++ HTREE_LOCK_COMPAT_PR = HTREE_LOCK_COMPAT_PW | HTREE_LOCK_BIT_PR, +++ HTREE_LOCK_COMPAT_CW = HTREE_LOCK_COMPAT_PW | HTREE_LOCK_BIT_CW, +++ HTREE_LOCK_COMPAT_CR = HTREE_LOCK_COMPAT_CW | HTREE_LOCK_BIT_PR | +++ HTREE_LOCK_BIT_PW, +++}; +++ +++static int htree_lock_compat[] = { +++ [HTREE_LOCK_EX] HTREE_LOCK_COMPAT_EX, +++ [HTREE_LOCK_PW] HTREE_LOCK_COMPAT_PW, +++ [HTREE_LOCK_PR] HTREE_LOCK_COMPAT_PR, +++ [HTREE_LOCK_CW] HTREE_LOCK_COMPAT_CW, +++ [HTREE_LOCK_CR] HTREE_LOCK_COMPAT_CR, +++}; +++ +++/* max allowed htree-lock depth. +++ * We only need depth=3 for ext4 although user can have higher value. */ +++#define HTREE_LOCK_DEP_MAX 16 +++ +++#ifdef HTREE_LOCK_DEBUG +++ +++static char *hl_name[] = { +++ [HTREE_LOCK_EX] "EX", +++ [HTREE_LOCK_PW] "PW", +++ [HTREE_LOCK_PR] "PR", +++ [HTREE_LOCK_CW] "CW", +++ [HTREE_LOCK_CR] "CR", +++}; +++ +++/* lock stats */ +++struct htree_lock_node_stats { +++ unsigned long long blocked[HTREE_LOCK_MAX]; +++ unsigned long long granted[HTREE_LOCK_MAX]; +++ unsigned long long retried[HTREE_LOCK_MAX]; +++ unsigned long long events; +++}; +++ +++struct htree_lock_stats { +++ struct htree_lock_node_stats nodes[HTREE_LOCK_DEP_MAX]; +++ unsigned long long granted[HTREE_LOCK_MAX]; +++ unsigned long long blocked[HTREE_LOCK_MAX]; +++}; +++ +++static struct htree_lock_stats hl_stats; +++ +++void htree_lock_stat_reset(void) +++{ +++ memset(&hl_stats, 0, sizeof(hl_stats)); +++} +++ +++void htree_lock_stat_print(int depth) +++{ +++ int i; +++ int j; +++ +++ printk(KERN_DEBUG "HTREE LOCK STATS:\n"); +++ for (i = 0; i < HTREE_LOCK_MAX; i++) { +++ printk(KERN_DEBUG "[%s]: G [%10llu], B [%10llu]\n", +++ hl_name[i], hl_stats.granted[i], hl_stats.blocked[i]); +++ } +++ for (i = 0; i < depth; i++) { +++ printk(KERN_DEBUG "HTREE CHILD [%d] STATS:\n", i); +++ for (j = 0; j < HTREE_LOCK_MAX; j++) { +++ printk(KERN_DEBUG +++ "[%s]: G [%10llu], B [%10llu], R [%10llu]\n", +++ hl_name[j], hl_stats.nodes[i].granted[j], +++ hl_stats.nodes[i].blocked[j], +++ hl_stats.nodes[i].retried[j]); +++ } +++ } +++} +++ +++#define lk_grant_inc(m) do { hl_stats.granted[m]++; } while (0) +++#define lk_block_inc(m) do { hl_stats.blocked[m]++; } while (0) +++#define ln_grant_inc(d, m) do { hl_stats.nodes[d].granted[m]++; } while (0) +++#define ln_block_inc(d, m) do { hl_stats.nodes[d].blocked[m]++; } while (0) +++#define ln_retry_inc(d, m) do { hl_stats.nodes[d].retried[m]++; } while (0) +++#define ln_event_inc(d) do { hl_stats.nodes[d].events++; } while (0) +++ +++#else /* !DEBUG */ +++ +++void htree_lock_stat_reset(void) {} +++void htree_lock_stat_print(int depth) {} +++ +++#define lk_grant_inc(m) do {} while (0) +++#define lk_block_inc(m) do {} while (0) +++#define ln_grant_inc(d, m) do {} while (0) +++#define ln_block_inc(d, m) do {} while (0) +++#define ln_retry_inc(d, m) do {} while (0) +++#define ln_event_inc(d) do {} while (0) +++ +++#endif /* DEBUG */ +++ +++EXPORT_SYMBOL(htree_lock_stat_reset); +++EXPORT_SYMBOL(htree_lock_stat_print); +++ +++#define HTREE_DEP_ROOT (-1) +++ +++#define htree_spin_lock(lhead, dep) \ +++ bit_spin_lock((dep) + 1, &(lhead)->lh_lock) +++#define htree_spin_unlock(lhead, dep) \ +++ bit_spin_unlock((dep) + 1, &(lhead)->lh_lock) +++ +++#define htree_key_event_ignore(child, ln) \ +++ (!((child)->lc_events & (1 << (ln)->ln_mode))) +++ +++static int +++htree_key_list_empty(struct htree_lock_node *ln) +++{ +++ return list_empty(&ln->ln_major_list) && list_empty(&ln->ln_minor_list); +++} +++ +++static void +++htree_key_list_del_init(struct htree_lock_node *ln) +++{ +++ struct htree_lock_node *tmp = NULL; +++ +++ if (!list_empty(&ln->ln_minor_list)) { +++ tmp = list_entry(ln->ln_minor_list.next, +++ struct htree_lock_node, ln_minor_list); +++ list_del_init(&ln->ln_minor_list); +++ } +++ +++ if (list_empty(&ln->ln_major_list)) +++ return; +++ +++ if (tmp == NULL) { /* not on minor key list */ +++ list_del_init(&ln->ln_major_list); +++ } else { +++ BUG_ON(!list_empty(&tmp->ln_major_list)); +++ list_replace_init(&ln->ln_major_list, &tmp->ln_major_list); +++ } +++} +++ +++static void +++htree_key_list_replace_init(struct htree_lock_node *old, +++ struct htree_lock_node *new) +++{ +++ if (!list_empty(&old->ln_major_list)) +++ list_replace_init(&old->ln_major_list, &new->ln_major_list); +++ +++ if (!list_empty(&old->ln_minor_list)) +++ list_replace_init(&old->ln_minor_list, &new->ln_minor_list); +++} +++ +++static void +++htree_key_event_enqueue(struct htree_lock_child *child, +++ struct htree_lock_node *ln, int dep, void *event) +++{ +++ struct htree_lock_node *tmp; +++ +++ /* NB: ALWAYS called holding lhead::lh_lock(dep) */ +++ BUG_ON(ln->ln_mode == HTREE_LOCK_NL); +++ if (event == NULL || htree_key_event_ignore(child, ln)) +++ return; +++ +++ /* shouldn't be a very long list */ +++ list_for_each_entry(tmp, &ln->ln_alive_list, ln_alive_list) { +++ if (tmp->ln_mode == HTREE_LOCK_NL) { +++ ln_event_inc(dep); +++ if (child->lc_callback != NULL) +++ child->lc_callback(tmp->ln_ev_target, event); +++ } +++ } +++} +++ +++static int +++htree_node_lock_enqueue(struct htree_lock *newlk, struct htree_lock *curlk, +++ unsigned dep, int wait, void *event) +++{ +++ struct htree_lock_child *child = &newlk->lk_head->lh_children[dep]; +++ struct htree_lock_node *newln = &newlk->lk_nodes[dep]; +++ struct htree_lock_node *curln = &curlk->lk_nodes[dep]; +++ +++ /* NB: ALWAYS called holding lhead::lh_lock(dep) */ +++ /* NB: we only expect PR/PW lock mode at here, only these two modes are +++ * allowed for htree_node_lock(asserted in htree_node_lock_internal), +++ * NL is only used for listener, user can't directly require NL mode */ +++ if ((curln->ln_mode == HTREE_LOCK_NL) || +++ (curln->ln_mode != HTREE_LOCK_PW && +++ newln->ln_mode != HTREE_LOCK_PW)) { +++ /* no conflict, attach it on granted list of @curlk */ +++ if (curln->ln_mode != HTREE_LOCK_NL) { +++ list_add(&newln->ln_granted_list, +++ &curln->ln_granted_list); +++ } else { +++ /* replace key owner */ +++ htree_key_list_replace_init(curln, newln); +++ } +++ +++ list_add(&newln->ln_alive_list, &curln->ln_alive_list); +++ htree_key_event_enqueue(child, newln, dep, event); +++ ln_grant_inc(dep, newln->ln_mode); +++ return 1; /* still hold lh_lock */ +++ } +++ +++ if (!wait) { /* can't grant and don't want to wait */ +++ ln_retry_inc(dep, newln->ln_mode); +++ newln->ln_mode = HTREE_LOCK_INVAL; +++ return -1; /* don't wait and just return -1 */ +++ } +++ +++ newlk->lk_task = current; +++ set_current_state(TASK_UNINTERRUPTIBLE); +++ /* conflict, attach it on blocked list of curlk */ +++ list_add_tail(&newln->ln_blocked_list, &curln->ln_blocked_list); +++ list_add(&newln->ln_alive_list, &curln->ln_alive_list); +++ ln_block_inc(dep, newln->ln_mode); +++ +++ htree_spin_unlock(newlk->lk_head, dep); +++ /* wait to be given the lock */ +++ if (newlk->lk_task != NULL) +++ schedule(); +++ /* granted, no doubt, wake up will set me RUNNING */ +++ if (event == NULL || htree_key_event_ignore(child, newln)) +++ return 0; /* granted without lh_lock */ +++ +++ htree_spin_lock(newlk->lk_head, dep); +++ htree_key_event_enqueue(child, newln, dep, event); +++ return 1; /* still hold lh_lock */ +++} +++ +++/* +++ * get PR/PW access to particular tree-node according to @dep and @key, +++ * it will return -1 if @wait is false and can't immediately grant this lock. +++ * All listeners(HTREE_LOCK_NL) on @dep and with the same @key will get +++ * @event if it's not NULL. +++ * NB: ALWAYS called holding lhead::lh_lock +++ */ +++static int +++htree_node_lock_internal(struct htree_lock_head *lhead, struct htree_lock *lck, +++ htree_lock_mode_t mode, u32 key, unsigned dep, +++ int wait, void *event) +++{ +++ LIST_HEAD (list); +++ struct htree_lock *tmp; +++ struct htree_lock *tmp2; +++ u16 major; +++ u16 minor; +++ u8 reverse; +++ u8 ma_bits; +++ u8 mi_bits; +++ +++ BUG_ON(mode != HTREE_LOCK_PW && mode != HTREE_LOCK_PR); +++ BUG_ON(htree_node_is_granted(lck, dep)); +++ +++ key = hash_long(key, lhead->lh_hbits); +++ +++ mi_bits = lhead->lh_hbits >> 1; +++ ma_bits = lhead->lh_hbits - mi_bits; +++ +++ lck->lk_nodes[dep].ln_major_key = major = key & ((1U << ma_bits) - 1); +++ lck->lk_nodes[dep].ln_minor_key = minor = key >> ma_bits; +++ lck->lk_nodes[dep].ln_mode = mode; +++ +++ /* +++ * The major key list is an ordered list, so searches are started +++ * at the end of the list that is numerically closer to major_key, +++ * so at most half of the list will be walked (for well-distributed +++ * keys). The list traversal aborts early if the expected key +++ * location is passed. +++ */ +++ reverse = (major >= (1 << (ma_bits - 1))); +++ +++ if (reverse) { +++ list_for_each_entry_reverse(tmp, +++ &lhead->lh_children[dep].lc_list, +++ lk_nodes[dep].ln_major_list) { +++ if (tmp->lk_nodes[dep].ln_major_key == major) { +++ goto search_minor; +++ +++ } else if (tmp->lk_nodes[dep].ln_major_key < major) { +++ /* attach _after_ @tmp */ +++ list_add(&lck->lk_nodes[dep].ln_major_list, +++ &tmp->lk_nodes[dep].ln_major_list); +++ goto out_grant_major; +++ } +++ } +++ +++ list_add(&lck->lk_nodes[dep].ln_major_list, +++ &lhead->lh_children[dep].lc_list); +++ goto out_grant_major; +++ +++ } else { +++ list_for_each_entry(tmp, &lhead->lh_children[dep].lc_list, +++ lk_nodes[dep].ln_major_list) { +++ if (tmp->lk_nodes[dep].ln_major_key == major) { +++ goto search_minor; +++ +++ } else if (tmp->lk_nodes[dep].ln_major_key > major) { +++ /* insert _before_ @tmp */ +++ list_add_tail(&lck->lk_nodes[dep].ln_major_list, +++ &tmp->lk_nodes[dep].ln_major_list); +++ goto out_grant_major; +++ } +++ } +++ +++ list_add_tail(&lck->lk_nodes[dep].ln_major_list, +++ &lhead->lh_children[dep].lc_list); +++ goto out_grant_major; +++ } +++ +++ search_minor: +++ /* +++ * NB: minor_key list doesn't have a "head", @list is just a +++ * temporary stub for helping list searching, make sure it's removed +++ * after searching. +++ * minor_key list is an ordered list too. +++ */ +++ list_add_tail(&list, &tmp->lk_nodes[dep].ln_minor_list); +++ +++ reverse = (minor >= (1 << (mi_bits - 1))); +++ +++ if (reverse) { +++ list_for_each_entry_reverse(tmp2, &list, +++ lk_nodes[dep].ln_minor_list) { +++ if (tmp2->lk_nodes[dep].ln_minor_key == minor) { +++ goto out_enqueue; +++ +++ } else if (tmp2->lk_nodes[dep].ln_minor_key < minor) { +++ /* attach _after_ @tmp2 */ +++ list_add(&lck->lk_nodes[dep].ln_minor_list, +++ &tmp2->lk_nodes[dep].ln_minor_list); +++ goto out_grant_minor; +++ } +++ } +++ +++ list_add(&lck->lk_nodes[dep].ln_minor_list, &list); +++ +++ } else { +++ list_for_each_entry(tmp2, &list, +++ lk_nodes[dep].ln_minor_list) { +++ if (tmp2->lk_nodes[dep].ln_minor_key == minor) { +++ goto out_enqueue; +++ +++ } else if (tmp2->lk_nodes[dep].ln_minor_key > minor) { +++ /* insert _before_ @tmp2 */ +++ list_add_tail(&lck->lk_nodes[dep].ln_minor_list, +++ &tmp2->lk_nodes[dep].ln_minor_list); +++ goto out_grant_minor; +++ } +++ } +++ +++ list_add_tail(&lck->lk_nodes[dep].ln_minor_list, &list); +++ } +++ +++ out_grant_minor: +++ if (list.next == &lck->lk_nodes[dep].ln_minor_list) { +++ /* new lock @lck is the first one on minor_key list, which +++ * means it has the smallest minor_key and it should +++ * replace @tmp as minor_key owner */ +++ list_replace_init(&tmp->lk_nodes[dep].ln_major_list, +++ &lck->lk_nodes[dep].ln_major_list); +++ } +++ /* remove the temporary head */ +++ list_del(&list); +++ +++ out_grant_major: +++ ln_grant_inc(dep, lck->lk_nodes[dep].ln_mode); +++ return 1; /* granted with holding lh_lock */ +++ +++ out_enqueue: +++ list_del(&list); /* remove temprary head */ +++ return htree_node_lock_enqueue(lck, tmp2, dep, wait, event); +++} +++ +++/* +++ * release the key of @lck at level @dep, and grant any blocked locks. +++ * caller will still listen on @key if @event is not NULL, which means +++ * caller can see a event (by event_cb) while granting any lock with +++ * the same key at level @dep. +++ * NB: ALWAYS called holding lhead::lh_lock +++ * NB: listener will not block anyone because listening mode is HTREE_LOCK_NL +++ */ +++static void +++htree_node_unlock_internal(struct htree_lock_head *lhead, +++ struct htree_lock *curlk, unsigned dep, void *event) +++{ +++ struct htree_lock_node *curln = &curlk->lk_nodes[dep]; +++ struct htree_lock *grtlk = NULL; +++ struct htree_lock_node *grtln; +++ struct htree_lock *poslk; +++ struct htree_lock *tmplk; +++ +++ if (!htree_node_is_granted(curlk, dep)) +++ return; +++ +++ if (!list_empty(&curln->ln_granted_list)) { +++ /* there is another granted lock */ +++ grtlk = list_entry(curln->ln_granted_list.next, +++ struct htree_lock, +++ lk_nodes[dep].ln_granted_list); +++ list_del_init(&curln->ln_granted_list); +++ } +++ +++ if (grtlk == NULL && !list_empty(&curln->ln_blocked_list)) { +++ /* +++ * @curlk is the only granted lock, so we confirmed: +++ * a) curln is key owner (attached on major/minor_list), +++ * so if there is any blocked lock, it should be attached +++ * on curln->ln_blocked_list +++ * b) we always can grant the first blocked lock +++ */ +++ grtlk = list_entry(curln->ln_blocked_list.next, +++ struct htree_lock, +++ lk_nodes[dep].ln_blocked_list); +++ BUG_ON(grtlk->lk_task == NULL); +++ wake_up_process(grtlk->lk_task); +++ } +++ +++ if (event != NULL && +++ lhead->lh_children[dep].lc_events != HTREE_EVENT_DISABLE) { +++ curln->ln_ev_target = event; +++ curln->ln_mode = HTREE_LOCK_NL; /* listen! */ +++ } else { +++ curln->ln_mode = HTREE_LOCK_INVAL; +++ } +++ +++ if (grtlk == NULL) { /* I must be the only one locking this key */ +++ struct htree_lock_node *tmpln; +++ +++ BUG_ON(htree_key_list_empty(curln)); +++ +++ if (curln->ln_mode == HTREE_LOCK_NL) /* listening */ +++ return; +++ +++ /* not listening */ +++ if (list_empty(&curln->ln_alive_list)) { /* no more listener */ +++ htree_key_list_del_init(curln); +++ return; +++ } +++ +++ tmpln = list_entry(curln->ln_alive_list.next, +++ struct htree_lock_node, ln_alive_list); +++ +++ BUG_ON(tmpln->ln_mode != HTREE_LOCK_NL); +++ +++ htree_key_list_replace_init(curln, tmpln); +++ list_del_init(&curln->ln_alive_list); +++ +++ return; +++ } +++ +++ /* have a granted lock */ +++ grtln = &grtlk->lk_nodes[dep]; +++ if (!list_empty(&curln->ln_blocked_list)) { +++ /* only key owner can be on both lists */ +++ BUG_ON(htree_key_list_empty(curln)); +++ +++ if (list_empty(&grtln->ln_blocked_list)) { +++ list_add(&grtln->ln_blocked_list, +++ &curln->ln_blocked_list); +++ } +++ list_del_init(&curln->ln_blocked_list); +++ } +++ /* +++ * NB: this is the tricky part: +++ * We have only two modes for child-lock (PR and PW), also, +++ * only owner of the key (attached on major/minor_list) can be on +++ * both blocked_list and granted_list, so @grtlk must be one +++ * of these two cases: +++ * +++ * a) @grtlk is taken from granted_list, which means we've granted +++ * more than one lock so @grtlk has to be PR, the first blocked +++ * lock must be PW and we can't grant it at all. +++ * So even @grtlk is not owner of the key (empty blocked_list), +++ * we don't care because we can't grant any lock. +++ * b) we just grant a new lock which is taken from head of blocked +++ * list, and it should be the first granted lock, and it should +++ * be the first one linked on blocked_list. +++ * +++ * Either way, we can get correct result by iterating blocked_list +++ * of @grtlk, and don't have to bother on how to find out +++ * owner of current key. +++ */ +++ list_for_each_entry_safe(poslk, tmplk, &grtln->ln_blocked_list, +++ lk_nodes[dep].ln_blocked_list) { +++ if (grtlk->lk_nodes[dep].ln_mode == HTREE_LOCK_PW || +++ poslk->lk_nodes[dep].ln_mode == HTREE_LOCK_PW) +++ break; +++ /* grant all readers */ +++ list_del_init(&poslk->lk_nodes[dep].ln_blocked_list); +++ list_add(&poslk->lk_nodes[dep].ln_granted_list, +++ &grtln->ln_granted_list); +++ +++ BUG_ON(poslk->lk_task == NULL); +++ wake_up_process(poslk->lk_task); +++ } +++ +++ /* if @curln is the owner of this key, replace it with @grtln */ +++ if (!htree_key_list_empty(curln)) +++ htree_key_list_replace_init(curln, grtln); +++ +++ if (curln->ln_mode == HTREE_LOCK_INVAL) +++ list_del_init(&curln->ln_alive_list); +++} +++ +++/* +++ * it's just wrapper of htree_node_lock_internal, it returns 1 on granted +++ * and 0 only if @wait is false and can't grant it immediately +++ */ +++int +++htree_node_lock_try(struct htree_lock *lck, htree_lock_mode_t mode, +++ u32 key, unsigned dep, int wait, void *event) +++{ +++ struct htree_lock_head *lhead = lck->lk_head; +++ int rc; +++ +++ BUG_ON(dep >= lck->lk_depth); +++ BUG_ON(lck->lk_mode == HTREE_LOCK_INVAL); +++ +++ htree_spin_lock(lhead, dep); +++ rc = htree_node_lock_internal(lhead, lck, mode, key, dep, wait, event); +++ if (rc != 0) +++ htree_spin_unlock(lhead, dep); +++ return rc >= 0; +++} +++EXPORT_SYMBOL(htree_node_lock_try); +++ +++/* it's wrapper of htree_node_unlock_internal */ +++void +++htree_node_unlock(struct htree_lock *lck, unsigned dep, void *event) +++{ +++ struct htree_lock_head *lhead = lck->lk_head; +++ +++ BUG_ON(dep >= lck->lk_depth); +++ BUG_ON(lck->lk_mode == HTREE_LOCK_INVAL); +++ +++ htree_spin_lock(lhead, dep); +++ htree_node_unlock_internal(lhead, lck, dep, event); +++ htree_spin_unlock(lhead, dep); +++} +++EXPORT_SYMBOL(htree_node_unlock); +++ +++/* stop listening on child-lock level @dep */ +++void +++htree_node_stop_listen(struct htree_lock *lck, unsigned dep) +++{ +++ struct htree_lock_node *ln = &lck->lk_nodes[dep]; +++ struct htree_lock_node *tmp; +++ +++ BUG_ON(htree_node_is_granted(lck, dep)); +++ BUG_ON(!list_empty(&ln->ln_blocked_list)); +++ BUG_ON(!list_empty(&ln->ln_granted_list)); +++ +++ if (!htree_node_is_listening(lck, dep)) +++ return; +++ +++ htree_spin_lock(lck->lk_head, dep); +++ ln->ln_mode = HTREE_LOCK_INVAL; +++ ln->ln_ev_target = NULL; +++ +++ if (htree_key_list_empty(ln)) { /* not owner */ +++ list_del_init(&ln->ln_alive_list); +++ goto out; +++ } +++ +++ /* I'm the owner... */ +++ if (list_empty(&ln->ln_alive_list)) { /* no more listener */ +++ htree_key_list_del_init(ln); +++ goto out; +++ } +++ +++ tmp = list_entry(ln->ln_alive_list.next, +++ struct htree_lock_node, ln_alive_list); +++ +++ BUG_ON(tmp->ln_mode != HTREE_LOCK_NL); +++ htree_key_list_replace_init(ln, tmp); +++ list_del_init(&ln->ln_alive_list); +++ out: +++ htree_spin_unlock(lck->lk_head, dep); +++} +++EXPORT_SYMBOL(htree_node_stop_listen); +++ +++/* release all child-locks if we have any */ +++static void +++htree_node_release_all(struct htree_lock *lck) +++{ +++ int i; +++ +++ for (i = 0; i < lck->lk_depth; i++) { +++ if (htree_node_is_granted(lck, i)) +++ htree_node_unlock(lck, i, NULL); +++ else if (htree_node_is_listening(lck, i)) +++ htree_node_stop_listen(lck, i); +++ } +++} +++ +++/* +++ * obtain htree lock, it could be blocked inside if there's conflict +++ * with any granted or blocked lock and @wait is true. +++ * NB: ALWAYS called holding lhead::lh_lock +++ */ +++static int +++htree_lock_internal(struct htree_lock *lck, int wait) +++{ +++ struct htree_lock_head *lhead = lck->lk_head; +++ int granted = 0; +++ int blocked = 0; +++ int i; +++ +++ for (i = 0; i < HTREE_LOCK_MAX; i++) { +++ if (lhead->lh_ngranted[i] != 0) +++ granted |= 1 << i; +++ if (lhead->lh_nblocked[i] != 0) +++ blocked |= 1 << i; +++ } +++ if ((htree_lock_compat[lck->lk_mode] & granted) != granted || +++ (htree_lock_compat[lck->lk_mode] & blocked) != blocked) { +++ /* will block current lock even it just conflicts with any +++ * other blocked lock, so lock like EX wouldn't starve */ +++ if (!wait) +++ return -1; +++ lhead->lh_nblocked[lck->lk_mode]++; +++ lk_block_inc(lck->lk_mode); +++ +++ lck->lk_task = current; +++ list_add_tail(&lck->lk_blocked_list, &lhead->lh_blocked_list); +++ +++ set_current_state(TASK_UNINTERRUPTIBLE); +++ htree_spin_unlock(lhead, HTREE_DEP_ROOT); +++ /* wait to be given the lock */ +++ if (lck->lk_task != NULL) +++ schedule(); +++ /* granted, no doubt. wake up will set me RUNNING */ +++ return 0; /* without lh_lock */ +++ } +++ lhead->lh_ngranted[lck->lk_mode]++; +++ lk_grant_inc(lck->lk_mode); +++ return 1; +++} +++ +++/* release htree lock. NB: ALWAYS called holding lhead::lh_lock */ +++static void +++htree_unlock_internal(struct htree_lock *lck) +++{ +++ struct htree_lock_head *lhead = lck->lk_head; +++ struct htree_lock *tmp; +++ struct htree_lock *tmp2; +++ int granted = 0; +++ int i; +++ +++ BUG_ON(lhead->lh_ngranted[lck->lk_mode] == 0); +++ +++ lhead->lh_ngranted[lck->lk_mode]--; +++ lck->lk_mode = HTREE_LOCK_INVAL; +++ +++ for (i = 0; i < HTREE_LOCK_MAX; i++) { +++ if (lhead->lh_ngranted[i] != 0) +++ granted |= 1 << i; +++ } +++ list_for_each_entry_safe(tmp, tmp2, +++ &lhead->lh_blocked_list, lk_blocked_list) { +++ /* conflict with any granted lock? */ +++ if ((htree_lock_compat[tmp->lk_mode] & granted) != granted) +++ break; +++ +++ list_del_init(&tmp->lk_blocked_list); +++ +++ BUG_ON(lhead->lh_nblocked[tmp->lk_mode] == 0); +++ +++ lhead->lh_nblocked[tmp->lk_mode]--; +++ lhead->lh_ngranted[tmp->lk_mode]++; +++ granted |= 1 << tmp->lk_mode; +++ +++ BUG_ON(tmp->lk_task == NULL); +++ wake_up_process(tmp->lk_task); +++ } +++} +++ +++/* it's wrapper of htree_lock_internal and exported interface. +++ * It always return 1 with granted lock if @wait is true, it can return 0 +++ * if @wait is false and locking request can't be granted immediately */ +++int +++htree_lock_try(struct htree_lock *lck, struct htree_lock_head *lhead, +++ htree_lock_mode_t mode, int wait) +++{ +++ int rc; +++ +++ BUG_ON(lck->lk_depth > lhead->lh_depth); +++ BUG_ON(lck->lk_head != NULL); +++ BUG_ON(lck->lk_task != NULL); +++ +++ lck->lk_head = lhead; +++ lck->lk_mode = mode; +++ +++ htree_spin_lock(lhead, HTREE_DEP_ROOT); +++ rc = htree_lock_internal(lck, wait); +++ if (rc != 0) +++ htree_spin_unlock(lhead, HTREE_DEP_ROOT); +++ return rc >= 0; +++} +++EXPORT_SYMBOL(htree_lock_try); +++ +++/* it's wrapper of htree_unlock_internal and exported interface. +++ * It will release all htree_node_locks and htree_lock */ +++void +++htree_unlock(struct htree_lock *lck) +++{ +++ BUG_ON(lck->lk_head == NULL); +++ BUG_ON(lck->lk_mode == HTREE_LOCK_INVAL); +++ +++ htree_node_release_all(lck); +++ +++ htree_spin_lock(lck->lk_head, HTREE_DEP_ROOT); +++ htree_unlock_internal(lck); +++ htree_spin_unlock(lck->lk_head, HTREE_DEP_ROOT); +++ lck->lk_head = NULL; +++ lck->lk_task = NULL; +++} +++EXPORT_SYMBOL(htree_unlock); +++ +++/* change lock mode */ +++void +++htree_change_mode(struct htree_lock *lck, htree_lock_mode_t mode) +++{ +++ BUG_ON(lck->lk_mode == HTREE_LOCK_INVAL); +++ lck->lk_mode = mode; +++} +++EXPORT_SYMBOL(htree_change_mode); +++ +++/* release htree lock, and lock it again with new mode. +++ * This function will first release all htree_node_locks and htree_lock, +++ * then try to gain htree_lock with new @mode. +++ * It always return 1 with granted lock if @wait is true, it can return 0 +++ * if @wait is false and locking request can't be granted immediately */ +++int +++htree_change_lock_try(struct htree_lock *lck, htree_lock_mode_t mode, int wait) +++{ +++ struct htree_lock_head *lhead = lck->lk_head; +++ int rc; +++ +++ BUG_ON(lhead == NULL); +++ BUG_ON(lck->lk_mode == mode); +++ BUG_ON(lck->lk_mode == HTREE_LOCK_INVAL || mode == HTREE_LOCK_INVAL); +++ +++ htree_node_release_all(lck); +++ +++ htree_spin_lock(lhead, HTREE_DEP_ROOT); +++ htree_unlock_internal(lck); +++ lck->lk_mode = mode; +++ rc = htree_lock_internal(lck, wait); +++ if (rc != 0) +++ htree_spin_unlock(lhead, HTREE_DEP_ROOT); +++ return rc >= 0; +++} +++EXPORT_SYMBOL(htree_change_lock_try); +++ +++/* create a htree_lock head with @depth levels (number of child-locks), +++ * it is a per resoruce structure */ +++struct htree_lock_head * +++htree_lock_head_alloc(unsigned depth, unsigned hbits, unsigned priv) +++{ +++ struct htree_lock_head *lhead; +++ int i; +++ +++ if (depth > HTREE_LOCK_DEP_MAX) { +++ printk(KERN_ERR "%d is larger than max htree_lock depth %d\n", +++ depth, HTREE_LOCK_DEP_MAX); +++ return NULL; +++ } +++ +++ lhead = kzalloc(offsetof(struct htree_lock_head, +++ lh_children[depth]) + priv, GFP_NOFS); +++ if (lhead == NULL) +++ return NULL; +++ +++ if (hbits < HTREE_HBITS_MIN) +++ lhead->lh_hbits = HTREE_HBITS_MIN; +++ else if (hbits > HTREE_HBITS_MAX) +++ lhead->lh_hbits = HTREE_HBITS_MAX; +++ +++ lhead->lh_lock = 0; +++ lhead->lh_depth = depth; +++ INIT_LIST_HEAD(&lhead->lh_blocked_list); +++ if (priv > 0) { +++ lhead->lh_private = (void *)lhead + +++ offsetof(struct htree_lock_head, lh_children[depth]); +++ } +++ +++ for (i = 0; i < depth; i++) { +++ INIT_LIST_HEAD(&lhead->lh_children[i].lc_list); +++ lhead->lh_children[i].lc_events = HTREE_EVENT_DISABLE; +++ } +++ return lhead; +++} +++EXPORT_SYMBOL(htree_lock_head_alloc); +++ +++/* free the htree_lock head */ +++void +++htree_lock_head_free(struct htree_lock_head *lhead) +++{ +++ int i; +++ +++ BUG_ON(!list_empty(&lhead->lh_blocked_list)); +++ for (i = 0; i < lhead->lh_depth; i++) +++ BUG_ON(!list_empty(&lhead->lh_children[i].lc_list)); +++ kfree(lhead); +++} +++EXPORT_SYMBOL(htree_lock_head_free); +++ +++/* register event callback for @events of child-lock at level @dep */ +++void +++htree_lock_event_attach(struct htree_lock_head *lhead, unsigned dep, +++ unsigned events, htree_event_cb_t callback) +++{ +++ BUG_ON(lhead->lh_depth <= dep); +++ lhead->lh_children[dep].lc_events = events; +++ lhead->lh_children[dep].lc_callback = callback; +++} +++EXPORT_SYMBOL(htree_lock_event_attach); +++ +++/* allocate a htree_lock, which is per-thread structure, @pbytes is some +++ * extra-bytes as private data for caller */ +++struct htree_lock * +++htree_lock_alloc(unsigned depth, unsigned pbytes) +++{ +++ struct htree_lock *lck; +++ int i = offsetof(struct htree_lock, lk_nodes[depth]); +++ +++ if (depth > HTREE_LOCK_DEP_MAX) { +++ printk(KERN_ERR "%d is larger than max htree_lock depth %d\n", +++ depth, HTREE_LOCK_DEP_MAX); +++ return NULL; +++ } +++ lck = kzalloc(i + pbytes, GFP_NOFS); +++ if (lck == NULL) +++ return NULL; +++ +++ if (pbytes != 0) +++ lck->lk_private = (void *)lck + i; +++ lck->lk_mode = HTREE_LOCK_INVAL; +++ lck->lk_depth = depth; +++ INIT_LIST_HEAD(&lck->lk_blocked_list); +++ +++ for (i = 0; i < depth; i++) { +++ struct htree_lock_node *node = &lck->lk_nodes[i]; +++ +++ node->ln_mode = HTREE_LOCK_INVAL; +++ INIT_LIST_HEAD(&node->ln_major_list); +++ INIT_LIST_HEAD(&node->ln_minor_list); +++ INIT_LIST_HEAD(&node->ln_alive_list); +++ INIT_LIST_HEAD(&node->ln_blocked_list); +++ INIT_LIST_HEAD(&node->ln_granted_list); +++ } +++ +++ return lck; +++} +++EXPORT_SYMBOL(htree_lock_alloc); +++ +++/* free htree_lock node */ +++void +++htree_lock_free(struct htree_lock *lck) +++{ +++ BUG_ON(lck->lk_mode != HTREE_LOCK_INVAL); +++ kfree(lck); +++} +++EXPORT_SYMBOL(htree_lock_free); ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/ext4.h ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/ext4.h +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/ext4.h ++@@ -27,6 +27,7 @@ ++ #include ++ #include ++ #include +++#include ++ #include ++ #include ++ #include ++@@ -1533,6 +1534,7 @@ static inline void ext4_clear_state_flag ++ EXT4_FEATURE_INCOMPAT_META_BG| \ ++ EXT4_FEATURE_INCOMPAT_EXTENTS| \ ++ EXT4_FEATURE_INCOMPAT_64BIT| \ +++ EXT4_FEATURE_INCOMPAT_LARGEDIR|\ ++ EXT4_FEATURE_INCOMPAT_FLEX_BG| \ ++ EXT4_FEATURE_INCOMPAT_EA_INODE| \ ++ EXT4_FEATURE_INCOMPAT_MMP | \ ++@@ -1958,6 +1960,76 @@ struct mmpd_data { ++ # define NORET_TYPE /**/ ++ # define ATTRIB_NORET __attribute__((noreturn)) ++ # define NORET_AND noreturn, +++/* htree levels for ext4 */ +++#define EXT4_HTREE_LEVEL_COMPAT 2 +++#define EXT4_HTREE_LEVEL 3 +++ +++static inline int +++ext4_dir_htree_level(struct super_block *sb) +++{ +++ return EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_LARGEDIR) ? +++ EXT4_HTREE_LEVEL : EXT4_HTREE_LEVEL_COMPAT; +++} +++ +++/* assume name-hash is protected by upper layer */ +++#define EXT4_HTREE_LOCK_HASH 0 +++ +++enum ext4_pdo_lk_types { +++#if EXT4_HTREE_LOCK_HASH +++ EXT4_LK_HASH, +++#endif +++ EXT4_LK_DX, /* index block */ +++ EXT4_LK_DE, /* directory entry block */ +++ EXT4_LK_SPIN, /* spinlock */ +++ EXT4_LK_MAX, +++}; +++ +++/* read-only bit */ +++#define EXT4_LB_RO(b) (1 << (b)) +++/* read + write, high bits for writer */ +++#define EXT4_LB_RW(b) ((1 << (b)) | (1 << (EXT4_LK_MAX + (b)))) +++ +++enum ext4_pdo_lock_bits { +++ /* DX lock bits */ +++ EXT4_LB_DX_RO = EXT4_LB_RO(EXT4_LK_DX), +++ EXT4_LB_DX = EXT4_LB_RW(EXT4_LK_DX), +++ /* DE lock bits */ +++ EXT4_LB_DE_RO = EXT4_LB_RO(EXT4_LK_DE), +++ EXT4_LB_DE = EXT4_LB_RW(EXT4_LK_DE), +++ /* DX spinlock bits */ +++ EXT4_LB_SPIN_RO = EXT4_LB_RO(EXT4_LK_SPIN), +++ EXT4_LB_SPIN = EXT4_LB_RW(EXT4_LK_SPIN), +++ /* accurate searching */ +++ EXT4_LB_EXACT = EXT4_LB_RO(EXT4_LK_MAX << 1), +++}; +++ +++enum ext4_pdo_lock_opc { +++ /* external */ +++ EXT4_HLOCK_READDIR = (EXT4_LB_DE_RO | EXT4_LB_DX_RO), +++ EXT4_HLOCK_LOOKUP = (EXT4_LB_DE_RO | EXT4_LB_SPIN_RO | +++ EXT4_LB_EXACT), +++ EXT4_HLOCK_DEL = (EXT4_LB_DE | EXT4_LB_SPIN_RO | +++ EXT4_LB_EXACT), +++ EXT4_HLOCK_ADD = (EXT4_LB_DE | EXT4_LB_SPIN_RO), +++ +++ /* internal */ +++ EXT4_HLOCK_LOOKUP_SAFE = (EXT4_LB_DE_RO | EXT4_LB_DX_RO | +++ EXT4_LB_EXACT), +++ EXT4_HLOCK_DEL_SAFE = (EXT4_LB_DE | EXT4_LB_DX_RO | EXT4_LB_EXACT), +++ EXT4_HLOCK_SPLIT = (EXT4_LB_DE | EXT4_LB_DX | EXT4_LB_SPIN), +++}; +++ +++extern struct htree_lock_head *ext4_htree_lock_head_alloc(unsigned hbits); +++#define ext4_htree_lock_head_free(lhead) htree_lock_head_free(lhead) +++ +++extern struct htree_lock *ext4_htree_lock_alloc(void); +++#define ext4_htree_lock_free(lck) htree_lock_free(lck) +++ +++extern void ext4_htree_lock(struct htree_lock *lck, +++ struct htree_lock_head *lhead, +++ struct inode *dir, unsigned flags); +++#define ext4_htree_unlock(lck) htree_unlock(lck) +++ ++ ++ /* bitmap.c */ ++ extern unsigned int ext4_count_free(char *bitmap, unsigned numchars); ++@@ -2050,9 +2122,17 @@ void ext4_insert_dentry(struct inode *in ++ const char *name, int namelen, void *data); ++ static inline void ext4_update_dx_flag(struct inode *inode) ++ { +++ /* Disable it for ldiskfs, because going from a DX directory to +++ * a non-DX directory while it is in use will completely break +++ * the htree-locking. +++ * If we really want to support this operation in the future, +++ * we need to exclusively lock the directory at here which will +++ * increase complexity of code */ +++#if 0 ++ if (!EXT4_HAS_COMPAT_FEATURE(inode->i_sb, ++ EXT4_FEATURE_COMPAT_DIR_INDEX)) ++ ext4_clear_inode_flag(inode, EXT4_INODE_INDEX); +++#endif ++ } ++ static unsigned char ext4_filetype_table[] = { ++ DT_UNKNOWN, DT_REG, DT_DIR, DT_CHR, DT_BLK, DT_FIFO, DT_SOCK, DT_LNK ++@@ -2215,15 +2295,16 @@ extern int ext4_htree_fill_tree(struct f ++ extern struct inode *ext4_create_inode(handle_t *handle, ++ struct inode * dir, int mode); ++ extern int ext4_add_entry(handle_t *handle, struct dentry *dentry, ++- struct inode *inode); +++ struct inode *inode, struct htree_lock *lck); ++ extern int ext4_delete_entry(handle_t *handle, struct inode * dir, ++ struct ext4_dir_entry_2 * de_del, ++ struct buffer_head * bh); ++ extern struct buffer_head * ext4_find_entry(struct inode *dir, ++ const struct qstr *d_name, ++ struct ext4_dir_entry_2 ** res_dir, ++- int *inlined); ++-#define ll_ext4_find_entry(inode, dentry, res_dir) ext4_find_entry(inode, &(dentry)->d_name, res_dir, NULL) +++ int *inlined, struct htree_lock *lck); +++#define ll_ext4_find_entry(inode, dentry, res_dir, lck) \ +++ ext4_find_entry(inode, &(dentry)->d_name, res_dir, NULL, lck) ++ extern struct buffer_head *ext4_append(handle_t *handle, ++ struct inode *inode, ++ ext4_lblk_t *block); ++@@ -2443,13 +2524,15 @@ static inline void ext4_r_blocks_count_s ++ es->s_r_blocks_count_hi = cpu_to_le32(blk >> 32); ++ } ++ ++-static inline loff_t ext4_isize(struct ext4_inode *raw_inode) +++static inline loff_t ext4_isize(struct super_block *sb, +++ struct ext4_inode *raw_inode) ++ { ++- if (S_ISREG(le16_to_cpu(raw_inode->i_mode))) +++ if (EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_LARGEDIR) || +++ S_ISREG(le16_to_cpu(raw_inode->i_mode))) ++ return ((loff_t)le32_to_cpu(raw_inode->i_size_high) << 32) | ++ le32_to_cpu(raw_inode->i_size_lo); ++- else ++- return (loff_t) le32_to_cpu(raw_inode->i_size_lo); +++ +++ return (loff_t) le32_to_cpu(raw_inode->i_size_lo); ++ } ++ ++ static inline void ext4_isize_set(struct ext4_inode *raw_inode, loff_t i_size) ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/namei.c ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/namei.c +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/namei.c ++@@ -254,7 +254,7 @@ static struct dx_frame *dx_probe(const s ++ struct inode *dir, ++ struct dx_hash_info *hinfo, ++ struct dx_frame *frame, ++- int *err); +++ struct htree_lock *lck, int *err); ++ static void dx_release(struct dx_frame *frames); ++ static int dx_make_map(struct ext4_dir_entry_2 *de, unsigned blocksize, ++ struct dx_hash_info *hinfo, struct dx_map_entry map[]); ++@@ -267,13 +267,13 @@ static void dx_insert_block(struct dx_fr ++ static int ext4_htree_next_block(struct inode *dir, __u32 hash, ++ struct dx_frame *frame, ++ struct dx_frame *frames, ++- __u32 *start_hash); +++ __u32 *start_hash, struct htree_lock *lck); ++ static struct buffer_head * ext4_dx_find_entry(struct inode *dir, ++ const struct qstr *d_name, ++ struct ext4_dir_entry_2 **res_dir, ++- int *err); +++ struct htree_lock *lck, int *err); ++ static int ext4_dx_add_entry(handle_t *handle, struct dentry *dentry, ++- struct inode *inode); +++ struct inode *inode, struct htree_lock *lck); ++ ++ /* checksumming functions */ ++ void initialize_dirent_tail(struct ext4_dir_entry_tail *t, ++@@ -525,7 +525,7 @@ struct dx_root_info * dx_get_dx_info(str ++ ++ static inline ext4_lblk_t dx_get_block(struct dx_entry *entry) ++ { ++- return le32_to_cpu(entry->block) & 0x00ffffff; +++ return le32_to_cpu(entry->block) & 0x0fffffff; ++ } ++ ++ static inline void dx_set_block(struct dx_entry *entry, ext4_lblk_t value) ++@@ -675,6 +675,223 @@ struct stats dx_show_entries(struct dx_h ++ } ++ #endif /* DX_DEBUG */ ++ +++/* private data for htree_lock */ +++struct ext4_dir_lock_data { +++ unsigned ld_flags; /* bits-map for lock types */ +++ unsigned ld_count; /* # entries of the last DX block */ +++ struct dx_entry ld_at_entry; /* copy of leaf dx_entry */ +++ struct dx_entry *ld_at; /* position of leaf dx_entry */ +++}; +++ +++#define ext4_htree_lock_data(l) ((struct ext4_dir_lock_data *)(l)->lk_private) +++ +++/* NB: ext4_lblk_t is 32 bits so we use high bits to identify invalid blk */ +++#define EXT4_HTREE_NODE_CHANGED (0xcafeULL << 32) +++ +++static void ext4_htree_event_cb(void *target, void *event) +++{ +++ u64 *block = (u64 *)target; +++ +++ if (*block == dx_get_block((struct dx_entry *)event)) +++ *block = EXT4_HTREE_NODE_CHANGED; +++} +++ +++struct htree_lock_head *ext4_htree_lock_head_alloc(unsigned hbits) +++{ +++ struct htree_lock_head *lhead; +++ +++ lhead = htree_lock_head_alloc(EXT4_LK_MAX, hbits, 0); +++ if (lhead != NULL) { +++ htree_lock_event_attach(lhead, EXT4_LK_SPIN, HTREE_EVENT_WR, +++ ext4_htree_event_cb); +++ } +++ return lhead; +++} +++EXPORT_SYMBOL(ext4_htree_lock_head_alloc); +++ +++struct htree_lock *ext4_htree_lock_alloc(void) +++{ +++ return htree_lock_alloc(EXT4_LK_MAX, +++ sizeof(struct ext4_dir_lock_data)); +++} +++EXPORT_SYMBOL(ext4_htree_lock_alloc); +++ +++static htree_lock_mode_t ext4_htree_mode(unsigned flags) +++{ +++ switch (flags) { +++ default: /* 0 or unknown flags require EX lock */ +++ return HTREE_LOCK_EX; +++ case EXT4_HLOCK_READDIR: +++ return HTREE_LOCK_PR; +++ case EXT4_HLOCK_LOOKUP: +++ return HTREE_LOCK_CR; +++ case EXT4_HLOCK_DEL: +++ case EXT4_HLOCK_ADD: +++ return HTREE_LOCK_CW; +++ } +++} +++ +++/* return PR for read-only operations, otherwise return EX */ +++static inline htree_lock_mode_t ext4_htree_safe_mode(unsigned flags) +++{ +++ int writer = (flags & EXT4_LB_DE) == EXT4_LB_DE; +++ +++ /* 0 requires EX lock */ +++ return (flags == 0 || writer) ? HTREE_LOCK_EX : HTREE_LOCK_PR; +++} +++ +++static int ext4_htree_safe_locked(struct htree_lock *lck) +++{ +++ int writer; +++ +++ if (lck == NULL || lck->lk_mode == HTREE_LOCK_EX) +++ return 1; +++ +++ writer = (ext4_htree_lock_data(lck)->ld_flags & EXT4_LB_DE) == +++ EXT4_LB_DE; +++ if (writer) /* all readers & writers are excluded? */ +++ return lck->lk_mode == HTREE_LOCK_EX; +++ +++ /* all writers are excluded? */ +++ return lck->lk_mode == HTREE_LOCK_PR || +++ lck->lk_mode == HTREE_LOCK_PW || +++ lck->lk_mode == HTREE_LOCK_EX; +++} +++ +++/* relock htree_lock with EX mode if it's change operation, otherwise +++ * relock it with PR mode. It's noop if PDO is disabled. */ +++static void ext4_htree_safe_relock(struct htree_lock *lck) +++{ +++ if (!ext4_htree_safe_locked(lck)) { +++ unsigned flags = ext4_htree_lock_data(lck)->ld_flags; +++ +++ htree_change_lock(lck, ext4_htree_safe_mode(flags)); +++ } +++} +++ +++void ext4_htree_lock(struct htree_lock *lck, struct htree_lock_head *lhead, +++ struct inode *dir, unsigned flags) +++{ +++ htree_lock_mode_t mode = is_dx(dir) ? ext4_htree_mode(flags) : +++ ext4_htree_safe_mode(flags); +++ +++ ext4_htree_lock_data(lck)->ld_flags = flags; +++ htree_lock(lck, lhead, mode); +++ if (!is_dx(dir)) +++ ext4_htree_safe_relock(lck); /* make sure it's safe locked */ +++} +++EXPORT_SYMBOL(ext4_htree_lock); +++ +++static int ext4_htree_node_lock(struct htree_lock *lck, struct dx_entry *at, +++ unsigned lmask, int wait, void *ev) +++{ +++ u32 key = (at == NULL) ? 0 : dx_get_block(at); +++ u32 mode; +++ +++ /* NOOP if htree is well protected or caller doesn't require the lock */ +++ if (ext4_htree_safe_locked(lck) || +++ !(ext4_htree_lock_data(lck)->ld_flags & lmask)) +++ return 1; +++ +++ mode = (ext4_htree_lock_data(lck)->ld_flags & lmask) == lmask ? +++ HTREE_LOCK_PW : HTREE_LOCK_PR; +++ while (1) { +++ if (htree_node_lock_try(lck, mode, key, ffz(~lmask), wait, ev)) +++ return 1; +++ if (!(lmask & EXT4_LB_SPIN)) /* not a spinlock */ +++ return 0; +++ cpu_relax(); /* spin until granted */ +++ } +++} +++ +++static int ext4_htree_node_locked(struct htree_lock *lck, unsigned lmask) +++{ +++ return ext4_htree_safe_locked(lck) || +++ htree_node_is_granted(lck, ffz(~lmask)); +++} +++ +++static void ext4_htree_node_unlock(struct htree_lock *lck, +++ unsigned lmask, void *buf) +++{ +++ /* NB: it's safe to call mutiple times or even it's not locked */ +++ if (!ext4_htree_safe_locked(lck) && +++ htree_node_is_granted(lck, ffz(~lmask))) +++ htree_node_unlock(lck, ffz(~lmask), buf); +++} +++ +++#define ext4_htree_dx_lock(lck, key) \ +++ ext4_htree_node_lock(lck, key, EXT4_LB_DX, 1, NULL) +++#define ext4_htree_dx_lock_try(lck, key) \ +++ ext4_htree_node_lock(lck, key, EXT4_LB_DX, 0, NULL) +++#define ext4_htree_dx_unlock(lck) \ +++ ext4_htree_node_unlock(lck, EXT4_LB_DX, NULL) +++#define ext4_htree_dx_locked(lck) \ +++ ext4_htree_node_locked(lck, EXT4_LB_DX) +++ +++static void ext4_htree_dx_need_lock(struct htree_lock *lck) +++{ +++ struct ext4_dir_lock_data *ld; +++ +++ if (ext4_htree_safe_locked(lck)) +++ return; +++ +++ ld = ext4_htree_lock_data(lck); +++ switch (ld->ld_flags) { +++ default: +++ return; +++ case EXT4_HLOCK_LOOKUP: +++ ld->ld_flags = EXT4_HLOCK_LOOKUP_SAFE; +++ return; +++ case EXT4_HLOCK_DEL: +++ ld->ld_flags = EXT4_HLOCK_DEL_SAFE; +++ return; +++ case EXT4_HLOCK_ADD: +++ ld->ld_flags = EXT4_HLOCK_SPLIT; +++ return; +++ } +++} +++ +++#define ext4_htree_de_lock(lck, key) \ +++ ext4_htree_node_lock(lck, key, EXT4_LB_DE, 1, NULL) +++#define ext4_htree_de_unlock(lck) \ +++ ext4_htree_node_unlock(lck, EXT4_LB_DE, NULL) +++ +++#define ext4_htree_spin_lock(lck, key, event) \ +++ ext4_htree_node_lock(lck, key, EXT4_LB_SPIN, 0, event) +++#define ext4_htree_spin_unlock(lck) \ +++ ext4_htree_node_unlock(lck, EXT4_LB_SPIN, NULL) +++#define ext4_htree_spin_unlock_listen(lck, p) \ +++ ext4_htree_node_unlock(lck, EXT4_LB_SPIN, p) +++ +++static void ext4_htree_spin_stop_listen(struct htree_lock *lck) +++{ +++ if (!ext4_htree_safe_locked(lck) && +++ htree_node_is_listening(lck, ffz(~EXT4_LB_SPIN))) +++ htree_node_stop_listen(lck, ffz(~EXT4_LB_SPIN)); +++} +++ +++enum { +++ DX_HASH_COL_IGNORE, /* ignore collision while probing frames */ +++ DX_HASH_COL_YES, /* there is collision and it does matter */ +++ DX_HASH_COL_NO, /* there is no collision */ +++}; +++ +++static int dx_probe_hash_collision(struct htree_lock *lck, +++ struct dx_entry *entries, +++ struct dx_entry *at, u32 hash) +++{ +++ if (!(ext4_htree_lock_data(lck)->ld_flags & EXT4_LB_EXACT)) { +++ return DX_HASH_COL_IGNORE; /* don't care about collision */ +++ +++ } else if (at == entries + dx_get_count(entries) - 1) { +++ return DX_HASH_COL_IGNORE; /* not in any leaf of this DX */ +++ +++ } else { /* hash collision? */ +++ return ((dx_get_hash(at + 1) & ~1) == hash) ? +++ DX_HASH_COL_YES : DX_HASH_COL_NO; +++ } +++} +++ ++ /* ++ * Probe for a directory leaf block to search. ++ * ++@@ -686,16 +903,17 @@ struct stats dx_show_entries(struct dx_h ++ */ ++ static struct dx_frame * ++ dx_probe(const struct qstr *d_name, struct inode *dir, ++- struct dx_hash_info *hinfo, struct dx_frame *frame_in, int *err) +++ struct dx_hash_info *hinfo, struct dx_frame *frame_in, +++ struct htree_lock *lck, int *err) ++ { ++ unsigned count, indirect; ++- struct dx_entry *at, *entries, *p, *q, *m; +++ struct dx_entry *at, *entries, *p, *q, *m, *dx = NULL; ++ struct dx_root_info * info; ++ struct buffer_head *bh; ++ struct dx_frame *frame = frame_in; ++ u32 hash; ++ ++- frame->bh = NULL; +++ memset(frame_in, 0, EXT4_HTREE_LEVEL * sizeof(frame_in[0])); ++ bh = ext4_read_dirblock(dir, 0, INDEX); ++ if (IS_ERR(bh)) { ++ *err = PTR_ERR(bh); ++@@ -728,9 +946,16 @@ dx_probe(const struct qstr *d_name, stru ++ goto fail; ++ } ++ ++- if ((indirect = info->indirect_levels) > 1) { ++- ext4_warning(dir->i_sb, "Unimplemented inode hash depth: %#06x", ++- info->indirect_levels); +++ indirect = info->indirect_levels; +++ if (indirect >= ext4_dir_htree_level(dir->i_sb)) { +++ ext4_warning(dir->i_sb, +++ "Directory (ino: %lu) htree depth %#06x exceed " +++ "supported value", dir->i_ino, +++ ext4_dir_htree_level(dir->i_sb)); +++ if (ext4_dir_htree_level(dir->i_sb) < EXT4_HTREE_LEVEL) { +++ ext4_warning(dir->i_sb, "Enable large directory " +++ "feature to access it"); +++ } ++ brelse(bh); ++ *err = ERR_BAD_DX_DIR; ++ goto fail; ++@@ -750,8 +975,15 @@ dx_probe(const struct qstr *d_name, stru ++ dxtrace(printk("Look up %x", hash)); ++ while (1) ++ { +++ if (indirect == 0) { /* the last index level */ +++ /* NB: ext4_htree_dx_lock() could be noop if +++ * DX-lock flag is not set for current operation */ +++ ext4_htree_dx_lock(lck, dx); +++ ext4_htree_spin_lock(lck, dx, NULL); +++ } ++ count = dx_get_count(entries); ++- if (!count || count > dx_get_limit(entries)) { +++ if (count == 0 || count > dx_get_limit(entries)) { +++ ext4_htree_spin_unlock(lck); /* release spin */ ++ ext4_warning(dir->i_sb, ++ "dx entry: no count or count > limit"); ++ brelse(bh); ++@@ -792,7 +1024,70 @@ dx_probe(const struct qstr *d_name, stru ++ frame->bh = bh; ++ frame->entries = entries; ++ frame->at = at; ++- if (!indirect--) return frame; +++ +++ if (indirect == 0) { /* the last index level */ +++ struct ext4_dir_lock_data *ld; +++ u64 myblock; +++ +++ /* By default we only lock DE-block, however, we will +++ * also lock the last level DX-block if: +++ * a) there is hash collision +++ * we will set DX-lock flag (a few lines below) +++ * and redo to lock DX-block +++ * see detail in dx_probe_hash_collision() +++ * b) it's a retry from splitting +++ * we need to lock the last level DX-block so nobody +++ * else can split any leaf blocks under the same +++ * DX-block, see detail in ext4_dx_add_entry() +++ */ +++ if (ext4_htree_dx_locked(lck)) { +++ /* DX-block is locked, just lock DE-block +++ * and return */ +++ ext4_htree_spin_unlock(lck); +++ if (!ext4_htree_safe_locked(lck)) +++ ext4_htree_de_lock(lck, frame->at); +++ return frame; +++ } +++ /* it's pdirop and no DX lock */ +++ if (dx_probe_hash_collision(lck, entries, at, hash) == +++ DX_HASH_COL_YES) { +++ /* found hash collision, set DX-lock flag +++ * and retry to abtain DX-lock */ +++ ext4_htree_spin_unlock(lck); +++ ext4_htree_dx_need_lock(lck); +++ continue; +++ } +++ ld = ext4_htree_lock_data(lck); +++ /* because I don't lock DX, so @at can't be trusted +++ * after I release spinlock so I have to save it */ +++ ld->ld_at = at; +++ ld->ld_at_entry = *at; +++ ld->ld_count = dx_get_count(entries); +++ +++ frame->at = &ld->ld_at_entry; +++ myblock = dx_get_block(at); +++ +++ /* NB: ordering locking */ +++ ext4_htree_spin_unlock_listen(lck, &myblock); +++ /* other thread can split this DE-block because: +++ * a) I don't have lock for the DE-block yet +++ * b) I released spinlock on DX-block +++ * if it happened I can detect it by listening +++ * splitting event on this DE-block */ +++ ext4_htree_de_lock(lck, frame->at); +++ ext4_htree_spin_stop_listen(lck); +++ +++ if (myblock == EXT4_HTREE_NODE_CHANGED) { +++ /* someone split this DE-block before +++ * I locked it, I need to retry and lock +++ * valid DE-block */ +++ ext4_htree_de_unlock(lck); +++ continue; +++ } +++ return frame; +++ } +++ dx = at; +++ indirect--; ++ bh = ext4_read_dirblock(dir, dx_get_block(at), INDEX); ++ if (IS_ERR(bh)) { ++ *err = PTR_ERR(bh); ++@@ -826,13 +1121,18 @@ fail: ++ static void dx_release (struct dx_frame *frames) ++ { ++ struct dx_root_info *info; +++ int i; +++ ++ if (frames[0].bh == NULL) ++ return; ++ ++ info = dx_get_dx_info((struct ext4_dir_entry_2*)frames[0].bh->b_data); ++- if (info->indirect_levels) ++- brelse(frames[1].bh); ++- brelse(frames[0].bh); +++ for (i = 0; i <= info->indirect_levels; i++) { +++ if (frames[i].bh == NULL) +++ break; +++ brelse(frames[i].bh); +++ frames[i].bh = NULL; +++ } ++ } ++ ++ /* ++@@ -855,7 +1155,7 @@ static void dx_release (struct dx_frame ++ static int ext4_htree_next_block(struct inode *dir, __u32 hash, ++ struct dx_frame *frame, ++ struct dx_frame *frames, ++- __u32 *start_hash) +++ __u32 *start_hash, struct htree_lock *lck) ++ { ++ struct dx_frame *p; ++ struct buffer_head *bh; ++@@ -870,12 +1170,22 @@ static int ext4_htree_next_block(struct ++ * this loop, num_frames indicates the number of interior ++ * nodes need to be read. ++ */ +++ ext4_htree_de_unlock(lck); ++ while (1) { ++- if (++(p->at) < p->entries + dx_get_count(p->entries)) ++- break; +++ if (num_frames > 0 || ext4_htree_dx_locked(lck)) { +++ /* num_frames > 0 : +++ * DX block +++ * ext4_htree_dx_locked: +++ * frame->at is reliable pointer returned by dx_probe, +++ * otherwise dx_probe already knew no collision */ +++ if (++(p->at) < p->entries + dx_get_count(p->entries)) +++ break; +++ } ++ if (p == frames) ++ return 0; ++ num_frames++; +++ if (num_frames == 1) +++ ext4_htree_dx_unlock(lck); ++ p--; ++ } ++ ++@@ -898,6 +1208,13 @@ static int ext4_htree_next_block(struct ++ * block so no check is necessary ++ */ ++ while (num_frames--) { +++ if (num_frames == 0) { +++ /* it's not always necessary, we just don't want to +++ * detect hash collision again */ +++ ext4_htree_dx_need_lock(lck); +++ ext4_htree_dx_lock(lck, p->at); +++ } +++ ++ bh = ext4_read_dirblock(dir, dx_get_block(p->at), INDEX); ++ if (IS_ERR(bh)) ++ return PTR_ERR(bh); ++@@ -906,6 +1223,7 @@ static int ext4_htree_next_block(struct ++ p->bh = bh; ++ p->at = p->entries = ((struct dx_node *) bh->b_data)->entries; ++ } +++ ext4_htree_de_lock(lck, p->at); ++ return 1; ++ } ++ ++@@ -974,7 +1292,7 @@ int ext4_htree_fill_tree(struct file *di ++ { ++ struct dx_hash_info hinfo; ++ struct ext4_dir_entry_2 *de; ++- struct dx_frame frames[2], *frame; +++ struct dx_frame frames[EXT4_HTREE_LEVEL], *frame; ++ struct inode *dir; ++ ext4_lblk_t block; ++ int count = 0; ++@@ -1008,10 +1326,10 @@ int ext4_htree_fill_tree(struct file *di ++ } ++ hinfo.hash = start_hash; ++ hinfo.minor_hash = 0; ++- frame = dx_probe(NULL, dir, &hinfo, frames, &err); +++ /* assume it's PR locked */ +++ frame = dx_probe(NULL, dir, &hinfo, frames, NULL, &err); ++ if (!frame) ++ return err; ++- ++ /* Add '.' and '..' from the htree header */ ++ if (!start_hash && !start_minor_hash) { ++ de = (struct ext4_dir_entry_2 *) frames[0].bh->b_data; ++@@ -1038,7 +1356,7 @@ int ext4_htree_fill_tree(struct file *di ++ count += ret; ++ hashval = ~0; ++ ret = ext4_htree_next_block(dir, HASH_NB_ALWAYS, ++- frame, frames, &hashval); +++ frame, frames, &hashval, NULL); ++ *next_hash = hashval; ++ if (ret < 0) { ++ err = ret; ++@@ -1234,7 +1552,7 @@ static int is_dx_internal_node(struct in ++ struct buffer_head * ext4_find_entry(struct inode *dir, ++ const struct qstr *d_name, ++ struct ext4_dir_entry_2 **res_dir, ++- int *inlined) +++ int *inlined, struct htree_lock *lck) ++ { ++ struct super_block *sb; ++ struct buffer_head *bh_use[NAMEI_RA_SIZE]; ++@@ -1278,7 +1596,7 @@ struct buffer_head * ext4_find_entry(str ++ goto restart; ++ } ++ if (is_dx(dir)) { ++- bh = ext4_dx_find_entry(dir, d_name, res_dir, &err); +++ bh = ext4_dx_find_entry(dir, d_name, res_dir, lck, &err); ++ /* ++ * On success, or if the error was file not found, ++ * return. Otherwise, fall back to doing a search the ++@@ -1288,6 +1606,7 @@ struct buffer_head * ext4_find_entry(str ++ return bh; ++ dxtrace(printk(KERN_DEBUG "ext4_find_entry: dx failed, " ++ "falling back\n")); +++ ext4_htree_safe_relock(lck); ++ } ++ nblocks = dir->i_size >> EXT4_BLOCK_SIZE_BITS(sb); ++ start = EXT4_I(dir)->i_dir_start_lookup; ++@@ -1377,17 +1696,19 @@ cleanup_and_exit: ++ } ++ EXPORT_SYMBOL(ext4_find_entry); ++ ++-static struct buffer_head * ext4_dx_find_entry(struct inode *dir, const struct qstr *d_name, ++- struct ext4_dir_entry_2 **res_dir, int *err) +++static struct buffer_head * ext4_dx_find_entry(struct inode *dir, +++ const struct qstr *d_name, +++ struct ext4_dir_entry_2 **res_dir, +++ struct htree_lock *lck, int *err) ++ { ++ struct super_block * sb = dir->i_sb; ++ struct dx_hash_info hinfo; ++- struct dx_frame frames[2], *frame; +++ struct dx_frame frames[EXT4_HTREE_LEVEL], *frame; ++ struct buffer_head *bh; ++ ext4_lblk_t block; ++ int retval; ++ ++- if (!(frame = dx_probe(d_name, dir, &hinfo, frames, err))) +++ if (!(frame = dx_probe(d_name, dir, &hinfo, frames, lck, err))) ++ return NULL; ++ do { ++ block = dx_get_block(frame->at); ++@@ -1411,7 +1732,7 @@ static struct buffer_head * ext4_dx_find ++ ++ /* Check to see if we should continue to search */ ++ retval = ext4_htree_next_block(dir, hinfo.hash, frame, ++- frames, NULL); +++ frames, NULL, lck); ++ if (retval < 0) { ++ ext4_warning(sb, ++ "error reading index page in directory #%lu", ++@@ -1437,7 +1758,7 @@ static struct dentry *ext4_lookup(struct ++ if (dentry->d_name.len > EXT4_NAME_LEN) ++ return ERR_PTR(-ENAMETOOLONG); ++ ++- bh = ext4_find_entry(dir, &dentry->d_name, &de, NULL); +++ bh = ext4_find_entry(dir, &dentry->d_name, &de, NULL, NULL); ++ inode = NULL; ++ if (bh) { ++ __u32 ino = le32_to_cpu(de->inode); ++@@ -1497,7 +1818,7 @@ struct dentry *ext4_get_parent(struct de ++ struct ext4_dir_entry_2 * de; ++ struct buffer_head *bh; ++ ++- bh = ext4_find_entry(child->d_inode, &dotdot, &de, NULL); +++ bh = ext4_find_entry(child->d_inode, &dotdot, &de, NULL, NULL); ++ if (!bh) ++ return ERR_PTR(-ENOENT); ++ ino = le32_to_cpu(de->inode); ++@@ -1567,8 +1888,9 @@ static struct ext4_dir_entry_2* dx_pack_ ++ * Returns pointer to de in block into which the new entry will be inserted. ++ */ ++ static struct ext4_dir_entry_2 *do_split(handle_t *handle, struct inode *dir, ++- struct buffer_head **bh,struct dx_frame *frame, ++- struct dx_hash_info *hinfo, int *error) +++ struct buffer_head **bh, struct dx_frame *frames, +++ struct dx_frame *frame, struct dx_hash_info *hinfo, +++ struct htree_lock *lck, int *error) ++ { ++ unsigned blocksize = dir->i_sb->s_blocksize; ++ unsigned count, continued; ++@@ -1632,7 +1954,14 @@ static struct ext4_dir_entry_2 *do_split ++ hash2, split, count-split)); ++ ++ /* Fancy dance to stay within two buffers */ ++- de2 = dx_move_dirents(data1, data2, map + split, count - split, blocksize); +++ if (hinfo->hash < hash2) { +++ de2 = dx_move_dirents(data1, data2, map + split, +++ count - split, blocksize); +++ } else { +++ /* make sure we will add entry to the same block which +++ * we have already locked */ +++ de2 = dx_move_dirents(data1, data2, map, split, blocksize); +++ } ++ de = dx_pack_dirents(data1, blocksize); ++ de->rec_len = ext4_rec_len_to_disk(data1 + (blocksize - csum_size) - ++ (char *) de, ++@@ -1651,13 +1980,21 @@ static struct ext4_dir_entry_2 *do_split ++ dxtrace(dx_show_leaf (hinfo, (struct ext4_dir_entry_2 *) data1, blocksize, 1)); ++ dxtrace(dx_show_leaf (hinfo, (struct ext4_dir_entry_2 *) data2, blocksize, 1)); ++ ++- /* Which block gets the new entry? */ ++- if (hinfo->hash >= hash2) ++- { ++- swap(*bh, bh2); ++- de = de2; +++ ext4_htree_spin_lock(lck, frame > frames ? (frame - 1)->at : NULL, +++ frame->at); /* notify block is being split */ +++ if (hinfo->hash < hash2) { +++ dx_insert_block(frame, hash2 + continued, newblock); +++ +++ } else { +++ /* switch block number */ +++ dx_insert_block(frame, hash2 + continued, +++ dx_get_block(frame->at)); +++ dx_set_block(frame->at, newblock); +++ (frame->at)++; ++ } ++- dx_insert_block(frame, hash2 + continued, newblock); +++ ext4_htree_spin_unlock(lck); +++ ext4_htree_dx_unlock(lck); +++ ++ err = ext4_handle_dirty_dirent_node(handle, dir, bh2); ++ if (err) ++ goto journal_error; ++@@ -1800,7 +2137,7 @@ static int add_dirent_to_buf(handle_t *h ++ if (!IS_NOCMTIME(dir)) ++ dir->i_mtime = dir->i_ctime = ext4_current_time(dir); ++ ext4_update_dx_flag(dir); ++- dir->i_version++; +++ inode_inc_iversion(dir); ++ ext4_mark_inode_dirty(handle, dir); ++ BUFFER_TRACE(bh, "call ext4_handle_dirty_metadata"); ++ err = ext4_handle_dirty_dirent_node(handle, dir, bh); ++@@ -1820,7 +2157,7 @@ static int make_indexed_dir(handle_t *ha ++ const char *name = dentry->d_name.name; ++ int namelen = dentry->d_name.len; ++ struct buffer_head *bh2; ++- struct dx_frame frames[2], *frame; +++ struct dx_frame frames[EXT4_HTREE_LEVEL], *frame; ++ struct dx_entry *entries; ++ struct ext4_dir_entry_2 *de, *de2, *dot_de, *dotdot_de; ++ struct ext4_dir_entry_tail *t; ++@@ -1914,7 +2251,7 @@ static int make_indexed_dir(handle_t *ha ++ ext4_handle_dirty_dx_node(handle, dir, frame->bh); ++ ext4_handle_dirty_dirent_node(handle, dir, bh); ++ ++- de = do_split(handle,dir, &bh, frame, &hinfo, &retval); +++ de = do_split(handle,dir, &bh, frames, frame, &hinfo, NULL, &retval); ++ if (!de) { ++ /* ++ * Even if the block split failed, we have to properly write ++@@ -2021,7 +2358,7 @@ out: ++ * the entry, as someone else might have used it while you slept. ++ */ ++ int ext4_add_entry(handle_t *handle, struct dentry *dentry, ++- struct inode *inode) +++ struct inode *inode, struct htree_lock *lck) ++ { ++ struct inode *dir = dentry->d_parent->d_inode; ++ struct buffer_head *bh; ++@@ -2057,9 +2394,10 @@ int ext4_add_entry(handle_t *handle, str ++ if (dentry->d_name.len == 2 && ++ memcmp(dentry->d_name.name, "..", 2) == 0) ++ return ext4_update_dotdot(handle, dentry, inode); ++- retval = ext4_dx_add_entry(handle, dentry, inode); +++ retval = ext4_dx_add_entry(handle, dentry, inode, lck); ++ if (!retval || (retval != ERR_BAD_DX_DIR)) ++ return retval; +++ ext4_htree_safe_relock(lck); ++ ext4_clear_inode_flag(dir, EXT4_INODE_INDEX); ++ dx_fallback++; ++ ext4_mark_inode_dirty(handle, dir); ++@@ -2105,18 +2443,21 @@ EXPORT_SYMBOL(ext4_add_entry); ++ * Returns 0 for success, or a negative error value ++ */ ++ static int ext4_dx_add_entry(handle_t *handle, struct dentry *dentry, ++- struct inode *inode) +++ struct inode *inode, struct htree_lock *lck) ++ { ++- struct dx_frame frames[2], *frame; +++ struct dx_frame frames[EXT4_HTREE_LEVEL], *frame; ++ struct dx_entry *entries, *at; ++ struct dx_hash_info hinfo; ++ struct buffer_head *bh; ++ struct inode *dir = dentry->d_parent->d_inode; ++ struct super_block *sb = dir->i_sb; ++ struct ext4_dir_entry_2 *de; +++ int restart; ++ int err; ++ ++- frame = dx_probe(&dentry->d_name, dir, &hinfo, frames, &err); +++again: +++ restart = 0; +++ frame = dx_probe(&dentry->d_name, dir, &hinfo, frames, lck, &err); ++ if (!frame) ++ return err; ++ entries = frame->entries; ++@@ -2128,33 +2469,53 @@ static int ext4_dx_add_entry(handle_t *h ++ goto cleanup; ++ } ++ ++- BUFFER_TRACE(bh, "get_write_access"); ++- err = ext4_journal_get_write_access(handle, bh); ++- if (err) ++- goto journal_error; ++- ++ err = add_dirent_to_buf(handle, dentry, inode, NULL, bh); ++ if (err != -ENOSPC) ++ goto cleanup; ++ +++ err = 0; ++ /* Block full, should compress but for now just split */ ++ dxtrace(printk(KERN_DEBUG "using %u of %u node entries\n", ++ dx_get_count(entries), dx_get_limit(entries))); ++ /* Need to split index? */ ++ if (dx_get_count(entries) == dx_get_limit(entries)) { ++ ext4_lblk_t newblock; ++- unsigned icount = dx_get_count(entries); ++- int levels = frame - frames; +++ int levels = frame - frames + 1; +++ unsigned icount; +++ int add_level = 1; ++ struct dx_entry *entries2; ++ struct dx_node *node2; ++ struct buffer_head *bh2; ++ ++- if (levels && (dx_get_count(frames->entries) == ++- dx_get_limit(frames->entries))) { ++- ext4_warning(sb, "Directory index full!"); +++ if (!ext4_htree_safe_locked(lck)) { /* retry with EX lock */ +++ ext4_htree_safe_relock(lck); +++ restart = 1; +++ goto cleanup; +++ } +++ while (frame > frames) { +++ if (dx_get_count((frame - 1)->entries) < +++ dx_get_limit((frame - 1)->entries)) { +++ add_level = 0; +++ break; +++ } +++ frame--; /* split higher index block */ +++ at = frame->at; +++ entries = frame->entries; +++ restart = 1; +++ } +++ if (add_level && levels == ext4_dir_htree_level(sb)) { +++ ext4_warning(sb, "Directory (ino: %lu) index full, " +++ "reach max htree level :%d", +++ dir->i_ino, levels); +++ if (ext4_dir_htree_level(sb) < EXT4_HTREE_LEVEL) { +++ ext4_warning(sb, "Large directory feature is" +++ "not enabled on this " +++ "filesystem"); +++ } ++ err = -ENOSPC; ++ goto cleanup; ++ } +++ icount = dx_get_count(entries); ++ bh2 = ext4_append(handle, dir, &newblock); ++ if (IS_ERR(bh2)) { ++ err = PTR_ERR(bh2); ++@@ -2169,7 +2530,7 @@ static int ext4_dx_add_entry(handle_t *h ++ err = ext4_journal_get_write_access(handle, frame->bh); ++ if (err) ++ goto journal_error; ++- if (levels) { +++ if (!add_level) { ++ unsigned icount1 = icount/2, icount2 = icount - icount1; ++ unsigned hash2 = dx_get_hash(entries + icount1); ++ dxtrace(printk(KERN_DEBUG "Split index %i/%i\n", ++@@ -2177,7 +2538,7 @@ static int ext4_dx_add_entry(handle_t *h ++ ++ BUFFER_TRACE(frame->bh, "get_write_access"); /* index root */ ++ err = ext4_journal_get_write_access(handle, ++- frames[0].bh); +++ (frame - 1)->bh); ++ if (err) ++ goto journal_error; ++ ++@@ -2193,18 +2554,24 @@ static int ext4_dx_add_entry(handle_t *h ++ frame->entries = entries = entries2; ++ swap(frame->bh, bh2); ++ } ++- dx_insert_block(frames + 0, hash2, newblock); ++- dxtrace(dx_show_index("node", frames[1].entries)); +++ dx_insert_block((frame - 1), hash2, newblock); +++ dxtrace(dx_show_index("node", frame->entries)); ++ dxtrace(dx_show_index("node", ++ ((struct dx_node *) bh2->b_data)->entries)); ++ err = ext4_handle_dirty_dx_node(handle, dir, bh2); ++ if (err) ++ goto journal_error; ++ brelse (bh2); +++ ext4_handle_dirty_metadata(handle, inode, +++ (frame - 1)->bh); +++ if (restart) { +++ ext4_handle_dirty_metadata(handle, inode, +++ frame->bh); +++ goto cleanup; +++ } ++ } else { ++ struct dx_root_info * info; ++- dxtrace(printk(KERN_DEBUG ++- "Creating second level index...\n")); +++ ++ memcpy((char *) entries2, (char *) entries, ++ icount * sizeof(struct dx_entry)); ++ dx_set_limit(entries2, dx_node_limit(dir)); ++@@ -2214,35 +2581,63 @@ static int ext4_dx_add_entry(handle_t *h ++ dx_set_block(entries + 0, newblock); ++ info = dx_get_dx_info((struct ext4_dir_entry_2*) ++ frames[0].bh->b_data); ++- info->indirect_levels = 1; +++ info->indirect_levels += 1; +++ dxtrace(printk(KERN_DEBUG +++ "Creating %d level index...\n", +++ info->indirect_levels)); +++ ext4_handle_dirty_metadata(handle, inode, frame->bh); +++ ext4_handle_dirty_metadata(handle, inode, bh2); +++ brelse(bh2); +++ restart = 1; +++ goto cleanup; +++ } +++ } else if (!ext4_htree_dx_locked(lck)) { +++ struct ext4_dir_lock_data *ld = ext4_htree_lock_data(lck); ++ ++- /* Add new access path frame */ ++- frame = frames + 1; ++- frame->at = at = at - entries + entries2; ++- frame->entries = entries = entries2; ++- frame->bh = bh2; ++- err = ext4_journal_get_write_access(handle, ++- frame->bh); ++- if (err) ++- goto journal_error; +++ /* not well protected, require DX lock */ +++ ext4_htree_dx_need_lock(lck); +++ at = frame > frames ? (frame - 1)->at : NULL; +++ +++ /* NB: no risk of deadlock because it's just a try. +++ * +++ * NB: we check ld_count for twice, the first time before +++ * having DX lock, the second time after holding DX lock. +++ * +++ * NB: We never free blocks for directory so far, which +++ * means value returned by dx_get_count() should equal to +++ * ld->ld_count if nobody split any DE-block under @at, +++ * and ld->ld_at still points to valid dx_entry. */ +++ if ((ld->ld_count != dx_get_count(entries)) || +++ !ext4_htree_dx_lock_try(lck, at) || +++ (ld->ld_count != dx_get_count(entries))) { +++ restart = 1; +++ goto cleanup; ++ } ++- err = ext4_handle_dirty_dx_node(handle, dir, frames[0].bh); +++ /* OK, I've got DX lock and nothing changed */ +++ frame->at = ld->ld_at; ++ if (err) { ++ ext4_std_error(inode->i_sb, err); ++ goto cleanup; ++ } ++ } ++- de = do_split(handle, dir, &bh, frame, &hinfo, &err); +++ de = do_split(handle, dir, &bh, frames, frame, &hinfo, lck, &err); ++ if (!de) ++ goto cleanup; +++ ++ err = add_dirent_to_buf(handle, dentry, inode, de, bh); ++ goto cleanup; ++ ++ journal_error: ++ ext4_std_error(dir->i_sb, err); ++ cleanup: +++ ext4_htree_dx_unlock(lck); +++ ext4_htree_de_unlock(lck); ++ brelse(bh); ++ dx_release(frames); +++ /* @restart is true means htree-path has been changed, we need to +++ * repeat dx_probe() to find out valid htree-path */ +++ if (restart && err == 0) +++ goto again; ++ return err; ++ } ++ ++@@ -2279,7 +2674,7 @@ int ext4_generic_delete_entry(handle_t * ++ blocksize); ++ else ++ de->inode = 0; ++- dir->i_version++; +++ inode_inc_iversion(dir); ++ return 0; ++ } ++ i += ext4_rec_len_from_disk(de->rec_len, blocksize); ++@@ -2364,7 +2759,7 @@ EXPORT_SYMBOL(ext4_dec_count); ++ static int ext4_add_nondir(handle_t *handle, ++ struct dentry *dentry, struct inode *inode) ++ { ++- int err = ext4_add_entry(handle, dentry, inode); +++ int err = ext4_add_entry(handle, dentry, inode, NULL); ++ if (!err) { ++ ext4_mark_inode_dirty(handle, inode); ++ unlock_new_inode(inode); ++@@ -2670,7 +3065,7 @@ retry: ++ goto out_clear_inode; ++ err = ext4_mark_inode_dirty(handle, inode); ++ if (!err) ++- err = ext4_add_entry(handle, dentry, inode); +++ err = ext4_add_entry(handle, dentry, inode, NULL); ++ if (err) { ++ out_clear_inode: ++ clear_nlink(inode); ++@@ -2936,7 +3331,7 @@ static int ext4_rmdir(struct inode *dir, ++ dquot_initialize(dentry->d_inode); ++ ++ retval = -ENOENT; ++- bh = ext4_find_entry(dir, &dentry->d_name, &de, NULL); +++ bh = ext4_find_entry(dir, &dentry->d_name, &de, NULL, NULL); ++ if (!bh) ++ goto end_rmdir; ++ ++@@ -3003,7 +3398,7 @@ static int ext4_unlink(struct inode *dir ++ dquot_initialize(dentry->d_inode); ++ ++ retval = -ENOENT; ++- bh = ext4_find_entry(dir, &dentry->d_name, &de, NULL); +++ bh = ext4_find_entry(dir, &dentry->d_name, &de, NULL, NULL); ++ if (!bh) ++ goto end_unlink; ++ ++@@ -3182,7 +3577,7 @@ retry: ++ ext4_inc_count(handle, inode); ++ ihold(inode); ++ ++- err = ext4_add_entry(handle, dentry, inode); +++ err = ext4_add_entry(handle, dentry, inode, NULL); ++ if (!err) { ++ ext4_mark_inode_dirty(handle, inode); ++ /* this can happen only for tmpfile being ++@@ -3264,7 +3659,7 @@ static int ext4_rename(struct inode *old ++ if (IS_DIRSYNC(old_dir) || IS_DIRSYNC(new_dir)) ++ ext4_handle_sync(handle); ++ ++- old_bh = ext4_find_entry(old_dir, &old_dentry->d_name, &old_de, NULL); +++ old_bh = ext4_find_entry(old_dir, &old_dentry->d_name, &old_de, NULL, NULL); ++ /* ++ * Check for inode number is _not_ due to possible IO errors. ++ * We might rmdir the source, keep it as pwd of some process ++@@ -3278,7 +3673,7 @@ static int ext4_rename(struct inode *old ++ ++ new_inode = new_dentry->d_inode; ++ new_bh = ext4_find_entry(new_dir, &new_dentry->d_name, ++- &new_de, &new_inlined); +++ &new_de, &new_inlined, NULL); ++ if (new_bh) { ++ if (!new_inode) { ++ brelse(new_bh); ++@@ -3309,7 +3704,7 @@ static int ext4_rename(struct inode *old ++ goto end_rename; ++ } ++ if (!new_bh) { ++- retval = ext4_add_entry(handle, new_dentry, old_inode); +++ retval = ext4_add_entry(handle, new_dentry, old_inode, NULL); ++ if (retval) ++ goto end_rename; ++ } else { ++@@ -3361,7 +3756,7 @@ static int ext4_rename(struct inode *old ++ struct ext4_dir_entry_2 *old_de2; ++ ++ old_bh2 = ext4_find_entry(old_dir, &old_dentry->d_name, ++- &old_de2, NULL); +++ &old_de2, NULL, NULL); ++ if (old_bh2) { ++ retval = ext4_delete_entry(handle, old_dir, ++ old_de2, old_bh2); ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/inode.c ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/inode.c +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/inode.c ++@@ -4091,7 +4091,7 @@ struct inode *ext4_iget(struct super_blo ++ if (EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_64BIT)) ++ ei->i_file_acl |= ++ ((__u64)le16_to_cpu(raw_inode->i_file_acl_high)) << 32; ++- inode->i_size = ext4_isize(raw_inode); +++ inode->i_size = ext4_isize(sb, raw_inode); ++ ei->i_disksize = inode->i_size; ++ #ifdef CONFIG_QUOTA ++ ei->i_reserved_quota = 0; ++@@ -4329,7 +4329,7 @@ static int ext4_do_update_inode(handle_t ++ raw_inode->i_file_acl_high = ++ cpu_to_le16(ei->i_file_acl >> 32); ++ raw_inode->i_file_acl_lo = cpu_to_le32(ei->i_file_acl); ++- if (ei->i_disksize != ext4_isize(raw_inode)) { +++ if (ei->i_disksize != ext4_isize(inode->i_sb, raw_inode)) { ++ ext4_isize_set(raw_inode, ei->i_disksize); ++ need_datasync = 1; ++ } ++Index: linux-3.11.1-200.fc19.x86_64/fs/ext4/Makefile ++=================================================================== ++--- linux-3.11.1-200.fc19.x86_64.orig/fs/ext4/Makefile +++++ linux-3.11.1-200.fc19.x86_64/fs/ext4/Makefile ++@@ -8,7 +8,7 @@ ext4-y := balloc.o bitmap.o dir.o file.o ++ ioctl.o namei.o super.o symlink.o hash.o resize.o extents.o \ ++ ext4_jbd2.o migrate.o mballoc.o block_validity.o move_extent.o \ ++ mmp.o indirect.o extents_status.o xattr.o xattr_user.o \ ++- xattr_trusted.o inline.o +++ xattr_trusted.o inline.o htree_lock.o ++ ++ ext4-$(CONFIG_EXT4_FS_POSIX_ACL) += acl.o ++ ext4-$(CONFIG_EXT4_FS_SECURITY) += xattr_security.o +diff --git a/ldiskfs/kernel_patches/series/ldiskfs-3.x-fc19.series b/ldiskfs/kernel_patches/series/ldiskfs-3.x-fc19.series +new file mode 100644 +index 0000000..5d0d7b0 +--- /dev/null ++++ b/ldiskfs/kernel_patches/series/ldiskfs-3.x-fc19.series +@@ -0,0 +1,22 @@ ++fc19/ext4-inode-version.patch ++fc19/ext4-lookup-dotdot.patch ++rhel6.3/ext4-print-inum-in-htree-warning.patch ++fc19/ext4-prealloc.patch ++fc19/ext4-mballoc-extra-checks.patch ++fc19/ext4-misc.patch ++fc19/ext4-force_over_128tb.patch ++fc19/ext4-pdir-fix.patch ++fc19/ext4-osd-iop-common.patch ++fc19/ext4-osd-iam-exports.patch ++rhel6.3/ext4-hash-indexed-dir-dotdot-update.patch ++fc19/ext4-kill-dx_root.patch ++fc19/ext4-fiemap.patch ++fc19/ext4-mballoc-pa_free-mismatch.patch ++fc19/ext4_data_in_dirent.patch ++fc19/ext4-large-eas.patch ++fc19/ext4-disable-mb-cache.patch ++fc19/ext4-nocmtime.patch ++fc19/ext4_pdirop.patch ++rhel6.3/ext4-not-discard-preallocation-umount.patch ++fc19/ext4-change-entry-avoid-conflict.patch ++fc19/ext4-max-dir-size.patch +-- +1.8.5.1 + diff --git a/sys-cluster/lustre/files/0003-LU-3079-kernel-3.9-hlist_for_each_entry-uses-3-args.patch b/sys-cluster/lustre/files/0003-LU-3079-kernel-3.9-hlist_for_each_entry-uses-3-args.patch deleted file mode 100644 index 0ed0061..0000000 --- a/sys-cluster/lustre/files/0003-LU-3079-kernel-3.9-hlist_for_each_entry-uses-3-args.patch +++ /dev/null @@ -1,117 +0,0 @@ -From 8eb9c28b3ac9bcc05f00259301c9bd92e8451a55 Mon Sep 17 00:00:00 2001 -From: James Simmons -Date: Wed, 15 May 2013 09:18:19 -0400 -Subject: [PATCH 3/4] LU-3079 kernel: 3.9 hlist_for_each_entry uses 3 args - -In 3.9 hlist_for_each_entry use 3 args since commit - -commit b67bfe0d42cac56c512dd5da4b1b347a23f4b70a -Author: Sasha Levin -Date: Wed Feb 27 17:06:00 2013 -0800 - - hlist: drop the node parameter from iterators - -Signed-off-by: Alexey Shvetsov -Signed-off-by: James Simmons -Change-Id: I8f38d562607de21771b03575e37cc0498867a570 ---- - libcfs/include/libcfs/list.h | 10 ++++++++++ - lustre/autoconf/lustre-core.m4 | 27 +++++++++++++++++++++++++++ - lustre/include/linux/lustre_compat25.h | 5 +++++ - 3 files changed, 42 insertions(+) - -diff --git a/libcfs/include/libcfs/list.h b/libcfs/include/libcfs/list.h -index be06e74..b747804 100644 ---- a/libcfs/include/libcfs/list.h -+++ b/libcfs/include/libcfs/list.h -@@ -91,14 +91,24 @@ typedef struct hlist_node cfs_hlist_node_t; - #define cfs_hlist_for_each(pos, head) hlist_for_each(pos, head) - #define cfs_hlist_for_each_safe(pos, n, head) \ - hlist_for_each_safe(pos, n, head) -+#ifdef HAVE_HLIST_FOR_EACH_3ARG -+#define cfs_hlist_for_each_entry(tpos, pos, head, member) \ -+ pos = NULL; hlist_for_each_entry(tpos, head, member) -+#else - #define cfs_hlist_for_each_entry(tpos, pos, head, member) \ - hlist_for_each_entry(tpos, pos, head, member) -+#endif - #define cfs_hlist_for_each_entry_continue(tpos, pos, member) \ - hlist_for_each_entry_continue(tpos, pos, member) - #define cfs_hlist_for_each_entry_from(tpos, pos, member) \ - hlist_for_each_entry_from(tpos, pos, member) -+#ifdef HAVE_HLIST_FOR_EACH_3ARG -+#define cfs_hlist_for_each_entry_safe(tpos, pos, n, head, member) \ -+ pos = NULL; hlist_for_each_entry_safe(tpos, n, head, member) -+#else - #define cfs_hlist_for_each_entry_safe(tpos, pos, n, head, member) \ - hlist_for_each_entry_safe(tpos, pos, n, head, member) -+#endif - - #define CFS_HLIST_HEAD_INIT HLIST_HEAD_INIT - #define CFS_HLIST_HEAD(n) HLIST_HEAD(n) -diff --git a/lustre/autoconf/lustre-core.m4 b/lustre/autoconf/lustre-core.m4 -index 9ac1194..2cb4edd 100644 ---- a/lustre/autoconf/lustre-core.m4 -+++ b/lustre/autoconf/lustre-core.m4 -@@ -1604,6 +1604,29 @@ LB_LINUX_TRY_COMPILE([ - ]) - - # -+# 3.9 uses hlist_for_each_entry with 3 args -+# b67bfe0d42cac56c512dd5da4b1b347a23f4b70a -+# -+AC_DEFUN([LC_HAVE_HLIST_FOR_EACH_3ARG], -+[AC_MSG_CHECKING([if hlist_for_each_entry has 3 args]) -+LB_LINUX_TRY_COMPILE([ -+ #include -+ #include -+],[ -+ struct inode *inode; -+ struct dentry *dentry; -+ hlist_for_each_entry(dentry, &inode->i_dentry, d_alias) { -+ continue; -+ } -+],[ -+ AC_DEFINE(HAVE_HLIST_FOR_EACH_3ARG, 1, [3 args]) -+ AC_MSG_RESULT([yes]) -+],[ -+ AC_MSG_RESULT([no]) -+]) -+]) -+ -+# - # LC_PROG_LINUX - # - # Lustre linux kernel checks -@@ -1740,6 +1763,10 @@ AC_DEFUN([LC_PROG_LINUX], - - # 3.7 - LC_HAVE_POSIXACL_USER_NS -+ -+ # 3.9 -+ LC_HAVE_HLIST_FOR_EACH_3ARG -+ - # - if test x$enable_server = xyes ; then - AC_DEFINE(HAVE_SERVER_SUPPORT, 1, [support server]) -diff --git a/lustre/include/linux/lustre_compat25.h b/lustre/include/linux/lustre_compat25.h -index 4e29e69..efb3fd1 100644 ---- a/lustre/include/linux/lustre_compat25.h -+++ b/lustre/include/linux/lustre_compat25.h -@@ -421,8 +421,13 @@ static inline int ll_quota_off(struct super_block *sb, int off, int remount) - #define ll_d_hlist_empty(list) hlist_empty(list) - #define ll_d_hlist_entry(ptr, type, name) hlist_entry(ptr.first, type, name) - #define ll_d_hlist_for_each(tmp, i_dentry) hlist_for_each(tmp, i_dentry) -+#ifdef HAVE_HLIST_FOR_EACH_3ARG -+#define ll_d_hlist_for_each_entry(dentry, p, i_dentry, alias) \ -+ p = NULL; hlist_for_each_entry(dentry, i_dentry, alias) -+#else - #define ll_d_hlist_for_each_entry(dentry, p, i_dentry, alias) \ - hlist_for_each_entry(dentry, p, i_dentry, alias) -+#endif - #else - #define ll_d_hlist_node list_head - #define ll_d_hlist_empty(list) list_empty(list) --- -1.8.2.1 - diff --git a/sys-cluster/lustre/files/0003-LU-3974-llite-dentry-d_compare-changes-in-3.11.patch b/sys-cluster/lustre/files/0003-LU-3974-llite-dentry-d_compare-changes-in-3.11.patch new file mode 100644 index 0000000..72698fc --- /dev/null +++ b/sys-cluster/lustre/files/0003-LU-3974-llite-dentry-d_compare-changes-in-3.11.patch @@ -0,0 +1,132 @@ +From 18cfd561fae3b2eac663b51f8e5147b59c711af7 Mon Sep 17 00:00:00 2001 +From: James Simmons +Date: Wed, 11 Dec 2013 10:29:41 -0500 +Subject: [PATCH 3/5] LU-3974 llite: dentry d_compare changes in 3.11 + +In the linux 3.11 kernel the d_compare function has +removed passing in any struct inode arguments. This +patch provides support to handle this case. + +Signed-off-by: James Simmons +Change-Id: I363057e4d0a119ad43a9907ec26e7e0079f7c305 +--- + lustre/autoconf/lustre-core.m4 | 19 +++++++++++++++++++ + lustre/llite/dcache.c | 27 +++++++++++---------------- + lustre/llite/llite_internal.h | 7 ------- + 3 files changed, 30 insertions(+), 23 deletions(-) + +diff --git a/lustre/autoconf/lustre-core.m4 b/lustre/autoconf/lustre-core.m4 +index f47bc5f..7f9bb45 100644 +--- a/lustre/autoconf/lustre-core.m4 ++++ b/lustre/autoconf/lustre-core.m4 +@@ -1287,6 +1287,24 @@ LB_LINUX_TRY_COMPILE([ + ]) + + # ++# 3.11 dentry_operations.d_compare() taken 5 arguments. ++# ++AC_DEFUN([LC_D_COMPARE_5ARGS], ++[AC_MSG_CHECKING([if d_compare taken 5 arguments]) ++LB_LINUX_TRY_COMPILE([ ++ #include ++],[ ++ ((struct dentry_operations*)0)->d_compare(NULL,NULL,0,NULL,NULL); ++],[ ++ AC_DEFINE(HAVE_D_COMPARE_5ARGS, 1, ++ [d_compare need 5 arguments]) ++ AC_MSG_RESULT([yes]) ++],[ ++ AC_MSG_RESULT([no]) ++]) ++]) ++ ++# + # 3.11 need to access d_count to get dentry reference count + # + AC_DEFUN([LC_HAVE_DCOUNT], +@@ -1405,6 +1423,7 @@ AC_DEFUN([LC_PROG_LINUX], + LC_BLKDEV_RELEASE_RETURN_INT + + # 3.11 ++ LC_D_COMPARE_5ARGS + LC_HAVE_DCOUNT + + # +diff --git a/lustre/llite/dcache.c b/lustre/llite/dcache.c +index 6fca4cb..8a8c100 100644 +--- a/lustre/llite/dcache.c ++++ b/lustre/llite/dcache.c +@@ -89,11 +89,19 @@ static void ll_release(struct dentry *de) + int ll_dcompare(const struct dentry *parent, const struct inode *pinode, + const struct dentry *dentry, const struct inode *inode, + unsigned int len, const char *str, const struct qstr *name) ++#elif defined(HAVE_D_COMPARE_5ARGS) ++int ll_dcompare(const struct dentry *parent, const struct dentry *dentry, ++ unsigned int len, const char *str, const struct qstr *name) + #else + int ll_dcompare(struct dentry *parent, struct qstr *d_name, struct qstr *name) + #endif + { +-#ifdef HAVE_D_COMPARE_7ARGS ++#if !defined(HAVE_D_COMPARE_7ARGS) && !defined(HAVE_D_COMPARE_5ARGS) ++ /* XXX: (ugh !) d_name must be in-dentry structure */ ++ struct dentry *dentry = container_of(d_name, struct dentry, d_name); ++ unsigned int len = d_name->len; ++ const char *str = d_name->name; ++#endif + ENTRY; + + if (len != name->len) +@@ -101,19 +109,6 @@ int ll_dcompare(struct dentry *parent, struct qstr *d_name, struct qstr *name) + + if (memcmp(str, name->name, len)) + RETURN(1); +-#else +- struct dentry *dentry; +- ENTRY; +- +- if (d_name->len != name->len) +- RETURN(1); +- +- if (memcmp(d_name->name, name->name, name->len)) +- RETURN(1); +- +- /* XXX: d_name must be in-dentry structure */ +- dentry = container_of(d_name, struct dentry, d_name); /* ugh */ +-#endif + + CDEBUG(D_DENTRY, "found name %.*s(%p) flags %#x refc %d\n", + name->len, name->name, dentry, dentry->d_flags, +@@ -124,9 +119,9 @@ int ll_dcompare(struct dentry *parent, struct qstr *d_name, struct qstr *name) + RETURN(0); + + if (d_lustre_invalid(dentry)) +- RETURN(1); ++ RETURN(1); + +- RETURN(0); ++ RETURN(0); + } + + static inline int return_if_equal(struct ldlm_lock *lock, void *data) +diff --git a/lustre/llite/llite_internal.h b/lustre/llite/llite_internal.h +index daeac51..9da81ca 100644 +--- a/lustre/llite/llite_internal.h ++++ b/lustre/llite/llite_internal.h +@@ -857,13 +857,6 @@ void ll_intent_release(struct lookup_intent *); + void ll_invalidate_aliases(struct inode *); + void ll_frob_intent(struct lookup_intent **itp, struct lookup_intent *deft); + void ll_lookup_finish_locks(struct lookup_intent *it, struct dentry *dentry); +-#ifdef HAVE_D_COMPARE_7ARGS +-int ll_dcompare(const struct dentry *parent, const struct inode *pinode, +- const struct dentry *dentry, const struct inode *inode, +- unsigned int len, const char *str, const struct qstr *d_name); +-#else +-int ll_dcompare(struct dentry *parent, struct qstr *d_name, struct qstr *name); +-#endif + int ll_revalidate_it_finish(struct ptlrpc_request *request, + struct lookup_intent *it, struct dentry *de); + +-- +1.8.5.1 + diff --git a/sys-cluster/lustre/files/0004-LU-3079-kernel-f_vfsmnt-replaced-by-f_path.mnt.patch b/sys-cluster/lustre/files/0004-LU-3079-kernel-f_vfsmnt-replaced-by-f_path.mnt.patch deleted file mode 100644 index 2dd2223..0000000 --- a/sys-cluster/lustre/files/0004-LU-3079-kernel-f_vfsmnt-replaced-by-f_path.mnt.patch +++ /dev/null @@ -1,82 +0,0 @@ -From 58a8bf674096caabc6edc0c598f16a0b04c05ebf Mon Sep 17 00:00:00 2001 -From: Alexey Shvetsov -Date: Sun, 31 Mar 2013 20:13:21 +0400 -Subject: [PATCH 4/4] LU-3079 kernel: f_vfsmnt replaced by f_path.mnt - -In linux 3.9 f_vfsmnt was killed and replaced - -commit 182be684784334598eee1d90274e7f7aa0063616 -Author: Al Viro -Date: Thu Jan 24 02:21:54 2013 -0500 - - kill f_vfsmnt - - very few users left... - -Signed-off-by: Alexey Shvetsov -Change-Id: I02f595f0b7bbe804c9695d6da2106deb2d17e0ee ---- - lustre/autoconf/lustre-core.m4 | 23 +++++++++++++++++++++++ - lustre/llite/dir.c | 4 ++++ - 2 files changed, 27 insertions(+) - -diff --git a/lustre/autoconf/lustre-core.m4 b/lustre/autoconf/lustre-core.m4 -index 2cb4edd..ba69c41 100644 ---- a/lustre/autoconf/lustre-core.m4 -+++ b/lustre/autoconf/lustre-core.m4 -@@ -1627,6 +1627,28 @@ LB_LINUX_TRY_COMPILE([ - ]) - - # -+# 3.9 killed f_vfsmnt by -+# 182be684784334598eee1d90274e7f7aa0063616 -+# replacement is f_path.mnt -+# -+AC_DEFUN([LC_HAVE_F_PATH_MNT], -+[AC_MSG_CHECKING([if struct file has f_path.mnt]) -+LB_LINUX_TRY_COMPILE([ -+ #include -+],[ -+ struct file *fp = NULL; -+ struct path path; -+ -+ path.mnt = fp->f_path.mnt; -+],[ -+ AC_DEFINE(HAVE_F_PATH_MNT,1,[yes]) -+ AC_MSG_RESULT([yes]) -+],[ -+ AC_MSG_RESULT([no]) -+]) -+]) -+ -+# - # LC_PROG_LINUX - # - # Lustre linux kernel checks -@@ -1766,6 +1788,7 @@ AC_DEFUN([LC_PROG_LINUX], - - # 3.9 - LC_HAVE_HLIST_FOR_EACH_3ARG -+ LC_HAVE_F_PATH_MNT - - # - if test x$enable_server = xyes ; then -diff --git a/lustre/llite/dir.c b/lustre/llite/dir.c -index dd7f7df..e625200 100644 ---- a/lustre/llite/dir.c -+++ b/lustre/llite/dir.c -@@ -629,7 +629,11 @@ static int ll_readdir(struct file *filp, void *cookie, filldir_t filldir) - } - filp->f_version = inode->i_version; - #ifdef HAVE_TOUCH_ATIME_1ARG -+#ifdef HAVE_F_PATH_MNT -+ path.mnt = filp->f_path.mnt; -+#else - path.mnt = filp->f_vfsmnt; -+#endif - path.dentry = filp->f_dentry; - touch_atime(&path); - #else --- -1.8.2.1 - diff --git a/sys-cluster/lustre/files/0004-LU-3974-llite-use-new-struct-dir_context.patch b/sys-cluster/lustre/files/0004-LU-3974-llite-use-new-struct-dir_context.patch new file mode 100644 index 0000000..b9714df --- /dev/null +++ b/sys-cluster/lustre/files/0004-LU-3974-llite-use-new-struct-dir_context.patch @@ -0,0 +1,276 @@ +From 82f692de87cb6c7db8f050b3201d23f4852a404c Mon Sep 17 00:00:00 2001 +From: James Simmons +Date: Mon, 2 Dec 2013 12:05:14 -0500 +Subject: [PATCH 4/5] LU-3974 llite: use new struct dir_context + +The readdir and nfs code over time has added more +parameters to be passed to be processed. For the 3.11 +kernel a new struct dir_context was introduced to +minimize the impact of future expansion. This patch +addresses this change. + +Signed-off-by: James Simmons +Change-Id: Ib42bf8cb06635a2a64e63b294d79e66ac82a1a5b +--- + lustre/autoconf/lustre-core.m4 | 20 ++++++++++ + lustre/llite/dir.c | 86 +++++++++++++++++++++++++++++------------- + lustre/llite/llite_internal.h | 7 ++++ + lustre/llite/llite_nfs.c | 33 ++++++++++------ + 4 files changed, 107 insertions(+), 39 deletions(-) + +diff --git a/lustre/autoconf/lustre-core.m4 b/lustre/autoconf/lustre-core.m4 +index 7f9bb45..3ac55d6 100644 +--- a/lustre/autoconf/lustre-core.m4 ++++ b/lustre/autoconf/lustre-core.m4 +@@ -1287,6 +1287,25 @@ LB_LINUX_TRY_COMPILE([ + ]) + + # ++# 3.11 readdir now takes the new struct dir_context ++# ++AC_DEFUN([LC_HAVE_DIR_CONTEXT], ++[AC_MSG_CHECKING([if dir_context exist]) ++LB_LINUX_TRY_COMPILE([ ++ #include ++],[ ++ struct dir_context ctx; ++ ++ ctx.pos = 0; ++],[ ++ AC_DEFINE(HAVE_DIR_CONTEXT, 1, [dir_context exist]) ++ AC_MSG_RESULT([yes]) ++],[ ++ AC_MSG_RESULT([no]) ++]) ++]) ++ ++# + # 3.11 dentry_operations.d_compare() taken 5 arguments. + # + AC_DEFUN([LC_D_COMPARE_5ARGS], +@@ -1423,6 +1442,7 @@ AC_DEFUN([LC_PROG_LINUX], + LC_BLKDEV_RELEASE_RETURN_INT + + # 3.11 ++ LC_HAVE_DIR_CONTEXT + LC_D_COMPARE_5ARGS + LC_HAVE_DCOUNT + +diff --git a/lustre/llite/dir.c b/lustre/llite/dir.c +index 2021897..e2546cc 100644 +--- a/lustre/llite/dir.c ++++ b/lustre/llite/dir.c +@@ -477,19 +477,25 @@ fail: + goto out_unlock; + } + ++#ifdef HAVE_DIR_CONTEXT ++int ll_dir_read(struct inode *inode, struct dir_context *ctx) ++{ ++ __u64 pos = ctx->pos; ++#else + int ll_dir_read(struct inode *inode, __u64 *_pos, void *cookie, + filldir_t filldir) + { +- struct ll_inode_info *info = ll_i2info(inode); +- struct ll_sb_info *sbi = ll_i2sbi(inode); +- __u64 pos = *_pos; +- int api32 = ll_need_32bit_api(sbi); +- int hash64 = sbi->ll_flags & LL_SBI_64BIT_HASH; +- struct page *page; +- struct ll_dir_chain chain; +- int done = 0; +- int rc = 0; +- ENTRY; ++ __u64 pos = *_pos; ++#endif ++ struct ll_inode_info *info = ll_i2info(inode); ++ struct ll_sb_info *sbi = ll_i2sbi(inode); ++ int api32 = ll_need_32bit_api(sbi); ++ int hash64 = sbi->ll_flags & LL_SBI_64BIT_HASH; ++ struct page *page; ++ struct ll_dir_chain chain; ++ int done = 0; ++ int rc = 0; ++ ENTRY; + + ll_dir_chain_init(&chain); + +@@ -542,12 +548,18 @@ int ll_dir_read(struct inode *inode, __u64 *_pos, void *cookie, + fid_le_to_cpu(&fid, &ent->lde_fid); + ino = cl_fid_build_ino(&fid, api32); + type = ll_dirent_type_get(ent); +- /* For 'll_nfs_get_name_filldir()', it will try +- * to access the 'ent' through its 'lde_name', +- * so the parameter 'name' for 'filldir()' must +- * be part of the 'ent'. */ +- done = filldir(cookie, ent->lde_name, namelen, +- lhash, ino, type); ++#ifdef HAVE_DIR_CONTEXT ++ ctx->pos = lhash; ++ /* For 'll_nfs_get_name_filldir()', it will try ++ * to access the 'ent' through its 'lde_name', ++ * so the parameter 'name' for 'filldir()' must ++ * be part of the 'ent'. */ ++ done = !dir_emit(ctx, ent->lde_name, ++ namelen, ino, type); ++#else ++ done = filldir(cookie, ent->lde_name, namelen, ++ lhash, ino, type); ++#endif + } + next = le64_to_cpu(dp->ldp_hash_end); + if (!done) { +@@ -588,12 +600,20 @@ int ll_dir_read(struct inode *inode, __u64 *_pos, void *cookie, + } + } + ++#ifdef HAVE_DIR_CONTEXT ++ ctx->pos = pos; ++#else + *_pos = pos; ++#endif + ll_dir_chain_fini(&chain); + RETURN(rc); + } + ++#ifdef HAVE_DIR_CONTEXT ++static int ll_iterate(struct file *filp, struct dir_context *ctx) ++#else + static int ll_readdir(struct file *filp, void *cookie, filldir_t filldir) ++#endif + { + struct inode *inode = filp->f_dentry->d_inode; + struct ll_file_data *lfd = LUSTRE_FPRIVATE(filp); +@@ -622,20 +642,28 @@ static int ll_readdir(struct file *filp, void *cookie, filldir_t filldir) + */ + GOTO(out, rc = 0); + ++#ifdef HAVE_DIR_CONTEXT ++ ctx->pos = pos; ++ rc = ll_dir_read(inode, ctx); ++ pos = ctx->pos; ++#else + rc = ll_dir_read(inode, &pos, cookie, filldir); ++#endif + if (lfd != NULL) + lfd->lfd_pos = pos; +- if (pos == MDS_DIR_END_OFF) { +- if (api32) +- filp->f_pos = LL_DIR_END_OFF_32BIT; +- else +- filp->f_pos = LL_DIR_END_OFF; +- } else { +- if (api32 && hash64) +- filp->f_pos = pos >> 32; +- else +- filp->f_pos = pos; ++ if (pos == MDS_DIR_END_OFF) { ++ if (api32) ++ pos = LL_DIR_END_OFF_32BIT; ++ else ++ pos = LL_DIR_END_OFF; ++ } else if (api32 && hash64) { ++ pos >>= 32; + } ++#ifdef HAVE_DIR_CONTEXT ++ ctx->pos = pos; ++#else ++ filp->f_pos = pos; ++#endif + filp->f_version = inode->i_version; + #ifdef HAVE_TOUCH_ATIME_1ARG + #ifdef HAVE_F_PATH_MNT +@@ -2020,7 +2048,11 @@ struct file_operations ll_dir_operations = { + .open = ll_dir_open, + .release = ll_dir_release, + .read = generic_read_dir, +- .readdir = ll_readdir, ++#ifdef HAVE_DIR_CONTEXT ++ .iterate = ll_iterate, ++#else ++ .readdir = ll_readdir, ++#endif + .unlocked_ioctl = ll_dir_ioctl, + .fsync = ll_fsync, + }; +diff --git a/lustre/llite/llite_internal.h b/lustre/llite/llite_internal.h +index 9da81ca..283e106 100644 +--- a/lustre/llite/llite_internal.h ++++ b/lustre/llite/llite_internal.h +@@ -91,6 +91,9 @@ extern struct file_operations ll_pgcache_seq_fops; + #define REMOTE_PERM_HASHSIZE 16 + + struct ll_getname_data { ++#ifdef HAVE_DIR_CONTEXT ++ struct dir_context ctx; ++#endif + char *lgd_name; /* points to a buffer with NAME_MAX+1 size */ + struct lu_fid lgd_fid; /* target fid we are looking for */ + int lgd_found; /* inode matched? */ +@@ -722,8 +725,12 @@ extern struct file_operations ll_dir_operations; + extern struct inode_operations ll_dir_inode_operations; + struct page *ll_get_dir_page(struct inode *dir, __u64 hash, + struct ll_dir_chain *chain); ++#ifdef HAVE_DIR_CONTEXT ++int ll_dir_read(struct inode *inode, struct dir_context *ctx); ++#else + int ll_dir_read(struct inode *inode, __u64 *_pos, void *cookie, + filldir_t filldir); ++#endif + + int ll_get_mdt_idx(struct inode *inode); + /* llite/namei.c */ +diff --git a/lustre/llite/llite_nfs.c b/lustre/llite/llite_nfs.c +index 2926caf..25b16e8 100644 +--- a/lustre/llite/llite_nfs.c ++++ b/lustre/llite/llite_nfs.c +@@ -235,24 +235,33 @@ static int ll_nfs_get_name_filldir(void *cookie, const char *name, int namelen, + static int ll_get_name(struct dentry *dentry, char *name, + struct dentry *child) + { +- struct inode *dir = dentry->d_inode; +- struct ll_getname_data lgd; ++ struct inode *dir = dentry->d_inode; ++ struct ll_getname_data lgd = { ++ .lgd_name = name, ++ .lgd_fid = ll_i2info(child->d_inode)->lli_fid, ++#ifdef HAVE_DIR_CONTEXT ++ .ctx.actor = ll_nfs_get_name_filldir, ++ }; ++#else ++ .lgd_found = 0, ++ }; + __u64 offset = 0; +- int rc; +- ENTRY; +- +- if (!dir || !S_ISDIR(dir->i_mode)) +- GOTO(out, rc = -ENOTDIR); ++#endif ++ int rc; ++ ENTRY; + +- if (!dir->i_fop) +- GOTO(out, rc = -EINVAL); ++ if (!dir || !S_ISDIR(dir->i_mode)) ++ GOTO(out, rc = -ENOTDIR); + +- lgd.lgd_name = name; +- lgd.lgd_fid = ll_i2info(child->d_inode)->lli_fid; +- lgd.lgd_found = 0; ++ if (!dir->i_fop) ++ GOTO(out, rc = -EINVAL); + + mutex_lock(&dir->i_mutex); ++#ifdef HAVE_DIR_CONTEXT ++ rc = ll_dir_read(dir, &lgd.ctx); ++#else + rc = ll_dir_read(dir, &offset, &lgd, ll_nfs_get_name_filldir); ++#endif + mutex_unlock(&dir->i_mutex); + if (!rc && !lgd.lgd_found) + rc = -ENOENT; +-- +1.8.5.1 + diff --git a/sys-cluster/lustre/files/0005-LU-3974-llite-invalidatepage-api-changed.patch b/sys-cluster/lustre/files/0005-LU-3974-llite-invalidatepage-api-changed.patch new file mode 100644 index 0000000..4e13bfe --- /dev/null +++ b/sys-cluster/lustre/files/0005-LU-3974-llite-invalidatepage-api-changed.patch @@ -0,0 +1,134 @@ +From 2725bc0f3bc5fa7706b9a475ccb0c191f21ca884 Mon Sep 17 00:00:00 2001 +From: James Simmons +Date: Tue, 24 Sep 2013 12:29:47 -0400 +Subject: [PATCH 5/5] LU-3974 llite: invalidatepage api changed + +Until recently invalidating pages from the buffer cache +was dependent only on the page passed in and the start +in the page to invalidate. Starting with the 3.11 kernel +you can also specify the length of the data in the page +to invalidate. This patch enables us to handle the new +case. + +Signed-off-by: James Simmons +Change-Id: Iedf458b20b2604bc3099d5ae38bf0ad07df83bd3 +--- + lustre/autoconf/lustre-core.m4 | 20 ++++++++++++++++++++ + lustre/include/linux/lustre_patchless_compat.h | 19 +++++++++++-------- + lustre/llite/rw26.c | 24 +++++++++++++++++------- + 3 files changed, 48 insertions(+), 15 deletions(-) + +diff --git a/lustre/autoconf/lustre-core.m4 b/lustre/autoconf/lustre-core.m4 +index 3ac55d6..ab94acd 100644 +--- a/lustre/autoconf/lustre-core.m4 ++++ b/lustre/autoconf/lustre-core.m4 +@@ -1287,6 +1287,25 @@ LB_LINUX_TRY_COMPILE([ + ]) + + # ++# 3.11 invalidatepage requires the length of the range to invalidate ++# ++AC_DEFUN([LC_INVALIDATE_RANGE], ++[AC_MSG_CHECKING([if address_space_operations.invalidatepage requires 3 arguments]) ++LB_LINUX_TRY_COMPILE([ ++ #include ++],[ ++ struct address_space_operations a_ops; ++ ++ a_ops.invalidatepage(NULL,0,0); ++],[ ++ AC_DEFINE(HAVE_INVALIDATE_RANGE, 1, [address_space_operations.invalidatepage needs 3 arguments]) ++ AC_MSG_RESULT([yes]) ++],[ ++ AC_MSG_RESULT([no]) ++]) ++]) ++ ++# + # 3.11 readdir now takes the new struct dir_context + # + AC_DEFUN([LC_HAVE_DIR_CONTEXT], +@@ -1442,6 +1461,7 @@ AC_DEFUN([LC_PROG_LINUX], + LC_BLKDEV_RELEASE_RETURN_INT + + # 3.11 ++ LC_INVALIDATE_RANGE + LC_HAVE_DIR_CONTEXT + LC_D_COMPARE_5ARGS + LC_HAVE_DCOUNT +diff --git a/lustre/include/linux/lustre_patchless_compat.h b/lustre/include/linux/lustre_patchless_compat.h +index 747bd4d..5b7bab6 100644 +--- a/lustre/include/linux/lustre_patchless_compat.h ++++ b/lustre/include/linux/lustre_patchless_compat.h +@@ -78,15 +78,18 @@ static inline void ll_delete_from_page_cache(struct page *page) + static inline void + truncate_complete_page(struct address_space *mapping, struct page *page) + { +- if (page->mapping != mapping) +- return; ++ if (page->mapping != mapping) ++ return; + +- if (PagePrivate(page)) +- page->mapping->a_ops->invalidatepage(page, 0); +- +- cancel_dirty_page(page, PAGE_SIZE); +- ClearPageMappedToDisk(page); +- ll_delete_from_page_cache(page); ++ if (PagePrivate(page)) ++#ifdef HAVE_INVALIDATE_RANGE ++ page->mapping->a_ops->invalidatepage(page, 0, PAGE_CACHE_SIZE); ++#else ++ page->mapping->a_ops->invalidatepage(page, 0); ++#endif ++ cancel_dirty_page(page, PAGE_SIZE); ++ ClearPageMappedToDisk(page); ++ ll_delete_from_page_cache(page); + } + #endif /* !HAVE_TRUNCATE_COMPLETE_PAGE */ + +diff --git a/lustre/llite/rw26.c b/lustre/llite/rw26.c +index 9365c74..bee7996 100644 +--- a/lustre/llite/rw26.c ++++ b/lustre/llite/rw26.c +@@ -76,7 +76,13 @@ + * aligned truncate). Lustre leaves partially truncated page in the cache, + * relying on struct inode::i_size to limit further accesses. + */ +-static void ll_invalidatepage(struct page *vmpage, unsigned long offset) ++static void ll_invalidatepage(struct page *vmpage, ++#ifdef HAVE_INVALIDATE_RANGE ++ unsigned int offset, unsigned int length ++#else ++ unsigned long offset ++#endif ++ ) + { + struct inode *inode; + struct lu_env *env; +@@ -88,12 +94,16 @@ static void ll_invalidatepage(struct page *vmpage, unsigned long offset) + LASSERT(PageLocked(vmpage)); + LASSERT(!PageWriteback(vmpage)); + +- /* +- * It is safe to not check anything in invalidatepage/releasepage +- * below because they are run with page locked and all our io is +- * happening with locked page too +- */ +- if (offset == 0) { ++ /* ++ * It is safe to not check anything in invalidatepage/releasepage ++ * below because they are run with page locked and all our io is ++ * happening with locked page too ++ */ ++#ifdef HAVE_INVALIDATE_RANGE ++ if (offset == 0 && length == PAGE_CACHE_SIZE) { ++#else ++ if (offset == 0) { ++#endif + env = cl_env_get(&refcheck); + if (!IS_ERR(env)) { + inode = vmpage->mapping->host; +-- +1.8.5.1 + diff --git a/sys-cluster/lustre/lustre-9999.ebuild b/sys-cluster/lustre/lustre-9999.ebuild index cb217db..09173cd 100644 --- a/sys-cluster/lustre/lustre-9999.ebuild +++ b/sys-cluster/lustre/lustre-9999.ebuild @@ -34,9 +34,10 @@ DEPEND="${RDEPEND} PATCHES=( "${FILESDIR}/0001-LU-2982-build-make-AC-check-for-linux-arch-sandbox-f.patch" - "${FILESDIR}/0002-LU-2686-kernel-Kernel-update-for-3.7.2-201.fc18.patch" - "${FILESDIR}/0003-LU-3079-kernel-3.9-hlist_for_each_entry-uses-3-args.patch" - "${FILESDIR}/0004-LU-3079-kernel-f_vfsmnt-replaced-by-f_path.mnt.patch" + "${FILESDIR}/0002-LU-3373-ldiskfs-ldiskfs-patches-for-3.11.1-fc19.patch" + "${FILESDIR}/0003-LU-3974-llite-dentry-d_compare-changes-in-3.11.patch" + "${FILESDIR}/0004-LU-3974-llite-use-new-struct-dir_context.patch" + "${FILESDIR}/0005-LU-3974-llite-invalidatepage-api-changed.patch" ) pkg_setup() { @@ -64,19 +65,6 @@ src_prepare() { eautomake eautoconf cd .. - einfo "Reconfiguring source in lustre-iokit" - cd lustre-iokit - eaclocal - eautomake - eautoconf - cd .. - einfo "Reconfiguring source in ldiskfs" - cd ldiskfs - eaclocal -I config - eautoheader - eautomake -W no-portability - eautoconf - cd .. } src_configure() {