Commit 8f40f672 authored by Linus Torvalds's avatar Linus Torvalds

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

* 'for-linus' of ssh://master.kernel.org/pub/scm/linux/kernel/git/ericvh/v9fs:
  9p: fix error path during early mount
  9p: make cryptic unknown error from server less scary
  9p: fix flags length in net
  9p: Correct fidpool creation failure in p9_client_create
  9p: use struct mutex instead of struct semaphore
  9p: propagate parse_option changes to client and transports
  fs/9p/v9fs.c (v9fs_parse_options): Handle kstrdup and match_strdup failure.
  9p: Documentation updates
  add match_strlcpy() us it to make v9fs make uname and remotename parsing more robust
parents 8978a318 887b3ece
......@@ -22,6 +22,21 @@
#include <linux/list.h>
/**
* struct v9fs_dentry - 9p private data stored in dentry d_fsdata
* @lock: protects the fidlist
* @fidlist: list of FIDs currently associated with this dentry
*
* This structure defines the 9p private data associated with
* a particular dentry. In particular, this private data is used
* to lookup which 9P FID handle should be used for a particular VFS
* operation. FID handles are associated with dentries instead of
* inodes in order to more closely map functionality to the Plan 9
* expected behavior for FID reclaimation and tracking.
*
* See Also: Mapping FIDs to Linux VFS model in
* Design and Implementation of the Linux 9P File System documentation
*/
struct v9fs_dentry {
spinlock_t lock; /* protect fidlist */
struct list_head fidlist;
......
......@@ -71,19 +71,19 @@ static match_table_t tokens = {
/**
* v9fs_parse_options - parse mount options into session structure
* @options: options string passed from mount
* @v9ses: existing v9fs session information
*
* Return 0 upon success, -ERRNO upon failure.
*/
static void v9fs_parse_options(struct v9fs_session_info *v9ses)
static int v9fs_parse_options(struct v9fs_session_info *v9ses)
{
char *options;
substring_t args[MAX_OPT_ARGS];
char *p;
int option = 0;
char *s, *e;
int ret;
int ret = 0;
/* setup defaults */
v9ses->afid = ~0;
......@@ -91,19 +91,26 @@ static void v9fs_parse_options(struct v9fs_session_info *v9ses)
v9ses->cache = 0;
if (!v9ses->options)
return;
return 0;
options = kstrdup(v9ses->options, GFP_KERNEL);
if (!options) {
P9_DPRINTK(P9_DEBUG_ERROR,
"failed to allocate copy of option string\n");
return -ENOMEM;
}
while ((p = strsep(&options, ",")) != NULL) {
int token;
if (!*p)
continue;
token = match_token(p, tokens, args);
if (token < Opt_uname) {
ret = match_int(&args[0], &option);
if (ret < 0) {
int r = match_int(&args[0], &option);
if (r < 0) {
P9_DPRINTK(P9_DEBUG_ERROR,
"integer field, but no integer?\n");
ret = r;
continue;
}
}
......@@ -125,10 +132,10 @@ static void v9fs_parse_options(struct v9fs_session_info *v9ses)
v9ses->afid = option;
break;
case Opt_uname:
match_strcpy(v9ses->uname, &args[0]);
match_strlcpy(v9ses->uname, &args[0], PATH_MAX);
break;
case Opt_remotename:
match_strcpy(v9ses->aname, &args[0]);
match_strlcpy(v9ses->aname, &args[0], PATH_MAX);
break;
case Opt_nodevmap:
v9ses->nodev = 1;
......@@ -139,6 +146,13 @@ static void v9fs_parse_options(struct v9fs_session_info *v9ses)
case Opt_access:
s = match_strdup(&args[0]);
if (!s) {
P9_DPRINTK(P9_DEBUG_ERROR,
"failed to allocate copy"
" of option argument\n");
ret = -ENOMEM;
break;
}
v9ses->flags &= ~V9FS_ACCESS_MASK;
if (strcmp(s, "user") == 0)
v9ses->flags |= V9FS_ACCESS_USER;
......@@ -158,6 +172,7 @@ static void v9fs_parse_options(struct v9fs_session_info *v9ses)
}
}
kfree(options);
return ret;
}
/**
......@@ -173,6 +188,7 @@ struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses,
{
int retval = -EINVAL;
struct p9_fid *fid;
int rc;
v9ses->uname = __getname();
if (!v9ses->uname)
......@@ -190,8 +206,21 @@ struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses,
v9ses->uid = ~0;
v9ses->dfltuid = V9FS_DEFUID;
v9ses->dfltgid = V9FS_DEFGID;
if (data) {
v9ses->options = kstrdup(data, GFP_KERNEL);
v9fs_parse_options(v9ses);
if (!v9ses->options) {
P9_DPRINTK(P9_DEBUG_ERROR,
"failed to allocate copy of option string\n");
retval = -ENOMEM;
goto error;
}
}
rc = v9fs_parse_options(v9ses);
if (rc < 0) {
retval = rc;
goto error;
}
v9ses->clnt = p9_client_create(dev_name, v9ses->options);
......@@ -233,7 +262,6 @@ struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses,
return fid;
error:
v9fs_session_close(v9ses);
return ERR_PTR(retval);
}
......@@ -256,9 +284,12 @@ void v9fs_session_close(struct v9fs_session_info *v9ses)
}
/**
* v9fs_session_cancel - mark transport as disconnected
* and cancel all pending requests.
* v9fs_session_cancel - terminate a session
* @v9ses: session to terminate
*
* mark transport as disconnected and cancel all pending requests.
*/
void v9fs_session_cancel(struct v9fs_session_info *v9ses) {
P9_DPRINTK(P9_DEBUG_ERROR, "cancel session %p\n", v9ses);
p9_client_disconnect(v9ses->clnt);
......
......@@ -21,18 +21,69 @@
*
*/
/*
* Session structure provides information for an opened session
/**
* enum p9_session_flags - option flags for each 9P session
* @V9FS_EXTENDED: whether or not to use 9P2000.u extensions
* @V9FS_ACCESS_SINGLE: only the mounting user can access the hierarchy
* @V9FS_ACCESS_USER: a new attach will be issued for every user (default)
* @V9FS_ACCESS_ANY: use a single attach for all users
* @V9FS_ACCESS_MASK: bit mask of different ACCESS options
*
* Session flags reflect options selected by users at mount time
*/
enum p9_session_flags {
V9FS_EXTENDED = 0x01,
V9FS_ACCESS_SINGLE = 0x02,
V9FS_ACCESS_USER = 0x04,
V9FS_ACCESS_ANY = 0x06,
V9FS_ACCESS_MASK = 0x06,
};
/* possible values of ->cache */
/**
* enum p9_cache_modes - user specified cache preferences
* @CACHE_NONE: do not cache data, dentries, or directory contents (default)
* @CACHE_LOOSE: cache data, dentries, and directory contents w/no consistency
*
* eventually support loose, tight, time, session, default always none
*/
enum p9_cache_modes {
CACHE_NONE,
CACHE_LOOSE,
};
/**
* struct v9fs_session_info - per-instance session information
* @flags: session options of type &p9_session_flags
* @nodev: set to 1 to disable device mapping
* @debug: debug level
* @afid: authentication handle
* @cache: cache mode of type &p9_cache_modes
* @options: copy of options string given by user
* @uname: string user name to mount hierarchy as
* @aname: mount specifier for remote hierarchy
* @maxdata: maximum data to be sent/recvd per protocol message
* @dfltuid: default numeric userid to mount hierarchy as
* @dfltgid: default numeric groupid to mount hierarchy as
* @uid: if %V9FS_ACCESS_SINGLE, the numeric uid which mounted the hierarchy
* @clnt: reference to 9P network client instantiated for this session
* @debugfs_dir: reference to debugfs_dir which can be used for add'l debug
*
* This structure holds state for each session instance established during
* a sys_mount() .
*
* Bugs: there seems to be a lot of state which could be condensed and/or
* removed.
*/
struct v9fs_session_info {
/* options */
unsigned char flags; /* session flags */
unsigned char nodev; /* set to 1 if no disable device mapping */
unsigned short debug; /* debug level */
unsigned int afid; /* authentication fid */
unsigned int cache; /* cache mode */
unsigned char flags;
unsigned char nodev;
unsigned short debug;
unsigned int afid;
unsigned int cache;
char *options; /* copy of mount options */
char *uname; /* user name to mount as */
......@@ -45,22 +96,6 @@ struct v9fs_session_info {
struct dentry *debugfs_dir;
};
/* session flags */
enum {
V9FS_EXTENDED = 0x01, /* 9P2000.u */
V9FS_ACCESS_MASK = 0x06, /* access mask */
V9FS_ACCESS_SINGLE = 0x02, /* only one user can access the files */
V9FS_ACCESS_USER = 0x04, /* attache per user */
V9FS_ACCESS_ANY = 0x06, /* use the same attach for all users */
};
/* possible values of ->cache */
/* eventually support loose, tight, time, session, default always none */
enum {
CACHE_NONE, /* default */
CACHE_LOOSE, /* no consistency */
};
extern struct dentry *v9fs_debugfs_root;
struct p9_fid *v9fs_session_init(struct v9fs_session_info *, const char *,
......
......@@ -43,7 +43,7 @@
/**
* v9fs_vfs_readpage - read an entire page in from 9P
*
* @file: file being read
* @filp: file being read
* @page: structure to page
*
*/
......
......@@ -60,7 +60,7 @@ static inline int dt_type(struct p9_stat *mistat)
/**
* v9fs_dir_readdir - read a directory
* @filep: opened file structure
* @filp: opened file structure
* @dirent: directory structure ???
* @filldir: function to populate directory structure ???
*
......
......@@ -90,10 +90,11 @@ int v9fs_file_open(struct inode *inode, struct file *file)
/**
* v9fs_file_lock - lock a file (or directory)
* @inode: inode to be opened
* @file: file being opened
* @filp: file to be locked
* @cmd: lock command
* @fl: file lock structure
*
* XXX - this looks like a local only lock, we should extend into 9P
* Bugs: this looks like a local only lock, we should extend into 9P
* by using open exclusive
*/
......@@ -118,7 +119,7 @@ static int v9fs_file_lock(struct file *filp, int cmd, struct file_lock *fl)
/**
* v9fs_file_read - read from a file
* @filep: file pointer to read
* @filp: file pointer to read
* @data: data buffer to read data into
* @count: size of buffer
* @offset: offset at which to read data
......@@ -142,7 +143,7 @@ v9fs_file_read(struct file *filp, char __user * data, size_t count,
/**
* v9fs_file_write - write to a file
* @filep: file pointer to write
* @filp: file pointer to write
* @data: data buffer to write data from
* @count: size of buffer
* @offset: offset at which to write data
......
......@@ -129,6 +129,12 @@ static int p9mode2unixmode(struct v9fs_session_info *v9ses, int mode)
return res;
}
/**
* v9fs_uflags2omode- convert posix open flags to plan 9 mode bits
* @uflags: flags to convert
*
*/
int v9fs_uflags2omode(int uflags)
{
int ret;
......@@ -312,6 +318,14 @@ error:
}
*/
/**
* v9fs_inode_from_fid - populate an inode by issuing a attribute request
* @v9ses: session information
* @fid: fid to issue attribute request for
* @sb: superblock on which to create inode
*
*/
static struct inode *
v9fs_inode_from_fid(struct v9fs_session_info *v9ses, struct p9_fid *fid,
struct super_block *sb)
......@@ -384,9 +398,12 @@ v9fs_open_created(struct inode *inode, struct file *file)
/**
* v9fs_create - Create a file
* @v9ses: session information
* @dir: directory that dentry is being created in
* @dentry: dentry that is being created
* @perm: create permissions
* @mode: open mode
* @extension: 9p2000.u extension string to support devices, etc.
*
*/
static struct p9_fid *
......@@ -461,7 +478,7 @@ error:
/**
* v9fs_vfs_create - VFS hook to create files
* @inode: directory inode that is being created
* @dir: directory inode that is being created
* @dentry: dentry that is being deleted
* @mode: create permissions
* @nd: path information
......@@ -519,7 +536,7 @@ error:
/**
* v9fs_vfs_mkdir - VFS mkdir hook to create a directory
* @inode: inode that is being unlinked
* @dir: inode that is being unlinked
* @dentry: dentry that is being unlinked
* @mode: mode for new directory
*
......@@ -703,9 +720,9 @@ done:
/**
* v9fs_vfs_getattr - retrieve file metadata
* @mnt - mount information
* @dentry - file to get attributes on
* @stat - metadata structure to populate
* @mnt: mount information
* @dentry: file to get attributes on
* @stat: metadata structure to populate
*
*/
......@@ -928,7 +945,7 @@ done:
/**
* v9fs_vfs_readlink - read a symlink's location
* @dentry: dentry for symlink
* @buf: buffer to load symlink location into
* @buffer: buffer to load symlink location into
* @buflen: length of buffer
*
*/
......@@ -996,10 +1013,12 @@ static void *v9fs_vfs_follow_link(struct dentry *dentry, struct nameidata *nd)
* v9fs_vfs_put_link - release a symlink path
* @dentry: dentry for symlink
* @nd: nameidata
* @p: unused
*
*/
static void v9fs_vfs_put_link(struct dentry *dentry, struct nameidata *nd, void *p)
static void
v9fs_vfs_put_link(struct dentry *dentry, struct nameidata *nd, void *p)
{
char *s = nd_get_link(nd);
......@@ -1008,6 +1027,15 @@ static void v9fs_vfs_put_link(struct dentry *dentry, struct nameidata *nd, void
__putname(s);
}
/**
* v9fs_vfs_mkspecial - create a special file
* @dir: inode to create special file in
* @dentry: dentry to create
* @mode: mode to create special file
* @extension: 9p2000.u format extension string representing special file
*
*/
static int v9fs_vfs_mkspecial(struct inode *dir, struct dentry *dentry,
int mode, const char *extension)
{
......@@ -1037,7 +1065,7 @@ static int v9fs_vfs_mkspecial(struct inode *dir, struct dentry *dentry,
* @dentry: dentry for symlink
* @symname: symlink data
*
* See 9P2000.u RFC for more information
* See Also: 9P2000.u RFC for more information
*
*/
......@@ -1058,10 +1086,6 @@ v9fs_vfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
*
*/
/* XXX - lots of code dup'd from symlink and creates,
* figure out a better reuse strategy
*/
static int
v9fs_vfs_link(struct dentry *old_dentry, struct inode *dir,
struct dentry *dentry)
......@@ -1098,7 +1122,7 @@ clunk_fid:
* @dir: inode destination for new link
* @dentry: dentry for file
* @mode: mode for creation
* @dev_t: device associated with special file
* @rdev: device associated with special file
*
*/
......
......@@ -75,6 +75,7 @@ static int v9fs_set_super(struct super_block *s, void *data)
* v9fs_fill_super - populate superblock with info
* @sb: superblock
* @v9ses: session information
* @flags: flags propagated from v9fs_get_sb()
*
*/
......@@ -127,29 +128,26 @@ static int v9fs_get_sb(struct file_system_type *fs_type, int flags,
fid = v9fs_session_init(v9ses, dev_name, data);
if (IS_ERR(fid)) {
retval = PTR_ERR(fid);
fid = NULL;
kfree(v9ses);
v9ses = NULL;
goto error;
goto close_session;
}
st = p9_client_stat(fid);
if (IS_ERR(st)) {
retval = PTR_ERR(st);
goto error;
goto clunk_fid;
}
sb = sget(fs_type, NULL, v9fs_set_super, v9ses);
if (IS_ERR(sb)) {
retval = PTR_ERR(sb);
goto error;
goto free_stat;
}
v9fs_fill_super(sb, v9ses, flags);
inode = v9fs_get_inode(sb, S_IFDIR | mode);
if (IS_ERR(inode)) {
retval = PTR_ERR(inode);
goto error;
goto release_sb;
}
inode->i_uid = uid;
......@@ -158,7 +156,7 @@ static int v9fs_get_sb(struct file_system_type *fs_type, int flags,
root = d_alloc_root(inode);
if (!root) {
retval = -ENOMEM;
goto error;
goto release_sb;
}
sb->s_root = root;
......@@ -169,20 +167,21 @@ static int v9fs_get_sb(struct file_system_type *fs_type, int flags,
return simple_set_mnt(mnt, sb);
error:
release_sb:
if (sb) {
up_write(&sb->s_umount);
deactivate_super(sb);
}
free_stat:
kfree(st);
if (fid)
clunk_fid:
p9_client_clunk(fid);
if (v9ses) {
close_session:
v9fs_session_close(v9ses);
kfree(v9ses);
}
if (sb) {
up_write(&sb->s_umount);
deactivate_super(sb);
}
return retval;
}
......
......@@ -29,5 +29,5 @@ int match_token(char *, match_table_t table, substring_t args[]);
int match_int(substring_t *, int *result);
int match_octal(substring_t *, int *result);
int match_hex(substring_t *, int *result);
void match_strcpy(char *, const substring_t *);
size_t match_strlcpy(char *, const substring_t *, size_t);
char *match_strdup(const substring_t *);
This diff is collapsed.
......@@ -26,6 +26,23 @@
#ifndef NET_9P_CLIENT_H
#define NET_9P_CLIENT_H
/**
* struct p9_client - per client instance state
* @lock: protect @fidlist
* @msize: maximum data size negotiated by protocol
* @dotu: extension flags negotiated by protocol
* @trans_mod: module API instantiated with this client
* @trans: tranport instance state and API
* @conn: connection state information used by trans_fd
* @fidpool: fid handle accounting for session
* @fidlist: List of active fid handles
*
* The client structure is used to keep track of various per-client
* state that has been instantiated.
*
* Bugs: duplicated data and potentially unnecessary elements.
*/
struct p9_client {
spinlock_t lock; /* protect client structure */
int msize;
......@@ -38,6 +55,24 @@ struct p9_client {
struct list_head fidlist;
};
/**
* struct p9_fid - file system entity handle
* @clnt: back pointer to instantiating &p9_client
* @fid: numeric identifier for this handle
* @mode: current mode of this fid (enum?)
* @qid: the &p9_qid server identifier this handle points to
* @iounit: the server reported maximum transaction size for this file
* @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
*
* TODO: This needs lots of explanation.
*/
struct p9_fid {
struct p9_client *clnt;
u32 fid;
......
......@@ -26,12 +26,40 @@
#ifndef NET_9P_TRANSPORT_H
#define NET_9P_TRANSPORT_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;
......@@ -42,6 +70,21 @@ struct p9_trans {
struct p9_fcall **rc);
};
/**
* struct p9_trans_module - transport module interface
* @list: used to maintain a list of currently available transports
* @name: the human-readable name of the transport
* @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
*
* 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.
*/
struct p9_trans_module {
struct list_head list;
char *name; /* name of transport */
......@@ -53,5 +96,4 @@ struct p9_trans_module {
void v9fs_register_trans(struct p9_trans_module *m);
struct p9_trans_module *v9fs_match_trans(const substring_t *name);
struct p9_trans_module *v9fs_default_trans(void);
#endif /* NET_9P_TRANSPORT_H */
......@@ -182,18 +182,25 @@ int match_hex(substring_t *s, int *result)
}
/**
* match_strcpy: - copies the characters from a substring_t to a string
* @to: string to copy characters to.
* @s: &substring_t to copy
* match_strlcpy: - Copy the characters from a substring_t to a sized buffer
* @dest: where to copy to
* @src: &substring_t to copy
* @size: size of destination buffer
*
* Description: Copies the set of characters represented by the given
* &substring_t @s to the c-style string @to. Caller guarantees that @to is
* large enough to hold the characters of @s.
* Description: Copy the characters in &substring_t @src to the
* c-style string @dest. Copy no more than @size - 1 characters, plus
* the terminating NUL. Return length of @src.
*/
void match_strcpy(char *to, const substring_t *s)
size_t match_strlcpy(char *dest, const substring_t *src, size_t size)
{
memcpy(to, s->from, s->to - s->from);
to[s->to - s->from] = '\0';
size_t ret = src->to - src->from;
if (size) {
size_t len = ret >= size ? size - 1 : ret;
memcpy(dest, src->from, len);
dest[len] = '\0';
}
return ret;
}
/**
......@@ -206,9 +213,10 @@ void match_strcpy(char *to, const substring_t *s)
*/
char *match_strdup(const substring_t *s)
{
char *p = kmalloc(s->to - s->from + 1, GFP_KERNEL);
size_t sz = s->to - s->from + 1;
char *p = kmalloc(sz, GFP_KERNEL);
if (p)
match_strcpy(p, s);
match_strlcpy(p, s, sz);
return p;
}
......@@ -216,5 +224,5 @@ EXPORT_SYMBOL(match_token);
EXPORT_SYMBOL(match_int);
EXPORT_SYMBOL(match_octal);
EXPORT_SYMBOL(match_hex);
EXPORT_SYMBOL(match_strcpy);
EXPORT_SYMBOL(match_strlcpy);
EXPORT_SYMBOL(match_strdup);
......@@ -13,16 +13,6 @@ menuconfig NET_9P
If unsure, say N.
config NET_9P_FD
depends on NET_9P
default y if NET_9P
tristate "9P File Descriptor Transports (Experimental)"
help
This builds support for file descriptor transports for 9p
which includes support for TCP/IP, named pipes, or passed
file descriptors. TCP/IP is the default transport for 9p,
so if you are going to use 9p, you'll likely want this.
config NET_9P_VIRTIO
depends on NET_9P && EXPERIMENTAL && VIRTIO
tristate "9P Virtio Transport (Experimental)"
......
obj-$(CONFIG_NET_9P) := 9pnet.o
obj-$(CONFIG_NET_9P_FD) += 9pnet_fd.o
obj-$(CONFIG_NET_9P_VIRTIO) += 9pnet_virtio.o
9pnet-objs := \
......@@ -9,8 +8,6 @@ obj-$(CONFIG_NET_9P_VIRTIO) += 9pnet_virtio.o
error.o \
fcprint.o \
util.o \
9pnet_fd-objs := \
trans_fd.o \
9pnet_virtio-objs := \
......
......@@ -64,21 +64,30 @@ static match_table_t tokens = {
* @options: options string passed from mount
* @v9ses: existing v9fs session information
*
* Return 0 upon success, -ERRNO upon failure
*/
static void parse_opts(char *options, struct p9_client *clnt)
static int parse_opts(char *opts, struct p9_client *clnt)
{
char *options;
char *p;
substring_t args[MAX_OPT_ARGS];
int option;
int ret;
int ret = 0;
clnt->trans_mod = v9fs_default_trans();
clnt->dotu = 1;
clnt->msize = 8192;
if (!options)
return;
if (!opts)
return 0;
options = kstrdup(opts, GFP_KERNEL);
if (!options) {
P9_DPRINTK(P9_DEBUG_ERROR,
"failed to allocate copy of option string\n");
return -ENOMEM;
}
while ((p = strsep(&options, ",")) != NULL) {
int token;
......@@ -86,10 +95,11 @@ static void parse_opts(char *options, struct p9_client *clnt)
continue;
token = match_token(p, tokens, args);
if (token < Opt_trans) {
ret = match_int(&args[0], &option);
if (ret < 0) {
int r = match_int(&args[0], &option);
if (r < 0) {
P9_DPRINTK(P9_DEBUG_ERROR,
"integer field, but no integer?\n");
ret = r;
continue;
}
}
......@@ -107,6 +117,8 @@ static void parse_opts(char *options, struct p9_client *clnt)
continue;
}
}
kfree(options);
return ret;
}
......@@ -138,16 +150,20 @@ struct p9_client *p9_client_create(const char *dev_name, char *options)
if (!clnt)
return ERR_PTR(-ENOMEM);
clnt->trans = NULL;
spin_lock_init(&clnt->lock);
INIT_LIST_HEAD(&clnt->fidlist);
clnt->fidpool = p9_idpool_create();
if (!clnt->fidpool) {
if (IS_ERR(clnt->fidpool)) {
err = PTR_ERR(clnt->fidpool);
clnt->fidpool = NULL;
goto error;
}
parse_opts(options, clnt);
err = parse_opts(options, clnt);
if (err < 0)
goto error;
if (clnt->trans_mod == NULL) {
err = -EPROTONOSUPPORT;
P9_DPRINTK(P9_DEBUG_ERROR,
......
......@@ -197,7 +197,7 @@ static void buf_get_qid(struct cbuf *bufp, struct p9_qid *qid)
/**
* p9_size_wstat - calculate the size of a variable length stat struct
* @stat: metadata (stat) structure
* @wstat: metadata (stat) structure
* @dotu: non-zero if 9P2000.u
*
*/
......@@ -511,6 +511,12 @@ p9_create_common(struct cbuf *bufp, u32 size, u8 id)
return fc;
}
/**
* p9_set_tag - set the tag field of an &p9_fcall structure
* @fc: fcall structure to set tag within
* @tag: tag id to set
*/
void p9_set_tag(struct p9_fcall *fc, u16 tag)
{
fc->tag = tag;
......@@ -518,6 +524,12 @@ void p9_set_tag(struct p9_fcall *fc, u16 tag)
}
EXPORT_SYMBOL(p9_set_tag);
/**
* p9_create_tversion - allocates and creates a T_VERSION request
* @msize: requested maximum data size
* @version: version string to negotiate
*
*/
struct p9_fcall *p9_create_tversion(u32 msize, char *version)
{
int size;
......@@ -542,6 +554,16 @@ error:
}
EXPORT_SYMBOL(p9_create_tversion);
/**
* p9_create_tauth - allocates and creates a T_AUTH request
* @afid: handle to use for authentication protocol
* @uname: user name attempting to authenticate
* @aname: mount specifier for remote server
* @n_uname: numeric id for user attempting to authneticate
* @dotu: 9P2000.u extension flag
*
*/
struct p9_fcall *p9_create_tauth(u32 afid, char *uname, char *aname,
u32 n_uname, int dotu)
{
......@@ -580,6 +602,18 @@ error:
}
EXPORT_SYMBOL(p9_create_tauth);
/**
* p9_create_tattach - allocates and creates a T_ATTACH request
* @fid: handle to use for the new mount point
* @afid: handle to use for authentication protocol
* @uname: user name attempting to attach
* @aname: mount specifier for remote server
* @n_uname: numeric id for user attempting to attach
* @n_uname: numeric id for user attempting to attach
* @dotu: 9P2000.u extension flag
*
*/
struct p9_fcall *
p9_create_tattach(u32 fid, u32 afid, char *uname, char *aname,
u32 n_uname, int dotu)
......@@ -616,6 +650,12 @@ error:
}
EXPORT_SYMBOL(p9_create_tattach);
/**
* p9_create_tflush - allocates and creates a T_FLUSH request
* @oldtag: tag id for the transaction we are attempting to cancel
*
*/
struct p9_fcall *p9_create_tflush(u16 oldtag)
{
int size;
......@@ -639,6 +679,15 @@ error:
}
EXPORT_SYMBOL(p9_create_tflush);
/**
* p9_create_twalk - allocates and creates a T_FLUSH request
* @fid: handle we are traversing from
* @newfid: a new handle for this transaction
* @nwname: number of path elements to traverse
* @wnames: array of path elements
*
*/
struct p9_fcall *p9_create_twalk(u32 fid, u32 newfid, u16 nwname,
char **wnames)
{
......@@ -677,6 +726,13 @@ error:
}
EXPORT_SYMBOL(p9_create_twalk);
/**
* p9_create_topen - allocates and creates a T_OPEN request
* @fid: handle we are trying to open
* @mode: what mode we are trying to open the file in
*
*/
struct p9_fcall *p9_create_topen(u32 fid, u8 mode)
{
int size;
......@@ -701,6 +757,19 @@ error:
}
EXPORT_SYMBOL(p9_create_topen);
/**
* p9_create_tcreate - allocates and creates a T_CREATE request
* @fid: handle of directory we are trying to create in
* @name: name of the file we are trying to create
* @perm: permissions for the file we are trying to create
* @mode: what mode we are trying to open the file in
* @extension: 9p2000.u extension string (for special files)
* @dotu: 9p2000.u enabled flag
*
* Note: Plan 9 create semantics include opening the resulting file
* which is why mode is included.
*/
struct p9_fcall *p9_create_tcreate(u32 fid, char *name, u32 perm, u8 mode,
char *extension, int dotu)
{
......@@ -736,6 +805,13 @@ error:
}
EXPORT_SYMBOL(p9_create_tcreate);
/**
* p9_create_tread - allocates and creates a T_READ request
* @fid: handle of the file we are trying to read
* @offset: offset to start reading from
* @count: how many bytes to read
*/
struct p9_fcall *p9_create_tread(u32 fid, u64 offset, u32 count)
{
int size;
......@@ -761,6 +837,17 @@ error:
}
EXPORT_SYMBOL(p9_create_tread);
/**
* p9_create_twrite - allocates and creates a T_WRITE request from the kernel
* @fid: handle of the file we are trying to write
* @offset: offset to start writing at
* @count: how many bytes to write
* @data: data to write
*
* This function will create a requst with data buffers from the kernel
* such as the page cache.
*/
struct p9_fcall *p9_create_twrite(u32 fid, u64 offset, u32 count,
const char *data)
{
......@@ -794,6 +881,16 @@ error:
}
EXPORT_SYMBOL(p9_create_twrite);
/**
* p9_create_twrite_u - allocates and creates a T_WRITE request from userspace
* @fid: handle of the file we are trying to write
* @offset: offset to start writing at
* @count: how many bytes to write
* @data: data to write
*
* This function will create a request with data buffers from userspace
*/
struct p9_fcall *p9_create_twrite_u(u32 fid, u64 offset, u32 count,
const char __user *data)
{
......@@ -827,6 +924,14 @@ error:
}
EXPORT_SYMBOL(p9_create_twrite_u);
/**
* p9_create_tclunk - allocate a request to forget about a file handle
* @fid: handle of the file we closing or forgetting about
*
* clunk is used both to close open files and to discard transient handles
* which may be created during meta-data operations and hierarchy traversal.
*/
struct p9_fcall *p9_create_tclunk(u32 fid)
{
int size;
......@@ -850,6 +955,12 @@ error:
}
EXPORT_SYMBOL(p9_create_tclunk);
/**
* p9_create_tremove - allocate and create a request to remove a file
* @fid: handle of the file or directory we are removing
*
*/
struct p9_fcall *p9_create_tremove(u32 fid)
{
int size;
......@@ -873,6 +984,12 @@ error:
}
EXPORT_SYMBOL(p9_create_tremove);
/**
* p9_create_tstat - allocate and populate a request for attributes
* @fid: handle of the file or directory we are trying to get the attributes of
*
*/
struct p9_fcall *p9_create_tstat(u32 fid)
{
int size;
......@@ -896,6 +1013,14 @@ error:
}
EXPORT_SYMBOL(p9_create_tstat);
/**
* p9_create_tstat - allocate and populate a request to change attributes
* @fid: handle of the file or directory we are trying to change
* @wstat: &p9_stat structure with attributes we wish to set
* @dotu: 9p2000.u enabled flag
*
*/
struct p9_fcall *p9_create_twstat(u32 fid, struct p9_wstat *wstat,
int dotu)
{
......@@ -922,3 +1047,4 @@ error:
return fc;
}
EXPORT_SYMBOL(p9_create_twstat);
......@@ -33,6 +33,13 @@
#include <linux/errno.h>
#include <net/9p/9p.h>
/**
* struct errormap - map string errors from Plan 9 to Linux numeric ids
* @name: string sent over 9P
* @val: numeric id most closely representing @name
* @namelen: length of string
* @list: hash-table list for string lookup
*/
struct errormap {
char *name;
int val;
......@@ -177,8 +184,7 @@ static struct errormap errmap[] = {
};
/**
* p9_error_init - preload
* @errstr: error string
* p9_error_init - preload mappings into hash list
*
*/
......@@ -206,6 +212,7 @@ EXPORT_SYMBOL(p9_error_init);
/**
* errstr2errno - convert error string to error number
* @errstr: error string
* @len: length of error string
*
*/
......@@ -230,8 +237,8 @@ int p9_errstr2errno(char *errstr, int len)
if (errno == 0) {
/* TODO: if error isn't found, add it dynamically */
errstr[len] = 0;
printk(KERN_ERR "%s: errstr :%s: not found\n", __func__,
errstr);
printk(KERN_ERR "%s: server reported unknown error %s\n",
__func__, errstr);
errno = 1;
}
......
......@@ -142,6 +142,14 @@ 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)
{
......
......@@ -39,9 +39,6 @@ module_param_named(debug, p9_debug_level, uint, 0);
MODULE_PARM_DESC(debug, "9P debugging level");
#endif
extern int p9_mux_global_init(void);
extern void p9_mux_global_exit(void);
/*
* Dynamic Transport Registration Routines
*
......@@ -52,7 +49,7 @@ static struct p9_trans_module *v9fs_default_transport;
/**
* v9fs_register_trans - register a new transport with 9p
* @m - structure describing the transport module and entry points
* @m: structure describing the transport module and entry points
*
*/
void v9fs_register_trans(struct p9_trans_module *m)
......@@ -65,7 +62,7 @@ EXPORT_SYMBOL(v9fs_register_trans);
/**
* v9fs_match_trans - match transport versus registered transports
* @arg: string identifying transport
* @name: string identifying transport
*
*/
struct p9_trans_module *v9fs_match_trans(const substring_t *name)
......@@ -110,6 +107,7 @@ static int __init init_p9(void)
p9_error_init();
printk(KERN_INFO "Installing 9P2000 support\n");
p9_trans_fd_init();
return ret;
}
......
This diff is collapsed.
......@@ -49,29 +49,75 @@
#define VIRTQUEUE_NUM 128
/* a single mutex to manage channel initialization and attachment */
static DECLARE_MUTEX(virtio_9p_lock);
static DEFINE_MUTEX(virtio_9p_lock);
/* global which tracks highest initialized channel */
static int chan_index;
#define P9_INIT_MAXTAG 16
#define REQ_STATUS_IDLE 0
#define REQ_STATUS_SENT 1
#define REQ_STATUS_RCVD 2
#define REQ_STATUS_FLSH 3
/**
* 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;
};
/* We keep all per-channel information in a structure.
/**
* struct virtio_chan - per-instance transport information
* @initialized: whether the channel is initialized
* @inuse: whether the channel is in use
* @lock: protects multiple elements within this structure
* @vdev: virtio dev associated with this channel
* @vq: virtio queue associated with this channel
* @tagpool: accounting for tag ids (and request slots)
* @reqs: array of request slots
* @max_tag: current number of request_slots allocated
* @sg: scatter gather list which is used to pack a request (protected?)
*
* We keep all per-channel information in a structure.
* This structure is allocated within the devices dev->mem space.
* A pointer to the structure will get put in the transport private.
*
*/
static struct virtio_chan {
bool initialized; /* channel is initialized */
bool inuse; /* channel is in use */
bool initialized;
bool inuse;
spinlock_t lock;
......@@ -86,7 +132,19 @@ static struct virtio_chan {
struct scatterlist sg[VIRTQUEUE_NUM];
} channels[MAX_9P_CHAN];
/* Lookup requests by tag */
/**
* 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
......@@ -130,11 +188,20 @@ static unsigned int rest_of_page(void *data)
return PAGE_SIZE - ((unsigned long)data % PAGE_SIZE);
}
/**
* p9_virtio_close - reclaim resources of a channel
* @trans: transport state
*
* This reclaims a channel by freeing its resources and
* reseting its inuse flag.
*
*/
static void p9_virtio_close(struct p9_trans *trans)
{
struct virtio_chan *chan = trans->priv;
int count;
unsigned int flags;
unsigned long flags;
spin_lock_irqsave(&chan->lock, flags);
p9_idpool_destroy(chan->tagpool);
......@@ -144,13 +211,26 @@ static void p9_virtio_close(struct p9_trans *trans)
chan->max_tag = 0;
spin_unlock_irqrestore(&chan->lock, flags);
down(&virtio_9p_lock);
mutex_lock(&virtio_9p_lock);
chan->inuse = false;
up(&virtio_9p_lock);
mutex_unlock(&virtio_9p_lock);
kfree(trans);
}
/**
* req_done - callback which signals activity from the server
* @vq: virtio queue activity was received on
*
* This notifies us that the server has triggered some activity
* on the virtio channel - most likely a response to request we
* sent. Figure out which requests now have responses and wake up
* those threads.
*
* Bugs: could do with some additional sanity checking, but appears to work.
*
*/
static void req_done(struct virtqueue *vq)
{
struct virtio_chan *chan = vq->vdev->priv;
......@@ -169,6 +249,20 @@ static void req_done(struct virtqueue *vq)
spin_unlock_irqrestore(&chan->lock, flags);
}
/**
* pack_sg_list - pack a scatter gather list from a linear buffer
* @sg: scatter/gather list to pack into
* @start: which segment of the sg_list to start at
* @limit: maximum segment to pack data to
* @data: data to pack into scatter/gather list
* @count: amount of data to pack into the scatter/gather list
*
* sg_lists have multiple segments of various sizes. This will pack
* arbitrary data into an existing scatter gather list, segmenting the
* data as necessary within constraints.
*
*/
static int
pack_sg_list(struct scatterlist *sg, int start, int limit, char *data,
int count)
......@@ -189,6 +283,14 @@ pack_sg_list(struct scatterlist *sg, int start, int limit, char *data,
return index-start;
}
/**
* p9_virtio_rpc - issue a request and wait for a response
* @t: transport state
* @tc: &p9_fcall request to transmit
* @rc: &p9_fcall to put reponse into
*
*/
static int
p9_virtio_rpc(struct p9_trans *t, struct p9_fcall *tc, struct p9_fcall **rc)
{
......@@ -263,16 +365,26 @@ p9_virtio_rpc(struct p9_trans *t, struct p9_fcall *tc, struct p9_fcall **rc)
return 0;
}
/**
* p9_virtio_probe - probe for existence of 9P virtio channels
* @vdev: virtio device to probe
*
* This probes for existing virtio channels. At present only
* a single channel is in use, so in the future more work may need
* to be done here.
*
*/
static int p9_virtio_probe(struct virtio_device *vdev)
{
int err;
struct virtio_chan *chan;
int index;
down(&virtio_9p_lock);
mutex_lock(&virtio_9p_lock);
index = chan_index++;
chan = &channels[index];
up(&virtio_9p_lock);
mutex_unlock(&virtio_9p_lock);
if (chan_index > MAX_9P_CHAN) {
printk(KERN_ERR "9p: virtio: Maximum channels exceeded\n");
......@@ -301,17 +413,34 @@ static int p9_virtio_probe(struct virtio_device *vdev)
out_free_vq:
vdev->config->del_vq(chan->vq);
fail:
down(&virtio_9p_lock);
mutex_lock(&virtio_9p_lock);
chan_index--;
up(&virtio_9p_lock);
mutex_unlock(&virtio_9p_lock);
return err;
}
/* This sets up a transport channel for 9p communication. Right now
/**
* p9_virtio_create - allocate a new virtio channel
* @devname: string identifying the channel to connect to (unused)
* @args: args passed from sys_mount() for per-transport options (unused)
* @msize: requested maximum packet size
* @extended: 9p2000.u enabled flag
*
* This sets up a transport channel for 9p communication. Right now
* we only match the first available channel, but eventually we couldlook up
* alternate channels by matching devname versus a virtio_config entry.
* We use a simple reference count mechanism to ensure that only a single
* mount has a channel open at a time. */
* mount has a channel open at a time.
*
* Bugs: doesn't allow identification of a specific channel
* to allocate, channels are allocated sequentially. This was
* a pragmatic decision to get things rolling, but ideally some
* way of identifying the channel to attach to would be nice
* if we are going to support multiple channels.
*
*/
static struct p9_trans *
p9_virtio_create(const char *devname, char *args, int msize,
unsigned char extended)
......@@ -320,7 +449,7 @@ p9_virtio_create(const char *devname, char *args, int msize,
struct virtio_chan *chan = channels;
int index = 0;
down(&virtio_9p_lock);
mutex_lock(&virtio_9p_lock);
while (index < MAX_9P_CHAN) {
if (chan->initialized && !chan->inuse) {
chan->inuse = true;
......@@ -330,7 +459,7 @@ p9_virtio_create(const char *devname, char *args, int msize,
chan = &channels[index];
}
}
up(&virtio_9p_lock);
mutex_unlock(&virtio_9p_lock);
if (index >= MAX_9P_CHAN) {
printk(KERN_ERR "9p: no channels available\n");
......@@ -360,6 +489,12 @@ p9_virtio_create(const char *devname, char *args, int msize,
return trans;
}
/**
* p9_virtio_remove - clean up resources associated with a virtio device
* @vdev: virtio device to remove
*
*/
static void p9_virtio_remove(struct virtio_device *vdev)
{
struct virtio_chan *chan = vdev->priv;
......
......@@ -32,11 +32,23 @@
#include <linux/idr.h>
#include <net/9p/9p.h>
/**
* struct p9_idpool - per-connection accounting for tag idpool
* @lock: protects the pool
* @pool: idr to allocate tag id from
*
*/
struct p9_idpool {
spinlock_t lock;
struct idr pool;
};
/**
* p9_idpool_create - create a new per-connection id pool
*
*/
struct p9_idpool *p9_idpool_create(void)
{
struct p9_idpool *p;
......@@ -52,6 +64,11 @@ struct p9_idpool *p9_idpool_create(void)
}
EXPORT_SYMBOL(p9_idpool_create);
/**
* p9_idpool_destroy - create a new per-connection id pool
* @p: idpool to destory
*/
void p9_idpool_destroy(struct p9_idpool *p)
{
idr_destroy(&p->pool);
......@@ -61,9 +78,9 @@ EXPORT_SYMBOL(p9_idpool_destroy);
/**
* p9_idpool_get - allocate numeric id from pool
* @p - pool to allocate from
* @p: pool to allocate from
*
* XXX - This seems to be an awful generic function, should it be in idr.c with
* Bugs: This seems to be an awful generic function, should it be in idr.c with
* the lock included in struct idr?
*/
......@@ -71,7 +88,7 @@ int p9_idpool_get(struct p9_idpool *p)
{
int i = 0;
int error;
unsigned int flags;
unsigned long flags;
retry:
if (idr_pre_get(&p->pool, GFP_KERNEL) == 0)
......@@ -94,15 +111,16 @@ EXPORT_SYMBOL(p9_idpool_get);
/**
* p9_idpool_put - release numeric id from pool
* @p - pool to allocate from
* @id: numeric id which is being released
* @p: pool to release id into
*
* XXX - This seems to be an awful generic function, should it be in idr.c with
* Bugs: This seems to be an awful generic function, should it be in idr.c with
* the lock included in struct idr?
*/
void p9_idpool_put(int id, struct p9_idpool *p)
{
unsigned int flags;
unsigned long flags;
spin_lock_irqsave(&p->lock, flags);
idr_remove(&p->pool, id);
spin_unlock_irqrestore(&p->lock, flags);
......@@ -111,11 +129,13 @@ EXPORT_SYMBOL(p9_idpool_put);
/**
* p9_idpool_check - check if the specified id is available
* @id - id to check
* @p - pool
* @id: id to check
* @p: pool to check
*/
int p9_idpool_check(int id, struct p9_idpool *p)
{
return idr_find(&p->pool, id) != NULL;
}
EXPORT_SYMBOL(p9_idpool_check);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment