patches.lists.linux.dev archive mirror
 help / color / mirror / Atom feed
From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
To: stable@vger.kernel.org
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	patches@lists.linux.dev, Jeff Layton <jlayton@kernel.org>,
	Joe Quanaim <jdq@meta.com>, Andrew Steffen <aksteffen@meta.com>,
	Trond Myklebust <trond.myklebust@hammerspace.com>
Subject: [PATCH 5.15 06/33] NFS: Fix a race when updating an existing write
Date: Tue,  2 Sep 2025 15:21:24 +0200	[thread overview]
Message-ID: <20250902131927.297887271@linuxfoundation.org> (raw)
In-Reply-To: <20250902131927.045875971@linuxfoundation.org>

5.15-stable review patch.  If anyone has any objections, please let me know.

------------------

From: Trond Myklebust <trond.myklebust@hammerspace.com>

commit 76d2e3890fb169168c73f2e4f8375c7cc24a765e upstream.

After nfs_lock_and_join_requests() tests for whether the request is
still attached to the mapping, nothing prevents a call to
nfs_inode_remove_request() from succeeding until we actually lock the
page group.
The reason is that whoever called nfs_inode_remove_request() doesn't
necessarily have a lock on the page group head.

So in order to avoid races, let's take the page group lock earlier in
nfs_lock_and_join_requests(), and hold it across the removal of the
request in nfs_inode_remove_request().

Reported-by: Jeff Layton <jlayton@kernel.org>
Tested-by: Joe Quanaim <jdq@meta.com>
Tested-by: Andrew Steffen <aksteffen@meta.com>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Fixes: bd37d6fce184 ("NFSv4: Convert nfs_lock_and_join_requests() to use nfs_page_find_head_request()")
Cc: stable@vger.kernel.org
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
 fs/nfs/pagelist.c        |    9 +++---
 fs/nfs/write.c           |   66 ++++++++++++++++-------------------------------
 include/linux/nfs_page.h |    1 
 3 files changed, 29 insertions(+), 47 deletions(-)

--- a/fs/nfs/pagelist.c
+++ b/fs/nfs/pagelist.c
@@ -234,13 +234,14 @@ nfs_page_group_unlock(struct nfs_page *r
 	nfs_page_clear_headlock(req);
 }
 
-/*
- * nfs_page_group_sync_on_bit_locked
+/**
+ * nfs_page_group_sync_on_bit_locked - Test if all requests have @bit set
+ * @req: request in page group
+ * @bit: PG_* bit that is used to sync page group
  *
  * must be called with page group lock held
  */
-static bool
-nfs_page_group_sync_on_bit_locked(struct nfs_page *req, unsigned int bit)
+bool nfs_page_group_sync_on_bit_locked(struct nfs_page *req, unsigned int bit)
 {
 	struct nfs_page *head = req->wb_head;
 	struct nfs_page *tmp;
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -155,20 +155,10 @@ nfs_page_set_inode_ref(struct nfs_page *
 	}
 }
 
-static int
-nfs_cancel_remove_inode(struct nfs_page *req, struct inode *inode)
+static void nfs_cancel_remove_inode(struct nfs_page *req, struct inode *inode)
 {
-	int ret;
-
-	if (!test_bit(PG_REMOVE, &req->wb_flags))
-		return 0;
-	ret = nfs_page_group_lock(req);
-	if (ret)
-		return ret;
 	if (test_and_clear_bit(PG_REMOVE, &req->wb_flags))
 		nfs_page_set_inode_ref(req, inode);
-	nfs_page_group_unlock(req);
-	return 0;
 }
 
 static struct nfs_page *
@@ -240,36 +230,6 @@ static struct nfs_page *nfs_page_find_he
 	return req;
 }
 
