nfscl: Add a Lookup+Open RPC for NFSv4.1/4.2

This patch adds a Lookup+Open compound RPC to the NFSv4.1/4.2
NFS client, which can be used by nfs_lookup() so that a
subsequent Open RPC is not required.
It uses the cn_flags OPENREAD, OPENWRITE added by commit c18c74a87c.
This reduced the number of RPCs by about 15% for a kernel
build over NFS.

For now, use of Lookup+Open is only done when the "oneopenown"
mount option is used.  It may be possible for Lookup+Open to
be used for non-oneopenown NFSv4.1/4.2 mounts, but that will
require extensive further testing to determine if it works.

While here, I've added the changes to the nfscommon module
that are needed to implement the Deallocate NFSv4.2 operation.
This avoids needing another cycle of changes to the internal
KAPI between the NFS modules.

This commit has changed the internal KAPI between the NFS
modules and, as such, all need to be rebuilt from sources.
I have not bumped __FreeBSD_version, since it was bumped a
few days ago.
This commit is contained in:
Rick Macklem 2021-08-11 18:49:26 -07:00
parent 5ae48eb998
commit 3ad1e1c1ce
7 changed files with 188 additions and 19 deletions

View File

@ -179,7 +179,7 @@ struct nfsv4_opflag nfsv4_opflag[NFSV42_NOPS] = {
{ 0, 1, 1, 1, LK_EXCLUSIVE, 1, 0 }, /* Allocate */
{ 2, 1, 1, 0, LK_SHARED, 1, 0 }, /* Copy */
{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 }, /* Copy Notify */
{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 }, /* Deallocate */
{ 0, 2, 1, 1, LK_EXCLUSIVE, 1, 0 }, /* Deallocate */
{ 0, 1, 0, 0, LK_SHARED, 1, 0 }, /* IO Advise */
{ 0, 1, 0, 0, LK_EXCLUSIVE, 1, 0 }, /* Layout Error */
{ 0, 1, 0, 0, LK_EXCLUSIVE, 1, 0 }, /* Layout Stats */
@ -219,7 +219,7 @@ static struct nfsrv_lughash *nfsgroupnamehash;
static int nfs_bigreply[NFSV42_NPROCS] = { 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 1, 0 };
1, 0, 0, 1, 0, 0, 0 };
/* local functions */
static int nfsrv_skipace(struct nfsrv_descript *nd, int *acesizep);
@ -303,6 +303,8 @@ static struct {
{ NFSV4OP_REMOVEXATTR, 2, "Rmxattr", 7, },
{ NFSV4OP_LISTXATTRS, 2, "Listxattr", 9, },
{ NFSV4OP_BINDCONNTOSESS, 1, "BindConSess", 11, },
{ NFSV4OP_LOOKUP, 5, "LookupOpen", 10, },
{ NFSV4OP_DEALLOCATE, 2, "Deallocate", 10, },
};
/*
@ -311,7 +313,7 @@ static struct {
static int nfs_bigrequest[NFSV42_NPROCS] = {
0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0
0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0
};
/*
@ -434,7 +436,8 @@ nfscl_reqstart(struct nfsrv_descript *nd, int procnum, struct nfsmount *nmp,
* attributes, so we can load the name cache.
*/
if (procnum == NFSPROC_LOOKUP ||
procnum == NFSPROC_LOOKUPP)
procnum == NFSPROC_LOOKUPP ||
procnum == NFSPROC_LOOKUPOPEN)
NFSGETATTR_ATTRBIT(&attrbits);
else {
NFSWCCATTR_ATTRBIT(&attrbits);

View File

@ -465,7 +465,7 @@ int nfsrpc_setattr(vnode_t, struct vattr *, NFSACL_T *, struct ucred *,
NFSPROC_T *, struct nfsvattr *, int *, void *);
int nfsrpc_lookup(vnode_t, char *, int, struct ucred *, NFSPROC_T *,
struct nfsvattr *, struct nfsvattr *, struct nfsfh **, int *, int *,
void *);
void *, uint32_t);
int nfsrpc_readlink(vnode_t, struct uio *, struct ucred *,
NFSPROC_T *, struct nfsvattr *, int *, void *);
int nfsrpc_read(vnode_t, struct uio *, struct ucred *, NFSPROC_T *,
@ -624,6 +624,8 @@ void nfscl_reclaimnode(vnode_t);
void nfscl_newnode(vnode_t);
void nfscl_delegmodtime(vnode_t);
void nfscl_deleggetmodtime(vnode_t, struct timespec *);
int nfscl_trydelegreturn(struct nfscldeleg *, struct ucred *,
struct nfsmount *, NFSPROC_T *);
int nfscl_tryclose(struct nfsclopen *, struct ucred *,
struct nfsmount *, NFSPROC_T *);
void nfscl_cleanup(NFSPROC_T *);

View File

@ -415,10 +415,16 @@
/* BindConnectionToSession, done by the krpc for a new connection. */
#define NFSPROC_BINDCONNTOSESS 65
/* Do a Lookup+Open for "oneopenown". */
#define NFSPROC_LOOKUPOPEN 66
/* Do an NFSv4.2 Deallocate. */
#define NFSPROC_DEALLOCATE 67
/*
* Must be defined as one higher than the last NFSv4.2 Proc# above.
*/
#define NFSV42_NPROCS 66
#define NFSV42_NPROCS 68
#endif /* NFS_V3NPROCS */
@ -447,7 +453,7 @@ struct nfsstatsv1 {
uint64_t readlink_bios;
uint64_t biocache_readdirs;
uint64_t readdir_bios;
uint64_t rpccnt[NFSV42_NPROCS + 14];
uint64_t rpccnt[NFSV42_NPROCS + 12];
uint64_t rpcretries;
uint64_t srvrpccnt[NFSV42_NOPS + NFSV4OP_FAKENOPS + 15];
uint64_t reserved_0;
@ -512,7 +518,7 @@ struct nfsstatsov1 {
uint64_t readlink_bios;
uint64_t biocache_readdirs;
uint64_t readdir_bios;
uint64_t rpccnt[NFSV42_NPROCS + 3];
uint64_t rpccnt[NFSV42_NPROCS + 1];
uint64_t rpcretries;
uint64_t srvrpccnt[NFSV42_PURENOPS + NFSV4OP_FAKENOPS];
uint64_t reserved_0;

View File

@ -396,10 +396,16 @@
/* BindConnectionToSession, done by the krpc for a new connection. */
#define NFSPROC_BINDCONNTOSESS 65
/* Do a Lookup+Open for "oneopenown". */
#define NFSPROC_LOOKUPOPEN 66
/* Do an NFSv4.2 Deallocate. */
#define NFSPROC_DEALLOCATE 67
/*
* Must be defined as one higher than the last NFSv4.2 Proc# above.
*/
#define NFSV42_NPROCS 66
#define NFSV42_NPROCS 68
#endif /* NFS_V3NPROCS */

View File

@ -1382,15 +1382,20 @@ nfsrpc_setattrrpc(vnode_t vp, struct vattr *vap,
int
nfsrpc_lookup(vnode_t dvp, char *name, int len, struct ucred *cred,
NFSPROC_T *p, struct nfsvattr *dnap, struct nfsvattr *nap,
struct nfsfh **nfhpp, int *attrflagp, int *dattrflagp, void *stuff)
struct nfsfh **nfhpp, int *attrflagp, int *dattrflagp, void *stuff,
uint32_t openmode)
{
u_int32_t *tl;
uint32_t deleg, rflags, *tl;
struct nfsrv_descript nfsd, *nd = &nfsd;
struct nfsmount *nmp;
struct nfsnode *np;
struct nfsfh *nfhp;
nfsattrbit_t attrbits;
int error = 0, lookupp = 0;
int error = 0, lookupp = 0, newone, ret, retop;
uint8_t own[NFSV4CL_LOCKNAMELEN];
struct nfsclopen *op;
struct nfscldeleg *ndp;
nfsv4stateid_t stateid;
*attrflagp = 0;
*dattrflagp = 0;
@ -1415,7 +1420,11 @@ nfsrpc_lookup(vnode_t dvp, char *name, int len, struct ucred *cred,
if (NFSHASNFSV4(nmp) && len == 2 &&
name[0] == '.' && name[1] == '.') {
lookupp = 1;
openmode = 0;
NFSCL_REQSTART(nd, NFSPROC_LOOKUPP, dvp);
} else if (openmode != 0) {
NFSCL_REQSTART(nd, NFSPROC_LOOKUPOPEN, dvp);
nfsm_strtom(nd, name, len);
} else {
NFSCL_REQSTART(nd, NFSPROC_LOOKUP, dvp);
(void) nfsm_strtom(nd, name, len);
@ -1426,10 +1435,36 @@ nfsrpc_lookup(vnode_t dvp, char *name, int len, struct ucred *cred,
*tl++ = txdr_unsigned(NFSV4OP_GETFH);
*tl = txdr_unsigned(NFSV4OP_GETATTR);
(void) nfsrv_putattrbit(nd, &attrbits);
if (openmode != 0) {
/* Test for a VREG file. */
NFSZERO_ATTRBIT(&attrbits);
NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_TYPE);
NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFSV4OP_VERIFY);
nfsrv_putattrbit(nd, &attrbits);
NFSM_BUILD(tl, uint32_t *, 2 * NFSX_UNSIGNED);
*tl++ = txdr_unsigned(NFSX_UNSIGNED);
*tl = vtonfsv34_type(VREG);
/* Attempt the Open for VREG. */
nfscl_filllockowner(NULL, own, F_POSIX);
NFSM_BUILD(tl, uint32_t *, 6 * NFSX_UNSIGNED);
*tl++ = txdr_unsigned(NFSV4OP_OPEN);
*tl++ = 0; /* seqid, ignored. */
*tl++ = txdr_unsigned(openmode);
*tl++ = txdr_unsigned(NFSV4OPEN_DENYNONE);
*tl++ = 0; /* ClientID, ignored. */
*tl = 0;
nfsm_strtom(nd, own, NFSV4CL_LOCKNAMELEN);
NFSM_BUILD(tl, uint32_t *, 2 * NFSX_UNSIGNED);
*tl++ = txdr_unsigned(NFSV4OPEN_NOCREATE);
*tl = txdr_unsigned(NFSV4OPEN_CLAIMFH);
}
}
error = nfscl_request(nd, dvp, p, cred, stuff);
if (error)
return (error);
ndp = NULL;
if (nd->nd_repstat) {
/*
* When an NFSv4 Lookupp returns ENOENT, it means that
@ -1453,6 +1488,33 @@ nfsrpc_lookup(vnode_t dvp, char *name, int len, struct ucred *cred,
error = nfsm_loadattr(nd, dnap);
if (error == 0)
*dattrflagp = 1;
else
goto nfsmout;
}
/* Check Lookup operation reply status. */
if (openmode != 0 && (nd->nd_flag & ND_NOMOREDATA) == 0) {
NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
if (*++tl != 0)
goto nfsmout;
}
/* Look for GetFH reply. */
if (openmode != 0 && (nd->nd_flag & ND_NOMOREDATA) == 0) {
NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
if (*++tl != 0)
goto nfsmout;
error = nfsm_getfh(nd, nfhpp);
if (error)
goto nfsmout;
}
/* Look for Getattr reply. */
if (openmode != 0 && (nd->nd_flag & ND_NOMOREDATA) == 0) {
NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
if (*++tl != 0)
goto nfsmout;
error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
if (error == 0)
/* Successfully got Lookup done. */
nd->nd_repstat = 0;
}
goto nfsmout;
}
@ -1470,12 +1532,84 @@ nfsrpc_lookup(vnode_t dvp, char *name, int len, struct ucred *cred,
goto nfsmout;
error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
if (openmode != 0 && error == 0) {
NFSM_DISSECT(tl, uint32_t *, NFSX_STATEID +
10 * NFSX_UNSIGNED);
tl += 4; /* Skip over Verify+Open status. */
stateid.seqid = *tl++;
stateid.other[0] = *tl++;
stateid.other[1] = *tl++;
stateid.other[2] = *tl;
rflags = fxdr_unsigned(uint32_t, *(tl + 6));
error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
if (error != 0)
goto nfsmout;
NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
deleg = fxdr_unsigned(uint32_t, *tl);
if (deleg == NFSV4OPEN_DELEGATEREAD ||
deleg == NFSV4OPEN_DELEGATEWRITE) {
/*
* Just need to fill in the fields used by
* nfscl_trydelegreturn().
* Mark the mount point as acquiring
* delegations, so NFSPROC_LOOKUPOPEN will
* no longer be done.
*/
NFSLOCKMNT(nmp);
nmp->nm_privflag |= NFSMNTP_DELEGISSUED;
NFSUNLOCKMNT(nmp);
ndp = malloc(sizeof(struct nfscldeleg) +
(*nfhpp)->nfh_len, M_NFSCLDELEG, M_WAITOK);
ndp->nfsdl_fhlen = (*nfhpp)->nfh_len;
NFSBCOPY((*nfhpp)->nfh_fh, ndp->nfsdl_fh,
ndp->nfsdl_fhlen);
newnfs_copyincred(cred, &ndp->nfsdl_cred);
NFSM_DISSECT(tl, uint32_t *, NFSX_STATEID);
ndp->nfsdl_stateid.seqid = *tl++;
ndp->nfsdl_stateid.other[0] = *tl++;
ndp->nfsdl_stateid.other[1] = *tl++;
ndp->nfsdl_stateid.other[2] = *tl++;
} else if (deleg != NFSV4OPEN_DELEGATENONE) {
error = NFSERR_BADXDR;
goto nfsmout;
}
ret = nfscl_open(dvp, (*nfhpp)->nfh_fh, (*nfhpp)->nfh_len,
openmode, 0, cred, p, NULL, &op, &newone, &retop, 1);
if (ret != 0)
goto nfsmout;
if (newone != 0) {
op->nfso_stateid.seqid = stateid.seqid;
op->nfso_stateid.other[0] = stateid.other[0];
op->nfso_stateid.other[1] = stateid.other[1];
op->nfso_stateid.other[2] = stateid.other[2];
op->nfso_mode = openmode;
} else {
op->nfso_stateid.seqid = stateid.seqid;
if (retop == NFSCLOPEN_DOOPEN)
op->nfso_mode |= openmode;
}
if ((rflags & NFSV4OPEN_LOCKTYPEPOSIX) != 0 ||
nfscl_assumeposixlocks)
op->nfso_posixlock = 1;
else
op->nfso_posixlock = 0;
nfscl_openrelease(nmp, op, 0, 0);
if (ndp != NULL) {
/*
* Since we do not have the vnode, we
* cannot invalidate cached attributes.
* Just return the delegation.
*/
nfscl_trydelegreturn(ndp, cred, nmp, p);
}
}
if ((nd->nd_flag & ND_NFSV3) && !error)
error = nfscl_postop_attr(nd, dnap, dattrflagp, stuff);
nfsmout:
m_freem(nd->nd_mrep);
if (!error && nd->nd_repstat)
error = nd->nd_repstat;
free(ndp, M_NFSCLDELEG);
return (error);
}

View File

@ -162,8 +162,6 @@ static int nfscl_recalldeleg(struct nfsclclient *, struct nfsmount *,
vnode_t *);
static void nfscl_freeopenowner(struct nfsclowner *, int);
static void nfscl_cleandeleg(struct nfscldeleg *);
static int nfscl_trydelegreturn(struct nfscldeleg *, struct ucred *,
struct nfsmount *, NFSPROC_T *);
static void nfscl_emptylockowner(struct nfscllockowner *,
struct nfscllockownerfhhead *);
static void nfscl_mergeflayouts(struct nfsclflayouthead *,
@ -4463,7 +4461,7 @@ nfscl_trylock(struct nfsmount *nmp, vnode_t vp, u_int8_t *fhp,
* retrying while NFSERR_DELAY. Also, try system credentials, if the passed in
* credentials fail.
*/
static int
int
nfscl_trydelegreturn(struct nfscldeleg *dp, struct ucred *cred,
struct nfsmount *nmp, NFSPROC_T *p)
{

View File

@ -1164,6 +1164,7 @@ nfs_lookup(struct vop_lookup_args *ap)
struct nfsvattr dnfsva, nfsva;
struct vattr vattr;
struct timespec nctime;
uint32_t openmode;
*vpp = NULLVP;
if ((flags & ISLASTCN) && (mp->mnt_flag & MNT_RDONLY) &&
@ -1265,11 +1266,30 @@ nfs_lookup(struct vop_lookup_args *ap)
cache_purge_negative(dvp);
}
/*
* If this an NFSv4.1/4.2 mount using the "oneopenown" mount
* option, it is possible to do the Open operation in the same
* compound as Lookup, so long as delegations are not being
* issued. This saves doing a separate RPC for Open.
*/
openmode = 0;
NFSLOCKMNT(nmp);
if (NFSHASNFSV4N(nmp) && NFSHASONEOPENOWN(nmp) &&
(nmp->nm_privflag & NFSMNTP_DELEGISSUED) == 0 &&
(!NFSMNT_RDONLY(mp) || (flags & OPENWRITE) == 0) &&
(flags & (ISLASTCN | ISOPEN)) == (ISLASTCN | ISOPEN)) {
if ((flags & OPENREAD) != 0)
openmode |= NFSV4OPEN_ACCESSREAD;
if ((flags & OPENWRITE) != 0)
openmode |= NFSV4OPEN_ACCESSWRITE;
}
NFSUNLOCKMNT(nmp);
newvp = NULLVP;
NFSINCRGLOBAL(nfsstatsv1.lookupcache_misses);
error = nfsrpc_lookup(dvp, cnp->cn_nameptr, cnp->cn_namelen,
cnp->cn_cred, td, &dnfsva, &nfsva, &nfhp, &attrflag, &dattrflag,
NULL);
NULL, openmode);
if (dattrflag)
(void) nfscl_loadattrcache(&dvp, &dnfsva, NULL, NULL, 0, 1);
if (error) {
@ -1577,7 +1597,7 @@ nfs_mknodrpc(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp,
(void) nfsrpc_lookup(dvp, cnp->cn_nameptr,
cnp->cn_namelen, cnp->cn_cred, cnp->cn_thread,
&dnfsva, &nfsva, &nfhp, &attrflag, &dattrflag,
NULL);
NULL, 0);
if (nfhp)
error = nfscl_nget(dvp->v_mount, dvp, nfhp, cnp,
cnp->cn_thread, &np, NULL, LK_EXCLUSIVE);
@ -1693,7 +1713,7 @@ nfs_create(struct vop_create_args *ap)
(void) nfsrpc_lookup(dvp, cnp->cn_nameptr,
cnp->cn_namelen, cnp->cn_cred, cnp->cn_thread,
&dnfsva, &nfsva, &nfhp, &attrflag, &dattrflag,
NULL);
NULL, 0);
if (nfhp != NULL)
error = nfscl_nget(dvp->v_mount, dvp, nfhp, cnp,
cnp->cn_thread, &np, NULL, LK_EXCLUSIVE);
@ -2611,7 +2631,7 @@ nfs_lookitup(struct vnode *dvp, char *name, int len, struct ucred *cred,
u_int hash;
error = nfsrpc_lookup(dvp, name, len, cred, td, &dnfsva, &nfsva,
&nfhp, &attrflag, &dattrflag, NULL);
&nfhp, &attrflag, &dattrflag, NULL, 0);
if (dattrflag)
(void) nfscl_loadattrcache(&dvp, &dnfsva, NULL, NULL, 0, 1);
if (npp && !error) {