Commit 26a992db authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ericvh/v9fs

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ericvh/v9fs: (46 commits)
  fs/9p: Make the writeback_fid owned by root
  fs/9p: Writeback dirty data before setattr
  fs/9p: call vmtruncate before setattr 9p opeation
  fs/9p: Properly update inode attributes on link
  fs/9p: Prevent multiple inclusion of same header
  fs/9p: Workaround vfs rename rehash bug
  fs/9p: Mark directory inode invalid for many directory inode operations
  fs/9p: Add . and .. dentry revalidation flag
  fs/9p: mark inode attribute invalid on rename, unlink and setattr
  fs/9p: Add support for marking inode attribute invalid
  fs/9p: Initialize root inode number for dotl
  fs/9p: Update link count correctly on different file system operations
  fs/9p: Add drop_inode 9p callback
  fs/9p: Add direct IO support in cached mode
  fs/9p: Fix inode i_size update in file_write
  fs/9p: set default readahead pages in cached mode
  fs/9p: Move writeback fid to v9fs_inode
  fs/9p: Add v9fs_inode
  fs/9p: Don't set stat.st_blocks based on nrpages
  fs/9p: Add inode hashing
  ...
parents abab012a 7c9e592e
...@@ -21,8 +21,8 @@ ...@@ -21,8 +21,8 @@
#include <linux/posix_acl_xattr.h> #include <linux/posix_acl_xattr.h>
#include "xattr.h" #include "xattr.h"
#include "acl.h" #include "acl.h"
#include "v9fs_vfs.h"
#include "v9fs.h" #include "v9fs.h"
#include "v9fs_vfs.h"
static struct posix_acl *__v9fs_get_acl(struct p9_fid *fid, char *name) static struct posix_acl *__v9fs_get_acl(struct p9_fid *fid, char *name)
{ {
...@@ -59,7 +59,8 @@ int v9fs_get_acl(struct inode *inode, struct p9_fid *fid) ...@@ -59,7 +59,8 @@ int v9fs_get_acl(struct inode *inode, struct p9_fid *fid)
struct v9fs_session_info *v9ses; struct v9fs_session_info *v9ses;
v9ses = v9fs_inode2v9ses(inode); v9ses = v9fs_inode2v9ses(inode);
if ((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT) { if (((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT) ||
((v9ses->flags & V9FS_ACL_MASK) != V9FS_POSIX_ACL)) {
set_cached_acl(inode, ACL_TYPE_DEFAULT, NULL); set_cached_acl(inode, ACL_TYPE_DEFAULT, NULL);
set_cached_acl(inode, ACL_TYPE_ACCESS, NULL); set_cached_acl(inode, ACL_TYPE_ACCESS, NULL);
return 0; return 0;
...@@ -71,11 +72,15 @@ int v9fs_get_acl(struct inode *inode, struct p9_fid *fid) ...@@ -71,11 +72,15 @@ int v9fs_get_acl(struct inode *inode, struct p9_fid *fid)
if (!IS_ERR(dacl) && !IS_ERR(pacl)) { if (!IS_ERR(dacl) && !IS_ERR(pacl)) {
set_cached_acl(inode, ACL_TYPE_DEFAULT, dacl); set_cached_acl(inode, ACL_TYPE_DEFAULT, dacl);
set_cached_acl(inode, ACL_TYPE_ACCESS, pacl); set_cached_acl(inode, ACL_TYPE_ACCESS, pacl);
posix_acl_release(dacl);
posix_acl_release(pacl);
} else } else
retval = -EIO; retval = -EIO;
if (!IS_ERR(dacl))
posix_acl_release(dacl);
if (!IS_ERR(pacl))
posix_acl_release(pacl);
return retval; return retval;
} }
...@@ -100,9 +105,10 @@ int v9fs_check_acl(struct inode *inode, int mask, unsigned int flags) ...@@ -100,9 +105,10 @@ int v9fs_check_acl(struct inode *inode, int mask, unsigned int flags)
return -ECHILD; return -ECHILD;
v9ses = v9fs_inode2v9ses(inode); v9ses = v9fs_inode2v9ses(inode);
if ((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT) { if (((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT) ||
((v9ses->flags & V9FS_ACL_MASK) != V9FS_POSIX_ACL)) {
/* /*
* On access = client mode get the acl * On access = client and acl = on mode get the acl
* values from the server * values from the server
*/ */
return 0; return 0;
...@@ -128,6 +134,10 @@ static int v9fs_set_acl(struct dentry *dentry, int type, struct posix_acl *acl) ...@@ -128,6 +134,10 @@ static int v9fs_set_acl(struct dentry *dentry, int type, struct posix_acl *acl)
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
set_cached_acl(inode, type, acl); set_cached_acl(inode, type, acl);
if (!acl)
return 0;
/* Set a setxattr request to server */ /* Set a setxattr request to server */
size = posix_acl_xattr_size(acl->a_count); size = posix_acl_xattr_size(acl->a_count);
buffer = kmalloc(size, GFP_KERNEL); buffer = kmalloc(size, GFP_KERNEL);
...@@ -177,9 +187,7 @@ int v9fs_acl_chmod(struct dentry *dentry) ...@@ -177,9 +187,7 @@ int v9fs_acl_chmod(struct dentry *dentry)
int v9fs_set_create_acl(struct dentry *dentry, int v9fs_set_create_acl(struct dentry *dentry,
struct posix_acl *dpacl, struct posix_acl *pacl) struct posix_acl *dpacl, struct posix_acl *pacl)
{ {
if (dpacl)
v9fs_set_acl(dentry, ACL_TYPE_DEFAULT, dpacl); v9fs_set_acl(dentry, ACL_TYPE_DEFAULT, dpacl);
if (pacl)
v9fs_set_acl(dentry, ACL_TYPE_ACCESS, pacl); v9fs_set_acl(dentry, ACL_TYPE_ACCESS, pacl);
posix_acl_release(dpacl); posix_acl_release(dpacl);
posix_acl_release(pacl); posix_acl_release(pacl);
......
This diff is collapsed.
...@@ -25,20 +25,6 @@ ...@@ -25,20 +25,6 @@
#include <linux/fscache.h> #include <linux/fscache.h>
#include <linux/spinlock.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 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_session_index_def;
extern const struct fscache_cookie_def v9fs_cache_inode_index_def; extern const struct fscache_cookie_def v9fs_cache_inode_index_def;
...@@ -64,23 +50,8 @@ extern int __v9fs_readpages_from_fscache(struct inode *inode, ...@@ -64,23 +50,8 @@ extern int __v9fs_readpages_from_fscache(struct inode *inode,
struct list_head *pages, struct list_head *pages,
unsigned *nr_pages); unsigned *nr_pages);
extern void __v9fs_readpage_to_fscache(struct inode *inode, struct page *page); extern void __v9fs_readpage_to_fscache(struct inode *inode, struct page *page);
extern void __v9fs_fscache_wait_on_page_write(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, static inline int v9fs_fscache_release_page(struct page *page,
gfp_t gfp) gfp_t gfp)
...@@ -117,28 +88,27 @@ static inline void v9fs_readpage_to_fscache(struct inode *inode, ...@@ -117,28 +88,27 @@ static inline void v9fs_readpage_to_fscache(struct inode *inode,
static inline void v9fs_uncache_page(struct inode *inode, struct page *page) static inline void v9fs_uncache_page(struct inode *inode, struct page *page)
{ {
struct v9fs_cookie *vcookie = v9fs_inode2cookie(inode); struct v9fs_inode *v9inode = V9FS_I(inode);
fscache_uncache_page(vcookie->fscache, page); fscache_uncache_page(v9inode->fscache, page);
BUG_ON(PageFsCache(page)); BUG_ON(PageFsCache(page));
} }
static inline void v9fs_vcookie_set_qid(struct inode *inode, static inline void v9fs_fscache_set_key(struct inode *inode,
struct p9_qid *qid) struct p9_qid *qid)
{ {
struct v9fs_cookie *vcookie = v9fs_inode2cookie(inode); struct v9fs_inode *v9inode = V9FS_I(inode);
spin_lock(&vcookie->lock); spin_lock(&v9inode->fscache_lock);
vcookie->qid = qid; v9inode->fscache_key = qid;
spin_unlock(&vcookie->lock); spin_unlock(&v9inode->fscache_lock);
} }
#else /* CONFIG_9P_FSCACHE */ static inline void v9fs_fscache_wait_on_page_write(struct inode *inode,
struct page *page)
static inline int v9fs_cache_register(void)
{ {
return 1; return __v9fs_fscache_wait_on_page_write(inode, page);
} }
static inline void v9fs_cache_unregister(void) {} #else /* CONFIG_9P_FSCACHE */
static inline int v9fs_fscache_release_page(struct page *page, static inline int v9fs_fscache_release_page(struct page *page,
gfp_t gfp) { gfp_t gfp) {
...@@ -168,9 +138,11 @@ static inline void v9fs_readpage_to_fscache(struct inode *inode, ...@@ -168,9 +138,11 @@ static inline void v9fs_readpage_to_fscache(struct inode *inode,
static inline void v9fs_uncache_page(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, static inline void v9fs_fscache_wait_on_page_write(struct inode *inode,
struct p9_qid *qid) struct page *page)
{} {
return;
}
#endif /* CONFIG_9P_FSCACHE */ #endif /* CONFIG_9P_FSCACHE */
#endif /* _9P_CACHE_H */ #endif /* _9P_CACHE_H */
...@@ -125,46 +125,17 @@ static int build_path_from_dentry(struct v9fs_session_info *v9ses, ...@@ -125,46 +125,17 @@ static int build_path_from_dentry(struct v9fs_session_info *v9ses,
return -ENOMEM; return -ENOMEM;
} }
/** static struct p9_fid *v9fs_fid_lookup_with_uid(struct dentry *dentry,
* v9fs_fid_lookup - lookup for a fid, try to walk if not found uid_t uid, int any)
* @dentry: dentry to look for fid in
*
* Look for a fid in the specified dentry for the current user.
* If no fid is found, try to create one walking from a fid from the parent
* dentry (if it has one), or the root dentry. If the user haven't accessed
* the fs yet, attach now and walk from the root.
*/
struct p9_fid *v9fs_fid_lookup(struct dentry *dentry)
{ {
int i, n, l, clone, any, access;
u32 uid;
struct p9_fid *fid, *old_fid = NULL;
struct dentry *ds; struct dentry *ds;
struct v9fs_session_info *v9ses;
char **wnames, *uname; char **wnames, *uname;
int i, n, l, clone, access;
struct v9fs_session_info *v9ses;
struct p9_fid *fid, *old_fid = NULL;
v9ses = v9fs_inode2v9ses(dentry->d_inode); v9ses = v9fs_inode2v9ses(dentry->d_inode);
access = v9ses->flags & V9FS_ACCESS_MASK; access = v9ses->flags & V9FS_ACCESS_MASK;
switch (access) {
case V9FS_ACCESS_SINGLE:
case V9FS_ACCESS_USER:
case V9FS_ACCESS_CLIENT:
uid = current_fsuid();
any = 0;
break;
case V9FS_ACCESS_ANY:
uid = v9ses->uid;
any = 1;
break;
default:
uid = ~0;
any = 0;
break;
}
fid = v9fs_fid_find(dentry, uid, any); fid = v9fs_fid_find(dentry, uid, any);
if (fid) if (fid)
return fid; return fid;
...@@ -250,6 +221,45 @@ struct p9_fid *v9fs_fid_lookup(struct dentry *dentry) ...@@ -250,6 +221,45 @@ struct p9_fid *v9fs_fid_lookup(struct dentry *dentry)
return fid; return fid;
} }
/**
* v9fs_fid_lookup - lookup for a fid, try to walk if not found
* @dentry: dentry to look for fid in
*
* Look for a fid in the specified dentry for the current user.
* If no fid is found, try to create one walking from a fid from the parent
* dentry (if it has one), or the root dentry. If the user haven't accessed
* the fs yet, attach now and walk from the root.
*/
struct p9_fid *v9fs_fid_lookup(struct dentry *dentry)
{
uid_t uid;
int any, access;
struct v9fs_session_info *v9ses;
v9ses = v9fs_inode2v9ses(dentry->d_inode);
access = v9ses->flags & V9FS_ACCESS_MASK;
switch (access) {
case V9FS_ACCESS_SINGLE:
case V9FS_ACCESS_USER:
case V9FS_ACCESS_CLIENT:
uid = current_fsuid();
any = 0;
break;
case V9FS_ACCESS_ANY:
uid = v9ses->uid;
any = 1;
break;
default:
uid = ~0;
any = 0;
break;
}
return v9fs_fid_lookup_with_uid(dentry, uid, any);
}
struct p9_fid *v9fs_fid_clone(struct dentry *dentry) struct p9_fid *v9fs_fid_clone(struct dentry *dentry)
{ {
struct p9_fid *fid, *ret; struct p9_fid *fid, *ret;
...@@ -261,3 +271,39 @@ struct p9_fid *v9fs_fid_clone(struct dentry *dentry) ...@@ -261,3 +271,39 @@ struct p9_fid *v9fs_fid_clone(struct dentry *dentry)
ret = p9_client_walk(fid, 0, NULL, 1); ret = p9_client_walk(fid, 0, NULL, 1);
return ret; return ret;
} }
static struct p9_fid *v9fs_fid_clone_with_uid(struct dentry *dentry, uid_t uid)
{
struct p9_fid *fid, *ret;
fid = v9fs_fid_lookup_with_uid(dentry, uid, 0);
if (IS_ERR(fid))
return fid;
ret = p9_client_walk(fid, 0, NULL, 1);
return ret;
}
struct p9_fid *v9fs_writeback_fid(struct dentry *dentry)
{
int err;
struct p9_fid *fid;
fid = v9fs_fid_clone_with_uid(dentry, 0);
if (IS_ERR(fid))
goto error_out;
/*
* writeback fid will only be used to write back the
* dirty pages. We always request for the open fid in read-write
* mode so that a partial page write which result in page
* read can work.
*/
err = p9_client_open(fid, O_RDWR);
if (err < 0) {
p9_client_clunk(fid);
fid = ERR_PTR(err);
goto error_out;
}
error_out:
return fid;
}
...@@ -19,7 +19,8 @@ ...@@ -19,7 +19,8 @@
* Boston, MA 02111-1301 USA * Boston, MA 02111-1301 USA
* *
*/ */
#ifndef FS_9P_FID_H
#define FS_9P_FID_H
#include <linux/list.h> #include <linux/list.h>
/** /**
...@@ -45,3 +46,5 @@ struct v9fs_dentry { ...@@ -45,3 +46,5 @@ struct v9fs_dentry {
struct p9_fid *v9fs_fid_lookup(struct dentry *dentry); struct p9_fid *v9fs_fid_lookup(struct dentry *dentry);
struct p9_fid *v9fs_fid_clone(struct dentry *dentry); struct p9_fid *v9fs_fid_clone(struct dentry *dentry);
int v9fs_fid_add(struct dentry *dentry, struct p9_fid *fid); int v9fs_fid_add(struct dentry *dentry, struct p9_fid *fid);
struct p9_fid *v9fs_writeback_fid(struct dentry *dentry);
#endif
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
static DEFINE_SPINLOCK(v9fs_sessionlist_lock); static DEFINE_SPINLOCK(v9fs_sessionlist_lock);
static LIST_HEAD(v9fs_sessionlist); static LIST_HEAD(v9fs_sessionlist);
struct kmem_cache *v9fs_inode_cache;
/* /*
* Option Parsing (code inspired by NFS code) * Option Parsing (code inspired by NFS code)
...@@ -55,7 +56,7 @@ enum { ...@@ -55,7 +56,7 @@ enum {
/* Cache options */ /* Cache options */
Opt_cache_loose, Opt_fscache, Opt_cache_loose, Opt_fscache,
/* Access options */ /* Access options */
Opt_access, Opt_access, Opt_posixacl,
/* Error token */ /* Error token */
Opt_err Opt_err
}; };
...@@ -73,6 +74,7 @@ static const match_table_t tokens = { ...@@ -73,6 +74,7 @@ static const match_table_t tokens = {
{Opt_fscache, "fscache"}, {Opt_fscache, "fscache"},
{Opt_cachetag, "cachetag=%s"}, {Opt_cachetag, "cachetag=%s"},
{Opt_access, "access=%s"}, {Opt_access, "access=%s"},
{Opt_posixacl, "posixacl"},
{Opt_err, NULL} {Opt_err, NULL}
}; };
...@@ -194,15 +196,7 @@ static int v9fs_parse_options(struct v9fs_session_info *v9ses, char *opts) ...@@ -194,15 +196,7 @@ static int v9fs_parse_options(struct v9fs_session_info *v9ses, char *opts)
else if (strcmp(s, "any") == 0) else if (strcmp(s, "any") == 0)
v9ses->flags |= V9FS_ACCESS_ANY; v9ses->flags |= V9FS_ACCESS_ANY;
else if (strcmp(s, "client") == 0) { else if (strcmp(s, "client") == 0) {
#ifdef CONFIG_9P_FS_POSIX_ACL
v9ses->flags |= V9FS_ACCESS_CLIENT; v9ses->flags |= V9FS_ACCESS_CLIENT;
#else
P9_DPRINTK(P9_DEBUG_ERROR,
"access=client option not supported\n");
kfree(s);
ret = -EINVAL;
goto free_and_return;
#endif
} else { } else {
v9ses->flags |= V9FS_ACCESS_SINGLE; v9ses->flags |= V9FS_ACCESS_SINGLE;
v9ses->uid = simple_strtoul(s, &e, 10); v9ses->uid = simple_strtoul(s, &e, 10);
...@@ -212,6 +206,16 @@ static int v9fs_parse_options(struct v9fs_session_info *v9ses, char *opts) ...@@ -212,6 +206,16 @@ static int v9fs_parse_options(struct v9fs_session_info *v9ses, char *opts)
kfree(s); kfree(s);
break; break;
case Opt_posixacl:
#ifdef CONFIG_9P_FS_POSIX_ACL
v9ses->flags |= V9FS_POSIX_ACL;
#else
P9_DPRINTK(P9_DEBUG_ERROR,
"Not defined CONFIG_9P_FS_POSIX_ACL. "
"Ignoring posixacl option\n");
#endif
break;
default: default:
continue; continue;
} }
...@@ -260,19 +264,12 @@ struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses, ...@@ -260,19 +264,12 @@ struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses,
list_add(&v9ses->slist, &v9fs_sessionlist); list_add(&v9ses->slist, &v9fs_sessionlist);
spin_unlock(&v9fs_sessionlist_lock); spin_unlock(&v9fs_sessionlist_lock);
v9ses->flags = V9FS_ACCESS_USER;
strcpy(v9ses->uname, V9FS_DEFUSER); strcpy(v9ses->uname, V9FS_DEFUSER);
strcpy(v9ses->aname, V9FS_DEFANAME); strcpy(v9ses->aname, V9FS_DEFANAME);
v9ses->uid = ~0; v9ses->uid = ~0;
v9ses->dfltuid = V9FS_DEFUID; v9ses->dfltuid = V9FS_DEFUID;
v9ses->dfltgid = V9FS_DEFGID; v9ses->dfltgid = V9FS_DEFGID;
rc = v9fs_parse_options(v9ses, data);
if (rc < 0) {
retval = rc;
goto error;
}
v9ses->clnt = p9_client_create(dev_name, data); v9ses->clnt = p9_client_create(dev_name, data);
if (IS_ERR(v9ses->clnt)) { if (IS_ERR(v9ses->clnt)) {
retval = PTR_ERR(v9ses->clnt); retval = PTR_ERR(v9ses->clnt);
...@@ -281,10 +278,20 @@ struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses, ...@@ -281,10 +278,20 @@ struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses,
goto error; goto error;
} }
if (p9_is_proto_dotl(v9ses->clnt)) v9ses->flags = V9FS_ACCESS_USER;
if (p9_is_proto_dotl(v9ses->clnt)) {
v9ses->flags = V9FS_ACCESS_CLIENT;
v9ses->flags |= V9FS_PROTO_2000L; v9ses->flags |= V9FS_PROTO_2000L;
else if (p9_is_proto_dotu(v9ses->clnt)) } else if (p9_is_proto_dotu(v9ses->clnt)) {
v9ses->flags |= V9FS_PROTO_2000U; v9ses->flags |= V9FS_PROTO_2000U;
}
rc = v9fs_parse_options(v9ses, data);
if (rc < 0) {
retval = rc;
goto error;
}
v9ses->maxdata = v9ses->clnt->msize - P9_IOHDRSZ; v9ses->maxdata = v9ses->clnt->msize - P9_IOHDRSZ;
...@@ -306,6 +313,14 @@ struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses, ...@@ -306,6 +313,14 @@ struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses,
v9ses->flags |= V9FS_ACCESS_ANY; v9ses->flags |= V9FS_ACCESS_ANY;
v9ses->uid = ~0; v9ses->uid = ~0;
} }
if (!v9fs_proto_dotl(v9ses) ||
!((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_CLIENT)) {
/*
* We support ACL checks on clinet only if the protocol is
* 9P2000.L and access is V9FS_ACCESS_CLIENT.
*/
v9ses->flags &= ~V9FS_ACL_MASK;
}
fid = p9_client_attach(v9ses->clnt, NULL, v9ses->uname, ~0, fid = p9_client_attach(v9ses->clnt, NULL, v9ses->uname, ~0,
v9ses->aname); v9ses->aname);
...@@ -467,6 +482,63 @@ static void v9fs_sysfs_cleanup(void) ...@@ -467,6 +482,63 @@ static void v9fs_sysfs_cleanup(void)
kobject_put(v9fs_kobj); kobject_put(v9fs_kobj);
} }
static void v9fs_inode_init_once(void *foo)
{
struct v9fs_inode *v9inode = (struct v9fs_inode *)foo;
#ifdef CONFIG_9P_FSCACHE
v9inode->fscache = NULL;
v9inode->fscache_key = NULL;
#endif
inode_init_once(&v9inode->vfs_inode);
}
/**
* v9fs_init_inode_cache - initialize a cache for 9P
* Returns 0 on success.
*/
static int v9fs_init_inode_cache(void)
{
v9fs_inode_cache = kmem_cache_create("v9fs_inode_cache",
sizeof(struct v9fs_inode),
0, (SLAB_RECLAIM_ACCOUNT|
SLAB_MEM_SPREAD),
v9fs_inode_init_once);
if (!v9fs_inode_cache)
return -ENOMEM;
return 0;
}
/**
* v9fs_destroy_inode_cache - destroy the cache of 9P inode
*
*/
static void v9fs_destroy_inode_cache(void)
{
kmem_cache_destroy(v9fs_inode_cache);
}
static int v9fs_cache_register(void)
{
int ret;
ret = v9fs_init_inode_cache();
if (ret < 0)
return ret;
#ifdef CONFIG_9P_FSCACHE
return fscache_register_netfs(&v9fs_cache_netfs);
#else
return ret;
#endif
}
static void v9fs_cache_unregister(void)
{
v9fs_destroy_inode_cache();
#ifdef CONFIG_9P_FSCACHE
fscache_unregister_netfs(&v9fs_cache_netfs);
#endif
}
/** /**
* init_v9fs - Initialize module * init_v9fs - Initialize module
* *
......
...@@ -20,6 +20,9 @@ ...@@ -20,6 +20,9 @@
* Boston, MA 02111-1301 USA * Boston, MA 02111-1301 USA
* *
*/ */
#ifndef FS_9P_V9FS_H
#define FS_9P_V9FS_H
#include <linux/backing-dev.h> #include <linux/backing-dev.h>
/** /**
...@@ -28,8 +31,10 @@ ...@@ -28,8 +31,10 @@
* @V9FS_PROTO_2000L: whether or not to use 9P2000.l extensions * @V9FS_PROTO_2000L: whether or not to use 9P2000.l extensions
* @V9FS_ACCESS_SINGLE: only the mounting user can access the hierarchy * @V9FS_ACCESS_SINGLE: only the mounting user can access the hierarchy
* @V9FS_ACCESS_USER: a new attach will be issued for every user (default) * @V9FS_ACCESS_USER: a new attach will be issued for every user (default)
* @V9FS_ACCESS_CLIENT: Just like user, but access check is performed on client.
* @V9FS_ACCESS_ANY: use a single attach for all users * @V9FS_ACCESS_ANY: use a single attach for all users
* @V9FS_ACCESS_MASK: bit mask of different ACCESS options * @V9FS_ACCESS_MASK: bit mask of different ACCESS options
* @V9FS_POSIX_ACL: POSIX ACLs are enforced
* *
* Session flags reflect options selected by users at mount time * Session flags reflect options selected by users at mount time
*/ */
...@@ -37,13 +42,15 @@ ...@@ -37,13 +42,15 @@
V9FS_ACCESS_USER | \ V9FS_ACCESS_USER | \
V9FS_ACCESS_CLIENT) V9FS_ACCESS_CLIENT)
#define V9FS_ACCESS_MASK V9FS_ACCESS_ANY #define V9FS_ACCESS_MASK V9FS_ACCESS_ANY
#define V9FS_ACL_MASK V9FS_POSIX_ACL
enum p9_session_flags { enum p9_session_flags {
V9FS_PROTO_2000U = 0x01, V9FS_PROTO_2000U = 0x01,
V9FS_PROTO_2000L = 0x02, V9FS_PROTO_2000L = 0x02,
V9FS_ACCESS_SINGLE = 0x04, V9FS_ACCESS_SINGLE = 0x04,
V9FS_ACCESS_USER = 0x08, V9FS_ACCESS_USER = 0x08,
V9FS_ACCESS_CLIENT = 0x10 V9FS_ACCESS_CLIENT = 0x10,
V9FS_POSIX_ACL = 0x20
}; };
/* possible values of ->cache */ /* possible values of ->cache */
...@@ -109,8 +116,28 @@ struct v9fs_session_info { ...@@ -109,8 +116,28 @@ struct v9fs_session_info {
struct list_head slist; /* list of sessions registered with v9fs */ struct list_head slist; /* list of sessions registered with v9fs */
struct backing_dev_info bdi; struct backing_dev_info bdi;
struct rw_semaphore rename_sem; struct rw_semaphore rename_sem;
struct p9_fid *root_fid; /* Used for file system sync */
};
/* cache_validity flags */
#define V9FS_INO_INVALID_ATTR 0x01
struct v9fs_inode {
#ifdef CONFIG_9P_FSCACHE
spinlock_t fscache_lock;
struct fscache_cookie *fscache;
struct p9_qid *fscache_key;
#endif
unsigned int cache_validity;
struct p9_fid *writeback_fid;
struct inode vfs_inode;
}; };
static inline struct v9fs_inode *V9FS_I(const struct inode *inode)
{
return container_of(inode, struct v9fs_inode, vfs_inode);
}
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 *);
extern void v9fs_session_close(struct v9fs_session_info *v9ses); extern void v9fs_session_close(struct v9fs_session_info *v9ses);
...@@ -124,14 +151,13 @@ extern int v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -124,14 +151,13 @@ extern int v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry); struct inode *new_dir, struct dentry *new_dentry);
extern void v9fs_vfs_put_link(struct dentry *dentry, struct nameidata *nd, extern void v9fs_vfs_put_link(struct dentry *dentry, struct nameidata *nd,
void *p); void *p);
extern struct inode *v9fs_inode(struct v9fs_session_info *v9ses, extern struct inode *v9fs_inode_from_fid(struct v9fs_session_info *v9ses,
struct p9_fid *fid, struct p9_fid *fid,
struct super_block *sb); struct super_block *sb);
extern const struct inode_operations v9fs_dir_inode_operations_dotl; extern const struct inode_operations v9fs_dir_inode_operations_dotl;
extern const struct inode_operations v9fs_file_inode_operations_dotl; extern const struct inode_operations v9fs_file_inode_operations_dotl;
extern const struct inode_operations v9fs_symlink_inode_operations_dotl; extern const struct inode_operations v9fs_symlink_inode_operations_dotl;
extern struct inode *v9fs_inode_dotl(struct v9fs_session_info *v9ses, extern struct inode *v9fs_inode_from_fid_dotl(struct v9fs_session_info *v9ses,
struct p9_fid *fid, struct p9_fid *fid,
struct super_block *sb); struct super_block *sb);
...@@ -158,7 +184,7 @@ static inline int v9fs_proto_dotl(struct v9fs_session_info *v9ses) ...@@ -158,7 +184,7 @@ static inline int v9fs_proto_dotl(struct v9fs_session_info *v9ses)
} }
/** /**
* v9fs_inode_from_fid - Helper routine to populate an inode by * v9fs_get_inode_from_fid - Helper routine to populate an inode by
* issuing a attribute request * issuing a attribute request
* @v9ses: session information * @v9ses: session information
* @fid: fid to issue attribute request for * @fid: fid to issue attribute request for
...@@ -166,11 +192,12 @@ static inline int v9fs_proto_dotl(struct v9fs_session_info *v9ses) ...@@ -166,11 +192,12 @@ static inline int v9fs_proto_dotl(struct v9fs_session_info *v9ses)
* *
*/ */
static inline struct inode * static inline struct inode *
v9fs_inode_from_fid(struct v9fs_session_info *v9ses, struct p9_fid *fid, v9fs_get_inode_from_fid(struct v9fs_session_info *v9ses, struct p9_fid *fid,
struct super_block *sb) struct super_block *sb)
{ {
if (v9fs_proto_dotl(v9ses)) if (v9fs_proto_dotl(v9ses))
return v9fs_inode_dotl(v9ses, fid, sb); return v9fs_inode_from_fid_dotl(v9ses, fid, sb);
else else
return v9fs_inode(v9ses, fid, sb); return v9fs_inode_from_fid(v9ses, fid, sb);
} }
#endif
...@@ -20,6 +20,8 @@ ...@@ -20,6 +20,8 @@
* Boston, MA 02111-1301 USA * Boston, MA 02111-1301 USA
* *
*/ */
#ifndef FS_9P_V9FS_VFS_H
#define FS_9P_V9FS_VFS_H
/* plan9 semantics are that created files are implicitly opened. /* plan9 semantics are that created files are implicitly opened.
* But linux semantics are that you call create, then open. * But linux semantics are that you call create, then open.
...@@ -36,6 +38,7 @@ ...@@ -36,6 +38,7 @@
* unlink calls remove, which is an implicit clunk. So we have to track * unlink calls remove, which is an implicit clunk. So we have to track
* that kind of thing so that we don't try to clunk a dead fid. * that kind of thing so that we don't try to clunk a dead fid.
*/ */
#define P9_LOCK_TIMEOUT (30*HZ)
extern struct file_system_type v9fs_fs_type; extern struct file_system_type v9fs_fs_type;
extern const struct address_space_operations v9fs_addr_operations; extern const struct address_space_operations v9fs_addr_operations;
...@@ -45,13 +48,15 @@ extern const struct file_operations v9fs_dir_operations; ...@@ -45,13 +48,15 @@ extern const struct file_operations v9fs_dir_operations;
extern const struct file_operations v9fs_dir_operations_dotl; extern const struct file_operations v9fs_dir_operations_dotl;
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;
extern const struct file_operations v9fs_cached_file_operations;
extern const struct file_operations v9fs_cached_file_operations_dotl;
extern struct kmem_cache *v9fs_inode_cache;
#ifdef CONFIG_9P_FSCACHE
struct inode *v9fs_alloc_inode(struct super_block *sb); struct inode *v9fs_alloc_inode(struct super_block *sb);
void v9fs_destroy_inode(struct inode *inode); 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);
int v9fs_init_inode(struct v9fs_session_info *v9ses,
struct inode *inode, int mode);
void v9fs_evict_inode(struct inode *inode); void v9fs_evict_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 *);
...@@ -62,8 +67,19 @@ void v9fs_inode2stat(struct inode *inode, struct p9_wstat *stat); ...@@ -62,8 +67,19 @@ void v9fs_inode2stat(struct inode *inode, struct p9_wstat *stat);
int v9fs_uflags2omode(int uflags, int extended); int v9fs_uflags2omode(int uflags, int extended);
ssize_t v9fs_file_readn(struct file *, char *, char __user *, u32, u64); ssize_t v9fs_file_readn(struct file *, char *, char __user *, u32, u64);
ssize_t v9fs_fid_readn(struct p9_fid *, char *, char __user *, u32, u64);
void v9fs_blank_wstat(struct p9_wstat *wstat); void v9fs_blank_wstat(struct p9_wstat *wstat);
int v9fs_vfs_setattr_dotl(struct dentry *, struct iattr *); int v9fs_vfs_setattr_dotl(struct dentry *, struct iattr *);
int v9fs_file_fsync_dotl(struct file *filp, int datasync); int v9fs_file_fsync_dotl(struct file *filp, int datasync);
ssize_t v9fs_file_write_internal(struct inode *, struct p9_fid *,
#define P9_LOCK_TIMEOUT (30*HZ) const char __user *, size_t, loff_t *, int);
int v9fs_refresh_inode(struct p9_fid *fid, struct inode *inode);
int v9fs_refresh_inode_dotl(struct p9_fid *fid, struct inode *inode);
static inline void v9fs_invalidate_inode_attr(struct inode *inode)
{
struct v9fs_inode *v9inode;
v9inode = V9FS_I(inode);
v9inode->cache_validity |= V9FS_INO_INVALID_ATTR;
return;
}
#endif
...@@ -39,16 +39,16 @@ ...@@ -39,16 +39,16 @@
#include "v9fs.h" #include "v9fs.h"
#include "v9fs_vfs.h" #include "v9fs_vfs.h"
#include "cache.h" #include "cache.h"
#include "fid.h"
/** /**
* v9fs_vfs_readpage - read an entire page in from 9P * v9fs_fid_readpage - read an entire page in from 9P
* *
* @filp: file being read * @fid: fid being read
* @page: structure to page * @page: structure to page
* *
*/ */
static int v9fs_fid_readpage(struct p9_fid *fid, struct page *page)
static int v9fs_vfs_readpage(struct file *filp, struct page *page)
{ {
int retval; int retval;
loff_t offset; loff_t offset;
...@@ -67,7 +67,7 @@ static int v9fs_vfs_readpage(struct file *filp, struct page *page) ...@@ -67,7 +67,7 @@ static int v9fs_vfs_readpage(struct file *filp, struct page *page)
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_fid_readn(fid, buffer, NULL, PAGE_CACHE_SIZE, offset);
if (retval < 0) { if (retval < 0) {
v9fs_uncache_page(inode, page); v9fs_uncache_page(inode, page);
goto done; goto done;
...@@ -86,6 +86,19 @@ static int v9fs_vfs_readpage(struct file *filp, struct page *page) ...@@ -86,6 +86,19 @@ static int v9fs_vfs_readpage(struct file *filp, struct page *page)
return retval; return retval;
} }
/**
* v9fs_vfs_readpage - read an entire page in from 9P
*
* @filp: file being read
* @page: structure to page
*
*/
static int v9fs_vfs_readpage(struct file *filp, struct page *page)
{
return v9fs_fid_readpage(filp->private_data, page);
}
/** /**
* v9fs_vfs_readpages - read a set of pages from 9P * v9fs_vfs_readpages - read a set of pages from 9P
* *
...@@ -124,7 +137,6 @@ static int v9fs_release_page(struct page *page, gfp_t gfp) ...@@ -124,7 +137,6 @@ static int v9fs_release_page(struct page *page, gfp_t gfp)
{ {
if (PagePrivate(page)) if (PagePrivate(page))
return 0; return 0;
return v9fs_fscache_release_page(page, gfp); return v9fs_fscache_release_page(page, gfp);
} }
...@@ -137,20 +149,89 @@ static int v9fs_release_page(struct page *page, gfp_t gfp) ...@@ -137,20 +149,89 @@ static int v9fs_release_page(struct page *page, gfp_t gfp)
static void v9fs_invalidate_page(struct page *page, unsigned long offset) static void v9fs_invalidate_page(struct page *page, unsigned long offset)
{ {
/*
* If called with zero offset, we should release
* the private state assocated with the page
*/
if (offset == 0) if (offset == 0)
v9fs_fscache_invalidate_page(page); v9fs_fscache_invalidate_page(page);
} }
static int v9fs_vfs_writepage_locked(struct page *page)
{
char *buffer;
int retval, len;
loff_t offset, size;
mm_segment_t old_fs;
struct v9fs_inode *v9inode;
struct inode *inode = page->mapping->host;
v9inode = V9FS_I(inode);
size = i_size_read(inode);
if (page->index == size >> PAGE_CACHE_SHIFT)
len = size & ~PAGE_CACHE_MASK;
else
len = PAGE_CACHE_SIZE;
set_page_writeback(page);
buffer = kmap(page);
offset = page_offset(page);
old_fs = get_fs();
set_fs(get_ds());
/* We should have writeback_fid always set */
BUG_ON(!v9inode->writeback_fid);
retval = v9fs_file_write_internal(inode,
v9inode->writeback_fid,
(__force const char __user *)buffer,
len, &offset, 0);
if (retval > 0)
retval = 0;
set_fs(old_fs);
kunmap(page);
end_page_writeback(page);
return retval;
}
static int v9fs_vfs_writepage(struct page *page, struct writeback_control *wbc)
{
int retval;
retval = v9fs_vfs_writepage_locked(page);
if (retval < 0) {
if (retval == -EAGAIN) {
redirty_page_for_writepage(wbc, page);
retval = 0;
} else {
SetPageError(page);
mapping_set_error(page->mapping, retval);
}
} else
retval = 0;
unlock_page(page);
return retval;
}
/** /**
* v9fs_launder_page - Writeback a dirty 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. * Returns 0 on success.
*/ */
static int v9fs_launder_page(struct page *page) static int v9fs_launder_page(struct page *page)
{ {
int retval;
struct inode *inode = page->mapping->host;
v9fs_fscache_wait_on_page_write(inode, page);
if (clear_page_dirty_for_io(page)) {
retval = v9fs_vfs_writepage_locked(page);
if (retval)
return retval;
}
return 0; return 0;
} }
...@@ -173,9 +254,15 @@ static int v9fs_launder_page(struct page *page) ...@@ -173,9 +254,15 @@ static int v9fs_launder_page(struct page *page)
* with an error. * with an error.
* *
*/ */
ssize_t v9fs_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, static ssize_t
v9fs_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
loff_t pos, unsigned long nr_segs) loff_t pos, unsigned long nr_segs)
{ {
/*
* FIXME
* Now that we do caching with cache mode enabled, We need
* to support direct IO
*/
P9_DPRINTK(P9_DEBUG_VFS, "v9fs_direct_IO: v9fs_direct_IO (%s) " P9_DPRINTK(P9_DEBUG_VFS, "v9fs_direct_IO: v9fs_direct_IO (%s) "
"off/no(%lld/%lu) EINVAL\n", "off/no(%lld/%lu) EINVAL\n",
iocb->ki_filp->f_path.dentry->d_name.name, iocb->ki_filp->f_path.dentry->d_name.name,
...@@ -183,9 +270,82 @@ ssize_t v9fs_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, ...@@ -183,9 +270,82 @@ ssize_t v9fs_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
return -EINVAL; return -EINVAL;
} }
static int v9fs_write_begin(struct file *filp, struct address_space *mapping,
loff_t pos, unsigned len, unsigned flags,
struct page **pagep, void **fsdata)
{
int retval = 0;
struct page *page;
struct v9fs_inode *v9inode;
pgoff_t index = pos >> PAGE_CACHE_SHIFT;
struct inode *inode = mapping->host;
v9inode = V9FS_I(inode);
start:
page = grab_cache_page_write_begin(mapping, index, flags);
if (!page) {
retval = -ENOMEM;
goto out;
}
BUG_ON(!v9inode->writeback_fid);
if (PageUptodate(page))
goto out;
if (len == PAGE_CACHE_SIZE)
goto out;
retval = v9fs_fid_readpage(v9inode->writeback_fid, page);
page_cache_release(page);
if (!retval)
goto start;
out:
*pagep = page;
return retval;
}
static int v9fs_write_end(struct file *filp, struct address_space *mapping,
loff_t pos, unsigned len, unsigned copied,
struct page *page, void *fsdata)
{
loff_t last_pos = pos + copied;
struct inode *inode = page->mapping->host;
if (unlikely(copied < len)) {
/*
* zero out the rest of the area
*/
unsigned from = pos & (PAGE_CACHE_SIZE - 1);
zero_user(page, from + copied, len - copied);
flush_dcache_page(page);
}
if (!PageUptodate(page))
SetPageUptodate(page);
/*
* No need to use i_size_read() here, the i_size
* cannot change under us because we hold the i_mutex.
*/
if (last_pos > inode->i_size) {
inode_add_bytes(inode, last_pos - inode->i_size);
i_size_write(inode, last_pos);
}
set_page_dirty(page);
unlock_page(page);
page_cache_release(page);
return copied;
}
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, .readpages = v9fs_vfs_readpages,
.set_page_dirty = __set_page_dirty_nobuffers,
.writepage = v9fs_vfs_writepage,
.write_begin = v9fs_write_begin,
.write_end = v9fs_write_end,
.releasepage = v9fs_release_page, .releasepage = v9fs_release_page,
.invalidatepage = v9fs_invalidate_page, .invalidatepage = v9fs_invalidate_page,
.launder_page = v9fs_launder_page, .launder_page = v9fs_launder_page,
......
...@@ -63,20 +63,15 @@ static int v9fs_dentry_delete(const struct dentry *dentry) ...@@ -63,20 +63,15 @@ static int v9fs_dentry_delete(const struct dentry *dentry)
* v9fs_cached_dentry_delete - called when dentry refcount equals 0 * v9fs_cached_dentry_delete - called when dentry refcount equals 0
* @dentry: dentry in question * @dentry: dentry in question
* *
* Only return 1 if our inode is invalid. Only non-synthetic files
* (ones without mtime == 0) should be calling this function.
*
*/ */
static int v9fs_cached_dentry_delete(const struct dentry *dentry) static int v9fs_cached_dentry_delete(const struct dentry *dentry)
{ {
struct inode *inode = dentry->d_inode; P9_DPRINTK(P9_DEBUG_VFS, " dentry: %s (%p)\n",
P9_DPRINTK(P9_DEBUG_VFS, " dentry: %s (%p)\n", dentry->d_name.name, dentry->d_name.name, dentry);
dentry);
if(!inode) /* Don't cache negative dentries */
if (!dentry->d_inode)
return 1; return 1;
return 0; return 0;
} }
...@@ -105,7 +100,41 @@ static void v9fs_dentry_release(struct dentry *dentry) ...@@ -105,7 +100,41 @@ static void v9fs_dentry_release(struct dentry *dentry)
} }
} }
static int v9fs_lookup_revalidate(struct dentry *dentry, struct nameidata *nd)
{
struct p9_fid *fid;
struct inode *inode;
struct v9fs_inode *v9inode;
if (nd->flags & LOOKUP_RCU)
return -ECHILD;
inode = dentry->d_inode;
if (!inode)
goto out_valid;
v9inode = V9FS_I(inode);
if (v9inode->cache_validity & V9FS_INO_INVALID_ATTR) {
int retval;
struct v9fs_session_info *v9ses;
fid = v9fs_fid_lookup(dentry);
if (IS_ERR(fid))
return PTR_ERR(fid);
v9ses = v9fs_inode2v9ses(inode);
if (v9fs_proto_dotl(v9ses))
retval = v9fs_refresh_inode_dotl(fid, inode);
else
retval = v9fs_refresh_inode(fid, inode);
if (retval <= 0)
return retval;
}
out_valid:
return 1;
}
const struct dentry_operations v9fs_cached_dentry_operations = { const struct dentry_operations v9fs_cached_dentry_operations = {
.d_revalidate = v9fs_lookup_revalidate,
.d_delete = v9fs_cached_dentry_delete, .d_delete = v9fs_cached_dentry_delete,
.d_release = v9fs_dentry_release, .d_release = v9fs_dentry_release,
}; };
......
...@@ -295,7 +295,6 @@ int v9fs_dir_release(struct inode *inode, struct file *filp) ...@@ -295,7 +295,6 @@ int v9fs_dir_release(struct inode *inode, struct file *filp)
P9_DPRINTK(P9_DEBUG_VFS, P9_DPRINTK(P9_DEBUG_VFS,
"v9fs_dir_release: inode: %p filp: %p fid: %d\n", "v9fs_dir_release: inode: %p filp: %p fid: %d\n",
inode, filp, fid ? fid->fid : -1); inode, filp, fid ? fid->fid : -1);
filemap_write_and_wait(inode->i_mapping);
if (fid) if (fid)
p9_client_clunk(fid); p9_client_clunk(fid);
return 0; return 0;
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -86,12 +86,15 @@ v9fs_fill_super(struct super_block *sb, struct v9fs_session_info *v9ses, ...@@ -86,12 +86,15 @@ v9fs_fill_super(struct super_block *sb, struct v9fs_session_info *v9ses,
} else } else
sb->s_op = &v9fs_super_ops; sb->s_op = &v9fs_super_ops;
sb->s_bdi = &v9ses->bdi; sb->s_bdi = &v9ses->bdi;
if (v9ses->cache)
sb->s_bdi->ra_pages = (VM_MAX_READAHEAD * 1024)/PAGE_CACHE_SIZE;
sb->s_flags = flags | MS_ACTIVE | MS_SYNCHRONOUS | MS_DIRSYNC | sb->s_flags = flags | MS_ACTIVE | MS_DIRSYNC | MS_NOATIME;
MS_NOATIME; if (!v9ses->cache)
sb->s_flags |= MS_SYNCHRONOUS;
#ifdef CONFIG_9P_FS_POSIX_ACL #ifdef CONFIG_9P_FS_POSIX_ACL
if ((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_CLIENT) if ((v9ses->flags & V9FS_ACL_MASK) == V9FS_POSIX_ACL)
sb->s_flags |= MS_POSIXACL; sb->s_flags |= MS_POSIXACL;
#endif #endif
...@@ -151,7 +154,6 @@ static struct dentry *v9fs_mount(struct file_system_type *fs_type, int flags, ...@@ -151,7 +154,6 @@ static struct dentry *v9fs_mount(struct file_system_type *fs_type, int flags,
retval = PTR_ERR(inode); retval = PTR_ERR(inode);
goto release_sb; goto release_sb;
} }
root = d_alloc_root(inode); root = d_alloc_root(inode);
if (!root) { if (!root) {
iput(inode); iput(inode);
...@@ -166,7 +168,7 @@ static struct dentry *v9fs_mount(struct file_system_type *fs_type, int flags, ...@@ -166,7 +168,7 @@ static struct dentry *v9fs_mount(struct file_system_type *fs_type, int flags,
retval = PTR_ERR(st); retval = PTR_ERR(st);
goto release_sb; goto release_sb;
} }
root->d_inode->i_ino = v9fs_qid2ino(&st->qid);
v9fs_stat2inode_dotl(st, root->d_inode); v9fs_stat2inode_dotl(st, root->d_inode);
kfree(st); kfree(st);
} else { } else {
...@@ -183,10 +185,21 @@ static struct dentry *v9fs_mount(struct file_system_type *fs_type, int flags, ...@@ -183,10 +185,21 @@ static struct dentry *v9fs_mount(struct file_system_type *fs_type, int flags,
p9stat_free(st); p9stat_free(st);
kfree(st); kfree(st);
} }
v9fs_fid_add(root, fid);
retval = v9fs_get_acl(inode, fid); retval = v9fs_get_acl(inode, fid);
if (retval) if (retval)
goto release_sb; goto release_sb;
v9fs_fid_add(root, fid); /*
* Add the root fid to session info. This is used
* for file system sync. We want a cloned fid here
* so that we can do a sync_filesystem after a
* shrink_dcache_for_umount
*/
v9ses->root_fid = v9fs_fid_clone(root);
if (IS_ERR(v9ses->root_fid)) {
retval = PTR_ERR(v9ses->root_fid);
goto release_sb;
}
P9_DPRINTK(P9_DEBUG_VFS, " simple set mount, return 0\n"); P9_DPRINTK(P9_DEBUG_VFS, " simple set mount, return 0\n");
return dget(sb->s_root); return dget(sb->s_root);
...@@ -197,15 +210,11 @@ static struct dentry *v9fs_mount(struct file_system_type *fs_type, int flags, ...@@ -197,15 +210,11 @@ static struct dentry *v9fs_mount(struct file_system_type *fs_type, int flags,
v9fs_session_close(v9ses); v9fs_session_close(v9ses);
kfree(v9ses); kfree(v9ses);
return ERR_PTR(retval); return ERR_PTR(retval);
release_sb: release_sb:
/* /*
* we will do the session_close and root dentry release * we will do the session_close and root dentry
* in the below call. But we need to clunk fid, because we haven't * release in the below call.
* attached the fid to dentry so it won't get clunked
* automatically.
*/ */
p9_client_clunk(fid);
deactivate_locked_super(sb); deactivate_locked_super(sb);
return ERR_PTR(retval); return ERR_PTR(retval);
} }
...@@ -223,7 +232,7 @@ static void v9fs_kill_super(struct super_block *s) ...@@ -223,7 +232,7 @@ static void v9fs_kill_super(struct super_block *s)
P9_DPRINTK(P9_DEBUG_VFS, " %p\n", s); P9_DPRINTK(P9_DEBUG_VFS, " %p\n", s);
kill_anon_super(s); kill_anon_super(s);
p9_client_clunk(v9ses->root_fid);
v9fs_session_cancel(v9ses); v9fs_session_cancel(v9ses);
v9fs_session_close(v9ses); v9fs_session_close(v9ses);
kfree(v9ses); kfree(v9ses);
...@@ -276,11 +285,31 @@ static int v9fs_statfs(struct dentry *dentry, struct kstatfs *buf) ...@@ -276,11 +285,31 @@ static int v9fs_statfs(struct dentry *dentry, struct kstatfs *buf)
return res; return res;
} }
static int v9fs_sync_fs(struct super_block *sb, int wait)
{
struct v9fs_session_info *v9ses = sb->s_fs_info;
P9_DPRINTK(P9_DEBUG_VFS, "v9fs_sync_fs: super_block %p\n", sb);
return p9_client_sync_fs(v9ses->root_fid);
}
static int v9fs_drop_inode(struct inode *inode)
{
struct v9fs_session_info *v9ses;
v9ses = v9fs_inode2v9ses(inode);
if (v9ses->cache)
return generic_drop_inode(inode);
/*
* in case of non cached mode always drop the
* the inode because we want the inode attribute
* to always match that on the server.
*/
return 1;
}
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, .alloc_inode = v9fs_alloc_inode,
.destroy_inode = v9fs_destroy_inode, .destroy_inode = v9fs_destroy_inode,
#endif
.statfs = simple_statfs, .statfs = simple_statfs,
.evict_inode = v9fs_evict_inode, .evict_inode = v9fs_evict_inode,
.show_options = generic_show_options, .show_options = generic_show_options,
...@@ -288,11 +317,11 @@ static const struct super_operations v9fs_super_ops = { ...@@ -288,11 +317,11 @@ static const struct super_operations v9fs_super_ops = {
}; };
static const struct super_operations v9fs_super_ops_dotl = { static const struct super_operations v9fs_super_ops_dotl = {
#ifdef CONFIG_9P_FSCACHE
.alloc_inode = v9fs_alloc_inode, .alloc_inode = v9fs_alloc_inode,
.destroy_inode = v9fs_destroy_inode, .destroy_inode = v9fs_destroy_inode,
#endif .sync_fs = v9fs_sync_fs,
.statfs = v9fs_statfs, .statfs = v9fs_statfs,
.drop_inode = v9fs_drop_inode,
.evict_inode = v9fs_evict_inode, .evict_inode = v9fs_evict_inode,
.show_options = generic_show_options, .show_options = generic_show_options,
.umount_begin = v9fs_umount_begin, .umount_begin = v9fs_umount_begin,
...@@ -303,5 +332,5 @@ struct file_system_type v9fs_fs_type = { ...@@ -303,5 +332,5 @@ struct file_system_type v9fs_fs_type = {
.mount = v9fs_mount, .mount = v9fs_mount,
.kill_sb = v9fs_kill_super, .kill_sb = v9fs_kill_super,
.owner = THIS_MODULE, .owner = THIS_MODULE,
.fs_flags = FS_RENAME_DOES_D_MOVE, .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT,
}; };
...@@ -139,6 +139,8 @@ do { \ ...@@ -139,6 +139,8 @@ do { \
*/ */
enum p9_msg_t { enum p9_msg_t {
P9_TSYNCFS = 0,
P9_RSYNCFS,
P9_TLERROR = 6, P9_TLERROR = 6,
P9_RLERROR, P9_RLERROR,
P9_TSTATFS = 8, P9_TSTATFS = 8,
...@@ -688,7 +690,11 @@ struct p9_rwstat { ...@@ -688,7 +690,11 @@ struct p9_rwstat {
* @id: protocol operating identifier of type &p9_msg_t * @id: protocol operating identifier of type &p9_msg_t
* @tag: transaction id of the request * @tag: transaction id of the request
* @offset: used by marshalling routines to track currentposition in buffer * @offset: used by marshalling routines to track currentposition in buffer
* @capacity: used by marshalling routines to track total capacity * @capacity: used by marshalling routines to track total malloc'd capacity
* @pubuf: Payload user buffer given by the caller
* @pubuf: Payload kernel buffer given by the caller
* @pbuf_size: pubuf/pkbuf(only one will be !NULL) size to be read/write.
* @private: For transport layer's use.
* @sdata: payload * @sdata: payload
* *
* &p9_fcall represents the structure for all 9P RPC * &p9_fcall represents the structure for all 9P RPC
...@@ -705,6 +711,10 @@ struct p9_fcall { ...@@ -705,6 +711,10 @@ struct p9_fcall {
size_t offset; size_t offset;
size_t capacity; size_t capacity;
char __user *pubuf;
char *pkbuf;
size_t pbuf_size;
void *private;
uint8_t *sdata; uint8_t *sdata;
}; };
......
...@@ -230,6 +230,7 @@ int p9_client_create_dotl(struct p9_fid *ofid, char *name, u32 flags, u32 mode, ...@@ -230,6 +230,7 @@ int p9_client_create_dotl(struct p9_fid *ofid, char *name, u32 flags, u32 mode,
gid_t gid, struct p9_qid *qid); gid_t gid, struct p9_qid *qid);
int p9_client_clunk(struct p9_fid *fid); int p9_client_clunk(struct p9_fid *fid);
int p9_client_fsync(struct p9_fid *fid, int datasync); int p9_client_fsync(struct p9_fid *fid, int datasync);
int p9_client_sync_fs(struct p9_fid *fid);
int p9_client_remove(struct p9_fid *fid); int p9_client_remove(struct p9_fid *fid);
int p9_client_read(struct p9_fid *fid, char *data, char __user *udata, int p9_client_read(struct p9_fid *fid, char *data, char __user *udata,
u64 offset, u32 count); u64 offset, u32 count);
......
...@@ -26,11 +26,19 @@ ...@@ -26,11 +26,19 @@
#ifndef NET_9P_TRANSPORT_H #ifndef NET_9P_TRANSPORT_H
#define NET_9P_TRANSPORT_H #define NET_9P_TRANSPORT_H
#define P9_TRANS_PREF_PAYLOAD_MASK 0x1
/* Default. Add Payload to PDU before sending it down to transport layer */
#define P9_TRANS_PREF_PAYLOAD_DEF 0x0
/* Send pay load seperately to transport layer along with PDU.*/
#define P9_TRANS_PREF_PAYLOAD_SEP 0x1
/** /**
* struct p9_trans_module - transport module interface * struct p9_trans_module - transport module interface
* @list: used to maintain a list of currently available transports * @list: used to maintain a list of currently available transports
* @name: the human-readable name of the transport * @name: the human-readable name of the transport
* @maxsize: transport provided maximum packet size * @maxsize: transport provided maximum packet size
* @pref: Preferences of this transport
* @def: set if this transport should be considered the default * @def: set if this transport should be considered the default
* @create: member function to create a new connection on this transport * @create: member function to create a new connection on this transport
* @request: member function to issue a request to the transport * @request: member function to issue a request to the transport
...@@ -47,6 +55,7 @@ struct p9_trans_module { ...@@ -47,6 +55,7 @@ struct p9_trans_module {
struct list_head list; struct list_head list;
char *name; /* name of transport */ char *name; /* name of transport */
int maxsize; /* max message size of transport */ int maxsize; /* max message size of transport */
int pref; /* Preferences of this transport */
int def; /* this transport should be default */ int def; /* this transport should be default */
struct module *owner; struct module *owner;
int (*create)(struct p9_client *, const char *, char *); int (*create)(struct p9_client *, const char *, char *);
......
...@@ -9,6 +9,7 @@ obj-$(CONFIG_NET_9P_RDMA) += 9pnet_rdma.o ...@@ -9,6 +9,7 @@ obj-$(CONFIG_NET_9P_RDMA) += 9pnet_rdma.o
util.o \ util.o \
protocol.o \ protocol.o \
trans_fd.o \ trans_fd.o \
trans_common.o \
9pnet_virtio-objs := \ 9pnet_virtio-objs := \
trans_virtio.o \ trans_virtio.o \
......
...@@ -229,10 +229,23 @@ static struct p9_req_t *p9_tag_alloc(struct p9_client *c, u16 tag) ...@@ -229,10 +229,23 @@ static struct p9_req_t *p9_tag_alloc(struct p9_client *c, u16 tag)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
} }
init_waitqueue_head(req->wq); init_waitqueue_head(req->wq);
if ((c->trans_mod->pref & P9_TRANS_PREF_PAYLOAD_MASK) ==
P9_TRANS_PREF_PAYLOAD_SEP) {
int alloc_msize = min(c->msize, 4096);
req->tc = kmalloc(sizeof(struct p9_fcall)+alloc_msize,
GFP_KERNEL);
req->tc->capacity = alloc_msize;
req->rc = kmalloc(sizeof(struct p9_fcall)+alloc_msize,
GFP_KERNEL);
req->rc->capacity = alloc_msize;
} else {
req->tc = kmalloc(sizeof(struct p9_fcall)+c->msize, req->tc = kmalloc(sizeof(struct p9_fcall)+c->msize,
GFP_KERNEL); GFP_KERNEL);
req->tc->capacity = c->msize;
req->rc = kmalloc(sizeof(struct p9_fcall)+c->msize, req->rc = kmalloc(sizeof(struct p9_fcall)+c->msize,
GFP_KERNEL); GFP_KERNEL);
req->rc->capacity = c->msize;
}
if ((!req->tc) || (!req->rc)) { if ((!req->tc) || (!req->rc)) {
printk(KERN_ERR "Couldn't grow tag array\n"); printk(KERN_ERR "Couldn't grow tag array\n");
kfree(req->tc); kfree(req->tc);
...@@ -243,9 +256,7 @@ static struct p9_req_t *p9_tag_alloc(struct p9_client *c, u16 tag) ...@@ -243,9 +256,7 @@ static struct p9_req_t *p9_tag_alloc(struct p9_client *c, u16 tag)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
} }
req->tc->sdata = (char *) req->tc + sizeof(struct p9_fcall); req->tc->sdata = (char *) req->tc + sizeof(struct p9_fcall);
req->tc->capacity = c->msize;
req->rc->sdata = (char *) req->rc + sizeof(struct p9_fcall); req->rc->sdata = (char *) req->rc + sizeof(struct p9_fcall);
req->rc->capacity = c->msize;
} }
p9pdu_reset(req->tc); p9pdu_reset(req->tc);
...@@ -443,6 +454,7 @@ static int p9_check_errors(struct p9_client *c, struct p9_req_t *req) ...@@ -443,6 +454,7 @@ static int p9_check_errors(struct p9_client *c, struct p9_req_t *req)
{ {
int8_t type; int8_t type;
int err; int err;
int ecode;
err = p9_parse_header(req->rc, NULL, &type, NULL, 0); err = p9_parse_header(req->rc, NULL, &type, NULL, 0);
if (err) { if (err) {
...@@ -450,12 +462,30 @@ static int p9_check_errors(struct p9_client *c, struct p9_req_t *req) ...@@ -450,12 +462,30 @@ static int p9_check_errors(struct p9_client *c, struct p9_req_t *req)
return err; return err;
} }
if (type == P9_RERROR || type == P9_RLERROR) { if (type != P9_RERROR && type != P9_RLERROR)
int ecode; return 0;
if (!p9_is_proto_dotl(c)) { if (!p9_is_proto_dotl(c)) {
char *ename; char *ename;
if (req->tc->pbuf_size) {
/* Handle user buffers */
size_t len = req->rc->size - req->rc->offset;
if (req->tc->pubuf) {
/* User Buffer */
err = copy_from_user(
&req->rc->sdata[req->rc->offset],
req->tc->pubuf, len);
if (err) {
err = -EFAULT;
goto out_err;
}
} else {
/* Kernel Buffer */
memmove(&req->rc->sdata[req->rc->offset],
req->tc->pkbuf, len);
}
}
err = p9pdu_readf(req->rc, c->proto_version, "s?d", err = p9pdu_readf(req->rc, c->proto_version, "s?d",
&ename, &ecode); &ename, &ecode);
if (err) if (err)
...@@ -467,7 +497,8 @@ static int p9_check_errors(struct p9_client *c, struct p9_req_t *req) ...@@ -467,7 +497,8 @@ static int p9_check_errors(struct p9_client *c, struct p9_req_t *req)
if (!err || !IS_ERR_VALUE(err)) { if (!err || !IS_ERR_VALUE(err)) {
err = p9_errstr2errno(ename, strlen(ename)); err = p9_errstr2errno(ename, strlen(ename));
P9_DPRINTK(P9_DEBUG_9P, "<<< RERROR (%d) %s\n", -ecode, ename); P9_DPRINTK(P9_DEBUG_9P, "<<< RERROR (%d) %s\n", -ecode,
ename);
kfree(ename); kfree(ename);
} }
...@@ -478,8 +509,6 @@ static int p9_check_errors(struct p9_client *c, struct p9_req_t *req) ...@@ -478,8 +509,6 @@ static int p9_check_errors(struct p9_client *c, struct p9_req_t *req)
P9_DPRINTK(P9_DEBUG_9P, "<<< RLERROR (%d)\n", -ecode); P9_DPRINTK(P9_DEBUG_9P, "<<< RLERROR (%d)\n", -ecode);
} }
} else
err = 0;
return err; return err;
...@@ -1191,6 +1220,27 @@ int p9_client_fsync(struct p9_fid *fid, int datasync) ...@@ -1191,6 +1220,27 @@ int p9_client_fsync(struct p9_fid *fid, int datasync)
} }
EXPORT_SYMBOL(p9_client_fsync); EXPORT_SYMBOL(p9_client_fsync);
int p9_client_sync_fs(struct p9_fid *fid)
{
int err = 0;
struct p9_req_t *req;
struct p9_client *clnt;
P9_DPRINTK(P9_DEBUG_9P, ">>> TSYNC_FS fid %d\n", fid->fid);
clnt = fid->clnt;
req = p9_client_rpc(clnt, P9_TSYNCFS, "d", fid->fid);
if (IS_ERR(req)) {
err = PTR_ERR(req);
goto error;
}
P9_DPRINTK(P9_DEBUG_9P, "<<< RSYNCFS fid %d\n", fid->fid);
p9_free_req(clnt, req);
error:
return err;
}
EXPORT_SYMBOL(p9_client_sync_fs);
int p9_client_clunk(struct p9_fid *fid) int p9_client_clunk(struct p9_fid *fid)
{ {
int err; int err;
...@@ -1270,7 +1320,15 @@ p9_client_read(struct p9_fid *fid, char *data, char __user *udata, u64 offset, ...@@ -1270,7 +1320,15 @@ p9_client_read(struct p9_fid *fid, char *data, char __user *udata, u64 offset,
if (count < rsize) if (count < rsize)
rsize = count; rsize = count;
req = p9_client_rpc(clnt, P9_TREAD, "dqd", fid->fid, offset, rsize); /* Don't bother zerocopy form small IO (< 1024) */
if (((clnt->trans_mod->pref & P9_TRANS_PREF_PAYLOAD_MASK) ==
P9_TRANS_PREF_PAYLOAD_SEP) && (rsize > 1024)) {
req = p9_client_rpc(clnt, P9_TREAD, "dqE", fid->fid, offset,
rsize, data, udata);
} else {
req = p9_client_rpc(clnt, P9_TREAD, "dqd", fid->fid, offset,
rsize);
}
if (IS_ERR(req)) { if (IS_ERR(req)) {
err = PTR_ERR(req); err = PTR_ERR(req);
goto error; goto error;
...@@ -1284,6 +1342,7 @@ p9_client_read(struct p9_fid *fid, char *data, char __user *udata, u64 offset, ...@@ -1284,6 +1342,7 @@ p9_client_read(struct p9_fid *fid, char *data, char __user *udata, u64 offset,
P9_DPRINTK(P9_DEBUG_9P, "<<< RREAD count %d\n", count); P9_DPRINTK(P9_DEBUG_9P, "<<< RREAD count %d\n", count);
if (!req->tc->pbuf_size) {
if (data) { if (data) {
memmove(data, dataptr, count); memmove(data, dataptr, count);
} else { } else {
...@@ -1293,6 +1352,7 @@ p9_client_read(struct p9_fid *fid, char *data, char __user *udata, u64 offset, ...@@ -1293,6 +1352,7 @@ p9_client_read(struct p9_fid *fid, char *data, char __user *udata, u64 offset,
goto free_and_error; goto free_and_error;
} }
} }
}
p9_free_req(clnt, req); p9_free_req(clnt, req);
return count; return count;
...@@ -1323,12 +1383,21 @@ p9_client_write(struct p9_fid *fid, char *data, const char __user *udata, ...@@ -1323,12 +1383,21 @@ p9_client_write(struct p9_fid *fid, char *data, const char __user *udata,
if (count < rsize) if (count < rsize)
rsize = count; rsize = count;
/* Don't bother zerocopy form small IO (< 1024) */
if (((clnt->trans_mod->pref & P9_TRANS_PREF_PAYLOAD_MASK) ==
P9_TRANS_PREF_PAYLOAD_SEP) && (rsize > 1024)) {
req = p9_client_rpc(clnt, P9_TWRITE, "dqE", fid->fid, offset,
rsize, data, udata);
} else {
if (data) if (data)
req = p9_client_rpc(clnt, P9_TWRITE, "dqD", fid->fid, offset, req = p9_client_rpc(clnt, P9_TWRITE, "dqD", fid->fid,
rsize, data); offset, rsize, data);
else else
req = p9_client_rpc(clnt, P9_TWRITE, "dqU", fid->fid, offset, req = p9_client_rpc(clnt, P9_TWRITE, "dqU", fid->fid,
rsize, udata); offset, rsize, udata);
}
if (IS_ERR(req)) { if (IS_ERR(req)) {
err = PTR_ERR(req); err = PTR_ERR(req);
goto error; goto error;
...@@ -1716,7 +1785,14 @@ int p9_client_readdir(struct p9_fid *fid, char *data, u32 count, u64 offset) ...@@ -1716,7 +1785,14 @@ int p9_client_readdir(struct p9_fid *fid, char *data, u32 count, u64 offset)
if (count < rsize) if (count < rsize)
rsize = count; rsize = count;
req = p9_client_rpc(clnt, P9_TREADDIR, "dqd", fid->fid, offset, rsize); if ((clnt->trans_mod->pref & P9_TRANS_PREF_PAYLOAD_MASK) ==
P9_TRANS_PREF_PAYLOAD_SEP) {
req = p9_client_rpc(clnt, P9_TREADDIR, "dqF", fid->fid,
offset, rsize, data);
} else {
req = p9_client_rpc(clnt, P9_TREADDIR, "dqd", fid->fid,
offset, rsize);
}
if (IS_ERR(req)) { if (IS_ERR(req)) {
err = PTR_ERR(req); err = PTR_ERR(req);
goto error; goto error;
...@@ -1730,7 +1806,7 @@ int p9_client_readdir(struct p9_fid *fid, char *data, u32 count, u64 offset) ...@@ -1730,7 +1806,7 @@ int p9_client_readdir(struct p9_fid *fid, char *data, u32 count, u64 offset)
P9_DPRINTK(P9_DEBUG_9P, "<<< RREADDIR count %d\n", count); P9_DPRINTK(P9_DEBUG_9P, "<<< RREADDIR count %d\n", count);
if (data) if (!req->tc->pbuf_size && data)
memmove(data, dataptr, count); memmove(data, dataptr, count);
p9_free_req(clnt, req); p9_free_req(clnt, req);
......
...@@ -114,6 +114,26 @@ pdu_write_u(struct p9_fcall *pdu, const char __user *udata, size_t size) ...@@ -114,6 +114,26 @@ pdu_write_u(struct p9_fcall *pdu, const char __user *udata, size_t size)
return size - len; return size - len;
} }
static size_t
pdu_write_urw(struct p9_fcall *pdu, const char *kdata, const char __user *udata,
size_t size)
{
BUG_ON(pdu->size > P9_IOHDRSZ);
pdu->pubuf = (char __user *)udata;
pdu->pkbuf = (char *)kdata;
pdu->pbuf_size = size;
return 0;
}
static size_t
pdu_write_readdir(struct p9_fcall *pdu, const char *kdata, size_t size)
{
BUG_ON(pdu->size > P9_READDIRHDRSZ);
pdu->pkbuf = (char *)kdata;
pdu->pbuf_size = size;
return 0;
}
/* /*
b - int8_t b - int8_t
w - int16_t w - int16_t
...@@ -445,6 +465,25 @@ p9pdu_vwritef(struct p9_fcall *pdu, int proto_version, const char *fmt, ...@@ -445,6 +465,25 @@ p9pdu_vwritef(struct p9_fcall *pdu, int proto_version, const char *fmt,
errcode = -EFAULT; errcode = -EFAULT;
} }
break; break;
case 'E':{
int32_t cnt = va_arg(ap, int32_t);
const char *k = va_arg(ap, const void *);
const char *u = va_arg(ap, const void *);
errcode = p9pdu_writef(pdu, proto_version, "d",
cnt);
if (!errcode && pdu_write_urw(pdu, k, u, cnt))
errcode = -EFAULT;
}
break;
case 'F':{
int32_t cnt = va_arg(ap, int32_t);
const char *k = va_arg(ap, const void *);
errcode = p9pdu_writef(pdu, proto_version, "d",
cnt);
if (!errcode && pdu_write_readdir(pdu, k, cnt))
errcode = -EFAULT;
}
break;
case 'U':{ case 'U':{
int32_t count = va_arg(ap, int32_t); int32_t count = va_arg(ap, int32_t);
const char __user *udata = const char __user *udata =
...@@ -579,6 +618,7 @@ EXPORT_SYMBOL(p9stat_read); ...@@ -579,6 +618,7 @@ EXPORT_SYMBOL(p9stat_read);
int p9pdu_prepare(struct p9_fcall *pdu, int16_t tag, int8_t type) int p9pdu_prepare(struct p9_fcall *pdu, int16_t tag, int8_t type)
{ {
pdu->id = type;
return p9pdu_writef(pdu, 0, "dbw", 0, type, tag); return p9pdu_writef(pdu, 0, "dbw", 0, type, tag);
} }
...@@ -606,6 +646,10 @@ void p9pdu_reset(struct p9_fcall *pdu) ...@@ -606,6 +646,10 @@ void p9pdu_reset(struct p9_fcall *pdu)
{ {
pdu->offset = 0; pdu->offset = 0;
pdu->size = 0; pdu->size = 0;
pdu->private = NULL;
pdu->pubuf = NULL;
pdu->pkbuf = NULL;
pdu->pbuf_size = 0;
} }
int p9dirent_read(char *buf, int len, struct p9_dirent *dirent, int p9dirent_read(char *buf, int len, struct p9_dirent *dirent,
......
/*
* Copyright IBM Corporation, 2010
* Author Venkateswararao Jujjuri <jvrao@linux.vnet.ibm.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2.1 of the GNU Lesser General Public License
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it would be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
*/
#include <linux/slab.h>
#include <linux/module.h>
#include <net/9p/9p.h>
#include <net/9p/client.h>
#include <linux/scatterlist.h>
#include "trans_common.h"
/**
* p9_release_req_pages - Release pages after the transaction.
* @*private: PDU's private page of struct trans_rpage_info
*/
void
p9_release_req_pages(struct trans_rpage_info *rpinfo)
{
int i = 0;
while (rpinfo->rp_data[i] && rpinfo->rp_nr_pages--) {
put_page(rpinfo->rp_data[i]);
i++;
}
}
EXPORT_SYMBOL(p9_release_req_pages);
/**
* p9_nr_pages - Return number of pages needed to accomodate the payload.
*/
int
p9_nr_pages(struct p9_req_t *req)
{
int start_page, end_page;
start_page = (unsigned long long)req->tc->pubuf >> PAGE_SHIFT;
end_page = ((unsigned long long)req->tc->pubuf + req->tc->pbuf_size +
PAGE_SIZE - 1) >> PAGE_SHIFT;
return end_page - start_page;
}
EXPORT_SYMBOL(p9_nr_pages);
/**
* payload_gup - Translates user buffer into kernel pages and
* pins them either for read/write through get_user_pages_fast().
* @req: Request to be sent to server.
* @pdata_off: data offset into the first page after translation (gup).
* @pdata_len: Total length of the IO. gup may not return requested # of pages.
* @nr_pages: number of pages to accomodate the payload
* @rw: Indicates if the pages are for read or write.
*/
int
p9_payload_gup(struct p9_req_t *req, size_t *pdata_off, int *pdata_len,
int nr_pages, u8 rw)
{
uint32_t first_page_bytes = 0;
uint32_t pdata_mapped_pages;
struct trans_rpage_info *rpinfo;
*pdata_off = (size_t)req->tc->pubuf & (PAGE_SIZE-1);
if (*pdata_off)
first_page_bytes = min((PAGE_SIZE - *pdata_off),
req->tc->pbuf_size);
rpinfo = req->tc->private;
pdata_mapped_pages = get_user_pages_fast((unsigned long)req->tc->pubuf,
nr_pages, rw, &rpinfo->rp_data[0]);
if (pdata_mapped_pages < 0) {
printk(KERN_ERR "get_user_pages_fast failed:%d udata:%p"
"nr_pages:%d\n", pdata_mapped_pages,
req->tc->pubuf, nr_pages);
pdata_mapped_pages = 0;
return -EIO;
}
rpinfo->rp_nr_pages = pdata_mapped_pages;
if (*pdata_off) {
*pdata_len = first_page_bytes;
*pdata_len += min((req->tc->pbuf_size - *pdata_len),
((size_t)pdata_mapped_pages - 1) << PAGE_SHIFT);
} else {
*pdata_len = min(req->tc->pbuf_size,
(size_t)pdata_mapped_pages << PAGE_SHIFT);
}
return 0;
}
EXPORT_SYMBOL(p9_payload_gup);
/*
* Copyright IBM Corporation, 2010
* Author Venkateswararao Jujjuri <jvrao@linux.vnet.ibm.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2.1 of the GNU Lesser General Public License
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it would be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
*/
/* TRUE if it is user context */
#define P9_IS_USER_CONTEXT (!segment_eq(get_fs(), KERNEL_DS))
/**
* struct trans_rpage_info - To store mapped page information in PDU.
* @rp_alloc:Set if this structure is allocd, not a reuse unused space in pdu.
* @rp_nr_pages: Number of mapped pages
* @rp_data: Array of page pointers
*/
struct trans_rpage_info {
u8 rp_alloc;
int rp_nr_pages;
struct page *rp_data[0];
};
void p9_release_req_pages(struct trans_rpage_info *);
int p9_payload_gup(struct p9_req_t *, size_t *, int *, int, u8);
int p9_nr_pages(struct p9_req_t *);
...@@ -45,6 +45,7 @@ ...@@ -45,6 +45,7 @@
#include <linux/scatterlist.h> #include <linux/scatterlist.h>
#include <linux/virtio.h> #include <linux/virtio.h>
#include <linux/virtio_9p.h> #include <linux/virtio_9p.h>
#include "trans_common.h"
#define VIRTQUEUE_NUM 128 #define VIRTQUEUE_NUM 128
...@@ -155,6 +156,14 @@ static void req_done(struct virtqueue *vq) ...@@ -155,6 +156,14 @@ static void req_done(struct virtqueue *vq)
rc->tag); rc->tag);
req = p9_tag_lookup(chan->client, rc->tag); req = p9_tag_lookup(chan->client, rc->tag);
req->status = REQ_STATUS_RCVD; req->status = REQ_STATUS_RCVD;
if (req->tc->private) {
struct trans_rpage_info *rp = req->tc->private;
/*Release pages */
p9_release_req_pages(rp);
if (rp->rp_alloc)
kfree(rp);
req->tc->private = NULL;
}
p9_client_cb(chan->client, req); p9_client_cb(chan->client, req);
} else { } else {
spin_unlock_irqrestore(&chan->lock, flags); spin_unlock_irqrestore(&chan->lock, flags);
...@@ -202,6 +211,38 @@ static int p9_virtio_cancel(struct p9_client *client, struct p9_req_t *req) ...@@ -202,6 +211,38 @@ static int p9_virtio_cancel(struct p9_client *client, struct p9_req_t *req)
return 1; return 1;
} }
/**
* pack_sg_list_p - Just like pack_sg_list. Instead of taking a buffer,
* this takes a list of pages.
* @sg: scatter/gather list to pack into
* @start: which segment of the sg_list to start at
* @pdata_off: Offset into the first page
* @**pdata: a list of pages to add into sg.
* @count: amount of data to pack into the scatter/gather list
*/
static int
pack_sg_list_p(struct scatterlist *sg, int start, int limit, size_t pdata_off,
struct page **pdata, int count)
{
int s;
int i = 0;
int index = start;
if (pdata_off) {
s = min((int)(PAGE_SIZE - pdata_off), count);
sg_set_page(&sg[index++], pdata[i++], s, pdata_off);
count -= s;
}
while (count) {
BUG_ON(index > limit);
s = min((int)PAGE_SIZE, count);
sg_set_page(&sg[index++], pdata[i++], s, 0);
count -= s;
}
return index-start;
}
/** /**
* p9_virtio_request - issue a request * p9_virtio_request - issue a request
* @client: client instance issuing the request * @client: client instance issuing the request
...@@ -212,22 +253,97 @@ static int p9_virtio_cancel(struct p9_client *client, struct p9_req_t *req) ...@@ -212,22 +253,97 @@ static int p9_virtio_cancel(struct p9_client *client, struct p9_req_t *req)
static int static int
p9_virtio_request(struct p9_client *client, struct p9_req_t *req) p9_virtio_request(struct p9_client *client, struct p9_req_t *req)
{ {
int in, out; int in, out, inp, outp;
struct virtio_chan *chan = client->trans; struct virtio_chan *chan = client->trans;
char *rdata = (char *)req->rc+sizeof(struct p9_fcall); char *rdata = (char *)req->rc+sizeof(struct p9_fcall);
unsigned long flags; unsigned long flags;
int err; size_t pdata_off = 0;
struct trans_rpage_info *rpinfo = NULL;
int err, pdata_len = 0;
P9_DPRINTK(P9_DEBUG_TRANS, "9p debug: virtio request\n"); P9_DPRINTK(P9_DEBUG_TRANS, "9p debug: virtio request\n");
req_retry: req_retry:
req->status = REQ_STATUS_SENT; req->status = REQ_STATUS_SENT;
if (req->tc->pbuf_size && (req->tc->pubuf && P9_IS_USER_CONTEXT)) {
int nr_pages = p9_nr_pages(req);
int rpinfo_size = sizeof(struct trans_rpage_info) +
sizeof(struct page *) * nr_pages;
if (rpinfo_size <= (req->tc->capacity - req->tc->size)) {
/* We can use sdata */
req->tc->private = req->tc->sdata + req->tc->size;
rpinfo = (struct trans_rpage_info *)req->tc->private;
rpinfo->rp_alloc = 0;
} else {
req->tc->private = kmalloc(rpinfo_size, GFP_NOFS);
if (!req->tc->private) {
P9_DPRINTK(P9_DEBUG_TRANS, "9p debug: "
"private kmalloc returned NULL");
return -ENOMEM;
}
rpinfo = (struct trans_rpage_info *)req->tc->private;
rpinfo->rp_alloc = 1;
}
err = p9_payload_gup(req, &pdata_off, &pdata_len, nr_pages,
req->tc->id == P9_TREAD ? 1 : 0);
if (err < 0) {
if (rpinfo->rp_alloc)
kfree(rpinfo);
return err;
}
}
spin_lock_irqsave(&chan->lock, flags); spin_lock_irqsave(&chan->lock, flags);
/* Handle out VirtIO ring buffers */
out = pack_sg_list(chan->sg, 0, VIRTQUEUE_NUM, req->tc->sdata, out = pack_sg_list(chan->sg, 0, VIRTQUEUE_NUM, req->tc->sdata,
req->tc->size); req->tc->size);
in = pack_sg_list(chan->sg, out, VIRTQUEUE_NUM-out, rdata,
if (req->tc->pbuf_size && (req->tc->id == P9_TWRITE)) {
/* We have additional write payload buffer to take care */
if (req->tc->pubuf && P9_IS_USER_CONTEXT) {
outp = pack_sg_list_p(chan->sg, out, VIRTQUEUE_NUM,
pdata_off, rpinfo->rp_data, pdata_len);
} else {
char *pbuf = req->tc->pubuf ? req->tc->pubuf :
req->tc->pkbuf;
outp = pack_sg_list(chan->sg, out, VIRTQUEUE_NUM, pbuf,
req->tc->pbuf_size);
}
out += outp;
}
/* Handle in VirtIO ring buffers */
if (req->tc->pbuf_size &&
((req->tc->id == P9_TREAD) || (req->tc->id == P9_TREADDIR))) {
/*
* Take care of additional Read payload.
* 11 is the read/write header = PDU Header(7) + IO Size (4).
* Arrange in such a way that server places header in the
* alloced memory and payload onto the user buffer.
*/
inp = pack_sg_list(chan->sg, out, VIRTQUEUE_NUM, rdata, 11);
/*
* Running executables in the filesystem may result in
* a read request with kernel buffer as opposed to user buffer.
*/
if (req->tc->pubuf && P9_IS_USER_CONTEXT) {
in = pack_sg_list_p(chan->sg, out+inp, VIRTQUEUE_NUM,
pdata_off, rpinfo->rp_data, pdata_len);
} else {
char *pbuf = req->tc->pubuf ? req->tc->pubuf :
req->tc->pkbuf;
in = pack_sg_list(chan->sg, out+inp, VIRTQUEUE_NUM,
pbuf, req->tc->pbuf_size);
}
in += inp;
} else {
in = pack_sg_list(chan->sg, out, VIRTQUEUE_NUM, rdata,
client->msize); client->msize);
}
err = virtqueue_add_buf(chan->vq, chan->sg, out, in, req->tc); err = virtqueue_add_buf(chan->vq, chan->sg, out, in, req->tc);
if (err < 0) { if (err < 0) {
...@@ -246,6 +362,8 @@ p9_virtio_request(struct p9_client *client, struct p9_req_t *req) ...@@ -246,6 +362,8 @@ p9_virtio_request(struct p9_client *client, struct p9_req_t *req)
P9_DPRINTK(P9_DEBUG_TRANS, P9_DPRINTK(P9_DEBUG_TRANS,
"9p debug: " "9p debug: "
"virtio rpc add_buf returned failure"); "virtio rpc add_buf returned failure");
if (rpinfo && rpinfo->rp_alloc)
kfree(rpinfo);
return -EIO; return -EIO;
} }
} }
...@@ -448,6 +566,7 @@ static struct p9_trans_module p9_virtio_trans = { ...@@ -448,6 +566,7 @@ static struct p9_trans_module p9_virtio_trans = {
.request = p9_virtio_request, .request = p9_virtio_request,
.cancel = p9_virtio_cancel, .cancel = p9_virtio_cancel,
.maxsize = PAGE_SIZE*16, .maxsize = PAGE_SIZE*16,
.pref = P9_TRANS_PREF_PAYLOAD_SEP,
.def = 0, .def = 0,
.owner = THIS_MODULE, .owner = THIS_MODULE,
}; };
......
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