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 key->nfsversion = clp->rpc_ops->version; 78 key->family = clp->cl_addr.ss_family; 79 80 memset(key, 0, len); 81 82 switch (clp->cl_addr.ss_family) { 83 case AF_INET: 84 key->port = sin->sin_port; 85 key->addr[0].ipv4_addr = sin->sin_addr; 86 len += sizeof(key->addr[0].ipv4_addr); 87 break; 88 89 case AF_INET6: 90 key->port = sin6->sin6_port; 91 key->addr[0].ipv6_addr = sin6->sin6_addr; 92 len += sizeof(key->addr[0].ipv6_addr); 93 break; 94 95 default: 96 printk(KERN_WARNING "NFS: Unknown network family '%d'\n", 97 clp->cl_addr.ss_family); 98 len = 0; 99 break; 100 } 101 102 return len; 103} 104 105/* 106 * Define the server object for FS-Cache. This is used to describe a server 107 * object to fscache_acquire_cookie(). It is keyed by the NFS protocol and 108 * server address parameters. 109 */ 110const struct fscache_cookie_def nfs_fscache_server_index_def = { 111 .name = "NFS.server", 112 .type = FSCACHE_COOKIE_TYPE_INDEX, 113 .get_key = nfs_server_get_key, 114}; 115 116/* 117 * Generate a key to describe a superblock key in the main NFS index 118 */ 119static uint16_t nfs_super_get_key(const void *cookie_netfs_data, 120 void *buffer, uint16_t bufmax) 121{ 122 const struct nfs_fscache_key *key; 123 const struct nfs_server *nfss = cookie_netfs_data; 124 uint16_t len; 125 126 key = nfss->fscache_key; 127 len = sizeof(key->key) + key->key.uniq_len; 128 if (len > bufmax) { 129 len = 0; 130 } else { 131 memcpy(buffer, &key->key, sizeof(key->key)); 132 memcpy(buffer + sizeof(key->key), 133 key->key.uniquifier, key->key.uniq_len); 134 } 135 136 return len; 137} 138 139/* 140 * Define the superblock object for FS-Cache. This is used to describe a 141 * superblock object to fscache_acquire_cookie(). It is keyed by all the NFS 142 * parameters that might cause a separate superblock. 143 */ 144const struct fscache_cookie_def nfs_fscache_super_index_def = { 145 .name = "NFS.super", 146 .type = FSCACHE_COOKIE_TYPE_INDEX, 147 .get_key = nfs_super_get_key, 148}; 149 150/* 151 * Definition of the auxiliary data attached to NFS inode storage objects 152 * within the cache. 153 * 154 * The contents of this struct are recorded in the on-disk local cache in the 155 * auxiliary data attached to the data storage object backing an inode. This 156 * permits coherency to be managed when a new inode binds to an already extant 157 * cache object. 158 */ 159struct nfs_fscache_inode_auxdata { 160 struct timespec mtime; 161 struct timespec ctime; 162 loff_t size; 163 u64 change_attr; 164}; 165 166/* 167 * Generate a key to describe an NFS inode in an NFS server's index 168 */ 169static uint16_t nfs_fscache_inode_get_key(const void *cookie_netfs_data, 170 void *buffer, uint16_t bufmax) 171{ 172 const struct nfs_inode *nfsi = cookie_netfs_data; 173 uint16_t nsize; 174 175 /* use the inode's NFS filehandle as the key */ 176 nsize = nfsi->fh.size; 177 memcpy(buffer, nfsi->fh.data, nsize); 178 return nsize; 179} 180 181/* 182 * Get certain file attributes from the netfs data 183 * - This function can be absent for an index 184 * - Not permitted to return an error 185 * - The netfs data from the cookie being used as the source is presented 186 */ 187static void nfs_fscache_inode_get_attr(const void *cookie_netfs_data, 188 uint64_t *size) 189{ 190 const struct nfs_inode *nfsi = cookie_netfs_data; 191 192 *size = nfsi->vfs_inode.i_size; 193} 194 195/* 196 * Get the auxiliary data from netfs data 197 * - This function can be absent if the index carries no state data 198 * - Should store the auxiliary data in the buffer 199 * - Should return the amount of amount stored 200 * - Not permitted to return an error 201 * - The netfs data from the cookie being used as the source is presented 202 */ 203static uint16_t nfs_fscache_inode_get_aux(const void *cookie_netfs_data, 204 void *buffer, uint16_t bufmax) 205{ 206 struct nfs_fscache_inode_auxdata auxdata; 207 const struct nfs_inode *nfsi = cookie_netfs_data; 208 209 memset(&auxdata, 0, sizeof(auxdata)); 210 auxdata.size = nfsi->vfs_inode.i_size; 211 auxdata.mtime = nfsi->vfs_inode.i_mtime; 212 auxdata.ctime = nfsi->vfs_inode.i_ctime; 213 214 if (NFS_SERVER(&nfsi->vfs_inode)->nfs_client->rpc_ops->version == 4) 215 auxdata.change_attr = nfsi->change_attr; 216 217 if (bufmax > sizeof(auxdata)) 218 bufmax = sizeof(auxdata); 219 220 memcpy(buffer, &auxdata, bufmax); 221 return bufmax; 222} 223 224/* 225 * Consult the netfs about the state of an object 226 * - This function can be absent if the index carries no state data 227 * - The netfs data from the cookie being used as the target is 228 * presented, as is the auxiliary data 229 */ 230static 231enum fscache_checkaux nfs_fscache_inode_check_aux(void *cookie_netfs_data, 232 const void *data, 233 uint16_t datalen) 234{ 235 struct nfs_fscache_inode_auxdata auxdata; 236 struct nfs_inode *nfsi = cookie_netfs_data; 237 238 if (datalen != sizeof(auxdata)) 239 return FSCACHE_CHECKAUX_OBSOLETE; 240 241 memset(&auxdata, 0, sizeof(auxdata)); 242 auxdata.size = nfsi->vfs_inode.i_size; 243 auxdata.mtime = nfsi->vfs_inode.i_mtime; 244 auxdata.ctime = nfsi->vfs_inode.i_ctime; 245 246 if (NFS_SERVER(&nfsi->vfs_inode)->nfs_client->rpc_ops->version == 4) 247 auxdata.change_attr = nfsi->change_attr; 248 249 if (memcmp(data, &auxdata, datalen) != 0) 250 return FSCACHE_CHECKAUX_OBSOLETE; 251 252 return FSCACHE_CHECKAUX_OKAY; 253} 254 255/* 256 * Indication from FS-Cache that the cookie is no longer cached 257 * - This function is called when the backing store currently caching a cookie 258 * is removed 259 * - The netfs should use this to clean up any markers indicating cached pages 260 * - This is mandatory for any object that may have data 261 */ 262static void nfs_fscache_inode_now_uncached(void *cookie_netfs_data) 263{ 264 struct nfs_inode *nfsi = cookie_netfs_data; 265 struct pagevec pvec; 266 pgoff_t first; 267 int loop, nr_pages; 268 269 pagevec_init(&pvec, 0); 270 first = 0; 271 272 dprintk("NFS: nfs_inode_now_uncached: nfs_inode 0x%p\n", nfsi); 273 274 for (;;) { 275 /* grab a bunch of pages to unmark */ 276 nr_pages = pagevec_lookup(&pvec, 277 nfsi->vfs_inode.i_mapping, 278 first, 279 PAGEVEC_SIZE - pagevec_count(&pvec)); 280 if (!nr_pages) 281 break; 282 283 for (loop = 0; loop < nr_pages; loop++) 284 ClearPageFsCache(pvec.pages[loop]); 285 286 first = pvec.pages[nr_pages - 1]->index + 1; 287 288 pvec.nr = nr_pages; 289 pagevec_release(&pvec); 290 cond_resched(); 291 } 292} 293 294/* 295 * Get an extra reference on a read context. 296 * - This function can be absent if the completion function doesn't require a 297 * context. 298 * - The read context is passed back to NFS in the event that a data read on the 299 * cache fails with EIO - in which case the server must be contacted to 300 * retrieve the data, which requires the read context for security. 301 */ 302static void nfs_fh_get_context(void *cookie_netfs_data, void *context) 303{ 304 get_nfs_open_context(context); 305} 306 307/* 308 * Release an extra reference on a read context. 309 * - This function can be absent if the completion function doesn't require a 310 * context. 311 */ 312static void nfs_fh_put_context(void *cookie_netfs_data, void *context) 313{ 314 if (context) 315 put_nfs_open_context(context); 316} 317 318/* 319 * Define the inode object for FS-Cache. This is used to describe an inode 320 * object to fscache_acquire_cookie(). It is keyed by the NFS file handle for 321 * an inode. 322 * 323 * Coherency is managed by comparing the copies of i_size, i_mtime and i_ctime 324 * held in the cache auxiliary data for the data storage object with those in 325 * the inode struct in memory. 326 */ 327const struct fscache_cookie_def nfs_fscache_inode_object_def = { 328 .name = "NFS.fh", 329 .type = FSCACHE_COOKIE_TYPE_DATAFILE, 330 .get_key = nfs_fscache_inode_get_key, 331 .get_attr = nfs_fscache_inode_get_attr, 332 .get_aux = nfs_fscache_inode_get_aux, 333 .check_aux = nfs_fscache_inode_check_aux, 334 .now_uncached = nfs_fscache_inode_now_uncached, 335 .get_context = nfs_fh_get_context, 336 .put_context = nfs_fh_put_context, 337}; 338