From: NeilBrown <neilb@suse.de>
Date: Mon, 8 Jul 2013 09:56:04 +1000
Subject: [PATCH] NFS: support "nosharetransport" option
Patch-mainline: upstream doesn't want this.
References: bnc#807502, bnc#828192, FATE#315593

This patch adds a "nosharetransport" option to allow two different
mounts from the same server to use different transports.
This only works for NFSv3 (and v2), not NFSv4.

As an alternate to "-o nosharetransport" you can set the module parameter
  nfs.always_nosharetransport=1

To impose the option on all mounts.

There are at least two circumstances where it might be desirable
to use separate transports:

1/ If the NFS server can get into a state where it will ignore
  requests for one filesystem while servicing request for another,
  then using separate connections for the separate filesystems can
  stop problems with one affecting access to the other.

  This is particularly relevant for NetApp filers where one filesystem
  has been "suspended".  Requests to that filesystem will be dropped
  (rather than the more correct NFS3ERR_JUKEBOX).  This currently
  interferes with other filesystems.

  Upstream thinks NetApp should fix their end.

2/ If a very fast network is used with a many-processor client, a
  single TCP connection can present a bottle neck which reduces total
  throughput.  Using multiple TCP connections (one per mount) removes
  the bottleneck.
  An alternate workaround is to configure multiple virtual IP
  addresses on the server and mount each filesystem from a different
  IP.  This is effective (throughput goes up) but an unnecessary
  administrative burden.

  Upstream wants to know exactly where the bottle neck is.

Signed-off-by: NeilBrown <neilb@suse.de>
Acked-by: NeilBrown <neilb@suse.de>

---
 fs/nfs/client.c           |   10 ++++++++++
 fs/nfs/super.c            |   15 +++++++++++++++
 include/linux/nfs_fs_sb.h |    1 +
 include/linux/nfs_mount.h |    1 +
 4 files changed, 27 insertions(+)

--- linux-3.0-SLE11-SP2.orig/fs/nfs/client.c
+++ linux-3.0-SLE11-SP2/fs/nfs/client.c
@@ -135,6 +135,7 @@ struct nfs_client_initdata {
 	const struct nfs_rpc_ops *rpc_ops;
 	int proto;
 	u32 minorversion;
+	unsigned long init_flags;
 };
 
 /*
@@ -185,6 +186,8 @@ static struct nfs_client *nfs_alloc_clie
 	clp->cl_minorversion = cl_init->minorversion;
 	clp->cl_mvops = nfs_v4_minor_ops[cl_init->minorversion];
 #endif
+	if (test_bit(NFS_CS_NO_SHARE, &cl_init->init_flags))
+		set_bit(NFS_CS_NO_SHARE, &clp->cl_res_state);
 	cred = rpc_lookup_machine_cred();
 	if (!IS_ERR(cred))
 		clp->cl_machine_cred = cred;
@@ -459,8 +462,13 @@ static struct nfs_client *nfs_match_clie
 	struct nfs_client *clp;
 	const struct sockaddr *sap = data->addr;
 
+	if (test_bit(NFS_CS_NO_SHARE, &data->init_flags))
+		return NULL;
+
 	list_for_each_entry(clp, &nfs_client_list, cl_share_link) {
 	        const struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr;
+		if (test_bit(NFS_CS_NO_SHARE,&clp->cl_res_state))
+			continue;
 		/* Don't match clients that failed to initialise properly */
 		if (clp->cl_cons_state < 0)
 			continue;
