Blob Blame History Raw
From: NeilBrown <neilb@suse.de>
Subject: NFS: Allow NFSv4 mounts to not share transports.
Patch-mainline: Never, upstream not interested
References: FATE#315593

The "nfs-no-share-transport" patch only supports NFSv2 and NFSv3.

Making the 'nosharetransport' option work for NFSv4 turns out to be
prohibitively difficult, and the design is not ideal.
A consequence of the design is that if

   mount -o nosharetransport server:/path /mountpoint

is run repeatedly, you get multiple entries in the mounts table.

So this patch add "-o sharetransport=N" where N is some unique number
greater than 0.
This syntax:
 - can be and is supported by NFSv4
 - prevents multiple attempts of the same mount from creating multiple
   mount table entries.

It does require entering a number each time, but that should just
happen once in /etc/fstab or similar.

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

---
 fs/nfs/client.c           |    3 +++
 fs/nfs/internal.h         |    2 ++
 fs/nfs/nfs4client.c       |   18 ++++++++++++++----
 fs/nfs/super.c            |   22 ++++++++++++++++++++++
 include/linux/nfs_fs_sb.h |    1 +
 5 files changed, 42 insertions(+), 4 deletions(-)

--- a/fs/nfs/client.c
+++ b/fs/nfs/client.c
@@ -182,6 +182,7 @@ struct nfs_client *nfs_alloc_client(cons
 	clp->cl_proto = cl_init->proto;
 	clp->cl_nconnect = cl_init->nconnect;
 	clp->cl_net = get_net(cl_init->net);
+	clp->cl_xprt_id = cl_init->xprt_id;
 
 	cred = rpc_lookup_machine_cred("*");
 	if (!IS_ERR(cred))
@@ -300,6 +301,8 @@ again:
 	        const struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr;
 		if (test_bit(NFS_CS_NO_SHARE,&clp->cl_flags))
 			continue;
+		if (clp->cl_xprt_id != data->xprt_id)
+			continue;
 		/* Don't match clients that failed to initialise properly */
 		if (clp->cl_cons_state < 0)
 			continue;
--- a/fs/nfs/internal.h
+++ b/fs/nfs/internal.h
@@ -81,6 +81,7 @@ struct nfs_client_initdata {
 	unsigned int nconnect;
 	struct net *net;
 	const struct rpc_timeout *timeparms;
+	unsigned int xprt_id;
 };
 
 /*
@@ -102,6 +103,7 @@ struct nfs_parsed_mount_data {
 	unsigned int		minorversion;
 	char			*fscache_uniq;
 	bool			need_mount;
+	unsigned int		xprt_id;
 
 	struct {
 		struct sockaddr_storage	address;
--- a/fs/nfs/nfs4client.c
+++ b/fs/nfs/nfs4client.c
@@ -565,6 +565,9 @@ int nfs40_walk_client_list(struct nfs_cl
 		 * way that a SETCLIENTID_CONFIRM to pos can succeed is
 		 * if new and pos point to the same server:
 		 */
+
+		if (pos->cl_xprt_id != new->cl_xprt_id)
+			continue;
 found:
 		atomic_inc(&pos->cl_count);
 		spin_unlock(&nn->nfs_client_lock);
@@ -707,6 +710,9 @@ int nfs41_walk_client_list(struct nfs_cl
 		if (pos == new)
 			goto found;
 
+		if (pos->cl_xprt_id != new->cl_xprt_id)
+			continue;
+
 		status = nfs4_match_client(pos, new, &prev, nn);
 		if (status < 0)
 			goto out;
@@ -840,7 +846,7 @@ static int nfs4_set_client(struct nfs_se
 		const char *ip_addr,
 		int proto, const struct rpc_timeout *timeparms,
 		u32 minorversion, unsigned int nconnect,
-		struct net *net)
+		struct net *net, unsigned int xprt_id)
 {
 	struct nfs_client_initdata cl_init = {
 		.hostname = hostname,
@@ -852,6 +858,7 @@ static int nfs4_set_client(struct nfs_se
 		.minorversion = minorversion,
 		.net = net,
 		.timeparms = timeparms,
+		.xprt_id = xprt_id,
 	};
 	struct nfs_client *clp;
 
@@ -912,6 +919,7 @@ struct nfs_client *nfs4_set_ds_client(st
 		.minorversion = minor_version,
 		.net = mds_clp->cl_net,
 		.timeparms = &ds_timeout,
+		.xprt_id = mds_clp->cl_xprt_id,
 	};
 	char buf[INET6_ADDRSTRLEN + 1];
 
@@ -1054,7 +1062,8 @@ static int nfs4_init_server(struct nfs_s
 			&timeparms,
 			data->minorversion,
 			data->nfs_server.nconnect,
-			data->net);
+			data->net,
+			data->xprt_id);
 	if (error < 0)
 		return error;
 
@@ -1139,7 +1148,8 @@ struct nfs_server *nfs4_create_referral_
 				parent_server->client->cl_timeout,
 				parent_client->cl_mvops->minor_version,
 				parent_client->cl_nconnect,
-				parent_client->cl_net);
+				parent_client->cl_net,
+				parent_client->cl_xprt_id);
 	if (error < 0)
 		goto error;
 
