aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/9p/v9fs.c4
-rw-r--r--fs/9p/v9fs_vfs.h6
-rw-r--r--fs/9p/vfs_addr.c5
-rw-r--r--fs/9p/vfs_dir.c60
-rw-r--r--fs/9p/vfs_file.c93
-rw-r--r--fs/9p/vfs_inode.c39
-rw-r--r--fs/9p/vfs_super.c6
-rw-r--r--include/net/9p/9p.h119
-rw-r--r--include/net/9p/client.h124
-rw-r--r--include/net/9p/transport.h55
-rw-r--r--net/9p/Makefile3
-rw-r--r--net/9p/client.c1467
-rw-r--r--net/9p/conv.c1054
-rw-r--r--net/9p/fcprint.c366
-rw-r--r--net/9p/mod.c1
-rw-r--r--net/9p/protocol.c558
-rw-r--r--net/9p/protocol.h34
-rw-r--r--net/9p/trans_fd.c1431
-rw-r--r--net/9p/trans_virtio.c246
-rw-r--r--net/9p/util.c4
20 files changed, 2224 insertions, 3451 deletions
diff --git a/fs/9p/v9fs.c b/fs/9p/v9fs.c
index c061c3f18e7..24eb01087b6 100644
--- a/fs/9p/v9fs.c
+++ b/fs/9p/v9fs.c
@@ -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) &&
diff --git a/fs/9p/v9fs_vfs.h b/fs/9p/v9fs_vfs.h
index 57997fa14e6..c295ba786ed 100644
--- a/fs/9p/v9fs_vfs.h
+++ b/fs/9p/v9fs_vfs.h
@@ -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);
diff --git a/fs/9p/vfs_addr.c b/fs/9p/vfs_addr.c
index 97d3aed5798..6fcb1e7095c 100644
--- a/fs/9p/vfs_addr.c
+++ b/fs/9p/vfs_addr.c
@@ -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;
diff --git a/fs/9p/vfs_dir.c b/fs/9p/vfs_dir.c
index e298fe19409..873cd31baa4 100644
--- a/fs/9p/vfs_dir.c
+++ b/fs/9p/vfs_dir.c
@@ -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;
}
diff --git a/fs/9p/vfs_file.c b/fs/9p/vfs_file.c
index 52944d2249a..041c5269228 100644
--- a/fs/9p/vfs_file.c
+++ b/fs/9p/vfs_file.c
@@ -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 = {
diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c
index e83aa5ebe86..8314d3f43b7 100644
--- a/fs/9p/vfs_inode.c
+++ b/fs/9p/vfs_inode.c
@@ -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;
diff --git a/fs/9p/vfs_super.c b/fs/9p/vfs_super.c
index bf59c396049..d6cb1a0ca72 100644
--- a/fs/9p/vfs_super.c
+++ b/fs/9p/vfs_super.c
@@ -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:
diff --git a/include/net/9p/9p.h b/include/net/9p/9p.h
index fb163e2e0de..d2c60c73619 100644
--- a/include/net/9p/9p.h
+++ b/include/net/9p/9p.h
@@ -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);
diff --git a/include/net/9p/client.h b/include/net/9p/client.h
index c936dd14de4..1f17f3d9372 100644
--- a/include/net/9p/client.h
+++ b/include/net/9p/client.h
@@ -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 */
diff --git a/include/net/9p/transport.h b/include/net/9p/transport.h
index 3ca737120a9..6d5886efb10 100644
--- a/include/net/9p/transport.h
+++ b/include/net/9p/transport.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);
diff --git a/net/9p/Makefile b/net/9p/Makefile
index 519219480db..1041b7bd12e 100644
--- a/net/9p/Makefile
+++ b/net/9p/Makefile
@@ -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 := \
diff --git a/net/9p/client.c b/net/9p/client.c
index e053e06028a..bbac2f72b4d 100644
--- a/net/9p/client.c
+++ b/net/9p/client.c
@@ -33,12 +33,9 @@
#include <linux/uaccess.h>
#include <net/9p/9p.h>
#include <linux/parser.h>
-#include <net/9p/transport.h>
#include <net/9p/client.h>
-
-static struct p9_fid *p9_fid_create(struct p9_client *clnt);
-static void p9_fid_destroy(struct p9_fid *fid);
-static struct p9_stat *p9_clone_stat(struct p9_stat *st, int dotu);
+#include <net/9p/transport.h>
+#include "protocol.h"
/*
* Client Option Parsing (code inspired by NFS code)
@@ -59,6 +56,9 @@ static const match_table_t tokens = {
{Opt_err, NULL},
};
+static struct p9_req_t *
+p9_client_rpc(struct p9_client *c, int8_t type, const char *fmt, ...);
+
/**
* v9fs_parse_options - parse mount options into session structure
* @options: options string passed from mount
@@ -124,31 +124,585 @@ static int parse_opts(char *opts, struct p9_client *clnt)
return ret;
}
+/**
+ * p9_tag_alloc - lookup/allocate a request by tag
+ * @c: client session to lookup tag within
+ * @tag: numeric id for transaction
+ *
+ * this is a simple array lookup, but will grow the
+ * request_slots as necessary to accomodate transaction
+ * ids which did not previously have a slot.
+ *
+ * this code relies on the client spinlock to manage locks, its
+ * possible we should switch to something else, but I'd rather
+ * stick with something low-overhead for the common case.
+ *
+ */
+
+static struct p9_req_t *p9_tag_alloc(struct p9_clie