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:
Linus Torvalds 2008-10-20 09:39:47 -07:00
commit 45e4a24f7b
20 changed files with 2487 additions and 3714 deletions

View File

@ -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) &&

View File

@ -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);

View File

@ -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;

View File

@ -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;
}

View File

@ -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 = {

View File

@ -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;

View File

@ -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:

View File

@ -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);

View File

@ -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 */

View File

@ -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);

View File

@ -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 := \

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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);

View File

@ -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>

558
net/9p/protocol.c Normal file
View File

@ -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;
}

34
net/9p/protocol.h Normal file
View File

@ -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);

File diff suppressed because it is too large Load Diff

View File

@ -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,

View File

@ -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);