2
0
mirror of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git synced 2025-09-04 20:19:47 +08:00

NFS client bugfix for Linux 6.17

Stable fixes:
 - NFS: Fix a data corrupting race when updating an existing write
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEESQctxSBg8JpV8KqEZwvnipYKAPIFAminvwMACgkQZwvnipYK
 APIFkxAAmmlm+6rVRmkb5W37hgcsntWS90CjIw6axH7WlFToypiDam83pCIYzAMc
 wzO5e03ZnKwp49zabTYSqoLD30/irZ3QwR6H1XIe6NToomYr43CZPjqctZLhVaeq
 HpLfBiJdxI2jJQMpHubQk66MyBhk0tXln+Pzzi59ZESOevbGWcT8bz6v849nIh0F
 emngSbMEKMXLrY+r/NsX31wF3Te4WYNyV/tumck3hOEZNouCKD/a2JKGEXdkWKw6
 1IFiTxo4IT8vsVrUTKsG3QUtkv/v8iZ0FSl1f9FD1C6eubX5Jo4JcEQmDc6NoJTF
 4viy0c19+8TUs//Kax4VlBE6opeb9jXba4iN0FOXeYsbqVXyv8fEuinLu174I+s4
 bQNpScZ8/o/dMio/Qa6RgTvxIvk9kICJEmfF1IKeIb7Kn+nczpsD8CqwT/EKiCV2
 ZotYZP5BP2BKYrtV5eUhWcl9mKpagz8ivHCevKvaG4JX+M2/XVNNspgkdr8hwu3j
 SX5lwOdLP//gHqt3x8PUCrD2G9Pn81qkshR5mfJAyWhPodwrP6vsDSvAnB93zeZB
 LVln0c1BHmvPpjAEnIzCh3mZXdzMHSVHxKldktYDfhfh/8tFJAkS7o3bXpgZ8dMF
 eHsYZHBJnByZBfi61B3RkWB9QU8KWi+winDQJpoU+MbZGxRdFmA=
 =Yh2D
 -----END PGP SIGNATURE-----

Merge tag 'nfs-for-6.17-2' of git://git.linux-nfs.org/projects/trondmy/linux-nfs

Pull NFS client fix from Trond Myklebust:

 - NFS: Fix a data corrupting race when updating an existing write

* tag 'nfs-for-6.17-2' of git://git.linux-nfs.org/projects/trondmy/linux-nfs:
  NFS: Fix a race when updating an existing write
This commit is contained in:
Linus Torvalds 2025-08-22 08:58:58 -04:00
commit e86ba12cf8
3 changed files with 16 additions and 23 deletions

View File

@ -253,13 +253,14 @@ nfs_page_group_unlock(struct nfs_page *req)
nfs_page_clear_headlock(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 * must be called with page group lock held
*/ */
static bool bool nfs_page_group_sync_on_bit_locked(struct nfs_page *req, unsigned int bit)
nfs_page_group_sync_on_bit_locked(struct nfs_page *req, unsigned int bit)
{ {
struct nfs_page *head = req->wb_head; struct nfs_page *head = req->wb_head;
struct nfs_page *tmp; struct nfs_page *tmp;

View File

@ -153,20 +153,10 @@ nfs_page_set_inode_ref(struct nfs_page *req, struct inode *inode)
} }
} }
static int static void nfs_cancel_remove_inode(struct nfs_page *req, struct inode *inode)
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)) if (test_and_clear_bit(PG_REMOVE, &req->wb_flags))
nfs_page_set_inode_ref(req, inode); 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 */ /* Ensure that nobody removed the request before we locked it */
if (head != folio->private) { if (head != folio->private) {
nfs_page_group_unlock(head);
nfs_unlock_and_release_request(head); nfs_unlock_and_release_request(head);
goto retry; goto retry;
} }
ret = nfs_cancel_remove_inode(head, inode); nfs_cancel_remove_inode(head, inode);
if (ret < 0)
goto out_unlock;
ret = nfs_page_group_lock(head);
if (ret < 0)
goto out_unlock;
/* lock each request in the page group */ /* lock each request in the page group */
for (subreq = head->wb_this_page; 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)); 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 folio *folio = nfs_page_to_folio(req->wb_head);
struct address_space *mapping = folio->mapping; 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); spin_unlock(&mapping->i_private_lock);
} }
nfs_page_group_unlock(req);
if (test_and_clear_bit(PG_INODE_REF, &req->wb_flags)) { if (test_and_clear_bit(PG_INODE_REF, &req->wb_flags)) {
atomic_long_dec(&nfsi->nrequests); atomic_long_dec(&nfsi->nrequests);

View File

@ -160,6 +160,7 @@ extern void nfs_join_page_group(struct nfs_page *head,
extern int nfs_page_group_lock(struct nfs_page *); extern int nfs_page_group_lock(struct nfs_page *);
extern void nfs_page_group_unlock(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(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 int nfs_page_set_headlock(struct nfs_page *req);
extern void nfs_page_clear_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 *); extern bool nfs_async_iocounter_wait(struct rpc_task *, struct nfs_lock_context *);