[PATCH v6 2/5] nfs: track number of pinned pages in nfs_page

From: Pranjal Shrivastava

Date: Fri Aug 14 2026 - 10:36:54 EST


Track the number of pinned pages in nfs_page to handle unpinning
correctly, ensuring that only primary requests perform the final
unpinning operation, preventing subrequests from incorrectly
performing unpinning on behalf of their parent requests.

Add wb_nr_pinned to struct nfs_page to store the count of pinned pages
owned by the request. Update request creation and cleanup helpers to
initialize and use wb_nr_pinned for primary requests. Use the
nfs_page_array_len() helper to calculate the number of pages spanned
by a request's offset and length.

Signed-off-by: Pranjal Shrivastava <praan@xxxxxxxxxx>
Reviewed-by: Christoph Hellwig <hch@xxxxxx>
---
fs/nfs/pagelist.c | 13 +++++++++----
include/linux/nfs_page.h | 1 +
2 files changed, 10 insertions(+), 4 deletions(-)

diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c
index a562cfe2a126..b9ccf2a87e3c 100644
--- a/fs/nfs/pagelist.c
+++ b/fs/nfs/pagelist.c
@@ -457,6 +457,8 @@ struct nfs_page *nfs_page_create_from_page(struct nfs_open_context *ctx,
offset_in_page(offset), count);
if (!IS_ERR(ret)) {
nfs_page_assign_page(ret, page, pinned);
+ if (pinned)
+ ret->wb_nr_pinned = 1;
nfs_page_group_init(ret, NULL);
}
nfs_put_lock_context(l_ctx);
@@ -489,6 +491,9 @@ struct nfs_page *nfs_page_create_from_folio(struct nfs_open_context *ctx,
ret = nfs_page_create(l_ctx, offset, folio->index, offset, count);
if (!IS_ERR(ret)) {
nfs_page_assign_folio(ret, folio, pinned);
+ if (pinned)
+ ret->wb_nr_pinned = nfs_page_array_len(offset_in_page(offset),
+ count);
nfs_page_group_init(ret, NULL);
}
nfs_put_lock_context(l_ctx);
@@ -567,8 +572,8 @@ static void nfs_clear_request(struct nfs_page *req)

if (folio != NULL) {
if (test_and_clear_bit(PG_PINNED, &req->wb_flags)) {
- if (req == req->wb_head)
- unpin_user_folio(folio, 1);
+ if (req->wb_nr_pinned > 0)
+ unpin_user_folio(folio, req->wb_nr_pinned);
} else {
folio_put(folio);
}
@@ -576,8 +581,8 @@ static void nfs_clear_request(struct nfs_page *req)
clear_bit(PG_FOLIO, &req->wb_flags);
} else if (page != NULL) {
if (test_and_clear_bit(PG_PINNED, &req->wb_flags)) {
- if (req == req->wb_head)
- unpin_user_page(page);
+ if (req->wb_nr_pinned > 0)
+ unpin_user_pages(&page, req->wb_nr_pinned);
} else {
put_page(page);
}
diff --git a/include/linux/nfs_page.h b/include/linux/nfs_page.h
index fd7aafe7cb54..080fa3e23580 100644
--- a/include/linux/nfs_page.h
+++ b/include/linux/nfs_page.h
@@ -59,6 +59,7 @@ struct nfs_page {
struct nfs_page *wb_this_page; /* list of reqs for this page */
struct nfs_page *wb_head; /* head pointer for req list */
unsigned short wb_nio; /* Number of I/O attempts */
+ unsigned int wb_nr_pinned; /* Number of pinned pages */
};

struct nfs_pgio_mirror;
--
2.55.0.691.gc56d675ccc-goog