1 /* 2 * V9FS cache definitions. 3 * 4 * Copyright (C) 2009 by Abhishek Kulkarni <adkulkar@umail.iu.edu> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 2 8 * as published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to: 17 * Free Software Foundation 18 * 51 Franklin Street, Fifth Floor 19 * Boston, MA 02111-1301 USA 20 * 21 */ 22 23 #include <linux/jiffies.h> 24 #include <linux/file.h> 25 #include <linux/stat.h> 26 #include <linux/sched.h> 27 #include <linux/fs.h> 28 #include <net/9p/9p.h> 29 30 #include "v9fs.h" 31 #include "cache.h" 32 33 #define CACHETAG_LEN 11 34 35 struct kmem_cache *vcookie_cache; 36 37 struct fscache_netfs v9fs_cache_netfs = { 38 .name = "9p", 39 .version = 0, 40 }; 41 42 static void init_once(void *foo) 43 { 44 struct v9fs_cookie *vcookie = (struct v9fs_cookie *) foo; 45 vcookie->fscache = NULL; 46 vcookie->qid = NULL; 47 inode_init_once(&vcookie->inode); 48 } 49 50 /** 51 * v9fs_init_vcookiecache - initialize a cache for vcookies to maintain 52 * vcookie to inode mapping 53 * 54 * Returns 0 on success. 55 */ 56 57 static int v9fs_init_vcookiecache(void) 58 { 59 vcookie_cache = kmem_cache_create("vcookie_cache", 60 sizeof(struct v9fs_cookie), 61 0, (SLAB_RECLAIM_ACCOUNT| 62 SLAB_MEM_SPREAD), 63 init_once); 64 if (!vcookie_cache) 65 return -ENOMEM; 66 67 return 0; 68 } 69 70 /** 71 * v9fs_destroy_vcookiecache - destroy the cache of vcookies 72 * 73 */ 74 75 static void v9fs_destroy_vcookiecache(void) 76 { 77 kmem_cache_destroy(vcookie_cache); 78 } 79 80 int __v9fs_cache_register(void) 81 { 82 int ret; 83 ret = v9fs_init_vcookiecache(); 84 if (ret < 0) 85 return ret; 86 87 return fscache_register_netfs(&v9fs_cache_netfs); 88 } 89 90 void __v9fs_cache_unregister(void) 91 { 92 v9fs_destroy_vcookiecache(); 93 fscache_unregister_netfs(&v9fs_cache_netfs); 94 } 95 96 /** 97 * v9fs_random_cachetag - Generate a random tag to be associated 98 * with a new cache session. 99 * 100 * The value of jiffies is used for a fairly randomly cache tag. 101 */ 102 103 static 104 int v9fs_random_cachetag(struct v9fs_session_info *v9ses) 105 { 106 v9ses->cachetag = kmalloc(CACHETAG_LEN, GFP_KERNEL); 107 if (!v9ses->cachetag) 108 return -ENOMEM; 109 110 return scnprintf(v9ses->cachetag, CACHETAG_LEN, "%lu", jiffies); 111 } 112 113 static uint16_t v9fs_cache_session_get_key(const void *cookie_netfs_data, 114 void *buffer, uint16_t bufmax) 115 { 116 struct v9fs_session_info *v9ses; 117 uint16_t klen = 0; 118 119 v9ses = (struct v9fs_session_info *)cookie_netfs_data; 120 P9_DPRINTK(P9_DEBUG_FSC, "session %p buf %p size %u", v9ses, 121 buffer, bufmax); 122 123 if (v9ses->cachetag) 124 klen = strlen(v9ses->cachetag); 125 126 if (klen > bufmax) 127 return 0; 128 129 memcpy(buffer, v9ses->cachetag, klen); 130 P9_DPRINTK(P9_DEBUG_FSC, "cache session tag %s", v9ses->cachetag); 131 return klen; 132 } 133 134 const struct fscache_cookie_def v9fs_cache_session_index_def = { 135 .name = "9P.session", 136 .type = FSCACHE_COOKIE_TYPE_INDEX, 137 .get_key = v9fs_cache_session_get_key, 138 }; 139 140 void v9fs_cache_session_get_cookie(struct v9fs_session_info *v9ses) 141 { 142 /* If no cache session tag was specified, we generate a random one. */ 143 if (!v9ses->cachetag) 144 v9fs_random_cachetag(v9ses); 145 146 v9ses->fscache = fscache_acquire_cookie(v9fs_cache_netfs.primary_index, 147 &v9fs_cache_session_index_def, 148 v9ses); 149 P9_DPRINTK(P9_DEBUG_FSC, "session %p get cookie %p", v9ses, 150 v9ses->fscache); 151 } 152 153 void v9fs_cache_session_put_cookie(struct v9fs_session_info *v9ses) 154 { 155 P9_DPRINTK(P9_DEBUG_FSC, "session %p put cookie %p", v9ses, 156 v9ses->fscache); 157 fscache_relinquish_cookie(v9ses->fscache, 0); 158 v9ses->fscache = NULL; 159 } 160 161 162 static uint16_t v9fs_cache_inode_get_key(const void *cookie_netfs_data, 163 void *buffer, uint16_t bufmax) 164 { 165 const struct v9fs_cookie *vcookie = cookie_netfs_data; 166 memcpy(buffer, &vcookie->qid->path, sizeof(vcookie->qid->path)); 167 168 P9_DPRINTK(P9_DEBUG_FSC, "inode %p get key %llu", &vcookie->inode, 169 vcookie->qid->path); 170 return sizeof(vcookie->qid->path); 171 } 172 173 static void v9fs_cache_inode_get_attr(const void *cookie_netfs_data, 174 uint64_t *size) 175 { 176 const struct v9fs_cookie *vcookie = cookie_netfs_data; 177 *size = i_size_read(&vcookie->inode); 178 179 P9_DPRINTK(P9_DEBUG_FSC, "inode %p get attr %llu", &vcookie->inode, 180 *size); 181 } 182 183 static uint16_t v9fs_cache_inode_get_aux(const void *cookie_netfs_data, 184 void *buffer, uint16_t buflen) 185 { 186 const struct v9fs_cookie *vcookie = cookie_netfs_data; 187 memcpy(buffer, &vcookie->qid->version, sizeof(vcookie->qid->version)); 188 189 P9_DPRINTK(P9_DEBUG_FSC, "inode %p get aux %u", &vcookie->inode, 190 vcookie->qid->version); 191 return sizeof(vcookie->qid->version); 192 } 193 194 static enum 195 fscache_checkaux v9fs_cache_inode_check_aux(void *cookie_netfs_data, 196 const void *buffer, 197 uint16_t buflen) 198 { 199 const struct v9fs_cookie *vcookie = cookie_netfs_data; 200 201 if (buflen != sizeof(vcookie->qid->version)) 202 return FSCACHE_CHECKAUX_OBSOLETE; 203 204 if (memcmp(buffer, &vcookie->qid->version, 205 sizeof(vcookie->qid->version))) 206 return FSCACHE_CHECKAUX_OBSOLETE; 207 208 return FSCACHE_CHECKAUX_OKAY; 209 } 210 211 static void v9fs_cache_inode_now_uncached(void *cookie_netfs_data) 212 { 213 struct v9fs_cookie *vcookie = cookie_netfs_data; 214 struct pagevec pvec; 215 pgoff_t first; 216 int loop, nr_pages; 217 218 pagevec_init(&pvec, 0); 219 first = 0; 220 221 for (;;) { 222 nr_pages = pagevec_lookup(&pvec, vcookie->inode.i_mapping, 223 first, 224 PAGEVEC_SIZE - pagevec_count(&pvec)); 225 if (!nr_pages) 226 break; 227 228 for (loop = 0; loop < nr_pages; loop++) 229 ClearPageFsCache(pvec.pages[loop]); 230 231 first = pvec.pages[nr_pages - 1]->index + 1; 232 233 pvec.nr = nr_pages; 234 pagevec_release(&pvec); 235 cond_resched(); 236 } 237 } 238 239 const struct fscache_cookie_def v9fs_cache_inode_index_def = { 240 .name = "9p.inode", 241 .type = FSCACHE_COOKIE_TYPE_DATAFILE, 242 .get_key = v9fs_cache_inode_get_key, 243 .get_attr = v9fs_cache_inode_get_attr, 244 .get_aux = v9fs_cache_inode_get_aux, 245 .check_aux = v9fs_cache_inode_check_aux, 246 .now_uncached = v9fs_cache_inode_now_uncached, 247 }; 248 249 void v9fs_cache_inode_get_cookie(struct inode *inode) 250 { 251 struct v9fs_cookie *vcookie; 252 struct v9fs_session_info *v9ses; 253 254 if (!S_ISREG(inode->i_mode)) 255 return; 256 257 vcookie = v9fs_inode2cookie(inode); 258 if (vcookie->fscache) 259 return; 260 261 v9ses = v9fs_inode2v9ses(inode); 262 vcookie->fscache = fscache_acquire_cookie(v9ses->fscache, 263 &v9fs_cache_inode_index_def, 264 vcookie); 265 266 P9_DPRINTK(P9_DEBUG_FSC, "inode %p get cookie %p", inode, 267 vcookie->fscache); 268 } 269 270 void v9fs_cache_inode_put_cookie(struct inode *inode) 271 { 272 struct v9fs_cookie *vcookie = v9fs_inode2cookie(inode); 273 274 if (!vcookie->fscache) 275 return; 276 P9_DPRINTK(P9_DEBUG_FSC, "inode %p put cookie %p", inode, 277 vcookie->fscache); 278 279 fscache_relinquish_cookie(vcookie->fscache, 0); 280 vcookie->fscache = NULL; 281 } 282 283 void v9fs_cache_inode_flush_cookie(struct inode *inode) 284 { 285 struct v9fs_cookie *vcookie = v9fs_inode2cookie(inode); 286 287 if (!vcookie->fscache) 288 return; 289 P9_DPRINTK(P9_DEBUG_FSC, "inode %p flush cookie %p", inode, 290 vcookie->fscache); 291 292 fscache_relinquish_cookie(vcookie->fscache, 1); 293 vcookie->fscache = NULL; 294 } 295 296 void v9fs_cache_inode_set_cookie(struct inode *inode, struct file *filp) 297 { 298 struct v9fs_cookie *vcookie = v9fs_inode2cookie(inode); 299 struct p9_fid *fid; 300 301 if (!vcookie->fscache) 302 return; 303 304 spin_lock(&vcookie->lock); 305 fid = filp->private_data; 306 if ((filp->f_flags & O_ACCMODE) != O_RDONLY) 307 v9fs_cache_inode_flush_cookie(inode); 308 else 309 v9fs_cache_inode_get_cookie(inode); 310 311 spin_unlock(&vcookie->lock); 312 } 313 314 void v9fs_cache_inode_reset_cookie(struct inode *inode) 315 { 316 struct v9fs_cookie *vcookie = v9fs_inode2cookie(inode); 317 struct v9fs_session_info *v9ses; 318 struct fscache_cookie *old; 319 320 if (!vcookie->fscache) 321 return; 322 323 old = vcookie->fscache; 324 325 spin_lock(&vcookie->lock); 326 fscache_relinquish_cookie(vcookie->fscache, 1); 327 328 v9ses = v9fs_inode2v9ses(inode); 329 vcookie->fscache = fscache_acquire_cookie(v9ses->fscache, 330 &v9fs_cache_inode_index_def, 331 vcookie); 332 333 P9_DPRINTK(P9_DEBUG_FSC, "inode %p revalidating cookie old %p new %p", 334 inode, old, vcookie->fscache); 335 336 spin_unlock(&vcookie->lock); 337 } 338 339 int __v9fs_fscache_release_page(struct page *page, gfp_t gfp) 340 { 341 struct inode *inode = page->mapping->host; 342 struct v9fs_cookie *vcookie = v9fs_inode2cookie(inode); 343 344 BUG_ON(!vcookie->fscache); 345 346 return fscache_maybe_release_page(vcookie->fscache, page, gfp); 347 } 348 349 void __v9fs_fscache_invalidate_page(struct page *page) 350 { 351 struct inode *inode = page->mapping->host; 352 struct v9fs_cookie *vcookie = v9fs_inode2cookie(inode); 353 354 BUG_ON(!vcookie->fscache); 355 356 if (PageFsCache(page)) { 357 fscache_wait_on_page_write(vcookie->fscache, page); 358 BUG_ON(!PageLocked(page)); 359 fscache_uncache_page(vcookie->fscache, page); 360 } 361 } 362 363 static void v9fs_vfs_readpage_complete(struct page *page, void *data, 364 int error) 365 { 366 if (!error) 367 SetPageUptodate(page); 368 369 unlock_page(page); 370 } 371 372 /** 373 * __v9fs_readpage_from_fscache - read a page from cache 374 * 375 * Returns 0 if the pages are in cache and a BIO is submitted, 376 * 1 if the pages are not in cache and -error otherwise. 377 */ 378 379 int __v9fs_readpage_from_fscache(struct inode *inode, struct page *page) 380 { 381 int ret; 382 const struct v9fs_cookie *vcookie = v9fs_inode2cookie(inode); 383 384 P9_DPRINTK(P9_DEBUG_FSC, "inode %p page %p", inode, page); 385 if (!vcookie->fscache) 386 return -ENOBUFS; 387 388 ret = fscache_read_or_alloc_page(vcookie->fscache, 389 page, 390 v9fs_vfs_readpage_complete, 391 NULL, 392 GFP_KERNEL); 393 switch (ret) { 394 case -ENOBUFS: 395 case -ENODATA: 396 P9_DPRINTK(P9_DEBUG_FSC, "page/inode not in cache %d", ret); 397 return 1; 398 case 0: 399 P9_DPRINTK(P9_DEBUG_FSC, "BIO submitted"); 400 return ret; 401 default: 402 P9_DPRINTK(P9_DEBUG_FSC, "ret %d", ret); 403 return ret; 404 } 405 } 406 407 /** 408 * __v9fs_readpages_from_fscache - read multiple pages from cache 409 * 410 * Returns 0 if the pages are in cache and a BIO is submitted, 411 * 1 if the pages are not in cache and -error otherwise. 412 */ 413 414 int __v9fs_readpages_from_fscache(struct inode *inode, 415 struct address_space *mapping, 416 struct list_head *pages, 417 unsigned *nr_pages) 418 { 419 int ret; 420 const struct v9fs_cookie *vcookie = v9fs_inode2cookie(inode); 421 422 P9_DPRINTK(P9_DEBUG_FSC, "inode %p pages %u", inode, *nr_pages); 423 if (!vcookie->fscache) 424 return -ENOBUFS; 425 426 ret = fscache_read_or_alloc_pages(vcookie->fscache, 427 mapping, pages, nr_pages, 428 v9fs_vfs_readpage_complete, 429 NULL, 430 mapping_gfp_mask(mapping)); 431 switch (ret) { 432 case -ENOBUFS: 433 case -ENODATA: 434 P9_DPRINTK(P9_DEBUG_FSC, "pages/inodes not in cache %d", ret); 435 return 1; 436 case 0: 437 BUG_ON(!list_empty(pages)); 438 BUG_ON(*nr_pages != 0); 439 P9_DPRINTK(P9_DEBUG_FSC, "BIO submitted"); 440 return ret; 441 default: 442 P9_DPRINTK(P9_DEBUG_FSC, "ret %d", ret); 443 return ret; 444 } 445 } 446 447 /** 448 * __v9fs_readpage_to_fscache - write a page to the cache 449 * 450 */ 451 452 void __v9fs_readpage_to_fscache(struct inode *inode, struct page *page) 453 { 454 int ret; 455 const struct v9fs_cookie *vcookie = v9fs_inode2cookie(inode); 456 457 P9_DPRINTK(P9_DEBUG_FSC, "inode %p page %p", inode, page); 458 ret = fscache_write_page(vcookie->fscache, page, GFP_KERNEL); 459 P9_DPRINTK(P9_DEBUG_FSC, "ret = %d", ret); 460 if (ret != 0) 461 v9fs_uncache_page(inode, page); 462 } 463