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