git: 08b9cc316a31 - main - nfscl: Add a VOP_DEALLOCATE() for the NFSv4.2 client
Rick Macklem
rmacklem at FreeBSD.org
Sat Aug 28 01:35:29 UTC 2021
The branch main has been updated by rmacklem:
URL: https://cgit.FreeBSD.org/src/commit/?id=08b9cc316a319fba95af8bb13b262fe0d5526ec3
commit 08b9cc316a319fba95af8bb13b262fe0d5526ec3
Author: Rick Macklem <rmacklem at FreeBSD.org>
AuthorDate: 2021-08-28 01:31:36 +0000
Commit: Rick Macklem <rmacklem at FreeBSD.org>
CommitDate: 2021-08-28 01:31:36 +0000
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
---
sys/fs/nfs/nfs_var.h | 2 +
sys/fs/nfsclient/nfs_clrpcops.c | 112 ++++++++++++++++++++++++++++++++++++++++
sys/fs/nfsclient/nfs_clvnops.c | 79 ++++++++++++++++++++++++++++
sys/fs/nfsclient/nfsmount.h | 1 +
4 files changed, 194 insertions(+)
diff --git a/sys/fs/nfs/nfs_var.h b/sys/fs/nfs/nfs_var.h
index 5ddae345f906..bae4affa72e2 100644
--- a/sys/fs/nfs/nfs_var.h
+++ b/sys/fs/nfs/nfs_var.h
@@ -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 *);
diff --git a/sys/fs/nfsclient/nfs_clrpcops.c b/sys/fs/nfsclient/nfs_clrpcops.c
index de4030876bfe..f9636bc28365 100644
--- a/sys/fs/nfsclient/nfs_clrpcops.c
+++ b/sys/fs/nfsclient/nfs_clrpcops.c
@@ -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 @@ nfsmout:
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
diff --git a/sys/fs/nfsclient/nfs_clvnops.c b/sys/fs/nfsclient/nfs_clvnops.c
index 39340692563f..bfd77d50bc91 100644
--- a/sys/fs/nfsclient/nfs_clvnops.c
+++ b/sys/fs/nfsclient/nfs_clvnops.c
@@ -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
*/
diff --git a/sys/fs/nfsclient/nfsmount.h b/sys/fs/nfsclient/nfsmount.h
index a5997e474be9..d5771f157034 100644
--- a/sys/fs/nfsclient/nfsmount.h
+++ b/sys/fs/nfsclient/nfsmount.h
@@ -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
More information about the dev-commits-src-all
mailing list