]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
3.16-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 2 Oct 2014 23:52:43 +0000 (16:52 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 2 Oct 2014 23:52:43 +0000 (16:52 -0700)
added patches:
acpi-lpss-complete-pm-entries-for-lpss-power-domain.patch
acpi-rtc-fix-cmos-rtc-opregion-handler-accesses-to-wrong-addresses.patch
cgroup-check-cgroup-liveliness-before-unbreaking-kernfs.patch
iommu-arm-smmu-fix-programming-of-smmu_cbn_tcr-for-stage-1.patch
iommu-fsl-fix-warning-resulting-from-adding-pci-device-twice.patch
iommu-vt-d-check-return-value-of-acpi_bus_get_device.patch
nfs-can_coalesce_requests-must-enforce-contiguity.patch
nfs-change-nfs_page_group_lock-argument.patch
nfs-check-wait_on_bit_lock-err-in-page_group_lock.patch
nfs-clear_request_commit-while-holding-i_lock.patch
nfs-disallow-duplicate-pages-in-pgio-page-vectors.patch
nfs-don-t-sleep-with-inode-lock-in-lock_and_join_requests.patch
nfs-fix-error-handling-in-lock_and_join_requests.patch
nfs-fix-nonblocking-calls-to-nfs_page_group_lock.patch
nfs-merge-nfs_pgio_data-into-_header.patch
nfs-move-nfs_pgio_data-and-remove-nfs_rw_header.patch
nfs-remove-pgio_header-refcount-related-cleanup.patch
nfs-rename-members-of-nfs_pgio_data.patch
nfs-use-blocking-page_group_lock-in-add_request.patch
nfsd4-fix-corruption-of-nfsv4-read-data.patch
nfsd4-fix-rd_dircount-enforcement.patch
nfsv4-fix-another-bug-in-the-close-open_downgrade-code.patch
nfsv4-nfs4_state_manager-vs.-nfs_server_remove_lists.patch
pnfs-add-pnfs_put_lseg_async.patch
revert-acpi-battery-fix-wrong-value-of-capacity_now-reported-when-fully-charged.patch

26 files changed:
queue-3.16/acpi-lpss-complete-pm-entries-for-lpss-power-domain.patch [new file with mode: 0644]
queue-3.16/acpi-rtc-fix-cmos-rtc-opregion-handler-accesses-to-wrong-addresses.patch [new file with mode: 0644]
queue-3.16/cgroup-check-cgroup-liveliness-before-unbreaking-kernfs.patch [new file with mode: 0644]
queue-3.16/iommu-arm-smmu-fix-programming-of-smmu_cbn_tcr-for-stage-1.patch [new file with mode: 0644]
queue-3.16/iommu-fsl-fix-warning-resulting-from-adding-pci-device-twice.patch [new file with mode: 0644]
queue-3.16/iommu-vt-d-check-return-value-of-acpi_bus_get_device.patch [new file with mode: 0644]
queue-3.16/nfs-can_coalesce_requests-must-enforce-contiguity.patch [new file with mode: 0644]
queue-3.16/nfs-change-nfs_page_group_lock-argument.patch [new file with mode: 0644]
queue-3.16/nfs-check-wait_on_bit_lock-err-in-page_group_lock.patch [new file with mode: 0644]
queue-3.16/nfs-clear_request_commit-while-holding-i_lock.patch [new file with mode: 0644]
queue-3.16/nfs-disallow-duplicate-pages-in-pgio-page-vectors.patch [new file with mode: 0644]
queue-3.16/nfs-don-t-sleep-with-inode-lock-in-lock_and_join_requests.patch [new file with mode: 0644]
queue-3.16/nfs-fix-error-handling-in-lock_and_join_requests.patch [new file with mode: 0644]
queue-3.16/nfs-fix-nonblocking-calls-to-nfs_page_group_lock.patch [new file with mode: 0644]
queue-3.16/nfs-merge-nfs_pgio_data-into-_header.patch [new file with mode: 0644]
queue-3.16/nfs-move-nfs_pgio_data-and-remove-nfs_rw_header.patch [new file with mode: 0644]
queue-3.16/nfs-remove-pgio_header-refcount-related-cleanup.patch [new file with mode: 0644]
queue-3.16/nfs-rename-members-of-nfs_pgio_data.patch [new file with mode: 0644]
queue-3.16/nfs-use-blocking-page_group_lock-in-add_request.patch [new file with mode: 0644]
queue-3.16/nfsd4-fix-corruption-of-nfsv4-read-data.patch [new file with mode: 0644]
queue-3.16/nfsd4-fix-rd_dircount-enforcement.patch [new file with mode: 0644]
queue-3.16/nfsv4-fix-another-bug-in-the-close-open_downgrade-code.patch [new file with mode: 0644]
queue-3.16/nfsv4-nfs4_state_manager-vs.-nfs_server_remove_lists.patch [new file with mode: 0644]
queue-3.16/pnfs-add-pnfs_put_lseg_async.patch [new file with mode: 0644]
queue-3.16/revert-acpi-battery-fix-wrong-value-of-capacity_now-reported-when-fully-charged.patch [new file with mode: 0644]
queue-3.16/series

diff --git a/queue-3.16/acpi-lpss-complete-pm-entries-for-lpss-power-domain.patch b/queue-3.16/acpi-lpss-complete-pm-entries-for-lpss-power-domain.patch
new file mode 100644 (file)
index 0000000..a717579
--- /dev/null
@@ -0,0 +1,56 @@
+From f4168b617ac09986c4333accaff5d8ba5a9db7bf Mon Sep 17 00:00:00 2001
+From: Fu Zhonghui <zhonghui.fu@linux.intel.com>
+Date: Tue, 9 Sep 2014 16:30:06 +0200
+Subject: ACPI / LPSS: complete PM entries for LPSS power domain
+
+From: Fu Zhonghui <zhonghui.fu@linux.intel.com>
+
+commit f4168b617ac09986c4333accaff5d8ba5a9db7bf upstream.
+
+PM entries of LPSS power domain were not implemented correctly
+in commit c78b0830667a "ACPI / LPSS: custom power domain for LPSS".
+
+This patch fixes and completes these PM entries.
+
+Fixes: c78b0830667a (ACPI / LPSS: custom power domain for LPSS)
+Signed-off-by: Li Aubrey <aubrey.li@linux.intel.com>
+Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
+Signed-off-by: Fu Zhonghui <zhonghui.fu@linux.intel.com>
+Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/acpi/acpi_lpss.c |   10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+--- a/drivers/acpi/acpi_lpss.c
++++ b/drivers/acpi/acpi_lpss.c
+@@ -583,7 +583,7 @@ static int acpi_lpss_suspend_late(struct
+       return acpi_dev_suspend_late(dev);
+ }
+-static int acpi_lpss_restore_early(struct device *dev)
++static int acpi_lpss_resume_early(struct device *dev)
+ {
+       int ret = acpi_dev_resume_early(dev);
+@@ -623,15 +623,15 @@ static int acpi_lpss_runtime_resume(stru
+ static struct dev_pm_domain acpi_lpss_pm_domain = {
+       .ops = {
+ #ifdef CONFIG_PM_SLEEP
+-              .suspend_late = acpi_lpss_suspend_late,
+-              .restore_early = acpi_lpss_restore_early,
+               .prepare = acpi_subsys_prepare,
+               .complete = acpi_subsys_complete,
+               .suspend = acpi_subsys_suspend,
+-              .resume_early = acpi_subsys_resume_early,
++              .suspend_late = acpi_lpss_suspend_late,
++              .resume_early = acpi_lpss_resume_early,
+               .freeze = acpi_subsys_freeze,
+               .poweroff = acpi_subsys_suspend,
+-              .poweroff_late = acpi_subsys_suspend_late,
++              .poweroff_late = acpi_lpss_suspend_late,
++              .restore_early = acpi_lpss_resume_early,
+ #endif
+ #ifdef CONFIG_PM_RUNTIME
+               .runtime_suspend = acpi_lpss_runtime_suspend,
diff --git a/queue-3.16/acpi-rtc-fix-cmos-rtc-opregion-handler-accesses-to-wrong-addresses.patch b/queue-3.16/acpi-rtc-fix-cmos-rtc-opregion-handler-accesses-to-wrong-addresses.patch
new file mode 100644 (file)
index 0000000..9491861
--- /dev/null
@@ -0,0 +1,35 @@
+From 9389f46e9782ea5e56fbd7b2e59ba7c08f3ba86b Mon Sep 17 00:00:00 2001
+From: "Lee, Chun-Yi" <joeyli.kernel@gmail.com>
+Date: Thu, 4 Sep 2014 15:13:39 +0800
+Subject: ACPI / RTC: Fix CMOS RTC opregion handler accesses to wrong addresses
+
+From: "Lee, Chun-Yi" <joeyli.kernel@gmail.com>
+
+commit 9389f46e9782ea5e56fbd7b2e59ba7c08f3ba86b upstream.
+
+The value64 parameter is an u64 point that used to transfer the value
+for write to CMOS, or used to return the value that's read from CMOS.
+
+The value64 is an u64 point, so don't need get address again. It causes
+acpi_cmos_rtc_space_handler always return 0 to reader and didn't write
+expected value to CMOS.
+
+Signed-off-by: Lee, Chun-Yi <jlee@suse.com>
+Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/acpi/acpi_cmos_rtc.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/acpi/acpi_cmos_rtc.c
++++ b/drivers/acpi/acpi_cmos_rtc.c
+@@ -33,7 +33,7 @@ acpi_cmos_rtc_space_handler(u32 function
+                     void *handler_context, void *region_context)
+ {
+       int i;
+-      u8 *value = (u8 *)&value64;
++      u8 *value = (u8 *)value64;
+       if (address > 0xff || !value64)
+               return AE_BAD_PARAMETER;
diff --git a/queue-3.16/cgroup-check-cgroup-liveliness-before-unbreaking-kernfs.patch b/queue-3.16/cgroup-check-cgroup-liveliness-before-unbreaking-kernfs.patch
new file mode 100644 (file)
index 0000000..9df31b8
--- /dev/null
@@ -0,0 +1,72 @@
+From aa32362f011c6e863132b16c1761487166a4bad2 Mon Sep 17 00:00:00 2001
+From: Li Zefan <lizefan@huawei.com>
+Date: Thu, 4 Sep 2014 14:43:38 +0800
+Subject: cgroup: check cgroup liveliness before unbreaking kernfs
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Li Zefan <lizefan@huawei.com>
+
+commit aa32362f011c6e863132b16c1761487166a4bad2 upstream.
+
+When cgroup_kn_lock_live() is called through some kernfs operation and
+another thread is calling cgroup_rmdir(), we'll trigger the warning in
+cgroup_get().
+
+------------[ cut here ]------------
+WARNING: CPU: 1 PID: 1228 at kernel/cgroup.c:1034 cgroup_get+0x89/0xa0()
+...
+Call Trace:
+ [<c16ee73d>] dump_stack+0x41/0x52
+ [<c10468ef>] warn_slowpath_common+0x7f/0xa0
+ [<c104692d>] warn_slowpath_null+0x1d/0x20
+ [<c10bb999>] cgroup_get+0x89/0xa0
+ [<c10bbe58>] cgroup_kn_lock_live+0x28/0x70
+ [<c10be3c1>] __cgroup_procs_write.isra.26+0x51/0x230
+ [<c10be5b2>] cgroup_tasks_write+0x12/0x20
+ [<c10bb7b0>] cgroup_file_write+0x40/0x130
+ [<c11aee71>] kernfs_fop_write+0xd1/0x160
+ [<c1148e58>] vfs_write+0x98/0x1e0
+ [<c114934d>] SyS_write+0x4d/0xa0
+ [<c16f656b>] sysenter_do_call+0x12/0x12
+---[ end trace 6f2e0c38c2108a74 ]---
+
+Fix this by calling css_tryget() instead of cgroup_get().
+
+v2:
+- move cgroup_tryget() right below cgroup_get() definition. (Tejun)
+
+Reported-by: Toralf Förster <toralf.foerster@gmx.de>
+Signed-off-by: Zefan Li <lizefan@huawei.com>
+Signed-off-by: Tejun Heo <tj@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ kernel/cgroup.c |    8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+--- a/kernel/cgroup.c
++++ b/kernel/cgroup.c
+@@ -1031,6 +1031,11 @@ static void cgroup_get(struct cgroup *cg
+       css_get(&cgrp->self);
+ }
++static bool cgroup_tryget(struct cgroup *cgrp)
++{
++      return css_tryget(&cgrp->self);
++}
++
+ static void cgroup_put(struct cgroup *cgrp)
+ {
+       css_put(&cgrp->self);
+@@ -1091,7 +1096,8 @@ static struct cgroup *cgroup_kn_lock_liv
+        * protection against removal.  Ensure @cgrp stays accessible and
+        * break the active_ref protection.
+        */
+-      cgroup_get(cgrp);
++      if (!cgroup_tryget(cgrp))
++              return NULL;
+       kernfs_break_active_protection(kn);
+       mutex_lock(&cgroup_mutex);
diff --git a/queue-3.16/iommu-arm-smmu-fix-programming-of-smmu_cbn_tcr-for-stage-1.patch b/queue-3.16/iommu-arm-smmu-fix-programming-of-smmu_cbn_tcr-for-stage-1.patch
new file mode 100644 (file)
index 0000000..9732f0f
--- /dev/null
@@ -0,0 +1,39 @@
+From 1fc870c7efa364862c3bc792cfbdb38afea26742 Mon Sep 17 00:00:00 2001
+From: Olav Haugan <ohaugan@codeaurora.org>
+Date: Mon, 4 Aug 2014 19:01:02 +0100
+Subject: iommu/arm-smmu: fix programming of SMMU_CBn_TCR for stage 1
+
+From: Olav Haugan <ohaugan@codeaurora.org>
+
+commit 1fc870c7efa364862c3bc792cfbdb38afea26742 upstream.
+
+Stage-1 context banks do not have the SMMU_CBn_TCR[SL0] field since it
+is only applicable to stage-2 context banks.
+
+This patch ensures that we don't set the reserved TCR bits for stage-1
+translations.
+
+Signed-off-by: Olav Haugan <ohaugan@codeaurora.org>
+Signed-off-by: Will Deacon <will.deacon@arm.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/iommu/arm-smmu.c |    7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+--- a/drivers/iommu/arm-smmu.c
++++ b/drivers/iommu/arm-smmu.c
+@@ -830,8 +830,11 @@ static void arm_smmu_init_context_bank(s
+       reg |= TTBCR_EAE |
+             (TTBCR_SH_IS << TTBCR_SH0_SHIFT) |
+             (TTBCR_RGN_WBWA << TTBCR_ORGN0_SHIFT) |
+-            (TTBCR_RGN_WBWA << TTBCR_IRGN0_SHIFT) |
+-            (TTBCR_SL0_LVL_1 << TTBCR_SL0_SHIFT);
++            (TTBCR_RGN_WBWA << TTBCR_IRGN0_SHIFT);
++
++      if (!stage1)
++              reg |= (TTBCR_SL0_LVL_1 << TTBCR_SL0_SHIFT);
++
+       writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBCR);
+       /* MAIR0 (stage-1 only) */
diff --git a/queue-3.16/iommu-fsl-fix-warning-resulting-from-adding-pci-device-twice.patch b/queue-3.16/iommu-fsl-fix-warning-resulting-from-adding-pci-device-twice.patch
new file mode 100644 (file)
index 0000000..47281a2
--- /dev/null
@@ -0,0 +1,92 @@
+From 5a9137a66b521d667236e95c307b92af532fe600 Mon Sep 17 00:00:00 2001
+From: Varun Sethi <Varun.Sethi@freescale.com>
+Date: Thu, 4 Sep 2014 17:08:45 +0530
+Subject: iommu/fsl: Fix warning resulting from adding PCI device twice
+
+From: Varun Sethi <Varun.Sethi@freescale.com>
+
+commit 5a9137a66b521d667236e95c307b92af532fe600 upstream.
+
+iommu_group_get_for_dev determines the iommu group for the PCI device and adds
+the device to the group.
+
+In the PAMU driver we were again adding the device to the same group without checking
+if the device already had an iommu group. This resulted in the following warning.
+
+sysfs: cannot create duplicate filename '/devices/ffe200000.pcie/pci0000:00/0000:00:00.0/iommu_group'
+------------[ cut here ]------------
+WARNING: at fs/sysfs/dir.c:31
+Modules linked in:
+CPU: 0 PID: 1 Comm: swapper/0 Not tainted 3.17.0-rc3-00002-g7505cea-dirty #126
+task: c0000001fe0a0000 ti: c0000001fe044000 task.ti: c0000001fe044000
+NIP: c00000000018879c LR: c000000000188798 CTR: c00000000001ea50
+REGS: c0000001fe047040 TRAP: 0700   Not tainted  (3.17.0-rc3-00002-g7505cea-dirty)
+MSR: 0000000080029000 <CE,EE,ME>  CR: 24ad8e22  XER: 20000000
+SOFTE: 1
+GPR00: c000000000188798 c0000001fe0472c0 c0000000009a52e0 0000000000000065
+GPR04: 0000000000000001 0000000000000000 3a30303a00000000 0000000027000000
+GPR08: 2f696f6d00000000 c0000000008d3830 c0000000009b3938 c0000000009bb3d0
+GPR12: 0000000028ad8e24 c00000000fff4000 c00000000000205c 0000000000000000
+GPR16: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
+GPR20: 0000000000000000 0000000000000000 0000000000000000 c0000000008a4c70
+GPR24: c0000000007e9010 c0000001fe0140a8 ffffffffffffffef 0000000000000001
+GPR28: c0000001fe22ebb8 c0000000007e9010 c00000000090bf10 c0000001fe220000
+NIP [c00000000018879c] .sysfs_warn_dup+0x74/0xa4
+LR [c000000000188798] .sysfs_warn_dup+0x70/0xa4
+Call Trace:
+[c0000001fe0472c0] [c000000000188798] .sysfs_warn_dup+0x70/0xa4 (unreliable)
+[c0000001fe047350] [c000000000188d34] .sysfs_do_create_link_sd.clone.2+0x168/0x174
+[c0000001fe047400] [c0000000004b3cf8] .iommu_group_add_device+0x78/0x244
+[c0000001fe0474b0] [c0000000004b6964] .fsl_pamu_add_device+0x88/0x1a8
+[c0000001fe047570] [c0000000004b3960] .iommu_bus_notifier+0xdc/0x15c
+[c0000001fe047600] [c000000000059848] .notifier_call_chain+0x8c/0xe8
+[c0000001fe0476a0] [c000000000059d04] .__blocking_notifier_call_chain+0x58/0x84
+[c0000001fe047750] [c00000000036619c] .device_add+0x464/0x5c8
+[c0000001fe047820] [c000000000300ebc] .pci_device_add+0x14c/0x17c
+[c0000001fe0478c0] [c000000000300fbc] .pci_scan_single_device+0xd0/0xf4
+[c0000001fe047970] [c00000000030104c] .pci_scan_slot+0x6c/0x18c
+[c0000001fe047a10] [c00000000030226c] .pci_scan_child_bus+0x40/0x114
+[c0000001fe047ac0] [c000000000021974] .pcibios_scan_phb+0x240/0x2c8
+[c0000001fe047b70] [c00000000085a970] .pcibios_init+0x64/0xc8
+[c0000001fe047c00] [c000000000001884] .do_one_initcall+0xbc/0x224
+[c0000001fe047d00] [c000000000852d50] .kernel_init_freeable+0x14c/0x21c
+[c0000001fe047db0] [c000000000002078] .kernel_init+0x1c/0xfa4
+[c0000001fe047e30] [c000000000000884] .ret_from_kernel_thread+0x58/0xd4
+Instruction dump:
+7c7f1b79 4182001c 7fe4fb78 7f83e378 38a01000 4bffc905 60000000 7c641b78
+e87e8008 7fa5eb78 48482ff5 60000000 <0fe00000> 7fe3fb78 4bf7bd39 60000000
+
+Signed-off-by: Varun Sethi <Varun.Sethi@freescale.com>
+Signed-off-by: Joerg Roedel <jroedel@suse.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/iommu/fsl_pamu_domain.c |   10 ++++++++--
+ 1 file changed, 8 insertions(+), 2 deletions(-)
+
+--- a/drivers/iommu/fsl_pamu_domain.c
++++ b/drivers/iommu/fsl_pamu_domain.c
+@@ -1048,7 +1048,7 @@ static int fsl_pamu_add_device(struct de
+       struct iommu_group *group = ERR_PTR(-ENODEV);
+       struct pci_dev *pdev;
+       const u32 *prop;
+-      int ret, len;
++      int ret = 0, len;
+       /*
+        * For platform devices we allocate a separate group for
+@@ -1071,7 +1071,13 @@ static int fsl_pamu_add_device(struct de
+       if (IS_ERR(group))
+               return PTR_ERR(group);
+-      ret = iommu_group_add_device(group, dev);
++      /*
++       * Check if device has already been added to an iommu group.
++       * Group could have already been created for a PCI device in
++       * the iommu_group_get_for_dev path.
++       */
++      if (!dev->iommu_group)
++              ret = iommu_group_add_device(group, dev);
+       iommu_group_put(group);
+       return ret;
diff --git a/queue-3.16/iommu-vt-d-check-return-value-of-acpi_bus_get_device.patch b/queue-3.16/iommu-vt-d-check-return-value-of-acpi_bus_get_device.patch
new file mode 100644 (file)
index 0000000..9943d74
--- /dev/null
@@ -0,0 +1,36 @@
+From c0df975f9045d6b2b13d88746e628ac308ff49ea Mon Sep 17 00:00:00 2001
+From: Joerg Roedel <jroedel@suse.de>
+Date: Thu, 21 Aug 2014 23:06:48 +0200
+Subject: iommu/vt-d: Check return value of acpi_bus_get_device()
+
+From: Joerg Roedel <jroedel@suse.de>
+
+commit c0df975f9045d6b2b13d88746e628ac308ff49ea upstream.
+
+Checking adev == NULL is not sufficient as
+acpi_bus_get_device() might not touch the value of this
+parameter in an error case, so check the return value
+directly.
+
+Fixes: ed40356b5fcf1ce28e026ab39c5b2b6939068b50
+Cc: David Woodhouse <dwmw2@infradead.org>
+Signed-off-by: Joerg Roedel <jroedel@suse.de>
+Reviewed-by: Alex Williamson <alex.williamson@redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/iommu/dmar.c |    3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+--- a/drivers/iommu/dmar.c
++++ b/drivers/iommu/dmar.c
+@@ -677,8 +677,7 @@ static int __init dmar_acpi_dev_scope_in
+                                      andd->object_name);
+                               continue;
+                       }
+-                      acpi_bus_get_device(h, &adev);
+-                      if (!adev) {
++                      if (acpi_bus_get_device(h, &adev)) {
+                               pr_err("Failed to get device for ACPI object %s\n",
+                                      andd->object_name);
+                               continue;
diff --git a/queue-3.16/nfs-can_coalesce_requests-must-enforce-contiguity.patch b/queue-3.16/nfs-can_coalesce_requests-must-enforce-contiguity.patch
new file mode 100644 (file)
index 0000000..5a366f6
--- /dev/null
@@ -0,0 +1,49 @@
+From trond.myklebust@primarydata.com  Thu Oct  2 16:50:10 2014
+From: Trond Myklebust <trond.myklebust@primarydata.com>
+Date: Mon, 15 Sep 2014 14:14:45 -0400
+Subject: nfs: can_coalesce_requests must enforce contiguity
+To: stable@vger.kernel.org
+Cc: Weston Andros Adamson <dros@primarydata.com>, linux-nfs@vger.kernel.org
+Message-ID: <1410804885-17228-15-git-send-email-trond.myklebust@primarydata.com>
+
+
+From: Weston Andros Adamson <dros@primarydata.com>
+
+commit 78270e8fbc2916bfc8305b8f58f33474cce1ec0e upstream.
+
+Commit 6094f83864c1d1296566a282cba05ba613f151ee
+"nfs: allow coalescing of subpage requests" got rid of the requirement
+that requests cover whole pages, but it made some incorrect assumptions.
+
+It turns out that callers of this interface can map adjacent requests
+(by file position as seen by req_offset + req->wb_bytes) to different pages,
+even when they could share a page. An example is the direct I/O interface -
+iov_iter_get_pages_alloc may return one segment with a partial page filled
+and the next segment (which is adjacent in the file position) starts with a
+new page.
+
+Reported-by: Toralf Förster <toralf.foerster@gmx.de>
+Signed-off-by: Weston Andros Adamson <dros@primarydata.com>
+Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/nfs/pagelist.c |    8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+--- a/fs/nfs/pagelist.c
++++ b/fs/nfs/pagelist.c
+@@ -833,6 +833,14 @@ static bool nfs_can_coalesce_requests(st
+                       return false;
+               if (req_offset(req) != req_offset(prev) + prev->wb_bytes)
+                       return false;
++              if (req->wb_page == prev->wb_page) {
++                      if (req->wb_pgbase != prev->wb_pgbase + prev->wb_bytes)
++                              return false;
++              } else {
++                      if (req->wb_pgbase != 0 ||
++                          prev->wb_pgbase + prev->wb_bytes != PAGE_CACHE_SIZE)
++                              return false;
++              }
+       }
+       size = pgio->pg_ops->pg_test(pgio, prev, req);
+       WARN_ON_ONCE(size > req->wb_bytes);
diff --git a/queue-3.16/nfs-change-nfs_page_group_lock-argument.patch b/queue-3.16/nfs-change-nfs_page_group_lock-argument.patch
new file mode 100644 (file)
index 0000000..9a6c739
--- /dev/null
@@ -0,0 +1,99 @@
+From trond.myklebust@primarydata.com  Thu Oct  2 16:49:24 2014
+From: Trond Myklebust <trond.myklebust@primarydata.com>
+Date: Mon, 15 Sep 2014 14:14:39 -0400
+Subject: nfs: change nfs_page_group_lock argument
+To: stable@vger.kernel.org
+Cc: Weston Andros Adamson <dros@primarydata.com>, linux-nfs@vger.kernel.org
+Message-ID: <1410804885-17228-9-git-send-email-trond.myklebust@primarydata.com>
+
+
+From: Weston Andros Adamson <dros@primarydata.com>
+
+commit fd2f3a06d30c85a17cf035ebc60c88c2a13a8ece upstream.
+
+Flip the meaning of the second argument from 'wait' to 'nonblock' to
+match related functions. Update all five calls to reflect this change.
+
+Signed-off-by: Weston Andros Adamson <dros@primarydata.com>
+Reviewed-by: Peng Tao <tao.peng@primarydata.com>
+Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/nfs/pagelist.c |   11 ++++++-----
+ fs/nfs/write.c    |    4 ++--
+ 2 files changed, 8 insertions(+), 7 deletions(-)
+
+--- a/fs/nfs/pagelist.c
++++ b/fs/nfs/pagelist.c
+@@ -145,13 +145,14 @@ static int nfs_wait_bit_uninterruptible(
+ /*
+  * nfs_page_group_lock - lock the head of the page group
+  * @req - request in group that is to be locked
++ * @nonblock - if true don't block waiting for lock
+  *
+  * this lock must be held if modifying the page group list
+  *
+  * returns result from wait_on_bit_lock: 0 on success, < 0 on error
+  */
+ int
+-nfs_page_group_lock(struct nfs_page *req, bool wait)
++nfs_page_group_lock(struct nfs_page *req, bool nonblock)
+ {
+       struct nfs_page *head = req->wb_head;
+       int ret;
+@@ -162,7 +163,7 @@ nfs_page_group_lock(struct nfs_page *req
+               ret = wait_on_bit_lock(&head->wb_flags, PG_HEADLOCK,
+                       nfs_wait_bit_uninterruptible,
+                       TASK_UNINTERRUPTIBLE);
+-      } while (wait && ret != 0);
++      } while (!nonblock && ret != 0);
+       WARN_ON_ONCE(ret > 0);
+       return ret;
+@@ -226,7 +227,7 @@ bool nfs_page_group_sync_on_bit(struct n
+ {
+       bool ret;
+-      nfs_page_group_lock(req, true);
++      nfs_page_group_lock(req, false);
+       ret = nfs_page_group_sync_on_bit_locked(req, bit);
+       nfs_page_group_unlock(req);
+@@ -869,7 +870,7 @@ static int __nfs_pageio_add_request(stru
+       unsigned int offset, pgbase;
+       int ret;
+-      ret = nfs_page_group_lock(req, false);
++      ret = nfs_page_group_lock(req, true);
+       if (ret < 0) {
+               desc->pg_error = ret;
+               return 0;
+@@ -895,7 +896,7 @@ static int __nfs_pageio_add_request(stru
+                       if (desc->pg_recoalesce)
+                               return 0;
+                       /* retry add_request for this subreq */
+-                      ret = nfs_page_group_lock(req, false);
++                      ret = nfs_page_group_lock(req, true);
+                       if (ret < 0) {
+                               desc->pg_error = ret;
+                               return 0;
+--- a/fs/nfs/write.c
++++ b/fs/nfs/write.c
+@@ -216,7 +216,7 @@ static bool nfs_page_group_covers_page(s
+       unsigned int pos = 0;
+       unsigned int len = nfs_page_length(req->wb_page);
+-      nfs_page_group_lock(req, true);
++      nfs_page_group_lock(req, false);
+       do {
+               tmp = nfs_page_group_search_locked(req->wb_head, pos);
+@@ -454,7 +454,7 @@ try_again:
+       }
+       /* lock each request in the page group */
+-      ret = nfs_page_group_lock(head, false);
++      ret = nfs_page_group_lock(head, true);
+       if (ret < 0)
+               return ERR_PTR(ret);
+       subreq = head;
diff --git a/queue-3.16/nfs-check-wait_on_bit_lock-err-in-page_group_lock.patch b/queue-3.16/nfs-check-wait_on_bit_lock-err-in-page_group_lock.patch
new file mode 100644 (file)
index 0000000..7d67e51
--- /dev/null
@@ -0,0 +1,129 @@
+From trond.myklebust@primarydata.com  Thu Oct  2 16:48:54 2014
+From: Trond Myklebust <trond.myklebust@primarydata.com>
+Date: Mon, 15 Sep 2014 14:14:36 -0400
+Subject: nfs: check wait_on_bit_lock err in page_group_lock
+To: stable@vger.kernel.org
+Cc: Weston Andros Adamson <dros@primarydata.com>, linux-nfs@vger.kernel.org
+Message-ID: <1410804885-17228-6-git-send-email-trond.myklebust@primarydata.com>
+
+
+From: Weston Andros Adamson <dros@primarydata.com>
+
+commit e7029206ff43f6cf7d6fcb741adb126f47200516 upstream.
+
+Return errors from wait_on_bit_lock from nfs_page_group_lock.
+
+Add a bool argument @wait to nfs_page_group_lock. If true, loop over
+wait_on_bit_lock until it returns cleanly. If false, return the error
+from wait_on_bit_lock.
+
+Signed-off-by: Weston Andros Adamson <dros@primarydata.com>
+Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/nfs/pagelist.c        |   29 +++++++++++++++++++++++------
+ fs/nfs/write.c           |    6 ++++--
+ include/linux/nfs_page.h |    2 +-
+ 3 files changed, 28 insertions(+), 9 deletions(-)
+
+--- a/fs/nfs/pagelist.c
++++ b/fs/nfs/pagelist.c
+@@ -147,17 +147,25 @@ static int nfs_wait_bit_uninterruptible(
+  * @req - request in group that is to be locked
+  *
+  * this lock must be held if modifying the page group list
++ *
++ * returns result from wait_on_bit_lock: 0 on success, < 0 on error
+  */
+-void
+-nfs_page_group_lock(struct nfs_page *req)
++int
++nfs_page_group_lock(struct nfs_page *req, bool wait)
+ {
+       struct nfs_page *head = req->wb_head;
++      int ret;
+       WARN_ON_ONCE(head != head->wb_head);
+-      wait_on_bit_lock(&head->wb_flags, PG_HEADLOCK,
++      do {
++              ret = wait_on_bit_lock(&head->wb_flags, PG_HEADLOCK,
+                       nfs_wait_bit_uninterruptible,
+                       TASK_UNINTERRUPTIBLE);
++      } while (wait && ret != 0);
++
++      WARN_ON_ONCE(ret > 0);
++      return ret;
+ }
+ /*
+@@ -218,7 +226,7 @@ bool nfs_page_group_sync_on_bit(struct n
+ {
+       bool ret;
+-      nfs_page_group_lock(req);
++      nfs_page_group_lock(req, true);
+       ret = nfs_page_group_sync_on_bit_locked(req, bit);
+       nfs_page_group_unlock(req);
+@@ -859,8 +867,13 @@ static int __nfs_pageio_add_request(stru
+       struct nfs_page *subreq;
+       unsigned int bytes_left = 0;
+       unsigned int offset, pgbase;
++      int ret;
+-      nfs_page_group_lock(req);
++      ret = nfs_page_group_lock(req, false);
++      if (ret < 0) {
++              desc->pg_error = ret;
++              return 0;
++      }
+       subreq = req;
+       bytes_left = subreq->wb_bytes;
+@@ -882,7 +895,11 @@ static int __nfs_pageio_add_request(stru
+                       if (desc->pg_recoalesce)
+                               return 0;
+                       /* retry add_request for this subreq */
+-                      nfs_page_group_lock(req);
++                      ret = nfs_page_group_lock(req, false);
++                      if (ret < 0) {
++                              desc->pg_error = ret;
++                              return 0;
++                      }
+                       continue;
+               }
+--- a/fs/nfs/write.c
++++ b/fs/nfs/write.c
+@@ -216,7 +216,7 @@ static bool nfs_page_group_covers_page(s
+       unsigned int pos = 0;
+       unsigned int len = nfs_page_length(req->wb_page);
+-      nfs_page_group_lock(req);
++      nfs_page_group_lock(req, true);
+       do {
+               tmp = nfs_page_group_search_locked(req->wb_head, pos);
+@@ -456,7 +456,9 @@ try_again:
+       }
+       /* lock each request in the page group */
+-      nfs_page_group_lock(head);
++      ret = nfs_page_group_lock(head, false);
++      if (ret < 0)
++              return ERR_PTR(ret);
+       subreq = head;
+       do {
+               /*
+--- a/include/linux/nfs_page.h
++++ b/include/linux/nfs_page.h
+@@ -120,7 +120,7 @@ extern size_t nfs_generic_pg_test(struct
+ extern  int nfs_wait_on_request(struct nfs_page *);
+ extern        void nfs_unlock_request(struct nfs_page *req);
+ extern        void nfs_unlock_and_release_request(struct nfs_page *);
+-extern void nfs_page_group_lock(struct nfs_page *);
++extern int nfs_page_group_lock(struct nfs_page *, bool);
+ extern void nfs_page_group_unlock(struct nfs_page *);
+ extern bool nfs_page_group_sync_on_bit(struct nfs_page *, unsigned int);
diff --git a/queue-3.16/nfs-clear_request_commit-while-holding-i_lock.patch b/queue-3.16/nfs-clear_request_commit-while-holding-i_lock.patch
new file mode 100644 (file)
index 0000000..78986e4
--- /dev/null
@@ -0,0 +1,115 @@
+From trond.myklebust@primarydata.com  Thu Oct  2 16:49:13 2014
+From: Trond Myklebust <trond.myklebust@primarydata.com>
+Date: Mon, 15 Sep 2014 14:14:38 -0400
+Subject: nfs: clear_request_commit while holding i_lock
+To: stable@vger.kernel.org
+Cc: Weston Andros Adamson <dros@primarydata.com>, linux-nfs@vger.kernel.org
+Message-ID: <1410804885-17228-8-git-send-email-trond.myklebust@primarydata.com>
+
+
+From: Weston Andros Adamson <dros@primarydata.com>
+
+commit 411a99adffb4f993eee29759f744de01487044ac upstream.
+
+Signed-off-by: Weston Andros Adamson <dros@primarydata.com>
+Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/nfs/filelayout/filelayout.c |    5 ++---
+ fs/nfs/write.c                 |   15 ++++-----------
+ 2 files changed, 6 insertions(+), 14 deletions(-)
+
+--- a/fs/nfs/filelayout/filelayout.c
++++ b/fs/nfs/filelayout/filelayout.c
+@@ -1015,6 +1015,7 @@ static u32 select_bucket_index(struct nf
+ /* The generic layer is about to remove the req from the commit list.
+  * If this will make the bucket empty, it will need to put the lseg reference.
++ * Note this is must be called holding the inode (/cinfo) lock
+  */
+ static void
+ filelayout_clear_request_commit(struct nfs_page *req,
+@@ -1022,7 +1023,6 @@ filelayout_clear_request_commit(struct n
+ {
+       struct pnfs_layout_segment *freeme = NULL;
+-      spin_lock(cinfo->lock);
+       if (!test_and_clear_bit(PG_COMMIT_TO_DS, &req->wb_flags))
+               goto out;
+       cinfo->ds->nwritten--;
+@@ -1037,8 +1037,7 @@ filelayout_clear_request_commit(struct n
+       }
+ out:
+       nfs_request_remove_commit_list(req, cinfo);
+-      spin_unlock(cinfo->lock);
+-      pnfs_put_lseg(freeme);
++      pnfs_put_lseg_async(freeme);
+ }
+ static struct list_head *
+--- a/fs/nfs/write.c
++++ b/fs/nfs/write.c
+@@ -379,8 +379,6 @@ nfs_destroy_unlinked_subrequests(struct
+               subreq->wb_head = subreq;
+               subreq->wb_this_page = subreq;
+-              nfs_clear_request_commit(subreq);
+-
+               /* subreq is now totally disconnected from page group or any
+                * write / commit lists. last chance to wake any waiters */
+               nfs_unlock_request(subreq);
+@@ -490,7 +488,7 @@ try_again:
+        * Commit list removal accounting is done after locks are dropped */
+       subreq = head;
+       do {
+-              nfs_list_remove_request(subreq);
++              nfs_clear_request_commit(subreq);
+               subreq = subreq->wb_this_page;
+       } while (subreq != head);
+@@ -520,15 +518,11 @@ try_again:
+       nfs_page_group_unlock(head);
+-      /* drop lock to clear_request_commit the head req and clean up
+-       * requests on destroy list */
++      /* drop lock to clean uprequests on destroy list */
+       spin_unlock(&inode->i_lock);
+       nfs_destroy_unlinked_subrequests(destroy_list, head);
+-      /* clean up commit list state */
+-      nfs_clear_request_commit(head);
+-
+       /* still holds ref on head from nfs_page_find_head_request_locked
+        * and still has lock on head from lock loop */
+       return head;
+@@ -810,6 +804,7 @@ nfs_clear_page_commit(struct page *page)
+       dec_bdi_stat(page_file_mapping(page)->backing_dev_info, BDI_RECLAIMABLE);
+ }
++/* Called holding inode (/cinfo) lock */
+ static void
+ nfs_clear_request_commit(struct nfs_page *req)
+ {
+@@ -819,9 +814,7 @@ nfs_clear_request_commit(struct nfs_page
+               nfs_init_cinfo_from_inode(&cinfo, inode);
+               if (!pnfs_clear_request_commit(req, &cinfo)) {
+-                      spin_lock(cinfo.lock);
+                       nfs_request_remove_commit_list(req, &cinfo);
+-                      spin_unlock(cinfo.lock);
+               }
+               nfs_clear_page_commit(req->wb_page);
+       }
+@@ -1040,9 +1033,9 @@ static struct nfs_page *nfs_try_to_updat
+       else
+               req->wb_bytes = rqend - req->wb_offset;
+ out_unlock:
+-      spin_unlock(&inode->i_lock);
+       if (req)
+               nfs_clear_request_commit(req);
++      spin_unlock(&inode->i_lock);
+       return req;
+ out_flushme:
+       spin_unlock(&inode->i_lock);
diff --git a/queue-3.16/nfs-disallow-duplicate-pages-in-pgio-page-vectors.patch b/queue-3.16/nfs-disallow-duplicate-pages-in-pgio-page-vectors.patch
new file mode 100644 (file)
index 0000000..cdb82ae
--- /dev/null
@@ -0,0 +1,74 @@
+From trond.myklebust@primarydata.com  Thu Oct  2 16:50:02 2014
+From: Trond Myklebust <trond.myklebust@primarydata.com>
+Date: Mon, 15 Sep 2014 14:14:44 -0400
+Subject: nfs: disallow duplicate pages in pgio page vectors
+To: stable@vger.kernel.org
+Cc: Weston Andros Adamson <dros@primarydata.com>, linux-nfs@vger.kernel.org
+Message-ID: <1410804885-17228-14-git-send-email-trond.myklebust@primarydata.com>
+
+
+From: Weston Andros Adamson <dros@primarydata.com>
+
+commit bba5c1887a925a9945d22217d38d58d8b3ba1043 upstream.
+
+Adjacent requests that share the same page are allowed, but should only
+use one entry in the page vector. This avoids overruning the page
+vector - it is sized based on how many bytes there are, not by
+request count.
+
+This fixes issues that manifest as "Redzone overwritten" bugs (the
+vector overrun) and hangs waiting on page read / write, as it waits on
+the same page more than once.
+
+This also adds bounds checking to the page vector with a graceful failure
+(WARN_ON_ONCE and pgio error returned to application).
+
+Reported-by: Toralf Förster <toralf.foerster@gmx.de>
+Signed-off-by: Weston Andros Adamson <dros@primarydata.com>
+Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/nfs/pagelist.c |   18 +++++++++++++++---
+ 1 file changed, 15 insertions(+), 3 deletions(-)
+
+--- a/fs/nfs/pagelist.c
++++ b/fs/nfs/pagelist.c
+@@ -734,10 +734,11 @@ int nfs_generic_pgio(struct nfs_pageio_d
+                    struct nfs_pgio_header *hdr)
+ {
+       struct nfs_page         *req;
+-      struct page             **pages;
++      struct page             **pages,
++                              *last_page;
+       struct list_head *head = &desc->pg_list;
+       struct nfs_commit_info cinfo;
+-      unsigned int pagecount;
++      unsigned int pagecount, pageused;
+       pagecount = nfs_page_array_len(desc->pg_base, desc->pg_count);
+       if (!nfs_pgarray_set(&hdr->page_array, pagecount))
+@@ -745,12 +746,23 @@ int nfs_generic_pgio(struct nfs_pageio_d
+       nfs_init_cinfo(&cinfo, desc->pg_inode, desc->pg_dreq);
+       pages = hdr->page_array.pagevec;
++      last_page = NULL;
++      pageused = 0;
+       while (!list_empty(head)) {
+               req = nfs_list_entry(head->next);
+               nfs_list_remove_request(req);
+               nfs_list_add_request(req, &hdr->pages);
+-              *pages++ = req->wb_page;
++
++              if (WARN_ON_ONCE(pageused >= pagecount))
++                      return nfs_pgio_error(desc, hdr);
++
++              if (!last_page || last_page != req->wb_page) {
++                      *pages++ = last_page = req->wb_page;
++                      pageused++;
++              }
+       }
++      if (WARN_ON_ONCE(pageused != pagecount))
++              return nfs_pgio_error(desc, hdr);
+       if ((desc->pg_ioflags & FLUSH_COND_STABLE) &&
+           (desc->pg_moreio || nfs_reqs_to_commit(&cinfo)))
diff --git a/queue-3.16/nfs-don-t-sleep-with-inode-lock-in-lock_and_join_requests.patch b/queue-3.16/nfs-don-t-sleep-with-inode-lock-in-lock_and_join_requests.patch
new file mode 100644 (file)
index 0000000..1836fdc
--- /dev/null
@@ -0,0 +1,94 @@
+From trond.myklebust@primarydata.com  Thu Oct  2 16:49:54 2014
+From: Trond Myklebust <trond.myklebust@primarydata.com>
+Date: Mon, 15 Sep 2014 14:14:43 -0400
+Subject: nfs: don't sleep with inode lock in lock_and_join_requests
+To: stable@vger.kernel.org
+Cc: Weston Andros Adamson <dros@primarydata.com>, linux-nfs@vger.kernel.org
+Message-ID: <1410804885-17228-13-git-send-email-trond.myklebust@primarydata.com>
+
+
+From: Weston Andros Adamson <dros@primarydata.com>
+
+commit 7c3af975257383ece54b83c0505d3e0656cb7daf upstream.
+
+This handles the 'nonblock=false' case in nfs_lock_and_join_requests.
+If the group is already locked and blocking is allowed, drop the inode lock
+and wait for the group lock to be cleared before trying it all again.
+This should fix warnings found in peterz's tree (sched/wait branch), where
+might_sleep() checks are added to wait.[ch].
+
+Reported-by: Fengguang Wu <fengguang.wu@intel.com>
+Signed-off-by: Weston Andros Adamson <dros@primarydata.com>
+Reviewed-by: Peng Tao <tao.peng@primarydata.com>
+Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/nfs/pagelist.c        |   18 ++++++++++++++++++
+ fs/nfs/write.c           |   12 +++++++++++-
+ include/linux/nfs_page.h |    1 +
+ 3 files changed, 30 insertions(+), 1 deletion(-)
+
+--- a/fs/nfs/pagelist.c
++++ b/fs/nfs/pagelist.c
+@@ -175,6 +175,24 @@ nfs_page_group_lock(struct nfs_page *req
+ }
+ /*
++ * nfs_page_group_lock_wait - wait for the lock to clear, but don't grab it
++ * @req - a request in the group
++ *
++ * This is a blocking call to wait for the group lock to be cleared.
++ */
++void
++nfs_page_group_lock_wait(struct nfs_page *req)
++{
++      struct nfs_page *head = req->wb_head;
++
++      WARN_ON_ONCE(head != head->wb_head);
++
++      wait_on_bit(&head->wb_flags, PG_HEADLOCK,
++              nfs_wait_bit_uninterruptible,
++              TASK_UNINTERRUPTIBLE);
++}
++
++/*
+  * nfs_page_group_unlock - unlock the head of the page group
+  * @req - request in group that is to be unlocked
+  */
+--- a/fs/nfs/write.c
++++ b/fs/nfs/write.c
+@@ -453,13 +453,23 @@ try_again:
+               return NULL;
+       }
+-      /* lock each request in the page group */
++      /* holding inode lock, so always make a non-blocking call to try the
++       * page group lock */
+       ret = nfs_page_group_lock(head, true);
+       if (ret < 0) {
+               spin_unlock(&inode->i_lock);
++
++              if (!nonblock && ret == -EAGAIN) {
++                      nfs_page_group_lock_wait(head);
++                      nfs_release_request(head);
++                      goto try_again;
++              }
++
+               nfs_release_request(head);
+               return ERR_PTR(ret);
+       }
++
++      /* lock each request in the page group */
+       subreq = head;
+       do {
+               /*
+--- a/include/linux/nfs_page.h
++++ b/include/linux/nfs_page.h
+@@ -121,6 +121,7 @@ extern  int nfs_wait_on_request(struct n
+ extern        void nfs_unlock_request(struct nfs_page *req);
+ extern        void nfs_unlock_and_release_request(struct nfs_page *);
+ extern int nfs_page_group_lock(struct nfs_page *, bool);
++extern void nfs_page_group_lock_wait(struct nfs_page *);
+ extern void nfs_page_group_unlock(struct nfs_page *);
+ extern bool nfs_page_group_sync_on_bit(struct nfs_page *, unsigned int);
diff --git a/queue-3.16/nfs-fix-error-handling-in-lock_and_join_requests.patch b/queue-3.16/nfs-fix-error-handling-in-lock_and_join_requests.patch
new file mode 100644 (file)
index 0000000..a2c81d3
--- /dev/null
@@ -0,0 +1,41 @@
+From trond.myklebust@primarydata.com  Thu Oct  2 16:49:47 2014
+From: Trond Myklebust <trond.myklebust@primarydata.com>
+Date: Mon, 15 Sep 2014 14:14:42 -0400
+Subject: nfs: fix error handling in lock_and_join_requests
+To: stable@vger.kernel.org
+Cc: Weston Andros Adamson <dros@primarydata.com>, linux-nfs@vger.kernel.org
+Message-ID: <1410804885-17228-12-git-send-email-trond.myklebust@primarydata.com>
+
+
+From: Weston Andros Adamson <dros@primarydata.com>
+
+commit 94970014c46223cbcdfbfc67b89596a412f9e3dd upstream.
+
+This fixes handling of errors from nfs_page_group_lock in
+nfs_lock_and_join_requests.  It now releases the inode lock and the
+reference to the head request.
+
+Reported-by: Peng Tao <tao.peng@primarydata.com>
+Signed-off-by: Weston Andros Adamson <dros@primarydata.com>
+Reviewed-by: Peng Tao <tao.peng@primarydata.com>
+Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/nfs/write.c |    5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+--- a/fs/nfs/write.c
++++ b/fs/nfs/write.c
+@@ -455,8 +455,11 @@ try_again:
+       /* lock each request in the page group */
+       ret = nfs_page_group_lock(head, true);
+-      if (ret < 0)
++      if (ret < 0) {
++              spin_unlock(&inode->i_lock);
++              nfs_release_request(head);
+               return ERR_PTR(ret);
++      }
+       subreq = head;
+       do {
+               /*
diff --git a/queue-3.16/nfs-fix-nonblocking-calls-to-nfs_page_group_lock.patch b/queue-3.16/nfs-fix-nonblocking-calls-to-nfs_page_group_lock.patch
new file mode 100644 (file)
index 0000000..a0cb548
--- /dev/null
@@ -0,0 +1,67 @@
+From trond.myklebust@primarydata.com  Thu Oct  2 16:49:33 2014
+From: Trond Myklebust <trond.myklebust@primarydata.com>
+Date: Mon, 15 Sep 2014 14:14:40 -0400
+Subject: nfs: fix nonblocking calls to nfs_page_group_lock
+To: stable@vger.kernel.org
+Cc: Weston Andros Adamson <dros@primarydata.com>, linux-nfs@vger.kernel.org
+Message-ID: <1410804885-17228-10-git-send-email-trond.myklebust@primarydata.com>
+
+
+From: Weston Andros Adamson <dros@primarydata.com>
+
+commit bc8a309e88a86205fc3e17f06e42a2e56fc6f807 upstream.
+
+nfs_page_group_lock was calling wait_on_bit_lock even when told not to
+block. Fix by first trying test_and_set_bit, followed by wait_on_bit_lock
+if and only if blocking is allowed.  Return -EAGAIN if nonblocking and the
+test_and_set of the bit was already locked.
+
+Signed-off-by: Weston Andros Adamson <dros@primarydata.com>
+Reviewed-by: Peng Tao <tao.peng@primarydata.com>
+Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/nfs/pagelist.c |   23 ++++++++++++++---------
+ 1 file changed, 14 insertions(+), 9 deletions(-)
+
+--- a/fs/nfs/pagelist.c
++++ b/fs/nfs/pagelist.c
+@@ -149,24 +149,29 @@ static int nfs_wait_bit_uninterruptible(
+  *
+  * this lock must be held if modifying the page group list
+  *
+- * returns result from wait_on_bit_lock: 0 on success, < 0 on error
++ * return 0 on success, < 0 on error: -EDELAY if nonblocking or the
++ * result from wait_on_bit_lock
++ *
++ * NOTE: calling with nonblock=false should always have set the
++ *       lock bit (see fs/buffer.c and other uses of wait_on_bit_lock
++ *       with TASK_UNINTERRUPTIBLE), so there is no need to check the result.
+  */
+ int
+ nfs_page_group_lock(struct nfs_page *req, bool nonblock)
+ {
+       struct nfs_page *head = req->wb_head;
+-      int ret;
+       WARN_ON_ONCE(head != head->wb_head);
+-      do {
+-              ret = wait_on_bit_lock(&head->wb_flags, PG_HEADLOCK,
+-                      nfs_wait_bit_uninterruptible,
+-                      TASK_UNINTERRUPTIBLE);
+-      } while (!nonblock && ret != 0);
++      if (!test_and_set_bit(PG_HEADLOCK, &head->wb_flags))
++              return 0;
++
++      if (!nonblock)
++              return wait_on_bit_lock(&head->wb_flags, PG_HEADLOCK,
++                              nfs_wait_bit_uninterruptible,
++                              TASK_UNINTERRUPTIBLE);
+-      WARN_ON_ONCE(ret > 0);
+-      return ret;
++      return -EAGAIN;
+ }
+ /*
diff --git a/queue-3.16/nfs-merge-nfs_pgio_data-into-_header.patch b/queue-3.16/nfs-merge-nfs_pgio_data-into-_header.patch
new file mode 100644 (file)
index 0000000..c0344dc
--- /dev/null
@@ -0,0 +1,2234 @@
+From trond.myklebust@primarydata.com  Thu Oct  2 16:46:47 2014
+From: Trond Myklebust <trond.myklebust@primarydata.com>
+Date: Mon, 15 Sep 2014 14:14:34 -0400
+Subject: nfs: merge nfs_pgio_data into _header
+To: stable@vger.kernel.org
+Cc: Weston Andros Adamson <dros@primarydata.com>, linux-nfs@vger.kernel.org
+Message-ID: <1410804885-17228-4-git-send-email-trond.myklebust@primarydata.com>
+
+
+From: Weston Andros Adamson <dros@primarydata.com>
+
+commit d45f60c67848b9f19160692581d78e5b4757a000 upstream.
+
+struct nfs_pgio_data only exists as a member of nfs_pgio_header, but is
+passed around everywhere, because there used to be multiple _data structs
+per _header. Many of these functions then use the _data to find a pointer
+to the _header.  This patch cleans this up by merging the nfs_pgio_data
+structure into nfs_pgio_header and passing nfs_pgio_header around instead.
+
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Signed-off-by: Weston Andros Adamson <dros@primarydata.com>
+Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/nfs/blocklayout/blocklayout.c |   98 ++++++++++------------
+ fs/nfs/direct.c                  |    8 -
+ fs/nfs/filelayout/filelayout.c   |  170 ++++++++++++++++++---------------------
+ fs/nfs/internal.h                |    6 -
+ fs/nfs/nfs3proc.c                |   21 ++--
+ fs/nfs/nfs4_fs.h                 |    6 -
+ fs/nfs/nfs4proc.c                |  105 ++++++++++++------------
+ fs/nfs/nfs4trace.h               |   28 +++---
+ fs/nfs/objlayout/objio_osd.c     |   24 ++---
+ fs/nfs/objlayout/objlayout.c     |   81 ++++++++----------
+ fs/nfs/objlayout/objlayout.h     |    8 -
+ fs/nfs/pagelist.c                |  120 +++++++++++++--------------
+ fs/nfs/pnfs.c                    |   80 +++++++-----------
+ fs/nfs/pnfs.h                    |   10 +-
+ fs/nfs/proc.c                    |   27 +++---
+ fs/nfs/read.c                    |   42 +++++----
+ fs/nfs/write.c                   |   56 ++++++------
+ include/linux/nfs_page.h         |    9 +-
+ include/linux/nfs_xdr.h          |   43 ++++-----
+ 19 files changed, 460 insertions(+), 482 deletions(-)
+
+--- a/fs/nfs/blocklayout/blocklayout.c
++++ b/fs/nfs/blocklayout/blocklayout.c
+@@ -210,8 +210,7 @@ static void bl_end_io_read(struct bio *b
+                       SetPageUptodate(bvec->bv_page);
+       if (err) {
+-              struct nfs_pgio_data *rdata = par->data;
+-              struct nfs_pgio_header *header = rdata->header;
++              struct nfs_pgio_header *header = par->data;
+               if (!header->pnfs_error)
+                       header->pnfs_error = -EIO;
+@@ -224,44 +223,44 @@ static void bl_end_io_read(struct bio *b
+ static void bl_read_cleanup(struct work_struct *work)
+ {
+       struct rpc_task *task;
+-      struct nfs_pgio_data *rdata;
++      struct nfs_pgio_header *hdr;
+       dprintk("%s enter\n", __func__);
+       task = container_of(work, struct rpc_task, u.tk_work);
+-      rdata = container_of(task, struct nfs_pgio_data, task);
+-      pnfs_ld_read_done(rdata);
++      hdr = container_of(task, struct nfs_pgio_header, task);
++      pnfs_ld_read_done(hdr);
+ }
+ static void
+ bl_end_par_io_read(void *data, int unused)
+ {
+-      struct nfs_pgio_data *rdata = data;
++      struct nfs_pgio_header *hdr = data;
+-      rdata->task.tk_status = rdata->header->pnfs_error;
+-      INIT_WORK(&rdata->task.u.tk_work, bl_read_cleanup);
+-      schedule_work(&rdata->task.u.tk_work);
++      hdr->task.tk_status = hdr->pnfs_error;
++      INIT_WORK(&hdr->task.u.tk_work, bl_read_cleanup);
++      schedule_work(&hdr->task.u.tk_work);
+ }
+ static enum pnfs_try_status
+-bl_read_pagelist(struct nfs_pgio_data *rdata)
++bl_read_pagelist(struct nfs_pgio_header *hdr)
+ {
+-      struct nfs_pgio_header *header = rdata->header;
++      struct nfs_pgio_header *header = hdr;
+       int i, hole;
+       struct bio *bio = NULL;
+       struct pnfs_block_extent *be = NULL, *cow_read = NULL;
+       sector_t isect, extent_length = 0;
+       struct parallel_io *par;
+-      loff_t f_offset = rdata->args.offset;
+-      size_t bytes_left = rdata->args.count;
++      loff_t f_offset = hdr->args.offset;
++      size_t bytes_left = hdr->args.count;
+       unsigned int pg_offset, pg_len;
+-      struct page **pages = rdata->args.pages;
+-      int pg_index = rdata->args.pgbase >> PAGE_CACHE_SHIFT;
++      struct page **pages = hdr->args.pages;
++      int pg_index = hdr->args.pgbase >> PAGE_CACHE_SHIFT;
+       const bool is_dio = (header->dreq != NULL);
+       dprintk("%s enter nr_pages %u offset %lld count %u\n", __func__,
+-              rdata->page_array.npages, f_offset,
+-              (unsigned int)rdata->args.count);
++              hdr->page_array.npages, f_offset,
++              (unsigned int)hdr->args.count);
+-      par = alloc_parallel(rdata);
++      par = alloc_parallel(hdr);
+       if (!par)
+               goto use_mds;
+       par->pnfs_callback = bl_end_par_io_read;
+@@ -269,7 +268,7 @@ bl_read_pagelist(struct nfs_pgio_data *r
+       isect = (sector_t) (f_offset >> SECTOR_SHIFT);
+       /* Code assumes extents are page-aligned */
+-      for (i = pg_index; i < rdata->page_array.npages; i++) {
++      for (i = pg_index; i < hdr->page_array.npages; i++) {
+               if (!extent_length) {
+                       /* We've used up the previous extent */
+                       bl_put_extent(be);
+@@ -319,7 +318,7 @@ bl_read_pagelist(struct nfs_pgio_data *r
+                       be_read = (hole && cow_read) ? cow_read : be;
+                       bio = do_add_page_to_bio(bio,
+-                                               rdata->page_array.npages - i,
++                                               hdr->page_array.npages - i,
+                                                READ,
+                                                isect, pages[i], be_read,
+                                                bl_end_io_read, par,
+@@ -334,10 +333,10 @@ bl_read_pagelist(struct nfs_pgio_data *r
+               extent_length -= PAGE_CACHE_SECTORS;
+       }
+       if ((isect << SECTOR_SHIFT) >= header->inode->i_size) {
+-              rdata->res.eof = 1;
+-              rdata->res.count = header->inode->i_size - rdata->args.offset;
++              hdr->res.eof = 1;
++              hdr->res.count = header->inode->i_size - hdr->args.offset;
+       } else {
+-              rdata->res.count = (isect << SECTOR_SHIFT) - rdata->args.offset;
++              hdr->res.count = (isect << SECTOR_SHIFT) - hdr->args.offset;
+       }
+ out:
+       bl_put_extent(be);
+@@ -392,8 +391,7 @@ static void bl_end_io_write_zero(struct
+       }
+       if (unlikely(err)) {
+-              struct nfs_pgio_data *data = par->data;
+-              struct nfs_pgio_header *header = data->header;
++              struct nfs_pgio_header *header = par->data;
+               if (!header->pnfs_error)
+                       header->pnfs_error = -EIO;
+@@ -407,8 +405,7 @@ static void bl_end_io_write(struct bio *
+ {
+       struct parallel_io *par = bio->bi_private;
+       const int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags);
+-      struct nfs_pgio_data *data = par->data;
+-      struct nfs_pgio_header *header = data->header;
++      struct nfs_pgio_header *header = par->data;
+       if (!uptodate) {
+               if (!header->pnfs_error)
+@@ -425,32 +422,32 @@ static void bl_end_io_write(struct bio *
+ static void bl_write_cleanup(struct work_struct *work)
+ {
+       struct rpc_task *task;
+-      struct nfs_pgio_data *wdata;
++      struct nfs_pgio_header *hdr;
+       dprintk("%s enter\n", __func__);
+       task = container_of(work, struct rpc_task, u.tk_work);
+-      wdata = container_of(task, struct nfs_pgio_data, task);
+-      if (likely(!wdata->header->pnfs_error)) {
++      hdr = container_of(task, struct nfs_pgio_header, task);
++      if (likely(!hdr->pnfs_error)) {
+               /* Marks for LAYOUTCOMMIT */
+-              mark_extents_written(BLK_LSEG2EXT(wdata->header->lseg),
+-                                   wdata->args.offset, wdata->args.count);
++              mark_extents_written(BLK_LSEG2EXT(hdr->lseg),
++                                   hdr->args.offset, hdr->args.count);
+       }
+-      pnfs_ld_write_done(wdata);
++      pnfs_ld_write_done(hdr);
+ }
+ /* Called when last of bios associated with a bl_write_pagelist call finishes */
+ static void bl_end_par_io_write(void *data, int num_se)
+ {
+-      struct nfs_pgio_data *wdata = data;
++      struct nfs_pgio_header *hdr = data;
+-      if (unlikely(wdata->header->pnfs_error)) {
+-              bl_free_short_extents(&BLK_LSEG2EXT(wdata->header->lseg)->bl_inval,
++      if (unlikely(hdr->pnfs_error)) {
++              bl_free_short_extents(&BLK_LSEG2EXT(hdr->lseg)->bl_inval,
+                                       num_se);
+       }
+-      wdata->task.tk_status = wdata->header->pnfs_error;
+-      wdata->writeverf.committed = NFS_FILE_SYNC;
+-      INIT_WORK(&wdata->task.u.tk_work, bl_write_cleanup);
+-      schedule_work(&wdata->task.u.tk_work);
++      hdr->task.tk_status = hdr->pnfs_error;
++      hdr->writeverf.committed = NFS_FILE_SYNC;
++      INIT_WORK(&hdr->task.u.tk_work, bl_write_cleanup);
++      schedule_work(&hdr->task.u.tk_work);
+ }
+ /* FIXME STUB - mark intersection of layout and page as bad, so is not
+@@ -675,18 +672,17 @@ check_page:
+ }
+ static enum pnfs_try_status
+-bl_write_pagelist(struct nfs_pgio_data *wdata, int sync)
++bl_write_pagelist(struct nfs_pgio_header *header, int sync)
+ {
+-      struct nfs_pgio_header *header = wdata->header;
+       int i, ret, npg_zero, pg_index, last = 0;
+       struct bio *bio = NULL;
+       struct pnfs_block_extent *be = NULL, *cow_read = NULL;
+       sector_t isect, last_isect = 0, extent_length = 0;
+       struct parallel_io *par = NULL;
+-      loff_t offset = wdata->args.offset;
+-      size_t count = wdata->args.count;
++      loff_t offset = header->args.offset;
++      size_t count = header->args.count;
+       unsigned int pg_offset, pg_len, saved_len;
+-      struct page **pages = wdata->args.pages;
++      struct page **pages = header->args.pages;
+       struct page *page;
+       pgoff_t index;
+       u64 temp;
+@@ -701,11 +697,11 @@ bl_write_pagelist(struct nfs_pgio_data *
+               dprintk("pnfsblock nonblock aligned DIO writes. Resend MDS\n");
+               goto out_mds;
+       }
+-      /* At this point, wdata->page_aray is a (sequential) list of nfs_pages.
++      /* At this point, header->page_aray is a (sequential) list of nfs_pages.
+        * We want to write each, and if there is an error set pnfs_error
+        * to have it redone using nfs.
+        */
+-      par = alloc_parallel(wdata);
++      par = alloc_parallel(header);
+       if (!par)
+               goto out_mds;
+       par->pnfs_callback = bl_end_par_io_write;
+@@ -792,8 +788,8 @@ next_page:
+       bio = bl_submit_bio(WRITE, bio);
+       /* Middle pages */
+-      pg_index = wdata->args.pgbase >> PAGE_CACHE_SHIFT;
+-      for (i = pg_index; i < wdata->page_array.npages; i++) {
++      pg_index = header->args.pgbase >> PAGE_CACHE_SHIFT;
++      for (i = pg_index; i < header->page_array.npages; i++) {
+               if (!extent_length) {
+                       /* We've used up the previous extent */
+                       bl_put_extent(be);
+@@ -864,7 +860,7 @@ next_page:
+               }
+-              bio = do_add_page_to_bio(bio, wdata->page_array.npages - i,
++              bio = do_add_page_to_bio(bio, header->page_array.npages - i,
+                                        WRITE,
+                                        isect, pages[i], be,
+                                        bl_end_io_write, par,
+@@ -893,7 +889,7 @@ next_page:
+       }
+ write_done:
+-      wdata->res.count = wdata->args.count;
++      header->res.count = header->args.count;
+ out:
+       bl_put_extent(be);
+       bl_put_extent(cow_read);
+--- a/fs/nfs/direct.c
++++ b/fs/nfs/direct.c
+@@ -148,8 +148,8 @@ static void nfs_direct_set_hdr_verf(stru
+ {
+       struct nfs_writeverf *verfp;
+-      verfp = nfs_direct_select_verf(dreq, hdr->data.ds_clp,
+-                                    hdr->data.ds_idx);
++      verfp = nfs_direct_select_verf(dreq, hdr->ds_clp,
++                                    hdr->ds_idx);
+       WARN_ON_ONCE(verfp->committed >= 0);
+       memcpy(verfp, &hdr->verf, sizeof(struct nfs_writeverf));
+       WARN_ON_ONCE(verfp->committed < 0);
+@@ -169,8 +169,8 @@ static int nfs_direct_set_or_cmp_hdr_ver
+ {
+       struct nfs_writeverf *verfp;
+-      verfp = nfs_direct_select_verf(dreq, hdr->data.ds_clp,
+-                                       hdr->data.ds_idx);
++      verfp = nfs_direct_select_verf(dreq, hdr->ds_clp,
++                                       hdr->ds_idx);
+       if (verfp->committed < 0) {
+               nfs_direct_set_hdr_verf(dreq, hdr);
+               return 0;
+--- a/fs/nfs/filelayout/filelayout.c
++++ b/fs/nfs/filelayout/filelayout.c
+@@ -84,19 +84,18 @@ filelayout_get_dserver_offset(struct pnf
+       BUG();
+ }
+-static void filelayout_reset_write(struct nfs_pgio_data *data)
++static void filelayout_reset_write(struct nfs_pgio_header *hdr)
+ {
+-      struct nfs_pgio_header *hdr = data->header;
+-      struct rpc_task *task = &data->task;
++      struct rpc_task *task = &hdr->task;
+       if (!test_and_set_bit(NFS_IOHDR_REDO, &hdr->flags)) {
+               dprintk("%s Reset task %5u for i/o through MDS "
+                       "(req %s/%llu, %u bytes @ offset %llu)\n", __func__,
+-                      data->task.tk_pid,
++                      hdr->task.tk_pid,
+                       hdr->inode->i_sb->s_id,
+                       (unsigned long long)NFS_FILEID(hdr->inode),
+-                      data->args.count,
+-                      (unsigned long long)data->args.offset);
++                      hdr->args.count,
++                      (unsigned long long)hdr->args.offset);
+               task->tk_status = pnfs_write_done_resend_to_mds(hdr->inode,
+                                                       &hdr->pages,
+@@ -105,19 +104,18 @@ static void filelayout_reset_write(struc
+       }
+ }
+-static void filelayout_reset_read(struct nfs_pgio_data *data)
++static void filelayout_reset_read(struct nfs_pgio_header *hdr)
+ {
+-      struct nfs_pgio_header *hdr = data->header;
+-      struct rpc_task *task = &data->task;
++      struct rpc_task *task = &hdr->task;
+       if (!test_and_set_bit(NFS_IOHDR_REDO, &hdr->flags)) {
+               dprintk("%s Reset task %5u for i/o through MDS "
+                       "(req %s/%llu, %u bytes @ offset %llu)\n", __func__,
+-                      data->task.tk_pid,
++                      hdr->task.tk_pid,
+                       hdr->inode->i_sb->s_id,
+                       (unsigned long long)NFS_FILEID(hdr->inode),
+-                      data->args.count,
+-                      (unsigned long long)data->args.offset);
++                      hdr->args.count,
++                      (unsigned long long)hdr->args.offset);
+               task->tk_status = pnfs_read_done_resend_to_mds(hdr->inode,
+                                                       &hdr->pages,
+@@ -243,18 +241,17 @@ wait_on_recovery:
+ /* NFS_PROTO call done callback routines */
+ static int filelayout_read_done_cb(struct rpc_task *task,
+-                              struct nfs_pgio_data *data)
++                              struct nfs_pgio_header *hdr)
+ {
+-      struct nfs_pgio_header *hdr = data->header;
+       int err;
+-      trace_nfs4_pnfs_read(data, task->tk_status);
+-      err = filelayout_async_handle_error(task, data->args.context->state,
+-                                          data->ds_clp, hdr->lseg);
++      trace_nfs4_pnfs_read(hdr, task->tk_status);
++      err = filelayout_async_handle_error(task, hdr->args.context->state,
++                                          hdr->ds_clp, hdr->lseg);
+       switch (err) {
+       case -NFS4ERR_RESET_TO_MDS:
+-              filelayout_reset_read(data);
++              filelayout_reset_read(hdr);
+               return task->tk_status;
+       case -EAGAIN:
+               rpc_restart_call_prepare(task);
+@@ -270,15 +267,14 @@ static int filelayout_read_done_cb(struc
+  * rfc5661 is not clear about which credential should be used.
+  */
+ static void
+-filelayout_set_layoutcommit(struct nfs_pgio_data *wdata)
++filelayout_set_layoutcommit(struct nfs_pgio_header *hdr)
+ {
+-      struct nfs_pgio_header *hdr = wdata->header;
+       if (FILELAYOUT_LSEG(hdr->lseg)->commit_through_mds ||
+-          wdata->res.verf->committed == NFS_FILE_SYNC)
++          hdr->res.verf->committed == NFS_FILE_SYNC)
+               return;
+-      pnfs_set_layoutcommit(wdata);
++      pnfs_set_layoutcommit(hdr);
+       dprintk("%s inode %lu pls_end_pos %lu\n", __func__, hdr->inode->i_ino,
+               (unsigned long) NFS_I(hdr->inode)->layout->plh_lwb);
+ }
+@@ -305,83 +301,82 @@ filelayout_reset_to_mds(struct pnfs_layo
+  */
+ static void filelayout_read_prepare(struct rpc_task *task, void *data)
+ {
+-      struct nfs_pgio_data *rdata = data;
++      struct nfs_pgio_header *hdr = data;
+-      if (unlikely(test_bit(NFS_CONTEXT_BAD, &rdata->args.context->flags))) {
++      if (unlikely(test_bit(NFS_CONTEXT_BAD, &hdr->args.context->flags))) {
+               rpc_exit(task, -EIO);
+               return;
+       }
+-      if (filelayout_reset_to_mds(rdata->header->lseg)) {
++      if (filelayout_reset_to_mds(hdr->lseg)) {
+               dprintk("%s task %u reset io to MDS\n", __func__, task->tk_pid);
+-              filelayout_reset_read(rdata);
++              filelayout_reset_read(hdr);
+               rpc_exit(task, 0);
+               return;
+       }
+-      rdata->pgio_done_cb = filelayout_read_done_cb;
++      hdr->pgio_done_cb = filelayout_read_done_cb;
+-      if (nfs41_setup_sequence(rdata->ds_clp->cl_session,
+-                      &rdata->args.seq_args,
+-                      &rdata->res.seq_res,
++      if (nfs41_setup_sequence(hdr->ds_clp->cl_session,
++                      &hdr->args.seq_args,
++                      &hdr->res.seq_res,
+                       task))
+               return;
+-      if (nfs4_set_rw_stateid(&rdata->args.stateid, rdata->args.context,
+-                      rdata->args.lock_context, FMODE_READ) == -EIO)
++      if (nfs4_set_rw_stateid(&hdr->args.stateid, hdr->args.context,
++                      hdr->args.lock_context, FMODE_READ) == -EIO)
+               rpc_exit(task, -EIO); /* lost lock, terminate I/O */
+ }
+ static void filelayout_read_call_done(struct rpc_task *task, void *data)
+ {
+-      struct nfs_pgio_data *rdata = data;
++      struct nfs_pgio_header *hdr = data;
+       dprintk("--> %s task->tk_status %d\n", __func__, task->tk_status);
+-      if (test_bit(NFS_IOHDR_REDO, &rdata->header->flags) &&
++      if (test_bit(NFS_IOHDR_REDO, &hdr->flags) &&
+           task->tk_status == 0) {
+-              nfs41_sequence_done(task, &rdata->res.seq_res);
++              nfs41_sequence_done(task, &hdr->res.seq_res);
+               return;
+       }
+       /* Note this may cause RPC to be resent */
+-      rdata->header->mds_ops->rpc_call_done(task, data);
++      hdr->mds_ops->rpc_call_done(task, data);
+ }
+ static void filelayout_read_count_stats(struct rpc_task *task, void *data)
+ {
+-      struct nfs_pgio_data *rdata = data;
++      struct nfs_pgio_header *hdr = data;
+-      rpc_count_iostats(task, NFS_SERVER(rdata->header->inode)->client->cl_metrics);
++      rpc_count_iostats(task, NFS_SERVER(hdr->inode)->client->cl_metrics);
+ }
+ static void filelayout_read_release(void *data)
+ {
+-      struct nfs_pgio_data *rdata = data;
+-      struct pnfs_layout_hdr *lo = rdata->header->lseg->pls_layout;
++      struct nfs_pgio_header *hdr = data;
++      struct pnfs_layout_hdr *lo = hdr->lseg->pls_layout;
+       filelayout_fenceme(lo->plh_inode, lo);
+-      nfs_put_client(rdata->ds_clp);
+-      rdata->header->mds_ops->rpc_release(data);
++      nfs_put_client(hdr->ds_clp);
++      hdr->mds_ops->rpc_release(data);
+ }
+ static int filelayout_write_done_cb(struct rpc_task *task,
+-                              struct nfs_pgio_data *data)
++                              struct nfs_pgio_header *hdr)
+ {
+-      struct nfs_pgio_header *hdr = data->header;
+       int err;
+-      trace_nfs4_pnfs_write(data, task->tk_status);
+-      err = filelayout_async_handle_error(task, data->args.context->state,
+-                                          data->ds_clp, hdr->lseg);
++      trace_nfs4_pnfs_write(hdr, task->tk_status);
++      err = filelayout_async_handle_error(task, hdr->args.context->state,
++                                          hdr->ds_clp, hdr->lseg);
+       switch (err) {
+       case -NFS4ERR_RESET_TO_MDS:
+-              filelayout_reset_write(data);
++              filelayout_reset_write(hdr);
+               return task->tk_status;
+       case -EAGAIN:
+               rpc_restart_call_prepare(task);
+               return -EAGAIN;
+       }
+-      filelayout_set_layoutcommit(data);
++      filelayout_set_layoutcommit(hdr);
+       return 0;
+ }
+@@ -419,57 +414,57 @@ static int filelayout_commit_done_cb(str
+ static void filelayout_write_prepare(struct rpc_task *task, void *data)
+ {
+-      struct nfs_pgio_data *wdata = data;
++      struct nfs_pgio_header *hdr = data;
+-      if (unlikely(test_bit(NFS_CONTEXT_BAD, &wdata->args.context->flags))) {
++      if (unlikely(test_bit(NFS_CONTEXT_BAD, &hdr->args.context->flags))) {
+               rpc_exit(task, -EIO);
+               return;
+       }
+-      if (filelayout_reset_to_mds(wdata->header->lseg)) {
++      if (filelayout_reset_to_mds(hdr->lseg)) {
+               dprintk("%s task %u reset io to MDS\n", __func__, task->tk_pid);
+-              filelayout_reset_write(wdata);
++              filelayout_reset_write(hdr);
+               rpc_exit(task, 0);
+               return;
+       }
+-      if (nfs41_setup_sequence(wdata->ds_clp->cl_session,
+-                      &wdata->args.seq_args,
+-                      &wdata->res.seq_res,
++      if (nfs41_setup_sequence(hdr->ds_clp->cl_session,
++                      &hdr->args.seq_args,
++                      &hdr->res.seq_res,
+                       task))
+               return;
+-      if (nfs4_set_rw_stateid(&wdata->args.stateid, wdata->args.context,
+-                      wdata->args.lock_context, FMODE_WRITE) == -EIO)
++      if (nfs4_set_rw_stateid(&hdr->args.stateid, hdr->args.context,
++                      hdr->args.lock_context, FMODE_WRITE) == -EIO)
+               rpc_exit(task, -EIO); /* lost lock, terminate I/O */
+ }
+ static void filelayout_write_call_done(struct rpc_task *task, void *data)
+ {
+-      struct nfs_pgio_data *wdata = data;
++      struct nfs_pgio_header *hdr = data;
+-      if (test_bit(NFS_IOHDR_REDO, &wdata->header->flags) &&
++      if (test_bit(NFS_IOHDR_REDO, &hdr->flags) &&
+           task->tk_status == 0) {
+-              nfs41_sequence_done(task, &wdata->res.seq_res);
++              nfs41_sequence_done(task, &hdr->res.seq_res);
+               return;
+       }
+       /* Note this may cause RPC to be resent */
+-      wdata->header->mds_ops->rpc_call_done(task, data);
++      hdr->mds_ops->rpc_call_done(task, data);
+ }
+ static void filelayout_write_count_stats(struct rpc_task *task, void *data)
+ {
+-      struct nfs_pgio_data *wdata = data;
++      struct nfs_pgio_header *hdr = data;
+-      rpc_count_iostats(task, NFS_SERVER(wdata->header->inode)->client->cl_metrics);
++      rpc_count_iostats(task, NFS_SERVER(hdr->inode)->client->cl_metrics);
+ }
+ static void filelayout_write_release(void *data)
+ {
+-      struct nfs_pgio_data *wdata = data;
+-      struct pnfs_layout_hdr *lo = wdata->header->lseg->pls_layout;
++      struct nfs_pgio_header *hdr = data;
++      struct pnfs_layout_hdr *lo = hdr->lseg->pls_layout;
+       filelayout_fenceme(lo->plh_inode, lo);
+-      nfs_put_client(wdata->ds_clp);
+-      wdata->header->mds_ops->rpc_release(data);
++      nfs_put_client(hdr->ds_clp);
++      hdr->mds_ops->rpc_release(data);
+ }
+ static void filelayout_commit_prepare(struct rpc_task *task, void *data)
+@@ -529,19 +524,18 @@ static const struct rpc_call_ops filelay
+ };
+ static enum pnfs_try_status
+-filelayout_read_pagelist(struct nfs_pgio_data *data)
++filelayout_read_pagelist(struct nfs_pgio_header *hdr)
+ {
+-      struct nfs_pgio_header *hdr = data->header;
+       struct pnfs_layout_segment *lseg = hdr->lseg;
+       struct nfs4_pnfs_ds *ds;
+       struct rpc_clnt *ds_clnt;
+-      loff_t offset = data->args.offset;
++      loff_t offset = hdr->args.offset;
+       u32 j, idx;
+       struct nfs_fh *fh;
+       dprintk("--> %s ino %lu pgbase %u req %Zu@%llu\n",
+               __func__, hdr->inode->i_ino,
+-              data->args.pgbase, (size_t)data->args.count, offset);
++              hdr->args.pgbase, (size_t)hdr->args.count, offset);
+       /* Retrieve the correct rpc_client for the byte range */
+       j = nfs4_fl_calc_j_index(lseg, offset);
+@@ -559,30 +553,29 @@ filelayout_read_pagelist(struct nfs_pgio
+       /* No multipath support. Use first DS */
+       atomic_inc(&ds->ds_clp->cl_count);
+-      data->ds_clp = ds->ds_clp;
+-      data->ds_idx = idx;
++      hdr->ds_clp = ds->ds_clp;
++      hdr->ds_idx = idx;
+       fh = nfs4_fl_select_ds_fh(lseg, j);
+       if (fh)
+-              data->args.fh = fh;
++              hdr->args.fh = fh;
+-      data->args.offset = filelayout_get_dserver_offset(lseg, offset);
+-      data->mds_offset = offset;
++      hdr->args.offset = filelayout_get_dserver_offset(lseg, offset);
++      hdr->mds_offset = offset;
+       /* Perform an asynchronous read to ds */
+-      nfs_initiate_pgio(ds_clnt, data,
++      nfs_initiate_pgio(ds_clnt, hdr,
+                           &filelayout_read_call_ops, 0, RPC_TASK_SOFTCONN);
+       return PNFS_ATTEMPTED;
+ }
+ /* Perform async writes. */
+ static enum pnfs_try_status
+-filelayout_write_pagelist(struct nfs_pgio_data *data, int sync)
++filelayout_write_pagelist(struct nfs_pgio_header *hdr, int sync)
+ {
+-      struct nfs_pgio_header *hdr = data->header;
+       struct pnfs_layout_segment *lseg = hdr->lseg;
+       struct nfs4_pnfs_ds *ds;
+       struct rpc_clnt *ds_clnt;
+-      loff_t offset = data->args.offset;
++      loff_t offset = hdr->args.offset;
+       u32 j, idx;
+       struct nfs_fh *fh;
+@@ -598,21 +591,20 @@ filelayout_write_pagelist(struct nfs_pgi
+               return PNFS_NOT_ATTEMPTED;
+       dprintk("%s ino %lu sync %d req %Zu@%llu DS: %s cl_count %d\n",
+-              __func__, hdr->inode->i_ino, sync, (size_t) data->args.count,
++              __func__, hdr->inode->i_ino, sync, (size_t) hdr->args.count,
+               offset, ds->ds_remotestr, atomic_read(&ds->ds_clp->cl_count));
+-      data->pgio_done_cb = filelayout_write_done_cb;
++      hdr->pgio_done_cb = filelayout_write_done_cb;
+       atomic_inc(&ds->ds_clp->cl_count);
+-      data->ds_clp = ds->ds_clp;
+-      data->ds_idx = idx;
++      hdr->ds_clp = ds->ds_clp;
++      hdr->ds_idx = idx;
+       fh = nfs4_fl_select_ds_fh(lseg, j);
+       if (fh)
+-              data->args.fh = fh;
+-
+-      data->args.offset = filelayout_get_dserver_offset(lseg, offset);
++              hdr->args.fh = fh;
++      hdr->args.offset = filelayout_get_dserver_offset(lseg, offset);
+       /* Perform an asynchronous write */
+-      nfs_initiate_pgio(ds_clnt, data,
++      nfs_initiate_pgio(ds_clnt, hdr,
+                                   &filelayout_write_call_ops, sync,
+                                   RPC_TASK_SOFTCONN);
+       return PNFS_ATTEMPTED;
+--- a/fs/nfs/internal.h
++++ b/fs/nfs/internal.h
+@@ -240,9 +240,9 @@ int nfs_iocounter_wait(struct nfs_io_cou
+ extern const struct nfs_pageio_ops nfs_pgio_rw_ops;
+ struct nfs_pgio_header *nfs_pgio_header_alloc(const struct nfs_rw_ops *);
+ void nfs_pgio_header_free(struct nfs_pgio_header *);
+-void nfs_pgio_data_destroy(struct nfs_pgio_data *);
++void nfs_pgio_data_destroy(struct nfs_pgio_header *);
+ int nfs_generic_pgio(struct nfs_pageio_descriptor *, struct nfs_pgio_header *);
+-int nfs_initiate_pgio(struct rpc_clnt *, struct nfs_pgio_data *,
++int nfs_initiate_pgio(struct rpc_clnt *, struct nfs_pgio_header *,
+                     const struct rpc_call_ops *, int, int);
+ void nfs_free_request(struct nfs_page *req);
+@@ -482,7 +482,7 @@ static inline void nfs_inode_dio_wait(st
+ extern ssize_t nfs_dreq_bytes_left(struct nfs_direct_req *dreq);
+ /* nfs4proc.c */
+-extern void __nfs4_read_done_cb(struct nfs_pgio_data *);
++extern void __nfs4_read_done_cb(struct nfs_pgio_header *);
+ extern struct nfs_client *nfs4_init_client(struct nfs_client *clp,
+                           const struct rpc_timeout *timeparms,
+                           const char *ip_addr);
+--- a/fs/nfs/nfs3proc.c
++++ b/fs/nfs/nfs3proc.c
+@@ -795,41 +795,44 @@ nfs3_proc_pathconf(struct nfs_server *se
+       return status;
+ }
+-static int nfs3_read_done(struct rpc_task *task, struct nfs_pgio_data *data)
++static int nfs3_read_done(struct rpc_task *task, struct nfs_pgio_header *hdr)
+ {
+-      struct inode *inode = data->header->inode;
++      struct inode *inode = hdr->inode;
+       if (nfs3_async_handle_jukebox(task, inode))
+               return -EAGAIN;
+       nfs_invalidate_atime(inode);
+-      nfs_refresh_inode(inode, &data->fattr);
++      nfs_refresh_inode(inode, &hdr->fattr);
+       return 0;
+ }
+-static void nfs3_proc_read_setup(struct nfs_pgio_data *data, struct rpc_message *msg)
++static void nfs3_proc_read_setup(struct nfs_pgio_header *hdr,
++                               struct rpc_message *msg)
+ {
+       msg->rpc_proc = &nfs3_procedures[NFS3PROC_READ];
+ }
+-static int nfs3_proc_pgio_rpc_prepare(struct rpc_task *task, struct nfs_pgio_data *data)
++static int nfs3_proc_pgio_rpc_prepare(struct rpc_task *task,
++                                    struct nfs_pgio_header *hdr)
+ {
+       rpc_call_start(task);
+       return 0;
+ }
+-static int nfs3_write_done(struct rpc_task *task, struct nfs_pgio_data *data)
++static int nfs3_write_done(struct rpc_task *task, struct nfs_pgio_header *hdr)
+ {
+-      struct inode *inode = data->header->inode;
++      struct inode *inode = hdr->inode;
+       if (nfs3_async_handle_jukebox(task, inode))
+               return -EAGAIN;
+       if (task->tk_status >= 0)
+-              nfs_post_op_update_inode_force_wcc(inode, data->res.fattr);
++              nfs_post_op_update_inode_force_wcc(inode, hdr->res.fattr);
+       return 0;
+ }
+-static void nfs3_proc_write_setup(struct nfs_pgio_data *data, struct rpc_message *msg)
++static void nfs3_proc_write_setup(struct nfs_pgio_header *hdr,
++                                struct rpc_message *msg)
+ {
+       msg->rpc_proc = &nfs3_procedures[NFS3PROC_WRITE];
+ }
+--- a/fs/nfs/nfs4_fs.h
++++ b/fs/nfs/nfs4_fs.h
+@@ -337,11 +337,11 @@ nfs4_state_protect(struct nfs_client *cl
+  */
+ static inline void
+ nfs4_state_protect_write(struct nfs_client *clp, struct rpc_clnt **clntp,
+-                       struct rpc_message *msg, struct nfs_pgio_data *wdata)
++                       struct rpc_message *msg, struct nfs_pgio_header *hdr)
+ {
+       if (_nfs4_state_protect(clp, NFS_SP4_MACH_CRED_WRITE, clntp, msg) &&
+           !test_bit(NFS_SP4_MACH_CRED_COMMIT, &clp->cl_sp4_flags))
+-              wdata->args.stable = NFS_FILE_SYNC;
++              hdr->args.stable = NFS_FILE_SYNC;
+ }
+ #else /* CONFIG_NFS_v4_1 */
+ static inline struct nfs4_session *nfs4_get_session(const struct nfs_server *server)
+@@ -369,7 +369,7 @@ nfs4_state_protect(struct nfs_client *cl
+ static inline void
+ nfs4_state_protect_write(struct nfs_client *clp, struct rpc_clnt **clntp,
+-                       struct rpc_message *msg, struct nfs_pgio_data *wdata)
++                       struct rpc_message *msg, struct nfs_pgio_header *hdr)
+ {
+ }
+ #endif /* CONFIG_NFS_V4_1 */
+--- a/fs/nfs/nfs4proc.c
++++ b/fs/nfs/nfs4proc.c
+@@ -4041,24 +4041,25 @@ static bool nfs4_error_stateid_expired(i
+       return false;
+ }
+-void __nfs4_read_done_cb(struct nfs_pgio_data *data)
++void __nfs4_read_done_cb(struct nfs_pgio_header *hdr)
+ {
+-      nfs_invalidate_atime(data->header->inode);
++      nfs_invalidate_atime(hdr->inode);
+ }
+-static int nfs4_read_done_cb(struct rpc_task *task, struct nfs_pgio_data *data)
++static int nfs4_read_done_cb(struct rpc_task *task, struct nfs_pgio_header *hdr)
+ {
+-      struct nfs_server *server = NFS_SERVER(data->header->inode);
++      struct nfs_server *server = NFS_SERVER(hdr->inode);
+-      trace_nfs4_read(data, task->tk_status);
+-      if (nfs4_async_handle_error(task, server, data->args.context->state) == -EAGAIN) {
++      trace_nfs4_read(hdr, task->tk_status);
++      if (nfs4_async_handle_error(task, server,
++                                  hdr->args.context->state) == -EAGAIN) {
+               rpc_restart_call_prepare(task);
+               return -EAGAIN;
+       }
+-      __nfs4_read_done_cb(data);
++      __nfs4_read_done_cb(hdr);
+       if (task->tk_status > 0)
+-              renew_lease(server, data->timestamp);
++              renew_lease(server, hdr->timestamp);
+       return 0;
+ }
+@@ -4076,54 +4077,59 @@ static bool nfs4_read_stateid_changed(st
+       return true;
+ }
+-static int nfs4_read_done(struct rpc_task *task, struct nfs_pgio_data *data)
++static int nfs4_read_done(struct rpc_task *task, struct nfs_pgio_header *hdr)
+ {
+       dprintk("--> %s\n", __func__);
+-      if (!nfs4_sequence_done(task, &data->res.seq_res))
++      if (!nfs4_sequence_done(task, &hdr->res.seq_res))
+               return -EAGAIN;
+-      if (nfs4_read_stateid_changed(task, &data->args))
++      if (nfs4_read_stateid_changed(task, &hdr->args))
+               return -EAGAIN;
+-      return data->pgio_done_cb ? data->pgio_done_cb(task, data) :
+-                                  nfs4_read_done_cb(task, data);
++      return hdr->pgio_done_cb ? hdr->pgio_done_cb(task, hdr) :
++                                  nfs4_read_done_cb(task, hdr);
+ }
+-static void nfs4_proc_read_setup(struct nfs_pgio_data *data, struct rpc_message *msg)
++static void nfs4_proc_read_setup(struct nfs_pgio_header *hdr,
++                               struct rpc_message *msg)
+ {
+-      data->timestamp   = jiffies;
+-      data->pgio_done_cb = nfs4_read_done_cb;
++      hdr->timestamp   = jiffies;
++      hdr->pgio_done_cb = nfs4_read_done_cb;
+       msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READ];
+-      nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 0);
++      nfs4_init_sequence(&hdr->args.seq_args, &hdr->res.seq_res, 0);
+ }
+-static int nfs4_proc_pgio_rpc_prepare(struct rpc_task *task, struct nfs_pgio_data *data)
++static int nfs4_proc_pgio_rpc_prepare(struct rpc_task *task,
++                                    struct nfs_pgio_header *hdr)
+ {
+-      if (nfs4_setup_sequence(NFS_SERVER(data->header->inode),
+-                      &data->args.seq_args,
+-                      &data->res.seq_res,
++      if (nfs4_setup_sequence(NFS_SERVER(hdr->inode),
++                      &hdr->args.seq_args,
++                      &hdr->res.seq_res,
+                       task))
+               return 0;
+-      if (nfs4_set_rw_stateid(&data->args.stateid, data->args.context,
+-                              data->args.lock_context, data->header->rw_ops->rw_mode) == -EIO)
++      if (nfs4_set_rw_stateid(&hdr->args.stateid, hdr->args.context,
++                              hdr->args.lock_context,
++                              hdr->rw_ops->rw_mode) == -EIO)
+               return -EIO;
+-      if (unlikely(test_bit(NFS_CONTEXT_BAD, &data->args.context->flags)))
++      if (unlikely(test_bit(NFS_CONTEXT_BAD, &hdr->args.context->flags)))
+               return -EIO;
+       return 0;
+ }
+-static int nfs4_write_done_cb(struct rpc_task *task, struct nfs_pgio_data *data)
++static int nfs4_write_done_cb(struct rpc_task *task,
++                            struct nfs_pgio_header *hdr)
+ {
+-      struct inode *inode = data->header->inode;
++      struct inode *inode = hdr->inode;
+       
+-      trace_nfs4_write(data, task->tk_status);
+-      if (nfs4_async_handle_error(task, NFS_SERVER(inode), data->args.context->state) == -EAGAIN) {
++      trace_nfs4_write(hdr, task->tk_status);
++      if (nfs4_async_handle_error(task, NFS_SERVER(inode),
++                                  hdr->args.context->state) == -EAGAIN) {
+               rpc_restart_call_prepare(task);
+               return -EAGAIN;
+       }
+       if (task->tk_status >= 0) {
+-              renew_lease(NFS_SERVER(inode), data->timestamp);
+-              nfs_post_op_update_inode_force_wcc(inode, &data->fattr);
++              renew_lease(NFS_SERVER(inode), hdr->timestamp);
++              nfs_post_op_update_inode_force_wcc(inode, &hdr->fattr);
+       }
+       return 0;
+ }
+@@ -4142,23 +4148,21 @@ static bool nfs4_write_stateid_changed(s
+       return true;
+ }
+-static int nfs4_write_done(struct rpc_task *task, struct nfs_pgio_data *data)
++static int nfs4_write_done(struct rpc_task *task, struct nfs_pgio_header *hdr)
+ {
+-      if (!nfs4_sequence_done(task, &data->res.seq_res))
++      if (!nfs4_sequence_done(task, &hdr->res.seq_res))
+               return -EAGAIN;
+-      if (nfs4_write_stateid_changed(task, &data->args))
++      if (nfs4_write_stateid_changed(task, &hdr->args))
+               return -EAGAIN;
+-      return data->pgio_done_cb ? data->pgio_done_cb(task, data) :
+-              nfs4_write_done_cb(task, data);
++      return hdr->pgio_done_cb ? hdr->pgio_done_cb(task, hdr) :
++              nfs4_write_done_cb(task, hdr);
+ }
+ static
+-bool nfs4_write_need_cache_consistency_data(const struct nfs_pgio_data *data)
++bool nfs4_write_need_cache_consistency_data(struct nfs_pgio_header *hdr)
+ {
+-      const struct nfs_pgio_header *hdr = data->header;
+-
+       /* Don't request attributes for pNFS or O_DIRECT writes */
+-      if (data->ds_clp != NULL || hdr->dreq != NULL)
++      if (hdr->ds_clp != NULL || hdr->dreq != NULL)
+               return false;
+       /* Otherwise, request attributes if and only if we don't hold
+        * a delegation
+@@ -4166,23 +4170,24 @@ bool nfs4_write_need_cache_consistency_d
+       return nfs4_have_delegation(hdr->inode, FMODE_READ) == 0;
+ }
+-static void nfs4_proc_write_setup(struct nfs_pgio_data *data, struct rpc_message *msg)
++static void nfs4_proc_write_setup(struct nfs_pgio_header *hdr,
++                                struct rpc_message *msg)
+ {
+-      struct nfs_server *server = NFS_SERVER(data->header->inode);
++      struct nfs_server *server = NFS_SERVER(hdr->inode);
+-      if (!nfs4_write_need_cache_consistency_data(data)) {
+-              data->args.bitmask = NULL;
+-              data->res.fattr = NULL;
++      if (!nfs4_write_need_cache_consistency_data(hdr)) {
++              hdr->args.bitmask = NULL;
++              hdr->res.fattr = NULL;
+       } else
+-              data->args.bitmask = server->cache_consistency_bitmask;
++              hdr->args.bitmask = server->cache_consistency_bitmask;
+-      if (!data->pgio_done_cb)
+-              data->pgio_done_cb = nfs4_write_done_cb;
+-      data->res.server = server;
+-      data->timestamp   = jiffies;
++      if (!hdr->pgio_done_cb)
++              hdr->pgio_done_cb = nfs4_write_done_cb;
++      hdr->res.server = server;
++      hdr->timestamp   = jiffies;
+       msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_WRITE];
+-      nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 1);
++      nfs4_init_sequence(&hdr->args.seq_args, &hdr->res.seq_res, 1);
+ }
+ static void nfs4_proc_commit_rpc_prepare(struct rpc_task *task, struct nfs_commit_data *data)
+--- a/fs/nfs/nfs4trace.h
++++ b/fs/nfs/nfs4trace.h
+@@ -932,11 +932,11 @@ DEFINE_NFS4_IDMAP_EVENT(nfs4_map_gid_to_
+ DECLARE_EVENT_CLASS(nfs4_read_event,
+               TP_PROTO(
+-                      const struct nfs_pgio_data *data,
++                      const struct nfs_pgio_header *hdr,
+                       int error
+               ),
+-              TP_ARGS(data, error),
++              TP_ARGS(hdr, error),
+               TP_STRUCT__entry(
+                       __field(dev_t, dev)
+@@ -948,12 +948,12 @@ DECLARE_EVENT_CLASS(nfs4_read_event,
+               ),
+               TP_fast_assign(
+-                      const struct inode *inode = data->header->inode;
++                      const struct inode *inode = hdr->inode;
+                       __entry->dev = inode->i_sb->s_dev;
+                       __entry->fileid = NFS_FILEID(inode);
+                       __entry->fhandle = nfs_fhandle_hash(NFS_FH(inode));
+-                      __entry->offset = data->args.offset;
+-                      __entry->count = data->args.count;
++                      __entry->offset = hdr->args.offset;
++                      __entry->count = hdr->args.count;
+                       __entry->error = error;
+               ),
+@@ -972,10 +972,10 @@ DECLARE_EVENT_CLASS(nfs4_read_event,
+ #define DEFINE_NFS4_READ_EVENT(name) \
+       DEFINE_EVENT(nfs4_read_event, name, \
+                       TP_PROTO( \
+-                              const struct nfs_pgio_data *data, \
++                              const struct nfs_pgio_header *hdr, \
+                               int error \
+                       ), \
+-                      TP_ARGS(data, error))
++                      TP_ARGS(hdr, error))
+ DEFINE_NFS4_READ_EVENT(nfs4_read);
+ #ifdef CONFIG_NFS_V4_1
+ DEFINE_NFS4_READ_EVENT(nfs4_pnfs_read);
+@@ -983,11 +983,11 @@ DEFINE_NFS4_READ_EVENT(nfs4_pnfs_read);
+ DECLARE_EVENT_CLASS(nfs4_write_event,
+               TP_PROTO(
+-                      const struct nfs_pgio_data *data,
++                      const struct nfs_pgio_header *hdr,
+                       int error
+               ),
+-              TP_ARGS(data, error),
++              TP_ARGS(hdr, error),
+               TP_STRUCT__entry(
+                       __field(dev_t, dev)
+@@ -999,12 +999,12 @@ DECLARE_EVENT_CLASS(nfs4_write_event,
+               ),
+               TP_fast_assign(
+-                      const struct inode *inode = data->header->inode;
++                      const struct inode *inode = hdr->inode;
+                       __entry->dev = inode->i_sb->s_dev;
+                       __entry->fileid = NFS_FILEID(inode);
+                       __entry->fhandle = nfs_fhandle_hash(NFS_FH(inode));
+-                      __entry->offset = data->args.offset;
+-                      __entry->count = data->args.count;
++                      __entry->offset = hdr->args.offset;
++                      __entry->count = hdr->args.count;
+                       __entry->error = error;
+               ),
+@@ -1024,10 +1024,10 @@ DECLARE_EVENT_CLASS(nfs4_write_event,
+ #define DEFINE_NFS4_WRITE_EVENT(name) \
+       DEFINE_EVENT(nfs4_write_event, name, \
+                       TP_PROTO( \
+-                              const struct nfs_pgio_data *data, \
++                              const struct nfs_pgio_header *hdr, \
+                               int error \
+                       ), \
+-                      TP_ARGS(data, error))
++                      TP_ARGS(hdr, error))
+ DEFINE_NFS4_WRITE_EVENT(nfs4_write);
+ #ifdef CONFIG_NFS_V4_1
+ DEFINE_NFS4_WRITE_EVENT(nfs4_pnfs_write);
+--- a/fs/nfs/objlayout/objio_osd.c
++++ b/fs/nfs/objlayout/objio_osd.c
+@@ -439,22 +439,21 @@ static void _read_done(struct ore_io_sta
+       objlayout_read_done(&objios->oir, status, objios->sync);
+ }
+-int objio_read_pagelist(struct nfs_pgio_data *rdata)
++int objio_read_pagelist(struct nfs_pgio_header *hdr)
+ {
+-      struct nfs_pgio_header *hdr = rdata->header;
+       struct objio_state *objios;
+       int ret;
+       ret = objio_alloc_io_state(NFS_I(hdr->inode)->layout, true,
+-                      hdr->lseg, rdata->args.pages, rdata->args.pgbase,
+-                      rdata->args.offset, rdata->args.count, rdata,
++                      hdr->lseg, hdr->args.pages, hdr->args.pgbase,
++                      hdr->args.offset, hdr->args.count, hdr,
+                       GFP_KERNEL, &objios);
+       if (unlikely(ret))
+               return ret;
+       objios->ios->done = _read_done;
+       dprintk("%s: offset=0x%llx length=0x%x\n", __func__,
+-              rdata->args.offset, rdata->args.count);
++              hdr->args.offset, hdr->args.count);
+       ret = ore_read(objios->ios);
+       if (unlikely(ret))
+               objio_free_result(&objios->oir);
+@@ -487,11 +486,11 @@ static void _write_done(struct ore_io_st
+ static struct page *__r4w_get_page(void *priv, u64 offset, bool *uptodate)
+ {
+       struct objio_state *objios = priv;
+-      struct nfs_pgio_data *wdata = objios->oir.rpcdata;
+-      struct address_space *mapping = wdata->header->inode->i_mapping;
++      struct nfs_pgio_header *hdr = objios->oir.rpcdata;
++      struct address_space *mapping = hdr->inode->i_mapping;
+       pgoff_t index = offset / PAGE_SIZE;
+       struct page *page;
+-      loff_t i_size = i_size_read(wdata->header->inode);
++      loff_t i_size = i_size_read(hdr->inode);
+       if (offset >= i_size) {
+               *uptodate = true;
+@@ -531,15 +530,14 @@ static const struct _ore_r4w_op _r4w_op
+       .put_page = &__r4w_put_page,
+ };
+-int objio_write_pagelist(struct nfs_pgio_data *wdata, int how)
++int objio_write_pagelist(struct nfs_pgio_header *hdr, int how)
+ {
+-      struct nfs_pgio_header *hdr = wdata->header;
+       struct objio_state *objios;
+       int ret;
+       ret = objio_alloc_io_state(NFS_I(hdr->inode)->layout, false,
+-                      hdr->lseg, wdata->args.pages, wdata->args.pgbase,
+-                      wdata->args.offset, wdata->args.count, wdata, GFP_NOFS,
++                      hdr->lseg, hdr->args.pages, hdr->args.pgbase,
++                      hdr->args.offset, hdr->args.count, hdr, GFP_NOFS,
+                       &objios);
+       if (unlikely(ret))
+               return ret;
+@@ -551,7 +549,7 @@ int objio_write_pagelist(struct nfs_pgio
+               objios->ios->done = _write_done;
+       dprintk("%s: offset=0x%llx length=0x%x\n", __func__,
+-              wdata->args.offset, wdata->args.count);
++              hdr->args.offset, hdr->args.count);
+       ret = ore_write(objios->ios);
+       if (unlikely(ret)) {
+               objio_free_result(&objios->oir);
+--- a/fs/nfs/objlayout/objlayout.c
++++ b/fs/nfs/objlayout/objlayout.c
+@@ -229,36 +229,36 @@ objlayout_io_set_result(struct objlayout
+ static void _rpc_read_complete(struct work_struct *work)
+ {
+       struct rpc_task *task;
+-      struct nfs_pgio_data *rdata;
++      struct nfs_pgio_header *hdr;
+       dprintk("%s enter\n", __func__);
+       task = container_of(work, struct rpc_task, u.tk_work);
+-      rdata = container_of(task, struct nfs_pgio_data, task);
++      hdr = container_of(task, struct nfs_pgio_header, task);
+-      pnfs_ld_read_done(rdata);
++      pnfs_ld_read_done(hdr);
+ }
+ void
+ objlayout_read_done(struct objlayout_io_res *oir, ssize_t status, bool sync)
+ {
+-      struct nfs_pgio_data *rdata = oir->rpcdata;
++      struct nfs_pgio_header *hdr = oir->rpcdata;
+-      oir->status = rdata->task.tk_status = status;
++      oir->status = hdr->task.tk_status = status;
+       if (status >= 0)
+-              rdata->res.count = status;
++              hdr->res.count = status;
+       else
+-              rdata->header->pnfs_error = status;
++              hdr->pnfs_error = status;
+       objlayout_iodone(oir);
+       /* must not use oir after this point */
+       dprintk("%s: Return status=%zd eof=%d sync=%d\n", __func__,
+-              status, rdata->res.eof, sync);
++              status, hdr->res.eof, sync);
+       if (sync)
+-              pnfs_ld_read_done(rdata);
++              pnfs_ld_read_done(hdr);
+       else {
+-              INIT_WORK(&rdata->task.u.tk_work, _rpc_read_complete);
+-              schedule_work(&rdata->task.u.tk_work);
++              INIT_WORK(&hdr->task.u.tk_work, _rpc_read_complete);
++              schedule_work(&hdr->task.u.tk_work);
+       }
+ }
+@@ -266,12 +266,11 @@ objlayout_read_done(struct objlayout_io_
+  * Perform sync or async reads.
+  */
+ enum pnfs_try_status
+-objlayout_read_pagelist(struct nfs_pgio_data *rdata)
++objlayout_read_pagelist(struct nfs_pgio_header *hdr)
+ {
+-      struct nfs_pgio_header *hdr = rdata->header;
+       struct inode *inode = hdr->inode;
+-      loff_t offset = rdata->args.offset;
+-      size_t count = rdata->args.count;
++      loff_t offset = hdr->args.offset;
++      size_t count = hdr->args.count;
+       int err;
+       loff_t eof;
+@@ -279,23 +278,23 @@ objlayout_read_pagelist(struct nfs_pgio_
+       if (unlikely(offset + count > eof)) {
+               if (offset >= eof) {
+                       err = 0;
+-                      rdata->res.count = 0;
+-                      rdata->res.eof = 1;
++                      hdr->res.count = 0;
++                      hdr->res.eof = 1;
+                       /*FIXME: do we need to call pnfs_ld_read_done() */
+                       goto out;
+               }
+               count = eof - offset;
+       }
+-      rdata->res.eof = (offset + count) >= eof;
+-      _fix_verify_io_params(hdr->lseg, &rdata->args.pages,
+-                            &rdata->args.pgbase,
+-                            rdata->args.offset, rdata->args.count);
++      hdr->res.eof = (offset + count) >= eof;
++      _fix_verify_io_params(hdr->lseg, &hdr->args.pages,
++                            &hdr->args.pgbase,
++                            hdr->args.offset, hdr->args.count);
+       dprintk("%s: inode(%lx) offset 0x%llx count 0x%Zx eof=%d\n",
+-              __func__, inode->i_ino, offset, count, rdata->res.eof);
++              __func__, inode->i_ino, offset, count, hdr->res.eof);
+-      err = objio_read_pagelist(rdata);
++      err = objio_read_pagelist(hdr);
+  out:
+       if (unlikely(err)) {
+               hdr->pnfs_error = err;
+@@ -312,38 +311,38 @@ objlayout_read_pagelist(struct nfs_pgio_
+ static void _rpc_write_complete(struct work_struct *work)
+ {
+       struct rpc_task *task;
+-      struct nfs_pgio_data *wdata;
++      struct nfs_pgio_header *hdr;
+       dprintk("%s enter\n", __func__);
+       task = container_of(work, struct rpc_task, u.tk_work);
+-      wdata = container_of(task, struct nfs_pgio_data, task);
++      hdr = container_of(task, struct nfs_pgio_header, task);
+-      pnfs_ld_write_done(wdata);
++      pnfs_ld_write_done(hdr);
+ }
+ void
+ objlayout_write_done(struct objlayout_io_res *oir, ssize_t status, bool sync)
+ {
+-      struct nfs_pgio_data *wdata = oir->rpcdata;
++      struct nfs_pgio_header *hdr = oir->rpcdata;
+-      oir->status = wdata->task.tk_status = status;
++      oir->status = hdr->task.tk_status = status;
+       if (status >= 0) {
+-              wdata->res.count = status;
+-              wdata->writeverf.committed = oir->committed;
++              hdr->res.count = status;
++              hdr->writeverf.committed = oir->committed;
+       } else {
+-              wdata->header->pnfs_error = status;
++              hdr->pnfs_error = status;
+       }
+       objlayout_iodone(oir);
+       /* must not use oir after this point */
+       dprintk("%s: Return status %zd committed %d sync=%d\n", __func__,
+-              status, wdata->writeverf.committed, sync);
++              status, hdr->writeverf.committed, sync);
+       if (sync)
+-              pnfs_ld_write_done(wdata);
++              pnfs_ld_write_done(hdr);
+       else {
+-              INIT_WORK(&wdata->task.u.tk_work, _rpc_write_complete);
+-              schedule_work(&wdata->task.u.tk_work);
++              INIT_WORK(&hdr->task.u.tk_work, _rpc_write_complete);
++              schedule_work(&hdr->task.u.tk_work);
+       }
+ }
+@@ -351,17 +350,15 @@ objlayout_write_done(struct objlayout_io
+  * Perform sync or async writes.
+  */
+ enum pnfs_try_status
+-objlayout_write_pagelist(struct nfs_pgio_data *wdata,
+-                       int how)
++objlayout_write_pagelist(struct nfs_pgio_header *hdr, int how)
+ {
+-      struct nfs_pgio_header *hdr = wdata->header;
+       int err;
+-      _fix_verify_io_params(hdr->lseg, &wdata->args.pages,
+-                            &wdata->args.pgbase,
+-                            wdata->args.offset, wdata->args.count);
++      _fix_verify_io_params(hdr->lseg, &hdr->args.pages,
++                            &hdr->args.pgbase,
++                            hdr->args.offset, hdr->args.count);
+-      err = objio_write_pagelist(wdata, how);
++      err = objio_write_pagelist(hdr, how);
+       if (unlikely(err)) {
+               hdr->pnfs_error = err;
+               dprintk("%s: Returned Error %d\n", __func__, err);
+--- a/fs/nfs/objlayout/objlayout.h
++++ b/fs/nfs/objlayout/objlayout.h
+@@ -119,8 +119,8 @@ extern void objio_free_lseg(struct pnfs_
+  */
+ extern void objio_free_result(struct objlayout_io_res *oir);
+-extern int objio_read_pagelist(struct nfs_pgio_data *rdata);
+-extern int objio_write_pagelist(struct nfs_pgio_data *wdata, int how);
++extern int objio_read_pagelist(struct nfs_pgio_header *rdata);
++extern int objio_write_pagelist(struct nfs_pgio_header *wdata, int how);
+ /*
+  * callback API
+@@ -168,10 +168,10 @@ extern struct pnfs_layout_segment *objla
+ extern void objlayout_free_lseg(struct pnfs_layout_segment *);
+ extern enum pnfs_try_status objlayout_read_pagelist(
+-      struct nfs_pgio_data *);
++      struct nfs_pgio_header *);
+ extern enum pnfs_try_status objlayout_write_pagelist(
+-      struct nfs_pgio_data *,
++      struct nfs_pgio_header *,
+       int how);
+ extern void objlayout_encode_layoutcommit(
+--- a/fs/nfs/pagelist.c
++++ b/fs/nfs/pagelist.c
+@@ -494,8 +494,7 @@ EXPORT_SYMBOL_GPL(nfs_pgio_header_free);
+ static bool nfs_pgio_data_init(struct nfs_pgio_header *hdr,
+                              unsigned int pagecount)
+ {
+-      if (nfs_pgarray_set(&hdr->data.page_array, pagecount)) {
+-              hdr->data.header = hdr;
++      if (nfs_pgarray_set(&hdr->page_array, pagecount)) {
+               atomic_inc(&hdr->refcnt);
+               return true;
+       }
+@@ -503,16 +502,14 @@ static bool nfs_pgio_data_init(struct nf
+ }
+ /**
+- * nfs_pgio_data_destroy - Properly free pageio data
+- * @data: The data to destroy
++ * nfs_pgio_data_destroy - Properly release pageio data
++ * @hdr: The header with data to destroy
+  */
+-void nfs_pgio_data_destroy(struct nfs_pgio_data *data)
++void nfs_pgio_data_destroy(struct nfs_pgio_header *hdr)
+ {
+-      struct nfs_pgio_header *hdr = data->header;
+-
+-      put_nfs_open_context(data->args.context);
+-      if (data->page_array.pagevec != data->page_array.page_array)
+-              kfree(data->page_array.pagevec);
++      put_nfs_open_context(hdr->args.context);
++      if (hdr->page_array.pagevec != hdr->page_array.page_array)
++              kfree(hdr->page_array.pagevec);
+       if (atomic_dec_and_test(&hdr->refcnt))
+               hdr->completion_ops->completion(hdr);
+ }
+@@ -520,31 +517,31 @@ EXPORT_SYMBOL_GPL(nfs_pgio_data_destroy)
+ /**
+  * nfs_pgio_rpcsetup - Set up arguments for a pageio call
+- * @data: The pageio data
++ * @hdr: The pageio hdr
+  * @count: Number of bytes to read
+  * @offset: Initial offset
+  * @how: How to commit data (writes only)
+  * @cinfo: Commit information for the call (writes only)
+  */
+-static void nfs_pgio_rpcsetup(struct nfs_pgio_data *data,
++static void nfs_pgio_rpcsetup(struct nfs_pgio_header *hdr,
+                             unsigned int count, unsigned int offset,
+                             int how, struct nfs_commit_info *cinfo)
+ {
+-      struct nfs_page *req = data->header->req;
++      struct nfs_page *req = hdr->req;
+       /* Set up the RPC argument and reply structs
+-       * NB: take care not to mess about with data->commit et al. */
++       * NB: take care not to mess about with hdr->commit et al. */
+-      data->args.fh     = NFS_FH(data->header->inode);
+-      data->args.offset = req_offset(req) + offset;
++      hdr->args.fh     = NFS_FH(hdr->inode);
++      hdr->args.offset = req_offset(req) + offset;
+       /* pnfs_set_layoutcommit needs this */
+-      data->mds_offset = data->args.offset;
+-      data->args.pgbase = req->wb_pgbase + offset;
+-      data->args.pages  = data->page_array.pagevec;
+-      data->args.count  = count;
+-      data->args.context = get_nfs_open_context(req->wb_context);
+-      data->args.lock_context = req->wb_lock_context;
+-      data->args.stable  = NFS_UNSTABLE;
++      hdr->mds_offset = hdr->args.offset;
++      hdr->args.pgbase = req->wb_pgbase + offset;
++      hdr->args.pages  = hdr->page_array.pagevec;
++      hdr->args.count  = count;
++      hdr->args.context = get_nfs_open_context(req->wb_context);
++      hdr->args.lock_context = req->wb_lock_context;
++      hdr->args.stable  = NFS_UNSTABLE;
+       switch (how & (FLUSH_STABLE | FLUSH_COND_STABLE)) {
+       case 0:
+               break;
+@@ -552,59 +549,60 @@ static void nfs_pgio_rpcsetup(struct nfs
+               if (nfs_reqs_to_commit(cinfo))
+                       break;
+       default:
+-              data->args.stable = NFS_FILE_SYNC;
++              hdr->args.stable = NFS_FILE_SYNC;
+       }
+-      data->res.fattr   = &data->fattr;
+-      data->res.count   = count;
+-      data->res.eof     = 0;
+-      data->res.verf    = &data->writeverf;
+-      nfs_fattr_init(&data->fattr);
++      hdr->res.fattr   = &hdr->fattr;
++      hdr->res.count   = count;
++      hdr->res.eof     = 0;
++      hdr->res.verf    = &hdr->writeverf;
++      nfs_fattr_init(&hdr->fattr);
+ }
+ /**
+- * nfs_pgio_prepare - Prepare pageio data to go over the wire
++ * nfs_pgio_prepare - Prepare pageio hdr to go over the wire
+  * @task: The current task
+- * @calldata: pageio data to prepare
++ * @calldata: pageio header to prepare
+  */
+ static void nfs_pgio_prepare(struct rpc_task *task, void *calldata)
+ {
+-      struct nfs_pgio_data *data = calldata;
++      struct nfs_pgio_header *hdr = calldata;
+       int err;
+-      err = NFS_PROTO(data->header->inode)->pgio_rpc_prepare(task, data);
++      err = NFS_PROTO(hdr->inode)->pgio_rpc_prepare(task, hdr);
+       if (err)
+               rpc_exit(task, err);
+ }
+-int nfs_initiate_pgio(struct rpc_clnt *clnt, struct nfs_pgio_data *data,
++int nfs_initiate_pgio(struct rpc_clnt *clnt, struct nfs_pgio_header *hdr,
+                     const struct rpc_call_ops *call_ops, int how, int flags)
+ {
++      struct inode *inode = hdr->inode;
+       struct rpc_task *task;
+       struct rpc_message msg = {
+-              .rpc_argp = &data->args,
+-              .rpc_resp = &data->res,
+-              .rpc_cred = data->header->cred,
++              .rpc_argp = &hdr->args,
++              .rpc_resp = &hdr->res,
++              .rpc_cred = hdr->cred,
+       };
+       struct rpc_task_setup task_setup_data = {
+               .rpc_client = clnt,
+-              .task = &data->task,
++              .task = &hdr->task,
+               .rpc_message = &msg,
+               .callback_ops = call_ops,
+-              .callback_data = data,
++              .callback_data = hdr,
+               .workqueue = nfsiod_workqueue,
+               .flags = RPC_TASK_ASYNC | flags,
+       };
+       int ret = 0;
+-      data->header->rw_ops->rw_initiate(data, &msg, &task_setup_data, how);
++      hdr->rw_ops->rw_initiate(hdr, &msg, &task_setup_data, how);
+       dprintk("NFS: %5u initiated pgio call "
+               "(req %s/%llu, %u bytes @ offset %llu)\n",
+-              data->task.tk_pid,
+-              data->header->inode->i_sb->s_id,
+-              (unsigned long long)NFS_FILEID(data->header->inode),
+-              data->args.count,
+-              (unsigned long long)data->args.offset);
++              hdr->task.tk_pid,
++              inode->i_sb->s_id,
++              (unsigned long long)NFS_FILEID(inode),
++              hdr->args.count,
++              (unsigned long long)hdr->args.offset);
+       task = rpc_run_task(&task_setup_data);
+       if (IS_ERR(task)) {
+@@ -631,21 +629,21 @@ static int nfs_pgio_error(struct nfs_pag
+                         struct nfs_pgio_header *hdr)
+ {
+       set_bit(NFS_IOHDR_REDO, &hdr->flags);
+-      nfs_pgio_data_destroy(&hdr->data);
++      nfs_pgio_data_destroy(hdr);
+       desc->pg_completion_ops->error_cleanup(&desc->pg_list);
+       return -ENOMEM;
+ }
+ /**
+  * nfs_pgio_release - Release pageio data
+- * @calldata: The pageio data to release
++ * @calldata: The pageio header to release
+  */
+ static void nfs_pgio_release(void *calldata)
+ {
+-      struct nfs_pgio_data *data = calldata;
+-      if (data->header->rw_ops->rw_release)
+-              data->header->rw_ops->rw_release(data);
+-      nfs_pgio_data_destroy(data);
++      struct nfs_pgio_header *hdr = calldata;
++      if (hdr->rw_ops->rw_release)
++              hdr->rw_ops->rw_release(hdr);
++      nfs_pgio_data_destroy(hdr);
+ }
+ /**
+@@ -686,22 +684,22 @@ EXPORT_SYMBOL_GPL(nfs_pageio_init);
+ /**
+  * nfs_pgio_result - Basic pageio error handling
+  * @task: The task that ran
+- * @calldata: Pageio data to check
++ * @calldata: Pageio header to check
+  */
+ static void nfs_pgio_result(struct rpc_task *task, void *calldata)
+ {
+-      struct nfs_pgio_data *data = calldata;
+-      struct inode *inode = data->header->inode;
++      struct nfs_pgio_header *hdr = calldata;
++      struct inode *inode = hdr->inode;
+       dprintk("NFS: %s: %5u, (status %d)\n", __func__,
+               task->tk_pid, task->tk_status);
+-      if (data->header->rw_ops->rw_done(task, data, inode) != 0)
++      if (hdr->rw_ops->rw_done(task, hdr, inode) != 0)
+               return;
+       if (task->tk_status < 0)
+-              nfs_set_pgio_error(data->header, task->tk_status, data->args.offset);
++              nfs_set_pgio_error(hdr, task->tk_status, hdr->args.offset);
+       else
+-              data->header->rw_ops->rw_result(task, data);
++              hdr->rw_ops->rw_result(task, hdr);
+ }
+ /*
+@@ -717,7 +715,6 @@ int nfs_generic_pgio(struct nfs_pageio_d
+ {
+       struct nfs_page         *req;
+       struct page             **pages;
+-      struct nfs_pgio_data    *data;
+       struct list_head *head = &desc->pg_list;
+       struct nfs_commit_info cinfo;
+@@ -725,9 +722,8 @@ int nfs_generic_pgio(struct nfs_pageio_d
+                          desc->pg_count)))
+               return nfs_pgio_error(desc, hdr);
+-      data = &hdr->data;
+       nfs_init_cinfo(&cinfo, desc->pg_inode, desc->pg_dreq);
+-      pages = data->page_array.pagevec;
++      pages = hdr->page_array.pagevec;
+       while (!list_empty(head)) {
+               req = nfs_list_entry(head->next);
+               nfs_list_remove_request(req);
+@@ -740,7 +736,7 @@ int nfs_generic_pgio(struct nfs_pageio_d
+               desc->pg_ioflags &= ~FLUSH_COND_STABLE;
+       /* Set up the argument struct */
+-      nfs_pgio_rpcsetup(data, desc->pg_count, 0, desc->pg_ioflags, &cinfo);
++      nfs_pgio_rpcsetup(hdr, desc->pg_count, 0, desc->pg_ioflags, &cinfo);
+       desc->pg_rpc_callops = &nfs_pgio_common_ops;
+       return 0;
+ }
+@@ -761,7 +757,7 @@ static int nfs_generic_pg_pgios(struct n
+       ret = nfs_generic_pgio(desc, hdr);
+       if (ret == 0)
+               ret = nfs_initiate_pgio(NFS_CLIENT(hdr->inode),
+-                                      &hdr->data, desc->pg_rpc_callops,
++                                      hdr, desc->pg_rpc_callops,
+                                       desc->pg_ioflags, 0);
+       if (atomic_dec_and_test(&hdr->refcnt))
+               hdr->completion_ops->completion(hdr);
+--- a/fs/nfs/pnfs.c
++++ b/fs/nfs/pnfs.c
+@@ -1502,9 +1502,8 @@ int pnfs_write_done_resend_to_mds(struct
+ }
+ EXPORT_SYMBOL_GPL(pnfs_write_done_resend_to_mds);
+-static void pnfs_ld_handle_write_error(struct nfs_pgio_data *data)
++static void pnfs_ld_handle_write_error(struct nfs_pgio_header *hdr)
+ {
+-      struct nfs_pgio_header *hdr = data->header;
+       dprintk("pnfs write error = %d\n", hdr->pnfs_error);
+       if (NFS_SERVER(hdr->inode)->pnfs_curr_ld->flags &
+@@ -1512,7 +1511,7 @@ static void pnfs_ld_handle_write_error(s
+               pnfs_return_layout(hdr->inode);
+       }
+       if (!test_and_set_bit(NFS_IOHDR_REDO, &hdr->flags))
+-              data->task.tk_status = pnfs_write_done_resend_to_mds(hdr->inode,
++              hdr->task.tk_status = pnfs_write_done_resend_to_mds(hdr->inode,
+                                                       &hdr->pages,
+                                                       hdr->completion_ops,
+                                                       hdr->dreq);
+@@ -1521,41 +1520,36 @@ static void pnfs_ld_handle_write_error(s
+ /*
+  * Called by non rpc-based layout drivers
+  */
+-void pnfs_ld_write_done(struct nfs_pgio_data *data)
++void pnfs_ld_write_done(struct nfs_pgio_header *hdr)
+ {
+-      struct nfs_pgio_header *hdr = data->header;
+-
+-      trace_nfs4_pnfs_write(data, hdr->pnfs_error);
++      trace_nfs4_pnfs_write(hdr, hdr->pnfs_error);
+       if (!hdr->pnfs_error) {
+-              pnfs_set_layoutcommit(data);
+-              hdr->mds_ops->rpc_call_done(&data->task, data);
++              pnfs_set_layoutcommit(hdr);
++              hdr->mds_ops->rpc_call_done(&hdr->task, hdr);
+       } else
+-              pnfs_ld_handle_write_error(data);
+-      hdr->mds_ops->rpc_release(data);
++              pnfs_ld_handle_write_error(hdr);
++      hdr->mds_ops->rpc_release(hdr);
+ }
+ EXPORT_SYMBOL_GPL(pnfs_ld_write_done);
+ static void
+ pnfs_write_through_mds(struct nfs_pageio_descriptor *desc,
+-              struct nfs_pgio_data *data)
++              struct nfs_pgio_header *hdr)
+ {
+-      struct nfs_pgio_header *hdr = data->header;
+-
+       if (!test_and_set_bit(NFS_IOHDR_REDO, &hdr->flags)) {
+               list_splice_tail_init(&hdr->pages, &desc->pg_list);
+               nfs_pageio_reset_write_mds(desc);
+               desc->pg_recoalesce = 1;
+       }
+-      nfs_pgio_data_destroy(data);
++      nfs_pgio_data_destroy(hdr);
+ }
+ static enum pnfs_try_status
+-pnfs_try_to_write_data(struct nfs_pgio_data *wdata,
++pnfs_try_to_write_data(struct nfs_pgio_header *hdr,
+                       const struct rpc_call_ops *call_ops,
+                       struct pnfs_layout_segment *lseg,
+                       int how)
+ {
+-      struct nfs_pgio_header *hdr = wdata->header;
+       struct inode *inode = hdr->inode;
+       enum pnfs_try_status trypnfs;
+       struct nfs_server *nfss = NFS_SERVER(inode);
+@@ -1563,8 +1557,8 @@ pnfs_try_to_write_data(struct nfs_pgio_d
+       hdr->mds_ops = call_ops;
+       dprintk("%s: Writing ino:%lu %u@%llu (how %d)\n", __func__,
+-              inode->i_ino, wdata->args.count, wdata->args.offset, how);
+-      trypnfs = nfss->pnfs_curr_ld->write_pagelist(wdata, how);
++              inode->i_ino, hdr->args.count, hdr->args.offset, how);
++      trypnfs = nfss->pnfs_curr_ld->write_pagelist(hdr, how);
+       if (trypnfs != PNFS_NOT_ATTEMPTED)
+               nfs_inc_stats(inode, NFSIOS_PNFS_WRITE);
+       dprintk("%s End (trypnfs:%d)\n", __func__, trypnfs);
+@@ -1575,15 +1569,14 @@ static void
+ pnfs_do_write(struct nfs_pageio_descriptor *desc,
+             struct nfs_pgio_header *hdr, int how)
+ {
+-      struct nfs_pgio_data *data = &hdr->data;
+       const struct rpc_call_ops *call_ops = desc->pg_rpc_callops;
+       struct pnfs_layout_segment *lseg = desc->pg_lseg;
+       enum pnfs_try_status trypnfs;
+       desc->pg_lseg = NULL;
+-      trypnfs = pnfs_try_to_write_data(data, call_ops, lseg, how);
++      trypnfs = pnfs_try_to_write_data(hdr, call_ops, lseg, how);
+       if (trypnfs == PNFS_NOT_ATTEMPTED)
+-              pnfs_write_through_mds(desc, data);
++              pnfs_write_through_mds(desc, hdr);
+       pnfs_put_lseg(lseg);
+ }
+@@ -1650,17 +1643,15 @@ int pnfs_read_done_resend_to_mds(struct
+ }
+ EXPORT_SYMBOL_GPL(pnfs_read_done_resend_to_mds);
+-static void pnfs_ld_handle_read_error(struct nfs_pgio_data *data)
++static void pnfs_ld_handle_read_error(struct nfs_pgio_header *hdr)
+ {
+-      struct nfs_pgio_header *hdr = data->header;
+-
+       dprintk("pnfs read error = %d\n", hdr->pnfs_error);
+       if (NFS_SERVER(hdr->inode)->pnfs_curr_ld->flags &
+           PNFS_LAYOUTRET_ON_ERROR) {
+               pnfs_return_layout(hdr->inode);
+       }
+       if (!test_and_set_bit(NFS_IOHDR_REDO, &hdr->flags))
+-              data->task.tk_status = pnfs_read_done_resend_to_mds(hdr->inode,
++              hdr->task.tk_status = pnfs_read_done_resend_to_mds(hdr->inode,
+                                                       &hdr->pages,
+                                                       hdr->completion_ops,
+                                                       hdr->dreq);
+@@ -1669,43 +1660,38 @@ static void pnfs_ld_handle_read_error(st
+ /*
+  * Called by non rpc-based layout drivers
+  */
+-void pnfs_ld_read_done(struct nfs_pgio_data *data)
++void pnfs_ld_read_done(struct nfs_pgio_header *hdr)
+ {
+-      struct nfs_pgio_header *hdr = data->header;
+-
+-      trace_nfs4_pnfs_read(data, hdr->pnfs_error);
++      trace_nfs4_pnfs_read(hdr, hdr->pnfs_error);
+       if (likely(!hdr->pnfs_error)) {
+-              __nfs4_read_done_cb(data);
+-              hdr->mds_ops->rpc_call_done(&data->task, data);
++              __nfs4_read_done_cb(hdr);
++              hdr->mds_ops->rpc_call_done(&hdr->task, hdr);
+       } else
+-              pnfs_ld_handle_read_error(data);
+-      hdr->mds_ops->rpc_release(data);
++              pnfs_ld_handle_read_error(hdr);
++      hdr->mds_ops->rpc_release(hdr);
+ }
+ EXPORT_SYMBOL_GPL(pnfs_ld_read_done);
+ static void
+ pnfs_read_through_mds(struct nfs_pageio_descriptor *desc,
+-              struct nfs_pgio_data *data)
++              struct nfs_pgio_header *hdr)
+ {
+-      struct nfs_pgio_header *hdr = data->header;
+-
+       if (!test_and_set_bit(NFS_IOHDR_REDO, &hdr->flags)) {
+               list_splice_tail_init(&hdr->pages, &desc->pg_list);
+               nfs_pageio_reset_read_mds(desc);
+               desc->pg_recoalesce = 1;
+       }
+-      nfs_pgio_data_destroy(data);
++      nfs_pgio_data_destroy(hdr);
+ }
+ /*
+  * Call the appropriate parallel I/O subsystem read function.
+  */
+ static enum pnfs_try_status
+-pnfs_try_to_read_data(struct nfs_pgio_data *rdata,
++pnfs_try_to_read_data(struct nfs_pgio_header *hdr,
+                      const struct rpc_call_ops *call_ops,
+                      struct pnfs_layout_segment *lseg)
+ {
+-      struct nfs_pgio_header *hdr = rdata->header;
+       struct inode *inode = hdr->inode;
+       struct nfs_server *nfss = NFS_SERVER(inode);
+       enum pnfs_try_status trypnfs;
+@@ -1713,9 +1699,9 @@ pnfs_try_to_read_data(struct nfs_pgio_da
+       hdr->mds_ops = call_ops;
+       dprintk("%s: Reading ino:%lu %u@%llu\n",
+-              __func__, inode->i_ino, rdata->args.count, rdata->args.offset);
++              __func__, inode->i_ino, hdr->args.count, hdr->args.offset);
+-      trypnfs = nfss->pnfs_curr_ld->read_pagelist(rdata);
++      trypnfs = nfss->pnfs_curr_ld->read_pagelist(hdr);
+       if (trypnfs != PNFS_NOT_ATTEMPTED)
+               nfs_inc_stats(inode, NFSIOS_PNFS_READ);
+       dprintk("%s End (trypnfs:%d)\n", __func__, trypnfs);
+@@ -1725,15 +1711,14 @@ pnfs_try_to_read_data(struct nfs_pgio_da
+ static void
+ pnfs_do_read(struct nfs_pageio_descriptor *desc, struct nfs_pgio_header *hdr)
+ {
+-      struct nfs_pgio_data *data = &hdr->data;
+       const struct rpc_call_ops *call_ops = desc->pg_rpc_callops;
+       struct pnfs_layout_segment *lseg = desc->pg_lseg;
+       enum pnfs_try_status trypnfs;
+       desc->pg_lseg = NULL;
+-      trypnfs = pnfs_try_to_read_data(data, call_ops, lseg);
++      trypnfs = pnfs_try_to_read_data(hdr, call_ops, lseg);
+       if (trypnfs == PNFS_NOT_ATTEMPTED)
+-              pnfs_read_through_mds(desc, data);
++              pnfs_read_through_mds(desc, hdr);
+       pnfs_put_lseg(lseg);
+ }
+@@ -1816,12 +1801,11 @@ void pnfs_set_lo_fail(struct pnfs_layout
+ EXPORT_SYMBOL_GPL(pnfs_set_lo_fail);
+ void
+-pnfs_set_layoutcommit(struct nfs_pgio_data *wdata)
++pnfs_set_layoutcommit(struct nfs_pgio_header *hdr)
+ {
+-      struct nfs_pgio_header *hdr = wdata->header;
+       struct inode *inode = hdr->inode;
+       struct nfs_inode *nfsi = NFS_I(inode);
+-      loff_t end_pos = wdata->mds_offset + wdata->res.count;
++      loff_t end_pos = hdr->mds_offset + hdr->res.count;
+       bool mark_as_dirty = false;
+       spin_lock(&inode->i_lock);
+--- a/fs/nfs/pnfs.h
++++ b/fs/nfs/pnfs.h
+@@ -113,8 +113,8 @@ struct pnfs_layoutdriver_type {
+        * Return PNFS_ATTEMPTED to indicate the layout code has attempted
+        * I/O, else return PNFS_NOT_ATTEMPTED to fall back to normal NFS
+        */
+-      enum pnfs_try_status (*read_pagelist) (struct nfs_pgio_data *nfs_data);
+-      enum pnfs_try_status (*write_pagelist) (struct nfs_pgio_data *nfs_data, int how);
++      enum pnfs_try_status (*read_pagelist)(struct nfs_pgio_header *);
++      enum pnfs_try_status (*write_pagelist)(struct nfs_pgio_header *, int);
+       void (*free_deviceid_node) (struct nfs4_deviceid_node *);
+@@ -213,13 +213,13 @@ bool pnfs_roc(struct inode *ino);
+ void pnfs_roc_release(struct inode *ino);
+ void pnfs_roc_set_barrier(struct inode *ino, u32 barrier);
+ bool pnfs_roc_drain(struct inode *ino, u32 *barrier, struct rpc_task *task);
+-void pnfs_set_layoutcommit(struct nfs_pgio_data *wdata);
++void pnfs_set_layoutcommit(struct nfs_pgio_header *);
+ void pnfs_cleanup_layoutcommit(struct nfs4_layoutcommit_data *data);
+ int pnfs_layoutcommit_inode(struct inode *inode, bool sync);
+ int _pnfs_return_layout(struct inode *);
+ int pnfs_commit_and_return_layout(struct inode *);
+-void pnfs_ld_write_done(struct nfs_pgio_data *);
+-void pnfs_ld_read_done(struct nfs_pgio_data *);
++void pnfs_ld_write_done(struct nfs_pgio_header *);
++void pnfs_ld_read_done(struct nfs_pgio_header *);
+ struct pnfs_layout_segment *pnfs_update_layout(struct inode *ino,
+                                              struct nfs_open_context *ctx,
+                                              loff_t pos,
+--- a/fs/nfs/proc.c
++++ b/fs/nfs/proc.c
+@@ -578,46 +578,49 @@ nfs_proc_pathconf(struct nfs_server *ser
+       return 0;
+ }
+-static int nfs_read_done(struct rpc_task *task, struct nfs_pgio_data *data)
++static int nfs_read_done(struct rpc_task *task, struct nfs_pgio_header *hdr)
+ {
+-      struct inode *inode = data->header->inode;
++      struct inode *inode = hdr->inode;
+       nfs_invalidate_atime(inode);
+       if (task->tk_status >= 0) {
+-              nfs_refresh_inode(inode, data->res.fattr);
++              nfs_refresh_inode(inode, hdr->res.fattr);
+               /* Emulate the eof flag, which isn't normally needed in NFSv2
+                * as it is guaranteed to always return the file attributes
+                */
+-              if (data->args.offset + data->res.count >= data->res.fattr->size)
+-                      data->res.eof = 1;
++              if (hdr->args.offset + hdr->res.count >= hdr->res.fattr->size)
++                      hdr->res.eof = 1;
+       }
+       return 0;
+ }
+-static void nfs_proc_read_setup(struct nfs_pgio_data *data, struct rpc_message *msg)
++static void nfs_proc_read_setup(struct nfs_pgio_header *hdr,
++                              struct rpc_message *msg)
+ {
+       msg->rpc_proc = &nfs_procedures[NFSPROC_READ];
+ }
+-static int nfs_proc_pgio_rpc_prepare(struct rpc_task *task, struct nfs_pgio_data *data)
++static int nfs_proc_pgio_rpc_prepare(struct rpc_task *task,
++                                   struct nfs_pgio_header *hdr)
+ {
+       rpc_call_start(task);
+       return 0;
+ }
+-static int nfs_write_done(struct rpc_task *task, struct nfs_pgio_data *data)
++static int nfs_write_done(struct rpc_task *task, struct nfs_pgio_header *hdr)
+ {
+-      struct inode *inode = data->header->inode;
++      struct inode *inode = hdr->inode;
+       if (task->tk_status >= 0)
+-              nfs_post_op_update_inode_force_wcc(inode, data->res.fattr);
++              nfs_post_op_update_inode_force_wcc(inode, hdr->res.fattr);
+       return 0;
+ }
+-static void nfs_proc_write_setup(struct nfs_pgio_data *data, struct rpc_message *msg)
++static void nfs_proc_write_setup(struct nfs_pgio_header *hdr,
++                               struct rpc_message *msg)
+ {
+       /* Note: NFSv2 ignores @stable and always uses NFS_FILE_SYNC */
+-      data->args.stable = NFS_FILE_SYNC;
++      hdr->args.stable = NFS_FILE_SYNC;
+       msg->rpc_proc = &nfs_procedures[NFSPROC_WRITE];
+ }
+--- a/fs/nfs/read.c
++++ b/fs/nfs/read.c
+@@ -172,14 +172,15 @@ out:
+       hdr->release(hdr);
+ }
+-static void nfs_initiate_read(struct nfs_pgio_data *data, struct rpc_message *msg,
++static void nfs_initiate_read(struct nfs_pgio_header *hdr,
++                            struct rpc_message *msg,
+                             struct rpc_task_setup *task_setup_data, int how)
+ {
+-      struct inode *inode = data->header->inode;
++      struct inode *inode = hdr->inode;
+       int swap_flags = IS_SWAPFILE(inode) ? NFS_RPC_SWAPFLAGS : 0;
+       task_setup_data->flags |= swap_flags;
+-      NFS_PROTO(inode)->read_setup(data, msg);
++      NFS_PROTO(inode)->read_setup(hdr, msg);
+ }
+ static void
+@@ -203,14 +204,15 @@ static const struct nfs_pgio_completion_
+  * This is the callback from RPC telling us whether a reply was
+  * received or some error occurred (timeout or socket shutdown).
+  */
+-static int nfs_readpage_done(struct rpc_task *task, struct nfs_pgio_data *data,
++static int nfs_readpage_done(struct rpc_task *task,
++                           struct nfs_pgio_header *hdr,
+                            struct inode *inode)
+ {
+-      int status = NFS_PROTO(inode)->read_done(task, data);
++      int status = NFS_PROTO(inode)->read_done(task, hdr);
+       if (status != 0)
+               return status;
+-      nfs_add_stats(inode, NFSIOS_SERVERREADBYTES, data->res.count);
++      nfs_add_stats(inode, NFSIOS_SERVERREADBYTES, hdr->res.count);
+       if (task->tk_status == -ESTALE) {
+               set_bit(NFS_INO_STALE, &NFS_I(inode)->flags);
+@@ -219,34 +221,34 @@ static int nfs_readpage_done(struct rpc_
+       return 0;
+ }
+-static void nfs_readpage_retry(struct rpc_task *task, struct nfs_pgio_data *data)
++static void nfs_readpage_retry(struct rpc_task *task,
++                             struct nfs_pgio_header *hdr)
+ {
+-      struct nfs_pgio_args *argp = &data->args;
+-      struct nfs_pgio_res  *resp = &data->res;
++      struct nfs_pgio_args *argp = &hdr->args;
++      struct nfs_pgio_res  *resp = &hdr->res;
+       /* This is a short read! */
+-      nfs_inc_stats(data->header->inode, NFSIOS_SHORTREAD);
++      nfs_inc_stats(hdr->inode, NFSIOS_SHORTREAD);
+       /* Has the server at least made some progress? */
+       if (resp->count == 0) {
+-              nfs_set_pgio_error(data->header, -EIO, argp->offset);
++              nfs_set_pgio_error(hdr, -EIO, argp->offset);
+               return;
+       }
+-      /* Yes, so retry the read at the end of the data */
+-      data->mds_offset += resp->count;
++      /* Yes, so retry the read at the end of the hdr */
++      hdr->mds_offset += resp->count;
+       argp->offset += resp->count;
+       argp->pgbase += resp->count;
+       argp->count -= resp->count;
+       rpc_restart_call_prepare(task);
+ }
+-static void nfs_readpage_result(struct rpc_task *task, struct nfs_pgio_data *data)
++static void nfs_readpage_result(struct rpc_task *task,
++                              struct nfs_pgio_header *hdr)
+ {
+-      struct nfs_pgio_header *hdr = data->header;
+-
+-      if (data->res.eof) {
++      if (hdr->res.eof) {
+               loff_t bound;
+-              bound = data->args.offset + data->res.count;
++              bound = hdr->args.offset + hdr->res.count;
+               spin_lock(&hdr->lock);
+               if (bound < hdr->io_start + hdr->good_bytes) {
+                       set_bit(NFS_IOHDR_EOF, &hdr->flags);
+@@ -254,8 +256,8 @@ static void nfs_readpage_result(struct r
+                       hdr->good_bytes = bound - hdr->io_start;
+               }
+               spin_unlock(&hdr->lock);
+-      } else if (data->res.count != data->args.count)
+-              nfs_readpage_retry(task, data);
++      } else if (hdr->res.count != hdr->args.count)
++              nfs_readpage_retry(task, hdr);
+ }
+ /*
+--- a/fs/nfs/write.c
++++ b/fs/nfs/write.c
+@@ -826,11 +826,11 @@ nfs_clear_request_commit(struct nfs_page
+ }
+ static inline
+-int nfs_write_need_commit(struct nfs_pgio_data *data)
++int nfs_write_need_commit(struct nfs_pgio_header *hdr)
+ {
+-      if (data->writeverf.committed == NFS_DATA_SYNC)
+-              return data->header->lseg == NULL;
+-      return data->writeverf.committed != NFS_FILE_SYNC;
++      if (hdr->writeverf.committed == NFS_DATA_SYNC)
++              return hdr->lseg == NULL;
++      return hdr->writeverf.committed != NFS_FILE_SYNC;
+ }
+ #else
+@@ -857,7 +857,7 @@ nfs_clear_request_commit(struct nfs_page
+ }
+ static inline
+-int nfs_write_need_commit(struct nfs_pgio_data *data)
++int nfs_write_need_commit(struct nfs_pgio_header *hdr)
+ {
+       return 0;
+ }
+@@ -1241,17 +1241,18 @@ static int flush_task_priority(int how)
+       return RPC_PRIORITY_NORMAL;
+ }
+-static void nfs_initiate_write(struct nfs_pgio_data *data, struct rpc_message *msg,
++static void nfs_initiate_write(struct nfs_pgio_header *hdr,
++                             struct rpc_message *msg,
+                              struct rpc_task_setup *task_setup_data, int how)
+ {
+-      struct inode *inode = data->header->inode;
++      struct inode *inode = hdr->inode;
+       int priority = flush_task_priority(how);
+       task_setup_data->priority = priority;
+-      NFS_PROTO(inode)->write_setup(data, msg);
++      NFS_PROTO(inode)->write_setup(hdr, msg);
+       nfs4_state_protect_write(NFS_SERVER(inode)->nfs_client,
+-                               &task_setup_data->rpc_client, msg, data);
++                               &task_setup_data->rpc_client, msg, hdr);
+ }
+ /* If a nfs_flush_* function fails, it should remove reqs from @head and
+@@ -1313,19 +1314,17 @@ void nfs_commit_prepare(struct rpc_task
+       NFS_PROTO(data->inode)->commit_rpc_prepare(task, data);
+ }
+-static void nfs_writeback_release_common(struct nfs_pgio_data *data)
++static void nfs_writeback_release_common(struct nfs_pgio_header *hdr)
+ {
+-      struct nfs_pgio_header *hdr = data->header;
+-      int status = data->task.tk_status;
++      int status = hdr->task.tk_status;
+-      if ((status >= 0) && nfs_write_need_commit(data)) {
++      if ((status >= 0) && nfs_write_need_commit(hdr)) {
+               spin_lock(&hdr->lock);
+               if (test_bit(NFS_IOHDR_NEED_RESCHED, &hdr->flags))
+                       ; /* Do nothing */
+               else if (!test_and_set_bit(NFS_IOHDR_NEED_COMMIT, &hdr->flags))
+-                      memcpy(&hdr->verf, &data->writeverf, sizeof(hdr->verf));
+-              else if (memcmp(&hdr->verf, &data->writeverf,
+-                       sizeof(hdr->verf)))
++                      memcpy(&hdr->verf, &hdr->writeverf, sizeof(hdr->verf));
++              else if (memcmp(&hdr->verf, &hdr->writeverf, sizeof(hdr->verf)))
+                       set_bit(NFS_IOHDR_NEED_RESCHED, &hdr->flags);
+               spin_unlock(&hdr->lock);
+       }
+@@ -1359,7 +1358,8 @@ static int nfs_should_remove_suid(const
+ /*
+  * This function is called when the WRITE call is complete.
+  */
+-static int nfs_writeback_done(struct rpc_task *task, struct nfs_pgio_data *data,
++static int nfs_writeback_done(struct rpc_task *task,
++                            struct nfs_pgio_header *hdr,
+                             struct inode *inode)
+ {
+       int status;
+@@ -1371,13 +1371,14 @@ static int nfs_writeback_done(struct rpc
+        * another writer had changed the file, but some applications
+        * depend on tighter cache coherency when writing.
+        */
+-      status = NFS_PROTO(inode)->write_done(task, data);
++      status = NFS_PROTO(inode)->write_done(task, hdr);
+       if (status != 0)
+               return status;
+-      nfs_add_stats(inode, NFSIOS_SERVERWRITTENBYTES, data->res.count);
++      nfs_add_stats(inode, NFSIOS_SERVERWRITTENBYTES, hdr->res.count);
+ #if IS_ENABLED(CONFIG_NFS_V3) || IS_ENABLED(CONFIG_NFS_V4)
+-      if (data->res.verf->committed < data->args.stable && task->tk_status >= 0) {
++      if (hdr->res.verf->committed < hdr->args.stable &&
++          task->tk_status >= 0) {
+               /* We tried a write call, but the server did not
+                * commit data to stable storage even though we
+                * requested it.
+@@ -1393,7 +1394,7 @@ static int nfs_writeback_done(struct rpc
+                       dprintk("NFS:       faulty NFS server %s:"
+                               " (committed = %d) != (stable = %d)\n",
+                               NFS_SERVER(inode)->nfs_client->cl_hostname,
+-                              data->res.verf->committed, data->args.stable);
++                              hdr->res.verf->committed, hdr->args.stable);
+                       complain = jiffies + 300 * HZ;
+               }
+       }
+@@ -1408,16 +1409,17 @@ static int nfs_writeback_done(struct rpc
+ /*
+  * This function is called when the WRITE call is complete.
+  */
+-static void nfs_writeback_result(struct rpc_task *task, struct nfs_pgio_data *data)
++static void nfs_writeback_result(struct rpc_task *task,
++                               struct nfs_pgio_header *hdr)
+ {
+-      struct nfs_pgio_args    *argp = &data->args;
+-      struct nfs_pgio_res     *resp = &data->res;
++      struct nfs_pgio_args    *argp = &hdr->args;
++      struct nfs_pgio_res     *resp = &hdr->res;
+       if (resp->count < argp->count) {
+               static unsigned long    complain;
+               /* This a short write! */
+-              nfs_inc_stats(data->header->inode, NFSIOS_SHORTWRITE);
++              nfs_inc_stats(hdr->inode, NFSIOS_SHORTWRITE);
+               /* Has the server at least made some progress? */
+               if (resp->count == 0) {
+@@ -1427,14 +1429,14 @@ static void nfs_writeback_result(struct
+                                      argp->count);
+                               complain = jiffies + 300 * HZ;
+                       }
+-                      nfs_set_pgio_error(data->header, -EIO, argp->offset);
++                      nfs_set_pgio_error(hdr, -EIO, argp->offset);
+                       task->tk_status = -EIO;
+                       return;
+               }
+               /* Was this an NFSv2 write or an NFSv3 stable write? */
+               if (resp->verf->committed != NFS_UNSTABLE) {
+                       /* Resend from where the server left off */
+-                      data->mds_offset += resp->count;
++                      hdr->mds_offset += resp->count;
+                       argp->offset += resp->count;
+                       argp->pgbase += resp->count;
+                       argp->count -= resp->count;
+--- a/include/linux/nfs_page.h
++++ b/include/linux/nfs_page.h
+@@ -64,10 +64,11 @@ struct nfs_rw_ops {
+       const fmode_t rw_mode;
+       struct nfs_pgio_header *(*rw_alloc_header)(void);
+       void (*rw_free_header)(struct nfs_pgio_header *);
+-      void (*rw_release)(struct nfs_pgio_data *);
+-      int  (*rw_done)(struct rpc_task *, struct nfs_pgio_data *, struct inode *);
+-      void (*rw_result)(struct rpc_task *, struct nfs_pgio_data *);
+-      void (*rw_initiate)(struct nfs_pgio_data *, struct rpc_message *,
++      void (*rw_release)(struct nfs_pgio_header *);
++      int  (*rw_done)(struct rpc_task *, struct nfs_pgio_header *,
++                      struct inode *);
++      void (*rw_result)(struct rpc_task *, struct nfs_pgio_header *);
++      void (*rw_initiate)(struct nfs_pgio_header *, struct rpc_message *,
+                           struct rpc_task_setup *, int);
+ };
+--- a/include/linux/nfs_xdr.h
++++ b/include/linux/nfs_xdr.h
+@@ -1257,27 +1257,10 @@ enum {
+       NFS_IOHDR_NEED_RESCHED,
+ };
+-struct nfs_pgio_data {
+-      struct nfs_pgio_header  *header;
+-      struct list_head        list;
+-      struct rpc_task         task;
+-      struct nfs_fattr        fattr;
+-      struct nfs_writeverf    writeverf;      /* Used for writes */
+-      struct nfs_pgio_args    args;           /* argument struct */
+-      struct nfs_pgio_res     res;            /* result struct */
+-      unsigned long           timestamp;      /* For lease renewal */
+-      int (*pgio_done_cb)(struct rpc_task *task, struct nfs_pgio_data *data);
+-      __u64                   mds_offset;     /* Filelayout dense stripe */
+-      struct nfs_page_array   page_array;
+-      struct nfs_client       *ds_clp;        /* pNFS data server */
+-      int                     ds_idx;         /* ds index if ds_clp is set */
+-};
+-
+ struct nfs_pgio_header {
+       struct inode            *inode;
+       struct rpc_cred         *cred;
+       struct list_head        pages;
+-      struct nfs_pgio_data    data;
+       atomic_t                refcnt;
+       struct nfs_page         *req;
+       struct nfs_writeverf    verf;           /* Used for writes */
+@@ -1295,6 +1278,21 @@ struct nfs_pgio_header {
+       int                     error;          /* merge with pnfs_error */
+       unsigned long           good_bytes;     /* boundary of good data */
+       unsigned long           flags;
++
++      /*
++       * rpc data
++       */
++      struct rpc_task         task;
++      struct nfs_fattr        fattr;
++      struct nfs_writeverf    writeverf;      /* Used for writes */
++      struct nfs_pgio_args    args;           /* argument struct */
++      struct nfs_pgio_res     res;            /* result struct */
++      unsigned long           timestamp;      /* For lease renewal */
++      int (*pgio_done_cb)(struct rpc_task *, struct nfs_pgio_header *);
++      __u64                   mds_offset;     /* Filelayout dense stripe */
++      struct nfs_page_array   page_array;
++      struct nfs_client       *ds_clp;        /* pNFS data server */
++      int                     ds_idx;         /* ds index if ds_clp is set */
+ };
+ struct nfs_mds_commit_info {
+@@ -1426,11 +1424,12 @@ struct nfs_rpc_ops {
+                            struct nfs_pathconf *);
+       int     (*set_capabilities)(struct nfs_server *, struct nfs_fh *);
+       int     (*decode_dirent)(struct xdr_stream *, struct nfs_entry *, int);
+-      int     (*pgio_rpc_prepare)(struct rpc_task *, struct nfs_pgio_data *);
+-      void    (*read_setup)   (struct nfs_pgio_data *, struct rpc_message *);
+-      int     (*read_done)  (struct rpc_task *, struct nfs_pgio_data *);
+-      void    (*write_setup)  (struct nfs_pgio_data *, struct rpc_message *);
+-      int     (*write_done)  (struct rpc_task *, struct nfs_pgio_data *);
++      int     (*pgio_rpc_prepare)(struct rpc_task *,
++                                  struct nfs_pgio_header *);
++      void    (*read_setup)(struct nfs_pgio_header *, struct rpc_message *);
++      int     (*read_done)(struct rpc_task *, struct nfs_pgio_header *);
++      void    (*write_setup)(struct nfs_pgio_header *, struct rpc_message *);
++      int     (*write_done)(struct rpc_task *, struct nfs_pgio_header *);
+       void    (*commit_setup) (struct nfs_commit_data *, struct rpc_message *);
+       void    (*commit_rpc_prepare)(struct rpc_task *, struct nfs_commit_data *);
+       int     (*commit_done) (struct rpc_task *, struct nfs_commit_data *);
diff --git a/queue-3.16/nfs-move-nfs_pgio_data-and-remove-nfs_rw_header.patch b/queue-3.16/nfs-move-nfs_pgio_data-and-remove-nfs_rw_header.patch
new file mode 100644 (file)
index 0000000..ce60036
--- /dev/null
@@ -0,0 +1,487 @@
+From trond.myklebust@primarydata.com  Thu Oct  2 16:46:24 2014
+From: Trond Myklebust <trond.myklebust@primarydata.com>
+Date: Mon, 15 Sep 2014 14:14:32 -0400
+Subject: nfs: move nfs_pgio_data and remove nfs_rw_header
+To: stable@vger.kernel.org
+Cc: Weston Andros Adamson <dros@primarydata.com>, linux-nfs@vger.kernel.org
+Message-ID: <1410804885-17228-2-git-send-email-trond.myklebust@primarydata.com>
+
+From: Weston Andros Adamson <dros@primarydata.com>
+
+commit 1e7f3a485922211b6e4a082ebc6bf05810b0b6ea upstream.
+
+nfs_rw_header was used to allocate an nfs_pgio_header along with an
+nfs_pgio_data, because a _header would need at least one _data.
+
+Now there is only ever one nfs_pgio_data for each nfs_pgio_header -- move
+it to nfs_pgio_header and get rid of nfs_rw_header.
+
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Signed-off-by: Weston Andros Adamson <dros@primarydata.com>
+Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/nfs/direct.c          |    8 ++--
+ fs/nfs/internal.h        |    6 +--
+ fs/nfs/pagelist.c        |   94 ++++++++++++++---------------------------------
+ fs/nfs/pnfs.c            |   24 +++++-------
+ fs/nfs/read.c            |    6 +--
+ fs/nfs/write.c           |   10 ++---
+ include/linux/nfs_page.h |    4 +-
+ include/linux/nfs_xdr.h  |   38 ++++++++-----------
+ 8 files changed, 71 insertions(+), 119 deletions(-)
+
+--- a/fs/nfs/direct.c
++++ b/fs/nfs/direct.c
+@@ -148,8 +148,8 @@ static void nfs_direct_set_hdr_verf(stru
+ {
+       struct nfs_writeverf *verfp;
+-      verfp = nfs_direct_select_verf(dreq, hdr->data->ds_clp,
+-                                    hdr->data->ds_idx);
++      verfp = nfs_direct_select_verf(dreq, hdr->data.ds_clp,
++                                    hdr->data.ds_idx);
+       WARN_ON_ONCE(verfp->committed >= 0);
+       memcpy(verfp, &hdr->verf, sizeof(struct nfs_writeverf));
+       WARN_ON_ONCE(verfp->committed < 0);
+@@ -169,8 +169,8 @@ static int nfs_direct_set_or_cmp_hdr_ver
+ {
+       struct nfs_writeverf *verfp;
+-      verfp = nfs_direct_select_verf(dreq, hdr->data->ds_clp,
+-                                       hdr->data->ds_idx);
++      verfp = nfs_direct_select_verf(dreq, hdr->data.ds_clp,
++                                       hdr->data.ds_idx);
+       if (verfp->committed < 0) {
+               nfs_direct_set_hdr_verf(dreq, hdr);
+               return 0;
+--- a/fs/nfs/internal.h
++++ b/fs/nfs/internal.h
+@@ -238,9 +238,9 @@ void nfs_set_pgio_error(struct nfs_pgio_
+ int nfs_iocounter_wait(struct nfs_io_counter *c);
+ extern const struct nfs_pageio_ops nfs_pgio_rw_ops;
+-struct nfs_rw_header *nfs_rw_header_alloc(const struct nfs_rw_ops *);
+-void nfs_rw_header_free(struct nfs_pgio_header *);
+-void nfs_pgio_data_release(struct nfs_pgio_data *);
++struct nfs_pgio_header *nfs_pgio_header_alloc(const struct nfs_rw_ops *);
++void nfs_pgio_header_free(struct nfs_pgio_header *);
++void nfs_pgio_data_destroy(struct nfs_pgio_data *);
+ int nfs_generic_pgio(struct nfs_pageio_descriptor *, struct nfs_pgio_header *);
+ int nfs_initiate_pgio(struct rpc_clnt *, struct nfs_pgio_data *,
+                     const struct rpc_call_ops *, int, int);
+--- a/fs/nfs/pagelist.c
++++ b/fs/nfs/pagelist.c
+@@ -462,95 +462,61 @@ size_t nfs_generic_pg_test(struct nfs_pa
+ }
+ EXPORT_SYMBOL_GPL(nfs_generic_pg_test);
+-static inline struct nfs_rw_header *NFS_RW_HEADER(struct nfs_pgio_header *hdr)
++struct nfs_pgio_header *nfs_pgio_header_alloc(const struct nfs_rw_ops *ops)
+ {
+-      return container_of(hdr, struct nfs_rw_header, header);
+-}
+-
+-/**
+- * nfs_rw_header_alloc - Allocate a header for a read or write
+- * @ops: Read or write function vector
+- */
+-struct nfs_rw_header *nfs_rw_header_alloc(const struct nfs_rw_ops *ops)
+-{
+-      struct nfs_rw_header *header = ops->rw_alloc_header();
+-
+-      if (header) {
+-              struct nfs_pgio_header *hdr = &header->header;
++      struct nfs_pgio_header *hdr = ops->rw_alloc_header();
++      if (hdr) {
+               INIT_LIST_HEAD(&hdr->pages);
+               spin_lock_init(&hdr->lock);
+               atomic_set(&hdr->refcnt, 0);
+               hdr->rw_ops = ops;
+       }
+-      return header;
++      return hdr;
+ }
+-EXPORT_SYMBOL_GPL(nfs_rw_header_alloc);
++EXPORT_SYMBOL_GPL(nfs_pgio_header_alloc);
+ /*
+- * nfs_rw_header_free - Free a read or write header
++ * nfs_pgio_header_free - Free a read or write header
+  * @hdr: The header to free
+  */
+-void nfs_rw_header_free(struct nfs_pgio_header *hdr)
++void nfs_pgio_header_free(struct nfs_pgio_header *hdr)
+ {
+-      hdr->rw_ops->rw_free_header(NFS_RW_HEADER(hdr));
++      hdr->rw_ops->rw_free_header(hdr);
+ }
+-EXPORT_SYMBOL_GPL(nfs_rw_header_free);
++EXPORT_SYMBOL_GPL(nfs_pgio_header_free);
+ /**
+  * nfs_pgio_data_alloc - Allocate pageio data
+  * @hdr: The header making a request
+  * @pagecount: Number of pages to create
+  */
+-static struct nfs_pgio_data *nfs_pgio_data_alloc(struct nfs_pgio_header *hdr,
+-                                               unsigned int pagecount)
++static bool nfs_pgio_data_init(struct nfs_pgio_header *hdr,
++                             unsigned int pagecount)
+ {
+-      struct nfs_pgio_data *data, *prealloc;
+-
+-      prealloc = &NFS_RW_HEADER(hdr)->rpc_data;
+-      if (prealloc->header == NULL)
+-              data = prealloc;
+-      else
+-              data = kzalloc(sizeof(*data), GFP_KERNEL);
+-      if (!data)
+-              goto out;
+-
+-      if (nfs_pgarray_set(&data->pages, pagecount)) {
+-              data->header = hdr;
++      if (nfs_pgarray_set(&hdr->data.pages, pagecount)) {
++              hdr->data.header = hdr;
+               atomic_inc(&hdr->refcnt);
+-      } else {
+-              if (data != prealloc)
+-                      kfree(data);
+-              data = NULL;
++              return true;
+       }
+-out:
+-      return data;
++      return false;
+ }
+ /**
+- * nfs_pgio_data_release - Properly free pageio data
+- * @data: The data to release
++ * nfs_pgio_data_destroy - Properly free pageio data
++ * @data: The data to destroy
+  */
+-void nfs_pgio_data_release(struct nfs_pgio_data *data)
++void nfs_pgio_data_destroy(struct nfs_pgio_data *data)
+ {
+       struct nfs_pgio_header *hdr = data->header;
+-      struct nfs_rw_header *pageio_header = NFS_RW_HEADER(hdr);
+       put_nfs_open_context(data->args.context);
+       if (data->pages.pagevec != data->pages.page_array)
+               kfree(data->pages.pagevec);
+-      if (data == &pageio_header->rpc_data) {
+-              data->header = NULL;
+-              data = NULL;
+-      }
+       if (atomic_dec_and_test(&hdr->refcnt))
+               hdr->completion_ops->completion(hdr);
+-      /* Note: we only free the rpc_task after callbacks are done.
+-       * See the comment in rpc_free_task() for why
+-       */
+-      kfree(data);
+ }
+-EXPORT_SYMBOL_GPL(nfs_pgio_data_release);
++EXPORT_SYMBOL_GPL(nfs_pgio_data_destroy);
+ /**
+  * nfs_pgio_rpcsetup - Set up arguments for a pageio call
+@@ -665,8 +631,7 @@ static int nfs_pgio_error(struct nfs_pag
+                         struct nfs_pgio_header *hdr)
+ {
+       set_bit(NFS_IOHDR_REDO, &hdr->flags);
+-      nfs_pgio_data_release(hdr->data);
+-      hdr->data = NULL;
++      nfs_pgio_data_destroy(&hdr->data);
+       desc->pg_completion_ops->error_cleanup(&desc->pg_list);
+       return -ENOMEM;
+ }
+@@ -680,7 +645,7 @@ static void nfs_pgio_release(void *calld
+       struct nfs_pgio_data *data = calldata;
+       if (data->header->rw_ops->rw_release)
+               data->header->rw_ops->rw_release(data);
+-      nfs_pgio_data_release(data);
++      nfs_pgio_data_destroy(data);
+ }
+ /**
+@@ -756,11 +721,11 @@ int nfs_generic_pgio(struct nfs_pageio_d
+       struct list_head *head = &desc->pg_list;
+       struct nfs_commit_info cinfo;
+-      data = nfs_pgio_data_alloc(hdr, nfs_page_array_len(desc->pg_base,
+-                                                         desc->pg_count));
+-      if (!data)
++      if (!nfs_pgio_data_init(hdr, nfs_page_array_len(desc->pg_base,
++                         desc->pg_count)))
+               return nfs_pgio_error(desc, hdr);
++      data = &hdr->data;
+       nfs_init_cinfo(&cinfo, desc->pg_inode, desc->pg_dreq);
+       pages = data->pages.pagevec;
+       while (!list_empty(head)) {
+@@ -776,7 +741,6 @@ int nfs_generic_pgio(struct nfs_pageio_d
+       /* Set up the argument struct */
+       nfs_pgio_rpcsetup(data, desc->pg_count, 0, desc->pg_ioflags, &cinfo);
+-      hdr->data = data;
+       desc->pg_rpc_callops = &nfs_pgio_common_ops;
+       return 0;
+ }
+@@ -784,22 +748,20 @@ EXPORT_SYMBOL_GPL(nfs_generic_pgio);
+ static int nfs_generic_pg_pgios(struct nfs_pageio_descriptor *desc)
+ {
+-      struct nfs_rw_header *rw_hdr;
+       struct nfs_pgio_header *hdr;
+       int ret;
+-      rw_hdr = nfs_rw_header_alloc(desc->pg_rw_ops);
+-      if (!rw_hdr) {
++      hdr = nfs_pgio_header_alloc(desc->pg_rw_ops);
++      if (!hdr) {
+               desc->pg_completion_ops->error_cleanup(&desc->pg_list);
+               return -ENOMEM;
+       }
+-      hdr = &rw_hdr->header;
+-      nfs_pgheader_init(desc, hdr, nfs_rw_header_free);
++      nfs_pgheader_init(desc, hdr, nfs_pgio_header_free);
+       atomic_inc(&hdr->refcnt);
+       ret = nfs_generic_pgio(desc, hdr);
+       if (ret == 0)
+               ret = nfs_initiate_pgio(NFS_CLIENT(hdr->inode),
+-                                      hdr->data, desc->pg_rpc_callops,
++                                      &hdr->data, desc->pg_rpc_callops,
+                                       desc->pg_ioflags, 0);
+       if (atomic_dec_and_test(&hdr->refcnt))
+               hdr->completion_ops->completion(hdr);
+--- a/fs/nfs/pnfs.c
++++ b/fs/nfs/pnfs.c
+@@ -1546,7 +1546,7 @@ pnfs_write_through_mds(struct nfs_pageio
+               nfs_pageio_reset_write_mds(desc);
+               desc->pg_recoalesce = 1;
+       }
+-      nfs_pgio_data_release(data);
++      nfs_pgio_data_destroy(data);
+ }
+ static enum pnfs_try_status
+@@ -1575,7 +1575,7 @@ static void
+ pnfs_do_write(struct nfs_pageio_descriptor *desc,
+             struct nfs_pgio_header *hdr, int how)
+ {
+-      struct nfs_pgio_data *data = hdr->data;
++      struct nfs_pgio_data *data = &hdr->data;
+       const struct rpc_call_ops *call_ops = desc->pg_rpc_callops;
+       struct pnfs_layout_segment *lseg = desc->pg_lseg;
+       enum pnfs_try_status trypnfs;
+@@ -1590,25 +1590,23 @@ pnfs_do_write(struct nfs_pageio_descript
+ static void pnfs_writehdr_free(struct nfs_pgio_header *hdr)
+ {
+       pnfs_put_lseg(hdr->lseg);
+-      nfs_rw_header_free(hdr);
++      nfs_pgio_header_free(hdr);
+ }
+ EXPORT_SYMBOL_GPL(pnfs_writehdr_free);
+ int
+ pnfs_generic_pg_writepages(struct nfs_pageio_descriptor *desc)
+ {
+-      struct nfs_rw_header *whdr;
+       struct nfs_pgio_header *hdr;
+       int ret;
+-      whdr = nfs_rw_header_alloc(desc->pg_rw_ops);
+-      if (!whdr) {
++      hdr = nfs_pgio_header_alloc(desc->pg_rw_ops);
++      if (!hdr) {
+               desc->pg_completion_ops->error_cleanup(&desc->pg_list);
+               pnfs_put_lseg(desc->pg_lseg);
+               desc->pg_lseg = NULL;
+               return -ENOMEM;
+       }
+-      hdr = &whdr->header;
+       nfs_pgheader_init(desc, hdr, pnfs_writehdr_free);
+       hdr->lseg = pnfs_get_lseg(desc->pg_lseg);
+       atomic_inc(&hdr->refcnt);
+@@ -1696,7 +1694,7 @@ pnfs_read_through_mds(struct nfs_pageio_
+               nfs_pageio_reset_read_mds(desc);
+               desc->pg_recoalesce = 1;
+       }
+-      nfs_pgio_data_release(data);
++      nfs_pgio_data_destroy(data);
+ }
+ /*
+@@ -1727,7 +1725,7 @@ pnfs_try_to_read_data(struct nfs_pgio_da
+ static void
+ pnfs_do_read(struct nfs_pageio_descriptor *desc, struct nfs_pgio_header *hdr)
+ {
+-      struct nfs_pgio_data *data = hdr->data;
++      struct nfs_pgio_data *data = &hdr->data;
+       const struct rpc_call_ops *call_ops = desc->pg_rpc_callops;
+       struct pnfs_layout_segment *lseg = desc->pg_lseg;
+       enum pnfs_try_status trypnfs;
+@@ -1742,26 +1740,24 @@ pnfs_do_read(struct nfs_pageio_descripto
+ static void pnfs_readhdr_free(struct nfs_pgio_header *hdr)
+ {
+       pnfs_put_lseg(hdr->lseg);
+-      nfs_rw_header_free(hdr);
++      nfs_pgio_header_free(hdr);
+ }
+ EXPORT_SYMBOL_GPL(pnfs_readhdr_free);
+ int
+ pnfs_generic_pg_readpages(struct nfs_pageio_descriptor *desc)
+ {
+-      struct nfs_rw_header *rhdr;
+       struct nfs_pgio_header *hdr;
+       int ret;
+-      rhdr = nfs_rw_header_alloc(desc->pg_rw_ops);
+-      if (!rhdr) {
++      hdr = nfs_pgio_header_alloc(desc->pg_rw_ops);
++      if (!hdr) {
+               desc->pg_completion_ops->error_cleanup(&desc->pg_list);
+               ret = -ENOMEM;
+               pnfs_put_lseg(desc->pg_lseg);
+               desc->pg_lseg = NULL;
+               return ret;
+       }
+-      hdr = &rhdr->header;
+       nfs_pgheader_init(desc, hdr, pnfs_readhdr_free);
+       hdr->lseg = pnfs_get_lseg(desc->pg_lseg);
+       atomic_inc(&hdr->refcnt);
+--- a/fs/nfs/read.c
++++ b/fs/nfs/read.c
+@@ -33,12 +33,12 @@ static const struct nfs_rw_ops nfs_rw_re
+ static struct kmem_cache *nfs_rdata_cachep;
+-static struct nfs_rw_header *nfs_readhdr_alloc(void)
++static struct nfs_pgio_header *nfs_readhdr_alloc(void)
+ {
+       return kmem_cache_zalloc(nfs_rdata_cachep, GFP_KERNEL);
+ }
+-static void nfs_readhdr_free(struct nfs_rw_header *rhdr)
++static void nfs_readhdr_free(struct nfs_pgio_header *rhdr)
+ {
+       kmem_cache_free(nfs_rdata_cachep, rhdr);
+ }
+@@ -404,7 +404,7 @@ out:
+ int __init nfs_init_readpagecache(void)
+ {
+       nfs_rdata_cachep = kmem_cache_create("nfs_read_data",
+-                                           sizeof(struct nfs_rw_header),
++                                           sizeof(struct nfs_pgio_header),
+                                            0, SLAB_HWCACHE_ALIGN,
+                                            NULL);
+       if (nfs_rdata_cachep == NULL)
+--- a/fs/nfs/write.c
++++ b/fs/nfs/write.c
+@@ -71,18 +71,18 @@ void nfs_commit_free(struct nfs_commit_d
+ }
+ EXPORT_SYMBOL_GPL(nfs_commit_free);
+-static struct nfs_rw_header *nfs_writehdr_alloc(void)
++static struct nfs_pgio_header *nfs_writehdr_alloc(void)
+ {
+-      struct nfs_rw_header *p = mempool_alloc(nfs_wdata_mempool, GFP_NOIO);
++      struct nfs_pgio_header *p = mempool_alloc(nfs_wdata_mempool, GFP_NOIO);
+       if (p)
+               memset(p, 0, sizeof(*p));
+       return p;
+ }
+-static void nfs_writehdr_free(struct nfs_rw_header *whdr)
++static void nfs_writehdr_free(struct nfs_pgio_header *hdr)
+ {
+-      mempool_free(whdr, nfs_wdata_mempool);
++      mempool_free(hdr, nfs_wdata_mempool);
+ }
+ static void nfs_context_set_write_error(struct nfs_open_context *ctx, int error)
+@@ -1884,7 +1884,7 @@ int nfs_migrate_page(struct address_spac
+ int __init nfs_init_writepagecache(void)
+ {
+       nfs_wdata_cachep = kmem_cache_create("nfs_write_data",
+-                                           sizeof(struct nfs_rw_header),
++                                           sizeof(struct nfs_pgio_header),
+                                            0, SLAB_HWCACHE_ALIGN,
+                                            NULL);
+       if (nfs_wdata_cachep == NULL)
+--- a/include/linux/nfs_page.h
++++ b/include/linux/nfs_page.h
+@@ -62,8 +62,8 @@ struct nfs_pageio_ops {
+ struct nfs_rw_ops {
+       const fmode_t rw_mode;
+-      struct nfs_rw_header *(*rw_alloc_header)(void);
+-      void (*rw_free_header)(struct nfs_rw_header *);
++      struct nfs_pgio_header *(*rw_alloc_header)(void);
++      void (*rw_free_header)(struct nfs_pgio_header *);
+       void (*rw_release)(struct nfs_pgio_data *);
+       int  (*rw_done)(struct rpc_task *, struct nfs_pgio_data *, struct inode *);
+       void (*rw_result)(struct rpc_task *, struct nfs_pgio_data *);
+--- a/include/linux/nfs_xdr.h
++++ b/include/linux/nfs_xdr.h
+@@ -1257,13 +1257,27 @@ enum {
+       NFS_IOHDR_NEED_RESCHED,
+ };
+-struct nfs_pgio_data;
++struct nfs_pgio_data {
++      struct nfs_pgio_header  *header;
++      struct list_head        list;
++      struct rpc_task         task;
++      struct nfs_fattr        fattr;
++      struct nfs_writeverf    verf;           /* Used for writes */
++      struct nfs_pgio_args    args;           /* argument struct */
++      struct nfs_pgio_res     res;            /* result struct */
++      unsigned long           timestamp;      /* For lease renewal */
++      int (*pgio_done_cb)(struct rpc_task *task, struct nfs_pgio_data *data);
++      __u64                   mds_offset;     /* Filelayout dense stripe */
++      struct nfs_page_array   pages;
++      struct nfs_client       *ds_clp;        /* pNFS data server */
++      int                     ds_idx;         /* ds index if ds_clp is set */
++};
+ struct nfs_pgio_header {
+       struct inode            *inode;
+       struct rpc_cred         *cred;
+       struct list_head        pages;
+-      struct nfs_pgio_data    *data;
++      struct nfs_pgio_data    data;
+       atomic_t                refcnt;
+       struct nfs_page         *req;
+       struct nfs_writeverf    verf;           /* Used for writes */
+@@ -1283,26 +1297,6 @@ struct nfs_pgio_header {
+       unsigned long           flags;
+ };
+-struct nfs_pgio_data {
+-      struct nfs_pgio_header  *header;
+-      struct rpc_task         task;
+-      struct nfs_fattr        fattr;
+-      struct nfs_writeverf    verf;           /* Used for writes */
+-      struct nfs_pgio_args    args;           /* argument struct */
+-      struct nfs_pgio_res     res;            /* result struct */
+-      unsigned long           timestamp;      /* For lease renewal */
+-      int (*pgio_done_cb) (struct rpc_task *task, struct nfs_pgio_data *data);
+-      __u64                   mds_offset;     /* Filelayout dense stripe */
+-      struct nfs_page_array   pages;
+-      struct nfs_client       *ds_clp;        /* pNFS data server */
+-      int                     ds_idx;         /* ds index if ds_clp is set */
+-};
+-
+-struct nfs_rw_header {
+-      struct nfs_pgio_header  header;
+-      struct nfs_pgio_data    rpc_data;
+-};
+-
+ struct nfs_mds_commit_info {
+       atomic_t rpcs_out;
+       unsigned long           ncommit;
diff --git a/queue-3.16/nfs-remove-pgio_header-refcount-related-cleanup.patch b/queue-3.16/nfs-remove-pgio_header-refcount-related-cleanup.patch
new file mode 100644 (file)
index 0000000..b167dcb
--- /dev/null
@@ -0,0 +1,163 @@
+From trond.myklebust@primarydata.com  Thu Oct  2 16:48:39 2014
+From: Trond Myklebust <trond.myklebust@primarydata.com>
+Date: Mon, 15 Sep 2014 14:14:35 -0400
+Subject: nfs: remove pgio_header refcount, related cleanup
+To: stable@vger.kernel.org
+Cc: Weston Andros Adamson <dros@primarydata.com>, linux-nfs@vger.kernel.org
+Message-ID: <1410804885-17228-5-git-send-email-trond.myklebust@primarydata.com>
+
+
+From: Weston Andros Adamson <dros@primarydata.com>
+
+commit 4714fb51fd03a14d8c73001438283e7f7b752f1e upstream.
+
+The refcounting on nfs_pgio_header was related to there being (possibly)
+more than one nfs_pgio_data. Now that nfs_pgio_data has been merged into
+nfs_pgio_header, there is no reason to do this ref counting.  Just call
+the completion callback on nfs_pgio_release/nfs_pgio_error.
+
+Signed-off-by: Weston Andros Adamson <dros@primarydata.com>
+Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/nfs/pagelist.c       |   36 +++++++++++-------------------------
+ fs/nfs/pnfs.c           |    6 ------
+ include/linux/nfs_xdr.h |    1 -
+ 3 files changed, 11 insertions(+), 32 deletions(-)
+
+--- a/fs/nfs/pagelist.c
++++ b/fs/nfs/pagelist.c
+@@ -469,7 +469,6 @@ struct nfs_pgio_header *nfs_pgio_header_
+       if (hdr) {
+               INIT_LIST_HEAD(&hdr->pages);
+               spin_lock_init(&hdr->lock);
+-              atomic_set(&hdr->refcnt, 0);
+               hdr->rw_ops = ops;
+       }
+       return hdr;
+@@ -487,31 +486,18 @@ void nfs_pgio_header_free(struct nfs_pgi
+ EXPORT_SYMBOL_GPL(nfs_pgio_header_free);
+ /**
+- * nfs_pgio_data_alloc - Allocate pageio data
+- * @hdr: The header making a request
+- * @pagecount: Number of pages to create
+- */
+-static bool nfs_pgio_data_init(struct nfs_pgio_header *hdr,
+-                             unsigned int pagecount)
+-{
+-      if (nfs_pgarray_set(&hdr->page_array, pagecount)) {
+-              atomic_inc(&hdr->refcnt);
+-              return true;
+-      }
+-      return false;
+-}
+-
+-/**
+- * nfs_pgio_data_destroy - Properly release pageio data
+- * @hdr: The header with data to destroy
++ * nfs_pgio_data_destroy - make @hdr suitable for reuse
++ *
++ * Frees memory and releases refs from nfs_generic_pgio, so that it may
++ * be called again.
++ *
++ * @hdr: A header that has had nfs_generic_pgio called
+  */
+ void nfs_pgio_data_destroy(struct nfs_pgio_header *hdr)
+ {
+       put_nfs_open_context(hdr->args.context);
+       if (hdr->page_array.pagevec != hdr->page_array.page_array)
+               kfree(hdr->page_array.pagevec);
+-      if (atomic_dec_and_test(&hdr->refcnt))
+-              hdr->completion_ops->completion(hdr);
+ }
+ EXPORT_SYMBOL_GPL(nfs_pgio_data_destroy);
+@@ -630,6 +616,7 @@ static int nfs_pgio_error(struct nfs_pag
+ {
+       set_bit(NFS_IOHDR_REDO, &hdr->flags);
+       nfs_pgio_data_destroy(hdr);
++      hdr->completion_ops->completion(hdr);
+       desc->pg_completion_ops->error_cleanup(&desc->pg_list);
+       return -ENOMEM;
+ }
+@@ -644,6 +631,7 @@ static void nfs_pgio_release(void *calld
+       if (hdr->rw_ops->rw_release)
+               hdr->rw_ops->rw_release(hdr);
+       nfs_pgio_data_destroy(hdr);
++      hdr->completion_ops->completion(hdr);
+ }
+ /**
+@@ -717,9 +705,10 @@ int nfs_generic_pgio(struct nfs_pageio_d
+       struct page             **pages;
+       struct list_head *head = &desc->pg_list;
+       struct nfs_commit_info cinfo;
++      unsigned int pagecount;
+-      if (!nfs_pgio_data_init(hdr, nfs_page_array_len(desc->pg_base,
+-                         desc->pg_count)))
++      pagecount = nfs_page_array_len(desc->pg_base, desc->pg_count);
++      if (!nfs_pgarray_set(&hdr->page_array, pagecount))
+               return nfs_pgio_error(desc, hdr);
+       nfs_init_cinfo(&cinfo, desc->pg_inode, desc->pg_dreq);
+@@ -753,14 +742,11 @@ static int nfs_generic_pg_pgios(struct n
+               return -ENOMEM;
+       }
+       nfs_pgheader_init(desc, hdr, nfs_pgio_header_free);
+-      atomic_inc(&hdr->refcnt);
+       ret = nfs_generic_pgio(desc, hdr);
+       if (ret == 0)
+               ret = nfs_initiate_pgio(NFS_CLIENT(hdr->inode),
+                                       hdr, desc->pg_rpc_callops,
+                                       desc->pg_ioflags, 0);
+-      if (atomic_dec_and_test(&hdr->refcnt))
+-              hdr->completion_ops->completion(hdr);
+       return ret;
+ }
+--- a/fs/nfs/pnfs.c
++++ b/fs/nfs/pnfs.c
+@@ -1602,15 +1602,12 @@ pnfs_generic_pg_writepages(struct nfs_pa
+       }
+       nfs_pgheader_init(desc, hdr, pnfs_writehdr_free);
+       hdr->lseg = pnfs_get_lseg(desc->pg_lseg);
+-      atomic_inc(&hdr->refcnt);
+       ret = nfs_generic_pgio(desc, hdr);
+       if (ret != 0) {
+               pnfs_put_lseg(desc->pg_lseg);
+               desc->pg_lseg = NULL;
+       } else
+               pnfs_do_write(desc, hdr, desc->pg_ioflags);
+-      if (atomic_dec_and_test(&hdr->refcnt))
+-              hdr->completion_ops->completion(hdr);
+       return ret;
+ }
+ EXPORT_SYMBOL_GPL(pnfs_generic_pg_writepages);
+@@ -1745,15 +1742,12 @@ pnfs_generic_pg_readpages(struct nfs_pag
+       }
+       nfs_pgheader_init(desc, hdr, pnfs_readhdr_free);
+       hdr->lseg = pnfs_get_lseg(desc->pg_lseg);
+-      atomic_inc(&hdr->refcnt);
+       ret = nfs_generic_pgio(desc, hdr);
+       if (ret != 0) {
+               pnfs_put_lseg(desc->pg_lseg);
+               desc->pg_lseg = NULL;
+       } else
+               pnfs_do_read(desc, hdr);
+-      if (atomic_dec_and_test(&hdr->refcnt))
+-              hdr->completion_ops->completion(hdr);
+       return ret;
+ }
+ EXPORT_SYMBOL_GPL(pnfs_generic_pg_readpages);
+--- a/include/linux/nfs_xdr.h
++++ b/include/linux/nfs_xdr.h
+@@ -1261,7 +1261,6 @@ struct nfs_pgio_header {
+       struct inode            *inode;
+       struct rpc_cred         *cred;
+       struct list_head        pages;
+-      atomic_t                refcnt;
+       struct nfs_page         *req;
+       struct nfs_writeverf    verf;           /* Used for writes */
+       struct pnfs_layout_segment *lseg;
diff --git a/queue-3.16/nfs-rename-members-of-nfs_pgio_data.patch b/queue-3.16/nfs-rename-members-of-nfs_pgio_data.patch
new file mode 100644 (file)
index 0000000..63e1337
--- /dev/null
@@ -0,0 +1,209 @@
+From trond.myklebust@primarydata.com  Thu Oct  2 16:46:37 2014
+From: Trond Myklebust <trond.myklebust@primarydata.com>
+Date: Mon, 15 Sep 2014 14:14:33 -0400
+Subject: nfs: rename members of nfs_pgio_data
+To: stable@vger.kernel.org
+Cc: Weston Andros Adamson <dros@primarydata.com>, linux-nfs@vger.kernel.org
+Message-ID: <1410804885-17228-3-git-send-email-trond.myklebust@primarydata.com>
+
+
+From: Weston Andros Adamson <dros@primarydata.com>
+
+commit 823b0c9d9800e712374cda89ac3565bd29f6701b upstream.
+
+Rename "verf" to "writeverf" and "pages" to "page_array" to prepare for
+merge of nfs_pgio_data and nfs_pgio_header.
+
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Signed-off-by: Weston Andros Adamson <dros@primarydata.com>
+Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/nfs/blocklayout/blocklayout.c |   17 ++++++++++-------
+ fs/nfs/objlayout/objlayout.c     |    4 ++--
+ fs/nfs/pagelist.c                |   12 ++++++------
+ fs/nfs/write.c                   |    9 +++++----
+ include/linux/nfs_xdr.h          |    4 ++--
+ 5 files changed, 25 insertions(+), 21 deletions(-)
+
+--- a/fs/nfs/blocklayout/blocklayout.c
++++ b/fs/nfs/blocklayout/blocklayout.c
+@@ -258,7 +258,8 @@ bl_read_pagelist(struct nfs_pgio_data *r
+       const bool is_dio = (header->dreq != NULL);
+       dprintk("%s enter nr_pages %u offset %lld count %u\n", __func__,
+-             rdata->pages.npages, f_offset, (unsigned int)rdata->args.count);
++              rdata->page_array.npages, f_offset,
++              (unsigned int)rdata->args.count);
+       par = alloc_parallel(rdata);
+       if (!par)
+@@ -268,7 +269,7 @@ bl_read_pagelist(struct nfs_pgio_data *r
+       isect = (sector_t) (f_offset >> SECTOR_SHIFT);
+       /* Code assumes extents are page-aligned */
+-      for (i = pg_index; i < rdata->pages.npages; i++) {
++      for (i = pg_index; i < rdata->page_array.npages; i++) {
+               if (!extent_length) {
+                       /* We've used up the previous extent */
+                       bl_put_extent(be);
+@@ -317,7 +318,8 @@ bl_read_pagelist(struct nfs_pgio_data *r
+                       struct pnfs_block_extent *be_read;
+                       be_read = (hole && cow_read) ? cow_read : be;
+-                      bio = do_add_page_to_bio(bio, rdata->pages.npages - i,
++                      bio = do_add_page_to_bio(bio,
++                                               rdata->page_array.npages - i,
+                                                READ,
+                                                isect, pages[i], be_read,
+                                                bl_end_io_read, par,
+@@ -446,7 +448,7 @@ static void bl_end_par_io_write(void *da
+       }
+       wdata->task.tk_status = wdata->header->pnfs_error;
+-      wdata->verf.committed = NFS_FILE_SYNC;
++      wdata->writeverf.committed = NFS_FILE_SYNC;
+       INIT_WORK(&wdata->task.u.tk_work, bl_write_cleanup);
+       schedule_work(&wdata->task.u.tk_work);
+ }
+@@ -699,7 +701,7 @@ bl_write_pagelist(struct nfs_pgio_data *
+               dprintk("pnfsblock nonblock aligned DIO writes. Resend MDS\n");
+               goto out_mds;
+       }
+-      /* At this point, wdata->pages is a (sequential) list of nfs_pages.
++      /* At this point, wdata->page_aray is a (sequential) list of nfs_pages.
+        * We want to write each, and if there is an error set pnfs_error
+        * to have it redone using nfs.
+        */
+@@ -791,7 +793,7 @@ next_page:
+       /* Middle pages */
+       pg_index = wdata->args.pgbase >> PAGE_CACHE_SHIFT;
+-      for (i = pg_index; i < wdata->pages.npages; i++) {
++      for (i = pg_index; i < wdata->page_array.npages; i++) {
+               if (!extent_length) {
+                       /* We've used up the previous extent */
+                       bl_put_extent(be);
+@@ -862,7 +864,8 @@ next_page:
+               }
+-              bio = do_add_page_to_bio(bio, wdata->pages.npages - i, WRITE,
++              bio = do_add_page_to_bio(bio, wdata->page_array.npages - i,
++                                       WRITE,
+                                        isect, pages[i], be,
+                                        bl_end_io_write, par,
+                                        pg_offset, pg_len);
+--- a/fs/nfs/objlayout/objlayout.c
++++ b/fs/nfs/objlayout/objlayout.c
+@@ -329,7 +329,7 @@ objlayout_write_done(struct objlayout_io
+       oir->status = wdata->task.tk_status = status;
+       if (status >= 0) {
+               wdata->res.count = status;
+-              wdata->verf.committed = oir->committed;
++              wdata->writeverf.committed = oir->committed;
+       } else {
+               wdata->header->pnfs_error = status;
+       }
+@@ -337,7 +337,7 @@ objlayout_write_done(struct objlayout_io
+       /* must not use oir after this point */
+       dprintk("%s: Return status %zd committed %d sync=%d\n", __func__,
+-              status, wdata->verf.committed, sync);
++              status, wdata->writeverf.committed, sync);
+       if (sync)
+               pnfs_ld_write_done(wdata);
+--- a/fs/nfs/pagelist.c
++++ b/fs/nfs/pagelist.c
+@@ -494,7 +494,7 @@ EXPORT_SYMBOL_GPL(nfs_pgio_header_free);
+ static bool nfs_pgio_data_init(struct nfs_pgio_header *hdr,
+                              unsigned int pagecount)
+ {
+-      if (nfs_pgarray_set(&hdr->data.pages, pagecount)) {
++      if (nfs_pgarray_set(&hdr->data.page_array, pagecount)) {
+               hdr->data.header = hdr;
+               atomic_inc(&hdr->refcnt);
+               return true;
+@@ -511,8 +511,8 @@ void nfs_pgio_data_destroy(struct nfs_pg
+       struct nfs_pgio_header *hdr = data->header;
+       put_nfs_open_context(data->args.context);
+-      if (data->pages.pagevec != data->pages.page_array)
+-              kfree(data->pages.pagevec);
++      if (data->page_array.pagevec != data->page_array.page_array)
++              kfree(data->page_array.pagevec);
+       if (atomic_dec_and_test(&hdr->refcnt))
+               hdr->completion_ops->completion(hdr);
+ }
+@@ -540,7 +540,7 @@ static void nfs_pgio_rpcsetup(struct nfs
+       /* pnfs_set_layoutcommit needs this */
+       data->mds_offset = data->args.offset;
+       data->args.pgbase = req->wb_pgbase + offset;
+-      data->args.pages  = data->pages.pagevec;
++      data->args.pages  = data->page_array.pagevec;
+       data->args.count  = count;
+       data->args.context = get_nfs_open_context(req->wb_context);
+       data->args.lock_context = req->wb_lock_context;
+@@ -558,7 +558,7 @@ static void nfs_pgio_rpcsetup(struct nfs
+       data->res.fattr   = &data->fattr;
+       data->res.count   = count;
+       data->res.eof     = 0;
+-      data->res.verf    = &data->verf;
++      data->res.verf    = &data->writeverf;
+       nfs_fattr_init(&data->fattr);
+ }
+@@ -727,7 +727,7 @@ int nfs_generic_pgio(struct nfs_pageio_d
+       data = &hdr->data;
+       nfs_init_cinfo(&cinfo, desc->pg_inode, desc->pg_dreq);
+-      pages = data->pages.pagevec;
++      pages = data->page_array.pagevec;
+       while (!list_empty(head)) {
+               req = nfs_list_entry(head->next);
+               nfs_list_remove_request(req);
+--- a/fs/nfs/write.c
++++ b/fs/nfs/write.c
+@@ -828,9 +828,9 @@ nfs_clear_request_commit(struct nfs_page
+ static inline
+ int nfs_write_need_commit(struct nfs_pgio_data *data)
+ {
+-      if (data->verf.committed == NFS_DATA_SYNC)
++      if (data->writeverf.committed == NFS_DATA_SYNC)
+               return data->header->lseg == NULL;
+-      return data->verf.committed != NFS_FILE_SYNC;
++      return data->writeverf.committed != NFS_FILE_SYNC;
+ }
+ #else
+@@ -1323,8 +1323,9 @@ static void nfs_writeback_release_common
+               if (test_bit(NFS_IOHDR_NEED_RESCHED, &hdr->flags))
+                       ; /* Do nothing */
+               else if (!test_and_set_bit(NFS_IOHDR_NEED_COMMIT, &hdr->flags))
+-                      memcpy(&hdr->verf, &data->verf, sizeof(hdr->verf));
+-              else if (memcmp(&hdr->verf, &data->verf, sizeof(hdr->verf)))
++                      memcpy(&hdr->verf, &data->writeverf, sizeof(hdr->verf));
++              else if (memcmp(&hdr->verf, &data->writeverf,
++                       sizeof(hdr->verf)))
+                       set_bit(NFS_IOHDR_NEED_RESCHED, &hdr->flags);
+               spin_unlock(&hdr->lock);
+       }
+--- a/include/linux/nfs_xdr.h
++++ b/include/linux/nfs_xdr.h
+@@ -1262,13 +1262,13 @@ struct nfs_pgio_data {
+       struct list_head        list;
+       struct rpc_task         task;
+       struct nfs_fattr        fattr;
+-      struct nfs_writeverf    verf;           /* Used for writes */
++      struct nfs_writeverf    writeverf;      /* Used for writes */
+       struct nfs_pgio_args    args;           /* argument struct */
+       struct nfs_pgio_res     res;            /* result struct */
+       unsigned long           timestamp;      /* For lease renewal */
+       int (*pgio_done_cb)(struct rpc_task *task, struct nfs_pgio_data *data);
+       __u64                   mds_offset;     /* Filelayout dense stripe */
+-      struct nfs_page_array   pages;
++      struct nfs_page_array   page_array;
+       struct nfs_client       *ds_clp;        /* pNFS data server */
+       int                     ds_idx;         /* ds index if ds_clp is set */
+ };
diff --git a/queue-3.16/nfs-use-blocking-page_group_lock-in-add_request.patch b/queue-3.16/nfs-use-blocking-page_group_lock-in-add_request.patch
new file mode 100644 (file)
index 0000000..d74085d
--- /dev/null
@@ -0,0 +1,58 @@
+From trond.myklebust@primarydata.com  Thu Oct  2 16:49:39 2014
+From: Trond Myklebust <trond.myklebust@primarydata.com>
+Date: Mon, 15 Sep 2014 14:14:41 -0400
+Subject: nfs: use blocking page_group_lock in add_request
+To: stable@vger.kernel.org
+Cc: Weston Andros Adamson <dros@primarydata.com>, linux-nfs@vger.kernel.org
+Message-ID: <1410804885-17228-11-git-send-email-trond.myklebust@primarydata.com>
+
+
+From: Weston Andros Adamson <dros@primarydata.com>
+
+commit bfd484a5606d6a0379a0a2f04251b1e5c1f8995c upstream.
+
+__nfs_pageio_add_request was calling nfs_page_group_lock nonblocking, but
+this can return -EAGAIN which would end up passing -EIO to the application.
+
+There is no reason not to block in this path, so change the two calls to
+do so. Also, there is no need to check the return value of
+nfs_page_group_lock when nonblock=false, so remove the error handling code.
+
+Signed-off-by: Weston Andros Adamson <dros@primarydata.com>
+Reviewed-by: Peng Tao <tao.peng@primarydata.com>
+Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/nfs/pagelist.c |   13 ++-----------
+ 1 file changed, 2 insertions(+), 11 deletions(-)
+
+--- a/fs/nfs/pagelist.c
++++ b/fs/nfs/pagelist.c
+@@ -873,13 +873,8 @@ static int __nfs_pageio_add_request(stru
+       struct nfs_page *subreq;
+       unsigned int bytes_left = 0;
+       unsigned int offset, pgbase;
+-      int ret;
+-      ret = nfs_page_group_lock(req, true);
+-      if (ret < 0) {
+-              desc->pg_error = ret;
+-              return 0;
+-      }
++      nfs_page_group_lock(req, false);
+       subreq = req;
+       bytes_left = subreq->wb_bytes;
+@@ -901,11 +896,7 @@ static int __nfs_pageio_add_request(stru
+                       if (desc->pg_recoalesce)
+                               return 0;
+                       /* retry add_request for this subreq */
+-                      ret = nfs_page_group_lock(req, true);
+-                      if (ret < 0) {
+-                              desc->pg_error = ret;
+-                              return 0;
+-                      }
++                      nfs_page_group_lock(req, false);
+                       continue;
+               }
diff --git a/queue-3.16/nfsd4-fix-corruption-of-nfsv4-read-data.patch b/queue-3.16/nfsd4-fix-corruption-of-nfsv4-read-data.patch
new file mode 100644 (file)
index 0000000..f894b29
--- /dev/null
@@ -0,0 +1,50 @@
+From 15b23ef5d348ea51c5e7573e2ef4116fbc7cb099 Mon Sep 17 00:00:00 2001
+From: "J. Bruce Fields" <bfields@redhat.com>
+Date: Wed, 24 Sep 2014 16:32:34 -0400
+Subject: nfsd4: fix corruption of NFSv4 read data
+
+From: "J. Bruce Fields" <bfields@redhat.com>
+
+commit 15b23ef5d348ea51c5e7573e2ef4116fbc7cb099 upstream.
+
+The calculation of page_ptr here is wrong in the case the read doesn't
+start at an offset that is a multiple of a page.
+
+The result is that nfs4svc_encode_compoundres sets rq_next_page to a
+value one too small, and then the loop in svc_free_res_pages may
+incorrectly fail to clear a page pointer in rq_respages[].
+
+Pages left in rq_respages[] are available for the next rpc request to
+use, so xdr data may be written to that page, which may hold data still
+waiting to be transmitted to the client or data in the page cache.
+
+The observed result was silent data corruption seen on an NFSv4 client.
+
+We tag this as "fixing" 05638dc73af2 because that commit exposed this
+bug, though the incorrect calculation predates it.
+
+Particular thanks to Andrea Arcangeli and David Gilbert for analysis and
+testing.
+
+Fixes: 05638dc73af2 "nfsd4: simplify server xdr->next_page use"
+Reported-by: Andrea Arcangeli <aarcange@redhat.com>
+Tested-by: "Dr. David Alan Gilbert" <dgilbert@redhat.com>
+Signed-off-by: J. Bruce Fields <bfields@redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ fs/nfsd/nfs4xdr.c |    3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/fs/nfsd/nfs4xdr.c
++++ b/fs/nfsd/nfs4xdr.c
+@@ -3112,7 +3112,8 @@ static __be32 nfsd4_encode_splice_read(
+       buf->page_len = maxcount;
+       buf->len += maxcount;
+-      xdr->page_ptr += (maxcount + PAGE_SIZE - 1) / PAGE_SIZE;
++      xdr->page_ptr += (buf->page_base + maxcount + PAGE_SIZE - 1)
++                                                      / PAGE_SIZE;
+       /* Use rest of head for padding and remaining ops: */
+       buf->tail[0].iov_base = xdr->p;
diff --git a/queue-3.16/nfsd4-fix-rd_dircount-enforcement.patch b/queue-3.16/nfsd4-fix-rd_dircount-enforcement.patch
new file mode 100644 (file)
index 0000000..29b30d1
--- /dev/null
@@ -0,0 +1,60 @@
+From aee3776441461c14ba6d8ed9e2149933e65abb6e Mon Sep 17 00:00:00 2001
+From: "J. Bruce Fields" <bfields@redhat.com>
+Date: Wed, 20 Aug 2014 14:49:50 -0400
+Subject: nfsd4: fix rd_dircount enforcement
+
+From: "J. Bruce Fields" <bfields@redhat.com>
+
+commit aee3776441461c14ba6d8ed9e2149933e65abb6e upstream.
+
+Commit 3b299709091b "nfsd4: enforce rd_dircount" totally misunderstood
+rd_dircount; it refers to total non-attribute bytes returned, not number
+of directory entries returned.
+
+Bring the code into agreement with RFC 3530 section 14.2.24.
+
+Fixes: 3b299709091b "nfsd4: enforce rd_dircount"
+Signed-off-by: J. Bruce Fields <bfields@redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ fs/nfsd/nfs4xdr.c |   14 +++++++++++++-
+ 1 file changed, 13 insertions(+), 1 deletion(-)
+
+--- a/fs/nfsd/nfs4xdr.c
++++ b/fs/nfsd/nfs4xdr.c
+@@ -2662,6 +2662,7 @@ nfsd4_encode_dirent(void *ccdv, const ch
+       struct xdr_stream *xdr = cd->xdr;
+       int start_offset = xdr->buf->len;
+       int cookie_offset;
++      u32 name_and_cookie;
+       int entry_bytes;
+       __be32 nfserr = nfserr_toosmall;
+       __be64 wire_offset;
+@@ -2723,7 +2724,14 @@ nfsd4_encode_dirent(void *ccdv, const ch
+       cd->rd_maxcount -= entry_bytes;
+       if (!cd->rd_dircount)
+               goto fail;
+-      cd->rd_dircount--;
++      /*
++       * RFC 3530 14.2.24 describes rd_dircount as only a "hint", so
++       * let's always let through the first entry, at least:
++       */
++      name_and_cookie = 4 * XDR_QUADLEN(namlen) + 8;
++      if (name_and_cookie > cd->rd_dircount && cd->cookie_offset)
++              goto fail;
++      cd->rd_dircount -= min(cd->rd_dircount, name_and_cookie);
+       cd->cookie_offset = cookie_offset;
+ skip_entry:
+       cd->common.err = nfs_ok;
+@@ -3333,6 +3341,10 @@ nfsd4_encode_readdir(struct nfsd4_compou
+       }
+       maxcount = min_t(int, maxcount-16, bytes_left);
++      /* RFC 3530 14.2.24 allows us to ignore dircount when it's 0: */
++      if (!readdir->rd_dircount)
++              readdir->rd_dircount = INT_MAX;
++
+       readdir->xdr = xdr;
+       readdir->rd_maxcount = maxcount;
+       readdir->common.err = 0;
diff --git a/queue-3.16/nfsv4-fix-another-bug-in-the-close-open_downgrade-code.patch b/queue-3.16/nfsv4-fix-another-bug-in-the-close-open_downgrade-code.patch
new file mode 100644 (file)
index 0000000..b1341d2
--- /dev/null
@@ -0,0 +1,65 @@
+From cd9288ffaea4359d5cfe2b8d264911506aed26a4 Mon Sep 17 00:00:00 2001
+From: Trond Myklebust <trond.myklebust@primarydata.com>
+Date: Thu, 18 Sep 2014 11:51:32 -0400
+Subject: NFSv4: Fix another bug in the close/open_downgrade code
+
+From: Trond Myklebust <trond.myklebust@primarydata.com>
+
+commit cd9288ffaea4359d5cfe2b8d264911506aed26a4 upstream.
+
+James Drew reports another bug whereby the NFS client is now sending
+an OPEN_DOWNGRADE in a situation where it should really have sent a
+CLOSE: the client is opening the file for O_RDWR, but then trying to
+do a downgrade to O_RDONLY, which is not allowed by the NFSv4 spec.
+
+Reported-by: James Drews <drews@engr.wisc.edu>
+Link: http://lkml.kernel.org/r/541AD7E5.8020409@engr.wisc.edu
+Fixes: aee7af356e15 (NFSv4: Fix problems with close in the presence...)
+Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ fs/nfs/nfs4proc.c |   30 +++++++++++++++---------------
+ 1 file changed, 15 insertions(+), 15 deletions(-)
+
+--- a/fs/nfs/nfs4proc.c
++++ b/fs/nfs/nfs4proc.c
+@@ -2599,23 +2599,23 @@ static void nfs4_close_prepare(struct rp
+       is_rdwr = test_bit(NFS_O_RDWR_STATE, &state->flags);
+       is_rdonly = test_bit(NFS_O_RDONLY_STATE, &state->flags);
+       is_wronly = test_bit(NFS_O_WRONLY_STATE, &state->flags);
+-      /* Calculate the current open share mode */
+-      calldata->arg.fmode = 0;
+-      if (is_rdonly || is_rdwr)
+-              calldata->arg.fmode |= FMODE_READ;
+-      if (is_wronly || is_rdwr)
+-              calldata->arg.fmode |= FMODE_WRITE;
+       /* Calculate the change in open mode */
++      calldata->arg.fmode = 0;
+       if (state->n_rdwr == 0) {
+-              if (state->n_rdonly == 0) {
+-                      call_close |= is_rdonly || is_rdwr;
+-                      calldata->arg.fmode &= ~FMODE_READ;
+-              }
+-              if (state->n_wronly == 0) {
+-                      call_close |= is_wronly || is_rdwr;
+-                      calldata->arg.fmode &= ~FMODE_WRITE;
+-              }
+-      }
++              if (state->n_rdonly == 0)
++                      call_close |= is_rdonly;
++              else if (is_rdonly)
++                      calldata->arg.fmode |= FMODE_READ;
++              if (state->n_wronly == 0)
++                      call_close |= is_wronly;
++              else if (is_wronly)
++                      calldata->arg.fmode |= FMODE_WRITE;
++      } else if (is_rdwr)
++              calldata->arg.fmode |= FMODE_READ|FMODE_WRITE;
++
++      if (calldata->arg.fmode == 0)
++              call_close |= is_rdwr;
++
+       if (!nfs4_valid_open_stateid(state))
+               call_close = 0;
+       spin_unlock(&state->owner->so_lock);
diff --git a/queue-3.16/nfsv4-nfs4_state_manager-vs.-nfs_server_remove_lists.patch b/queue-3.16/nfsv4-nfs4_state_manager-vs.-nfs_server_remove_lists.patch
new file mode 100644 (file)
index 0000000..5654486
--- /dev/null
@@ -0,0 +1,101 @@
+From 080af20cc945d110f9912d01cf6b66f94a375b8d Mon Sep 17 00:00:00 2001
+From: Steve Dickson <steved@redhat.com>
+Date: Thu, 18 Sep 2014 09:13:17 -0400
+Subject: NFSv4: nfs4_state_manager() vs. nfs_server_remove_lists()
+
+From: Steve Dickson <steved@redhat.com>
+
+commit 080af20cc945d110f9912d01cf6b66f94a375b8d upstream.
+
+There is a race between nfs4_state_manager() and
+nfs_server_remove_lists() that happens during a nfsv3 mount.
+
+The v3 mount notices there is already a supper block so
+nfs_server_remove_lists() called which uses the nfs_client_lock
+spin lock to synchronize access to the client list.
+
+At the same time nfs4_state_manager() is running through
+the client list looking for work to do, using the same
+lock. When nfs4_state_manager() wins the race to the
+list, a v3 client pointer is found and not ignored
+properly which causes the panic.
+
+Moving some protocol checks before the state checking
+avoids the panic.
+
+Signed-off-by: Steve Dickson <steved@redhat.com>
+Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ fs/nfs/nfs4client.c |   38 ++++++++++++++++++++------------------
+ 1 file changed, 20 insertions(+), 18 deletions(-)
+
+--- a/fs/nfs/nfs4client.c
++++ b/fs/nfs/nfs4client.c
+@@ -482,6 +482,16 @@ int nfs40_walk_client_list(struct nfs_cl
+       spin_lock(&nn->nfs_client_lock);
+       list_for_each_entry(pos, &nn->nfs_client_list, cl_share_link) {
++
++              if (pos->rpc_ops != new->rpc_ops)
++                      continue;
++
++              if (pos->cl_proto != new->cl_proto)
++                      continue;
++
++              if (pos->cl_minorversion != new->cl_minorversion)
++                      continue;
++
+               /* If "pos" isn't marked ready, we can't trust the
+                * remaining fields in "pos" */
+               if (pos->cl_cons_state > NFS_CS_READY) {
+@@ -501,15 +511,6 @@ int nfs40_walk_client_list(struct nfs_cl
+               if (pos->cl_cons_state != NFS_CS_READY)
+                       continue;
+-              if (pos->rpc_ops != new->rpc_ops)
+-                      continue;
+-
+-              if (pos->cl_proto != new->cl_proto)
+-                      continue;
+-
+-              if (pos->cl_minorversion != new->cl_minorversion)
+-                      continue;
+-
+               if (pos->cl_clientid != new->cl_clientid)
+                       continue;
+@@ -622,6 +623,16 @@ int nfs41_walk_client_list(struct nfs_cl
+       spin_lock(&nn->nfs_client_lock);
+       list_for_each_entry(pos, &nn->nfs_client_list, cl_share_link) {
++
++              if (pos->rpc_ops != new->rpc_ops)
++                      continue;
++
++              if (pos->cl_proto != new->cl_proto)
++                      continue;
++
++              if (pos->cl_minorversion != new->cl_minorversion)
++                      continue;
++
+               /* If "pos" isn't marked ready, we can't trust the
+                * remaining fields in "pos", especially the client
+                * ID and serverowner fields.  Wait for CREATE_SESSION
+@@ -647,15 +658,6 @@ int nfs41_walk_client_list(struct nfs_cl
+               if (pos->cl_cons_state != NFS_CS_READY)
+                       continue;
+-              if (pos->rpc_ops != new->rpc_ops)
+-                      continue;
+-
+-              if (pos->cl_proto != new->cl_proto)
+-                      continue;
+-
+-              if (pos->cl_minorversion != new->cl_minorversion)
+-                      continue;
+-
+               if (!nfs4_match_clientids(pos, new))
+                       continue;
diff --git a/queue-3.16/pnfs-add-pnfs_put_lseg_async.patch b/queue-3.16/pnfs-add-pnfs_put_lseg_async.patch
new file mode 100644 (file)
index 0000000..01820fb
--- /dev/null
@@ -0,0 +1,86 @@
+From trond.myklebust@primarydata.com  Thu Oct  2 16:49:03 2014
+From: Trond Myklebust <trond.myklebust@primarydata.com>
+Date: Mon, 15 Sep 2014 14:14:37 -0400
+Subject: pnfs: add pnfs_put_lseg_async
+To: stable@vger.kernel.org
+Cc: Weston Andros Adamson <dros@primarydata.com>, linux-nfs@vger.kernel.org
+Message-ID: <1410804885-17228-7-git-send-email-trond.myklebust@primarydata.com>
+
+
+From: Weston Andros Adamson <dros@primarydata.com>
+
+commit e6cf82d1830f5e16a10d566f58db70f297ba5da8 upstream.
+
+This is useful when lsegs need to be released while holding locks.
+
+Signed-off-by: Weston Andros Adamson <dros@primarydata.com>
+Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/nfs/pnfs.c |   17 +++++++++++++++++
+ fs/nfs/pnfs.h |    7 +++++++
+ 2 files changed, 24 insertions(+)
+
+--- a/fs/nfs/pnfs.c
++++ b/fs/nfs/pnfs.c
+@@ -361,6 +361,23 @@ pnfs_put_lseg(struct pnfs_layout_segment
+ }
+ EXPORT_SYMBOL_GPL(pnfs_put_lseg);
++static void pnfs_put_lseg_async_work(struct work_struct *work)
++{
++      struct pnfs_layout_segment *lseg;
++
++      lseg = container_of(work, struct pnfs_layout_segment, pls_work);
++
++      pnfs_put_lseg(lseg);
++}
++
++void
++pnfs_put_lseg_async(struct pnfs_layout_segment *lseg)
++{
++      INIT_WORK(&lseg->pls_work, pnfs_put_lseg_async_work);
++      schedule_work(&lseg->pls_work);
++}
++EXPORT_SYMBOL_GPL(pnfs_put_lseg_async);
++
+ static u64
+ end_offset(u64 start, u64 len)
+ {
+--- a/fs/nfs/pnfs.h
++++ b/fs/nfs/pnfs.h
+@@ -32,6 +32,7 @@
+ #include <linux/nfs_fs.h>
+ #include <linux/nfs_page.h>
++#include <linux/workqueue.h>
+ enum {
+       NFS_LSEG_VALID = 0,     /* cleared when lseg is recalled/returned */
+@@ -46,6 +47,7 @@ struct pnfs_layout_segment {
+       atomic_t pls_refcount;
+       unsigned long pls_flags;
+       struct pnfs_layout_hdr *pls_layout;
++      struct work_struct pls_work;
+ };
+ enum pnfs_try_status {
+@@ -179,6 +181,7 @@ extern int nfs4_proc_layoutreturn(struct
+ /* pnfs.c */
+ void pnfs_get_layout_hdr(struct pnfs_layout_hdr *lo);
+ void pnfs_put_lseg(struct pnfs_layout_segment *lseg);
++void pnfs_put_lseg_async(struct pnfs_layout_segment *lseg);
+ void set_pnfs_layoutdriver(struct nfs_server *, const struct nfs_fh *, u32);
+ void unset_pnfs_layoutdriver(struct nfs_server *);
+@@ -410,6 +413,10 @@ static inline void pnfs_put_lseg(struct
+ {
+ }
++static inline void pnfs_put_lseg_async(struct pnfs_layout_segment *lseg)
++{
++}
++
+ static inline int pnfs_return_layout(struct inode *ino)
+ {
+       return 0;
diff --git a/queue-3.16/revert-acpi-battery-fix-wrong-value-of-capacity_now-reported-when-fully-charged.patch b/queue-3.16/revert-acpi-battery-fix-wrong-value-of-capacity_now-reported-when-fully-charged.patch
new file mode 100644 (file)
index 0000000..c4f0007
--- /dev/null
@@ -0,0 +1,65 @@
+From 508b3c677601797f2d51df3df5caa436dd235cb9 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= <bjorn@mork.no>
+Date: Tue, 9 Sep 2014 10:45:18 +0200
+Subject: Revert "ACPI / battery: fix wrong value of capacity_now reported when fully charged"
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= <bjorn@mork.no>
+
+commit 508b3c677601797f2d51df3df5caa436dd235cb9 upstream.
+
+This reverts commit 232de5143790 ("ACPI / battery: fix wrong value of
+capacity_now reported when fully charged")
+
+There is nothing wrong or unexpected about 'capacity_now' increasing above
+the last 'full_charge_capacity' value. Different charging cycles will cause
+'full_charge_capacity' to vary, both up and down.  Good battery firmwares
+will update 'full_charge_capacity' when the current charging cycle is
+complete, increasing it if necessary. It might even go above
+'design_capacity' on a fresh and healthy battery.
+
+Capping 'capacity_now' to 'full_charge_capacity' is plain wrong, and
+printing a warning if this doesn't happen to match the 'design_capacity'
+is both annoying and terribly wrong.
+
+This results in bogus warnings on perfectly working systems/firmwares:
+
+ [Firmware Bug]: battery: reported current charge level (39800) is higher than reported maximum charge level (39800).
+
+and wrong values being reported for 'capacity_now' and
+'full_charge_capacity' after the warning has been triggered.
+
+Fixes: 232de5143790 ("ACPI / battery: fix wrong value of capacity_now reported when fully charged")
+Signed-off-by: Bjørn Mork <bjorn@mork.no>
+Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/acpi/battery.c |   14 --------------
+ 1 file changed, 14 deletions(-)
+
+--- a/drivers/acpi/battery.c
++++ b/drivers/acpi/battery.c
+@@ -535,20 +535,6 @@ static int acpi_battery_get_state(struct
+                       " invalid.\n");
+       }
+-      /*
+-       * When fully charged, some batteries wrongly report
+-       * capacity_now = design_capacity instead of = full_charge_capacity
+-       */
+-      if (battery->capacity_now > battery->full_charge_capacity
+-          && battery->full_charge_capacity != ACPI_BATTERY_VALUE_UNKNOWN) {
+-              battery->capacity_now = battery->full_charge_capacity;
+-              if (battery->capacity_now != battery->design_capacity)
+-                      printk_once(KERN_WARNING FW_BUG
+-                              "battery: reported current charge level (%d) "
+-                              "is higher than reported maximum charge level (%d).\n",
+-                              battery->capacity_now, battery->full_charge_capacity);
+-      }
+-
+       if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags)
+           && battery->capacity_now >= 0 && battery->capacity_now <= 100)
+               battery->capacity_now = (battery->capacity_now *
index 89825baa308a41eb6b6a5ea0c35102375cfdc8a5..fa3dda184468ef0e22af29edf9530d4fc2a1e81f 100644 (file)
@@ -146,3 +146,28 @@ usb-dwc2-gadget-break-infinite-loop-in-endpoint-disable-code.patch
 usb-dwc2-gadget-do-not-call-disconnect-method-in-pullup.patch
 usb-dwc2-gadget-delay-enabling-irq-once-hardware-is-configured-properly.patch
 usb-dwc2-gadget-avoid-disabling-ep0.patch
+acpi-rtc-fix-cmos-rtc-opregion-handler-accesses-to-wrong-addresses.patch
+acpi-lpss-complete-pm-entries-for-lpss-power-domain.patch
+revert-acpi-battery-fix-wrong-value-of-capacity_now-reported-when-fully-charged.patch
+iommu-vt-d-check-return-value-of-acpi_bus_get_device.patch
+iommu-fsl-fix-warning-resulting-from-adding-pci-device-twice.patch
+iommu-arm-smmu-fix-programming-of-smmu_cbn_tcr-for-stage-1.patch
+nfsd4-fix-rd_dircount-enforcement.patch
+cgroup-check-cgroup-liveliness-before-unbreaking-kernfs.patch
+nfsv4-nfs4_state_manager-vs.-nfs_server_remove_lists.patch
+nfsv4-fix-another-bug-in-the-close-open_downgrade-code.patch
+nfsd4-fix-corruption-of-nfsv4-read-data.patch
+nfs-move-nfs_pgio_data-and-remove-nfs_rw_header.patch
+nfs-rename-members-of-nfs_pgio_data.patch
+nfs-merge-nfs_pgio_data-into-_header.patch
+nfs-remove-pgio_header-refcount-related-cleanup.patch
+nfs-check-wait_on_bit_lock-err-in-page_group_lock.patch
+pnfs-add-pnfs_put_lseg_async.patch
+nfs-clear_request_commit-while-holding-i_lock.patch
+nfs-change-nfs_page_group_lock-argument.patch
+nfs-fix-nonblocking-calls-to-nfs_page_group_lock.patch
+nfs-use-blocking-page_group_lock-in-add_request.patch
+nfs-fix-error-handling-in-lock_and_join_requests.patch
+nfs-don-t-sleep-with-inode-lock-in-lock_and_join_requests.patch
+nfs-disallow-duplicate-pages-in-pgio-page-vectors.patch
+nfs-can_coalesce_requests-must-enforce-contiguity.patch