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 */