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);
 }