9p: Add fscache support to 9p
[linux-2.6.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/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         if (PageFsCache(page)) {
347                 if (fscache_check_page_write(vcookie->fscache, page)) {
348                         if (!(gfp & __GFP_WAIT))
349                                 return 0;
350                         fscache_wait_on_page_write(vcookie->fscache, page);
351                 }
352
353                 fscache_uncache_page(vcookie->fscache, page);
354                 ClearPageFsCache(page);
355         }
356
357         return 1;
358 }
359
360 void __v9fs_fscache_invalidate_page(struct page *page)
361 {
362         struct inode *inode = page->mapping->host;
363         struct v9fs_cookie *vcookie = v9fs_inode2cookie(inode);
364
365         BUG_ON(!vcookie->fscache);
366
367         if (PageFsCache(page)) {
368                 fscache_wait_on_page_write(vcookie->fscache, page);
369                 BUG_ON(!PageLocked(page));
370                 fscache_uncache_page(vcookie->fscache, page);
371                 ClearPageFsCache(page);
372         }
373 }
374
375 static void v9fs_vfs_readpage_complete(struct page *page, void *data,
376                                        int error)
377 {
378         if (!error)
379                 SetPageUptodate(page);
380
381         unlock_page(page);
382 }
383
384 /**
385  * __v9fs_readpage_from_fscache - read a page from cache
386  *
387  * Returns 0 if the pages are in cache and a BIO is submitted,
388  * 1 if the pages are not in cache and -error otherwise.
389  */
390
391 int __v9fs_readpage_from_fscache(struct inode *inode, struct page *page)
392 {
393         int ret;
394         const struct v9fs_cookie *vcookie = v9fs_inode2cookie(inode);
395
396         P9_DPRINTK(P9_DEBUG_FSC, "inode %p page %p", inode, page);
397         if (!vcookie->fscache)
398                 return -ENOBUFS;
399
400         ret = fscache_read_or_alloc_page(vcookie->fscache,
401                                          page,
402                                          v9fs_vfs_readpage_complete,
403                                          NULL,
404                                          GFP_KERNEL);
405         switch (ret) {
406         case -ENOBUFS:
407         case -ENODATA:
408                 P9_DPRINTK(P9_DEBUG_FSC, "page/inode not in cache %d", ret);
409                 return 1;
410         case 0:
411                 P9_DPRINTK(P9_DEBUG_FSC, "BIO submitted");
412                 return ret;
413         default:
414                 P9_DPRINTK(P9_DEBUG_FSC, "ret %d", ret);
415                 return ret;
416         }
417 }
418
419 /**
420  * __v9fs_readpages_from_fscache - read multiple pages from cache
421  *
422  * Returns 0 if the pages are in cache and a BIO is submitted,
423  * 1 if the pages are not in cache and -error otherwise.
424  */
425
426 int __v9fs_readpages_from_fscache(struct inode *inode,
427                                   struct address_space *mapping,
428                                   struct list_head *pages,
429                                   unsigned *nr_pages)
430 {
431         int ret;
432         const struct v9fs_cookie *vcookie = v9fs_inode2cookie(inode);
433
434         P9_DPRINTK(P9_DEBUG_FSC, "inode %p pages %u", inode, *nr_pages);
435         if (!vcookie->fscache)
436                 return -ENOBUFS;
437
438         ret = fscache_read_or_alloc_pages(vcookie->fscache,
439                                           mapping, pages, nr_pages,
440                                           v9fs_vfs_readpage_complete,
441                                           NULL,
442                                           mapping_gfp_mask(mapping));
443         switch (ret) {
444         case -ENOBUFS:
445         case -ENODATA:
446                 P9_DPRINTK(P9_DEBUG_FSC, "pages/inodes not in cache %d", ret);
447                 return 1;
448         case 0:
449                 BUG_ON(!list_empty(pages));
450                 BUG_ON(*nr_pages != 0);
451                 P9_DPRINTK(P9_DEBUG_FSC, "BIO submitted");
452                 return ret;
453         default:
454                 P9_DPRINTK(P9_DEBUG_FSC, "ret %d", ret);
455                 return ret;
456         }
457 }
458
459 /**
460  * __v9fs_readpage_to_fscache - write a page to the cache
461  *
462  */
463
464 void __v9fs_readpage_to_fscache(struct inode *inode, struct page *page)
465 {
466         int ret;
467         const struct v9fs_cookie *vcookie = v9fs_inode2cookie(inode);
468
469         P9_DPRINTK(P9_DEBUG_FSC, "inode %p page %p", inode, page);
470         ret = fscache_write_page(vcookie->fscache, page, GFP_KERNEL);
471         P9_DPRINTK(P9_DEBUG_FSC, "ret =  %d", ret);
472         if (ret != 0)
473                 v9fs_uncache_page(inode, page);
474 }