mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
nfs: make nfs_page pin-aware
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 <praan@google.com> Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Anna Schumaker <anna.schumaker@hammerspace.com>
This commit is contained in:
parent
a90ee4305c
commit
d91ae903f4
|
|
@ -145,11 +145,17 @@ static void nfs_direct_file_adjust_size_locked(struct inode *inode,
|
|||
}
|
||||
}
|
||||
|
||||
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,
|
||||
|
|
@ -351,7 +357,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;
|
||||
|
|
@ -366,7 +373,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;
|
||||
|
|
@ -887,7 +894,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;
|
||||
|
|
@ -930,7 +938,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;
|
||||
|
|
|
|||
|
|
@ -404,20 +404,28 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -425,6 +433,7 @@ static void nfs_page_assign_page(struct nfs_page *req, struct page *page)
|
|||
* nfs_page_create_from_page - Create an NFS read/write request.
|
||||
* @ctx: open context to use
|
||||
* @page: page to write
|
||||
* @pinned: true if page is pinned
|
||||
* @pgbase: starting offset within the page for the write
|
||||
* @offset: file offset for the write
|
||||
* @count: number of bytes to read/write
|
||||
|
|
@ -435,6 +444,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,
|
||||
unsigned int pgbase, loff_t offset,
|
||||
unsigned int count)
|
||||
{
|
||||
|
|
@ -446,7 +456,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);
|
||||
|
|
@ -457,6 +467,7 @@ struct nfs_page *nfs_page_create_from_page(struct nfs_open_context *ctx,
|
|||
* nfs_page_create_from_folio - Create an NFS read/write request.
|
||||
* @ctx: open context to use
|
||||
* @folio: folio to write
|
||||
* @pinned: true if folio is pinned
|
||||
* @offset: starting offset within the folio for the write
|
||||
* @count: number of bytes to read/write
|
||||
*
|
||||
|
|
@ -466,6 +477,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,
|
||||
unsigned int offset,
|
||||
unsigned int count)
|
||||
{
|
||||
|
|
@ -476,7 +488,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 +510,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 +566,21 @@ 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)) {
|
||||
if (req == req->wb_head)
|
||||
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)) {
|
||||
if (req == req->wb_head)
|
||||
unpin_user_page(page);
|
||||
} else {
|
||||
put_page(page);
|
||||
}
|
||||
req->wb_page = NULL;
|
||||
}
|
||||
if (l_ctx != NULL) {
|
||||
|
|
|
|||
|
|
@ -324,7 +324,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))
|
||||
|
|
|
|||
|
|
@ -1089,7 +1089,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);
|
||||
|
|
|
|||
|
|
@ -38,6 +38,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;
|
||||
|
|
@ -125,11 +126,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 *);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user