Commit 60e78d2c authored by Abhishek Kulkarni's avatar Abhishek Kulkarni Committed by Eric Van Hensbergen

9p: Add fscache support to 9p

This patch adds a persistent, read-only caching facility for
9p clients using the FS-Cache caching backend.

When the fscache facility is enabled, each inode is associated
with a corresponding vcookie which is an index into the FS-Cache
indexing tree. The FS-Cache indexing tree is indexed at 3 levels:
- session object associated with each mount.
- inode/vcookie
- actual data (pages)

A cache tag is chosen randomly for each session. These tags can
be read off /sys/fs/9p/caches and can be passed as a mount-time
parameter to re-attach to the specified caching session.
Signed-off-by: default avatarAbhishek Kulkarni <adkulkar@umail.iu.edu>
Signed-off-by: default avatarEric Van Hensbergen <ericvh@gmail.com>
parent 637d020a
...@@ -8,3 +8,12 @@ config 9P_FS ...@@ -8,3 +8,12 @@ config 9P_FS
See <http://v9fs.sf.net> for more information. See <http://v9fs.sf.net> for more information.
If unsure, say N. If unsure, say N.
config 9P_FSCACHE
bool "Enable 9P client caching support (EXPERIMENTAL)"
depends on EXPERIMENTAL
depends on 9P_FS=m && FSCACHE || 9P_FS=y && FSCACHE=y
help
Choose Y here to enable persistent, read-only local
caching support for 9p clients using FS-Cache
...@@ -8,5 +8,6 @@ obj-$(CONFIG_9P_FS) := 9p.o ...@@ -8,5 +8,6 @@ obj-$(CONFIG_9P_FS) := 9p.o
vfs_dir.o \ vfs_dir.o \
vfs_dentry.o \ vfs_dentry.o \
v9fs.o \ v9fs.o \
fid.o \ fid.o
9p-$(CONFIG_9P_FSCACHE) += cache.o
This diff is collapsed.
/*
* V9FS cache definitions.
*
* Copyright (C) 2009 by Abhishek Kulkarni <adkulkar@umail.iu.edu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to:
* Free Software Foundation
* 51 Franklin Street, Fifth Floor
* Boston, MA 02111-1301 USA
*
*/
#ifndef _9P_CACHE_H
#ifdef CONFIG_9P_FSCACHE
#include <linux/fscache.h>
#include <linux/spinlock.h>
extern struct kmem_cache *vcookie_cache;
struct v9fs_cookie {
spinlock_t lock;
struct inode inode;
struct fscache_cookie *fscache;
struct p9_qid *qid;
};
static inline struct v9fs_cookie *v9fs_inode2cookie(const struct inode *inode)
{
return container_of(inode, struct v9fs_cookie, inode);
}
extern struct fscache_netfs v9fs_cache_netfs;
extern const struct fscache_cookie_def v9fs_cache_session_index_def;
extern const struct fscache_cookie_def v9fs_cache_inode_index_def;
extern void v9fs_cache_session_get_cookie(struct v9fs_session_info *v9ses);
extern void v9fs_cache_session_put_cookie(struct v9fs_session_info *v9ses);
extern void v9fs_cache_inode_get_cookie(struct inode *inode);
extern void v9fs_cache_inode_put_cookie(struct inode *inode);
extern void v9fs_cache_inode_flush_cookie(struct inode *inode);
extern void v9fs_cache_inode_set_cookie(struct inode *inode, struct file *filp);
extern void v9fs_cache_inode_reset_cookie(struct inode *inode);
extern int __v9fs_cache_register(void);
extern void __v9fs_cache_unregister(void);
extern int __v9fs_fscache_release_page(struct page *page, gfp_t gfp);
extern void __v9fs_fscache_invalidate_page(struct page *page);
extern int __v9fs_readpage_from_fscache(struct inode *inode,
struct page *page);
extern int __v9fs_readpages_from_fscache(struct inode *inode,
struct address_space *mapping,
struct list_head *pages,
unsigned *nr_pages);
extern void __v9fs_readpage_to_fscache(struct inode *inode, struct page *page);
/**
* v9fs_cache_register - Register v9fs file system with the cache
*/
static inline int v9fs_cache_register(void)
{
return __v9fs_cache_register();
}
/**
* v9fs_cache_unregister - Unregister v9fs from the cache
*/
static inline void v9fs_cache_unregister(void)
{
__v9fs_cache_unregister();
}
static inline int v9fs_fscache_release_page(struct page *page,
gfp_t gfp)
{
return __v9fs_fscache_release_page(page, gfp);
}
static inline void v9fs_fscache_invalidate_page(struct page *page)
{
__v9fs_fscache_invalidate_page(page);
}
static inline int v9fs_readpage_from_fscache(struct inode *inode,
struct page *page)
{
return __v9fs_readpage_from_fscache(inode, page);
}
static inline int v9fs_readpages_from_fscache(struct inode *inode,
struct address_space *mapping,
struct list_head *pages,
unsigned *nr_pages)
{
return __v9fs_readpages_from_fscache(inode, mapping, pages,
nr_pages);
}
static inline void v9fs_readpage_to_fscache(struct inode *inode,
struct page *page)
{
if (PageFsCache(page))
__v9fs_readpage_to_fscache(inode, page);
}
static inline void v9fs_uncache_page(struct inode *inode, struct page *page)
{
struct v9fs_cookie *vcookie = v9fs_inode2cookie(inode);
fscache_uncache_page(vcookie->fscache, page);
BUG_ON(PageFsCache(page));
}
static inline void v9fs_vcookie_set_qid(struct inode *inode,
struct p9_qid *qid)
{
struct v9fs_cookie *vcookie = v9fs_inode2cookie(inode);
spin_lock(&vcookie->lock);
vcookie->qid = qid;
spin_unlock(&vcookie->lock);
}
#else /* CONFIG_9P_FSCACHE */
static inline int v9fs_cache_register(void)
{
return 1;
}
static inline void v9fs_cache_unregister(void) {}
static inline int v9fs_fscache_release_page(struct page *page,
gfp_t gfp) {
return 1;
}
static inline void v9fs_fscache_invalidate_page(struct page *page) {}
static inline int v9fs_readpage_from_fscache(struct inode *inode,
struct page *page)
{
return -ENOBUFS;
}
static inline int v9fs_readpages_from_fscache(struct inode *inode,
struct address_space *mapping,
struct list_head *pages,
unsigned *nr_pages)
{
return -ENOBUFS;
}
static inline void v9fs_readpage_to_fscache(struct inode *inode,
struct page *page)
{}
static inline void v9fs_uncache_page(struct inode *inode, struct page *page)
{}
static inline void v9fs_vcookie_set_qid(struct inode *inode,
struct p9_qid *qid)
{}
#endif /* CONFIG_9P_FSCACHE */
#endif /* _9P_CACHE_H */
...@@ -34,21 +34,25 @@ ...@@ -34,21 +34,25 @@
#include <net/9p/transport.h> #include <net/9p/transport.h>
#include "v9fs.h" #include "v9fs.h"
#include "v9fs_vfs.h" #include "v9fs_vfs.h"
#include "cache.h"
static DEFINE_SPINLOCK(v9fs_sessionlist_lock);
static LIST_HEAD(v9fs_sessionlist);
/* /*
* Option Parsing (code inspired by NFS code) * Option Parsing (code inspired by NFS code)
* NOTE: each transport will parse its own options * NOTE: each transport will parse its own options
*/ */
enum { enum {
/* Options that take integer arguments */ /* Options that take integer arguments */
Opt_debug, Opt_dfltuid, Opt_dfltgid, Opt_afid, Opt_debug, Opt_dfltuid, Opt_dfltgid, Opt_afid,
/* String options */ /* String options */
Opt_uname, Opt_remotename, Opt_trans, Opt_uname, Opt_remotename, Opt_trans, Opt_cache, Opt_cachetag,
/* Options that take no arguments */ /* Options that take no arguments */
Opt_nodevmap, Opt_nodevmap,
/* Cache options */ /* Cache options */
Opt_cache_loose, Opt_cache_loose, Opt_fscache,
/* Access options */ /* Access options */
Opt_access, Opt_access,
/* Error token */ /* Error token */
...@@ -63,8 +67,10 @@ static const match_table_t tokens = { ...@@ -63,8 +67,10 @@ static const match_table_t tokens = {
{Opt_uname, "uname=%s"}, {Opt_uname, "uname=%s"},
{Opt_remotename, "aname=%s"}, {Opt_remotename, "aname=%s"},
{Opt_nodevmap, "nodevmap"}, {Opt_nodevmap, "nodevmap"},
{Opt_cache_loose, "cache=loose"}, {Opt_cache, "cache=%s"},
{Opt_cache_loose, "loose"}, {Opt_cache_loose, "loose"},
{Opt_fscache, "fscache"},
{Opt_cachetag, "cachetag=%s"},
{Opt_access, "access=%s"}, {Opt_access, "access=%s"},
{Opt_err, NULL} {Opt_err, NULL}
}; };
...@@ -89,16 +95,16 @@ static int v9fs_parse_options(struct v9fs_session_info *v9ses, char *opts) ...@@ -89,16 +95,16 @@ static int v9fs_parse_options(struct v9fs_session_info *v9ses, char *opts)
v9ses->afid = ~0; v9ses->afid = ~0;
v9ses->debug = 0; v9ses->debug = 0;
v9ses->cache = 0; v9ses->cache = 0;
#ifdef CONFIG_9P_FSCACHE
v9ses->cachetag = NULL;
#endif
if (!opts) if (!opts)
return 0; return 0;
options = kstrdup(opts, GFP_KERNEL); options = kstrdup(opts, GFP_KERNEL);
if (!options) { if (!options)
P9_DPRINTK(P9_DEBUG_ERROR, goto fail_option_alloc;
"failed to allocate copy of option string\n");
return -ENOMEM;
}
while ((p = strsep(&options, ",")) != NULL) { while ((p = strsep(&options, ",")) != NULL) {
int token; int token;
...@@ -143,16 +149,33 @@ static int v9fs_parse_options(struct v9fs_session_info *v9ses, char *opts) ...@@ -143,16 +149,33 @@ static int v9fs_parse_options(struct v9fs_session_info *v9ses, char *opts)
case Opt_cache_loose: case Opt_cache_loose:
v9ses->cache = CACHE_LOOSE; v9ses->cache = CACHE_LOOSE;
break; break;
case Opt_fscache:
v9ses->cache = CACHE_FSCACHE;
break;
case Opt_cachetag:
#ifdef CONFIG_9P_FSCACHE
v9ses->cachetag = match_strdup(&args[0]);
#endif
break;
case Opt_cache:
s = match_strdup(&args[0]);
if (!s)
goto fail_option_alloc;
if (strcmp(s, "loose") == 0)
v9ses->cache = CACHE_LOOSE;
else if (strcmp(s, "fscache") == 0)
v9ses->cache = CACHE_FSCACHE;
else
v9ses->cache = CACHE_NONE;
kfree(s);
break;
case Opt_access: case Opt_access:
s = match_strdup(&args[0]); s = match_strdup(&args[0]);
if (!s) { if (!s)
P9_DPRINTK(P9_DEBUG_ERROR, goto fail_option_alloc;
"failed to allocate copy"
" of option argument\n");
ret = -ENOMEM;
break;
}
v9ses->flags &= ~V9FS_ACCESS_MASK; v9ses->flags &= ~V9FS_ACCESS_MASK;
if (strcmp(s, "user") == 0) if (strcmp(s, "user") == 0)
v9ses->flags |= V9FS_ACCESS_USER; v9ses->flags |= V9FS_ACCESS_USER;
...@@ -173,6 +196,11 @@ static int v9fs_parse_options(struct v9fs_session_info *v9ses, char *opts) ...@@ -173,6 +196,11 @@ static int v9fs_parse_options(struct v9fs_session_info *v9ses, char *opts)
} }
kfree(options); kfree(options);
return ret; return ret;
fail_option_alloc:
P9_DPRINTK(P9_DEBUG_ERROR,
"failed to allocate copy of option argument\n");
return -ENOMEM;
} }
/** /**
...@@ -200,6 +228,10 @@ struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses, ...@@ -200,6 +228,10 @@ struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses,
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
} }
spin_lock(&v9fs_sessionlist_lock);
list_add(&v9ses->slist, &v9fs_sessionlist);
spin_unlock(&v9fs_sessionlist_lock);
v9ses->flags = V9FS_EXTENDED | V9FS_ACCESS_USER; v9ses->flags = V9FS_EXTENDED | V9FS_ACCESS_USER;
strcpy(v9ses->uname, V9FS_DEFUSER); strcpy(v9ses->uname, V9FS_DEFUSER);
strcpy(v9ses->aname, V9FS_DEFANAME); strcpy(v9ses->aname, V9FS_DEFANAME);
...@@ -249,6 +281,11 @@ struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses, ...@@ -249,6 +281,11 @@ struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses,
else else
fid->uid = ~0; fid->uid = ~0;
#ifdef CONFIG_9P_FSCACHE
/* register the session for caching */
v9fs_cache_session_get_cookie(v9ses);
#endif
return fid; return fid;
error: error:
...@@ -268,8 +305,18 @@ void v9fs_session_close(struct v9fs_session_info *v9ses) ...@@ -268,8 +305,18 @@ void v9fs_session_close(struct v9fs_session_info *v9ses)
v9ses->clnt = NULL; v9ses->clnt = NULL;
} }
#ifdef CONFIG_9P_FSCACHE
if (v9ses->fscache) {
v9fs_cache_session_put_cookie(v9ses);
kfree(v9ses->cachetag);
}
#endif
__putname(v9ses->uname); __putname(v9ses->uname);
__putname(v9ses->aname); __putname(v9ses->aname);
spin_lock(&v9fs_sessionlist_lock);
list_del(&v9ses->slist);
spin_unlock(&v9fs_sessionlist_lock);
} }
/** /**
...@@ -286,25 +333,132 @@ void v9fs_session_cancel(struct v9fs_session_info *v9ses) { ...@@ -286,25 +333,132 @@ void v9fs_session_cancel(struct v9fs_session_info *v9ses) {
extern int v9fs_error_init(void); extern int v9fs_error_init(void);
static struct kobject *v9fs_kobj;
#ifdef CONFIG_9P_FSCACHE
/** /**
* v9fs_init - Initialize module * caches_show - list caches associated with a session
*
* Returns the size of buffer written.
*/
static ssize_t caches_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
{
ssize_t n = 0, count = 0, limit = PAGE_SIZE;
struct v9fs_session_info *v9ses;
spin_lock(&v9fs_sessionlist_lock);
list_for_each_entry(v9ses, &v9fs_sessionlist, slist) {
if (v9ses->cachetag) {
n = snprintf(buf, limit, "%s\n", v9ses->cachetag);
if (n < 0) {
count = n;
break;
}
count += n;
limit -= n;
}
}
spin_unlock(&v9fs_sessionlist_lock);
return count;
}
static struct kobj_attribute v9fs_attr_cache = __ATTR_RO(caches);
#endif /* CONFIG_9P_FSCACHE */
static struct attribute *v9fs_attrs[] = {
#ifdef CONFIG_9P_FSCACHE
&v9fs_attr_cache.attr,
#endif
NULL,
};
static struct attribute_group v9fs_attr_group = {
.attrs = v9fs_attrs,
};
/**
* v9fs_sysfs_init - Initialize the v9fs sysfs interface
*
*/
static int v9fs_sysfs_init(void)
{
v9fs_kobj = kobject_create_and_add("9p", fs_kobj);
if (!v9fs_kobj)
return -ENOMEM;
if (sysfs_create_group(v9fs_kobj, &v9fs_attr_group)) {
kobject_put(v9fs_kobj);
return -ENOMEM;
}
return 0;
}
/**
* v9fs_sysfs_cleanup - Unregister the v9fs sysfs interface
*
*/
static void v9fs_sysfs_cleanup(void)
{
sysfs_remove_group(v9fs_kobj, &v9fs_attr_group);
kobject_put(v9fs_kobj);
}
/**
* init_v9fs - Initialize module
* *
*/ */
static int __init init_v9fs(void) static int __init init_v9fs(void)
{ {
int err;
printk(KERN_INFO "Installing v9fs 9p2000 file system support\n"); printk(KERN_INFO "Installing v9fs 9p2000 file system support\n");
/* TODO: Setup list of registered trasnport modules */ /* TODO: Setup list of registered trasnport modules */
return register_filesystem(&v9fs_fs_type); err = register_filesystem(&v9fs_fs_type);
if (err < 0) {
printk(KERN_ERR "Failed to register filesystem\n");
return err;
}
err = v9fs_cache_register();
if (err < 0) {
printk(KERN_ERR "Failed to register v9fs for caching\n");
goto out_fs_unreg;
}
err = v9fs_sysfs_init();
if (err < 0) {
printk(KERN_ERR "Failed to register with sysfs\n");
goto out_sysfs_cleanup;
}
return 0;
out_sysfs_cleanup:
v9fs_sysfs_cleanup();
out_fs_unreg:
unregister_filesystem(&v9fs_fs_type);
return err;
} }
/** /**
* v9fs_init - shutdown module * exit_v9fs - shutdown module
* *
*/ */
static void __exit exit_v9fs(void) static void __exit exit_v9fs(void)
{ {
v9fs_sysfs_cleanup();
v9fs_cache_unregister();
unregister_filesystem(&v9fs_fs_type); unregister_filesystem(&v9fs_fs_type);
} }
......
...@@ -51,6 +51,7 @@ enum p9_session_flags { ...@@ -51,6 +51,7 @@ enum p9_session_flags {
enum p9_cache_modes { enum p9_cache_modes {
CACHE_NONE, CACHE_NONE,
CACHE_LOOSE, CACHE_LOOSE,
CACHE_FSCACHE,
}; };
/** /**
...@@ -60,6 +61,8 @@ enum p9_cache_modes { ...@@ -60,6 +61,8 @@ enum p9_cache_modes {
* @debug: debug level * @debug: debug level
* @afid: authentication handle * @afid: authentication handle
* @cache: cache mode of type &p9_cache_modes * @cache: cache mode of type &p9_cache_modes
* @cachetag: the tag of the cache associated with this session
* @fscache: session cookie associated with FS-Cache
* @options: copy of options string given by user * @options: copy of options string given by user
* @uname: string user name to mount hierarchy as * @uname: string user name to mount hierarchy as
* @aname: mount specifier for remote hierarchy * @aname: mount specifier for remote hierarchy
...@@ -68,7 +71,7 @@ enum p9_cache_modes { ...@@ -68,7 +71,7 @@ enum p9_cache_modes {
* @dfltgid: default numeric groupid to mount hierarchy as * @dfltgid: default numeric groupid to mount hierarchy as
* @uid: if %V9FS_ACCESS_SINGLE, the numeric uid which mounted the hierarchy * @uid: if %V9FS_ACCESS_SINGLE, the numeric uid which mounted the hierarchy
* @clnt: reference to 9P network client instantiated for this session * @clnt: reference to 9P network client instantiated for this session
* @debugfs_dir: reference to debugfs_dir which can be used for add'l debug * @slist: reference to list of registered 9p sessions
* *
* This structure holds state for each session instance established during * This structure holds state for each session instance established during
* a sys_mount() . * a sys_mount() .
...@@ -84,6 +87,10 @@ struct v9fs_session_info { ...@@ -84,6 +87,10 @@ struct v9fs_session_info {
unsigned short debug; unsigned short debug;
unsigned int afid; unsigned int afid;
unsigned int cache; unsigned int cache;
#ifdef CONFIG_9P_FSCACHE
char *cachetag;
struct fscache_cookie *fscache;
#endif
char *uname; /* user name to mount as */ char *uname; /* user name to mount as */
char *aname; /* name of remote hierarchy being mounted */ char *aname; /* name of remote hierarchy being mounted */
...@@ -92,11 +99,9 @@ struct v9fs_session_info { ...@@ -92,11 +99,9 @@ struct v9fs_session_info {
unsigned int dfltgid; /* default gid for legacy support */ unsigned int dfltgid; /* default gid for legacy support */
u32 uid; /* if ACCESS_SINGLE, the uid that has access */ u32 uid; /* if ACCESS_SINGLE, the uid that has access */
struct p9_client *clnt; /* 9p client */ struct p9_client *clnt; /* 9p client */
struct dentry *debugfs_dir; struct list_head slist; /* list of sessions registered with v9fs */
}; };
extern struct dentry *v9fs_debugfs_root;
struct p9_fid *v9fs_session_init(struct v9fs_session_info *, const char *, struct p9_fid *v9fs_session_init(struct v9fs_session_info *, const char *,
char *); char *);
void v9fs_session_close(struct v9fs_session_info *v9ses); void v9fs_session_close(struct v9fs_session_info *v9ses);
......
...@@ -44,7 +44,13 @@ extern const struct file_operations v9fs_dir_operations; ...@@ -44,7 +44,13 @@ extern const struct file_operations v9fs_dir_operations;
extern const struct dentry_operations v9fs_dentry_operations; extern const struct dentry_operations v9fs_dentry_operations;
extern const struct dentry_operations v9fs_cached_dentry_operations; extern const struct dentry_operations v9fs_cached_dentry_operations;
#ifdef CONFIG_9P_FSCACHE
struct inode *v9fs_alloc_inode(struct super_block *sb);
void v9fs_destroy_inode(struct inode *inode);
#endif
struct inode *v9fs_get_inode(struct super_block *sb, int mode); struct inode *v9fs_get_inode(struct super_block *sb, int mode);
void v9fs_clear_inode(struct inode *inode);
ino_t v9fs_qid2ino(struct p9_qid *qid); ino_t v9fs_qid2ino(struct p9_qid *qid);
void v9fs_stat2inode(struct p9_wstat *, struct inode *, struct super_block *); void v9fs_stat2inode(struct p9_wstat *, struct inode *, struct super_block *);
int v9fs_dir_release(struct inode *inode, struct file *filp); int v9fs_dir_release(struct inode *inode, struct file *filp);
......
...@@ -38,6 +38,7 @@ ...@@ -38,6 +38,7 @@
#include "v9fs.h" #include "v9fs.h"
#include "v9fs_vfs.h" #include "v9fs_vfs.h"
#include "cache.h"
/** /**
* v9fs_vfs_readpage - read an entire page in from 9P * v9fs_vfs_readpage - read an entire page in from 9P
...@@ -52,18 +53,31 @@ static int v9fs_vfs_readpage(struct file *filp, struct page *page) ...@@ -52,18 +53,31 @@ static int v9fs_vfs_readpage(struct file *filp, struct page *page)
int retval; int retval;
loff_t offset; loff_t offset;
char *buffer; char *buffer;
struct inode *inode;
inode = page->mapping->host;
P9_DPRINTK(P9_DEBUG_VFS, "\n"); P9_DPRINTK(P9_DEBUG_VFS, "\n");
BUG_ON(!PageLocked(page));
retval = v9fs_readpage_from_fscache(inode, page);
if (retval == 0)
return retval;
buffer = kmap(page); buffer = kmap(page);
offset = page_offset(page); offset = page_offset(page);
retval = v9fs_file_readn(filp, buffer, NULL, PAGE_CACHE_SIZE, offset); retval = v9fs_file_readn(filp, buffer, NULL, PAGE_CACHE_SIZE, offset);
if (retval < 0) if (retval < 0) {
v9fs_uncache_page(inode, page);
goto done; goto done;
}
memset(buffer + retval, 0, PAGE_CACHE_SIZE - retval); memset(buffer + retval, 0, PAGE_CACHE_SIZE - retval);
flush_dcache_page(page); flush_dcache_page(page);
SetPageUptodate(page); SetPageUptodate(page);
v9fs_readpage_to_fscache(inode, page);
retval = 0; retval = 0;
done: done:
...@@ -72,6 +86,78 @@ static int v9fs_vfs_readpage(struct file *filp, struct page *page) ...@@ -72,6 +86,78 @@ static int v9fs_vfs_readpage(struct file *filp, struct page *page)
return retval; return retval;
} }
/**
* v9fs_vfs_readpages - read a set of pages from 9P
*
* @filp: file being read
* @mapping: the address space
* @pages: list of pages to read
* @nr_pages: count of pages to read
*
*/
static int v9fs_vfs_readpages(struct file *filp, struct address_space *mapping,
struct list_head *pages, unsigned nr_pages)
{
int ret = 0;
struct inode *inode;
inode = mapping->host;
P9_DPRINTK(P9_DEBUG_VFS, "inode: %p file: %p\n", inode, filp);
ret = v9fs_readpages_from_fscache(inode, mapping, pages, &nr_pages);
if (ret == 0)
return ret;
ret = read_cache_pages(mapping, pages, (void *)v9fs_vfs_readpage, filp);
P9_DPRINTK(P9_DEBUG_VFS, " = %d\n", ret);
return ret;
}
/**
* v9fs_release_page - release the private state associated with a page
*
* Returns 1 if the page can be released, false otherwise.
*/
static int v9fs_release_page(struct page *page, gfp_t gfp)
{
if (PagePrivate(page))
return 0;
return v9fs_fscache_release_page(page, gfp);
}
/**
* v9fs_invalidate_page - Invalidate a page completely or partially
*
* @page: structure to page
* @offset: offset in the page
*/
static void v9fs_invalidate_page(struct page *page, unsigned long offset)
{
if (offset == 0)
v9fs_fscache_invalidate_page(page);
}
/**
* v9fs_launder_page - Writeback a dirty page
* Since the writes go directly to the server, we simply return a 0
* here to indicate success.
*
* Returns 0 on success.
*/
static int v9fs_launder_page(struct page *page)
{
return 0;
}
const struct address_space_operations v9fs_addr_operations = { const struct address_space_operations v9fs_addr_operations = {
.readpage = v9fs_vfs_readpage, .readpage = v9fs_vfs_readpage,
.readpages = v9fs_vfs_readpages,
.releasepage = v9fs_release_page,
.invalidatepage = v9fs_invalidate_page,
.launder_page = v9fs_launder_page,
}; };
...@@ -41,6 +41,7 @@ ...@@ -41,6 +41,7 @@
#include "v9fs.h" #include "v9fs.h"
#include "v9fs_vfs.h" #include "v9fs_vfs.h"
#include "fid.h" #include "fid.h"
#include "cache.h"
static const struct file_operations v9fs_cached_file_operations; static const struct file_operations v9fs_cached_file_operations;
...@@ -86,6 +87,10 @@ int v9fs_file_open(struct inode *inode, struct file *file) ...@@ -86,6 +87,10 @@ int v9fs_file_open(struct inode *inode, struct file *file)
/* enable cached file options */ /* enable cached file options */
if(file->f_op == &v9fs_file_operations) if(file->f_op == &v9fs_file_operations)
file->f_op = &v9fs_cached_file_operations; file->f_op = &v9fs_cached_file_operations;
#ifdef CONFIG_9P_FSCACHE
v9fs_cache_inode_set_cookie(inode, file);
#endif
} }
return 0; return 0;
...@@ -238,8 +243,9 @@ v9fs_file_write(struct file *filp, const char __user * data, ...@@ -238,8 +243,9 @@ v9fs_file_write(struct file *filp, const char __user * data,
if (total > 0) { if (total > 0) {
pg_start = origin >> PAGE_CACHE_SHIFT; pg_start = origin >> PAGE_CACHE_SHIFT;
pg_end = (origin + total - 1) >> PAGE_CACHE_SHIFT; pg_end = (origin + total - 1) >> PAGE_CACHE_SHIFT;
invalidate_inode_pages2_range(inode->i_mapping, pg_start, if (inode->i_mapping && inode->i_mapping->nrpages)
pg_end); invalidate_inode_pages2_range(inode->i_mapping,
pg_start, pg_end);
*offset += total; *offset += total;
i_size_write(inode, i_size_read(inode) + total); i_size_write(inode, i_size_read(inode) + total);
inode->i_blocks = (i_size_read(inode) + 512 - 1) >> 9; inode->i_blocks = (i_size_read(inode) + 512 - 1) >> 9;
......
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
#include "v9fs.h" #include "v9fs.h"
#include "v9fs_vfs.h" #include "v9fs_vfs.h"
#include "fid.h" #include "fid.h"
#include "cache.h"
static const struct inode_operations v9fs_dir_inode_operations; static const struct inode_operations v9fs_dir_inode_operations;
static const struct inode_operations v9fs_dir_inode_operations_ext; static const struct inode_operations v9fs_dir_inode_operations_ext;
...@@ -197,6 +198,39 @@ v9fs_blank_wstat(struct p9_wstat *wstat) ...@@ -197,6 +198,39 @@ v9fs_blank_wstat(struct p9_wstat *wstat)
wstat->extension = NULL; wstat->extension = NULL;
} }
#ifdef CONFIG_9P_FSCACHE
/**
* v9fs_alloc_inode - helper function to allocate an inode
* This callback is executed before setting up the inode so that we
* can associate a vcookie with each inode.
*
*/
struct inode *v9fs_alloc_inode(struct super_block *sb)
{
struct v9fs_cookie *vcookie;
vcookie = (struct v9fs_cookie *)kmem_cache_alloc(vcookie_cache,
GFP_KERNEL);
if (!vcookie)
return NULL;
vcookie->fscache = NULL;
vcookie->qid = NULL;
spin_lock_init(&vcookie->lock);
return &vcookie->inode;
}
/**
* v9fs_destroy_inode - destroy an inode
*
*/
void v9fs_destroy_inode(struct inode *inode)
{
kmem_cache_free(vcookie_cache, v9fs_inode2cookie(inode));
}
#endif
/** /**
* v9fs_get_inode - helper function to setup an inode * v9fs_get_inode - helper function to setup an inode
* @sb: superblock * @sb: superblock
...@@ -326,6 +360,21 @@ v9fs_clone_walk(struct v9fs_session_info *v9ses, u32 fid, struct dentry *dentry) ...@@ -326,6 +360,21 @@ v9fs_clone_walk(struct v9fs_session_info *v9ses, u32 fid, struct dentry *dentry)
} }
*/ */
/**
* v9fs_clear_inode - release an inode
* @inode: inode to release
*
*/
void v9fs_clear_inode(struct inode *inode)
{
filemap_fdatawrite(inode->i_mapping);
#ifdef CONFIG_9P_FSCACHE
v9fs_cache_inode_put_cookie(inode);
#endif
}
/** /**
* v9fs_inode_from_fid - populate an inode by issuing a attribute request * v9fs_inode_from_fid - populate an inode by issuing a attribute request
* @v9ses: session information * @v9ses: session information
...@@ -356,8 +405,14 @@ v9fs_inode_from_fid(struct v9fs_session_info *v9ses, struct p9_fid *fid, ...@@ -356,8 +405,14 @@ v9fs_inode_from_fid(struct v9fs_session_info *v9ses, struct p9_fid *fid,
v9fs_stat2inode(st, ret, sb); v9fs_stat2inode(st, ret, sb);
ret->i_ino = v9fs_qid2ino(&st->qid); ret->i_ino = v9fs_qid2ino(&st->qid);
#ifdef CONFIG_9P_FSCACHE
v9fs_vcookie_set_qid(ret, &st->qid);
v9fs_cache_inode_get_cookie(ret);
#endif
p9stat_free(st); p9stat_free(st);
kfree(st); kfree(st);
return ret; return ret;
error: error:
...@@ -751,7 +806,7 @@ v9fs_vfs_getattr(struct vfsmount *mnt, struct dentry *dentry, ...@@ -751,7 +806,7 @@ v9fs_vfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
P9_DPRINTK(P9_DEBUG_VFS, "dentry: %p\n", dentry); P9_DPRINTK(P9_DEBUG_VFS, "dentry: %p\n", dentry);
err = -EPERM; err = -EPERM;
v9ses = v9fs_inode2v9ses(dentry->d_inode); v9ses = v9fs_inode2v9ses(dentry->d_inode);
if (v9ses->cache == CACHE_LOOSE) if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE)
return simple_getattr(mnt, dentry, stat); return simple_getattr(mnt, dentry, stat);
fid = v9fs_fid_lookup(dentry); fid = v9fs_fid_lookup(dentry);
......
...@@ -44,20 +44,8 @@ ...@@ -44,20 +44,8 @@
#include "v9fs_vfs.h" #include "v9fs_vfs.h"
#include "fid.h" #include "fid.h"
static void v9fs_clear_inode(struct inode *);
static const struct super_operations v9fs_super_ops; static const struct super_operations v9fs_super_ops;
/**
* v9fs_clear_inode - release an inode
* @inode: inode to release
*
*/
static void v9fs_clear_inode(struct inode *inode)
{
filemap_fdatawrite(inode->i_mapping);
}
/** /**
* v9fs_set_super - set the superblock * v9fs_set_super - set the superblock
* @s: super block * @s: super block
...@@ -220,6 +208,10 @@ v9fs_umount_begin(struct super_block *sb) ...@@ -220,6 +208,10 @@ v9fs_umount_begin(struct super_block *sb)
} }
static const struct super_operations v9fs_super_ops = { static const struct super_operations v9fs_super_ops = {
#ifdef CONFIG_9P_FSCACHE
.alloc_inode = v9fs_alloc_inode,
.destroy_inode = v9fs_destroy_inode,
#endif
.statfs = simple_statfs, .statfs = simple_statfs,
.clear_inode = v9fs_clear_inode, .clear_inode = v9fs_clear_inode,
.show_options = generic_show_options, .show_options = generic_show_options,
......
...@@ -38,6 +38,8 @@ ...@@ -38,6 +38,8 @@
* @P9_DEBUG_SLABS: memory management tracing * @P9_DEBUG_SLABS: memory management tracing
* @P9_DEBUG_FCALL: verbose dump of protocol messages * @P9_DEBUG_FCALL: verbose dump of protocol messages
* @P9_DEBUG_FID: fid allocation/deallocation tracking * @P9_DEBUG_FID: fid allocation/deallocation tracking
* @P9_DEBUG_PKT: packet marshalling/unmarshalling
* @P9_DEBUG_FSC: FS-cache tracing
* *
* These flags are passed at mount time to turn on various levels of * These flags are passed at mount time to turn on various levels of
* verbosity and tracing which will be output to the system logs. * verbosity and tracing which will be output to the system logs.
...@@ -54,6 +56,7 @@ enum p9_debug_flags { ...@@ -54,6 +56,7 @@ enum p9_debug_flags {
P9_DEBUG_FCALL = (1<<8), P9_DEBUG_FCALL = (1<<8),
P9_DEBUG_FID = (1<<9), P9_DEBUG_FID = (1<<9),
P9_DEBUG_PKT = (1<<10), P9_DEBUG_PKT = (1<<10),
P9_DEBUG_FSC = (1<<11),
}; };
#ifdef CONFIG_NET_9P_DEBUG #ifdef CONFIG_NET_9P_DEBUG
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment