Skip to content

Commit c23266d

Browse files
androsadamsonTrond Myklebust
authored andcommitted
NFS4.1 Fix data server connection race
Unlike meta data server mounts which support multiple mount points to the same server via struct nfs_server, data servers support a single connection. Concurrent calls to setup the data server connection can race where the first call allocates the nfs_client struct, and before the cache struct nfs_client pointer can be set, a second call also tries to setup the connection, finds the already allocated nfs_client, bumps the reference count, re-initializes the session,etc. This results in a hanging data server session after umount. Signed-off-by: Andy Adamson <[email protected]> Signed-off-by: Trond Myklebust <[email protected]>
1 parent d497ab9 commit c23266d

File tree

2 files changed

+26
-2
lines changed

2 files changed

+26
-2
lines changed

fs/nfs/nfs4filelayout.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ struct nfs4_pnfs_ds {
7070
struct list_head ds_addrs;
7171
struct nfs_client *ds_clp;
7272
atomic_t ds_count;
73+
unsigned long ds_state;
74+
#define NFS4DS_CONNECTING 0 /* ds is establishing connection */
7375
};
7476

7577
struct nfs4_file_layout_dsaddr {

fs/nfs/nfs4filelayoutdev.c

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -775,6 +775,22 @@ nfs4_fl_select_ds_fh(struct pnfs_layout_segment *lseg, u32 j)
775775
return flseg->fh_array[i];
776776
}
777777

778+
static void nfs4_wait_ds_connect(struct nfs4_pnfs_ds *ds)
779+
{
780+
might_sleep();
781+
wait_on_bit(&ds->ds_state, NFS4DS_CONNECTING,
782+
nfs_wait_bit_killable, TASK_KILLABLE);
783+
}
784+
785+
static void nfs4_clear_ds_conn_bit(struct nfs4_pnfs_ds *ds)
786+
{
787+
smp_mb__before_clear_bit();
788+
clear_bit(NFS4DS_CONNECTING, &ds->ds_state);
789+
smp_mb__after_clear_bit();
790+
wake_up_bit(&ds->ds_state, NFS4DS_CONNECTING);
791+
}
792+
793+
778794
struct nfs4_pnfs_ds *
779795
nfs4_fl_prepare_ds(struct pnfs_layout_segment *lseg, u32 ds_idx)
780796
{
@@ -791,16 +807,22 @@ nfs4_fl_prepare_ds(struct pnfs_layout_segment *lseg, u32 ds_idx)
791807
filelayout_mark_devid_invalid(devid);
792808
return NULL;
793809
}
810+
if (ds->ds_clp)
811+
return ds;
794812

795-
if (!ds->ds_clp) {
813+
if (test_and_set_bit(NFS4DS_CONNECTING, &ds->ds_state) == 0) {
796814
struct nfs_server *s = NFS_SERVER(lseg->pls_layout->plh_inode);
797815
int err;
798816

799817
err = nfs4_ds_connect(s, ds);
800818
if (err) {
801819
nfs4_mark_deviceid_unavailable(devid);
802-
return NULL;
820+
ds = NULL;
803821
}
822+
nfs4_clear_ds_conn_bit(ds);
823+
} else {
824+
/* Either ds is connected, or ds is NULL */
825+
nfs4_wait_ds_connect(ds);
804826
}
805827
return ds;
806828
}

0 commit comments

Comments
 (0)