xref: /openbmc/linux/fs/nfs/fscache.c (revision ad27382f)
1b4d0d230SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
214727281SDavid Howells /* NFS filesystem cache interface
314727281SDavid Howells  *
414727281SDavid Howells  * Copyright (C) 2008 Red Hat, Inc. All Rights Reserved.
514727281SDavid Howells  * Written by David Howells (dhowells@redhat.com)
614727281SDavid Howells  */
714727281SDavid Howells 
814727281SDavid Howells #include <linux/init.h>
914727281SDavid Howells #include <linux/kernel.h>
1014727281SDavid Howells #include <linux/sched.h>
1114727281SDavid Howells #include <linux/mm.h>
1214727281SDavid Howells #include <linux/nfs_fs.h>
1314727281SDavid Howells #include <linux/nfs_fs_sb.h>
1414727281SDavid Howells #include <linux/in6.h>
1514727281SDavid Howells #include <linux/seq_file.h>
165a0e3ad6STejun Heo #include <linux/slab.h>
17402cb8ddSDavid Howells #include <linux/iversion.h>
18000dbe0bSDave Wysochanski #include <linux/xarray.h>
19000dbe0bSDave Wysochanski #include <linux/fscache.h>
20000dbe0bSDave Wysochanski #include <linux/netfs.h>
2114727281SDavid Howells 
2214727281SDavid Howells #include "internal.h"
23545db45fSDavid Howells #include "iostat.h"
2414727281SDavid Howells #include "fscache.h"
25e3f0a7feSDave Wysochanski #include "nfstrace.h"
2614727281SDavid Howells 
27a6b5a28eSDave Wysochanski #define NFS_MAX_KEY_LEN 1000
2808734048SDavid Howells 
nfs_append_int(char * key,int * _len,unsigned long long x)29a6b5a28eSDave Wysochanski static bool nfs_append_int(char *key, int *_len, unsigned long long x)
30a6b5a28eSDave Wysochanski {
31a6b5a28eSDave Wysochanski 	if (*_len > NFS_MAX_KEY_LEN)
32a6b5a28eSDave Wysochanski 		return false;
33a6b5a28eSDave Wysochanski 	if (x == 0)
34a6b5a28eSDave Wysochanski 		key[(*_len)++] = ',';
35a6b5a28eSDave Wysochanski 	else
36a6b5a28eSDave Wysochanski 		*_len += sprintf(key + *_len, ",%llx", x);
37a6b5a28eSDave Wysochanski 	return true;
38a6b5a28eSDave Wysochanski }
39402cb8ddSDavid Howells 
40402cb8ddSDavid Howells /*
4114727281SDavid Howells  * Get the per-client index cookie for an NFS client if the appropriate mount
4214727281SDavid Howells  * flag was set
4314727281SDavid Howells  * - We always try and get an index cookie for the client, but get filehandle
4414727281SDavid Howells  *   cookies on a per-superblock basis, depending on the mount flags
4514727281SDavid Howells  */
nfs_fscache_get_client_key(struct nfs_client * clp,char * key,int * _len)46a6b5a28eSDave Wysochanski static bool nfs_fscache_get_client_key(struct nfs_client *clp,
47a6b5a28eSDave Wysochanski 				       char *key, int *_len)
4814727281SDavid Howells {
49402cb8ddSDavid Howells 	const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &clp->cl_addr;
50402cb8ddSDavid Howells 	const struct sockaddr_in *sin = (struct sockaddr_in *) &clp->cl_addr;
51402cb8ddSDavid Howells 
52a6b5a28eSDave Wysochanski 	*_len += snprintf(key + *_len, NFS_MAX_KEY_LEN - *_len,
53a6b5a28eSDave Wysochanski 			  ",%u.%u,%x",
54a6b5a28eSDave Wysochanski 			  clp->rpc_ops->version,
55a6b5a28eSDave Wysochanski 			  clp->cl_minorversion,
56a6b5a28eSDave Wysochanski 			  clp->cl_addr.ss_family);
57402cb8ddSDavid Howells 
58402cb8ddSDavid Howells 	switch (clp->cl_addr.ss_family) {
59402cb8ddSDavid Howells 	case AF_INET:
60a6b5a28eSDave Wysochanski 		if (!nfs_append_int(key, _len, sin->sin_port) ||
61a6b5a28eSDave Wysochanski 		    !nfs_append_int(key, _len, sin->sin_addr.s_addr))
62a6b5a28eSDave Wysochanski 			return false;
63a6b5a28eSDave Wysochanski 		return true;
64402cb8ddSDavid Howells 
65402cb8ddSDavid Howells 	case AF_INET6:
66a6b5a28eSDave Wysochanski 		if (!nfs_append_int(key, _len, sin6->sin6_port) ||
67a6b5a28eSDave Wysochanski 		    !nfs_append_int(key, _len, sin6->sin6_addr.s6_addr32[0]) ||
68a6b5a28eSDave Wysochanski 		    !nfs_append_int(key, _len, sin6->sin6_addr.s6_addr32[1]) ||
69a6b5a28eSDave Wysochanski 		    !nfs_append_int(key, _len, sin6->sin6_addr.s6_addr32[2]) ||
70a6b5a28eSDave Wysochanski 		    !nfs_append_int(key, _len, sin6->sin6_addr.s6_addr32[3]))
71a6b5a28eSDave Wysochanski 			return false;
72a6b5a28eSDave Wysochanski 		return true;
73402cb8ddSDavid Howells 
74402cb8ddSDavid Howells 	default:
75402cb8ddSDavid Howells 		printk(KERN_WARNING "NFS: Unknown network family '%d'\n",
76402cb8ddSDavid Howells 		       clp->cl_addr.ss_family);
77a6b5a28eSDave Wysochanski 		return false;
78402cb8ddSDavid Howells 	}
7914727281SDavid Howells }
8014727281SDavid Howells 
8114727281SDavid Howells /*
82a6b5a28eSDave Wysochanski  * Get the cache cookie for an NFS superblock.
832df54806SDavid Howells  *
842df54806SDavid Howells  * The default uniquifier is just an empty string, but it may be overridden
852df54806SDavid Howells  * either by the 'fsc=xxx' option to mount, or by inheriting it from the parent
862df54806SDavid Howells  * superblock across an automount point of some nature.
8708734048SDavid Howells  */
nfs_fscache_get_super_cookie(struct super_block * sb,const char * uniq,int ulen)88a6b5a28eSDave Wysochanski int nfs_fscache_get_super_cookie(struct super_block *sb, const char *uniq, int ulen)
8908734048SDavid Howells {
90a6b5a28eSDave Wysochanski 	struct fscache_volume *vcookie;
9108734048SDavid Howells 	struct nfs_server *nfss = NFS_SB(sb);
92a6b5a28eSDave Wysochanski 	unsigned int len = 3;
93a6b5a28eSDave Wysochanski 	char *key;
942df54806SDavid Howells 
95a6b5a28eSDave Wysochanski 	if (uniq) {
96a6b5a28eSDave Wysochanski 		nfss->fscache_uniq = kmemdup_nul(uniq, ulen, GFP_KERNEL);
97a6b5a28eSDave Wysochanski 		if (!nfss->fscache_uniq)
98a6b5a28eSDave Wysochanski 			return -ENOMEM;
992df54806SDavid Howells 	}
1002df54806SDavid Howells 
101a6b5a28eSDave Wysochanski 	key = kmalloc(NFS_MAX_KEY_LEN + 24, GFP_KERNEL);
10208734048SDavid Howells 	if (!key)
103a6b5a28eSDave Wysochanski 		return -ENOMEM;
10408734048SDavid Howells 
105a6b5a28eSDave Wysochanski 	memcpy(key, "nfs", 3);
106a6b5a28eSDave Wysochanski 	if (!nfs_fscache_get_client_key(nfss->nfs_client, key, &len) ||
107a6b5a28eSDave Wysochanski 	    !nfs_append_int(key, &len, nfss->fsid.major) ||
108a6b5a28eSDave Wysochanski 	    !nfs_append_int(key, &len, nfss->fsid.minor) ||
109a6b5a28eSDave Wysochanski 	    !nfs_append_int(key, &len, sb->s_flags & NFS_SB_MASK) ||
110a6b5a28eSDave Wysochanski 	    !nfs_append_int(key, &len, nfss->flags) ||
111a6b5a28eSDave Wysochanski 	    !nfs_append_int(key, &len, nfss->rsize) ||
112a6b5a28eSDave Wysochanski 	    !nfs_append_int(key, &len, nfss->wsize) ||
113a6b5a28eSDave Wysochanski 	    !nfs_append_int(key, &len, nfss->acregmin) ||
114a6b5a28eSDave Wysochanski 	    !nfs_append_int(key, &len, nfss->acregmax) ||
115a6b5a28eSDave Wysochanski 	    !nfs_append_int(key, &len, nfss->acdirmin) ||
116a6b5a28eSDave Wysochanski 	    !nfs_append_int(key, &len, nfss->acdirmax) ||
117a6b5a28eSDave Wysochanski 	    !nfs_append_int(key, &len, nfss->client->cl_auth->au_flavor))
118a6b5a28eSDave Wysochanski 		goto out;
11908734048SDavid Howells 
120a6b5a28eSDave Wysochanski 	if (ulen > 0) {
121a6b5a28eSDave Wysochanski 		if (ulen > NFS_MAX_KEY_LEN - len)
122a6b5a28eSDave Wysochanski 			goto out;
123a6b5a28eSDave Wysochanski 		key[len++] = ',';
124a6b5a28eSDave Wysochanski 		memcpy(key + len, uniq, ulen);
125a6b5a28eSDave Wysochanski 		len += ulen;
12608734048SDavid Howells 	}
127a6b5a28eSDave Wysochanski 	key[len] = 0;
12808734048SDavid Howells 
12908734048SDavid Howells 	/* create a cache index for looking up filehandles */
130a6b5a28eSDave Wysochanski 	vcookie = fscache_acquire_volume(key,
131a6b5a28eSDave Wysochanski 					 NULL, /* preferred_cache */
132a6b5a28eSDave Wysochanski 					 NULL, 0 /* coherency_data */);
133a6b5a28eSDave Wysochanski 	if (IS_ERR(vcookie)) {
134a6b5a28eSDave Wysochanski 		if (vcookie != ERR_PTR(-EBUSY)) {
13508734048SDavid Howells 			kfree(key);
136a6b5a28eSDave Wysochanski 			return PTR_ERR(vcookie);
137a6b5a28eSDave Wysochanski 		}
138a6b5a28eSDave Wysochanski 		pr_err("NFS: Cache volume key already in use (%s)\n", key);
139a6b5a28eSDave Wysochanski 		vcookie = NULL;
140a6b5a28eSDave Wysochanski 	}
141a6b5a28eSDave Wysochanski 	nfss->fscache = vcookie;
142a6b5a28eSDave Wysochanski 
143a6b5a28eSDave Wysochanski out:
144a6b5a28eSDave Wysochanski 	kfree(key);
145a6b5a28eSDave Wysochanski 	return 0;
14608734048SDavid Howells }
14708734048SDavid Howells 
14808734048SDavid Howells /*
14908734048SDavid Howells  * release a per-superblock cookie
15008734048SDavid Howells  */
nfs_fscache_release_super_cookie(struct super_block * sb)15108734048SDavid Howells void nfs_fscache_release_super_cookie(struct super_block *sb)
15208734048SDavid Howells {
15308734048SDavid Howells 	struct nfs_server *nfss = NFS_SB(sb);
15408734048SDavid Howells 
155a6b5a28eSDave Wysochanski 	fscache_relinquish_volume(nfss->fscache, NULL, false);
15608734048SDavid Howells 	nfss->fscache = NULL;
157a6b5a28eSDave Wysochanski 	kfree(nfss->fscache_uniq);
15850eaa652SDave Wysochanski }
15950eaa652SDave Wysochanski 
160ef79c097SDavid Howells /*
161ef79c097SDavid Howells  * Initialise the per-inode cache cookie pointer for an NFS inode.
162ef79c097SDavid Howells  */
nfs_fscache_init_inode(struct inode * inode)163f1fe29b4SDavid Howells void nfs_fscache_init_inode(struct inode *inode)
164ef79c097SDavid Howells {
165402cb8ddSDavid Howells 	struct nfs_fscache_inode_auxdata auxdata;
166dea1bb35STrond Myklebust 	struct nfs_server *nfss = NFS_SERVER(inode);
167ef79c097SDavid Howells 	struct nfs_inode *nfsi = NFS_I(inode);
168ef79c097SDavid Howells 
16988a4d7bdSDave Wysochanski 	netfs_inode(inode)->cache = NULL;
170dea1bb35STrond Myklebust 	if (!(nfss->fscache && S_ISREG(inode->i_mode)))
171ef79c097SDavid Howells 		return;
172402cb8ddSDavid Howells 
17345f3a70bSDave Wysochanski 	nfs_fscache_update_auxdata(&auxdata, inode);
174402cb8ddSDavid Howells 
17588a4d7bdSDave Wysochanski 	netfs_inode(inode)->cache = fscache_acquire_cookie(
17688a4d7bdSDave Wysochanski 					       nfss->fscache,
177a6b5a28eSDave Wysochanski 					       0,
178a6b5a28eSDave Wysochanski 					       nfsi->fh.data, /* index_key */
179a6b5a28eSDave Wysochanski 					       nfsi->fh.size,
180a6b5a28eSDave Wysochanski 					       &auxdata,      /* aux_data */
181a6b5a28eSDave Wysochanski 					       sizeof(auxdata),
18245f3a70bSDave Wysochanski 					       i_size_read(inode));
183b4fa966fSDavid Howells 
184b4fa966fSDavid Howells 	if (netfs_inode(inode)->cache)
185b4fa966fSDavid Howells 		mapping_set_release_always(inode->i_mapping);
186ef79c097SDavid Howells }
187ef79c097SDavid Howells 
188ef79c097SDavid Howells /*
189ef79c097SDavid Howells  * Release a per-inode cookie.
190ef79c097SDavid Howells  */
nfs_fscache_clear_inode(struct inode * inode)191f1fe29b4SDavid Howells void nfs_fscache_clear_inode(struct inode *inode)
192ef79c097SDavid Howells {
19388a4d7bdSDave Wysochanski 	fscache_relinquish_cookie(netfs_i_cookie(netfs_inode(inode)), false);
19488a4d7bdSDave Wysochanski 	netfs_inode(inode)->cache = NULL;
195ef79c097SDavid Howells }
196ef79c097SDavid Howells 
197ef79c097SDavid Howells /*
198f1fe29b4SDavid Howells  * Enable or disable caching for a file that is being opened as appropriate.
199f1fe29b4SDavid Howells  * The cookie is allocated when the inode is initialised, but is not enabled at
200f1fe29b4SDavid Howells  * that time.  Enablement is deferred to file-open time to avoid stat() and
201f1fe29b4SDavid Howells  * access() thrashing the cache.
202f1fe29b4SDavid Howells  *
203f1fe29b4SDavid Howells  * For now, with NFS, only regular files that are open read-only will be able
204ef79c097SDavid Howells  * to use the cache.
205f1fe29b4SDavid Howells  *
206f1fe29b4SDavid Howells  * We enable the cache for an inode if we open it read-only and it isn't
207f1fe29b4SDavid Howells  * currently open for writing.  We disable the cache if the inode is open
208f1fe29b4SDavid Howells  * write-only.
209f1fe29b4SDavid Howells  *
210f1fe29b4SDavid Howells  * The caller uses the file struct to pin i_writecount on the inode before
211f1fe29b4SDavid Howells  * calling us when a file is opened for writing, so we can make use of that.
212f1fe29b4SDavid Howells  *
213f1fe29b4SDavid Howells  * Note that this may be invoked multiple times in parallel by parallel
214f1fe29b4SDavid Howells  * nfs_open() functions.
215ef79c097SDavid Howells  */
nfs_fscache_open_file(struct inode * inode,struct file * filp)216f1fe29b4SDavid Howells void nfs_fscache_open_file(struct inode *inode, struct file *filp)
217ef79c097SDavid Howells {
218402cb8ddSDavid Howells 	struct nfs_fscache_inode_auxdata auxdata;
21988a4d7bdSDave Wysochanski 	struct fscache_cookie *cookie = netfs_i_cookie(netfs_inode(inode));
220a6b5a28eSDave Wysochanski 	bool open_for_write = inode_is_open_for_write(inode);
221ef79c097SDavid Howells 
222f1fe29b4SDavid Howells 	if (!fscache_cookie_valid(cookie))
223f1fe29b4SDavid Howells 		return;
224ef79c097SDavid Howells 
225a6b5a28eSDave Wysochanski 	fscache_use_cookie(cookie, open_for_write);
226a6b5a28eSDave Wysochanski 	if (open_for_write) {
22745f3a70bSDave Wysochanski 		nfs_fscache_update_auxdata(&auxdata, inode);
228a6b5a28eSDave Wysochanski 		fscache_invalidate(cookie, &auxdata, i_size_read(inode),
229a6b5a28eSDave Wysochanski 				   FSCACHE_INVAL_DIO_WRITE);
230ef79c097SDavid Howells 	}
231ef79c097SDavid Howells }
232f1fe29b4SDavid Howells EXPORT_SYMBOL_GPL(nfs_fscache_open_file);
233545db45fSDavid Howells 
nfs_fscache_release_file(struct inode * inode,struct file * filp)234a6b5a28eSDave Wysochanski void nfs_fscache_release_file(struct inode *inode, struct file *filp)
235545db45fSDavid Howells {
236a6b5a28eSDave Wysochanski 	struct nfs_fscache_inode_auxdata auxdata;
23788a4d7bdSDave Wysochanski 	struct fscache_cookie *cookie = netfs_i_cookie(netfs_inode(inode));
2389c4a5c75SDave Wysochanski 	loff_t i_size = i_size_read(inode);
239545db45fSDavid Howells 
24045f3a70bSDave Wysochanski 	nfs_fscache_update_auxdata(&auxdata, inode);
2419c4a5c75SDave Wysochanski 	fscache_unuse_cookie(cookie, &auxdata, &i_size);
2429a9fc1c0SDavid Howells }
2439a9fc1c0SDavid Howells 
nfs_netfs_read_folio(struct file * file,struct folio * folio)244000dbe0bSDave Wysochanski int nfs_netfs_read_folio(struct file *file, struct folio *folio)
24516f2f4e6SDavid Howells {
246000dbe0bSDave Wysochanski 	if (!netfs_inode(folio_inode(folio))->cache)
247000dbe0bSDave Wysochanski 		return -ENOBUFS;
24816f2f4e6SDavid Howells 
249000dbe0bSDave Wysochanski 	return netfs_read_folio(file, folio);
25016f2f4e6SDavid Howells }
25116f2f4e6SDavid Howells 
nfs_netfs_readahead(struct readahead_control * ractl)252000dbe0bSDave Wysochanski int nfs_netfs_readahead(struct readahead_control *ractl)
25316f2f4e6SDavid Howells {
254000dbe0bSDave Wysochanski 	struct inode *inode = ractl->mapping->host;
25516f2f4e6SDavid Howells 
256000dbe0bSDave Wysochanski 	if (!netfs_inode(inode)->cache)
257000dbe0bSDave Wysochanski 		return -ENOBUFS;
25816f2f4e6SDavid Howells 
259000dbe0bSDave Wysochanski 	netfs_readahead(ractl);
260000dbe0bSDave Wysochanski 	return 0;
26116f2f4e6SDavid Howells }
26216f2f4e6SDavid Howells 
263c5733ae6STom Rix static atomic_t nfs_netfs_debug_id;
nfs_netfs_init_request(struct netfs_io_request * rreq,struct file * file)264000dbe0bSDave Wysochanski static int nfs_netfs_init_request(struct netfs_io_request *rreq, struct file *file)
2659a9fc1c0SDavid Howells {
266000dbe0bSDave Wysochanski 	rreq->netfs_priv = get_nfs_open_context(nfs_file_open_context(file));
267000dbe0bSDave Wysochanski 	rreq->debug_id = atomic_inc_return(&nfs_netfs_debug_id);
26816f2f4e6SDavid Howells 
269000dbe0bSDave Wysochanski 	return 0;
270000dbe0bSDave Wysochanski }
271000dbe0bSDave Wysochanski 
nfs_netfs_free_request(struct netfs_io_request * rreq)272000dbe0bSDave Wysochanski static void nfs_netfs_free_request(struct netfs_io_request *rreq)
273000dbe0bSDave Wysochanski {
274000dbe0bSDave Wysochanski 	put_nfs_open_context(rreq->netfs_priv);
275000dbe0bSDave Wysochanski }
276000dbe0bSDave Wysochanski 
nfs_netfs_begin_cache_operation(struct netfs_io_request * rreq)277000dbe0bSDave Wysochanski static inline int nfs_netfs_begin_cache_operation(struct netfs_io_request *rreq)
278000dbe0bSDave Wysochanski {
279000dbe0bSDave Wysochanski 	return fscache_begin_read_operation(&rreq->cache_resources,
280000dbe0bSDave Wysochanski 					    netfs_i_cookie(netfs_inode(rreq->inode)));
281000dbe0bSDave Wysochanski }
282000dbe0bSDave Wysochanski 
nfs_netfs_alloc(struct netfs_io_subrequest * sreq)283000dbe0bSDave Wysochanski static struct nfs_netfs_io_data *nfs_netfs_alloc(struct netfs_io_subrequest *sreq)
284000dbe0bSDave Wysochanski {
285000dbe0bSDave Wysochanski 	struct nfs_netfs_io_data *netfs;
286000dbe0bSDave Wysochanski 
287000dbe0bSDave Wysochanski 	netfs = kzalloc(sizeof(*netfs), GFP_KERNEL_ACCOUNT);
288000dbe0bSDave Wysochanski 	if (!netfs)
289000dbe0bSDave Wysochanski 		return NULL;
290000dbe0bSDave Wysochanski 	netfs->sreq = sreq;
291000dbe0bSDave Wysochanski 	refcount_set(&netfs->refcount, 1);
292000dbe0bSDave Wysochanski 	return netfs;
293000dbe0bSDave Wysochanski }
294000dbe0bSDave Wysochanski 
nfs_netfs_clamp_length(struct netfs_io_subrequest * sreq)295000dbe0bSDave Wysochanski static bool nfs_netfs_clamp_length(struct netfs_io_subrequest *sreq)
296000dbe0bSDave Wysochanski {
297000dbe0bSDave Wysochanski 	size_t	rsize = NFS_SB(sreq->rreq->inode->i_sb)->rsize;
298000dbe0bSDave Wysochanski 
299000dbe0bSDave Wysochanski 	sreq->len = min(sreq->len, rsize);
300000dbe0bSDave Wysochanski 	return true;
301000dbe0bSDave Wysochanski }
302000dbe0bSDave Wysochanski 
nfs_netfs_issue_read(struct netfs_io_subrequest * sreq)303000dbe0bSDave Wysochanski static void nfs_netfs_issue_read(struct netfs_io_subrequest *sreq)
304000dbe0bSDave Wysochanski {
305000dbe0bSDave Wysochanski 	struct nfs_netfs_io_data	*netfs;
306000dbe0bSDave Wysochanski 	struct nfs_pageio_descriptor	pgio;
307000dbe0bSDave Wysochanski 	struct inode *inode = sreq->rreq->inode;
308000dbe0bSDave Wysochanski 	struct nfs_open_context *ctx = sreq->rreq->netfs_priv;
309000dbe0bSDave Wysochanski 	struct page *page;
310*ad27382fSDave Wysochanski 	unsigned long idx;
311000dbe0bSDave Wysochanski 	int err;
312000dbe0bSDave Wysochanski 	pgoff_t start = (sreq->start + sreq->transferred) >> PAGE_SHIFT;
313000dbe0bSDave Wysochanski 	pgoff_t last = ((sreq->start + sreq->len -
314000dbe0bSDave Wysochanski 			 sreq->transferred - 1) >> PAGE_SHIFT);
315000dbe0bSDave Wysochanski 
316000dbe0bSDave Wysochanski 	nfs_pageio_init_read(&pgio, inode, false,
317000dbe0bSDave Wysochanski 			     &nfs_async_read_completion_ops);
318000dbe0bSDave Wysochanski 
319000dbe0bSDave Wysochanski 	netfs = nfs_netfs_alloc(sreq);
320000dbe0bSDave Wysochanski 	if (!netfs)
321000dbe0bSDave Wysochanski 		return netfs_subreq_terminated(sreq, -ENOMEM, false);
322000dbe0bSDave Wysochanski 
323000dbe0bSDave Wysochanski 	pgio.pg_netfs = netfs; /* used in completion */
324000dbe0bSDave Wysochanski 
325*ad27382fSDave Wysochanski 	xa_for_each_range(&sreq->rreq->mapping->i_pages, idx, page, start, last) {
326000dbe0bSDave Wysochanski 		/* nfs_read_add_folio() may schedule() due to pNFS layout and other RPCs  */
327000dbe0bSDave Wysochanski 		err = nfs_read_add_folio(&pgio, ctx, page_folio(page));
328000dbe0bSDave Wysochanski 		if (err < 0) {
329000dbe0bSDave Wysochanski 			netfs->error = err;
330e3f0a7feSDave Wysochanski 			goto out;
331ba512c1bSDave Wysochanski 		}
33216f2f4e6SDavid Howells 	}
333e3f0a7feSDave Wysochanski out:
334000dbe0bSDave Wysochanski 	nfs_pageio_complete_read(&pgio);
335000dbe0bSDave Wysochanski 	nfs_netfs_put(netfs);
33616f2f4e6SDavid Howells }
33716f2f4e6SDavid Howells 
nfs_netfs_initiate_read(struct nfs_pgio_header * hdr)338000dbe0bSDave Wysochanski void nfs_netfs_initiate_read(struct nfs_pgio_header *hdr)
339000dbe0bSDave Wysochanski {
340000dbe0bSDave Wysochanski 	struct nfs_netfs_io_data        *netfs = hdr->netfs;
341000dbe0bSDave Wysochanski 
342000dbe0bSDave Wysochanski 	if (!netfs)
343000dbe0bSDave Wysochanski 		return;
344000dbe0bSDave Wysochanski 
345000dbe0bSDave Wysochanski 	nfs_netfs_get(netfs);
346000dbe0bSDave Wysochanski }
347000dbe0bSDave Wysochanski 
nfs_netfs_folio_unlock(struct folio * folio)348000dbe0bSDave Wysochanski int nfs_netfs_folio_unlock(struct folio *folio)
349000dbe0bSDave Wysochanski {
350000dbe0bSDave Wysochanski 	struct inode *inode = folio_file_mapping(folio)->host;
351000dbe0bSDave Wysochanski 
35216f2f4e6SDavid Howells 	/*
353000dbe0bSDave Wysochanski 	 * If fscache is enabled, netfs will unlock pages.
35416f2f4e6SDavid Howells 	 */
355000dbe0bSDave Wysochanski 	if (netfs_inode(inode)->cache)
356000dbe0bSDave Wysochanski 		return 0;
357000dbe0bSDave Wysochanski 
358000dbe0bSDave Wysochanski 	return 1;
359000dbe0bSDave Wysochanski }
360000dbe0bSDave Wysochanski 
nfs_netfs_read_completion(struct nfs_pgio_header * hdr)361000dbe0bSDave Wysochanski void nfs_netfs_read_completion(struct nfs_pgio_header *hdr)
36216f2f4e6SDavid Howells {
363000dbe0bSDave Wysochanski 	struct nfs_netfs_io_data        *netfs = hdr->netfs;
364000dbe0bSDave Wysochanski 	struct netfs_io_subrequest      *sreq;
36516f2f4e6SDavid Howells 
366000dbe0bSDave Wysochanski 	if (!netfs)
367000dbe0bSDave Wysochanski 		return;
36816f2f4e6SDavid Howells 
369000dbe0bSDave Wysochanski 	sreq = netfs->sreq;
370000dbe0bSDave Wysochanski 	if (test_bit(NFS_IOHDR_EOF, &hdr->flags))
371000dbe0bSDave Wysochanski 		__set_bit(NETFS_SREQ_CLEAR_TAIL, &sreq->flags);
37216f2f4e6SDavid Howells 
373000dbe0bSDave Wysochanski 	if (hdr->error)
374000dbe0bSDave Wysochanski 		netfs->error = hdr->error;
375000dbe0bSDave Wysochanski 	else
376000dbe0bSDave Wysochanski 		atomic64_add(hdr->res.count, &netfs->transferred);
377000dbe0bSDave Wysochanski 
378000dbe0bSDave Wysochanski 	nfs_netfs_put(netfs);
379000dbe0bSDave Wysochanski 	hdr->netfs = NULL;
38016f2f4e6SDavid Howells }
381000dbe0bSDave Wysochanski 
382000dbe0bSDave Wysochanski const struct netfs_request_ops nfs_netfs_ops = {
383000dbe0bSDave Wysochanski 	.init_request		= nfs_netfs_init_request,
384000dbe0bSDave Wysochanski 	.free_request		= nfs_netfs_free_request,
385000dbe0bSDave Wysochanski 	.begin_cache_operation	= nfs_netfs_begin_cache_operation,
386000dbe0bSDave Wysochanski 	.issue_read		= nfs_netfs_issue_read,
387000dbe0bSDave Wysochanski 	.clamp_length		= nfs_netfs_clamp_length
388000dbe0bSDave Wysochanski };
389