Skip to content

Commit aba79b0

Browse files
committed
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
1 parent 71f8702 commit aba79b0

File tree

2 files changed

+83
-44
lines changed

2 files changed

+83
-44
lines changed

sys/kern/uipc_mbuf.c

Lines changed: 82 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1938,65 +1938,103 @@ m_uiotombuf_nomap(struct uio *uio, int how, int len, int maxseg, int flags)
19381938

19391939
/*
19401940
* Copy the contents of uio into a properly sized mbuf chain.
1941+
* A compat KPI. Users are recommended to use direct calls to backing
1942+
* functions.
19411943
*/
19421944
struct mbuf *
1943-
m_uiotombuf(struct uio *uio, int how, int len, int align, int flags)
1945+
m_uiotombuf(struct uio *uio, int how, int len, int lspace, int flags)
19441946
{
1945-
struct mbuf *m, *mb;
1946-
int error, length;
1947-
ssize_t total;
1948-
int progress = 0;
19491947

1950-
if (flags & M_EXTPG)
1951-
return (m_uiotombuf_nomap(uio, how, len, align, flags));
1948+
if (flags & M_EXTPG) {
1949+
/* XXX: 'lspace' magically becomes maxseg! */
1950+
return (m_uiotombuf_nomap(uio, how, len, lspace, flags));
1951+
} else if (__predict_false(uio->uio_resid + len == 0)) {
1952+
struct mbuf *m;
19521953

1953-
/*
1954-
* len can be zero or an arbitrary large value bound by
1955-
* the total data supplied by the uio.
1956-
*/
1957-
if (len > 0)
1958-
total = (uio->uio_resid < len) ? uio->uio_resid : len;
1954+
/*
1955+
* m_uiotombuf() is known to return zero length buffer, keep
1956+
* this compatibility. mc_uiotomc() won't do that.
1957+
*/
1958+
if (flags & M_PKTHDR) {
1959+
m = m_gethdr(how, MT_DATA);
1960+
m->m_pkthdr.memlen = MSIZE;
1961+
} else
1962+
m = m_get(how, MT_DATA);
1963+
if (m != NULL)
1964+
m->m_data += lspace;
1965+
return (m);
1966+
} else {
1967+
struct mchain mc;
1968+
int error;
1969+
1970+
error = mc_uiotomc(&mc, uio, len, lspace, how, flags);
1971+
if (__predict_true(error == 0)) {
1972+
if (flags & M_PKTHDR) {
1973+
mc_first(&mc)->m_pkthdr.len = mc.mc_len;
1974+
mc_first(&mc)->m_pkthdr.memlen = mc.mc_mlen;
1975+
}
1976+
return (mc_first(&mc));
1977+
} else
1978+
return (NULL);
1979+
}
1980+
}
1981+
1982+
/*
1983+
* Copy the contents of uio into a properly sized mbuf chain.
1984+
* In case of failure state of mchain is inconsistent.
1985+
* @param length Limit copyout length. If 0 entire uio_resid is copied.
1986+
* @param lspace Provide leading space in the first mbuf in the chain.
1987+
*/
1988+
int
1989+
mc_uiotomc(struct mchain *mc, struct uio *uio, u_int length, u_int lspace,
1990+
int how, int flags)
1991+
{
1992+
struct mbuf *mb;
1993+
u_int total;
1994+
int error;
1995+
1996+
MPASS(lspace < MHLEN);
1997+
MPASS(UINT_MAX - lspace >= length);
1998+
MPASS(uio->uio_rw == UIO_WRITE);
1999+
MPASS(uio->uio_resid >= 0);
2000+
2001+
if (length > 0) {
2002+
if (uio->uio_resid > length) {
2003+
total = length;
2004+
flags &= ~M_EOR;
2005+
} else
2006+
total = uio->uio_resid;
2007+
} else if (__predict_false(uio->uio_resid + lspace > UINT_MAX))
2008+
return (EOVERFLOW);
19592009
else
19602010
total = uio->uio_resid;
19612011

1962-
/*
1963-
* The smallest unit returned by m_getm2() is a single mbuf
1964-
* with pkthdr. We can't align past it.
1965-
*/
1966-
if (align >= MHLEN)
1967-
return (NULL);
2012+
if (__predict_false(total + lspace == 0)) {
2013+
*mc = MCHAIN_INITIALIZER(mc);
2014+
return (0);
2015+
}
19682016

1969-
/*
1970-
* Give us the full allocation or nothing.
1971-
* If len is zero return the smallest empty mbuf.
1972-
*/
1973-
m = m_getm2(NULL, max(total + align, 1), how, MT_DATA, flags);
1974-
if (m == NULL)
1975-
return (NULL);
1976-
m->m_data += align;
2017+
error = mc_get(mc, total + lspace, how, MT_DATA, flags);
2018+
if (__predict_false(error))
2019+
return (error);
2020+
mc_first(mc)->m_data += lspace;
19772021

19782022
/* Fill all mbufs with uio data and update header information. */
1979-
for (mb = m; mb != NULL; mb = mb->m_next) {
1980-
length = min(M_TRAILINGSPACE(mb), total - progress);
1981-
1982-
error = uiomove(mtod(mb, void *), length, uio);
1983-
if (error) {
1984-
m_freem(m);
1985-
return (NULL);
1986-
}
2023+
STAILQ_FOREACH(mb, &mc->mc_q, m_stailq) {
2024+
u_int mlen;
19872025

1988-
mb->m_len = length;
1989-
progress += length;
1990-
if (flags & M_PKTHDR) {
1991-
m->m_pkthdr.len += length;
1992-
m->m_pkthdr.memlen += MSIZE;
1993-
if (mb->m_flags & M_EXT)
1994-
m->m_pkthdr.memlen += mb->m_ext.ext_size;
2026+
mlen = min(M_TRAILINGSPACE(mb), total - mc->mc_len);
2027+
error = uiomove(mtod(mb, void *), mlen, uio);
2028+
if (__predict_false(error)) {
2029+
mc_freem(mc);
2030+
return (error);
19952031
}
2032+
mb->m_len = mlen;
2033+
mc->mc_len += mlen;
19962034
}
1997-
KASSERT(progress == total, ("%s: progress != total", __func__));
2035+
MPASS(mc->mc_len == total);
19982036

1999-
return (m);
2037+
return (0);
20002038
}
20012039

20022040
/*

sys/sys/mbuf.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1797,6 +1797,7 @@ mc_remove(struct mchain *mc, struct mbuf *m)
17971797

17981798
int mc_get(struct mchain *, u_int, int, short, int);
17991799
int mc_split(struct mchain *, struct mchain *, u_int, int);
1800+
int mc_uiotomc(struct mchain *, struct uio *, u_int, u_int, int, int);
18001801

18011802
#ifdef _SYS_TIMESPEC_H_
18021803
static inline void

0 commit comments

Comments
 (0)