1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * linux/fs/nfs/pagelist.c
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds * A set of helper functions for managing NFS read and write requests.
61da177e4SLinus Torvalds * The main purpose of these routines is to provide support for the
71da177e4SLinus Torvalds * coalescing of several requests into a single RPC call.
81da177e4SLinus Torvalds *
91da177e4SLinus Torvalds * Copyright 2000, 2001 (c) Trond Myklebust <trond.myklebust@fys.uio.no>
101da177e4SLinus Torvalds *
111da177e4SLinus Torvalds */
121da177e4SLinus Torvalds
131da177e4SLinus Torvalds #include <linux/slab.h>
141da177e4SLinus Torvalds #include <linux/file.h>
15e8edc6e0SAlexey Dobriyan #include <linux/sched.h>
161da177e4SLinus Torvalds #include <linux/sunrpc/clnt.h>
171313e603STrond Myklebust #include <linux/nfs.h>
181da177e4SLinus Torvalds #include <linux/nfs3.h>
191da177e4SLinus Torvalds #include <linux/nfs4.h>
201da177e4SLinus Torvalds #include <linux/nfs_fs.h>
2133344e0fSTrond Myklebust #include <linux/nfs_page.h>
221da177e4SLinus Torvalds #include <linux/nfs_mount.h>
23afeacc8cSPaul Gortmaker #include <linux/export.h>
245970e15dSJeff Layton #include <linux/filelock.h>
251da177e4SLinus Torvalds
268d5658c9STrond Myklebust #include "internal.h"
27bae724efSFred Isaman #include "pnfs.h"
28cd2ed9bdSChuck Lever #include "nfstrace.h"
29000dbe0bSDave Wysochanski #include "fscache.h"
308d5658c9STrond Myklebust
310eecb214SAnna Schumaker #define NFSDBG_FACILITY NFSDBG_PAGECACHE
321da177e4SLinus Torvalds
331da177e4SLinus Torvalds static struct kmem_cache *nfs_page_cachep;
34ef2c488cSAnna Schumaker static const struct rpc_call_ops nfs_pgio_common_ops;
351da177e4SLinus Torvalds
36eb9f2a5aSTrond Myklebust struct nfs_page_iter_page {
37eb9f2a5aSTrond Myklebust const struct nfs_page *req;
38eb9f2a5aSTrond Myklebust size_t count;
39eb9f2a5aSTrond Myklebust };
40eb9f2a5aSTrond Myklebust
nfs_page_iter_page_init(struct nfs_page_iter_page * i,const struct nfs_page * req)41eb9f2a5aSTrond Myklebust static void nfs_page_iter_page_init(struct nfs_page_iter_page *i,
42eb9f2a5aSTrond Myklebust const struct nfs_page *req)
43eb9f2a5aSTrond Myklebust {
44eb9f2a5aSTrond Myklebust i->req = req;
45eb9f2a5aSTrond Myklebust i->count = 0;
46eb9f2a5aSTrond Myklebust }
47eb9f2a5aSTrond Myklebust
nfs_page_iter_page_advance(struct nfs_page_iter_page * i,size_t sz)48eb9f2a5aSTrond Myklebust static void nfs_page_iter_page_advance(struct nfs_page_iter_page *i, size_t sz)
49eb9f2a5aSTrond Myklebust {
50eb9f2a5aSTrond Myklebust const struct nfs_page *req = i->req;
51eb9f2a5aSTrond Myklebust size_t tmp = i->count + sz;
52eb9f2a5aSTrond Myklebust
53eb9f2a5aSTrond Myklebust i->count = (tmp < req->wb_bytes) ? tmp : req->wb_bytes;
54eb9f2a5aSTrond Myklebust }
55eb9f2a5aSTrond Myklebust
nfs_page_iter_page_get(struct nfs_page_iter_page * i)56eb9f2a5aSTrond Myklebust static struct page *nfs_page_iter_page_get(struct nfs_page_iter_page *i)
57eb9f2a5aSTrond Myklebust {
58eb9f2a5aSTrond Myklebust const struct nfs_page *req = i->req;
59eb9f2a5aSTrond Myklebust struct page *page;
60eb9f2a5aSTrond Myklebust
61eb9f2a5aSTrond Myklebust if (i->count != req->wb_bytes) {
62eb9f2a5aSTrond Myklebust size_t base = i->count + req->wb_pgbase;
63eb9f2a5aSTrond Myklebust size_t len = PAGE_SIZE - offset_in_page(base);
64eb9f2a5aSTrond Myklebust
65eb9f2a5aSTrond Myklebust page = nfs_page_to_page(req, base);
66eb9f2a5aSTrond Myklebust nfs_page_iter_page_advance(i, len);
67eb9f2a5aSTrond Myklebust return page;
68eb9f2a5aSTrond Myklebust }
69eb9f2a5aSTrond Myklebust return NULL;
70eb9f2a5aSTrond Myklebust }
71eb9f2a5aSTrond Myklebust
7263e2fffaSTrond Myklebust static struct nfs_pgio_mirror *
nfs_pgio_get_mirror(struct nfs_pageio_descriptor * desc,u32 idx)7363e2fffaSTrond Myklebust nfs_pgio_get_mirror(struct nfs_pageio_descriptor *desc, u32 idx)
7463e2fffaSTrond Myklebust {
7563e2fffaSTrond Myklebust if (desc->pg_ops->pg_get_mirror)
7663e2fffaSTrond Myklebust return desc->pg_ops->pg_get_mirror(desc, idx);
7763e2fffaSTrond Myklebust return &desc->pg_mirrors[0];
7863e2fffaSTrond Myklebust }
7963e2fffaSTrond Myklebust
8048d635f1SPeng Tao struct nfs_pgio_mirror *
nfs_pgio_current_mirror(struct nfs_pageio_descriptor * desc)8148d635f1SPeng Tao nfs_pgio_current_mirror(struct nfs_pageio_descriptor *desc)
8248d635f1SPeng Tao {
8363e2fffaSTrond Myklebust return nfs_pgio_get_mirror(desc, desc->pg_mirror_idx);
8448d635f1SPeng Tao }
8548d635f1SPeng Tao EXPORT_SYMBOL_GPL(nfs_pgio_current_mirror);
8648d635f1SPeng Tao
8763e2fffaSTrond Myklebust static u32
nfs_pgio_set_current_mirror(struct nfs_pageio_descriptor * desc,u32 idx)8863e2fffaSTrond Myklebust nfs_pgio_set_current_mirror(struct nfs_pageio_descriptor *desc, u32 idx)
8963e2fffaSTrond Myklebust {
9063e2fffaSTrond Myklebust if (desc->pg_ops->pg_set_mirror)
9163e2fffaSTrond Myklebust return desc->pg_ops->pg_set_mirror(desc, idx);
9263e2fffaSTrond Myklebust return desc->pg_mirror_idx;
9363e2fffaSTrond Myklebust }
9463e2fffaSTrond Myklebust
nfs_pgheader_init(struct nfs_pageio_descriptor * desc,struct nfs_pgio_header * hdr,void (* release)(struct nfs_pgio_header * hdr))954db6e0b7SFred Isaman void nfs_pgheader_init(struct nfs_pageio_descriptor *desc,
964db6e0b7SFred Isaman struct nfs_pgio_header *hdr,
974db6e0b7SFred Isaman void (*release)(struct nfs_pgio_header *hdr))
984db6e0b7SFred Isaman {
9948d635f1SPeng Tao struct nfs_pgio_mirror *mirror = nfs_pgio_current_mirror(desc);
100a7d42ddbSWeston Andros Adamson
101a7d42ddbSWeston Andros Adamson
102a7d42ddbSWeston Andros Adamson hdr->req = nfs_list_entry(mirror->pg_list.next);
1034db6e0b7SFred Isaman hdr->inode = desc->pg_inode;
1049fcd5960STrond Myklebust hdr->cred = nfs_req_openctx(hdr->req)->cred;
1054db6e0b7SFred Isaman hdr->io_start = req_offset(hdr->req);
106a7d42ddbSWeston Andros Adamson hdr->good_bytes = mirror->pg_count;
107919e3bd9STrond Myklebust hdr->io_completion = desc->pg_io_completion;
108584aa810SFred Isaman hdr->dreq = desc->pg_dreq;
109000dbe0bSDave Wysochanski nfs_netfs_set_pgio_header(hdr, desc);
1104db6e0b7SFred Isaman hdr->release = release;
111061ae2edSFred Isaman hdr->completion_ops = desc->pg_completion_ops;
112584aa810SFred Isaman if (hdr->completion_ops->init_hdr)
113584aa810SFred Isaman hdr->completion_ops->init_hdr(hdr);
114a7d42ddbSWeston Andros Adamson
115a7d42ddbSWeston Andros Adamson hdr->pgio_mirror_idx = desc->pg_mirror_idx;
1164db6e0b7SFred Isaman }
11789d77c8fSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_pgheader_init);
1184db6e0b7SFred Isaman
nfs_set_pgio_error(struct nfs_pgio_header * hdr,int error,loff_t pos)1194db6e0b7SFred Isaman void nfs_set_pgio_error(struct nfs_pgio_header *hdr, int error, loff_t pos)
1204db6e0b7SFred Isaman {
1211c6c4b74STrond Myklebust unsigned int new = pos - hdr->io_start;
1221c6c4b74STrond Myklebust
123cd2ed9bdSChuck Lever trace_nfs_pgio_error(hdr, error, pos);
1241c6c4b74STrond Myklebust if (hdr->good_bytes > new) {
1251c6c4b74STrond Myklebust hdr->good_bytes = new;
1264db6e0b7SFred Isaman clear_bit(NFS_IOHDR_EOF, &hdr->flags);
1271c6c4b74STrond Myklebust if (!test_and_set_bit(NFS_IOHDR_ERROR, &hdr->flags))
1284db6e0b7SFred Isaman hdr->error = error;
1294db6e0b7SFred Isaman }
1304db6e0b7SFred Isaman }
1314db6e0b7SFred Isaman
nfs_page_alloc(void)1320bae835bSTrond Myklebust static inline struct nfs_page *nfs_page_alloc(void)
1331da177e4SLinus Torvalds {
1340bae835bSTrond Myklebust struct nfs_page *p =
1350bae835bSTrond Myklebust kmem_cache_zalloc(nfs_page_cachep, nfs_io_gfp_mask());
13672895b1aSJesper Juhl if (p)
1371da177e4SLinus Torvalds INIT_LIST_HEAD(&p->wb_list);
1381da177e4SLinus Torvalds return p;
1391da177e4SLinus Torvalds }
1401da177e4SLinus Torvalds
1411da177e4SLinus Torvalds static inline void
nfs_page_free(struct nfs_page * p)1421da177e4SLinus Torvalds nfs_page_free(struct nfs_page *p)
1431da177e4SLinus Torvalds {
1441da177e4SLinus Torvalds kmem_cache_free(nfs_page_cachep, p);
1451da177e4SLinus Torvalds }
1461da177e4SLinus Torvalds
147577b4232STrond Myklebust /**
148577b4232STrond Myklebust * nfs_iocounter_wait - wait for i/o to complete
149210c7c17SBenjamin Coddington * @l_ctx: nfs_lock_context with io_counter to use
150577b4232STrond Myklebust *
151577b4232STrond Myklebust * returns -ERESTARTSYS if interrupted by a fatal signal.
152577b4232STrond Myklebust * Otherwise returns 0 once the io_count hits 0.
153577b4232STrond Myklebust */
154577b4232STrond Myklebust int
nfs_iocounter_wait(struct nfs_lock_context * l_ctx)155210c7c17SBenjamin Coddington nfs_iocounter_wait(struct nfs_lock_context *l_ctx)
156577b4232STrond Myklebust {
157723c921eSPeter Zijlstra return wait_var_event_killable(&l_ctx->io_count,
158723c921eSPeter Zijlstra !atomic_read(&l_ctx->io_count));
159577b4232STrond Myklebust }
160577b4232STrond Myklebust
1617d6ddf88SBenjamin Coddington /**
1627d6ddf88SBenjamin Coddington * nfs_async_iocounter_wait - wait on a rpc_waitqueue for I/O
1637d6ddf88SBenjamin Coddington * to complete
1647d6ddf88SBenjamin Coddington * @task: the rpc_task that should wait
1657d6ddf88SBenjamin Coddington * @l_ctx: nfs_lock_context with io_counter to check
1667d6ddf88SBenjamin Coddington *
1677d6ddf88SBenjamin Coddington * Returns true if there is outstanding I/O to wait on and the
1687d6ddf88SBenjamin Coddington * task has been put to sleep.
1697d6ddf88SBenjamin Coddington */
1707d6ddf88SBenjamin Coddington bool
nfs_async_iocounter_wait(struct rpc_task * task,struct nfs_lock_context * l_ctx)1717d6ddf88SBenjamin Coddington nfs_async_iocounter_wait(struct rpc_task *task, struct nfs_lock_context *l_ctx)
1727d6ddf88SBenjamin Coddington {
1737d6ddf88SBenjamin Coddington struct inode *inode = d_inode(l_ctx->open_context->dentry);
1747d6ddf88SBenjamin Coddington bool ret = false;
1757d6ddf88SBenjamin Coddington
1767d6ddf88SBenjamin Coddington if (atomic_read(&l_ctx->io_count) > 0) {
1777d6ddf88SBenjamin Coddington rpc_sleep_on(&NFS_SERVER(inode)->uoc_rpcwaitq, task, NULL);
1787d6ddf88SBenjamin Coddington ret = true;
1797d6ddf88SBenjamin Coddington }
1807d6ddf88SBenjamin Coddington
1817d6ddf88SBenjamin Coddington if (atomic_read(&l_ctx->io_count) == 0) {
1827d6ddf88SBenjamin Coddington rpc_wake_up_queued_task(&NFS_SERVER(inode)->uoc_rpcwaitq, task);
1837d6ddf88SBenjamin Coddington ret = false;
1847d6ddf88SBenjamin Coddington }
1857d6ddf88SBenjamin Coddington
1867d6ddf88SBenjamin Coddington return ret;
1877d6ddf88SBenjamin Coddington }
1887d6ddf88SBenjamin Coddington EXPORT_SYMBOL_GPL(nfs_async_iocounter_wait);
1897d6ddf88SBenjamin Coddington
1902bfc6e56SWeston Andros Adamson /*
191e00ed89dSTrond Myklebust * nfs_page_lock_head_request - page lock the head of the page group
192e00ed89dSTrond Myklebust * @req: any member of the page group
193e00ed89dSTrond Myklebust */
194e00ed89dSTrond Myklebust struct nfs_page *
nfs_page_group_lock_head(struct nfs_page * req)195e00ed89dSTrond Myklebust nfs_page_group_lock_head(struct nfs_page *req)
196e00ed89dSTrond Myklebust {
197e00ed89dSTrond Myklebust struct nfs_page *head = req->wb_head;
198e00ed89dSTrond Myklebust
199e00ed89dSTrond Myklebust while (!nfs_lock_request(head)) {
200e00ed89dSTrond Myklebust int ret = nfs_wait_on_request(head);
201e00ed89dSTrond Myklebust if (ret < 0)
202e00ed89dSTrond Myklebust return ERR_PTR(ret);
203e00ed89dSTrond Myklebust }
204e00ed89dSTrond Myklebust if (head != req)
205e00ed89dSTrond Myklebust kref_get(&head->wb_kref);
206e00ed89dSTrond Myklebust return head;
207e00ed89dSTrond Myklebust }
208e00ed89dSTrond Myklebust
209e00ed89dSTrond Myklebust /*
210a62f8e3bSTrond Myklebust * nfs_unroll_locks - unlock all newly locked reqs and wait on @req
211a62f8e3bSTrond Myklebust * @head: head request of page group, must be holding head lock
212a62f8e3bSTrond Myklebust * @req: request that couldn't lock and needs to wait on the req bit lock
213a62f8e3bSTrond Myklebust *
214a62f8e3bSTrond Myklebust * This is a helper function for nfs_lock_and_join_requests
215a62f8e3bSTrond Myklebust * returns 0 on success, < 0 on error.
216a62f8e3bSTrond Myklebust */
217a62f8e3bSTrond Myklebust static void
nfs_unroll_locks(struct nfs_page * head,struct nfs_page * req)218a62f8e3bSTrond Myklebust nfs_unroll_locks(struct nfs_page *head, struct nfs_page *req)
219a62f8e3bSTrond Myklebust {
220a62f8e3bSTrond Myklebust struct nfs_page *tmp;
221a62f8e3bSTrond Myklebust
222a62f8e3bSTrond Myklebust /* relinquish all the locks successfully grabbed this run */
223a62f8e3bSTrond Myklebust for (tmp = head->wb_this_page ; tmp != req; tmp = tmp->wb_this_page) {
224a62f8e3bSTrond Myklebust if (!kref_read(&tmp->wb_kref))
225a62f8e3bSTrond Myklebust continue;
226a62f8e3bSTrond Myklebust nfs_unlock_and_release_request(tmp);
227a62f8e3bSTrond Myklebust }
228a62f8e3bSTrond Myklebust }
229a62f8e3bSTrond Myklebust
230a62f8e3bSTrond Myklebust /*
231a62f8e3bSTrond Myklebust * nfs_page_group_lock_subreq - try to lock a subrequest
232a62f8e3bSTrond Myklebust * @head: head request of page group
233a62f8e3bSTrond Myklebust * @subreq: request to lock
234a62f8e3bSTrond Myklebust *
235a62f8e3bSTrond Myklebust * This is a helper function for nfs_lock_and_join_requests which
236a62f8e3bSTrond Myklebust * must be called with the head request and page group both locked.
237a62f8e3bSTrond Myklebust * On error, it returns with the page group unlocked.
238a62f8e3bSTrond Myklebust */
239a62f8e3bSTrond Myklebust static int
nfs_page_group_lock_subreq(struct nfs_page * head,struct nfs_page * subreq)240a62f8e3bSTrond Myklebust nfs_page_group_lock_subreq(struct nfs_page *head, struct nfs_page *subreq)
241a62f8e3bSTrond Myklebust {
242a62f8e3bSTrond Myklebust int ret;
243a62f8e3bSTrond Myklebust
244a62f8e3bSTrond Myklebust if (!kref_get_unless_zero(&subreq->wb_kref))
245a62f8e3bSTrond Myklebust return 0;
246a62f8e3bSTrond Myklebust while (!nfs_lock_request(subreq)) {
247a62f8e3bSTrond Myklebust nfs_page_group_unlock(head);
248a62f8e3bSTrond Myklebust ret = nfs_wait_on_request(subreq);
249a62f8e3bSTrond Myklebust if (!ret)
250a62f8e3bSTrond Myklebust ret = nfs_page_group_lock(head);
251a62f8e3bSTrond Myklebust if (ret < 0) {
252a62f8e3bSTrond Myklebust nfs_unroll_locks(head, subreq);
253a62f8e3bSTrond Myklebust nfs_release_request(subreq);
254a62f8e3bSTrond Myklebust return ret;
255a62f8e3bSTrond Myklebust }
256a62f8e3bSTrond Myklebust }
257a62f8e3bSTrond Myklebust return 0;
258a62f8e3bSTrond Myklebust }
259a62f8e3bSTrond Myklebust
260a62f8e3bSTrond Myklebust /*
261a62f8e3bSTrond Myklebust * nfs_page_group_lock_subrequests - try to lock the subrequests
262a62f8e3bSTrond Myklebust * @head: head request of page group
263a62f8e3bSTrond Myklebust *
264a62f8e3bSTrond Myklebust * This is a helper function for nfs_lock_and_join_requests which
265e00ed89dSTrond Myklebust * must be called with the head request locked.
266a62f8e3bSTrond Myklebust */
nfs_page_group_lock_subrequests(struct nfs_page * head)267a62f8e3bSTrond Myklebust int nfs_page_group_lock_subrequests(struct nfs_page *head)
268a62f8e3bSTrond Myklebust {
269a62f8e3bSTrond Myklebust struct nfs_page *subreq;
270a62f8e3bSTrond Myklebust int ret;
271a62f8e3bSTrond Myklebust
272e00ed89dSTrond Myklebust ret = nfs_page_group_lock(head);
273e00ed89dSTrond Myklebust if (ret < 0)
274e00ed89dSTrond Myklebust return ret;
275a62f8e3bSTrond Myklebust /* lock each request in the page group */
276a62f8e3bSTrond Myklebust for (subreq = head->wb_this_page; subreq != head;
277a62f8e3bSTrond Myklebust subreq = subreq->wb_this_page) {
278a62f8e3bSTrond Myklebust ret = nfs_page_group_lock_subreq(head, subreq);
279a62f8e3bSTrond Myklebust if (ret < 0)
280a62f8e3bSTrond Myklebust return ret;
281a62f8e3bSTrond Myklebust }
282e00ed89dSTrond Myklebust nfs_page_group_unlock(head);
283a62f8e3bSTrond Myklebust return 0;
284a62f8e3bSTrond Myklebust }
285a62f8e3bSTrond Myklebust
286a62f8e3bSTrond Myklebust /*
28708ca8b21STrond Myklebust * nfs_page_set_headlock - set the request PG_HEADLOCK
28808ca8b21STrond Myklebust * @req: request that is to be locked
28908ca8b21STrond Myklebust *
29008ca8b21STrond Myklebust * this lock must be held when modifying req->wb_head
29108ca8b21STrond Myklebust *
29208ca8b21STrond Myklebust * return 0 on success, < 0 on error
29308ca8b21STrond Myklebust */
29408ca8b21STrond Myklebust int
nfs_page_set_headlock(struct nfs_page * req)29508ca8b21STrond Myklebust nfs_page_set_headlock(struct nfs_page *req)
29608ca8b21STrond Myklebust {
29708ca8b21STrond Myklebust if (!test_and_set_bit(PG_HEADLOCK, &req->wb_flags))
29808ca8b21STrond Myklebust return 0;
29908ca8b21STrond Myklebust
30008ca8b21STrond Myklebust set_bit(PG_CONTENDED1, &req->wb_flags);
30108ca8b21STrond Myklebust smp_mb__after_atomic();
30208ca8b21STrond Myklebust return wait_on_bit_lock(&req->wb_flags, PG_HEADLOCK,
30308ca8b21STrond Myklebust TASK_UNINTERRUPTIBLE);
30408ca8b21STrond Myklebust }
30508ca8b21STrond Myklebust
30608ca8b21STrond Myklebust /*
30708ca8b21STrond Myklebust * nfs_page_clear_headlock - clear the request PG_HEADLOCK
30808ca8b21STrond Myklebust * @req: request that is to be locked
30908ca8b21STrond Myklebust */
31008ca8b21STrond Myklebust void
nfs_page_clear_headlock(struct nfs_page * req)31108ca8b21STrond Myklebust nfs_page_clear_headlock(struct nfs_page *req)
31208ca8b21STrond Myklebust {
31343d20e80STrond Myklebust clear_bit_unlock(PG_HEADLOCK, &req->wb_flags);
31408ca8b21STrond Myklebust smp_mb__after_atomic();
31508ca8b21STrond Myklebust if (!test_bit(PG_CONTENDED1, &req->wb_flags))
31608ca8b21STrond Myklebust return;
31708ca8b21STrond Myklebust wake_up_bit(&req->wb_flags, PG_HEADLOCK);
31808ca8b21STrond Myklebust }
31908ca8b21STrond Myklebust
32008ca8b21STrond Myklebust /*
3212bfc6e56SWeston Andros Adamson * nfs_page_group_lock - lock the head of the page group
32208ca8b21STrond Myklebust * @req: request in group that is to be locked
3232bfc6e56SWeston Andros Adamson *
3241344b7eaSTrond Myklebust * this lock must be held when traversing or modifying the page
3251344b7eaSTrond Myklebust * group list
326e7029206SWeston Andros Adamson *
3271344b7eaSTrond Myklebust * return 0 on success, < 0 on error
3282bfc6e56SWeston Andros Adamson */
329e7029206SWeston Andros Adamson int
nfs_page_group_lock(struct nfs_page * req)3301344b7eaSTrond Myklebust nfs_page_group_lock(struct nfs_page *req)
3312bfc6e56SWeston Andros Adamson {
33208ca8b21STrond Myklebust int ret;
3332bfc6e56SWeston Andros Adamson
33408ca8b21STrond Myklebust ret = nfs_page_set_headlock(req);
33508ca8b21STrond Myklebust if (ret || req->wb_head == req)
33608ca8b21STrond Myklebust return ret;
33708ca8b21STrond Myklebust return nfs_page_set_headlock(req->wb_head);
338b4f937cfSTrond Myklebust }
339bc8a309eSWeston Andros Adamson
3402bfc6e56SWeston Andros Adamson /*
3412bfc6e56SWeston Andros Adamson * nfs_page_group_unlock - unlock the head of the page group
34208ca8b21STrond Myklebust * @req: request in group that is to be unlocked
3432bfc6e56SWeston Andros Adamson */
3442bfc6e56SWeston Andros Adamson void
nfs_page_group_unlock(struct nfs_page * req)3452bfc6e56SWeston Andros Adamson nfs_page_group_unlock(struct nfs_page *req)
3462bfc6e56SWeston Andros Adamson {
34708ca8b21STrond Myklebust if (req != req->wb_head)
34808ca8b21STrond Myklebust nfs_page_clear_headlock(req->wb_head);
34908ca8b21STrond Myklebust nfs_page_clear_headlock(req);
3502bfc6e56SWeston Andros Adamson }
3512bfc6e56SWeston Andros Adamson
3522bfc6e56SWeston Andros Adamson /*
3532bfc6e56SWeston Andros Adamson * nfs_page_group_sync_on_bit_locked
3542bfc6e56SWeston Andros Adamson *
3552bfc6e56SWeston Andros Adamson * must be called with page group lock held
3562bfc6e56SWeston Andros Adamson */
3572bfc6e56SWeston Andros Adamson static bool
nfs_page_group_sync_on_bit_locked(struct nfs_page * req,unsigned int bit)3582bfc6e56SWeston Andros Adamson nfs_page_group_sync_on_bit_locked(struct nfs_page *req, unsigned int bit)
3592bfc6e56SWeston Andros Adamson {
3602bfc6e56SWeston Andros Adamson struct nfs_page *head = req->wb_head;
3612bfc6e56SWeston Andros Adamson struct nfs_page *tmp;
3622bfc6e56SWeston Andros Adamson
3632bfc6e56SWeston Andros Adamson WARN_ON_ONCE(!test_bit(PG_HEADLOCK, &head->wb_flags));
3642bfc6e56SWeston Andros Adamson WARN_ON_ONCE(test_and_set_bit(bit, &req->wb_flags));
3652bfc6e56SWeston Andros Adamson
3662bfc6e56SWeston Andros Adamson tmp = req->wb_this_page;
3672bfc6e56SWeston Andros Adamson while (tmp != req) {
3682bfc6e56SWeston Andros Adamson if (!test_bit(bit, &tmp->wb_flags))
3692bfc6e56SWeston Andros Adamson return false;
3702bfc6e56SWeston Andros Adamson tmp = tmp->wb_this_page;
3712bfc6e56SWeston Andros Adamson }
3722bfc6e56SWeston Andros Adamson
3732bfc6e56SWeston Andros Adamson /* true! reset all bits */
3742bfc6e56SWeston Andros Adamson tmp = req;
3752bfc6e56SWeston Andros Adamson do {
3762bfc6e56SWeston Andros Adamson clear_bit(bit, &tmp->wb_flags);
3772bfc6e56SWeston Andros Adamson tmp = tmp->wb_this_page;
3782bfc6e56SWeston Andros Adamson } while (tmp != req);
3792bfc6e56SWeston Andros Adamson
3802bfc6e56SWeston Andros Adamson return true;
3812bfc6e56SWeston Andros Adamson }
3822bfc6e56SWeston Andros Adamson
3832bfc6e56SWeston Andros Adamson /*
3842bfc6e56SWeston Andros Adamson * nfs_page_group_sync_on_bit - set bit on current request, but only
3852bfc6e56SWeston Andros Adamson * return true if the bit is set for all requests in page group
3862bfc6e56SWeston Andros Adamson * @req - request in page group
3872bfc6e56SWeston Andros Adamson * @bit - PG_* bit that is used to sync page group
3882bfc6e56SWeston Andros Adamson */
nfs_page_group_sync_on_bit(struct nfs_page * req,unsigned int bit)3892bfc6e56SWeston Andros Adamson bool nfs_page_group_sync_on_bit(struct nfs_page *req, unsigned int bit)
3902bfc6e56SWeston Andros Adamson {
3912bfc6e56SWeston Andros Adamson bool ret;
3922bfc6e56SWeston Andros Adamson
3931344b7eaSTrond Myklebust nfs_page_group_lock(req);
3942bfc6e56SWeston Andros Adamson ret = nfs_page_group_sync_on_bit_locked(req, bit);
3952bfc6e56SWeston Andros Adamson nfs_page_group_unlock(req);
3962bfc6e56SWeston Andros Adamson
3972bfc6e56SWeston Andros Adamson return ret;
3982bfc6e56SWeston Andros Adamson }
3992bfc6e56SWeston Andros Adamson
4002bfc6e56SWeston Andros Adamson /*
4012bfc6e56SWeston Andros Adamson * nfs_page_group_init - Initialize the page group linkage for @req
4022bfc6e56SWeston Andros Adamson * @req - a new nfs request
4032bfc6e56SWeston Andros Adamson * @prev - the previous request in page group, or NULL if @req is the first
4042bfc6e56SWeston Andros Adamson * or only request in the group (the head).
4052bfc6e56SWeston Andros Adamson */
4062bfc6e56SWeston Andros Adamson static inline void
nfs_page_group_init(struct nfs_page * req,struct nfs_page * prev)4072bfc6e56SWeston Andros Adamson nfs_page_group_init(struct nfs_page *req, struct nfs_page *prev)
4082bfc6e56SWeston Andros Adamson {
409cb1410c7SWeston Andros Adamson struct inode *inode;
4102bfc6e56SWeston Andros Adamson WARN_ON_ONCE(prev == req);
4112bfc6e56SWeston Andros Adamson
4122bfc6e56SWeston Andros Adamson if (!prev) {
41385710a83SWeston Andros Adamson /* a head request */
4142bfc6e56SWeston Andros Adamson req->wb_head = req;
4152bfc6e56SWeston Andros Adamson req->wb_this_page = req;
4162bfc6e56SWeston Andros Adamson } else {
41785710a83SWeston Andros Adamson /* a subrequest */
4182bfc6e56SWeston Andros Adamson WARN_ON_ONCE(prev->wb_this_page != prev->wb_head);
4192bfc6e56SWeston Andros Adamson WARN_ON_ONCE(!test_bit(PG_HEADLOCK, &prev->wb_head->wb_flags));
4202bfc6e56SWeston Andros Adamson req->wb_head = prev->wb_head;
4212bfc6e56SWeston Andros Adamson req->wb_this_page = prev->wb_this_page;
4222bfc6e56SWeston Andros Adamson prev->wb_this_page = req;
4232bfc6e56SWeston Andros Adamson
42485710a83SWeston Andros Adamson /* All subrequests take a ref on the head request until
42585710a83SWeston Andros Adamson * nfs_page_group_destroy is called */
42685710a83SWeston Andros Adamson kref_get(&req->wb_head->wb_kref);
42785710a83SWeston Andros Adamson
428cb1410c7SWeston Andros Adamson /* grab extra ref and bump the request count if head request
429cb1410c7SWeston Andros Adamson * has extra ref from the write/commit path to handle handoff
430cb1410c7SWeston Andros Adamson * between write and commit lists. */
43117089a29SWeston Andros Adamson if (test_bit(PG_INODE_REF, &prev->wb_head->wb_flags)) {
4326dd85e83STrond Myklebust inode = nfs_page_to_inode(req);
43317089a29SWeston Andros Adamson set_bit(PG_INODE_REF, &req->wb_flags);
4342bfc6e56SWeston Andros Adamson kref_get(&req->wb_kref);
435a6b6d5b8STrond Myklebust atomic_long_inc(&NFS_I(inode)->nrequests);
4362bfc6e56SWeston Andros Adamson }
4372bfc6e56SWeston Andros Adamson }
43817089a29SWeston Andros Adamson }
4392bfc6e56SWeston Andros Adamson
4402bfc6e56SWeston Andros Adamson /*
4412bfc6e56SWeston Andros Adamson * nfs_page_group_destroy - sync the destruction of page groups
4422bfc6e56SWeston Andros Adamson * @req - request that no longer needs the page group
4432bfc6e56SWeston Andros Adamson *
4442bfc6e56SWeston Andros Adamson * releases the page group reference from each member once all
4452bfc6e56SWeston Andros Adamson * members have called this function.
4462bfc6e56SWeston Andros Adamson */
4472bfc6e56SWeston Andros Adamson static void
nfs_page_group_destroy(struct kref * kref)4482bfc6e56SWeston Andros Adamson nfs_page_group_destroy(struct kref *kref)
4492bfc6e56SWeston Andros Adamson {
4502bfc6e56SWeston Andros Adamson struct nfs_page *req = container_of(kref, struct nfs_page, wb_kref);
45108fead2aSTrond Myklebust struct nfs_page *head = req->wb_head;
4522bfc6e56SWeston Andros Adamson struct nfs_page *tmp, *next;
4532bfc6e56SWeston Andros Adamson
4542bfc6e56SWeston Andros Adamson if (!nfs_page_group_sync_on_bit(req, PG_TEARDOWN))
45508fead2aSTrond Myklebust goto out;
4562bfc6e56SWeston Andros Adamson
4572bfc6e56SWeston Andros Adamson tmp = req;
4582bfc6e56SWeston Andros Adamson do {
4592bfc6e56SWeston Andros Adamson next = tmp->wb_this_page;
4602bfc6e56SWeston Andros Adamson /* unlink and free */
4612bfc6e56SWeston Andros Adamson tmp->wb_this_page = tmp;
4622bfc6e56SWeston Andros Adamson tmp->wb_head = tmp;
4632bfc6e56SWeston Andros Adamson nfs_free_request(tmp);
4642bfc6e56SWeston Andros Adamson tmp = next;
4652bfc6e56SWeston Andros Adamson } while (tmp != req);
46608fead2aSTrond Myklebust out:
46708fead2aSTrond Myklebust /* subrequests must release the ref on the head request */
46808fead2aSTrond Myklebust if (head != req)
46908fead2aSTrond Myklebust nfs_release_request(head);
4702bfc6e56SWeston Andros Adamson }
4712bfc6e56SWeston Andros Adamson
nfs_page_create(struct nfs_lock_context * l_ctx,unsigned int pgbase,pgoff_t index,unsigned int offset,unsigned int count)472cbefa53cSTrond Myklebust static struct nfs_page *nfs_page_create(struct nfs_lock_context *l_ctx,
473cbefa53cSTrond Myklebust unsigned int pgbase, pgoff_t index,
474cbefa53cSTrond Myklebust unsigned int offset, unsigned int count)
475c917cfafSTrond Myklebust {
476c917cfafSTrond Myklebust struct nfs_page *req;
477c917cfafSTrond Myklebust struct nfs_open_context *ctx = l_ctx->open_context;
478c917cfafSTrond Myklebust
479c917cfafSTrond Myklebust if (test_bit(NFS_CONTEXT_BAD, &ctx->flags))
480c917cfafSTrond Myklebust return ERR_PTR(-EBADF);
481c917cfafSTrond Myklebust /* try to allocate the request struct */
482c917cfafSTrond Myklebust req = nfs_page_alloc();
483c917cfafSTrond Myklebust if (req == NULL)
484c917cfafSTrond Myklebust return ERR_PTR(-ENOMEM);
485c917cfafSTrond Myklebust
486c917cfafSTrond Myklebust req->wb_lock_context = l_ctx;
487c917cfafSTrond Myklebust refcount_inc(&l_ctx->count);
488c917cfafSTrond Myklebust atomic_inc(&l_ctx->io_count);
489c917cfafSTrond Myklebust
490c917cfafSTrond Myklebust /* Initialize the request struct. Initially, we assume a
491c917cfafSTrond Myklebust * long write-back delay. This will be adjusted in
492c917cfafSTrond Myklebust * update_nfs_request below if the region is not locked. */
493c917cfafSTrond Myklebust req->wb_pgbase = pgbase;
494cbefa53cSTrond Myklebust req->wb_index = index;
495cbefa53cSTrond Myklebust req->wb_offset = offset;
496c917cfafSTrond Myklebust req->wb_bytes = count;
497c917cfafSTrond Myklebust kref_init(&req->wb_kref);
49833344e0fSTrond Myklebust req->wb_nio = 0;
499c917cfafSTrond Myklebust return req;
500c917cfafSTrond Myklebust }
501c917cfafSTrond Myklebust
nfs_page_assign_folio(struct nfs_page * req,struct folio * folio)502cbefa53cSTrond Myklebust static void nfs_page_assign_folio(struct nfs_page *req, struct folio *folio)
503cbefa53cSTrond Myklebust {
504cbefa53cSTrond Myklebust if (folio != NULL) {
505cbefa53cSTrond Myklebust req->wb_folio = folio;
506cbefa53cSTrond Myklebust folio_get(folio);
507cbefa53cSTrond Myklebust set_bit(PG_FOLIO, &req->wb_flags);
508cbefa53cSTrond Myklebust }
509cbefa53cSTrond Myklebust }
510cbefa53cSTrond Myklebust
nfs_page_assign_page(struct nfs_page * req,struct page * page)511cbefa53cSTrond Myklebust static void nfs_page_assign_page(struct nfs_page *req, struct page *page)
512cbefa53cSTrond Myklebust {
513cbefa53cSTrond Myklebust if (page != NULL) {
514cbefa53cSTrond Myklebust req->wb_page = page;
515cbefa53cSTrond Myklebust get_page(page);
516cbefa53cSTrond Myklebust }
517cbefa53cSTrond Myklebust }
518cbefa53cSTrond Myklebust
5191da177e4SLinus Torvalds /**
52070e9db69STrond Myklebust * nfs_page_create_from_page - Create an NFS read/write request.
521c02f557dSChuck Lever * @ctx: open context to use
5221da177e4SLinus Torvalds * @page: page to write
52370e9db69STrond Myklebust * @pgbase: starting offset within the page for the write
52470e9db69STrond Myklebust * @offset: file offset for the write
5251da177e4SLinus Torvalds * @count: number of bytes to read/write
5261da177e4SLinus Torvalds *
5271da177e4SLinus Torvalds * The page must be locked by the caller. This makes sure we never
528a19b89caSJason Uhlenkott * create two different requests for the same page.
5291da177e4SLinus Torvalds * User should ensure it is safe to sleep in this function.
5301da177e4SLinus Torvalds */
nfs_page_create_from_page(struct nfs_open_context * ctx,struct page * page,unsigned int pgbase,loff_t offset,unsigned int count)53170e9db69STrond Myklebust struct nfs_page *nfs_page_create_from_page(struct nfs_open_context *ctx,
53270e9db69STrond Myklebust struct page *page,
53370e9db69STrond Myklebust unsigned int pgbase, loff_t offset,
53470e9db69STrond Myklebust unsigned int count)
5351da177e4SLinus Torvalds {
536c917cfafSTrond Myklebust struct nfs_lock_context *l_ctx = nfs_get_lock_context(ctx);
537c917cfafSTrond Myklebust struct nfs_page *ret;
5381da177e4SLinus Torvalds
539c917cfafSTrond Myklebust if (IS_ERR(l_ctx))
540b3c54de6STrond Myklebust return ERR_CAST(l_ctx);
54170e9db69STrond Myklebust ret = nfs_page_create(l_ctx, pgbase, offset >> PAGE_SHIFT,
54270e9db69STrond Myklebust offset_in_page(offset), count);
543cbefa53cSTrond Myklebust if (!IS_ERR(ret)) {
544cbefa53cSTrond Myklebust nfs_page_assign_page(ret, page);
54528b1d3f5STrond Myklebust nfs_page_group_init(ret, NULL);
546cbefa53cSTrond Myklebust }
547c917cfafSTrond Myklebust nfs_put_lock_context(l_ctx);
548c917cfafSTrond Myklebust return ret;
549015f0212SJeff Layton }
550015f0212SJeff Layton
551ab75bff1STrond Myklebust /**
552ab75bff1STrond Myklebust * nfs_page_create_from_folio - Create an NFS read/write request.
553ab75bff1STrond Myklebust * @ctx: open context to use
554ab75bff1STrond Myklebust * @folio: folio to write
555ab75bff1STrond Myklebust * @offset: starting offset within the folio for the write
556ab75bff1STrond Myklebust * @count: number of bytes to read/write
557ab75bff1STrond Myklebust *
558ab75bff1STrond Myklebust * The page must be locked by the caller. This makes sure we never
559ab75bff1STrond Myklebust * create two different requests for the same page.
560ab75bff1STrond Myklebust * User should ensure it is safe to sleep in this function.
561ab75bff1STrond Myklebust */
nfs_page_create_from_folio(struct nfs_open_context * ctx,struct folio * folio,unsigned int offset,unsigned int count)562ab75bff1STrond Myklebust struct nfs_page *nfs_page_create_from_folio(struct nfs_open_context *ctx,
563ab75bff1STrond Myklebust struct folio *folio,
564ab75bff1STrond Myklebust unsigned int offset,
565ab75bff1STrond Myklebust unsigned int count)
566ab75bff1STrond Myklebust {
567ab75bff1STrond Myklebust struct nfs_lock_context *l_ctx = nfs_get_lock_context(ctx);
568ab75bff1STrond Myklebust struct nfs_page *ret;
569ab75bff1STrond Myklebust
570ab75bff1STrond Myklebust if (IS_ERR(l_ctx))
571ab75bff1STrond Myklebust return ERR_CAST(l_ctx);
572ab75bff1STrond Myklebust ret = nfs_page_create(l_ctx, offset, folio_index(folio), offset, count);
573ab75bff1STrond Myklebust if (!IS_ERR(ret)) {
574ab75bff1STrond Myklebust nfs_page_assign_folio(ret, folio);
575ab75bff1STrond Myklebust nfs_page_group_init(ret, NULL);
576ab75bff1STrond Myklebust }
57718eb8842STrond Myklebust nfs_put_lock_context(l_ctx);
5781da177e4SLinus Torvalds return ret;
5791da177e4SLinus Torvalds }
5801da177e4SLinus Torvalds
581c917cfafSTrond Myklebust static struct nfs_page *
nfs_create_subreq(struct nfs_page * req,unsigned int pgbase,unsigned int offset,unsigned int count)58244a65a0cSTrond Myklebust nfs_create_subreq(struct nfs_page *req,
58344a65a0cSTrond Myklebust unsigned int pgbase,
58444a65a0cSTrond Myklebust unsigned int offset,
585c917cfafSTrond Myklebust unsigned int count)
586c917cfafSTrond Myklebust {
58744a65a0cSTrond Myklebust struct nfs_page *last;
588c917cfafSTrond Myklebust struct nfs_page *ret;
589cbefa53cSTrond Myklebust struct folio *folio = nfs_page_to_folio(req);
59035c5db0eSTrond Myklebust struct page *page = nfs_page_to_page(req, pgbase);
591c917cfafSTrond Myklebust
592cbefa53cSTrond Myklebust ret = nfs_page_create(req->wb_lock_context, pgbase, req->wb_index,
593cbefa53cSTrond Myklebust offset, count);
594c917cfafSTrond Myklebust if (!IS_ERR(ret)) {
595cbefa53cSTrond Myklebust if (folio)
596cbefa53cSTrond Myklebust nfs_page_assign_folio(ret, folio);
597cbefa53cSTrond Myklebust else
598cbefa53cSTrond Myklebust nfs_page_assign_page(ret, page);
59944a65a0cSTrond Myklebust /* find the last request */
60044a65a0cSTrond Myklebust for (last = req->wb_head;
60144a65a0cSTrond Myklebust last->wb_this_page != req->wb_head;
60244a65a0cSTrond Myklebust last = last->wb_this_page)
60344a65a0cSTrond Myklebust ;
60444a65a0cSTrond Myklebust
605c917cfafSTrond Myklebust nfs_lock_request(ret);
60628b1d3f5STrond Myklebust nfs_page_group_init(ret, last);
60733344e0fSTrond Myklebust ret->wb_nio = req->wb_nio;
60867911c8fSAnna Schumaker }
609c917cfafSTrond Myklebust return ret;
6101da177e4SLinus Torvalds }
6111da177e4SLinus Torvalds
6121da177e4SLinus Torvalds /**
6131d1afcbcSTrond Myklebust * nfs_unlock_request - Unlock request and wake up sleepers.
614302fad7bSTrond Myklebust * @req: pointer to request
6151da177e4SLinus Torvalds */
nfs_unlock_request(struct nfs_page * req)6161d1afcbcSTrond Myklebust void nfs_unlock_request(struct nfs_page *req)
6171da177e4SLinus Torvalds {
61843d20e80STrond Myklebust clear_bit_unlock(PG_BUSY, &req->wb_flags);
6194e857c58SPeter Zijlstra smp_mb__after_atomic();
620b4f937cfSTrond Myklebust if (!test_bit(PG_CONTENDED2, &req->wb_flags))
621b4f937cfSTrond Myklebust return;
622464a98bdSTrond Myklebust wake_up_bit(&req->wb_flags, PG_BUSY);
6233aff4ebbSTrond Myklebust }
6243aff4ebbSTrond Myklebust
6253aff4ebbSTrond Myklebust /**
6261d1afcbcSTrond Myklebust * nfs_unlock_and_release_request - Unlock request and release the nfs_page
627302fad7bSTrond Myklebust * @req: pointer to request
6283aff4ebbSTrond Myklebust */
nfs_unlock_and_release_request(struct nfs_page * req)6291d1afcbcSTrond Myklebust void nfs_unlock_and_release_request(struct nfs_page *req)
6303aff4ebbSTrond Myklebust {
6311d1afcbcSTrond Myklebust nfs_unlock_request(req);
6321da177e4SLinus Torvalds nfs_release_request(req);
6331da177e4SLinus Torvalds }
6341da177e4SLinus Torvalds
6354d65c520STrond Myklebust /*
6361da177e4SLinus Torvalds * nfs_clear_request - Free up all resources allocated to the request
6371da177e4SLinus Torvalds * @req:
6381da177e4SLinus Torvalds *
639bb6fbc45STrond Myklebust * Release page and open context resources associated with a read/write
640bb6fbc45STrond Myklebust * request after it has completed.
6411da177e4SLinus Torvalds */
nfs_clear_request(struct nfs_page * req)6424d65c520STrond Myklebust static void nfs_clear_request(struct nfs_page *req)
6431da177e4SLinus Torvalds {
644cbefa53cSTrond Myklebust struct folio *folio = nfs_page_to_folio(req);
645cd52ed35STrond Myklebust struct page *page = req->wb_page;
646f11ac8dbSTrond Myklebust struct nfs_lock_context *l_ctx = req->wb_lock_context;
647c79d183eSTrond Myklebust struct nfs_open_context *ctx;
648bb6fbc45STrond Myklebust
649cbefa53cSTrond Myklebust if (folio != NULL) {
650cbefa53cSTrond Myklebust folio_put(folio);
651cbefa53cSTrond Myklebust req->wb_folio = NULL;
652cbefa53cSTrond Myklebust clear_bit(PG_FOLIO, &req->wb_flags);
653cbefa53cSTrond Myklebust } else if (page != NULL) {
65409cbfeafSKirill A. Shutemov put_page(page);
6551da177e4SLinus Torvalds req->wb_page = NULL;
6561da177e4SLinus Torvalds }
657f11ac8dbSTrond Myklebust if (l_ctx != NULL) {
6587d6ddf88SBenjamin Coddington if (atomic_dec_and_test(&l_ctx->io_count)) {
659723c921eSPeter Zijlstra wake_up_var(&l_ctx->io_count);
660c79d183eSTrond Myklebust ctx = l_ctx->open_context;
6617d6ddf88SBenjamin Coddington if (test_bit(NFS_CONTEXT_UNLOCK, &ctx->flags))
6627d6ddf88SBenjamin Coddington rpc_wake_up(&NFS_SERVER(d_inode(ctx->dentry))->uoc_rpcwaitq);
6637d6ddf88SBenjamin Coddington }
664f11ac8dbSTrond Myklebust nfs_put_lock_context(l_ctx);
665f11ac8dbSTrond Myklebust req->wb_lock_context = NULL;
666f11ac8dbSTrond Myklebust }
6671da177e4SLinus Torvalds }
6681da177e4SLinus Torvalds
6691da177e4SLinus Torvalds /**
6706453bcd0STrond Myklebust * nfs_free_request - Release the count on an NFS read/write request
6711da177e4SLinus Torvalds * @req: request to release
6721da177e4SLinus Torvalds *
6731da177e4SLinus Torvalds * Note: Should never be called with the spinlock held!
6741da177e4SLinus Torvalds */
nfs_free_request(struct nfs_page * req)675d4581383SWeston Andros Adamson void nfs_free_request(struct nfs_page *req)
6761da177e4SLinus Torvalds {
6772bfc6e56SWeston Andros Adamson WARN_ON_ONCE(req->wb_this_page != req);
6782bfc6e56SWeston Andros Adamson
6792bfc6e56SWeston Andros Adamson /* extra debug: make sure no sync bits are still set */
6802bfc6e56SWeston Andros Adamson WARN_ON_ONCE(test_bit(PG_TEARDOWN, &req->wb_flags));
68167d0338eSWeston Andros Adamson WARN_ON_ONCE(test_bit(PG_UNLOCKPAGE, &req->wb_flags));
68267d0338eSWeston Andros Adamson WARN_ON_ONCE(test_bit(PG_UPTODATE, &req->wb_flags));
68320633f04SWeston Andros Adamson WARN_ON_ONCE(test_bit(PG_WB_END, &req->wb_flags));
68420633f04SWeston Andros Adamson WARN_ON_ONCE(test_bit(PG_REMOVE, &req->wb_flags));
6851da177e4SLinus Torvalds
686bb6fbc45STrond Myklebust /* Release struct file and open context */
6871da177e4SLinus Torvalds nfs_clear_request(req);
6881da177e4SLinus Torvalds nfs_page_free(req);
6891da177e4SLinus Torvalds }
6901da177e4SLinus Torvalds
nfs_release_request(struct nfs_page * req)691c03b4024STrond Myklebust void nfs_release_request(struct nfs_page *req)
692c03b4024STrond Myklebust {
6932bfc6e56SWeston Andros Adamson kref_put(&req->wb_kref, nfs_page_group_destroy);
6949f557cd8STrond Myklebust }
6952ce209c4STrond Myklebust EXPORT_SYMBOL_GPL(nfs_release_request);
6969f557cd8STrond Myklebust
6971da177e4SLinus Torvalds /**
6981da177e4SLinus Torvalds * nfs_wait_on_request - Wait for a request to complete.
6991da177e4SLinus Torvalds * @req: request to wait upon.
7001da177e4SLinus Torvalds *
701150030b7SMatthew Wilcox * Interruptible by fatal signals only.
7021da177e4SLinus Torvalds * The user is responsible for holding a count on the request.
7031da177e4SLinus Torvalds */
7041da177e4SLinus Torvalds int
nfs_wait_on_request(struct nfs_page * req)7051da177e4SLinus Torvalds nfs_wait_on_request(struct nfs_page *req)
7061da177e4SLinus Torvalds {
707b4f937cfSTrond Myklebust if (!test_bit(PG_BUSY, &req->wb_flags))
708b4f937cfSTrond Myklebust return 0;
709b4f937cfSTrond Myklebust set_bit(PG_CONTENDED2, &req->wb_flags);
710b4f937cfSTrond Myklebust smp_mb__after_atomic();
71174316201SNeilBrown return wait_on_bit_io(&req->wb_flags, PG_BUSY,
7129f557cd8STrond Myklebust TASK_UNINTERRUPTIBLE);
7131da177e4SLinus Torvalds }
7142ce209c4STrond Myklebust EXPORT_SYMBOL_GPL(nfs_wait_on_request);
7151da177e4SLinus Torvalds
7165b36c7dcSBoaz Harrosh /*
717b4fdac1aSWeston Andros Adamson * nfs_generic_pg_test - determine if requests can be coalesced
718b4fdac1aSWeston Andros Adamson * @desc: pointer to descriptor
719b4fdac1aSWeston Andros Adamson * @prev: previous request in desc, or NULL
720b4fdac1aSWeston Andros Adamson * @req: this request
721b4fdac1aSWeston Andros Adamson *
722ac0aa5e8SPavel Tikhomirov * Returns zero if @req cannot be coalesced into @desc, otherwise it returns
723b4fdac1aSWeston Andros Adamson * the size of the request.
7245b36c7dcSBoaz Harrosh */
nfs_generic_pg_test(struct nfs_pageio_descriptor * desc,struct nfs_page * prev,struct nfs_page * req)725b4fdac1aSWeston Andros Adamson size_t nfs_generic_pg_test(struct nfs_pageio_descriptor *desc,
726b4fdac1aSWeston Andros Adamson struct nfs_page *prev, struct nfs_page *req)
7275b36c7dcSBoaz Harrosh {
72848d635f1SPeng Tao struct nfs_pgio_mirror *mirror = nfs_pgio_current_mirror(desc);
729a7d42ddbSWeston Andros Adamson
730a7d42ddbSWeston Andros Adamson
731a7d42ddbSWeston Andros Adamson if (mirror->pg_count > mirror->pg_bsize) {
732f0cb9ab8SWeston Andros Adamson /* should never happen */
733f0cb9ab8SWeston Andros Adamson WARN_ON_ONCE(1);
7345b36c7dcSBoaz Harrosh return 0;
735f0cb9ab8SWeston Andros Adamson }
7365b36c7dcSBoaz Harrosh
7372e11f829SChristoph Hellwig /*
7382e11f829SChristoph Hellwig * Limit the request size so that we can still allocate a page array
7392e11f829SChristoph Hellwig * for it without upsetting the slab allocator.
7402e11f829SChristoph Hellwig */
741a7d42ddbSWeston Andros Adamson if (((mirror->pg_count + req->wb_bytes) >> PAGE_SHIFT) *
742048883e0SPeng Tao sizeof(struct page *) > PAGE_SIZE)
7432e11f829SChristoph Hellwig return 0;
7442e11f829SChristoph Hellwig
745a7d42ddbSWeston Andros Adamson return min(mirror->pg_bsize - mirror->pg_count, (size_t)req->wb_bytes);
7465b36c7dcSBoaz Harrosh }
74719345cb2SBenny Halevy EXPORT_SYMBOL_GPL(nfs_generic_pg_test);
7485b36c7dcSBoaz Harrosh
nfs_pgio_header_alloc(const struct nfs_rw_ops * ops)7491e7f3a48SWeston Andros Adamson struct nfs_pgio_header *nfs_pgio_header_alloc(const struct nfs_rw_ops *ops)
75000bfa30aSAnna Schumaker {
7511e7f3a48SWeston Andros Adamson struct nfs_pgio_header *hdr = ops->rw_alloc_header();
75200bfa30aSAnna Schumaker
7531e7f3a48SWeston Andros Adamson if (hdr) {
7544a0de55cSAnna Schumaker INIT_LIST_HEAD(&hdr->pages);
7554a0de55cSAnna Schumaker hdr->rw_ops = ops;
7564a0de55cSAnna Schumaker }
7571e7f3a48SWeston Andros Adamson return hdr;
7584a0de55cSAnna Schumaker }
7591e7f3a48SWeston Andros Adamson EXPORT_SYMBOL_GPL(nfs_pgio_header_alloc);
7604a0de55cSAnna Schumaker
7614a0de55cSAnna Schumaker /**
7624714fb51SWeston Andros Adamson * nfs_pgio_data_destroy - make @hdr suitable for reuse
7634714fb51SWeston Andros Adamson *
7644714fb51SWeston Andros Adamson * Frees memory and releases refs from nfs_generic_pgio, so that it may
7654714fb51SWeston Andros Adamson * be called again.
7664714fb51SWeston Andros Adamson *
7674714fb51SWeston Andros Adamson * @hdr: A header that has had nfs_generic_pgio called
76800bfa30aSAnna Schumaker */
nfs_pgio_data_destroy(struct nfs_pgio_header * hdr)769196639ebSTrond Myklebust static void nfs_pgio_data_destroy(struct nfs_pgio_header *hdr)
77000bfa30aSAnna Schumaker {
7713caa0c6eSTrond Myklebust if (hdr->args.context)
772d45f60c6SWeston Andros Adamson put_nfs_open_context(hdr->args.context);
773d45f60c6SWeston Andros Adamson if (hdr->page_array.pagevec != hdr->page_array.page_array)
774d45f60c6SWeston Andros Adamson kfree(hdr->page_array.pagevec);
77500bfa30aSAnna Schumaker }
776196639ebSTrond Myklebust
777196639ebSTrond Myklebust /*
778196639ebSTrond Myklebust * nfs_pgio_header_free - Free a read or write header
779196639ebSTrond Myklebust * @hdr: The header to free
780196639ebSTrond Myklebust */
nfs_pgio_header_free(struct nfs_pgio_header * hdr)781196639ebSTrond Myklebust void nfs_pgio_header_free(struct nfs_pgio_header *hdr)
782196639ebSTrond Myklebust {
783196639ebSTrond Myklebust nfs_pgio_data_destroy(hdr);
784196639ebSTrond Myklebust hdr->rw_ops->rw_free_header(hdr);
785196639ebSTrond Myklebust }
786196639ebSTrond Myklebust EXPORT_SYMBOL_GPL(nfs_pgio_header_free);
78700bfa30aSAnna Schumaker
7881da177e4SLinus Torvalds /**
789ce59515cSAnna Schumaker * nfs_pgio_rpcsetup - Set up arguments for a pageio call
790d45f60c6SWeston Andros Adamson * @hdr: The pageio hdr
791eb9f2a5aSTrond Myklebust * @pgbase: base
792ce59515cSAnna Schumaker * @count: Number of bytes to read
793ce59515cSAnna Schumaker * @how: How to commit data (writes only)
794ce59515cSAnna Schumaker * @cinfo: Commit information for the call (writes only)
795ce59515cSAnna Schumaker */
nfs_pgio_rpcsetup(struct nfs_pgio_header * hdr,unsigned int pgbase,unsigned int count,int how,struct nfs_commit_info * cinfo)796eb9f2a5aSTrond Myklebust static void nfs_pgio_rpcsetup(struct nfs_pgio_header *hdr, unsigned int pgbase,
797eb9f2a5aSTrond Myklebust unsigned int count, int how,
798eb9f2a5aSTrond Myklebust struct nfs_commit_info *cinfo)
799ce59515cSAnna Schumaker {
800d45f60c6SWeston Andros Adamson struct nfs_page *req = hdr->req;
801ce59515cSAnna Schumaker
802ce59515cSAnna Schumaker /* Set up the RPC argument and reply structs
803d45f60c6SWeston Andros Adamson * NB: take care not to mess about with hdr->commit et al. */
804ce59515cSAnna Schumaker
805d45f60c6SWeston Andros Adamson hdr->args.fh = NFS_FH(hdr->inode);
806e545735aSBenjamin Coddington hdr->args.offset = req_offset(req);
807ce59515cSAnna Schumaker /* pnfs_set_layoutcommit needs this */
808d45f60c6SWeston Andros Adamson hdr->mds_offset = hdr->args.offset;
809eb9f2a5aSTrond Myklebust hdr->args.pgbase = pgbase;
810d45f60c6SWeston Andros Adamson hdr->args.pages = hdr->page_array.pagevec;
811d45f60c6SWeston Andros Adamson hdr->args.count = count;
8129fcd5960STrond Myklebust hdr->args.context = get_nfs_open_context(nfs_req_openctx(req));
813d45f60c6SWeston Andros Adamson hdr->args.lock_context = req->wb_lock_context;
814d45f60c6SWeston Andros Adamson hdr->args.stable = NFS_UNSTABLE;
815ce59515cSAnna Schumaker switch (how & (FLUSH_STABLE | FLUSH_COND_STABLE)) {
816ce59515cSAnna Schumaker case 0:
817ce59515cSAnna Schumaker break;
818ce59515cSAnna Schumaker case FLUSH_COND_STABLE:
819ce59515cSAnna Schumaker if (nfs_reqs_to_commit(cinfo))
820ce59515cSAnna Schumaker break;
821df561f66SGustavo A. R. Silva fallthrough;
822ce59515cSAnna Schumaker default:
823d45f60c6SWeston Andros Adamson hdr->args.stable = NFS_FILE_SYNC;
824ce59515cSAnna Schumaker }
825ce59515cSAnna Schumaker
826d45f60c6SWeston Andros Adamson hdr->res.fattr = &hdr->fattr;
82717d8c5d1STrond Myklebust hdr->res.count = 0;
828d45f60c6SWeston Andros Adamson hdr->res.eof = 0;
829c65e6254SWeston Andros Adamson hdr->res.verf = &hdr->verf;
830d45f60c6SWeston Andros Adamson nfs_fattr_init(&hdr->fattr);
831ce59515cSAnna Schumaker }
832ce59515cSAnna Schumaker
833ce59515cSAnna Schumaker /**
834d45f60c6SWeston Andros Adamson * nfs_pgio_prepare - Prepare pageio hdr to go over the wire
835a4cdda59SAnna Schumaker * @task: The current task
836d45f60c6SWeston Andros Adamson * @calldata: pageio header to prepare
837a4cdda59SAnna Schumaker */
nfs_pgio_prepare(struct rpc_task * task,void * calldata)8386f92fa45SAnna Schumaker static void nfs_pgio_prepare(struct rpc_task *task, void *calldata)
839a4cdda59SAnna Schumaker {
840d45f60c6SWeston Andros Adamson struct nfs_pgio_header *hdr = calldata;
841a4cdda59SAnna Schumaker int err;
842d45f60c6SWeston Andros Adamson err = NFS_PROTO(hdr->inode)->pgio_rpc_prepare(task, hdr);
843a4cdda59SAnna Schumaker if (err)
844a4cdda59SAnna Schumaker rpc_exit(task, err);
845a4cdda59SAnna Schumaker }
846a4cdda59SAnna Schumaker
nfs_initiate_pgio(struct rpc_clnt * clnt,struct nfs_pgio_header * hdr,const struct cred * cred,const struct nfs_rpc_ops * rpc_ops,const struct rpc_call_ops * call_ops,int how,int flags)847d45f60c6SWeston Andros Adamson int nfs_initiate_pgio(struct rpc_clnt *clnt, struct nfs_pgio_header *hdr,
848a52458b4SNeilBrown const struct cred *cred, const struct nfs_rpc_ops *rpc_ops,
8491ed26f33SAnna Schumaker const struct rpc_call_ops *call_ops, int how, int flags)
8501ed26f33SAnna Schumaker {
8511ed26f33SAnna Schumaker struct rpc_task *task;
8521ed26f33SAnna Schumaker struct rpc_message msg = {
853d45f60c6SWeston Andros Adamson .rpc_argp = &hdr->args,
854d45f60c6SWeston Andros Adamson .rpc_resp = &hdr->res,
85546a5ab47SPeng Tao .rpc_cred = cred,
8561ed26f33SAnna Schumaker };
8571ed26f33SAnna Schumaker struct rpc_task_setup task_setup_data = {
8581ed26f33SAnna Schumaker .rpc_client = clnt,
859d45f60c6SWeston Andros Adamson .task = &hdr->task,
8601ed26f33SAnna Schumaker .rpc_message = &msg,
8611ed26f33SAnna Schumaker .callback_ops = call_ops,
862d45f60c6SWeston Andros Adamson .callback_data = hdr,
8631ed26f33SAnna Schumaker .workqueue = nfsiod_workqueue,
8644fa7ef69STrond Myklebust .flags = RPC_TASK_ASYNC | flags,
8651ed26f33SAnna Schumaker };
8661ed26f33SAnna Schumaker
867118f09edSOlga Kornievskaia if (nfs_server_capable(hdr->inode, NFS_CAP_MOVEABLE))
868118f09edSOlga Kornievskaia task_setup_data.flags |= RPC_TASK_MOVEABLE;
869118f09edSOlga Kornievskaia
870abde71f4STom Haynes hdr->rw_ops->rw_initiate(hdr, &msg, rpc_ops, &task_setup_data, how);
8711ed26f33SAnna Schumaker
872b4839ebeSKinglong Mee dprintk("NFS: initiated pgio call "
8731ed26f33SAnna Schumaker "(req %s/%llu, %u bytes @ offset %llu)\n",
874343ae531SAnna Schumaker hdr->inode->i_sb->s_id,
875343ae531SAnna Schumaker (unsigned long long)NFS_FILEID(hdr->inode),
876d45f60c6SWeston Andros Adamson hdr->args.count,
877d45f60c6SWeston Andros Adamson (unsigned long long)hdr->args.offset);
8781ed26f33SAnna Schumaker
8791ed26f33SAnna Schumaker task = rpc_run_task(&task_setup_data);
8801de3af98STrond Myklebust if (IS_ERR(task))
8811de3af98STrond Myklebust return PTR_ERR(task);
8821ed26f33SAnna Schumaker rpc_put_task(task);
8831de3af98STrond Myklebust return 0;
8841ed26f33SAnna Schumaker }
8851ed26f33SAnna Schumaker EXPORT_SYMBOL_GPL(nfs_initiate_pgio);
8861ed26f33SAnna Schumaker
887a4cdda59SAnna Schumaker /**
888844c9e69SAnna Schumaker * nfs_pgio_error - Clean up from a pageio error
889844c9e69SAnna Schumaker * @hdr: pageio header
890844c9e69SAnna Schumaker */
nfs_pgio_error(struct nfs_pgio_header * hdr)8912bff2288SPeng Tao static void nfs_pgio_error(struct nfs_pgio_header *hdr)
892844c9e69SAnna Schumaker {
893844c9e69SAnna Schumaker set_bit(NFS_IOHDR_REDO, &hdr->flags);
8944714fb51SWeston Andros Adamson hdr->completion_ops->completion(hdr);
895844c9e69SAnna Schumaker }
896844c9e69SAnna Schumaker
897844c9e69SAnna Schumaker /**
898a4cdda59SAnna Schumaker * nfs_pgio_release - Release pageio data
899d45f60c6SWeston Andros Adamson * @calldata: The pageio header to release
900a4cdda59SAnna Schumaker */
nfs_pgio_release(void * calldata)9016f92fa45SAnna Schumaker static void nfs_pgio_release(void *calldata)
902a4cdda59SAnna Schumaker {
903d45f60c6SWeston Andros Adamson struct nfs_pgio_header *hdr = calldata;
9044714fb51SWeston Andros Adamson hdr->completion_ops->completion(hdr);
905a4cdda59SAnna Schumaker }
906a4cdda59SAnna Schumaker
nfs_pageio_mirror_init(struct nfs_pgio_mirror * mirror,unsigned int bsize)907a7d42ddbSWeston Andros Adamson static void nfs_pageio_mirror_init(struct nfs_pgio_mirror *mirror,
908a7d42ddbSWeston Andros Adamson unsigned int bsize)
909a7d42ddbSWeston Andros Adamson {
910a7d42ddbSWeston Andros Adamson INIT_LIST_HEAD(&mirror->pg_list);
911a7d42ddbSWeston Andros Adamson mirror->pg_bytes_written = 0;
912a7d42ddbSWeston Andros Adamson mirror->pg_count = 0;
913a7d42ddbSWeston Andros Adamson mirror->pg_bsize = bsize;
914a7d42ddbSWeston Andros Adamson mirror->pg_base = 0;
915a7d42ddbSWeston Andros Adamson mirror->pg_recoalesce = 0;
916a7d42ddbSWeston Andros Adamson }
917a7d42ddbSWeston Andros Adamson
9181da177e4SLinus Torvalds /**
919d8a5ad75STrond Myklebust * nfs_pageio_init - initialise a page io descriptor
920d8a5ad75STrond Myklebust * @desc: pointer to descriptor
921bcb71bbaSTrond Myklebust * @inode: pointer to inode
922dfad7000SYijing Wang * @pg_ops: pointer to pageio operations
923dfad7000SYijing Wang * @compl_ops: pointer to pageio completion operations
924dfad7000SYijing Wang * @rw_ops: pointer to nfs read/write operations
925bcb71bbaSTrond Myklebust * @bsize: io block size
926bcb71bbaSTrond Myklebust * @io_flags: extra parameters for the io function
927d8a5ad75STrond Myklebust */
nfs_pageio_init(struct nfs_pageio_descriptor * desc,struct inode * inode,const struct nfs_pageio_ops * pg_ops,const struct nfs_pgio_completion_ops * compl_ops,const struct nfs_rw_ops * rw_ops,size_t bsize,int io_flags)928bcb71bbaSTrond Myklebust void nfs_pageio_init(struct nfs_pageio_descriptor *desc,
929bcb71bbaSTrond Myklebust struct inode *inode,
9301751c363STrond Myklebust const struct nfs_pageio_ops *pg_ops,
931061ae2edSFred Isaman const struct nfs_pgio_completion_ops *compl_ops,
9324a0de55cSAnna Schumaker const struct nfs_rw_ops *rw_ops,
93384dde76cSTrond Myklebust size_t bsize,
9343bde7afdSTrond Myklebust int io_flags)
935d8a5ad75STrond Myklebust {
936b31268acSTrond Myklebust desc->pg_moreio = 0;
937bcb71bbaSTrond Myklebust desc->pg_inode = inode;
9381751c363STrond Myklebust desc->pg_ops = pg_ops;
939061ae2edSFred Isaman desc->pg_completion_ops = compl_ops;
9404a0de55cSAnna Schumaker desc->pg_rw_ops = rw_ops;
941bcb71bbaSTrond Myklebust desc->pg_ioflags = io_flags;
942bcb71bbaSTrond Myklebust desc->pg_error = 0;
94394ad1c80SFred Isaman desc->pg_lseg = NULL;
944919e3bd9STrond Myklebust desc->pg_io_completion = NULL;
945584aa810SFred Isaman desc->pg_dreq = NULL;
946000dbe0bSDave Wysochanski nfs_netfs_reset_pageio_descriptor(desc);
947a7d42ddbSWeston Andros Adamson desc->pg_bsize = bsize;
948a7d42ddbSWeston Andros Adamson
949a7d42ddbSWeston Andros Adamson desc->pg_mirror_count = 1;
950a7d42ddbSWeston Andros Adamson desc->pg_mirror_idx = 0;
951a7d42ddbSWeston Andros Adamson
952a7d42ddbSWeston Andros Adamson desc->pg_mirrors_dynamic = NULL;
953a7d42ddbSWeston Andros Adamson desc->pg_mirrors = desc->pg_mirrors_static;
954a7d42ddbSWeston Andros Adamson nfs_pageio_mirror_init(&desc->pg_mirrors[0], bsize);
95533344e0fSTrond Myklebust desc->pg_maxretrans = 0;
956a7d42ddbSWeston Andros Adamson }
957d8a5ad75STrond Myklebust
9580eecb214SAnna Schumaker /**
9590eecb214SAnna Schumaker * nfs_pgio_result - Basic pageio error handling
9600eecb214SAnna Schumaker * @task: The task that ran
961d45f60c6SWeston Andros Adamson * @calldata: Pageio header to check
9620eecb214SAnna Schumaker */
nfs_pgio_result(struct rpc_task * task,void * calldata)9636f92fa45SAnna Schumaker static void nfs_pgio_result(struct rpc_task *task, void *calldata)
9640eecb214SAnna Schumaker {
965d45f60c6SWeston Andros Adamson struct nfs_pgio_header *hdr = calldata;
966d45f60c6SWeston Andros Adamson struct inode *inode = hdr->inode;
9670eecb214SAnna Schumaker
968d45f60c6SWeston Andros Adamson if (hdr->rw_ops->rw_done(task, hdr, inode) != 0)
9690eecb214SAnna Schumaker return;
9700eecb214SAnna Schumaker if (task->tk_status < 0)
971d45f60c6SWeston Andros Adamson nfs_set_pgio_error(hdr, task->tk_status, hdr->args.offset);
9720eecb214SAnna Schumaker else
973d45f60c6SWeston Andros Adamson hdr->rw_ops->rw_result(task, hdr);
9740eecb214SAnna Schumaker }
9750eecb214SAnna Schumaker
976ef2c488cSAnna Schumaker /*
977ef2c488cSAnna Schumaker * Create an RPC task for the given read or write request and kick it.
978ef2c488cSAnna Schumaker * The page must have been locked by the caller.
979ef2c488cSAnna Schumaker *
980ef2c488cSAnna Schumaker * It may happen that the page we're passed is not marked dirty.
981ef2c488cSAnna Schumaker * This is the case if nfs_updatepage detects a conflicting request
982ef2c488cSAnna Schumaker * that has been written but not committed.
983ef2c488cSAnna Schumaker */
nfs_generic_pgio(struct nfs_pageio_descriptor * desc,struct nfs_pgio_header * hdr)984f0cb9ab8SWeston Andros Adamson int nfs_generic_pgio(struct nfs_pageio_descriptor *desc,
985ef2c488cSAnna Schumaker struct nfs_pgio_header *hdr)
986ef2c488cSAnna Schumaker {
98748d635f1SPeng Tao struct nfs_pgio_mirror *mirror = nfs_pgio_current_mirror(desc);
988a7d42ddbSWeston Andros Adamson
989ef2c488cSAnna Schumaker struct nfs_page *req;
990bba5c188SWeston Andros Adamson struct page **pages,
991bba5c188SWeston Andros Adamson *last_page;
992a7d42ddbSWeston Andros Adamson struct list_head *head = &mirror->pg_list;
993ef2c488cSAnna Schumaker struct nfs_commit_info cinfo;
9948ef9b0b9SBenjamin Coddington struct nfs_page_array *pg_array = &hdr->page_array;
995bba5c188SWeston Andros Adamson unsigned int pagecount, pageused;
996eb9f2a5aSTrond Myklebust unsigned int pg_base = offset_in_page(mirror->pg_base);
9970bae835bSTrond Myklebust gfp_t gfp_flags = nfs_io_gfp_mask();
998ef2c488cSAnna Schumaker
999eb9f2a5aSTrond Myklebust pagecount = nfs_page_array_len(pg_base, mirror->pg_count);
10002eb3aea7SBenjamin Coddington pg_array->npages = pagecount;
10018ef9b0b9SBenjamin Coddington
10028ef9b0b9SBenjamin Coddington if (pagecount <= ARRAY_SIZE(pg_array->page_array))
10038ef9b0b9SBenjamin Coddington pg_array->pagevec = pg_array->page_array;
10048ef9b0b9SBenjamin Coddington else {
10058ef9b0b9SBenjamin Coddington pg_array->pagevec = kcalloc(pagecount, sizeof(struct page *), gfp_flags);
10068ef9b0b9SBenjamin Coddington if (!pg_array->pagevec) {
10078ef9b0b9SBenjamin Coddington pg_array->npages = 0;
10082bff2288SPeng Tao nfs_pgio_error(hdr);
10092bff2288SPeng Tao desc->pg_error = -ENOMEM;
10102bff2288SPeng Tao return desc->pg_error;
10112bff2288SPeng Tao }
10128ef9b0b9SBenjamin Coddington }
1013ef2c488cSAnna Schumaker
1014ef2c488cSAnna Schumaker nfs_init_cinfo(&cinfo, desc->pg_inode, desc->pg_dreq);
1015d45f60c6SWeston Andros Adamson pages = hdr->page_array.pagevec;
1016bba5c188SWeston Andros Adamson last_page = NULL;
1017bba5c188SWeston Andros Adamson pageused = 0;
1018ef2c488cSAnna Schumaker while (!list_empty(head)) {
1019eb9f2a5aSTrond Myklebust struct nfs_page_iter_page i;
1020eb9f2a5aSTrond Myklebust struct page *page;
1021eb9f2a5aSTrond Myklebust
1022ef2c488cSAnna Schumaker req = nfs_list_entry(head->next);
1023078b5fd9STrond Myklebust nfs_list_move_request(req, &hdr->pages);
1024bba5c188SWeston Andros Adamson
1025785207aaSTrond Myklebust if (req->wb_pgbase == 0)
1026785207aaSTrond Myklebust last_page = NULL;
1027785207aaSTrond Myklebust
1028eb9f2a5aSTrond Myklebust nfs_page_iter_page_init(&i, req);
1029eb9f2a5aSTrond Myklebust while ((page = nfs_page_iter_page_get(&i)) != NULL) {
1030eb9f2a5aSTrond Myklebust if (last_page != page) {
1031bba5c188SWeston Andros Adamson pageused++;
1032b8fb9c30STrond Myklebust if (pageused > pagecount)
1033eb9f2a5aSTrond Myklebust goto full;
1034eb9f2a5aSTrond Myklebust *pages++ = last_page = page;
1035ef2c488cSAnna Schumaker }
1036bba5c188SWeston Andros Adamson }
1037eb9f2a5aSTrond Myklebust }
1038eb9f2a5aSTrond Myklebust full:
10392bff2288SPeng Tao if (WARN_ON_ONCE(pageused != pagecount)) {
10402bff2288SPeng Tao nfs_pgio_error(hdr);
10412bff2288SPeng Tao desc->pg_error = -EINVAL;
10422bff2288SPeng Tao return desc->pg_error;
10432bff2288SPeng Tao }
1044ef2c488cSAnna Schumaker
1045ef2c488cSAnna Schumaker if ((desc->pg_ioflags & FLUSH_COND_STABLE) &&
1046ef2c488cSAnna Schumaker (desc->pg_moreio || nfs_reqs_to_commit(&cinfo)))
1047ef2c488cSAnna Schumaker desc->pg_ioflags &= ~FLUSH_COND_STABLE;
1048ef2c488cSAnna Schumaker
1049ef2c488cSAnna Schumaker /* Set up the argument struct */
1050eb9f2a5aSTrond Myklebust nfs_pgio_rpcsetup(hdr, pg_base, mirror->pg_count, desc->pg_ioflags,
1051eb9f2a5aSTrond Myklebust &cinfo);
1052ef2c488cSAnna Schumaker desc->pg_rpc_callops = &nfs_pgio_common_ops;
1053ef2c488cSAnna Schumaker return 0;
1054ef2c488cSAnna Schumaker }
1055f0cb9ab8SWeston Andros Adamson EXPORT_SYMBOL_GPL(nfs_generic_pgio);
1056ef2c488cSAnna Schumaker
nfs_generic_pg_pgios(struct nfs_pageio_descriptor * desc)105741d8d5b7SAnna Schumaker static int nfs_generic_pg_pgios(struct nfs_pageio_descriptor *desc)
1058cf485fcdSAnna Schumaker {
1059cf485fcdSAnna Schumaker struct nfs_pgio_header *hdr;
1060cf485fcdSAnna Schumaker int ret;
106185e39feeSOlga Kornievskaia unsigned short task_flags = 0;
1062cf485fcdSAnna Schumaker
10631e7f3a48SWeston Andros Adamson hdr = nfs_pgio_header_alloc(desc->pg_rw_ops);
10641e7f3a48SWeston Andros Adamson if (!hdr) {
10652bff2288SPeng Tao desc->pg_error = -ENOMEM;
10662bff2288SPeng Tao return desc->pg_error;
1067cf485fcdSAnna Schumaker }
10681e7f3a48SWeston Andros Adamson nfs_pgheader_init(desc, hdr, nfs_pgio_header_free);
1069cf485fcdSAnna Schumaker ret = nfs_generic_pgio(desc, hdr);
107085e39feeSOlga Kornievskaia if (ret == 0) {
107185e39feeSOlga Kornievskaia if (NFS_SERVER(hdr->inode)->nfs_client->cl_minorversion)
107285e39feeSOlga Kornievskaia task_flags = RPC_TASK_MOVEABLE;
10737f714720SWeston Andros Adamson ret = nfs_initiate_pgio(NFS_CLIENT(hdr->inode),
107446a5ab47SPeng Tao hdr,
107546a5ab47SPeng Tao hdr->cred,
107646a5ab47SPeng Tao NFS_PROTO(hdr->inode),
1077abde71f4STom Haynes desc->pg_rpc_callops,
10784fa7ef69STrond Myklebust desc->pg_ioflags,
107985e39feeSOlga Kornievskaia RPC_TASK_CRED_NOREF | task_flags);
108085e39feeSOlga Kornievskaia }
1081cf485fcdSAnna Schumaker return ret;
1082cf485fcdSAnna Schumaker }
1083cf485fcdSAnna Schumaker
108414abcb0bSTrond Myklebust static struct nfs_pgio_mirror *
nfs_pageio_alloc_mirrors(struct nfs_pageio_descriptor * desc,unsigned int mirror_count)108514abcb0bSTrond Myklebust nfs_pageio_alloc_mirrors(struct nfs_pageio_descriptor *desc,
108614abcb0bSTrond Myklebust unsigned int mirror_count)
108714abcb0bSTrond Myklebust {
108814abcb0bSTrond Myklebust struct nfs_pgio_mirror *ret;
108914abcb0bSTrond Myklebust unsigned int i;
109014abcb0bSTrond Myklebust
109114abcb0bSTrond Myklebust kfree(desc->pg_mirrors_dynamic);
109214abcb0bSTrond Myklebust desc->pg_mirrors_dynamic = NULL;
109314abcb0bSTrond Myklebust if (mirror_count == 1)
109414abcb0bSTrond Myklebust return desc->pg_mirrors_static;
10950bae835bSTrond Myklebust ret = kmalloc_array(mirror_count, sizeof(*ret), nfs_io_gfp_mask());
109614abcb0bSTrond Myklebust if (ret != NULL) {
109714abcb0bSTrond Myklebust for (i = 0; i < mirror_count; i++)
109814abcb0bSTrond Myklebust nfs_pageio_mirror_init(&ret[i], desc->pg_bsize);
109914abcb0bSTrond Myklebust desc->pg_mirrors_dynamic = ret;
110014abcb0bSTrond Myklebust }
110114abcb0bSTrond Myklebust return ret;
110214abcb0bSTrond Myklebust }
110314abcb0bSTrond Myklebust
1104a7d42ddbSWeston Andros Adamson /*
1105a7d42ddbSWeston Andros Adamson * nfs_pageio_setup_mirroring - determine if mirroring is to be used
1106a7d42ddbSWeston Andros Adamson * by calling the pg_get_mirror_count op
1107a7d42ddbSWeston Andros Adamson */
nfs_pageio_setup_mirroring(struct nfs_pageio_descriptor * pgio,struct nfs_page * req)110814abcb0bSTrond Myklebust static void nfs_pageio_setup_mirroring(struct nfs_pageio_descriptor *pgio,
1109a7d42ddbSWeston Andros Adamson struct nfs_page *req)
1110a7d42ddbSWeston Andros Adamson {
111114abcb0bSTrond Myklebust unsigned int mirror_count = 1;
1112a7d42ddbSWeston Andros Adamson
111314abcb0bSTrond Myklebust if (pgio->pg_ops->pg_get_mirror_count)
1114a7d42ddbSWeston Andros Adamson mirror_count = pgio->pg_ops->pg_get_mirror_count(pgio, req);
111514abcb0bSTrond Myklebust if (mirror_count == pgio->pg_mirror_count || pgio->pg_error < 0)
111614abcb0bSTrond Myklebust return;
1117a7d42ddbSWeston Andros Adamson
111814abcb0bSTrond Myklebust if (!mirror_count || mirror_count > NFS_PAGEIO_DESCRIPTOR_MIRROR_MAX) {
111914abcb0bSTrond Myklebust pgio->pg_error = -EINVAL;
112014abcb0bSTrond Myklebust return;
112114abcb0bSTrond Myklebust }
1122d600ad1fSPeng Tao
112314abcb0bSTrond Myklebust pgio->pg_mirrors = nfs_pageio_alloc_mirrors(pgio, mirror_count);
112414abcb0bSTrond Myklebust if (pgio->pg_mirrors == NULL) {
112514abcb0bSTrond Myklebust pgio->pg_error = -ENOMEM;
112614abcb0bSTrond Myklebust pgio->pg_mirrors = pgio->pg_mirrors_static;
112714abcb0bSTrond Myklebust mirror_count = 1;
112814abcb0bSTrond Myklebust }
1129a7d42ddbSWeston Andros Adamson pgio->pg_mirror_count = mirror_count;
1130a7d42ddbSWeston Andros Adamson }
1131a7d42ddbSWeston Andros Adamson
nfs_pageio_cleanup_mirroring(struct nfs_pageio_descriptor * pgio)1132a7d42ddbSWeston Andros Adamson static void nfs_pageio_cleanup_mirroring(struct nfs_pageio_descriptor *pgio)
1133a7d42ddbSWeston Andros Adamson {
1134a7d42ddbSWeston Andros Adamson pgio->pg_mirror_count = 1;
1135a7d42ddbSWeston Andros Adamson pgio->pg_mirror_idx = 0;
1136a7d42ddbSWeston Andros Adamson pgio->pg_mirrors = pgio->pg_mirrors_static;
1137a7d42ddbSWeston Andros Adamson kfree(pgio->pg_mirrors_dynamic);
1138a7d42ddbSWeston Andros Adamson pgio->pg_mirrors_dynamic = NULL;
1139a7d42ddbSWeston Andros Adamson }
1140a7d42ddbSWeston Andros Adamson
nfs_match_lock_context(const struct nfs_lock_context * l1,const struct nfs_lock_context * l2)11414109bb74STrond Myklebust static bool nfs_match_lock_context(const struct nfs_lock_context *l1,
11424109bb74STrond Myklebust const struct nfs_lock_context *l2)
11434109bb74STrond Myklebust {
1144d51fdb87SNeilBrown return l1->lockowner == l2->lockowner;
11454109bb74STrond Myklebust }
11464109bb74STrond Myklebust
nfs_page_is_contiguous(const struct nfs_page * prev,const struct nfs_page * req)11478e0bdc70STrond Myklebust static bool nfs_page_is_contiguous(const struct nfs_page *prev,
11488e0bdc70STrond Myklebust const struct nfs_page *req)
11498e0bdc70STrond Myklebust {
11508e0bdc70STrond Myklebust size_t prev_end = prev->wb_pgbase + prev->wb_bytes;
11518e0bdc70STrond Myklebust
11528e0bdc70STrond Myklebust if (req_offset(req) != req_offset(prev) + prev->wb_bytes)
11538e0bdc70STrond Myklebust return false;
11548e0bdc70STrond Myklebust if (req->wb_pgbase == 0)
11558e0bdc70STrond Myklebust return prev_end == nfs_page_max_length(prev);
11568e0bdc70STrond Myklebust if (req->wb_pgbase == prev_end) {
11578e0bdc70STrond Myklebust struct folio *folio = nfs_page_to_folio(req);
11588e0bdc70STrond Myklebust if (folio)
11598e0bdc70STrond Myklebust return folio == nfs_page_to_folio(prev);
11608e0bdc70STrond Myklebust return req->wb_page == prev->wb_page;
11618e0bdc70STrond Myklebust }
11628e0bdc70STrond Myklebust return false;
11638e0bdc70STrond Myklebust }
11648e0bdc70STrond Myklebust
1165d8a5ad75STrond Myklebust /**
116644a65a0cSTrond Myklebust * nfs_coalesce_size - test two requests for compatibility
1167d8a5ad75STrond Myklebust * @prev: pointer to nfs_page
1168d8a5ad75STrond Myklebust * @req: pointer to nfs_page
1169302fad7bSTrond Myklebust * @pgio: pointer to nfs_pagio_descriptor
1170d8a5ad75STrond Myklebust *
1171d8a5ad75STrond Myklebust * The nfs_page structures 'prev' and 'req' are compared to ensure that the
1172d8a5ad75STrond Myklebust * page data area they describe is contiguous, and that their RPC
1173d8a5ad75STrond Myklebust * credentials, NFSv4 open state, and lockowners are the same.
1174d8a5ad75STrond Myklebust *
117544a65a0cSTrond Myklebust * Returns size of the request that can be coalesced
1176d8a5ad75STrond Myklebust */
nfs_coalesce_size(struct nfs_page * prev,struct nfs_page * req,struct nfs_pageio_descriptor * pgio)117744a65a0cSTrond Myklebust static unsigned int nfs_coalesce_size(struct nfs_page *prev,
117894ad1c80SFred Isaman struct nfs_page *req,
117994ad1c80SFred Isaman struct nfs_pageio_descriptor *pgio)
1180d8a5ad75STrond Myklebust {
11815263e31eSJeff Layton struct file_lock_context *flctx;
1182b4fdac1aSWeston Andros Adamson
1183ab75e417SWeston Andros Adamson if (prev) {
11849fcd5960STrond Myklebust if (!nfs_match_open_context(nfs_req_openctx(req), nfs_req_openctx(prev)))
118544a65a0cSTrond Myklebust return 0;
118617b985deSJeff Layton flctx = locks_inode_context(d_inode(nfs_req_openctx(req)->dentry));
1187bd61e0a9SJeff Layton if (flctx != NULL &&
1188bd61e0a9SJeff Layton !(list_empty_careful(&flctx->flc_posix) &&
1189bd61e0a9SJeff Layton list_empty_careful(&flctx->flc_flock)) &&
11905263e31eSJeff Layton !nfs_match_lock_context(req->wb_lock_context,
11915263e31eSJeff Layton prev->wb_lock_context))
119244a65a0cSTrond Myklebust return 0;
11938e0bdc70STrond Myklebust if (!nfs_page_is_contiguous(prev, req))
119444a65a0cSTrond Myklebust return 0;
1195ab75e417SWeston Andros Adamson }
119644a65a0cSTrond Myklebust return pgio->pg_ops->pg_test(pgio, prev, req);
1197d8a5ad75STrond Myklebust }
1198d8a5ad75STrond Myklebust
1199d8a5ad75STrond Myklebust /**
1200bcb71bbaSTrond Myklebust * nfs_pageio_do_add_request - Attempt to coalesce a request into a page list.
1201d8a5ad75STrond Myklebust * @desc: destination io descriptor
1202d8a5ad75STrond Myklebust * @req: request
1203d8a5ad75STrond Myklebust *
120444a65a0cSTrond Myklebust * If the request 'req' was successfully coalesced into the existing list
120544a65a0cSTrond Myklebust * of pages 'desc', it returns the size of req.
1206d8a5ad75STrond Myklebust */
120744a65a0cSTrond Myklebust static unsigned int
nfs_pageio_do_add_request(struct nfs_pageio_descriptor * desc,struct nfs_page * req)120844a65a0cSTrond Myklebust nfs_pageio_do_add_request(struct nfs_pageio_descriptor *desc,
1209d8a5ad75STrond Myklebust struct nfs_page *req)
1210d8a5ad75STrond Myklebust {
121148d635f1SPeng Tao struct nfs_pgio_mirror *mirror = nfs_pgio_current_mirror(desc);
1212ab75e417SWeston Andros Adamson struct nfs_page *prev = NULL;
121344a65a0cSTrond Myklebust unsigned int size;
1214a7d42ddbSWeston Andros Adamson
121556517ab9STrond Myklebust if (list_empty(&mirror->pg_list)) {
1216d8007d4dSTrond Myklebust if (desc->pg_ops->pg_init)
1217d8007d4dSTrond Myklebust desc->pg_ops->pg_init(desc, req);
1218d600ad1fSPeng Tao if (desc->pg_error < 0)
1219d600ad1fSPeng Tao return 0;
1220a7d42ddbSWeston Andros Adamson mirror->pg_base = req->wb_pgbase;
122156517ab9STrond Myklebust mirror->pg_count = 0;
122256517ab9STrond Myklebust mirror->pg_recoalesce = 0;
122356517ab9STrond Myklebust } else
122456517ab9STrond Myklebust prev = nfs_list_entry(mirror->pg_list.prev);
122533344e0fSTrond Myklebust
122633344e0fSTrond Myklebust if (desc->pg_maxretrans && req->wb_nio > desc->pg_maxretrans) {
122733344e0fSTrond Myklebust if (NFS_SERVER(desc->pg_inode)->flags & NFS_MOUNT_SOFTERR)
122833344e0fSTrond Myklebust desc->pg_error = -ETIMEDOUT;
122933344e0fSTrond Myklebust else
123033344e0fSTrond Myklebust desc->pg_error = -EIO;
123133344e0fSTrond Myklebust return 0;
123233344e0fSTrond Myklebust }
123333344e0fSTrond Myklebust
123444a65a0cSTrond Myklebust size = nfs_coalesce_size(prev, req, desc);
123544a65a0cSTrond Myklebust if (size < req->wb_bytes)
123644a65a0cSTrond Myklebust return size;
1237078b5fd9STrond Myklebust nfs_list_move_request(req, &mirror->pg_list);
1238a7d42ddbSWeston Andros Adamson mirror->pg_count += req->wb_bytes;
123944a65a0cSTrond Myklebust return req->wb_bytes;
1240d8a5ad75STrond Myklebust }
1241d8a5ad75STrond Myklebust
1242bcb71bbaSTrond Myklebust /*
1243bcb71bbaSTrond Myklebust * Helper for nfs_pageio_add_request and nfs_pageio_complete
1244bcb71bbaSTrond Myklebust */
nfs_pageio_doio(struct nfs_pageio_descriptor * desc)1245bcb71bbaSTrond Myklebust static void nfs_pageio_doio(struct nfs_pageio_descriptor *desc)
1246bcb71bbaSTrond Myklebust {
124748d635f1SPeng Tao struct nfs_pgio_mirror *mirror = nfs_pgio_current_mirror(desc);
1248a7d42ddbSWeston Andros Adamson
1249a7d42ddbSWeston Andros Adamson if (!list_empty(&mirror->pg_list)) {
12501751c363STrond Myklebust int error = desc->pg_ops->pg_doio(desc);
1251bcb71bbaSTrond Myklebust if (error < 0)
1252bcb71bbaSTrond Myklebust desc->pg_error = error;
125370536bf4STrond Myklebust if (list_empty(&mirror->pg_list))
12540d0ea309STrond Myklebust mirror->pg_bytes_written += mirror->pg_count;
1255bcb71bbaSTrond Myklebust }
1256bcb71bbaSTrond Myklebust }
1257bcb71bbaSTrond Myklebust
1258f57dcf4cSTrond Myklebust static void
nfs_pageio_cleanup_request(struct nfs_pageio_descriptor * desc,struct nfs_page * req)1259f57dcf4cSTrond Myklebust nfs_pageio_cleanup_request(struct nfs_pageio_descriptor *desc,
1260f57dcf4cSTrond Myklebust struct nfs_page *req)
1261f57dcf4cSTrond Myklebust {
1262f57dcf4cSTrond Myklebust LIST_HEAD(head);
1263f57dcf4cSTrond Myklebust
1264078b5fd9STrond Myklebust nfs_list_move_request(req, &head);
1265df3accb8STrond Myklebust desc->pg_completion_ops->error_cleanup(&head, desc->pg_error);
1266f57dcf4cSTrond Myklebust }
1267f57dcf4cSTrond Myklebust
1268bcb71bbaSTrond Myklebust /**
12696453bcd0STrond Myklebust * __nfs_pageio_add_request - Attempt to coalesce a request into a page list.
1270bcb71bbaSTrond Myklebust * @desc: destination io descriptor
1271bcb71bbaSTrond Myklebust * @req: request
1272bcb71bbaSTrond Myklebust *
12732bfc6e56SWeston Andros Adamson * This may split a request into subrequests which are all part of the
127444a65a0cSTrond Myklebust * same page group. If so, it will submit @req as the last one, to ensure
127544a65a0cSTrond Myklebust * the pointer to @req is still valid in case of failure.
12762bfc6e56SWeston Andros Adamson *
1277bcb71bbaSTrond Myklebust * Returns true if the request 'req' was successfully coalesced into the
1278bcb71bbaSTrond Myklebust * existing list of pages 'desc'.
1279bcb71bbaSTrond Myklebust */
__nfs_pageio_add_request(struct nfs_pageio_descriptor * desc,struct nfs_page * req)1280d9156f9fSTrond Myklebust static int __nfs_pageio_add_request(struct nfs_pageio_descriptor *desc,
1281bcb71bbaSTrond Myklebust struct nfs_page *req)
1282bcb71bbaSTrond Myklebust {
128348d635f1SPeng Tao struct nfs_pgio_mirror *mirror = nfs_pgio_current_mirror(desc);
12842bfc6e56SWeston Andros Adamson struct nfs_page *subreq;
128544a65a0cSTrond Myklebust unsigned int size, subreq_size;
12862bfc6e56SWeston Andros Adamson
12871344b7eaSTrond Myklebust nfs_page_group_lock(req);
12882bfc6e56SWeston Andros Adamson
12892bfc6e56SWeston Andros Adamson subreq = req;
129044a65a0cSTrond Myklebust subreq_size = subreq->wb_bytes;
129144a65a0cSTrond Myklebust for(;;) {
129244a65a0cSTrond Myklebust size = nfs_pageio_do_add_request(desc, subreq);
129344a65a0cSTrond Myklebust if (size == subreq_size) {
129444a65a0cSTrond Myklebust /* We successfully submitted a request */
129544a65a0cSTrond Myklebust if (subreq == req)
129644a65a0cSTrond Myklebust break;
129744a65a0cSTrond Myklebust req->wb_pgbase += size;
129844a65a0cSTrond Myklebust req->wb_bytes -= size;
129944a65a0cSTrond Myklebust req->wb_offset += size;
130044a65a0cSTrond Myklebust subreq_size = req->wb_bytes;
130144a65a0cSTrond Myklebust subreq = req;
130244a65a0cSTrond Myklebust continue;
130344a65a0cSTrond Myklebust }
130444a65a0cSTrond Myklebust if (WARN_ON_ONCE(subreq != req)) {
130544a65a0cSTrond Myklebust nfs_page_group_unlock(req);
130644a65a0cSTrond Myklebust nfs_pageio_cleanup_request(desc, subreq);
130744a65a0cSTrond Myklebust subreq = req;
130844a65a0cSTrond Myklebust subreq_size = req->wb_bytes;
130944a65a0cSTrond Myklebust nfs_page_group_lock(req);
131044a65a0cSTrond Myklebust }
131144a65a0cSTrond Myklebust if (!size) {
131244a65a0cSTrond Myklebust /* Can't coalesce any more, so do I/O */
13132bfc6e56SWeston Andros Adamson nfs_page_group_unlock(req);
1314b31268acSTrond Myklebust desc->pg_moreio = 1;
1315bcb71bbaSTrond Myklebust nfs_pageio_doio(desc);
1316f57dcf4cSTrond Myklebust if (desc->pg_error < 0 || mirror->pg_recoalesce)
131744a65a0cSTrond Myklebust return 0;
13182bfc6e56SWeston Andros Adamson /* retry add_request for this subreq */
13191344b7eaSTrond Myklebust nfs_page_group_lock(req);
13202bfc6e56SWeston Andros Adamson continue;
1321bcb71bbaSTrond Myklebust }
132244a65a0cSTrond Myklebust subreq = nfs_create_subreq(req, req->wb_pgbase,
132344a65a0cSTrond Myklebust req->wb_offset, size);
1324c1109558STrond Myklebust if (IS_ERR(subreq))
1325c1109558STrond Myklebust goto err_ptr;
132644a65a0cSTrond Myklebust subreq_size = size;
13272bfc6e56SWeston Andros Adamson }
13282bfc6e56SWeston Andros Adamson
13292bfc6e56SWeston Andros Adamson nfs_page_group_unlock(req);
1330bcb71bbaSTrond Myklebust return 1;
1331c1109558STrond Myklebust err_ptr:
1332c1109558STrond Myklebust desc->pg_error = PTR_ERR(subreq);
1333c1109558STrond Myklebust nfs_page_group_unlock(req);
1334c1109558STrond Myklebust return 0;
1335bcb71bbaSTrond Myklebust }
1336bcb71bbaSTrond Myklebust
nfs_do_recoalesce(struct nfs_pageio_descriptor * desc)1337d9156f9fSTrond Myklebust static int nfs_do_recoalesce(struct nfs_pageio_descriptor *desc)
1338d9156f9fSTrond Myklebust {
133948d635f1SPeng Tao struct nfs_pgio_mirror *mirror = nfs_pgio_current_mirror(desc);
1340d9156f9fSTrond Myklebust LIST_HEAD(head);
1341d9156f9fSTrond Myklebust
1342d9156f9fSTrond Myklebust do {
1343a7d42ddbSWeston Andros Adamson list_splice_init(&mirror->pg_list, &head);
1344d02d81efSTrond Myklebust mirror->pg_recoalesce = 0;
1345a7d42ddbSWeston Andros Adamson
1346d9156f9fSTrond Myklebust while (!list_empty(&head)) {
1347d9156f9fSTrond Myklebust struct nfs_page *req;
1348d9156f9fSTrond Myklebust
1349d9156f9fSTrond Myklebust req = list_first_entry(&head, struct nfs_page, wb_list);
1350d9156f9fSTrond Myklebust if (__nfs_pageio_add_request(desc, req))
1351d9156f9fSTrond Myklebust continue;
135203d5eb65STrond Myklebust if (desc->pg_error < 0) {
135303d5eb65STrond Myklebust list_splice_tail(&head, &mirror->pg_list);
135403d5eb65STrond Myklebust mirror->pg_recoalesce = 1;
1355d9156f9fSTrond Myklebust return 0;
135603d5eb65STrond Myklebust }
1357d9156f9fSTrond Myklebust break;
1358d9156f9fSTrond Myklebust }
1359a7d42ddbSWeston Andros Adamson } while (mirror->pg_recoalesce);
1360d9156f9fSTrond Myklebust return 1;
1361d9156f9fSTrond Myklebust }
1362d9156f9fSTrond Myklebust
nfs_pageio_add_request_mirror(struct nfs_pageio_descriptor * desc,struct nfs_page * req)1363a7d42ddbSWeston Andros Adamson static int nfs_pageio_add_request_mirror(struct nfs_pageio_descriptor *desc,
1364d9156f9fSTrond Myklebust struct nfs_page *req)
1365d9156f9fSTrond Myklebust {
1366d9156f9fSTrond Myklebust int ret;
1367d9156f9fSTrond Myklebust
1368d9156f9fSTrond Myklebust do {
1369d9156f9fSTrond Myklebust ret = __nfs_pageio_add_request(desc, req);
1370d9156f9fSTrond Myklebust if (ret)
1371d9156f9fSTrond Myklebust break;
1372d9156f9fSTrond Myklebust if (desc->pg_error < 0)
1373d9156f9fSTrond Myklebust break;
1374d9156f9fSTrond Myklebust ret = nfs_do_recoalesce(desc);
1375d9156f9fSTrond Myklebust } while (ret);
1376a7d42ddbSWeston Andros Adamson
1377d9156f9fSTrond Myklebust return ret;
1378d9156f9fSTrond Myklebust }
1379d9156f9fSTrond Myklebust
nfs_pageio_error_cleanup(struct nfs_pageio_descriptor * desc)1380fdbd1a2eSBenjamin Coddington static void nfs_pageio_error_cleanup(struct nfs_pageio_descriptor *desc)
1381fdbd1a2eSBenjamin Coddington {
1382fdbd1a2eSBenjamin Coddington u32 midx;
1383fdbd1a2eSBenjamin Coddington struct nfs_pgio_mirror *mirror;
1384fdbd1a2eSBenjamin Coddington
1385fdbd1a2eSBenjamin Coddington if (!desc->pg_error)
1386fdbd1a2eSBenjamin Coddington return;
1387fdbd1a2eSBenjamin Coddington
1388fdbd1a2eSBenjamin Coddington for (midx = 0; midx < desc->pg_mirror_count; midx++) {
138963e2fffaSTrond Myklebust mirror = nfs_pgio_get_mirror(desc, midx);
1390df3accb8STrond Myklebust desc->pg_completion_ops->error_cleanup(&mirror->pg_list,
1391df3accb8STrond Myklebust desc->pg_error);
1392fdbd1a2eSBenjamin Coddington }
1393fdbd1a2eSBenjamin Coddington }
1394fdbd1a2eSBenjamin Coddington
nfs_pageio_add_request(struct nfs_pageio_descriptor * desc,struct nfs_page * req)1395a7d42ddbSWeston Andros Adamson int nfs_pageio_add_request(struct nfs_pageio_descriptor *desc,
1396a7d42ddbSWeston Andros Adamson struct nfs_page *req)
1397a7d42ddbSWeston Andros Adamson {
1398a7d42ddbSWeston Andros Adamson u32 midx;
1399a7d42ddbSWeston Andros Adamson unsigned int pgbase, offset, bytes;
140044a65a0cSTrond Myklebust struct nfs_page *dupreq;
1401a7d42ddbSWeston Andros Adamson
1402a7d42ddbSWeston Andros Adamson pgbase = req->wb_pgbase;
1403a7d42ddbSWeston Andros Adamson offset = req->wb_offset;
1404a7d42ddbSWeston Andros Adamson bytes = req->wb_bytes;
1405a7d42ddbSWeston Andros Adamson
1406a7d42ddbSWeston Andros Adamson nfs_pageio_setup_mirroring(desc, req);
1407d600ad1fSPeng Tao if (desc->pg_error < 0)
1408c18b96a1SPeng Tao goto out_failed;
1409a7d42ddbSWeston Andros Adamson
1410dc9dc2feSTrond Myklebust /* Create the mirror instances first, and fire them off */
1411dc9dc2feSTrond Myklebust for (midx = 1; midx < desc->pg_mirror_count; midx++) {
14121344b7eaSTrond Myklebust nfs_page_group_lock(req);
1413a7d42ddbSWeston Andros Adamson
141444a65a0cSTrond Myklebust dupreq = nfs_create_subreq(req,
1415c917cfafSTrond Myklebust pgbase, offset, bytes);
1416a7d42ddbSWeston Andros Adamson
1417a7d42ddbSWeston Andros Adamson nfs_page_group_unlock(req);
1418c917cfafSTrond Myklebust if (IS_ERR(dupreq)) {
1419c18b96a1SPeng Tao desc->pg_error = PTR_ERR(dupreq);
1420c18b96a1SPeng Tao goto out_failed;
1421a7d42ddbSWeston Andros Adamson }
1422a7d42ddbSWeston Andros Adamson
142363e2fffaSTrond Myklebust nfs_pgio_set_current_mirror(desc, midx);
1424a7d42ddbSWeston Andros Adamson if (!nfs_pageio_add_request_mirror(desc, dupreq))
1425f57dcf4cSTrond Myklebust goto out_cleanup_subreq;
1426a7d42ddbSWeston Andros Adamson }
1427a7d42ddbSWeston Andros Adamson
142863e2fffaSTrond Myklebust nfs_pgio_set_current_mirror(desc, 0);
1429dc9dc2feSTrond Myklebust if (!nfs_pageio_add_request_mirror(desc, req))
1430dc9dc2feSTrond Myklebust goto out_failed;
1431dc9dc2feSTrond Myklebust
1432a7d42ddbSWeston Andros Adamson return 1;
1433c18b96a1SPeng Tao
1434f57dcf4cSTrond Myklebust out_cleanup_subreq:
1435f57dcf4cSTrond Myklebust nfs_pageio_cleanup_request(desc, dupreq);
1436c18b96a1SPeng Tao out_failed:
1437fdbd1a2eSBenjamin Coddington nfs_pageio_error_cleanup(desc);
1438c18b96a1SPeng Tao return 0;
1439a7d42ddbSWeston Andros Adamson }
1440a7d42ddbSWeston Andros Adamson
1441a7d42ddbSWeston Andros Adamson /*
1442a7d42ddbSWeston Andros Adamson * nfs_pageio_complete_mirror - Complete I/O on the current mirror of an
1443a7d42ddbSWeston Andros Adamson * nfs_pageio_descriptor
1444a7d42ddbSWeston Andros Adamson * @desc: pointer to io descriptor
1445dfad7000SYijing Wang * @mirror_idx: pointer to mirror index
1446a7d42ddbSWeston Andros Adamson */
nfs_pageio_complete_mirror(struct nfs_pageio_descriptor * desc,u32 mirror_idx)1447a7d42ddbSWeston Andros Adamson static void nfs_pageio_complete_mirror(struct nfs_pageio_descriptor *desc,
1448a7d42ddbSWeston Andros Adamson u32 mirror_idx)
1449a7d42ddbSWeston Andros Adamson {
145063e2fffaSTrond Myklebust struct nfs_pgio_mirror *mirror;
145163e2fffaSTrond Myklebust u32 restore_idx;
1452a7d42ddbSWeston Andros Adamson
145363e2fffaSTrond Myklebust restore_idx = nfs_pgio_set_current_mirror(desc, mirror_idx);
145463e2fffaSTrond Myklebust mirror = nfs_pgio_current_mirror(desc);
145563e2fffaSTrond Myklebust
1456a7d42ddbSWeston Andros Adamson for (;;) {
1457a7d42ddbSWeston Andros Adamson nfs_pageio_doio(desc);
14588127d827STrond Myklebust if (desc->pg_error < 0 || !mirror->pg_recoalesce)
1459a7d42ddbSWeston Andros Adamson break;
1460a7d42ddbSWeston Andros Adamson if (!nfs_do_recoalesce(desc))
1461a7d42ddbSWeston Andros Adamson break;
1462a7d42ddbSWeston Andros Adamson }
146363e2fffaSTrond Myklebust nfs_pgio_set_current_mirror(desc, restore_idx);
1464a7d42ddbSWeston Andros Adamson }
1465a7d42ddbSWeston Andros Adamson
146653113ad3SWeston Andros Adamson /*
146753113ad3SWeston Andros Adamson * nfs_pageio_resend - Transfer requests to new descriptor and resend
146853113ad3SWeston Andros Adamson * @hdr - the pgio header to move request from
146953113ad3SWeston Andros Adamson * @desc - the pageio descriptor to add requests to
147053113ad3SWeston Andros Adamson *
147153113ad3SWeston Andros Adamson * Try to move each request (nfs_page) from @hdr to @desc then attempt
147253113ad3SWeston Andros Adamson * to send them.
147353113ad3SWeston Andros Adamson *
147453113ad3SWeston Andros Adamson * Returns 0 on success and < 0 on error.
147553113ad3SWeston Andros Adamson */
nfs_pageio_resend(struct nfs_pageio_descriptor * desc,struct nfs_pgio_header * hdr)147653113ad3SWeston Andros Adamson int nfs_pageio_resend(struct nfs_pageio_descriptor *desc,
147753113ad3SWeston Andros Adamson struct nfs_pgio_header *hdr)
147853113ad3SWeston Andros Adamson {
1479f4340e93STrond Myklebust LIST_HEAD(pages);
148053113ad3SWeston Andros Adamson
1481919e3bd9STrond Myklebust desc->pg_io_completion = hdr->io_completion;
148253113ad3SWeston Andros Adamson desc->pg_dreq = hdr->dreq;
1483000dbe0bSDave Wysochanski nfs_netfs_set_pageio_descriptor(desc, hdr);
1484f4340e93STrond Myklebust list_splice_init(&hdr->pages, &pages);
1485f4340e93STrond Myklebust while (!list_empty(&pages)) {
1486f4340e93STrond Myklebust struct nfs_page *req = nfs_list_entry(pages.next);
148753113ad3SWeston Andros Adamson
148853113ad3SWeston Andros Adamson if (!nfs_pageio_add_request(desc, req))
1489f4340e93STrond Myklebust break;
149053113ad3SWeston Andros Adamson }
149153113ad3SWeston Andros Adamson nfs_pageio_complete(desc);
1492f4340e93STrond Myklebust if (!list_empty(&pages)) {
1493f4340e93STrond Myklebust int err = desc->pg_error < 0 ? desc->pg_error : -EIO;
1494f4340e93STrond Myklebust hdr->completion_ops->error_cleanup(&pages, err);
1495eb2c50daSTrond Myklebust nfs_set_pgio_error(hdr, err, hdr->io_start);
1496f4340e93STrond Myklebust return err;
149753113ad3SWeston Andros Adamson }
149853113ad3SWeston Andros Adamson return 0;
149953113ad3SWeston Andros Adamson }
150053113ad3SWeston Andros Adamson EXPORT_SYMBOL_GPL(nfs_pageio_resend);
1501d8a5ad75STrond Myklebust
1502d8a5ad75STrond Myklebust /**
15032176bf42SWeston Andros Adamson * nfs_pageio_complete - Complete I/O then cleanup an nfs_pageio_descriptor
1504bcb71bbaSTrond Myklebust * @desc: pointer to io descriptor
1505bcb71bbaSTrond Myklebust */
nfs_pageio_complete(struct nfs_pageio_descriptor * desc)1506bcb71bbaSTrond Myklebust void nfs_pageio_complete(struct nfs_pageio_descriptor *desc)
1507bcb71bbaSTrond Myklebust {
1508a7d42ddbSWeston Andros Adamson u32 midx;
1509a7d42ddbSWeston Andros Adamson
1510a7d42ddbSWeston Andros Adamson for (midx = 0; midx < desc->pg_mirror_count; midx++)
1511a7d42ddbSWeston Andros Adamson nfs_pageio_complete_mirror(desc, midx);
15122176bf42SWeston Andros Adamson
1513fdbd1a2eSBenjamin Coddington if (desc->pg_error < 0)
1514fdbd1a2eSBenjamin Coddington nfs_pageio_error_cleanup(desc);
15152176bf42SWeston Andros Adamson if (desc->pg_ops->pg_cleanup)
15162176bf42SWeston Andros Adamson desc->pg_ops->pg_cleanup(desc);
1517a7d42ddbSWeston Andros Adamson nfs_pageio_cleanup_mirroring(desc);
1518bcb71bbaSTrond Myklebust }
1519bcb71bbaSTrond Myklebust
15207fe7f848STrond Myklebust /**
15217fe7f848STrond Myklebust * nfs_pageio_cond_complete - Conditional I/O completion
15227fe7f848STrond Myklebust * @desc: pointer to io descriptor
15237fe7f848STrond Myklebust * @index: page index
15247fe7f848STrond Myklebust *
15257fe7f848STrond Myklebust * It is important to ensure that processes don't try to take locks
15267fe7f848STrond Myklebust * on non-contiguous ranges of pages as that might deadlock. This
15277fe7f848STrond Myklebust * function should be called before attempting to wait on a locked
15287fe7f848STrond Myklebust * nfs_page. It will complete the I/O if the page index 'index'
15297fe7f848STrond Myklebust * is not contiguous with the existing list of pages in 'desc'.
15307fe7f848STrond Myklebust */
nfs_pageio_cond_complete(struct nfs_pageio_descriptor * desc,pgoff_t index)15317fe7f848STrond Myklebust void nfs_pageio_cond_complete(struct nfs_pageio_descriptor *desc, pgoff_t index)
15327fe7f848STrond Myklebust {
1533a7d42ddbSWeston Andros Adamson struct nfs_pgio_mirror *mirror;
1534a7d42ddbSWeston Andros Adamson struct nfs_page *prev;
1535cbefa53cSTrond Myklebust struct folio *folio;
1536a7d42ddbSWeston Andros Adamson u32 midx;
1537a7d42ddbSWeston Andros Adamson
1538a7d42ddbSWeston Andros Adamson for (midx = 0; midx < desc->pg_mirror_count; midx++) {
153963e2fffaSTrond Myklebust mirror = nfs_pgio_get_mirror(desc, midx);
1540a7d42ddbSWeston Andros Adamson if (!list_empty(&mirror->pg_list)) {
1541a7d42ddbSWeston Andros Adamson prev = nfs_list_entry(mirror->pg_list.prev);
1542cbefa53cSTrond Myklebust folio = nfs_page_to_folio(prev);
1543cbefa53cSTrond Myklebust if (folio) {
1544cbefa53cSTrond Myklebust if (index == folio_next_index(folio))
1545cbefa53cSTrond Myklebust continue;
1546cbefa53cSTrond Myklebust } else if (index == prev->wb_index + 1)
1547cbefa53cSTrond Myklebust continue;
1548*e3adf998SJan Kara /*
1549*e3adf998SJan Kara * We will submit more requests after these. Indicate
1550*e3adf998SJan Kara * this to the underlying layers.
1551*e3adf998SJan Kara */
1552*e3adf998SJan Kara desc->pg_moreio = 1;
155343b7d964SBenjamin Coddington nfs_pageio_complete(desc);
155443b7d964SBenjamin Coddington break;
155543b7d964SBenjamin Coddington }
1556a7d42ddbSWeston Andros Adamson }
15577fe7f848STrond Myklebust }
15587fe7f848STrond Myklebust
1559862f35c9STrond Myklebust /*
1560862f35c9STrond Myklebust * nfs_pageio_stop_mirroring - stop using mirroring (set mirror count to 1)
1561862f35c9STrond Myklebust */
nfs_pageio_stop_mirroring(struct nfs_pageio_descriptor * pgio)1562862f35c9STrond Myklebust void nfs_pageio_stop_mirroring(struct nfs_pageio_descriptor *pgio)
1563862f35c9STrond Myklebust {
1564862f35c9STrond Myklebust nfs_pageio_complete(pgio);
1565862f35c9STrond Myklebust }
1566862f35c9STrond Myklebust
nfs_init_nfspagecache(void)1567f7b422b1SDavid Howells int __init nfs_init_nfspagecache(void)
15681da177e4SLinus Torvalds {
15691da177e4SLinus Torvalds nfs_page_cachep = kmem_cache_create("nfs_page",
15701da177e4SLinus Torvalds sizeof(struct nfs_page),
15711da177e4SLinus Torvalds 0, SLAB_HWCACHE_ALIGN,
157220c2df83SPaul Mundt NULL);
15731da177e4SLinus Torvalds if (nfs_page_cachep == NULL)
15741da177e4SLinus Torvalds return -ENOMEM;
15751da177e4SLinus Torvalds
15761da177e4SLinus Torvalds return 0;
15771da177e4SLinus Torvalds }
15781da177e4SLinus Torvalds
nfs_destroy_nfspagecache(void)1579266bee88SDavid Brownell void nfs_destroy_nfspagecache(void)
15801da177e4SLinus Torvalds {
15811a1d92c1SAlexey Dobriyan kmem_cache_destroy(nfs_page_cachep);
15821da177e4SLinus Torvalds }
15831da177e4SLinus Torvalds
1584ef2c488cSAnna Schumaker static const struct rpc_call_ops nfs_pgio_common_ops = {
15856f92fa45SAnna Schumaker .rpc_call_prepare = nfs_pgio_prepare,
15866f92fa45SAnna Schumaker .rpc_call_done = nfs_pgio_result,
15876f92fa45SAnna Schumaker .rpc_release = nfs_pgio_release,
15886f92fa45SAnna Schumaker };
158941d8d5b7SAnna Schumaker
159041d8d5b7SAnna Schumaker const struct nfs_pageio_ops nfs_pgio_rw_ops = {
159141d8d5b7SAnna Schumaker .pg_test = nfs_generic_pg_test,
159241d8d5b7SAnna Schumaker .pg_doio = nfs_generic_pg_pgios,
159341d8d5b7SAnna Schumaker };
1594