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>

diff --git a/fs/9p/v9fs.c b/fs/9p/v9fs.c
index 0a7068e..08d880f 100644
--- a/fs/9p/v9fs.c
+++ b/fs/9p/v9fs.c
@@ -37,18 +37,58 @@
 #include "v9fs_vfs.h"
 
 /*
+ * Dynamic Transport Registration Routines
+ *
+ */
+
+static LIST_HEAD(v9fs_trans_list);
+static struct p9_trans_module *v9fs_default_trans;
+
+/**
+ * v9fs_register_trans - register a new transport with 9p
+ * @m - structure describing the transport module and entry points
+ *
+ */
+void v9fs_register_trans(struct p9_trans_module *m)
+{
+	list_add_tail(&m->list, &v9fs_trans_list);
+	if (m->def)
+		v9fs_default_trans = m;
+}
+EXPORT_SYMBOL(v9fs_register_trans);
+
+/**
+ * v9fs_match_trans - match transport versus registered transports
+ * @arg: string identifying transport
+ *
+ */
+static struct p9_trans_module *v9fs_match_trans(const substring_t *name)
+{
+	struct list_head *p;
+	struct p9_trans_module *t = NULL;
+
+	list_for_each(p, &v9fs_trans_list) {
+		t = list_entry(p, struct p9_trans_module, list);
+		if (strncmp(t->name, name->from, name->to-name->from) == 0) {
+			P9_DPRINTK(P9_DEBUG_TRANS, "trans=%s\n", t->name);
+			break;
+		}
+	}
+	return t;
+}
+
+/*
   * Option Parsing (code inspired by NFS code)
-  *
+  *  NOTE: each transport will parse its own options
   */
 
 enum {
 	/* Options that take integer arguments */
-	Opt_debug, Opt_port, Opt_msize, Opt_uid, Opt_gid, Opt_afid,
-	Opt_rfdno, Opt_wfdno,
+	Opt_debug, Opt_msize, Opt_uid, Opt_gid, Opt_afid,
 	/* String options */
-	Opt_uname, Opt_remotename,
+	Opt_uname, Opt_remotename, Opt_trans,
 	/* Options that take no arguments */
-	Opt_legacy, Opt_nodevmap, Opt_unix, Opt_tcp, Opt_fd, Opt_pci,
+	Opt_legacy, Opt_nodevmap,
 	/* Cache options */
 	Opt_cache_loose,
 	/* Error token */
@@ -57,24 +97,13 @@
 
 static match_table_t tokens = {
 	{Opt_debug, "debug=%x"},
-	{Opt_port, "port=%u"},
 	{Opt_msize, "msize=%u"},
 	{Opt_uid, "uid=%u"},
 	{Opt_gid, "gid=%u"},
 	{Opt_afid, "afid=%u"},
-	{Opt_rfdno, "rfdno=%u"},
-	{Opt_wfdno, "wfdno=%u"},
 	{Opt_uname, "uname=%s"},
 	{Opt_remotename, "aname=%s"},
-	{Opt_unix, "proto=unix"},
-	{Opt_tcp, "proto=tcp"},
-	{Opt_fd, "proto=fd"},
-#ifdef CONFIG_PCI_9P
-	{Opt_pci, "proto=pci"},
-#endif
-	{Opt_tcp, "tcp"},
-	{Opt_unix, "unix"},
-	{Opt_fd, "fd"},
+	{Opt_trans, "trans=%s"},
 	{Opt_legacy, "noextend"},
 	{Opt_nodevmap, "nodevmap"},
 	{Opt_cache_loose, "cache=loose"},
@@ -82,12 +111,6 @@
 	{Opt_err, NULL}
 };
 
-extern struct p9_transport *p9pci_trans_create(void);
-
-/*
- *  Parse option string.
- */
-
 /**
  * v9fs_parse_options - parse mount options into session structure
  * @options: options string passed from mount
@@ -95,23 +118,21 @@
  *
  */
 
-static void v9fs_parse_options(char *options, struct v9fs_session_info *v9ses)
+static void v9fs_parse_options(struct v9fs_session_info *v9ses)
 {
-	char *p;
+	char *options = v9ses->options;
 	substring_t args[MAX_OPT_ARGS];
+	char *p;
 	int option;
 	int ret;
 
 	/* setup defaults */
-	v9ses->port = V9FS_PORT;
-	v9ses->maxdata = 9000;
-	v9ses->proto = PROTO_TCP;
+	v9ses->maxdata = 8192;
 	v9ses->extended = 1;
 	v9ses->afid = ~0;
 	v9ses->debug = 0;
-	v9ses->rfdno = ~0;
-	v9ses->wfdno = ~0;
 	v9ses->cache = 0;
+	v9ses->trans = v9fs_default_trans;
 
 	if (!options)
 		return;
@@ -135,9 +156,6 @@
 			p9_debug_level = option;
 #endif
 			break;
-		case Opt_port:
-			v9ses->port = option;
-			break;
 		case Opt_msize:
 			v9ses->maxdata = option;
 			break;
@@ -150,23 +168,8 @@
 		case Opt_afid:
 			v9ses->afid = option;
 			break;
-		case Opt_rfdno:
-			v9ses->rfdno = option;
-			break;
-		case Opt_wfdno:
-			v9ses->wfdno = option;
-			break;
-		case Opt_tcp:
-			v9ses->proto = PROTO_TCP;
-			break;
-		case Opt_unix:
-			v9ses->proto = PROTO_UNIX;
-			break;
-		case Opt_pci:
-			v9ses->proto = PROTO_PCI;
-			break;
-		case Opt_fd:
-			v9ses->proto = PROTO_FD;
+		case Opt_trans:
+			v9ses->trans = v9fs_match_trans(&args[0]);
 			break;
 		case Opt_uname:
 			match_strcpy(v9ses->name, &args[0]);
@@ -201,7 +204,7 @@
 		  const char *dev_name, char *data)
 {
 	int retval = -EINVAL;
-	struct p9_transport *trans;
+	struct p9_trans *trans = NULL;
 	struct p9_fid *fid;
 
 	v9ses->name = __getname();
@@ -217,39 +220,30 @@
 	strcpy(v9ses->name, V9FS_DEFUSER);
 	strcpy(v9ses->remotename, V9FS_DEFANAME);
 
-	v9fs_parse_options(data, v9ses);
+	v9ses->options = kstrdup(data, GFP_KERNEL);
+	v9fs_parse_options(v9ses);
 
-	switch (v9ses->proto) {
-	case PROTO_TCP:
-		trans = p9_trans_create_tcp(dev_name, v9ses->port);
-		break;
-	case PROTO_UNIX:
-		trans = p9_trans_create_unix(dev_name);
-		*v9ses->remotename = 0;
-		break;
-	case PROTO_FD:
-		trans = p9_trans_create_fd(v9ses->rfdno, v9ses->wfdno);
-		*v9ses->remotename = 0;
-		break;
-#ifdef CONFIG_PCI_9P
-	case PROTO_PCI:
-		trans = p9pci_trans_create();
-		*v9ses->remotename = 0;
-		break;
-#endif
-	default:
-		printk(KERN_ERR "v9fs: Bad mount protocol %d\n", v9ses->proto);
-		retval = -ENOPROTOOPT;
+	if ((v9ses->trans == NULL) && !list_empty(&v9fs_trans_list))
+		v9ses->trans = list_first_entry(&v9fs_trans_list,
+		 struct p9_trans_module, list);
+
+	if (v9ses->trans == NULL) {
+		retval = -EPROTONOSUPPORT;
+		P9_DPRINTK(P9_DEBUG_ERROR,
+				"No transport defined or default transport\n");
 		goto error;
-	};
+	}
 
+	trans = v9ses->trans->create(dev_name, v9ses->options);
 	if (IS_ERR(trans)) {
 		retval = PTR_ERR(trans);
 		trans = NULL;
 		goto error;
 	}
+	if ((v9ses->maxdata+P9_IOHDRSZ) > v9ses->trans->maxsize)
+		v9ses->maxdata = v9ses->trans->maxsize-P9_IOHDRSZ;
 
-	v9ses->clnt = p9_client_create(trans, v9ses->maxdata + P9_IOHDRSZ,
+	v9ses->clnt = p9_client_create(trans, v9ses->maxdata+P9_IOHDRSZ,
 		v9ses->extended);
 
 	if (IS_ERR(v9ses->clnt)) {
@@ -290,6 +284,7 @@
 
 	__putname(v9ses->name);
 	__putname(v9ses->remotename);
+	kfree(v9ses->options);
 }
 
 /**
@@ -311,7 +306,7 @@
 static int __init init_v9fs(void)
 {
 	printk(KERN_INFO "Installing v9fs 9p2000 file system support\n");
-
+	/* TODO: Setup list of registered trasnport modules */
 	return register_filesystem(&v9fs_fs_type);
 }
 
diff --git a/fs/9p/v9fs.h b/fs/9p/v9fs.h
index abc4b16..7eb135c 100644
--- a/fs/9p/v9fs.h
+++ b/fs/9p/v9fs.h
@@ -31,31 +31,20 @@
 	unsigned int maxdata;
 	unsigned char extended;	/* set to 1 if we are using UNIX extensions */
 	unsigned char nodev;	/* set to 1 if no disable device mapping */
-	unsigned short port;	/* port to connect to */
 	unsigned short debug;	/* debug level */
-	unsigned short proto;	/* protocol to use */
 	unsigned int afid;	/* authentication fid */
-	unsigned int rfdno;	/* read file descriptor number */
-	unsigned int wfdno;	/* write file descriptor number */
 	unsigned int cache;	/* cache mode */
 
+	char *options;		/* copy of mount options */
 	char *name;		/* user name to mount as */
 	char *remotename;	/* name of remote hierarchy being mounted */
 	unsigned int uid;	/* default uid/muid for legacy support */
 	unsigned int gid;	/* default gid for legacy support */
-
+	struct p9_trans_module *trans; /* 9p transport */
 	struct p9_client *clnt;	/* 9p client */
 	struct dentry *debugfs_dir;
 };
 
-/* possible values of ->proto */
-enum {
-	PROTO_TCP,
-	PROTO_UNIX,
-	PROTO_FD,
-	PROTO_PCI,
-};
-
 /* possible values of ->cache */
 /* eventually support loose, tight, time, session, default always none */
 enum {
diff --git a/fs/9p/vfs_super.c b/fs/9p/vfs_super.c
index ba90437..bb0cef9 100644
--- a/fs/9p/vfs_super.c
+++ b/fs/9p/vfs_super.c
@@ -216,24 +216,7 @@
 {
 	struct v9fs_session_info *v9ses = mnt->mnt_sb->s_fs_info;
 
-	if (v9ses->debug != 0)
-		seq_printf(m, ",debug=%x", v9ses->debug);
-	if (v9ses->port != V9FS_PORT)
-		seq_printf(m, ",port=%u", v9ses->port);
-	if (v9ses->maxdata != 9000)
-		seq_printf(m, ",msize=%u", v9ses->maxdata);
-	if (v9ses->afid != ~0)
-		seq_printf(m, ",afid=%u", v9ses->afid);
-	if (v9ses->proto == PROTO_UNIX)
-		seq_puts(m, ",proto=unix");
-	if (v9ses->extended == 0)
-		seq_puts(m, ",noextend");
-	if (v9ses->nodev == 1)
-		seq_puts(m, ",nodevmap");
-	seq_printf(m, ",name=%s", v9ses->name);
-	seq_printf(m, ",aname=%s", v9ses->remotename);
-	seq_printf(m, ",uid=%u", v9ses->uid);
-	seq_printf(m, ",gid=%u", v9ses->gid);
+	seq_printf(m, "%s", v9ses->options);
 	return 0;
 }