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:
parent
da779f262c
commit
08b9cc316a
@ -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 *);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user