From: NeilBrown 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 Signed-off-by: Neil Brown --- 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 */