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
36struct kmem_cache *vcookie_cache;
37
38struct fscache_netfs v9fs_cache_netfs = {
39	.name 		= "9p",
40	.version 	= 0,
41};
42
43static 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
58static 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
76static void v9fs_destroy_vcookiecache(void)
77{
78	kmem_cache_destroy(vcookie_cache);
79}
80
81int __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
91void __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
104static
105int 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
114static 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
135const 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
141void 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
154void 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
163static 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
174static 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
184static 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
195static enum
196fscache_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
212static 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
240const 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
250void 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
271void 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
284void 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
297void 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
315void 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
340int __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
350void __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
364static 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
380int __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
415int __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
453void __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