Add optional support for ext_pgs mbufs to the NFS server's read, readlink

and getxattr operations.

This patch optionally enables generation of read, readlink and getxattr replies
in ext_pgs mbufs.  Since neither of ND_EXTPG or ND_TLS are currently ever set,
there is no change in semantics at this time.
It also corrects the message in a couple of panic()s that should never occur.

This is another in the series of commits that add support to the NFS client
and server for building RPC messages in ext_pgs mbufs with anonymous pages.
This is useful so that the entire mbuf list does not need to be
copied before calling sosend() when NFS over TLS is enabled.

Use of ext_pgs mbufs will not be enabled until the kernel RPC is updated
to handle TLS.
This commit is contained in:
Rick Macklem 2020-07-31 23:35:49 +00:00
parent 0eed04c802
commit cb889ce631
3 changed files with 165 additions and 23 deletions

View File

@ -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 *,

View File

@ -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;

View File

@ -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);