nfscl: Add a VOP_DEALLOCATE() for the NFSv4.2 client

This patch adds a VOP_DEALLOCATE() to the NFS client.
For NFSv4.2 servers that support the Deallocate operation,
it is used. Otherwise, it falls back on calling
vop_stddeallocate().

Reviewed by:	kib
Differential Revision:	https://reviews.freebsd.org/D31640
This commit is contained in:
Rick Macklem 2021-08-27 18:31:36 -07:00
parent da779f262c
commit 08b9cc316a
4 changed files with 194 additions and 0 deletions

View File

@ -553,6 +553,8 @@ int nfscl_findlayoutforio(struct nfscllayout *, uint64_t, uint32_t,
void nfscl_freenfsclds(struct nfsclds *);
int nfsrpc_allocate(vnode_t, off_t, off_t, struct nfsvattr *, int *,
struct ucred *, NFSPROC_T *, void *);
int nfsrpc_deallocate(vnode_t, off_t, off_t, struct nfsvattr *, int *,
struct ucred *, NFSPROC_T *, void *);
int nfsrpc_copy_file_range(vnode_t, off_t *, vnode_t, off_t *, size_t *,
unsigned int, int *, struct nfsvattr *, int *, struct nfsvattr *,
struct ucred *, bool, bool *);

View File

@ -132,6 +132,8 @@ static int nfsrpc_readrpc(vnode_t , struct uio *, struct ucred *,
static int nfsrpc_writerpc(vnode_t , struct uio *, int *, int *,
struct ucred *, nfsv4stateid_t *, NFSPROC_T *, struct nfsvattr *, int *,
void *);
static int nfsrpc_deallocaterpc(vnode_t, off_t, off_t, nfsv4stateid_t *,
struct nfsvattr *, int *, struct ucred *, NFSPROC_T *, void *);
static int nfsrpc_createv23(vnode_t , char *, int, struct vattr *,
nfsquad_t, int, struct ucred *, NFSPROC_T *, struct nfsvattr *,
struct nfsvattr *, struct nfsfh **, int *, int *, void *);
@ -2089,6 +2091,116 @@ nfsrpc_writerpc(vnode_t vp, struct uio *uiop, int *iomode,
return (error);
}
/*
* Do an nfs deallocate operation.
*/
int
nfsrpc_deallocate(vnode_t vp, off_t offs, off_t len, struct nfsvattr *nap,
int *attrflagp, struct ucred *cred, NFSPROC_T *p, void *stuff)
{
int error, expireret = 0, openerr, retrycnt;
uint32_t clidrev = 0;
struct nfsmount *nmp = VFSTONFS(vp->v_mount);
struct nfsfh *nfhp;
nfsv4stateid_t stateid;
void *lckp;
if (nmp->nm_clp != NULL)
clidrev = nmp->nm_clp->nfsc_clientidrev;
retrycnt = 0;
do {
lckp = NULL;
openerr = 1;
nfhp = VTONFS(vp)->n_fhp;
error = nfscl_getstateid(vp, nfhp->nfh_fh, nfhp->nfh_len,
NFSV4OPEN_ACCESSWRITE, 0, cred, p, &stateid, &lckp);
if (error != 0) {
/*
* No Open stateid, so try and open the file
* now.
*/
openerr = nfsrpc_open(vp, FWRITE, cred, p);
if (openerr == 0)
nfscl_getstateid(vp, nfhp->nfh_fh,
nfhp->nfh_len, NFSV4OPEN_ACCESSWRITE, 0,
cred, p, &stateid, &lckp);
}
error = nfsrpc_deallocaterpc(vp, offs, len, &stateid, nap,
attrflagp, cred, p, stuff);
if (error == NFSERR_STALESTATEID)
nfscl_initiate_recovery(nmp->nm_clp);
if (lckp != NULL)
nfscl_lockderef(lckp);
if (openerr == 0)
nfsrpc_close(vp, 0, p);
if (error == NFSERR_GRACE || error == NFSERR_STALESTATEID ||
error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY ||
error == NFSERR_OLDSTATEID || error == NFSERR_BADSESSION) {
(void) nfs_catnap(PZERO, error, "nfs_deallocate");
} else if ((error == NFSERR_EXPIRED ||
error == NFSERR_BADSTATEID) && clidrev != 0) {
expireret = nfscl_hasexpired(nmp->nm_clp, clidrev, p);
}
retrycnt++;
} while (error == NFSERR_GRACE || error == NFSERR_STALESTATEID ||
error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY ||
error == NFSERR_BADSESSION ||
(error == NFSERR_OLDSTATEID && retrycnt < 20) ||
((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID) &&
expireret == 0 && clidrev != 0 && retrycnt < 4));
if (error && retrycnt >= 4)
error = EIO;
return (error);
}
/*
* The actual deallocate RPC.
*/
static int
nfsrpc_deallocaterpc(vnode_t vp, off_t offs, off_t len,
nfsv4stateid_t *stateidp, struct nfsvattr *nap, int *attrflagp,
struct ucred *cred, NFSPROC_T *p, void *stuff)
{
uint32_t *tl;
struct nfsnode *np = VTONFS(vp);
int error, wccflag;
struct nfsrv_descript nfsd;
struct nfsrv_descript *nd = &nfsd;
nfsattrbit_t attrbits;
*attrflagp = 0;
NFSCL_REQSTART(nd, NFSPROC_DEALLOCATE, vp);
nfsm_stateidtom(nd, stateidp, NFSSTATEID_PUTSTATEID);
NFSM_BUILD(tl, uint32_t *, 2 * NFSX_HYPER);
txdr_hyper(offs, tl);
tl += 2;
txdr_hyper(len, tl);
NFSWRITEGETATTR_ATTRBIT(&attrbits);
NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFSV4OP_GETATTR);
nfsrv_putattrbit(nd, &attrbits);
error = nfscl_request(nd, vp, p, cred, stuff);
if (error != 0)
return (error);
wccflag = 0;
error = nfscl_wcc_data(nd, vp, nap, attrflagp, &wccflag, stuff);
if (error != 0)
goto nfsmout;
if (nd->nd_repstat == 0) {
NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
error = nfsm_loadattr(nd, nap);
if (error != 0)
goto nfsmout;
*attrflagp = NFS_LATTR_NOSHRINK;
}
NFSWRITERPC_SETTIME(wccflag, np, nap, 1);
nfsmout:
m_freem(nd->nd_mrep);
if (nd->nd_repstat != 0 && error == 0)
error = nd->nd_repstat;
return (error);
}
/*
* nfs mknod rpc
* For NFS v2 this is a kludge. Use a create rpc but with the IFMT bits of the

View File

@ -146,6 +146,7 @@ static vop_getacl_t nfs_getacl;
static vop_setacl_t nfs_setacl;
static vop_advise_t nfs_advise;
static vop_allocate_t nfs_allocate;
static vop_deallocate_t nfs_deallocate;
static vop_copy_file_range_t nfs_copy_file_range;
static vop_ioctl_t nfs_ioctl;
static vop_getextattr_t nfs_getextattr;
@ -193,6 +194,7 @@ static struct vop_vector newnfs_vnodeops_nosig = {
.vop_setacl = nfs_setacl,
.vop_advise = nfs_advise,
.vop_allocate = nfs_allocate,
.vop_deallocate = nfs_deallocate,
.vop_copy_file_range = nfs_copy_file_range,
.vop_ioctl = nfs_ioctl,
.vop_getextattr = nfs_getextattr,
@ -3681,6 +3683,83 @@ nfs_allocate(struct vop_allocate_args *ap)
return (error);
}
/*
* nfs deallocate call
*/
static int
nfs_deallocate(struct vop_deallocate_args *ap)
{
struct vnode *vp = ap->a_vp;
struct thread *td = curthread;
struct nfsvattr nfsva;
struct nfsmount *nmp;
off_t tlen;
int attrflag, error, ret;
error = 0;
attrflag = 0;
nmp = VFSTONFS(vp->v_mount);
mtx_lock(&nmp->nm_mtx);
if (NFSHASNFSV4(nmp) && nmp->nm_minorvers >= NFSV42_MINORVERSION &&
(nmp->nm_privflag & NFSMNTP_NODEALLOCATE) == 0) {
mtx_unlock(&nmp->nm_mtx);
tlen = omin(OFF_MAX - *ap->a_offset, *ap->a_len);
NFSCL_DEBUG(4, "dealloc: off=%jd len=%jd maxfilesize=%ju\n",
(intmax_t)*ap->a_offset, (intmax_t)tlen,
(uintmax_t)nmp->nm_maxfilesize);
if ((uint64_t)*ap->a_offset >= nmp->nm_maxfilesize) {
/* Avoid EFBIG error return from the NFSv4.2 server. */
*ap->a_len = 0;
return (0);
}
if ((uint64_t)*ap->a_offset + tlen > nmp->nm_maxfilesize)
tlen = nmp->nm_maxfilesize - *ap->a_offset;
if (error == 0)
error = ncl_vinvalbuf(vp, V_SAVE, td, 1);
if (error == 0) {
vnode_pager_purge_range(vp, *ap->a_offset,
*ap->a_offset + tlen);
error = nfsrpc_deallocate(vp, *ap->a_offset, tlen,
&nfsva, &attrflag, ap->a_cred, td, NULL);
NFSCL_DEBUG(4, "dealloc: rpc=%d\n", error);
}
if (error == 0) {
NFSCL_DEBUG(4, "dealloc: attrflag=%d na_size=%ju\n",
attrflag, (uintmax_t)nfsva.na_size);
if (attrflag != 0) {
if ((uint64_t)*ap->a_offset < nfsva.na_size)
*ap->a_offset += omin((off_t)
nfsva.na_size - *ap->a_offset,
tlen);
}
*ap->a_len = 0;
} else if (error == NFSERR_NOTSUPP) {
mtx_lock(&nmp->nm_mtx);
nmp->nm_privflag |= NFSMNTP_NODEALLOCATE;
mtx_unlock(&nmp->nm_mtx);
}
} else {
mtx_unlock(&nmp->nm_mtx);
error = EIO;
}
/*
* If the NFS server cannot perform the Deallocate operation, just call
* vop_stddeallocate() to perform it.
*/
if (error != 0 && error != NFSERR_FBIG && error != NFSERR_INVAL) {
error = vop_stddeallocate(ap);
NFSCL_DEBUG(4, "dealloc: stddeallocate=%d\n", error);
}
if (attrflag != 0) {
ret = nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0, 1);
if (error == 0 && ret != 0)
error = ret;
}
if (error != 0)
error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0);
return (error);
}
/*
* nfs copy_file_range call
*/

View File

@ -124,6 +124,7 @@ struct nfsmount {
#define NFSMNTP_NOADVISE 0x00000100
#define NFSMNTP_NOALLOCATE 0x00000200
#define NFSMNTP_DELEGISSUED 0x00000400
#define NFSMNTP_NODEALLOCATE 0x00000800
/* New mount flags only used by the kernel via nmount(2). */
#define NFSMNT_TLS 0x00000001