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 ...
This commit is contained in:
commit
45e4a24f7b
|
@ -30,8 +30,8 @@
|
|||
#include <linux/parser.h>
|
||||
#include <linux/idr.h>
|
||||
#include <net/9p/9p.h>
|
||||
#include <net/9p/transport.h>
|
||||
#include <net/9p/client.h>
|
||||
#include <net/9p/transport.h>
|
||||
#include "v9fs.h"
|
||||
#include "v9fs_vfs.h"
|
||||
|
||||
|
@ -234,7 +234,7 @@ struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses,
|
|||
if (!v9ses->clnt->dotu)
|
||||
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 */
|
||||
if (!v9fs_extended(v9ses) &&
|
||||
|
|
|
@ -46,9 +46,11 @@ extern struct dentry_operations v9fs_cached_dentry_operations;
|
|||
|
||||
struct inode *v9fs_get_inode(struct super_block *sb, int mode);
|
||||
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_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 *);
|
||||
int v9fs_uflags2omode(int uflags, int extended);
|
||||
|
||||
ssize_t v9fs_file_readn(struct file *, char *, char __user *, u32, u64);
|
||||
|
|
|
@ -38,7 +38,6 @@
|
|||
|
||||
#include "v9fs.h"
|
||||
#include "v9fs_vfs.h"
|
||||
#include "fid.h"
|
||||
|
||||
/**
|
||||
* 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)
|
|||
int retval;
|
||||
loff_t offset;
|
||||
char *buffer;
|
||||
struct p9_fid *fid;
|
||||
|
||||
P9_DPRINTK(P9_DEBUG_VFS, "\n");
|
||||
fid = filp->private_data;
|
||||
buffer = kmap(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)
|
||||
goto done;
|
||||
|
||||
|
|
|
@ -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;
|
||||
int rettype = DT_REG;
|
||||
|
@ -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)
|
||||
{
|
||||
int over;
|
||||
struct p9_wstat st;
|
||||
int err;
|
||||
struct p9_fid *fid;
|
||||
struct v9fs_session_info *v9ses;
|
||||
struct inode *inode;
|
||||
struct p9_stat *st;
|
||||
int buflen;
|
||||
char *statbuf;
|
||||
int n, i = 0;
|
||||
|
||||
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;
|
||||
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,
|
||||
v9fs_qid2ino(&st->qid), dt_type(st));
|
||||
buflen = fid->clnt->msize - P9_IOHDRSZ;
|
||||
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;
|
||||
|
||||
filp->f_pos += st->size;
|
||||
kfree(st);
|
||||
st = NULL;
|
||||
n = err;
|
||||
while (i < n) {
|
||||
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;
|
||||
}
|
||||
|
||||
i += st.size+2;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
kfree(st);
|
||||
return 0;
|
||||
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)
|
|||
}
|
||||
|
||||
/**
|
||||
* v9fs_file_read - read from a file
|
||||
* v9fs_file_readn - read from a file
|
||||
* @filp: file pointer to read
|
||||
* @data: data buffer to read data into
|
||||
* @udata: user data buffer to read data into
|
||||
* @count: size of buffer
|
||||
* @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
|
||||
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)
|
||||
{
|
||||
int ret;
|
||||
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;
|
||||
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)
|
||||
*offset += ret;
|
||||
|
||||
|
@ -156,19 +205,38 @@ static ssize_t
|
|||
v9fs_file_write(struct file *filp, const char __user * data,
|
||||
size_t count, loff_t * offset)
|
||||
{
|
||||
int ret;
|
||||
int n, rsize, total = 0;
|
||||
struct p9_fid *fid;
|
||||
struct p9_client *clnt;
|
||||
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,
|
||||
(int)count, (int)*offset);
|
||||
|
||||
fid = filp->private_data;
|
||||
ret = p9_client_uwrite(fid, data, *offset, count);
|
||||
if (ret > 0) {
|
||||
invalidate_inode_pages2_range(inode->i_mapping, *offset,
|
||||
*offset+ret);
|
||||
*offset += ret;
|
||||
clnt = fid->clnt;
|
||||
|
||||
rsize = fid->iounit;
|
||||
if (!rsize || rsize > clnt->msize-P9_IOHDRSZ)
|
||||
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) {
|
||||
|
@ -176,7 +244,10 @@ v9fs_file_write(struct file *filp, const char __user * data,
|
|||
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 = {
|
||||
|
|
|
@ -334,7 +334,7 @@ v9fs_inode_from_fid(struct v9fs_session_info *v9ses, struct p9_fid *fid,
|
|||
{
|
||||
int err, umode;
|
||||
struct inode *ret;
|
||||
struct p9_stat *st;
|
||||
struct p9_wstat *st;
|
||||
|
||||
ret = NULL;
|
||||
st = p9_client_stat(fid);
|
||||
|
@ -417,6 +417,8 @@ v9fs_create(struct v9fs_session_info *v9ses, struct inode *dir,
|
|||
struct p9_fid *dfid, *ofid, *fid;
|
||||
struct inode *inode;
|
||||
|
||||
P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", dentry->d_name.name);
|
||||
|
||||
err = 0;
|
||||
ofid = NULL;
|
||||
fid = NULL;
|
||||
|
@ -424,6 +426,7 @@ v9fs_create(struct v9fs_session_info *v9ses, struct inode *dir,
|
|||
dfid = v9fs_fid_clone(dentry->d_parent);
|
||||
if (IS_ERR(dfid)) {
|
||||
err = PTR_ERR(dfid);
|
||||
P9_DPRINTK(P9_DEBUG_VFS, "fid clone failed %d\n", err);
|
||||
dfid = NULL;
|
||||
goto error;
|
||||
}
|
||||
|
@ -432,18 +435,22 @@ v9fs_create(struct v9fs_session_info *v9ses, struct inode *dir,
|
|||
ofid = p9_client_walk(dfid, 0, NULL, 1);
|
||||
if (IS_ERR(ofid)) {
|
||||
err = PTR_ERR(ofid);
|
||||
P9_DPRINTK(P9_DEBUG_VFS, "p9_client_walk failed %d\n", err);
|
||||
ofid = NULL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* now walk from the parent so we can get unopened fid */
|
||||
fid = p9_client_walk(dfid, 1, &name, 0);
|
||||
if (IS_ERR(fid)) {
|
||||
err = PTR_ERR(fid);
|
||||
P9_DPRINTK(P9_DEBUG_VFS, "p9_client_walk failed %d\n", err);
|
||||
fid = NULL;
|
||||
goto error;
|
||||
} else
|
||||
|
@ -453,6 +460,7 @@ v9fs_create(struct v9fs_session_info *v9ses, struct inode *dir,
|
|||
inode = v9fs_inode_from_fid(v9ses, fid, dir->i_sb);
|
||||
if (IS_ERR(inode)) {
|
||||
err = PTR_ERR(inode);
|
||||
P9_DPRINTK(P9_DEBUG_VFS, "inode creation failed %d\n", err);
|
||||
goto error;
|
||||
}
|
||||
|
||||
|
@ -734,7 +742,7 @@ v9fs_vfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
|
|||
int err;
|
||||
struct v9fs_session_info *v9ses;
|
||||
struct p9_fid *fid;
|
||||
struct p9_stat *st;
|
||||
struct p9_wstat *st;
|
||||
|
||||
P9_DPRINTK(P9_DEBUG_VFS, "dentry: %p\n", dentry);
|
||||
err = -EPERM;
|
||||
|
@ -815,10 +823,9 @@ static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr)
|
|||
*/
|
||||
|
||||
void
|
||||
v9fs_stat2inode(struct p9_stat *stat, struct inode *inode,
|
||||
v9fs_stat2inode(struct p9_wstat *stat, struct inode *inode,
|
||||
struct super_block *sb)
|
||||
{
|
||||
int n;
|
||||
char ext[32];
|
||||
struct v9fs_session_info *v9ses = sb->s_fs_info;
|
||||
|
||||
|
@ -842,11 +849,7 @@ v9fs_stat2inode(struct p9_stat *stat, struct inode *inode,
|
|||
int major = -1;
|
||||
int minor = -1;
|
||||
|
||||
n = stat->extension.len;
|
||||
if (n > sizeof(ext)-1)
|
||||
n = sizeof(ext)-1;
|
||||
memmove(ext, stat->extension.str, n);
|
||||
ext[n] = 0;
|
||||
strncpy(ext, stat->extension, sizeof(ext));
|
||||
sscanf(ext, "%c %u %u", &type, &major, &minor);
|
||||
switch (type) {
|
||||
case 'c':
|
||||
|
@ -857,10 +860,11 @@ v9fs_stat2inode(struct p9_stat *stat, struct inode *inode,
|
|||
break;
|
||||
default:
|
||||
P9_DPRINTK(P9_DEBUG_ERROR,
|
||||
"Unknown special type %c (%.*s)\n", type,
|
||||
stat->extension.len, stat->extension.str);
|
||||
"Unknown special type %c %s\n", type,
|
||||
stat->extension);
|
||||
};
|
||||
inode->i_rdev = MKDEV(major, minor);
|
||||
init_special_inode(inode, inode->i_mode, inode->i_rdev);
|
||||
} else
|
||||
inode->i_rdev = 0;
|
||||
|
||||
|
@ -904,7 +908,7 @@ static int v9fs_readlink(struct dentry *dentry, char *buffer, int buflen)
|
|||
|
||||
struct v9fs_session_info *v9ses;
|
||||
struct p9_fid *fid;
|
||||
struct p9_stat *st;
|
||||
struct p9_wstat *st;
|
||||
|
||||
P9_DPRINTK(P9_DEBUG_VFS, " %s\n", dentry->d_name.name);
|
||||
retval = -EPERM;
|
||||
|
@ -926,15 +930,10 @@ static int v9fs_readlink(struct dentry *dentry, char *buffer, int buflen)
|
|||
}
|
||||
|
||||
/* copy extension buffer into buffer */
|
||||
if (st->extension.len < buflen)
|
||||
buflen = st->extension.len + 1;
|
||||
|
||||
memmove(buffer, st->extension.str, buflen - 1);
|
||||
buffer[buflen-1] = 0;
|
||||
strncpy(buffer, st->extension, buflen);
|
||||
|
||||
P9_DPRINTK(P9_DEBUG_VFS,
|
||||
"%s -> %.*s (%s)\n", dentry->d_name.name, st->extension.len,
|
||||
st->extension.str, buffer);
|
||||
"%s -> %s (%s)\n", dentry->d_name.name, st->extension, buffer);
|
||||
|
||||
retval = buflen;
|
||||
|
||||
|
|
|
@ -111,7 +111,7 @@ static int v9fs_get_sb(struct file_system_type *fs_type, int flags,
|
|||
struct inode *inode = NULL;
|
||||
struct dentry *root = NULL;
|
||||
struct v9fs_session_info *v9ses = NULL;
|
||||
struct p9_stat *st = NULL;
|
||||
struct p9_wstat *st = NULL;
|
||||
int mode = S_IRWXUGO | S_ISVTX;
|
||||
uid_t uid = current->fsuid;
|
||||
gid_t gid = current->fsgid;
|
||||
|
@ -161,10 +161,14 @@ static int v9fs_get_sb(struct file_system_type *fs_type, int flags,
|
|||
|
||||
sb->s_root = root;
|
||||
root->d_inode->i_ino = v9fs_qid2ino(&st->qid);
|
||||
|
||||
v9fs_stat2inode(st, root->d_inode, sb);
|
||||
|
||||
v9fs_fid_add(root, fid);
|
||||
p9stat_free(st);
|
||||
kfree(st);
|
||||
|
||||
P9_DPRINTK(P9_DEBUG_VFS, " return simple set mount\n");
|
||||
return simple_set_mnt(mnt, sb);
|
||||
|
||||
release_sb:
|
||||
|
|
|
@ -27,8 +27,6 @@
|
|||
#ifndef NET_9P_H
|
||||
#define NET_9P_H
|
||||
|
||||
#ifdef CONFIG_NET_9P_DEBUG
|
||||
|
||||
/**
|
||||
* enum p9_debug_flags - bits for mount time debug parameter
|
||||
* @P9_DEBUG_ERROR: more verbose error messages including original error string
|
||||
|
@ -39,6 +37,7 @@
|
|||
* @P9_DEBUG_TRANS: transport tracing
|
||||
* @P9_DEBUG_SLABS: memory management tracing
|
||||
* @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
|
||||
* verbosity and tracing which will be output to the system logs.
|
||||
|
@ -53,24 +52,27 @@ enum p9_debug_flags {
|
|||
P9_DEBUG_TRANS = (1<<6),
|
||||
P9_DEBUG_SLABS = (1<<7),
|
||||
P9_DEBUG_FCALL = (1<<8),
|
||||
P9_DEBUG_FID = (1<<9),
|
||||
P9_DEBUG_PKT = (1<<10),
|
||||
};
|
||||
|
||||
extern unsigned int p9_debug_level;
|
||||
|
||||
#ifdef CONFIG_NET_9P_DEBUG
|
||||
#define P9_DPRINTK(level, format, arg...) \
|
||||
do { \
|
||||
if ((p9_debug_level & level) == level) \
|
||||
printk(KERN_NOTICE "-- %s (%d): " \
|
||||
format , __func__, task_pid_nr(current) , ## arg); \
|
||||
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): " \
|
||||
format , __func__, task_pid_nr(current) , ## arg); \
|
||||
} \
|
||||
} 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
|
||||
#define P9_DPRINTK(level, format, arg...) do { } while (0)
|
||||
#define PRINT_FCALL_ERROR(s, fcall) do { } while (0)
|
||||
#endif
|
||||
|
||||
#define P9_EPRINTK(level, format, arg...) \
|
||||
|
@ -325,33 +327,6 @@ struct p9_qid {
|
|||
* 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 {
|
||||
u16 size;
|
||||
u16 type;
|
||||
|
@ -493,12 +468,12 @@ struct p9_tstat {
|
|||
};
|
||||
|
||||
struct p9_rstat {
|
||||
struct p9_stat stat;
|
||||
struct p9_wstat stat;
|
||||
};
|
||||
|
||||
struct p9_twstat {
|
||||
u32 fid;
|
||||
struct p9_stat stat;
|
||||
struct p9_wstat stat;
|
||||
};
|
||||
|
||||
struct p9_rwstat {
|
||||
|
@ -509,8 +484,9 @@ struct p9_rwstat {
|
|||
* @size: prefixed length of the structure
|
||||
* @id: protocol operating identifier of type &p9_msg_t
|
||||
* @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
|
||||
* @params: per-operation parameters
|
||||
*
|
||||
* &p9_fcall represents the structure for all 9P RPC
|
||||
* transactions. Requests are packaged into fcalls, and reponses
|
||||
|
@ -523,68 +499,15 @@ struct p9_fcall {
|
|||
u32 size;
|
||||
u8 id;
|
||||
u16 tag;
|
||||
void *sdata;
|
||||
|
||||
union {
|
||||
struct p9_tversion tversion;
|
||||
struct p9_rversion rversion;
|
||||
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;
|
||||
size_t offset;
|
||||
size_t capacity;
|
||||
|
||||
uint8_t *sdata;
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
struct p9_idpool *p9_idpool_create(void);
|
||||
|
|
|
@ -26,6 +26,87 @@
|
|||
#ifndef 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
|
||||
* @lock: protect @fidlist
|
||||
|
@ -36,9 +117,20 @@
|
|||
* @conn: connection state information used by trans_fd
|
||||
* @fidpool: fid handle accounting for session
|
||||
* @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
|
||||
* 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.
|
||||
*/
|
||||
|
@ -48,11 +140,16 @@ struct p9_client {
|
|||
int msize;
|
||||
unsigned char dotu;
|
||||
struct p9_trans_module *trans_mod;
|
||||
struct p9_trans *trans;
|
||||
enum p9_trans_status status;
|
||||
void *trans;
|
||||
struct p9_conn *conn;
|
||||
|
||||
struct p9_idpool *fidpool;
|
||||
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 {
|
|||
* @uid: the numeric uid of the local user who owns this handle
|
||||
* @aux: transport specific information (unused?)
|
||||
* @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
|
||||
* @dlist: per-dentry fid tracking
|
||||
*
|
||||
|
@ -83,8 +178,6 @@ struct p9_fid {
|
|||
void *aux;
|
||||
|
||||
int rdir_fpos;
|
||||
int rdir_pos;
|
||||
struct p9_fcall *rdir_fcall;
|
||||
struct list_head flist;
|
||||
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,
|
|||
char *extension);
|
||||
int p9_client_clunk(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_readn(struct p9_fid *fid, char *data, u64 offset, u32 count);
|
||||
int p9_client_write(struct p9_fid *fid, char *data, u64 offset, u32 count);
|
||||
int p9_client_uread(struct p9_fid *fid, char __user *data, u64 offset,
|
||||
u32 count);
|
||||
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_read(struct p9_fid *fid, char *data, char __user *udata,
|
||||
u64 offset, u32 count);
|
||||
int p9_client_write(struct p9_fid *fid, char *data, const char __user *udata,
|
||||
u64 offset, u32 count);
|
||||
struct p9_wstat *p9_client_stat(struct p9_fid *fid);
|
||||
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 */
|
||||
|
|
|
@ -26,52 +26,6 @@
|
|||
#ifndef 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
|
||||
* @list: used to maintain a list of currently available transports
|
||||
|
@ -79,12 +33,14 @@ struct p9_trans {
|
|||
* @maxsize: transport provided maximum packet size
|
||||
* @def: set if this transport should be considered the default
|
||||
* @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
|
||||
* transport module with the 9P core network module and used by the client
|
||||
* 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 {
|
||||
|
@ -92,8 +48,11 @@ struct p9_trans_module {
|
|||
char *name; /* name of transport */
|
||||
int maxsize; /* max message size of transport */
|
||||
int def; /* this transport should be default */
|
||||
struct p9_trans * (*create)(const char *, char *, int, unsigned char);
|
||||
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);
|
||||
|
|
|
@ -4,10 +4,9 @@ obj-$(CONFIG_NET_9P_VIRTIO) += 9pnet_virtio.o
|
|||
9pnet-objs := \
|
||||
mod.o \
|
||||
client.o \
|
||||
conv.o \
|
||||
error.o \
|
||||
fcprint.o \
|
||||
util.o \
|
||||
protocol.o \
|
||||
trans_fd.o \
|
||||
|
||||
9pnet_virtio-objs := \
|
||||
|
|
1897
net/9p/client.c
1897
net/9p/client.c
File diff suppressed because it is too large
Load Diff
1054
net/9p/conv.c
1054
net/9p/conv.c
File diff suppressed because it is too large
Load Diff
366
net/9p/fcprint.c
366
net/9p/fcprint.c
|
@ -1,366 +0,0 @@
|
|||
/*
|
||||
* 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 @@
|
|||
#include <net/9p/9p.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/parser.h>
|
||||
#include <net/9p/client.h>
|
||||
#include <net/9p/transport.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
|
|
@ -0,0 +1,558 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* 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);
|
1529
net/9p/trans_fd.c
1529
net/9p/trans_fd.c
File diff suppressed because it is too large
Load Diff
|
@ -1,12 +1,10 @@
|
|||
/*
|
||||
* The Guest 9p transport driver
|
||||
* The Virtio 9p transport driver
|
||||
*
|
||||
* This is a block based transport driver based on the lguest block driver
|
||||
* code.
|
||||
*
|
||||
*/
|
||||
/*
|
||||
* Copyright (C) 2007 Eric Van Hensbergen, IBM Corporation
|
||||
* Copyright (C) 2007, 2008 Eric Van Hensbergen, IBM Corporation
|
||||
*
|
||||
* Based on virtio console driver
|
||||
* Copyright (C) 2006, 2007 Rusty Russell, IBM Corporation
|
||||
|
@ -41,6 +39,7 @@
|
|||
#include <linux/file.h>
|
||||
#include <net/9p/9p.h>
|
||||
#include <linux/parser.h>
|
||||
#include <net/9p/client.h>
|
||||
#include <net/9p/transport.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/virtio.h>
|
||||
|
@ -53,50 +52,6 @@ static DEFINE_MUTEX(virtio_9p_lock);
|
|||
/* global which tracks highest initialized channel */
|
||||
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
|
||||
* @initialized: whether the channel is initialized
|
||||
|
@ -121,67 +76,14 @@ static struct virtio_chan {
|
|||
|
||||
spinlock_t lock;
|
||||
|
||||
struct p9_client *client;
|
||||
struct virtio_device *vdev;
|
||||
struct virtqueue *vq;
|
||||
|
||||
struct p9_idpool *tagpool;
|
||||
struct p9_req_t *reqs;
|
||||
int max_tag;
|
||||
|
||||
/* Scatterlist: can be too big for stack. */
|
||||
struct scatterlist sg[VIRTQUEUE_NUM];
|
||||
} 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. */
|
||||
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;
|
||||
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);
|
||||
struct virtio_chan *chan = client->trans;
|
||||
|
||||
mutex_lock(&virtio_9p_lock);
|
||||
chan->inuse = false;
|
||||
mutex_unlock(&virtio_9p_lock);
|
||||
|
||||
kfree(trans);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -236,17 +126,16 @@ static void req_done(struct virtqueue *vq)
|
|||
struct virtio_chan *chan = vq->vdev->priv;
|
||||
struct p9_fcall *rc;
|
||||
unsigned int len;
|
||||
unsigned long flags;
|
||||
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) {
|
||||
req = p9_lookup_tag(chan, rc->tag);
|
||||
req->status = REQ_STATUS_RCVD;
|
||||
wake_up(req->wq);
|
||||
P9_DPRINTK(P9_DEBUG_TRANS, ": rc %p\n", rc);
|
||||
P9_DPRINTK(P9_DEBUG_TRANS, ": lookup tag %d\n", rc->tag);
|
||||
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,
|
|||
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
|
||||
* @tc: &p9_fcall request to transmit
|
||||
* @rc: &p9_fcall to put reponse into
|
||||
|
@ -292,44 +187,22 @@ pack_sg_list(struct scatterlist *sg, int start, int limit, char *data,
|
|||
*/
|
||||
|
||||
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 n, err, size;
|
||||
struct virtio_chan *chan = t->priv;
|
||||
char *rdata;
|
||||
struct p9_req_t *req;
|
||||
unsigned long flags;
|
||||
struct virtio_chan *chan = client->trans;
|
||||
char *rdata = (char *)req->rc+sizeof(struct p9_fcall);
|
||||
|
||||
if (*rc == NULL) {
|
||||
*rc = kmalloc(sizeof(struct p9_fcall) + t->msize, GFP_KERNEL);
|
||||
if (!*rc)
|
||||
return -ENOMEM;
|
||||
}
|
||||
P9_DPRINTK(P9_DEBUG_TRANS, "9p debug: virtio request\n");
|
||||
|
||||
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);
|
||||
req = p9_lookup_tag(chan, n);
|
||||
spin_unlock_irqrestore(&chan->lock, flags);
|
||||
|
||||
p9_set_tag(tc, n);
|
||||
|
||||
P9_DPRINTK(P9_DEBUG_TRANS, "9p debug: virtio rpc tag %d\n", n);
|
||||
|
||||
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);
|
||||
out = pack_sg_list(chan->sg, 0, VIRTQUEUE_NUM, req->tc->sdata,
|
||||
req->tc->size);
|
||||
in = pack_sg_list(chan->sg, out, VIRTQUEUE_NUM-out, rdata,
|
||||
client->msize);
|
||||
|
||||
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,
|
||||
"9p debug: virtio rpc add_buf returned failure");
|
||||
return -EIO;
|
||||
|
@ -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);
|
||||
|
||||
wait_event(*req->wq, req->status == REQ_STATUS_RCVD);
|
||||
|
||||
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;
|
||||
|
||||
P9_DPRINTK(P9_DEBUG_TRANS, "9p debug: virtio request kicked\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -422,10 +271,9 @@ fail:
|
|||
|
||||
/**
|
||||
* p9_virtio_create - allocate a new virtio channel
|
||||
* @client: client instance invoking this transport
|
||||
* @devname: string identifying the channel to connect to (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
|
||||
* we only match the first available channel, but eventually we couldlook up
|
||||
|
@ -441,11 +289,9 @@ fail:
|
|||
*
|
||||
*/
|
||||
|
||||
static struct p9_trans *
|
||||
p9_virtio_create(const char *devname, char *args, int msize,
|
||||
unsigned char extended)
|
||||
static int
|
||||
p9_virtio_create(struct p9_client *client, const char *devname, char *args)
|
||||
{
|
||||
struct p9_trans *trans;
|
||||
struct virtio_chan *chan = channels;
|
||||
int index = 0;
|
||||
|
||||
|
@ -463,30 +309,13 @@ p9_virtio_create(const char *devname, char *args, int msize,
|
|||
|
||||
if (index >= MAX_9P_CHAN) {
|
||||
printk(KERN_ERR "9p: no channels available\n");
|
||||
return ERR_PTR(-ENODEV);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
chan->tagpool = p9_idpool_create();
|
||||
if (IS_ERR(chan->tagpool)) {
|
||||
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;
|
||||
client->trans = (void *)chan;
|
||||
chan->client = client;
|
||||
|
||||
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 = {
|
|||
static struct p9_trans_module p9_virtio_trans = {
|
||||
.name = "virtio",
|
||||
.create = p9_virtio_create,
|
||||
.close = p9_virtio_close,
|
||||
.request = p9_virtio_request,
|
||||
.cancel = p9_virtio_cancel,
|
||||
.maxsize = PAGE_SIZE*16,
|
||||
.def = 0,
|
||||
.owner = THIS_MODULE,
|
||||
|
|
|
@ -105,6 +105,7 @@ retry:
|
|||
else if (error)
|
||||
return -1;
|
||||
|
||||
P9_DPRINTK(P9_DEBUG_MUX, " id %d pool %p\n", i, p);
|
||||
return i;
|
||||
}
|
||||
EXPORT_SYMBOL(p9_idpool_get);
|
||||
|
@ -121,6 +122,9 @@ EXPORT_SYMBOL(p9_idpool_get);
|
|||
void p9_idpool_put(int id, struct p9_idpool *p)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
P9_DPRINTK(P9_DEBUG_MUX, " id %d pool %p\n", id, p);
|
||||
|
||||
spin_lock_irqsave(&p->lock, flags);
|
||||
idr_remove(&p->pool, id);
|
||||
spin_unlock_irqrestore(&p->lock, flags);
|
||||
|
|
Loading…
Reference in New Issue