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 ...hifive-unleashed-5.1
commit
45e4a24f7b
|
@ -30,8 +30,8 @@
|
||||||
#include <linux/parser.h>
|
#include <linux/parser.h>
|
||||||
#include <linux/idr.h>
|
#include <linux/idr.h>
|
||||||
#include <net/9p/9p.h>
|
#include <net/9p/9p.h>
|
||||||
#include <net/9p/transport.h>
|
|
||||||
#include <net/9p/client.h>
|
#include <net/9p/client.h>
|
||||||
|
#include <net/9p/transport.h>
|
||||||
#include "v9fs.h"
|
#include "v9fs.h"
|
||||||
#include "v9fs_vfs.h"
|
#include "v9fs_vfs.h"
|
||||||
|
|
||||||
|
@ -234,7 +234,7 @@ struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses,
|
||||||
if (!v9ses->clnt->dotu)
|
if (!v9ses->clnt->dotu)
|
||||||
v9ses->flags &= ~V9FS_EXTENDED;
|
v9ses->flags &= ~V9FS_EXTENDED;
|
||||||
|
|
||||||
v9ses->maxdata = v9ses->clnt->msize;
|
v9ses->maxdata = v9ses->clnt->msize - P9_IOHDRSZ;
|
||||||
|
|
||||||
/* for legacy mode, fall back to V9FS_ACCESS_ANY */
|
/* for legacy mode, fall back to V9FS_ACCESS_ANY */
|
||||||
if (!v9fs_extended(v9ses) &&
|
if (!v9fs_extended(v9ses) &&
|
||||||
|
|
|
@ -46,9 +46,11 @@ extern struct dentry_operations v9fs_cached_dentry_operations;
|
||||||
|
|
||||||
struct inode *v9fs_get_inode(struct super_block *sb, int mode);
|
struct inode *v9fs_get_inode(struct super_block *sb, int mode);
|
||||||
ino_t v9fs_qid2ino(struct p9_qid *qid);
|
ino_t v9fs_qid2ino(struct p9_qid *qid);
|
||||||
void v9fs_stat2inode(struct p9_stat *, struct inode *, struct super_block *);
|
void v9fs_stat2inode(struct p9_wstat *, struct inode *, struct super_block *);
|
||||||
int v9fs_dir_release(struct inode *inode, struct file *filp);
|
int v9fs_dir_release(struct inode *inode, struct file *filp);
|
||||||
int v9fs_file_open(struct inode *inode, struct file *file);
|
int v9fs_file_open(struct inode *inode, struct file *file);
|
||||||
void v9fs_inode2stat(struct inode *inode, struct p9_stat *stat);
|
void v9fs_inode2stat(struct inode *inode, struct p9_wstat *stat);
|
||||||
void v9fs_dentry_release(struct dentry *);
|
void v9fs_dentry_release(struct dentry *);
|
||||||
int v9fs_uflags2omode(int uflags, int extended);
|
int v9fs_uflags2omode(int uflags, int extended);
|
||||||
|
|
||||||
|
ssize_t v9fs_file_readn(struct file *, char *, char __user *, u32, u64);
|
||||||
|
|
|
@ -38,7 +38,6 @@
|
||||||
|
|
||||||
#include "v9fs.h"
|
#include "v9fs.h"
|
||||||
#include "v9fs_vfs.h"
|
#include "v9fs_vfs.h"
|
||||||
#include "fid.h"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* v9fs_vfs_readpage - read an entire page in from 9P
|
* v9fs_vfs_readpage - read an entire page in from 9P
|
||||||
|
@ -53,14 +52,12 @@ static int v9fs_vfs_readpage(struct file *filp, struct page *page)
|
||||||
int retval;
|
int retval;
|
||||||
loff_t offset;
|
loff_t offset;
|
||||||
char *buffer;
|
char *buffer;
|
||||||
struct p9_fid *fid;
|
|
||||||
|
|
||||||
P9_DPRINTK(P9_DEBUG_VFS, "\n");
|
P9_DPRINTK(P9_DEBUG_VFS, "\n");
|
||||||
fid = filp->private_data;
|
|
||||||
buffer = kmap(page);
|
buffer = kmap(page);
|
||||||
offset = page_offset(page);
|
offset = page_offset(page);
|
||||||
|
|
||||||
retval = p9_client_readn(fid, buffer, offset, PAGE_CACHE_SIZE);
|
retval = v9fs_file_readn(filp, buffer, NULL, offset, PAGE_CACHE_SIZE);
|
||||||
if (retval < 0)
|
if (retval < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static inline int dt_type(struct p9_stat *mistat)
|
static inline int dt_type(struct p9_wstat *mistat)
|
||||||
{
|
{
|
||||||
unsigned long perm = mistat->mode;
|
unsigned long perm = mistat->mode;
|
||||||
int rettype = DT_REG;
|
int rettype = DT_REG;
|
||||||
|
@ -69,32 +69,58 @@ static inline int dt_type(struct p9_stat *mistat)
|
||||||
static int v9fs_dir_readdir(struct file *filp, void *dirent, filldir_t filldir)
|
static int v9fs_dir_readdir(struct file *filp, void *dirent, filldir_t filldir)
|
||||||
{
|
{
|
||||||
int over;
|
int over;
|
||||||
|
struct p9_wstat st;
|
||||||
|
int err;
|
||||||
struct p9_fid *fid;
|
struct p9_fid *fid;
|
||||||
struct v9fs_session_info *v9ses;
|
int buflen;
|
||||||
struct inode *inode;
|
char *statbuf;
|
||||||
struct p9_stat *st;
|
int n, i = 0;
|
||||||
|
|
||||||
P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", filp->f_path.dentry->d_name.name);
|
P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", filp->f_path.dentry->d_name.name);
|
||||||
inode = filp->f_path.dentry->d_inode;
|
|
||||||
v9ses = v9fs_inode2v9ses(inode);
|
|
||||||
fid = filp->private_data;
|
fid = filp->private_data;
|
||||||
while ((st = p9_client_dirread(fid, filp->f_pos)) != NULL) {
|
|
||||||
if (IS_ERR(st))
|
|
||||||
return PTR_ERR(st);
|
|
||||||
|
|
||||||
over = filldir(dirent, st->name.str, st->name.len, filp->f_pos,
|
buflen = fid->clnt->msize - P9_IOHDRSZ;
|
||||||
v9fs_qid2ino(&st->qid), dt_type(st));
|
statbuf = kmalloc(buflen, GFP_KERNEL);
|
||||||
|
if (!statbuf)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
if (over)
|
while (1) {
|
||||||
|
err = v9fs_file_readn(filp, statbuf, NULL, buflen,
|
||||||
|
fid->rdir_fpos);
|
||||||
|
if (err <= 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
filp->f_pos += st->size;
|
n = err;
|
||||||
kfree(st);
|
while (i < n) {
|
||||||
st = NULL;
|
err = p9stat_read(statbuf + i, buflen-i, &st,
|
||||||
|
fid->clnt->dotu);
|
||||||
|
if (err) {
|
||||||
|
P9_DPRINTK(P9_DEBUG_VFS, "returned %d\n", err);
|
||||||
|
err = -EIO;
|
||||||
|
p9stat_free(&st);
|
||||||
|
goto free_and_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
free_and_exit:
|
||||||
return 0;
|
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
|
* @filp: file pointer to read
|
||||||
* @data: data buffer to read data into
|
* @data: data buffer to read data into
|
||||||
|
* @udata: user data buffer to read data into
|
||||||
* @count: size of buffer
|
* @count: size of buffer
|
||||||
* @offset: offset at which to read data
|
* @offset: offset at which to read data
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
ssize_t
|
||||||
|
v9fs_file_readn(struct file *filp, char *data, char __user *udata, u32 count,
|
||||||
|
u64 offset)
|
||||||
|
{
|
||||||
|
int n, total;
|
||||||
|
struct p9_fid *fid = filp->private_data;
|
||||||
|
|
||||||
|
P9_DPRINTK(P9_DEBUG_VFS, "fid %d offset %llu count %d\n", fid->fid,
|
||||||
|
(long long unsigned) offset, count);
|
||||||
|
|
||||||
|
n = 0;
|
||||||
|
total = 0;
|
||||||
|
do {
|
||||||
|
n = p9_client_read(fid, data, udata, offset, count);
|
||||||
|
if (n <= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (data)
|
||||||
|
data += n;
|
||||||
|
if (udata)
|
||||||
|
udata += n;
|
||||||
|
|
||||||
|
offset += n;
|
||||||
|
count -= n;
|
||||||
|
total += n;
|
||||||
|
} while (count > 0 && n == (fid->clnt->msize - P9_IOHDRSZ));
|
||||||
|
|
||||||
|
if (n < 0)
|
||||||
|
total = n;
|
||||||
|
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* v9fs_file_read - read from a file
|
||||||
|
* @filp: file pointer to read
|
||||||
|
* @udata: user data buffer to read data into
|
||||||
|
* @count: size of buffer
|
||||||
|
* @offset: offset at which to read data
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
static ssize_t
|
static ssize_t
|
||||||
v9fs_file_read(struct file *filp, char __user * data, size_t count,
|
v9fs_file_read(struct file *filp, char __user *udata, size_t count,
|
||||||
loff_t * offset)
|
loff_t * offset)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
struct p9_fid *fid;
|
struct p9_fid *fid;
|
||||||
|
|
||||||
P9_DPRINTK(P9_DEBUG_VFS, "\n");
|
P9_DPRINTK(P9_DEBUG_VFS, "count %d offset %lld\n", count, *offset);
|
||||||
fid = filp->private_data;
|
fid = filp->private_data;
|
||||||
ret = p9_client_uread(fid, data, *offset, count);
|
|
||||||
|
if (count > (fid->clnt->msize - P9_IOHDRSZ))
|
||||||
|
ret = v9fs_file_readn(filp, NULL, udata, count, *offset);
|
||||||
|
else
|
||||||
|
ret = p9_client_read(fid, NULL, udata, *offset, count);
|
||||||
|
|
||||||
if (ret > 0)
|
if (ret > 0)
|
||||||
*offset += ret;
|
*offset += ret;
|
||||||
|
|
||||||
|
@ -156,19 +205,38 @@ static ssize_t
|
||||||
v9fs_file_write(struct file *filp, const char __user * data,
|
v9fs_file_write(struct file *filp, const char __user * data,
|
||||||
size_t count, loff_t * offset)
|
size_t count, loff_t * offset)
|
||||||
{
|
{
|
||||||
int ret;
|
int n, rsize, total = 0;
|
||||||
struct p9_fid *fid;
|
struct p9_fid *fid;
|
||||||
|
struct p9_client *clnt;
|
||||||
struct inode *inode = filp->f_path.dentry->d_inode;
|
struct inode *inode = filp->f_path.dentry->d_inode;
|
||||||
|
int origin = *offset;
|
||||||
|
|
||||||
P9_DPRINTK(P9_DEBUG_VFS, "data %p count %d offset %x\n", data,
|
P9_DPRINTK(P9_DEBUG_VFS, "data %p count %d offset %x\n", data,
|
||||||
(int)count, (int)*offset);
|
(int)count, (int)*offset);
|
||||||
|
|
||||||
fid = filp->private_data;
|
fid = filp->private_data;
|
||||||
ret = p9_client_uwrite(fid, data, *offset, count);
|
clnt = fid->clnt;
|
||||||
if (ret > 0) {
|
|
||||||
invalidate_inode_pages2_range(inode->i_mapping, *offset,
|
rsize = fid->iounit;
|
||||||
*offset+ret);
|
if (!rsize || rsize > clnt->msize-P9_IOHDRSZ)
|
||||||
*offset += ret;
|
rsize = clnt->msize - P9_IOHDRSZ;
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (count < rsize)
|
||||||
|
rsize = count;
|
||||||
|
|
||||||
|
n = p9_client_write(fid, NULL, data+total, *offset+total,
|
||||||
|
rsize);
|
||||||
|
if (n <= 0)
|
||||||
|
break;
|
||||||
|
count -= n;
|
||||||
|
total += n;
|
||||||
|
} while (count > 0);
|
||||||
|
|
||||||
|
if (total > 0) {
|
||||||
|
invalidate_inode_pages2_range(inode->i_mapping, origin,
|
||||||
|
origin+total);
|
||||||
|
*offset += total;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*offset > inode->i_size) {
|
if (*offset > inode->i_size) {
|
||||||
|
@ -176,7 +244,10 @@ v9fs_file_write(struct file *filp, const char __user * data,
|
||||||
inode->i_blocks = (inode->i_size + 512 - 1) >> 9;
|
inode->i_blocks = (inode->i_size + 512 - 1) >> 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
if (n < 0)
|
||||||
|
return n;
|
||||||
|
|
||||||
|
return total;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct file_operations v9fs_cached_file_operations = {
|
static const struct file_operations v9fs_cached_file_operations = {
|
||||||
|
|
|
@ -334,7 +334,7 @@ v9fs_inode_from_fid(struct v9fs_session_info *v9ses, struct p9_fid *fid,
|
||||||
{
|
{
|
||||||
int err, umode;
|
int err, umode;
|
||||||
struct inode *ret;
|
struct inode *ret;
|
||||||
struct p9_stat *st;
|
struct p9_wstat *st;
|
||||||
|
|
||||||
ret = NULL;
|
ret = NULL;
|
||||||
st = p9_client_stat(fid);
|
st = p9_client_stat(fid);
|
||||||
|
@ -417,6 +417,8 @@ v9fs_create(struct v9fs_session_info *v9ses, struct inode *dir,
|
||||||
struct p9_fid *dfid, *ofid, *fid;
|
struct p9_fid *dfid, *ofid, *fid;
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
|
|
||||||
|
P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", dentry->d_name.name);
|
||||||
|
|
||||||
err = 0;
|
err = 0;
|
||||||
ofid = NULL;
|
ofid = NULL;
|
||||||
fid = NULL;
|
fid = NULL;
|
||||||
|
@ -424,6 +426,7 @@ v9fs_create(struct v9fs_session_info *v9ses, struct inode *dir,
|
||||||
dfid = v9fs_fid_clone(dentry->d_parent);
|
dfid = v9fs_fid_clone(dentry->d_parent);
|
||||||
if (IS_ERR(dfid)) {
|
if (IS_ERR(dfid)) {
|
||||||
err = PTR_ERR(dfid);
|
err = PTR_ERR(dfid);
|
||||||
|
P9_DPRINTK(P9_DEBUG_VFS, "fid clone failed %d\n", err);
|
||||||
dfid = NULL;
|
dfid = NULL;
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
@ -432,18 +435,22 @@ v9fs_create(struct v9fs_session_info *v9ses, struct inode *dir,
|
||||||
ofid = p9_client_walk(dfid, 0, NULL, 1);
|
ofid = p9_client_walk(dfid, 0, NULL, 1);
|
||||||
if (IS_ERR(ofid)) {
|
if (IS_ERR(ofid)) {
|
||||||
err = PTR_ERR(ofid);
|
err = PTR_ERR(ofid);
|
||||||
|
P9_DPRINTK(P9_DEBUG_VFS, "p9_client_walk failed %d\n", err);
|
||||||
ofid = NULL;
|
ofid = NULL;
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = p9_client_fcreate(ofid, name, perm, mode, extension);
|
err = p9_client_fcreate(ofid, name, perm, mode, extension);
|
||||||
if (err < 0)
|
if (err < 0) {
|
||||||
|
P9_DPRINTK(P9_DEBUG_VFS, "p9_client_fcreate failed %d\n", err);
|
||||||
goto error;
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
/* now walk from the parent so we can get unopened fid */
|
/* now walk from the parent so we can get unopened fid */
|
||||||
fid = p9_client_walk(dfid, 1, &name, 0);
|
fid = p9_client_walk(dfid, 1, &name, 0);
|
||||||
if (IS_ERR(fid)) {
|
if (IS_ERR(fid)) {
|
||||||
err = PTR_ERR(fid);
|
err = PTR_ERR(fid);
|
||||||
|
P9_DPRINTK(P9_DEBUG_VFS, "p9_client_walk failed %d\n", err);
|
||||||
fid = NULL;
|
fid = NULL;
|
||||||
goto error;
|
goto error;
|
||||||
} else
|
} else
|
||||||
|
@ -453,6 +460,7 @@ v9fs_create(struct v9fs_session_info *v9ses, struct inode *dir,
|
||||||
inode = v9fs_inode_from_fid(v9ses, fid, dir->i_sb);
|
inode = v9fs_inode_from_fid(v9ses, fid, dir->i_sb);
|
||||||
if (IS_ERR(inode)) {
|
if (IS_ERR(inode)) {
|
||||||
err = PTR_ERR(inode);
|
err = PTR_ERR(inode);
|
||||||
|
P9_DPRINTK(P9_DEBUG_VFS, "inode creation failed %d\n", err);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -734,7 +742,7 @@ v9fs_vfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
|
||||||
int err;
|
int err;
|
||||||
struct v9fs_session_info *v9ses;
|
struct v9fs_session_info *v9ses;
|
||||||
struct p9_fid *fid;
|
struct p9_fid *fid;
|
||||||
struct p9_stat *st;
|
struct p9_wstat *st;
|
||||||
|
|
||||||
P9_DPRINTK(P9_DEBUG_VFS, "dentry: %p\n", dentry);
|
P9_DPRINTK(P9_DEBUG_VFS, "dentry: %p\n", dentry);
|
||||||
err = -EPERM;
|
err = -EPERM;
|
||||||
|
@ -815,10 +823,9 @@ static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void
|
void
|
||||||
v9fs_stat2inode(struct p9_stat *stat, struct inode *inode,
|
v9fs_stat2inode(struct p9_wstat *stat, struct inode *inode,
|
||||||
struct super_block *sb)
|
struct super_block *sb)
|
||||||
{
|
{
|
||||||
int n;
|
|
||||||
char ext[32];
|
char ext[32];
|
||||||
struct v9fs_session_info *v9ses = sb->s_fs_info;
|
struct v9fs_session_info *v9ses = sb->s_fs_info;
|
||||||
|
|
||||||
|
@ -842,11 +849,7 @@ v9fs_stat2inode(struct p9_stat *stat, struct inode *inode,
|
||||||
int major = -1;
|
int major = -1;
|
||||||
int minor = -1;
|
int minor = -1;
|
||||||
|
|
||||||
n = stat->extension.len;
|
strncpy(ext, stat->extension, sizeof(ext));
|
||||||
if (n > sizeof(ext)-1)
|
|
||||||
n = sizeof(ext)-1;
|
|
||||||
memmove(ext, stat->extension.str, n);
|
|
||||||
ext[n] = 0;
|
|
||||||
sscanf(ext, "%c %u %u", &type, &major, &minor);
|
sscanf(ext, "%c %u %u", &type, &major, &minor);
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'c':
|
case 'c':
|
||||||
|
@ -857,10 +860,11 @@ v9fs_stat2inode(struct p9_stat *stat, struct inode *inode,
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
P9_DPRINTK(P9_DEBUG_ERROR,
|
P9_DPRINTK(P9_DEBUG_ERROR,
|
||||||
"Unknown special type %c (%.*s)\n", type,
|
"Unknown special type %c %s\n", type,
|
||||||
stat->extension.len, stat->extension.str);
|
stat->extension);
|
||||||
};
|
};
|
||||||
inode->i_rdev = MKDEV(major, minor);
|
inode->i_rdev = MKDEV(major, minor);
|
||||||
|
init_special_inode(inode, inode->i_mode, inode->i_rdev);
|
||||||
} else
|
} else
|
||||||
inode->i_rdev = 0;
|
inode->i_rdev = 0;
|
||||||
|
|
||||||
|
@ -904,7 +908,7 @@ static int v9fs_readlink(struct dentry *dentry, char *buffer, int buflen)
|
||||||
|
|
||||||
struct v9fs_session_info *v9ses;
|
struct v9fs_session_info *v9ses;
|
||||||
struct p9_fid *fid;
|
struct p9_fid *fid;
|
||||||
struct p9_stat *st;
|
struct p9_wstat *st;
|
||||||
|
|
||||||
P9_DPRINTK(P9_DEBUG_VFS, " %s\n", dentry->d_name.name);
|
P9_DPRINTK(P9_DEBUG_VFS, " %s\n", dentry->d_name.name);
|
||||||
retval = -EPERM;
|
retval = -EPERM;
|
||||||
|
@ -926,15 +930,10 @@ static int v9fs_readlink(struct dentry *dentry, char *buffer, int buflen)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* copy extension buffer into buffer */
|
/* copy extension buffer into buffer */
|
||||||
if (st->extension.len < buflen)
|
strncpy(buffer, st->extension, buflen);
|
||||||
buflen = st->extension.len + 1;
|
|
||||||
|
|
||||||
memmove(buffer, st->extension.str, buflen - 1);
|
|
||||||
buffer[buflen-1] = 0;
|
|
||||||
|
|
||||||
P9_DPRINTK(P9_DEBUG_VFS,
|
P9_DPRINTK(P9_DEBUG_VFS,
|
||||||
"%s -> %.*s (%s)\n", dentry->d_name.name, st->extension.len,
|
"%s -> %s (%s)\n", dentry->d_name.name, st->extension, buffer);
|
||||||
st->extension.str, buffer);
|
|
||||||
|
|
||||||
retval = buflen;
|
retval = buflen;
|
||||||
|
|
||||||
|
|
|
@ -111,7 +111,7 @@ static int v9fs_get_sb(struct file_system_type *fs_type, int flags,
|
||||||
struct inode *inode = NULL;
|
struct inode *inode = NULL;
|
||||||
struct dentry *root = NULL;
|
struct dentry *root = NULL;
|
||||||
struct v9fs_session_info *v9ses = NULL;
|
struct v9fs_session_info *v9ses = NULL;
|
||||||
struct p9_stat *st = NULL;
|
struct p9_wstat *st = NULL;
|
||||||
int mode = S_IRWXUGO | S_ISVTX;
|
int mode = S_IRWXUGO | S_ISVTX;
|
||||||
uid_t uid = current->fsuid;
|
uid_t uid = current->fsuid;
|
||||||
gid_t gid = current->fsgid;
|
gid_t gid = current->fsgid;
|
||||||
|
@ -161,10 +161,14 @@ static int v9fs_get_sb(struct file_system_type *fs_type, int flags,
|
||||||
|
|
||||||
sb->s_root = root;
|
sb->s_root = root;
|
||||||
root->d_inode->i_ino = v9fs_qid2ino(&st->qid);
|
root->d_inode->i_ino = v9fs_qid2ino(&st->qid);
|
||||||
|
|
||||||
v9fs_stat2inode(st, root->d_inode, sb);
|
v9fs_stat2inode(st, root->d_inode, sb);
|
||||||
|
|
||||||
v9fs_fid_add(root, fid);
|
v9fs_fid_add(root, fid);
|
||||||
|
p9stat_free(st);
|
||||||
kfree(st);
|
kfree(st);
|
||||||
|
|
||||||
|
P9_DPRINTK(P9_DEBUG_VFS, " return simple set mount\n");
|
||||||
return simple_set_mnt(mnt, sb);
|
return simple_set_mnt(mnt, sb);
|
||||||
|
|
||||||
release_sb:
|
release_sb:
|
||||||
|
|
|
@ -27,8 +27,6 @@
|
||||||
#ifndef NET_9P_H
|
#ifndef NET_9P_H
|
||||||
#define NET_9P_H
|
#define NET_9P_H
|
||||||
|
|
||||||
#ifdef CONFIG_NET_9P_DEBUG
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* enum p9_debug_flags - bits for mount time debug parameter
|
* enum p9_debug_flags - bits for mount time debug parameter
|
||||||
* @P9_DEBUG_ERROR: more verbose error messages including original error string
|
* @P9_DEBUG_ERROR: more verbose error messages including original error string
|
||||||
|
@ -39,6 +37,7 @@
|
||||||
* @P9_DEBUG_TRANS: transport tracing
|
* @P9_DEBUG_TRANS: transport tracing
|
||||||
* @P9_DEBUG_SLABS: memory management tracing
|
* @P9_DEBUG_SLABS: memory management tracing
|
||||||
* @P9_DEBUG_FCALL: verbose dump of protocol messages
|
* @P9_DEBUG_FCALL: verbose dump of protocol messages
|
||||||
|
* @P9_DEBUG_FID: fid allocation/deallocation tracking
|
||||||
*
|
*
|
||||||
* These flags are passed at mount time to turn on various levels of
|
* These flags are passed at mount time to turn on various levels of
|
||||||
* verbosity and tracing which will be output to the system logs.
|
* verbosity and tracing which will be output to the system logs.
|
||||||
|
@ -53,24 +52,27 @@ enum p9_debug_flags {
|
||||||
P9_DEBUG_TRANS = (1<<6),
|
P9_DEBUG_TRANS = (1<<6),
|
||||||
P9_DEBUG_SLABS = (1<<7),
|
P9_DEBUG_SLABS = (1<<7),
|
||||||
P9_DEBUG_FCALL = (1<<8),
|
P9_DEBUG_FCALL = (1<<8),
|
||||||
|
P9_DEBUG_FID = (1<<9),
|
||||||
|
P9_DEBUG_PKT = (1<<10),
|
||||||
};
|
};
|
||||||
|
|
||||||
extern unsigned int p9_debug_level;
|
extern unsigned int p9_debug_level;
|
||||||
|
|
||||||
|
#ifdef CONFIG_NET_9P_DEBUG
|
||||||
#define P9_DPRINTK(level, format, arg...) \
|
#define P9_DPRINTK(level, format, arg...) \
|
||||||
do { \
|
do { \
|
||||||
if ((p9_debug_level & level) == level) \
|
if ((p9_debug_level & level) == level) {\
|
||||||
printk(KERN_NOTICE "-- %s (%d): " \
|
if (level == P9_DEBUG_9P) \
|
||||||
format , __func__, task_pid_nr(current) , ## arg); \
|
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)
|
} while (0)
|
||||||
|
|
||||||
#define PRINT_FCALL_ERROR(s, fcall) P9_DPRINTK(P9_DEBUG_ERROR, \
|
|
||||||
"%s: %.*s\n", s, fcall?fcall->params.rerror.error.len:0, \
|
|
||||||
fcall?fcall->params.rerror.error.str:"");
|
|
||||||
|
|
||||||
#else
|
#else
|
||||||
#define P9_DPRINTK(level, format, arg...) do { } while (0)
|
#define P9_DPRINTK(level, format, arg...) do { } while (0)
|
||||||
#define PRINT_FCALL_ERROR(s, fcall) do { } while (0)
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define P9_EPRINTK(level, format, arg...) \
|
#define P9_EPRINTK(level, format, arg...) \
|
||||||
|
@ -325,33 +327,6 @@ struct p9_qid {
|
||||||
* See Also: http://plan9.bell-labs.com/magic/man2html/2/stat
|
* See Also: http://plan9.bell-labs.com/magic/man2html/2/stat
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct p9_stat {
|
|
||||||
u16 size;
|
|
||||||
u16 type;
|
|
||||||
u32 dev;
|
|
||||||
struct p9_qid qid;
|
|
||||||
u32 mode;
|
|
||||||
u32 atime;
|
|
||||||
u32 mtime;
|
|
||||||
u64 length;
|
|
||||||
struct p9_str name;
|
|
||||||
struct p9_str uid;
|
|
||||||
struct p9_str gid;
|
|
||||||
struct p9_str muid;
|
|
||||||
struct p9_str extension; /* 9p2000.u extensions */
|
|
||||||
u32 n_uid; /* 9p2000.u extensions */
|
|
||||||
u32 n_gid; /* 9p2000.u extensions */
|
|
||||||
u32 n_muid; /* 9p2000.u extensions */
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* file metadata (stat) structure used to create Twstat message
|
|
||||||
* The is identical to &p9_stat, but the strings don't point to
|
|
||||||
* the same memory block and should be freed separately
|
|
||||||
*
|
|
||||||
* See Also: http://plan9.bell-labs.com/magic/man2html/2/stat
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct p9_wstat {
|
struct p9_wstat {
|
||||||
u16 size;
|
u16 size;
|
||||||
u16 type;
|
u16 type;
|
||||||
|
@ -493,12 +468,12 @@ struct p9_tstat {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct p9_rstat {
|
struct p9_rstat {
|
||||||
struct p9_stat stat;
|
struct p9_wstat stat;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct p9_twstat {
|
struct p9_twstat {
|
||||||
u32 fid;
|
u32 fid;
|
||||||
struct p9_stat stat;
|
struct p9_wstat stat;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct p9_rwstat {
|
struct p9_rwstat {
|
||||||
|
@ -509,8 +484,9 @@ struct p9_rwstat {
|
||||||
* @size: prefixed length of the structure
|
* @size: prefixed length of the structure
|
||||||
* @id: protocol operating identifier of type &p9_msg_t
|
* @id: protocol operating identifier of type &p9_msg_t
|
||||||
* @tag: transaction id of the request
|
* @tag: transaction id of the request
|
||||||
|
* @offset: used by marshalling routines to track currentposition in buffer
|
||||||
|
* @capacity: used by marshalling routines to track total capacity
|
||||||
* @sdata: payload
|
* @sdata: payload
|
||||||
* @params: per-operation parameters
|
|
||||||
*
|
*
|
||||||
* &p9_fcall represents the structure for all 9P RPC
|
* &p9_fcall represents the structure for all 9P RPC
|
||||||
* transactions. Requests are packaged into fcalls, and reponses
|
* transactions. Requests are packaged into fcalls, and reponses
|
||||||
|
@ -523,68 +499,15 @@ struct p9_fcall {
|
||||||
u32 size;
|
u32 size;
|
||||||
u8 id;
|
u8 id;
|
||||||
u16 tag;
|
u16 tag;
|
||||||
void *sdata;
|
|
||||||
|
|
||||||
union {
|
size_t offset;
|
||||||
struct p9_tversion tversion;
|
size_t capacity;
|
||||||
struct p9_rversion rversion;
|
|
||||||
struct p9_tauth tauth;
|
uint8_t *sdata;
|
||||||
struct p9_rauth rauth;
|
|
||||||
struct p9_rerror rerror;
|
|
||||||
struct p9_tflush tflush;
|
|
||||||
struct p9_rflush rflush;
|
|
||||||
struct p9_tattach tattach;
|
|
||||||
struct p9_rattach rattach;
|
|
||||||
struct p9_twalk twalk;
|
|
||||||
struct p9_rwalk rwalk;
|
|
||||||
struct p9_topen topen;
|
|
||||||
struct p9_ropen ropen;
|
|
||||||
struct p9_tcreate tcreate;
|
|
||||||
struct p9_rcreate rcreate;
|
|
||||||
struct p9_tread tread;
|
|
||||||
struct p9_rread rread;
|
|
||||||
struct p9_twrite twrite;
|
|
||||||
struct p9_rwrite rwrite;
|
|
||||||
struct p9_tclunk tclunk;
|
|
||||||
struct p9_rclunk rclunk;
|
|
||||||
struct p9_tremove tremove;
|
|
||||||
struct p9_rremove rremove;
|
|
||||||
struct p9_tstat tstat;
|
|
||||||
struct p9_rstat rstat;
|
|
||||||
struct p9_twstat twstat;
|
|
||||||
struct p9_rwstat rwstat;
|
|
||||||
} params;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct p9_idpool;
|
struct p9_idpool;
|
||||||
|
|
||||||
int p9_deserialize_stat(void *buf, u32 buflen, struct p9_stat *stat,
|
|
||||||
int dotu);
|
|
||||||
int p9_deserialize_fcall(void *buf, u32 buflen, struct p9_fcall *fc, int dotu);
|
|
||||||
void p9_set_tag(struct p9_fcall *fc, u16 tag);
|
|
||||||
struct p9_fcall *p9_create_tversion(u32 msize, char *version);
|
|
||||||
struct p9_fcall *p9_create_tattach(u32 fid, u32 afid, char *uname,
|
|
||||||
char *aname, u32 n_uname, int dotu);
|
|
||||||
struct p9_fcall *p9_create_tauth(u32 afid, char *uname, char *aname,
|
|
||||||
u32 n_uname, int dotu);
|
|
||||||
struct p9_fcall *p9_create_tflush(u16 oldtag);
|
|
||||||
struct p9_fcall *p9_create_twalk(u32 fid, u32 newfid, u16 nwname,
|
|
||||||
char **wnames);
|
|
||||||
struct p9_fcall *p9_create_topen(u32 fid, u8 mode);
|
|
||||||
struct p9_fcall *p9_create_tcreate(u32 fid, char *name, u32 perm, u8 mode,
|
|
||||||
char *extension, int dotu);
|
|
||||||
struct p9_fcall *p9_create_tread(u32 fid, u64 offset, u32 count);
|
|
||||||
struct p9_fcall *p9_create_twrite(u32 fid, u64 offset, u32 count,
|
|
||||||
const char *data);
|
|
||||||
struct p9_fcall *p9_create_twrite_u(u32 fid, u64 offset, u32 count,
|
|
||||||
const char __user *data);
|
|
||||||
struct p9_fcall *p9_create_tclunk(u32 fid);
|
|
||||||
struct p9_fcall *p9_create_tremove(u32 fid);
|
|
||||||
struct p9_fcall *p9_create_tstat(u32 fid);
|
|
||||||
struct p9_fcall *p9_create_twstat(u32 fid, struct p9_wstat *wstat,
|
|
||||||
int dotu);
|
|
||||||
|
|
||||||
int p9_printfcall(char *buf, int buflen, struct p9_fcall *fc, int dotu);
|
|
||||||
int p9_errstr2errno(char *errstr, int len);
|
int p9_errstr2errno(char *errstr, int len);
|
||||||
|
|
||||||
struct p9_idpool *p9_idpool_create(void);
|
struct p9_idpool *p9_idpool_create(void);
|
||||||
|
|
|
@ -26,6 +26,87 @@
|
||||||
#ifndef NET_9P_CLIENT_H
|
#ifndef NET_9P_CLIENT_H
|
||||||
#define NET_9P_CLIENT_H
|
#define NET_9P_CLIENT_H
|
||||||
|
|
||||||
|
/* Number of requests per row */
|
||||||
|
#define P9_ROW_MAXTAG 255
|
||||||
|
|
||||||
|
/**
|
||||||
|
* enum p9_trans_status - different states of underlying transports
|
||||||
|
* @Connected: transport is connected and healthy
|
||||||
|
* @Disconnected: transport has been disconnected
|
||||||
|
* @Hung: transport is connected by wedged
|
||||||
|
*
|
||||||
|
* This enumeration details the various states a transport
|
||||||
|
* instatiation can be in.
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum p9_trans_status {
|
||||||
|
Connected,
|
||||||
|
Disconnected,
|
||||||
|
Hung,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* enum p9_req_status_t - virtio request status
|
||||||
|
* @REQ_STATUS_IDLE: request slot unused
|
||||||
|
* @REQ_STATUS_ALLOC: request has been allocated but not sent
|
||||||
|
* @REQ_STATUS_UNSENT: request waiting to be sent
|
||||||
|
* @REQ_STATUS_SENT: request sent to server
|
||||||
|
* @REQ_STATUS_FLSH: a flush has been sent for this request
|
||||||
|
* @REQ_STATUS_RCVD: response received from server
|
||||||
|
* @REQ_STATUS_FLSHD: request has been flushed
|
||||||
|
* @REQ_STATUS_ERROR: request encountered an error on the client side
|
||||||
|
*
|
||||||
|
* The @REQ_STATUS_IDLE state is used to mark a request slot as unused
|
||||||
|
* but use is actually tracked by the idpool structure which handles tag
|
||||||
|
* id allocation.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum p9_req_status_t {
|
||||||
|
REQ_STATUS_IDLE,
|
||||||
|
REQ_STATUS_ALLOC,
|
||||||
|
REQ_STATUS_UNSENT,
|
||||||
|
REQ_STATUS_SENT,
|
||||||
|
REQ_STATUS_FLSH,
|
||||||
|
REQ_STATUS_RCVD,
|
||||||
|
REQ_STATUS_FLSHD,
|
||||||
|
REQ_STATUS_ERROR,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct p9_req_t - request slots
|
||||||
|
* @status: status of this request slot
|
||||||
|
* @t_err: transport error
|
||||||
|
* @flush_tag: tag of request being flushed (for flush requests)
|
||||||
|
* @wq: wait_queue for the client to block on for this request
|
||||||
|
* @tc: the request fcall structure
|
||||||
|
* @rc: the response fcall structure
|
||||||
|
* @aux: transport specific data (provided for trans_fd migration)
|
||||||
|
* @req_list: link for higher level objects to chain requests
|
||||||
|
*
|
||||||
|
* Transport use an array to track outstanding requests
|
||||||
|
* instead of a list. While this may incurr overhead during initial
|
||||||
|
* allocation or expansion, it makes request lookup much easier as the
|
||||||
|
* tag id is a index into an array. (We use tag+1 so that we can accomodate
|
||||||
|
* the -1 tag for the T_VERSION request).
|
||||||
|
* This also has the nice effect of only having to allocate wait_queues
|
||||||
|
* once, instead of constantly allocating and freeing them. Its possible
|
||||||
|
* other resources could benefit from this scheme as well.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct p9_req_t {
|
||||||
|
int status;
|
||||||
|
int t_err;
|
||||||
|
u16 flush_tag;
|
||||||
|
wait_queue_head_t *wq;
|
||||||
|
struct p9_fcall *tc;
|
||||||
|
struct p9_fcall *rc;
|
||||||
|
void *aux;
|
||||||
|
|
||||||
|
struct list_head req_list;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct p9_client - per client instance state
|
* struct p9_client - per client instance state
|
||||||
* @lock: protect @fidlist
|
* @lock: protect @fidlist
|
||||||
|
@ -36,9 +117,20 @@
|
||||||
* @conn: connection state information used by trans_fd
|
* @conn: connection state information used by trans_fd
|
||||||
* @fidpool: fid handle accounting for session
|
* @fidpool: fid handle accounting for session
|
||||||
* @fidlist: List of active fid handles
|
* @fidlist: List of active fid handles
|
||||||
|
* @tagpool - transaction id accounting for session
|
||||||
|
* @reqs - 2D array of requests
|
||||||
|
* @max_tag - current maximum tag id allocated
|
||||||
*
|
*
|
||||||
* The client structure is used to keep track of various per-client
|
* The client structure is used to keep track of various per-client
|
||||||
* state that has been instantiated.
|
* state that has been instantiated.
|
||||||
|
* In order to minimize per-transaction overhead we use a
|
||||||
|
* simple array to lookup requests instead of a hash table
|
||||||
|
* or linked list. In order to support larger number of
|
||||||
|
* transactions, we make this a 2D array, allocating new rows
|
||||||
|
* when we need to grow the total number of the transactions.
|
||||||
|
*
|
||||||
|
* Each row is 256 requests and we'll support up to 256 rows for
|
||||||
|
* a total of 64k concurrent requests per session.
|
||||||
*
|
*
|
||||||
* Bugs: duplicated data and potentially unnecessary elements.
|
* Bugs: duplicated data and potentially unnecessary elements.
|
||||||
*/
|
*/
|
||||||
|
@ -48,11 +140,16 @@ struct p9_client {
|
||||||
int msize;
|
int msize;
|
||||||
unsigned char dotu;
|
unsigned char dotu;
|
||||||
struct p9_trans_module *trans_mod;
|
struct p9_trans_module *trans_mod;
|
||||||
struct p9_trans *trans;
|
enum p9_trans_status status;
|
||||||
|
void *trans;
|
||||||
struct p9_conn *conn;
|
struct p9_conn *conn;
|
||||||
|
|
||||||
struct p9_idpool *fidpool;
|
struct p9_idpool *fidpool;
|
||||||
struct list_head fidlist;
|
struct list_head fidlist;
|
||||||
|
|
||||||
|
struct p9_idpool *tagpool;
|
||||||
|
struct p9_req_t *reqs[P9_ROW_MAXTAG];
|
||||||
|
int max_tag;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -65,8 +162,6 @@ struct p9_client {
|
||||||
* @uid: the numeric uid of the local user who owns this handle
|
* @uid: the numeric uid of the local user who owns this handle
|
||||||
* @aux: transport specific information (unused?)
|
* @aux: transport specific information (unused?)
|
||||||
* @rdir_fpos: tracks offset of file position when reading directory contents
|
* @rdir_fpos: tracks offset of file position when reading directory contents
|
||||||
* @rdir_pos: (unused?)
|
|
||||||
* @rdir_fcall: holds response of last directory read request
|
|
||||||
* @flist: per-client-instance fid tracking
|
* @flist: per-client-instance fid tracking
|
||||||
* @dlist: per-dentry fid tracking
|
* @dlist: per-dentry fid tracking
|
||||||
*
|
*
|
||||||
|
@ -83,8 +178,6 @@ struct p9_fid {
|
||||||
void *aux;
|
void *aux;
|
||||||
|
|
||||||
int rdir_fpos;
|
int rdir_fpos;
|
||||||
int rdir_pos;
|
|
||||||
struct p9_fcall *rdir_fcall;
|
|
||||||
struct list_head flist;
|
struct list_head flist;
|
||||||
struct list_head dlist; /* list of all fids attached to a dentry */
|
struct list_head dlist; /* list of all fids attached to a dentry */
|
||||||
};
|
};
|
||||||
|
@ -103,15 +196,18 @@ int p9_client_fcreate(struct p9_fid *fid, char *name, u32 perm, int mode,
|
||||||
char *extension);
|
char *extension);
|
||||||
int p9_client_clunk(struct p9_fid *fid);
|
int p9_client_clunk(struct p9_fid *fid);
|
||||||
int p9_client_remove(struct p9_fid *fid);
|
int p9_client_remove(struct p9_fid *fid);
|
||||||
int p9_client_read(struct p9_fid *fid, char *data, u64 offset, u32 count);
|
int p9_client_read(struct p9_fid *fid, char *data, char __user *udata,
|
||||||
int p9_client_readn(struct p9_fid *fid, char *data, u64 offset, u32 count);
|
u64 offset, u32 count);
|
||||||
int p9_client_write(struct p9_fid *fid, char *data, u64 offset, u32 count);
|
int p9_client_write(struct p9_fid *fid, char *data, const char __user *udata,
|
||||||
int p9_client_uread(struct p9_fid *fid, char __user *data, u64 offset,
|
u64 offset, u32 count);
|
||||||
u32 count);
|
struct p9_wstat *p9_client_stat(struct p9_fid *fid);
|
||||||
int p9_client_uwrite(struct p9_fid *fid, const char __user *data, u64 offset,
|
|
||||||
u32 count);
|
|
||||||
struct p9_stat *p9_client_stat(struct p9_fid *fid);
|
|
||||||
int p9_client_wstat(struct p9_fid *fid, struct p9_wstat *wst);
|
int p9_client_wstat(struct p9_fid *fid, struct p9_wstat *wst);
|
||||||
struct p9_stat *p9_client_dirread(struct p9_fid *fid, u64 offset);
|
|
||||||
|
struct p9_req_t *p9_tag_lookup(struct p9_client *, u16);
|
||||||
|
void p9_client_cb(struct p9_client *c, struct p9_req_t *req);
|
||||||
|
|
||||||
|
int p9stat_read(char *, int, struct p9_wstat *, int);
|
||||||
|
void p9stat_free(struct p9_wstat *);
|
||||||
|
|
||||||
|
|
||||||
#endif /* NET_9P_CLIENT_H */
|
#endif /* NET_9P_CLIENT_H */
|
||||||
|
|
|
@ -26,52 +26,6 @@
|
||||||
#ifndef NET_9P_TRANSPORT_H
|
#ifndef NET_9P_TRANSPORT_H
|
||||||
#define NET_9P_TRANSPORT_H
|
#define NET_9P_TRANSPORT_H
|
||||||
|
|
||||||
#include <linux/module.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* enum p9_trans_status - different states of underlying transports
|
|
||||||
* @Connected: transport is connected and healthy
|
|
||||||
* @Disconnected: transport has been disconnected
|
|
||||||
* @Hung: transport is connected by wedged
|
|
||||||
*
|
|
||||||
* This enumeration details the various states a transport
|
|
||||||
* instatiation can be in.
|
|
||||||
*/
|
|
||||||
|
|
||||||
enum p9_trans_status {
|
|
||||||
Connected,
|
|
||||||
Disconnected,
|
|
||||||
Hung,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* struct p9_trans - per-transport state and API
|
|
||||||
* @status: transport &p9_trans_status
|
|
||||||
* @msize: negotiated maximum packet size (duplicate from client)
|
|
||||||
* @extended: negotiated protocol extensions (duplicate from client)
|
|
||||||
* @priv: transport private data
|
|
||||||
* @close: member function to disconnect and close the transport
|
|
||||||
* @rpc: member function to issue a request to the transport
|
|
||||||
*
|
|
||||||
* This is the basic API for a transport instance. It is used as
|
|
||||||
* a handle by the client to issue requests. This interface is currently
|
|
||||||
* in flux during reorganization.
|
|
||||||
*
|
|
||||||
* Bugs: there is lots of duplicated data here and its not clear that
|
|
||||||
* the member functions need to be per-instance versus per transport
|
|
||||||
* module.
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct p9_trans {
|
|
||||||
enum p9_trans_status status;
|
|
||||||
int msize;
|
|
||||||
unsigned char extended;
|
|
||||||
void *priv;
|
|
||||||
void (*close) (struct p9_trans *);
|
|
||||||
int (*rpc) (struct p9_trans *t, struct p9_fcall *tc,
|
|
||||||
struct p9_fcall **rc);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct p9_trans_module - transport module interface
|
* struct p9_trans_module - transport module interface
|
||||||
* @list: used to maintain a list of currently available transports
|
* @list: used to maintain a list of currently available transports
|
||||||
|
@ -79,12 +33,14 @@ struct p9_trans {
|
||||||
* @maxsize: transport provided maximum packet size
|
* @maxsize: transport provided maximum packet size
|
||||||
* @def: set if this transport should be considered the default
|
* @def: set if this transport should be considered the default
|
||||||
* @create: member function to create a new connection on this transport
|
* @create: member function to create a new connection on this transport
|
||||||
|
* @request: member function to issue a request to the transport
|
||||||
|
* @cancel: member function to cancel a request (if it hasn't been sent)
|
||||||
*
|
*
|
||||||
* This is the basic API for a transport module which is registered by the
|
* This is the basic API for a transport module which is registered by the
|
||||||
* transport module with the 9P core network module and used by the client
|
* transport module with the 9P core network module and used by the client
|
||||||
* to instantiate a new connection on a transport.
|
* to instantiate a new connection on a transport.
|
||||||
*
|
*
|
||||||
* Bugs: the transport module list isn't protected.
|
* BUGS: the transport module list isn't protected.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct p9_trans_module {
|
struct p9_trans_module {
|
||||||
|
@ -92,8 +48,11 @@ struct p9_trans_module {
|
||||||
char *name; /* name of transport */
|
char *name; /* name of transport */
|
||||||
int maxsize; /* max message size of transport */
|
int maxsize; /* max message size of transport */
|
||||||
int def; /* this transport should be default */
|
int def; /* this transport should be default */
|
||||||
struct p9_trans * (*create)(const char *, char *, int, unsigned char);
|
|
||||||
struct module *owner;
|
struct module *owner;
|
||||||
|
int (*create)(struct p9_client *, const char *, char *);
|
||||||
|
void (*close) (struct p9_client *);
|
||||||
|
int (*request) (struct p9_client *, struct p9_req_t *req);
|
||||||
|
int (*cancel) (struct p9_client *, struct p9_req_t *req);
|
||||||
};
|
};
|
||||||
|
|
||||||
void v9fs_register_trans(struct p9_trans_module *m);
|
void v9fs_register_trans(struct p9_trans_module *m);
|
||||||
|
|
|
@ -4,10 +4,9 @@ obj-$(CONFIG_NET_9P_VIRTIO) += 9pnet_virtio.o
|
||||||
9pnet-objs := \
|
9pnet-objs := \
|
||||||
mod.o \
|
mod.o \
|
||||||
client.o \
|
client.o \
|
||||||
conv.o \
|
|
||||||
error.o \
|
error.o \
|
||||||
fcprint.o \
|
|
||||||
util.o \
|
util.o \
|
||||||
|
protocol.o \
|
||||||
trans_fd.o \
|
trans_fd.o \
|
||||||
|
|
||||||
9pnet_virtio-objs := \
|
9pnet_virtio-objs := \
|
||||||
|
|
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 <net/9p/9p.h>
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
#include <linux/parser.h>
|
#include <linux/parser.h>
|
||||||
|
#include <net/9p/client.h>
|
||||||
#include <net/9p/transport.h>
|
#include <net/9p/transport.h>
|
||||||
#include <linux/list.h>
|
#include <linux/list.h>
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
|
|
|
@ -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
|
* This is a block based transport driver based on the lguest block driver
|
||||||
* code.
|
* code.
|
||||||
*
|
*
|
||||||
*/
|
* Copyright (C) 2007, 2008 Eric Van Hensbergen, IBM Corporation
|
||||||
/*
|
|
||||||
* Copyright (C) 2007 Eric Van Hensbergen, IBM Corporation
|
|
||||||
*
|
*
|
||||||
* Based on virtio console driver
|
* Based on virtio console driver
|
||||||
* Copyright (C) 2006, 2007 Rusty Russell, IBM Corporation
|
* Copyright (C) 2006, 2007 Rusty Russell, IBM Corporation
|
||||||
|
@ -41,6 +39,7 @@
|
||||||
#include <linux/file.h>
|
#include <linux/file.h>
|
||||||
#include <net/9p/9p.h>
|
#include <net/9p/9p.h>
|
||||||
#include <linux/parser.h>
|
#include <linux/parser.h>
|
||||||
|
#include <net/9p/client.h>
|
||||||
#include <net/9p/transport.h>
|
#include <net/9p/transport.h>
|
||||||
#include <linux/scatterlist.h>
|
#include <linux/scatterlist.h>
|
||||||
#include <linux/virtio.h>
|
#include <linux/virtio.h>
|
||||||
|
@ -53,50 +52,6 @@ static DEFINE_MUTEX(virtio_9p_lock);
|
||||||
/* global which tracks highest initialized channel */
|
/* global which tracks highest initialized channel */
|
||||||
static int chan_index;
|
static int chan_index;
|
||||||
|
|
||||||
#define P9_INIT_MAXTAG 16
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* enum p9_req_status_t - virtio request status
|
|
||||||
* @REQ_STATUS_IDLE: request slot unused
|
|
||||||
* @REQ_STATUS_SENT: request sent to server
|
|
||||||
* @REQ_STATUS_RCVD: response received from server
|
|
||||||
* @REQ_STATUS_FLSH: request has been flushed
|
|
||||||
*
|
|
||||||
* The @REQ_STATUS_IDLE state is used to mark a request slot as unused
|
|
||||||
* but use is actually tracked by the idpool structure which handles tag
|
|
||||||
* id allocation.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
enum p9_req_status_t {
|
|
||||||
REQ_STATUS_IDLE,
|
|
||||||
REQ_STATUS_SENT,
|
|
||||||
REQ_STATUS_RCVD,
|
|
||||||
REQ_STATUS_FLSH,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* struct p9_req_t - virtio request slots
|
|
||||||
* @status: status of this request slot
|
|
||||||
* @wq: wait_queue for the client to block on for this request
|
|
||||||
*
|
|
||||||
* The virtio transport uses an array to track outstanding requests
|
|
||||||
* instead of a list. While this may incurr overhead during initial
|
|
||||||
* allocation or expansion, it makes request lookup much easier as the
|
|
||||||
* tag id is a index into an array. (We use tag+1 so that we can accomodate
|
|
||||||
* the -1 tag for the T_VERSION request).
|
|
||||||
* This also has the nice effect of only having to allocate wait_queues
|
|
||||||
* once, instead of constantly allocating and freeing them. Its possible
|
|
||||||
* other resources could benefit from this scheme as well.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct p9_req_t {
|
|
||||||
int status;
|
|
||||||
wait_queue_head_t *wq;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct virtio_chan - per-instance transport information
|
* struct virtio_chan - per-instance transport information
|
||||||
* @initialized: whether the channel is initialized
|
* @initialized: whether the channel is initialized
|
||||||
|
@ -121,67 +76,14 @@ static struct virtio_chan {
|
||||||
|
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
|
|
||||||
|
struct p9_client *client;
|
||||||
struct virtio_device *vdev;
|
struct virtio_device *vdev;
|
||||||
struct virtqueue *vq;
|
struct virtqueue *vq;
|
||||||
|
|
||||||
struct p9_idpool *tagpool;
|
|
||||||
struct p9_req_t *reqs;
|
|
||||||
int max_tag;
|
|
||||||
|
|
||||||
/* Scatterlist: can be too big for stack. */
|
/* Scatterlist: can be too big for stack. */
|
||||||
struct scatterlist sg[VIRTQUEUE_NUM];
|
struct scatterlist sg[VIRTQUEUE_NUM];
|
||||||
} channels[MAX_9P_CHAN];
|
} channels[MAX_9P_CHAN];
|
||||||
|
|
||||||
/**
|
|
||||||
* p9_lookup_tag - Lookup requests by tag
|
|
||||||
* @c: virtio channel to lookup tag within
|
|
||||||
* @tag: numeric id for transaction
|
|
||||||
*
|
|
||||||
* this is a simple array lookup, but will grow the
|
|
||||||
* request_slots as necessary to accomodate transaction
|
|
||||||
* ids which did not previously have a slot.
|
|
||||||
*
|
|
||||||
* Bugs: there is currently no upper limit on request slots set
|
|
||||||
* here, but that should be constrained by the id accounting.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static struct p9_req_t *p9_lookup_tag(struct virtio_chan *c, u16 tag)
|
|
||||||
{
|
|
||||||
/* This looks up the original request by tag so we know which
|
|
||||||
* buffer to read the data into */
|
|
||||||
tag++;
|
|
||||||
|
|
||||||
while (tag >= c->max_tag) {
|
|
||||||
int old_max = c->max_tag;
|
|
||||||
int count;
|
|
||||||
|
|
||||||
if (c->max_tag)
|
|
||||||
c->max_tag *= 2;
|
|
||||||
else
|
|
||||||
c->max_tag = P9_INIT_MAXTAG;
|
|
||||||
|
|
||||||
c->reqs = krealloc(c->reqs, sizeof(struct p9_req_t)*c->max_tag,
|
|
||||||
GFP_ATOMIC);
|
|
||||||
if (!c->reqs) {
|
|
||||||
printk(KERN_ERR "Couldn't grow tag array\n");
|
|
||||||
BUG();
|
|
||||||
}
|
|
||||||
for (count = old_max; count < c->max_tag; count++) {
|
|
||||||
c->reqs[count].status = REQ_STATUS_IDLE;
|
|
||||||
c->reqs[count].wq = kmalloc(sizeof(wait_queue_head_t),
|
|
||||||
GFP_ATOMIC);
|
|
||||||
if (!c->reqs[count].wq) {
|
|
||||||
printk(KERN_ERR "Couldn't grow tag array\n");
|
|
||||||
BUG();
|
|
||||||
}
|
|
||||||
init_waitqueue_head(c->reqs[count].wq);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &c->reqs[tag];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* How many bytes left in this page. */
|
/* How many bytes left in this page. */
|
||||||
static unsigned int rest_of_page(void *data)
|
static unsigned int rest_of_page(void *data)
|
||||||
{
|
{
|
||||||
|
@ -197,25 +99,13 @@ static unsigned int rest_of_page(void *data)
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void p9_virtio_close(struct p9_trans *trans)
|
static void p9_virtio_close(struct p9_client *client)
|
||||||
{
|
{
|
||||||
struct virtio_chan *chan = trans->priv;
|
struct virtio_chan *chan = client->trans;
|
||||||
int count;
|
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
spin_lock_irqsave(&chan->lock, flags);
|
|
||||||
p9_idpool_destroy(chan->tagpool);
|
|
||||||
for (count = 0; count < chan->max_tag; count++)
|
|
||||||
kfree(chan->reqs[count].wq);
|
|
||||||
kfree(chan->reqs);
|
|
||||||
chan->max_tag = 0;
|
|
||||||
spin_unlock_irqrestore(&chan->lock, flags);
|
|
||||||
|
|
||||||
mutex_lock(&virtio_9p_lock);
|
mutex_lock(&virtio_9p_lock);
|
||||||
chan->inuse = false;
|
chan->inuse = false;
|
||||||
mutex_unlock(&virtio_9p_lock);
|
mutex_unlock(&virtio_9p_lock);
|
||||||
|
|
||||||
kfree(trans);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -236,17 +126,16 @@ static void req_done(struct virtqueue *vq)
|
||||||
struct virtio_chan *chan = vq->vdev->priv;
|
struct virtio_chan *chan = vq->vdev->priv;
|
||||||
struct p9_fcall *rc;
|
struct p9_fcall *rc;
|
||||||
unsigned int len;
|
unsigned int len;
|
||||||
unsigned long flags;
|
|
||||||
struct p9_req_t *req;
|
struct p9_req_t *req;
|
||||||
|
|
||||||
spin_lock_irqsave(&chan->lock, flags);
|
P9_DPRINTK(P9_DEBUG_TRANS, ": request done\n");
|
||||||
|
|
||||||
while ((rc = chan->vq->vq_ops->get_buf(chan->vq, &len)) != NULL) {
|
while ((rc = chan->vq->vq_ops->get_buf(chan->vq, &len)) != NULL) {
|
||||||
req = p9_lookup_tag(chan, rc->tag);
|
P9_DPRINTK(P9_DEBUG_TRANS, ": rc %p\n", rc);
|
||||||
req->status = REQ_STATUS_RCVD;
|
P9_DPRINTK(P9_DEBUG_TRANS, ": lookup tag %d\n", rc->tag);
|
||||||
wake_up(req->wq);
|
req = p9_tag_lookup(chan->client, rc->tag);
|
||||||
|
p9_client_cb(chan->client, req);
|
||||||
}
|
}
|
||||||
/* In case queue is stopped waiting for more buffers. */
|
|
||||||
spin_unlock_irqrestore(&chan->lock, flags);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -283,8 +172,14 @@ pack_sg_list(struct scatterlist *sg, int start, int limit, char *data,
|
||||||
return index-start;
|
return index-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* We don't currently allow canceling of virtio requests */
|
||||||
|
static int p9_virtio_cancel(struct p9_client *client, struct p9_req_t *req)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* p9_virtio_rpc - issue a request and wait for a response
|
* p9_virtio_request - issue a request
|
||||||
* @t: transport state
|
* @t: transport state
|
||||||
* @tc: &p9_fcall request to transmit
|
* @tc: &p9_fcall request to transmit
|
||||||
* @rc: &p9_fcall to put reponse into
|
* @rc: &p9_fcall to put reponse into
|
||||||
|
@ -292,44 +187,22 @@ pack_sg_list(struct scatterlist *sg, int start, int limit, char *data,
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int
|
static int
|
||||||
p9_virtio_rpc(struct p9_trans *t, struct p9_fcall *tc, struct p9_fcall **rc)
|
p9_virtio_request(struct p9_client *client, struct p9_req_t *req)
|
||||||
{
|
{
|
||||||
int in, out;
|
int in, out;
|
||||||
int n, err, size;
|
struct virtio_chan *chan = client->trans;
|
||||||
struct virtio_chan *chan = t->priv;
|
char *rdata = (char *)req->rc+sizeof(struct p9_fcall);
|
||||||
char *rdata;
|
|
||||||
struct p9_req_t *req;
|
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
if (*rc == NULL) {
|
P9_DPRINTK(P9_DEBUG_TRANS, "9p debug: virtio request\n");
|
||||||
*rc = kmalloc(sizeof(struct p9_fcall) + t->msize, GFP_KERNEL);
|
|
||||||
if (!*rc)
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
rdata = (char *)*rc+sizeof(struct p9_fcall);
|
out = pack_sg_list(chan->sg, 0, VIRTQUEUE_NUM, req->tc->sdata,
|
||||||
|
req->tc->size);
|
||||||
n = P9_NOTAG;
|
in = pack_sg_list(chan->sg, out, VIRTQUEUE_NUM-out, rdata,
|
||||||
if (tc->id != P9_TVERSION) {
|
client->msize);
|
||||||
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);
|
|
||||||
|
|
||||||
req->status = REQ_STATUS_SENT;
|
req->status = REQ_STATUS_SENT;
|
||||||
|
|
||||||
if (chan->vq->vq_ops->add_buf(chan->vq, chan->sg, out, in, tc)) {
|
if (chan->vq->vq_ops->add_buf(chan->vq, chan->sg, out, in, req->tc)) {
|
||||||
P9_DPRINTK(P9_DEBUG_TRANS,
|
P9_DPRINTK(P9_DEBUG_TRANS,
|
||||||
"9p debug: virtio rpc add_buf returned failure");
|
"9p debug: virtio rpc add_buf returned failure");
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
@ -337,31 +210,7 @@ p9_virtio_rpc(struct p9_trans *t, struct p9_fcall *tc, struct p9_fcall **rc)
|
||||||
|
|
||||||
chan->vq->vq_ops->kick(chan->vq);
|
chan->vq->vq_ops->kick(chan->vq);
|
||||||
|
|
||||||
wait_event(*req->wq, req->status == REQ_STATUS_RCVD);
|
P9_DPRINTK(P9_DEBUG_TRANS, "9p debug: virtio request kicked\n");
|
||||||
|
|
||||||
size = le32_to_cpu(*(__le32 *) rdata);
|
|
||||||
|
|
||||||
err = p9_deserialize_fcall(rdata, size, *rc, t->extended);
|
|
||||||
if (err < 0) {
|
|
||||||
P9_DPRINTK(P9_DEBUG_TRANS,
|
|
||||||
"9p debug: virtio rpc deserialize returned %d\n", err);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_NET_9P_DEBUG
|
|
||||||
if ((p9_debug_level&P9_DEBUG_FCALL) == P9_DEBUG_FCALL) {
|
|
||||||
char buf[150];
|
|
||||||
|
|
||||||
p9_printfcall(buf, sizeof(buf), *rc, t->extended);
|
|
||||||
printk(KERN_NOTICE ">>> %p %s\n", t, buf);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (n != P9_NOTAG && p9_idpool_check(n, chan->tagpool))
|
|
||||||
p9_idpool_put(n, chan->tagpool);
|
|
||||||
|
|
||||||
req->status = REQ_STATUS_IDLE;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -422,10 +271,9 @@ fail:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* p9_virtio_create - allocate a new virtio channel
|
* p9_virtio_create - allocate a new virtio channel
|
||||||
|
* @client: client instance invoking this transport
|
||||||
* @devname: string identifying the channel to connect to (unused)
|
* @devname: string identifying the channel to connect to (unused)
|
||||||
* @args: args passed from sys_mount() for per-transport options (unused)
|
* @args: args passed from sys_mount() for per-transport options (unused)
|
||||||
* @msize: requested maximum packet size
|
|
||||||
* @extended: 9p2000.u enabled flag
|
|
||||||
*
|
*
|
||||||
* This sets up a transport channel for 9p communication. Right now
|
* This sets up a transport channel for 9p communication. Right now
|
||||||
* we only match the first available channel, but eventually we couldlook up
|
* we only match the first available channel, but eventually we couldlook up
|
||||||
|
@ -441,11 +289,9 @@ fail:
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static struct p9_trans *
|
static int
|
||||||
p9_virtio_create(const char *devname, char *args, int msize,
|
p9_virtio_create(struct p9_client *client, const char *devname, char *args)
|
||||||
unsigned char extended)
|
|
||||||
{
|
{
|
||||||
struct p9_trans *trans;
|
|
||||||
struct virtio_chan *chan = channels;
|
struct virtio_chan *chan = channels;
|
||||||
int index = 0;
|
int index = 0;
|
||||||
|
|
||||||
|
@ -463,30 +309,13 @@ p9_virtio_create(const char *devname, char *args, int msize,
|
||||||
|
|
||||||
if (index >= MAX_9P_CHAN) {
|
if (index >= MAX_9P_CHAN) {
|
||||||
printk(KERN_ERR "9p: no channels available\n");
|
printk(KERN_ERR "9p: no channels available\n");
|
||||||
return ERR_PTR(-ENODEV);
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
chan->tagpool = p9_idpool_create();
|
client->trans = (void *)chan;
|
||||||
if (IS_ERR(chan->tagpool)) {
|
chan->client = client;
|
||||||
printk(KERN_ERR "9p: couldn't allocate tagpool\n");
|
|
||||||
return ERR_PTR(-ENOMEM);
|
|
||||||
}
|
|
||||||
p9_idpool_get(chan->tagpool); /* reserve tag 0 */
|
|
||||||
chan->max_tag = 0;
|
|
||||||
chan->reqs = NULL;
|
|
||||||
|
|
||||||
trans = kmalloc(sizeof(struct p9_trans), GFP_KERNEL);
|
return 0;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -526,6 +355,9 @@ static struct virtio_driver p9_virtio_drv = {
|
||||||
static struct p9_trans_module p9_virtio_trans = {
|
static struct p9_trans_module p9_virtio_trans = {
|
||||||
.name = "virtio",
|
.name = "virtio",
|
||||||
.create = p9_virtio_create,
|
.create = p9_virtio_create,
|
||||||
|
.close = p9_virtio_close,
|
||||||
|
.request = p9_virtio_request,
|
||||||
|
.cancel = p9_virtio_cancel,
|
||||||
.maxsize = PAGE_SIZE*16,
|
.maxsize = PAGE_SIZE*16,
|
||||||
.def = 0,
|
.def = 0,
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
|
|
|
@ -105,6 +105,7 @@ retry:
|
||||||
else if (error)
|
else if (error)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
P9_DPRINTK(P9_DEBUG_MUX, " id %d pool %p\n", i, p);
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(p9_idpool_get);
|
EXPORT_SYMBOL(p9_idpool_get);
|
||||||
|
@ -121,6 +122,9 @@ EXPORT_SYMBOL(p9_idpool_get);
|
||||||
void p9_idpool_put(int id, struct p9_idpool *p)
|
void p9_idpool_put(int id, struct p9_idpool *p)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
|
P9_DPRINTK(P9_DEBUG_MUX, " id %d pool %p\n", id, p);
|
||||||
|
|
||||||
spin_lock_irqsave(&p->lock, flags);
|
spin_lock_irqsave(&p->lock, flags);
|
||||||
idr_remove(&p->pool, id);
|
idr_remove(&p->pool, id);
|
||||||
spin_unlock_irqrestore(&p->lock, flags);
|
spin_unlock_irqrestore(&p->lock, flags);
|
||||||
|
|
Loading…
Reference in New Issue