NFSv4: Fix a deadlock between nfs4_open_recover_helper() and delegreturn

If we're asked to recover open state while a delegation return is
outstanding, then the state manager thread cannot use a cached open, so
if the server returns a delegation, we can end up deadlocked behind the
pending delegreturn.
To avoid this problem, let's just ask the server not to give us a
delegation unless we're explicitly reclaiming one.

Fixes: be36e185bd ("NFSv4: nfs4_open_recover_helper() must set share access")
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
This commit is contained in:
Trond Myklebust 2022-11-04 13:20:01 -04:00
parent e83458fce0
commit 51069e4aef

View File

@ -2131,18 +2131,18 @@ static struct nfs4_opendata *nfs4_open_recoverdata_alloc(struct nfs_open_context
} }
static int nfs4_open_recover_helper(struct nfs4_opendata *opendata, static int nfs4_open_recover_helper(struct nfs4_opendata *opendata,
fmode_t fmode) fmode_t fmode)
{ {
struct nfs4_state *newstate; struct nfs4_state *newstate;
struct nfs_server *server = NFS_SB(opendata->dentry->d_sb);
int openflags = opendata->o_arg.open_flags;
int ret; int ret;
if (!nfs4_mode_match_open_stateid(opendata->state, fmode)) if (!nfs4_mode_match_open_stateid(opendata->state, fmode))
return 0; return 0;
opendata->o_arg.open_flags = 0;
opendata->o_arg.fmode = fmode; opendata->o_arg.fmode = fmode;
opendata->o_arg.share_access = nfs4_map_atomic_open_share( opendata->o_arg.share_access =
NFS_SB(opendata->dentry->d_sb), nfs4_map_atomic_open_share(server, fmode, openflags);
fmode, 0);
memset(&opendata->o_res, 0, sizeof(opendata->o_res)); memset(&opendata->o_res, 0, sizeof(opendata->o_res));
memset(&opendata->c_res, 0, sizeof(opendata->c_res)); memset(&opendata->c_res, 0, sizeof(opendata->c_res));
nfs4_init_opendata_res(opendata); nfs4_init_opendata_res(opendata);
@ -2724,10 +2724,15 @@ static int _nfs4_open_expired(struct nfs_open_context *ctx, struct nfs4_state *s
struct nfs4_opendata *opendata; struct nfs4_opendata *opendata;
int ret; int ret;
opendata = nfs4_open_recoverdata_alloc(ctx, state, opendata = nfs4_open_recoverdata_alloc(ctx, state, NFS4_OPEN_CLAIM_FH);
NFS4_OPEN_CLAIM_FH);
if (IS_ERR(opendata)) if (IS_ERR(opendata))
return PTR_ERR(opendata); return PTR_ERR(opendata);
/*
* We're not recovering a delegation, so ask for no delegation.
* Otherwise the recovery thread could deadlock with an outstanding
* delegation return.
*/
opendata->o_arg.open_flags = O_DIRECT;
ret = nfs4_open_recover(opendata, state); ret = nfs4_open_recover(opendata, state);
if (ret == -ESTALE) if (ret == -ESTALE)
d_drop(ctx->dentry); d_drop(ctx->dentry);