@@ -1231,7 +1241,7 @@ int nfs4_update_server(struct nfs_server
 	error = nfs4_set_client(server, hostname, sap, salen, buf,
 				clp->cl_proto, clnt->cl_timeout,
 				clp->cl_minorversion,
-				clp->cl_nconnect, net);
+				clp->cl_nconnect, net, clp->cl_xprt_id);
 	if (error != 0) {
 		nfs_server_insert_lists(server);
 		return error;
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -90,6 +90,7 @@ enum {
 	Opt_rdirplus, Opt_nordirplus,
 	Opt_sharecache, Opt_nosharecache,
 	Opt_sharetransport, Opt_nosharetransport,
+	Opt_sharetransportid,
 	Opt_resvport, Opt_noresvport,
 	Opt_fscache, Opt_nofscache,
 	Opt_migration, Opt_nomigration,
@@ -151,6 +152,7 @@ static const match_table_t nfs_mount_opt
 	{ Opt_nosharecache, "nosharecache" },
 	{ Opt_sharetransport, "sharetransport"},
 	{ Opt_nosharetransport, "nosharetransport"},
+	{ Opt_sharetransportid, "sharetransport=%s"},
 	{ Opt_resvport, "resvport" },
 	{ Opt_noresvport, "noresvport" },
 	{ Opt_fscache, "fsc" },
@@ -673,6 +675,8 @@ static void nfs_show_mount_options(struc
 		else
 			seq_puts(m, nfs_infop->nostr);
 	}
+	if (clp->cl_xprt_id)
+		seq_printf(m, ",sharetransport=%u", clp->cl_xprt_id);
 	rcu_read_lock();
 	seq_printf(m, ",proto=%s",
 		   rpc_peeraddr2str(nfss->client, RPC_DISPLAY_NETID));
@@ -1437,6 +1441,12 @@ static int nfs_parse_mount_options(char
 				goto out_invalid_value;
 			mnt->minorversion = option;
 			break;
+		case Opt_sharetransportid:
+			if (nfs_get_option_ul(args, &option) ||
+			    option <= 0)
+				goto out_invalid_value;
+			mnt->xprt_id = option;
+			break;
 
 		/*
 		 * options that take text values
@@ -1654,6 +1664,10 @@ static int nfs_parse_mount_options(char
 	    (mnt->version != 4 || mnt->minorversion != 0))
 		goto out_migration_misuse;
 
+	if (mnt->flags & NFS_MOUNT_NOSHARE_XPRT &&
+	    mnt->version == 4)
+		goto out_noshare_misuse;
+
 	/*
 	 * verify that any proto=/mountproto= options match the address
 	 * families in the addr=/mountaddr= options.
@@ -1674,6 +1688,10 @@ static int nfs_parse_mount_options(char
 
 	return 1;
 
+out_noshare_misuse:
+	printk(KERN_INFO "NFS: nosharetransport is not compatible with vers=4\n");
+	printk(KERN_INFO "NFS: use sharetransport=N for some unique N\n");
+	return 0;
 out_mountproto_mismatch:
 	printk(KERN_INFO "NFS: mount server address does not match mountproto= "
 			 "option\n");
@@ -2464,6 +2482,10 @@ static int nfs_compare_super_address(str
 	if (!net_eq(xprt1->xprt_net, xprt2->xprt_net))
 		return 0;
 
+	if (server1->nfs_client->cl_xprt_id !=
+	    server2->nfs_client->cl_xprt_id)
+		return 0;
+
 	sap1 = (struct sockaddr *)&server1->nfs_client->cl_addr;
 	sap2 = (struct sockaddr *)&server2->nfs_client->cl_addr;
 
--- a/include/linux/nfs_fs_sb.h
+++ b/include/linux/nfs_fs_sb.h
@@ -59,6 +59,7 @@ struct nfs_client {
 	unsigned int		cl_nconnect;	/* Number of connections */
 	struct rpc_cred		*cl_machine_cred;
 
+	unsigned int		cl_xprt_id;
 #if IS_ENABLED(CONFIG_NFS_V4)
 	struct list_head	cl_ds_clients; /* auth flavor data servers */
 	u64			cl_clientid;	/* constant */