Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

nfs: don't share pNFS DS connections between net namespaces

Currently, different NFS clients can share the same DS connections, even
when they are in different net namespaces. If a containerized client
creates a DS connection, another container can find and use it. When the
first client exits, the connection will close which can lead to stalls
in other clients.

Add a net namespace pointer to struct nfs4_pnfs_ds, and compare those
value to the caller's netns in _data_server_lookup_locked() when
searching for a nfs4_pnfs_ds to match.

Reported-by: Omar Sandoval <osandov@osandov.com>
Reported-by: Sargun Dillon <sargun@sargun.me>
Closes: https://lore.kernel.org/linux-nfs/Z_ArpQC_vREh_hEA@telecaster/
Tested-by: Sargun Dillon <sargun@sargun.me>
Signed-off-by: Jeff Layton <jlayton@kernel.org>
Reviewed-by: Benjamin Coddington <bcodding@redhat.com>
Link: https://lore.kernel.org/r/20250410-nfs-ds-netns-v2-1-f80b7979ba80@kernel.org
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>

authored by

Jeff Layton and committed by
Trond Myklebust
6b9785dc c457dc1e

+14 -11
+3 -3
fs/nfs/filelayout/filelayoutdev.c
··· 76 76 struct page *scratch; 77 77 struct list_head dsaddrs; 78 78 struct nfs4_pnfs_ds_addr *da; 79 + struct net *net = server->nfs_client->cl_net; 79 80 80 81 /* set up xdr stream */ 81 82 scratch = alloc_page(gfp_flags); ··· 160 159 161 160 mp_count = be32_to_cpup(p); /* multipath count */ 162 161 for (j = 0; j < mp_count; j++) { 163 - da = nfs4_decode_mp_ds_addr(server->nfs_client->cl_net, 164 - &stream, gfp_flags); 162 + da = nfs4_decode_mp_ds_addr(net, &stream, gfp_flags); 165 163 if (da) 166 164 list_add_tail(&da->da_node, &dsaddrs); 167 165 } ··· 170 170 goto out_err_free_deviceid; 171 171 } 172 172 173 - dsaddr->ds_list[i] = nfs4_pnfs_ds_add(&dsaddrs, gfp_flags); 173 + dsaddr->ds_list[i] = nfs4_pnfs_ds_add(net, &dsaddrs, gfp_flags); 174 174 if (!dsaddr->ds_list[i]) 175 175 goto out_err_drain_dsaddrs; 176 176 trace_fl_getdevinfo(server, &pdev->dev_id, dsaddr->ds_list[i]->ds_remotestr);
+3 -3
fs/nfs/flexfilelayout/flexfilelayoutdev.c
··· 49 49 struct nfs4_pnfs_ds_addr *da; 50 50 struct nfs4_ff_layout_ds *new_ds = NULL; 51 51 struct nfs4_ff_ds_version *ds_versions = NULL; 52 + struct net *net = server->nfs_client->cl_net; 52 53 u32 mp_count; 53 54 u32 version_count; 54 55 __be32 *p; ··· 81 80 82 81 for (i = 0; i < mp_count; i++) { 83 82 /* multipath ds */ 84 - da = nfs4_decode_mp_ds_addr(server->nfs_client->cl_net, 85 - &stream, gfp_flags); 83 + da = nfs4_decode_mp_ds_addr(net, &stream, gfp_flags); 86 84 if (da) 87 85 list_add_tail(&da->da_node, &dsaddrs); 88 86 } ··· 149 149 new_ds->ds_versions = ds_versions; 150 150 new_ds->ds_versions_cnt = version_count; 151 151 152 - new_ds->ds = nfs4_pnfs_ds_add(&dsaddrs, gfp_flags); 152 + new_ds->ds = nfs4_pnfs_ds_add(net, &dsaddrs, gfp_flags); 153 153 if (!new_ds->ds) 154 154 goto out_err_drain_dsaddrs; 155 155
+3 -1
fs/nfs/pnfs.h
··· 60 60 struct list_head ds_node; /* nfs4_pnfs_dev_hlist dev_dslist */ 61 61 char *ds_remotestr; /* comma sep list of addrs */ 62 62 struct list_head ds_addrs; 63 + const struct net *ds_net; 63 64 struct nfs_client *ds_clp; 64 65 refcount_t ds_count; 65 66 unsigned long ds_state; ··· 416 415 int pnfs_generic_scan_commit_lists(struct nfs_commit_info *cinfo, int max); 417 416 void pnfs_generic_write_commit_done(struct rpc_task *task, void *data); 418 417 void nfs4_pnfs_ds_put(struct nfs4_pnfs_ds *ds); 419 - struct nfs4_pnfs_ds *nfs4_pnfs_ds_add(struct list_head *dsaddrs, 418 + struct nfs4_pnfs_ds *nfs4_pnfs_ds_add(const struct net *net, 419 + struct list_head *dsaddrs, 420 420 gfp_t gfp_flags); 421 421 void nfs4_pnfs_v3_ds_connect_unload(void); 422 422 int nfs4_pnfs_ds_connect(struct nfs_server *mds_srv, struct nfs4_pnfs_ds *ds,
+5 -4
fs/nfs/pnfs_nfs.c
··· 604 604 * Lookup DS by addresses. nfs4_ds_cache_lock is held 605 605 */ 606 606 static struct nfs4_pnfs_ds * 607 - _data_server_lookup_locked(const struct list_head *dsaddrs) 607 + _data_server_lookup_locked(const struct net *net, const struct list_head *dsaddrs) 608 608 { 609 609 struct nfs4_pnfs_ds *ds; 610 610 611 611 list_for_each_entry(ds, &nfs4_data_server_cache, ds_node) 612 - if (_same_data_server_addrs_locked(&ds->ds_addrs, dsaddrs)) 612 + if (ds->ds_net == net && _same_data_server_addrs_locked(&ds->ds_addrs, dsaddrs)) 613 613 return ds; 614 614 return NULL; 615 615 } ··· 716 716 * uncached and return cached struct nfs4_pnfs_ds. 717 717 */ 718 718 struct nfs4_pnfs_ds * 719 - nfs4_pnfs_ds_add(struct list_head *dsaddrs, gfp_t gfp_flags) 719 + nfs4_pnfs_ds_add(const struct net *net, struct list_head *dsaddrs, gfp_t gfp_flags) 720 720 { 721 721 struct nfs4_pnfs_ds *tmp_ds, *ds = NULL; 722 722 char *remotestr; ··· 734 734 remotestr = nfs4_pnfs_remotestr(dsaddrs, gfp_flags); 735 735 736 736 spin_lock(&nfs4_ds_cache_lock); 737 - tmp_ds = _data_server_lookup_locked(dsaddrs); 737 + tmp_ds = _data_server_lookup_locked(net, dsaddrs); 738 738 if (tmp_ds == NULL) { 739 739 INIT_LIST_HEAD(&ds->ds_addrs); 740 740 list_splice_init(dsaddrs, &ds->ds_addrs); 741 741 ds->ds_remotestr = remotestr; 742 742 refcount_set(&ds->ds_count, 1); 743 743 INIT_LIST_HEAD(&ds->ds_node); 744 + ds->ds_net = net; 744 745 ds->ds_clp = NULL; 745 746 list_add(&ds->ds_node, &nfs4_data_server_cache); 746 747 dprintk("%s add new data server %s\n", __func__,