Re: [PATCH v1 1/7] nfs: make nfs_page pin-aware

"Anna Schumaker" <[email protected]>
Newsgroups gmane.linux.kernel,gmane.linux.nfs
Message-ID <[email protected]>
Hi Pranjal,

On Wed, Jun 3, 2026, at 1:30 AM, Pranjal Shrivastava wrote:
> Modernizing the NFS Direct I/O path to use iov_iter_extract_pages()
> introduces page pinning (GUP) instead of standard page referencing.
> To handle this correctly, nfs_page must track whether it holds a
> pin or a standard reference.
>
> Introduce a new flag, PG_PINNED, to struct nfs_page. Update the creation
> path (nfs_page_create_from_page and nfs_page_create_from_folio) to
> accept a pinned bool and set the flag accordingly. If the page is pinned,
> we skip the existing reference increment (get_page/folio_get) as the pin
> itself acts as a reference.
>
> Update nfs_clear_request() & nfs_direct_release_pages() to use
> unpin_user_page() or unpin_user_folio() instead of only refcount
> decrement (put_page) when PG_PINNED flag is set. Finally, ensure
> subrequests inherit the pinning status from their parent request.
>
> Signed-off-by: Pranjal Shrivastava <[email protected]>
> ---
>  fs/nfs/direct.c          | 22 +++++++++++++++-------
>  fs/nfs/pagelist.c        | 36 ++++++++++++++++++++++++++----------
>  fs/nfs/read.c            |  2 +-
>  fs/nfs/write.c           |  2 +-
>  include/linux/nfs_page.h |  3 +++
>  5 files changed, 46 insertions(+), 19 deletions(-)
>
> diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c
> index 48d89716193a..80319c5eeed4 100644
> --- a/fs/nfs/direct.c
> +++ b/fs/nfs/direct.c
> @@ -165,11 +165,17 @@ int nfs_swap_rw(struct kiocb *iocb, struct iov_iter *iter)
>  	return 0;
>  }
> 
> -static void nfs_direct_release_pages(struct page **pages, unsigned int npages)
> +static void nfs_direct_release_pages(struct page **pages, unsigned int npages,
> +				     bool pinned)
>  {
>  	unsigned int i;
> -	for (i = 0; i < npages; i++)
> -		put_page(pages[i]);
> +
> +	if (pinned) {
> +		unpin_user_pages(pages, npages);
> +	} else {
> +		for (i = 0; i < npages; i++)
> +			put_page(pages[i]);
> +	}
>  }
> 
>  void nfs_init_cinfo_from_dreq(struct nfs_commit_info *cinfo,
> @@ -371,7 +377,8 @@ static ssize_t 
> nfs_direct_read_schedule_iovec(struct nfs_direct_req *dreq,
>  			unsigned int req_len = min_t(size_t, bytes, PAGE_SIZE - pgbase);
>  			/* XXX do we need to do the eof zeroing found in async_filler? */
>  			req = nfs_page_create_from_page(dreq->ctx, pagevec[i],
> -							pgbase, pos, req_len);
> +							false, pgbase, pos,
> +							req_len);
>  			if (IS_ERR(req)) {
>  				result = PTR_ERR(req);
>  				break;
> @@ -386,7 +393,7 @@ static ssize_t 
> nfs_direct_read_schedule_iovec(struct nfs_direct_req *dreq,
>  			requested_bytes += req_len;
>  			pos += req_len;
>  		}
> -		nfs_direct_release_pages(pagevec, npages);
> +		nfs_direct_release_pages(pagevec, npages, false);
>  		kvfree(pagevec);
>  		if (result < 0)
>  			break;
> @@ -899,7 +906,8 @@ static ssize_t 
> nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq,
>  			unsigned int req_len = min_t(size_t, bytes, PAGE_SIZE - pgbase);
> 
>  			req = nfs_page_create_from_page(dreq->ctx, pagevec[i],
> -							pgbase, pos, req_len);
> +							false, pgbase, pos,
> +							req_len);
>  			if (IS_ERR(req)) {
>  				result = PTR_ERR(req);
>  				break;
> @@ -942,7 +950,7 @@ static ssize_t 
> nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq,
>  			desc.pg_error = 0;
>  			defer = true;
>  		}
> -		nfs_direct_release_pages(pagevec, npages);
> +		nfs_direct_release_pages(pagevec, npages, false);
>  		kvfree(pagevec);
>  		if (result < 0)
>  			break;
> diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c
> index 4a87b2fdb2e6..5c5601a27663 100644
> --- a/fs/nfs/pagelist.c
> +++ b/fs/nfs/pagelist.c
> @@ -404,20 +404,26 @@ static struct nfs_page *nfs_page_create(struct 
> nfs_lock_context *l_ctx,
>  	return req;
>  }
> 
> -static void nfs_page_assign_folio(struct nfs_page *req, struct folio 
> *folio)
> +static void nfs_page_assign_folio(struct nfs_page *req, struct folio 
> *folio, bool pinned)
>  {
>  	if (folio != NULL) {
>  		req->wb_folio = folio;
> -		folio_get(folio);
> +		if (pinned)
> +			set_bit(PG_PINNED, &req->wb_flags);
> +		else
> +			folio_get(folio);
>  		set_bit(PG_FOLIO, &req->wb_flags);
>  	}
>  }
> 
> -static void nfs_page_assign_page(struct nfs_page *req, struct page 
> *page)
> +static void nfs_page_assign_page(struct nfs_page *req, struct page 
> *page, bool pinned)
>  {
>  	if (page != NULL) {
>  		req->wb_page = page;
> -		get_page(page);
> +		if (pinned)
> +			set_bit(PG_PINNED, &req->wb_flags);
> +		else
> +			get_page(page);
>  	}
>  }
> 
> @@ -435,6 +441,7 @@ static void nfs_page_assign_page(struct nfs_page 
> *req, struct page *page)
>   */
>  struct nfs_page *nfs_page_create_from_page(struct nfs_open_context 
> *ctx,
>  					   struct page *page,
> +					   bool pinned,

Can you add a description for "pinned" to the kernel doc comment right
above this function? I'm seeing:

Warning: fs/nfs/pagelist.c:446 function parameter 'pinned' not described in 'nfs_page_create_from_page'


>  					   unsigned int pgbase, loff_t offset,
>  					   unsigned int count)
>  {
> @@ -446,7 +453,7 @@ struct nfs_page *nfs_page_create_from_page(struct 
> nfs_open_context *ctx,
>  	ret = nfs_page_create(l_ctx, pgbase, offset >> PAGE_SHIFT,
>  			      offset_in_page(offset), count);
>  	if (!IS_ERR(ret)) {
> -		nfs_page_assign_page(ret, page);
> +		nfs_page_assign_page(ret, page, pinned);
>  		nfs_page_group_init(ret, NULL);
>  	}
>  	nfs_put_lock_context(l_ctx);
> @@ -466,6 +473,7 @@ struct nfs_page *nfs_page_create_from_page(struct 
> nfs_open_context *ctx,
>   */
>  struct nfs_page *nfs_page_create_from_folio(struct nfs_open_context 
> *ctx,
>  					    struct folio *folio,
> +					    bool pinned,

Same here:

Warning: fs/nfs/pagelist.c:478 function parameter 'pinned' not described in 'nfs_page_create_from_folio'

Thanks,
Anna

>  					    unsigned int offset,
>  					    unsigned int count)
>  {
> @@ -476,7 +484,7 @@ struct nfs_page *nfs_page_create_from_folio(struct 
> nfs_open_context *ctx,
>  		return ERR_CAST(l_ctx);
>  	ret = nfs_page_create(l_ctx, offset, folio->index, offset, count);
>  	if (!IS_ERR(ret)) {
> -		nfs_page_assign_folio(ret, folio);
> +		nfs_page_assign_folio(ret, folio, pinned);
>  		nfs_page_group_init(ret, NULL);
>  	}
>  	nfs_put_lock_context(l_ctx);
> @@ -498,9 +506,11 @@ nfs_create_subreq(struct nfs_page *req,
>  			      offset, count);
>  	if (!IS_ERR(ret)) {
>  		if (folio)
> -			nfs_page_assign_folio(ret, folio);
> +			nfs_page_assign_folio(ret, folio,
> +					      test_bit(PG_PINNED, &req->wb_flags));
>  		else
> -			nfs_page_assign_page(ret, page);
> +			nfs_page_assign_page(ret, page,
> +					     test_bit(PG_PINNED, &req->wb_flags));
>  		/* find the last request */
>  		for (last = req->wb_head;
>  		     last->wb_this_page != req->wb_head;
> @@ -552,11 +562,17 @@ static void nfs_clear_request(struct nfs_page 
> *req)
>  	struct nfs_open_context *ctx;
> 
>  	if (folio != NULL) {
> -		folio_put(folio);
> +		if (test_and_clear_bit(PG_PINNED, &req->wb_flags))
> +			unpin_user_folio(folio, 1);
> +		else
> +			folio_put(folio);
>  		req->wb_folio = NULL;
>  		clear_bit(PG_FOLIO, &req->wb_flags);
>  	} else if (page != NULL) {
> -		put_page(page);
> +		if (test_and_clear_bit(PG_PINNED, &req->wb_flags))
> +			unpin_user_page(page);
> +		else
> +			put_page(page);
>  		req->wb_page = NULL;
>  	}
>  	if (l_ctx != NULL) {
> diff --git a/fs/nfs/read.c b/fs/nfs/read.c
> index e1fe78d7b8d0..ebfebc47966d 100644
> --- a/fs/nfs/read.c
> +++ b/fs/nfs/read.c
> @@ -301,7 +301,7 @@ int nfs_read_add_folio(struct nfs_pageio_descriptor *pgio,
> 
>  	aligned_len = min_t(unsigned int, ALIGN(len, rsize), fsize);
> 
> -	new = nfs_page_create_from_folio(ctx, folio, 0, aligned_len);
> +	new = nfs_page_create_from_folio(ctx, folio, false, 0, aligned_len);
>  	if (IS_ERR(new)) {
>  		error = PTR_ERR(new);
>  		if (nfs_netfs_folio_unlock(folio))
> diff --git a/fs/nfs/write.c b/fs/nfs/write.c
> index 3134bb17f3e3..cf56c07ae2ee 100644
> --- a/fs/nfs/write.c
> +++ b/fs/nfs/write.c
> @@ -1086,7 +1086,7 @@ static struct nfs_page 
> *nfs_setup_write_request(struct nfs_open_context *ctx,
>  	req = nfs_try_to_update_request(folio, offset, bytes);
>  	if (req != NULL)
>  		goto out;
> -	req = nfs_page_create_from_folio(ctx, folio, offset, bytes);
> +	req = nfs_page_create_from_folio(ctx, folio, false, offset, bytes);
>  	if (IS_ERR(req))
>  		goto out;
>  	nfs_inode_add_request(req);
> diff --git a/include/linux/nfs_page.h b/include/linux/nfs_page.h
> index afe1d8f09d89..9ece78f1b788 100644
> --- a/include/linux/nfs_page.h
> +++ b/include/linux/nfs_page.h
> @@ -37,6 +37,7 @@ enum {
>  	PG_REMOVE,		/* page group sync bit in write path */
>  	PG_CONTENDED1,		/* Is someone waiting for a lock? */
>  	PG_CONTENDED2,		/* Is someone waiting for a lock? */
> +	PG_PINNED,		/* page is pinned by GUP */
>  };
> 
>  struct nfs_inode;
> @@ -124,11 +125,13 @@ struct nfs_pageio_descriptor {
> 
>  extern struct nfs_page *nfs_page_create_from_page(struct 
> nfs_open_context *ctx,
>  						  struct page *page,
> +						  bool pinned,
>  						  unsigned int pgbase,
>  						  loff_t offset,
>  						  unsigned int count);
>  extern struct nfs_page *nfs_page_create_from_folio(struct 
> nfs_open_context *ctx,
>  						   struct folio *folio,
> +						   bool pinned,
>  						   unsigned int offset,
>  						   unsigned int count);
>  extern	void nfs_release_request(struct nfs_page *);
> -- 
> 2.54.0.1013.g208068f2d8-goog
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.