18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/* NFS filesystem cache interface
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2008 Red Hat, Inc. All Rights Reserved.
58c2ecf20Sopenharmony_ci * Written by David Howells (dhowells@redhat.com)
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/init.h>
98c2ecf20Sopenharmony_ci#include <linux/kernel.h>
108c2ecf20Sopenharmony_ci#include <linux/sched.h>
118c2ecf20Sopenharmony_ci#include <linux/mm.h>
128c2ecf20Sopenharmony_ci#include <linux/nfs_fs.h>
138c2ecf20Sopenharmony_ci#include <linux/nfs_fs_sb.h>
148c2ecf20Sopenharmony_ci#include <linux/in6.h>
158c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
168c2ecf20Sopenharmony_ci#include <linux/slab.h>
178c2ecf20Sopenharmony_ci#include <linux/iversion.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include "internal.h"
208c2ecf20Sopenharmony_ci#include "iostat.h"
218c2ecf20Sopenharmony_ci#include "fscache.h"
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define NFSDBG_FACILITY		NFSDBG_FSCACHE
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistatic struct rb_root nfs_fscache_keys = RB_ROOT;
268c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(nfs_fscache_keys_lock);
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci/*
298c2ecf20Sopenharmony_ci * Layout of the key for an NFS server cache object.
308c2ecf20Sopenharmony_ci */
318c2ecf20Sopenharmony_cistruct nfs_server_key {
328c2ecf20Sopenharmony_ci	struct {
338c2ecf20Sopenharmony_ci		uint16_t	nfsversion;		/* NFS protocol version */
348c2ecf20Sopenharmony_ci		uint32_t	minorversion;		/* NFSv4 minor version */
358c2ecf20Sopenharmony_ci		uint16_t	family;			/* address family */
368c2ecf20Sopenharmony_ci		__be16		port;			/* IP port */
378c2ecf20Sopenharmony_ci	} hdr;
388c2ecf20Sopenharmony_ci	union {
398c2ecf20Sopenharmony_ci		struct in_addr	ipv4_addr;	/* IPv4 address */
408c2ecf20Sopenharmony_ci		struct in6_addr ipv6_addr;	/* IPv6 address */
418c2ecf20Sopenharmony_ci	};
428c2ecf20Sopenharmony_ci} __packed;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci/*
458c2ecf20Sopenharmony_ci * Get the per-client index cookie for an NFS client if the appropriate mount
468c2ecf20Sopenharmony_ci * flag was set
478c2ecf20Sopenharmony_ci * - We always try and get an index cookie for the client, but get filehandle
488c2ecf20Sopenharmony_ci *   cookies on a per-superblock basis, depending on the mount flags
498c2ecf20Sopenharmony_ci */
508c2ecf20Sopenharmony_civoid nfs_fscache_get_client_cookie(struct nfs_client *clp)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &clp->cl_addr;
538c2ecf20Sopenharmony_ci	const struct sockaddr_in *sin = (struct sockaddr_in *) &clp->cl_addr;
548c2ecf20Sopenharmony_ci	struct nfs_server_key key;
558c2ecf20Sopenharmony_ci	uint16_t len = sizeof(key.hdr);
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	memset(&key, 0, sizeof(key));
588c2ecf20Sopenharmony_ci	key.hdr.nfsversion = clp->rpc_ops->version;
598c2ecf20Sopenharmony_ci	key.hdr.minorversion = clp->cl_minorversion;
608c2ecf20Sopenharmony_ci	key.hdr.family = clp->cl_addr.ss_family;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	switch (clp->cl_addr.ss_family) {
638c2ecf20Sopenharmony_ci	case AF_INET:
648c2ecf20Sopenharmony_ci		key.hdr.port = sin->sin_port;
658c2ecf20Sopenharmony_ci		key.ipv4_addr = sin->sin_addr;
668c2ecf20Sopenharmony_ci		len += sizeof(key.ipv4_addr);
678c2ecf20Sopenharmony_ci		break;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	case AF_INET6:
708c2ecf20Sopenharmony_ci		key.hdr.port = sin6->sin6_port;
718c2ecf20Sopenharmony_ci		key.ipv6_addr = sin6->sin6_addr;
728c2ecf20Sopenharmony_ci		len += sizeof(key.ipv6_addr);
738c2ecf20Sopenharmony_ci		break;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	default:
768c2ecf20Sopenharmony_ci		printk(KERN_WARNING "NFS: Unknown network family '%d'\n",
778c2ecf20Sopenharmony_ci		       clp->cl_addr.ss_family);
788c2ecf20Sopenharmony_ci		clp->fscache = NULL;
798c2ecf20Sopenharmony_ci		return;
808c2ecf20Sopenharmony_ci	}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	/* create a cache index for looking up filehandles */
838c2ecf20Sopenharmony_ci	clp->fscache = fscache_acquire_cookie(nfs_fscache_netfs.primary_index,
848c2ecf20Sopenharmony_ci					      &nfs_fscache_server_index_def,
858c2ecf20Sopenharmony_ci					      &key, len,
868c2ecf20Sopenharmony_ci					      NULL, 0,
878c2ecf20Sopenharmony_ci					      clp, 0, true);
888c2ecf20Sopenharmony_ci	dfprintk(FSCACHE, "NFS: get client cookie (0x%p/0x%p)\n",
898c2ecf20Sopenharmony_ci		 clp, clp->fscache);
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci/*
938c2ecf20Sopenharmony_ci * Dispose of a per-client cookie
948c2ecf20Sopenharmony_ci */
958c2ecf20Sopenharmony_civoid nfs_fscache_release_client_cookie(struct nfs_client *clp)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	dfprintk(FSCACHE, "NFS: releasing client cookie (0x%p/0x%p)\n",
988c2ecf20Sopenharmony_ci		 clp, clp->fscache);
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	fscache_relinquish_cookie(clp->fscache, NULL, false);
1018c2ecf20Sopenharmony_ci	clp->fscache = NULL;
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci/*
1058c2ecf20Sopenharmony_ci * Get the cache cookie for an NFS superblock.  We have to handle
1068c2ecf20Sopenharmony_ci * uniquification here because the cache doesn't do it for us.
1078c2ecf20Sopenharmony_ci *
1088c2ecf20Sopenharmony_ci * The default uniquifier is just an empty string, but it may be overridden
1098c2ecf20Sopenharmony_ci * either by the 'fsc=xxx' option to mount, or by inheriting it from the parent
1108c2ecf20Sopenharmony_ci * superblock across an automount point of some nature.
1118c2ecf20Sopenharmony_ci */
1128c2ecf20Sopenharmony_civoid nfs_fscache_get_super_cookie(struct super_block *sb, const char *uniq, int ulen)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	struct nfs_fscache_key *key, *xkey;
1158c2ecf20Sopenharmony_ci	struct nfs_server *nfss = NFS_SB(sb);
1168c2ecf20Sopenharmony_ci	struct rb_node **p, *parent;
1178c2ecf20Sopenharmony_ci	int diff;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	nfss->fscache_key = NULL;
1208c2ecf20Sopenharmony_ci	nfss->fscache = NULL;
1218c2ecf20Sopenharmony_ci	if (!uniq) {
1228c2ecf20Sopenharmony_ci		uniq = "";
1238c2ecf20Sopenharmony_ci		ulen = 1;
1248c2ecf20Sopenharmony_ci	}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	key = kzalloc(sizeof(*key) + ulen, GFP_KERNEL);
1278c2ecf20Sopenharmony_ci	if (!key)
1288c2ecf20Sopenharmony_ci		return;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	key->nfs_client = nfss->nfs_client;
1318c2ecf20Sopenharmony_ci	key->key.super.s_flags = sb->s_flags & NFS_SB_MASK;
1328c2ecf20Sopenharmony_ci	key->key.nfs_server.flags = nfss->flags;
1338c2ecf20Sopenharmony_ci	key->key.nfs_server.rsize = nfss->rsize;
1348c2ecf20Sopenharmony_ci	key->key.nfs_server.wsize = nfss->wsize;
1358c2ecf20Sopenharmony_ci	key->key.nfs_server.acregmin = nfss->acregmin;
1368c2ecf20Sopenharmony_ci	key->key.nfs_server.acregmax = nfss->acregmax;
1378c2ecf20Sopenharmony_ci	key->key.nfs_server.acdirmin = nfss->acdirmin;
1388c2ecf20Sopenharmony_ci	key->key.nfs_server.acdirmax = nfss->acdirmax;
1398c2ecf20Sopenharmony_ci	key->key.nfs_server.fsid = nfss->fsid;
1408c2ecf20Sopenharmony_ci	key->key.rpc_auth.au_flavor = nfss->client->cl_auth->au_flavor;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	key->key.uniq_len = ulen;
1438c2ecf20Sopenharmony_ci	memcpy(key->key.uniquifier, uniq, ulen);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	spin_lock(&nfs_fscache_keys_lock);
1468c2ecf20Sopenharmony_ci	p = &nfs_fscache_keys.rb_node;
1478c2ecf20Sopenharmony_ci	parent = NULL;
1488c2ecf20Sopenharmony_ci	while (*p) {
1498c2ecf20Sopenharmony_ci		parent = *p;
1508c2ecf20Sopenharmony_ci		xkey = rb_entry(parent, struct nfs_fscache_key, node);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci		if (key->nfs_client < xkey->nfs_client)
1538c2ecf20Sopenharmony_ci			goto go_left;
1548c2ecf20Sopenharmony_ci		if (key->nfs_client > xkey->nfs_client)
1558c2ecf20Sopenharmony_ci			goto go_right;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci		diff = memcmp(&key->key, &xkey->key, sizeof(key->key));
1588c2ecf20Sopenharmony_ci		if (diff < 0)
1598c2ecf20Sopenharmony_ci			goto go_left;
1608c2ecf20Sopenharmony_ci		if (diff > 0)
1618c2ecf20Sopenharmony_ci			goto go_right;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci		if (key->key.uniq_len == 0)
1648c2ecf20Sopenharmony_ci			goto non_unique;
1658c2ecf20Sopenharmony_ci		diff = memcmp(key->key.uniquifier,
1668c2ecf20Sopenharmony_ci			      xkey->key.uniquifier,
1678c2ecf20Sopenharmony_ci			      key->key.uniq_len);
1688c2ecf20Sopenharmony_ci		if (diff < 0)
1698c2ecf20Sopenharmony_ci			goto go_left;
1708c2ecf20Sopenharmony_ci		if (diff > 0)
1718c2ecf20Sopenharmony_ci			goto go_right;
1728c2ecf20Sopenharmony_ci		goto non_unique;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	go_left:
1758c2ecf20Sopenharmony_ci		p = &(*p)->rb_left;
1768c2ecf20Sopenharmony_ci		continue;
1778c2ecf20Sopenharmony_ci	go_right:
1788c2ecf20Sopenharmony_ci		p = &(*p)->rb_right;
1798c2ecf20Sopenharmony_ci	}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	rb_link_node(&key->node, parent, p);
1828c2ecf20Sopenharmony_ci	rb_insert_color(&key->node, &nfs_fscache_keys);
1838c2ecf20Sopenharmony_ci	spin_unlock(&nfs_fscache_keys_lock);
1848c2ecf20Sopenharmony_ci	nfss->fscache_key = key;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	/* create a cache index for looking up filehandles */
1878c2ecf20Sopenharmony_ci	nfss->fscache = fscache_acquire_cookie(nfss->nfs_client->fscache,
1888c2ecf20Sopenharmony_ci					       &nfs_fscache_super_index_def,
1898c2ecf20Sopenharmony_ci					       &key->key,
1908c2ecf20Sopenharmony_ci					       sizeof(key->key) + ulen,
1918c2ecf20Sopenharmony_ci					       NULL, 0,
1928c2ecf20Sopenharmony_ci					       nfss, 0, true);
1938c2ecf20Sopenharmony_ci	dfprintk(FSCACHE, "NFS: get superblock cookie (0x%p/0x%p)\n",
1948c2ecf20Sopenharmony_ci		 nfss, nfss->fscache);
1958c2ecf20Sopenharmony_ci	return;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_cinon_unique:
1988c2ecf20Sopenharmony_ci	spin_unlock(&nfs_fscache_keys_lock);
1998c2ecf20Sopenharmony_ci	kfree(key);
2008c2ecf20Sopenharmony_ci	nfss->fscache_key = NULL;
2018c2ecf20Sopenharmony_ci	nfss->fscache = NULL;
2028c2ecf20Sopenharmony_ci	printk(KERN_WARNING "NFS:"
2038c2ecf20Sopenharmony_ci	       " Cache request denied due to non-unique superblock keys\n");
2048c2ecf20Sopenharmony_ci}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci/*
2078c2ecf20Sopenharmony_ci * release a per-superblock cookie
2088c2ecf20Sopenharmony_ci */
2098c2ecf20Sopenharmony_civoid nfs_fscache_release_super_cookie(struct super_block *sb)
2108c2ecf20Sopenharmony_ci{
2118c2ecf20Sopenharmony_ci	struct nfs_server *nfss = NFS_SB(sb);
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	dfprintk(FSCACHE, "NFS: releasing superblock cookie (0x%p/0x%p)\n",
2148c2ecf20Sopenharmony_ci		 nfss, nfss->fscache);
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	fscache_relinquish_cookie(nfss->fscache, NULL, false);
2178c2ecf20Sopenharmony_ci	nfss->fscache = NULL;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	if (nfss->fscache_key) {
2208c2ecf20Sopenharmony_ci		spin_lock(&nfs_fscache_keys_lock);
2218c2ecf20Sopenharmony_ci		rb_erase(&nfss->fscache_key->node, &nfs_fscache_keys);
2228c2ecf20Sopenharmony_ci		spin_unlock(&nfs_fscache_keys_lock);
2238c2ecf20Sopenharmony_ci		kfree(nfss->fscache_key);
2248c2ecf20Sopenharmony_ci		nfss->fscache_key = NULL;
2258c2ecf20Sopenharmony_ci	}
2268c2ecf20Sopenharmony_ci}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_cistatic void nfs_fscache_update_auxdata(struct nfs_fscache_inode_auxdata *auxdata,
2298c2ecf20Sopenharmony_ci				  struct nfs_inode *nfsi)
2308c2ecf20Sopenharmony_ci{
2318c2ecf20Sopenharmony_ci	memset(auxdata, 0, sizeof(*auxdata));
2328c2ecf20Sopenharmony_ci	auxdata->mtime_sec  = nfsi->vfs_inode.i_mtime.tv_sec;
2338c2ecf20Sopenharmony_ci	auxdata->mtime_nsec = nfsi->vfs_inode.i_mtime.tv_nsec;
2348c2ecf20Sopenharmony_ci	auxdata->ctime_sec  = nfsi->vfs_inode.i_ctime.tv_sec;
2358c2ecf20Sopenharmony_ci	auxdata->ctime_nsec = nfsi->vfs_inode.i_ctime.tv_nsec;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	if (NFS_SERVER(&nfsi->vfs_inode)->nfs_client->rpc_ops->version == 4)
2388c2ecf20Sopenharmony_ci		auxdata->change_attr = inode_peek_iversion_raw(&nfsi->vfs_inode);
2398c2ecf20Sopenharmony_ci}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci/*
2428c2ecf20Sopenharmony_ci * Initialise the per-inode cache cookie pointer for an NFS inode.
2438c2ecf20Sopenharmony_ci */
2448c2ecf20Sopenharmony_civoid nfs_fscache_init_inode(struct inode *inode)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	struct nfs_fscache_inode_auxdata auxdata;
2478c2ecf20Sopenharmony_ci	struct nfs_server *nfss = NFS_SERVER(inode);
2488c2ecf20Sopenharmony_ci	struct nfs_inode *nfsi = NFS_I(inode);
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	nfsi->fscache = NULL;
2518c2ecf20Sopenharmony_ci	if (!(nfss->fscache && S_ISREG(inode->i_mode)))
2528c2ecf20Sopenharmony_ci		return;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	nfs_fscache_update_auxdata(&auxdata, nfsi);
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	nfsi->fscache = fscache_acquire_cookie(NFS_SB(inode->i_sb)->fscache,
2578c2ecf20Sopenharmony_ci					       &nfs_fscache_inode_object_def,
2588c2ecf20Sopenharmony_ci					       nfsi->fh.data, nfsi->fh.size,
2598c2ecf20Sopenharmony_ci					       &auxdata, sizeof(auxdata),
2608c2ecf20Sopenharmony_ci					       nfsi, nfsi->vfs_inode.i_size, false);
2618c2ecf20Sopenharmony_ci}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci/*
2648c2ecf20Sopenharmony_ci * Release a per-inode cookie.
2658c2ecf20Sopenharmony_ci */
2668c2ecf20Sopenharmony_civoid nfs_fscache_clear_inode(struct inode *inode)
2678c2ecf20Sopenharmony_ci{
2688c2ecf20Sopenharmony_ci	struct nfs_fscache_inode_auxdata auxdata;
2698c2ecf20Sopenharmony_ci	struct nfs_inode *nfsi = NFS_I(inode);
2708c2ecf20Sopenharmony_ci	struct fscache_cookie *cookie = nfs_i_fscache(inode);
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	dfprintk(FSCACHE, "NFS: clear cookie (0x%p/0x%p)\n", nfsi, cookie);
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	nfs_fscache_update_auxdata(&auxdata, nfsi);
2758c2ecf20Sopenharmony_ci	fscache_relinquish_cookie(cookie, &auxdata, false);
2768c2ecf20Sopenharmony_ci	nfsi->fscache = NULL;
2778c2ecf20Sopenharmony_ci}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_cistatic bool nfs_fscache_can_enable(void *data)
2808c2ecf20Sopenharmony_ci{
2818c2ecf20Sopenharmony_ci	struct inode *inode = data;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	return !inode_is_open_for_write(inode);
2848c2ecf20Sopenharmony_ci}
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci/*
2878c2ecf20Sopenharmony_ci * Enable or disable caching for a file that is being opened as appropriate.
2888c2ecf20Sopenharmony_ci * The cookie is allocated when the inode is initialised, but is not enabled at
2898c2ecf20Sopenharmony_ci * that time.  Enablement is deferred to file-open time to avoid stat() and
2908c2ecf20Sopenharmony_ci * access() thrashing the cache.
2918c2ecf20Sopenharmony_ci *
2928c2ecf20Sopenharmony_ci * For now, with NFS, only regular files that are open read-only will be able
2938c2ecf20Sopenharmony_ci * to use the cache.
2948c2ecf20Sopenharmony_ci *
2958c2ecf20Sopenharmony_ci * We enable the cache for an inode if we open it read-only and it isn't
2968c2ecf20Sopenharmony_ci * currently open for writing.  We disable the cache if the inode is open
2978c2ecf20Sopenharmony_ci * write-only.
2988c2ecf20Sopenharmony_ci *
2998c2ecf20Sopenharmony_ci * The caller uses the file struct to pin i_writecount on the inode before
3008c2ecf20Sopenharmony_ci * calling us when a file is opened for writing, so we can make use of that.
3018c2ecf20Sopenharmony_ci *
3028c2ecf20Sopenharmony_ci * Note that this may be invoked multiple times in parallel by parallel
3038c2ecf20Sopenharmony_ci * nfs_open() functions.
3048c2ecf20Sopenharmony_ci */
3058c2ecf20Sopenharmony_civoid nfs_fscache_open_file(struct inode *inode, struct file *filp)
3068c2ecf20Sopenharmony_ci{
3078c2ecf20Sopenharmony_ci	struct nfs_fscache_inode_auxdata auxdata;
3088c2ecf20Sopenharmony_ci	struct nfs_inode *nfsi = NFS_I(inode);
3098c2ecf20Sopenharmony_ci	struct fscache_cookie *cookie = nfs_i_fscache(inode);
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	if (!fscache_cookie_valid(cookie))
3128c2ecf20Sopenharmony_ci		return;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	nfs_fscache_update_auxdata(&auxdata, nfsi);
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	if (inode_is_open_for_write(inode)) {
3178c2ecf20Sopenharmony_ci		dfprintk(FSCACHE, "NFS: nfsi 0x%p disabling cache\n", nfsi);
3188c2ecf20Sopenharmony_ci		clear_bit(NFS_INO_FSCACHE, &nfsi->flags);
3198c2ecf20Sopenharmony_ci		fscache_disable_cookie(cookie, &auxdata, true);
3208c2ecf20Sopenharmony_ci		fscache_uncache_all_inode_pages(cookie, inode);
3218c2ecf20Sopenharmony_ci	} else {
3228c2ecf20Sopenharmony_ci		dfprintk(FSCACHE, "NFS: nfsi 0x%p enabling cache\n", nfsi);
3238c2ecf20Sopenharmony_ci		fscache_enable_cookie(cookie, &auxdata, nfsi->vfs_inode.i_size,
3248c2ecf20Sopenharmony_ci				      nfs_fscache_can_enable, inode);
3258c2ecf20Sopenharmony_ci		if (fscache_cookie_enabled(cookie))
3268c2ecf20Sopenharmony_ci			set_bit(NFS_INO_FSCACHE, &NFS_I(inode)->flags);
3278c2ecf20Sopenharmony_ci	}
3288c2ecf20Sopenharmony_ci}
3298c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_fscache_open_file);
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci/*
3328c2ecf20Sopenharmony_ci * Release the caching state associated with a page, if the page isn't busy
3338c2ecf20Sopenharmony_ci * interacting with the cache.
3348c2ecf20Sopenharmony_ci * - Returns true (can release page) or false (page busy).
3358c2ecf20Sopenharmony_ci */
3368c2ecf20Sopenharmony_ciint nfs_fscache_release_page(struct page *page, gfp_t gfp)
3378c2ecf20Sopenharmony_ci{
3388c2ecf20Sopenharmony_ci	if (PageFsCache(page)) {
3398c2ecf20Sopenharmony_ci		struct fscache_cookie *cookie = nfs_i_fscache(page->mapping->host);
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci		BUG_ON(!cookie);
3428c2ecf20Sopenharmony_ci		dfprintk(FSCACHE, "NFS: fscache releasepage (0x%p/0x%p/0x%p)\n",
3438c2ecf20Sopenharmony_ci			 cookie, page, NFS_I(page->mapping->host));
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci		if (!fscache_maybe_release_page(cookie, page, gfp))
3468c2ecf20Sopenharmony_ci			return 0;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci		nfs_inc_fscache_stats(page->mapping->host,
3498c2ecf20Sopenharmony_ci				      NFSIOS_FSCACHE_PAGES_UNCACHED);
3508c2ecf20Sopenharmony_ci	}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	return 1;
3538c2ecf20Sopenharmony_ci}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci/*
3568c2ecf20Sopenharmony_ci * Release the caching state associated with a page if undergoing complete page
3578c2ecf20Sopenharmony_ci * invalidation.
3588c2ecf20Sopenharmony_ci */
3598c2ecf20Sopenharmony_civoid __nfs_fscache_invalidate_page(struct page *page, struct inode *inode)
3608c2ecf20Sopenharmony_ci{
3618c2ecf20Sopenharmony_ci	struct fscache_cookie *cookie = nfs_i_fscache(inode);
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	BUG_ON(!cookie);
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	dfprintk(FSCACHE, "NFS: fscache invalidatepage (0x%p/0x%p/0x%p)\n",
3668c2ecf20Sopenharmony_ci		 cookie, page, NFS_I(inode));
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	fscache_wait_on_page_write(cookie, page);
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	BUG_ON(!PageLocked(page));
3718c2ecf20Sopenharmony_ci	fscache_uncache_page(cookie, page);
3728c2ecf20Sopenharmony_ci	nfs_inc_fscache_stats(page->mapping->host,
3738c2ecf20Sopenharmony_ci			      NFSIOS_FSCACHE_PAGES_UNCACHED);
3748c2ecf20Sopenharmony_ci}
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci/*
3778c2ecf20Sopenharmony_ci * Handle completion of a page being read from the cache.
3788c2ecf20Sopenharmony_ci * - Called in process (keventd) context.
3798c2ecf20Sopenharmony_ci */
3808c2ecf20Sopenharmony_cistatic void nfs_readpage_from_fscache_complete(struct page *page,
3818c2ecf20Sopenharmony_ci					       void *context,
3828c2ecf20Sopenharmony_ci					       int error)
3838c2ecf20Sopenharmony_ci{
3848c2ecf20Sopenharmony_ci	dfprintk(FSCACHE,
3858c2ecf20Sopenharmony_ci		 "NFS: readpage_from_fscache_complete (0x%p/0x%p/%d)\n",
3868c2ecf20Sopenharmony_ci		 page, context, error);
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	/* if the read completes with an error, we just unlock the page and let
3898c2ecf20Sopenharmony_ci	 * the VM reissue the readpage */
3908c2ecf20Sopenharmony_ci	if (!error) {
3918c2ecf20Sopenharmony_ci		SetPageUptodate(page);
3928c2ecf20Sopenharmony_ci		unlock_page(page);
3938c2ecf20Sopenharmony_ci	} else {
3948c2ecf20Sopenharmony_ci		error = nfs_readpage_async(context, page->mapping->host, page);
3958c2ecf20Sopenharmony_ci		if (error)
3968c2ecf20Sopenharmony_ci			unlock_page(page);
3978c2ecf20Sopenharmony_ci	}
3988c2ecf20Sopenharmony_ci}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci/*
4018c2ecf20Sopenharmony_ci * Retrieve a page from fscache
4028c2ecf20Sopenharmony_ci */
4038c2ecf20Sopenharmony_ciint __nfs_readpage_from_fscache(struct nfs_open_context *ctx,
4048c2ecf20Sopenharmony_ci				struct inode *inode, struct page *page)
4058c2ecf20Sopenharmony_ci{
4068c2ecf20Sopenharmony_ci	int ret;
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	dfprintk(FSCACHE,
4098c2ecf20Sopenharmony_ci		 "NFS: readpage_from_fscache(fsc:%p/p:%p(i:%lx f:%lx)/0x%p)\n",
4108c2ecf20Sopenharmony_ci		 nfs_i_fscache(inode), page, page->index, page->flags, inode);
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	ret = fscache_read_or_alloc_page(nfs_i_fscache(inode),
4138c2ecf20Sopenharmony_ci					 page,
4148c2ecf20Sopenharmony_ci					 nfs_readpage_from_fscache_complete,
4158c2ecf20Sopenharmony_ci					 ctx,
4168c2ecf20Sopenharmony_ci					 GFP_KERNEL);
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	switch (ret) {
4198c2ecf20Sopenharmony_ci	case 0: /* read BIO submitted (page in fscache) */
4208c2ecf20Sopenharmony_ci		dfprintk(FSCACHE,
4218c2ecf20Sopenharmony_ci			 "NFS:    readpage_from_fscache: BIO submitted\n");
4228c2ecf20Sopenharmony_ci		nfs_inc_fscache_stats(inode, NFSIOS_FSCACHE_PAGES_READ_OK);
4238c2ecf20Sopenharmony_ci		return ret;
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	case -ENOBUFS: /* inode not in cache */
4268c2ecf20Sopenharmony_ci	case -ENODATA: /* page not in cache */
4278c2ecf20Sopenharmony_ci		nfs_inc_fscache_stats(inode, NFSIOS_FSCACHE_PAGES_READ_FAIL);
4288c2ecf20Sopenharmony_ci		dfprintk(FSCACHE,
4298c2ecf20Sopenharmony_ci			 "NFS:    readpage_from_fscache %d\n", ret);
4308c2ecf20Sopenharmony_ci		return 1;
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	default:
4338c2ecf20Sopenharmony_ci		dfprintk(FSCACHE, "NFS:    readpage_from_fscache %d\n", ret);
4348c2ecf20Sopenharmony_ci		nfs_inc_fscache_stats(inode, NFSIOS_FSCACHE_PAGES_READ_FAIL);
4358c2ecf20Sopenharmony_ci	}
4368c2ecf20Sopenharmony_ci	return ret;
4378c2ecf20Sopenharmony_ci}
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci/*
4408c2ecf20Sopenharmony_ci * Retrieve a set of pages from fscache
4418c2ecf20Sopenharmony_ci */
4428c2ecf20Sopenharmony_ciint __nfs_readpages_from_fscache(struct nfs_open_context *ctx,
4438c2ecf20Sopenharmony_ci				 struct inode *inode,
4448c2ecf20Sopenharmony_ci				 struct address_space *mapping,
4458c2ecf20Sopenharmony_ci				 struct list_head *pages,
4468c2ecf20Sopenharmony_ci				 unsigned *nr_pages)
4478c2ecf20Sopenharmony_ci{
4488c2ecf20Sopenharmony_ci	unsigned npages = *nr_pages;
4498c2ecf20Sopenharmony_ci	int ret;
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	dfprintk(FSCACHE, "NFS: nfs_getpages_from_fscache (0x%p/%u/0x%p)\n",
4528c2ecf20Sopenharmony_ci		 nfs_i_fscache(inode), npages, inode);
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	ret = fscache_read_or_alloc_pages(nfs_i_fscache(inode),
4558c2ecf20Sopenharmony_ci					  mapping, pages, nr_pages,
4568c2ecf20Sopenharmony_ci					  nfs_readpage_from_fscache_complete,
4578c2ecf20Sopenharmony_ci					  ctx,
4588c2ecf20Sopenharmony_ci					  mapping_gfp_mask(mapping));
4598c2ecf20Sopenharmony_ci	if (*nr_pages < npages)
4608c2ecf20Sopenharmony_ci		nfs_add_fscache_stats(inode, NFSIOS_FSCACHE_PAGES_READ_OK,
4618c2ecf20Sopenharmony_ci				      npages);
4628c2ecf20Sopenharmony_ci	if (*nr_pages > 0)
4638c2ecf20Sopenharmony_ci		nfs_add_fscache_stats(inode, NFSIOS_FSCACHE_PAGES_READ_FAIL,
4648c2ecf20Sopenharmony_ci				      *nr_pages);
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	switch (ret) {
4678c2ecf20Sopenharmony_ci	case 0: /* read submitted to the cache for all pages */
4688c2ecf20Sopenharmony_ci		BUG_ON(!list_empty(pages));
4698c2ecf20Sopenharmony_ci		BUG_ON(*nr_pages != 0);
4708c2ecf20Sopenharmony_ci		dfprintk(FSCACHE,
4718c2ecf20Sopenharmony_ci			 "NFS: nfs_getpages_from_fscache: submitted\n");
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci		return ret;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	case -ENOBUFS: /* some pages aren't cached and can't be */
4768c2ecf20Sopenharmony_ci	case -ENODATA: /* some pages aren't cached */
4778c2ecf20Sopenharmony_ci		dfprintk(FSCACHE,
4788c2ecf20Sopenharmony_ci			 "NFS: nfs_getpages_from_fscache: no page: %d\n", ret);
4798c2ecf20Sopenharmony_ci		return 1;
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	default:
4828c2ecf20Sopenharmony_ci		dfprintk(FSCACHE,
4838c2ecf20Sopenharmony_ci			 "NFS: nfs_getpages_from_fscache: ret  %d\n", ret);
4848c2ecf20Sopenharmony_ci	}
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	return ret;
4878c2ecf20Sopenharmony_ci}
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci/*
4908c2ecf20Sopenharmony_ci * Store a newly fetched page in fscache
4918c2ecf20Sopenharmony_ci * - PG_fscache must be set on the page
4928c2ecf20Sopenharmony_ci */
4938c2ecf20Sopenharmony_civoid __nfs_readpage_to_fscache(struct inode *inode, struct page *page, int sync)
4948c2ecf20Sopenharmony_ci{
4958c2ecf20Sopenharmony_ci	int ret;
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	dfprintk(FSCACHE,
4988c2ecf20Sopenharmony_ci		 "NFS: readpage_to_fscache(fsc:%p/p:%p(i:%lx f:%lx)/%d)\n",
4998c2ecf20Sopenharmony_ci		 nfs_i_fscache(inode), page, page->index, page->flags, sync);
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	ret = fscache_write_page(nfs_i_fscache(inode), page,
5028c2ecf20Sopenharmony_ci				 inode->i_size, GFP_KERNEL);
5038c2ecf20Sopenharmony_ci	dfprintk(FSCACHE,
5048c2ecf20Sopenharmony_ci		 "NFS:     readpage_to_fscache: p:%p(i:%lu f:%lx) ret %d\n",
5058c2ecf20Sopenharmony_ci		 page, page->index, page->flags, ret);
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	if (ret != 0) {
5088c2ecf20Sopenharmony_ci		fscache_uncache_page(nfs_i_fscache(inode), page);
5098c2ecf20Sopenharmony_ci		nfs_inc_fscache_stats(inode,
5108c2ecf20Sopenharmony_ci				      NFSIOS_FSCACHE_PAGES_WRITTEN_FAIL);
5118c2ecf20Sopenharmony_ci		nfs_inc_fscache_stats(inode, NFSIOS_FSCACHE_PAGES_UNCACHED);
5128c2ecf20Sopenharmony_ci	} else {
5138c2ecf20Sopenharmony_ci		nfs_inc_fscache_stats(inode,
5148c2ecf20Sopenharmony_ci				      NFSIOS_FSCACHE_PAGES_WRITTEN_OK);
5158c2ecf20Sopenharmony_ci	}
5168c2ecf20Sopenharmony_ci}
517