diff --git a/sys/fs/nfs/nfs_var.h b/sys/fs/nfs/nfs_var.h index 1cf792edece..7bf89011d2f 100644 --- a/sys/fs/nfs/nfs_var.h +++ b/sys/fs/nfs/nfs_var.h @@ -680,9 +680,9 @@ int nfsvno_namei(struct nfsrv_descript *, struct nameidata *, vnode_t, int, struct nfsexstuff *, NFSPROC_T *, vnode_t *); void nfsvno_setpathbuf(struct nameidata *, char **, u_long **); void nfsvno_relpathbuf(struct nameidata *); -int nfsvno_readlink(vnode_t, struct ucred *, NFSPROC_T *, struct mbuf **, +int nfsvno_readlink(vnode_t, struct ucred *, int, NFSPROC_T *, struct mbuf **, struct mbuf **, int *); -int nfsvno_read(vnode_t, off_t, int, struct ucred *, NFSPROC_T *, +int nfsvno_read(vnode_t, off_t, int, struct ucred *, int, NFSPROC_T *, struct mbuf **, struct mbuf **); int nfsvno_write(vnode_t, off_t, int, int *, struct mbuf *, char *, struct ucred *, NFSPROC_T *); @@ -748,7 +748,7 @@ int nfsvno_seek(struct nfsrv_descript *, struct vnode *, u_long, off_t *, int, bool *, struct ucred *, NFSPROC_T *); int nfsvno_allocate(struct vnode *, off_t, off_t, struct ucred *, NFSPROC_T *); int nfsvno_getxattr(struct vnode *, char *, uint32_t, struct ucred *, - struct thread *, struct mbuf **, struct mbuf **, int *); + uint64_t, int, struct thread *, struct mbuf **, struct mbuf **, int *); int nfsvno_setxattr(struct vnode *, char *, int, struct mbuf *, char *, struct ucred *, struct thread *); int nfsvno_rmxattr(struct nfsrv_descript *, struct vnode *, char *, diff --git a/sys/fs/nfsserver/nfs_nfsdport.c b/sys/fs/nfsserver/nfs_nfsdport.c index eb971d73d53..2dda74d885d 100644 --- a/sys/fs/nfsserver/nfs_nfsdport.c +++ b/sys/fs/nfsserver/nfs_nfsdport.c @@ -108,6 +108,8 @@ extern struct nfsdevicehead nfsrv_devidhead; static int nfsrv_createiovec(int, struct mbuf **, struct mbuf **, struct iovec **); +static int nfsrv_createiovec_extpgs(int, int, struct mbuf **, + struct mbuf **, struct iovec **); static int nfsrv_createiovecw(int, struct mbuf *, char *, struct iovec **, int *); static void nfsrv_pnfscreate(struct vnode *, struct vattr *, struct ucred *, @@ -738,8 +740,8 @@ nfsvno_relpathbuf(struct nameidata *ndp) * Readlink vnode op into an mbuf list. */ int -nfsvno_readlink(struct vnode *vp, struct ucred *cred, struct thread *p, - struct mbuf **mpp, struct mbuf **mpendp, int *lenp) +nfsvno_readlink(struct vnode *vp, struct ucred *cred, int maxextsiz, + struct thread *p, struct mbuf **mpp, struct mbuf **mpendp, int *lenp) { struct iovec *iv; struct uio io, *uiop = &io; @@ -747,7 +749,11 @@ nfsvno_readlink(struct vnode *vp, struct ucred *cred, struct thread *p, int len, tlen, error = 0; len = NFS_MAXPATHLEN; - uiop->uio_iovcnt = nfsrv_createiovec(len, &mp3, &mp, &iv); + if (maxextsiz > 0) + uiop->uio_iovcnt = nfsrv_createiovec_extpgs(len, maxextsiz, + &mp3, &mp, &iv); + else + uiop->uio_iovcnt = nfsrv_createiovec(len, &mp3, &mp, &iv); uiop->uio_iov = iv; uiop->uio_offset = 0; uiop->uio_resid = len; @@ -819,7 +825,7 @@ nfsrv_createiovec(int len, struct mbuf **mpp, struct mbuf **mpendp, i = 0; while (left > 0) { if (m == NULL) - panic("nfsvno_read iov"); + panic("nfsrv_createiovec iov"); siz = min(M_TRAILINGSPACE(m), left); if (siz > 0) { iv->iov_base = mtod(m, caddr_t) + m->m_len; @@ -836,12 +842,77 @@ nfsrv_createiovec(int len, struct mbuf **mpp, struct mbuf **mpendp, return (i); } +/* + * Create an mbuf chain and an associated iovec that can be used to Read + * or Getextattr of data. + * Upon success, return pointers to the first and last mbufs in the chain + * plus the malloc'd iovec and its iovlen. + * Same as above, but creates ext_pgs mbuf(s). + */ +static int +nfsrv_createiovec_extpgs(int len, int maxextsiz, struct mbuf **mpp, + struct mbuf **mpendp, struct iovec **ivp) +{ + struct mbuf *m, *m2 = NULL, *m3; + struct iovec *iv; + int i, left, pgno, siz; + + left = len; + m3 = NULL; + /* + * Generate the mbuf list with the uio_iov ref. to it. + */ + i = 0; + while (left > 0) { + siz = min(left, maxextsiz); + m = mb_alloc_ext_plus_pages(siz, M_WAITOK); + left -= siz; + i += m->m_epg_npgs; + if (m3 != NULL) + m2->m_next = m; + else + m3 = m; + m2 = m; + } + *ivp = iv = malloc(i * sizeof (struct iovec), M_TEMP, M_WAITOK); + m = m3; + left = len; + i = 0; + pgno = 0; + while (left > 0) { + if (m == NULL) + panic("nfsvno_createiovec_extpgs iov"); + siz = min(PAGE_SIZE, left); + if (siz > 0) { + iv->iov_base = (void *)PHYS_TO_DMAP(m->m_epg_pa[pgno]); + iv->iov_len = siz; + m->m_len += siz; + if (pgno == m->m_epg_npgs - 1) + m->m_epg_last_len = siz; + left -= siz; + iv++; + i++; + pgno++; + } + if (pgno == m->m_epg_npgs && left > 0) { + m = m->m_next; + if (m == NULL) + panic("nfsvno_createiovec_extpgs iov"); + pgno = 0; + } + } + *mpp = m3; + *mpendp = m2; + return (i); +} + /* * Read vnode op call into mbuf list. */ int nfsvno_read(struct vnode *vp, off_t off, int cnt, struct ucred *cred, - struct thread *p, struct mbuf **mpp, struct mbuf **mpendp) + int maxextsiz, struct thread *p, struct mbuf **mpp, + struct mbuf **mpendp) { struct mbuf *m; struct iovec *iv; @@ -860,7 +931,11 @@ nfsvno_read(struct vnode *vp, off_t off, int cnt, struct ucred *cred, return (error); len = NFSM_RNDUP(cnt); - uiop->uio_iovcnt = nfsrv_createiovec(len, &m3, &m, &iv); + if (maxextsiz > 0) + uiop->uio_iovcnt = nfsrv_createiovec_extpgs(len, maxextsiz, + &m3, &m, &iv); + else + uiop->uio_iovcnt = nfsrv_createiovec(len, &m3, &m, &iv); uiop->uio_iov = iv; uiop->uio_offset = off; uiop->uio_resid = len; @@ -938,7 +1013,7 @@ nfsrv_createiovecw(int retlen, struct mbuf *m, char *cp, struct iovec **ivpp, len = retlen; while (len > 0) { if (mp == NULL) - panic("nfsvno_write"); + panic("nfsrv_createiovecw"); if (i > 0) { i = min(i, len); ivp->iov_base = cp; @@ -6241,8 +6316,8 @@ nfsvno_allocate(struct vnode *vp, off_t off, off_t len, struct ucred *cred, */ int nfsvno_getxattr(struct vnode *vp, char *name, uint32_t maxresp, - struct ucred *cred, struct thread *p, struct mbuf **mpp, - struct mbuf **mpendp, int *lenp) + struct ucred *cred, uint64_t flag, int maxextsiz, struct thread *p, + struct mbuf **mpp, struct mbuf **mpendp, int *lenp) { struct iovec *iv; struct uio io, *uiop = &io; @@ -6260,7 +6335,21 @@ nfsvno_getxattr(struct vnode *vp, char *name, uint32_t maxresp, len = siz; tlen = NFSM_RNDUP(len); if (tlen > 0) { - uiop->uio_iovcnt = nfsrv_createiovec(tlen, &m, &m2, &iv); + /* + * If cnt > MCLBYTES and the reply will not be saved, use + * ext_pgs mbufs for TLS. + * For NFSv4.0, we do not know for sure if the reply will + * be saved, so do not use ext_pgs mbufs for NFSv4.0. + * Always use ext_pgs mbufs if ND_EXTPG is set. + */ + if ((flag & ND_EXTPG) != 0 || (tlen > MCLBYTES && + (flag & (ND_TLS | ND_SAVEREPLY)) == ND_TLS && + (flag & (ND_NFSV4 | ND_NFSV41)) != ND_NFSV4)) + uiop->uio_iovcnt = nfsrv_createiovec_extpgs(tlen, + maxextsiz, &m, &m2, &iv); + else + uiop->uio_iovcnt = nfsrv_createiovec(tlen, &m, &m2, + &iv); uiop->uio_iov = iv; } else { uiop->uio_iovcnt = 0; diff --git a/sys/fs/nfsserver/nfs_nfsdserv.c b/sys/fs/nfsserver/nfs_nfsdserv.c index 1b34cb27dd0..d2fe9e60a0e 100644 --- a/sys/fs/nfsserver/nfs_nfsdserv.c +++ b/sys/fs/nfsserver/nfs_nfsdserv.c @@ -667,6 +667,7 @@ nfsrvd_readlink(struct nfsrv_descript *nd, __unused int isdgram, int getret = 1, len; struct nfsvattr nva; struct thread *p = curthread; + uint16_t off; if (nd->nd_repstat) { nfsrv_postopattr(nd, getret, &nva); @@ -678,9 +679,14 @@ nfsrvd_readlink(struct nfsrv_descript *nd, __unused int isdgram, else nd->nd_repstat = EINVAL; } - if (!nd->nd_repstat) - nd->nd_repstat = nfsvno_readlink(vp, nd->nd_cred, p, - &mp, &mpend, &len); + if (nd->nd_repstat == 0) { + if ((nd->nd_flag & ND_EXTPG) != 0) + nd->nd_repstat = nfsvno_readlink(vp, nd->nd_cred, + nd->nd_maxextsiz, p, &mp, &mpend, &len); + else + nd->nd_repstat = nfsvno_readlink(vp, nd->nd_cred, + 0, p, &mp, &mpend, &len); + } if (nd->nd_flag & ND_NFSV3) getret = nfsvno_getattr(vp, &nva, nd, p, 1, NULL); vput(vp); @@ -693,7 +699,16 @@ nfsrvd_readlink(struct nfsrv_descript *nd, __unused int isdgram, if (mp != NULL) { nd->nd_mb->m_next = mp; nd->nd_mb = mpend; - nd->nd_bpos = mtod(mpend, caddr_t) + mpend->m_len; + if ((mpend->m_flags & M_EXTPG) != 0) { + nd->nd_bextpg = mpend->m_epg_npgs - 1; + nd->nd_bpos = (char *)(void *) + PHYS_TO_DMAP(mpend->m_epg_pa[nd->nd_bextpg]); + off = (nd->nd_bextpg == 0) ? mpend->m_epg_1st_off : 0; + nd->nd_bpos += off + mpend->m_epg_last_len; + nd->nd_bextpgsiz = PAGE_SIZE - mpend->m_epg_last_len - + off; + } else + nd->nd_bpos = mtod(mpend, char *) + mpend->m_len; } out: @@ -718,6 +733,7 @@ nfsrvd_read(struct nfsrv_descript *nd, __unused int isdgram, nfsv4stateid_t stateid; nfsquad_t clientid; struct thread *p = curthread; + uint16_t poff; if (nd->nd_repstat) { nfsrv_postopattr(nd, getret, &nva); @@ -839,8 +855,21 @@ nfsrvd_read(struct nfsrv_descript *nd, __unused int isdgram, cnt = reqlen; m3 = NULL; if (cnt > 0) { - nd->nd_repstat = nfsvno_read(vp, off, cnt, nd->nd_cred, p, - &m3, &m2); + /* + * If cnt > MCLBYTES and the reply will not be saved, use + * ext_pgs mbufs for TLS. + * For NFSv4.0, we do not know for sure if the reply will + * be saved, so do not use ext_pgs mbufs for NFSv4.0. + * Always use ext_pgs mbufs if ND_EXTPG is set. + */ + if ((nd->nd_flag & ND_EXTPG) != 0 || (cnt > MCLBYTES && + (nd->nd_flag & (ND_TLS | ND_SAVEREPLY)) == ND_TLS && + (nd->nd_flag & (ND_NFSV4 | ND_NFSV41)) != ND_NFSV4)) + nd->nd_repstat = nfsvno_read(vp, off, cnt, nd->nd_cred, + nd->nd_maxextsiz, p, &m3, &m2); + else + nd->nd_repstat = nfsvno_read(vp, off, cnt, nd->nd_cred, + 0, p, &m3, &m2); if (!(nd->nd_flag & ND_NFSV4)) { getret = nfsvno_getattr(vp, &nva, nd, p, 1, NULL); if (!nd->nd_repstat) @@ -875,7 +904,17 @@ nfsrvd_read(struct nfsrv_descript *nd, __unused int isdgram, if (m3) { nd->nd_mb->m_next = m3; nd->nd_mb = m2; - nd->nd_bpos = mtod(m2, caddr_t) + m2->m_len; + if ((m2->m_flags & M_EXTPG) != 0) { + nd->nd_flag |= ND_EXTPG; + nd->nd_bextpg = m2->m_epg_npgs - 1; + nd->nd_bpos = (char *)(void *) + PHYS_TO_DMAP(m2->m_epg_pa[nd->nd_bextpg]); + poff = (nd->nd_bextpg == 0) ? m2->m_epg_1st_off : 0; + nd->nd_bpos += poff + m2->m_epg_last_len; + nd->nd_bextpgsiz = PAGE_SIZE - m2->m_epg_last_len - + poff; + } else + nd->nd_bpos = mtod(m2, char *) + m2->m_len; } out: @@ -5536,6 +5575,7 @@ nfsrvd_getxattr(struct nfsrv_descript *nd, __unused int isdgram, int error, len; char *name; struct thread *p = curthread; + uint16_t off; error = 0; if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) { @@ -5555,8 +5595,9 @@ nfsrvd_getxattr(struct nfsrv_descript *nd, __unused int isdgram, name = malloc(len + 1, M_TEMP, M_WAITOK); nd->nd_repstat = nfsrv_mtostr(nd, name, len); if (nd->nd_repstat == 0) - nd->nd_repstat = nfsvno_getxattr(vp, name, nd->nd_maxresp, - nd->nd_cred, p, &mp, &mpend, &len); + nd->nd_repstat = nfsvno_getxattr(vp, name, + nd->nd_maxresp, nd->nd_cred, nd->nd_flag, + nd->nd_maxextsiz, p, &mp, &mpend, &len); if (nd->nd_repstat == ENOATTR) nd->nd_repstat = NFSERR_NOXATTR; else if (nd->nd_repstat == EOPNOTSUPP) @@ -5567,7 +5608,19 @@ nfsrvd_getxattr(struct nfsrv_descript *nd, __unused int isdgram, if (len > 0) { nd->nd_mb->m_next = mp; nd->nd_mb = mpend; - nd->nd_bpos = mtod(mpend, caddr_t) + mpend->m_len; + if ((mpend->m_flags & M_EXTPG) != 0) { + nd->nd_flag |= ND_EXTPG; + nd->nd_bextpg = mpend->m_epg_npgs - 1; + nd->nd_bpos = (char *)(void *) + PHYS_TO_DMAP(mpend->m_epg_pa[nd->nd_bextpg]); + off = (nd->nd_bextpg == 0) ? + mpend->m_epg_1st_off : 0; + nd->nd_bpos += off + mpend->m_epg_last_len; + nd->nd_bextpgsiz = PAGE_SIZE - + mpend->m_epg_last_len - off; + } else + nd->nd_bpos = mtod(mpend, char *) + + mpend->m_len; } } free(name, M_TEMP);