NFSv4/pNFS: Don't call _nfs4_pnfs_v3_ds_connect multiple times

[ Upstream commit f46f84931a0aa344678efe412d4b071d84d8a805 ]

After we grab the lock in nfs4_pnfs_ds_connect(), there is no check for
whether or not ds->ds_clp has already been initialised, so we can end up
adding the same transports multiple times.

Fixes: fc821d5920 ("pnfs/NFSv4.1: Add multipath capabilities to pNFS flexfiles servers over NFSv3")
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
Trond Myklebust 2021-07-03 14:34:20 -04:00 committed by Greg Kroah-Hartman
parent 9f02e9dd8c
commit 3b03882123
1 changed files with 26 additions and 26 deletions

View File

@ -556,19 +556,16 @@ out:
} }
EXPORT_SYMBOL_GPL(nfs4_pnfs_ds_add); EXPORT_SYMBOL_GPL(nfs4_pnfs_ds_add);
static void nfs4_wait_ds_connect(struct nfs4_pnfs_ds *ds) static int nfs4_wait_ds_connect(struct nfs4_pnfs_ds *ds)
{ {
might_sleep(); might_sleep();
wait_on_bit(&ds->ds_state, NFS4DS_CONNECTING, return wait_on_bit(&ds->ds_state, NFS4DS_CONNECTING, TASK_KILLABLE);
TASK_KILLABLE);
} }
static void nfs4_clear_ds_conn_bit(struct nfs4_pnfs_ds *ds) static void nfs4_clear_ds_conn_bit(struct nfs4_pnfs_ds *ds)
{ {
smp_mb__before_atomic(); smp_mb__before_atomic();
clear_bit(NFS4DS_CONNECTING, &ds->ds_state); clear_and_wake_up_bit(NFS4DS_CONNECTING, &ds->ds_state);
smp_mb__after_atomic();
wake_up_bit(&ds->ds_state, NFS4DS_CONNECTING);
} }
static struct nfs_client *(*get_v3_ds_connect)( static struct nfs_client *(*get_v3_ds_connect)(
@ -734,30 +731,33 @@ int nfs4_pnfs_ds_connect(struct nfs_server *mds_srv, struct nfs4_pnfs_ds *ds,
{ {
int err; int err;
again: do {
err = 0; err = nfs4_wait_ds_connect(ds);
if (test_and_set_bit(NFS4DS_CONNECTING, &ds->ds_state) == 0) { if (err || ds->ds_clp)
if (version == 3) { goto out;
err = _nfs4_pnfs_v3_ds_connect(mds_srv, ds, timeo, if (nfs4_test_deviceid_unavailable(devid))
retrans); return -ENODEV;
} else if (version == 4) { } while (test_and_set_bit(NFS4DS_CONNECTING, &ds->ds_state) != 0);
err = _nfs4_pnfs_v4_ds_connect(mds_srv, ds, timeo,
retrans, minor_version);
} else {
dprintk("%s: unsupported DS version %d\n", __func__,
version);
err = -EPROTONOSUPPORT;
}
nfs4_clear_ds_conn_bit(ds); if (ds->ds_clp)
} else { goto connect_done;
nfs4_wait_ds_connect(ds);
/* what was waited on didn't connect AND didn't mark unavail */ switch (version) {
if (!ds->ds_clp && !nfs4_test_deviceid_unavailable(devid)) case 3:
goto again; err = _nfs4_pnfs_v3_ds_connect(mds_srv, ds, timeo, retrans);
break;
case 4:
err = _nfs4_pnfs_v4_ds_connect(mds_srv, ds, timeo, retrans,
minor_version);
break;
default:
dprintk("%s: unsupported DS version %d\n", __func__, version);
err = -EPROTONOSUPPORT;
} }
connect_done:
nfs4_clear_ds_conn_bit(ds);
out:
/* /*
* At this point the ds->ds_clp should be ready, but it might have * At this point the ds->ds_clp should be ready, but it might have
* hit an error. * hit an error.