@@ -841,6 +849,8 @@ static int nfs_init_server(struct nfs_se
 	nfs_init_timeout_values(&timeparms, data->nfs_server.protocol,
 			data->timeo, data->retrans);
 
+	if (data->flags & NFS_MOUNT_NOSHARE_XPRT)
+		set_bit(NFS_CS_NO_SHARE, &cl_init.init_flags);
 	/* Allocate or find a client reference we can use */
 	clp = nfs_get_client(&cl_init, &timeparms, NULL, RPC_AUTH_UNIX,
 			     data->flags & NFS_MOUNT_NORESVPORT);
--- linux-3.0-SLE11-SP2.orig/fs/nfs/super.c
+++ linux-3.0-SLE11-SP2/fs/nfs/super.c
@@ -85,6 +85,7 @@ enum {
 	Opt_acl, Opt_noacl,
 	Opt_rdirplus, Opt_nordirplus,
 	Opt_sharecache, Opt_nosharecache,
+	Opt_sharetransport, Opt_nosharetransport,
 	Opt_resvport, Opt_noresvport,
 	Opt_fscache, Opt_nofscache,
 
@@ -145,6 +146,8 @@ static const match_table_t nfs_mount_opt
 	{ Opt_nordirplus, "nordirplus" },
 	{ Opt_sharecache, "sharecache" },
 	{ Opt_nosharecache, "nosharecache" },
+	{ Opt_sharetransport, "sharetransport"},
+	{ Opt_nosharetransport, "nosharetransport"},
 	{ Opt_resvport, "resvport" },
 	{ Opt_noresvport, "noresvport" },
 	{ Opt_fscache, "fsc" },
@@ -649,6 +652,7 @@ static void nfs_show_mount_options(struc
 		{ NFS_MOUNT_NOACL, ",noacl", "" },
 		{ NFS_MOUNT_NORDIRPLUS, ",nordirplus", "" },
 		{ NFS_MOUNT_UNSHARED, ",nosharecache", "" },
+		{ NFS_MOUNT_NOSHARE_XPRT, ",nosharetransport", ""},
 		{ NFS_MOUNT_NORESVPORT, ",noresvport", "" },
 		{ 0, NULL, NULL }
 	};
@@ -1196,6 +1200,12 @@ static int nfs_parse_mount_options(char
 		case Opt_nosharecache:
 			mnt->flags |= NFS_MOUNT_UNSHARED;
 			break;
+		case Opt_sharetransport:
+			mnt->flags &= ~NFS_MOUNT_NOSHARE_XPRT;
+			break;
+		case Opt_nosharetransport:
+			mnt->flags |= NFS_MOUNT_NOSHARE_XPRT;
+			break;
 		case Opt_resvport:
 			mnt->flags &= ~NFS_MOUNT_NORESVPORT;
 			break;
@@ -2223,6 +2233,9 @@ static int nfs_bdi_register(struct nfs_s
 	return bdi_register_dev(&server->backing_dev_info, server->s_dev);
 }
 
+static int always_nosharetransport = 0;
+module_param(always_nosharetransport, bool, 0644);
+
 static struct dentry *nfs_fs_mount(struct file_system_type *fs_type,
 	int flags, const char *dev_name, void *raw_data)
 {
@@ -2241,6 +2254,8 @@ static struct dentry *nfs_fs_mount(struc
 	mntfh = nfs_alloc_fhandle();
 	if (data == NULL || mntfh == NULL)
 		goto out;
+	if (always_nosharetransport)
+		data->flags |= NFS_MOUNT_NOSHARE_XPRT;
 
 	/* Validate the mount data */
 	error = nfs_validate_mount_data(raw_data, data, mntfh, dev_name);
--- linux-3.0-SLE11-SP2.orig/include/linux/nfs_fs_sb.h
+++ linux-3.0-SLE11-SP2/include/linux/nfs_fs_sb.h
@@ -32,6 +32,7 @@ struct nfs_client {
 #define NFS_CS_RENEWD		3		/* - renewd started */
 #define NFS_CS_STOP_RENEW	4		/* no more state to renew */
 #define NFS_CS_CHECK_LEASE_TIME	5		/* need to check lease time */
+#define NFS_CS_NO_SHARE		16		/* - don't share across mounts */
 	struct sockaddr_storage	cl_addr;	/* server identifier */
 	size_t			cl_addrlen;
 	char *			cl_hostname;	/* hostname of server */
--- linux-3.0-SLE11-SP2.orig/include/linux/nfs_mount.h
+++ linux-3.0-SLE11-SP2/include/linux/nfs_mount.h
@@ -73,5 +73,6 @@ struct nfs_mount_data {
 
 #define NFS_MOUNT_LOCAL_FLOCK	0x100000
 #define NFS_MOUNT_LOCAL_FCNTL	0x200000
+#define NFS_MOUNT_NOSHARE_XPRT	0x400000
 
 #endif
