NFSv4/pNFS: Don't call _nfs4_pnfs_v3_ds_connect multiple times

[ Upstream commit f46f84931a ]

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 8f7d42d54a
commit e3eeeaed0a

View File

@ -555,19 +555,16 @@ out:
}
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();
wait_on_bit(&ds->ds_state, NFS4DS_CONNECTING,
TASK_KILLABLE);
return wait_on_bit(&ds->ds_state, NFS4DS_CONNECTING, TASK_KILLABLE);
}
static void nfs4_clear_ds_conn_bit(struct nfs4_pnfs_ds *ds)
{
smp_mb__before_atomic();
clear_bit(NFS4DS_CONNECTING, &ds->ds_state);
smp_mb__after_atomic();
wake_up_bit(&ds->ds_state, NFS4DS_CONNECTING);
clear_and_wake_up_bit(NFS4DS_CONNECTING, &ds->ds_state);
}
static struct nfs_client *(*get_v3_ds_connect)(
@ -728,30 +725,33 @@ int nfs4_pnfs_ds_connect(struct nfs_server *mds_srv, struct nfs4_pnfs_ds *ds,
{
int err;
again:
err = 0;
if (test_and_set_bit(NFS4DS_CONNECTING, &ds->ds_state) == 0) {
if (version == 3) {
err = _nfs4_pnfs_v3_ds_connect(mds_srv, ds, timeo,
retrans);
} else if (version == 4) {
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;
}
do {
err = nfs4_wait_ds_connect(ds);
if (err || ds->ds_clp)
goto out;
if (nfs4_test_deviceid_unavailable(devid))
return -ENODEV;
} while (test_and_set_bit(NFS4DS_CONNECTING, &ds->ds_state) != 0);
nfs4_clear_ds_conn_bit(ds);
} else {
nfs4_wait_ds_connect(ds);
if (ds->ds_clp)
goto connect_done;
/* what was waited on didn't connect AND didn't mark unavail */
if (!ds->ds_clp && !nfs4_test_deviceid_unavailable(devid))
goto again;
switch (version) {
case 3:
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
* hit an error.