diff --git a/sys/fs/nfs/nfs_commonsubs.c b/sys/fs/nfs/nfs_commonsubs.c index 817d8928409..20f5618c202 100644 --- a/sys/fs/nfs/nfs_commonsubs.c +++ b/sys/fs/nfs/nfs_commonsubs.c @@ -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); diff --git a/sys/fs/nfs/nfs_var.h b/sys/fs/nfs/nfs_var.h index 9cbaeae361a..b4c21c6f702 100644 --- a/sys/fs/nfs/nfs_var.h +++ b/sys/fs/nfs/nfs_var.h @@ -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 *); diff --git a/sys/fs/nfs/nfsport.h b/sys/fs/nfs/nfsport.h index cb82666ab39..64ba4d48b3f 100644 --- a/sys/fs/nfs/nfsport.h +++ b/sys/fs/nfs/nfsport.h @@ -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; diff --git a/sys/fs/nfs/nfsproto.h b/sys/fs/nfs/nfsproto.h index 13e14615480..7ac7cdc3bac 100644 --- a/sys/fs/nfs/nfsproto.h +++ b/sys/fs/nfs/nfsproto.h @@ -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 */ diff --git a/sys/fs/nfsclient/nfs_clrpcops.c b/sys/fs/nfsclient/nfs_clrpcops.c index 74803e255aa..de4030876bf 100644 --- a/sys/fs/nfsclient/nfs_clrpcops.c +++ b/sys/fs/nfsclient/nfs_clrpcops.c @@ -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); } diff --git a/sys/fs/nfsclient/nfs_clstate.c b/sys/fs/nfsclient/nfs_clstate.c index bb2c78a72ed..8ec5b80489f 100644 --- a/sys/fs/nfsclient/nfs_clstate.c +++ b/sys/fs/nfsclient/nfs_clstate.c @@ -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) { diff --git a/sys/fs/nfsclient/nfs_clvnops.c b/sys/fs/nfsclient/nfs_clvnops.c index 72d9eac8e96..39340692563 100644 --- a/sys/fs/nfsclient/nfs_clvnops.c +++ b/sys/fs/nfsclient/nfs_clvnops.c @@ -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) {