FAILED: patch "[PATCH] NFS: Fix a race when updating an existing write" failed to apply to 5.10-stable tree
Subsystems (2)
[email protected]
maintainerThe patch below does not apply to the 5.10-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-5.10.y
git checkout FETCH_HEAD
git cherry-pick -x 76d2e3890fb169168c73f2e4f8375c7cc24a765e
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2025082225-attribute-embark-823b@gregkh' --subject-prefix 'PATCH 5.10.y' HEAD^..
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 76d2e3890fb169168c73f2e4f8375c7cc24a765e Mon Sep 17 00:00:00 2001
From: Trond Myklebust <[email protected]>
Date: Sat, 16 Aug 2025 07:25:20 -0700
Subject: [PATCH] NFS: Fix a race when updating an existing write
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().
fs/nfs/pagelist.c +5 -4
@@ -253,13 +253,14 @@ nfs_page_group_unlock(struct nfs_page *req)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;
fs/nfs/write.c +10 -19
@@ -153,20 +153,10 @@ nfs_page_set_inode_ref(struct nfs_page *req, struct inode *inode) } } -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 ; }/**
@@ -585,19 +575,18 @@ retry: } } + ret =nfs_page_group_lock (head); +if (ret <0 ) + goto out_unlock; +/* Ensure that nobody removed the request before we locked it */ if (head != folio->private) { +nfs_page_group_unlock (head);nfs_unlock_and_release_request (head); goto retry; } - ret =nfs_cancel_remove_inode (head, inode); -if (ret <0 ) - goto out_unlock; - - ret =nfs_page_group_lock (head); -if (ret <0 ) - goto out_unlock; +nfs_cancel_remove_inode (head, inode);/* lock each request in the page group */ for (subreq = head->wb_this_page;
@@ -786,7 +775,8 @@ static void nfs_inode_remove_request(struct nfs_page *req) {struct nfs_inode *nfsi =NFS_I (nfs_page_to_inode (req)); -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 )) {struct folio *folio =nfs_page_to_folio (req->wb_head);struct address_space *mapping = folio->mapping;
@@ -798,6 +788,7 @@ static void nfs_inode_remove_request(struct nfs_page *req) }spin_unlock (&mapping->i_private_lock); } +nfs_page_group_unlock (req);if (test_and_clear_bit (PG_INODE_REF , &req->wb_flags)) {atomic_long_dec (&nfsi->nrequests);
include/linux/nfs_page.h +1 -0
@@ -160,6 +160,7 @@ extern void nfs_join_page_group(struct nfs_page *head,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 *);
Trond Myklebust
maintainercover FAILED: patch "[PATCH] NFS: Fix a race when updating an existing write" failed to apply to 5.10-stable tree
From: Trond Myklebust <[email protected]>
The patch in question has a dependency on a previous patch by Christoph. Both needed backporting to Linux 5.10.y
Christoph Hellwig (1):
nfs: fold nfs_page_group_lock_subrequests into
nfs_lock_and_join_requests
Trond Myklebust (1):
NFS: Fix a race when updating an existing write
fs/nfs/pagelist.c | 86 ++---------------------- fs/nfs/write.c | 140 +++++++++++++++++++++++++-------------- include/linux/nfs_page.h | 2 +- 3 files changed, 97 insertions(+), 131 deletions(-)
Signature · 1 line
2.51.0
Trond Myklebust
maintainerPATCH 1/2 nfs: fold nfs_page_group_lock_subrequests into nfs_lock_and_join_requests
From: Christoph Hellwig <[email protected]>
Fold nfs_page_group_lock_subrequests into nfs_lock_and_join_requests to prepare for future changes to this code, and move the helpers to write.c as well.
--- fs/nfs/pagelist.c | 77 ---------------------------------------- fs/nfs/write.c | 74 ++++++++++++++++++++++++++++++++++---- include/linux/nfs_page.h | 1 - 3 files changed, 68 insertions(+), 84 deletions(-)
fs/nfs/pagelist.c +0 -77
@@ -167,83 +167,6 @@ nfs_page_group_lock_head(struct nfs_page *req)return head; } -/* -* nfs_unroll_locks - unlock all newly locked reqs and wait on @req -* @head: head request of page group, must be holding head lock -* @req: request that couldn't lock and needs to wait on the req bit lock -* -* This is a helper function for nfs_lock_and_join_requests -* returns 0 on success, < 0 on error. -*/ -static void -nfs_unroll_locks (struct nfs_page *head,struct nfs_page *req) -{ -struct nfs_page *tmp; - -/* relinquish all the locks successfully grabbed this run */ -for (tmp = head->wb_this_page ; tmp != req; tmp = tmp->wb_this_page) { -if (!kref_read (&tmp->wb_kref)) -continue ; -nfs_unlock_and_release_request (tmp); - } -} - -/* -* nfs_page_group_lock_subreq - try to lock a subrequest -* @head: head request of page group -* @subreq: request to lock -* -* This is a helper function for nfs_lock_and_join_requests which -* must be called with the head request and page group both locked. -* On error, it returns with the page group unlocked. -*/ -static int -nfs_page_group_lock_subreq (struct nfs_page *head,struct nfs_page *subreq) -{ -int ret; - -if (!kref_get_unless_zero (&subreq->wb_kref)) -return 0 ; -while (!nfs_lock_request (subreq)) { -nfs_page_group_unlock (head); - ret =nfs_wait_on_request (subreq); -if (!ret) - ret =nfs_page_group_lock (head); -if (ret <0 ) { -nfs_unroll_locks (head, subreq); -nfs_release_request (subreq); -return ret; - } - } -return 0 ; -} - -/* -* nfs_page_group_lock_subrequests - try to lock the subrequests -* @head: head request of page group -* -* This is a helper function for nfs_lock_and_join_requests which -* must be called with the head request locked. -*/ -int nfs_page_group_lock_subrequests (struct nfs_page *head) -{ -struct nfs_page *subreq; -int ret; - - ret =nfs_page_group_lock (head); -if (ret <0 ) -return ret; -/* lock each request in the page group */ -for (subreq = head->wb_this_page; subreq != head; - subreq = subreq->wb_this_page) { - ret =nfs_page_group_lock_subreq (head, subreq); -if (ret <0 ) -return ret; - } -nfs_page_group_unlock (head); -return 0 ; -} -/* * nfs_page_set_headlock - set the request PG_HEADLOCK * @req: request that is to be locked
fs/nfs/write.c +68 -6
97 lines not on this page · raw message
include/linux/nfs_page.h +0 -1
@@ -144,7 +144,6 @@ 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 struct nfs_page *nfs_page_group_lock_head (struct nfs_page *req); -extern int nfs_page_group_lock_subrequests (struct nfs_page *head);extern void nfs_join_page_group (struct nfs_page *head,struct nfs_commit_info *cinfo,struct inode *inode);
Signature · 1 line
2.51.0
Trond Myklebust
maintainerPATCH 2/2 NFS: Fix a race when updating an existing write
In reply to Trond MyklebustFrom: Trond Myklebust <[email protected]>
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().
--- fs/nfs/pagelist.c | 9 +++--- fs/nfs/write.c | 66 ++++++++++++++-------------------------- include/linux/nfs_page.h | 1 + 3 files changed, 29 insertions(+), 47 deletions(-)
fs/nfs/pagelist.c +5 -4
@@ -234,13 +234,14 @@ nfs_page_group_unlock(struct nfs_page *req)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;
fs/nfs/write.c +23 -43
106 lines not on this page · raw message
include/linux/nfs_page.h +1 -0
@@ -150,6 +150,7 @@ extern void nfs_join_page_group(struct nfs_page *head,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 *);
Signature · 1 line
2.51.0