162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/fs/nfs/pagelist.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * A set of helper functions for managing NFS read and write requests. 662306a36Sopenharmony_ci * The main purpose of these routines is to provide support for the 762306a36Sopenharmony_ci * coalescing of several requests into a single RPC call. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Copyright 2000, 2001 (c) Trond Myklebust <trond.myklebust@fys.uio.no> 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci#include <linux/file.h> 1562306a36Sopenharmony_ci#include <linux/sched.h> 1662306a36Sopenharmony_ci#include <linux/sunrpc/clnt.h> 1762306a36Sopenharmony_ci#include <linux/nfs.h> 1862306a36Sopenharmony_ci#include <linux/nfs3.h> 1962306a36Sopenharmony_ci#include <linux/nfs4.h> 2062306a36Sopenharmony_ci#include <linux/nfs_fs.h> 2162306a36Sopenharmony_ci#include <linux/nfs_page.h> 2262306a36Sopenharmony_ci#include <linux/nfs_mount.h> 2362306a36Sopenharmony_ci#include <linux/export.h> 2462306a36Sopenharmony_ci#include <linux/filelock.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include "internal.h" 2762306a36Sopenharmony_ci#include "pnfs.h" 2862306a36Sopenharmony_ci#include "nfstrace.h" 2962306a36Sopenharmony_ci#include "fscache.h" 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define NFSDBG_FACILITY NFSDBG_PAGECACHE 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic struct kmem_cache *nfs_page_cachep; 3462306a36Sopenharmony_cistatic const struct rpc_call_ops nfs_pgio_common_ops; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistruct nfs_page_iter_page { 3762306a36Sopenharmony_ci const struct nfs_page *req; 3862306a36Sopenharmony_ci size_t count; 3962306a36Sopenharmony_ci}; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic void nfs_page_iter_page_init(struct nfs_page_iter_page *i, 4262306a36Sopenharmony_ci const struct nfs_page *req) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci i->req = req; 4562306a36Sopenharmony_ci i->count = 0; 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic void nfs_page_iter_page_advance(struct nfs_page_iter_page *i, size_t sz) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci const struct nfs_page *req = i->req; 5162306a36Sopenharmony_ci size_t tmp = i->count + sz; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci i->count = (tmp < req->wb_bytes) ? tmp : req->wb_bytes; 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic struct page *nfs_page_iter_page_get(struct nfs_page_iter_page *i) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci const struct nfs_page *req = i->req; 5962306a36Sopenharmony_ci struct page *page; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci if (i->count != req->wb_bytes) { 6262306a36Sopenharmony_ci size_t base = i->count + req->wb_pgbase; 6362306a36Sopenharmony_ci size_t len = PAGE_SIZE - offset_in_page(base); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci page = nfs_page_to_page(req, base); 6662306a36Sopenharmony_ci nfs_page_iter_page_advance(i, len); 6762306a36Sopenharmony_ci return page; 6862306a36Sopenharmony_ci } 6962306a36Sopenharmony_ci return NULL; 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic struct nfs_pgio_mirror * 7362306a36Sopenharmony_cinfs_pgio_get_mirror(struct nfs_pageio_descriptor *desc, u32 idx) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci if (desc->pg_ops->pg_get_mirror) 7662306a36Sopenharmony_ci return desc->pg_ops->pg_get_mirror(desc, idx); 7762306a36Sopenharmony_ci return &desc->pg_mirrors[0]; 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistruct nfs_pgio_mirror * 8162306a36Sopenharmony_cinfs_pgio_current_mirror(struct nfs_pageio_descriptor *desc) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci return nfs_pgio_get_mirror(desc, desc->pg_mirror_idx); 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_pgio_current_mirror); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic u32 8862306a36Sopenharmony_cinfs_pgio_set_current_mirror(struct nfs_pageio_descriptor *desc, u32 idx) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci if (desc->pg_ops->pg_set_mirror) 9162306a36Sopenharmony_ci return desc->pg_ops->pg_set_mirror(desc, idx); 9262306a36Sopenharmony_ci return desc->pg_mirror_idx; 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_civoid nfs_pgheader_init(struct nfs_pageio_descriptor *desc, 9662306a36Sopenharmony_ci struct nfs_pgio_header *hdr, 9762306a36Sopenharmony_ci void (*release)(struct nfs_pgio_header *hdr)) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci struct nfs_pgio_mirror *mirror = nfs_pgio_current_mirror(desc); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci hdr->req = nfs_list_entry(mirror->pg_list.next); 10362306a36Sopenharmony_ci hdr->inode = desc->pg_inode; 10462306a36Sopenharmony_ci hdr->cred = nfs_req_openctx(hdr->req)->cred; 10562306a36Sopenharmony_ci hdr->io_start = req_offset(hdr->req); 10662306a36Sopenharmony_ci hdr->good_bytes = mirror->pg_count; 10762306a36Sopenharmony_ci hdr->io_completion = desc->pg_io_completion; 10862306a36Sopenharmony_ci hdr->dreq = desc->pg_dreq; 10962306a36Sopenharmony_ci nfs_netfs_set_pgio_header(hdr, desc); 11062306a36Sopenharmony_ci hdr->release = release; 11162306a36Sopenharmony_ci hdr->completion_ops = desc->pg_completion_ops; 11262306a36Sopenharmony_ci if (hdr->completion_ops->init_hdr) 11362306a36Sopenharmony_ci hdr->completion_ops->init_hdr(hdr); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci hdr->pgio_mirror_idx = desc->pg_mirror_idx; 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_pgheader_init); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_civoid nfs_set_pgio_error(struct nfs_pgio_header *hdr, int error, loff_t pos) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci unsigned int new = pos - hdr->io_start; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci trace_nfs_pgio_error(hdr, error, pos); 12462306a36Sopenharmony_ci if (hdr->good_bytes > new) { 12562306a36Sopenharmony_ci hdr->good_bytes = new; 12662306a36Sopenharmony_ci clear_bit(NFS_IOHDR_EOF, &hdr->flags); 12762306a36Sopenharmony_ci if (!test_and_set_bit(NFS_IOHDR_ERROR, &hdr->flags)) 12862306a36Sopenharmony_ci hdr->error = error; 12962306a36Sopenharmony_ci } 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic inline struct nfs_page *nfs_page_alloc(void) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci struct nfs_page *p = 13562306a36Sopenharmony_ci kmem_cache_zalloc(nfs_page_cachep, nfs_io_gfp_mask()); 13662306a36Sopenharmony_ci if (p) 13762306a36Sopenharmony_ci INIT_LIST_HEAD(&p->wb_list); 13862306a36Sopenharmony_ci return p; 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic inline void 14262306a36Sopenharmony_cinfs_page_free(struct nfs_page *p) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci kmem_cache_free(nfs_page_cachep, p); 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci/** 14862306a36Sopenharmony_ci * nfs_iocounter_wait - wait for i/o to complete 14962306a36Sopenharmony_ci * @l_ctx: nfs_lock_context with io_counter to use 15062306a36Sopenharmony_ci * 15162306a36Sopenharmony_ci * returns -ERESTARTSYS if interrupted by a fatal signal. 15262306a36Sopenharmony_ci * Otherwise returns 0 once the io_count hits 0. 15362306a36Sopenharmony_ci */ 15462306a36Sopenharmony_ciint 15562306a36Sopenharmony_cinfs_iocounter_wait(struct nfs_lock_context *l_ctx) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci return wait_var_event_killable(&l_ctx->io_count, 15862306a36Sopenharmony_ci !atomic_read(&l_ctx->io_count)); 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci/** 16262306a36Sopenharmony_ci * nfs_async_iocounter_wait - wait on a rpc_waitqueue for I/O 16362306a36Sopenharmony_ci * to complete 16462306a36Sopenharmony_ci * @task: the rpc_task that should wait 16562306a36Sopenharmony_ci * @l_ctx: nfs_lock_context with io_counter to check 16662306a36Sopenharmony_ci * 16762306a36Sopenharmony_ci * Returns true if there is outstanding I/O to wait on and the 16862306a36Sopenharmony_ci * task has been put to sleep. 16962306a36Sopenharmony_ci */ 17062306a36Sopenharmony_cibool 17162306a36Sopenharmony_cinfs_async_iocounter_wait(struct rpc_task *task, struct nfs_lock_context *l_ctx) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci struct inode *inode = d_inode(l_ctx->open_context->dentry); 17462306a36Sopenharmony_ci bool ret = false; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (atomic_read(&l_ctx->io_count) > 0) { 17762306a36Sopenharmony_ci rpc_sleep_on(&NFS_SERVER(inode)->uoc_rpcwaitq, task, NULL); 17862306a36Sopenharmony_ci ret = true; 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci if (atomic_read(&l_ctx->io_count) == 0) { 18262306a36Sopenharmony_ci rpc_wake_up_queued_task(&NFS_SERVER(inode)->uoc_rpcwaitq, task); 18362306a36Sopenharmony_ci ret = false; 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci return ret; 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_async_iocounter_wait); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci/* 19162306a36Sopenharmony_ci * nfs_page_lock_head_request - page lock the head of the page group 19262306a36Sopenharmony_ci * @req: any member of the page group 19362306a36Sopenharmony_ci */ 19462306a36Sopenharmony_cistruct nfs_page * 19562306a36Sopenharmony_cinfs_page_group_lock_head(struct nfs_page *req) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci struct nfs_page *head = req->wb_head; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci while (!nfs_lock_request(head)) { 20062306a36Sopenharmony_ci int ret = nfs_wait_on_request(head); 20162306a36Sopenharmony_ci if (ret < 0) 20262306a36Sopenharmony_ci return ERR_PTR(ret); 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci if (head != req) 20562306a36Sopenharmony_ci kref_get(&head->wb_kref); 20662306a36Sopenharmony_ci return head; 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci/* 21062306a36Sopenharmony_ci * nfs_unroll_locks - unlock all newly locked reqs and wait on @req 21162306a36Sopenharmony_ci * @head: head request of page group, must be holding head lock 21262306a36Sopenharmony_ci * @req: request that couldn't lock and needs to wait on the req bit lock 21362306a36Sopenharmony_ci * 21462306a36Sopenharmony_ci * This is a helper function for nfs_lock_and_join_requests 21562306a36Sopenharmony_ci * returns 0 on success, < 0 on error. 21662306a36Sopenharmony_ci */ 21762306a36Sopenharmony_cistatic void 21862306a36Sopenharmony_cinfs_unroll_locks(struct nfs_page *head, struct nfs_page *req) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci struct nfs_page *tmp; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci /* relinquish all the locks successfully grabbed this run */ 22362306a36Sopenharmony_ci for (tmp = head->wb_this_page ; tmp != req; tmp = tmp->wb_this_page) { 22462306a36Sopenharmony_ci if (!kref_read(&tmp->wb_kref)) 22562306a36Sopenharmony_ci continue; 22662306a36Sopenharmony_ci nfs_unlock_and_release_request(tmp); 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci/* 23162306a36Sopenharmony_ci * nfs_page_group_lock_subreq - try to lock a subrequest 23262306a36Sopenharmony_ci * @head: head request of page group 23362306a36Sopenharmony_ci * @subreq: request to lock 23462306a36Sopenharmony_ci * 23562306a36Sopenharmony_ci * This is a helper function for nfs_lock_and_join_requests which 23662306a36Sopenharmony_ci * must be called with the head request and page group both locked. 23762306a36Sopenharmony_ci * On error, it returns with the page group unlocked. 23862306a36Sopenharmony_ci */ 23962306a36Sopenharmony_cistatic int 24062306a36Sopenharmony_cinfs_page_group_lock_subreq(struct nfs_page *head, struct nfs_page *subreq) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci int ret; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci if (!kref_get_unless_zero(&subreq->wb_kref)) 24562306a36Sopenharmony_ci return 0; 24662306a36Sopenharmony_ci while (!nfs_lock_request(subreq)) { 24762306a36Sopenharmony_ci nfs_page_group_unlock(head); 24862306a36Sopenharmony_ci ret = nfs_wait_on_request(subreq); 24962306a36Sopenharmony_ci if (!ret) 25062306a36Sopenharmony_ci ret = nfs_page_group_lock(head); 25162306a36Sopenharmony_ci if (ret < 0) { 25262306a36Sopenharmony_ci nfs_unroll_locks(head, subreq); 25362306a36Sopenharmony_ci nfs_release_request(subreq); 25462306a36Sopenharmony_ci return ret; 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci return 0; 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci/* 26162306a36Sopenharmony_ci * nfs_page_group_lock_subrequests - try to lock the subrequests 26262306a36Sopenharmony_ci * @head: head request of page group 26362306a36Sopenharmony_ci * 26462306a36Sopenharmony_ci * This is a helper function for nfs_lock_and_join_requests which 26562306a36Sopenharmony_ci * must be called with the head request locked. 26662306a36Sopenharmony_ci */ 26762306a36Sopenharmony_ciint nfs_page_group_lock_subrequests(struct nfs_page *head) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci struct nfs_page *subreq; 27062306a36Sopenharmony_ci int ret; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci ret = nfs_page_group_lock(head); 27362306a36Sopenharmony_ci if (ret < 0) 27462306a36Sopenharmony_ci return ret; 27562306a36Sopenharmony_ci /* lock each request in the page group */ 27662306a36Sopenharmony_ci for (subreq = head->wb_this_page; subreq != head; 27762306a36Sopenharmony_ci subreq = subreq->wb_this_page) { 27862306a36Sopenharmony_ci ret = nfs_page_group_lock_subreq(head, subreq); 27962306a36Sopenharmony_ci if (ret < 0) 28062306a36Sopenharmony_ci return ret; 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci nfs_page_group_unlock(head); 28362306a36Sopenharmony_ci return 0; 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci/* 28762306a36Sopenharmony_ci * nfs_page_set_headlock - set the request PG_HEADLOCK 28862306a36Sopenharmony_ci * @req: request that is to be locked 28962306a36Sopenharmony_ci * 29062306a36Sopenharmony_ci * this lock must be held when modifying req->wb_head 29162306a36Sopenharmony_ci * 29262306a36Sopenharmony_ci * return 0 on success, < 0 on error 29362306a36Sopenharmony_ci */ 29462306a36Sopenharmony_ciint 29562306a36Sopenharmony_cinfs_page_set_headlock(struct nfs_page *req) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci if (!test_and_set_bit(PG_HEADLOCK, &req->wb_flags)) 29862306a36Sopenharmony_ci return 0; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci set_bit(PG_CONTENDED1, &req->wb_flags); 30162306a36Sopenharmony_ci smp_mb__after_atomic(); 30262306a36Sopenharmony_ci return wait_on_bit_lock(&req->wb_flags, PG_HEADLOCK, 30362306a36Sopenharmony_ci TASK_UNINTERRUPTIBLE); 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci/* 30762306a36Sopenharmony_ci * nfs_page_clear_headlock - clear the request PG_HEADLOCK 30862306a36Sopenharmony_ci * @req: request that is to be locked 30962306a36Sopenharmony_ci */ 31062306a36Sopenharmony_civoid 31162306a36Sopenharmony_cinfs_page_clear_headlock(struct nfs_page *req) 31262306a36Sopenharmony_ci{ 31362306a36Sopenharmony_ci clear_bit_unlock(PG_HEADLOCK, &req->wb_flags); 31462306a36Sopenharmony_ci smp_mb__after_atomic(); 31562306a36Sopenharmony_ci if (!test_bit(PG_CONTENDED1, &req->wb_flags)) 31662306a36Sopenharmony_ci return; 31762306a36Sopenharmony_ci wake_up_bit(&req->wb_flags, PG_HEADLOCK); 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci/* 32162306a36Sopenharmony_ci * nfs_page_group_lock - lock the head of the page group 32262306a36Sopenharmony_ci * @req: request in group that is to be locked 32362306a36Sopenharmony_ci * 32462306a36Sopenharmony_ci * this lock must be held when traversing or modifying the page 32562306a36Sopenharmony_ci * group list 32662306a36Sopenharmony_ci * 32762306a36Sopenharmony_ci * return 0 on success, < 0 on error 32862306a36Sopenharmony_ci */ 32962306a36Sopenharmony_ciint 33062306a36Sopenharmony_cinfs_page_group_lock(struct nfs_page *req) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci int ret; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci ret = nfs_page_set_headlock(req); 33562306a36Sopenharmony_ci if (ret || req->wb_head == req) 33662306a36Sopenharmony_ci return ret; 33762306a36Sopenharmony_ci return nfs_page_set_headlock(req->wb_head); 33862306a36Sopenharmony_ci} 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci/* 34162306a36Sopenharmony_ci * nfs_page_group_unlock - unlock the head of the page group 34262306a36Sopenharmony_ci * @req: request in group that is to be unlocked 34362306a36Sopenharmony_ci */ 34462306a36Sopenharmony_civoid 34562306a36Sopenharmony_cinfs_page_group_unlock(struct nfs_page *req) 34662306a36Sopenharmony_ci{ 34762306a36Sopenharmony_ci if (req != req->wb_head) 34862306a36Sopenharmony_ci nfs_page_clear_headlock(req->wb_head); 34962306a36Sopenharmony_ci nfs_page_clear_headlock(req); 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci/* 35362306a36Sopenharmony_ci * nfs_page_group_sync_on_bit_locked 35462306a36Sopenharmony_ci * 35562306a36Sopenharmony_ci * must be called with page group lock held 35662306a36Sopenharmony_ci */ 35762306a36Sopenharmony_cistatic bool 35862306a36Sopenharmony_cinfs_page_group_sync_on_bit_locked(struct nfs_page *req, unsigned int bit) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci struct nfs_page *head = req->wb_head; 36162306a36Sopenharmony_ci struct nfs_page *tmp; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci WARN_ON_ONCE(!test_bit(PG_HEADLOCK, &head->wb_flags)); 36462306a36Sopenharmony_ci WARN_ON_ONCE(test_and_set_bit(bit, &req->wb_flags)); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci tmp = req->wb_this_page; 36762306a36Sopenharmony_ci while (tmp != req) { 36862306a36Sopenharmony_ci if (!test_bit(bit, &tmp->wb_flags)) 36962306a36Sopenharmony_ci return false; 37062306a36Sopenharmony_ci tmp = tmp->wb_this_page; 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci /* true! reset all bits */ 37462306a36Sopenharmony_ci tmp = req; 37562306a36Sopenharmony_ci do { 37662306a36Sopenharmony_ci clear_bit(bit, &tmp->wb_flags); 37762306a36Sopenharmony_ci tmp = tmp->wb_this_page; 37862306a36Sopenharmony_ci } while (tmp != req); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci return true; 38162306a36Sopenharmony_ci} 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci/* 38462306a36Sopenharmony_ci * nfs_page_group_sync_on_bit - set bit on current request, but only 38562306a36Sopenharmony_ci * return true if the bit is set for all requests in page group 38662306a36Sopenharmony_ci * @req - request in page group 38762306a36Sopenharmony_ci * @bit - PG_* bit that is used to sync page group 38862306a36Sopenharmony_ci */ 38962306a36Sopenharmony_cibool nfs_page_group_sync_on_bit(struct nfs_page *req, unsigned int bit) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci bool ret; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci nfs_page_group_lock(req); 39462306a36Sopenharmony_ci ret = nfs_page_group_sync_on_bit_locked(req, bit); 39562306a36Sopenharmony_ci nfs_page_group_unlock(req); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci return ret; 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci/* 40162306a36Sopenharmony_ci * nfs_page_group_init - Initialize the page group linkage for @req 40262306a36Sopenharmony_ci * @req - a new nfs request 40362306a36Sopenharmony_ci * @prev - the previous request in page group, or NULL if @req is the first 40462306a36Sopenharmony_ci * or only request in the group (the head). 40562306a36Sopenharmony_ci */ 40662306a36Sopenharmony_cistatic inline void 40762306a36Sopenharmony_cinfs_page_group_init(struct nfs_page *req, struct nfs_page *prev) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci struct inode *inode; 41062306a36Sopenharmony_ci WARN_ON_ONCE(prev == req); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci if (!prev) { 41362306a36Sopenharmony_ci /* a head request */ 41462306a36Sopenharmony_ci req->wb_head = req; 41562306a36Sopenharmony_ci req->wb_this_page = req; 41662306a36Sopenharmony_ci } else { 41762306a36Sopenharmony_ci /* a subrequest */ 41862306a36Sopenharmony_ci WARN_ON_ONCE(prev->wb_this_page != prev->wb_head); 41962306a36Sopenharmony_ci WARN_ON_ONCE(!test_bit(PG_HEADLOCK, &prev->wb_head->wb_flags)); 42062306a36Sopenharmony_ci req->wb_head = prev->wb_head; 42162306a36Sopenharmony_ci req->wb_this_page = prev->wb_this_page; 42262306a36Sopenharmony_ci prev->wb_this_page = req; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci /* All subrequests take a ref on the head request until 42562306a36Sopenharmony_ci * nfs_page_group_destroy is called */ 42662306a36Sopenharmony_ci kref_get(&req->wb_head->wb_kref); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci /* grab extra ref and bump the request count if head request 42962306a36Sopenharmony_ci * has extra ref from the write/commit path to handle handoff 43062306a36Sopenharmony_ci * between write and commit lists. */ 43162306a36Sopenharmony_ci if (test_bit(PG_INODE_REF, &prev->wb_head->wb_flags)) { 43262306a36Sopenharmony_ci inode = nfs_page_to_inode(req); 43362306a36Sopenharmony_ci set_bit(PG_INODE_REF, &req->wb_flags); 43462306a36Sopenharmony_ci kref_get(&req->wb_kref); 43562306a36Sopenharmony_ci atomic_long_inc(&NFS_I(inode)->nrequests); 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci} 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci/* 44162306a36Sopenharmony_ci * nfs_page_group_destroy - sync the destruction of page groups 44262306a36Sopenharmony_ci * @req - request that no longer needs the page group 44362306a36Sopenharmony_ci * 44462306a36Sopenharmony_ci * releases the page group reference from each member once all 44562306a36Sopenharmony_ci * members have called this function. 44662306a36Sopenharmony_ci */ 44762306a36Sopenharmony_cistatic void 44862306a36Sopenharmony_cinfs_page_group_destroy(struct kref *kref) 44962306a36Sopenharmony_ci{ 45062306a36Sopenharmony_ci struct nfs_page *req = container_of(kref, struct nfs_page, wb_kref); 45162306a36Sopenharmony_ci struct nfs_page *head = req->wb_head; 45262306a36Sopenharmony_ci struct nfs_page *tmp, *next; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci if (!nfs_page_group_sync_on_bit(req, PG_TEARDOWN)) 45562306a36Sopenharmony_ci goto out; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci tmp = req; 45862306a36Sopenharmony_ci do { 45962306a36Sopenharmony_ci next = tmp->wb_this_page; 46062306a36Sopenharmony_ci /* unlink and free */ 46162306a36Sopenharmony_ci tmp->wb_this_page = tmp; 46262306a36Sopenharmony_ci tmp->wb_head = tmp; 46362306a36Sopenharmony_ci nfs_free_request(tmp); 46462306a36Sopenharmony_ci tmp = next; 46562306a36Sopenharmony_ci } while (tmp != req); 46662306a36Sopenharmony_ciout: 46762306a36Sopenharmony_ci /* subrequests must release the ref on the head request */ 46862306a36Sopenharmony_ci if (head != req) 46962306a36Sopenharmony_ci nfs_release_request(head); 47062306a36Sopenharmony_ci} 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_cistatic struct nfs_page *nfs_page_create(struct nfs_lock_context *l_ctx, 47362306a36Sopenharmony_ci unsigned int pgbase, pgoff_t index, 47462306a36Sopenharmony_ci unsigned int offset, unsigned int count) 47562306a36Sopenharmony_ci{ 47662306a36Sopenharmony_ci struct nfs_page *req; 47762306a36Sopenharmony_ci struct nfs_open_context *ctx = l_ctx->open_context; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci if (test_bit(NFS_CONTEXT_BAD, &ctx->flags)) 48062306a36Sopenharmony_ci return ERR_PTR(-EBADF); 48162306a36Sopenharmony_ci /* try to allocate the request struct */ 48262306a36Sopenharmony_ci req = nfs_page_alloc(); 48362306a36Sopenharmony_ci if (req == NULL) 48462306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci req->wb_lock_context = l_ctx; 48762306a36Sopenharmony_ci refcount_inc(&l_ctx->count); 48862306a36Sopenharmony_ci atomic_inc(&l_ctx->io_count); 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci /* Initialize the request struct. Initially, we assume a 49162306a36Sopenharmony_ci * long write-back delay. This will be adjusted in 49262306a36Sopenharmony_ci * update_nfs_request below if the region is not locked. */ 49362306a36Sopenharmony_ci req->wb_pgbase = pgbase; 49462306a36Sopenharmony_ci req->wb_index = index; 49562306a36Sopenharmony_ci req->wb_offset = offset; 49662306a36Sopenharmony_ci req->wb_bytes = count; 49762306a36Sopenharmony_ci kref_init(&req->wb_kref); 49862306a36Sopenharmony_ci req->wb_nio = 0; 49962306a36Sopenharmony_ci return req; 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_cistatic void nfs_page_assign_folio(struct nfs_page *req, struct folio *folio) 50362306a36Sopenharmony_ci{ 50462306a36Sopenharmony_ci if (folio != NULL) { 50562306a36Sopenharmony_ci req->wb_folio = folio; 50662306a36Sopenharmony_ci folio_get(folio); 50762306a36Sopenharmony_ci set_bit(PG_FOLIO, &req->wb_flags); 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci} 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_cistatic void nfs_page_assign_page(struct nfs_page *req, struct page *page) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci if (page != NULL) { 51462306a36Sopenharmony_ci req->wb_page = page; 51562306a36Sopenharmony_ci get_page(page); 51662306a36Sopenharmony_ci } 51762306a36Sopenharmony_ci} 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci/** 52062306a36Sopenharmony_ci * nfs_page_create_from_page - Create an NFS read/write request. 52162306a36Sopenharmony_ci * @ctx: open context to use 52262306a36Sopenharmony_ci * @page: page to write 52362306a36Sopenharmony_ci * @pgbase: starting offset within the page for the write 52462306a36Sopenharmony_ci * @offset: file offset for the write 52562306a36Sopenharmony_ci * @count: number of bytes to read/write 52662306a36Sopenharmony_ci * 52762306a36Sopenharmony_ci * The page must be locked by the caller. This makes sure we never 52862306a36Sopenharmony_ci * create two different requests for the same page. 52962306a36Sopenharmony_ci * User should ensure it is safe to sleep in this function. 53062306a36Sopenharmony_ci */ 53162306a36Sopenharmony_cistruct nfs_page *nfs_page_create_from_page(struct nfs_open_context *ctx, 53262306a36Sopenharmony_ci struct page *page, 53362306a36Sopenharmony_ci unsigned int pgbase, loff_t offset, 53462306a36Sopenharmony_ci unsigned int count) 53562306a36Sopenharmony_ci{ 53662306a36Sopenharmony_ci struct nfs_lock_context *l_ctx = nfs_get_lock_context(ctx); 53762306a36Sopenharmony_ci struct nfs_page *ret; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci if (IS_ERR(l_ctx)) 54062306a36Sopenharmony_ci return ERR_CAST(l_ctx); 54162306a36Sopenharmony_ci ret = nfs_page_create(l_ctx, pgbase, offset >> PAGE_SHIFT, 54262306a36Sopenharmony_ci offset_in_page(offset), count); 54362306a36Sopenharmony_ci if (!IS_ERR(ret)) { 54462306a36Sopenharmony_ci nfs_page_assign_page(ret, page); 54562306a36Sopenharmony_ci nfs_page_group_init(ret, NULL); 54662306a36Sopenharmony_ci } 54762306a36Sopenharmony_ci nfs_put_lock_context(l_ctx); 54862306a36Sopenharmony_ci return ret; 54962306a36Sopenharmony_ci} 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci/** 55262306a36Sopenharmony_ci * nfs_page_create_from_folio - Create an NFS read/write request. 55362306a36Sopenharmony_ci * @ctx: open context to use 55462306a36Sopenharmony_ci * @folio: folio to write 55562306a36Sopenharmony_ci * @offset: starting offset within the folio for the write 55662306a36Sopenharmony_ci * @count: number of bytes to read/write 55762306a36Sopenharmony_ci * 55862306a36Sopenharmony_ci * The page must be locked by the caller. This makes sure we never 55962306a36Sopenharmony_ci * create two different requests for the same page. 56062306a36Sopenharmony_ci * User should ensure it is safe to sleep in this function. 56162306a36Sopenharmony_ci */ 56262306a36Sopenharmony_cistruct nfs_page *nfs_page_create_from_folio(struct nfs_open_context *ctx, 56362306a36Sopenharmony_ci struct folio *folio, 56462306a36Sopenharmony_ci unsigned int offset, 56562306a36Sopenharmony_ci unsigned int count) 56662306a36Sopenharmony_ci{ 56762306a36Sopenharmony_ci struct nfs_lock_context *l_ctx = nfs_get_lock_context(ctx); 56862306a36Sopenharmony_ci struct nfs_page *ret; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci if (IS_ERR(l_ctx)) 57162306a36Sopenharmony_ci return ERR_CAST(l_ctx); 57262306a36Sopenharmony_ci ret = nfs_page_create(l_ctx, offset, folio_index(folio), offset, count); 57362306a36Sopenharmony_ci if (!IS_ERR(ret)) { 57462306a36Sopenharmony_ci nfs_page_assign_folio(ret, folio); 57562306a36Sopenharmony_ci nfs_page_group_init(ret, NULL); 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci nfs_put_lock_context(l_ctx); 57862306a36Sopenharmony_ci return ret; 57962306a36Sopenharmony_ci} 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_cistatic struct nfs_page * 58262306a36Sopenharmony_cinfs_create_subreq(struct nfs_page *req, 58362306a36Sopenharmony_ci unsigned int pgbase, 58462306a36Sopenharmony_ci unsigned int offset, 58562306a36Sopenharmony_ci unsigned int count) 58662306a36Sopenharmony_ci{ 58762306a36Sopenharmony_ci struct nfs_page *last; 58862306a36Sopenharmony_ci struct nfs_page *ret; 58962306a36Sopenharmony_ci struct folio *folio = nfs_page_to_folio(req); 59062306a36Sopenharmony_ci struct page *page = nfs_page_to_page(req, pgbase); 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci ret = nfs_page_create(req->wb_lock_context, pgbase, req->wb_index, 59362306a36Sopenharmony_ci offset, count); 59462306a36Sopenharmony_ci if (!IS_ERR(ret)) { 59562306a36Sopenharmony_ci if (folio) 59662306a36Sopenharmony_ci nfs_page_assign_folio(ret, folio); 59762306a36Sopenharmony_ci else 59862306a36Sopenharmony_ci nfs_page_assign_page(ret, page); 59962306a36Sopenharmony_ci /* find the last request */ 60062306a36Sopenharmony_ci for (last = req->wb_head; 60162306a36Sopenharmony_ci last->wb_this_page != req->wb_head; 60262306a36Sopenharmony_ci last = last->wb_this_page) 60362306a36Sopenharmony_ci ; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci nfs_lock_request(ret); 60662306a36Sopenharmony_ci nfs_page_group_init(ret, last); 60762306a36Sopenharmony_ci ret->wb_nio = req->wb_nio; 60862306a36Sopenharmony_ci } 60962306a36Sopenharmony_ci return ret; 61062306a36Sopenharmony_ci} 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci/** 61362306a36Sopenharmony_ci * nfs_unlock_request - Unlock request and wake up sleepers. 61462306a36Sopenharmony_ci * @req: pointer to request 61562306a36Sopenharmony_ci */ 61662306a36Sopenharmony_civoid nfs_unlock_request(struct nfs_page *req) 61762306a36Sopenharmony_ci{ 61862306a36Sopenharmony_ci clear_bit_unlock(PG_BUSY, &req->wb_flags); 61962306a36Sopenharmony_ci smp_mb__after_atomic(); 62062306a36Sopenharmony_ci if (!test_bit(PG_CONTENDED2, &req->wb_flags)) 62162306a36Sopenharmony_ci return; 62262306a36Sopenharmony_ci wake_up_bit(&req->wb_flags, PG_BUSY); 62362306a36Sopenharmony_ci} 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci/** 62662306a36Sopenharmony_ci * nfs_unlock_and_release_request - Unlock request and release the nfs_page 62762306a36Sopenharmony_ci * @req: pointer to request 62862306a36Sopenharmony_ci */ 62962306a36Sopenharmony_civoid nfs_unlock_and_release_request(struct nfs_page *req) 63062306a36Sopenharmony_ci{ 63162306a36Sopenharmony_ci nfs_unlock_request(req); 63262306a36Sopenharmony_ci nfs_release_request(req); 63362306a36Sopenharmony_ci} 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci/* 63662306a36Sopenharmony_ci * nfs_clear_request - Free up all resources allocated to the request 63762306a36Sopenharmony_ci * @req: 63862306a36Sopenharmony_ci * 63962306a36Sopenharmony_ci * Release page and open context resources associated with a read/write 64062306a36Sopenharmony_ci * request after it has completed. 64162306a36Sopenharmony_ci */ 64262306a36Sopenharmony_cistatic void nfs_clear_request(struct nfs_page *req) 64362306a36Sopenharmony_ci{ 64462306a36Sopenharmony_ci struct folio *folio = nfs_page_to_folio(req); 64562306a36Sopenharmony_ci struct page *page = req->wb_page; 64662306a36Sopenharmony_ci struct nfs_lock_context *l_ctx = req->wb_lock_context; 64762306a36Sopenharmony_ci struct nfs_open_context *ctx; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci if (folio != NULL) { 65062306a36Sopenharmony_ci folio_put(folio); 65162306a36Sopenharmony_ci req->wb_folio = NULL; 65262306a36Sopenharmony_ci clear_bit(PG_FOLIO, &req->wb_flags); 65362306a36Sopenharmony_ci } else if (page != NULL) { 65462306a36Sopenharmony_ci put_page(page); 65562306a36Sopenharmony_ci req->wb_page = NULL; 65662306a36Sopenharmony_ci } 65762306a36Sopenharmony_ci if (l_ctx != NULL) { 65862306a36Sopenharmony_ci if (atomic_dec_and_test(&l_ctx->io_count)) { 65962306a36Sopenharmony_ci wake_up_var(&l_ctx->io_count); 66062306a36Sopenharmony_ci ctx = l_ctx->open_context; 66162306a36Sopenharmony_ci if (test_bit(NFS_CONTEXT_UNLOCK, &ctx->flags)) 66262306a36Sopenharmony_ci rpc_wake_up(&NFS_SERVER(d_inode(ctx->dentry))->uoc_rpcwaitq); 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci nfs_put_lock_context(l_ctx); 66562306a36Sopenharmony_ci req->wb_lock_context = NULL; 66662306a36Sopenharmony_ci } 66762306a36Sopenharmony_ci} 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci/** 67062306a36Sopenharmony_ci * nfs_free_request - Release the count on an NFS read/write request 67162306a36Sopenharmony_ci * @req: request to release 67262306a36Sopenharmony_ci * 67362306a36Sopenharmony_ci * Note: Should never be called with the spinlock held! 67462306a36Sopenharmony_ci */ 67562306a36Sopenharmony_civoid nfs_free_request(struct nfs_page *req) 67662306a36Sopenharmony_ci{ 67762306a36Sopenharmony_ci WARN_ON_ONCE(req->wb_this_page != req); 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci /* extra debug: make sure no sync bits are still set */ 68062306a36Sopenharmony_ci WARN_ON_ONCE(test_bit(PG_TEARDOWN, &req->wb_flags)); 68162306a36Sopenharmony_ci WARN_ON_ONCE(test_bit(PG_UNLOCKPAGE, &req->wb_flags)); 68262306a36Sopenharmony_ci WARN_ON_ONCE(test_bit(PG_UPTODATE, &req->wb_flags)); 68362306a36Sopenharmony_ci WARN_ON_ONCE(test_bit(PG_WB_END, &req->wb_flags)); 68462306a36Sopenharmony_ci WARN_ON_ONCE(test_bit(PG_REMOVE, &req->wb_flags)); 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci /* Release struct file and open context */ 68762306a36Sopenharmony_ci nfs_clear_request(req); 68862306a36Sopenharmony_ci nfs_page_free(req); 68962306a36Sopenharmony_ci} 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_civoid nfs_release_request(struct nfs_page *req) 69262306a36Sopenharmony_ci{ 69362306a36Sopenharmony_ci kref_put(&req->wb_kref, nfs_page_group_destroy); 69462306a36Sopenharmony_ci} 69562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_release_request); 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci/** 69862306a36Sopenharmony_ci * nfs_wait_on_request - Wait for a request to complete. 69962306a36Sopenharmony_ci * @req: request to wait upon. 70062306a36Sopenharmony_ci * 70162306a36Sopenharmony_ci * Interruptible by fatal signals only. 70262306a36Sopenharmony_ci * The user is responsible for holding a count on the request. 70362306a36Sopenharmony_ci */ 70462306a36Sopenharmony_ciint 70562306a36Sopenharmony_cinfs_wait_on_request(struct nfs_page *req) 70662306a36Sopenharmony_ci{ 70762306a36Sopenharmony_ci if (!test_bit(PG_BUSY, &req->wb_flags)) 70862306a36Sopenharmony_ci return 0; 70962306a36Sopenharmony_ci set_bit(PG_CONTENDED2, &req->wb_flags); 71062306a36Sopenharmony_ci smp_mb__after_atomic(); 71162306a36Sopenharmony_ci return wait_on_bit_io(&req->wb_flags, PG_BUSY, 71262306a36Sopenharmony_ci TASK_UNINTERRUPTIBLE); 71362306a36Sopenharmony_ci} 71462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_wait_on_request); 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci/* 71762306a36Sopenharmony_ci * nfs_generic_pg_test - determine if requests can be coalesced 71862306a36Sopenharmony_ci * @desc: pointer to descriptor 71962306a36Sopenharmony_ci * @prev: previous request in desc, or NULL 72062306a36Sopenharmony_ci * @req: this request 72162306a36Sopenharmony_ci * 72262306a36Sopenharmony_ci * Returns zero if @req cannot be coalesced into @desc, otherwise it returns 72362306a36Sopenharmony_ci * the size of the request. 72462306a36Sopenharmony_ci */ 72562306a36Sopenharmony_cisize_t nfs_generic_pg_test(struct nfs_pageio_descriptor *desc, 72662306a36Sopenharmony_ci struct nfs_page *prev, struct nfs_page *req) 72762306a36Sopenharmony_ci{ 72862306a36Sopenharmony_ci struct nfs_pgio_mirror *mirror = nfs_pgio_current_mirror(desc); 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci if (mirror->pg_count > mirror->pg_bsize) { 73262306a36Sopenharmony_ci /* should never happen */ 73362306a36Sopenharmony_ci WARN_ON_ONCE(1); 73462306a36Sopenharmony_ci return 0; 73562306a36Sopenharmony_ci } 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci /* 73862306a36Sopenharmony_ci * Limit the request size so that we can still allocate a page array 73962306a36Sopenharmony_ci * for it without upsetting the slab allocator. 74062306a36Sopenharmony_ci */ 74162306a36Sopenharmony_ci if (((mirror->pg_count + req->wb_bytes) >> PAGE_SHIFT) * 74262306a36Sopenharmony_ci sizeof(struct page *) > PAGE_SIZE) 74362306a36Sopenharmony_ci return 0; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci return min(mirror->pg_bsize - mirror->pg_count, (size_t)req->wb_bytes); 74662306a36Sopenharmony_ci} 74762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_generic_pg_test); 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_cistruct nfs_pgio_header *nfs_pgio_header_alloc(const struct nfs_rw_ops *ops) 75062306a36Sopenharmony_ci{ 75162306a36Sopenharmony_ci struct nfs_pgio_header *hdr = ops->rw_alloc_header(); 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci if (hdr) { 75462306a36Sopenharmony_ci INIT_LIST_HEAD(&hdr->pages); 75562306a36Sopenharmony_ci hdr->rw_ops = ops; 75662306a36Sopenharmony_ci } 75762306a36Sopenharmony_ci return hdr; 75862306a36Sopenharmony_ci} 75962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_pgio_header_alloc); 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci/** 76262306a36Sopenharmony_ci * nfs_pgio_data_destroy - make @hdr suitable for reuse 76362306a36Sopenharmony_ci * 76462306a36Sopenharmony_ci * Frees memory and releases refs from nfs_generic_pgio, so that it may 76562306a36Sopenharmony_ci * be called again. 76662306a36Sopenharmony_ci * 76762306a36Sopenharmony_ci * @hdr: A header that has had nfs_generic_pgio called 76862306a36Sopenharmony_ci */ 76962306a36Sopenharmony_cistatic void nfs_pgio_data_destroy(struct nfs_pgio_header *hdr) 77062306a36Sopenharmony_ci{ 77162306a36Sopenharmony_ci if (hdr->args.context) 77262306a36Sopenharmony_ci put_nfs_open_context(hdr->args.context); 77362306a36Sopenharmony_ci if (hdr->page_array.pagevec != hdr->page_array.page_array) 77462306a36Sopenharmony_ci kfree(hdr->page_array.pagevec); 77562306a36Sopenharmony_ci} 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci/* 77862306a36Sopenharmony_ci * nfs_pgio_header_free - Free a read or write header 77962306a36Sopenharmony_ci * @hdr: The header to free 78062306a36Sopenharmony_ci */ 78162306a36Sopenharmony_civoid nfs_pgio_header_free(struct nfs_pgio_header *hdr) 78262306a36Sopenharmony_ci{ 78362306a36Sopenharmony_ci nfs_pgio_data_destroy(hdr); 78462306a36Sopenharmony_ci hdr->rw_ops->rw_free_header(hdr); 78562306a36Sopenharmony_ci} 78662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_pgio_header_free); 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci/** 78962306a36Sopenharmony_ci * nfs_pgio_rpcsetup - Set up arguments for a pageio call 79062306a36Sopenharmony_ci * @hdr: The pageio hdr 79162306a36Sopenharmony_ci * @pgbase: base 79262306a36Sopenharmony_ci * @count: Number of bytes to read 79362306a36Sopenharmony_ci * @how: How to commit data (writes only) 79462306a36Sopenharmony_ci * @cinfo: Commit information for the call (writes only) 79562306a36Sopenharmony_ci */ 79662306a36Sopenharmony_cistatic void nfs_pgio_rpcsetup(struct nfs_pgio_header *hdr, unsigned int pgbase, 79762306a36Sopenharmony_ci unsigned int count, int how, 79862306a36Sopenharmony_ci struct nfs_commit_info *cinfo) 79962306a36Sopenharmony_ci{ 80062306a36Sopenharmony_ci struct nfs_page *req = hdr->req; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci /* Set up the RPC argument and reply structs 80362306a36Sopenharmony_ci * NB: take care not to mess about with hdr->commit et al. */ 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci hdr->args.fh = NFS_FH(hdr->inode); 80662306a36Sopenharmony_ci hdr->args.offset = req_offset(req); 80762306a36Sopenharmony_ci /* pnfs_set_layoutcommit needs this */ 80862306a36Sopenharmony_ci hdr->mds_offset = hdr->args.offset; 80962306a36Sopenharmony_ci hdr->args.pgbase = pgbase; 81062306a36Sopenharmony_ci hdr->args.pages = hdr->page_array.pagevec; 81162306a36Sopenharmony_ci hdr->args.count = count; 81262306a36Sopenharmony_ci hdr->args.context = get_nfs_open_context(nfs_req_openctx(req)); 81362306a36Sopenharmony_ci hdr->args.lock_context = req->wb_lock_context; 81462306a36Sopenharmony_ci hdr->args.stable = NFS_UNSTABLE; 81562306a36Sopenharmony_ci switch (how & (FLUSH_STABLE | FLUSH_COND_STABLE)) { 81662306a36Sopenharmony_ci case 0: 81762306a36Sopenharmony_ci break; 81862306a36Sopenharmony_ci case FLUSH_COND_STABLE: 81962306a36Sopenharmony_ci if (nfs_reqs_to_commit(cinfo)) 82062306a36Sopenharmony_ci break; 82162306a36Sopenharmony_ci fallthrough; 82262306a36Sopenharmony_ci default: 82362306a36Sopenharmony_ci hdr->args.stable = NFS_FILE_SYNC; 82462306a36Sopenharmony_ci } 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci hdr->res.fattr = &hdr->fattr; 82762306a36Sopenharmony_ci hdr->res.count = 0; 82862306a36Sopenharmony_ci hdr->res.eof = 0; 82962306a36Sopenharmony_ci hdr->res.verf = &hdr->verf; 83062306a36Sopenharmony_ci nfs_fattr_init(&hdr->fattr); 83162306a36Sopenharmony_ci} 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci/** 83462306a36Sopenharmony_ci * nfs_pgio_prepare - Prepare pageio hdr to go over the wire 83562306a36Sopenharmony_ci * @task: The current task 83662306a36Sopenharmony_ci * @calldata: pageio header to prepare 83762306a36Sopenharmony_ci */ 83862306a36Sopenharmony_cistatic void nfs_pgio_prepare(struct rpc_task *task, void *calldata) 83962306a36Sopenharmony_ci{ 84062306a36Sopenharmony_ci struct nfs_pgio_header *hdr = calldata; 84162306a36Sopenharmony_ci int err; 84262306a36Sopenharmony_ci err = NFS_PROTO(hdr->inode)->pgio_rpc_prepare(task, hdr); 84362306a36Sopenharmony_ci if (err) 84462306a36Sopenharmony_ci rpc_exit(task, err); 84562306a36Sopenharmony_ci} 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ciint nfs_initiate_pgio(struct rpc_clnt *clnt, struct nfs_pgio_header *hdr, 84862306a36Sopenharmony_ci const struct cred *cred, const struct nfs_rpc_ops *rpc_ops, 84962306a36Sopenharmony_ci const struct rpc_call_ops *call_ops, int how, int flags) 85062306a36Sopenharmony_ci{ 85162306a36Sopenharmony_ci struct rpc_task *task; 85262306a36Sopenharmony_ci struct rpc_message msg = { 85362306a36Sopenharmony_ci .rpc_argp = &hdr->args, 85462306a36Sopenharmony_ci .rpc_resp = &hdr->res, 85562306a36Sopenharmony_ci .rpc_cred = cred, 85662306a36Sopenharmony_ci }; 85762306a36Sopenharmony_ci struct rpc_task_setup task_setup_data = { 85862306a36Sopenharmony_ci .rpc_client = clnt, 85962306a36Sopenharmony_ci .task = &hdr->task, 86062306a36Sopenharmony_ci .rpc_message = &msg, 86162306a36Sopenharmony_ci .callback_ops = call_ops, 86262306a36Sopenharmony_ci .callback_data = hdr, 86362306a36Sopenharmony_ci .workqueue = nfsiod_workqueue, 86462306a36Sopenharmony_ci .flags = RPC_TASK_ASYNC | flags, 86562306a36Sopenharmony_ci }; 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci if (nfs_server_capable(hdr->inode, NFS_CAP_MOVEABLE)) 86862306a36Sopenharmony_ci task_setup_data.flags |= RPC_TASK_MOVEABLE; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci hdr->rw_ops->rw_initiate(hdr, &msg, rpc_ops, &task_setup_data, how); 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci dprintk("NFS: initiated pgio call " 87362306a36Sopenharmony_ci "(req %s/%llu, %u bytes @ offset %llu)\n", 87462306a36Sopenharmony_ci hdr->inode->i_sb->s_id, 87562306a36Sopenharmony_ci (unsigned long long)NFS_FILEID(hdr->inode), 87662306a36Sopenharmony_ci hdr->args.count, 87762306a36Sopenharmony_ci (unsigned long long)hdr->args.offset); 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci task = rpc_run_task(&task_setup_data); 88062306a36Sopenharmony_ci if (IS_ERR(task)) 88162306a36Sopenharmony_ci return PTR_ERR(task); 88262306a36Sopenharmony_ci rpc_put_task(task); 88362306a36Sopenharmony_ci return 0; 88462306a36Sopenharmony_ci} 88562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_initiate_pgio); 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci/** 88862306a36Sopenharmony_ci * nfs_pgio_error - Clean up from a pageio error 88962306a36Sopenharmony_ci * @hdr: pageio header 89062306a36Sopenharmony_ci */ 89162306a36Sopenharmony_cistatic void nfs_pgio_error(struct nfs_pgio_header *hdr) 89262306a36Sopenharmony_ci{ 89362306a36Sopenharmony_ci set_bit(NFS_IOHDR_REDO, &hdr->flags); 89462306a36Sopenharmony_ci hdr->completion_ops->completion(hdr); 89562306a36Sopenharmony_ci} 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci/** 89862306a36Sopenharmony_ci * nfs_pgio_release - Release pageio data 89962306a36Sopenharmony_ci * @calldata: The pageio header to release 90062306a36Sopenharmony_ci */ 90162306a36Sopenharmony_cistatic void nfs_pgio_release(void *calldata) 90262306a36Sopenharmony_ci{ 90362306a36Sopenharmony_ci struct nfs_pgio_header *hdr = calldata; 90462306a36Sopenharmony_ci hdr->completion_ops->completion(hdr); 90562306a36Sopenharmony_ci} 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_cistatic void nfs_pageio_mirror_init(struct nfs_pgio_mirror *mirror, 90862306a36Sopenharmony_ci unsigned int bsize) 90962306a36Sopenharmony_ci{ 91062306a36Sopenharmony_ci INIT_LIST_HEAD(&mirror->pg_list); 91162306a36Sopenharmony_ci mirror->pg_bytes_written = 0; 91262306a36Sopenharmony_ci mirror->pg_count = 0; 91362306a36Sopenharmony_ci mirror->pg_bsize = bsize; 91462306a36Sopenharmony_ci mirror->pg_base = 0; 91562306a36Sopenharmony_ci mirror->pg_recoalesce = 0; 91662306a36Sopenharmony_ci} 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci/** 91962306a36Sopenharmony_ci * nfs_pageio_init - initialise a page io descriptor 92062306a36Sopenharmony_ci * @desc: pointer to descriptor 92162306a36Sopenharmony_ci * @inode: pointer to inode 92262306a36Sopenharmony_ci * @pg_ops: pointer to pageio operations 92362306a36Sopenharmony_ci * @compl_ops: pointer to pageio completion operations 92462306a36Sopenharmony_ci * @rw_ops: pointer to nfs read/write operations 92562306a36Sopenharmony_ci * @bsize: io block size 92662306a36Sopenharmony_ci * @io_flags: extra parameters for the io function 92762306a36Sopenharmony_ci */ 92862306a36Sopenharmony_civoid nfs_pageio_init(struct nfs_pageio_descriptor *desc, 92962306a36Sopenharmony_ci struct inode *inode, 93062306a36Sopenharmony_ci const struct nfs_pageio_ops *pg_ops, 93162306a36Sopenharmony_ci const struct nfs_pgio_completion_ops *compl_ops, 93262306a36Sopenharmony_ci const struct nfs_rw_ops *rw_ops, 93362306a36Sopenharmony_ci size_t bsize, 93462306a36Sopenharmony_ci int io_flags) 93562306a36Sopenharmony_ci{ 93662306a36Sopenharmony_ci desc->pg_moreio = 0; 93762306a36Sopenharmony_ci desc->pg_inode = inode; 93862306a36Sopenharmony_ci desc->pg_ops = pg_ops; 93962306a36Sopenharmony_ci desc->pg_completion_ops = compl_ops; 94062306a36Sopenharmony_ci desc->pg_rw_ops = rw_ops; 94162306a36Sopenharmony_ci desc->pg_ioflags = io_flags; 94262306a36Sopenharmony_ci desc->pg_error = 0; 94362306a36Sopenharmony_ci desc->pg_lseg = NULL; 94462306a36Sopenharmony_ci desc->pg_io_completion = NULL; 94562306a36Sopenharmony_ci desc->pg_dreq = NULL; 94662306a36Sopenharmony_ci nfs_netfs_reset_pageio_descriptor(desc); 94762306a36Sopenharmony_ci desc->pg_bsize = bsize; 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci desc->pg_mirror_count = 1; 95062306a36Sopenharmony_ci desc->pg_mirror_idx = 0; 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci desc->pg_mirrors_dynamic = NULL; 95362306a36Sopenharmony_ci desc->pg_mirrors = desc->pg_mirrors_static; 95462306a36Sopenharmony_ci nfs_pageio_mirror_init(&desc->pg_mirrors[0], bsize); 95562306a36Sopenharmony_ci desc->pg_maxretrans = 0; 95662306a36Sopenharmony_ci} 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci/** 95962306a36Sopenharmony_ci * nfs_pgio_result - Basic pageio error handling 96062306a36Sopenharmony_ci * @task: The task that ran 96162306a36Sopenharmony_ci * @calldata: Pageio header to check 96262306a36Sopenharmony_ci */ 96362306a36Sopenharmony_cistatic void nfs_pgio_result(struct rpc_task *task, void *calldata) 96462306a36Sopenharmony_ci{ 96562306a36Sopenharmony_ci struct nfs_pgio_header *hdr = calldata; 96662306a36Sopenharmony_ci struct inode *inode = hdr->inode; 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci if (hdr->rw_ops->rw_done(task, hdr, inode) != 0) 96962306a36Sopenharmony_ci return; 97062306a36Sopenharmony_ci if (task->tk_status < 0) 97162306a36Sopenharmony_ci nfs_set_pgio_error(hdr, task->tk_status, hdr->args.offset); 97262306a36Sopenharmony_ci else 97362306a36Sopenharmony_ci hdr->rw_ops->rw_result(task, hdr); 97462306a36Sopenharmony_ci} 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci/* 97762306a36Sopenharmony_ci * Create an RPC task for the given read or write request and kick it. 97862306a36Sopenharmony_ci * The page must have been locked by the caller. 97962306a36Sopenharmony_ci * 98062306a36Sopenharmony_ci * It may happen that the page we're passed is not marked dirty. 98162306a36Sopenharmony_ci * This is the case if nfs_updatepage detects a conflicting request 98262306a36Sopenharmony_ci * that has been written but not committed. 98362306a36Sopenharmony_ci */ 98462306a36Sopenharmony_ciint nfs_generic_pgio(struct nfs_pageio_descriptor *desc, 98562306a36Sopenharmony_ci struct nfs_pgio_header *hdr) 98662306a36Sopenharmony_ci{ 98762306a36Sopenharmony_ci struct nfs_pgio_mirror *mirror = nfs_pgio_current_mirror(desc); 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci struct nfs_page *req; 99062306a36Sopenharmony_ci struct page **pages, 99162306a36Sopenharmony_ci *last_page; 99262306a36Sopenharmony_ci struct list_head *head = &mirror->pg_list; 99362306a36Sopenharmony_ci struct nfs_commit_info cinfo; 99462306a36Sopenharmony_ci struct nfs_page_array *pg_array = &hdr->page_array; 99562306a36Sopenharmony_ci unsigned int pagecount, pageused; 99662306a36Sopenharmony_ci unsigned int pg_base = offset_in_page(mirror->pg_base); 99762306a36Sopenharmony_ci gfp_t gfp_flags = nfs_io_gfp_mask(); 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci pagecount = nfs_page_array_len(pg_base, mirror->pg_count); 100062306a36Sopenharmony_ci pg_array->npages = pagecount; 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci if (pagecount <= ARRAY_SIZE(pg_array->page_array)) 100362306a36Sopenharmony_ci pg_array->pagevec = pg_array->page_array; 100462306a36Sopenharmony_ci else { 100562306a36Sopenharmony_ci pg_array->pagevec = kcalloc(pagecount, sizeof(struct page *), gfp_flags); 100662306a36Sopenharmony_ci if (!pg_array->pagevec) { 100762306a36Sopenharmony_ci pg_array->npages = 0; 100862306a36Sopenharmony_ci nfs_pgio_error(hdr); 100962306a36Sopenharmony_ci desc->pg_error = -ENOMEM; 101062306a36Sopenharmony_ci return desc->pg_error; 101162306a36Sopenharmony_ci } 101262306a36Sopenharmony_ci } 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci nfs_init_cinfo(&cinfo, desc->pg_inode, desc->pg_dreq); 101562306a36Sopenharmony_ci pages = hdr->page_array.pagevec; 101662306a36Sopenharmony_ci last_page = NULL; 101762306a36Sopenharmony_ci pageused = 0; 101862306a36Sopenharmony_ci while (!list_empty(head)) { 101962306a36Sopenharmony_ci struct nfs_page_iter_page i; 102062306a36Sopenharmony_ci struct page *page; 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci req = nfs_list_entry(head->next); 102362306a36Sopenharmony_ci nfs_list_move_request(req, &hdr->pages); 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci if (req->wb_pgbase == 0) 102662306a36Sopenharmony_ci last_page = NULL; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci nfs_page_iter_page_init(&i, req); 102962306a36Sopenharmony_ci while ((page = nfs_page_iter_page_get(&i)) != NULL) { 103062306a36Sopenharmony_ci if (last_page != page) { 103162306a36Sopenharmony_ci pageused++; 103262306a36Sopenharmony_ci if (pageused > pagecount) 103362306a36Sopenharmony_ci goto full; 103462306a36Sopenharmony_ci *pages++ = last_page = page; 103562306a36Sopenharmony_ci } 103662306a36Sopenharmony_ci } 103762306a36Sopenharmony_ci } 103862306a36Sopenharmony_cifull: 103962306a36Sopenharmony_ci if (WARN_ON_ONCE(pageused != pagecount)) { 104062306a36Sopenharmony_ci nfs_pgio_error(hdr); 104162306a36Sopenharmony_ci desc->pg_error = -EINVAL; 104262306a36Sopenharmony_ci return desc->pg_error; 104362306a36Sopenharmony_ci } 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci if ((desc->pg_ioflags & FLUSH_COND_STABLE) && 104662306a36Sopenharmony_ci (desc->pg_moreio || nfs_reqs_to_commit(&cinfo))) 104762306a36Sopenharmony_ci desc->pg_ioflags &= ~FLUSH_COND_STABLE; 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci /* Set up the argument struct */ 105062306a36Sopenharmony_ci nfs_pgio_rpcsetup(hdr, pg_base, mirror->pg_count, desc->pg_ioflags, 105162306a36Sopenharmony_ci &cinfo); 105262306a36Sopenharmony_ci desc->pg_rpc_callops = &nfs_pgio_common_ops; 105362306a36Sopenharmony_ci return 0; 105462306a36Sopenharmony_ci} 105562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_generic_pgio); 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_cistatic int nfs_generic_pg_pgios(struct nfs_pageio_descriptor *desc) 105862306a36Sopenharmony_ci{ 105962306a36Sopenharmony_ci struct nfs_pgio_header *hdr; 106062306a36Sopenharmony_ci int ret; 106162306a36Sopenharmony_ci unsigned short task_flags = 0; 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci hdr = nfs_pgio_header_alloc(desc->pg_rw_ops); 106462306a36Sopenharmony_ci if (!hdr) { 106562306a36Sopenharmony_ci desc->pg_error = -ENOMEM; 106662306a36Sopenharmony_ci return desc->pg_error; 106762306a36Sopenharmony_ci } 106862306a36Sopenharmony_ci nfs_pgheader_init(desc, hdr, nfs_pgio_header_free); 106962306a36Sopenharmony_ci ret = nfs_generic_pgio(desc, hdr); 107062306a36Sopenharmony_ci if (ret == 0) { 107162306a36Sopenharmony_ci if (NFS_SERVER(hdr->inode)->nfs_client->cl_minorversion) 107262306a36Sopenharmony_ci task_flags = RPC_TASK_MOVEABLE; 107362306a36Sopenharmony_ci ret = nfs_initiate_pgio(NFS_CLIENT(hdr->inode), 107462306a36Sopenharmony_ci hdr, 107562306a36Sopenharmony_ci hdr->cred, 107662306a36Sopenharmony_ci NFS_PROTO(hdr->inode), 107762306a36Sopenharmony_ci desc->pg_rpc_callops, 107862306a36Sopenharmony_ci desc->pg_ioflags, 107962306a36Sopenharmony_ci RPC_TASK_CRED_NOREF | task_flags); 108062306a36Sopenharmony_ci } 108162306a36Sopenharmony_ci return ret; 108262306a36Sopenharmony_ci} 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_cistatic struct nfs_pgio_mirror * 108562306a36Sopenharmony_cinfs_pageio_alloc_mirrors(struct nfs_pageio_descriptor *desc, 108662306a36Sopenharmony_ci unsigned int mirror_count) 108762306a36Sopenharmony_ci{ 108862306a36Sopenharmony_ci struct nfs_pgio_mirror *ret; 108962306a36Sopenharmony_ci unsigned int i; 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci kfree(desc->pg_mirrors_dynamic); 109262306a36Sopenharmony_ci desc->pg_mirrors_dynamic = NULL; 109362306a36Sopenharmony_ci if (mirror_count == 1) 109462306a36Sopenharmony_ci return desc->pg_mirrors_static; 109562306a36Sopenharmony_ci ret = kmalloc_array(mirror_count, sizeof(*ret), nfs_io_gfp_mask()); 109662306a36Sopenharmony_ci if (ret != NULL) { 109762306a36Sopenharmony_ci for (i = 0; i < mirror_count; i++) 109862306a36Sopenharmony_ci nfs_pageio_mirror_init(&ret[i], desc->pg_bsize); 109962306a36Sopenharmony_ci desc->pg_mirrors_dynamic = ret; 110062306a36Sopenharmony_ci } 110162306a36Sopenharmony_ci return ret; 110262306a36Sopenharmony_ci} 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci/* 110562306a36Sopenharmony_ci * nfs_pageio_setup_mirroring - determine if mirroring is to be used 110662306a36Sopenharmony_ci * by calling the pg_get_mirror_count op 110762306a36Sopenharmony_ci */ 110862306a36Sopenharmony_cistatic void nfs_pageio_setup_mirroring(struct nfs_pageio_descriptor *pgio, 110962306a36Sopenharmony_ci struct nfs_page *req) 111062306a36Sopenharmony_ci{ 111162306a36Sopenharmony_ci unsigned int mirror_count = 1; 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci if (pgio->pg_ops->pg_get_mirror_count) 111462306a36Sopenharmony_ci mirror_count = pgio->pg_ops->pg_get_mirror_count(pgio, req); 111562306a36Sopenharmony_ci if (mirror_count == pgio->pg_mirror_count || pgio->pg_error < 0) 111662306a36Sopenharmony_ci return; 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci if (!mirror_count || mirror_count > NFS_PAGEIO_DESCRIPTOR_MIRROR_MAX) { 111962306a36Sopenharmony_ci pgio->pg_error = -EINVAL; 112062306a36Sopenharmony_ci return; 112162306a36Sopenharmony_ci } 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci pgio->pg_mirrors = nfs_pageio_alloc_mirrors(pgio, mirror_count); 112462306a36Sopenharmony_ci if (pgio->pg_mirrors == NULL) { 112562306a36Sopenharmony_ci pgio->pg_error = -ENOMEM; 112662306a36Sopenharmony_ci pgio->pg_mirrors = pgio->pg_mirrors_static; 112762306a36Sopenharmony_ci mirror_count = 1; 112862306a36Sopenharmony_ci } 112962306a36Sopenharmony_ci pgio->pg_mirror_count = mirror_count; 113062306a36Sopenharmony_ci} 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_cistatic void nfs_pageio_cleanup_mirroring(struct nfs_pageio_descriptor *pgio) 113362306a36Sopenharmony_ci{ 113462306a36Sopenharmony_ci pgio->pg_mirror_count = 1; 113562306a36Sopenharmony_ci pgio->pg_mirror_idx = 0; 113662306a36Sopenharmony_ci pgio->pg_mirrors = pgio->pg_mirrors_static; 113762306a36Sopenharmony_ci kfree(pgio->pg_mirrors_dynamic); 113862306a36Sopenharmony_ci pgio->pg_mirrors_dynamic = NULL; 113962306a36Sopenharmony_ci} 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_cistatic bool nfs_match_lock_context(const struct nfs_lock_context *l1, 114262306a36Sopenharmony_ci const struct nfs_lock_context *l2) 114362306a36Sopenharmony_ci{ 114462306a36Sopenharmony_ci return l1->lockowner == l2->lockowner; 114562306a36Sopenharmony_ci} 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_cistatic bool nfs_page_is_contiguous(const struct nfs_page *prev, 114862306a36Sopenharmony_ci const struct nfs_page *req) 114962306a36Sopenharmony_ci{ 115062306a36Sopenharmony_ci size_t prev_end = prev->wb_pgbase + prev->wb_bytes; 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci if (req_offset(req) != req_offset(prev) + prev->wb_bytes) 115362306a36Sopenharmony_ci return false; 115462306a36Sopenharmony_ci if (req->wb_pgbase == 0) 115562306a36Sopenharmony_ci return prev_end == nfs_page_max_length(prev); 115662306a36Sopenharmony_ci if (req->wb_pgbase == prev_end) { 115762306a36Sopenharmony_ci struct folio *folio = nfs_page_to_folio(req); 115862306a36Sopenharmony_ci if (folio) 115962306a36Sopenharmony_ci return folio == nfs_page_to_folio(prev); 116062306a36Sopenharmony_ci return req->wb_page == prev->wb_page; 116162306a36Sopenharmony_ci } 116262306a36Sopenharmony_ci return false; 116362306a36Sopenharmony_ci} 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci/** 116662306a36Sopenharmony_ci * nfs_coalesce_size - test two requests for compatibility 116762306a36Sopenharmony_ci * @prev: pointer to nfs_page 116862306a36Sopenharmony_ci * @req: pointer to nfs_page 116962306a36Sopenharmony_ci * @pgio: pointer to nfs_pagio_descriptor 117062306a36Sopenharmony_ci * 117162306a36Sopenharmony_ci * The nfs_page structures 'prev' and 'req' are compared to ensure that the 117262306a36Sopenharmony_ci * page data area they describe is contiguous, and that their RPC 117362306a36Sopenharmony_ci * credentials, NFSv4 open state, and lockowners are the same. 117462306a36Sopenharmony_ci * 117562306a36Sopenharmony_ci * Returns size of the request that can be coalesced 117662306a36Sopenharmony_ci */ 117762306a36Sopenharmony_cistatic unsigned int nfs_coalesce_size(struct nfs_page *prev, 117862306a36Sopenharmony_ci struct nfs_page *req, 117962306a36Sopenharmony_ci struct nfs_pageio_descriptor *pgio) 118062306a36Sopenharmony_ci{ 118162306a36Sopenharmony_ci struct file_lock_context *flctx; 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci if (prev) { 118462306a36Sopenharmony_ci if (!nfs_match_open_context(nfs_req_openctx(req), nfs_req_openctx(prev))) 118562306a36Sopenharmony_ci return 0; 118662306a36Sopenharmony_ci flctx = locks_inode_context(d_inode(nfs_req_openctx(req)->dentry)); 118762306a36Sopenharmony_ci if (flctx != NULL && 118862306a36Sopenharmony_ci !(list_empty_careful(&flctx->flc_posix) && 118962306a36Sopenharmony_ci list_empty_careful(&flctx->flc_flock)) && 119062306a36Sopenharmony_ci !nfs_match_lock_context(req->wb_lock_context, 119162306a36Sopenharmony_ci prev->wb_lock_context)) 119262306a36Sopenharmony_ci return 0; 119362306a36Sopenharmony_ci if (!nfs_page_is_contiguous(prev, req)) 119462306a36Sopenharmony_ci return 0; 119562306a36Sopenharmony_ci } 119662306a36Sopenharmony_ci return pgio->pg_ops->pg_test(pgio, prev, req); 119762306a36Sopenharmony_ci} 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci/** 120062306a36Sopenharmony_ci * nfs_pageio_do_add_request - Attempt to coalesce a request into a page list. 120162306a36Sopenharmony_ci * @desc: destination io descriptor 120262306a36Sopenharmony_ci * @req: request 120362306a36Sopenharmony_ci * 120462306a36Sopenharmony_ci * If the request 'req' was successfully coalesced into the existing list 120562306a36Sopenharmony_ci * of pages 'desc', it returns the size of req. 120662306a36Sopenharmony_ci */ 120762306a36Sopenharmony_cistatic unsigned int 120862306a36Sopenharmony_cinfs_pageio_do_add_request(struct nfs_pageio_descriptor *desc, 120962306a36Sopenharmony_ci struct nfs_page *req) 121062306a36Sopenharmony_ci{ 121162306a36Sopenharmony_ci struct nfs_pgio_mirror *mirror = nfs_pgio_current_mirror(desc); 121262306a36Sopenharmony_ci struct nfs_page *prev = NULL; 121362306a36Sopenharmony_ci unsigned int size; 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci if (list_empty(&mirror->pg_list)) { 121662306a36Sopenharmony_ci if (desc->pg_ops->pg_init) 121762306a36Sopenharmony_ci desc->pg_ops->pg_init(desc, req); 121862306a36Sopenharmony_ci if (desc->pg_error < 0) 121962306a36Sopenharmony_ci return 0; 122062306a36Sopenharmony_ci mirror->pg_base = req->wb_pgbase; 122162306a36Sopenharmony_ci mirror->pg_count = 0; 122262306a36Sopenharmony_ci mirror->pg_recoalesce = 0; 122362306a36Sopenharmony_ci } else 122462306a36Sopenharmony_ci prev = nfs_list_entry(mirror->pg_list.prev); 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci if (desc->pg_maxretrans && req->wb_nio > desc->pg_maxretrans) { 122762306a36Sopenharmony_ci if (NFS_SERVER(desc->pg_inode)->flags & NFS_MOUNT_SOFTERR) 122862306a36Sopenharmony_ci desc->pg_error = -ETIMEDOUT; 122962306a36Sopenharmony_ci else 123062306a36Sopenharmony_ci desc->pg_error = -EIO; 123162306a36Sopenharmony_ci return 0; 123262306a36Sopenharmony_ci } 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_ci size = nfs_coalesce_size(prev, req, desc); 123562306a36Sopenharmony_ci if (size < req->wb_bytes) 123662306a36Sopenharmony_ci return size; 123762306a36Sopenharmony_ci nfs_list_move_request(req, &mirror->pg_list); 123862306a36Sopenharmony_ci mirror->pg_count += req->wb_bytes; 123962306a36Sopenharmony_ci return req->wb_bytes; 124062306a36Sopenharmony_ci} 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci/* 124362306a36Sopenharmony_ci * Helper for nfs_pageio_add_request and nfs_pageio_complete 124462306a36Sopenharmony_ci */ 124562306a36Sopenharmony_cistatic void nfs_pageio_doio(struct nfs_pageio_descriptor *desc) 124662306a36Sopenharmony_ci{ 124762306a36Sopenharmony_ci struct nfs_pgio_mirror *mirror = nfs_pgio_current_mirror(desc); 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci if (!list_empty(&mirror->pg_list)) { 125062306a36Sopenharmony_ci int error = desc->pg_ops->pg_doio(desc); 125162306a36Sopenharmony_ci if (error < 0) 125262306a36Sopenharmony_ci desc->pg_error = error; 125362306a36Sopenharmony_ci if (list_empty(&mirror->pg_list)) 125462306a36Sopenharmony_ci mirror->pg_bytes_written += mirror->pg_count; 125562306a36Sopenharmony_ci } 125662306a36Sopenharmony_ci} 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_cistatic void 125962306a36Sopenharmony_cinfs_pageio_cleanup_request(struct nfs_pageio_descriptor *desc, 126062306a36Sopenharmony_ci struct nfs_page *req) 126162306a36Sopenharmony_ci{ 126262306a36Sopenharmony_ci LIST_HEAD(head); 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci nfs_list_move_request(req, &head); 126562306a36Sopenharmony_ci desc->pg_completion_ops->error_cleanup(&head, desc->pg_error); 126662306a36Sopenharmony_ci} 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci/** 126962306a36Sopenharmony_ci * __nfs_pageio_add_request - Attempt to coalesce a request into a page list. 127062306a36Sopenharmony_ci * @desc: destination io descriptor 127162306a36Sopenharmony_ci * @req: request 127262306a36Sopenharmony_ci * 127362306a36Sopenharmony_ci * This may split a request into subrequests which are all part of the 127462306a36Sopenharmony_ci * same page group. If so, it will submit @req as the last one, to ensure 127562306a36Sopenharmony_ci * the pointer to @req is still valid in case of failure. 127662306a36Sopenharmony_ci * 127762306a36Sopenharmony_ci * Returns true if the request 'req' was successfully coalesced into the 127862306a36Sopenharmony_ci * existing list of pages 'desc'. 127962306a36Sopenharmony_ci */ 128062306a36Sopenharmony_cistatic int __nfs_pageio_add_request(struct nfs_pageio_descriptor *desc, 128162306a36Sopenharmony_ci struct nfs_page *req) 128262306a36Sopenharmony_ci{ 128362306a36Sopenharmony_ci struct nfs_pgio_mirror *mirror = nfs_pgio_current_mirror(desc); 128462306a36Sopenharmony_ci struct nfs_page *subreq; 128562306a36Sopenharmony_ci unsigned int size, subreq_size; 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci nfs_page_group_lock(req); 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci subreq = req; 129062306a36Sopenharmony_ci subreq_size = subreq->wb_bytes; 129162306a36Sopenharmony_ci for(;;) { 129262306a36Sopenharmony_ci size = nfs_pageio_do_add_request(desc, subreq); 129362306a36Sopenharmony_ci if (size == subreq_size) { 129462306a36Sopenharmony_ci /* We successfully submitted a request */ 129562306a36Sopenharmony_ci if (subreq == req) 129662306a36Sopenharmony_ci break; 129762306a36Sopenharmony_ci req->wb_pgbase += size; 129862306a36Sopenharmony_ci req->wb_bytes -= size; 129962306a36Sopenharmony_ci req->wb_offset += size; 130062306a36Sopenharmony_ci subreq_size = req->wb_bytes; 130162306a36Sopenharmony_ci subreq = req; 130262306a36Sopenharmony_ci continue; 130362306a36Sopenharmony_ci } 130462306a36Sopenharmony_ci if (WARN_ON_ONCE(subreq != req)) { 130562306a36Sopenharmony_ci nfs_page_group_unlock(req); 130662306a36Sopenharmony_ci nfs_pageio_cleanup_request(desc, subreq); 130762306a36Sopenharmony_ci subreq = req; 130862306a36Sopenharmony_ci subreq_size = req->wb_bytes; 130962306a36Sopenharmony_ci nfs_page_group_lock(req); 131062306a36Sopenharmony_ci } 131162306a36Sopenharmony_ci if (!size) { 131262306a36Sopenharmony_ci /* Can't coalesce any more, so do I/O */ 131362306a36Sopenharmony_ci nfs_page_group_unlock(req); 131462306a36Sopenharmony_ci desc->pg_moreio = 1; 131562306a36Sopenharmony_ci nfs_pageio_doio(desc); 131662306a36Sopenharmony_ci if (desc->pg_error < 0 || mirror->pg_recoalesce) 131762306a36Sopenharmony_ci return 0; 131862306a36Sopenharmony_ci /* retry add_request for this subreq */ 131962306a36Sopenharmony_ci nfs_page_group_lock(req); 132062306a36Sopenharmony_ci continue; 132162306a36Sopenharmony_ci } 132262306a36Sopenharmony_ci subreq = nfs_create_subreq(req, req->wb_pgbase, 132362306a36Sopenharmony_ci req->wb_offset, size); 132462306a36Sopenharmony_ci if (IS_ERR(subreq)) 132562306a36Sopenharmony_ci goto err_ptr; 132662306a36Sopenharmony_ci subreq_size = size; 132762306a36Sopenharmony_ci } 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci nfs_page_group_unlock(req); 133062306a36Sopenharmony_ci return 1; 133162306a36Sopenharmony_cierr_ptr: 133262306a36Sopenharmony_ci desc->pg_error = PTR_ERR(subreq); 133362306a36Sopenharmony_ci nfs_page_group_unlock(req); 133462306a36Sopenharmony_ci return 0; 133562306a36Sopenharmony_ci} 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_cistatic int nfs_do_recoalesce(struct nfs_pageio_descriptor *desc) 133862306a36Sopenharmony_ci{ 133962306a36Sopenharmony_ci struct nfs_pgio_mirror *mirror = nfs_pgio_current_mirror(desc); 134062306a36Sopenharmony_ci LIST_HEAD(head); 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci do { 134362306a36Sopenharmony_ci list_splice_init(&mirror->pg_list, &head); 134462306a36Sopenharmony_ci mirror->pg_recoalesce = 0; 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci while (!list_empty(&head)) { 134762306a36Sopenharmony_ci struct nfs_page *req; 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci req = list_first_entry(&head, struct nfs_page, wb_list); 135062306a36Sopenharmony_ci if (__nfs_pageio_add_request(desc, req)) 135162306a36Sopenharmony_ci continue; 135262306a36Sopenharmony_ci if (desc->pg_error < 0) { 135362306a36Sopenharmony_ci list_splice_tail(&head, &mirror->pg_list); 135462306a36Sopenharmony_ci mirror->pg_recoalesce = 1; 135562306a36Sopenharmony_ci return 0; 135662306a36Sopenharmony_ci } 135762306a36Sopenharmony_ci break; 135862306a36Sopenharmony_ci } 135962306a36Sopenharmony_ci } while (mirror->pg_recoalesce); 136062306a36Sopenharmony_ci return 1; 136162306a36Sopenharmony_ci} 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_cistatic int nfs_pageio_add_request_mirror(struct nfs_pageio_descriptor *desc, 136462306a36Sopenharmony_ci struct nfs_page *req) 136562306a36Sopenharmony_ci{ 136662306a36Sopenharmony_ci int ret; 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci do { 136962306a36Sopenharmony_ci ret = __nfs_pageio_add_request(desc, req); 137062306a36Sopenharmony_ci if (ret) 137162306a36Sopenharmony_ci break; 137262306a36Sopenharmony_ci if (desc->pg_error < 0) 137362306a36Sopenharmony_ci break; 137462306a36Sopenharmony_ci ret = nfs_do_recoalesce(desc); 137562306a36Sopenharmony_ci } while (ret); 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci return ret; 137862306a36Sopenharmony_ci} 137962306a36Sopenharmony_ci 138062306a36Sopenharmony_cistatic void nfs_pageio_error_cleanup(struct nfs_pageio_descriptor *desc) 138162306a36Sopenharmony_ci{ 138262306a36Sopenharmony_ci u32 midx; 138362306a36Sopenharmony_ci struct nfs_pgio_mirror *mirror; 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_ci if (!desc->pg_error) 138662306a36Sopenharmony_ci return; 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_ci for (midx = 0; midx < desc->pg_mirror_count; midx++) { 138962306a36Sopenharmony_ci mirror = nfs_pgio_get_mirror(desc, midx); 139062306a36Sopenharmony_ci desc->pg_completion_ops->error_cleanup(&mirror->pg_list, 139162306a36Sopenharmony_ci desc->pg_error); 139262306a36Sopenharmony_ci } 139362306a36Sopenharmony_ci} 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ciint nfs_pageio_add_request(struct nfs_pageio_descriptor *desc, 139662306a36Sopenharmony_ci struct nfs_page *req) 139762306a36Sopenharmony_ci{ 139862306a36Sopenharmony_ci u32 midx; 139962306a36Sopenharmony_ci unsigned int pgbase, offset, bytes; 140062306a36Sopenharmony_ci struct nfs_page *dupreq; 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_ci pgbase = req->wb_pgbase; 140362306a36Sopenharmony_ci offset = req->wb_offset; 140462306a36Sopenharmony_ci bytes = req->wb_bytes; 140562306a36Sopenharmony_ci 140662306a36Sopenharmony_ci nfs_pageio_setup_mirroring(desc, req); 140762306a36Sopenharmony_ci if (desc->pg_error < 0) 140862306a36Sopenharmony_ci goto out_failed; 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci /* Create the mirror instances first, and fire them off */ 141162306a36Sopenharmony_ci for (midx = 1; midx < desc->pg_mirror_count; midx++) { 141262306a36Sopenharmony_ci nfs_page_group_lock(req); 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_ci dupreq = nfs_create_subreq(req, 141562306a36Sopenharmony_ci pgbase, offset, bytes); 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_ci nfs_page_group_unlock(req); 141862306a36Sopenharmony_ci if (IS_ERR(dupreq)) { 141962306a36Sopenharmony_ci desc->pg_error = PTR_ERR(dupreq); 142062306a36Sopenharmony_ci goto out_failed; 142162306a36Sopenharmony_ci } 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_ci nfs_pgio_set_current_mirror(desc, midx); 142462306a36Sopenharmony_ci if (!nfs_pageio_add_request_mirror(desc, dupreq)) 142562306a36Sopenharmony_ci goto out_cleanup_subreq; 142662306a36Sopenharmony_ci } 142762306a36Sopenharmony_ci 142862306a36Sopenharmony_ci nfs_pgio_set_current_mirror(desc, 0); 142962306a36Sopenharmony_ci if (!nfs_pageio_add_request_mirror(desc, req)) 143062306a36Sopenharmony_ci goto out_failed; 143162306a36Sopenharmony_ci 143262306a36Sopenharmony_ci return 1; 143362306a36Sopenharmony_ci 143462306a36Sopenharmony_ciout_cleanup_subreq: 143562306a36Sopenharmony_ci nfs_pageio_cleanup_request(desc, dupreq); 143662306a36Sopenharmony_ciout_failed: 143762306a36Sopenharmony_ci nfs_pageio_error_cleanup(desc); 143862306a36Sopenharmony_ci return 0; 143962306a36Sopenharmony_ci} 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_ci/* 144262306a36Sopenharmony_ci * nfs_pageio_complete_mirror - Complete I/O on the current mirror of an 144362306a36Sopenharmony_ci * nfs_pageio_descriptor 144462306a36Sopenharmony_ci * @desc: pointer to io descriptor 144562306a36Sopenharmony_ci * @mirror_idx: pointer to mirror index 144662306a36Sopenharmony_ci */ 144762306a36Sopenharmony_cistatic void nfs_pageio_complete_mirror(struct nfs_pageio_descriptor *desc, 144862306a36Sopenharmony_ci u32 mirror_idx) 144962306a36Sopenharmony_ci{ 145062306a36Sopenharmony_ci struct nfs_pgio_mirror *mirror; 145162306a36Sopenharmony_ci u32 restore_idx; 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ci restore_idx = nfs_pgio_set_current_mirror(desc, mirror_idx); 145462306a36Sopenharmony_ci mirror = nfs_pgio_current_mirror(desc); 145562306a36Sopenharmony_ci 145662306a36Sopenharmony_ci for (;;) { 145762306a36Sopenharmony_ci nfs_pageio_doio(desc); 145862306a36Sopenharmony_ci if (desc->pg_error < 0 || !mirror->pg_recoalesce) 145962306a36Sopenharmony_ci break; 146062306a36Sopenharmony_ci if (!nfs_do_recoalesce(desc)) 146162306a36Sopenharmony_ci break; 146262306a36Sopenharmony_ci } 146362306a36Sopenharmony_ci nfs_pgio_set_current_mirror(desc, restore_idx); 146462306a36Sopenharmony_ci} 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_ci/* 146762306a36Sopenharmony_ci * nfs_pageio_resend - Transfer requests to new descriptor and resend 146862306a36Sopenharmony_ci * @hdr - the pgio header to move request from 146962306a36Sopenharmony_ci * @desc - the pageio descriptor to add requests to 147062306a36Sopenharmony_ci * 147162306a36Sopenharmony_ci * Try to move each request (nfs_page) from @hdr to @desc then attempt 147262306a36Sopenharmony_ci * to send them. 147362306a36Sopenharmony_ci * 147462306a36Sopenharmony_ci * Returns 0 on success and < 0 on error. 147562306a36Sopenharmony_ci */ 147662306a36Sopenharmony_ciint nfs_pageio_resend(struct nfs_pageio_descriptor *desc, 147762306a36Sopenharmony_ci struct nfs_pgio_header *hdr) 147862306a36Sopenharmony_ci{ 147962306a36Sopenharmony_ci LIST_HEAD(pages); 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_ci desc->pg_io_completion = hdr->io_completion; 148262306a36Sopenharmony_ci desc->pg_dreq = hdr->dreq; 148362306a36Sopenharmony_ci nfs_netfs_set_pageio_descriptor(desc, hdr); 148462306a36Sopenharmony_ci list_splice_init(&hdr->pages, &pages); 148562306a36Sopenharmony_ci while (!list_empty(&pages)) { 148662306a36Sopenharmony_ci struct nfs_page *req = nfs_list_entry(pages.next); 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_ci if (!nfs_pageio_add_request(desc, req)) 148962306a36Sopenharmony_ci break; 149062306a36Sopenharmony_ci } 149162306a36Sopenharmony_ci nfs_pageio_complete(desc); 149262306a36Sopenharmony_ci if (!list_empty(&pages)) { 149362306a36Sopenharmony_ci int err = desc->pg_error < 0 ? desc->pg_error : -EIO; 149462306a36Sopenharmony_ci hdr->completion_ops->error_cleanup(&pages, err); 149562306a36Sopenharmony_ci nfs_set_pgio_error(hdr, err, hdr->io_start); 149662306a36Sopenharmony_ci return err; 149762306a36Sopenharmony_ci } 149862306a36Sopenharmony_ci return 0; 149962306a36Sopenharmony_ci} 150062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_pageio_resend); 150162306a36Sopenharmony_ci 150262306a36Sopenharmony_ci/** 150362306a36Sopenharmony_ci * nfs_pageio_complete - Complete I/O then cleanup an nfs_pageio_descriptor 150462306a36Sopenharmony_ci * @desc: pointer to io descriptor 150562306a36Sopenharmony_ci */ 150662306a36Sopenharmony_civoid nfs_pageio_complete(struct nfs_pageio_descriptor *desc) 150762306a36Sopenharmony_ci{ 150862306a36Sopenharmony_ci u32 midx; 150962306a36Sopenharmony_ci 151062306a36Sopenharmony_ci for (midx = 0; midx < desc->pg_mirror_count; midx++) 151162306a36Sopenharmony_ci nfs_pageio_complete_mirror(desc, midx); 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_ci if (desc->pg_error < 0) 151462306a36Sopenharmony_ci nfs_pageio_error_cleanup(desc); 151562306a36Sopenharmony_ci if (desc->pg_ops->pg_cleanup) 151662306a36Sopenharmony_ci desc->pg_ops->pg_cleanup(desc); 151762306a36Sopenharmony_ci nfs_pageio_cleanup_mirroring(desc); 151862306a36Sopenharmony_ci} 151962306a36Sopenharmony_ci 152062306a36Sopenharmony_ci/** 152162306a36Sopenharmony_ci * nfs_pageio_cond_complete - Conditional I/O completion 152262306a36Sopenharmony_ci * @desc: pointer to io descriptor 152362306a36Sopenharmony_ci * @index: page index 152462306a36Sopenharmony_ci * 152562306a36Sopenharmony_ci * It is important to ensure that processes don't try to take locks 152662306a36Sopenharmony_ci * on non-contiguous ranges of pages as that might deadlock. This 152762306a36Sopenharmony_ci * function should be called before attempting to wait on a locked 152862306a36Sopenharmony_ci * nfs_page. It will complete the I/O if the page index 'index' 152962306a36Sopenharmony_ci * is not contiguous with the existing list of pages in 'desc'. 153062306a36Sopenharmony_ci */ 153162306a36Sopenharmony_civoid nfs_pageio_cond_complete(struct nfs_pageio_descriptor *desc, pgoff_t index) 153262306a36Sopenharmony_ci{ 153362306a36Sopenharmony_ci struct nfs_pgio_mirror *mirror; 153462306a36Sopenharmony_ci struct nfs_page *prev; 153562306a36Sopenharmony_ci struct folio *folio; 153662306a36Sopenharmony_ci u32 midx; 153762306a36Sopenharmony_ci 153862306a36Sopenharmony_ci for (midx = 0; midx < desc->pg_mirror_count; midx++) { 153962306a36Sopenharmony_ci mirror = nfs_pgio_get_mirror(desc, midx); 154062306a36Sopenharmony_ci if (!list_empty(&mirror->pg_list)) { 154162306a36Sopenharmony_ci prev = nfs_list_entry(mirror->pg_list.prev); 154262306a36Sopenharmony_ci folio = nfs_page_to_folio(prev); 154362306a36Sopenharmony_ci if (folio) { 154462306a36Sopenharmony_ci if (index == folio_next_index(folio)) 154562306a36Sopenharmony_ci continue; 154662306a36Sopenharmony_ci } else if (index == prev->wb_index + 1) 154762306a36Sopenharmony_ci continue; 154862306a36Sopenharmony_ci nfs_pageio_complete(desc); 154962306a36Sopenharmony_ci break; 155062306a36Sopenharmony_ci } 155162306a36Sopenharmony_ci } 155262306a36Sopenharmony_ci} 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_ci/* 155562306a36Sopenharmony_ci * nfs_pageio_stop_mirroring - stop using mirroring (set mirror count to 1) 155662306a36Sopenharmony_ci */ 155762306a36Sopenharmony_civoid nfs_pageio_stop_mirroring(struct nfs_pageio_descriptor *pgio) 155862306a36Sopenharmony_ci{ 155962306a36Sopenharmony_ci nfs_pageio_complete(pgio); 156062306a36Sopenharmony_ci} 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_ciint __init nfs_init_nfspagecache(void) 156362306a36Sopenharmony_ci{ 156462306a36Sopenharmony_ci nfs_page_cachep = kmem_cache_create("nfs_page", 156562306a36Sopenharmony_ci sizeof(struct nfs_page), 156662306a36Sopenharmony_ci 0, SLAB_HWCACHE_ALIGN, 156762306a36Sopenharmony_ci NULL); 156862306a36Sopenharmony_ci if (nfs_page_cachep == NULL) 156962306a36Sopenharmony_ci return -ENOMEM; 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci return 0; 157262306a36Sopenharmony_ci} 157362306a36Sopenharmony_ci 157462306a36Sopenharmony_civoid nfs_destroy_nfspagecache(void) 157562306a36Sopenharmony_ci{ 157662306a36Sopenharmony_ci kmem_cache_destroy(nfs_page_cachep); 157762306a36Sopenharmony_ci} 157862306a36Sopenharmony_ci 157962306a36Sopenharmony_cistatic const struct rpc_call_ops nfs_pgio_common_ops = { 158062306a36Sopenharmony_ci .rpc_call_prepare = nfs_pgio_prepare, 158162306a36Sopenharmony_ci .rpc_call_done = nfs_pgio_result, 158262306a36Sopenharmony_ci .rpc_release = nfs_pgio_release, 158362306a36Sopenharmony_ci}; 158462306a36Sopenharmony_ci 158562306a36Sopenharmony_ciconst struct nfs_pageio_ops nfs_pgio_rw_ops = { 158662306a36Sopenharmony_ci .pg_test = nfs_generic_pg_test, 158762306a36Sopenharmony_ci .pg_doio = nfs_generic_pg_pgios, 158862306a36Sopenharmony_ci}; 1589