Commit 45e4a24f authored by Linus Torvalds's avatar Linus Torvalds

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

* 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/ericvh/v9fs: (26 commits)
  9p: add more conservative locking
  9p: fix oops in protocol stat parsing error path.
  9p: fix device file handling
  9p: Improve debug support
  9p: eliminate depricated conv functions
  9p: rework client code to use new protocol support functions
  9p: remove unnecessary tag field from p9_req_t structure
  9p: remove 9p fcall debug prints
  9p: add new protocol support code
  9p: encapsulate version function
  9p: move dirread to fs layer
  9p: adjust 9p vfs write operation
  9p: move readn meta-function from client to fs layer
  9p: consolidate read/write functions
  9p: drop broken unused error path from p9_conn_create()
  9p: make rpc code common and rework flush code
  9p: use the rcall structure passed in the request in trans_fd read_work
  9p: apply common request code to trans_fd
  9p: apply common tagpool handling to trans_fd
  9p: move request management to client code
  ...
parents 52c6738b 7eb923b8
...@@ -30,8 +30,8 @@ ...@@ -30,8 +30,8 @@
#include <linux/parser.h> #include <linux/parser.h>
#include <linux/idr.h> #include <linux/idr.h>
#include <net/9p/9p.h> #include <net/9p/9p.h>
#include <net/9p/transport.h>
#include <net/9p/client.h> #include <net/9p/client.h>
#include <net/9p/transport.h>
#include "v9fs.h" #include "v9fs.h"
#include "v9fs_vfs.h" #include "v9fs_vfs.h"
...@@ -234,7 +234,7 @@ struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses, ...@@ -234,7 +234,7 @@ struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses,
if (!v9ses->clnt->dotu) if (!v9ses->clnt->dotu)
v9ses->flags &= ~V9FS_EXTENDED; v9ses->flags &= ~V9FS_EXTENDED;
v9ses->maxdata = v9ses->clnt->msize; v9ses->maxdata = v9ses->clnt->msize - P9_IOHDRSZ;
/* for legacy mode, fall back to V9FS_ACCESS_ANY */ /* for legacy mode, fall back to V9FS_ACCESS_ANY */
if (!v9fs_extended(v9ses) && if (!v9fs_extended(v9ses) &&
......
...@@ -46,9 +46,11 @@ extern struct dentry_operations v9fs_cached_dentry_operations; ...@@ -46,9 +46,11 @@ extern struct dentry_operations v9fs_cached_dentry_operations;
struct inode *v9fs_get_inode(struct super_block *sb, int mode); struct inode *v9fs_get_inode(struct super_block *sb, int mode);
ino_t v9fs_qid2ino(struct p9_qid *qid); ino_t v9fs_qid2ino(struct p9_qid *qid);
void v9fs_stat2inode(struct p9_stat *, 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);
int v9fs_file_open(struct inode *inode, struct file *file); int v9fs_file_open(struct inode *inode, struct file *file);
void v9fs_inode2stat(struct inode *inode, struct p9_stat *stat); void v9fs_inode2stat(struct inode *inode, struct p9_wstat *stat);
void v9fs_dentry_release(struct dentry *); void v9fs_dentry_release(struct dentry *);
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);
...@@ -38,7 +38,6 @@ ...@@ -38,7 +38,6 @@
#include "v9fs.h" #include "v9fs.h"
#include "v9fs_vfs.h" #include "v9fs_vfs.h"
#include "fid.h"
/** /**
* v9fs_vfs_readpage - read an entire page in from 9P * v9fs_vfs_readpage - read an entire page in from 9P
...@@ -53,14 +52,12 @@ static int v9fs_vfs_readpage(struct file *filp, struct page *page) ...@@ -53,14 +52,12 @@ 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 p9_fid *fid;
P9_DPRINTK(P9_DEBUG_VFS, "\n"); P9_DPRINTK(P9_DEBUG_VFS, "\n");
fid = filp->private_data;
buffer = kmap(page); buffer = kmap(page);
offset = page_offset(page); offset = page_offset(page);
retval = p9_client_readn(fid, buffer, offset, PAGE_CACHE_SIZE); retval = v9fs_file_readn(filp, buffer, NULL, offset, PAGE_CACHE_SIZE);
if (retval < 0) if (retval < 0)
goto done; goto done;
......
...@@ -45,7 +45,7 @@ ...@@ -45,7 +45,7 @@
* *
*/ */
static inline int dt_type(struct p9_stat *mistat) static inline int dt_type(struct p9_wstat *mistat)
{ {
unsigned long perm = mistat->mode; unsigned long perm = mistat->mode;
int rettype = DT_REG; int rettype = DT_REG;
...@@ -69,32 +69,58 @@ static inline int dt_type(struct p9_stat *mistat) ...@@ -69,32 +69,58 @@ static inline int dt_type(struct p9_stat *mistat)
static int v9fs_dir_readdir(struct file *filp, void *dirent, filldir_t filldir) static int v9fs_dir_readdir(struct file *filp, void *dirent, filldir_t filldir)
{ {
int over; int over;
struct p9_wstat st;
int err;
struct p9_fid *fid; struct p9_fid *fid;
struct v9fs_session_info *v9ses; int buflen;
struct inode *inode; char *statbuf;
struct p9_stat *st; int n, i = 0;
P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", filp->f_path.dentry->d_name.name); P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", filp->f_path.dentry->d_name.name);
inode = filp->f_path.dentry->d_inode;
v9ses = v9fs_inode2v9ses(inode);
fid = filp->private_data; fid = filp->private_data;
while ((st = p9_client_dirread(fid, filp->f_pos)) != NULL) {
if (IS_ERR(st))
return PTR_ERR(st);
over = filldir(dirent, st->name.str, st->name.len, filp->f_pos, buflen = fid->clnt->msize - P9_IOHDRSZ;
v9fs_qid2ino(&st->qid), dt_type(st)); statbuf = kmalloc(buflen, GFP_KERNEL);
if (!statbuf)
return -ENOMEM;
if (over) while (1) {
err = v9fs_file_readn(filp, statbuf, NULL, buflen,
fid->rdir_fpos);
if (err <= 0)
break; break;
filp->f_pos += st->size; n = err;
kfree(st); while (i < n) {
st = NULL; err = p9stat_read(statbuf + i, buflen-i, &st,
fid->clnt->dotu);
if (err) {
P9_DPRINTK(P9_DEBUG_VFS, "returned %d\n", err);
err = -EIO;
p9stat_free(&st);
goto free_and_exit;
} }
kfree(st); i += st.size+2;
return 0; fid->rdir_fpos += st.size+2;
over = filldir(dirent, st.name, strlen(st.name),
filp->f_pos, v9fs_qid2ino(&st.qid), dt_type(&st));
filp->f_pos += st.size+2;
p9stat_free(&st);
if (over) {
err = 0;
goto free_and_exit;
}
}
}
free_and_exit:
kfree(statbuf);
return err;
} }
......
...@@ -120,23 +120,72 @@ static int v9fs_file_lock(struct file *filp, int cmd, struct file_lock *fl) ...@@ -120,23 +120,72 @@ static int v9fs_file_lock(struct file *filp, int cmd, struct file_lock *fl)
} }
/** /**
* v9fs_file_read - read from a file * v9fs_file_readn - read from a file
* @filp: file pointer to read * @filp: file pointer to read
* @data: data buffer to read data into * @data: data buffer to read data into
* @udata: user data buffer to read data into
* @count: size of buffer * @count: size of buffer
* @offset: offset at which to read data * @offset: offset at which to read data
* *
*/ */
ssize_t
v9fs_file_readn(struct file *filp, char *data, char __user *udata, u32 count,
u64 offset)
{
int n, total;
struct p9_fid *fid = filp->private_data;
P9_DPRINTK(P9_DEBUG_VFS, "fid %d offset %llu count %d\n", fid->fid,
(long long unsigned) offset, count);
n = 0;
total = 0;
do {
n = p9_client_read(fid, data, udata, offset, count);
if (n <= 0)
break;
if (data)
data += n;
if (udata)
udata += n;
offset += n;
count -= n;
total += n;
} while (count > 0 && n == (fid->clnt->msize - P9_IOHDRSZ));
if (n < 0)
total = n;
return total;
}
/**
* v9fs_file_read - read from a file
* @filp: file pointer to read
* @udata: user data buffer to read data into
* @count: size of buffer
* @offset: offset at which to read data
*
*/
static ssize_t static ssize_t
v9fs_file_read(struct file *filp, char __user * data, size_t count, v9fs_file_read(struct file *filp, char __user *udata, size_t count,
loff_t * offset) loff_t * offset)
{ {
int ret; int ret;
struct p9_fid *fid; struct p9_fid *fid;
P9_DPRINTK(P9_DEBUG_VFS, "\n"); P9_DPRINTK(P9_DEBUG_VFS, "count %d offset %lld\n", count, *offset);
fid = filp->private_data; fid = filp->private_data;
ret = p9_client_uread(fid, data, *offset, count);
if (count > (fid->clnt->msize - P9_IOHDRSZ))
ret = v9fs_file_readn(filp, NULL, udata, count, *offset);
else
ret = p9_client_read(fid, NULL, udata, *offset, count);
if (ret > 0) if (ret > 0)
*offset += ret; *offset += ret;
...@@ -156,19 +205,38 @@ static ssize_t ...@@ -156,19 +205,38 @@ static ssize_t
v9fs_file_write(struct file *filp, const char __user * data, v9fs_file_write(struct file *filp, const char __user * data,
size_t count, loff_t * offset) size_t count, loff_t * offset)
{ {
int ret; int n, rsize, total = 0;
struct p9_fid *fid; struct p9_fid *fid;
struct p9_client *clnt;
struct inode *inode = filp->f_path.dentry->d_inode; struct inode *inode = filp->f_path.dentry->d_inode;
int origin = *offset;
P9_DPRINTK(P9_DEBUG_VFS, "data %p count %d offset %x\n", data, P9_DPRINTK(P9_DEBUG_VFS, "data %p count %d offset %x\n", data,
(int)count, (int)*offset); (int)count, (int)*offset);
fid = filp->private_data; fid = filp->private_data;
ret = p9_client_uwrite(fid, data, *offset, count); clnt = fid->clnt;
if (ret > 0) {
invalidate_inode_pages2_range(inode->i_mapping, *offset, rsize = fid->iounit;
*offset+ret); if (!rsize || rsize > clnt->msize-P9_IOHDRSZ)
*offset += ret; rsize = clnt->msize - P9_IOHDRSZ;
do {
if (count < rsize)
rsize = count;
n = p9_client_write(fid, NULL, data+total, *offset+total,
rsize);
if (n <= 0)
break;
count -= n;
total += n;
} while (count > 0);
if (total > 0) {
invalidate_inode_pages2_range(inode->i_mapping, origin,
origin+total);
*offset += total;
} }
if (*offset > inode->i_size) { if (*offset > inode->i_size) {
...@@ -176,7 +244,10 @@ v9fs_file_write(struct file *filp, const char __user * data, ...@@ -176,7 +244,10 @@ v9fs_file_write(struct file *filp, const char __user * data,
inode->i_blocks = (inode->i_size + 512 - 1) >> 9; inode->i_blocks = (inode->i_size + 512 - 1) >> 9;
} }
return ret; if (n < 0)
return n;
return total;
} }
static const struct file_operations v9fs_cached_file_operations = { static const struct file_operations v9fs_cached_file_operations = {
......
...@@ -334,7 +334,7 @@ v9fs_inode_from_fid(struct v9fs_session_info *v9ses, struct p9_fid *fid, ...@@ -334,7 +334,7 @@ v9fs_inode_from_fid(struct v9fs_session_info *v9ses, struct p9_fid *fid,
{ {
int err, umode; int err, umode;
struct inode *ret; struct inode *ret;
struct p9_stat *st; struct p9_wstat *st;
ret = NULL; ret = NULL;
st = p9_client_stat(fid); st = p9_client_stat(fid);
...@@ -417,6 +417,8 @@ v9fs_create(struct v9fs_session_info *v9ses, struct inode *dir, ...@@ -417,6 +417,8 @@ v9fs_create(struct v9fs_session_info *v9ses, struct inode *dir,
struct p9_fid *dfid, *ofid, *fid; struct p9_fid *dfid, *ofid, *fid;
struct inode *inode; struct inode *inode;
P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", dentry->d_name.name);
err = 0; err = 0;
ofid = NULL; ofid = NULL;
fid = NULL; fid = NULL;
...@@ -424,6 +426,7 @@ v9fs_create(struct v9fs_session_info *v9ses, struct inode *dir, ...@@ -424,6 +426,7 @@ v9fs_create(struct v9fs_session_info *v9ses, struct inode *dir,
dfid = v9fs_fid_clone(dentry->d_parent); dfid = v9fs_fid_clone(dentry->d_parent);
if (IS_ERR(dfid)) { if (IS_ERR(dfid)) {
err = PTR_ERR(dfid); err = PTR_ERR(dfid);
P9_DPRINTK(P9_DEBUG_VFS, "fid clone failed %d\n", err);
dfid = NULL; dfid = NULL;
goto error; goto error;
} }
...@@ -432,18 +435,22 @@ v9fs_create(struct v9fs_session_info *v9ses, struct inode *dir, ...@@ -432,18 +435,22 @@ v9fs_create(struct v9fs_session_info *v9ses, struct inode *dir,
ofid = p9_client_walk(dfid, 0, NULL, 1); ofid = p9_client_walk(dfid, 0, NULL, 1);
if (IS_ERR(ofid)) { if (IS_ERR(ofid)) {
err = PTR_ERR(ofid); err = PTR_ERR(ofid);
P9_DPRINTK(P9_DEBUG_VFS, "p9_client_walk failed %d\n", err);
ofid = NULL; ofid = NULL;
goto error; goto error;
} }
err = p9_client_fcreate(ofid, name, perm, mode, extension); err = p9_client_fcreate(ofid, name, perm, mode, extension);
if (err < 0) if (err < 0) {
P9_DPRINTK(P9_DEBUG_VFS, "p9_client_fcreate failed %d\n", err);
goto error; goto error;
}
/* now walk from the parent so we can get unopened fid */ /* now walk from the parent so we can get unopened fid */
fid = p9_client_walk(dfid, 1, &name, 0); fid = p9_client_walk(dfid, 1, &name, 0);
if (IS_ERR(fid)) { if (IS_ERR(fid)) {
err = PTR_ERR(fid); err = PTR_ERR(fid);
P9_DPRINTK(P9_DEBUG_VFS, "p9_client_walk failed %d\n", err);
fid = NULL; fid = NULL;
goto error; goto error;
} else } else
...@@ -453,6 +460,7 @@ v9fs_create(struct v9fs_session_info *v9ses, struct inode *dir, ...@@ -453,6 +460,7 @@ v9fs_create(struct v9fs_session_info *v9ses, struct inode *dir,
inode = v9fs_inode_from_fid(v9ses, fid, dir->i_sb); inode = v9fs_inode_from_fid(v9ses, fid, dir->i_sb);
if (IS_ERR(inode)) { if (IS_ERR(inode)) {
err = PTR_ERR(inode); err = PTR_ERR(inode);
P9_DPRINTK(P9_DEBUG_VFS, "inode creation failed %d\n", err);
goto error; goto error;
} }
...@@ -734,7 +742,7 @@ v9fs_vfs_getattr(struct vfsmount *mnt, struct dentry *dentry, ...@@ -734,7 +742,7 @@ v9fs_vfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
int err; int err;
struct v9fs_session_info *v9ses; struct v9fs_session_info *v9ses;
struct p9_fid *fid; struct p9_fid *fid;
struct p9_stat *st; struct p9_wstat *st;
P9_DPRINTK(P9_DEBUG_VFS, "dentry: %p\n", dentry); P9_DPRINTK(P9_DEBUG_VFS, "dentry: %p\n", dentry);
err = -EPERM; err = -EPERM;
...@@ -815,10 +823,9 @@ static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr) ...@@ -815,10 +823,9 @@ static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr)
*/ */
void void
v9fs_stat2inode(struct p9_stat *stat, struct inode *inode, v9fs_stat2inode(struct p9_wstat *stat, struct inode *inode,
struct super_block *sb) struct super_block *sb)
{ {
int n;
char ext[32]; char ext[32];
struct v9fs_session_info *v9ses = sb->s_fs_info; struct v9fs_session_info *v9ses = sb->s_fs_info;
...@@ -842,11 +849,7 @@ v9fs_stat2inode(struct p9_stat *stat, struct inode *inode, ...@@ -842,11 +849,7 @@ v9fs_stat2inode(struct p9_stat *stat, struct inode *inode,
int major = -1; int major = -1;
int minor = -1; int minor = -1;
n = stat->extension.len; strncpy(ext, stat->extension, sizeof(ext));
if (n > sizeof(ext)-1)
n = sizeof(ext)-1;
memmove(ext, stat->extension.str, n);
ext[n] = 0;
sscanf(ext, "%c %u %u", &type, &major, &minor); sscanf(ext, "%c %u %u", &type, &major, &minor);
switch (type) { switch (type) {
case 'c': case 'c':
...@@ -857,10 +860,11 @@ v9fs_stat2inode(struct p9_stat *stat, struct inode *inode, ...@@ -857,10 +860,11 @@ v9fs_stat2inode(struct p9_stat *stat, struct inode *inode,
break; break;
default: default:
P9_DPRINTK(P9_DEBUG_ERROR, P9_DPRINTK(P9_DEBUG_ERROR,
"Unknown special type %c (%.*s)\n", type, "Unknown special type %c %s\n", type,
stat->extension.len, stat->extension.str); stat->extension);
}; };
inode->i_rdev = MKDEV(major, minor); inode->i_rdev = MKDEV(major, minor);
init_special_inode(inode, inode->i_mode, inode->i_rdev);
} else } else
inode->i_rdev = 0; inode->i_rdev = 0;
...@@ -904,7 +908,7 @@ static int v9fs_readlink(struct dentry *dentry, char *buffer, int buflen) ...@@ -904,7 +908,7 @@ static int v9fs_readlink(struct dentry *dentry, char *buffer, int buflen)
struct v9fs_session_info *v9ses; struct v9fs_session_info *v9ses;
struct p9_fid *fid; struct p9_fid *fid;
struct p9_stat *st; struct p9_wstat *st;
P9_DPRINTK(P9_DEBUG_VFS, " %s\n", dentry->d_name.name); P9_DPRINTK(P9_DEBUG_VFS, " %s\n", dentry->d_name.name);
retval = -EPERM; retval = -EPERM;
...@@ -926,15 +930,10 @@ static int v9fs_readlink(struct dentry *dentry, char *buffer, int buflen) ...@@ -926,15 +930,10 @@ static int v9fs_readlink(struct dentry *dentry, char *buffer, int buflen)
} }
/* copy extension buffer into buffer */ /* copy extension buffer into buffer */
if (st->extension.len < buflen) strncpy(buffer, st->extension, buflen);
buflen = st->extension.len + 1;
memmove(buffer, st->extension.str, buflen - 1);
buffer[buflen-1] = 0;
P9_DPRINTK(P9_DEBUG_VFS, P9_DPRINTK(P9_DEBUG_VFS,
"%s -> %.*s (%s)\n", dentry->d_name.name, st->extension.len, "%s -> %s (%s)\n", dentry->d_name.name, st->extension, buffer);
st->extension.str, buffer);
retval = buflen; retval = buflen;
......
...@@ -111,7 +111,7 @@ static int v9fs_get_sb(struct file_system_type *fs_type, int flags, ...@@ -111,7 +111,7 @@ static int v9fs_get_sb(struct file_system_type *fs_type, int flags,
struct inode *inode = NULL; struct inode *inode = NULL;
struct dentry *root = NULL; struct dentry *root = NULL;
struct v9fs_session_info *v9ses = NULL; struct v9fs_session_info *v9ses = NULL;
struct p9_stat *st = NULL; struct p9_wstat *st = NULL;
int mode = S_IRWXUGO | S_ISVTX; int mode = S_IRWXUGO | S_ISVTX;
uid_t uid = current->fsuid; uid_t uid = current->fsuid;
gid_t gid = current->fsgid; gid_t gid = current->fsgid;
...@@ -161,10 +161,14 @@ static int v9fs_get_sb(struct file_system_type *fs_type, int flags, ...@@ -161,10 +161,14 @@ static int v9fs_get_sb(struct file_system_type *fs_type, int flags,
sb->s_root = root; sb->s_root = root;
root->d_inode->i_ino = v9fs_qid2ino(&st->qid); root->d_inode->i_ino = v9fs_qid2ino(&st->qid);
v9fs_stat2inode(st, root->d_inode, sb); v9fs_stat2inode(st, root->d_inode, sb);
v9fs_fid_add(root, fid); v9fs_fid_add(root, fid);
p9stat_free(st);
kfree(st); kfree(st);
P9_DPRINTK(P9_DEBUG_VFS, " return simple set mount\n");
return simple_set_mnt(mnt, sb); return simple_set_mnt(mnt, sb);
release_sb: release_sb:
......
...@@ -27,8 +27,6 @@ ...@@ -27,8 +27,6 @@
#ifndef NET_9P_H #ifndef NET_9P_H
#define NET_9P_H #define NET_9P_H
#ifdef CONFIG_NET_9P_DEBUG
/** /**
* enum p9_debug_flags - bits for mount time debug parameter * enum p9_debug_flags - bits for mount time debug parameter
* @P9_DEBUG_ERROR: more verbose error messages including original error string * @P9_DEBUG_ERROR: more verbose error messages including original error string
...@@ -39,6 +37,7 @@ ...@@ -39,6 +37,7 @@
* @P9_DEBUG_TRANS: transport tracing * @P9_DEBUG_TRANS: transport tracing
* @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
* *
* 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.
...@@ -53,24 +52,27 @@ enum p9_debug_flags { ...@@ -53,24 +52,27 @@ enum p9_debug_flags {
P9_DEBUG_TRANS = (1<<6), P9_DEBUG_TRANS = (1<<6),
P9_DEBUG_SLABS = (1<<7), P9_DEBUG_SLABS = (1<<7),
P9_DEBUG_FCALL = (1<<8), P9_DEBUG_FCALL = (1<<8),
P9_DEBUG_FID = (1<<9),
P9_DEBUG_PKT = (1<<10),
}; };
extern unsigned int p9_debug_level; extern unsigned int p9_debug_level;
#ifdef CONFIG_NET_9P_DEBUG
#define P9_DPRINTK(level, format, arg...) \ #define P9_DPRINTK(level, format, arg...) \
do { \ do { \
if ((p9_debug_level & level) == level) \ if ((p9_debug_level & level) == level) {\
if (level == P9_DEBUG_9P) \
printk(KERN_NOTICE "(%8.8d) " \
format , task_pid_nr(current) , ## arg); \
else \
printk(KERN_NOTICE "-- %s (%d): " \ printk(KERN_NOTICE "-- %s (%d): " \
format , __func__, task_pid_nr(current) , ## arg); \ format , __func__, task_pid_nr(current) , ## arg); \
} \
} while (0) } while (0)
#define PRINT_FCALL_ERROR(s, fcall) P9_DPRINTK(P9_DEBUG_ERROR, \
"%s: %.*s\n", s, fcall?fcall->params.rerror.error.len:0, \
fcall?fcall->params.rerror.error.str:"");
#else #else
#define P9_DPRINTK(level, format, arg...) do { } while (0) #define P9_DPRINTK(level, format, arg...) do { } while (0)
#define PRINT_FCALL_ERROR(s, fcall) do { } while (0)
#endif #endif
#define P9_EPRINTK(level, format, arg...) \ #define P9_EPRINTK(level, format, arg...) \
...@@ -325,33 +327,6 @@ struct p9_qid { ...@@ -325,33 +327,6 @@ struct p9_qid {
* See Also: http://plan9.bell-labs.com/magic/man2html/2/stat * See Also: http://plan9.bell-labs.com/magic/man2html/2/stat
*/ */
struct p9_stat {
u16 size;
u16 type;
u32 dev;
struct p9_qid qid;
u32 mode;
u32 atime;
u32 mtime;
u64 length;
struct p9_str name;
struct p9_str uid;
struct p9_str gid;
struct p9_str muid;
struct p9_str extension; /* 9p2000.u extensions */
u32 n_uid; /* 9p2000.u extensions */
u32 n_gid; /* 9p2000.u extensions */
u32 n_muid; /* 9p2000.u extensions */
};
/*
* file metadata (stat) structure used to create Twstat message
* The is identical to &p9_stat, but the strings don't point to
* the same memory block and should be freed separately
*
* See Also: http://plan9.bell-labs.com/magic/man2html/2/stat
*/
struct p9_wstat { struct p9_wstat {
u16 size; u16 size;
u16 type; u16 type;
...@@ -493,12 +468,12 @@ struct p9_tstat { ...@@ -493,12 +468,12 @@ struct p9_tstat {
}; };
struct p9_rstat { struct p9_rstat {
struct p9_stat stat; struct p9_wstat stat;
}; };
struct p9_twstat { struct p9_twstat {
u32 fid; u32 fid;
struct p9_stat stat; struct p9_wstat stat;
}; };
struct p9_rwstat { struct p9_rwstat {
...@@ -509,8 +484,9 @@ struct p9_rwstat { ...@@ -509,8 +484,9 @@ struct p9_rwstat {
* @size: prefixed length of the structure * @size: prefixed length of the structure
* @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
* @capacity: used by marshalling routines to track total capacity
* @sdata: payload * @sdata: payload
* @params: per-operation parameters
* *
* &p9_fcall represents the structure for all 9P RPC * &p9_fcall represents the structure for all 9P RPC
* transactions. Requests are packaged into fcalls, and reponses * transactions. Requests are packaged into fcalls, and reponses
...@@ -523,68 +499,15 @@ struct p9_fcall { ...@@ -523,68 +499,15 @@ struct p9_fcall {
u32 size; u32 size;
u8 id; u8 id;
u16 tag; u16 tag;
void *sdata;
size_t offset;
union { size_t capacity;
struct p9_tversion tversion;
struct p9_rversion rversion; uint8_t *sdata;
struct p9_tauth tauth;
struct p9_rauth rauth;
struct p9_rerror rerror;
struct p9_tflush tflush;
struct p9_rflush rflush;
struct p9_tattach tattach;
struct p9_rattach rattach;
struct p9_twalk twalk;
struct p9_rwalk rwalk;
struct p9_topen topen;
struct p9_ropen ropen;
struct p9_tcreate tcreate;
struct p9_rcreate rcreate;
struct p9_tread tread;
struct p9_rread rread;
struct p9_twrite twrite;
struct p9_rwrite rwrite;
struct p9_tclunk tclunk;
struct p9_rclunk rclunk;
struct p9_tremove tremove;
struct p9_rremove rremove;
struct p9_tstat tstat;
struct p9_rstat rstat;
struct p9_twstat twstat;
struct p9_rwstat rwstat;
} params;
}; };
struct p9_idpool; struct p9_idpool;
int p9_deserialize_stat(void *buf, u32 buflen, struct p9_stat *stat,
int dotu);
int p9_deserialize_fcall(void *buf, u32 buflen, struct p9_fcall *fc, int dotu);
void p9_set_tag(struct p9_fcall *fc, u16 tag);
struct p9_fcall *p9_create_tversion(u32 msize, char *version);
struct p9_fcall *p9_create_tattach(u32 fid, u32 afid, char *uname,
char *aname, u32 n_uname, int dotu);
struct p9_fcall *p9_create_tauth(u32 afid, char *uname, char *aname,
u32 n_uname, int dotu);
struct p9_fcall *p9_create_tflush(u16 oldtag);
struct p9_fcall *p9_create_twalk(u32 fid, u32 newfid, u16 nwname,
char **wnames);
struct p9_fcall *p9_create_topen(u32 fid, u8 mode);
struct p9_fcall *p9_create_tcreate(u32 fid, char *name, u32 perm, u8 mode,
char *extension, int dotu);
struct p9_fcall *p9_create_tread(u32 fid, u64 offset, u32 count);
struct p9_fcall *p9_create_twrite(u32 fid, u64 offset, u32 count,
const char *data);
struct p9_fcall *p9_create_twrite_u(u32 fid, u64 offset, u32 count,
const char __user *data);
struct p9_fcall *p9_create_tclunk(u32 fid);
struct p9_fcall *p9_create_tremove(u32 fid);
struct p9_fcall *p9_create_tstat(u32 fid);
struct p9_fcall *p9_create_twstat(u32 fid, struct p9_wstat *wstat,
int dotu);
int p9_printfcall(char *buf, int buflen, struct p9_fcall *fc, int dotu);
int p9_errstr2errno(char *errstr, int len); int p9_errstr2errno(char *errstr, int len);
struct p9_idpool *p9_idpool_create(void); struct p9_idpool *p9_idpool_create(void);
......
...@@ -26,6 +26,87 @@ ...@@ -26,6 +26,87 @@
#ifndef NET_9P_CLIENT_H #ifndef NET_9P_CLIENT_H
#define NET_9P_CLIENT_H #define NET_9P_CLIENT_H
/* Number of requests per row */
#define P9_ROW_MAXTAG 255
/**
* enum p9_trans_status - different states of underlying transports
* @Connected: transport is connected and healthy
* @Disconnected: transport has been disconnected
* @Hung: transport is connected by wedged
*
* This enumeration details the various states a transport
* instatiation can be in.
*/
enum p9_trans_status {
Connected,
Disconnected,
Hung,
};
/**
* enum p9_req_status_t - virtio request status
* @REQ_STATUS_IDLE: request slot unused
* @REQ_STATUS_ALLOC: request has been allocated but not sent
* @REQ_STATUS_UNSENT: request waiting to be sent
* @REQ_STATUS_SENT: request sent to server
* @REQ_STATUS_FLSH: a flush has been sent for this request
* @REQ_STATUS_RCVD: response received from server
* @REQ_STATUS_FLSHD: request has been flushed
* @REQ_STATUS_ERROR: request encountered an error on the client side
*
* The @REQ_STATUS_IDLE state is used to mark a request slot as unused
* but use is actually tracked by the idpool structure which handles tag
* id allocation.
*
*/
enum p9_req_status_t {
REQ_STATUS_IDLE,
REQ_STATUS_ALLOC,
REQ_STATUS_UNSENT,
REQ_STATUS_SENT,
REQ_STATUS_FLSH,
REQ_STATUS_RCVD,
REQ_STATUS_FLSHD,
REQ_STATUS_ERROR,
};
/**
* struct p9_req_t - request slots
* @status: status of this request slot
* @t_err: transport error
* @flush_tag: tag of request being flushed (for flush requests)
* @wq: wait_queue for the client to block on for this request
* @tc: the request fcall structure
* @rc: the response fcall structure
* @aux: transport specific data (provided for trans_fd migration)
* @req_list: link for higher level objects to chain requests
*
* Transport use an array to track outstanding requests
* instead of a list. While this may incurr overhead during initial
* allocation or expansion, it makes request lookup much easier as the
* tag id is a index into an array. (We use tag+1 so that we can accomodate
* the -1 tag for the T_VERSION request).
* This also has the nice effect of only having to allocate wait_queues
* once, instead of constantly allocating and freeing them. Its possible
* other resources could benefit from this scheme as well.
*
*/
struct p9_req_t {
int status;
int t_err;
u16 flush_tag;
wait_queue_head_t *wq;
struct p9_fcall *tc;
struct p9_fcall *rc;
void *aux;
struct list_head req_list;
};
/** /**
* struct p9_client - per client instance state * struct p9_client - per client instance state
* @lock: protect @fidlist * @lock: protect @fidlist
...@@ -36,9 +117,20 @@ ...@@ -36,9 +117,20 @@
* @conn: connection state information used by trans_fd * @conn: connection state information used by trans_fd
* @fidpool: fid handle accounting for session * @fidpool: fid handle accounting for session
* @fidlist: List of active fid handles * @fidlist: List of active fid handles
* @tagpool - transaction id accounting for session
* @reqs - 2D array of requests
* @max_tag - current maximum tag id allocated
* *
* The client structure is used to keep track of various per-client * The client structure is used to keep track of various per-client
* state that has been instantiated. * state that has been instantiated.
* In order to minimize per-transaction overhead we use a
* simple array to lookup requests instead of a hash table
* or linked list. In order to support larger number of
* transactions, we make this a 2D array, allocating new rows
* when we need to grow the total number of the transactions.
*
* Each row is 256 requests and we'll support up to 256 rows for
* a total of 64k concurrent requests per session.
* *
* Bugs: duplicated data and potentially unnecessary elements. * Bugs: duplicated data and potentially unnecessary elements.
*/ */
...@@ -48,11 +140,16 @@ struct p9_client { ...@@ -48,11 +140,16 @@ struct p9_client {
int msize; int msize;
unsigned char dotu; unsigned char dotu;
struct p9_trans_module *trans_mod; struct p9_trans_module *trans_mod;
struct p9_trans *trans; enum p9_trans_status status;
void *trans;
struct p9_conn *conn; struct p9_conn *conn;
struct p9_idpool *fidpool; struct p9_idpool *fidpool;
struct list_head fidlist; struct list_head fidlist;
struct p9_idpool *tagpool;
struct p9_req_t *reqs[P9_ROW_MAXTAG];
int max_tag;
}; };
/** /**
...@@ -65,8 +162,6 @@ struct p9_client { ...@@ -65,8 +162,6 @@ struct p9_client {
* @uid: the numeric uid of the local user who owns this handle * @uid: the numeric uid of the local user who owns this handle
* @aux: transport specific information (unused?) * @aux: transport specific information (unused?)
* @rdir_fpos: tracks offset of file position when reading directory contents * @rdir_fpos: tracks offset of file position when reading directory contents
* @rdir_pos: (unused?)
* @rdir_fcall: holds response of last directory read request
* @flist: per-client-instance fid tracking * @flist: per-client-instance fid tracking
* @dlist: per-dentry fid tracking * @dlist: per-dentry fid tracking
* *
...@@ -83,8 +178,6 @@ struct p9_fid { ...@@ -83,8 +178,6 @@ struct p9_fid {
void *aux; void *aux;
int rdir_fpos; int rdir_fpos;
int rdir_pos;
struct p9_fcall *rdir_fcall;
struct list_head flist; struct list_head flist;
struct list_head dlist; /* list of all fids attached to a dentry */ struct list_head dlist; /* list of all fids attached to a dentry */
}; };
...@@ -103,15 +196,18 @@ int p9_client_fcreate(struct p9_fid *fid, char *name, u32 perm, int mode, ...@@ -103,15 +196,18 @@ int p9_client_fcreate(struct p9_fid *fid, char *name, u32 perm, int mode,
char *extension); char *extension);
int p9_client_clunk(struct p9_fid *fid); int p9_client_clunk(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, u64 offset, u32 count); int p9_client_read(struct p9_fid *fid, char *data, char __user *udata,
int p9_client_readn(struct p9_fid *fid, char *data, u64 offset, u32 count); u64 offset, u32 count);
int p9_client_write(struct p9_fid *fid, char *data, u64 offset, u32 count); int p9_client_write(struct p9_fid *fid, char *data, const char __user *udata,
int p9_client_uread(struct p9_fid *fid, char __user *data, u64 offset, u64 offset, u32 count);
u32 count); struct p9_wstat *p9_client_stat(struct p9_fid *fid);
int p9_client_uwrite(struct p9_fid *fid, const char __user *data, u64 offset,
u32 count);
struct p9_stat *p9_client_stat(struct p9_fid *fid);
int p9_client_wstat(struct p9_fid *fid, struct p9_wstat *wst); int p9_client_wstat(struct p9_fid *fid, struct p9_wstat *wst);
struct p9_stat *p9_client_dirread(struct p9_fid *fid, u64 offset);
struct p9_req_t *p9_tag_lookup(struct p9_client *, u16);
void p9_client_cb(struct p9_client *c, struct p9_req_t *req);
int p9stat_read(char *, int, struct p9_wstat *, int);
void p9stat_free(struct p9_wstat *);
#endif /* NET_9P_CLIENT_H */ #endif /* NET_9P_CLIENT_H */
...@@ -26,52 +26,6 @@ ...@@ -26,52 +26,6 @@
#ifndef NET_9P_TRANSPORT_H #ifndef NET_9P_TRANSPORT_H
#define NET_9P_TRANSPORT_H #define NET_9P_TRANSPORT_H
#include <linux/module.h>
/**
* enum p9_trans_status - different states of underlying transports
* @Connected: transport is connected and healthy
* @Disconnected: transport has been disconnected
* @Hung: transport is connected by wedged
*
* This enumeration details the various states a transport
* instatiation can be in.
*/
enum p9_trans_status {
Connected,
Disconnected,
Hung,
};
/**
* struct p9_trans - per-transport state and API
* @status: transport &p9_trans_status
* @msize: negotiated maximum packet size (duplicate from client)
* @extended: negotiated protocol extensions (duplicate from client)
* @priv: transport private data
* @close: member function to disconnect and close the transport
* @rpc: member function to issue a request to the transport
*
* This is the basic API for a transport instance. It is used as
* a handle by the client to issue requests. This interface is currently
* in flux during reorganization.
*
* Bugs: there is lots of duplicated data here and its not clear that
* the member functions need to be per-instance versus per transport
* module.
*/
struct p9_trans {
enum p9_trans_status status;
int msize;
unsigned char extended;
void *priv;
void (*close) (struct p9_trans *);
int (*rpc) (struct p9_trans *t, struct p9_fcall *tc,
struct p9_fcall **rc);
};
/** /**
* 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
...@@ -79,12 +33,14 @@ struct p9_trans { ...@@ -79,12 +33,14 @@ struct p9_trans {
* @maxsize: transport provided maximum packet size * @maxsize: transport provided maximum packet size
* @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
* @cancel: member function to cancel a request (if it hasn't been sent)
* *
* This is the basic API for a transport module which is registered by the * This is the basic API for a transport module which is registered by the
* transport module with the 9P core network module and used by the client * transport module with the 9P core network module and used by the client
* to instantiate a new connection on a transport. * to instantiate a new connection on a transport.
* *
* Bugs: the transport module list isn't protected. * BUGS: the transport module list isn't protected.
*/ */
struct p9_trans_module { struct p9_trans_module {
...@@ -92,8 +48,11 @@ struct p9_trans_module { ...@@ -92,8 +48,11 @@ struct p9_trans_module {
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 def; /* this transport should be default */ int def; /* this transport should be default */
struct p9_trans * (*create)(const char *, char *, int, unsigned char);
struct module *owner; struct module *owner;
int (*create)(struct p9_client *, const char *, char *);
void (*close) (struct p9_client *);
int (*request) (struct p9_client *, struct p9_req_t *req);
int (*cancel) (struct p9_client *, struct p9_req_t *req);
}; };
void v9fs_register_trans(struct p9_trans_module *m); void v9fs_register_trans(struct p9_trans_module *m);
......
...@@ -4,10 +4,9 @@ obj-$(CONFIG_NET_9P_VIRTIO) += 9pnet_virtio.o ...@@ -4,10 +4,9 @@ obj-$(CONFIG_NET_9P_VIRTIO) += 9pnet_virtio.o
9pnet-objs := \ 9pnet-objs := \
mod.o \ mod.o \
client.o \ client.o \
conv.o \
error.o \ error.o \
fcprint.o \
util.o \ util.o \
protocol.o \
trans_fd.o \ trans_fd.o \
9pnet_virtio-objs := \ 9pnet_virtio-objs := \
......
...@@ -33,12 +33,9 @@ ...@@ -33,12 +33,9 @@
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <net/9p/9p.h> #include <net/9p/9p.h>
#include <linux/parser.h> #include <linux/parser.h>
#include <net/9p/transport.h>
#include <net/9p/client.h> #include <net/9p/client.h>
#include <net/9p/transport.h>
static struct p9_fid *p9_fid_create(struct p9_client *clnt); #include "protocol.h"
static void p9_fid_destroy(struct p9_fid *fid);
static struct p9_stat *p9_clone_stat(struct p9_stat *st, int dotu);
/* /*
* Client Option Parsing (code inspired by NFS code) * Client Option Parsing (code inspired by NFS code)
...@@ -59,6 +56,9 @@ static const match_table_t tokens = { ...@@ -59,6 +56,9 @@ static const match_table_t tokens = {
{Opt_err, NULL}, {Opt_err, NULL},
}; };
static struct p9_req_t *
p9_client_rpc(struct p9_client *c, int8_t type, const char *fmt, ...);
/** /**
* v9fs_parse_options - parse mount options into session structure * v9fs_parse_options - parse mount options into session structure
* @options: options string passed from mount * @options: options string passed from mount
...@@ -124,31 +124,585 @@ static int parse_opts(char *opts, struct p9_client *clnt) ...@@ -124,31 +124,585 @@ static int parse_opts(char *opts, struct p9_client *clnt)
return ret; return ret;
} }
/**
* p9_tag_alloc - lookup/allocate a request by tag
* @c: client session to lookup tag within
* @tag: numeric id for transaction
*
* this is a simple array lookup, but will grow the
* request_slots as necessary to accomodate transaction
* ids which did not previously have a slot.
*
* this code relies on the client spinlock to manage locks, its
* possible we should switch to something else, but I'd rather
* stick with something low-overhead for the common case.
*
*/
static struct p9_req_t *p9_tag_alloc(struct p9_client *c, u16 tag)
{
unsigned long flags;
int row, col;
struct p9_req_t *req;
/* This looks up the original request by tag so we know which
* buffer to read the data into */
tag++;
if (tag >= c->max_tag) {
spin_lock_irqsave(&c->lock, flags);
/* check again since original check was outside of lock */
while (tag >= c->max_tag) {
row = (tag / P9_ROW_MAXTAG);
c->reqs[row] = kcalloc(P9_ROW_MAXTAG,
sizeof(struct p9_req_t), GFP_ATOMIC);
if (!c->reqs[row]) {
printk(KERN_ERR "Couldn't grow tag array\n");
return ERR_PTR(-ENOMEM);
}
for (col = 0; col < P9_ROW_MAXTAG; col++) {
c->reqs[row][col].status = REQ_STATUS_IDLE;
c->reqs[row][col].tc = NULL;
}
c->max_tag += P9_ROW_MAXTAG;
}
spin_unlock_irqrestore(&c->lock, flags);
}
row = tag / P9_ROW_MAXTAG;
col = tag % P9_ROW_MAXTAG;
req = &c->reqs[row][col];
if (!req->tc) {
req->wq = kmalloc(sizeof(wait_queue_head_t), GFP_KERNEL);
if (!req->wq) {
printk(KERN_ERR "Couldn't grow tag array\n");
return ERR_PTR(-ENOMEM);
}
init_waitqueue_head(req->wq);
req->tc = kmalloc(sizeof(struct p9_fcall)+c->msize,
GFP_KERNEL);
req->rc = kmalloc(sizeof(struct p9_fcall)+c->msize,
GFP_KERNEL);
if ((!req->tc) || (!req->rc)) {
printk(KERN_ERR "Couldn't grow tag array\n");
kfree(req->tc);
kfree(req->rc);
return ERR_PTR(-ENOMEM);
}
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->capacity = c->msize;
}
p9pdu_reset(req->tc);
p9pdu_reset(req->rc);
req->flush_tag = 0;
req->tc->tag = tag-1;
req->status = REQ_STATUS_ALLOC;
return &c->reqs[row][col];
}
/**
* p9_tag_lookup - lookup a request by tag
* @c: client session to lookup tag within
* @tag: numeric id for transaction
*
*/
struct p9_req_t *p9_tag_lookup(struct p9_client *c, u16 tag)
{
int row, col;
/* This looks up the original request by tag so we know which
* buffer to read the data into */
tag++;
BUG_ON(tag >= c->max_tag);
row = tag / P9_ROW_MAXTAG;
col = tag % P9_ROW_MAXTAG;
return &c->reqs[row][col];
}
EXPORT_SYMBOL(p9_tag_lookup);
/**
* p9_tag_init - setup tags structure and contents
* @tags: tags structure from the client struct
*
* This initializes the tags structure for each client instance.
*
*/
static int p9_tag_init(struct p9_client *c)
{
int err = 0;
c->tagpool = p9_idpool_create();
if (IS_ERR(c->tagpool)) {
err = PTR_ERR(c->tagpool);
c->tagpool = NULL;
goto error;
}
p9_idpool_get(c->tagpool); /* reserve tag 0 */
c->max_tag = 0;
error:
return err;
}
/**
* p9_tag_cleanup - cleans up tags structure and reclaims resources
* @tags: tags structure from the client struct
*
* This frees resources associated with the tags structure
*
*/
static void p9_tag_cleanup(struct p9_client *c)
{
int row, col;
/* check to insure all requests are idle */
for (row = 0; row < (c->max_tag/P9_ROW_MAXTAG); row++) {
for (col = 0; col < P9_ROW_MAXTAG; col++) {
if (c->reqs[row][col].status != REQ_STATUS_IDLE) {
P9_DPRINTK(P9_DEBUG_MUX,
"Attempting to cleanup non-free tag %d,%d\n",
row, col);
/* TODO: delay execution of cleanup */
return;
}
}
}
if (c->tagpool)
p9_idpool_destroy(c->tagpool);
/* free requests associated with tags */
for (row = 0; row < (c->max_tag/P9_ROW_MAXTAG); row++) {
for (col = 0; col < P9_ROW_MAXTAG; col++) {
kfree(c->reqs[row][col].wq);
kfree(c->reqs[row][col].tc);
kfree(c->reqs[row][col].rc);
}
kfree(c->reqs[row]);
}
c->max_tag = 0;
}
/**
* p9_free_req - free a request and clean-up as necessary
* c: client state
* r: request to release
*
*/
static void p9_free_req(struct p9_client *c, struct p9_req_t *r)
{
int tag = r->tc->tag;
P9_DPRINTK(P9_DEBUG_MUX, "clnt %p req %p tag: %d\n", c, r, tag);
r->status = REQ_STATUS_IDLE;
if (tag != P9_NOTAG && p9_idpool_check(tag, c->tagpool))
p9_idpool_put(tag, c->tagpool);
/* if this was a flush request we have to free response fcall */
if (r->rc->id == P9_RFLUSH) {
kfree(r->tc);
kfree(r->rc);
}
}
/** /**
* p9_client_rpc - sends 9P request and waits until a response is available. * p9_client_cb - call back from transport to client
* The function can be interrupted. * c: client state
* @c: client data * req: request received
* @tc: request to be sent *
* @rc: pointer where a pointer to the response is stored
*/ */
void p9_client_cb(struct p9_client *c, struct p9_req_t *req)
{
struct p9_req_t *other_req;
unsigned long flags;
P9_DPRINTK(P9_DEBUG_MUX, " tag %d\n", req->tc->tag);
if (req->status == REQ_STATUS_ERROR)
wake_up(req->wq);
if (req->flush_tag) { /* flush receive path */
P9_DPRINTK(P9_DEBUG_9P, "<<< RFLUSH %d\n", req->tc->tag);
spin_lock_irqsave(&c->lock, flags);
other_req = p9_tag_lookup(c, req->flush_tag);
if (other_req->status != REQ_STATUS_FLSH) /* stale flush */
spin_unlock_irqrestore(&c->lock, flags);
else {
other_req->status = REQ_STATUS_FLSHD;
spin_unlock_irqrestore(&c->lock, flags);
wake_up(other_req->wq);
}
p9_free_req(c, req);
} else { /* normal receive path */
P9_DPRINTK(P9_DEBUG_MUX, "normal: tag %d\n", req->tc->tag);
spin_lock_irqsave(&c->lock, flags);
if (req->status != REQ_STATUS_FLSHD)
req->status = REQ_STATUS_RCVD;
spin_unlock_irqrestore(&c->lock, flags);
wake_up(req->wq);
P9_DPRINTK(P9_DEBUG_MUX, "wakeup: %d\n", req->tc->tag);
}
}
EXPORT_SYMBOL(p9_client_cb);
/**
* p9_parse_header - parse header arguments out of a packet
* @pdu: packet to parse
* @size: size of packet
* @type: type of request
* @tag: tag of packet
* @rewind: set if we need to rewind offset afterwards
*/
int int
p9_client_rpc(struct p9_client *c, struct p9_fcall *tc, p9_parse_header(struct p9_fcall *pdu, int32_t *size, int8_t *type, int16_t *tag,
struct p9_fcall **rc) int rewind)
{
int8_t r_type;
int16_t r_tag;
int32_t r_size;
int offset = pdu->offset;
int err;
pdu->offset = 0;
if (pdu->size == 0)
pdu->size = 7;
err = p9pdu_readf(pdu, 0, "dbw", &r_size, &r_type, &r_tag);
if (err)
goto rewind_and_exit;
pdu->size = r_size;
pdu->id = r_type;
pdu->tag = r_tag;
P9_DPRINTK(P9_DEBUG_9P, "<<< size=%d type: %d tag: %d\n", pdu->size,
pdu->id, pdu->tag);
if (type)
*type = r_type;
if (tag)
*tag = r_tag;
if (size)
*size = r_size;
rewind_and_exit:
if (rewind)
pdu->offset = offset;
return err;
}
EXPORT_SYMBOL(p9_parse_header);
/**
* p9_check_errors - check 9p packet for error return and process it
* @c: current client instance
* @req: request to parse and check for error conditions
*
* returns error code if one is discovered, otherwise returns 0
*
* this will have to be more complicated if we have multiple
* error packet types
*/
static int p9_check_errors(struct p9_client *c, struct p9_req_t *req)
{
int8_t type;
int err;
err = p9_parse_header(req->rc, NULL, &type, NULL, 0);
if (err) {
P9_DPRINTK(P9_DEBUG_ERROR, "couldn't parse header %d\n", err);
return err;
}
if (type == P9_RERROR) {
int ecode;
char *ename;
err = p9pdu_readf(req->rc, c->dotu, "s?d", &ename, &ecode);
if (err) {
P9_DPRINTK(P9_DEBUG_ERROR, "couldn't parse error%d\n",
err);
return err;
}
if (c->dotu)
err = -ecode;
if (!err) {
err = p9_errstr2errno(ename, strlen(ename));
/* string match failed */
if (!err)
err = -ESERVERFAULT;
}
P9_DPRINTK(P9_DEBUG_9P, "<<< RERROR (%d) %s\n", -ecode, ename);
kfree(ename);
} else
err = 0;
return err;
}
/**
* p9_client_flush - flush (cancel) a request
* c: client state
* req: request to cancel
*
* This sents a flush for a particular requests and links
* the flush request to the original request. The current
* code only supports a single flush request although the protocol
* allows for multiple flush requests to be sent for a single request.
*
*/
static int p9_client_flush(struct p9_client *c, struct p9_req_t *oldreq)
{
struct p9_req_t *req;
int16_t oldtag;
int err;
err = p9_parse_header(oldreq->tc, NULL, NULL, &oldtag, 1);
if (err)
return err;
P9_DPRINTK(P9_DEBUG_9P, ">>> TFLUSH tag %d\n", oldtag);
req = p9_client_rpc(c, P9_TFLUSH, "w", oldtag);
if (IS_ERR(req))
return PTR_ERR(req);
req->flush_tag = oldtag;
/* we don't free anything here because RPC isn't complete */
return 0;
}
/**
* p9_client_rpc - issue a request and wait for a response
* @c: client session
* @type: type of request
* @fmt: protocol format string (see protocol.c)
*
* Returns request structure (which client must free using p9_free_req)
*/
static struct p9_req_t *
p9_client_rpc(struct p9_client *c, int8_t type, const char *fmt, ...)
{
va_list ap;
int tag, err;
struct p9_req_t *req;
unsigned long flags;
int sigpending;
int flushed = 0;
P9_DPRINTK(P9_DEBUG_MUX, "client %p op %d\n", c, type);
if (c->status != Connected)
return ERR_PTR(-EIO);
if (signal_pending(current)) {
sigpending = 1;
clear_thread_flag(TIF_SIGPENDING);
} else
sigpending = 0;
tag = P9_NOTAG;
if (type != P9_TVERSION) {
tag = p9_idpool_get(c->tagpool);
if (tag < 0)
return ERR_PTR(-ENOMEM);
}
req = p9_tag_alloc(c, tag);
if (IS_ERR(req))
return req;
/* marshall the data */
p9pdu_prepare(req->tc, tag, type);
va_start(ap, fmt);
err = p9pdu_vwritef(req->tc, c->dotu, fmt, ap);
va_end(ap);
p9pdu_finalize(req->tc);
err = c->trans_mod->request(c, req);
if (err < 0) {
c->status = Disconnected;
goto reterr;
}
/* if it was a flush we just transmitted, return our tag */
if (type == P9_TFLUSH)
return req;
again:
P9_DPRINTK(P9_DEBUG_MUX, "wait %p tag: %d\n", req->wq, tag);
err = wait_event_interruptible(*req->wq,
req->status >= REQ_STATUS_RCVD);
P9_DPRINTK(P9_DEBUG_MUX, "wait %p tag: %d returned %d (flushed=%d)\n",
req->wq, tag, err, flushed);
if (req->status == REQ_STATUS_ERROR) {
P9_DPRINTK(P9_DEBUG_ERROR, "req_status error %d\n", req->t_err);
err = req->t_err;
} else if (err == -ERESTARTSYS && flushed) {
P9_DPRINTK(P9_DEBUG_MUX, "flushed - going again\n");
goto again;
} else if (req->status == REQ_STATUS_FLSHD) {
P9_DPRINTK(P9_DEBUG_MUX, "flushed - erestartsys\n");
err = -ERESTARTSYS;
}
if ((err == -ERESTARTSYS) && (c->status == Connected) && (!flushed)) {
P9_DPRINTK(P9_DEBUG_MUX, "flushing\n");
spin_lock_irqsave(&c->lock, flags);
if (req->status == REQ_STATUS_SENT)
req->status = REQ_STATUS_FLSH;
spin_unlock_irqrestore(&c->lock, flags);
sigpending = 1;
flushed = 1;
clear_thread_flag(TIF_SIGPENDING);
if (c->trans_mod->cancel(c, req)) {
err = p9_client_flush(c, req);
if (err == 0)
goto again;
}
}
if (sigpending) {
spin_lock_irqsave(&current->sighand->siglock, flags);
recalc_sigpending();
spin_unlock_irqrestore(&current->sighand->siglock, flags);
}
if (err < 0)
goto reterr;
err = p9_check_errors(c, req);
if (!err) {
P9_DPRINTK(P9_DEBUG_MUX, "exit: client %p op %d\n", c, type);
return req;
}
reterr:
P9_DPRINTK(P9_DEBUG_MUX, "exit: client %p op %d error: %d\n", c, type,
err);
p9_free_req(c, req);
return ERR_PTR(err);
}
static struct p9_fid *p9_fid_create(struct p9_client *clnt)
{
int err;
struct p9_fid *fid;
P9_DPRINTK(P9_DEBUG_FID, "clnt %p\n", clnt);
fid = kmalloc(sizeof(struct p9_fid), GFP_KERNEL);
if (!fid)
return ERR_PTR(-ENOMEM);
fid->fid = p9_idpool_get(clnt->fidpool);
if (fid->fid < 0) {
err = -ENOSPC;
goto error;
}
memset(&fid->qid, 0, sizeof(struct p9_qid));
fid->mode = -1;
fid->rdir_fpos = 0;
fid->uid = current->fsuid;
fid->clnt = clnt;
fid->aux = NULL;
spin_lock(&clnt->lock);
list_add(&fid->flist, &clnt->fidlist);
spin_unlock(&clnt->lock);
return fid;
error:
kfree(fid);
return ERR_PTR(err);
}
static void p9_fid_destroy(struct p9_fid *fid)
{
struct p9_client *clnt;
P9_DPRINTK(P9_DEBUG_FID, "fid %d\n", fid->fid);
clnt = fid->clnt;
p9_idpool_put(fid->fid, clnt->fidpool);
spin_lock(&clnt->lock);
list_del(&fid->flist);
spin_unlock(&clnt->lock);
kfree(fid);
}
int p9_client_version(struct p9_client *c)
{ {
return c->trans->rpc(c->trans, tc, rc); int err = 0;
struct p9_req_t *req;
char *version;
int msize;
P9_DPRINTK(P9_DEBUG_9P, ">>> TVERSION msize %d extended %d\n",
c->msize, c->dotu);
req = p9_client_rpc(c, P9_TVERSION, "ds", c->msize,
c->dotu ? "9P2000.u" : "9P2000");
if (IS_ERR(req))
return PTR_ERR(req);
err = p9pdu_readf(req->rc, c->dotu, "ds", &msize, &version);
if (err) {
P9_DPRINTK(P9_DEBUG_9P, "version error %d\n", err);
p9pdu_dump(1, req->rc);
goto error;
}
P9_DPRINTK(P9_DEBUG_9P, "<<< RVERSION msize %d %s\n", msize, version);
if (!memcmp(version, "9P2000.u", 8))
c->dotu = 1;
else if (!memcmp(version, "9P2000", 6))
c->dotu = 0;
else {
err = -EREMOTEIO;
goto error;
}
if (msize < c->msize)
c->msize = msize;
error:
kfree(version);
p9_free_req(c, req);
return err;
} }
EXPORT_SYMBOL(p9_client_version);
struct p9_client *p9_client_create(const char *dev_name, char *options) struct p9_client *p9_client_create(const char *dev_name, char *options)
{ {
int err, n; int err;
struct p9_client *clnt; struct p9_client *clnt;
struct p9_fcall *tc, *rc;
struct p9_str *version;
err = 0; err = 0;
tc = NULL;
rc = NULL;
clnt = kmalloc(sizeof(struct p9_client), GFP_KERNEL); clnt = kmalloc(sizeof(struct p9_client), GFP_KERNEL);
if (!clnt) if (!clnt)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
...@@ -164,6 +718,8 @@ struct p9_client *p9_client_create(const char *dev_name, char *options) ...@@ -164,6 +718,8 @@ struct p9_client *p9_client_create(const char *dev_name, char *options)
goto error; goto error;
} }
p9_tag_init(clnt);
err = parse_opts(options, clnt); err = parse_opts(options, clnt);
if (err < 0) if (err < 0)
goto error; goto error;
...@@ -175,53 +731,23 @@ struct p9_client *p9_client_create(const char *dev_name, char *options) ...@@ -175,53 +731,23 @@ struct p9_client *p9_client_create(const char *dev_name, char *options)
goto error; goto error;
} }
P9_DPRINTK(P9_DEBUG_9P, "clnt %p trans %p msize %d dotu %d\n", P9_DPRINTK(P9_DEBUG_MUX, "clnt %p trans %p msize %d dotu %d\n",
clnt, clnt->trans_mod, clnt->msize, clnt->dotu); clnt, clnt->trans_mod, clnt->msize, clnt->dotu);
err = clnt->trans_mod->create(clnt, dev_name, options);
clnt->trans = clnt->trans_mod->create(dev_name, options, clnt->msize, if (err)
clnt->dotu);
if (IS_ERR(clnt->trans)) {
err = PTR_ERR(clnt->trans);
clnt->trans = NULL;
goto error; goto error;
}
if ((clnt->msize+P9_IOHDRSZ) > clnt->trans_mod->maxsize) if ((clnt->msize+P9_IOHDRSZ) > clnt->trans_mod->maxsize)
clnt->msize = clnt->trans_mod->maxsize-P9_IOHDRSZ; clnt->msize = clnt->trans_mod->maxsize-P9_IOHDRSZ;
tc = p9_create_tversion(clnt->msize, clnt->dotu?"9P2000.u":"9P2000"); err = p9_client_version(clnt);
if (IS_ERR(tc)) {
err = PTR_ERR(tc);
tc = NULL;
goto error;
}
err = p9_client_rpc(clnt, tc, &rc);
if (err) if (err)
goto error; goto error;
version = &rc->params.rversion.version;
if (version->len == 8 && !memcmp(version->str, "9P2000.u", 8))
clnt->dotu = 1;
else if (version->len == 6 && !memcmp(version->str, "9P2000", 6))
clnt->dotu = 0;
else {
err = -EREMOTEIO;
goto error;
}
n = rc->params.rversion.msize;
if (n < clnt->msize)
clnt->msize = n;
kfree(tc);
kfree(rc);
return clnt; return clnt;
error: error:
kfree(tc);
kfree(rc);
p9_client_destroy(clnt); p9_client_destroy(clnt);
return ERR_PTR(err); return ERR_PTR(err);
} }
...@@ -231,13 +757,10 @@ void p9_client_destroy(struct p9_client *clnt) ...@@ -231,13 +757,10 @@ void p9_client_destroy(struct p9_client *clnt)
{ {
struct p9_fid *fid, *fidptr; struct p9_fid *fid, *fidptr;
P9_DPRINTK(P9_DEBUG_9P, "clnt %p\n", clnt); P9_DPRINTK(P9_DEBUG_MUX, "clnt %p\n", clnt);
if (clnt->trans) { if (clnt->trans_mod)
clnt->trans->close(clnt->trans); clnt->trans_mod->close(clnt);
kfree(clnt->trans);
clnt->trans = NULL;
}
v9fs_put_trans(clnt->trans_mod); v9fs_put_trans(clnt->trans_mod);
...@@ -247,6 +770,8 @@ void p9_client_destroy(struct p9_client *clnt) ...@@ -247,6 +770,8 @@ void p9_client_destroy(struct p9_client *clnt)
if (clnt->fidpool) if (clnt->fidpool)
p9_idpool_destroy(clnt->fidpool); p9_idpool_destroy(clnt->fidpool);
p9_tag_cleanup(clnt);
kfree(clnt); kfree(clnt);
} }
EXPORT_SYMBOL(p9_client_destroy); EXPORT_SYMBOL(p9_client_destroy);
...@@ -254,7 +779,7 @@ EXPORT_SYMBOL(p9_client_destroy); ...@@ -254,7 +779,7 @@ EXPORT_SYMBOL(p9_client_destroy);
void p9_client_disconnect(struct p9_client *clnt) void p9_client_disconnect(struct p9_client *clnt)
{ {
P9_DPRINTK(P9_DEBUG_9P, "clnt %p\n", clnt); P9_DPRINTK(P9_DEBUG_9P, "clnt %p\n", clnt);
clnt->trans->status = Disconnected; clnt->status = Disconnected;
} }
EXPORT_SYMBOL(p9_client_disconnect); EXPORT_SYMBOL(p9_client_disconnect);
...@@ -262,14 +787,13 @@ struct p9_fid *p9_client_attach(struct p9_client *clnt, struct p9_fid *afid, ...@@ -262,14 +787,13 @@ struct p9_fid *p9_client_attach(struct p9_client *clnt, struct p9_fid *afid,
char *uname, u32 n_uname, char *aname) char *uname, u32 n_uname, char *aname)
{ {
int err; int err;
struct p9_fcall *tc, *rc; struct p9_req_t *req;
struct p9_fid *fid; struct p9_fid *fid;
struct p9_qid qid;
P9_DPRINTK(P9_DEBUG_9P, "clnt %p afid %d uname %s aname %s\n", P9_DPRINTK(P9_DEBUG_9P, ">>> TATTACH afid %d uname %s aname %s\n",
clnt, afid?afid->fid:-1, uname, aname); afid ? afid->fid : -1, uname, aname);
err = 0; err = 0;
tc = NULL;
rc = NULL;
fid = p9_fid_create(clnt); fid = p9_fid_create(clnt);
if (IS_ERR(fid)) { if (IS_ERR(fid)) {
...@@ -278,73 +802,77 @@ struct p9_fid *p9_client_attach(struct p9_client *clnt, struct p9_fid *afid, ...@@ -278,73 +802,77 @@ struct p9_fid *p9_client_attach(struct p9_client *clnt, struct p9_fid *afid,
goto error; goto error;
} }
tc = p9_create_tattach(fid->fid, afid?afid->fid:P9_NOFID, uname, aname, req = p9_client_rpc(clnt, P9_TATTACH, "ddss?d", fid->fid,
n_uname, clnt->dotu); afid ? afid->fid : P9_NOFID, uname, aname, n_uname);
if (IS_ERR(tc)) { if (IS_ERR(req)) {
err = PTR_ERR(tc); err = PTR_ERR(req);
tc = NULL;
goto error; goto error;
} }
err = p9_client_rpc(clnt, tc, &rc); err = p9pdu_readf(req->rc, clnt->dotu, "Q", &qid);
if (err) if (err) {
p9pdu_dump(1, req->rc);
p9_free_req(clnt, req);
goto error; goto error;
}
P9_DPRINTK(P9_DEBUG_9P, "<<< RATTACH qid %x.%llx.%x\n",
qid.type, qid.path, qid.version);
memmove(&fid->qid, &rc->params.rattach.qid, sizeof(struct p9_qid)); memmove(&fid->qid, &qid, sizeof(struct p9_qid));
kfree(tc);
kfree(rc); p9_free_req(clnt, req);
return fid; return fid;
error: error:
kfree(tc);
kfree(rc);
if (fid) if (fid)
p9_fid_destroy(fid); p9_fid_destroy(fid);
return ERR_PTR(err); return ERR_PTR(err);
} }
EXPORT_SYMBOL(p9_client_attach); EXPORT_SYMBOL(p9_client_attach);
struct p9_fid *p9_client_auth(struct p9_client *clnt, char *uname, struct p9_fid *
u32 n_uname, char *aname) p9_client_auth(struct p9_client *clnt, char *uname, u32 n_uname, char *aname)
{ {
int err; int err;
struct p9_fcall *tc, *rc; struct p9_req_t *req;
struct p9_fid *fid; struct p9_qid qid;
struct p9_fid *afid;
P9_DPRINTK(P9_DEBUG_9P, "clnt %p uname %s aname %s\n", clnt, uname, P9_DPRINTK(P9_DEBUG_9P, ">>> TAUTH uname %s aname %s\n", uname, aname);
aname);
err = 0; err = 0;
tc = NULL;
rc = NULL;
fid = p9_fid_create(clnt); afid = p9_fid_create(clnt);
if (IS_ERR(fid)) { if (IS_ERR(afid)) {
err = PTR_ERR(fid); err = PTR_ERR(afid);
fid = NULL; afid = NULL;
goto error; goto error;
} }
tc = p9_create_tauth(fid->fid, uname, aname, n_uname, clnt->dotu); req = p9_client_rpc(clnt, P9_TAUTH, "dss?d",
if (IS_ERR(tc)) { afid ? afid->fid : P9_NOFID, uname, aname, n_uname);
err = PTR_ERR(tc); if (IS_ERR(req)) {
tc = NULL; err = PTR_ERR(req);
goto error; goto error;
} }
err = p9_client_rpc(clnt, tc, &rc); err = p9pdu_readf(req->rc, clnt->dotu, "Q", &qid);
if (err) if (err) {
p9pdu_dump(1, req->rc);
p9_free_req(clnt, req);
goto error; goto error;
}
memmove(&fid->qid, &rc->params.rauth.qid, sizeof(struct p9_qid)); P9_DPRINTK(P9_DEBUG_9P, "<<< RAUTH qid %x.%llx.%x\n",
kfree(tc); qid.type, qid.path, qid.version);
kfree(rc);
return fid; memmove(&afid->qid, &qid, sizeof(struct p9_qid));
p9_free_req(clnt, req);
return afid;
error: error:
kfree(tc); if (afid)
kfree(rc); p9_fid_destroy(afid);
if (fid)
p9_fid_destroy(fid);
return ERR_PTR(err); return ERR_PTR(err);
} }
EXPORT_SYMBOL(p9_client_auth); EXPORT_SYMBOL(p9_client_auth);
...@@ -353,15 +881,13 @@ struct p9_fid *p9_client_walk(struct p9_fid *oldfid, int nwname, char **wnames, ...@@ -353,15 +881,13 @@ struct p9_fid *p9_client_walk(struct p9_fid *oldfid, int nwname, char **wnames,
int clone) int clone)
{ {
int err; int err;
struct p9_fcall *tc, *rc;
struct p9_client *clnt; struct p9_client *clnt;
struct p9_fid *fid; struct p9_fid *fid;
struct p9_qid *wqids;
struct p9_req_t *req;
int16_t nwqids, count;
P9_DPRINTK(P9_DEBUG_9P, "fid %d nwname %d wname[0] %s\n",
oldfid->fid, nwname, wnames?wnames[0]:NULL);
err = 0; err = 0;
tc = NULL;
rc = NULL;
clnt = oldfid->clnt; clnt = oldfid->clnt;
if (clone) { if (clone) {
fid = p9_fid_create(clnt); fid = p9_fid_create(clnt);
...@@ -375,53 +901,49 @@ struct p9_fid *p9_client_walk(struct p9_fid *oldfid, int nwname, char **wnames, ...@@ -375,53 +901,49 @@ struct p9_fid *p9_client_walk(struct p9_fid *oldfid, int nwname, char **wnames,
} else } else
fid = oldfid; fid = oldfid;
tc = p9_create_twalk(oldfid->fid, fid->fid, nwname, wnames);
if (IS_ERR(tc)) { P9_DPRINTK(P9_DEBUG_9P, ">>> TWALK fids %d,%d nwname %d wname[0] %s\n",
err = PTR_ERR(tc); oldfid->fid, fid->fid, nwname, wnames ? wnames[0] : NULL);
tc = NULL;
req = p9_client_rpc(clnt, P9_TWALK, "ddT", oldfid->fid, fid->fid,
nwname, wnames);
if (IS_ERR(req)) {
err = PTR_ERR(req);
goto error; goto error;
} }
err = p9_client_rpc(clnt, tc, &rc); err = p9pdu_readf(req->rc, clnt->dotu, "R", &nwqids, &wqids);
if (err) { if (err) {
if (rc && rc->id == P9_RWALK) p9pdu_dump(1, req->rc);
p9_free_req(clnt, req);
goto clunk_fid; goto clunk_fid;
else
goto error;
} }
p9_free_req(clnt, req);
P9_DPRINTK(P9_DEBUG_9P, "<<< RWALK nwqid %d:\n", nwqids);
if (rc->params.rwalk.nwqid != nwname) { if (nwqids != nwname) {
err = -ENOENT; err = -ENOENT;
goto clunk_fid; goto clunk_fid;
} }
for (count = 0; count < nwqids; count++)
P9_DPRINTK(P9_DEBUG_9P, "<<< [%d] %x.%llx.%x\n",
count, wqids[count].type, wqids[count].path,
wqids[count].version);
if (nwname) if (nwname)
memmove(&fid->qid, memmove(&fid->qid, &wqids[nwqids - 1], sizeof(struct p9_qid));
&rc->params.rwalk.wqids[rc->params.rwalk.nwqid - 1],
sizeof(struct p9_qid));
else else
fid->qid = oldfid->qid; fid->qid = oldfid->qid;
kfree(tc);
kfree(rc);
return fid; return fid;
clunk_fid: clunk_fid:
kfree(tc); p9_client_clunk(fid);
kfree(rc); fid = NULL;
rc = NULL;
tc = p9_create_tclunk(fid->fid);
if (IS_ERR(tc)) {
err = PTR_ERR(tc);
tc = NULL;
goto error;
}
p9_client_rpc(clnt, tc, &rc);
error: error:
kfree(tc);
kfree(rc);
if (fid && (fid != oldfid)) if (fid && (fid != oldfid))
p9_fid_destroy(fid); p9_fid_destroy(fid);
...@@ -432,35 +954,39 @@ EXPORT_SYMBOL(p9_client_walk); ...@@ -432,35 +954,39 @@ EXPORT_SYMBOL(p9_client_walk);
int p9_client_open(struct p9_fid *fid, int mode) int p9_client_open(struct p9_fid *fid, int mode)
{ {
int err; int err;
struct p9_fcall *tc, *rc;
struct p9_client *clnt; struct p9_client *clnt;
struct p9_req_t *req;
struct p9_qid qid;
int iounit;
P9_DPRINTK(P9_DEBUG_9P, "fid %d mode %d\n", fid->fid, mode); P9_DPRINTK(P9_DEBUG_9P, ">>> TOPEN fid %d mode %d\n", fid->fid, mode);
err = 0; err = 0;
tc = NULL;
rc = NULL;
clnt = fid->clnt; clnt = fid->clnt;
if (fid->mode != -1) if (fid->mode != -1)
return -EINVAL; return -EINVAL;
tc = p9_create_topen(fid->fid, mode); req = p9_client_rpc(clnt, P9_TOPEN, "db", fid->fid, mode);
if (IS_ERR(tc)) { if (IS_ERR(req)) {
err = PTR_ERR(tc); err = PTR_ERR(req);
tc = NULL; goto error;
goto done;
} }
err = p9_client_rpc(clnt, tc, &rc); err = p9pdu_readf(req->rc, clnt->dotu, "Qd", &qid, &iounit);
if (err) if (err) {
goto done; p9pdu_dump(1, req->rc);
goto free_and_error;
}
P9_DPRINTK(P9_DEBUG_9P, "<<< ROPEN qid %x.%llx.%x iounit %x\n",
qid.type, qid.path, qid.version, iounit);
fid->mode = mode; fid->mode = mode;
fid->iounit = rc->params.ropen.iounit; fid->iounit = iounit;
done: free_and_error:
kfree(tc); p9_free_req(clnt, req);
kfree(rc); error:
return err; return err;
} }
EXPORT_SYMBOL(p9_client_open); EXPORT_SYMBOL(p9_client_open);
...@@ -469,37 +995,41 @@ int p9_client_fcreate(struct p9_fid *fid, char *name, u32 perm, int mode, ...@@ -469,37 +995,41 @@ int p9_client_fcreate(struct p9_fid *fid, char *name, u32 perm, int mode,
char *extension) char *extension)
{ {
int err; int err;
struct p9_fcall *tc, *rc;
struct p9_client *clnt; struct p9_client *clnt;
struct p9_req_t *req;
struct p9_qid qid;
int iounit;
P9_DPRINTK(P9_DEBUG_9P, "fid %d name %s perm %d mode %d\n", fid->fid, P9_DPRINTK(P9_DEBUG_9P, ">>> TCREATE fid %d name %s perm %d mode %d\n",
name, perm, mode); fid->fid, name, perm, mode);
err = 0; err = 0;
tc = NULL;
rc = NULL;
clnt = fid->clnt; clnt = fid->clnt;
if (fid->mode != -1) if (fid->mode != -1)
return -EINVAL; return -EINVAL;
tc = p9_create_tcreate(fid->fid, name, perm, mode, extension, req = p9_client_rpc(clnt, P9_TCREATE, "dsdb?s", fid->fid, name, perm,
clnt->dotu); mode, extension);
if (IS_ERR(tc)) { if (IS_ERR(req)) {
err = PTR_ERR(tc); err = PTR_ERR(req);
tc = NULL; goto error;
goto done;
} }
err = p9_client_rpc(clnt, tc, &rc); err = p9pdu_readf(req->rc, clnt->dotu, "Qd", &qid, &iounit);
if (err) if (err) {
goto done; p9pdu_dump(1, req->rc);
goto free_and_error;
}
P9_DPRINTK(P9_DEBUG_9P, "<<< RCREATE qid %x.%llx.%x iounit %x\n",
qid.type, qid.path, qid.version, iounit);
fid->mode = mode; fid->mode = mode;
fid->iounit = rc->params.ropen.iounit; fid->iounit = iounit;
done: free_and_error:
kfree(tc); p9_free_req(clnt, req);
kfree(rc); error:
return err; return err;
} }
EXPORT_SYMBOL(p9_client_fcreate); EXPORT_SYMBOL(p9_client_fcreate);
...@@ -507,31 +1037,25 @@ EXPORT_SYMBOL(p9_client_fcreate); ...@@ -507,31 +1037,25 @@ EXPORT_SYMBOL(p9_client_fcreate);
int p9_client_clunk(struct p9_fid *fid) int p9_client_clunk(struct p9_fid *fid)
{ {
int err; int err;
struct p9_fcall *tc, *rc;
struct p9_client *clnt; struct p9_client *clnt;
struct p9_req_t *req;
P9_DPRINTK(P9_DEBUG_9P, "fid %d\n", fid->fid); P9_DPRINTK(P9_DEBUG_9P, ">>> TCLUNK fid %d\n", fid->fid);
err = 0; err = 0;
tc = NULL;
rc = NULL;
clnt = fid->clnt; clnt = fid->clnt;
tc = p9_create_tclunk(fid->fid); req = p9_client_rpc(clnt, P9_TCLUNK, "d", fid->fid);
if (IS_ERR(tc)) { if (IS_ERR(req)) {
err = PTR_ERR(tc); err = PTR_ERR(req);
tc = NULL; goto error;
goto done;
} }
err = p9_client_rpc(clnt, tc, &rc); P9_DPRINTK(P9_DEBUG_9P, "<<< RCLUNK fid %d\n", fid->fid);
if (err)
goto done;
p9_free_req(clnt, req);
p9_fid_destroy(fid); p9_fid_destroy(fid);
done: error:
kfree(tc);
kfree(rc);
return err; return err;
} }
EXPORT_SYMBOL(p9_client_clunk); EXPORT_SYMBOL(p9_client_clunk);
...@@ -539,46 +1063,41 @@ EXPORT_SYMBOL(p9_client_clunk); ...@@ -539,46 +1063,41 @@ EXPORT_SYMBOL(p9_client_clunk);
int p9_client_remove(struct p9_fid *fid) int p9_client_remove(struct p9_fid *fid)
{ {
int err; int err;
struct p9_fcall *tc, *rc;
struct p9_client *clnt; struct p9_client *clnt;
struct p9_req_t *req;
P9_DPRINTK(P9_DEBUG_9P, "fid %d\n", fid->fid); P9_DPRINTK(P9_DEBUG_9P, ">>> TREMOVE fid %d\n", fid->fid);
err = 0; err = 0;
tc = NULL;
rc = NULL;
clnt = fid->clnt; clnt = fid->clnt;
tc = p9_create_tremove(fid->fid); req = p9_client_rpc(clnt, P9_TREMOVE, "d", fid->fid);
if (IS_ERR(tc)) { if (IS_ERR(req)) {
err = PTR_ERR(tc); err = PTR_ERR(req);
tc = NULL; goto error;
goto done;
} }
err = p9_client_rpc(clnt, tc, &rc); P9_DPRINTK(P9_DEBUG_9P, "<<< RREMOVE fid %d\n", fid->fid);
if (err)
goto done;
p9_free_req(clnt, req);
p9_fid_destroy(fid); p9_fid_destroy(fid);
done: error:
kfree(tc);
kfree(rc);
return err; return err;
} }
EXPORT_SYMBOL(p9_client_remove); EXPORT_SYMBOL(p9_client_remove);
int p9_client_read(struct p9_fid *fid, char *data, u64 offset, u32 count) int
p9_client_read(struct p9_fid *fid, char *data, char __user *udata, u64 offset,
u32 count)
{ {
int err, n, rsize, total; int err, rsize, total;
struct p9_fcall *tc, *rc;
struct p9_client *clnt; struct p9_client *clnt;
struct p9_req_t *req;
char *dataptr;
P9_DPRINTK(P9_DEBUG_9P, "fid %d offset %llu %d\n", fid->fid, P9_DPRINTK(P9_DEBUG_9P, ">>> TREAD fid %d offset %llu %d\n", fid->fid,
(long long unsigned) offset, count); (long long unsigned) offset, count);
err = 0; err = 0;
tc = NULL;
rc = NULL;
clnt = fid->clnt; clnt = fid->clnt;
total = 0; total = 0;
...@@ -586,174 +1105,57 @@ int p9_client_read(struct p9_fid *fid, char *data, u64 offset, u32 count) ...@@ -586,174 +1105,57 @@ int p9_client_read(struct p9_fid *fid, char *data, u64 offset, u32 count)
if (!rsize || rsize > clnt->msize-P9_IOHDRSZ) if (!rsize || rsize > clnt->msize-P9_IOHDRSZ)
rsize = clnt->msize - P9_IOHDRSZ; rsize = clnt->msize - P9_IOHDRSZ;
do {
if (count < rsize) if (count < rsize)
rsize = count; rsize = count;
tc = p9_create_tread(fid->fid, offset, rsize); req = p9_client_rpc(clnt, P9_TREAD, "dqd", fid->fid, offset, rsize);
if (IS_ERR(tc)) { if (IS_ERR(req)) {
err = PTR_ERR(tc); err = PTR_ERR(req);
tc = NULL;
goto error; goto error;
} }
err = p9_client_rpc(clnt, tc, &rc); err = p9pdu_readf(req->rc, clnt->dotu, "D", &count, &dataptr);
if (err) if (err) {
goto error; p9pdu_dump(1, req->rc);
goto free_and_error;
n = rc->params.rread.count;
if (n > count)
n = count;
memmove(data, rc->params.rread.data, n);
count -= n;
data += n;
offset += n;
total += n;
kfree(tc);
tc = NULL;
kfree(rc);
rc = NULL;
} while (count > 0 && n == rsize);
return total;
error:
kfree(tc);
kfree(rc);
return err;
}
EXPORT_SYMBOL(p9_client_read);
int p9_client_write(struct p9_fid *fid, char *data, u64 offset, u32 count)
{
int err, n, rsize, total;
struct p9_fcall *tc, *rc;
struct p9_client *clnt;
P9_DPRINTK(P9_DEBUG_9P, "fid %d offset %llu count %d\n", fid->fid,
(long long unsigned) offset, count);
err = 0;
tc = NULL;
rc = NULL;
clnt = fid->clnt;
total = 0;
rsize = fid->iounit;
if (!rsize || rsize > clnt->msize-P9_IOHDRSZ)
rsize = clnt->msize - P9_IOHDRSZ;
do {
if (count < rsize)
rsize = count;
tc = p9_create_twrite(fid->fid, offset, rsize, data);
if (IS_ERR(tc)) {
err = PTR_ERR(tc);
tc = NULL;
goto error;
} }
err = p9_client_rpc(clnt, tc, &rc); P9_DPRINTK(P9_DEBUG_9P, "<<< RREAD count %d\n", count);
if (err)
goto error;
n = rc->params.rread.count;
count -= n;
data += n;
offset += n;
total += n;
kfree(tc);
tc = NULL;
kfree(rc);
rc = NULL;
} while (count > 0);
return total;
error:
kfree(tc);
kfree(rc);
return err;
}
EXPORT_SYMBOL(p9_client_write);
int
p9_client_uread(struct p9_fid *fid, char __user *data, u64 offset, u32 count)
{
int err, n, rsize, total;
struct p9_fcall *tc, *rc;
struct p9_client *clnt;
P9_DPRINTK(P9_DEBUG_9P, "fid %d offset %llu count %d\n", fid->fid,
(long long unsigned) offset, count);
err = 0;
tc = NULL;
rc = NULL;
clnt = fid->clnt;
total = 0;
rsize = fid->iounit;
if (!rsize || rsize > clnt->msize-P9_IOHDRSZ)
rsize = clnt->msize - P9_IOHDRSZ;
do {
if (count < rsize)
rsize = count;
tc = p9_create_tread(fid->fid, offset, rsize); if (data) {
if (IS_ERR(tc)) { memmove(data, dataptr, count);
err = PTR_ERR(tc); data += count;
tc = NULL;
goto error;
} }
err = p9_client_rpc(clnt, tc, &rc); if (udata) {
if (err) err = copy_to_user(udata, dataptr, count);
goto error;
n = rc->params.rread.count;
if (n > count)
n = count;
err = copy_to_user(data, rc->params.rread.data, n);
if (err) { if (err) {
err = -EFAULT; err = -EFAULT;
goto error; goto free_and_error;
}
} }
count -= n; p9_free_req(clnt, req);
data += n; return count;
offset += n;
total += n;
kfree(tc);
tc = NULL;
kfree(rc);
rc = NULL;
} while (count > 0 && n == rsize);
return total;
free_and_error:
p9_free_req(clnt, req);
error: error:
kfree(tc);
kfree(rc);
return err; return err;
} }
EXPORT_SYMBOL(p9_client_uread); EXPORT_SYMBOL(p9_client_read);
int int
p9_client_uwrite(struct p9_fid *fid, const char __user *data, u64 offset, p9_client_write(struct p9_fid *fid, char *data, const char __user *udata,
u32 count) u64 offset, u32 count)
{ {
int err, n, rsize, total; int err, rsize, total;
struct p9_fcall *tc, *rc;
struct p9_client *clnt; struct p9_client *clnt;
struct p9_req_t *req;
P9_DPRINTK(P9_DEBUG_9P, "fid %d offset %llu count %d\n", fid->fid, P9_DPRINTK(P9_DEBUG_9P, ">>> TWRITE fid %d offset %llu count %d\n",
(long long unsigned) offset, count); fid->fid, (long long unsigned) offset, count);
err = 0; err = 0;
tc = NULL;
rc = NULL;
clnt = fid->clnt; clnt = fid->clnt;
total = 0; total = 0;
...@@ -761,325 +1163,114 @@ p9_client_uwrite(struct p9_fid *fid, const char __user *data, u64 offset, ...@@ -761,325 +1163,114 @@ p9_client_uwrite(struct p9_fid *fid, const char __user *data, u64 offset,
if (!rsize || rsize > clnt->msize-P9_IOHDRSZ) if (!rsize || rsize > clnt->msize-P9_IOHDRSZ)
rsize = clnt->msize - P9_IOHDRSZ; rsize = clnt->msize - P9_IOHDRSZ;
do {
if (count < rsize) if (count < rsize)
rsize = count; rsize = count;
if (data)
tc = p9_create_twrite_u(fid->fid, offset, rsize, data); req = p9_client_rpc(clnt, P9_TWRITE, "dqD", fid->fid, offset,
if (IS_ERR(tc)) { rsize, data);
err = PTR_ERR(tc); else
tc = NULL; req = p9_client_rpc(clnt, P9_TWRITE, "dqU", fid->fid, offset,
rsize, udata);
if (IS_ERR(req)) {
err = PTR_ERR(req);
goto error; goto error;
} }
err = p9_client_rpc(clnt, tc, &rc); err = p9pdu_readf(req->rc, clnt->dotu, "d", &count);
if (err) if (err) {
goto error; p9pdu_dump(1, req->rc);
goto free_and_error;
}
n = rc->params.rread.count; P9_DPRINTK(P9_DEBUG_9P, "<<< RWRITE count %d\n", count);
count -= n;
data += n;
offset += n;
total += n;
kfree(tc);
tc = NULL;
kfree(rc);
rc = NULL;
} while (count > 0);
return total; p9_free_req(clnt, req);
return count;
free_and_error:
p9_free_req(clnt, req);
error: error:
kfree(tc);
kfree(rc);
return err; return err;
} }
EXPORT_SYMBOL(p9_client_uwrite); EXPORT_SYMBOL(p9_client_write);
int p9_client_readn(struct p9_fid *fid, char *data, u64 offset, u32 count)
{
int n, total;
P9_DPRINTK(P9_DEBUG_9P, "fid %d offset %llu count %d\n", fid->fid,
(long long unsigned) offset, count);
n = 0;
total = 0;
while (count) {
n = p9_client_read(fid, data, offset, count);
if (n <= 0)
break;
data += n;
offset += n;
count -= n;
total += n;
}
if (n < 0)
total = n;
return total;
}
EXPORT_SYMBOL(p9_client_readn);
struct p9_stat *p9_client_stat(struct p9_fid *fid) struct p9_wstat *p9_client_stat(struct p9_fid *fid)
{ {
int err; int err;
struct p9_fcall *tc, *rc;
struct p9_client *clnt; struct p9_client *clnt;
struct p9_stat *ret; struct p9_wstat *ret = kmalloc(sizeof(struct p9_wstat), GFP_KERNEL);
struct p9_req_t *req;
u16 ignored;
P9_DPRINTK(P9_DEBUG_9P, ">>> TSTAT fid %d\n", fid->fid);
if (!ret)
return ERR_PTR(-ENOMEM);
P9_DPRINTK(P9_DEBUG_9P, "fid %d\n", fid->fid);
err = 0; err = 0;
tc = NULL;
rc = NULL;
ret = NULL;
clnt = fid->clnt; clnt = fid->clnt;
tc = p9_create_tstat(fid->fid); req = p9_client_rpc(clnt, P9_TSTAT, "d", fid->fid);
if (IS_ERR(tc)) { if (IS_ERR(req)) {
err = PTR_ERR(tc); err = PTR_ERR(req);
tc = NULL;
goto error; goto error;
} }
err = p9_client_rpc(clnt, tc, &rc); err = p9pdu_readf(req->rc, clnt->dotu, "wS", &ignored, ret);
if (err) if (err) {
goto error; ret = ERR_PTR(err);
p9pdu_dump(1, req->rc);
ret = p9_clone_stat(&rc->params.rstat.stat, clnt->dotu); goto free_and_error;
if (IS_ERR(ret)) {
err = PTR_ERR(ret);
ret = NULL;
goto error;
} }
kfree(tc); P9_DPRINTK(P9_DEBUG_9P,
kfree(rc); "<<< RSTAT sz=%x type=%x dev=%x qid=%x.%llx.%x\n"
return ret; "<<< mode=%8.8x atime=%8.8x mtime=%8.8x length=%llx\n"
"<<< name=%s uid=%s gid=%s muid=%s extension=(%s)\n"
"<<< uid=%d gid=%d n_muid=%d\n",
ret->size, ret->type, ret->dev, ret->qid.type,
ret->qid.path, ret->qid.version, ret->mode,
ret->atime, ret->mtime, ret->length, ret->name,
ret->uid, ret->gid, ret->muid, ret->extension,
ret->n_uid, ret->n_gid, ret->n_muid);
free_and_error:
p9_free_req(clnt, req);
error: error:
kfree(tc); return ret;
kfree(rc);
kfree(ret);
return ERR_PTR(err);
} }
EXPORT_SYMBOL(p9_client_stat); EXPORT_SYMBOL(p9_client_stat);
int p9_client_wstat(struct p9_fid *fid, struct p9_wstat *wst) int p9_client_wstat(struct p9_fid *fid, struct p9_wstat *wst)
{ {
int err; int err;
struct p9_fcall *tc, *rc; struct p9_req_t *req;
struct p9_client *clnt;
P9_DPRINTK(P9_DEBUG_9P, "fid %d\n", fid->fid);
err = 0;
tc = NULL;
rc = NULL;
clnt = fid->clnt;
tc = p9_create_twstat(fid->fid, wst, clnt->dotu);
if (IS_ERR(tc)) {
err = PTR_ERR(tc);
tc = NULL;
goto done;
}
err = p9_client_rpc(clnt, tc, &rc);
done:
kfree(tc);
kfree(rc);
return err;
}
EXPORT_SYMBOL(p9_client_wstat);
struct p9_stat *p9_client_dirread(struct p9_fid *fid, u64 offset)
{
int err, n, m;
struct p9_fcall *tc, *rc;
struct p9_client *clnt; struct p9_client *clnt;
struct p9_stat st, *ret;
P9_DPRINTK(P9_DEBUG_9P, "fid %d offset %llu\n", fid->fid, P9_DPRINTK(P9_DEBUG_9P, ">>> TWSTAT fid %d\n", fid->fid);
(long long unsigned) offset); P9_DPRINTK(P9_DEBUG_9P,
" sz=%x type=%x dev=%x qid=%x.%llx.%x\n"
" mode=%8.8x atime=%8.8x mtime=%8.8x length=%llx\n"
" name=%s uid=%s gid=%s muid=%s extension=(%s)\n"
" uid=%d gid=%d n_muid=%d\n",
wst->size, wst->type, wst->dev, wst->qid.type,
wst->qid.path, wst->qid.version, wst->mode,
wst->atime, wst->mtime, wst->length, wst->name,
wst->uid, wst->gid, wst->muid, wst->extension,
wst->n_uid, wst->n_gid, wst->n_muid);
err = 0; err = 0;
tc = NULL;
rc = NULL;
ret = NULL;
clnt = fid->clnt; clnt = fid->clnt;
/* if the offset is below or above the current response, free it */ req = p9_client_rpc(clnt, P9_TWSTAT, "dwS", fid->fid, 0, wst);
if (offset < fid->rdir_fpos || (fid->rdir_fcall && if (IS_ERR(req)) {
offset >= fid->rdir_fpos+fid->rdir_fcall->params.rread.count)) { err = PTR_ERR(req);
fid->rdir_pos = 0;
if (fid->rdir_fcall)
fid->rdir_fpos += fid->rdir_fcall->params.rread.count;
kfree(fid->rdir_fcall);
fid->rdir_fcall = NULL;
if (offset < fid->rdir_fpos)
fid->rdir_fpos = 0;
}
if (!fid->rdir_fcall) {
n = fid->iounit;
if (!n || n > clnt->msize-P9_IOHDRSZ)
n = clnt->msize - P9_IOHDRSZ;
while (1) {
if (fid->rdir_fcall) {
fid->rdir_fpos +=
fid->rdir_fcall->params.rread.count;
kfree(fid->rdir_fcall);
fid->rdir_fcall = NULL;
}
tc = p9_create_tread(fid->fid, fid->rdir_fpos, n);
if (IS_ERR(tc)) {
err = PTR_ERR(tc);
tc = NULL;
goto error; goto error;
} }
err = p9_client_rpc(clnt, tc, &rc); P9_DPRINTK(P9_DEBUG_9P, "<<< RWSTAT fid %d\n", fid->fid);
if (err)
goto error;
n = rc->params.rread.count;
if (n == 0)
goto done;
fid->rdir_fcall = rc;
rc = NULL;
if (offset >= fid->rdir_fpos &&
offset < fid->rdir_fpos+n)
break;
}
fid->rdir_pos = 0;
}
m = offset - fid->rdir_fpos;
if (m < 0)
goto done;
n = p9_deserialize_stat(fid->rdir_fcall->params.rread.data + m,
fid->rdir_fcall->params.rread.count - m, &st, clnt->dotu);
if (!n) {
err = -EIO;
goto error;
}
fid->rdir_pos += n;
st.size = n;
ret = p9_clone_stat(&st, clnt->dotu);
if (IS_ERR(ret)) {
err = PTR_ERR(ret);
ret = NULL;
goto error;
}
done:
kfree(tc);
kfree(rc);
return ret;
p9_free_req(clnt, req);
error: error:
kfree(tc); return err;
kfree(rc);
kfree(ret);
return ERR_PTR(err);
}
EXPORT_SYMBOL(p9_client_dirread);
static struct p9_stat *p9_clone_stat(struct p9_stat *st, int dotu)
{
int n;
char *p;
struct p9_stat *ret;
n = sizeof(struct p9_stat) + st->name.len + st->uid.len + st->gid.len +
st->muid.len;
if (dotu)
n += st->extension.len;
ret = kmalloc(n, GFP_KERNEL);
if (!ret)
return ERR_PTR(-ENOMEM);
memmove(ret, st, sizeof(struct p9_stat));
p = ((char *) ret) + sizeof(struct p9_stat);
memmove(p, st->name.str, st->name.len);
ret->name.str = p;
p += st->name.len;
memmove(p, st->uid.str, st->uid.len);
ret->uid.str = p;
p += st->uid.len;
memmove(p, st->gid.str, st->gid.len);
ret->gid.str = p;
p += st->gid.len;
memmove(p, st->muid.str, st->muid.len);
ret->muid.str = p;
p += st->muid.len;
if (dotu) {
memmove(p, st->extension.str, st->extension.len);
ret->extension.str = p;
p += st->extension.len;
}
return ret;
}
static struct p9_fid *p9_fid_create(struct p9_client *clnt)
{
int err;
struct p9_fid *fid;
P9_DPRINTK(P9_DEBUG_9P, "clnt %p\n", clnt);
fid = kmalloc(sizeof(struct p9_fid), GFP_KERNEL);
if (!fid)
return ERR_PTR(-ENOMEM);
fid->fid = p9_idpool_get(clnt->fidpool);
if (fid->fid < 0) {
err = -ENOSPC;
goto error;
}
memset(&fid->qid, 0, sizeof(struct p9_qid));
fid->mode = -1;
fid->rdir_fpos = 0;
fid->rdir_pos = 0;
fid->rdir_fcall = NULL;
fid->uid = current->fsuid;
fid->clnt = clnt;
fid->aux = NULL;
spin_lock(&clnt->lock);
list_add(&fid->flist, &clnt->fidlist);
spin_unlock(&clnt->lock);
return fid;
error:
kfree(fid);
return ERR_PTR(err);
}
static void p9_fid_destroy(struct p9_fid *fid)
{
struct p9_client *clnt;
P9_DPRINTK(P9_DEBUG_9P, "fid %d\n", fid->fid);
clnt = fid->clnt;
p9_idpool_put(fid->fid, clnt->fidpool);
spin_lock(&clnt->lock);
list_del(&fid->flist);
spin_unlock(&clnt->lock);
kfree(fid->rdir_fcall);
kfree(fid);
} }
EXPORT_SYMBOL(p9_client_wstat);
/*
* net/9p/conv.c
*
* 9P protocol conversion functions
*
* Copyright (C) 2004, 2005 by Latchesar Ionkov <lucho@ionkov.net>
* Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
* Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
*
* 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
*
*/
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/idr.h>
#include <linux/uaccess.h>
#include <net/9p/9p.h>
/*
* Buffer to help with string parsing
*/
struct cbuf {
unsigned char *sp;
unsigned char *p;
unsigned char *ep;
};
static inline void buf_init(struct cbuf *buf, void *data, int datalen)
{
buf->sp = buf->p = data;
buf->ep = data + datalen;
}
static inline int buf_check_overflow(struct cbuf *buf)
{
return buf->p > buf->ep;
}
static int buf_check_size(struct cbuf *buf, int len)
{
if (buf->p + len > buf->ep) {
if (buf->p < buf->ep) {
P9_EPRINTK(KERN_ERR,
"buffer overflow: want %d has %d\n", len,
(int)(buf->ep - buf->p));
dump_stack();
buf->p = buf->ep + 1;
}
return 0;
}
return 1;
}
static void *buf_alloc(struct cbuf *buf, int len)
{
void *ret = NULL;
if (buf_check_size(buf, len)) {
ret = buf->p;
buf->p += len;
}
return ret;
}
static void buf_put_int8(struct cbuf *buf, u8 val)
{
if (buf_check_size(buf, 1)) {
buf->p[0] = val;
buf->p++;
}
}
static void buf_put_int16(struct cbuf *buf, u16 val)
{
if (buf_check_size(buf, 2)) {
*(__le16 *) buf->p = cpu_to_le16(val);
buf->p += 2;
}
}
static void buf_put_int32(struct cbuf *buf, u32 val)
{
if (buf_check_size(buf, 4)) {
*(__le32 *)buf->p = cpu_to_le32(val);
buf->p += 4;
}
}
static void buf_put_int64(struct cbuf *buf, u64 val)
{
if (buf_check_size(buf, 8)) {
*(__le64 *)buf->p = cpu_to_le64(val);
buf->p += 8;
}
}
static char *buf_put_stringn(struct cbuf *buf, const char *s, u16 slen)
{
char *ret;
ret = NULL;
if (buf_check_size(buf, slen + 2)) {
buf_put_int16(buf, slen);
ret = buf->p;
memcpy(buf->p, s, slen);
buf->p += slen;
}
return ret;
}
static u8 buf_get_int8(struct cbuf *buf)
{
u8 ret = 0;
if (buf_check_size(buf, 1)) {
ret = buf->p[0];
buf->p++;
}
return ret;
}
static u16 buf_get_int16(struct cbuf *buf)
{
u16 ret = 0;
if (buf_check_size(buf, 2)) {
ret = le16_to_cpu(*(__le16 *)buf->p);
buf->p += 2;
}
return ret;
}
static u32 buf_get_int32(struct cbuf *buf)
{
u32 ret = 0;
if (buf_check_size(buf, 4)) {
ret = le32_to_cpu(*(__le32 *)buf->p);
buf->p += 4;
}
return ret;
}
static u64 buf_get_int64(struct cbuf *buf)
{
u64 ret = 0;
if (buf_check_size(buf, 8)) {
ret = le64_to_cpu(*(__le64 *)buf->p);
buf->p += 8;
}
return ret;
}
static void buf_get_str(struct cbuf *buf, struct p9_str *vstr)
{
vstr->len = buf_get_int16(buf);
if (!buf_check_overflow(buf) && buf_check_size(buf, vstr->len)) {
vstr->str = buf->p;
buf->p += vstr->len;
} else {
vstr->len = 0;
vstr->str = NULL;
}
}
static void buf_get_qid(struct cbuf *bufp, struct p9_qid *qid)
{
qid->type = buf_get_int8(bufp);
qid->version = buf_get_int32(bufp);
qid->path = buf_get_int64(bufp);
}
/**
* p9_size_wstat - calculate the size of a variable length stat struct
* @wstat: metadata (stat) structure
* @dotu: non-zero if 9P2000.u
*
*/
static int p9_size_wstat(struct p9_wstat *wstat, int dotu)
{
int size = 0;
if (wstat == NULL) {
P9_EPRINTK(KERN_ERR, "p9_size_stat: got a NULL stat pointer\n");
return 0;
}
size = /* 2 + *//* size[2] */
2 + /* type[2] */
4 + /* dev[4] */
1 + /* qid.type[1] */
4 + /* qid.vers[4] */
8 + /* qid.path[8] */
4 + /* mode[4] */
4 + /* atime[4] */
4 + /* mtime[4] */
8 + /* length[8] */
8; /* minimum sum of string lengths */
if (wstat->name)
size += strlen(wstat->name);
if (wstat->uid)
size += strlen(wstat->uid);
if (wstat->gid)
size += strlen(wstat->gid);
if (wstat->muid)
size += strlen(wstat->muid);
if (dotu) {
size += 4 + /* n_uid[4] */
4 + /* n_gid[4] */
4 + /* n_muid[4] */
2; /* string length of extension[4] */
if (wstat->extension)
size += strlen(wstat->extension);
}
return size;
}
/**
* buf_get_stat - safely decode a recieved metadata (stat) structure
* @bufp: buffer to deserialize
* @stat: metadata (stat) structure
* @dotu: non-zero if 9P2000.u
*
*/
static void
buf_get_stat(struct cbuf *bufp, struct p9_stat *stat, int dotu)
{
stat->size = buf_get_int16(bufp);
stat->type = buf_get_int16(bufp);
stat->dev = buf_get_int32(bufp);
stat->qid.type = buf_get_int8(bufp);
stat->qid.version = buf_get_int32(bufp);
stat->qid.path = buf_get_int64(bufp);
stat->mode = buf_get_int32(bufp);
stat->atime = buf_get_int32(bufp);
stat->mtime = buf_get_int32(bufp);
stat->length = buf_get_int64(bufp);
buf_get_str(bufp, &stat->name);
buf_get_str(bufp, &stat->uid);
buf_get_str(bufp, &stat->gid);
buf_get_str(bufp, &stat->muid);
if (dotu) {
buf_get_str(bufp, &stat->extension);
stat->n_uid = buf_get_int32(bufp);
stat->n_gid = buf_get_int32(bufp);
stat->n_muid = buf_get_int32(bufp);
}
}
/**
* p9_deserialize_stat - decode a received metadata structure
* @buf: buffer to deserialize
* @buflen: length of received buffer
* @stat: metadata structure to decode into
* @dotu: non-zero if 9P2000.u
*
* Note: stat will point to the buf region.
*/
int
p9_deserialize_stat(void *buf, u32 buflen, struct p9_stat *stat,
int dotu)
{
struct cbuf buffer;
struct cbuf *bufp = &buffer;
unsigned char *p;
buf_init(bufp, buf, buflen);
p = bufp->p;
buf_get_stat(bufp, stat, dotu);
if (buf_check_overflow(bufp))
return 0;
else
return bufp->p - p;
}
EXPORT_SYMBOL(p9_deserialize_stat);
/**
* deserialize_fcall - unmarshal a response
* @buf: recieved buffer
* @buflen: length of received buffer
* @rcall: fcall structure to populate
* @rcalllen: length of fcall structure to populate
* @dotu: non-zero if 9P2000.u
*
*/
int
p9_deserialize_fcall(void *buf, u32 buflen, struct p9_fcall *rcall,
int dotu)
{
struct cbuf buffer;
struct cbuf *bufp = &buffer;
int i = 0;
buf_init(bufp, buf, buflen);
rcall->size = buf_get_int32(bufp);
rcall->id = buf_get_int8(bufp);
rcall->tag = buf_get_int16(bufp);
P9_DPRINTK(P9_DEBUG_CONV, "size %d id %d tag %d\n", rcall->size,
rcall->id, rcall->tag);
switch (rcall->id) {
default:
P9_EPRINTK(KERN_ERR, "unknown message type: %d\n", rcall->id);
return -EPROTO;
case P9_RVERSION:
rcall->params.rversion.msize = buf_get_int32(bufp);
buf_get_str(bufp, &rcall->params.rversion.version);
break;
case P9_RFLUSH:
break;
case P9_RATTACH:
rcall->params.rattach.qid.type = buf_get_int8(bufp);
rcall->params.rattach.qid.version = buf_get_int32(bufp);
rcall->params.rattach.qid.path = buf_get_int64(bufp);
break;
case P9_RWALK:
rcall->params.rwalk.nwqid = buf_get_int16(bufp);
if (rcall->params.rwalk.nwqid > P9_MAXWELEM) {
P9_EPRINTK(KERN_ERR,
"Rwalk with more than %d qids: %d\n",
P9_MAXWELEM, rcall->params.rwalk.nwqid);
return -EPROTO;
}
for (i = 0; i < rcall->params.rwalk.nwqid; i++)
buf_get_qid(bufp, &rcall->params.rwalk.wqids[i]);
break;
case P9_ROPEN:
buf_get_qid(bufp, &rcall->params.ropen.qid);
rcall->params.ropen.iounit = buf_get_int32(bufp);
break;
case P9_RCREATE:
buf_get_qid(bufp, &rcall->params.rcreate.qid);
rcall->params.rcreate.iounit = buf_get_int32(bufp);
break;
case P9_RREAD:
rcall->params.rread.count = buf_get_int32(bufp);
rcall->params.rread.data = bufp->p;
buf_check_size(bufp, rcall->params.rread.count);
break;
case P9_RWRITE:
rcall->params.rwrite.count = buf_get_int32(bufp);
break;
case P9_RCLUNK:
break;
case P9_RREMOVE:
break;
case P9_RSTAT:
buf_get_int16(bufp);
buf_get_stat(bufp, &rcall->params.rstat.stat, dotu);
break;
case P9_RWSTAT:
break;
case P9_RERROR:
buf_get_str(bufp, &rcall->params.rerror.error);
if (dotu)
rcall->params.rerror.errno = buf_get_int16(bufp);
break;
}
if (buf_check_overflow(bufp)) {
P9_DPRINTK(P9_DEBUG_ERROR, "buffer overflow\n");
return -EIO;
}
return bufp->p - bufp->sp;
}
EXPORT_SYMBOL(p9_deserialize_fcall);
static inline void p9_put_int8(struct cbuf *bufp, u8 val, u8 * p)
{
*p = val;
buf_put_int8(bufp, val);
}
static inline void p9_put_int16(struct cbuf *bufp, u16 val, u16 * p)
{
*p = val;
buf_put_int16(bufp, val);
}
static inline void p9_put_int32(struct cbuf *bufp, u32 val, u32 * p)
{
*p = val;
buf_put_int32(bufp, val);
}
static inline void p9_put_int64(struct cbuf *bufp, u64 val, u64 * p)
{
*p = val;
buf_put_int64(bufp, val);
}
static void
p9_put_str(struct cbuf *bufp, char *data, struct p9_str *str)
{
int len;
char *s;
if (data)
len = strlen(data);
else
len = 0;
s = buf_put_stringn(bufp, data, len);
if (str) {
str->len = len;
str->str = s;
}
}
static int
p9_put_data(struct cbuf *bufp, const char *data, int count,
unsigned char **pdata)
{
*pdata = buf_alloc(bufp, count);
if (*pdata == NULL)
return -ENOMEM;
memmove(*pdata, data, count);
return 0;
}
static int
p9_put_user_data(struct cbuf *bufp, const char __user *data, int count,
unsigned char **pdata)
{
*pdata = buf_alloc(bufp, count);
if (*pdata == NULL)
return -ENOMEM;
return copy_from_user(*pdata, data, count);
}
static void
p9_put_wstat(struct cbuf *bufp, struct p9_wstat *wstat,
struct p9_stat *stat, int statsz, int dotu)
{
p9_put_int16(bufp, statsz, &stat->size);
p9_put_int16(bufp, wstat->type, &stat->type);
p9_put_int32(bufp, wstat->dev, &stat->dev);
p9_put_int8(bufp, wstat->qid.type, &stat->qid.type);
p9_put_int32(bufp, wstat->qid.version, &stat->qid.version);
p9_put_int64(bufp, wstat->qid.path, &stat->qid.path);
p9_put_int32(bufp, wstat->mode, &stat->mode);
p9_put_int32(bufp, wstat->atime, &stat->atime);
p9_put_int32(bufp, wstat->mtime, &stat->mtime);
p9_put_int64(bufp, wstat->length, &stat->length);
p9_put_str(bufp, wstat->name, &stat->name);
p9_put_str(bufp, wstat->uid, &stat->uid);
p9_put_str(bufp, wstat->gid, &stat->gid);
p9_put_str(bufp, wstat->muid, &stat->muid);
if (dotu) {
p9_put_str(bufp, wstat->extension, &stat->extension);
p9_put_int32(bufp, wstat->n_uid, &stat->n_uid);
p9_put_int32(bufp, wstat->n_gid, &stat->n_gid);
p9_put_int32(bufp, wstat->n_muid, &stat->n_muid);
}
}
static struct p9_fcall *
p9_create_common(struct cbuf *bufp, u32 size, u8 id)
{
struct p9_fcall *fc;
size += 4 + 1 + 2; /* size[4] id[1] tag[2] */
fc = kmalloc(sizeof(struct p9_fcall) + size, GFP_KERNEL);
if (!fc)
return ERR_PTR(-ENOMEM);
fc->sdata = (char *)fc + sizeof(*fc);
buf_init(bufp, (char *)fc->sdata, size);
p9_put_int32(bufp, size, &fc->size);
p9_put_int8(bufp, id, &fc->id);
p9_put_int16(bufp, P9_NOTAG, &fc->tag);
return fc;
}
/**
* p9_set_tag - set the tag field of an &p9_fcall structure
* @fc: fcall structure to set tag within
* @tag: tag id to set
*/
void p9_set_tag(struct p9_fcall *fc, u16 tag)
{
fc->tag = tag;
*(__le16 *) (fc->sdata + 5) = cpu_to_le16(tag);
}
EXPORT_SYMBOL(p9_set_tag);
/**
* p9_create_tversion - allocates and creates a T_VERSION request
* @msize: requested maximum data size
* @version: version string to negotiate
*
*/
struct p9_fcall *p9_create_tversion(u32 msize, char *version)
{
int size;
struct p9_fcall *fc;
struct cbuf buffer;
struct cbuf *bufp = &buffer;
size = 4 + 2 + strlen(version); /* msize[4] version[s] */
fc = p9_create_common(bufp, size, P9_TVERSION);
if (IS_ERR(fc))
goto error;
p9_put_int32(bufp, msize, &fc->params.tversion.msize);
p9_put_str(bufp, version, &fc->params.tversion.version);
if (buf_check_overflow(bufp)) {
kfree(fc);
fc = ERR_PTR(-ENOMEM);
}
error:
return fc;
}
EXPORT_SYMBOL(p9_create_tversion);
/**
* p9_create_tauth - allocates and creates a T_AUTH request
* @afid: handle to use for authentication protocol
* @uname: user name attempting to authenticate
* @aname: mount specifier for remote server
* @n_uname: numeric id for user attempting to authneticate
* @dotu: 9P2000.u extension flag
*
*/
struct p9_fcall *p9_create_tauth(u32 afid, char *uname, char *aname,
u32 n_uname, int dotu)
{
int size;
struct p9_fcall *fc;
struct cbuf buffer;
struct cbuf *bufp = &buffer;
/* afid[4] uname[s] aname[s] */
size = 4 + 2 + 2;
if (uname)
size += strlen(uname);
if (aname)
size += strlen(aname);
if (dotu)
size += 4; /* n_uname */
fc = p9_create_common(bufp, size, P9_TAUTH);
if (IS_ERR(fc))
goto error;
p9_put_int32(bufp, afid, &fc->params.tauth.afid);
p9_put_str(bufp, uname, &fc->params.tauth.uname);
p9_put_str(bufp, aname, &fc->params.tauth.aname);
if (dotu)
p9_put_int32(bufp, n_uname, &fc->params.tauth.n_uname);
if (buf_check_overflow(bufp)) {
kfree(fc);
fc = ERR_PTR(-ENOMEM);
}
error:
return fc;
}
EXPORT_SYMBOL(p9_create_tauth);
/**
* p9_create_tattach - allocates and creates a T_ATTACH request
* @fid: handle to use for the new mount point
* @afid: handle to use for authentication protocol
* @uname: user name attempting to attach
* @aname: mount specifier for remote server
* @n_uname: numeric id for user attempting to attach
* @n_uname: numeric id for user attempting to attach
* @dotu: 9P2000.u extension flag
*
*/
struct p9_fcall *
p9_create_tattach(u32 fid, u32 afid, char *uname, char *aname,
u32 n_uname, int dotu)
{
int size;
struct p9_fcall *fc;
struct cbuf buffer;
struct cbuf *bufp = &buffer;
/* fid[4] afid[4] uname[s] aname[s] */
size = 4 + 4 + 2 + 2;
if (uname)
size += strlen(uname);
if (aname)
size += strlen(aname);
if (dotu)
size += 4; /* n_uname */
fc = p9_create_common(bufp, size, P9_TATTACH);
if (IS_ERR(fc))
goto error;
p9_put_int32(bufp, fid, &fc->params.tattach.fid);
p9_put_int32(bufp, afid, &fc->params.tattach.afid);
p9_put_str(bufp, uname, &fc->params.tattach.uname);
p9_put_str(bufp, aname, &fc->params.tattach.aname);
if (dotu)
p9_put_int32(bufp, n_uname, &fc->params.tattach.n_uname);
error:
return fc;
}
EXPORT_SYMBOL(p9_create_tattach);
/**
* p9_create_tflush - allocates and creates a T_FLUSH request
* @oldtag: tag id for the transaction we are attempting to cancel
*
*/
struct p9_fcall *p9_create_tflush(u16 oldtag)
{
int size;
struct p9_fcall *fc;
struct cbuf buffer;
struct cbuf *bufp = &buffer;
size = 2; /* oldtag[2] */
fc = p9_create_common(bufp, size, P9_TFLUSH);
if (IS_ERR(fc))
goto error;
p9_put_int16(bufp, oldtag, &fc->params.tflush.oldtag);
if (buf_check_overflow(bufp)) {
kfree(fc);
fc = ERR_PTR(-ENOMEM);
}
error:
return fc;
}
EXPORT_SYMBOL(p9_create_tflush);
/**
* p9_create_twalk - allocates and creates a T_FLUSH request
* @fid: handle we are traversing from
* @newfid: a new handle for this transaction
* @nwname: number of path elements to traverse
* @wnames: array of path elements
*
*/
struct p9_fcall *p9_create_twalk(u32 fid, u32 newfid, u16 nwname,
char **wnames)
{
int i, size;
struct p9_fcall *fc;
struct cbuf buffer;
struct cbuf *bufp = &buffer;
if (nwname > P9_MAXWELEM) {
P9_DPRINTK(P9_DEBUG_ERROR, "nwname > %d\n", P9_MAXWELEM);
return NULL;
}
size = 4 + 4 + 2; /* fid[4] newfid[4] nwname[2] ... */
for (i = 0; i < nwname; i++) {
size += 2 + strlen(wnames[i]); /* wname[s] */
}
fc = p9_create_common(bufp, size, P9_TWALK);
if (IS_ERR(fc))
goto error;
p9_put_int32(bufp, fid, &fc->params.twalk.fid);
p9_put_int32(bufp, newfid, &fc->params.twalk.newfid);
p9_put_int16(bufp, nwname, &fc->params.twalk.nwname);
for (i = 0; i < nwname; i++) {
p9_put_str(bufp, wnames[i], &fc->params.twalk.wnames[i]);
}
if (buf_check_overflow(bufp)) {
kfree(fc);
fc = ERR_PTR(-ENOMEM);
}
error:
return fc;
}
EXPORT_SYMBOL(p9_create_twalk);
/**
* p9_create_topen - allocates and creates a T_OPEN request
* @fid: handle we are trying to open
* @mode: what mode we are trying to open the file in
*
*/
struct p9_fcall *p9_create_topen(u32 fid, u8 mode)
{
int size;
struct p9_fcall *fc;
struct cbuf buffer;
struct cbuf *bufp = &buffer;
size = 4 + 1; /* fid[4] mode[1] */
fc = p9_create_common(bufp, size, P9_TOPEN);
if (IS_ERR(fc))
goto error;
p9_put_int32(bufp, fid, &fc->params.topen.fid);
p9_put_int8(bufp, mode, &fc->params.topen.mode);
if (buf_check_overflow(bufp)) {
kfree(fc);
fc = ERR_PTR(-ENOMEM);
}
error:
return fc;
}
EXPORT_SYMBOL(p9_create_topen);
/**
* p9_create_tcreate - allocates and creates a T_CREATE request
* @fid: handle of directory we are trying to create in
* @name: name of the file we are trying to create
* @perm: permissions for the file we are trying to create
* @mode: what mode we are trying to open the file in
* @extension: 9p2000.u extension string (for special files)
* @dotu: 9p2000.u enabled flag
*
* Note: Plan 9 create semantics include opening the resulting file
* which is why mode is included.
*/
struct p9_fcall *p9_create_tcreate(u32 fid, char *name, u32 perm, u8 mode,
char *extension, int dotu)
{
int size;
struct p9_fcall *fc;
struct cbuf buffer;
struct cbuf *bufp = &buffer;
/* fid[4] name[s] perm[4] mode[1] */
size = 4 + 2 + strlen(name) + 4 + 1;
if (dotu) {
size += 2 + /* extension[s] */
(extension == NULL ? 0 : strlen(extension));
}
fc = p9_create_common(bufp, size, P9_TCREATE);
if (IS_ERR(fc))
goto error;
p9_put_int32(bufp, fid, &fc->params.tcreate.fid);
p9_put_str(bufp, name, &fc->params.tcreate.name);
p9_put_int32(bufp, perm, &fc->params.tcreate.perm);
p9_put_int8(bufp, mode, &fc->params.tcreate.mode);
if (dotu)
p9_put_str(bufp, extension, &fc->params.tcreate.extension);
if (buf_check_overflow(bufp)) {
kfree(fc);
fc = ERR_PTR(-ENOMEM);
}
error:
return fc;
}
EXPORT_SYMBOL(p9_create_tcreate);
/**
* p9_create_tread - allocates and creates a T_READ request
* @fid: handle of the file we are trying to read
* @offset: offset to start reading from
* @count: how many bytes to read
*/
struct p9_fcall *p9_create_tread(u32 fid, u64 offset, u32 count)
{
int size;
struct p9_fcall *fc;
struct cbuf buffer;
struct cbuf *bufp = &buffer;
size = 4 + 8 + 4; /* fid[4] offset[8] count[4] */
fc = p9_create_common(bufp, size, P9_TREAD);
if (IS_ERR(fc))
goto error;
p9_put_int32(bufp, fid, &fc->params.tread.fid);
p9_put_int64(bufp, offset, &fc->params.tread.offset);
p9_put_int32(bufp, count, &fc->params.tread.count);
if (buf_check_overflow(bufp)) {
kfree(fc);
fc = ERR_PTR(-ENOMEM);
}
error:
return fc;
}
EXPORT_SYMBOL(p9_create_tread);
/**
* p9_create_twrite - allocates and creates a T_WRITE request from the kernel
* @fid: handle of the file we are trying to write
* @offset: offset to start writing at
* @count: how many bytes to write
* @data: data to write
*
* This function will create a requst with data buffers from the kernel
* such as the page cache.
*/
struct p9_fcall *p9_create_twrite(u32 fid, u64 offset, u32 count,
const char *data)
{
int size, err;
struct p9_fcall *fc;
struct cbuf buffer;
struct cbuf *bufp = &buffer;
/* fid[4] offset[8] count[4] data[count] */
size = 4 + 8 + 4 + count;
fc = p9_create_common(bufp, size, P9_TWRITE);
if (IS_ERR(fc))
goto error;
p9_put_int32(bufp, fid, &fc->params.twrite.fid);
p9_put_int64(bufp, offset, &fc->params.twrite.offset);
p9_put_int32(bufp, count, &fc->params.twrite.count);
err = p9_put_data(bufp, data, count, &fc->params.twrite.data);
if (err) {
kfree(fc);
fc = ERR_PTR(err);
goto error;
}
if (buf_check_overflow(bufp)) {
kfree(fc);
fc = ERR_PTR(-ENOMEM);
}
error:
return fc;
}
EXPORT_SYMBOL(p9_create_twrite);
/**
* p9_create_twrite_u - allocates and creates a T_WRITE request from userspace
* @fid: handle of the file we are trying to write
* @offset: offset to start writing at
* @count: how many bytes to write
* @data: data to write
*
* This function will create a request with data buffers from userspace
*/
struct p9_fcall *p9_create_twrite_u(u32 fid, u64 offset, u32 count,
const char __user *data)
{
int size, err;
struct p9_fcall *fc;
struct cbuf buffer;
struct cbuf *bufp = &buffer;
/* fid[4] offset[8] count[4] data[count] */
size = 4 + 8 + 4 + count;
fc = p9_create_common(bufp, size, P9_TWRITE);
if (IS_ERR(fc))
goto error;
p9_put_int32(bufp, fid, &fc->params.twrite.fid);
p9_put_int64(bufp, offset, &fc->params.twrite.offset);
p9_put_int32(bufp, count, &fc->params.twrite.count);
err = p9_put_user_data(bufp, data, count, &fc->params.twrite.data);
if (err) {
kfree(fc);
fc = ERR_PTR(err);
goto error;
}
if (buf_check_overflow(bufp)) {
kfree(fc);
fc = ERR_PTR(-ENOMEM);
}
error:
return fc;
}
EXPORT_SYMBOL(p9_create_twrite_u);
/**
* p9_create_tclunk - allocate a request to forget about a file handle
* @fid: handle of the file we closing or forgetting about
*
* clunk is used both to close open files and to discard transient handles
* which may be created during meta-data operations and hierarchy traversal.
*/
struct p9_fcall *p9_create_tclunk(u32 fid)
{
int size;
struct p9_fcall *fc;
struct cbuf buffer;
struct cbuf *bufp = &buffer;
size = 4; /* fid[4] */
fc = p9_create_common(bufp, size, P9_TCLUNK);
if (IS_ERR(fc))
goto error;
p9_put_int32(bufp, fid, &fc->params.tclunk.fid);
if (buf_check_overflow(bufp)) {
kfree(fc);
fc = ERR_PTR(-ENOMEM);
}
error:
return fc;
}
EXPORT_SYMBOL(p9_create_tclunk);
/**
* p9_create_tremove - allocate and create a request to remove a file
* @fid: handle of the file or directory we are removing
*
*/
struct p9_fcall *p9_create_tremove(u32 fid)
{
int size;
struct p9_fcall *fc;
struct cbuf buffer;
struct cbuf *bufp = &buffer;
size = 4; /* fid[4] */
fc = p9_create_common(bufp, size, P9_TREMOVE);
if (IS_ERR(fc))
goto error;
p9_put_int32(bufp, fid, &fc->params.tremove.fid);
if (buf_check_overflow(bufp)) {
kfree(fc);
fc = ERR_PTR(-ENOMEM);
}
error:
return fc;
}
EXPORT_SYMBOL(p9_create_tremove);
/**
* p9_create_tstat - allocate and populate a request for attributes
* @fid: handle of the file or directory we are trying to get the attributes of
*
*/
struct p9_fcall *p9_create_tstat(u32 fid)
{
int size;
struct p9_fcall *fc;
struct cbuf buffer;
struct cbuf *bufp = &buffer;
size = 4; /* fid[4] */
fc = p9_create_common(bufp, size, P9_TSTAT);
if (IS_ERR(fc))
goto error;
p9_put_int32(bufp, fid, &fc->params.tstat.fid);
if (buf_check_overflow(bufp)) {
kfree(fc);
fc = ERR_PTR(-ENOMEM);
}
error:
return fc;
}
EXPORT_SYMBOL(p9_create_tstat);
/**
* p9_create_tstat - allocate and populate a request to change attributes
* @fid: handle of the file or directory we are trying to change
* @wstat: &p9_stat structure with attributes we wish to set
* @dotu: 9p2000.u enabled flag
*
*/
struct p9_fcall *p9_create_twstat(u32 fid, struct p9_wstat *wstat,
int dotu)
{
int size, statsz;
struct p9_fcall *fc;
struct cbuf buffer;
struct cbuf *bufp = &buffer;
statsz = p9_size_wstat(wstat, dotu);
size = 4 + 2 + 2 + statsz; /* fid[4] stat[n] */
fc = p9_create_common(bufp, size, P9_TWSTAT);
if (IS_ERR(fc))
goto error;
p9_put_int32(bufp, fid, &fc->params.twstat.fid);
buf_put_int16(bufp, statsz + 2);
p9_put_wstat(bufp, wstat, &fc->params.twstat.stat, statsz, dotu);
if (buf_check_overflow(bufp)) {
kfree(fc);
fc = ERR_PTR(-ENOMEM);
}
error:
return fc;
}
EXPORT_SYMBOL(p9_create_twstat);
/*
* net/9p/fcprint.c
*
* Print 9P call.
*
* Copyright (C) 2005 by Latchesar Ionkov <lucho@ionkov.net>
*
* 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
*
*/
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/idr.h>
#include <net/9p/9p.h>
#ifdef CONFIG_NET_9P_DEBUG
static int
p9_printqid(char *buf, int buflen, struct p9_qid *q)
{
int n;
char b[10];
n = 0;
if (q->type & P9_QTDIR)
b[n++] = 'd';
if (q->type & P9_QTAPPEND)
b[n++] = 'a';
if (q->type & P9_QTAUTH)
b[n++] = 'A';
if (q->type & P9_QTEXCL)
b[n++] = 'l';
if (q->type & P9_QTTMP)
b[n++] = 't';
if (q->type & P9_QTSYMLINK)
b[n++] = 'L';
b[n] = '\0';
return scnprintf(buf, buflen, "(%.16llx %x %s)",
(long long int) q->path, q->version, b);
}
static int
p9_printperm(char *buf, int buflen, int perm)
{
int n;
char b[15];
n = 0;
if (perm & P9_DMDIR)
b[n++] = 'd';
if (perm & P9_DMAPPEND)
b[n++] = 'a';
if (perm & P9_DMAUTH)
b[n++] = 'A';
if (perm & P9_DMEXCL)
b[n++] = 'l';
if (perm & P9_DMTMP)
b[n++] = 't';
if (perm & P9_DMDEVICE)
b[n++] = 'D';
if (perm & P9_DMSOCKET)
b[n++] = 'S';
if (perm & P9_DMNAMEDPIPE)
b[n++] = 'P';
if (perm & P9_DMSYMLINK)
b[n++] = 'L';
b[n] = '\0';
return scnprintf(buf, buflen, "%s%03o", b, perm&077);
}
static int
p9_printstat(char *buf, int buflen, struct p9_stat *st, int extended)
{
int n;
n = scnprintf(buf, buflen, "'%.*s' '%.*s'", st->name.len,
st->name.str, st->uid.len, st->uid.str);
if (extended)
n += scnprintf(buf+n, buflen-n, "(%d)", st->n_uid);
n += scnprintf(buf+n, buflen-n, " '%.*s'", st->gid.len, st->gid.str);
if (extended)
n += scnprintf(buf+n, buflen-n, "(%d)", st->n_gid);
n += scnprintf(buf+n, buflen-n, " '%.*s'", st->muid.len, st->muid.str);
if (extended)
n += scnprintf(buf+n, buflen-n, "(%d)", st->n_muid);
n += scnprintf(buf+n, buflen-n, " q ");
n += p9_printqid(buf+n, buflen-n, &st->qid);
n += scnprintf(buf+n, buflen-n, " m ");
n += p9_printperm(buf+n, buflen-n, st->mode);
n += scnprintf(buf+n, buflen-n, " at %d mt %d l %lld",
st->atime, st->mtime, (long long int) st->length);
if (extended)
n += scnprintf(buf+n, buflen-n, " ext '%.*s'",
st->extension.len, st->extension.str);
return n;
}
static int
p9_dumpdata(char *buf, int buflen, u8 *data, int datalen)
{
int i, n;
i = n = 0;
while (i < datalen) {
n += scnprintf(buf + n, buflen - n, "%02x", data[i]);
if (i%4 == 3)
n += scnprintf(buf + n, buflen - n, " ");
if (i%32 == 31)
n += scnprintf(buf + n, buflen - n, "\n");
i++;
}
n += scnprintf(buf + n, buflen - n, "\n");
return n;
}
static int
p9_printdata(char *buf, int buflen, u8 *data, int datalen)
{
return p9_dumpdata(buf, buflen, data, datalen < 16?datalen:16);
}
/**
* p9_printfcall - decode and print a protocol structure into a buffer
* @buf: buffer to deposit decoded structure into
* @buflen: available space in buffer
* @fc: protocol rpc structure of type &p9_fcall
* @extended: whether or not session is operating with extended protocol
*/
int
p9_printfcall(char *buf, int buflen, struct p9_fcall *fc, int extended)
{
int i, ret, type, tag;
if (!fc)
return scnprintf(buf, buflen, "<NULL>");
type = fc->id;
tag = fc->tag;
ret = 0;
switch (type) {
case P9_TVERSION:
ret += scnprintf(buf+ret, buflen-ret,
"Tversion tag %u msize %u version '%.*s'", tag,
fc->params.tversion.msize,
fc->params.tversion.version.len,
fc->params.tversion.version.str);
break;
case P9_RVERSION:
ret += scnprintf(buf+ret, buflen-ret,
"Rversion tag %u msize %u version '%.*s'", tag,
fc->params.rversion.msize,
fc->params.rversion.version.len,
fc->params.rversion.version.str);
break;
case P9_TAUTH:
ret += scnprintf(buf+ret, buflen-ret,
"Tauth tag %u afid %d uname '%.*s' aname '%.*s'", tag,
fc->params.tauth.afid, fc->params.tauth.uname.len,
fc->params.tauth.uname.str, fc->params.tauth.aname.len,
fc->params.tauth.aname.str);
break;
case P9_RAUTH:
ret += scnprintf(buf+ret, buflen-ret, "Rauth tag %u qid ", tag);
p9_printqid(buf+ret, buflen-ret, &fc->params.rauth.qid);
break;
case P9_TATTACH:
ret += scnprintf(buf+ret, buflen-ret,
"Tattach tag %u fid %d afid %d uname '%.*s' aname '%.*s'", tag,
fc->params.tattach.fid, fc->params.tattach.afid,
fc->params.tattach.uname.len, fc->params.tattach.uname.str,
fc->params.tattach.aname.len, fc->params.tattach.aname.str);
break;
case P9_RATTACH:
ret += scnprintf(buf+ret, buflen-ret, "Rattach tag %u qid ",
tag);
p9_printqid(buf+ret, buflen-ret, &fc->params.rattach.qid);
break;
case P9_RERROR:
ret += scnprintf(buf+ret, buflen-ret,
"Rerror tag %u ename '%.*s'", tag,
fc->params.rerror.error.len,
fc->params.rerror.error.str);
if (extended)
ret += scnprintf(buf+ret, buflen-ret, " ecode %d\n",
fc->params.rerror.errno);
break;
case P9_TFLUSH:
ret += scnprintf(buf+ret, buflen-ret, "Tflush tag %u oldtag %u",
tag, fc->params.tflush.oldtag);
break;
case P9_RFLUSH:
ret += scnprintf(buf+ret, buflen-ret, "Rflush tag %u", tag);
break;
case P9_TWALK:
ret += scnprintf(buf+ret, buflen-ret,
"Twalk tag %u fid %d newfid %d nwname %d", tag,
fc->params.twalk.fid, fc->params.twalk.newfid,
fc->params.twalk.nwname);
for (i = 0; i < fc->params.twalk.nwname; i++)
ret += scnprintf(buf+ret, buflen-ret, " '%.*s'",
fc->params.twalk.wnames[i].len,
fc->params.twalk.wnames[i].str);
break;
case P9_RWALK:
ret += scnprintf(buf+ret, buflen-ret, "Rwalk tag %u nwqid %d",
tag, fc->params.rwalk.nwqid);
for (i = 0; i < fc->params.rwalk.nwqid; i++)
ret += p9_printqid(buf+ret, buflen-ret,
&fc->params.rwalk.wqids[i]);
break;
case P9_TOPEN:
ret += scnprintf(buf+ret, buflen-ret,
"Topen tag %u fid %d mode %d", tag,
fc->params.topen.fid, fc->params.topen.mode);
break;
case P9_ROPEN:
ret += scnprintf(buf+ret, buflen-ret, "Ropen tag %u", tag);
ret += p9_printqid(buf+ret, buflen-ret, &fc->params.ropen.qid);
ret += scnprintf(buf+ret, buflen-ret, " iounit %d",
fc->params.ropen.iounit);
break;
case P9_TCREATE:
ret += scnprintf(buf+ret, buflen-ret,
"Tcreate tag %u fid %d name '%.*s' perm ", tag,
fc->params.tcreate.fid, fc->params.tcreate.name.len,
fc->params.tcreate.name.str);
ret += p9_printperm(buf+ret, buflen-ret,
fc->params.tcreate.perm);
ret += scnprintf(buf+ret, buflen-ret, " mode %d",
fc->params.tcreate.mode);
break;
case P9_RCREATE:
ret += scnprintf(buf+ret, buflen-ret, "Rcreate tag %u", tag);
ret += p9_printqid(buf+ret, buflen-ret,
&fc->params.rcreate.qid);
ret += scnprintf(buf+ret, buflen-ret, " iounit %d",
fc->params.rcreate.iounit);
break;
case P9_TREAD:
ret += scnprintf(buf+ret, buflen-ret,
"Tread tag %u fid %d offset %lld count %u", tag,
fc->params.tread.fid,
(long long int) fc->params.tread.offset,
fc->params.tread.count);
break;
case P9_RREAD:
ret += scnprintf(buf+ret, buflen-ret,
"Rread tag %u count %u data ", tag,
fc->params.rread.count);
ret += p9_printdata(buf+ret, buflen-ret, fc->params.rread.data,
fc->params.rread.count);
break;
case P9_TWRITE:
ret += scnprintf(buf+ret, buflen-ret,
"Twrite tag %u fid %d offset %lld count %u data ",
tag, fc->params.twrite.fid,
(long long int) fc->params.twrite.offset,
fc->params.twrite.count);
ret += p9_printdata(buf+ret, buflen-ret, fc->params.twrite.data,
fc->params.twrite.count);
break;
case P9_RWRITE:
ret += scnprintf(buf+ret, buflen-ret, "Rwrite tag %u count %u",
tag, fc->params.rwrite.count);
break;
case P9_TCLUNK:
ret += scnprintf(buf+ret, buflen-ret, "Tclunk tag %u fid %d",
tag, fc->params.tclunk.fid);
break;
case P9_RCLUNK:
ret += scnprintf(buf+ret, buflen-ret, "Rclunk tag %u", tag);
break;
case P9_TREMOVE:
ret += scnprintf(buf+ret, buflen-ret, "Tremove tag %u fid %d",
tag, fc->params.tremove.fid);
break;
case P9_RREMOVE:
ret += scnprintf(buf+ret, buflen-ret, "Rremove tag %u", tag);
break;
case P9_TSTAT:
ret += scnprintf(buf+ret, buflen-ret, "Tstat tag %u fid %d",
tag, fc->params.tstat.fid);
break;
case P9_RSTAT:
ret += scnprintf(buf+ret, buflen-ret, "Rstat tag %u ", tag);
ret += p9_printstat(buf+ret, buflen-ret, &fc->params.rstat.stat,
extended);
break;
case P9_TWSTAT:
ret += scnprintf(buf+ret, buflen-ret, "Twstat tag %u fid %d ",
tag, fc->params.twstat.fid);
ret += p9_printstat(buf+ret, buflen-ret,
&fc->params.twstat.stat, extended);
break;
case P9_RWSTAT:
ret += scnprintf(buf+ret, buflen-ret, "Rwstat tag %u", tag);
break;
default:
ret += scnprintf(buf+ret, buflen-ret, "unknown type %d", type);
break;
}
return ret;
}
#else
int
p9_printfcall(char *buf, int buflen, struct p9_fcall *fc, int extended)
{
return 0;
}
#endif /* CONFIG_NET_9P_DEBUG */
EXPORT_SYMBOL(p9_printfcall);
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include <net/9p/9p.h> #include <net/9p/9p.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/parser.h> #include <linux/parser.h>
#include <net/9p/client.h>
#include <net/9p/transport.h> #include <net/9p/transport.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
......
/*
* net/9p/protocol.c
*
* 9P Protocol Support Code
*
* Copyright (C) 2008 by Eric Van Hensbergen <ericvh@gmail.com>
*
* Base on code from Anthony Liguori <aliguori@us.ibm.com>
* Copyright (C) 2008 by IBM, Corp.
*
* 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
*
*/
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/uaccess.h>
#include <linux/sched.h>
#include <net/9p/9p.h>
#include <net/9p/client.h>
#include "protocol.h"
#ifndef MIN
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#endif
#ifndef MAX
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#endif
#ifndef offset_of
#define offset_of(type, memb) \
((unsigned long)(&((type *)0)->memb))
#endif
#ifndef container_of
#define container_of(obj, type, memb) \
((type *)(((char *)obj) - offset_of(type, memb)))
#endif
static int
p9pdu_writef(struct p9_fcall *pdu, int optional, const char *fmt, ...);
void
p9pdu_dump(int way, struct p9_fcall *pdu)
{
int i, n;
u8 *data = pdu->sdata;
int datalen = pdu->size;
char buf[255];
int buflen = 255;
i = n = 0;
if (datalen > (buflen-16))
datalen = buflen-16;
while (i < datalen) {
n += scnprintf(buf + n, buflen - n, "%02x ", data[i]);
if (i%4 == 3)
n += scnprintf(buf + n, buflen - n, " ");
if (i%32 == 31)
n += scnprintf(buf + n, buflen - n, "\n");
i++;
}
n += scnprintf(buf + n, buflen - n, "\n");
if (way)
P9_DPRINTK(P9_DEBUG_PKT, "[[[(%d) %s\n", datalen, buf);
else
P9_DPRINTK(P9_DEBUG_PKT, "]]](%d) %s\n", datalen, buf);
}
EXPORT_SYMBOL(p9pdu_dump);
void p9stat_free(struct p9_wstat *stbuf)
{
kfree(stbuf->name);
kfree(stbuf->uid);
kfree(stbuf->gid);
kfree(stbuf->muid);
kfree(stbuf->extension);
}
EXPORT_SYMBOL(p9stat_free);
static size_t pdu_read(struct p9_fcall *pdu, void *data, size_t size)
{
size_t len = MIN(pdu->size - pdu->offset, size);
memcpy(data, &pdu->sdata[pdu->offset], len);
pdu->offset += len;
return size - len;
}
static size_t pdu_write(struct p9_fcall *pdu, const void *data, size_t size)
{
size_t len = MIN(pdu->capacity - pdu->size, size);
memcpy(&pdu->sdata[pdu->size], data, len);
pdu->size += len;
return size - len;
}
static size_t
pdu_write_u(struct p9_fcall *pdu, const char __user *udata, size_t size)
{
size_t len = MIN(pdu->capacity - pdu->size, size);
int err = copy_from_user(&pdu->sdata[pdu->size], udata, len);
if (err)
printk(KERN_WARNING "pdu_write_u returning: %d\n", err);
pdu->size += len;
return size - len;
}
/*
b - int8_t
w - int16_t
d - int32_t
q - int64_t
s - string
S - stat
Q - qid
D - data blob (int32_t size followed by void *, results are not freed)
T - array of strings (int16_t count, followed by strings)
R - array of qids (int16_t count, followed by qids)
? - if optional = 1, continue parsing
*/
static int
p9pdu_vreadf(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap)
{
const char *ptr;
int errcode = 0;
for (ptr = fmt; *ptr; ptr++) {
switch (*ptr) {
case 'b':{
int8_t *val = va_arg(ap, int8_t *);
if (pdu_read(pdu, val, sizeof(*val))) {
errcode = -EFAULT;
break;
}
}
break;
case 'w':{
int16_t *val = va_arg(ap, int16_t *);
if (pdu_read(pdu, val, sizeof(*val))) {
errcode = -EFAULT;
break;
}
*val = cpu_to_le16(*val);
}
break;
case 'd':{
int32_t *val = va_arg(ap, int32_t *);
if (pdu_read(pdu, val, sizeof(*val))) {
errcode = -EFAULT;
break;
}
*val = cpu_to_le32(*val);
}
break;
case 'q':{
int64_t *val = va_arg(ap, int64_t *);
if (pdu_read(pdu, val, sizeof(*val))) {
errcode = -EFAULT;
break;
}
*val = cpu_to_le64(*val);
}
break;
case 's':{
char **ptr = va_arg(ap, char **);
int16_t len;
int size;
errcode = p9pdu_readf(pdu, optional, "w", &len);
if (errcode)
break;
size = MAX(len, 0);
*ptr = kmalloc(size + 1, GFP_KERNEL);
if (*ptr == NULL) {
errcode = -EFAULT;
break;
}
if (pdu_read(pdu, *ptr, size)) {
errcode = -EFAULT;
kfree(*ptr);
*ptr = NULL;
} else
(*ptr)[size] = 0;
}
break;
case 'Q':{
struct p9_qid *qid =
va_arg(ap, struct p9_qid *);
errcode = p9pdu_readf(pdu, optional, "bdq",
&qid->type, &qid->version,
&qid->path);
}
break;
case 'S':{
struct p9_wstat *stbuf =
va_arg(ap, struct p9_wstat *);
memset(stbuf, 0, sizeof(struct p9_wstat));
stbuf->n_uid = stbuf->n_gid = stbuf->n_muid =
-1;
errcode =
p9pdu_readf(pdu, optional,
"wwdQdddqssss?sddd",
&stbuf->size, &stbuf->type,
&stbuf->dev, &stbuf->qid,
&stbuf->mode, &stbuf->atime,
&stbuf->mtime, &stbuf->length,
&stbuf->name, &stbuf->uid,
&stbuf->gid, &stbuf->muid,
&stbuf->extension,
&stbuf->n_uid, &stbuf->n_gid,
&stbuf->n_muid);
if (errcode)
p9stat_free(stbuf);
}
break;
case 'D':{
int32_t *count = va_arg(ap, int32_t *);
void **data = va_arg(ap, void **);
errcode =
p9pdu_readf(pdu, optional, "d", count);
if (!errcode) {
*count =
MIN(*count,
pdu->size - pdu->offset);
*data = &pdu->sdata[pdu->offset];
}
}
break;
case 'T':{
int16_t *nwname = va_arg(ap, int16_t *);
char ***wnames = va_arg(ap, char ***);
errcode =
p9pdu_readf(pdu, optional, "w", nwname);
if (!errcode) {
*wnames =
kmalloc(sizeof(char *) * *nwname,
GFP_KERNEL);
if (!*wnames)
errcode = -ENOMEM;
}
if (!errcode) {
int i;
for (i = 0; i < *nwname; i++) {
errcode =
p9pdu_readf(pdu, optional,
"s",
&(*wnames)[i]);
if (errcode)
break;
}
}
if (errcode) {
if (*wnames) {
int i;
for (i = 0; i < *nwname; i++)
kfree((*wnames)[i]);
}
kfree(*wnames);
*wnames = NULL;
}
}
break;
case 'R':{
int16_t *nwqid = va_arg(ap, int16_t *);
struct p9_qid **wqids =
va_arg(ap, struct p9_qid **);
*wqids = NULL;
errcode =
p9pdu_readf(pdu, optional, "w", nwqid);
if (!errcode) {
*wqids =
kmalloc(*nwqid *
sizeof(struct p9_qid),
GFP_KERNEL);
if (*wqids == NULL)
errcode = -ENOMEM;
}
if (!errcode) {
int i;
for (i = 0; i < *nwqid; i++) {
errcode =
p9pdu_readf(pdu, optional,
"Q",
&(*wqids)[i]);
if (errcode)
break;
}
}
if (errcode) {
kfree(*wqids);
*wqids = NULL;
}
}
break;
case '?':
if (!optional)
return 0;
break;
default:
BUG();
break;
}
if (errcode)
break;
}
return errcode;
}
int
p9pdu_vwritef(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap)
{
const char *ptr;
int errcode = 0;
for (ptr = fmt; *ptr; ptr++) {
switch (*ptr) {
case 'b':{
int8_t val = va_arg(ap, int);
if (pdu_write(pdu, &val, sizeof(val)))
errcode = -EFAULT;
}
break;
case 'w':{
int16_t val = va_arg(ap, int);
if (pdu_write(pdu, &val, sizeof(val)))
errcode = -EFAULT;
}
break;
case 'd':{
int32_t val = va_arg(ap, int32_t);
if (pdu_write(pdu, &val, sizeof(val)))
errcode = -EFAULT;
}
break;
case 'q':{
int64_t val = va_arg(ap, int64_t);
if (pdu_write(pdu, &val, sizeof(val)))
errcode = -EFAULT;
}
break;
case 's':{
const char *ptr = va_arg(ap, const char *);
int16_t len = 0;
if (ptr)
len = MIN(strlen(ptr), USHORT_MAX);
errcode = p9pdu_writef(pdu, optional, "w", len);
if (!errcode && pdu_write(pdu, ptr, len))
errcode = -EFAULT;
}
break;
case 'Q':{
const struct p9_qid *qid =
va_arg(ap, const struct p9_qid *);
errcode =
p9pdu_writef(pdu, optional, "bdq",
qid->type, qid->version,
qid->path);
} break;
case 'S':{
const struct p9_wstat *stbuf =
va_arg(ap, const struct p9_wstat *);
errcode =
p9pdu_writef(pdu, optional,
"wwdQdddqssss?sddd",
stbuf->size, stbuf->type,
stbuf->dev, &stbuf->qid,
stbuf->mode, stbuf->atime,
stbuf->mtime, stbuf->length,
stbuf->name, stbuf->uid,
stbuf->gid, stbuf->muid,
stbuf->extension, stbuf->n_uid,
stbuf->n_gid, stbuf->n_muid);
} break;
case 'D':{
int32_t count = va_arg(ap, int32_t);
const void *data = va_arg(ap, const void *);
errcode =
p9pdu_writef(pdu, optional, "d", count);
if (!errcode && pdu_write(pdu, data, count))
errcode = -EFAULT;
}
break;
case 'U':{
int32_t count = va_arg(ap, int32_t);
const char __user *udata =
va_arg(ap, const void *);
errcode =
p9pdu_writef(pdu, optional, "d", count);
if (!errcode && pdu_write_u(pdu, udata, count))
errcode = -EFAULT;
}
break;
case 'T':{
int16_t nwname = va_arg(ap, int);
const char **wnames = va_arg(ap, const char **);
errcode =
p9pdu_writef(pdu, optional, "w", nwname);
if (!errcode) {
int i;
for (i = 0; i < nwname; i++) {
errcode =
p9pdu_writef(pdu, optional,
"s",
wnames[i]);
if (errcode)
break;
}
}
}
break;
case 'R':{
int16_t nwqid = va_arg(ap, int);
struct p9_qid *wqids =
va_arg(ap, struct p9_qid *);
errcode =
p9pdu_writef(pdu, optional, "w", nwqid);
if (!errcode) {
int i;
for (i = 0; i < nwqid; i++) {
errcode =
p9pdu_writef(pdu, optional,
"Q",
&wqids[i]);
if (errcode)
break;
}
}
}
break;
case '?':
if (!optional)
return 0;
break;
default:
BUG();
break;
}
if (errcode)
break;
}
return errcode;
}
int p9pdu_readf(struct p9_fcall *pdu, int optional, const char *fmt, ...)
{
va_list ap;
int ret;
va_start(ap, fmt);
ret = p9pdu_vreadf(pdu, optional, fmt, ap);
va_end(ap);
return ret;
}
static int
p9pdu_writef(struct p9_fcall *pdu, int optional, const char *fmt, ...)
{
va_list ap;
int ret;
va_start(ap, fmt);
ret = p9pdu_vwritef(pdu, optional, fmt, ap);
va_end(ap);
return ret;
}
int p9stat_read(char *buf, int len, struct p9_wstat *st, int dotu)
{
struct p9_fcall fake_pdu;
int ret;
fake_pdu.size = len;
fake_pdu.capacity = len;
fake_pdu.sdata = buf;
fake_pdu.offset = 0;
ret = p9pdu_readf(&fake_pdu, dotu, "S", st);
if (ret) {
P9_DPRINTK(P9_DEBUG_9P, "<<< p9stat_read failed: %d\n", ret);
p9pdu_dump(1, &fake_pdu);
}
return ret;
}
EXPORT_SYMBOL(p9stat_read);
int p9pdu_prepare(struct p9_fcall *pdu, int16_t tag, int8_t type)
{
return p9pdu_writef(pdu, 0, "dbw", 0, type, tag);
}
int p9pdu_finalize(struct p9_fcall *pdu)
{
int size = pdu->size;
int err;
pdu->size = 0;
err = p9pdu_writef(pdu, 0, "d", size);
pdu->size = size;
if ((p9_debug_level & P9_DEBUG_PKT) == P9_DEBUG_PKT)
p9pdu_dump(0, pdu);
P9_DPRINTK(P9_DEBUG_9P, ">>> size=%d type: %d tag: %d\n", pdu->size,
pdu->id, pdu->tag);
return err;
}
void p9pdu_reset(struct p9_fcall *pdu)
{
pdu->offset = 0;
pdu->size = 0;
}
/*
* net/9p/protocol.h
*
* 9P Protocol Support Code
*
* Copyright (C) 2008 by Eric Van Hensbergen <ericvh@gmail.com>
*
* Base on code from Anthony Liguori <aliguori@us.ibm.com>
* Copyright (C) 2008 by IBM, Corp.
*
* 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
*
*/
int
p9pdu_vwritef(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap);
int p9pdu_readf(struct p9_fcall *pdu, int optional, const char *fmt, ...);
int p9pdu_prepare(struct p9_fcall *pdu, int16_t tag, int8_t type);
int p9pdu_finalize(struct p9_fcall *pdu);
void p9pdu_dump(int, struct p9_fcall *);
void p9pdu_reset(struct p9_fcall *pdu);
...@@ -39,12 +39,11 @@ ...@@ -39,12 +39,11 @@
#include <linux/file.h> #include <linux/file.h>
#include <linux/parser.h> #include <linux/parser.h>
#include <net/9p/9p.h> #include <net/9p/9p.h>
#include <net/9p/client.h>
#include <net/9p/transport.h> #include <net/9p/transport.h>
#define P9_PORT 564 #define P9_PORT 564
#define MAX_SOCK_BUF (64*1024) #define MAX_SOCK_BUF (64*1024)
#define ERREQFLUSH 1
#define SCHED_TIMEOUT 10
#define MAXPOLLWADDR 2 #define MAXPOLLWADDR 2
/** /**
...@@ -61,7 +60,6 @@ struct p9_fd_opts { ...@@ -61,7 +60,6 @@ struct p9_fd_opts {
u16 port; u16 port;
}; };
/** /**
* struct p9_trans_fd - transport state * struct p9_trans_fd - transport state
* @rd: reference to file to read from * @rd: reference to file to read from
...@@ -100,60 +98,22 @@ enum { ...@@ -100,60 +98,22 @@ enum {
Wpending = 8, /* can write */ Wpending = 8, /* can write */
}; };
enum { struct p9_poll_wait {
None, struct p9_conn *conn;
Flushing, wait_queue_t wait;
Flushed, wait_queue_head_t *wait_addr;
};
struct p9_req;
typedef void (*p9_conn_req_callback)(struct p9_req *req, void *a);
/**
* struct p9_req - fd mux encoding of an rpc transaction
* @lock: protects req_list
* @tag: numeric tag for rpc transaction
* @tcall: request &p9_fcall structure
* @rcall: response &p9_fcall structure
* @err: error state
* @cb: callback for when response is received
* @cba: argument to pass to callback
* @flush: flag to indicate RPC has been flushed
* @req_list: list link for higher level objects to chain requests
*
*/
struct p9_req {
spinlock_t lock;
int tag;
struct p9_fcall *tcall;
struct p9_fcall *rcall;
int err;
p9_conn_req_callback cb;
void *cba;
int flush;
struct list_head req_list;
};
struct p9_mux_poll_task {
struct task_struct *task;
struct list_head mux_list;
int muxnum;
}; };
/** /**
* struct p9_conn - fd mux connection state information * struct p9_conn - fd mux connection state information
* @lock: protects mux_list (?)
* @mux_list: list link for mux to manage multiple connections (?) * @mux_list: list link for mux to manage multiple connections (?)
* @poll_task: task polling on this connection * @client: reference to client instance for this connection
* @msize: maximum size for connection (dup)
* @extended: 9p2000.u flag (dup)
* @trans: reference to transport instance for this connection
* @tagpool: id accounting for transactions
* @err: error state * @err: error state
* @req_list: accounting for requests which have been sent * @req_list: accounting for requests which have been sent
* @unsent_req_list: accounting for requests that haven't been sent * @unsent_req_list: accounting for requests that haven't been sent
* @rcall: current response &p9_fcall structure * @req: current request being processed (if any)
* @tmp_buf: temporary buffer to read in header
* @rsize: amount to read for current frame
* @rpos: read position in current frame * @rpos: read position in current frame
* @rbuf: current read buffer * @rbuf: current read buffer
* @wpos: write position for current frame * @wpos: write position for current frame
...@@ -169,409 +129,300 @@ struct p9_mux_poll_task { ...@@ -169,409 +129,300 @@ struct p9_mux_poll_task {
*/ */
struct p9_conn { struct p9_conn {
spinlock_t lock; /* protect lock structure */
struct list_head mux_list; struct list_head mux_list;
struct p9_mux_poll_task *poll_task; struct p9_client *client;
int msize;
unsigned char extended;
struct p9_trans *trans;
struct p9_idpool *tagpool;
int err; int err;
struct list_head req_list; struct list_head req_list;
struct list_head unsent_req_list; struct list_head unsent_req_list;
struct p9_fcall *rcall; struct p9_req_t *req;
char tmp_buf[7];
int rsize;
int rpos; int rpos;
char *rbuf; char *rbuf;
int wpos; int wpos;
int wsize; int wsize;
char *wbuf; char *wbuf;
wait_queue_t poll_wait[MAXPOLLWADDR]; struct list_head poll_pending_link;
wait_queue_head_t *poll_waddr[MAXPOLLWADDR]; struct p9_poll_wait poll_wait[MAXPOLLWADDR];
poll_table pt; poll_table pt;
struct work_struct rq; struct work_struct rq;
struct work_struct wq; struct work_struct wq;
unsigned long wsched; unsigned long wsched;
}; };
/** static DEFINE_SPINLOCK(p9_poll_lock);
* struct p9_mux_rpc - fd mux rpc accounting structure static LIST_HEAD(p9_poll_pending_list);
* @m: connection this request was issued on
* @err: error state
* @tcall: request &p9_fcall
* @rcall: response &p9_fcall
* @wqueue: wait queue that client is blocked on for this rpc
*
* Bug: isn't this information duplicated elsewhere like &p9_req
*/
struct p9_mux_rpc {
struct p9_conn *m;
int err;
struct p9_fcall *tcall;
struct p9_fcall *rcall;
wait_queue_head_t wqueue;
};
static int p9_poll_proc(void *);
static void p9_read_work(struct work_struct *work);
static void p9_write_work(struct work_struct *work);
static void p9_pollwait(struct file *filp, wait_queue_head_t *wait_address,
poll_table *p);
static int p9_fd_write(struct p9_trans *trans, void *v, int len);
static int p9_fd_read(struct p9_trans *trans, void *v, int len);
static DEFINE_MUTEX(p9_mux_task_lock);
static struct workqueue_struct *p9_mux_wq; static struct workqueue_struct *p9_mux_wq;
static struct task_struct *p9_poll_task;
static int p9_mux_num; static void p9_mux_poll_stop(struct p9_conn *m)
static int p9_mux_poll_task_num;
static struct p9_mux_poll_task p9_mux_poll_tasks[100];
static void p9_conn_destroy(struct p9_conn *);
static unsigned int p9_fd_poll(struct p9_trans *trans,
struct poll_table_struct *pt);
#ifdef P9_NONBLOCK
static int p9_conn_rpcnb(struct p9_conn *m, struct p9_fcall *tc,
p9_conn_req_callback cb, void *a);
#endif /* P9_NONBLOCK */
static void p9_conn_cancel(struct p9_conn *m, int err);
static u16 p9_mux_get_tag(struct p9_conn *m)
{ {
int tag; unsigned long flags;
int i;
tag = p9_idpool_get(m->tagpool); for (i = 0; i < ARRAY_SIZE(m->poll_wait); i++) {
if (tag < 0) struct p9_poll_wait *pwait = &m->poll_wait[i];
return P9_NOTAG;
else
return (u16) tag;
}
static void p9_mux_put_tag(struct p9_conn *m, u16 tag) if (pwait->wait_addr) {
{ remove_wait_queue(pwait->wait_addr, &pwait->wait);
if (tag != P9_NOTAG && p9_idpool_check(tag, m->tagpool)) pwait->wait_addr = NULL;
p9_idpool_put(tag, m->tagpool); }
}
spin_lock_irqsave(&p9_poll_lock, flags);
list_del_init(&m->poll_pending_link);
spin_unlock_irqrestore(&p9_poll_lock, flags);
} }
/** /**
* p9_mux_calc_poll_procs - calculates the number of polling procs * p9_conn_cancel - cancel all pending requests with error
* @muxnum: number of mounts * @m: mux data
* @err: error code
* *
* Calculation is based on the number of mounted v9fs filesystems.
* The current implementation returns sqrt of the number of mounts.
*/ */
static int p9_mux_calc_poll_procs(int muxnum) static void p9_conn_cancel(struct p9_conn *m, int err)
{ {
int n; struct p9_req_t *req, *rtmp;
unsigned long flags;
if (p9_mux_poll_task_num) LIST_HEAD(cancel_list);
n = muxnum / p9_mux_poll_task_num +
(muxnum % p9_mux_poll_task_num ? 1 : 0);
else
n = 1;
if (n > ARRAY_SIZE(p9_mux_poll_tasks))
n = ARRAY_SIZE(p9_mux_poll_tasks);
return n; P9_DPRINTK(P9_DEBUG_ERROR, "mux %p err %d\n", m, err);
}
static int p9_mux_poll_start(struct p9_conn *m) spin_lock_irqsave(&m->client->lock, flags);
{
int i, n;
struct p9_mux_poll_task *vpt, *vptlast;
struct task_struct *pproc;
P9_DPRINTK(P9_DEBUG_MUX, "mux %p muxnum %d procnum %d\n", m, p9_mux_num,
p9_mux_poll_task_num);
mutex_lock(&p9_mux_task_lock);
n = p9_mux_calc_poll_procs(p9_mux_num + 1);
if (n > p9_mux_poll_task_num) {
for (i = 0; i < ARRAY_SIZE(p9_mux_poll_tasks); i++) {
if (p9_mux_poll_tasks[i].task == NULL) {
vpt = &p9_mux_poll_tasks[i];
P9_DPRINTK(P9_DEBUG_MUX, "create proc %p\n",
vpt);
pproc = kthread_create(p9_poll_proc, vpt,
"v9fs-poll");
if (!IS_ERR(pproc)) {
vpt->task = pproc;
INIT_LIST_HEAD(&vpt->mux_list);
vpt->muxnum = 0;
p9_mux_poll_task_num++;
wake_up_process(vpt->task);
}
break;
}
}
if (i >= ARRAY_SIZE(p9_mux_poll_tasks)) if (m->err) {
P9_DPRINTK(P9_DEBUG_ERROR, spin_unlock_irqrestore(&m->client->lock, flags);
"warning: no free poll slots\n"); return;
} }
n = (p9_mux_num + 1) / p9_mux_poll_task_num + m->err = err;
((p9_mux_num + 1) % p9_mux_poll_task_num ? 1 : 0);
vptlast = NULL;
for (i = 0; i < ARRAY_SIZE(p9_mux_poll_tasks); i++) {
vpt = &p9_mux_poll_tasks[i];
if (vpt->task != NULL) {
vptlast = vpt;
if (vpt->muxnum < n) {
P9_DPRINTK(P9_DEBUG_MUX, "put in proc %d\n", i);
list_add(&m->mux_list, &vpt->mux_list);
vpt->muxnum++;
m->poll_task = vpt;
memset(&m->poll_waddr, 0,
sizeof(m->poll_waddr));
init_poll_funcptr(&m->pt, p9_pollwait);
break;
}
}
}
if (i >= ARRAY_SIZE(p9_mux_poll_tasks)) { list_for_each_entry_safe(req, rtmp, &m->req_list, req_list) {
if (vptlast == NULL) { req->status = REQ_STATUS_ERROR;
mutex_unlock(&p9_mux_task_lock); if (!req->t_err)
return -ENOMEM; req->t_err = err;
list_move(&req->req_list, &cancel_list);
} }
list_for_each_entry_safe(req, rtmp, &m->unsent_req_list, req_list) {
P9_DPRINTK(P9_DEBUG_MUX, "put in proc %d\n", i); req->status = REQ_STATUS_ERROR;
list_add(&m->mux_list, &vptlast->mux_list); if (!req->t_err)
vptlast->muxnum++; req->t_err = err;
m->poll_task = vptlast; list_move(&req->req_list, &cancel_list);
memset(&m->poll_waddr, 0, sizeof(m->poll_waddr));
init_poll_funcptr(&m->pt, p9_pollwait);
} }
spin_unlock_irqrestore(&m->client->lock, flags);
p9_mux_num++; list_for_each_entry_safe(req, rtmp, &cancel_list, req_list) {
mutex_unlock(&p9_mux_task_lock); list_del(&req->req_list);
P9_DPRINTK(P9_DEBUG_ERROR, "call back req %p\n", req);
return 0; p9_client_cb(m->client, req);
}
static void p9_mux_poll_stop(struct p9_conn *m)
{
int i;
struct p9_mux_poll_task *vpt;
mutex_lock(&p9_mux_task_lock);
vpt = m->poll_task;
list_del(&m->mux_list);
for (i = 0; i < ARRAY_SIZE(m->poll_waddr); i++) {
if (m->poll_waddr[i] != NULL) {
remove_wait_queue(m->poll_waddr[i], &m->poll_wait[i]);
m->poll_waddr[i] = NULL;
}
}
vpt->muxnum--;
if (!vpt->muxnum) {
P9_DPRINTK(P9_DEBUG_MUX, "destroy proc %p\n", vpt);
kthread_stop(vpt->task);
vpt->task = NULL;
p9_mux_poll_task_num--;
} }
p9_mux_num--;
mutex_unlock(&p9_mux_task_lock);
} }
/** static unsigned int
* p9_conn_create - allocate and initialize the per-session mux data p9_fd_poll(struct p9_client *client, struct poll_table_struct *pt)
* @trans: transport structure
*
* Note: Creates the polling task if this is the first session.
*/
static struct p9_conn *p9_conn_create(struct p9_trans *trans)
{ {
int i, n; int ret, n;
struct p9_conn *m; struct p9_trans_fd *ts = NULL;
P9_DPRINTK(P9_DEBUG_MUX, "transport %p msize %d\n", trans, if (client && client->status == Connected)
trans->msize); ts = client->trans;
m = kzalloc(sizeof(struct p9_conn), GFP_KERNEL);
if (!m)
return ERR_PTR(-ENOMEM);
spin_lock_init(&m->lock); if (!ts)
INIT_LIST_HEAD(&m->mux_list); return -EREMOTEIO;
m->msize = trans->msize;
m->extended = trans->extended;
m->trans = trans;
m->tagpool = p9_idpool_create();
if (IS_ERR(m->tagpool)) {
kfree(m);
return ERR_PTR(-ENOMEM);
}
INIT_LIST_HEAD(&m->req_list); if (!ts->rd->f_op || !ts->rd->f_op->poll)
INIT_LIST_HEAD(&m->unsent_req_list); return -EIO;
INIT_WORK(&m->rq, p9_read_work);
INIT_WORK(&m->wq, p9_write_work);
n = p9_mux_poll_start(m);
if (n) {
kfree(m);
return ERR_PTR(n);
}
n = p9_fd_poll(trans, &m->pt); if (!ts->wr->f_op || !ts->wr->f_op->poll)
if (n & POLLIN) { return -EIO;
P9_DPRINTK(P9_DEBUG_MUX, "mux %p can read\n", m);
set_bit(Rpending, &m->wsched);
}
if (n & POLLOUT) { ret = ts->rd->f_op->poll(ts->rd, pt);
P9_DPRINTK(P9_DEBUG_MUX, "mux %p can write\n", m); if (ret < 0)
set_bit(Wpending, &m->wsched); return ret;
}
for (i = 0; i < ARRAY_SIZE(m->poll_waddr); i++) { if (ts->rd != ts->wr) {
if (IS_ERR(m->poll_waddr[i])) { n = ts->wr->f_op->poll(ts->wr, pt);
p9_mux_poll_stop(m); if (n < 0)
kfree(m); return n;
return (void *)m->poll_waddr; /* the error code */ ret = (ret & ~POLLOUT) | (n & ~POLLIN);
}
} }
return m; return ret;
} }
/** /**
* p9_mux_destroy - cancels all pending requests and frees mux resources * p9_fd_read- read from a fd
* @m: mux to destroy * @client: client instance
* @v: buffer to receive data into
* @len: size of receive buffer
* *
*/ */
static void p9_conn_destroy(struct p9_conn *m) static int p9_fd_read(struct p9_client *client, void *v, int len)
{ {
P9_DPRINTK(P9_DEBUG_MUX, "mux %p prev %p next %p\n", m, int ret;
m->mux_list.prev, m->mux_list.next); struct p9_trans_fd *ts = NULL;
p9_mux_poll_stop(m); if (client && client->status != Disconnected)
cancel_work_sync(&m->rq); ts = client->trans;
cancel_work_sync(&m->wq);
p9_conn_cancel(m, -ECONNRESET); if (!ts)
return -EREMOTEIO;
m->trans = NULL; if (!(ts->rd->f_flags & O_NONBLOCK))
p9_idpool_destroy(m->tagpool); P9_DPRINTK(P9_DEBUG_ERROR, "blocking read ...\n");
kfree(m);
ret = kernel_read(ts->rd, ts->rd->f_pos, v, len);
if (ret <= 0 && ret != -ERESTARTSYS && ret != -EAGAIN)
client->status = Disconnected;
return ret;
} }
/** /**
* p9_pollwait - add poll task to the wait queue * p9_read_work - called when there is some data to be read from a transport
* @filp: file pointer being polled * @work: container of work to be done
* @wait_address: wait_q to block on
* @p: poll state
* *
* called by files poll operation to add v9fs-poll task to files wait queue
*/ */
static void static void p9_read_work(struct work_struct *work)
p9_pollwait(struct file *filp, wait_queue_head_t *wait_address, poll_table *p)
{ {
int i; int n, err;
struct p9_conn *m; struct p9_conn *m;
m = container_of(p, struct p9_conn, pt); m = container_of(work, struct p9_conn, rq);
for (i = 0; i < ARRAY_SIZE(m->poll_waddr); i++)
if (m->poll_waddr[i] == NULL)
break;
if (i >= ARRAY_SIZE(m->poll_waddr)) { if (m->err < 0)
P9_DPRINTK(P9_DEBUG_ERROR, "not enough wait_address slots\n");
return; return;
}
m->poll_waddr[i] = wait_address; P9_DPRINTK(P9_DEBUG_TRANS, "start mux %p pos %d\n", m, m->rpos);
if (!wait_address) { if (!m->rbuf) {
P9_DPRINTK(P9_DEBUG_ERROR, "no wait_address\n"); m->rbuf = m->tmp_buf;
m->poll_waddr[i] = ERR_PTR(-EIO); m->rpos = 0;
m->rsize = 7; /* start by reading header */
}
clear_bit(Rpending, &m->wsched);
P9_DPRINTK(P9_DEBUG_TRANS, "read mux %p pos %d size: %d = %d\n", m,
m->rpos, m->rsize, m->rsize-m->rpos);
err = p9_fd_read(m->client, m->rbuf + m->rpos,
m->rsize - m->rpos);
P9_DPRINTK(P9_DEBUG_TRANS, "mux %p got %d bytes\n", m, err);
if (err == -EAGAIN) {
clear_bit(Rworksched, &m->wsched);
return; return;
} }
init_waitqueue_entry(&m->poll_wait[i], m->poll_task->task); if (err <= 0)
add_wait_queue(wait_address, &m->poll_wait[i]); goto error;
}
/** m->rpos += err;
* p9_poll_mux - polls a mux and schedules read or write works if necessary
* @m: connection to poll
*
*/
static void p9_poll_mux(struct p9_conn *m) if ((!m->req) && (m->rpos == m->rsize)) { /* header read in */
{ u16 tag;
int n; P9_DPRINTK(P9_DEBUG_TRANS, "got new header\n");
if (m->err < 0) n = le32_to_cpu(*(__le32 *) m->rbuf); /* read packet size */
return; if (n >= m->client->msize) {
P9_DPRINTK(P9_DEBUG_ERROR,
"requested packet size too big: %d\n", n);
err = -EIO;
goto error;
}
n = p9_fd_poll(m->trans, NULL); tag = le16_to_cpu(*(__le16 *) (m->rbuf+5)); /* read tag */
if (n < 0 || n & (POLLERR | POLLHUP | POLLNVAL)) { P9_DPRINTK(P9_DEBUG_TRANS,
P9_DPRINTK(P9_DEBUG_MUX, "error mux %p err %d\n", m, n); "mux %p pkt: size: %d bytes tag: %d\n", m, n, tag);
if (n >= 0)
n = -ECONNRESET; m->req = p9_tag_lookup(m->client, tag);
p9_conn_cancel(m, n); if (!m->req) {
P9_DPRINTK(P9_DEBUG_ERROR, "Unexpected packet tag %d\n",
tag);
err = -EIO;
goto error;
} }
if (n & POLLIN) { if (m->req->rc == NULL) {
set_bit(Rpending, &m->wsched); m->req->rc = kmalloc(sizeof(struct p9_fcall) +
P9_DPRINTK(P9_DEBUG_MUX, "mux %p can read\n", m); m->client->msize, GFP_KERNEL);
if (!test_and_set_bit(Rworksched, &m->wsched)) { if (!m->req->rc) {
P9_DPRINTK(P9_DEBUG_MUX, "schedule read work %p\n", m); m->req = NULL;
queue_work(p9_mux_wq, &m->rq); err = -ENOMEM;
goto error;
} }
} }
m->rbuf = (char *)m->req->rc + sizeof(struct p9_fcall);
if (n & POLLOUT) { memcpy(m->rbuf, m->tmp_buf, m->rsize);
set_bit(Wpending, &m->wsched); m->rsize = n;
P9_DPRINTK(P9_DEBUG_MUX, "mux %p can write\n", m);
if ((m->wsize || !list_empty(&m->unsent_req_list))
&& !test_and_set_bit(Wworksched, &m->wsched)) {
P9_DPRINTK(P9_DEBUG_MUX, "schedule write work %p\n", m);
queue_work(p9_mux_wq, &m->wq);
} }
/* not an else because some packets (like clunk) have no payload */
if ((m->req) && (m->rpos == m->rsize)) { /* packet is read in */
P9_DPRINTK(P9_DEBUG_TRANS, "got new packet\n");
spin_lock(&m->client->lock);
list_del(&m->req->req_list);
spin_unlock(&m->client->lock);
p9_client_cb(m->client, m->req);
m->rbuf = NULL;
m->rpos = 0;
m->rsize = 0;
m->req = NULL;
} }
if (!list_empty(&m->req_list)) {
if (test_and_clear_bit(Rpending, &m->wsched))
n = POLLIN;
else
n = p9_fd_poll(m->client, NULL);
if (n & POLLIN) {
P9_DPRINTK(P9_DEBUG_TRANS, "sched read work %p\n", m);
queue_work(p9_mux_wq, &m->rq);
} else
clear_bit(Rworksched, &m->wsched);
} else
clear_bit(Rworksched, &m->wsched);
return;
error:
p9_conn_cancel(m, err);
clear_bit(Rworksched, &m->wsched);
} }
/** /**
* p9_poll_proc - poll worker thread * p9_fd_write - write to a socket
* @a: thread state and arguments * @client: client instance
* * @v: buffer to send data from
* polls all v9fs transports for new events and queues the appropriate * @len: size of send buffer
* work to the work queue
* *
*/ */
static int p9_poll_proc(void *a) static int p9_fd_write(struct p9_client *client, void *v, int len)
{ {
struct p9_conn *m, *mtmp; int ret;
struct p9_mux_poll_task *vpt; mm_segment_t oldfs;
struct p9_trans_fd *ts = NULL;
vpt = a; if (client && client->status != Disconnected)
P9_DPRINTK(P9_DEBUG_MUX, "start %p %p\n", current, vpt); ts = client->trans;
while (!kthread_should_stop()) {
set_current_state(TASK_INTERRUPTIBLE);
list_for_each_entry_safe(m, mtmp, &vpt->mux_list, mux_list) { if (!ts)
p9_poll_mux(m); return -EREMOTEIO;
}
P9_DPRINTK(P9_DEBUG_MUX, "sleeping...\n"); if (!(ts->wr->f_flags & O_NONBLOCK))
schedule_timeout(SCHED_TIMEOUT * HZ); P9_DPRINTK(P9_DEBUG_ERROR, "blocking write ...\n");
}
__set_current_state(TASK_RUNNING); oldfs = get_fs();
P9_DPRINTK(P9_DEBUG_MUX, "finish\n"); set_fs(get_ds());
return 0; /* The cast to a user pointer is valid due to the set_fs() */
ret = vfs_write(ts->wr, (void __user *)v, len, &ts->wr->f_pos);
set_fs(oldfs);
if (ret <= 0 && ret != -ERESTARTSYS && ret != -EAGAIN)
client->status = Disconnected;
return ret;
} }
/** /**
...@@ -584,7 +435,7 @@ static void p9_write_work(struct work_struct *work) ...@@ -584,7 +435,7 @@ static void p9_write_work(struct work_struct *work)
{ {
int n, err; int n, err;
struct p9_conn *m; struct p9_conn *m;
struct p9_req *req; struct p9_req_t *req;
m = container_of(work, struct p9_conn, wq); m = container_of(work, struct p9_conn, wq);
...@@ -599,25 +450,23 @@ static void p9_write_work(struct work_struct *work) ...@@ -599,25 +450,23 @@ static void p9_write_work(struct work_struct *work)
return; return;
} }
spin_lock(&m->lock); spin_lock(&m->client->lock);
again: req = list_entry(m->unsent_req_list.next, struct p9_req_t,
req = list_entry(m->unsent_req_list.next, struct p9_req,
req_list); req_list);
req->status = REQ_STATUS_SENT;
list_move_tail(&req->req_list, &m->req_list); list_move_tail(&req->req_list, &m->req_list);
if (req->err == ERREQFLUSH)
goto again;
m->wbuf = req->tcall->sdata; m->wbuf = req->tc->sdata;
m->wsize = req->tcall->size; m->wsize = req->tc->size;
m->wpos = 0; m->wpos = 0;
spin_unlock(&m->lock); spin_unlock(&m->client->lock);
} }
P9_DPRINTK(P9_DEBUG_MUX, "mux %p pos %d size %d\n", m, m->wpos, P9_DPRINTK(P9_DEBUG_TRANS, "mux %p pos %d size %d\n", m, m->wpos,
m->wsize); m->wsize);
clear_bit(Wpending, &m->wsched); clear_bit(Wpending, &m->wsched);
err = p9_fd_write(m->trans, m->wbuf + m->wpos, m->wsize - m->wpos); err = p9_fd_write(m->client, m->wbuf + m->wpos, m->wsize - m->wpos);
P9_DPRINTK(P9_DEBUG_MUX, "mux %p sent %d bytes\n", m, err); P9_DPRINTK(P9_DEBUG_TRANS, "mux %p sent %d bytes\n", m, err);
if (err == -EAGAIN) { if (err == -EAGAIN) {
clear_bit(Wworksched, &m->wsched); clear_bit(Wworksched, &m->wsched);
return; return;
...@@ -638,10 +487,10 @@ static void p9_write_work(struct work_struct *work) ...@@ -638,10 +487,10 @@ static void p9_write_work(struct work_struct *work)
if (test_and_clear_bit(Wpending, &m->wsched)) if (test_and_clear_bit(Wpending, &m->wsched))
n = POLLOUT; n = POLLOUT;
else else
n = p9_fd_poll(m->trans, NULL); n = p9_fd_poll(m->client, NULL);
if (n & POLLOUT) { if (n & POLLOUT) {
P9_DPRINTK(P9_DEBUG_MUX, "schedule write work %p\n", m); P9_DPRINTK(P9_DEBUG_TRANS, "sched write work %p\n", m);
queue_work(p9_mux_wq, &m->wq); queue_work(p9_mux_wq, &m->wq);
} else } else
clear_bit(Wworksched, &m->wsched); clear_bit(Wworksched, &m->wsched);
...@@ -652,507 +501,200 @@ static void p9_write_work(struct work_struct *work) ...@@ -652,507 +501,200 @@ static void p9_write_work(struct work_struct *work)
error: error:
p9_conn_cancel(m, err); p9_conn_cancel(m, err);
clear_bit(Wworksched, &m->wsched); clear_bit(Wworksched, &m->wsched);
}
static void process_request(struct p9_conn *m, struct p9_req *req)
{
int ecode;
struct p9_str *ename;
if (!req->err && req->rcall->id == P9_RERROR) {
ecode = req->rcall->params.rerror.errno;
ename = &req->rcall->params.rerror.error;
P9_DPRINTK(P9_DEBUG_MUX, "Rerror %.*s\n", ename->len,
ename->str);
if (m->extended)
req->err = -ecode;
if (!req->err) {
req->err = p9_errstr2errno(ename->str, ename->len);
/* string match failed */
if (!req->err) {
PRINT_FCALL_ERROR("unknown error", req->rcall);
req->err = -ESERVERFAULT;
}
}
} else if (req->tcall && req->rcall->id != req->tcall->id + 1) {
P9_DPRINTK(P9_DEBUG_ERROR,
"fcall mismatch: expected %d, got %d\n",
req->tcall->id + 1, req->rcall->id);
if (!req->err)
req->err = -EIO;
}
}
/**
* p9_read_work - called when there is some data to be read from a transport
* @work: container of work to be done
*
*/
static void p9_read_work(struct work_struct *work)
{
int n, err;
struct p9_conn *m;
struct p9_req *req, *rptr, *rreq;
struct p9_fcall *rcall;
char *rbuf;
m = container_of(work, struct p9_conn, rq);
if (m->err < 0)
return;
rcall = NULL;
P9_DPRINTK(P9_DEBUG_MUX, "start mux %p pos %d\n", m, m->rpos);
if (!m->rcall) {
m->rcall =
kmalloc(sizeof(struct p9_fcall) + m->msize, GFP_KERNEL);
if (!m->rcall) {
err = -ENOMEM;
goto error;
}
m->rbuf = (char *)m->rcall + sizeof(struct p9_fcall);
m->rpos = 0;
}
clear_bit(Rpending, &m->wsched);
err = p9_fd_read(m->trans, m->rbuf + m->rpos, m->msize - m->rpos);
P9_DPRINTK(P9_DEBUG_MUX, "mux %p got %d bytes\n", m, err);
if (err == -EAGAIN) {
clear_bit(Rworksched, &m->wsched);
return;
}
if (err <= 0)
goto error;
m->rpos += err;
while (m->rpos > 4) {
n = le32_to_cpu(*(__le32 *) m->rbuf);
if (n >= m->msize) {
P9_DPRINTK(P9_DEBUG_ERROR,
"requested packet size too big: %d\n", n);
err = -EIO;
goto error;
}
if (m->rpos < n)
break;
err =
p9_deserialize_fcall(m->rbuf, n, m->rcall, m->extended);
if (err < 0)
goto error;
#ifdef CONFIG_NET_9P_DEBUG
if ((p9_debug_level&P9_DEBUG_FCALL) == P9_DEBUG_FCALL) {
char buf[150];
p9_printfcall(buf, sizeof(buf), m->rcall,
m->extended);
printk(KERN_NOTICE ">>> %p %s\n", m, buf);
}
#endif
rcall = m->rcall;
rbuf = m->rbuf;
if (m->rpos > n) {
m->rcall = kmalloc(sizeof(struct p9_fcall) + m->msize,
GFP_KERNEL);
if (!m->rcall) {
err = -ENOMEM;
goto error;
}
m->rbuf = (char *)m->rcall + sizeof(struct p9_fcall);
memmove(m->rbuf, rbuf + n, m->rpos - n);
m->rpos -= n;
} else {
m->rcall = NULL;
m->rbuf = NULL;
m->rpos = 0;
}
P9_DPRINTK(P9_DEBUG_MUX, "mux %p fcall id %d tag %d\n", m,
rcall->id, rcall->tag);
req = NULL;
spin_lock(&m->lock);
list_for_each_entry_safe(rreq, rptr, &m->req_list, req_list) {
if (rreq->tag == rcall->tag) {
req = rreq;
if (req->flush != Flushing)
list_del(&req->req_list);
break;
}
}
spin_unlock(&m->lock);
if (req) {
req->rcall = rcall;
process_request(m, req);
if (req->flush != Flushing) {
if (req->cb)
(*req->cb) (req, req->cba);
else
kfree(req->rcall);
}
} else {
if (err >= 0 && rcall->id != P9_RFLUSH)
P9_DPRINTK(P9_DEBUG_ERROR,
"unexpected response mux %p id %d tag %d\n",
m, rcall->id, rcall->tag);
kfree(rcall);
}
}
if (!list_empty(&m->req_list)) {
if (test_and_clear_bit(Rpending, &m->wsched))
n = POLLIN;
else
n = p9_fd_poll(m->trans, NULL);
if (n & POLLIN) {
P9_DPRINTK(P9_DEBUG_MUX, "schedule read work %p\n", m);
queue_work(p9_mux_wq, &m->rq);
} else
clear_bit(Rworksched, &m->wsched);
} else
clear_bit(Rworksched, &m->wsched);
return;
error:
p9_conn_cancel(m, err);
clear_bit(Rworksched, &m->wsched);
}
/**
* p9_send_request - send 9P request
* The function can sleep until the request is scheduled for sending.
* The function can be interrupted. Return from the function is not
* a guarantee that the request is sent successfully. Can return errors
* that can be retrieved by PTR_ERR macros.
*
* @m: mux data
* @tc: request to be sent
* @cb: callback function to call when response is received
* @cba: parameter to pass to the callback function
*
*/
static struct p9_req *p9_send_request(struct p9_conn *m,
struct p9_fcall *tc,
p9_conn_req_callback cb, void *cba)
{
int n;
struct p9_req *req;
P9_DPRINTK(P9_DEBUG_MUX, "mux %p task %p tcall %p id %d\n", m, current,
tc, tc->id);
if (m->err < 0)
return ERR_PTR(m->err);
req = kmalloc(sizeof(struct p9_req), GFP_KERNEL);
if (!req)
return ERR_PTR(-ENOMEM);
if (tc->id == P9_TVERSION)
n = P9_NOTAG;
else
n = p9_mux_get_tag(m);
if (n < 0) {
kfree(req);
return ERR_PTR(-ENOMEM);
}
p9_set_tag(tc, n);
#ifdef CONFIG_NET_9P_DEBUG
if ((p9_debug_level&P9_DEBUG_FCALL) == P9_DEBUG_FCALL) {
char buf[150];
p9_printfcall(buf, sizeof(buf), tc, m->extended);
printk(KERN_NOTICE "<<< %p %s\n", m, buf);
}
#endif
spin_lock_init(&req->lock);
req->tag = n;
req->tcall = tc;
req->rcall = NULL;
req->err = 0;
req->cb = cb;
req->cba = cba;
req->flush = None;
spin_lock(&m->lock);
list_add_tail(&req->req_list, &m->unsent_req_list);
spin_unlock(&m->lock);
if (test_and_clear_bit(Wpending, &m->wsched))
n = POLLOUT;
else
n = p9_fd_poll(m->trans, NULL);
if (n & POLLOUT && !test_and_set_bit(Wworksched, &m->wsched))
queue_work(p9_mux_wq, &m->wq);
return req;
} }
static void p9_mux_free_request(struct p9_conn *m, struct p9_req *req) static int p9_pollwake(wait_queue_t *wait, unsigned mode, int sync, void *key)
{ {
p9_mux_put_tag(m, req->tag); struct p9_poll_wait *pwait =
kfree(req); container_of(wait, struct p9_poll_wait, wait);
struct p9_conn *m = pwait->conn;
unsigned long flags;
DECLARE_WAITQUEUE(dummy_wait, p9_poll_task);
spin_lock_irqsave(&p9_poll_lock, flags);
if (list_empty(&m->poll_pending_link))
list_add_tail(&m->poll_pending_link, &p9_poll_pending_list);
spin_unlock_irqrestore(&p9_poll_lock, flags);
/* perform the default wake up operation */
return default_wake_function(&dummy_wait, mode, sync, key);
} }
static void p9_mux_flush_cb(struct p9_req *freq, void *a) /**
* p9_pollwait - add poll task to the wait queue
* @filp: file pointer being polled
* @wait_address: wait_q to block on
* @p: poll state
*
* called by files poll operation to add v9fs-poll task to files wait queue
*/
static void
p9_pollwait(struct file *filp, wait_queue_head_t *wait_address, poll_table *p)
{ {
int tag; struct p9_conn *m = container_of(p, struct p9_conn, pt);
struct p9_conn *m; struct p9_poll_wait *pwait = NULL;
struct p9_req *req, *rreq, *rptr; int i;
m = a; for (i = 0; i < ARRAY_SIZE(m->poll_wait); i++) {
P9_DPRINTK(P9_DEBUG_MUX, "mux %p tc %p rc %p err %d oldtag %d\n", m, if (m->poll_wait[i].wait_addr == NULL) {
freq->tcall, freq->rcall, freq->err, pwait = &m->poll_wait[i];
freq->tcall->params.tflush.oldtag);
spin_lock(&m->lock);
tag = freq->tcall->params.tflush.oldtag;
req = NULL;
list_for_each_entry_safe(rreq, rptr, &m->req_list, req_list) {
if (rreq->tag == tag) {
req = rreq;
list_del(&req->req_list);
break; break;
} }
} }
spin_unlock(&m->lock);
if (req) {
spin_lock(&req->lock);
req->flush = Flushed;
spin_unlock(&req->lock);
if (req->cb) if (!pwait) {
(*req->cb) (req, req->cba); P9_DPRINTK(P9_DEBUG_ERROR, "not enough wait_address slots\n");
else return;
kfree(req->rcall);
} }
kfree(freq->tcall); pwait->conn = m;
kfree(freq->rcall); pwait->wait_addr = wait_address;
p9_mux_free_request(m, freq); init_waitqueue_func_entry(&pwait->wait, p9_pollwake);
add_wait_queue(wait_address, &pwait->wait);
} }
static int /**
p9_mux_flush_request(struct p9_conn *m, struct p9_req *req) * p9_conn_create - allocate and initialize the per-session mux data
{ * @client: client instance
struct p9_fcall *fc; *
struct p9_req *rreq, *rptr; * Note: Creates the polling task if this is the first session.
*/
P9_DPRINTK(P9_DEBUG_MUX, "mux %p req %p tag %d\n", m, req, req->tag);
/* if a response was received for a request, do nothing */ static struct p9_conn *p9_conn_create(struct p9_client *client)
spin_lock(&req->lock); {
if (req->rcall || req->err) { int n;
spin_unlock(&req->lock); struct p9_conn *m;
P9_DPRINTK(P9_DEBUG_MUX,
"mux %p req %p response already received\n", m, req);
return 0;
}
req->flush = Flushing; P9_DPRINTK(P9_DEBUG_TRANS, "client %p msize %d\n", client,
spin_unlock(&req->lock); client->msize);
m = kzalloc(sizeof(struct p9_conn), GFP_KERNEL);
spin_lock(&m->lock); if (!m)
/* if the request is not sent yet, just remove it from the list */ return ERR_PTR(-ENOMEM);
list_for_each_entry_safe(rreq, rptr, &m->unsent_req_list, req_list) {
if (rreq->tag == req->tag) {
P9_DPRINTK(P9_DEBUG_MUX,
"mux %p req %p request is not sent yet\n", m, req);
list_del(&rreq->req_list);
req->flush = Flushed;
spin_unlock(&m->lock);
if (req->cb)
(*req->cb) (req, req->cba);
return 0;
}
}
spin_unlock(&m->lock);
clear_thread_flag(TIF_SIGPENDING); INIT_LIST_HEAD(&m->mux_list);
fc = p9_create_tflush(req->tag); m->client = client;
p9_send_request(m, fc, p9_mux_flush_cb, m);
return 1;
}
static void INIT_LIST_HEAD(&m->req_list);
p9_conn_rpc_cb(struct p9_req *req, void *a) INIT_LIST_HEAD(&m->unsent_req_list);
{ INIT_WORK(&m->rq, p9_read_work);
struct p9_mux_rpc *r; INIT_WORK(&m->wq, p9_write_work);
INIT_LIST_HEAD(&m->poll_pending_link);
init_poll_funcptr(&m->pt, p9_pollwait);
P9_DPRINTK(P9_DEBUG_MUX, "req %p r %p\n", req, a); n = p9_fd_poll(client, &m->pt);
r = a; if (n & POLLIN) {
r->rcall = req->rcall; P9_DPRINTK(P9_DEBUG_TRANS, "mux %p can read\n", m);
r->err = req->err; set_bit(Rpending, &m->wsched);
}
if (req->flush != None && !req->err) if (n & POLLOUT) {
r->err = -ERESTARTSYS; P9_DPRINTK(P9_DEBUG_TRANS, "mux %p can write\n", m);
set_bit(Wpending, &m->wsched);
}
wake_up(&r->wqueue); return m;
} }
/** /**
* p9_fd_rpc- sends 9P request and waits until a response is available. * p9_poll_mux - polls a mux and schedules read or write works if necessary
* The function can be interrupted. * @m: connection to poll
* @t: transport data
* @tc: request to be sent
* @rc: pointer where a pointer to the response is stored
* *
*/ */
int static void p9_poll_mux(struct p9_conn *m)
p9_fd_rpc(struct p9_trans *t, struct p9_fcall *tc, struct p9_fcall **rc)
{ {
struct p9_trans_fd *p = t->priv; int n;
struct p9_conn *m = p->conn;
int err, sigpending;
unsigned long flags;
struct p9_req *req;
struct p9_mux_rpc r;
r.err = 0;
r.tcall = tc;
r.rcall = NULL;
r.m = m;
init_waitqueue_head(&r.wqueue);
if (rc)
*rc = NULL;
sigpending = 0;
if (signal_pending(current)) {
sigpending = 1;
clear_thread_flag(TIF_SIGPENDING);
}
req = p9_send_request(m, tc, p9_conn_rpc_cb, &r); if (m->err < 0)
if (IS_ERR(req)) { return;
err = PTR_ERR(req);
P9_DPRINTK(P9_DEBUG_MUX, "error %d\n", err); n = p9_fd_poll(m->client, NULL);
return err; if (n < 0 || n & (POLLERR | POLLHUP | POLLNVAL)) {
P9_DPRINTK(P9_DEBUG_TRANS, "error mux %p err %d\n", m, n);
if (n >= 0)
n = -ECONNRESET;
p9_conn_cancel(m, n);
} }
err = wait_event_interruptible(r.wqueue, r.rcall != NULL || r.err < 0); if (n & POLLIN) {
if (r.err < 0) set_bit(Rpending, &m->wsched);
err = r.err; P9_DPRINTK(P9_DEBUG_TRANS, "mux %p can read\n", m);
if (!test_and_set_bit(Rworksched, &m->wsched)) {
if (err == -ERESTARTSYS && m->trans->status == Connected P9_DPRINTK(P9_DEBUG_TRANS, "sched read work %p\n", m);
&& m->err == 0) { queue_work(p9_mux_wq, &m->rq);
if (p9_mux_flush_request(m, req)) {
/* wait until we get response of the flush message */
do {
clear_thread_flag(TIF_SIGPENDING);
err = wait_event_interruptible(r.wqueue,
r.rcall || r.err);
} while (!r.rcall && !r.err && err == -ERESTARTSYS &&
m->trans->status == Connected && !m->err);
err = -ERESTARTSYS;
} }
sigpending = 1;
} }
if (sigpending) { if (n & POLLOUT) {
spin_lock_irqsave(&current->sighand->siglock, flags); set_bit(Wpending, &m->wsched);
recalc_sigpending(); P9_DPRINTK(P9_DEBUG_TRANS, "mux %p can write\n", m);
spin_unlock_irqrestore(&current->sighand->siglock, flags); if ((m->wsize || !list_empty(&m->unsent_req_list))
&& !test_and_set_bit(Wworksched, &m->wsched)) {
P9_DPRINTK(P9_DEBUG_TRANS, "sched write work %p\n", m);
queue_work(p9_mux_wq, &m->wq);
}
} }
if (rc)
*rc = r.rcall;
else
kfree(r.rcall);
p9_mux_free_request(m, req);
if (err > 0)
err = -EIO;
return err;
} }
#ifdef P9_NONBLOCK
/** /**
* p9_conn_rpcnb - sends 9P request without waiting for response. * p9_fd_request - send 9P request
* @m: mux data * The function can sleep until the request is scheduled for sending.
* @tc: request to be sent * The function can be interrupted. Return from the function is not
* @cb: callback function to be called when response arrives * a guarantee that the request is sent successfully.
* @a: value to pass to the callback function *
* @client: client instance
* @req: request to be sent
* *
*/ */
int p9_conn_rpcnb(struct p9_conn *m, struct p9_fcall *tc, static int p9_fd_request(struct p9_client *client, struct p9_req_t *req)
p9_conn_req_callback cb, void *a)
{ {
int err; int n;
struct p9_req *req; struct p9_trans_fd *ts = client->trans;
struct p9_conn *m = ts->conn;
req = p9_send_request(m, tc, cb, a); P9_DPRINTK(P9_DEBUG_TRANS, "mux %p task %p tcall %p id %d\n", m,
if (IS_ERR(req)) { current, req->tc, req->tc->id);
err = PTR_ERR(req); if (m->err < 0)
P9_DPRINTK(P9_DEBUG_MUX, "error %d\n", err); return m->err;
return PTR_ERR(req);
} spin_lock(&client->lock);
req->status = REQ_STATUS_UNSENT;
list_add_tail(&req->req_list, &m->unsent_req_list);
spin_unlock(&client->lock);
if (test_and_clear_bit(Wpending, &m->wsched))
n = POLLOUT;
else
n = p9_fd_poll(m->client, NULL);
if (n & POLLOUT && !test_and_set_bit(Wworksched, &m->wsched))
queue_work(p9_mux_wq, &m->wq);
P9_DPRINTK(P9_DEBUG_MUX, "mux %p tc %p tag %d\n", m, tc, req->tag);
return 0; return 0;
} }
#endif /* P9_NONBLOCK */
/**
* p9_conn_cancel - cancel all pending requests with error
* @m: mux data
* @err: error code
*
*/
void p9_conn_cancel(struct p9_conn *m, int err) static int p9_fd_cancel(struct p9_client *client, struct p9_req_t *req)
{ {
struct p9_req *req, *rtmp; struct p9_trans_fd *ts = client->trans;
LIST_HEAD(cancel_list); struct p9_conn *m = ts->conn;
int ret = 1;
P9_DPRINTK(P9_DEBUG_ERROR, "mux %p err %d\n", m, err); P9_DPRINTK(P9_DEBUG_TRANS, "mux %p req %p\n", m, req);
m->err = err;
spin_lock(&m->lock);
list_for_each_entry_safe(req, rtmp, &m->req_list, req_list) {
list_move(&req->req_list, &cancel_list);
}
list_for_each_entry_safe(req, rtmp, &m->unsent_req_list, req_list) {
list_move(&req->req_list, &cancel_list);
}
spin_unlock(&m->lock);
list_for_each_entry_safe(req, rtmp, &cancel_list, req_list) { spin_lock(&client->lock);
list_del(&req->req_list); list_del(&req->req_list);
if (!req->err)
req->err = err;
if (req->cb) if (req->status == REQ_STATUS_UNSENT) {
(*req->cb) (req, req->cba); req->status = REQ_STATUS_FLSHD;
else ret = 0;
kfree(req->rcall);
} }
spin_unlock(&client->lock);
return ret;
} }
/** /**
...@@ -1216,7 +758,7 @@ static int parse_opts(char *params, struct p9_fd_opts *opts) ...@@ -1216,7 +758,7 @@ static int parse_opts(char *params, struct p9_fd_opts *opts)
return 0; return 0;
} }
static int p9_fd_open(struct p9_trans *trans, int rfd, int wfd) static int p9_fd_open(struct p9_client *client, int rfd, int wfd)
{ {
struct p9_trans_fd *ts = kmalloc(sizeof(struct p9_trans_fd), struct p9_trans_fd *ts = kmalloc(sizeof(struct p9_trans_fd),
GFP_KERNEL); GFP_KERNEL);
...@@ -1234,13 +776,13 @@ static int p9_fd_open(struct p9_trans *trans, int rfd, int wfd) ...@@ -1234,13 +776,13 @@ static int p9_fd_open(struct p9_trans *trans, int rfd, int wfd)
return -EIO; return -EIO;
} }
trans->priv = ts; client->trans = ts;
trans->status = Connected; client->status = Connected;
return 0; return 0;
} }
static int p9_socket_open(struct p9_trans *trans, struct socket *csocket) static int p9_socket_open(struct p9_client *client, struct socket *csocket)
{ {
int fd, ret; int fd, ret;
...@@ -1251,137 +793,65 @@ static int p9_socket_open(struct p9_trans *trans, struct socket *csocket) ...@@ -1251,137 +793,65 @@ static int p9_socket_open(struct p9_trans *trans, struct socket *csocket)
return fd; return fd;
} }
ret = p9_fd_open(trans, fd, fd); ret = p9_fd_open(client, fd, fd);
if (ret < 0) { if (ret < 0) {
P9_EPRINTK(KERN_ERR, "p9_socket_open: failed to open fd\n"); P9_EPRINTK(KERN_ERR, "p9_socket_open: failed to open fd\n");
sockfd_put(csocket); sockfd_put(csocket);
return ret; return ret;
} }
((struct p9_trans_fd *)trans->priv)->rd->f_flags |= O_NONBLOCK; ((struct p9_trans_fd *)client->trans)->rd->f_flags |= O_NONBLOCK;
return 0; return 0;
} }
/** /**
* p9_fd_read- read from a fd * p9_mux_destroy - cancels all pending requests and frees mux resources
* @trans: transport instance state * @m: mux to destroy
* @v: buffer to receive data into
* @len: size of receive buffer
*
*/
static int p9_fd_read(struct p9_trans *trans, void *v, int len)
{
int ret;
struct p9_trans_fd *ts = NULL;
if (trans && trans->status != Disconnected)
ts = trans->priv;
if (!ts)
return -EREMOTEIO;
if (!(ts->rd->f_flags & O_NONBLOCK))
P9_DPRINTK(P9_DEBUG_ERROR, "blocking read ...\n");
ret = kernel_read(ts->rd, ts->rd->f_pos, v, len);
if (ret <= 0 && ret != -ERESTARTSYS && ret != -EAGAIN)
trans->status = Disconnected;
return ret;
}
/**
* p9_fd_write - write to a socket
* @trans: transport instance state
* @v: buffer to send data from
* @len: size of send buffer
* *
*/ */
static int p9_fd_write(struct p9_trans *trans, void *v, int len) static void p9_conn_destroy(struct p9_conn *m)
{
int ret;
mm_segment_t oldfs;
struct p9_trans_fd *ts = NULL;
if (trans && trans->status != Disconnected)
ts = trans->priv;
if (!ts)
return -EREMOTEIO;
if (!(ts->wr->f_flags & O_NONBLOCK))
P9_DPRINTK(P9_DEBUG_ERROR, "blocking write ...\n");
oldfs = get_fs();
set_fs(get_ds());
/* The cast to a user pointer is valid due to the set_fs() */
ret = vfs_write(ts->wr, (void __user *)v, len, &ts->wr->f_pos);
set_fs(oldfs);
if (ret <= 0 && ret != -ERESTARTSYS && ret != -EAGAIN)
trans->status = Disconnected;
return ret;
}
static unsigned int
p9_fd_poll(struct p9_trans *trans, struct poll_table_struct *pt)
{ {
int ret, n; P9_DPRINTK(P9_DEBUG_TRANS, "mux %p prev %p next %p\n", m,
struct p9_trans_fd *ts = NULL; m->mux_list.prev, m->mux_list.next);
if (trans && trans->status == Connected)
ts = trans->priv;
if (!ts)
return -EREMOTEIO;
if (!ts->rd->f_op || !ts->rd->f_op->poll)
return -EIO;
if (!ts->wr->f_op || !ts->wr->f_op->poll)
return -EIO;
ret = ts->rd->f_op->poll(ts->rd, pt); p9_mux_poll_stop(m);
if (ret < 0) cancel_work_sync(&m->rq);
return ret; cancel_work_sync(&m->wq);
if (ts->rd != ts->wr) { p9_conn_cancel(m, -ECONNRESET);
n = ts->wr->f_op->poll(ts->wr, pt);
if (n < 0)
return n;
ret = (ret & ~POLLOUT) | (n & ~POLLIN);
}
return ret; m->client = NULL;
kfree(m);
} }
/** /**
* p9_fd_close - shutdown socket * p9_fd_close - shutdown file descriptor transport
* @trans: private socket structure * @client: client instance
* *
*/ */
static void p9_fd_close(struct p9_trans *trans) static void p9_fd_close(struct p9_client *client)
{ {
struct p9_trans_fd *ts; struct p9_trans_fd *ts;
if (!trans) if (!client)
return; return;
ts = xchg(&trans->priv, NULL); ts = client->trans;
if (!ts) if (!ts)
return; return;
client->status = Disconnected;
p9_conn_destroy(ts->conn); p9_conn_destroy(ts->conn);
trans->status = Disconnected;
if (ts->rd) if (ts->rd)
fput(ts->rd); fput(ts->rd);
if (ts->wr) if (ts->wr)
fput(ts->wr); fput(ts->wr);
kfree(ts); kfree(ts);
} }
...@@ -1402,31 +872,23 @@ static inline int valid_ipaddr4(const char *buf) ...@@ -1402,31 +872,23 @@ static inline int valid_ipaddr4(const char *buf)
return 0; return 0;
} }
static struct p9_trans * static int
p9_trans_create_tcp(const char *addr, char *args, int msize, unsigned char dotu) p9_fd_create_tcp(struct p9_client *client, const char *addr, char *args)
{ {
int err; int err;
struct p9_trans *trans;
struct socket *csocket; struct socket *csocket;
struct sockaddr_in sin_server; struct sockaddr_in sin_server;
struct p9_fd_opts opts; struct p9_fd_opts opts;
struct p9_trans_fd *p; struct p9_trans_fd *p = NULL; /* this gets allocated in p9_fd_open */
err = parse_opts(args, &opts); err = parse_opts(args, &opts);
if (err < 0) if (err < 0)
return ERR_PTR(err); return err;
if (valid_ipaddr4(addr) < 0) if (valid_ipaddr4(addr) < 0)
return ERR_PTR(-EINVAL); return -EINVAL;
csocket = NULL; csocket = NULL;
trans = kmalloc(sizeof(struct p9_trans), GFP_KERNEL);
if (!trans)
return ERR_PTR(-ENOMEM);
trans->msize = msize;
trans->extended = dotu;
trans->rpc = p9_fd_rpc;
trans->close = p9_fd_close;
sin_server.sin_family = AF_INET; sin_server.sin_family = AF_INET;
sin_server.sin_addr.s_addr = in_aton(addr); sin_server.sin_addr.s_addr = in_aton(addr);
...@@ -1449,45 +911,38 @@ p9_trans_create_tcp(const char *addr, char *args, int msize, unsigned char dotu) ...@@ -1449,45 +911,38 @@ p9_trans_create_tcp(const char *addr, char *args, int msize, unsigned char dotu)
goto error; goto error;
} }
err = p9_socket_open(trans, csocket); err = p9_socket_open(client, csocket);
if (err < 0) if (err < 0)
goto error; goto error;
p = (struct p9_trans_fd *) trans->priv; p = (struct p9_trans_fd *) client->trans;
p->conn = p9_conn_create(trans); p->conn = p9_conn_create(client);
if (IS_ERR(p->conn)) { if (IS_ERR(p->conn)) {
err = PTR_ERR(p->conn); err = PTR_ERR(p->conn);
p->conn = NULL; p->conn = NULL;
goto error; goto error;
} }
return trans; return 0;
error: error:
if (csocket) if (csocket)
sock_release(csocket); sock_release(csocket);
kfree(trans); kfree(p);
return ERR_PTR(err);
return err;
} }
static struct p9_trans * static int
p9_trans_create_unix(const char *addr, char *args, int msize, p9_fd_create_unix(struct p9_client *client, const char *addr, char *args)
unsigned char dotu)
{ {
int err; int err;
struct socket *csocket; struct socket *csocket;
struct sockaddr_un sun_server; struct sockaddr_un sun_server;
struct p9_trans *trans; struct p9_trans_fd *p = NULL; /* this gets allocated in p9_fd_open */
struct p9_trans_fd *p;
csocket = NULL; csocket = NULL;
trans = kmalloc(sizeof(struct p9_trans), GFP_KERNEL);
if (!trans)
return ERR_PTR(-ENOMEM);
trans->rpc = p9_fd_rpc;
trans->close = p9_fd_close;
if (strlen(addr) > UNIX_PATH_MAX) { if (strlen(addr) > UNIX_PATH_MAX) {
P9_EPRINTK(KERN_ERR, "p9_trans_unix: address too long: %s\n", P9_EPRINTK(KERN_ERR, "p9_trans_unix: address too long: %s\n",
...@@ -1508,79 +963,69 @@ p9_trans_create_unix(const char *addr, char *args, int msize, ...@@ -1508,79 +963,69 @@ p9_trans_create_unix(const char *addr, char *args, int msize,
goto error; goto error;
} }
err = p9_socket_open(trans, csocket); err = p9_socket_open(client, csocket);
if (err < 0) if (err < 0)
goto error; goto error;
trans->msize = msize; p = (struct p9_trans_fd *) client->trans;
trans->extended = dotu; p->conn = p9_conn_create(client);
p = (struct p9_trans_fd *) trans->priv;
p->conn = p9_conn_create(trans);
if (IS_ERR(p->conn)) { if (IS_ERR(p->conn)) {
err = PTR_ERR(p->conn); err = PTR_ERR(p->conn);
p->conn = NULL; p->conn = NULL;
goto error; goto error;
} }
return trans; return 0;
error: error:
if (csocket) if (csocket)
sock_release(csocket); sock_release(csocket);
kfree(trans); kfree(p);
return ERR_PTR(err); return err;
} }
static struct p9_trans * static int
p9_trans_create_fd(const char *name, char *args, int msize, p9_fd_create(struct p9_client *client, const char *addr, char *args)
unsigned char extended)
{ {
int err; int err;
struct p9_trans *trans;
struct p9_fd_opts opts; struct p9_fd_opts opts;
struct p9_trans_fd *p; struct p9_trans_fd *p = NULL; /* this get allocated in p9_fd_open */
parse_opts(args, &opts); parse_opts(args, &opts);
if (opts.rfd == ~0 || opts.wfd == ~0) { if (opts.rfd == ~0 || opts.wfd == ~0) {
printk(KERN_ERR "v9fs: Insufficient options for proto=fd\n"); printk(KERN_ERR "v9fs: Insufficient options for proto=fd\n");
return ERR_PTR(-ENOPROTOOPT); return -ENOPROTOOPT;
} }
trans = kmalloc(sizeof(struct p9_trans), GFP_KERNEL); err = p9_fd_open(client, opts.rfd, opts.wfd);
if (!trans)
return ERR_PTR(-ENOMEM);
trans->rpc = p9_fd_rpc;
trans->close = p9_fd_close;
err = p9_fd_open(trans, opts.rfd, opts.wfd);
if (err < 0) if (err < 0)
goto error; goto error;
trans->msize = msize; p = (struct p9_trans_fd *) client->trans;
trans->extended = extended; p->conn = p9_conn_create(client);
p = (struct p9_trans_fd *) trans->priv;
p->conn = p9_conn_create(trans);
if (IS_ERR(p->conn)) { if (IS_ERR(p->conn)) {
err = PTR_ERR(p->conn); err = PTR_ERR(p->conn);
p->conn = NULL; p->conn = NULL;
goto error; goto error;
} }
return trans; return 0;
error: error:
kfree(trans); kfree(p);
return ERR_PTR(err); return err;
} }
static struct p9_trans_module p9_tcp_trans = { static struct p9_trans_module p9_tcp_trans = {
.name = "tcp", .name = "tcp",
.maxsize = MAX_SOCK_BUF, .maxsize = MAX_SOCK_BUF,
.def = 1, .def = 1,
.create = p9_trans_create_tcp, .create = p9_fd_create_tcp,
.close = p9_fd_close,
.request = p9_fd_request,
.cancel = p9_fd_cancel,
.owner = THIS_MODULE, .owner = THIS_MODULE,
}; };
...@@ -1588,7 +1033,10 @@ static struct p9_trans_module p9_unix_trans = { ...@@ -1588,7 +1033,10 @@ static struct p9_trans_module p9_unix_trans = {
.name = "unix", .name = "unix",
.maxsize = MAX_SOCK_BUF, .maxsize = MAX_SOCK_BUF,
.def = 0, .def = 0,
.create = p9_trans_create_unix, .create = p9_fd_create_unix,
.close = p9_fd_close,
.request = p9_fd_request,
.cancel = p9_fd_cancel,
.owner = THIS_MODULE, .owner = THIS_MODULE,
}; };
...@@ -1596,23 +1044,71 @@ static struct p9_trans_module p9_fd_trans = { ...@@ -1596,23 +1044,71 @@ static struct p9_trans_module p9_fd_trans = {
.name = "fd", .name = "fd",
.maxsize = MAX_SOCK_BUF, .maxsize = MAX_SOCK_BUF,
.def = 0, .def = 0,
.create = p9_trans_create_fd, .create = p9_fd_create,
.close = p9_fd_close,
.request = p9_fd_request,
.cancel = p9_fd_cancel,
.owner = THIS_MODULE, .owner = THIS_MODULE,
}; };
int p9_trans_fd_init(void) /**
* p9_poll_proc - poll worker thread
* @a: thread state and arguments
*
* polls all v9fs transports for new events and queues the appropriate
* work to the work queue
*
*/
static int p9_poll_proc(void *a)
{ {
int i; unsigned long flags;
for (i = 0; i < ARRAY_SIZE(p9_mux_poll_tasks); i++) P9_DPRINTK(P9_DEBUG_TRANS, "start %p\n", current);
p9_mux_poll_tasks[i].task = NULL; repeat:
spin_lock_irqsave(&p9_poll_lock, flags);
while (!list_empty(&p9_poll_pending_list)) {
struct p9_conn *conn = list_first_entry(&p9_poll_pending_list,
struct p9_conn,
poll_pending_link);
list_del_init(&conn->poll_pending_link);
spin_unlock_irqrestore(&p9_poll_lock, flags);
p9_poll_mux(conn);
spin_lock_irqsave(&p9_poll_lock, flags);
}
spin_unlock_irqrestore(&p9_poll_lock, flags);
set_current_state(TASK_INTERRUPTIBLE);
if (list_empty(&p9_poll_pending_list)) {
P9_DPRINTK(P9_DEBUG_TRANS, "sleeping...\n");
schedule();
}
__set_current_state(TASK_RUNNING);
if (!kthread_should_stop())
goto repeat;
P9_DPRINTK(P9_DEBUG_TRANS, "finish\n");
return 0;
}
int p9_trans_fd_init(void)
{
p9_mux_wq = create_workqueue("v9fs"); p9_mux_wq = create_workqueue("v9fs");
if (!p9_mux_wq) { if (!p9_mux_wq) {
printk(KERN_WARNING "v9fs: mux: creating workqueue failed\n"); printk(KERN_WARNING "v9fs: mux: creating workqueue failed\n");
return -ENOMEM; return -ENOMEM;
} }
p9_poll_task = kthread_run(p9_poll_proc, NULL, "v9fs-poll");
if (IS_ERR(p9_poll_task)) {
destroy_workqueue(p9_mux_wq);
printk(KERN_WARNING "v9fs: mux: creating poll task failed\n");
return PTR_ERR(p9_poll_task);
}
v9fs_register_trans(&p9_tcp_trans); v9fs_register_trans(&p9_tcp_trans);
v9fs_register_trans(&p9_unix_trans); v9fs_register_trans(&p9_unix_trans);
v9fs_register_trans(&p9_fd_trans); v9fs_register_trans(&p9_fd_trans);
...@@ -1622,6 +1118,7 @@ int p9_trans_fd_init(void) ...@@ -1622,6 +1118,7 @@ int p9_trans_fd_init(void)
void p9_trans_fd_exit(void) void p9_trans_fd_exit(void)
{ {
kthread_stop(p9_poll_task);
v9fs_unregister_trans(&p9_tcp_trans); v9fs_unregister_trans(&p9_tcp_trans);
v9fs_unregister_trans(&p9_unix_trans); v9fs_unregister_trans(&p9_unix_trans);
v9fs_unregister_trans(&p9_fd_trans); v9fs_unregister_trans(&p9_fd_trans);
......
/* /*
* The Guest 9p transport driver * The Virtio 9p transport driver
* *
* This is a block based transport driver based on the lguest block driver * This is a block based transport driver based on the lguest block driver
* code. * code.
* *
*/ * Copyright (C) 2007, 2008 Eric Van Hensbergen, IBM Corporation
/*
* Copyright (C) 2007 Eric Van Hensbergen, IBM Corporation
* *
* Based on virtio console driver * Based on virtio console driver
* Copyright (C) 2006, 2007 Rusty Russell, IBM Corporation * Copyright (C) 2006, 2007 Rusty Russell, IBM Corporation
...@@ -41,6 +39,7 @@ ...@@ -41,6 +39,7 @@
#include <linux/file.h> #include <linux/file.h>
#include <net/9p/9p.h> #include <net/9p/9p.h>
#include <linux/parser.h> #include <linux/parser.h>
#include <net/9p/client.h>
#include <net/9p/transport.h> #include <net/9p/transport.h>
#include <linux/scatterlist.h> #include <linux/scatterlist.h>
#include <linux/virtio.h> #include <linux/virtio.h>
...@@ -53,50 +52,6 @@ static DEFINE_MUTEX(virtio_9p_lock); ...@@ -53,50 +52,6 @@ static DEFINE_MUTEX(virtio_9p_lock);
/* global which tracks highest initialized channel */ /* global which tracks highest initialized channel */
static int chan_index; static int chan_index;
#define P9_INIT_MAXTAG 16
/**
* enum p9_req_status_t - virtio request status
* @REQ_STATUS_IDLE: request slot unused
* @REQ_STATUS_SENT: request sent to server
* @REQ_STATUS_RCVD: response received from server
* @REQ_STATUS_FLSH: request has been flushed
*
* The @REQ_STATUS_IDLE state is used to mark a request slot as unused
* but use is actually tracked by the idpool structure which handles tag
* id allocation.
*
*/
enum p9_req_status_t {
REQ_STATUS_IDLE,
REQ_STATUS_SENT,
REQ_STATUS_RCVD,
REQ_STATUS_FLSH,
};
/**
* struct p9_req_t - virtio request slots
* @status: status of this request slot
* @wq: wait_queue for the client to block on for this request
*
* The virtio transport uses an array to track outstanding requests
* instead of a list. While this may incurr overhead during initial
* allocation or expansion, it makes request lookup much easier as the
* tag id is a index into an array. (We use tag+1 so that we can accomodate
* the -1 tag for the T_VERSION request).
* This also has the nice effect of only having to allocate wait_queues
* once, instead of constantly allocating and freeing them. Its possible
* other resources could benefit from this scheme as well.
*
*/
struct p9_req_t {
int status;
wait_queue_head_t *wq;
};
/** /**
* struct virtio_chan - per-instance transport information * struct virtio_chan - per-instance transport information
* @initialized: whether the channel is initialized * @initialized: whether the channel is initialized
...@@ -121,67 +76,14 @@ static struct virtio_chan { ...@@ -121,67 +76,14 @@ static struct virtio_chan {
spinlock_t lock; spinlock_t lock;
struct p9_client *client;
struct virtio_device *vdev; struct virtio_device *vdev;
struct virtqueue *vq; struct virtqueue *vq;
struct p9_idpool *tagpool;
struct p9_req_t *reqs;
int max_tag;
/* Scatterlist: can be too big for stack. */ /* Scatterlist: can be too big for stack. */
struct scatterlist sg[VIRTQUEUE_NUM]; struct scatterlist sg[VIRTQUEUE_NUM];
} channels[MAX_9P_CHAN]; } channels[MAX_9P_CHAN];
/**
* p9_lookup_tag - Lookup requests by tag
* @c: virtio channel to lookup tag within
* @tag: numeric id for transaction
*
* this is a simple array lookup, but will grow the
* request_slots as necessary to accomodate transaction
* ids which did not previously have a slot.
*
* Bugs: there is currently no upper limit on request slots set
* here, but that should be constrained by the id accounting.
*/
static struct p9_req_t *p9_lookup_tag(struct virtio_chan *c, u16 tag)
{
/* This looks up the original request by tag so we know which
* buffer to read the data into */
tag++;
while (tag >= c->max_tag) {
int old_max = c->max_tag;
int count;
if (c->max_tag)
c->max_tag *= 2;
else
c->max_tag = P9_INIT_MAXTAG;
c->reqs = krealloc(c->reqs, sizeof(struct p9_req_t)*c->max_tag,
GFP_ATOMIC);
if (!c->reqs) {
printk(KERN_ERR "Couldn't grow tag array\n");
BUG();
}
for (count = old_max; count < c->max_tag; count++) {
c->reqs[count].status = REQ_STATUS_IDLE;
c->reqs[count].wq = kmalloc(sizeof(wait_queue_head_t),
GFP_ATOMIC);
if (!c->reqs[count].wq) {
printk(KERN_ERR "Couldn't grow tag array\n");
BUG();
}
init_waitqueue_head(c->reqs[count].wq);
}
}
return &c->reqs[tag];
}
/* How many bytes left in this page. */ /* How many bytes left in this page. */
static unsigned int rest_of_page(void *data) static unsigned int rest_of_page(void *data)
{ {
...@@ -197,25 +99,13 @@ static unsigned int rest_of_page(void *data) ...@@ -197,25 +99,13 @@ static unsigned int rest_of_page(void *data)
* *
*/ */
static void p9_virtio_close(struct p9_trans *trans) static void p9_virtio_close(struct p9_client *client)
{ {
struct virtio_chan *chan = trans->priv; struct virtio_chan *chan = client->trans;
int count;
unsigned long flags;
spin_lock_irqsave(&chan->lock, flags);
p9_idpool_destroy(chan->tagpool);
for (count = 0; count < chan->max_tag; count++)
kfree(chan->reqs[count].wq);
kfree(chan->reqs);
chan->max_tag = 0;
spin_unlock_irqrestore(&chan->lock, flags);
mutex_lock(&virtio_9p_lock); mutex_lock(&virtio_9p_lock);
chan->inuse = false; chan->inuse = false;
mutex_unlock(&virtio_9p_lock); mutex_unlock(&virtio_9p_lock);
kfree(trans);
} }
/** /**
...@@ -236,17 +126,16 @@ static void req_done(struct virtqueue *vq) ...@@ -236,17 +126,16 @@ static void req_done(struct virtqueue *vq)
struct virtio_chan *chan = vq->vdev->priv; struct virtio_chan *chan = vq->vdev->priv;
struct p9_fcall *rc; struct p9_fcall *rc;
unsigned int len; unsigned int len;
unsigned long flags;
struct p9_req_t *req; struct p9_req_t *req;
spin_lock_irqsave(&chan->lock, flags); P9_DPRINTK(P9_DEBUG_TRANS, ": request done\n");
while ((rc = chan->vq->vq_ops->get_buf(chan->vq, &len)) != NULL) { while ((rc = chan->vq->vq_ops->get_buf(chan->vq, &len)) != NULL) {
req = p9_lookup_tag(chan, rc->tag); P9_DPRINTK(P9_DEBUG_TRANS, ": rc %p\n", rc);
req->status = REQ_STATUS_RCVD; P9_DPRINTK(P9_DEBUG_TRANS, ": lookup tag %d\n", rc->tag);
wake_up(req->wq); req = p9_tag_lookup(chan->client, rc->tag);
p9_client_cb(chan->client, req);
} }
/* In case queue is stopped waiting for more buffers. */
spin_unlock_irqrestore(&chan->lock, flags);
} }
/** /**
...@@ -283,8 +172,14 @@ pack_sg_list(struct scatterlist *sg, int start, int limit, char *data, ...@@ -283,8 +172,14 @@ pack_sg_list(struct scatterlist *sg, int start, int limit, char *data,
return index-start; return index-start;
} }
/* We don't currently allow canceling of virtio requests */
static int p9_virtio_cancel(struct p9_client *client, struct p9_req_t *req)
{
return 1;
}
/** /**
* p9_virtio_rpc - issue a request and wait for a response * p9_virtio_request - issue a request
* @t: transport state * @t: transport state
* @tc: &p9_fcall request to transmit * @tc: &p9_fcall request to transmit
* @rc: &p9_fcall to put reponse into * @rc: &p9_fcall to put reponse into
...@@ -292,44 +187,22 @@ pack_sg_list(struct scatterlist *sg, int start, int limit, char *data, ...@@ -292,44 +187,22 @@ pack_sg_list(struct scatterlist *sg, int start, int limit, char *data,
*/ */
static int static int
p9_virtio_rpc(struct p9_trans *t, struct p9_fcall *tc, struct p9_fcall **rc) p9_virtio_request(struct p9_client *client, struct p9_req_t *req)
{ {
int in, out; int in, out;
int n, err, size; struct virtio_chan *chan = client->trans;
struct virtio_chan *chan = t->priv; char *rdata = (char *)req->rc+sizeof(struct p9_fcall);
char *rdata;
struct p9_req_t *req;
unsigned long flags;
if (*rc == NULL) {
*rc = kmalloc(sizeof(struct p9_fcall) + t->msize, GFP_KERNEL);
if (!*rc)
return -ENOMEM;
}
rdata = (char *)*rc+sizeof(struct p9_fcall);
n = P9_NOTAG;
if (tc->id != P9_TVERSION) {
n = p9_idpool_get(chan->tagpool);
if (n < 0)
return -ENOMEM;
}
spin_lock_irqsave(&chan->lock, flags); P9_DPRINTK(P9_DEBUG_TRANS, "9p debug: virtio request\n");
req = p9_lookup_tag(chan, n);
spin_unlock_irqrestore(&chan->lock, flags);
p9_set_tag(tc, n); out = pack_sg_list(chan->sg, 0, VIRTQUEUE_NUM, req->tc->sdata,
req->tc->size);
P9_DPRINTK(P9_DEBUG_TRANS, "9p debug: virtio rpc tag %d\n", n); in = pack_sg_list(chan->sg, out, VIRTQUEUE_NUM-out, rdata,
client->msize);
out = pack_sg_list(chan->sg, 0, VIRTQUEUE_NUM, tc->sdata, tc->size);
in = pack_sg_list(chan->sg, out, VIRTQUEUE_NUM-out, rdata, t->msize);
req->status = REQ_STATUS_SENT; req->status = REQ_STATUS_SENT;
if (chan->vq->vq_ops->add_buf(chan->vq, chan->sg, out, in, tc)) { if (chan->vq->vq_ops->add_buf(chan->vq, chan->sg, out, in, req->tc)) {
P9_DPRINTK(P9_DEBUG_TRANS, P9_DPRINTK(P9_DEBUG_TRANS,
"9p debug: virtio rpc add_buf returned failure"); "9p debug: virtio rpc add_buf returned failure");
return -EIO; return -EIO;
...@@ -337,31 +210,7 @@ p9_virtio_rpc(struct p9_trans *t, struct p9_fcall *tc, struct p9_fcall **rc) ...@@ -337,31 +210,7 @@ p9_virtio_rpc(struct p9_trans *t, struct p9_fcall *tc, struct p9_fcall **rc)
chan->vq->vq_ops->kick(chan->vq); chan->vq->vq_ops->kick(chan->vq);
wait_event(*req->wq, req->status == REQ_STATUS_RCVD); P9_DPRINTK(P9_DEBUG_TRANS, "9p debug: virtio request kicked\n");
size = le32_to_cpu(*(__le32 *) rdata);
err = p9_deserialize_fcall(rdata, size, *rc, t->extended);
if (err < 0) {
P9_DPRINTK(P9_DEBUG_TRANS,
"9p debug: virtio rpc deserialize returned %d\n", err);
return err;
}
#ifdef CONFIG_NET_9P_DEBUG
if ((p9_debug_level&P9_DEBUG_FCALL) == P9_DEBUG_FCALL) {
char buf[150];
p9_printfcall(buf, sizeof(buf), *rc, t->extended);
printk(KERN_NOTICE ">>> %p %s\n", t, buf);
}
#endif
if (n != P9_NOTAG && p9_idpool_check(n, chan->tagpool))
p9_idpool_put(n, chan->tagpool);
req->status = REQ_STATUS_IDLE;
return 0; return 0;
} }
...@@ -422,10 +271,9 @@ static int p9_virtio_probe(struct virtio_device *vdev) ...@@ -422,10 +271,9 @@ static int p9_virtio_probe(struct virtio_device *vdev)
/** /**
* p9_virtio_create - allocate a new virtio channel * p9_virtio_create - allocate a new virtio channel
* @client: client instance invoking this transport
* @devname: string identifying the channel to connect to (unused) * @devname: string identifying the channel to connect to (unused)
* @args: args passed from sys_mount() for per-transport options (unused) * @args: args passed from sys_mount() for per-transport options (unused)
* @msize: requested maximum packet size
* @extended: 9p2000.u enabled flag
* *
* This sets up a transport channel for 9p communication. Right now * This sets up a transport channel for 9p communication. Right now
* we only match the first available channel, but eventually we couldlook up * we only match the first available channel, but eventually we couldlook up
...@@ -441,11 +289,9 @@ static int p9_virtio_probe(struct virtio_device *vdev) ...@@ -441,11 +289,9 @@ static int p9_virtio_probe(struct virtio_device *vdev)
* *
*/ */
static struct p9_trans * static int
p9_virtio_create(const char *devname, char *args, int msize, p9_virtio_create(struct p9_client *client, const char *devname, char *args)
unsigned char extended)
{ {
struct p9_trans *trans;
struct virtio_chan *chan = channels; struct virtio_chan *chan = channels;
int index = 0; int index = 0;
...@@ -463,30 +309,13 @@ p9_virtio_create(const char *devname, char *args, int msize, ...@@ -463,30 +309,13 @@ p9_virtio_create(const char *devname, char *args, int msize,
if (index >= MAX_9P_CHAN) { if (index >= MAX_9P_CHAN) {
printk(KERN_ERR "9p: no channels available\n"); printk(KERN_ERR "9p: no channels available\n");
return ERR_PTR(-ENODEV); return -ENODEV;
} }
chan->tagpool = p9_idpool_create(); client->trans = (void *)chan;
if (IS_ERR(chan->tagpool)) { chan->client = client;
printk(KERN_ERR "9p: couldn't allocate tagpool\n");
return ERR_PTR(-ENOMEM);
}
p9_idpool_get(chan->tagpool); /* reserve tag 0 */
chan->max_tag = 0;
chan->reqs = NULL;
trans = kmalloc(sizeof(struct p9_trans), GFP_KERNEL);
if (!trans) {
printk(KERN_ERR "9p: couldn't allocate transport\n");
return ERR_PTR(-ENOMEM);
}
trans->extended = extended;
trans->msize = msize;
trans->close = p9_virtio_close;
trans->rpc = p9_virtio_rpc;
trans->priv = chan;
return trans; return 0;
} }
/** /**
...@@ -526,6 +355,9 @@ static struct virtio_driver p9_virtio_drv = { ...@@ -526,6 +355,9 @@ static struct virtio_driver p9_virtio_drv = {
static struct p9_trans_module p9_virtio_trans = { static struct p9_trans_module p9_virtio_trans = {
.name = "virtio", .name = "virtio",
.create = p9_virtio_create, .create = p9_virtio_create,
.close = p9_virtio_close,
.request = p9_virtio_request,
.cancel = p9_virtio_cancel,
.maxsize = PAGE_SIZE*16, .maxsize = PAGE_SIZE*16,
.def = 0, .def = 0,
.owner = THIS_MODULE, .owner = THIS_MODULE,
......
...@@ -105,6 +105,7 @@ int p9_idpool_get(struct p9_idpool *p) ...@@ -105,6 +105,7 @@ int p9_idpool_get(struct p9_idpool *p)
else if (error) else if (error)
return -1; return -1;
P9_DPRINTK(P9_DEBUG_MUX, " id %d pool %p\n", i, p);
return i; return i;
} }
EXPORT_SYMBOL(p9_idpool_get); EXPORT_SYMBOL(p9_idpool_get);
...@@ -121,6 +122,9 @@ EXPORT_SYMBOL(p9_idpool_get); ...@@ -121,6 +122,9 @@ EXPORT_SYMBOL(p9_idpool_get);
void p9_idpool_put(int id, struct p9_idpool *p) void p9_idpool_put(int id, struct p9_idpool *p)
{ {
unsigned long flags; unsigned long flags;
P9_DPRINTK(P9_DEBUG_MUX, " id %d pool %p\n", id, p);
spin_lock_irqsave(&p->lock, flags); spin_lock_irqsave(&p->lock, flags);
idr_remove(&p->pool, id); idr_remove(&p->pool, id);
spin_unlock_irqrestore(&p->lock, flags); spin_unlock_irqrestore(&p->lock, flags);
......
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