aboutsummaryrefslogtreecommitdiff
path: root/sys/fs/nfsclient
diff options
context:
space:
mode:
Diffstat (limited to 'sys/fs/nfsclient')
-rw-r--r--sys/fs/nfsclient/nfs_clcomsubs.c2
-rw-r--r--sys/fs/nfsclient/nfs_clport.c19
-rw-r--r--sys/fs/nfsclient/nfs_clrpcops.c237
-rw-r--r--sys/fs/nfsclient/nfs_clstate.c2
-rw-r--r--sys/fs/nfsclient/nfs_clvfsops.c10
-rw-r--r--sys/fs/nfsclient/nfs_clvnops.c143
-rw-r--r--sys/fs/nfsclient/nfsmount.h1
7 files changed, 353 insertions, 61 deletions
diff --git a/sys/fs/nfsclient/nfs_clcomsubs.c b/sys/fs/nfsclient/nfs_clcomsubs.c
index bca0bdcd0df1..05963074e53d 100644
--- a/sys/fs/nfsclient/nfs_clcomsubs.c
+++ b/sys/fs/nfsclient/nfs_clcomsubs.c
@@ -272,7 +272,7 @@ nfsm_loadattr(struct nfsrv_descript *nd, struct nfsvattr *nap)
if (nd->nd_flag & ND_NFSV4) {
error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0, NULL,
NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL,
- NULL);
+ NULL, NULL);
} else if (nd->nd_flag & ND_NFSV3) {
NFSM_DISSECT(fp, struct nfs_fattr *, NFSX_V3FATTR);
nap->na_type = nfsv34tov_type(fp->fa_type);
diff --git a/sys/fs/nfsclient/nfs_clport.c b/sys/fs/nfsclient/nfs_clport.c
index b25d967982a1..e9f1dc23ddbe 100644
--- a/sys/fs/nfsclient/nfs_clport.c
+++ b/sys/fs/nfsclient/nfs_clport.c
@@ -412,7 +412,7 @@ nfscl_warn_fileid(struct nfsmount *nmp, struct nfsvattr *oldnap,
}
void
-ncl_copy_vattr(struct vattr *dst, struct vattr *src)
+ncl_copy_vattr(struct vnode *vp, struct vattr *dst, struct vattr *src)
{
dst->va_type = src->va_type;
dst->va_mode = src->va_mode;
@@ -429,7 +429,7 @@ ncl_copy_vattr(struct vattr *dst, struct vattr *src)
dst->va_birthtime = src->va_birthtime;
dst->va_gen = src->va_gen;
dst->va_flags = src->va_flags;
- dst->va_rdev = src->va_rdev;
+ dst->va_rdev = VN_ISDEV(vp) ? src->va_rdev : NODEV;
dst->va_bytes = src->va_bytes;
dst->va_filerev = src->va_filerev;
}
@@ -595,7 +595,7 @@ nfscl_loadattrcache(struct vnode **vpp, struct nfsvattr *nap, void *nvaper,
KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(vp);
}
if (vaper != NULL) {
- ncl_copy_vattr(vaper, vap);
+ ncl_copy_vattr(vp, vaper, vap);
if (np->n_flag & NCHG) {
if (np->n_flag & NACC)
vaper->va_atime = np->n_atim;
@@ -828,7 +828,7 @@ nfscl_wcc_data(struct nfsrv_descript *nd, struct vnode *vp,
== (ND_NFSV4 | ND_V4WCCATTR)) {
error = nfsv4_loadattr(nd, NULL, &nfsva, NULL,
NULL, 0, NULL, NULL, NULL, NULL, NULL, 0,
- NULL, NULL, NULL, NULL, NULL, NULL);
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL);
if (error)
return (error);
/*
@@ -963,7 +963,8 @@ nfscl_loadsbinfo(struct nfsmount *nmp, struct nfsstatfs *sfp, void *statfs)
* Use the fsinfo stuff to update the mount point.
*/
void
-nfscl_loadfsinfo(struct nfsmount *nmp, struct nfsfsinfo *fsp)
+nfscl_loadfsinfo(struct nfsmount *nmp, struct nfsfsinfo *fsp,
+ uint32_t clone_blksize)
{
if ((nmp->nm_wsize == 0 || fsp->fs_wtpref < nmp->nm_wsize) &&
@@ -1003,6 +1004,14 @@ nfscl_loadfsinfo(struct nfsmount *nmp, struct nfsfsinfo *fsp)
fsp->fs_maxfilesize < nmp->nm_maxfilesize)
nmp->nm_maxfilesize = fsp->fs_maxfilesize;
nmp->nm_mountp->mnt_stat.f_iosize = newnfs_iosize(nmp);
+
+ /*
+ * Although ZFS reports a clone_blksize of 16Mbytes,
+ * 128Kbytes usually works, so set it to that.
+ */
+ if (clone_blksize > 128 * 1024)
+ clone_blksize = 128 * 1024;
+ nmp->nm_cloneblksize = clone_blksize;
nmp->nm_state |= NFSSTA_GOTFSINFO;
}
diff --git a/sys/fs/nfsclient/nfs_clrpcops.c b/sys/fs/nfsclient/nfs_clrpcops.c
index 920fcf7b8c61..4ec621de2eff 100644
--- a/sys/fs/nfsclient/nfs_clrpcops.c
+++ b/sys/fs/nfsclient/nfs_clrpcops.c
@@ -225,6 +225,9 @@ static int nfsrpc_layoutgetres(struct nfsmount *, vnode_t, uint8_t *,
static int nfsrpc_copyrpc(vnode_t, off_t, vnode_t, off_t, size_t *,
nfsv4stateid_t *, nfsv4stateid_t *, struct nfsvattr *, int *,
struct nfsvattr *, int *, bool, int *, struct ucred *, NFSPROC_T *);
+static int nfsrpc_clonerpc(vnode_t, off_t, vnode_t, off_t, size_t *, bool,
+ nfsv4stateid_t *, nfsv4stateid_t *, struct nfsvattr *, int *,
+ struct nfsvattr *, int *, struct ucred *, NFSPROC_T *);
static int nfsrpc_seekrpc(vnode_t, off_t *, nfsv4stateid_t *, bool *,
int, struct nfsvattr *, int *, struct ucred *);
static struct mbuf *nfsm_split(struct mbuf *, uint64_t);
@@ -696,7 +699,7 @@ nfsrpc_openrpc(struct nfsmount *nmp, vnode_t vp, u_int8_t *nfhp, int fhlen,
("nfsrpc_openrpc: Getattr repstat"));
error = nfsv4_loadattr(nd, NULL, &nfsva, NULL,
NULL, 0, NULL, NULL, NULL, NULL, NULL, 0,
- NULL, NULL, NULL, NULL, p, cred);
+ NULL, NULL, NULL, NULL, NULL, p, cred);
if (error)
goto nfsmout;
}
@@ -1355,7 +1358,7 @@ nfsrpc_getattrnovp(struct nfsmount *nmp, u_int8_t *fhp, int fhlen, int syscred,
if ((nd->nd_flag & ND_NFSV4) != 0)
error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0,
NULL, NULL, NULL, NULL, NULL, 0, NULL, leasep, NULL,
- NULL, NULL, NULL);
+ NULL, NULL, NULL, NULL);
else
error = nfsm_loadattr(nd, nap);
} else
@@ -3597,7 +3600,7 @@ nfsrpc_readdir(vnode_t vp, struct uio *uiop, nfsuint64 *cookiep,
nfsva.na_mntonfileno = UINT64_MAX;
error = nfsv4_loadattr(nd, NULL, &nfsva, NULL,
NULL, 0, NULL, NULL, NULL, NULL, NULL, 0,
- NULL, NULL, NULL, NULL, p, cred);
+ NULL, NULL, NULL, NULL, NULL, p, cred);
if (error) {
dotdotfileid = dotfileid;
} else if (gotmnton) {
@@ -3847,7 +3850,7 @@ nfsrpc_readdir(vnode_t vp, struct uio *uiop, nfsuint64 *cookiep,
nfsva.na_mntonfileno = UINT64_MAX;
error = nfsv4_loadattr(nd, NULL, &nfsva, NULL,
NULL, 0, NULL, NULL, NULL, NULL, NULL, 0,
- NULL, NULL, &rderr, NULL, p, cred);
+ NULL, NULL, &rderr, NULL, NULL, p, cred);
if (error)
goto nfsmout;
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
@@ -4072,7 +4075,7 @@ nfsrpc_readdirplus(vnode_t vp, struct uio *uiop, nfsuint64 *cookiep,
nfsva.na_mntonfileno = UINT64_MAX;
error = nfsv4_loadattr(nd, NULL, &nfsva, NULL,
NULL, 0, NULL, NULL, NULL, NULL, NULL, 0,
- NULL, NULL, NULL, NULL, p, cred);
+ NULL, NULL, NULL, NULL, NULL, p, cred);
if (error) {
dotdotfileid = dotfileid;
} else if (gotmnton) {
@@ -4346,7 +4349,7 @@ nfsrpc_readdirplus(vnode_t vp, struct uio *uiop, nfsuint64 *cookiep,
nfsva.na_mntonfileno = 0xffffffff;
error = nfsv4_loadattr(nd, NULL, &nfsva, &nfhp,
NULL, 0, NULL, NULL, NULL, NULL, NULL, 0,
- NULL, NULL, &rderr, NULL, p, cred);
+ NULL, NULL, &rderr, NULL, NULL, p, cred);
if (error)
goto nfsmout;
}
@@ -4981,8 +4984,8 @@ nfsmout:
*/
int
nfsrpc_statfs(vnode_t vp, struct nfsstatfs *sbp, struct nfsfsinfo *fsp,
- uint32_t *leasep, struct ucred *cred, NFSPROC_T *p, struct nfsvattr *nap,
- int *attrflagp)
+ uint32_t *leasep, uint32_t *cloneblksizep, struct ucred *cred, NFSPROC_T *p,
+ struct nfsvattr *nap, int *attrflagp)
{
u_int32_t *tl = NULL;
struct nfsrv_descript nfsd, *nd = &nfsd;
@@ -4991,6 +4994,8 @@ nfsrpc_statfs(vnode_t vp, struct nfsstatfs *sbp, struct nfsfsinfo *fsp,
int error;
*attrflagp = 0;
+ if (cloneblksizep != NULL)
+ *cloneblksizep = 0;
nmp = VFSTONFS(vp->v_mount);
if (NFSHASNFSV4(nmp)) {
/*
@@ -5009,7 +5014,7 @@ nfsrpc_statfs(vnode_t vp, struct nfsstatfs *sbp, struct nfsfsinfo *fsp,
if (nd->nd_repstat == 0) {
error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0,
NULL, NULL, sbp, fsp, NULL, 0, NULL, leasep, NULL,
- NULL, p, cred);
+ NULL, cloneblksizep, p, cred);
if (!error) {
nmp->nm_fsid[0] = nap->na_filesid[0];
nmp->nm_fsid[1] = nap->na_filesid[1];
@@ -5063,7 +5068,8 @@ nfsmout:
*/
int
nfsrpc_pathconf(vnode_t vp, struct nfsv3_pathconf *pc, bool *has_namedattrp,
- struct ucred *cred, NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp)
+ uint32_t *clone_blksizep, struct ucred *cred, NFSPROC_T *p,
+ struct nfsvattr *nap, int *attrflagp)
{
struct nfsrv_descript nfsd, *nd = &nfsd;
struct nfsmount *nmp;
@@ -5074,6 +5080,7 @@ nfsrpc_pathconf(vnode_t vp, struct nfsv3_pathconf *pc, bool *has_namedattrp,
*has_namedattrp = false;
*attrflagp = 0;
+ *clone_blksizep = 0;
nmp = VFSTONFS(vp->v_mount);
if (NFSHASNFSV4(nmp)) {
np = VTONFS(vp);
@@ -5100,7 +5107,7 @@ nfsrpc_pathconf(vnode_t vp, struct nfsv3_pathconf *pc, bool *has_namedattrp,
if (nd->nd_repstat == 0) {
error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0,
pc, NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL,
- has_namedattrp, p, cred);
+ has_namedattrp, clone_blksizep, p, cred);
if (!error)
*attrflagp = 1;
} else {
@@ -5395,7 +5402,8 @@ nfsrpc_getacl(vnode_t vp, struct ucred *cred, NFSPROC_T *p, struct acl *aclp)
return (error);
if (!nd->nd_repstat)
error = nfsv4_loadattr(nd, vp, NULL, NULL, NULL, 0, NULL,
- NULL, NULL, NULL, aclp, 0, NULL, NULL, NULL, NULL, p, cred);
+ NULL, NULL, NULL, aclp, 0, NULL, NULL, NULL, NULL, NULL, p,
+ cred);
else
error = nd->nd_repstat;
m_freem(nd->nd_mrep);
@@ -5437,7 +5445,7 @@ nfsrpc_setaclrpc(vnode_t vp, struct ucred *cred, NFSPROC_T *p,
NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_ACL);
(void) nfsv4_fillattr(nd, vp->v_mount, vp, aclp, NULL, NULL, 0,
&attrbits, NULL, NULL, 0, 0, 0, 0, (uint64_t)0, NULL, false, false,
- false);
+ false, 0);
error = nfscl_request(nd, vp, p, cred);
if (error)
return (error);
@@ -8496,7 +8504,7 @@ nfsrpc_openlayoutrpc(struct nfsmount *nmp, vnode_t vp, u_int8_t *nfhp,
if (*++tl == 0) {
error = nfsv4_loadattr(nd, NULL, &nfsva, NULL,
NULL, 0, NULL, NULL, NULL, NULL, NULL, 0,
- NULL, NULL, NULL, NULL, p, cred);
+ NULL, NULL, NULL, NULL, NULL, p, cred);
if (error != 0)
goto nfsmout;
if (ndp != NULL) {
@@ -9168,6 +9176,199 @@ nfsmout:
}
/*
+ * nfs clone operation.
+ */
+int
+nfsrpc_clone(vnode_t invp, off_t *inoffp, vnode_t outvp,
+ off_t *outoffp, size_t *lenp, bool toeof, int *inattrflagp,
+ struct nfsvattr *innap, int *outattrflagp, struct nfsvattr *outnap,
+ struct ucred *cred)
+{
+ int error, expireret = 0, retrycnt;
+ uint32_t clidrev = 0;
+ struct nfsmount *nmp = VFSTONFS(invp->v_mount);
+ struct nfsfh *innfhp = NULL, *outnfhp = NULL;
+ nfsv4stateid_t instateid, outstateid;
+ void *inlckp, *outlckp;
+
+ if (nmp->nm_clp != NULL)
+ clidrev = nmp->nm_clp->nfsc_clientidrev;
+ innfhp = VTONFS(invp)->n_fhp;
+ outnfhp = VTONFS(outvp)->n_fhp;
+ retrycnt = 0;
+ do {
+ /* Get both stateids. */
+ inlckp = NULL;
+ nfscl_getstateid(invp, innfhp->nfh_fh, innfhp->nfh_len,
+ NFSV4OPEN_ACCESSREAD, 0, NULL, curthread, &instateid,
+ &inlckp);
+ outlckp = NULL;
+ nfscl_getstateid(outvp, outnfhp->nfh_fh, outnfhp->nfh_len,
+ NFSV4OPEN_ACCESSWRITE, 0, NULL, curthread, &outstateid,
+ &outlckp);
+
+ error = nfsrpc_clonerpc(invp, *inoffp, outvp, *outoffp, lenp,
+ toeof, &instateid, &outstateid, innap, inattrflagp, outnap,
+ outattrflagp, cred, curthread);
+ if (error == 0) {
+ *inoffp += *lenp;
+ *outoffp += *lenp;
+ } else if (error == NFSERR_STALESTATEID)
+ nfscl_initiate_recovery(nmp->nm_clp);
+ if (inlckp != NULL)
+ nfscl_lockderef(inlckp);
+ if (outlckp != NULL)
+ nfscl_lockderef(outlckp);
+ 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_cfr");
+ } else if ((error == NFSERR_EXPIRED || (!NFSHASINT(nmp) &&
+ error == NFSERR_BADSTATEID)) && clidrev != 0) {
+ expireret = nfscl_hasexpired(nmp->nm_clp, clidrev,
+ curthread);
+ } else if (error == NFSERR_BADSTATEID && NFSHASINT(nmp)) {
+ error = EIO;
+ }
+ retrycnt++;
+ } while (error == NFSERR_GRACE || error == NFSERR_DELAY ||
+ error == NFSERR_STALESTATEID || error == NFSERR_BADSESSION ||
+ error == NFSERR_STALEDONTRECOVER ||
+ (error == NFSERR_OLDSTATEID && retrycnt < 20) ||
+ ((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID) &&
+ expireret == 0 && clidrev != 0 && retrycnt < 4));
+ if (error != 0 && (retrycnt >= 4 ||
+ error == NFSERR_STALESTATEID || error == NFSERR_BADSESSION ||
+ error == NFSERR_STALEDONTRECOVER))
+ error = EIO;
+ return (error);
+}
+
+/*
+ * The clone RPC.
+ */
+static int
+nfsrpc_clonerpc(vnode_t invp, off_t inoff, vnode_t outvp, off_t outoff,
+ size_t *lenp, bool toeof, nfsv4stateid_t *instateidp,
+ nfsv4stateid_t *outstateidp, struct nfsvattr *innap, int *inattrflagp,
+ struct nfsvattr *outnap, int *outattrflagp, struct ucred *cred,
+ NFSPROC_T *p)
+{
+ uint32_t *tl, *opcntp;
+ int error;
+ struct nfsrv_descript nfsd;
+ struct nfsrv_descript *nd = &nfsd;
+ struct nfsmount *nmp;
+ nfsattrbit_t attrbits;
+ struct vattr va;
+ uint64_t len;
+
+ nmp = VFSTONFS(invp->v_mount);
+ *inattrflagp = *outattrflagp = 0;
+ len = *lenp;
+ if (len == 0)
+ return (0);
+ if (toeof)
+ len = 0;
+ nfscl_reqstart(nd, NFSPROC_CLONE, nmp, VTONFS(invp)->n_fhp->nfh_fh,
+ VTONFS(invp)->n_fhp->nfh_len, &opcntp, NULL, 0, 0, cred);
+ /*
+ * First do a Setattr of atime to the server's clock
+ * time. The FreeBSD "collective" was of the opinion
+ * that setting atime was necessary for this syscall.
+ * Do the Setattr before the Clone, so that it can be
+ * handled well if the server replies NFSERR_DELAY to
+ * the Setattr operation.
+ */
+ if ((nmp->nm_mountp->mnt_flag & MNT_NOATIME) == 0) {
+ NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
+ *tl = txdr_unsigned(NFSV4OP_SETATTR);
+ nfsm_stateidtom(nd, instateidp, NFSSTATEID_PUTSTATEID);
+ VATTR_NULL(&va);
+ va.va_atime.tv_sec = va.va_atime.tv_nsec = 0;
+ va.va_vaflags = VA_UTIMES_NULL;
+ nfscl_fillsattr(nd, &va, invp, 0, 0);
+ /* Bump opcnt from 7 to 8. */
+ *opcntp = txdr_unsigned(8);
+ }
+
+ /* Now Getattr the invp attributes. */
+ NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
+ *tl = txdr_unsigned(NFSV4OP_GETATTR);
+ NFSGETATTR_ATTRBIT(&attrbits);
+ nfsrv_putattrbit(nd, &attrbits);
+
+ /* Set outvp. */
+ NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
+ *tl = txdr_unsigned(NFSV4OP_PUTFH);
+ (void)nfsm_fhtom(nmp, nd, VTONFS(outvp)->n_fhp->nfh_fh,
+ VTONFS(outvp)->n_fhp->nfh_len, 0);
+
+ /* Do the Clone. */
+ NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
+ *tl = txdr_unsigned(NFSV4OP_CLONE);
+ nfsm_stateidtom(nd, instateidp, NFSSTATEID_PUTSTATEID);
+ nfsm_stateidtom(nd, outstateidp, NFSSTATEID_PUTSTATEID);
+ NFSM_BUILD(tl, uint32_t *, 3 * NFSX_HYPER + NFSX_UNSIGNED);
+ txdr_hyper(inoff, tl); tl += 2;
+ txdr_hyper(outoff, tl); tl += 2;
+ txdr_hyper(len, tl); tl += 2;
+
+ /* Get the outvp attributes. */
+ *tl = txdr_unsigned(NFSV4OP_GETATTR);
+ NFSWRITEGETATTR_ATTRBIT(&attrbits);
+ nfsrv_putattrbit(nd, &attrbits);
+
+ error = nfscl_request(nd, invp, p, cred);
+ if (error != 0)
+ return (error);
+ /* Skip over the Setattr reply. */
+ if ((nd->nd_flag & ND_NOMOREDATA) == 0 &&
+ (nmp->nm_mountp->mnt_flag & MNT_NOATIME) == 0) {
+ NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
+ if (*(tl + 1) == 0) {
+ error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
+ if (error != 0)
+ goto nfsmout;
+ } else
+ nd->nd_flag |= ND_NOMOREDATA;
+ }
+ if ((nd->nd_flag & ND_NOMOREDATA) == 0) {
+ /* Get the input file's attributes. */
+ NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
+ if (*(tl + 1) == 0) {
+ error = nfsm_loadattr(nd, innap);
+ if (error != 0)
+ goto nfsmout;
+ *inattrflagp = 1;
+ } else
+ nd->nd_flag |= ND_NOMOREDATA;
+ }
+ /* Skip over return stat for PutFH. */
+ if ((nd->nd_flag & ND_NOMOREDATA) == 0) {
+ NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
+ if (*++tl != 0)
+ nd->nd_flag |= ND_NOMOREDATA;
+ }
+ /* Skip over return stat for Clone. */
+ if ((nd->nd_flag & ND_NOMOREDATA) == 0)
+ NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
+ if (nd->nd_repstat == 0) {
+ NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
+ error = nfsm_loadattr(nd, outnap);
+ if (error == 0)
+ *outattrflagp = NFS_LATTR_NOSHRINK;
+ } else {
+ *lenp = 0;
+ }
+ if (error == 0)
+ error = nd->nd_repstat;
+nfsmout:
+ m_freem(nd->nd_mrep);
+ return (error);
+}
+
+/*
* Seek operation.
*/
int
@@ -9724,13 +9925,13 @@ nfscl_statfs(struct vnode *vp, struct ucred *cred, NFSPROC_T *td)
struct nfsstatfs sb;
struct mount *mp;
struct nfsmount *nmp;
- uint32_t lease;
+ uint32_t clone_blksize, lease;
int attrflag, error;
mp = vp->v_mount;
nmp = VFSTONFS(mp);
- error = nfsrpc_statfs(vp, &sb, &fs, &lease, cred, td, &nfsva,
- &attrflag);
+ error = nfsrpc_statfs(vp, &sb, &fs, &lease, &clone_blksize, cred, td,
+ &nfsva, &attrflag);
if (attrflag != 0)
(void) nfscl_loadattrcache(&vp, &nfsva, NULL, 0, 1);
if (error == 0) {
@@ -9739,7 +9940,7 @@ nfscl_statfs(struct vnode *vp, struct ucred *cred, NFSPROC_T *td)
nmp->nm_clp->nfsc_renew = NFSCL_RENEW(lease);
NFSUNLOCKCLSTATE();
mtx_lock(&nmp->nm_mtx);
- nfscl_loadfsinfo(nmp, &fs);
+ nfscl_loadfsinfo(nmp, &fs, clone_blksize);
nfscl_loadsbinfo(nmp, &sb, &mp->mnt_stat);
mp->mnt_stat.f_iosize = newnfs_iosize(nmp);
mtx_unlock(&nmp->nm_mtx);
diff --git a/sys/fs/nfsclient/nfs_clstate.c b/sys/fs/nfsclient/nfs_clstate.c
index 99a781640c53..aa9d01fc4632 100644
--- a/sys/fs/nfsclient/nfs_clstate.c
+++ b/sys/fs/nfsclient/nfs_clstate.c
@@ -3701,7 +3701,7 @@ nfscl_docb(struct nfsrv_descript *nd, NFSPROC_T *p)
if (!error)
(void) nfsv4_fillattr(nd, NULL, NULL, NULL, &va,
NULL, 0, &rattrbits, NULL, p, 0, 0, 0, 0,
- (uint64_t)0, NULL, false, false, false);
+ (uint64_t)0, NULL, false, false, false, 0);
break;
case NFSV4OP_CBRECALL:
NFSCL_DEBUG(4, "cbrecall\n");
diff --git a/sys/fs/nfsclient/nfs_clvfsops.c b/sys/fs/nfsclient/nfs_clvfsops.c
index 0bd05c03885b..5ea7eab07632 100644
--- a/sys/fs/nfsclient/nfs_clvfsops.c
+++ b/sys/fs/nfsclient/nfs_clvfsops.c
@@ -292,8 +292,10 @@ nfs_statfs(struct mount *mp, struct statfs *sbp)
int error = 0, attrflag, gotfsinfo = 0, ret;
struct nfsnode *np;
char *fakefh;
+ uint32_t clone_blksize;
td = curthread;
+ clone_blksize = 0;
error = vfs_busy(mp, MBF_NOWAIT);
if (error)
@@ -337,8 +339,8 @@ nfs_statfs(struct mount *mp, struct statfs *sbp)
} else
mtx_unlock(&nmp->nm_mtx);
if (!error)
- error = nfsrpc_statfs(vp, &sb, &fs, NULL, td->td_ucred, td,
- &nfsva, &attrflag);
+ error = nfsrpc_statfs(vp, &sb, &fs, NULL, &clone_blksize,
+ td->td_ucred, td, &nfsva, &attrflag);
if ((nmp->nm_privflag & NFSMNTP_FAKEROOTFH) != 0 &&
error == NFSERR_WRONGSEC) {
/* Cannot get new stats, so return what is in mnt_stat. */
@@ -375,7 +377,7 @@ nfs_statfs(struct mount *mp, struct statfs *sbp)
if (!error) {
mtx_lock(&nmp->nm_mtx);
if (gotfsinfo || (nmp->nm_flag & NFSMNT_NFSV4))
- nfscl_loadfsinfo(nmp, &fs);
+ nfscl_loadfsinfo(nmp, &fs, clone_blksize);
nfscl_loadsbinfo(nmp, &sb, sbp);
sbp->f_iosize = newnfs_iosize(nmp);
mtx_unlock(&nmp->nm_mtx);
@@ -408,7 +410,7 @@ ncl_fsinfo(struct nfsmount *nmp, struct vnode *vp, struct ucred *cred,
if (attrflag)
(void) nfscl_loadattrcache(&vp, &nfsva, NULL, 0, 1);
mtx_lock(&nmp->nm_mtx);
- nfscl_loadfsinfo(nmp, &fs);
+ nfscl_loadfsinfo(nmp, &fs, 0);
mtx_unlock(&nmp->nm_mtx);
}
return (error);
diff --git a/sys/fs/nfsclient/nfs_clvnops.c b/sys/fs/nfsclient/nfs_clvnops.c
index fa451887e73e..a8b06fdb261b 100644
--- a/sys/fs/nfsclient/nfs_clvnops.c
+++ b/sys/fs/nfsclient/nfs_clvnops.c
@@ -1026,7 +1026,7 @@ nfs_getattr(struct vop_getattr_args *ap)
* cached attributes should be ignored.
*/
if (nmp->nm_fhsize > 0 && ncl_getattrcache(vp, &vattr) == 0) {
- ncl_copy_vattr(vap, &vattr);
+ ncl_copy_vattr(vp, vap, &vattr);
/*
* Get the local modify time for the case of a write
@@ -1782,7 +1782,7 @@ nfs_mknodrpc(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp,
int error = 0, attrflag, dattrflag;
u_int32_t rdev;
- if (vap->va_type == VCHR || vap->va_type == VBLK)
+ if (VATTR_ISDEV(vap))
rdev = vap->va_rdev;
else if (vap->va_type == VFIFO || vap->va_type == VSOCK)
rdev = 0xffffffff;
@@ -4027,31 +4027,51 @@ nfs_copy_file_range(struct vop_copy_file_range_args *ap)
struct vattr va, *vap;
struct uio io;
struct nfsmount *nmp;
+ struct nfsnode *np;
size_t len, len2;
ssize_t r;
int error, inattrflag, outattrflag, ret, ret2, invp_lock;
off_t inoff, outoff;
- bool consecutive, must_commit, tryoutcred;
+ bool consecutive, must_commit, onevp, toeof, tryclone, tryoutcred;
+ bool mustclone;
/*
* NFSv4.2 Copy is not permitted for infile == outfile.
+ * The NFSv4.2 Clone operation does work on non-overlapping
+ * byte ranges in the same file, but only if offsets
+ * (and len if not to EOF) are aligned properly.
* TODO: copy_file_range() between multiple NFS mountpoints
+ * --> This is not possible now, since each mount appears to
+ * the NFSv4.n server as a separate client.
*/
- if (invp == outvp || invp->v_mount != outvp->v_mount) {
+ if ((invp == outvp && (ap->a_flags & COPY_FILE_RANGE_CLONE) == 0) ||
+ (invp != outvp && invp->v_mount != outvp->v_mount)) {
generic_copy:
return (ENOSYS);
}
-
- invp_lock = LK_SHARED;
+ if (invp == outvp) {
+ onevp = true;
+ invp_lock = LK_EXCLUSIVE;
+ } else {
+ onevp = false;
+ invp_lock = LK_SHARED;
+ }
+ mustclone = false;
+ if (onevp || (ap->a_flags & COPY_FILE_RANGE_CLONE) != 0)
+ mustclone = true;
relock:
+ inoff = *ap->a_inoffp;
+ outoff = *ap->a_outoffp;
- /* Lock both vnodes, avoiding risk of deadlock. */
+ /* Lock vnode(s), avoiding risk of deadlock. */
do {
mp = NULL;
error = vn_start_write(outvp, &mp, V_WAIT);
if (error == 0) {
error = vn_lock(outvp, LK_EXCLUSIVE);
if (error == 0) {
+ if (onevp)
+ break;
error = vn_lock(invp, invp_lock | LK_NOWAIT);
if (error == 0)
break;
@@ -4071,16 +4091,24 @@ relock:
return (error);
/*
- * More reasons to avoid nfs copy: not NFSv4.2, or explicitly
- * disabled.
+ * More reasons to avoid nfs copy/clone: not NFSv4.2, explicitly
+ * disabled or requires cloning and unable to clone.
+ * Only clone if the clone_blksize attribute is supported
+ * and the clone_blksize is greater than 0.
+ * Alignment of offsets and length will be checked later.
*/
nmp = VFSTONFS(invp->v_mount);
+ np = VTONFS(invp);
mtx_lock(&nmp->nm_mtx);
+ if ((nmp->nm_privflag & NFSMNTP_NOCOPY) != 0)
+ mustclone = true;
if (!NFSHASNFSV4(nmp) || nmp->nm_minorvers < NFSV42_MINORVERSION ||
- (nmp->nm_privflag & NFSMNTP_NOCOPY) != 0) {
+ (mustclone && (!NFSISSET_ATTRBIT(&np->n_vattr.na_suppattr,
+ NFSATTRBIT_CLONEBLKSIZE) || nmp->nm_cloneblksize == 0))) {
mtx_unlock(&nmp->nm_mtx);
VOP_UNLOCK(invp);
- VOP_UNLOCK(outvp);
+ if (!onevp)
+ VOP_UNLOCK(outvp); /* For onevp, same as invp. */
if (mp != NULL)
vn_finished_write(mp);
goto generic_copy;
@@ -4111,6 +4139,8 @@ relock:
invp_obj = invp->v_object;
if (invp_obj != NULL && vm_object_mightbedirty(invp_obj)) {
if (invp_lock != LK_EXCLUSIVE) {
+ KASSERT(!onevp, ("nfs_copy_file_range: "
+ "invp_lock LK_SHARED for onevp"));
invp_lock = LK_EXCLUSIVE;
VOP_UNLOCK(invp);
VOP_UNLOCK(outvp);
@@ -4134,10 +4164,10 @@ relock:
else
consecutive = false;
mtx_unlock(&nmp->nm_mtx);
- inoff = *ap->a_inoffp;
- outoff = *ap->a_outoffp;
tryoutcred = true;
must_commit = false;
+ toeof = false;
+
if (error == 0) {
vap = &VTONFS(invp)->n_vattr.na_vattr;
error = VOP_GETATTR(invp, vap, ap->a_incred);
@@ -4169,29 +4199,63 @@ relock:
if (error == 0 && ret != 0)
error = ret;
}
- } else if (inoff + len > vap->va_size)
+ } else if (inoff + len >= vap->va_size) {
+ toeof = true;
*ap->a_lenp = len = vap->va_size - inoff;
+ }
} else
error = 0;
}
/*
+ * For cloning, the offsets must be clone blksize aligned and
+ * the len must be blksize aligned unless it goes to EOF on
+ * the input file.
+ */
+ tryclone = false;
+ if (len > 0) {
+ if (error == 0 && NFSISSET_ATTRBIT(&np->n_vattr.na_suppattr,
+ NFSATTRBIT_CLONEBLKSIZE) && nmp->nm_cloneblksize != 0 &&
+ (inoff % nmp->nm_cloneblksize) == 0 &&
+ (outoff % nmp->nm_cloneblksize) == 0 &&
+ (toeof || (len % nmp->nm_cloneblksize) == 0))
+ tryclone = true;
+ else if (mustclone)
+ error = ENOSYS;
+ }
+
+ /*
* len will be set to 0 upon a successful Copy RPC.
- * As such, this only loops when the Copy RPC needs to be retried.
+ * As such, this only loops when the Copy/Clone RPC needs to be retried.
*/
while (len > 0 && error == 0) {
inattrflag = outattrflag = 0;
len2 = len;
- if (tryoutcred)
- error = nfsrpc_copy_file_range(invp, ap->a_inoffp,
- outvp, ap->a_outoffp, &len2, ap->a_flags,
- &inattrflag, &innfsva, &outattrflag, &outnfsva,
- ap->a_outcred, consecutive, &must_commit);
- else
- error = nfsrpc_copy_file_range(invp, ap->a_inoffp,
- outvp, ap->a_outoffp, &len2, ap->a_flags,
- &inattrflag, &innfsva, &outattrflag, &outnfsva,
- ap->a_incred, consecutive, &must_commit);
+ if (tryclone) {
+ if (tryoutcred)
+ error = nfsrpc_clone(invp, ap->a_inoffp, outvp,
+ ap->a_outoffp, &len2, toeof, &inattrflag,
+ &innfsva, &outattrflag, &outnfsva,
+ ap->a_outcred);
+ else
+ error = nfsrpc_clone(invp, ap->a_inoffp, outvp,
+ ap->a_outoffp, &len2, toeof, &inattrflag,
+ &innfsva, &outattrflag, &outnfsva,
+ ap->a_incred);
+ } else {
+ if (tryoutcred)
+ error = nfsrpc_copy_file_range(invp,
+ ap->a_inoffp, outvp, ap->a_outoffp, &len2,
+ ap->a_flags, &inattrflag, &innfsva,
+ &outattrflag, &outnfsva,
+ ap->a_outcred, consecutive, &must_commit);
+ else
+ error = nfsrpc_copy_file_range(invp,
+ ap->a_inoffp, outvp, ap->a_outoffp, &len2,
+ ap->a_flags, &inattrflag, &innfsva,
+ &outattrflag, &outnfsva,
+ ap->a_incred, consecutive, &must_commit);
+ }
if (inattrflag != 0)
ret = nfscl_loadattrcache(&invp, &innfsva, NULL, 0, 1);
if (outattrflag != 0)
@@ -4230,6 +4294,13 @@ relock:
/* Try again with incred. */
tryoutcred = false;
error = 0;
+ } else if (tryclone && error != 0) {
+ if (mustclone) {
+ error = ENOSYS;
+ } else {
+ tryclone = false;
+ error = 0;
+ }
}
if (error == NFSERR_STALEWRITEVERF) {
/*
@@ -4243,11 +4314,12 @@ relock:
}
}
VOP_UNLOCK(invp);
- VOP_UNLOCK(outvp);
+ if (!onevp)
+ VOP_UNLOCK(outvp); /* For onevp, same as invp. */
if (mp != NULL)
vn_finished_write(mp);
if (error == NFSERR_NOTSUPP || error == NFSERR_OFFLOADNOREQS ||
- error == NFSERR_ACCES) {
+ error == NFSERR_ACCES || error == ENOSYS) {
/*
* Unlike the NFSv4.2 Copy, vn_generic_copy_file_range() can
* use a_incred for the read and a_outcred for the write, so
@@ -4255,7 +4327,7 @@ relock:
* For NFSERR_NOTSUPP and NFSERR_OFFLOADNOREQS, the Copy can
* never succeed, so disable it.
*/
- if (error != NFSERR_ACCES) {
+ if (error != NFSERR_ACCES && error != ENOSYS) {
/* Can never do Copy on this mount. */
mtx_lock(&nmp->nm_mtx);
nmp->nm_privflag |= NFSMNTP_NOCOPY;
@@ -4596,6 +4668,7 @@ nfs_pathconf(struct vop_pathconf_args *ap)
struct nfsmount *nmp;
struct thread *td = curthread;
off_t off;
+ uint32_t clone_blksize;
bool eof, has_namedattr, named_enabled;
int attrflag, error;
struct nfsnode *np;
@@ -4604,19 +4677,22 @@ nfs_pathconf(struct vop_pathconf_args *ap)
np = VTONFS(vp);
named_enabled = false;
has_namedattr = false;
+ clone_blksize = 0;
if ((NFS_ISV34(vp) && (ap->a_name == _PC_LINK_MAX ||
ap->a_name == _PC_NAME_MAX || ap->a_name == _PC_CHOWN_RESTRICTED ||
ap->a_name == _PC_NO_TRUNC)) ||
(NFS_ISV4(vp) && (ap->a_name == _PC_ACL_NFS4 ||
- ap->a_name == _PC_HAS_NAMEDATTR))) {
+ ap->a_name == _PC_HAS_NAMEDATTR ||
+ ap->a_name == _PC_CLONE_BLKSIZE))) {
/*
* Since only the above 4 a_names are returned by the NFSv3
* Pathconf RPC, there is no point in doing it for others.
* For NFSv4, the Pathconf RPC (actually a Getattr Op.) can
- * be used for _PC_ACL_NFS4 and _PC_HAS_NAMEDATTR as well.
+ * be used for _PC_ACL_NFS4, _PC_HAS_NAMEDATTR and
+ * _PC_CLONE_BLKSIZE as well.
*/
- error = nfsrpc_pathconf(vp, &pc, &has_namedattr, td->td_ucred,
- td, &nfsva, &attrflag);
+ error = nfsrpc_pathconf(vp, &pc, &has_namedattr, &clone_blksize,
+ td->td_ucred, td, &nfsva, &attrflag);
if (attrflag != 0)
(void) nfscl_loadattrcache(&vp, &nfsva, NULL, 0, 1);
if (error != 0)
@@ -4771,6 +4847,9 @@ nfs_pathconf(struct vop_pathconf_args *ap)
else
*ap->a_retval = 0;
break;
+ case _PC_CLONE_BLKSIZE:
+ *ap->a_retval = clone_blksize;
+ break;
default:
error = vop_stdpathconf(ap);
diff --git a/sys/fs/nfsclient/nfsmount.h b/sys/fs/nfsclient/nfsmount.h
index 37b84a015dab..ef876dd30e59 100644
--- a/sys/fs/nfsclient/nfsmount.h
+++ b/sys/fs/nfsclient/nfsmount.h
@@ -87,6 +87,7 @@ struct nfsmount {
/* unclipped, wraps to 0 */
struct __rpc_client *nm_aconn[NFS_MAXNCONN - 1]; /* Additional nconn */
/* Locked via nm_sockreq.nr_mtx */
+ uint32_t nm_cloneblksize; /* Block cloning alignment */
u_int16_t nm_krbnamelen; /* Krb5 host principal, if any */
u_int16_t nm_dirpathlen; /* and mount dirpath, for V4 */
u_int16_t nm_srvkrbnamelen; /* and the server's target name */