blob: 6985cacf4700df1759239ddf0e73d44f59806f06 [file] [log] [blame]
Thomas Gleixner457c8992019-05-19 13:08:55 +01001// SPDX-License-Identifier: GPL-2.0-only
Linus Torvalds1da177e2005-04-16 15:20:36 -07002/*
3 * linux/fs/nfs/pagelist.c
4 *
5 * A set of helper functions for managing NFS read and write requests.
6 * The main purpose of these routines is to provide support for the
7 * coalescing of several requests into a single RPC call.
8 *
9 * Copyright 2000, 2001 (c) Trond Myklebust <trond.myklebust@fys.uio.no>
10 *
11 */
12
Linus Torvalds1da177e2005-04-16 15:20:36 -070013#include <linux/slab.h>
14#include <linux/file.h>
Alexey Dobriyane8edc6e2007-05-21 01:22:52 +040015#include <linux/sched.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070016#include <linux/sunrpc/clnt.h>
Trond Myklebust1313e602012-01-17 22:04:24 -050017#include <linux/nfs.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070018#include <linux/nfs3.h>
19#include <linux/nfs4.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070020#include <linux/nfs_fs.h>
Trond Myklebust33344e02019-04-07 13:59:08 -040021#include <linux/nfs_page.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070022#include <linux/nfs_mount.h>
Paul Gortmakerafeacc82011-05-26 16:00:52 -040023#include <linux/export.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070024
Trond Myklebust8d5658c2007-04-10 09:26:35 -040025#include "internal.h"
Fred Isamanbae724e2011-03-01 01:34:15 +000026#include "pnfs.h"
Chuck Levercd2ed9b2020-05-12 17:14:11 -040027#include "nfstrace.h"
Trond Myklebust8d5658c2007-04-10 09:26:35 -040028
Anna Schumaker0eecb212014-05-06 09:12:32 -040029#define NFSDBG_FACILITY NFSDBG_PAGECACHE
Linus Torvalds1da177e2005-04-16 15:20:36 -070030
Linus Torvalds1da177e2005-04-16 15:20:36 -070031static struct kmem_cache *nfs_page_cachep;
Anna Schumakeref2c4882014-05-06 09:12:36 -040032static const struct rpc_call_ops nfs_pgio_common_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -070033
Peng Tao48d635f2014-11-10 08:35:35 +080034struct nfs_pgio_mirror *
35nfs_pgio_current_mirror(struct nfs_pageio_descriptor *desc)
36{
Trond Myklebust377840e2020-03-29 20:03:33 -040037 return &desc->pg_mirrors[desc->pg_mirror_idx];
Peng Tao48d635f2014-11-10 08:35:35 +080038}
39EXPORT_SYMBOL_GPL(nfs_pgio_current_mirror);
40
Fred Isaman4db6e0b2012-04-20 14:47:46 -040041void nfs_pgheader_init(struct nfs_pageio_descriptor *desc,
42 struct nfs_pgio_header *hdr,
43 void (*release)(struct nfs_pgio_header *hdr))
44{
Peng Tao48d635f2014-11-10 08:35:35 +080045 struct nfs_pgio_mirror *mirror = nfs_pgio_current_mirror(desc);
Weston Andros Adamsona7d42dd2014-09-19 10:55:07 -040046
47
48 hdr->req = nfs_list_entry(mirror->pg_list.next);
Fred Isaman4db6e0b2012-04-20 14:47:46 -040049 hdr->inode = desc->pg_inode;
Trond Myklebust9fcd5962019-04-07 13:59:11 -040050 hdr->cred = nfs_req_openctx(hdr->req)->cred;
Fred Isaman4db6e0b2012-04-20 14:47:46 -040051 hdr->io_start = req_offset(hdr->req);
Weston Andros Adamsona7d42dd2014-09-19 10:55:07 -040052 hdr->good_bytes = mirror->pg_count;
Trond Myklebust919e3bd2017-06-20 19:35:37 -040053 hdr->io_completion = desc->pg_io_completion;
Fred Isaman584aa812012-04-20 14:47:51 -040054 hdr->dreq = desc->pg_dreq;
Fred Isaman4db6e0b2012-04-20 14:47:46 -040055 hdr->release = release;
Fred Isaman061ae2e2012-04-20 14:47:48 -040056 hdr->completion_ops = desc->pg_completion_ops;
Fred Isaman584aa812012-04-20 14:47:51 -040057 if (hdr->completion_ops->init_hdr)
58 hdr->completion_ops->init_hdr(hdr);
Weston Andros Adamsona7d42dd2014-09-19 10:55:07 -040059
60 hdr->pgio_mirror_idx = desc->pg_mirror_idx;
Fred Isaman4db6e0b2012-04-20 14:47:46 -040061}
Bryan Schumaker89d77c82012-07-30 16:05:25 -040062EXPORT_SYMBOL_GPL(nfs_pgheader_init);
Fred Isaman4db6e0b2012-04-20 14:47:46 -040063
64void nfs_set_pgio_error(struct nfs_pgio_header *hdr, int error, loff_t pos)
65{
Trond Myklebust1c6c4b72018-09-25 12:34:43 -040066 unsigned int new = pos - hdr->io_start;
67
Chuck Levercd2ed9b2020-05-12 17:14:11 -040068 trace_nfs_pgio_error(hdr, error, pos);
Trond Myklebust1c6c4b72018-09-25 12:34:43 -040069 if (hdr->good_bytes > new) {
70 hdr->good_bytes = new;
Fred Isaman4db6e0b2012-04-20 14:47:46 -040071 clear_bit(NFS_IOHDR_EOF, &hdr->flags);
Trond Myklebust1c6c4b72018-09-25 12:34:43 -040072 if (!test_and_set_bit(NFS_IOHDR_ERROR, &hdr->flags))
73 hdr->error = error;
Fred Isaman4db6e0b2012-04-20 14:47:46 -040074 }
Fred Isaman4db6e0b2012-04-20 14:47:46 -040075}
76
Linus Torvalds1da177e2005-04-16 15:20:36 -070077static inline struct nfs_page *
78nfs_page_alloc(void)
79{
Trond Myklebust2b17d7252019-06-11 16:49:52 -040080 struct nfs_page *p = kmem_cache_zalloc(nfs_page_cachep, GFP_KERNEL);
Jesper Juhl72895b12010-12-09 23:17:15 +010081 if (p)
Linus Torvalds1da177e2005-04-16 15:20:36 -070082 INIT_LIST_HEAD(&p->wb_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -070083 return p;
84}
85
86static inline void
87nfs_page_free(struct nfs_page *p)
88{
89 kmem_cache_free(nfs_page_cachep, p);
90}
91
Trond Myklebust577b4232013-04-08 21:38:12 -040092/**
93 * nfs_iocounter_wait - wait for i/o to complete
Benjamin Coddington210c7c12016-01-06 10:40:18 -050094 * @l_ctx: nfs_lock_context with io_counter to use
Trond Myklebust577b4232013-04-08 21:38:12 -040095 *
96 * returns -ERESTARTSYS if interrupted by a fatal signal.
97 * Otherwise returns 0 once the io_count hits 0.
98 */
99int
Benjamin Coddington210c7c12016-01-06 10:40:18 -0500100nfs_iocounter_wait(struct nfs_lock_context *l_ctx)
Trond Myklebust577b4232013-04-08 21:38:12 -0400101{
Peter Zijlstra723c9212018-03-15 11:44:34 +0100102 return wait_var_event_killable(&l_ctx->io_count,
103 !atomic_read(&l_ctx->io_count));
Trond Myklebust577b4232013-04-08 21:38:12 -0400104}
105
Benjamin Coddington7d6ddf82017-04-11 12:50:10 -0400106/**
107 * nfs_async_iocounter_wait - wait on a rpc_waitqueue for I/O
108 * to complete
109 * @task: the rpc_task that should wait
110 * @l_ctx: nfs_lock_context with io_counter to check
111 *
112 * Returns true if there is outstanding I/O to wait on and the
113 * task has been put to sleep.
114 */
115bool
116nfs_async_iocounter_wait(struct rpc_task *task, struct nfs_lock_context *l_ctx)
117{
118 struct inode *inode = d_inode(l_ctx->open_context->dentry);
119 bool ret = false;
120
121 if (atomic_read(&l_ctx->io_count) > 0) {
122 rpc_sleep_on(&NFS_SERVER(inode)->uoc_rpcwaitq, task, NULL);
123 ret = true;
124 }
125
126 if (atomic_read(&l_ctx->io_count) == 0) {
127 rpc_wake_up_queued_task(&NFS_SERVER(inode)->uoc_rpcwaitq, task);
128 ret = false;
129 }
130
131 return ret;
132}
133EXPORT_SYMBOL_GPL(nfs_async_iocounter_wait);
134
Weston Andros Adamson2bfc6e52014-05-15 11:56:45 -0400135/*
Trond Myklebuste00ed892020-03-30 12:40:47 -0400136 * nfs_page_lock_head_request - page lock the head of the page group
137 * @req: any member of the page group
138 */
139struct nfs_page *
140nfs_page_group_lock_head(struct nfs_page *req)
141{
142 struct nfs_page *head = req->wb_head;
143
144 while (!nfs_lock_request(head)) {
145 int ret = nfs_wait_on_request(head);
146 if (ret < 0)
147 return ERR_PTR(ret);
148 }
149 if (head != req)
150 kref_get(&head->wb_kref);
151 return head;
152}
153
154/*
Trond Myklebusta62f8e32020-03-30 11:12:16 -0400155 * nfs_unroll_locks - unlock all newly locked reqs and wait on @req
156 * @head: head request of page group, must be holding head lock
157 * @req: request that couldn't lock and needs to wait on the req bit lock
158 *
159 * This is a helper function for nfs_lock_and_join_requests
160 * returns 0 on success, < 0 on error.
161 */
162static void
163nfs_unroll_locks(struct nfs_page *head, struct nfs_page *req)
164{
165 struct nfs_page *tmp;
166
167 /* relinquish all the locks successfully grabbed this run */
168 for (tmp = head->wb_this_page ; tmp != req; tmp = tmp->wb_this_page) {
169 if (!kref_read(&tmp->wb_kref))
170 continue;
171 nfs_unlock_and_release_request(tmp);
172 }
173}
174
175/*
176 * nfs_page_group_lock_subreq - try to lock a subrequest
177 * @head: head request of page group
178 * @subreq: request to lock
179 *
180 * This is a helper function for nfs_lock_and_join_requests which
181 * must be called with the head request and page group both locked.
182 * On error, it returns with the page group unlocked.
183 */
184static int
185nfs_page_group_lock_subreq(struct nfs_page *head, struct nfs_page *subreq)
186{
187 int ret;
188
189 if (!kref_get_unless_zero(&subreq->wb_kref))
190 return 0;
191 while (!nfs_lock_request(subreq)) {
192 nfs_page_group_unlock(head);
193 ret = nfs_wait_on_request(subreq);
194 if (!ret)
195 ret = nfs_page_group_lock(head);
196 if (ret < 0) {
197 nfs_unroll_locks(head, subreq);
198 nfs_release_request(subreq);
199 return ret;
200 }
201 }
202 return 0;
203}
204
205/*
206 * nfs_page_group_lock_subrequests - try to lock the subrequests
207 * @head: head request of page group
208 *
209 * This is a helper function for nfs_lock_and_join_requests which
Trond Myklebuste00ed892020-03-30 12:40:47 -0400210 * must be called with the head request locked.
Trond Myklebusta62f8e32020-03-30 11:12:16 -0400211 */
212int nfs_page_group_lock_subrequests(struct nfs_page *head)
213{
214 struct nfs_page *subreq;
215 int ret;
216
Trond Myklebuste00ed892020-03-30 12:40:47 -0400217 ret = nfs_page_group_lock(head);
218 if (ret < 0)
219 return ret;
Trond Myklebusta62f8e32020-03-30 11:12:16 -0400220 /* lock each request in the page group */
221 for (subreq = head->wb_this_page; subreq != head;
222 subreq = subreq->wb_this_page) {
223 ret = nfs_page_group_lock_subreq(head, subreq);
224 if (ret < 0)
225 return ret;
226 }
Trond Myklebuste00ed892020-03-30 12:40:47 -0400227 nfs_page_group_unlock(head);
Trond Myklebusta62f8e32020-03-30 11:12:16 -0400228 return 0;
229}
230
231/*
Trond Myklebust08ca8b22020-04-01 13:04:49 -0400232 * nfs_page_set_headlock - set the request PG_HEADLOCK
233 * @req: request that is to be locked
234 *
235 * this lock must be held when modifying req->wb_head
236 *
237 * return 0 on success, < 0 on error
238 */
239int
240nfs_page_set_headlock(struct nfs_page *req)
241{
242 if (!test_and_set_bit(PG_HEADLOCK, &req->wb_flags))
243 return 0;
244
245 set_bit(PG_CONTENDED1, &req->wb_flags);
246 smp_mb__after_atomic();
247 return wait_on_bit_lock(&req->wb_flags, PG_HEADLOCK,
248 TASK_UNINTERRUPTIBLE);
249}
250
251/*
252 * nfs_page_clear_headlock - clear the request PG_HEADLOCK
253 * @req: request that is to be locked
254 */
255void
256nfs_page_clear_headlock(struct nfs_page *req)
257{
258 smp_mb__before_atomic();
259 clear_bit(PG_HEADLOCK, &req->wb_flags);
260 smp_mb__after_atomic();
261 if (!test_bit(PG_CONTENDED1, &req->wb_flags))
262 return;
263 wake_up_bit(&req->wb_flags, PG_HEADLOCK);
264}
265
266/*
Weston Andros Adamson2bfc6e52014-05-15 11:56:45 -0400267 * nfs_page_group_lock - lock the head of the page group
Trond Myklebust08ca8b22020-04-01 13:04:49 -0400268 * @req: request in group that is to be locked
Weston Andros Adamson2bfc6e52014-05-15 11:56:45 -0400269 *
Trond Myklebust1344b7e2017-07-17 10:54:14 -0400270 * this lock must be held when traversing or modifying the page
271 * group list
Weston Andros Adamsone7029202014-07-17 20:42:15 -0400272 *
Trond Myklebust1344b7e2017-07-17 10:54:14 -0400273 * return 0 on success, < 0 on error
Weston Andros Adamson2bfc6e52014-05-15 11:56:45 -0400274 */
Weston Andros Adamsone7029202014-07-17 20:42:15 -0400275int
Trond Myklebust1344b7e2017-07-17 10:54:14 -0400276nfs_page_group_lock(struct nfs_page *req)
Weston Andros Adamson2bfc6e52014-05-15 11:56:45 -0400277{
Trond Myklebust08ca8b22020-04-01 13:04:49 -0400278 int ret;
Weston Andros Adamson2bfc6e52014-05-15 11:56:45 -0400279
Trond Myklebust08ca8b22020-04-01 13:04:49 -0400280 ret = nfs_page_set_headlock(req);
281 if (ret || req->wb_head == req)
282 return ret;
283 return nfs_page_set_headlock(req->wb_head);
Weston Andros Adamson2bfc6e52014-05-15 11:56:45 -0400284}
285
286/*
287 * nfs_page_group_unlock - unlock the head of the page group
Trond Myklebust08ca8b22020-04-01 13:04:49 -0400288 * @req: request in group that is to be unlocked
Weston Andros Adamson2bfc6e52014-05-15 11:56:45 -0400289 */
290void
291nfs_page_group_unlock(struct nfs_page *req)
292{
Trond Myklebust08ca8b22020-04-01 13:04:49 -0400293 if (req != req->wb_head)
294 nfs_page_clear_headlock(req->wb_head);
295 nfs_page_clear_headlock(req);
Weston Andros Adamson2bfc6e52014-05-15 11:56:45 -0400296}
297
298/*
299 * nfs_page_group_sync_on_bit_locked
300 *
301 * must be called with page group lock held
302 */
303static bool
304nfs_page_group_sync_on_bit_locked(struct nfs_page *req, unsigned int bit)
305{
306 struct nfs_page *head = req->wb_head;
307 struct nfs_page *tmp;
308
309 WARN_ON_ONCE(!test_bit(PG_HEADLOCK, &head->wb_flags));
310 WARN_ON_ONCE(test_and_set_bit(bit, &req->wb_flags));
311
312 tmp = req->wb_this_page;
313 while (tmp != req) {
314 if (!test_bit(bit, &tmp->wb_flags))
315 return false;
316 tmp = tmp->wb_this_page;
317 }
318
319 /* true! reset all bits */
320 tmp = req;
321 do {
322 clear_bit(bit, &tmp->wb_flags);
323 tmp = tmp->wb_this_page;
324 } while (tmp != req);
325
326 return true;
327}
328
329/*
330 * nfs_page_group_sync_on_bit - set bit on current request, but only
331 * return true if the bit is set for all requests in page group
332 * @req - request in page group
333 * @bit - PG_* bit that is used to sync page group
334 */
335bool nfs_page_group_sync_on_bit(struct nfs_page *req, unsigned int bit)
336{
337 bool ret;
338
Trond Myklebust1344b7e2017-07-17 10:54:14 -0400339 nfs_page_group_lock(req);
Weston Andros Adamson2bfc6e52014-05-15 11:56:45 -0400340 ret = nfs_page_group_sync_on_bit_locked(req, bit);
341 nfs_page_group_unlock(req);
342
343 return ret;
344}
345
346/*
347 * nfs_page_group_init - Initialize the page group linkage for @req
348 * @req - a new nfs request
349 * @prev - the previous request in page group, or NULL if @req is the first
350 * or only request in the group (the head).
351 */
352static inline void
353nfs_page_group_init(struct nfs_page *req, struct nfs_page *prev)
354{
Weston Andros Adamsoncb1410c2014-11-12 12:08:00 -0500355 struct inode *inode;
Weston Andros Adamson2bfc6e52014-05-15 11:56:45 -0400356 WARN_ON_ONCE(prev == req);
357
358 if (!prev) {
Weston Andros Adamson85710a82014-07-11 10:20:46 -0400359 /* a head request */
Weston Andros Adamson2bfc6e52014-05-15 11:56:45 -0400360 req->wb_head = req;
361 req->wb_this_page = req;
362 } else {
Weston Andros Adamson85710a82014-07-11 10:20:46 -0400363 /* a subrequest */
Weston Andros Adamson2bfc6e52014-05-15 11:56:45 -0400364 WARN_ON_ONCE(prev->wb_this_page != prev->wb_head);
365 WARN_ON_ONCE(!test_bit(PG_HEADLOCK, &prev->wb_head->wb_flags));
366 req->wb_head = prev->wb_head;
367 req->wb_this_page = prev->wb_this_page;
368 prev->wb_this_page = req;
369
Weston Andros Adamson85710a82014-07-11 10:20:46 -0400370 /* All subrequests take a ref on the head request until
371 * nfs_page_group_destroy is called */
372 kref_get(&req->wb_head->wb_kref);
373
Weston Andros Adamsoncb1410c2014-11-12 12:08:00 -0500374 /* grab extra ref and bump the request count if head request
375 * has extra ref from the write/commit path to handle handoff
376 * between write and commit lists. */
Weston Andros Adamson17089a22014-07-11 10:20:45 -0400377 if (test_bit(PG_INODE_REF, &prev->wb_head->wb_flags)) {
Weston Andros Adamsoncb1410c2014-11-12 12:08:00 -0500378 inode = page_file_mapping(req->wb_page)->host;
Weston Andros Adamson17089a22014-07-11 10:20:45 -0400379 set_bit(PG_INODE_REF, &req->wb_flags);
Weston Andros Adamson2bfc6e52014-05-15 11:56:45 -0400380 kref_get(&req->wb_kref);
Trond Myklebusta6b6d5b2017-08-01 15:39:46 -0400381 atomic_long_inc(&NFS_I(inode)->nrequests);
Weston Andros Adamson17089a22014-07-11 10:20:45 -0400382 }
Weston Andros Adamson2bfc6e52014-05-15 11:56:45 -0400383 }
384}
385
386/*
387 * nfs_page_group_destroy - sync the destruction of page groups
388 * @req - request that no longer needs the page group
389 *
390 * releases the page group reference from each member once all
391 * members have called this function.
392 */
393static void
394nfs_page_group_destroy(struct kref *kref)
395{
396 struct nfs_page *req = container_of(kref, struct nfs_page, wb_kref);
Trond Myklebust08fead22017-07-18 19:31:10 -0400397 struct nfs_page *head = req->wb_head;
Weston Andros Adamson2bfc6e52014-05-15 11:56:45 -0400398 struct nfs_page *tmp, *next;
399
400 if (!nfs_page_group_sync_on_bit(req, PG_TEARDOWN))
Trond Myklebust08fead22017-07-18 19:31:10 -0400401 goto out;
Weston Andros Adamson2bfc6e52014-05-15 11:56:45 -0400402
403 tmp = req;
404 do {
405 next = tmp->wb_this_page;
406 /* unlink and free */
407 tmp->wb_this_page = tmp;
408 tmp->wb_head = tmp;
409 nfs_free_request(tmp);
410 tmp = next;
411 } while (tmp != req);
Trond Myklebust08fead22017-07-18 19:31:10 -0400412out:
413 /* subrequests must release the ref on the head request */
414 if (head != req)
415 nfs_release_request(head);
Weston Andros Adamson2bfc6e52014-05-15 11:56:45 -0400416}
417
Trond Myklebustc917cfa2019-04-07 13:59:06 -0400418static struct nfs_page *
419__nfs_create_request(struct nfs_lock_context *l_ctx, struct page *page,
Trond Myklebust28b1d3f2019-04-07 13:59:07 -0400420 unsigned int pgbase, unsigned int offset,
421 unsigned int count)
Trond Myklebustc917cfa2019-04-07 13:59:06 -0400422{
423 struct nfs_page *req;
424 struct nfs_open_context *ctx = l_ctx->open_context;
425
426 if (test_bit(NFS_CONTEXT_BAD, &ctx->flags))
427 return ERR_PTR(-EBADF);
428 /* try to allocate the request struct */
429 req = nfs_page_alloc();
430 if (req == NULL)
431 return ERR_PTR(-ENOMEM);
432
433 req->wb_lock_context = l_ctx;
434 refcount_inc(&l_ctx->count);
435 atomic_inc(&l_ctx->io_count);
436
437 /* Initialize the request struct. Initially, we assume a
438 * long write-back delay. This will be adjusted in
439 * update_nfs_request below if the region is not locked. */
440 req->wb_page = page;
441 if (page) {
442 req->wb_index = page_index(page);
443 get_page(page);
444 }
445 req->wb_offset = offset;
446 req->wb_pgbase = pgbase;
447 req->wb_bytes = count;
Trond Myklebustc917cfa2019-04-07 13:59:06 -0400448 kref_init(&req->wb_kref);
Trond Myklebust33344e02019-04-07 13:59:08 -0400449 req->wb_nio = 0;
Trond Myklebustc917cfa2019-04-07 13:59:06 -0400450 return req;
451}
452
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453/**
454 * nfs_create_request - Create an NFS read/write request.
Chuck Leverc02f5572011-10-25 12:17:43 -0400455 * @ctx: open context to use
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456 * @page: page to write
457 * @offset: starting offset within the page for the write
458 * @count: number of bytes to read/write
459 *
460 * The page must be locked by the caller. This makes sure we never
Jason Uhlenkotta19b89c2007-04-26 17:25:51 -0700461 * create two different requests for the same page.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700462 * User should ensure it is safe to sleep in this function.
463 */
464struct nfs_page *
Weston Andros Adamson8c8f1ac12014-05-15 11:56:42 -0400465nfs_create_request(struct nfs_open_context *ctx, struct page *page,
Trond Myklebust28b1d3f2019-04-07 13:59:07 -0400466 unsigned int offset, unsigned int count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467{
Trond Myklebustc917cfa2019-04-07 13:59:06 -0400468 struct nfs_lock_context *l_ctx = nfs_get_lock_context(ctx);
469 struct nfs_page *ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470
Trond Myklebustc917cfa2019-04-07 13:59:06 -0400471 if (IS_ERR(l_ctx))
Trond Myklebustb3c54de2012-08-13 17:15:50 -0400472 return ERR_CAST(l_ctx);
Trond Myklebust28b1d3f2019-04-07 13:59:07 -0400473 ret = __nfs_create_request(l_ctx, page, offset, offset, count);
474 if (!IS_ERR(ret))
475 nfs_page_group_init(ret, NULL);
Trond Myklebustc917cfa2019-04-07 13:59:06 -0400476 nfs_put_lock_context(l_ctx);
477 return ret;
478}
Jeff Layton015f0212010-10-28 10:10:37 -0400479
Trond Myklebustc917cfa2019-04-07 13:59:06 -0400480static struct nfs_page *
Trond Myklebust44a65a02020-03-31 18:27:26 -0400481nfs_create_subreq(struct nfs_page *req,
482 unsigned int pgbase,
483 unsigned int offset,
Trond Myklebustc917cfa2019-04-07 13:59:06 -0400484 unsigned int count)
485{
Trond Myklebust44a65a02020-03-31 18:27:26 -0400486 struct nfs_page *last;
Trond Myklebustc917cfa2019-04-07 13:59:06 -0400487 struct nfs_page *ret;
488
Trond Myklebust28b1d3f2019-04-07 13:59:07 -0400489 ret = __nfs_create_request(req->wb_lock_context, req->wb_page,
Trond Myklebustc917cfa2019-04-07 13:59:06 -0400490 pgbase, offset, count);
491 if (!IS_ERR(ret)) {
Trond Myklebust44a65a02020-03-31 18:27:26 -0400492 /* find the last request */
493 for (last = req->wb_head;
494 last->wb_this_page != req->wb_head;
495 last = last->wb_this_page)
496 ;
497
Trond Myklebustc917cfa2019-04-07 13:59:06 -0400498 nfs_lock_request(ret);
499 ret->wb_index = req->wb_index;
Trond Myklebust28b1d3f2019-04-07 13:59:07 -0400500 nfs_page_group_init(ret, last);
Trond Myklebust33344e02019-04-07 13:59:08 -0400501 ret->wb_nio = req->wb_nio;
Anna Schumaker67911c82016-01-19 14:37:30 -0500502 }
Trond Myklebustc917cfa2019-04-07 13:59:06 -0400503 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504}
505
506/**
Trond Myklebust1d1afcb2012-05-09 14:04:55 -0400507 * nfs_unlock_request - Unlock request and wake up sleepers.
Trond Myklebust302fad72019-02-18 13:32:38 -0500508 * @req: pointer to request
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509 */
Trond Myklebust1d1afcb2012-05-09 14:04:55 -0400510void nfs_unlock_request(struct nfs_page *req)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511{
512 if (!NFS_WBACK_BUSY(req)) {
513 printk(KERN_ERR "NFS: Invalid unlock attempted\n");
514 BUG();
515 }
Peter Zijlstra4e857c52014-03-17 18:06:10 +0100516 smp_mb__before_atomic();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517 clear_bit(PG_BUSY, &req->wb_flags);
Peter Zijlstra4e857c52014-03-17 18:06:10 +0100518 smp_mb__after_atomic();
Trond Myklebustb4f937c2017-07-11 17:53:48 -0400519 if (!test_bit(PG_CONTENDED2, &req->wb_flags))
520 return;
Trond Myklebust464a98b2005-06-22 17:16:21 +0000521 wake_up_bit(&req->wb_flags, PG_BUSY);
Trond Myklebust3aff4eb2012-05-09 14:30:35 -0400522}
523
524/**
Trond Myklebust1d1afcb2012-05-09 14:04:55 -0400525 * nfs_unlock_and_release_request - Unlock request and release the nfs_page
Trond Myklebust302fad72019-02-18 13:32:38 -0500526 * @req: pointer to request
Trond Myklebust3aff4eb2012-05-09 14:30:35 -0400527 */
Trond Myklebust1d1afcb2012-05-09 14:04:55 -0400528void nfs_unlock_and_release_request(struct nfs_page *req)
Trond Myklebust3aff4eb2012-05-09 14:30:35 -0400529{
Trond Myklebust1d1afcb2012-05-09 14:04:55 -0400530 nfs_unlock_request(req);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531 nfs_release_request(req);
532}
533
Trond Myklebust4d65c522011-03-25 14:15:11 -0400534/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535 * nfs_clear_request - Free up all resources allocated to the request
536 * @req:
537 *
Trond Myklebustbb6fbc42010-03-11 09:19:35 -0500538 * Release page and open context resources associated with a read/write
539 * request after it has completed.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540 */
Trond Myklebust4d65c522011-03-25 14:15:11 -0400541static void nfs_clear_request(struct nfs_page *req)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542{
Trond Myklebustcd52ed32006-03-20 13:44:04 -0500543 struct page *page = req->wb_page;
Trond Myklebustf11ac8d2010-06-25 16:35:53 -0400544 struct nfs_lock_context *l_ctx = req->wb_lock_context;
Trond Myklebustc79d1832019-04-07 13:59:12 -0400545 struct nfs_open_context *ctx;
Trond Myklebustbb6fbc42010-03-11 09:19:35 -0500546
Trond Myklebustcd52ed32006-03-20 13:44:04 -0500547 if (page != NULL) {
Kirill A. Shutemov09cbfea2016-04-01 15:29:47 +0300548 put_page(page);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 req->wb_page = NULL;
550 }
Trond Myklebustf11ac8d2010-06-25 16:35:53 -0400551 if (l_ctx != NULL) {
Benjamin Coddington7d6ddf82017-04-11 12:50:10 -0400552 if (atomic_dec_and_test(&l_ctx->io_count)) {
Peter Zijlstra723c9212018-03-15 11:44:34 +0100553 wake_up_var(&l_ctx->io_count);
Trond Myklebustc79d1832019-04-07 13:59:12 -0400554 ctx = l_ctx->open_context;
Benjamin Coddington7d6ddf82017-04-11 12:50:10 -0400555 if (test_bit(NFS_CONTEXT_UNLOCK, &ctx->flags))
556 rpc_wake_up(&NFS_SERVER(d_inode(ctx->dentry))->uoc_rpcwaitq);
557 }
Trond Myklebustf11ac8d2010-06-25 16:35:53 -0400558 nfs_put_lock_context(l_ctx);
559 req->wb_lock_context = NULL;
560 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561}
562
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563/**
564 * nfs_release_request - Release the count on an NFS read/write request
565 * @req: request to release
566 *
567 * Note: Should never be called with the spinlock held!
568 */
Weston Andros Adamsond4581382014-07-11 10:20:48 -0400569void nfs_free_request(struct nfs_page *req)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570{
Weston Andros Adamson2bfc6e52014-05-15 11:56:45 -0400571 WARN_ON_ONCE(req->wb_this_page != req);
572
573 /* extra debug: make sure no sync bits are still set */
574 WARN_ON_ONCE(test_bit(PG_TEARDOWN, &req->wb_flags));
Weston Andros Adamson67d03382014-05-15 11:56:46 -0400575 WARN_ON_ONCE(test_bit(PG_UNLOCKPAGE, &req->wb_flags));
576 WARN_ON_ONCE(test_bit(PG_UPTODATE, &req->wb_flags));
Weston Andros Adamson20633f02014-05-15 11:56:47 -0400577 WARN_ON_ONCE(test_bit(PG_WB_END, &req->wb_flags));
578 WARN_ON_ONCE(test_bit(PG_REMOVE, &req->wb_flags));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579
Trond Myklebustbb6fbc42010-03-11 09:19:35 -0500580 /* Release struct file and open context */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581 nfs_clear_request(req);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582 nfs_page_free(req);
583}
584
Trond Myklebustc03b4022007-06-17 13:26:38 -0400585void nfs_release_request(struct nfs_page *req)
586{
Weston Andros Adamson2bfc6e52014-05-15 11:56:45 -0400587 kref_put(&req->wb_kref, nfs_page_group_destroy);
Trond Myklebust9f557cd2010-02-03 08:27:22 -0500588}
Trond Myklebust2ce209c2017-08-01 17:29:29 -0400589EXPORT_SYMBOL_GPL(nfs_release_request);
Trond Myklebust9f557cd2010-02-03 08:27:22 -0500590
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591/**
592 * nfs_wait_on_request - Wait for a request to complete.
593 * @req: request to wait upon.
594 *
Matthew Wilcox150030b2007-12-06 16:24:39 -0500595 * Interruptible by fatal signals only.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596 * The user is responsible for holding a count on the request.
597 */
598int
599nfs_wait_on_request(struct nfs_page *req)
600{
Trond Myklebustb4f937c2017-07-11 17:53:48 -0400601 if (!test_bit(PG_BUSY, &req->wb_flags))
602 return 0;
603 set_bit(PG_CONTENDED2, &req->wb_flags);
604 smp_mb__after_atomic();
NeilBrown74316202014-07-07 15:16:04 +1000605 return wait_on_bit_io(&req->wb_flags, PG_BUSY,
606 TASK_UNINTERRUPTIBLE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607}
Trond Myklebust2ce209c2017-08-01 17:29:29 -0400608EXPORT_SYMBOL_GPL(nfs_wait_on_request);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609
Weston Andros Adamsonb4fdac12014-05-15 11:56:43 -0400610/*
611 * nfs_generic_pg_test - determine if requests can be coalesced
612 * @desc: pointer to descriptor
613 * @prev: previous request in desc, or NULL
614 * @req: this request
615 *
Pavel Tikhomirovac0aa5e2018-11-14 11:12:05 +0300616 * Returns zero if @req cannot be coalesced into @desc, otherwise it returns
Weston Andros Adamsonb4fdac12014-05-15 11:56:43 -0400617 * the size of the request.
618 */
619size_t nfs_generic_pg_test(struct nfs_pageio_descriptor *desc,
620 struct nfs_page *prev, struct nfs_page *req)
Boaz Harrosh5b36c7d2011-05-29 11:45:39 +0300621{
Peng Tao48d635f2014-11-10 08:35:35 +0800622 struct nfs_pgio_mirror *mirror = nfs_pgio_current_mirror(desc);
Weston Andros Adamsona7d42dd2014-09-19 10:55:07 -0400623
624
625 if (mirror->pg_count > mirror->pg_bsize) {
Weston Andros Adamsonf0cb9ab2014-05-15 11:56:52 -0400626 /* should never happen */
627 WARN_ON_ONCE(1);
Boaz Harrosh5b36c7d2011-05-29 11:45:39 +0300628 return 0;
Weston Andros Adamsonf0cb9ab2014-05-15 11:56:52 -0400629 }
Boaz Harrosh5b36c7d2011-05-29 11:45:39 +0300630
Christoph Hellwig2e11f822014-08-21 11:09:17 -0500631 /*
632 * Limit the request size so that we can still allocate a page array
633 * for it without upsetting the slab allocator.
634 */
Weston Andros Adamsona7d42dd2014-09-19 10:55:07 -0400635 if (((mirror->pg_count + req->wb_bytes) >> PAGE_SHIFT) *
Peng Tao048883e2015-09-11 11:14:06 +0800636 sizeof(struct page *) > PAGE_SIZE)
Christoph Hellwig2e11f822014-08-21 11:09:17 -0500637 return 0;
638
Weston Andros Adamsona7d42dd2014-09-19 10:55:07 -0400639 return min(mirror->pg_bsize - mirror->pg_count, (size_t)req->wb_bytes);
Boaz Harrosh5b36c7d2011-05-29 11:45:39 +0300640}
Benny Halevy19345cb2011-06-19 18:33:46 -0400641EXPORT_SYMBOL_GPL(nfs_generic_pg_test);
Boaz Harrosh5b36c7d2011-05-29 11:45:39 +0300642
Weston Andros Adamson1e7f3a42014-06-09 11:48:33 -0400643struct nfs_pgio_header *nfs_pgio_header_alloc(const struct nfs_rw_ops *ops)
Anna Schumaker00bfa302014-05-06 09:12:29 -0400644{
Weston Andros Adamson1e7f3a42014-06-09 11:48:33 -0400645 struct nfs_pgio_header *hdr = ops->rw_alloc_header();
Anna Schumaker00bfa302014-05-06 09:12:29 -0400646
Weston Andros Adamson1e7f3a42014-06-09 11:48:33 -0400647 if (hdr) {
Anna Schumaker4a0de552014-05-06 09:12:30 -0400648 INIT_LIST_HEAD(&hdr->pages);
Anna Schumaker4a0de552014-05-06 09:12:30 -0400649 hdr->rw_ops = ops;
650 }
Weston Andros Adamson1e7f3a42014-06-09 11:48:33 -0400651 return hdr;
Anna Schumaker4a0de552014-05-06 09:12:30 -0400652}
Weston Andros Adamson1e7f3a42014-06-09 11:48:33 -0400653EXPORT_SYMBOL_GPL(nfs_pgio_header_alloc);
Anna Schumaker4a0de552014-05-06 09:12:30 -0400654
Anna Schumaker4a0de552014-05-06 09:12:30 -0400655/**
Weston Andros Adamson4714fb52014-06-09 11:48:37 -0400656 * nfs_pgio_data_destroy - make @hdr suitable for reuse
657 *
658 * Frees memory and releases refs from nfs_generic_pgio, so that it may
659 * be called again.
660 *
661 * @hdr: A header that has had nfs_generic_pgio called
Anna Schumaker00bfa302014-05-06 09:12:29 -0400662 */
Trond Myklebust196639e2017-09-08 21:28:11 -0400663static void nfs_pgio_data_destroy(struct nfs_pgio_header *hdr)
Anna Schumaker00bfa302014-05-06 09:12:29 -0400664{
Trond Myklebust3caa0c62014-10-13 10:26:43 -0400665 if (hdr->args.context)
666 put_nfs_open_context(hdr->args.context);
Weston Andros Adamsond45f60c2014-06-09 11:48:35 -0400667 if (hdr->page_array.pagevec != hdr->page_array.page_array)
668 kfree(hdr->page_array.pagevec);
Anna Schumaker00bfa302014-05-06 09:12:29 -0400669}
Trond Myklebust196639e2017-09-08 21:28:11 -0400670
671/*
672 * nfs_pgio_header_free - Free a read or write header
673 * @hdr: The header to free
674 */
675void nfs_pgio_header_free(struct nfs_pgio_header *hdr)
676{
677 nfs_pgio_data_destroy(hdr);
678 hdr->rw_ops->rw_free_header(hdr);
679}
680EXPORT_SYMBOL_GPL(nfs_pgio_header_free);
Anna Schumaker00bfa302014-05-06 09:12:29 -0400681
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682/**
Anna Schumakerce595152014-05-06 09:12:34 -0400683 * nfs_pgio_rpcsetup - Set up arguments for a pageio call
Weston Andros Adamsond45f60c2014-06-09 11:48:35 -0400684 * @hdr: The pageio hdr
Anna Schumakerce595152014-05-06 09:12:34 -0400685 * @count: Number of bytes to read
Anna Schumakerce595152014-05-06 09:12:34 -0400686 * @how: How to commit data (writes only)
687 * @cinfo: Commit information for the call (writes only)
688 */
Weston Andros Adamsond45f60c2014-06-09 11:48:35 -0400689static void nfs_pgio_rpcsetup(struct nfs_pgio_header *hdr,
Benjamin Coddingtone5457352017-12-08 12:52:37 -0500690 unsigned int count,
Anna Schumakerce595152014-05-06 09:12:34 -0400691 int how, struct nfs_commit_info *cinfo)
692{
Weston Andros Adamsond45f60c2014-06-09 11:48:35 -0400693 struct nfs_page *req = hdr->req;
Anna Schumakerce595152014-05-06 09:12:34 -0400694
695 /* Set up the RPC argument and reply structs
Weston Andros Adamsond45f60c2014-06-09 11:48:35 -0400696 * NB: take care not to mess about with hdr->commit et al. */
Anna Schumakerce595152014-05-06 09:12:34 -0400697
Weston Andros Adamsond45f60c2014-06-09 11:48:35 -0400698 hdr->args.fh = NFS_FH(hdr->inode);
Benjamin Coddingtone5457352017-12-08 12:52:37 -0500699 hdr->args.offset = req_offset(req);
Anna Schumakerce595152014-05-06 09:12:34 -0400700 /* pnfs_set_layoutcommit needs this */
Weston Andros Adamsond45f60c2014-06-09 11:48:35 -0400701 hdr->mds_offset = hdr->args.offset;
Benjamin Coddingtone5457352017-12-08 12:52:37 -0500702 hdr->args.pgbase = req->wb_pgbase;
Weston Andros Adamsond45f60c2014-06-09 11:48:35 -0400703 hdr->args.pages = hdr->page_array.pagevec;
704 hdr->args.count = count;
Trond Myklebust9fcd5962019-04-07 13:59:11 -0400705 hdr->args.context = get_nfs_open_context(nfs_req_openctx(req));
Weston Andros Adamsond45f60c2014-06-09 11:48:35 -0400706 hdr->args.lock_context = req->wb_lock_context;
707 hdr->args.stable = NFS_UNSTABLE;
Anna Schumakerce595152014-05-06 09:12:34 -0400708 switch (how & (FLUSH_STABLE | FLUSH_COND_STABLE)) {
709 case 0:
710 break;
711 case FLUSH_COND_STABLE:
712 if (nfs_reqs_to_commit(cinfo))
713 break;
Gustavo A. R. Silvadf561f662020-08-23 17:36:59 -0500714 fallthrough;
Anna Schumakerce595152014-05-06 09:12:34 -0400715 default:
Weston Andros Adamsond45f60c2014-06-09 11:48:35 -0400716 hdr->args.stable = NFS_FILE_SYNC;
Anna Schumakerce595152014-05-06 09:12:34 -0400717 }
718
Weston Andros Adamsond45f60c2014-06-09 11:48:35 -0400719 hdr->res.fattr = &hdr->fattr;
Trond Myklebust17d8c5d2019-08-14 14:19:09 -0400720 hdr->res.count = 0;
Weston Andros Adamsond45f60c2014-06-09 11:48:35 -0400721 hdr->res.eof = 0;
Weston Andros Adamsonc65e6252014-06-09 11:48:36 -0400722 hdr->res.verf = &hdr->verf;
Weston Andros Adamsond45f60c2014-06-09 11:48:35 -0400723 nfs_fattr_init(&hdr->fattr);
Anna Schumakerce595152014-05-06 09:12:34 -0400724}
725
726/**
Weston Andros Adamsond45f60c2014-06-09 11:48:35 -0400727 * nfs_pgio_prepare - Prepare pageio hdr to go over the wire
Anna Schumakera4cdda52014-05-06 09:12:31 -0400728 * @task: The current task
Weston Andros Adamsond45f60c2014-06-09 11:48:35 -0400729 * @calldata: pageio header to prepare
Anna Schumakera4cdda52014-05-06 09:12:31 -0400730 */
Anna Schumaker6f92fa42014-05-06 09:12:33 -0400731static void nfs_pgio_prepare(struct rpc_task *task, void *calldata)
Anna Schumakera4cdda52014-05-06 09:12:31 -0400732{
Weston Andros Adamsond45f60c2014-06-09 11:48:35 -0400733 struct nfs_pgio_header *hdr = calldata;
Anna Schumakera4cdda52014-05-06 09:12:31 -0400734 int err;
Weston Andros Adamsond45f60c2014-06-09 11:48:35 -0400735 err = NFS_PROTO(hdr->inode)->pgio_rpc_prepare(task, hdr);
Anna Schumakera4cdda52014-05-06 09:12:31 -0400736 if (err)
737 rpc_exit(task, err);
738}
739
Weston Andros Adamsond45f60c2014-06-09 11:48:35 -0400740int nfs_initiate_pgio(struct rpc_clnt *clnt, struct nfs_pgio_header *hdr,
NeilBrowna52458b2018-12-03 11:30:31 +1100741 const struct cred *cred, const struct nfs_rpc_ops *rpc_ops,
Anna Schumaker1ed26f32014-05-06 09:12:37 -0400742 const struct rpc_call_ops *call_ops, int how, int flags)
743{
744 struct rpc_task *task;
745 struct rpc_message msg = {
Weston Andros Adamsond45f60c2014-06-09 11:48:35 -0400746 .rpc_argp = &hdr->args,
747 .rpc_resp = &hdr->res,
Peng Tao46a5ab42014-06-13 23:02:25 +0800748 .rpc_cred = cred,
Anna Schumaker1ed26f32014-05-06 09:12:37 -0400749 };
750 struct rpc_task_setup task_setup_data = {
751 .rpc_client = clnt,
Weston Andros Adamsond45f60c2014-06-09 11:48:35 -0400752 .task = &hdr->task,
Anna Schumaker1ed26f32014-05-06 09:12:37 -0400753 .rpc_message = &msg,
754 .callback_ops = call_ops,
Weston Andros Adamsond45f60c2014-06-09 11:48:35 -0400755 .callback_data = hdr,
Anna Schumaker1ed26f32014-05-06 09:12:37 -0400756 .workqueue = nfsiod_workqueue,
Trond Myklebust4fa7ef62020-05-13 09:55:36 -0400757 .flags = RPC_TASK_ASYNC | flags,
Anna Schumaker1ed26f32014-05-06 09:12:37 -0400758 };
Anna Schumaker1ed26f32014-05-06 09:12:37 -0400759
Tom Haynesabde71f2014-06-09 13:12:20 -0700760 hdr->rw_ops->rw_initiate(hdr, &msg, rpc_ops, &task_setup_data, how);
Anna Schumaker1ed26f32014-05-06 09:12:37 -0400761
Kinglong Meeb4839eb2015-07-01 12:00:13 +0800762 dprintk("NFS: initiated pgio call "
Anna Schumaker1ed26f32014-05-06 09:12:37 -0400763 "(req %s/%llu, %u bytes @ offset %llu)\n",
Anna Schumaker343ae532014-06-20 13:30:26 -0400764 hdr->inode->i_sb->s_id,
765 (unsigned long long)NFS_FILEID(hdr->inode),
Weston Andros Adamsond45f60c2014-06-09 11:48:35 -0400766 hdr->args.count,
767 (unsigned long long)hdr->args.offset);
Anna Schumaker1ed26f32014-05-06 09:12:37 -0400768
769 task = rpc_run_task(&task_setup_data);
Trond Myklebust1de3af92020-03-28 11:39:29 -0400770 if (IS_ERR(task))
771 return PTR_ERR(task);
Anna Schumaker1ed26f32014-05-06 09:12:37 -0400772 rpc_put_task(task);
Trond Myklebust1de3af92020-03-28 11:39:29 -0400773 return 0;
Anna Schumaker1ed26f32014-05-06 09:12:37 -0400774}
775EXPORT_SYMBOL_GPL(nfs_initiate_pgio);
776
Anna Schumakera4cdda52014-05-06 09:12:31 -0400777/**
Anna Schumaker844c9e62014-05-06 09:12:35 -0400778 * nfs_pgio_error - Clean up from a pageio error
Anna Schumaker844c9e62014-05-06 09:12:35 -0400779 * @hdr: pageio header
780 */
Peng Tao2bff2282015-12-05 02:03:17 +0800781static void nfs_pgio_error(struct nfs_pgio_header *hdr)
Anna Schumaker844c9e62014-05-06 09:12:35 -0400782{
Anna Schumaker844c9e62014-05-06 09:12:35 -0400783 set_bit(NFS_IOHDR_REDO, &hdr->flags);
Weston Andros Adamson4714fb52014-06-09 11:48:37 -0400784 hdr->completion_ops->completion(hdr);
Anna Schumaker844c9e62014-05-06 09:12:35 -0400785}
786
787/**
Anna Schumakera4cdda52014-05-06 09:12:31 -0400788 * nfs_pgio_release - Release pageio data
Weston Andros Adamsond45f60c2014-06-09 11:48:35 -0400789 * @calldata: The pageio header to release
Anna Schumakera4cdda52014-05-06 09:12:31 -0400790 */
Anna Schumaker6f92fa42014-05-06 09:12:33 -0400791static void nfs_pgio_release(void *calldata)
Anna Schumakera4cdda52014-05-06 09:12:31 -0400792{
Weston Andros Adamsond45f60c2014-06-09 11:48:35 -0400793 struct nfs_pgio_header *hdr = calldata;
Weston Andros Adamson4714fb52014-06-09 11:48:37 -0400794 hdr->completion_ops->completion(hdr);
Anna Schumakera4cdda52014-05-06 09:12:31 -0400795}
796
Weston Andros Adamsona7d42dd2014-09-19 10:55:07 -0400797static void nfs_pageio_mirror_init(struct nfs_pgio_mirror *mirror,
798 unsigned int bsize)
799{
800 INIT_LIST_HEAD(&mirror->pg_list);
801 mirror->pg_bytes_written = 0;
802 mirror->pg_count = 0;
803 mirror->pg_bsize = bsize;
804 mirror->pg_base = 0;
805 mirror->pg_recoalesce = 0;
806}
807
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808/**
Trond Myklebustd8a5ad72007-04-02 18:48:28 -0400809 * nfs_pageio_init - initialise a page io descriptor
810 * @desc: pointer to descriptor
Trond Myklebustbcb71bb2007-04-02 18:48:28 -0400811 * @inode: pointer to inode
Yijing Wangdfad7002015-06-18 19:37:13 +0800812 * @pg_ops: pointer to pageio operations
813 * @compl_ops: pointer to pageio completion operations
814 * @rw_ops: pointer to nfs read/write operations
Trond Myklebustbcb71bb2007-04-02 18:48:28 -0400815 * @bsize: io block size
816 * @io_flags: extra parameters for the io function
Trond Myklebustd8a5ad72007-04-02 18:48:28 -0400817 */
Trond Myklebustbcb71bb2007-04-02 18:48:28 -0400818void nfs_pageio_init(struct nfs_pageio_descriptor *desc,
819 struct inode *inode,
Trond Myklebust1751c362011-06-10 13:30:23 -0400820 const struct nfs_pageio_ops *pg_ops,
Fred Isaman061ae2e2012-04-20 14:47:48 -0400821 const struct nfs_pgio_completion_ops *compl_ops,
Anna Schumaker4a0de552014-05-06 09:12:30 -0400822 const struct nfs_rw_ops *rw_ops,
Trond Myklebust84dde762007-05-04 14:44:06 -0400823 size_t bsize,
Trond Myklebust3bde7af2017-08-20 11:33:25 -0400824 int io_flags)
Trond Myklebustd8a5ad72007-04-02 18:48:28 -0400825{
Trond Myklebustb31268a2011-03-21 17:02:00 -0400826 desc->pg_moreio = 0;
Trond Myklebustbcb71bb2007-04-02 18:48:28 -0400827 desc->pg_inode = inode;
Trond Myklebust1751c362011-06-10 13:30:23 -0400828 desc->pg_ops = pg_ops;
Fred Isaman061ae2e2012-04-20 14:47:48 -0400829 desc->pg_completion_ops = compl_ops;
Anna Schumaker4a0de552014-05-06 09:12:30 -0400830 desc->pg_rw_ops = rw_ops;
Trond Myklebustbcb71bb2007-04-02 18:48:28 -0400831 desc->pg_ioflags = io_flags;
832 desc->pg_error = 0;
Fred Isaman94ad1c82011-03-01 01:34:14 +0000833 desc->pg_lseg = NULL;
Trond Myklebust919e3bd2017-06-20 19:35:37 -0400834 desc->pg_io_completion = NULL;
Fred Isaman584aa812012-04-20 14:47:51 -0400835 desc->pg_dreq = NULL;
Weston Andros Adamsona7d42dd2014-09-19 10:55:07 -0400836 desc->pg_bsize = bsize;
837
838 desc->pg_mirror_count = 1;
839 desc->pg_mirror_idx = 0;
840
Trond Myklebust14abcb02017-08-19 10:10:34 -0400841 desc->pg_mirrors_dynamic = NULL;
842 desc->pg_mirrors = desc->pg_mirrors_static;
843 nfs_pageio_mirror_init(&desc->pg_mirrors[0], bsize);
Trond Myklebust33344e02019-04-07 13:59:08 -0400844 desc->pg_maxretrans = 0;
Trond Myklebustd8a5ad72007-04-02 18:48:28 -0400845}
846
Anna Schumaker0eecb212014-05-06 09:12:32 -0400847/**
848 * nfs_pgio_result - Basic pageio error handling
849 * @task: The task that ran
Weston Andros Adamsond45f60c2014-06-09 11:48:35 -0400850 * @calldata: Pageio header to check
Anna Schumaker0eecb212014-05-06 09:12:32 -0400851 */
Anna Schumaker6f92fa42014-05-06 09:12:33 -0400852static void nfs_pgio_result(struct rpc_task *task, void *calldata)
Anna Schumaker0eecb212014-05-06 09:12:32 -0400853{
Weston Andros Adamsond45f60c2014-06-09 11:48:35 -0400854 struct nfs_pgio_header *hdr = calldata;
855 struct inode *inode = hdr->inode;
Anna Schumaker0eecb212014-05-06 09:12:32 -0400856
857 dprintk("NFS: %s: %5u, (status %d)\n", __func__,
858 task->tk_pid, task->tk_status);
859
Weston Andros Adamsond45f60c2014-06-09 11:48:35 -0400860 if (hdr->rw_ops->rw_done(task, hdr, inode) != 0)
Anna Schumaker0eecb212014-05-06 09:12:32 -0400861 return;
862 if (task->tk_status < 0)
Weston Andros Adamsond45f60c2014-06-09 11:48:35 -0400863 nfs_set_pgio_error(hdr, task->tk_status, hdr->args.offset);
Anna Schumaker0eecb212014-05-06 09:12:32 -0400864 else
Weston Andros Adamsond45f60c2014-06-09 11:48:35 -0400865 hdr->rw_ops->rw_result(task, hdr);
Anna Schumaker0eecb212014-05-06 09:12:32 -0400866}
867
Anna Schumakeref2c4882014-05-06 09:12:36 -0400868/*
Anna Schumakeref2c4882014-05-06 09:12:36 -0400869 * Create an RPC task for the given read or write request and kick it.
870 * The page must have been locked by the caller.
871 *
872 * It may happen that the page we're passed is not marked dirty.
873 * This is the case if nfs_updatepage detects a conflicting request
874 * that has been written but not committed.
875 */
Weston Andros Adamsonf0cb9ab2014-05-15 11:56:52 -0400876int nfs_generic_pgio(struct nfs_pageio_descriptor *desc,
877 struct nfs_pgio_header *hdr)
Anna Schumakeref2c4882014-05-06 09:12:36 -0400878{
Peng Tao48d635f2014-11-10 08:35:35 +0800879 struct nfs_pgio_mirror *mirror = nfs_pgio_current_mirror(desc);
Weston Andros Adamsona7d42dd2014-09-19 10:55:07 -0400880
Anna Schumakeref2c4882014-05-06 09:12:36 -0400881 struct nfs_page *req;
Weston Andros Adamsonbba5c182014-08-14 17:39:32 -0400882 struct page **pages,
883 *last_page;
Weston Andros Adamsona7d42dd2014-09-19 10:55:07 -0400884 struct list_head *head = &mirror->pg_list;
Anna Schumakeref2c4882014-05-06 09:12:36 -0400885 struct nfs_commit_info cinfo;
Benjamin Coddington8ef9b0b2017-04-19 10:11:34 -0400886 struct nfs_page_array *pg_array = &hdr->page_array;
Weston Andros Adamsonbba5c182014-08-14 17:39:32 -0400887 unsigned int pagecount, pageused;
Benjamin Coddingtonae97aa52017-04-19 10:11:33 -0400888 gfp_t gfp_flags = GFP_KERNEL;
Anna Schumakeref2c4882014-05-06 09:12:36 -0400889
Weston Andros Adamsona7d42dd2014-09-19 10:55:07 -0400890 pagecount = nfs_page_array_len(mirror->pg_base, mirror->pg_count);
Benjamin Coddington2eb3aea7d2017-06-09 11:03:23 -0400891 pg_array->npages = pagecount;
Benjamin Coddington8ef9b0b2017-04-19 10:11:34 -0400892
893 if (pagecount <= ARRAY_SIZE(pg_array->page_array))
894 pg_array->pagevec = pg_array->page_array;
895 else {
Benjamin Coddington8ef9b0b2017-04-19 10:11:34 -0400896 pg_array->pagevec = kcalloc(pagecount, sizeof(struct page *), gfp_flags);
897 if (!pg_array->pagevec) {
898 pg_array->npages = 0;
899 nfs_pgio_error(hdr);
900 desc->pg_error = -ENOMEM;
901 return desc->pg_error;
902 }
Peng Tao2bff2282015-12-05 02:03:17 +0800903 }
Anna Schumakeref2c4882014-05-06 09:12:36 -0400904
905 nfs_init_cinfo(&cinfo, desc->pg_inode, desc->pg_dreq);
Weston Andros Adamsond45f60c2014-06-09 11:48:35 -0400906 pages = hdr->page_array.pagevec;
Weston Andros Adamsonbba5c182014-08-14 17:39:32 -0400907 last_page = NULL;
908 pageused = 0;
Anna Schumakeref2c4882014-05-06 09:12:36 -0400909 while (!list_empty(head)) {
910 req = nfs_list_entry(head->next);
Trond Myklebust078b5fd2019-02-18 11:35:54 -0500911 nfs_list_move_request(req, &hdr->pages);
Weston Andros Adamsonbba5c182014-08-14 17:39:32 -0400912
Weston Andros Adamsonbba5c182014-08-14 17:39:32 -0400913 if (!last_page || last_page != req->wb_page) {
Weston Andros Adamsonbba5c182014-08-14 17:39:32 -0400914 pageused++;
Trond Myklebustb8fb9c32014-10-13 10:56:12 -0400915 if (pageused > pagecount)
916 break;
917 *pages++ = last_page = req->wb_page;
Weston Andros Adamsonbba5c182014-08-14 17:39:32 -0400918 }
Anna Schumakeref2c4882014-05-06 09:12:36 -0400919 }
Peng Tao2bff2282015-12-05 02:03:17 +0800920 if (WARN_ON_ONCE(pageused != pagecount)) {
921 nfs_pgio_error(hdr);
922 desc->pg_error = -EINVAL;
923 return desc->pg_error;
924 }
Anna Schumakeref2c4882014-05-06 09:12:36 -0400925
926 if ((desc->pg_ioflags & FLUSH_COND_STABLE) &&
927 (desc->pg_moreio || nfs_reqs_to_commit(&cinfo)))
928 desc->pg_ioflags &= ~FLUSH_COND_STABLE;
929
930 /* Set up the argument struct */
Benjamin Coddingtone5457352017-12-08 12:52:37 -0500931 nfs_pgio_rpcsetup(hdr, mirror->pg_count, desc->pg_ioflags, &cinfo);
Anna Schumakeref2c4882014-05-06 09:12:36 -0400932 desc->pg_rpc_callops = &nfs_pgio_common_ops;
933 return 0;
934}
Weston Andros Adamsonf0cb9ab2014-05-15 11:56:52 -0400935EXPORT_SYMBOL_GPL(nfs_generic_pgio);
Anna Schumakeref2c4882014-05-06 09:12:36 -0400936
Anna Schumaker41d8d5b2014-05-06 09:12:40 -0400937static int nfs_generic_pg_pgios(struct nfs_pageio_descriptor *desc)
Anna Schumakercf485fc2014-05-06 09:12:39 -0400938{
Anna Schumakercf485fc2014-05-06 09:12:39 -0400939 struct nfs_pgio_header *hdr;
940 int ret;
941
Weston Andros Adamson1e7f3a42014-06-09 11:48:33 -0400942 hdr = nfs_pgio_header_alloc(desc->pg_rw_ops);
943 if (!hdr) {
Peng Tao2bff2282015-12-05 02:03:17 +0800944 desc->pg_error = -ENOMEM;
945 return desc->pg_error;
Anna Schumakercf485fc2014-05-06 09:12:39 -0400946 }
Weston Andros Adamson1e7f3a42014-06-09 11:48:33 -0400947 nfs_pgheader_init(desc, hdr, nfs_pgio_header_free);
Anna Schumakercf485fc2014-05-06 09:12:39 -0400948 ret = nfs_generic_pgio(desc, hdr);
949 if (ret == 0)
Weston Andros Adamson7f714722014-05-15 11:56:53 -0400950 ret = nfs_initiate_pgio(NFS_CLIENT(hdr->inode),
Peng Tao46a5ab42014-06-13 23:02:25 +0800951 hdr,
952 hdr->cred,
953 NFS_PROTO(hdr->inode),
Tom Haynesabde71f2014-06-09 13:12:20 -0700954 desc->pg_rpc_callops,
Trond Myklebust4fa7ef62020-05-13 09:55:36 -0400955 desc->pg_ioflags,
956 RPC_TASK_CRED_NOREF);
Anna Schumakercf485fc2014-05-06 09:12:39 -0400957 return ret;
958}
959
Trond Myklebust14abcb02017-08-19 10:10:34 -0400960static struct nfs_pgio_mirror *
961nfs_pageio_alloc_mirrors(struct nfs_pageio_descriptor *desc,
962 unsigned int mirror_count)
963{
964 struct nfs_pgio_mirror *ret;
965 unsigned int i;
966
967 kfree(desc->pg_mirrors_dynamic);
968 desc->pg_mirrors_dynamic = NULL;
969 if (mirror_count == 1)
970 return desc->pg_mirrors_static;
Trond Myklebust2b17d7252019-06-11 16:49:52 -0400971 ret = kmalloc_array(mirror_count, sizeof(*ret), GFP_KERNEL);
Trond Myklebust14abcb02017-08-19 10:10:34 -0400972 if (ret != NULL) {
973 for (i = 0; i < mirror_count; i++)
974 nfs_pageio_mirror_init(&ret[i], desc->pg_bsize);
975 desc->pg_mirrors_dynamic = ret;
976 }
977 return ret;
978}
979
Weston Andros Adamsona7d42dd2014-09-19 10:55:07 -0400980/*
981 * nfs_pageio_setup_mirroring - determine if mirroring is to be used
982 * by calling the pg_get_mirror_count op
983 */
Trond Myklebust14abcb02017-08-19 10:10:34 -0400984static void nfs_pageio_setup_mirroring(struct nfs_pageio_descriptor *pgio,
Weston Andros Adamsona7d42dd2014-09-19 10:55:07 -0400985 struct nfs_page *req)
986{
Trond Myklebust14abcb02017-08-19 10:10:34 -0400987 unsigned int mirror_count = 1;
Weston Andros Adamsona7d42dd2014-09-19 10:55:07 -0400988
Trond Myklebust14abcb02017-08-19 10:10:34 -0400989 if (pgio->pg_ops->pg_get_mirror_count)
990 mirror_count = pgio->pg_ops->pg_get_mirror_count(pgio, req);
991 if (mirror_count == pgio->pg_mirror_count || pgio->pg_error < 0)
992 return;
Weston Andros Adamsona7d42dd2014-09-19 10:55:07 -0400993
Trond Myklebust14abcb02017-08-19 10:10:34 -0400994 if (!mirror_count || mirror_count > NFS_PAGEIO_DESCRIPTOR_MIRROR_MAX) {
995 pgio->pg_error = -EINVAL;
996 return;
997 }
Weston Andros Adamsona7d42dd2014-09-19 10:55:07 -0400998
Trond Myklebust14abcb02017-08-19 10:10:34 -0400999 pgio->pg_mirrors = nfs_pageio_alloc_mirrors(pgio, mirror_count);
1000 if (pgio->pg_mirrors == NULL) {
1001 pgio->pg_error = -ENOMEM;
1002 pgio->pg_mirrors = pgio->pg_mirrors_static;
1003 mirror_count = 1;
1004 }
Weston Andros Adamsona7d42dd2014-09-19 10:55:07 -04001005 pgio->pg_mirror_count = mirror_count;
Weston Andros Adamsona7d42dd2014-09-19 10:55:07 -04001006}
1007
Weston Andros Adamsona7d42dd2014-09-19 10:55:07 -04001008static void nfs_pageio_cleanup_mirroring(struct nfs_pageio_descriptor *pgio)
1009{
1010 pgio->pg_mirror_count = 1;
1011 pgio->pg_mirror_idx = 0;
1012 pgio->pg_mirrors = pgio->pg_mirrors_static;
1013 kfree(pgio->pg_mirrors_dynamic);
1014 pgio->pg_mirrors_dynamic = NULL;
1015}
1016
Trond Myklebust4109bb72013-09-06 11:09:38 -04001017static bool nfs_match_lock_context(const struct nfs_lock_context *l1,
1018 const struct nfs_lock_context *l2)
1019{
NeilBrownd51fdb82016-10-13 15:26:47 +11001020 return l1->lockowner == l2->lockowner;
Trond Myklebust4109bb72013-09-06 11:09:38 -04001021}
1022
Trond Myklebustd8a5ad72007-04-02 18:48:28 -04001023/**
Trond Myklebust44a65a02020-03-31 18:27:26 -04001024 * nfs_coalesce_size - test two requests for compatibility
Trond Myklebustd8a5ad72007-04-02 18:48:28 -04001025 * @prev: pointer to nfs_page
1026 * @req: pointer to nfs_page
Trond Myklebust302fad72019-02-18 13:32:38 -05001027 * @pgio: pointer to nfs_pagio_descriptor
Trond Myklebustd8a5ad72007-04-02 18:48:28 -04001028 *
1029 * The nfs_page structures 'prev' and 'req' are compared to ensure that the
1030 * page data area they describe is contiguous, and that their RPC
1031 * credentials, NFSv4 open state, and lockowners are the same.
1032 *
Trond Myklebust44a65a02020-03-31 18:27:26 -04001033 * Returns size of the request that can be coalesced
Trond Myklebustd8a5ad72007-04-02 18:48:28 -04001034 */
Trond Myklebust44a65a02020-03-31 18:27:26 -04001035static unsigned int nfs_coalesce_size(struct nfs_page *prev,
Benny Halevy18ad0a92011-05-25 21:03:56 +03001036 struct nfs_page *req,
1037 struct nfs_pageio_descriptor *pgio)
Trond Myklebustd8a5ad72007-04-02 18:48:28 -04001038{
Jeff Layton5263e312015-01-16 15:05:55 -05001039 struct file_lock_context *flctx;
Weston Andros Adamsonb4fdac12014-05-15 11:56:43 -04001040
Weston Andros Adamsonab75e412014-05-15 11:56:44 -04001041 if (prev) {
Trond Myklebust9fcd5962019-04-07 13:59:11 -04001042 if (!nfs_match_open_context(nfs_req_openctx(req), nfs_req_openctx(prev)))
Trond Myklebust44a65a02020-03-31 18:27:26 -04001043 return 0;
Trond Myklebust9fcd5962019-04-07 13:59:11 -04001044 flctx = d_inode(nfs_req_openctx(req)->dentry)->i_flctx;
Jeff Laytonbd61e0a2015-01-16 15:05:55 -05001045 if (flctx != NULL &&
1046 !(list_empty_careful(&flctx->flc_posix) &&
1047 list_empty_careful(&flctx->flc_flock)) &&
Jeff Layton5263e312015-01-16 15:05:55 -05001048 !nfs_match_lock_context(req->wb_lock_context,
1049 prev->wb_lock_context))
Trond Myklebust44a65a02020-03-31 18:27:26 -04001050 return 0;
Weston Andros Adamsonab75e412014-05-15 11:56:44 -04001051 if (req_offset(req) != req_offset(prev) + prev->wb_bytes)
Trond Myklebust44a65a02020-03-31 18:27:26 -04001052 return 0;
Weston Andros Adamson78270e82014-08-14 17:39:33 -04001053 if (req->wb_page == prev->wb_page) {
1054 if (req->wb_pgbase != prev->wb_pgbase + prev->wb_bytes)
Trond Myklebust44a65a02020-03-31 18:27:26 -04001055 return 0;
Weston Andros Adamson78270e82014-08-14 17:39:33 -04001056 } else {
1057 if (req->wb_pgbase != 0 ||
Kirill A. Shutemov09cbfea2016-04-01 15:29:47 +03001058 prev->wb_pgbase + prev->wb_bytes != PAGE_SIZE)
Trond Myklebust44a65a02020-03-31 18:27:26 -04001059 return 0;
Weston Andros Adamson78270e82014-08-14 17:39:33 -04001060 }
Weston Andros Adamsonab75e412014-05-15 11:56:44 -04001061 }
Trond Myklebust44a65a02020-03-31 18:27:26 -04001062 return pgio->pg_ops->pg_test(pgio, prev, req);
Trond Myklebustd8a5ad72007-04-02 18:48:28 -04001063}
1064
1065/**
Trond Myklebustbcb71bb2007-04-02 18:48:28 -04001066 * nfs_pageio_do_add_request - Attempt to coalesce a request into a page list.
Trond Myklebustd8a5ad72007-04-02 18:48:28 -04001067 * @desc: destination io descriptor
1068 * @req: request
1069 *
Trond Myklebust44a65a02020-03-31 18:27:26 -04001070 * If the request 'req' was successfully coalesced into the existing list
1071 * of pages 'desc', it returns the size of req.
Trond Myklebustd8a5ad72007-04-02 18:48:28 -04001072 */
Trond Myklebust44a65a02020-03-31 18:27:26 -04001073static unsigned int
1074nfs_pageio_do_add_request(struct nfs_pageio_descriptor *desc,
1075 struct nfs_page *req)
Trond Myklebustd8a5ad72007-04-02 18:48:28 -04001076{
Peng Tao48d635f2014-11-10 08:35:35 +08001077 struct nfs_pgio_mirror *mirror = nfs_pgio_current_mirror(desc);
Weston Andros Adamsonab75e412014-05-15 11:56:44 -04001078 struct nfs_page *prev = NULL;
Trond Myklebust44a65a02020-03-31 18:27:26 -04001079 unsigned int size;
Weston Andros Adamsona7d42dd2014-09-19 10:55:07 -04001080
1081 if (mirror->pg_count != 0) {
1082 prev = nfs_list_entry(mirror->pg_list.prev);
Boaz Harrosh5b36c7d2011-05-29 11:45:39 +03001083 } else {
Trond Myklebustd8007d42011-06-10 13:30:23 -04001084 if (desc->pg_ops->pg_init)
1085 desc->pg_ops->pg_init(desc, req);
Peng Taod600ad12015-12-04 02:57:48 +08001086 if (desc->pg_error < 0)
1087 return 0;
Weston Andros Adamsona7d42dd2014-09-19 10:55:07 -04001088 mirror->pg_base = req->wb_pgbase;
Boaz Harrosh5b36c7d2011-05-29 11:45:39 +03001089 }
Trond Myklebust33344e02019-04-07 13:59:08 -04001090
1091 if (desc->pg_maxretrans && req->wb_nio > desc->pg_maxretrans) {
1092 if (NFS_SERVER(desc->pg_inode)->flags & NFS_MOUNT_SOFTERR)
1093 desc->pg_error = -ETIMEDOUT;
1094 else
1095 desc->pg_error = -EIO;
1096 return 0;
1097 }
1098
Trond Myklebust44a65a02020-03-31 18:27:26 -04001099 size = nfs_coalesce_size(prev, req, desc);
1100 if (size < req->wb_bytes)
1101 return size;
Trond Myklebust078b5fd2019-02-18 11:35:54 -05001102 nfs_list_move_request(req, &mirror->pg_list);
Weston Andros Adamsona7d42dd2014-09-19 10:55:07 -04001103 mirror->pg_count += req->wb_bytes;
Trond Myklebust44a65a02020-03-31 18:27:26 -04001104 return req->wb_bytes;
Trond Myklebustd8a5ad72007-04-02 18:48:28 -04001105}
1106
Trond Myklebustbcb71bb2007-04-02 18:48:28 -04001107/*
1108 * Helper for nfs_pageio_add_request and nfs_pageio_complete
1109 */
1110static void nfs_pageio_doio(struct nfs_pageio_descriptor *desc)
1111{
Peng Tao48d635f2014-11-10 08:35:35 +08001112 struct nfs_pgio_mirror *mirror = nfs_pgio_current_mirror(desc);
Weston Andros Adamsona7d42dd2014-09-19 10:55:07 -04001113
1114
1115 if (!list_empty(&mirror->pg_list)) {
Trond Myklebust1751c362011-06-10 13:30:23 -04001116 int error = desc->pg_ops->pg_doio(desc);
Trond Myklebustbcb71bb2007-04-02 18:48:28 -04001117 if (error < 0)
1118 desc->pg_error = error;
1119 else
Weston Andros Adamsona7d42dd2014-09-19 10:55:07 -04001120 mirror->pg_bytes_written += mirror->pg_count;
Trond Myklebustbcb71bb2007-04-02 18:48:28 -04001121 }
Weston Andros Adamsona7d42dd2014-09-19 10:55:07 -04001122 if (list_empty(&mirror->pg_list)) {
1123 mirror->pg_count = 0;
1124 mirror->pg_base = 0;
Trond Myklebustbcb71bb2007-04-02 18:48:28 -04001125 }
1126}
1127
Trond Myklebustf57dcf42019-02-13 09:21:38 -05001128static void
1129nfs_pageio_cleanup_request(struct nfs_pageio_descriptor *desc,
1130 struct nfs_page *req)
1131{
1132 LIST_HEAD(head);
1133
Trond Myklebust078b5fd2019-02-18 11:35:54 -05001134 nfs_list_move_request(req, &head);
Trond Myklebustdf3accb2019-02-13 10:39:39 -05001135 desc->pg_completion_ops->error_cleanup(&head, desc->pg_error);
Trond Myklebustf57dcf42019-02-13 09:21:38 -05001136}
1137
Trond Myklebustbcb71bb2007-04-02 18:48:28 -04001138/**
1139 * nfs_pageio_add_request - Attempt to coalesce a request into a page list.
1140 * @desc: destination io descriptor
1141 * @req: request
1142 *
Weston Andros Adamson2bfc6e52014-05-15 11:56:45 -04001143 * This may split a request into subrequests which are all part of the
Trond Myklebust44a65a02020-03-31 18:27:26 -04001144 * same page group. If so, it will submit @req as the last one, to ensure
1145 * the pointer to @req is still valid in case of failure.
Weston Andros Adamson2bfc6e52014-05-15 11:56:45 -04001146 *
Trond Myklebustbcb71bb2007-04-02 18:48:28 -04001147 * Returns true if the request 'req' was successfully coalesced into the
1148 * existing list of pages 'desc'.
1149 */
Trond Myklebustd9156f92011-07-12 13:42:02 -04001150static int __nfs_pageio_add_request(struct nfs_pageio_descriptor *desc,
Trond Myklebust8b09bee2007-04-02 18:48:28 -04001151 struct nfs_page *req)
Trond Myklebustbcb71bb2007-04-02 18:48:28 -04001152{
Peng Tao48d635f2014-11-10 08:35:35 +08001153 struct nfs_pgio_mirror *mirror = nfs_pgio_current_mirror(desc);
Weston Andros Adamson2bfc6e52014-05-15 11:56:45 -04001154 struct nfs_page *subreq;
Trond Myklebust44a65a02020-03-31 18:27:26 -04001155 unsigned int size, subreq_size;
Weston Andros Adamson2bfc6e52014-05-15 11:56:45 -04001156
Trond Myklebust1344b7e2017-07-17 10:54:14 -04001157 nfs_page_group_lock(req);
Weston Andros Adamson2bfc6e52014-05-15 11:56:45 -04001158
1159 subreq = req;
Trond Myklebust44a65a02020-03-31 18:27:26 -04001160 subreq_size = subreq->wb_bytes;
1161 for(;;) {
1162 size = nfs_pageio_do_add_request(desc, subreq);
1163 if (size == subreq_size) {
1164 /* We successfully submitted a request */
1165 if (subreq == req)
1166 break;
1167 req->wb_pgbase += size;
1168 req->wb_bytes -= size;
1169 req->wb_offset += size;
1170 subreq_size = req->wb_bytes;
1171 subreq = req;
1172 continue;
1173 }
1174 if (WARN_ON_ONCE(subreq != req)) {
1175 nfs_page_group_unlock(req);
1176 nfs_pageio_cleanup_request(desc, subreq);
1177 subreq = req;
1178 subreq_size = req->wb_bytes;
1179 nfs_page_group_lock(req);
1180 }
1181 if (!size) {
1182 /* Can't coalesce any more, so do I/O */
Weston Andros Adamson2bfc6e52014-05-15 11:56:45 -04001183 nfs_page_group_unlock(req);
1184 desc->pg_moreio = 1;
1185 nfs_pageio_doio(desc);
Trond Myklebustf57dcf42019-02-13 09:21:38 -05001186 if (desc->pg_error < 0 || mirror->pg_recoalesce)
Trond Myklebust44a65a02020-03-31 18:27:26 -04001187 return 0;
Weston Andros Adamson2bfc6e52014-05-15 11:56:45 -04001188 /* retry add_request for this subreq */
Trond Myklebust1344b7e2017-07-17 10:54:14 -04001189 nfs_page_group_lock(req);
Weston Andros Adamson2bfc6e52014-05-15 11:56:45 -04001190 continue;
1191 }
Trond Myklebust44a65a02020-03-31 18:27:26 -04001192 subreq = nfs_create_subreq(req, req->wb_pgbase,
1193 req->wb_offset, size);
1194 if (IS_ERR(subreq))
1195 goto err_ptr;
1196 subreq_size = size;
1197 }
Weston Andros Adamson2bfc6e52014-05-15 11:56:45 -04001198
1199 nfs_page_group_unlock(req);
Trond Myklebustbcb71bb2007-04-02 18:48:28 -04001200 return 1;
Trond Myklebustc1109552014-05-29 11:38:15 -04001201err_ptr:
1202 desc->pg_error = PTR_ERR(subreq);
1203 nfs_page_group_unlock(req);
1204 return 0;
Trond Myklebustbcb71bb2007-04-02 18:48:28 -04001205}
1206
Trond Myklebustd9156f92011-07-12 13:42:02 -04001207static int nfs_do_recoalesce(struct nfs_pageio_descriptor *desc)
1208{
Peng Tao48d635f2014-11-10 08:35:35 +08001209 struct nfs_pgio_mirror *mirror = nfs_pgio_current_mirror(desc);
Trond Myklebustd9156f92011-07-12 13:42:02 -04001210 LIST_HEAD(head);
1211
1212 do {
Weston Andros Adamsona7d42dd2014-09-19 10:55:07 -04001213 list_splice_init(&mirror->pg_list, &head);
1214 mirror->pg_bytes_written -= mirror->pg_count;
1215 mirror->pg_count = 0;
1216 mirror->pg_base = 0;
1217 mirror->pg_recoalesce = 0;
1218
Trond Myklebustd9156f92011-07-12 13:42:02 -04001219 while (!list_empty(&head)) {
1220 struct nfs_page *req;
1221
1222 req = list_first_entry(&head, struct nfs_page, wb_list);
Trond Myklebustd9156f92011-07-12 13:42:02 -04001223 if (__nfs_pageio_add_request(desc, req))
1224 continue;
Trond Myklebust03d5eb62015-07-27 10:23:19 -04001225 if (desc->pg_error < 0) {
1226 list_splice_tail(&head, &mirror->pg_list);
1227 mirror->pg_recoalesce = 1;
Trond Myklebustd9156f92011-07-12 13:42:02 -04001228 return 0;
Trond Myklebust03d5eb62015-07-27 10:23:19 -04001229 }
Trond Myklebustd9156f92011-07-12 13:42:02 -04001230 break;
1231 }
Weston Andros Adamsona7d42dd2014-09-19 10:55:07 -04001232 } while (mirror->pg_recoalesce);
Trond Myklebustd9156f92011-07-12 13:42:02 -04001233 return 1;
1234}
1235
Weston Andros Adamsona7d42dd2014-09-19 10:55:07 -04001236static int nfs_pageio_add_request_mirror(struct nfs_pageio_descriptor *desc,
Trond Myklebustd9156f92011-07-12 13:42:02 -04001237 struct nfs_page *req)
1238{
1239 int ret;
1240
1241 do {
1242 ret = __nfs_pageio_add_request(desc, req);
1243 if (ret)
1244 break;
1245 if (desc->pg_error < 0)
1246 break;
1247 ret = nfs_do_recoalesce(desc);
1248 } while (ret);
Weston Andros Adamsona7d42dd2014-09-19 10:55:07 -04001249
Trond Myklebustd9156f92011-07-12 13:42:02 -04001250 return ret;
1251}
1252
Benjamin Coddingtonfdbd1a22018-10-18 15:01:48 -04001253static void nfs_pageio_error_cleanup(struct nfs_pageio_descriptor *desc)
1254{
1255 u32 midx;
1256 struct nfs_pgio_mirror *mirror;
1257
1258 if (!desc->pg_error)
1259 return;
1260
1261 for (midx = 0; midx < desc->pg_mirror_count; midx++) {
1262 mirror = &desc->pg_mirrors[midx];
Trond Myklebustdf3accb2019-02-13 10:39:39 -05001263 desc->pg_completion_ops->error_cleanup(&mirror->pg_list,
1264 desc->pg_error);
Benjamin Coddingtonfdbd1a22018-10-18 15:01:48 -04001265 }
1266}
1267
Weston Andros Adamsona7d42dd2014-09-19 10:55:07 -04001268int nfs_pageio_add_request(struct nfs_pageio_descriptor *desc,
1269 struct nfs_page *req)
1270{
1271 u32 midx;
1272 unsigned int pgbase, offset, bytes;
Trond Myklebust44a65a02020-03-31 18:27:26 -04001273 struct nfs_page *dupreq;
Weston Andros Adamsona7d42dd2014-09-19 10:55:07 -04001274
1275 pgbase = req->wb_pgbase;
1276 offset = req->wb_offset;
1277 bytes = req->wb_bytes;
1278
1279 nfs_pageio_setup_mirroring(desc, req);
Peng Taod600ad12015-12-04 02:57:48 +08001280 if (desc->pg_error < 0)
Peng Taoc18b96a2015-12-05 01:59:56 +08001281 goto out_failed;
Weston Andros Adamsona7d42dd2014-09-19 10:55:07 -04001282
Trond Myklebustdc9dc2f2020-03-29 19:55:05 -04001283 /* Create the mirror instances first, and fire them off */
1284 for (midx = 1; midx < desc->pg_mirror_count; midx++) {
1285 nfs_page_group_lock(req);
Weston Andros Adamsona7d42dd2014-09-19 10:55:07 -04001286
Trond Myklebust44a65a02020-03-31 18:27:26 -04001287 dupreq = nfs_create_subreq(req,
Trond Myklebustdc9dc2f2020-03-29 19:55:05 -04001288 pgbase, offset, bytes);
Weston Andros Adamsona7d42dd2014-09-19 10:55:07 -04001289
Trond Myklebustdc9dc2f2020-03-29 19:55:05 -04001290 nfs_page_group_unlock(req);
1291 if (IS_ERR(dupreq)) {
1292 desc->pg_error = PTR_ERR(dupreq);
1293 goto out_failed;
1294 }
Weston Andros Adamsona7d42dd2014-09-19 10:55:07 -04001295
Trond Myklebustdc9dc2f2020-03-29 19:55:05 -04001296 desc->pg_mirror_idx = midx;
Weston Andros Adamsona7d42dd2014-09-19 10:55:07 -04001297 if (!nfs_pageio_add_request_mirror(desc, dupreq))
Trond Myklebustf57dcf42019-02-13 09:21:38 -05001298 goto out_cleanup_subreq;
Weston Andros Adamsona7d42dd2014-09-19 10:55:07 -04001299 }
1300
Trond Myklebustdc9dc2f2020-03-29 19:55:05 -04001301 desc->pg_mirror_idx = 0;
1302 if (!nfs_pageio_add_request_mirror(desc, req))
1303 goto out_failed;
1304
Weston Andros Adamsona7d42dd2014-09-19 10:55:07 -04001305 return 1;
Peng Taoc18b96a2015-12-05 01:59:56 +08001306
Trond Myklebustf57dcf42019-02-13 09:21:38 -05001307out_cleanup_subreq:
Trond Myklebustdc9dc2f2020-03-29 19:55:05 -04001308 nfs_pageio_cleanup_request(desc, dupreq);
Peng Taoc18b96a2015-12-05 01:59:56 +08001309out_failed:
Benjamin Coddingtonfdbd1a22018-10-18 15:01:48 -04001310 nfs_pageio_error_cleanup(desc);
Peng Taoc18b96a2015-12-05 01:59:56 +08001311 return 0;
Weston Andros Adamsona7d42dd2014-09-19 10:55:07 -04001312}
1313
1314/*
1315 * nfs_pageio_complete_mirror - Complete I/O on the current mirror of an
1316 * nfs_pageio_descriptor
1317 * @desc: pointer to io descriptor
Yijing Wangdfad7002015-06-18 19:37:13 +08001318 * @mirror_idx: pointer to mirror index
Weston Andros Adamsona7d42dd2014-09-19 10:55:07 -04001319 */
1320static void nfs_pageio_complete_mirror(struct nfs_pageio_descriptor *desc,
1321 u32 mirror_idx)
1322{
1323 struct nfs_pgio_mirror *mirror = &desc->pg_mirrors[mirror_idx];
1324 u32 restore_idx = desc->pg_mirror_idx;
1325
Trond Myklebust377840e2020-03-29 20:03:33 -04001326 desc->pg_mirror_idx = mirror_idx;
Weston Andros Adamsona7d42dd2014-09-19 10:55:07 -04001327 for (;;) {
1328 nfs_pageio_doio(desc);
Trond Myklebust8127d822019-02-15 16:08:25 -05001329 if (desc->pg_error < 0 || !mirror->pg_recoalesce)
Weston Andros Adamsona7d42dd2014-09-19 10:55:07 -04001330 break;
1331 if (!nfs_do_recoalesce(desc))
1332 break;
1333 }
1334 desc->pg_mirror_idx = restore_idx;
1335}
1336
Weston Andros Adamson53113ad2014-06-09 11:48:38 -04001337/*
1338 * nfs_pageio_resend - Transfer requests to new descriptor and resend
1339 * @hdr - the pgio header to move request from
1340 * @desc - the pageio descriptor to add requests to
1341 *
1342 * Try to move each request (nfs_page) from @hdr to @desc then attempt
1343 * to send them.
1344 *
1345 * Returns 0 on success and < 0 on error.
1346 */
1347int nfs_pageio_resend(struct nfs_pageio_descriptor *desc,
1348 struct nfs_pgio_header *hdr)
1349{
Trond Myklebustf4340e92019-08-12 15:19:54 -04001350 LIST_HEAD(pages);
Weston Andros Adamson53113ad2014-06-09 11:48:38 -04001351
Trond Myklebust919e3bd2017-06-20 19:35:37 -04001352 desc->pg_io_completion = hdr->io_completion;
Weston Andros Adamson53113ad2014-06-09 11:48:38 -04001353 desc->pg_dreq = hdr->dreq;
Trond Myklebustf4340e92019-08-12 15:19:54 -04001354 list_splice_init(&hdr->pages, &pages);
1355 while (!list_empty(&pages)) {
1356 struct nfs_page *req = nfs_list_entry(pages.next);
Weston Andros Adamson53113ad2014-06-09 11:48:38 -04001357
Weston Andros Adamson53113ad2014-06-09 11:48:38 -04001358 if (!nfs_pageio_add_request(desc, req))
Trond Myklebustf4340e92019-08-12 15:19:54 -04001359 break;
Weston Andros Adamson53113ad2014-06-09 11:48:38 -04001360 }
1361 nfs_pageio_complete(desc);
Trond Myklebustf4340e92019-08-12 15:19:54 -04001362 if (!list_empty(&pages)) {
1363 int err = desc->pg_error < 0 ? desc->pg_error : -EIO;
1364 hdr->completion_ops->error_cleanup(&pages, err);
Trond Myklebusteb2c50d2019-08-12 18:04:36 -04001365 nfs_set_pgio_error(hdr, err, hdr->io_start);
Trond Myklebustf4340e92019-08-12 15:19:54 -04001366 return err;
Weston Andros Adamson53113ad2014-06-09 11:48:38 -04001367 }
1368 return 0;
1369}
1370EXPORT_SYMBOL_GPL(nfs_pageio_resend);
Trond Myklebustd8a5ad72007-04-02 18:48:28 -04001371
1372/**
Weston Andros Adamson2176bf42014-09-10 15:44:18 -04001373 * nfs_pageio_complete - Complete I/O then cleanup an nfs_pageio_descriptor
Trond Myklebustbcb71bb2007-04-02 18:48:28 -04001374 * @desc: pointer to io descriptor
1375 */
1376void nfs_pageio_complete(struct nfs_pageio_descriptor *desc)
1377{
Weston Andros Adamsona7d42dd2014-09-19 10:55:07 -04001378 u32 midx;
1379
1380 for (midx = 0; midx < desc->pg_mirror_count; midx++)
1381 nfs_pageio_complete_mirror(desc, midx);
Weston Andros Adamson2176bf42014-09-10 15:44:18 -04001382
Benjamin Coddingtonfdbd1a22018-10-18 15:01:48 -04001383 if (desc->pg_error < 0)
1384 nfs_pageio_error_cleanup(desc);
Weston Andros Adamson2176bf42014-09-10 15:44:18 -04001385 if (desc->pg_ops->pg_cleanup)
1386 desc->pg_ops->pg_cleanup(desc);
Weston Andros Adamsona7d42dd2014-09-19 10:55:07 -04001387 nfs_pageio_cleanup_mirroring(desc);
Trond Myklebustbcb71bb2007-04-02 18:48:28 -04001388}
1389
Trond Myklebust7fe7f842007-05-20 10:18:27 -04001390/**
1391 * nfs_pageio_cond_complete - Conditional I/O completion
1392 * @desc: pointer to io descriptor
1393 * @index: page index
1394 *
1395 * It is important to ensure that processes don't try to take locks
1396 * on non-contiguous ranges of pages as that might deadlock. This
1397 * function should be called before attempting to wait on a locked
1398 * nfs_page. It will complete the I/O if the page index 'index'
1399 * is not contiguous with the existing list of pages in 'desc'.
1400 */
1401void nfs_pageio_cond_complete(struct nfs_pageio_descriptor *desc, pgoff_t index)
1402{
Weston Andros Adamsona7d42dd2014-09-19 10:55:07 -04001403 struct nfs_pgio_mirror *mirror;
1404 struct nfs_page *prev;
1405 u32 midx;
1406
1407 for (midx = 0; midx < desc->pg_mirror_count; midx++) {
1408 mirror = &desc->pg_mirrors[midx];
1409 if (!list_empty(&mirror->pg_list)) {
1410 prev = nfs_list_entry(mirror->pg_list.prev);
Benjamin Coddington43b7d962017-04-14 12:29:54 -04001411 if (index != prev->wb_index + 1) {
1412 nfs_pageio_complete(desc);
1413 break;
1414 }
Weston Andros Adamsona7d42dd2014-09-19 10:55:07 -04001415 }
Trond Myklebust7fe7f842007-05-20 10:18:27 -04001416 }
1417}
1418
Trond Myklebust862f35c2020-03-29 20:06:45 -04001419/*
1420 * nfs_pageio_stop_mirroring - stop using mirroring (set mirror count to 1)
1421 */
1422void nfs_pageio_stop_mirroring(struct nfs_pageio_descriptor *pgio)
1423{
1424 nfs_pageio_complete(pgio);
1425}
1426
David Howellsf7b422b2006-06-09 09:34:33 -04001427int __init nfs_init_nfspagecache(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001428{
1429 nfs_page_cachep = kmem_cache_create("nfs_page",
1430 sizeof(struct nfs_page),
1431 0, SLAB_HWCACHE_ALIGN,
Paul Mundt20c2df82007-07-20 10:11:58 +09001432 NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001433 if (nfs_page_cachep == NULL)
1434 return -ENOMEM;
1435
1436 return 0;
1437}
1438
David Brownell266bee82006-06-27 12:59:15 -07001439void nfs_destroy_nfspagecache(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001440{
Alexey Dobriyan1a1d92c2006-09-27 01:49:40 -07001441 kmem_cache_destroy(nfs_page_cachep);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001442}
1443
Anna Schumakeref2c4882014-05-06 09:12:36 -04001444static const struct rpc_call_ops nfs_pgio_common_ops = {
Anna Schumaker6f92fa42014-05-06 09:12:33 -04001445 .rpc_call_prepare = nfs_pgio_prepare,
1446 .rpc_call_done = nfs_pgio_result,
1447 .rpc_release = nfs_pgio_release,
1448};
Anna Schumaker41d8d5b2014-05-06 09:12:40 -04001449
1450const struct nfs_pageio_ops nfs_pgio_rw_ops = {
1451 .pg_test = nfs_generic_pg_test,
1452 .pg_doio = nfs_generic_pg_pgios,
1453};