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