mbuf: provide mc_uiotomc() a function to copy from uio(9) to mchain
Implement m_uiotombuf() as a wrapper around mc_uiotomc(). The M_EXTPG is left untouched. The m_uiotombuf() is left as a compat KPI. New code should use either mc_uiotomc() or m_uiotombuf_nomap(). Reviewed by: markj, tuexen Differential Revision: https://reviews.freebsd.org/D44150
This commit is contained in:
+82
-44
@@ -1938,65 +1938,103 @@ m_uiotombuf_nomap(struct uio *uio, int how, int len, int maxseg, int flags)
|
||||
|
||||
/*
|
||||
* Copy the contents of uio into a properly sized mbuf chain.
|
||||
* A compat KPI. Users are recommended to use direct calls to backing
|
||||
* functions.
|
||||
*/
|
||||
struct mbuf *
|
||||
m_uiotombuf(struct uio *uio, int how, int len, int align, int flags)
|
||||
m_uiotombuf(struct uio *uio, int how, int len, int lspace, int flags)
|
||||
{
|
||||
struct mbuf *m, *mb;
|
||||
int error, length;
|
||||
ssize_t total;
|
||||
int progress = 0;
|
||||
|
||||
if (flags & M_EXTPG)
|
||||
return (m_uiotombuf_nomap(uio, how, len, align, flags));
|
||||
if (flags & M_EXTPG) {
|
||||
/* XXX: 'lspace' magically becomes maxseg! */
|
||||
return (m_uiotombuf_nomap(uio, how, len, lspace, flags));
|
||||
} else if (__predict_false(uio->uio_resid + len == 0)) {
|
||||
struct mbuf *m;
|
||||
|
||||
/*
|
||||
* len can be zero or an arbitrary large value bound by
|
||||
* the total data supplied by the uio.
|
||||
*/
|
||||
if (len > 0)
|
||||
total = (uio->uio_resid < len) ? uio->uio_resid : len;
|
||||
/*
|
||||
* m_uiotombuf() is known to return zero length buffer, keep
|
||||
* this compatibility. mc_uiotomc() won't do that.
|
||||
*/
|
||||
if (flags & M_PKTHDR) {
|
||||
m = m_gethdr(how, MT_DATA);
|
||||
m->m_pkthdr.memlen = MSIZE;
|
||||
} else
|
||||
m = m_get(how, MT_DATA);
|
||||
if (m != NULL)
|
||||
m->m_data += lspace;
|
||||
return (m);
|
||||
} else {
|
||||
struct mchain mc;
|
||||
int error;
|
||||
|
||||
error = mc_uiotomc(&mc, uio, len, lspace, how, flags);
|
||||
if (__predict_true(error == 0)) {
|
||||
if (flags & M_PKTHDR) {
|
||||
mc_first(&mc)->m_pkthdr.len = mc.mc_len;
|
||||
mc_first(&mc)->m_pkthdr.memlen = mc.mc_mlen;
|
||||
}
|
||||
return (mc_first(&mc));
|
||||
} else
|
||||
return (NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy the contents of uio into a properly sized mbuf chain.
|
||||
* In case of failure state of mchain is inconsistent.
|
||||
* @param length Limit copyout length. If 0 entire uio_resid is copied.
|
||||
* @param lspace Provide leading space in the first mbuf in the chain.
|
||||
*/
|
||||
int
|
||||
mc_uiotomc(struct mchain *mc, struct uio *uio, u_int length, u_int lspace,
|
||||
int how, int flags)
|
||||
{
|
||||
struct mbuf *mb;
|
||||
u_int total;
|
||||
int error;
|
||||
|
||||
MPASS(lspace < MHLEN);
|
||||
MPASS(UINT_MAX - lspace >= length);
|
||||
MPASS(uio->uio_rw == UIO_WRITE);
|
||||
MPASS(uio->uio_resid >= 0);
|
||||
|
||||
if (length > 0) {
|
||||
if (uio->uio_resid > length) {
|
||||
total = length;
|
||||
flags &= ~M_EOR;
|
||||
} else
|
||||
total = uio->uio_resid;
|
||||
} else if (__predict_false(uio->uio_resid + lspace > UINT_MAX))
|
||||
return (EOVERFLOW);
|
||||
else
|
||||
total = uio->uio_resid;
|
||||
|
||||
/*
|
||||
* The smallest unit returned by m_getm2() is a single mbuf
|
||||
* with pkthdr. We can't align past it.
|
||||
*/
|
||||
if (align >= MHLEN)
|
||||
return (NULL);
|
||||
if (__predict_false(total + lspace == 0)) {
|
||||
*mc = MCHAIN_INITIALIZER(mc);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Give us the full allocation or nothing.
|
||||
* If len is zero return the smallest empty mbuf.
|
||||
*/
|
||||
m = m_getm2(NULL, max(total + align, 1), how, MT_DATA, flags);
|
||||
if (m == NULL)
|
||||
return (NULL);
|
||||
m->m_data += align;
|
||||
error = mc_get(mc, total + lspace, how, MT_DATA, flags);
|
||||
if (__predict_false(error))
|
||||
return (error);
|
||||
mc_first(mc)->m_data += lspace;
|
||||
|
||||
/* Fill all mbufs with uio data and update header information. */
|
||||
for (mb = m; mb != NULL; mb = mb->m_next) {
|
||||
length = min(M_TRAILINGSPACE(mb), total - progress);
|
||||
STAILQ_FOREACH(mb, &mc->mc_q, m_stailq) {
|
||||
u_int mlen;
|
||||
|
||||
error = uiomove(mtod(mb, void *), length, uio);
|
||||
if (error) {
|
||||
m_freem(m);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
mb->m_len = length;
|
||||
progress += length;
|
||||
if (flags & M_PKTHDR) {
|
||||
m->m_pkthdr.len += length;
|
||||
m->m_pkthdr.memlen += MSIZE;
|
||||
if (mb->m_flags & M_EXT)
|
||||
m->m_pkthdr.memlen += mb->m_ext.ext_size;
|
||||
mlen = min(M_TRAILINGSPACE(mb), total - mc->mc_len);
|
||||
error = uiomove(mtod(mb, void *), mlen, uio);
|
||||
if (__predict_false(error)) {
|
||||
mc_freem(mc);
|
||||
return (error);
|
||||
}
|
||||
mb->m_len = mlen;
|
||||
mc->mc_len += mlen;
|
||||
}
|
||||
KASSERT(progress == total, ("%s: progress != total", __func__));
|
||||
MPASS(mc->mc_len == total);
|
||||
|
||||
return (m);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -1797,6 +1797,7 @@ mc_remove(struct mchain *mc, struct mbuf *m)
|
||||
|
||||
int mc_get(struct mchain *, u_int, int, short, int);
|
||||
int mc_split(struct mchain *, struct mchain *, u_int, int);
|
||||
int mc_uiotomc(struct mchain *, struct uio *, u_int, u_int, int, int);
|
||||
|
||||
#ifdef _SYS_TIMESPEC_H_
|
||||
static inline void
|
||||
|
||||
Reference in New Issue
Block a user