9P loopback transport that can be used between 9P in-kernel servers and v9fs on the same machine. Signed-off-by: Latchesar Ionkov <lucho@xxxxxxxxxx> --- net/9p/Kconfig | 7 + net/9p/Makefile | 4 + net/9p/trans_loop.c | 371 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 382 insertions(+), 0 deletions(-) diff --git a/net/9p/Kconfig b/net/9p/Kconfig index 979c781..ae341f0 100644 --- a/net/9p/Kconfig +++ b/net/9p/Kconfig @@ -23,6 +23,13 @@ config NET_9P_FD 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_LOOP + depends on NET_9P + default y if NET_9P + tristate "9P Loopback Transport (Experimental)" + help + Loopback transport. + config NET_9P_DEBUG bool "Debug information" depends on NET_9P diff --git a/net/9p/Makefile b/net/9p/Makefile index d143e4a..da05d35 100644 --- a/net/9p/Makefile +++ b/net/9p/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_NET_9P) := 9pnet.o obj-$(CONFIG_NET_9P_FD) += 9pnet_fd.o +obj-$(CONFIG_NET_9P_LOOP) += 9pnet_loop.o obj-$(CONFIG_NET_9P_SRV) += 9psrv.o 9pnet-objs := \ @@ -10,6 +11,9 @@ obj-$(CONFIG_NET_9P_SRV) += 9psrv.o fcprint.o \ util.o \ +9pnet_loop-objs := \ + trans_loop.o \ + 9psrv-objs := \ srv.o \ diff --git a/net/9p/trans_loop.c b/net/9p/trans_loop.c new file mode 100644 index 0000000..7e7793b --- /dev/null +++ b/net/9p/trans_loop.c @@ -0,0 +1,371 @@ +/* + * linux/fs/9p/trans_fd.c + * + * Loopback transport. Used for testing. + * + * Copyright (C) 2007 by Latchesar Ionkov <lucho@xxxxxxxxxx> + * + * 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/in.h> +#include <linux/module.h> +#include <linux/net.h> +#include <linux/ipv6.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/un.h> +#include <linux/uaccess.h> +#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> + +struct p9_trans_loop { + spinlock_t lock; + char *name; + struct p9_trans *ctrans; + struct p9_trans *strans; + struct p9_trans_listener listener; + struct list_head trans_list; +}; + +struct p9_loop_opts { + char *name; +}; + +static struct p9_trans *p9lo_clt_create(const char *, char *); +static void p9lo_clt_request(struct p9_trans *, struct p9_trans_req *); +static int p9lo_clt_cancel(struct p9_trans *, struct p9_trans_req *); +static void p9lo_clt_destroy(struct p9_trans *); +static int p9lo_srv_create(struct p9_trans_loop *lt); +static void p9lo_srv_destroy(struct p9_trans *); +static int p9lo_srv_cancel(struct p9_trans *, struct p9_trans_req *); +static void p9lo_srv_respond(struct p9_trans *, struct p9_trans_req *); +static struct p9_trans_listener *p9lo_listener_create(char *); +static void p9lo_trans_listener_destroy(struct p9_trans_listener *); + +static struct p9_trans_module p9lo_trans_mod = { + .name = "loop", + .maxsize = 65536, + .def = 0, + .create_client = p9lo_clt_create, + .create_listener = p9lo_listener_create, +}; + +enum { + Opt_name, Opt_err, +}; + +static match_table_t tokens = { + {Opt_name, "name=%s"}, + {Opt_err, NULL}, +}; + +static DEFINE_MUTEX(p9lo_lock); +static LIST_HEAD(p9lo_trans_list); + +static void parse_opts(char *options, struct p9_loop_opts *opts) +{ + int token; + char *p; + substring_t args[MAX_OPT_ARGS]; + + opts->name = NULL; + if (!options) + return; + + while ((p = strsep(&options, ",")) != NULL) { + if (*p == '\0') + continue; + + token = match_token(p, tokens, args); + switch (token) { + case Opt_name: + opts->name = match_strdup(&args[0]); + break; + default: + continue; + } + } +} + +static struct p9_trans_loop *p9lo_trans_find(char *name) +{ + struct p9_trans_loop *lo; + + mutex_lock(&p9lo_lock); + list_for_each_entry(lo, &p9lo_trans_list, trans_list) { + if (!strcmp(name, lo->name)) { + mutex_unlock(&p9lo_lock); + return lo; + } + } + mutex_unlock(&p9lo_lock); + return ERR_PTR(-ENOENT); +} + +static struct p9_trans_loop *p9lo_trans_create(char *name) +{ + struct p9_trans_loop *lo; + + mutex_lock(&p9lo_lock); + list_for_each_entry(lo, &p9lo_trans_list, trans_list) { + if (!strcmp(name, lo->name)) { + mutex_unlock(&p9lo_lock); + return ERR_PTR(-EEXIST); + } + } + + lo = kmalloc(sizeof(*lo) + strlen(name) + 1, GFP_KERNEL); + spin_lock_init(&lo->lock); + lo->name = (char *) lo + sizeof(*lo); + strcpy(lo->name, name); + lo->ctrans = NULL; + lo->strans = NULL; + lo->listener.err = 0; + lo->listener.aux = NULL; + lo->listener.taux = lo; + lo->listener.newtrans = NULL; + lo->listener.destroy = p9lo_trans_listener_destroy; + list_add_tail(&lo->trans_list, &p9lo_trans_list); + mutex_unlock(&p9lo_lock); + + P9_DPRINTK(P9_DEBUG_TRANS, "trans %p\n", lo); + return lo; +} + +static void p9lo_trans_destroy(struct p9_trans_loop *lo) +{ + if (lo->ctrans || lo->strans || !list_empty(&lo->trans_list)) + return; + + kfree(lo); +} + +static struct p9_trans *p9lo_clt_create(const char *devname, char *options) +{ + int err; + struct p9_trans *ctrans; + struct p9_trans_loop *lt; + struct p9_loop_opts opts; + + parse_opts(options, &opts); + if (!opts.name) + return ERR_PTR(-ENOENT); + + lt = p9lo_trans_find(opts.name); + if (IS_ERR(lt)) + return (struct p9_trans *) lt; + + ctrans = kmalloc(sizeof(*ctrans), GFP_KERNEL); + if (!ctrans) + return ERR_PTR(-ENOMEM); + + ctrans->err = 0; + ctrans->priv = lt; + ctrans->request = p9lo_clt_request; + ctrans->cancel = p9lo_clt_cancel; + ctrans->destroy = p9lo_clt_destroy; + + spin_lock(<->lock); + if (lt->ctrans || lt->strans) { + spin_unlock(<->lock); + kfree(ctrans); + return ERR_PTR(-EEXIST); + } + + lt->ctrans = ctrans; + spin_unlock(<->lock); + + err = p9lo_srv_create(lt); + if (err) { + spin_lock(<->lock); + lt->ctrans = NULL; + spin_unlock(<->lock); + kfree(ctrans); + return ERR_PTR(err); + } + + P9_DPRINTK(P9_DEBUG_TRANS, "trans %p ctrans %p\n", ctrans->priv, + ctrans); + + return ctrans; +} + +static void p9lo_clt_destroy(struct p9_trans *ctrans) +{ + struct p9_trans_loop *lt; + + lt = ctrans->priv; + spin_lock(<->lock); + lt->ctrans = NULL; + if (lt->strans) + lt->strans->err = -EIO; + spin_unlock(<->lock); + + if (lt->strans) + (*lt->strans->request)(lt->strans, NULL); + + p9lo_trans_destroy(lt); +} + +static void p9lo_clt_request(struct p9_trans *trans, struct p9_trans_req *creq) +{ + struct p9_trans_req *sreq; + struct p9_trans_loop *lt; + + lt = trans->priv; + P9_DPRINTK(P9_DEBUG_TRANS, "trans %p ctrans %p\n", lt, trans); + sreq = kmalloc(sizeof(*sreq), GFP_KERNEL); + sreq->tag = creq->tag; + sreq->err = 0; + sreq->tc = creq->tc; + sreq->rc = creq->rc; + sreq->cb = p9lo_srv_respond; + sreq->cba = NULL; + sreq->aux = creq; + INIT_LIST_HEAD(&sreq->req_list); + + spin_lock(<->lock); + if (!lt->strans) { + lt->strans->err = -EIO; + spin_unlock(<->lock); + return; + } + spin_unlock(<->lock); + + (*lt->strans->request)(lt->strans, sreq); +} + +static int p9lo_clt_cancel(struct p9_trans *trans, struct p9_trans_req *req) +{ + return 0; +} + +static int p9lo_srv_create(struct p9_trans_loop *lt) +{ + struct p9_trans *strans; + + strans = kmalloc(sizeof(*strans), GFP_KERNEL); + if (!strans) + return -ENOMEM; + + strans->err = 0; + strans->priv = lt; + strans->request = NULL; + strans->cancel = p9lo_srv_cancel; + strans->destroy = p9lo_srv_destroy; + + spin_lock(<->lock); + if (lt->strans) { + spin_unlock(<->lock); + kfree(strans); + return -EEXIST; + } + + lt->strans = strans; + spin_unlock(<->lock); + + P9_DPRINTK(P9_DEBUG_TRANS, "trans %p strans %p\n", strans->priv, + strans); + + (*lt->listener.newtrans)(<->listener, strans); + return 0; +} + +static void p9lo_srv_destroy(struct p9_trans *strans) +{ + struct p9_trans_loop *trans; + + trans = strans->priv; + spin_lock(&trans->lock); + trans->strans = NULL; + if (trans->ctrans) + trans->ctrans->err = -EIO; + spin_unlock(&trans->lock); +} + +static int p9lo_srv_cancel(struct p9_trans *trans, struct p9_trans_req *req) +{ + return 0; +} + +static void p9lo_srv_respond(struct p9_trans *trans, struct p9_trans_req *sreq) +{ + struct p9_trans_loop *t; + struct p9_trans_req *creq; + + t = trans->priv; + creq = sreq->aux; + + if (sreq->rc != creq->rc) { + kfree(creq->rc); + creq->rc = sreq->rc; + } + + kfree(sreq); + P9_DPRINTK(P9_DEBUG_TRANS, "id %d size %d\n", creq->rc->id, creq->rc->size); + (*creq->cb)(t->ctrans, creq); +} + +static struct p9_trans_listener *p9lo_listener_create(char *options) +{ + struct p9_trans_loop *lt; + struct p9_loop_opts opts; + + parse_opts(options, &opts); + if (!opts.name) + return ERR_PTR(-ENOENT); + + lt = p9lo_trans_create(opts.name); + if (IS_ERR(lt)) + return (struct p9_trans_listener *) lt; + + return <->listener; +} + +static void p9lo_trans_listener_destroy(struct p9_trans_listener *lis) +{ + struct p9_trans_loop *lt; + + lt = lis->taux; + mutex_lock(&p9lo_lock); + list_del(<->trans_list); + mutex_unlock(&p9lo_lock); + p9lo_trans_destroy(lt); +} + +static int __init p9lo_init(void) +{ + v9fs_register_trans(&p9lo_trans_mod); + + return 1; +} + +static void __exit p9lo_exit(void) { + printk(KERN_ERR "Removal of 9p transports not implemented\n"); + BUG(); +} + +module_init(p9lo_init); +module_exit(p9lo_exit); + +MODULE_AUTHOR("Latchesar Ionkov <lucho@xxxxxxxxxx>"); +MODULE_LICENSE("GPL"); - To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html