1/* NFS FS-Cache index structure definition 2 * 3 * Copyright (C) 2008 Red Hat, Inc. All Rights Reserved. 4 * Written by David Howells (dhowells@redhat.com) 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public Licence 8 * as published by the Free Software Foundation; either version 9 * 2 of the Licence, or (at your option) any later version. 10 */ 11 12#include <linux/init.h> 13#include <linux/kernel.h> 14#include <linux/sched.h> 15#include <linux/mm.h> 16#include <linux/nfs_fs.h> 17#include <linux/nfs_fs_sb.h> 18#include <linux/in6.h> 19 20#include "internal.h" 21#include "fscache.h" 22 23#define NFSDBG_FACILITY NFSDBG_FSCACHE 24 25/* 26 * Define the NFS filesystem for FS-Cache. Upon registration FS-Cache sticks 27 * the cookie for the top-level index object for NFS into here. The top-level 28 * index can than have other cache objects inserted into it. 29 */ 30struct fscache_netfs nfs_fscache_netfs = { 31 .name = "nfs", 32 .version = 0, 33}; 34 35/* 36 * Register NFS for caching 37 */ 38int nfs_fscache_register(void) 39{ 40 return fscache_register_netfs(&nfs_fscache_netfs); 41} 42 43/* 44 * Unregister NFS for caching 45 */ 46void nfs_fscache_unregister(void) 47{ 48 fscache_unregister_netfs(&nfs_fscache_netfs); 49} 50 51/* 52 * Layout of the key for an NFS server cache object. 53 */ 54struct nfs_server_key { 55 uint16_t nfsversion; /* NFS protocol version */ 56 uint16_t family; /* address family */ 57 uint16_t port; /* IP port */ 58 union { 59 struct in_addr ipv4_addr; /* IPv4 address */ 60 struct in6_addr ipv6_addr; /* IPv6 address */ 61 } addr[0]; 62}; 63 64/* 65 * Generate a key to describe a server in the main NFS index 66 * - We return the length of the key, or 0 if we can't generate one 67 */ 68static uint16_t nfs_server_get_key(const void *cookie_netfs_data, 69 void *buffer, uint16_t bufmax) 70{ 71 const struct nfs_client *clp = cookie_netfs_data; 72 const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &clp->cl_addr; 73 const struct sockaddr_in *sin = (struct sockaddr_in *) &clp->cl_addr; 74 struct nfs_server_key *key = buffer; 75 uint16_t len = sizeof(struct nfs_server_key); 76 77 memset(key, 0, len); 78 key->nfsversion = clp->rpc_ops->version; 79 key->family = clp->cl_addr.ss_family; 80 81 switch (clp->cl_addr.ss_family) { 82 case AF_INET: 83 key->port = sin->sin_port; 84 key->addr[0].ipv4_addr = sin->sin_addr; 85 len += sizeof(key->addr[0].ipv4_addr); 86 break; 87 88 case AF_INET6: 89 key->port = sin6->sin6_port; 90 key->addr[0].ipv6_addr = sin6->sin6_addr; 91 len += sizeof(key->addr[0].ipv6_addr); 92 break; 93 94 default: 95 printk(KERN_WARNING "NFS: Unknown network family '%d'\n", 96 clp->cl_addr.ss_family); 97 len = 0; 98 break; 99 } 100 101 return len; 102} 103 104/* 105 * Define the server object for FS-Cache. This is used to describe a server 106 * object to fscache_acquire_cookie(). It is keyed by the NFS protocol and 107 * server address parameters. 108 */ 109const struct fscache_cookie_def nfs_fscache_server_index_def = { 110 .name = "NFS.server", 111 .type = FSCACHE_COOKIE_TYPE_INDEX, 112 .get_key = nfs_server_get_key, 113}; 114 115/* 116 * Generate a key to describe a superblock key in the main NFS index 117 */ 118static uint16_t nfs_super_get_key(const void *cookie_netfs_data, 119 void *buffer, uint16_t bufmax) 120{ 121 const struct nfs_fscache_key *key; 122 const struct nfs_server *nfss = cookie_netfs_data; 123 uint16_t len; 124 125 key = nfss->fscache_key; 126 len = sizeof(key->key) + key->key.uniq_len; 127 if (len > bufmax) { 128 len = 0; 129 } else { 130 memcpy(buffer, &key->key, sizeof(key->key)); 131 memcpy(buffer + sizeof(key->key), 132 key->key.uniquifier, key->key.uniq_len); 133 } 134 135 return len; 136} 137 138/* 139 * Define the superblock object for FS-Cache. This is used to describe a 140 * superblock object to fscache_acquire_cookie(). It is keyed by all the NFS 141 * parameters that might cause a separate superblock. 142 */ 143const struct fscache_cookie_def nfs_fscache_super_index_def = { 144 .name = "NFS.super", 145 .type = FSCACHE_COOKIE_TYPE_INDEX, 146 .get_key = nfs_super_get_key, 147}; 148 149/* 150 * Definition of the auxiliary data attached to NFS inode storage objects 151 * within the cache. 152 * 153 * The contents of this struct are recorded in the on-disk local cache in the 154 * auxiliary data attached to the data storage object backing an inode. This 155 * permits coherency to be managed when a new inode binds to an already extant 156 * cache object. 157 */ 158struct nfs_fscache_inode_auxdata { 159 struct timespec mtime; 160 struct timespec ctime; 161 loff_t size; 162 u64 change_attr; 163}; 164 165/* 166 * Generate a key to describe an NFS inode in an NFS server's index 167 */ 168static uint16_t nfs_fscache_inode_get_key(const void *cookie_netfs_data, 169 void *buffer, uint16_t bufmax) 170{ 171 const struct nfs_inode *nfsi = cookie_netfs_data; 172 uint16_t nsize; 173 174 /* use the inode's NFS filehandle as the key */ 175 nsize = nfsi->fh.size; 176 memcpy(buffer, nfsi->fh.data, nsize); 177 return nsize; 178} 179 180/* 181 * Get certain file attributes from the netfs data 182 * - This function can be absent for an index 183 * - Not permitted to return an error 184 * - The netfs data from the cookie being used as the source is presented 185 */ 186static void nfs_fscache_inode_get_attr(const void *cookie_netfs_data, 187 uint64_t *size) 188{ 189 const struct nfs_inode *nfsi = cookie_netfs_data; 190 191 *size = nfsi->vfs_inode.i_size; 192} 193 194/* 195 * Get the auxiliary data from netfs data 196 * - This function can be absent if the index carries no state data 197 * - Should store the auxiliary data in the buffer 198 * - Should return the amount of amount stored 199 * - Not permitted to return an error 200 * - The netfs data from the cookie being used as the source is presented 201 */ 202static uint16_t nfs_fscache_inode_get_aux(const void *cookie_netfs_data, 203 void *buffer, uint16_t bufmax) 204{ 205 struct nfs_fscache_inode_auxdata auxdata; 206 const struct nfs_inode *nfsi = cookie_netfs_data; 207 208 memset(&auxdata, 0, sizeof(auxdata)); 209 auxdata.size = nfsi->vfs_inode.i_size; 210 auxdata.mtime = nfsi->vfs_inode.i_mtime; 211 auxdata.ctime = nfsi->vfs_inode.i_ctime; 212 213 if (NFS_SERVER(&nfsi->vfs_inode)->nfs_client->rpc_ops->version == 4) 214 auxdata.change_attr = nfsi->vfs_inode.i_version; 215 216 if (bufmax > sizeof(auxdata)) 217 bufmax = sizeof(auxdata); 218 219 memcpy(buffer, &auxdata, bufmax); 220 return bufmax; 221} 222 223/* 224 * Consult the netfs about the state of an object 225 * - This function can be absent if the index carries no state data 226 * - The netfs data from the cookie being used as the target is 227 * presented, as is the auxiliary data 228 */ 229static 230enum fscache_checkaux nfs_fscache_inode_check_aux(void *cookie_netfs_data, 231 const void *data, 232 uint16_t datalen) 233{ 234 struct nfs_fscache_inode_auxdata auxdata; 235 struct nfs_inode *nfsi = cookie_netfs_data; 236 237 if (datalen != sizeof(auxdata)) 238 return FSCACHE_CHECKAUX_OBSOLETE; 239 240 memset(&auxdata, 0, sizeof(auxdata)); 241 auxdata.size = nfsi->vfs_inode.i_size; 242 auxdata.mtime = nfsi->vfs_inode.i_mtime; 243 auxdata.ctime = nfsi->vfs_inode.i_ctime; 244 245 if (NFS_SERVER(&nfsi->vfs_inode)->nfs_client->rpc_ops->version == 4) 246 auxdata.change_attr = nfsi->vfs_inode.i_version; 247 248 if (memcmp(data, &auxdata, datalen) != 0) 249 return FSCACHE_CHECKAUX_OBSOLETE; 250 251 return FSCACHE_CHECKAUX_OKAY; 252} 253 254/* 255 * Indication from FS-Cache that the cookie is no longer cached 256 * - This function is called when the backing store currently caching a cookie 257 * is removed 258 * - The netfs should use this to clean up any markers indicating cached pages 259 * - This is mandatory for any object that may have data 260 */ 261static void nfs_fscache_inode_now_uncached(void *cookie_netfs_data) 262{ 263 struct nfs_inode *nfsi = cookie_netfs_data; 264 struct pagevec pvec; 265 pgoff_t first; 266 int loop, nr_pages; 267 268 pagevec_init(&pvec, 0); 269 first = 0; 270 271 dprintk("NFS: nfs_inode_now_uncached: nfs_inode 0x%p\n", nfsi); 272 273 for (;;) { 274 /* grab a bunch of pages to unmark */ 275 nr_pages = pagevec_lookup(&pvec, 276 nfsi->vfs_inode.i_mapping, 277 first, 278 PAGEVEC_SIZE - pagevec_count(&pvec)); 279 if (!nr_pages) 280 break; 281 282 for (loop = 0; loop < nr_pages; loop++) 283 ClearPageFsCache(pvec.pages[loop]); 284 285 first = pvec.pages[nr_pages - 1]->index + 1; 286 287 pvec.nr = nr_pages; 288 pagevec_release(&pvec); 289 cond_resched(); 290 } 291} 292 293/* 294 * Get an extra reference on a read context. 295 * - This function can be absent if the completion function doesn't require a 296 * context. 297 * - The read context is passed back to NFS in the event that a data read on the 298 * cache fails with EIO - in which case the server must be contacted to 299 * retrieve the data, which requires the read context for security. 300 */ 301static void nfs_fh_get_context(void *cookie_netfs_data, void *context) 302{ 303 get_nfs_open_context(context); 304} 305 306/* 307 * Release an extra reference on a read context. 308 * - This function can be absent if the completion function doesn't require a 309 * context. 310 */ 311static void nfs_fh_put_context(void *cookie_netfs_data, void *context) 312{ 313 if (context) 314 put_nfs_open_context(context); 315} 316 317/* 318 * Define the inode object for FS-Cache. This is used to describe an inode 319 * object to fscache_acquire_cookie(). It is keyed by the NFS file handle for 320 * an inode. 321 * 322 * Coherency is managed by comparing the copies of i_size, i_mtime and i_ctime 323 * held in the cache auxiliary data for the data storage object with those in 324 * the inode struct in memory. 325 */ 326const struct fscache_cookie_def nfs_fscache_inode_object_def = { 327 .name = "NFS.fh", 328 .type = FSCACHE_COOKIE_TYPE_DATAFILE, 329 .get_key = nfs_fscache_inode_get_key, 330 .get_attr = nfs_fscache_inode_get_attr, 331 .get_aux = nfs_fscache_inode_get_aux, 332 .check_aux = nfs_fscache_inode_check_aux, 333 .now_uncached = nfs_fscache_inode_now_uncached, 334 .get_context = nfs_fh_get_context, 335 .put_context = nfs_fh_put_context, 336}; 337