diff options
author | Eric Van Hensbergen <ericvh@opteron.(none)> | 2007-10-17 14:31:07 -0500 |
---|---|---|
committer | Eric Van Hensbergen <ericvh@ericvh-desktop.austin.ibm.com> | 2007-10-17 14:31:07 -0500 |
commit | a80d923e1321a7ed69a0918de37e39871bb536a0 (patch) | |
tree | 8294e5f14a0e938ae4675ef912a32fbade0f832b /net/9p/trans_fd.c | |
parent | 0eafaae84e21ac033815cc9f33c3ae889cd7ccfe (diff) | |
download | linux-a80d923e1321a7ed69a0918de37e39871bb536a0.tar.gz linux-a80d923e1321a7ed69a0918de37e39871bb536a0.tar.bz2 linux-a80d923e1321a7ed69a0918de37e39871bb536a0.zip |
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 <ericvh@gmail.com>
Diffstat (limited to 'net/9p/trans_fd.c')
-rw-r--r-- | net/9p/trans_fd.c | 419 |
1 files changed, 264 insertions, 155 deletions
diff --git a/net/9p/trans_fd.c b/net/9p/trans_fd.c index fd636e94358f..30269a4ff22a 100644 --- a/net/9p/trans_fd.c +++ b/net/9p/trans_fd.c @@ -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,160 +36,114 @@ #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); - -struct p9_transport *p9_trans_create_tcp(const char *addr, int port) -{ - int err; - struct p9_transport *trans; - struct socket *csocket; - struct sockaddr_in sin_server; - - 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; - - 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; - } - - 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; +/* + * Option Parsing (code inspired by NFS code) + * - a little lazy - parse all fd-transport options + */ - return trans; +enum { + /* Options that take integer arguments */ + Opt_port, Opt_rfdno, Opt_wfdno, +}; -error: - if (csocket) - sock_release(csocket); +static match_table_t tokens = { + {Opt_port, "port=%u"}, + {Opt_rfdno, "rfdno=%u"}, + {Opt_wfdno, "wfdno=%u"}, +}; - kfree(trans); - return ERR_PTR(err); -} -EXPORT_SYMBOL(p9_trans_create_tcp); +/** + * v9fs_parse_options - parse mount options into session structure + * @options: options string passed from mount + * @v9ses: existing v9fs session information + * + */ -struct p9_transport *p9_trans_create_unix(const char *addr) +static void parse_opts(char *options, struct p9_fd_opts *opts) { - 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); + char *p; + substring_t args[MAX_OPT_ARGS]; + int option; + int ret; - trans->write = p9_fd_write; - trans->read = p9_fd_read; - trans->close = p9_fd_close; - trans->poll = p9_fd_poll; + opts->port = P9_PORT; + opts->rfd = ~0; + opts->wfd = ~0; - if (strlen(addr) > UNIX_PATH_MAX) { - P9_EPRINTK(KERN_ERR, "p9_trans_unix: address too long: %s\n", - addr); - err = -ENAMETOOLONG; - goto error; - } + if (!options) + return; - 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; + 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 = 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) +static int p9_fd_open(struct p9_trans *trans, int rfd, int wfd) { - int err; - struct p9_transport *trans; + struct p9_trans_fd *ts = kmalloc(sizeof(struct p9_trans_fd), + GFP_KERNEL); + if (!ts) + return -ENOMEM; - if (rfd == ~0 || wfd == ~0) { - printk(KERN_ERR "v9fs: Insufficient options for proto=fd\n"); - return ERR_PTR(-ENOPROTOOPT); + ts->rd = fget(rfd); + ts->wr = fget(wfd); + if (!ts->rd || !ts->wr) { + if (ts->rd) + fput(ts->rd); + if (ts->wr) + fput(ts->wr); + kfree(ts); + return -EIO; } - 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; + trans->priv = ts; + trans->status = Connected; -error: - kfree(trans); - return ERR_PTR(err); + return 0; } -EXPORT_SYMBOL(p9_trans_create_fd); -static int p9_socket_open(struct p9_transport *trans, struct socket *csocket) +static int p9_socket_open(struct p9_trans *trans, struct socket *csocket) { int fd, ret; @@ -212,30 +166,6 @@ static int p9_socket_open(struct p9_transport *trans, struct socket *csocket) return 0; } -static int p9_fd_open(struct p9_transport *trans, int rfd, int wfd) -{ - struct p9_trans_fd *ts = kmalloc(sizeof(struct p9_trans_fd), - GFP_KERNEL); - if (!ts) - return -ENOMEM; - - ts->rd = fget(rfd); - ts->wr = fget(wfd); - if (!ts->rd || !ts->wr) { - if (ts->rd) - fput(ts->rd); - if (ts->wr) - fput(ts->wr); - kfree(ts); - return -EIO; - } - - trans->priv = ts; - trans->status = Connected; - - 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 <lucho@ionkov.net>"); +MODULE_AUTHOR("Eric Van Hensbergen <ericvh@gmail.com>"); +MODULE_LICENSE("GPL"); |