9p: Make transports dynamic

This patch abstracts out the interfaces to underlying transports so that
new transports can be added as modules.  This should also allow kernel
configuration of transports without ifdef-hell.

Signed-off-by: Eric Van Hensbergen <[email protected]>
This commit is contained in:
Eric Van Hensbergen 2007-10-17 14:31:07 -05:00 • committed by Eric Van Hensbergen
commit a80d923e13
12 changed files with 379 additions and 285 deletions

View file

@ -13,6 +13,16 @@ 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_DEBUG
bool "Debug information"
depends on NET_9P

View file

@ -1,8 +1,8 @@
obj-$(CONFIG_NET_9P) := 9pnet.o
obj-$(CONFIG_NET_9P_FD) += 9pnet_fd.o
9pnet-objs := \
mod.o \
trans_fd.o \
mux.o \
client.o \
conv.o \
@ -11,3 +11,6 @@ obj-$(CONFIG_NET_9P) := 9pnet.o
util.o \
9pnet-$(CONFIG_SYSCTL) += sysctl.o
9pnet_fd-objs := \
trans_fd.o \

View file

@ -38,7 +38,7 @@ static struct p9_fid *p9_fid_create(struct p9_client *clnt);
static void p9_fid_destroy(struct p9_fid *fid);
static struct p9_stat *p9_clone_stat(struct p9_stat *st, int dotu);
struct p9_client *p9_client_create(struct p9_transport *trans, int msize,
struct p9_client *p9_client_create(struct p9_trans *trans, int msize,
int dotu)
{
int err, n;

View file

@ -71,7 +71,7 @@ struct p9_conn {
struct p9_mux_poll_task *poll_task;
int msize;
unsigned char *extended;
struct p9_transport *trans;
struct p9_trans *trans;
struct p9_idpool *tagpool;
int err;
wait_queue_head_t equeue;
@ -271,7 +271,7 @@ static void p9_mux_poll_stop(struct p9_conn *m)
* @msize - maximum message size
* @extended - pointer to the extended flag
*/
struct p9_conn *p9_conn_create(struct p9_transport *trans, int msize,
struct p9_conn *p9_conn_create(struct p9_trans *trans, int msize,
unsigned char *extended)
{
int i, n;

View file

@ -5,7 +5,7 @@
*
* Copyright (C) 2006 by Russ Cox <rsc@swtch.com>
* Copyright (C) 2004-2005 by Latchesar Ionkov <lucho@ionkov.net>
* Copyright (C) 2004-2005 by Eric Van Hensbergen <ericvh@gmail.com>
* Copyright (C) 2004-2007 by Eric Van Hensbergen <ericvh@gmail.com>
* Copyright (C) 1997-2002 by Ron Minnich <rminnich@sarnoff.com>
*
* This program is free software; you can redistribute it and/or modify
@ -36,183 +36,90 @@
#include <linux/inet.h>
#include <linux/idr.h>
#include <linux/file.h>
#include <linux/parser.h>
#include <net/9p/9p.h>
#include <net/9p/transport.h>
#define P9_PORT 564
#define MAX_SOCK_BUF (64*1024)
struct p9_fd_opts {
int rfd;
int wfd;
u16 port;
};
struct p9_trans_fd {
struct file *rd;
struct file *wr;
};
static int p9_socket_open(struct p9_transport *trans, struct socket *csocket);
static int p9_fd_open(struct p9_transport *trans, int rfd, int wfd);
static int p9_fd_read(struct p9_transport *trans, void *v, int len);
static int p9_fd_write(struct p9_transport *trans, void *v, int len);
static unsigned int p9_fd_poll(struct p9_transport *trans,
struct poll_table_struct *pt);
static void p9_fd_close(struct p9_transport *trans);
/*
* Option Parsing (code inspired by NFS code)
* - a little lazy - parse all fd-transport options
*/
struct p9_transport *p9_trans_create_tcp(const char *addr, int port)
enum {
/* Options that take integer arguments */
Opt_port, Opt_rfdno, Opt_wfdno,
};
static match_table_t tokens = {
{Opt_port, "port=%u"},
{Opt_rfdno, "rfdno=%u"},
{Opt_wfdno, "wfdno=%u"},
};
/**
* v9fs_parse_options - parse mount options into session structure
* @options: options string passed from mount
* @v9ses: existing v9fs session information
*
*/
static void parse_opts(char *options, struct p9_fd_opts *opts)
{
int err;
struct p9_transport *trans;
struct socket *csocket;
struct sockaddr_in sin_server;
char *p;
substring_t args[MAX_OPT_ARGS];
int option;
int ret;
csocket = NULL;
trans = kmalloc(sizeof(struct p9_transport), GFP_KERNEL);
if (!trans)
return ERR_PTR(-ENOMEM);
opts->port = P9_PORT;
opts->rfd = ~0;
opts->wfd = ~0;
trans->write = p9_fd_write;
trans->read = p9_fd_read;
trans->close = p9_fd_close;
trans->poll = p9_fd_poll;
if (!options)
return;
sin_server.sin_family = AF_INET;
sin_server.sin_addr.s_addr = in_aton(addr);
sin_server.sin_port = htons(port);
sock_create_kern(PF_INET, SOCK_STREAM, IPPROTO_TCP, &csocket);
if (!csocket) {
P9_EPRINTK(KERN_ERR, "p9_trans_tcp: problem creating socket\n");
err = -EIO;
goto error;
while ((p = strsep(&options, ",")) != NULL) {
int token;
if (!*p)
continue;
token = match_token(p, tokens, args);
ret = match_int(&args[0], &option);
if (ret < 0) {
P9_DPRINTK(P9_DEBUG_ERROR,
"integer field, but no integer?\n");
continue;
}
switch (token) {
case Opt_port:
opts->port = option;
break;
case Opt_rfdno:
opts->rfd = option;
break;
case Opt_wfdno:
opts->wfd = option;
break;
default:
continue;
}
}
err = csocket->ops->connect(csocket,
(struct sockaddr *)&sin_server,
sizeof(struct sockaddr_in), 0);
if (err < 0) {
P9_EPRINTK(KERN_ERR,
"p9_trans_tcp: problem connecting socket to %s\n",
addr);
goto error;
}
err = p9_socket_open(trans, csocket);
if (err < 0)
goto error;
return trans;
error:
if (csocket)
sock_release(csocket);
kfree(trans);
return ERR_PTR(err);
}
EXPORT_SYMBOL(p9_trans_create_tcp);
struct p9_transport *p9_trans_create_unix(const char *addr)
{
int err;
struct socket *csocket;
struct sockaddr_un sun_server;
struct p9_transport *trans;
csocket = NULL;
trans = kmalloc(sizeof(struct p9_transport), GFP_KERNEL);
if (!trans)
return ERR_PTR(-ENOMEM);
trans->write = p9_fd_write;
trans->read = p9_fd_read;
trans->close = p9_fd_close;
trans->poll = p9_fd_poll;
if (strlen(addr) > UNIX_PATH_MAX) {
P9_EPRINTK(KERN_ERR, "p9_trans_unix: address too long: %s\n",
addr);
err = -ENAMETOOLONG;
goto error;
}
sun_server.sun_family = PF_UNIX;
strcpy(sun_server.sun_path, addr);
sock_create_kern(PF_UNIX, SOCK_STREAM, 0, &csocket);
err = csocket->ops->connect(csocket, (struct sockaddr *)&sun_server,
sizeof(struct sockaddr_un) - 1, 0);
if (err < 0) {
P9_EPRINTK(KERN_ERR,
"p9_trans_unix: problem connecting socket: %s: %d\n",
addr, err);
goto error;
}
err = p9_socket_open(trans, csocket);
if (err < 0)
goto error;
return trans;
error:
if (csocket)
sock_release(csocket);
kfree(trans);
return ERR_PTR(err);
}
EXPORT_SYMBOL(p9_trans_create_unix);
struct p9_transport *p9_trans_create_fd(int rfd, int wfd)
{
int err;
struct p9_transport *trans;
if (rfd == ~0 || wfd == ~0) {
printk(KERN_ERR "v9fs: Insufficient options for proto=fd\n");
return ERR_PTR(-ENOPROTOOPT);
}
trans = kmalloc(sizeof(struct p9_transport), GFP_KERNEL);
if (!trans)
return ERR_PTR(-ENOMEM);
trans->write = p9_fd_write;
trans->read = p9_fd_read;
trans->close = p9_fd_close;
trans->poll = p9_fd_poll;
err = p9_fd_open(trans, rfd, wfd);
if (err < 0)
goto error;
return trans;
error:
kfree(trans);
return ERR_PTR(err);
}
EXPORT_SYMBOL(p9_trans_create_fd);
static int p9_socket_open(struct p9_transport *trans, struct socket *csocket)
{
int fd, ret;
csocket->sk->sk_allocation = GFP_NOIO;
fd = sock_map_fd(csocket);
if (fd < 0) {
P9_EPRINTK(KERN_ERR, "p9_socket_open: failed to map fd\n");
return fd;
}
ret = p9_fd_open(trans, fd, fd);
if (ret < 0) {
P9_EPRINTK(KERN_ERR, "p9_socket_open: failed to open fd\n");
sockfd_put(csocket);
return ret;
}
((struct p9_trans_fd *)trans->priv)->rd->f_flags |= O_NONBLOCK;
return 0;
}
static int p9_fd_open(struct p9_transport *trans, int rfd, int wfd)
static int p9_fd_open(struct p9_trans *trans, int rfd, int wfd)
{
struct p9_trans_fd *ts = kmalloc(sizeof(struct p9_trans_fd),
GFP_KERNEL);
@ -236,6 +143,29 @@ static int p9_fd_open(struct p9_transport *trans, int rfd, int wfd)
return 0;
}
static int p9_socket_open(struct p9_trans *trans, struct socket *csocket)
{
int fd, ret;
csocket->sk->sk_allocation = GFP_NOIO;
fd = sock_map_fd(csocket);
if (fd < 0) {
P9_EPRINTK(KERN_ERR, "p9_socket_open: failed to map fd\n");
return fd;
}
ret = p9_fd_open(trans, fd, fd);
if (ret < 0) {
P9_EPRINTK(KERN_ERR, "p9_socket_open: failed to open fd\n");
sockfd_put(csocket);
return ret;
}
((struct p9_trans_fd *)trans->priv)->rd->f_flags |= O_NONBLOCK;
return 0;
}
/**
* p9_fd_read- read from a fd
* @v9ses: session information
@ -243,7 +173,7 @@ static int p9_fd_open(struct p9_transport *trans, int rfd, int wfd)
* @len: size of receive buffer
*
*/
static int p9_fd_read(struct p9_transport *trans, void *v, int len)
static int p9_fd_read(struct p9_trans *trans, void *v, int len)
{
int ret;
struct p9_trans_fd *ts = NULL;
@ -270,7 +200,7 @@ static int p9_fd_read(struct p9_transport *trans, void *v, int len)
* @len: size of send buffer
*
*/
static int p9_fd_write(struct p9_transport *trans, void *v, int len)
static int p9_fd_write(struct p9_trans *trans, void *v, int len)
{
int ret;
mm_segment_t oldfs;
@ -297,7 +227,7 @@ static int p9_fd_write(struct p9_transport *trans, void *v, int len)
}
static unsigned int
p9_fd_poll(struct p9_transport *trans, struct poll_table_struct *pt)
p9_fd_poll(struct p9_trans *trans, struct poll_table_struct *pt)
{
int ret, n;
struct p9_trans_fd *ts = NULL;
@ -341,7 +271,7 @@ end:
* @trans: private socket structure
*
*/
static void p9_fd_close(struct p9_transport *trans)
static void p9_fd_close(struct p9_trans *trans)
{
struct p9_trans_fd *ts;
@ -361,3 +291,182 @@ static void p9_fd_close(struct p9_transport *trans)
kfree(ts);
}
static struct p9_trans *p9_trans_create_tcp(const char *addr, char *args)
{
int err;
struct p9_trans *trans;
struct socket *csocket;
struct sockaddr_in sin_server;
struct p9_fd_opts opts;
parse_opts(args, &opts);
csocket = NULL;
trans = kmalloc(sizeof(struct p9_trans), GFP_KERNEL);
if (!trans)
return ERR_PTR(-ENOMEM);
trans->write = p9_fd_write;
trans->read = p9_fd_read;
trans->close = p9_fd_close;
trans->poll = p9_fd_poll;
sin_server.sin_family = AF_INET;
sin_server.sin_addr.s_addr = in_aton(addr);
sin_server.sin_port = htons(opts.port);
sock_create_kern(PF_INET, SOCK_STREAM, IPPROTO_TCP, &csocket);
if (!csocket) {
P9_EPRINTK(KERN_ERR, "p9_trans_tcp: problem creating socket\n");
err = -EIO;
goto error;
}
err = csocket->ops->connect(csocket,
(struct sockaddr *)&sin_server,
sizeof(struct sockaddr_in), 0);
if (err < 0) {
P9_EPRINTK(KERN_ERR,
"p9_trans_tcp: problem connecting socket to %s\n",
addr);
goto error;
}
err = p9_socket_open(trans, csocket);
if (err < 0)
goto error;
return trans;
error:
if (csocket)
sock_release(csocket);
kfree(trans);
return ERR_PTR(err);
}
static struct p9_trans *p9_trans_create_unix(const char *addr, char *args)
{
int err;
struct socket *csocket;
struct sockaddr_un sun_server;
struct p9_trans *trans;
csocket = NULL;
trans = kmalloc(sizeof(struct p9_trans), GFP_KERNEL);
if (!trans)
return ERR_PTR(-ENOMEM);
trans->write = p9_fd_write;
trans->read = p9_fd_read;
trans->close = p9_fd_close;
trans->poll = p9_fd_poll;
if (strlen(addr) > UNIX_PATH_MAX) {
P9_EPRINTK(KERN_ERR, "p9_trans_unix: address too long: %s\n",
addr);
err = -ENAMETOOLONG;
goto error;
}
sun_server.sun_family = PF_UNIX;
strcpy(sun_server.sun_path, addr);
sock_create_kern(PF_UNIX, SOCK_STREAM, 0, &csocket);
err = csocket->ops->connect(csocket, (struct sockaddr *)&sun_server,
sizeof(struct sockaddr_un) - 1, 0);
if (err < 0) {
P9_EPRINTK(KERN_ERR,
"p9_trans_unix: problem connecting socket: %s: %d\n",
addr, err);
goto error;
}
err = p9_socket_open(trans, csocket);
if (err < 0)
goto error;
return trans;
error:
if (csocket)
sock_release(csocket);
kfree(trans);
return ERR_PTR(err);
}
static struct p9_trans *p9_trans_create_fd(const char *name, char *args)
{
int err;
struct p9_trans *trans;
struct p9_fd_opts opts;
parse_opts(args, &opts);
if (opts.rfd == ~0 || opts.wfd == ~0) {
printk(KERN_ERR "v9fs: Insufficient options for proto=fd\n");
return ERR_PTR(-ENOPROTOOPT);
}
trans = kmalloc(sizeof(struct p9_trans), GFP_KERNEL);
if (!trans)
return ERR_PTR(-ENOMEM);
trans->write = p9_fd_write;
trans->read = p9_fd_read;
trans->close = p9_fd_close;
trans->poll = p9_fd_poll;
err = p9_fd_open(trans, opts.rfd, opts.wfd);
if (err < 0)
goto error;
return trans;
error:
kfree(trans);
return ERR_PTR(err);
}
static struct p9_trans_module p9_tcp_trans = {
.name = "tcp",
.maxsize = MAX_SOCK_BUF,
.def = 1,
.create = p9_trans_create_tcp,
};
static struct p9_trans_module p9_unix_trans = {
.name = "unix",
.maxsize = MAX_SOCK_BUF,
.def = 0,
.create = p9_trans_create_unix,
};
static struct p9_trans_module p9_fd_trans = {
.name = "fd",
.maxsize = MAX_SOCK_BUF,
.def = 0,
.create = p9_trans_create_fd,
};
static int __init p9_trans_fd_init(void)
{
v9fs_register_trans(&p9_tcp_trans);
v9fs_register_trans(&p9_unix_trans);
v9fs_register_trans(&p9_fd_trans);
return 1;
}
static void __exit p9_trans_fd_exit(void) {
printk(KERN_ERR "Removal of 9p transports not implemented\n");
BUG();
}
module_init(p9_trans_fd_init);
module_exit(p9_trans_fd_exit);
MODULE_AUTHOR("Latchesar Ionkov <[email protected]>");
MODULE_AUTHOR("Eric Van Hensbergen <[email protected]>");
MODULE_LICENSE("GPL");