114727281SDavid Howells /* NFS filesystem cache interface 214727281SDavid Howells * 314727281SDavid Howells * Copyright (C) 2008 Red Hat, Inc. All Rights Reserved. 414727281SDavid Howells * Written by David Howells (dhowells@redhat.com) 514727281SDavid Howells * 614727281SDavid Howells * This program is free software; you can redistribute it and/or 714727281SDavid Howells * modify it under the terms of the GNU General Public Licence 814727281SDavid Howells * as published by the Free Software Foundation; either version 914727281SDavid Howells * 2 of the Licence, or (at your option) any later version. 1014727281SDavid Howells */ 1114727281SDavid Howells 1214727281SDavid Howells #include <linux/init.h> 1314727281SDavid Howells #include <linux/kernel.h> 1414727281SDavid Howells #include <linux/sched.h> 1514727281SDavid Howells #include <linux/mm.h> 1614727281SDavid Howells #include <linux/nfs_fs.h> 1714727281SDavid Howells #include <linux/nfs_fs_sb.h> 1814727281SDavid Howells #include <linux/in6.h> 1914727281SDavid Howells #include <linux/seq_file.h> 2014727281SDavid Howells 2114727281SDavid Howells #include "internal.h" 22545db45fSDavid Howells #include "iostat.h" 2314727281SDavid Howells #include "fscache.h" 2414727281SDavid Howells 2514727281SDavid Howells #define NFSDBG_FACILITY NFSDBG_FSCACHE 2614727281SDavid Howells 2708734048SDavid Howells static struct rb_root nfs_fscache_keys = RB_ROOT; 2808734048SDavid Howells static DEFINE_SPINLOCK(nfs_fscache_keys_lock); 2908734048SDavid Howells 3014727281SDavid Howells /* 3114727281SDavid Howells * Get the per-client index cookie for an NFS client if the appropriate mount 3214727281SDavid Howells * flag was set 3314727281SDavid Howells * - We always try and get an index cookie for the client, but get filehandle 3414727281SDavid Howells * cookies on a per-superblock basis, depending on the mount flags 3514727281SDavid Howells */ 3614727281SDavid Howells void nfs_fscache_get_client_cookie(struct nfs_client *clp) 3714727281SDavid Howells { 3814727281SDavid Howells /* create a cache index for looking up filehandles */ 3914727281SDavid Howells clp->fscache = fscache_acquire_cookie(nfs_fscache_netfs.primary_index, 4014727281SDavid Howells &nfs_fscache_server_index_def, 4114727281SDavid Howells clp); 4214727281SDavid Howells dfprintk(FSCACHE, "NFS: get client cookie (0x%p/0x%p)\n", 4314727281SDavid Howells clp, clp->fscache); 4414727281SDavid Howells } 4514727281SDavid Howells 4614727281SDavid Howells /* 4714727281SDavid Howells * Dispose of a per-client cookie 4814727281SDavid Howells */ 4914727281SDavid Howells void nfs_fscache_release_client_cookie(struct nfs_client *clp) 5014727281SDavid Howells { 5114727281SDavid Howells dfprintk(FSCACHE, "NFS: releasing client cookie (0x%p/0x%p)\n", 5214727281SDavid Howells clp, clp->fscache); 5314727281SDavid Howells 5414727281SDavid Howells fscache_relinquish_cookie(clp->fscache, 0); 5514727281SDavid Howells clp->fscache = NULL; 5614727281SDavid Howells } 5708734048SDavid Howells 5808734048SDavid Howells /* 5908734048SDavid Howells * Get the cache cookie for an NFS superblock. We have to handle 6008734048SDavid Howells * uniquification here because the cache doesn't do it for us. 612df54806SDavid Howells * 622df54806SDavid Howells * The default uniquifier is just an empty string, but it may be overridden 632df54806SDavid Howells * either by the 'fsc=xxx' option to mount, or by inheriting it from the parent 642df54806SDavid Howells * superblock across an automount point of some nature. 6508734048SDavid Howells */ 662df54806SDavid Howells void nfs_fscache_get_super_cookie(struct super_block *sb, const char *uniq, 672df54806SDavid Howells struct nfs_clone_mount *mntdata) 6808734048SDavid Howells { 6908734048SDavid Howells struct nfs_fscache_key *key, *xkey; 7008734048SDavid Howells struct nfs_server *nfss = NFS_SB(sb); 7108734048SDavid Howells struct rb_node **p, *parent; 7208734048SDavid Howells int diff, ulen; 7308734048SDavid Howells 742df54806SDavid Howells if (uniq) { 7508734048SDavid Howells ulen = strlen(uniq); 762df54806SDavid Howells } else if (mntdata) { 772df54806SDavid Howells struct nfs_server *mnt_s = NFS_SB(mntdata->sb); 782df54806SDavid Howells if (mnt_s->fscache_key) { 792df54806SDavid Howells uniq = mnt_s->fscache_key->key.uniquifier; 802df54806SDavid Howells ulen = mnt_s->fscache_key->key.uniq_len; 812df54806SDavid Howells } 822df54806SDavid Howells } 832df54806SDavid Howells 842df54806SDavid Howells if (!uniq) { 852df54806SDavid Howells uniq = ""; 862df54806SDavid Howells ulen = 1; 872df54806SDavid Howells } 882df54806SDavid Howells 8908734048SDavid Howells key = kzalloc(sizeof(*key) + ulen, GFP_KERNEL); 9008734048SDavid Howells if (!key) 9108734048SDavid Howells return; 9208734048SDavid Howells 9308734048SDavid Howells key->nfs_client = nfss->nfs_client; 9408734048SDavid Howells key->key.super.s_flags = sb->s_flags & NFS_MS_MASK; 9508734048SDavid Howells key->key.nfs_server.flags = nfss->flags; 9608734048SDavid Howells key->key.nfs_server.rsize = nfss->rsize; 9708734048SDavid Howells key->key.nfs_server.wsize = nfss->wsize; 9808734048SDavid Howells key->key.nfs_server.acregmin = nfss->acregmin; 9908734048SDavid Howells key->key.nfs_server.acregmax = nfss->acregmax; 10008734048SDavid Howells key->key.nfs_server.acdirmin = nfss->acdirmin; 10108734048SDavid Howells key->key.nfs_server.acdirmax = nfss->acdirmax; 10208734048SDavid Howells key->key.nfs_server.fsid = nfss->fsid; 10308734048SDavid Howells key->key.rpc_auth.au_flavor = nfss->client->cl_auth->au_flavor; 10408734048SDavid Howells 10508734048SDavid Howells key->key.uniq_len = ulen; 10608734048SDavid Howells memcpy(key->key.uniquifier, uniq, ulen); 10708734048SDavid Howells 10808734048SDavid Howells spin_lock(&nfs_fscache_keys_lock); 10908734048SDavid Howells p = &nfs_fscache_keys.rb_node; 11008734048SDavid Howells parent = NULL; 11108734048SDavid Howells while (*p) { 11208734048SDavid Howells parent = *p; 11308734048SDavid Howells xkey = rb_entry(parent, struct nfs_fscache_key, node); 11408734048SDavid Howells 11508734048SDavid Howells if (key->nfs_client < xkey->nfs_client) 11608734048SDavid Howells goto go_left; 11708734048SDavid Howells if (key->nfs_client > xkey->nfs_client) 11808734048SDavid Howells goto go_right; 11908734048SDavid Howells 12008734048SDavid Howells diff = memcmp(&key->key, &xkey->key, sizeof(key->key)); 12108734048SDavid Howells if (diff < 0) 12208734048SDavid Howells goto go_left; 12308734048SDavid Howells if (diff > 0) 12408734048SDavid Howells goto go_right; 12508734048SDavid Howells 12608734048SDavid Howells if (key->key.uniq_len == 0) 12708734048SDavid Howells goto non_unique; 12808734048SDavid Howells diff = memcmp(key->key.uniquifier, 12908734048SDavid Howells xkey->key.uniquifier, 13008734048SDavid Howells key->key.uniq_len); 13108734048SDavid Howells if (diff < 0) 13208734048SDavid Howells goto go_left; 13308734048SDavid Howells if (diff > 0) 13408734048SDavid Howells goto go_right; 13508734048SDavid Howells goto non_unique; 13608734048SDavid Howells 13708734048SDavid Howells go_left: 13808734048SDavid Howells p = &(*p)->rb_left; 13908734048SDavid Howells continue; 14008734048SDavid Howells go_right: 14108734048SDavid Howells p = &(*p)->rb_right; 14208734048SDavid Howells } 14308734048SDavid Howells 14408734048SDavid Howells rb_link_node(&key->node, parent, p); 14508734048SDavid Howells rb_insert_color(&key->node, &nfs_fscache_keys); 14608734048SDavid Howells spin_unlock(&nfs_fscache_keys_lock); 14708734048SDavid Howells nfss->fscache_key = key; 14808734048SDavid Howells 14908734048SDavid Howells /* create a cache index for looking up filehandles */ 15008734048SDavid Howells nfss->fscache = fscache_acquire_cookie(nfss->nfs_client->fscache, 15108734048SDavid Howells &nfs_fscache_super_index_def, 15208734048SDavid Howells nfss); 15308734048SDavid Howells dfprintk(FSCACHE, "NFS: get superblock cookie (0x%p/0x%p)\n", 15408734048SDavid Howells nfss, nfss->fscache); 15508734048SDavid Howells return; 15608734048SDavid Howells 15708734048SDavid Howells non_unique: 15808734048SDavid Howells spin_unlock(&nfs_fscache_keys_lock); 15908734048SDavid Howells kfree(key); 16008734048SDavid Howells nfss->fscache_key = NULL; 16108734048SDavid Howells nfss->fscache = NULL; 16208734048SDavid Howells printk(KERN_WARNING "NFS:" 16308734048SDavid Howells " Cache request denied due to non-unique superblock keys\n"); 16408734048SDavid Howells } 16508734048SDavid Howells 16608734048SDavid Howells /* 16708734048SDavid Howells * release a per-superblock cookie 16808734048SDavid Howells */ 16908734048SDavid Howells void nfs_fscache_release_super_cookie(struct super_block *sb) 17008734048SDavid Howells { 17108734048SDavid Howells struct nfs_server *nfss = NFS_SB(sb); 17208734048SDavid Howells 17308734048SDavid Howells dfprintk(FSCACHE, "NFS: releasing superblock cookie (0x%p/0x%p)\n", 17408734048SDavid Howells nfss, nfss->fscache); 17508734048SDavid Howells 17608734048SDavid Howells fscache_relinquish_cookie(nfss->fscache, 0); 17708734048SDavid Howells nfss->fscache = NULL; 17808734048SDavid Howells 17908734048SDavid Howells if (nfss->fscache_key) { 18008734048SDavid Howells spin_lock(&nfs_fscache_keys_lock); 18108734048SDavid Howells rb_erase(&nfss->fscache_key->node, &nfs_fscache_keys); 18208734048SDavid Howells spin_unlock(&nfs_fscache_keys_lock); 18308734048SDavid Howells kfree(nfss->fscache_key); 18408734048SDavid Howells nfss->fscache_key = NULL; 18508734048SDavid Howells } 18608734048SDavid Howells } 187ef79c097SDavid Howells 188ef79c097SDavid Howells /* 189ef79c097SDavid Howells * Initialise the per-inode cache cookie pointer for an NFS inode. 190ef79c097SDavid Howells */ 191ef79c097SDavid Howells void nfs_fscache_init_inode_cookie(struct inode *inode) 192ef79c097SDavid Howells { 193ef79c097SDavid Howells NFS_I(inode)->fscache = NULL; 194ef79c097SDavid Howells if (S_ISREG(inode->i_mode)) 195ef79c097SDavid Howells set_bit(NFS_INO_FSCACHE, &NFS_I(inode)->flags); 196ef79c097SDavid Howells } 197ef79c097SDavid Howells 198ef79c097SDavid Howells /* 199ef79c097SDavid Howells * Get the per-inode cache cookie for an NFS inode. 200ef79c097SDavid Howells */ 201ef79c097SDavid Howells static void nfs_fscache_enable_inode_cookie(struct inode *inode) 202ef79c097SDavid Howells { 203ef79c097SDavid Howells struct super_block *sb = inode->i_sb; 204ef79c097SDavid Howells struct nfs_inode *nfsi = NFS_I(inode); 205ef79c097SDavid Howells 206ef79c097SDavid Howells if (nfsi->fscache || !NFS_FSCACHE(inode)) 207ef79c097SDavid Howells return; 208ef79c097SDavid Howells 209ef79c097SDavid Howells if ((NFS_SB(sb)->options & NFS_OPTION_FSCACHE)) { 210ef79c097SDavid Howells nfsi->fscache = fscache_acquire_cookie( 211ef79c097SDavid Howells NFS_SB(sb)->fscache, 212ef79c097SDavid Howells &nfs_fscache_inode_object_def, 213ef79c097SDavid Howells nfsi); 214ef79c097SDavid Howells 215ef79c097SDavid Howells dfprintk(FSCACHE, "NFS: get FH cookie (0x%p/0x%p/0x%p)\n", 216ef79c097SDavid Howells sb, nfsi, nfsi->fscache); 217ef79c097SDavid Howells } 218ef79c097SDavid Howells } 219ef79c097SDavid Howells 220ef79c097SDavid Howells /* 221ef79c097SDavid Howells * Release a per-inode cookie. 222ef79c097SDavid Howells */ 223ef79c097SDavid Howells void nfs_fscache_release_inode_cookie(struct inode *inode) 224ef79c097SDavid Howells { 225ef79c097SDavid Howells struct nfs_inode *nfsi = NFS_I(inode); 226ef79c097SDavid Howells 227ef79c097SDavid Howells dfprintk(FSCACHE, "NFS: clear cookie (0x%p/0x%p)\n", 228ef79c097SDavid Howells nfsi, nfsi->fscache); 229ef79c097SDavid Howells 230ef79c097SDavid Howells fscache_relinquish_cookie(nfsi->fscache, 0); 231ef79c097SDavid Howells nfsi->fscache = NULL; 232ef79c097SDavid Howells } 233ef79c097SDavid Howells 234ef79c097SDavid Howells /* 235ef79c097SDavid Howells * Retire a per-inode cookie, destroying the data attached to it. 236ef79c097SDavid Howells */ 237ef79c097SDavid Howells void nfs_fscache_zap_inode_cookie(struct inode *inode) 238ef79c097SDavid Howells { 239ef79c097SDavid Howells struct nfs_inode *nfsi = NFS_I(inode); 240ef79c097SDavid Howells 241ef79c097SDavid Howells dfprintk(FSCACHE, "NFS: zapping cookie (0x%p/0x%p)\n", 242ef79c097SDavid Howells nfsi, nfsi->fscache); 243ef79c097SDavid Howells 244ef79c097SDavid Howells fscache_relinquish_cookie(nfsi->fscache, 1); 245ef79c097SDavid Howells nfsi->fscache = NULL; 246ef79c097SDavid Howells } 247ef79c097SDavid Howells 248ef79c097SDavid Howells /* 249ef79c097SDavid Howells * Turn off the cache with regard to a per-inode cookie if opened for writing, 250ef79c097SDavid Howells * invalidating all the pages in the page cache relating to the associated 251ef79c097SDavid Howells * inode to clear the per-page caching. 252ef79c097SDavid Howells */ 253ef79c097SDavid Howells static void nfs_fscache_disable_inode_cookie(struct inode *inode) 254ef79c097SDavid Howells { 255ef79c097SDavid Howells clear_bit(NFS_INO_FSCACHE, &NFS_I(inode)->flags); 256ef79c097SDavid Howells 257ef79c097SDavid Howells if (NFS_I(inode)->fscache) { 258ef79c097SDavid Howells dfprintk(FSCACHE, 259ef79c097SDavid Howells "NFS: nfsi 0x%p turning cache off\n", NFS_I(inode)); 260ef79c097SDavid Howells 261ef79c097SDavid Howells /* Need to invalidate any mapped pages that were read in before 262ef79c097SDavid Howells * turning off the cache. 263ef79c097SDavid Howells */ 264ef79c097SDavid Howells if (inode->i_mapping && inode->i_mapping->nrpages) 265ef79c097SDavid Howells invalidate_inode_pages2(inode->i_mapping); 266ef79c097SDavid Howells 267ef79c097SDavid Howells nfs_fscache_zap_inode_cookie(inode); 268ef79c097SDavid Howells } 269ef79c097SDavid Howells } 270ef79c097SDavid Howells 271ef79c097SDavid Howells /* 272ef79c097SDavid Howells * wait_on_bit() sleep function for uninterruptible waiting 273ef79c097SDavid Howells */ 274ef79c097SDavid Howells static int nfs_fscache_wait_bit(void *flags) 275ef79c097SDavid Howells { 276ef79c097SDavid Howells schedule(); 277ef79c097SDavid Howells return 0; 278ef79c097SDavid Howells } 279ef79c097SDavid Howells 280ef79c097SDavid Howells /* 281ef79c097SDavid Howells * Lock against someone else trying to also acquire or relinquish a cookie 282ef79c097SDavid Howells */ 283ef79c097SDavid Howells static inline void nfs_fscache_inode_lock(struct inode *inode) 284ef79c097SDavid Howells { 285ef79c097SDavid Howells struct nfs_inode *nfsi = NFS_I(inode); 286ef79c097SDavid Howells 287ef79c097SDavid Howells while (test_and_set_bit(NFS_INO_FSCACHE_LOCK, &nfsi->flags)) 288ef79c097SDavid Howells wait_on_bit(&nfsi->flags, NFS_INO_FSCACHE_LOCK, 289ef79c097SDavid Howells nfs_fscache_wait_bit, TASK_UNINTERRUPTIBLE); 290ef79c097SDavid Howells } 291ef79c097SDavid Howells 292ef79c097SDavid Howells /* 293ef79c097SDavid Howells * Unlock cookie management lock 294ef79c097SDavid Howells */ 295ef79c097SDavid Howells static inline void nfs_fscache_inode_unlock(struct inode *inode) 296ef79c097SDavid Howells { 297ef79c097SDavid Howells struct nfs_inode *nfsi = NFS_I(inode); 298ef79c097SDavid Howells 299ef79c097SDavid Howells smp_mb__before_clear_bit(); 300ef79c097SDavid Howells clear_bit(NFS_INO_FSCACHE_LOCK, &nfsi->flags); 301ef79c097SDavid Howells smp_mb__after_clear_bit(); 302ef79c097SDavid Howells wake_up_bit(&nfsi->flags, NFS_INO_FSCACHE_LOCK); 303ef79c097SDavid Howells } 304ef79c097SDavid Howells 305ef79c097SDavid Howells /* 306ef79c097SDavid Howells * Decide if we should enable or disable local caching for this inode. 307ef79c097SDavid Howells * - For now, with NFS, only regular files that are open read-only will be able 308ef79c097SDavid Howells * to use the cache. 309ef79c097SDavid Howells * - May be invoked multiple times in parallel by parallel nfs_open() functions. 310ef79c097SDavid Howells */ 311ef79c097SDavid Howells void nfs_fscache_set_inode_cookie(struct inode *inode, struct file *filp) 312ef79c097SDavid Howells { 313ef79c097SDavid Howells if (NFS_FSCACHE(inode)) { 314ef79c097SDavid Howells nfs_fscache_inode_lock(inode); 315ef79c097SDavid Howells if ((filp->f_flags & O_ACCMODE) != O_RDONLY) 316ef79c097SDavid Howells nfs_fscache_disable_inode_cookie(inode); 317ef79c097SDavid Howells else 318ef79c097SDavid Howells nfs_fscache_enable_inode_cookie(inode); 319ef79c097SDavid Howells nfs_fscache_inode_unlock(inode); 320ef79c097SDavid Howells } 321ef79c097SDavid Howells } 322ef79c097SDavid Howells 323ef79c097SDavid Howells /* 324ef79c097SDavid Howells * Replace a per-inode cookie due to revalidation detecting a file having 325ef79c097SDavid Howells * changed on the server. 326ef79c097SDavid Howells */ 327ef79c097SDavid Howells void nfs_fscache_reset_inode_cookie(struct inode *inode) 328ef79c097SDavid Howells { 329ef79c097SDavid Howells struct nfs_inode *nfsi = NFS_I(inode); 330ef79c097SDavid Howells struct nfs_server *nfss = NFS_SERVER(inode); 331ef79c097SDavid Howells struct fscache_cookie *old = nfsi->fscache; 332ef79c097SDavid Howells 333ef79c097SDavid Howells nfs_fscache_inode_lock(inode); 334ef79c097SDavid Howells if (nfsi->fscache) { 335ef79c097SDavid Howells /* retire the current fscache cache and get a new one */ 336ef79c097SDavid Howells fscache_relinquish_cookie(nfsi->fscache, 1); 337ef79c097SDavid Howells 338ef79c097SDavid Howells nfsi->fscache = fscache_acquire_cookie( 339ef79c097SDavid Howells nfss->nfs_client->fscache, 340ef79c097SDavid Howells &nfs_fscache_inode_object_def, 341ef79c097SDavid Howells nfsi); 342ef79c097SDavid Howells 343ef79c097SDavid Howells dfprintk(FSCACHE, 344ef79c097SDavid Howells "NFS: revalidation new cookie (0x%p/0x%p/0x%p/0x%p)\n", 345ef79c097SDavid Howells nfss, nfsi, old, nfsi->fscache); 346ef79c097SDavid Howells } 347ef79c097SDavid Howells nfs_fscache_inode_unlock(inode); 348ef79c097SDavid Howells } 349545db45fSDavid Howells 350545db45fSDavid Howells /* 351545db45fSDavid Howells * Release the caching state associated with a page, if the page isn't busy 352545db45fSDavid Howells * interacting with the cache. 353545db45fSDavid Howells * - Returns true (can release page) or false (page busy). 354545db45fSDavid Howells */ 355545db45fSDavid Howells int nfs_fscache_release_page(struct page *page, gfp_t gfp) 356545db45fSDavid Howells { 357545db45fSDavid Howells struct nfs_inode *nfsi = NFS_I(page->mapping->host); 358545db45fSDavid Howells struct fscache_cookie *cookie = nfsi->fscache; 359545db45fSDavid Howells 360545db45fSDavid Howells BUG_ON(!cookie); 361545db45fSDavid Howells 362545db45fSDavid Howells if (fscache_check_page_write(cookie, page)) { 363545db45fSDavid Howells if (!(gfp & __GFP_WAIT)) 364545db45fSDavid Howells return 0; 365545db45fSDavid Howells fscache_wait_on_page_write(cookie, page); 366545db45fSDavid Howells } 367545db45fSDavid Howells 368545db45fSDavid Howells if (PageFsCache(page)) { 369545db45fSDavid Howells dfprintk(FSCACHE, "NFS: fscache releasepage (0x%p/0x%p/0x%p)\n", 370545db45fSDavid Howells cookie, page, nfsi); 371545db45fSDavid Howells 372545db45fSDavid Howells fscache_uncache_page(cookie, page); 373545db45fSDavid Howells nfs_add_fscache_stats(page->mapping->host, 374545db45fSDavid Howells NFSIOS_FSCACHE_PAGES_UNCACHED, 1); 375545db45fSDavid Howells } 376545db45fSDavid Howells 377545db45fSDavid Howells return 1; 378545db45fSDavid Howells } 379545db45fSDavid Howells 380545db45fSDavid Howells /* 381545db45fSDavid Howells * Release the caching state associated with a page if undergoing complete page 382545db45fSDavid Howells * invalidation. 383545db45fSDavid Howells */ 384545db45fSDavid Howells void __nfs_fscache_invalidate_page(struct page *page, struct inode *inode) 385545db45fSDavid Howells { 386545db45fSDavid Howells struct nfs_inode *nfsi = NFS_I(inode); 387545db45fSDavid Howells struct fscache_cookie *cookie = nfsi->fscache; 388545db45fSDavid Howells 389545db45fSDavid Howells BUG_ON(!cookie); 390545db45fSDavid Howells 391545db45fSDavid Howells dfprintk(FSCACHE, "NFS: fscache invalidatepage (0x%p/0x%p/0x%p)\n", 392545db45fSDavid Howells cookie, page, nfsi); 393545db45fSDavid Howells 394545db45fSDavid Howells fscache_wait_on_page_write(cookie, page); 395545db45fSDavid Howells 396545db45fSDavid Howells BUG_ON(!PageLocked(page)); 397545db45fSDavid Howells fscache_uncache_page(cookie, page); 398545db45fSDavid Howells nfs_add_fscache_stats(page->mapping->host, 399545db45fSDavid Howells NFSIOS_FSCACHE_PAGES_UNCACHED, 1); 400545db45fSDavid Howells } 4019a9fc1c0SDavid Howells 4029a9fc1c0SDavid Howells /* 4039a9fc1c0SDavid Howells * Handle completion of a page being read from the cache. 4049a9fc1c0SDavid Howells * - Called in process (keventd) context. 4059a9fc1c0SDavid Howells */ 4069a9fc1c0SDavid Howells static void nfs_readpage_from_fscache_complete(struct page *page, 4079a9fc1c0SDavid Howells void *context, 4089a9fc1c0SDavid Howells int error) 4099a9fc1c0SDavid Howells { 4109a9fc1c0SDavid Howells dfprintk(FSCACHE, 4119a9fc1c0SDavid Howells "NFS: readpage_from_fscache_complete (0x%p/0x%p/%d)\n", 4129a9fc1c0SDavid Howells page, context, error); 4139a9fc1c0SDavid Howells 4149a9fc1c0SDavid Howells /* if the read completes with an error, we just unlock the page and let 4159a9fc1c0SDavid Howells * the VM reissue the readpage */ 4169a9fc1c0SDavid Howells if (!error) { 4179a9fc1c0SDavid Howells SetPageUptodate(page); 4189a9fc1c0SDavid Howells unlock_page(page); 4199a9fc1c0SDavid Howells } else { 4209a9fc1c0SDavid Howells error = nfs_readpage_async(context, page->mapping->host, page); 4219a9fc1c0SDavid Howells if (error) 4229a9fc1c0SDavid Howells unlock_page(page); 4239a9fc1c0SDavid Howells } 4249a9fc1c0SDavid Howells } 4259a9fc1c0SDavid Howells 4269a9fc1c0SDavid Howells /* 4279a9fc1c0SDavid Howells * Retrieve a page from fscache 4289a9fc1c0SDavid Howells */ 4299a9fc1c0SDavid Howells int __nfs_readpage_from_fscache(struct nfs_open_context *ctx, 4309a9fc1c0SDavid Howells struct inode *inode, struct page *page) 4319a9fc1c0SDavid Howells { 4329a9fc1c0SDavid Howells int ret; 4339a9fc1c0SDavid Howells 4349a9fc1c0SDavid Howells dfprintk(FSCACHE, 4359a9fc1c0SDavid Howells "NFS: readpage_from_fscache(fsc:%p/p:%p(i:%lx f:%lx)/0x%p)\n", 4369a9fc1c0SDavid Howells NFS_I(inode)->fscache, page, page->index, page->flags, inode); 4379a9fc1c0SDavid Howells 4389a9fc1c0SDavid Howells ret = fscache_read_or_alloc_page(NFS_I(inode)->fscache, 4399a9fc1c0SDavid Howells page, 4409a9fc1c0SDavid Howells nfs_readpage_from_fscache_complete, 4419a9fc1c0SDavid Howells ctx, 4429a9fc1c0SDavid Howells GFP_KERNEL); 4439a9fc1c0SDavid Howells 4449a9fc1c0SDavid Howells switch (ret) { 4459a9fc1c0SDavid Howells case 0: /* read BIO submitted (page in fscache) */ 4469a9fc1c0SDavid Howells dfprintk(FSCACHE, 4479a9fc1c0SDavid Howells "NFS: readpage_from_fscache: BIO submitted\n"); 4489a9fc1c0SDavid Howells nfs_add_fscache_stats(inode, NFSIOS_FSCACHE_PAGES_READ_OK, 1); 4499a9fc1c0SDavid Howells return ret; 4509a9fc1c0SDavid Howells 4519a9fc1c0SDavid Howells case -ENOBUFS: /* inode not in cache */ 4529a9fc1c0SDavid Howells case -ENODATA: /* page not in cache */ 4539a9fc1c0SDavid Howells nfs_add_fscache_stats(inode, NFSIOS_FSCACHE_PAGES_READ_FAIL, 1); 4549a9fc1c0SDavid Howells dfprintk(FSCACHE, 4559a9fc1c0SDavid Howells "NFS: readpage_from_fscache %d\n", ret); 4569a9fc1c0SDavid Howells return 1; 4579a9fc1c0SDavid Howells 4589a9fc1c0SDavid Howells default: 4599a9fc1c0SDavid Howells dfprintk(FSCACHE, "NFS: readpage_from_fscache %d\n", ret); 4609a9fc1c0SDavid Howells nfs_add_fscache_stats(inode, NFSIOS_FSCACHE_PAGES_READ_FAIL, 1); 4619a9fc1c0SDavid Howells } 4629a9fc1c0SDavid Howells return ret; 4639a9fc1c0SDavid Howells } 4649a9fc1c0SDavid Howells 4659a9fc1c0SDavid Howells /* 4669a9fc1c0SDavid Howells * Retrieve a set of pages from fscache 4679a9fc1c0SDavid Howells */ 4689a9fc1c0SDavid Howells int __nfs_readpages_from_fscache(struct nfs_open_context *ctx, 4699a9fc1c0SDavid Howells struct inode *inode, 4709a9fc1c0SDavid Howells struct address_space *mapping, 4719a9fc1c0SDavid Howells struct list_head *pages, 4729a9fc1c0SDavid Howells unsigned *nr_pages) 4739a9fc1c0SDavid Howells { 4749a9fc1c0SDavid Howells int ret, npages = *nr_pages; 4759a9fc1c0SDavid Howells 4769a9fc1c0SDavid Howells dfprintk(FSCACHE, "NFS: nfs_getpages_from_fscache (0x%p/%u/0x%p)\n", 4779a9fc1c0SDavid Howells NFS_I(inode)->fscache, npages, inode); 4789a9fc1c0SDavid Howells 4799a9fc1c0SDavid Howells ret = fscache_read_or_alloc_pages(NFS_I(inode)->fscache, 4809a9fc1c0SDavid Howells mapping, pages, nr_pages, 4819a9fc1c0SDavid Howells nfs_readpage_from_fscache_complete, 4829a9fc1c0SDavid Howells ctx, 4839a9fc1c0SDavid Howells mapping_gfp_mask(mapping)); 4849a9fc1c0SDavid Howells if (*nr_pages < npages) 4859a9fc1c0SDavid Howells nfs_add_fscache_stats(inode, NFSIOS_FSCACHE_PAGES_READ_OK, 4869a9fc1c0SDavid Howells npages); 4879a9fc1c0SDavid Howells if (*nr_pages > 0) 4889a9fc1c0SDavid Howells nfs_add_fscache_stats(inode, NFSIOS_FSCACHE_PAGES_READ_FAIL, 4899a9fc1c0SDavid Howells *nr_pages); 4909a9fc1c0SDavid Howells 4919a9fc1c0SDavid Howells switch (ret) { 4929a9fc1c0SDavid Howells case 0: /* read submitted to the cache for all pages */ 4939a9fc1c0SDavid Howells BUG_ON(!list_empty(pages)); 4949a9fc1c0SDavid Howells BUG_ON(*nr_pages != 0); 4959a9fc1c0SDavid Howells dfprintk(FSCACHE, 4969a9fc1c0SDavid Howells "NFS: nfs_getpages_from_fscache: submitted\n"); 4979a9fc1c0SDavid Howells 4989a9fc1c0SDavid Howells return ret; 4999a9fc1c0SDavid Howells 5009a9fc1c0SDavid Howells case -ENOBUFS: /* some pages aren't cached and can't be */ 5019a9fc1c0SDavid Howells case -ENODATA: /* some pages aren't cached */ 5029a9fc1c0SDavid Howells dfprintk(FSCACHE, 5039a9fc1c0SDavid Howells "NFS: nfs_getpages_from_fscache: no page: %d\n", ret); 5049a9fc1c0SDavid Howells return 1; 5059a9fc1c0SDavid Howells 5069a9fc1c0SDavid Howells default: 5079a9fc1c0SDavid Howells dfprintk(FSCACHE, 5089a9fc1c0SDavid Howells "NFS: nfs_getpages_from_fscache: ret %d\n", ret); 5099a9fc1c0SDavid Howells } 5109a9fc1c0SDavid Howells 5119a9fc1c0SDavid Howells return ret; 5129a9fc1c0SDavid Howells } 5137f8e05f6SDavid Howells 5147f8e05f6SDavid Howells /* 5157f8e05f6SDavid Howells * Store a newly fetched page in fscache 5167f8e05f6SDavid Howells * - PG_fscache must be set on the page 5177f8e05f6SDavid Howells */ 5187f8e05f6SDavid Howells void __nfs_readpage_to_fscache(struct inode *inode, struct page *page, int sync) 5197f8e05f6SDavid Howells { 5207f8e05f6SDavid Howells int ret; 5217f8e05f6SDavid Howells 5227f8e05f6SDavid Howells dfprintk(FSCACHE, 5237f8e05f6SDavid Howells "NFS: readpage_to_fscache(fsc:%p/p:%p(i:%lx f:%lx)/%d)\n", 5247f8e05f6SDavid Howells NFS_I(inode)->fscache, page, page->index, page->flags, sync); 5257f8e05f6SDavid Howells 5267f8e05f6SDavid Howells ret = fscache_write_page(NFS_I(inode)->fscache, page, GFP_KERNEL); 5277f8e05f6SDavid Howells dfprintk(FSCACHE, 5287f8e05f6SDavid Howells "NFS: readpage_to_fscache: p:%p(i:%lu f:%lx) ret %d\n", 5297f8e05f6SDavid Howells page, page->index, page->flags, ret); 5307f8e05f6SDavid Howells 5317f8e05f6SDavid Howells if (ret != 0) { 5327f8e05f6SDavid Howells fscache_uncache_page(NFS_I(inode)->fscache, page); 5337f8e05f6SDavid Howells nfs_add_fscache_stats(inode, 5347f8e05f6SDavid Howells NFSIOS_FSCACHE_PAGES_WRITTEN_FAIL, 1); 5357f8e05f6SDavid Howells nfs_add_fscache_stats(inode, NFSIOS_FSCACHE_PAGES_UNCACHED, 1); 5367f8e05f6SDavid Howells } else { 5377f8e05f6SDavid Howells nfs_add_fscache_stats(inode, 5387f8e05f6SDavid Howells NFSIOS_FSCACHE_PAGES_WRITTEN_OK, 1); 5397f8e05f6SDavid Howells } 5407f8e05f6SDavid Howells } 541