-static struct nfs_page *nfs_find_and_lock_page_request(struct page *page)
-{
-	struct inode *inode = page_file_mapping(page)->host;
-	struct nfs_page *req, *head;
-	int ret;
-
-	for (;;) {
-		req = nfs_page_find_head_request(page);
-		if (!req)
-			return req;
-		head = nfs_page_group_lock_head(req);
-		if (head != req)
-			nfs_release_request(req);
-		if (IS_ERR(head))
-			return head;
-		ret = nfs_cancel_remove_inode(head, inode);
-		if (ret < 0) {
-			nfs_unlock_and_release_request(head);
-			return ERR_PTR(ret);
-		}
-		/* Ensure that nobody removed the request before we locked it */
-		if (head == nfs_page_private_request(page))
-			break;
-		if (PageSwapCache(page))
-			break;
-		nfs_unlock_and_release_request(head);
-	}
-	return head;
-}
-
 /* Adjust the file length if we're writing beyond the end */
 static void nfs_grow_file(struct page *page, unsigned int offset, unsigned int count)
 {
@@ -626,14 +586,32 @@ nfs_lock_and_join_requests(struct page *
 	 * reference to the whole page group - the group will not be destroyed
 	 * until the head reference is released.
 	 */
-	head = nfs_find_and_lock_page_request(page);
+retry:
+	head = nfs_page_find_head_request(page);
 	if (IS_ERR_OR_NULL(head))
 		return head;
 
+	while (!nfs_lock_request(head)) {
+		ret = nfs_wait_on_request(head);
+		if (ret < 0) {
+			nfs_release_request(head);
+			return ERR_PTR(ret);
+		}
+	}
+
 	ret = nfs_page_group_lock(head);
 	if (ret < 0)
 		goto out_unlock;
 
+	/* Ensure that nobody removed the request before we locked it */
+	if (head != nfs_page_private_request(page) && !PageSwapCache(page)) {
+		nfs_page_group_unlock(head);
+		nfs_unlock_and_release_request(head);
+		goto retry;
+	}
+
+	nfs_cancel_remove_inode(head, inode);
+
 	/* lock each request in the page group */
 	for (subreq = head->wb_this_page;
 	     subreq != head;
@@ -843,7 +821,8 @@ static void nfs_inode_remove_request(str
 	struct nfs_inode *nfsi = NFS_I(inode);
 	struct nfs_page *head;
 
-	if (nfs_page_group_sync_on_bit(req, PG_REMOVE)) {
+	nfs_page_group_lock(req);
+	if (nfs_page_group_sync_on_bit_locked(req, PG_REMOVE)) {
 		head = req->wb_head;
 
 		spin_lock(&mapping->private_lock);
@@ -854,6 +833,7 @@ static void nfs_inode_remove_request(str
 		}
 		spin_unlock(&mapping->private_lock);
 	}
+	nfs_page_group_unlock(req);
 
 	if (test_and_clear_bit(PG_INODE_REF, &req->wb_flags)) {
 		nfs_release_request(req);
--- a/include/linux/nfs_page.h
+++ b/include/linux/nfs_page.h
@@ -150,6 +150,7 @@ extern void nfs_join_page_group(struct n
 extern int nfs_page_group_lock(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);
+extern bool nfs_page_group_sync_on_bit_locked(struct nfs_page *, unsigned int);
 extern	int nfs_page_set_headlock(struct nfs_page *req);
 extern void nfs_page_clear_headlock(struct nfs_page *req);
 extern bool nfs_async_iocounter_wait(struct rpc_task *, struct nfs_lock_context *);



  parent reply	other threads:[~2025-09-02 13:42 UTC|newest]

Thread overview: 41+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-09-02 13:21 [PATCH 5.15 00/33] 5.15.191-rc1 review Greg Kroah-Hartman
2025-09-02 13:21 ` [PATCH 5.15 01/33] pinctrl: STMFX: add missing HAS_IOMEM dependency Greg Kroah-Hartman
2025-09-02 13:21 ` [PATCH 5.15 02/33] ftrace: Fix potential warning in trace_printk_seq during ftrace_dump Greg Kroah-Hartman
2025-09-02 13:21 ` [PATCH 5.15 03/33] scsi: core: sysfs: Correct sysfs attributes access rights Greg Kroah-Hartman
2025-09-02 13:21 ` [PATCH 5.15 04/33] ASoC: codecs: tx-macro: correct tx_macro_component_drv name Greg Kroah-Hartman
2025-09-02 13:21 ` [PATCH 5.15 05/33] nfs: fold nfs_page_group_lock_subrequests into nfs_lock_and_join_requests Greg Kroah-Hartman
2025-09-02 13:21 ` Greg Kroah-Hartman [this message]
2025-09-02 13:21 ` [PATCH 5.15 07/33] vhost/net: Protect ubufs with rcu read lock in vhost_net_ubuf_put() Greg Kroah-Hartman
2025-09-02 13:21 ` [PATCH 5.15 08/33] udf: Fix directory iteration for longer tail extents Greg Kroah-Hartman
2025-09-02 13:21 ` [PATCH 5.15 09/33] net: ipv4: fix regression in local-broadcast routes Greg Kroah-Hartman
2025-09-02 13:21 ` [PATCH 5.15 10/33] powerpc/kvm: Fix ifdef to remove build warning Greg Kroah-Hartman
2025-09-02 13:21 ` [PATCH 5.15 11/33] Bluetooth: hci_event: Detect if HCI_EV_NUM_COMP_PKTS is unbalanced Greg Kroah-Hartman
2025-09-02 13:21 ` [PATCH 5.15 12/33] atm: atmtcp: Prevent arbitrary write in atmtcp_recv_control() Greg Kroah-Hartman
2025-09-02 13:21 ` [PATCH 5.15 13/33] net: dlink: fix multicast stats being counted incorrectly Greg Kroah-Hartman
2025-09-02 13:21 ` [PATCH 5.15 14/33] phy: mscc: Fix when PTP clock is register and unregister Greg Kroah-Hartman
2025-09-02 13:21 ` [PATCH 5.15 15/33] net/mlx5e: Update and set Xon/Xoff upon MTU set Greg Kroah-Hartman
2025-09-02 13:21 ` [PATCH 5.15 16/33] net/mlx5e: Update and set Xon/Xoff upon port speed set Greg Kroah-Hartman
2025-09-02 13:21 ` [PATCH 5.15 17/33] net/mlx5e: Set local Xoff after FW update Greg Kroah-Hartman
2025-09-02 13:21 ` [PATCH 5.15 18/33] net: stmmac: xgmac: Do not enable RX FIFO Overflow interrupts Greg Kroah-Hartman
2025-09-02 13:21 ` [PATCH 5.15 19/33] sctp: initialize more fields in sctp_v6_from_sk() Greg Kroah-Hartman
2025-09-02 13:21 ` [PATCH 5.15 20/33] efivarfs: Fix slab-out-of-bounds in efivarfs_d_compare Greg Kroah-Hartman
2025-09-02 13:21 ` [PATCH 5.15 21/33] KVM: x86: use array_index_nospec with indices that come from guest Greg Kroah-Hartman
2025-09-02 13:21 ` [PATCH 5.15 22/33] HID: asus: fix UAF via HID_CLAIMED_INPUT validation Greg Kroah-Hartman
2025-09-02 13:21 ` [PATCH 5.15 23/33] HID: multitouch: fix slab out-of-bounds access in mt_report_fixup() Greg Kroah-Hartman
2025-09-02 13:21 ` [PATCH 5.15 24/33] HID: wacom: Add a new Art Pen 2 Greg Kroah-Hartman
2025-09-02 13:21 ` [PATCH 5.15 25/33] HID: hid-ntrig: fix unable to handle page fault in ntrig_report_version() Greg Kroah-Hartman
2025-09-02 13:21 ` [PATCH 5.15 26/33] Revert "drm/amdgpu: fix incorrect vm flags to map bo" Greg Kroah-Hartman
2025-09-02 13:21 ` [PATCH 5.15 27/33] dma/pool: Ensure DMA_DIRECT_REMAP allocations are decrypted Greg Kroah-Hartman
2025-09-02 13:21 ` [PATCH 5.15 28/33] net: usb: qmi_wwan: add Telit Cinterion LE910C4-WWX new compositions Greg Kroah-Hartman
2025-09-02 13:21 ` [PATCH 5.15 29/33] drm/nouveau/disp: Always accept linear modifier Greg Kroah-Hartman
2025-09-02 13:21 ` [PATCH 5.15 30/33] HID: mcp2221: Dont set bus speed on every transfer Greg Kroah-Hartman
2025-09-02 13:21 ` [PATCH 5.15 31/33] HID: mcp2221: Handle reads greater than 60 bytes Greg Kroah-Hartman
2025-09-02 13:21 ` [PATCH 5.15 32/33] Revert "drm/dp: Change AUX DPCD probe address from DPCD_REV to LANE0_1_STATUS" Greg Kroah-Hartman
2025-09-02 13:21 ` [PATCH 5.15 33/33] xfs: do not propagate ENODATA disk errors into xattr code Greg Kroah-Hartman
2025-09-02 16:31 ` 5.15.191-rc1 review Brett A C Sheffield
2025-09-02 17:32 ` [PATCH 5.15 00/33] " Florian Fainelli
2025-09-02 18:03 ` Jon Hunter
2025-09-03  7:03 ` Naresh Kamboju
2025-09-03  8:00 ` Vijayendra Suman
2025-09-03  9:36 ` Ron Economos
2025-09-03 10:51 ` Mark Brown

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250902131927.297887271@linuxfoundation.org \
    --to=gregkh@linuxfoundation.org \
    --cc=aksteffen@meta.com \
    --cc=jdq@meta.com \
    --cc=jlayton@kernel.org \
    --cc=patches@lists.linux.dev \
    --cc=stable@vger.kernel.org \
    --cc=trond.myklebust@hammerspace.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).