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