nfsd: fix NFS server for ERELOOKUP

r367672 modified UFS such that certain VOPs, such as
VOP_CREATE() will intermittently return ERELOOKUP.
When this happens, the entire system call, or NFS
operation in the case of the NFS server, must be redone.

This patch adds that support to the NFS server by rolling
back the state of the NFS request arguments and NFS
reply arguments mbuf lists to the condition they were
in before the operation and then redoing the operation.

Tested by:	pho
Reviewed by:	kib
Differential Revision:	https://reviews.freebsd.org/D27875
This commit is contained in:
Rick Macklem 2021-01-01 13:55:51 -08:00
parent fb6bc290fb
commit 774a36851e
3 changed files with 65 additions and 4 deletions

View File

@ -753,6 +753,8 @@ int nfsvno_rmxattr(struct nfsrv_descript *, struct vnode *, char *,
struct ucred *, struct thread *);
int nfsvno_listxattr(struct vnode *, uint64_t, struct ucred *, struct thread *,
u_char **, uint32_t *, bool *);
void nfsm_trimtrailing(struct nfsrv_descript *, struct mbuf *, char *, int,
int);
/* nfs_commonkrpc.c */
int newnfs_nmcancelreqs(struct nfsmount *);

View File

@ -146,8 +146,6 @@ static int nfsrv_dsremove(struct vnode *, char *, struct ucred *, NFSPROC_T *);
static int nfsrv_dssetacl(struct vnode *, struct acl *, struct ucred *,
NFSPROC_T *);
static int nfsrv_pnfsstatfs(struct statfs *, struct mount *);
static void nfsm_trimtrailing(struct nfsrv_descript *, struct mbuf *,
char *, int, int);
int nfs_pnfsio(task_fn_t *, void *);
@ -6564,7 +6562,7 @@ nfsvno_listxattr(struct vnode *vp, uint64_t cookie, struct ucred *cred,
/*
* Trim trailing data off the mbuf list being built.
*/
static void
void
nfsm_trimtrailing(struct nfsrv_descript *nd, struct mbuf *mb, char *bpos,
int bextpg, int bextpgsiz)
{

View File

@ -534,9 +534,21 @@ nfsrvd_dorpc(struct nfsrv_descript *nd, int isdgram, u_char *tag, int taglen,
{
int error = 0, lktype;
vnode_t vp;
mount_t mp = NULL;
mount_t mp;
struct nfsrvfh fh;
struct nfsexstuff nes;
struct mbuf *md;
char *dpos;
/*
* Save the current position in the request mbuf list so
* that a rollback to this location can be done upon an
* ERELOOKUP error return from an RPC function.
*/
md = nd->nd_md;
dpos = nd->nd_dpos;
tryagain:
mp = NULL;
/*
* Get a locked vnode for the first file handle
@ -634,6 +646,21 @@ nfsrvd_dorpc(struct nfsrv_descript *nd, int isdgram, u_char *tag, int taglen,
if (mp != NULL && nfsrv_writerpc[nd->nd_procnum] != 0)
vn_finished_write(mp);
if (error == 0 && nd->nd_repstat == ERELOOKUP) {
/*
* Roll back to the beginning of the RPC request
* arguments.
*/
nd->nd_md = md;
nd->nd_dpos = dpos;
/* Free the junk RPC reply and redo the RPC. */
m_freem(nd->nd_mreq);
nd->nd_mreq = nd->nd_mb = NULL;
nd->nd_repstat = 0;
goto tryagain;
}
nfsrvd_statend(nfsv3to4op[nd->nd_procnum], /*bytes*/ 0,
/*now*/ NULL, /*then*/ &start_time);
}
@ -691,6 +718,9 @@ nfsrvd_compound(struct nfsrv_descript *nd, int isdgram, u_char *tag,
static u_int64_t compref = 0;
struct bintime start_time;
struct thread *p;
struct mbuf *mb, *md;
char *bpos, *dpos;
int bextpg, bextpgsiz;
p = curthread;
@ -1045,6 +1075,20 @@ nfsrvd_compound(struct nfsrv_descript *nd, int isdgram, u_char *tag,
break;
}
}
/*
* Save the current positions in the mbuf lists so
* that a rollback to this location can be done upon a
* redo due to a ERELOOKUP return for a operation.
*/
mb = nd->nd_mb;
bpos = nd->nd_bpos;
bextpg = nd->nd_bextpg;
bextpgsiz = nd->nd_bextpgsiz;
md = nd->nd_md;
dpos = nd->nd_dpos;
tryagain:
if (nfsv4_opflag[op].retfh == 1) {
if (!vp) {
nd->nd_repstat = NFSERR_NOFILEHANDLE;
@ -1154,6 +1198,23 @@ nfsrvd_compound(struct nfsrv_descript *nd, int isdgram, u_char *tag,
error = 0;
}
if (nd->nd_repstat == ERELOOKUP) {
/*
* Roll back to the beginning of the operation
* arguments.
*/
nd->nd_md = md;
nd->nd_dpos = dpos;
/*
* Trim off the bogus reply for this operation
* and redo the operation.
*/
nfsm_trimtrailing(nd, mb, bpos, bextpg, bextpgsiz);
nd->nd_repstat = 0;
goto tryagain;
}
if (statsinprog != 0) {
nfsrvd_statend(op, /*bytes*/ 0, /*now*/ NULL,
/*then*/ &start_time);