1f561c2ecSJohn Baldwin /*-
2f561c2ecSJohn Baldwin * SPDX-License-Identifier: BSD-2-Clause
3f561c2ecSJohn Baldwin *
4f561c2ecSJohn Baldwin * Copyright (c) 2023 Chelsio Communications, Inc.
5f561c2ecSJohn Baldwin * Written by: John Baldwin <jhb@FreeBSD.org>
6f561c2ecSJohn Baldwin *
7f561c2ecSJohn Baldwin * Redistribution and use in source and binary forms, with or without
8f561c2ecSJohn Baldwin * modification, are permitted provided that the following conditions
9f561c2ecSJohn Baldwin * are met:
10f561c2ecSJohn Baldwin * 1. Redistributions of source code must retain the above copyright
11f561c2ecSJohn Baldwin * notice, this list of conditions and the following disclaimer.
12f561c2ecSJohn Baldwin * 2. Redistributions in binary form must reproduce the above copyright
13f561c2ecSJohn Baldwin * notice, this list of conditions and the following disclaimer in the
14f561c2ecSJohn Baldwin * documentation and/or other materials provided with the distribution.
15f561c2ecSJohn Baldwin *
16f561c2ecSJohn Baldwin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17f561c2ecSJohn Baldwin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18f561c2ecSJohn Baldwin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19f561c2ecSJohn Baldwin * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20f561c2ecSJohn Baldwin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21f561c2ecSJohn Baldwin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22f561c2ecSJohn Baldwin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23f561c2ecSJohn Baldwin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24f561c2ecSJohn Baldwin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25f561c2ecSJohn Baldwin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26f561c2ecSJohn Baldwin * SUCH DAMAGE.
27f561c2ecSJohn Baldwin */
28f561c2ecSJohn Baldwin
29f561c2ecSJohn Baldwin #include <sys/param.h>
30f561c2ecSJohn Baldwin #include <sys/mbuf.h>
31f561c2ecSJohn Baldwin #include <sys/memdesc.h>
32f561c2ecSJohn Baldwin #include <sys/systm.h>
33f561c2ecSJohn Baldwin #include <sys/uio.h>
34f561c2ecSJohn Baldwin #include <vm/vm.h>
35f561c2ecSJohn Baldwin #include <vm/pmap.h>
36*a9b61512SJohn Baldwin #include <vm/vm_page.h>
37f561c2ecSJohn Baldwin #include <vm/vm_param.h>
38f561c2ecSJohn Baldwin #include <machine/bus.h>
39f561c2ecSJohn Baldwin
40*a9b61512SJohn Baldwin /*
41*a9b61512SJohn Baldwin * memdesc_copyback copies data from a source buffer into a buffer
42*a9b61512SJohn Baldwin * described by a memory descriptor.
43*a9b61512SJohn Baldwin */
44f561c2ecSJohn Baldwin static void
phys_copyback(vm_paddr_t pa,int off,int size,const void * src)45f561c2ecSJohn Baldwin phys_copyback(vm_paddr_t pa, int off, int size, const void *src)
46f561c2ecSJohn Baldwin {
47f561c2ecSJohn Baldwin const char *cp;
48f561c2ecSJohn Baldwin u_int page_off;
49f561c2ecSJohn Baldwin int todo;
50f561c2ecSJohn Baldwin void *p;
51f561c2ecSJohn Baldwin
52f561c2ecSJohn Baldwin KASSERT(PMAP_HAS_DMAP, ("direct-map required"));
53f561c2ecSJohn Baldwin
54f561c2ecSJohn Baldwin cp = src;
55f561c2ecSJohn Baldwin pa += off;
56f561c2ecSJohn Baldwin page_off = pa & PAGE_MASK;
57f561c2ecSJohn Baldwin while (size > 0) {
58f561c2ecSJohn Baldwin todo = min(PAGE_SIZE - page_off, size);
59f561c2ecSJohn Baldwin p = (void *)PHYS_TO_DMAP(pa);
60f561c2ecSJohn Baldwin memcpy(p, cp, todo);
61f561c2ecSJohn Baldwin size -= todo;
62f561c2ecSJohn Baldwin cp += todo;
63f561c2ecSJohn Baldwin pa += todo;
64f561c2ecSJohn Baldwin page_off = 0;
65f561c2ecSJohn Baldwin }
66f561c2ecSJohn Baldwin }
67f561c2ecSJohn Baldwin
68f561c2ecSJohn Baldwin static void
vlist_copyback(struct bus_dma_segment * vlist,int sglist_cnt,int off,int size,const void * src)69f561c2ecSJohn Baldwin vlist_copyback(struct bus_dma_segment *vlist, int sglist_cnt, int off,
70f561c2ecSJohn Baldwin int size, const void *src)
71f561c2ecSJohn Baldwin {
72f561c2ecSJohn Baldwin const char *p;
73f561c2ecSJohn Baldwin int todo;
74f561c2ecSJohn Baldwin
75f561c2ecSJohn Baldwin while (vlist->ds_len <= off) {
76f561c2ecSJohn Baldwin KASSERT(sglist_cnt > 1, ("out of sglist entries"));
77f561c2ecSJohn Baldwin
78f561c2ecSJohn Baldwin off -= vlist->ds_len;
79f561c2ecSJohn Baldwin vlist++;
80f561c2ecSJohn Baldwin sglist_cnt--;
81f561c2ecSJohn Baldwin }
82f561c2ecSJohn Baldwin
83f561c2ecSJohn Baldwin p = src;
84f561c2ecSJohn Baldwin while (size > 0) {
85f561c2ecSJohn Baldwin KASSERT(sglist_cnt >= 1, ("out of sglist entries"));
86f561c2ecSJohn Baldwin
87f561c2ecSJohn Baldwin todo = size;
88f561c2ecSJohn Baldwin if (todo > vlist->ds_len - off)
89f561c2ecSJohn Baldwin todo = vlist->ds_len - off;
90f561c2ecSJohn Baldwin
91f561c2ecSJohn Baldwin memcpy((char *)(uintptr_t)vlist->ds_addr + off, p, todo);
92f561c2ecSJohn Baldwin off = 0;
93f561c2ecSJohn Baldwin vlist++;
94f561c2ecSJohn Baldwin sglist_cnt--;
95f561c2ecSJohn Baldwin size -= todo;
96f561c2ecSJohn Baldwin p += todo;
97f561c2ecSJohn Baldwin }
98f561c2ecSJohn Baldwin }
99f561c2ecSJohn Baldwin
100f561c2ecSJohn Baldwin static void
plist_copyback(struct bus_dma_segment * plist,int sglist_cnt,int off,int size,const void * src)101f561c2ecSJohn Baldwin plist_copyback(struct bus_dma_segment *plist, int sglist_cnt, int off,
102f561c2ecSJohn Baldwin int size, const void *src)
103f561c2ecSJohn Baldwin {
104f561c2ecSJohn Baldwin const char *p;
105f561c2ecSJohn Baldwin int todo;
106f561c2ecSJohn Baldwin
107f561c2ecSJohn Baldwin while (plist->ds_len <= off) {
108f561c2ecSJohn Baldwin KASSERT(sglist_cnt > 1, ("out of sglist entries"));
109f561c2ecSJohn Baldwin
110f561c2ecSJohn Baldwin off -= plist->ds_len;
111f561c2ecSJohn Baldwin plist++;
112f561c2ecSJohn Baldwin sglist_cnt--;
113f561c2ecSJohn Baldwin }
114f561c2ecSJohn Baldwin
115f561c2ecSJohn Baldwin p = src;
116f561c2ecSJohn Baldwin while (size > 0) {
117f561c2ecSJohn Baldwin KASSERT(sglist_cnt >= 1, ("out of sglist entries"));
118f561c2ecSJohn Baldwin
119f561c2ecSJohn Baldwin todo = size;
120f561c2ecSJohn Baldwin if (todo > plist->ds_len - off)
121f561c2ecSJohn Baldwin todo = plist->ds_len - off;
122f561c2ecSJohn Baldwin
123f561c2ecSJohn Baldwin phys_copyback(plist->ds_addr, off, todo, p);
124f561c2ecSJohn Baldwin off = 0;
125f561c2ecSJohn Baldwin plist++;
126f561c2ecSJohn Baldwin sglist_cnt--;
127f561c2ecSJohn Baldwin size -= todo;
128f561c2ecSJohn Baldwin p += todo;
129f561c2ecSJohn Baldwin }
130f561c2ecSJohn Baldwin }
131f561c2ecSJohn Baldwin
132f561c2ecSJohn Baldwin static void
vmpages_copyback(vm_page_t * m,int off,int size,const void * src)133f561c2ecSJohn Baldwin vmpages_copyback(vm_page_t *m, int off, int size, const void *src)
134f561c2ecSJohn Baldwin {
135f561c2ecSJohn Baldwin struct iovec iov[1];
136f561c2ecSJohn Baldwin struct uio uio;
137f561c2ecSJohn Baldwin int error __diagused;
138f561c2ecSJohn Baldwin
139f561c2ecSJohn Baldwin iov[0].iov_base = __DECONST(void *, src);
140f561c2ecSJohn Baldwin iov[0].iov_len = size;
141f561c2ecSJohn Baldwin uio.uio_iov = iov;
142f561c2ecSJohn Baldwin uio.uio_iovcnt = 1;
143f561c2ecSJohn Baldwin uio.uio_offset = 0;
144f561c2ecSJohn Baldwin uio.uio_resid = size;
145f561c2ecSJohn Baldwin uio.uio_segflg = UIO_SYSSPACE;
146f561c2ecSJohn Baldwin uio.uio_rw = UIO_WRITE;
147f561c2ecSJohn Baldwin error = uiomove_fromphys(m, off, size, &uio);
148f561c2ecSJohn Baldwin KASSERT(error == 0 && uio.uio_resid == 0, ("copy failed"));
149f561c2ecSJohn Baldwin }
150f561c2ecSJohn Baldwin
151f561c2ecSJohn Baldwin void
memdesc_copyback(struct memdesc * mem,int off,int size,const void * src)152f561c2ecSJohn Baldwin memdesc_copyback(struct memdesc *mem, int off, int size, const void *src)
153f561c2ecSJohn Baldwin {
154f561c2ecSJohn Baldwin KASSERT(off >= 0, ("%s: invalid offset %d", __func__, off));
155f561c2ecSJohn Baldwin KASSERT(size >= 0, ("%s: invalid size %d", __func__, off));
156f561c2ecSJohn Baldwin
157f561c2ecSJohn Baldwin switch (mem->md_type) {
158f561c2ecSJohn Baldwin case MEMDESC_VADDR:
159f561c2ecSJohn Baldwin KASSERT(off + size <= mem->md_len, ("copy out of bounds"));
160f561c2ecSJohn Baldwin memcpy((char *)mem->u.md_vaddr + off, src, size);
161f561c2ecSJohn Baldwin break;
162f561c2ecSJohn Baldwin case MEMDESC_PADDR:
163f561c2ecSJohn Baldwin KASSERT(off + size <= mem->md_len, ("copy out of bounds"));
164f561c2ecSJohn Baldwin phys_copyback(mem->u.md_paddr, off, size, src);
165f561c2ecSJohn Baldwin break;
166f561c2ecSJohn Baldwin case MEMDESC_VLIST:
167f561c2ecSJohn Baldwin vlist_copyback(mem->u.md_list, mem->md_nseg, off, size, src);
168f561c2ecSJohn Baldwin break;
169f561c2ecSJohn Baldwin case MEMDESC_PLIST:
170f561c2ecSJohn Baldwin plist_copyback(mem->u.md_list, mem->md_nseg, off, size, src);
171f561c2ecSJohn Baldwin break;
172f561c2ecSJohn Baldwin case MEMDESC_UIO:
173f561c2ecSJohn Baldwin panic("Use uiomove instead");
174f561c2ecSJohn Baldwin break;
175f561c2ecSJohn Baldwin case MEMDESC_MBUF:
176f561c2ecSJohn Baldwin m_copyback(mem->u.md_mbuf, off, size, src);
177f561c2ecSJohn Baldwin break;
178f561c2ecSJohn Baldwin case MEMDESC_VMPAGES:
179f561c2ecSJohn Baldwin KASSERT(off + size <= mem->md_len, ("copy out of bounds"));
180f561c2ecSJohn Baldwin vmpages_copyback(mem->u.md_ma, mem->md_offset + off, size,
181f561c2ecSJohn Baldwin src);
182f561c2ecSJohn Baldwin break;
183f561c2ecSJohn Baldwin default:
184f561c2ecSJohn Baldwin __assert_unreachable();
185f561c2ecSJohn Baldwin }
186f561c2ecSJohn Baldwin }
187f561c2ecSJohn Baldwin
188*a9b61512SJohn Baldwin /*
189*a9b61512SJohn Baldwin * memdesc_copydata copies data from a buffer described by a memory
190*a9b61512SJohn Baldwin * descriptor into a destination buffer.
191*a9b61512SJohn Baldwin */
192f561c2ecSJohn Baldwin static void
phys_copydata(vm_paddr_t pa,int off,int size,void * dst)193f561c2ecSJohn Baldwin phys_copydata(vm_paddr_t pa, int off, int size, void *dst)
194f561c2ecSJohn Baldwin {
195f561c2ecSJohn Baldwin char *cp;
196f561c2ecSJohn Baldwin u_int page_off;
197f561c2ecSJohn Baldwin int todo;
198f561c2ecSJohn Baldwin const void *p;
199f561c2ecSJohn Baldwin
200f561c2ecSJohn Baldwin KASSERT(PMAP_HAS_DMAP, ("direct-map required"));
201f561c2ecSJohn Baldwin
202f561c2ecSJohn Baldwin cp = dst;
203f561c2ecSJohn Baldwin pa += off;
204f561c2ecSJohn Baldwin page_off = pa & PAGE_MASK;
205f561c2ecSJohn Baldwin while (size > 0) {
206f561c2ecSJohn Baldwin todo = min(PAGE_SIZE - page_off, size);
207f561c2ecSJohn Baldwin p = (const void *)PHYS_TO_DMAP(pa);
208f561c2ecSJohn Baldwin memcpy(cp, p, todo);
209f561c2ecSJohn Baldwin size -= todo;
210f561c2ecSJohn Baldwin cp += todo;
211f561c2ecSJohn Baldwin pa += todo;
212f561c2ecSJohn Baldwin page_off = 0;
213f561c2ecSJohn Baldwin }
214f561c2ecSJohn Baldwin }
215f561c2ecSJohn Baldwin
216f561c2ecSJohn Baldwin static void
vlist_copydata(struct bus_dma_segment * vlist,int sglist_cnt,int off,int size,void * dst)217f561c2ecSJohn Baldwin vlist_copydata(struct bus_dma_segment *vlist, int sglist_cnt, int off,
218f561c2ecSJohn Baldwin int size, void *dst)
219f561c2ecSJohn Baldwin {
220f561c2ecSJohn Baldwin char *p;
221f561c2ecSJohn Baldwin int todo;
222f561c2ecSJohn Baldwin
223f561c2ecSJohn Baldwin while (vlist->ds_len <= off) {
224f561c2ecSJohn Baldwin KASSERT(sglist_cnt > 1, ("out of sglist entries"));
225f561c2ecSJohn Baldwin
226f561c2ecSJohn Baldwin off -= vlist->ds_len;
227f561c2ecSJohn Baldwin vlist++;
228f561c2ecSJohn Baldwin sglist_cnt--;
229f561c2ecSJohn Baldwin }
230f561c2ecSJohn Baldwin
231f561c2ecSJohn Baldwin p = dst;
232f561c2ecSJohn Baldwin while (size > 0) {
233f561c2ecSJohn Baldwin KASSERT(sglist_cnt >= 1, ("out of sglist entries"));
234f561c2ecSJohn Baldwin
235f561c2ecSJohn Baldwin todo = size;
236f561c2ecSJohn Baldwin if (todo > vlist->ds_len - off)
237f561c2ecSJohn Baldwin todo = vlist->ds_len - off;
238f561c2ecSJohn Baldwin
239f561c2ecSJohn Baldwin memcpy(p, (char *)(uintptr_t)vlist->ds_addr + off, todo);
240f561c2ecSJohn Baldwin off = 0;
241f561c2ecSJohn Baldwin vlist++;
242f561c2ecSJohn Baldwin sglist_cnt--;
243f561c2ecSJohn Baldwin size -= todo;
244f561c2ecSJohn Baldwin p += todo;
245f561c2ecSJohn Baldwin }
246f561c2ecSJohn Baldwin }
247f561c2ecSJohn Baldwin
248f561c2ecSJohn Baldwin static void
plist_copydata(struct bus_dma_segment * plist,int sglist_cnt,int off,int size,void * dst)249f561c2ecSJohn Baldwin plist_copydata(struct bus_dma_segment *plist, int sglist_cnt, int off,
250f561c2ecSJohn Baldwin int size, void *dst)
251f561c2ecSJohn Baldwin {
252f561c2ecSJohn Baldwin char *p;
253f561c2ecSJohn Baldwin int todo;
254f561c2ecSJohn Baldwin
255f561c2ecSJohn Baldwin while (plist->ds_len <= off) {
256f561c2ecSJohn Baldwin KASSERT(sglist_cnt > 1, ("out of sglist entries"));
257f561c2ecSJohn Baldwin
258f561c2ecSJohn Baldwin off -= plist->ds_len;
259f561c2ecSJohn Baldwin plist++;
260f561c2ecSJohn Baldwin sglist_cnt--;
261f561c2ecSJohn Baldwin }
262f561c2ecSJohn Baldwin
263f561c2ecSJohn Baldwin p = dst;
264f561c2ecSJohn Baldwin while (size > 0) {
265f561c2ecSJohn Baldwin KASSERT(sglist_cnt >= 1, ("out of sglist entries"));
266f561c2ecSJohn Baldwin
267f561c2ecSJohn Baldwin todo = size;
268f561c2ecSJohn Baldwin if (todo > plist->ds_len - off)
269f561c2ecSJohn Baldwin todo = plist->ds_len - off;
270f561c2ecSJohn Baldwin
271f561c2ecSJohn Baldwin phys_copydata(plist->ds_addr, off, todo, p);
272f561c2ecSJohn Baldwin off = 0;
273f561c2ecSJohn Baldwin plist++;
274f561c2ecSJohn Baldwin sglist_cnt--;
275f561c2ecSJohn Baldwin size -= todo;
276f561c2ecSJohn Baldwin p += todo;
277f561c2ecSJohn Baldwin }
278f561c2ecSJohn Baldwin }
279f561c2ecSJohn Baldwin
280f561c2ecSJohn Baldwin static void
vmpages_copydata(vm_page_t * m,int off,int size,void * dst)281f561c2ecSJohn Baldwin vmpages_copydata(vm_page_t *m, int off, int size, void *dst)
282f561c2ecSJohn Baldwin {
283f561c2ecSJohn Baldwin struct iovec iov[1];
284f561c2ecSJohn Baldwin struct uio uio;
285f561c2ecSJohn Baldwin int error __diagused;
286f561c2ecSJohn Baldwin
287f561c2ecSJohn Baldwin iov[0].iov_base = dst;
288f561c2ecSJohn Baldwin iov[0].iov_len = size;
289f561c2ecSJohn Baldwin uio.uio_iov = iov;
290f561c2ecSJohn Baldwin uio.uio_iovcnt = 1;
291f561c2ecSJohn Baldwin uio.uio_offset = 0;
292f561c2ecSJohn Baldwin uio.uio_resid = size;
293f561c2ecSJohn Baldwin uio.uio_segflg = UIO_SYSSPACE;
294f561c2ecSJohn Baldwin uio.uio_rw = UIO_READ;
295f561c2ecSJohn Baldwin error = uiomove_fromphys(m, off, size, &uio);
296f561c2ecSJohn Baldwin KASSERT(error == 0 && uio.uio_resid == 0, ("copy failed"));
297f561c2ecSJohn Baldwin }
298f561c2ecSJohn Baldwin
299f561c2ecSJohn Baldwin void
memdesc_copydata(struct memdesc * mem,int off,int size,void * dst)300f561c2ecSJohn Baldwin memdesc_copydata(struct memdesc *mem, int off, int size, void *dst)
301f561c2ecSJohn Baldwin {
302f561c2ecSJohn Baldwin KASSERT(off >= 0, ("%s: invalid offset %d", __func__, off));
303f561c2ecSJohn Baldwin KASSERT(size >= 0, ("%s: invalid size %d", __func__, off));
304f561c2ecSJohn Baldwin
305f561c2ecSJohn Baldwin switch (mem->md_type) {
306f561c2ecSJohn Baldwin case MEMDESC_VADDR:
307f561c2ecSJohn Baldwin KASSERT(off + size <= mem->md_len, ("copy out of bounds"));
308f561c2ecSJohn Baldwin memcpy(dst, (const char *)mem->u.md_vaddr + off, size);
309f561c2ecSJohn Baldwin break;
310f561c2ecSJohn Baldwin case MEMDESC_PADDR:
311f561c2ecSJohn Baldwin KASSERT(off + size <= mem->md_len, ("copy out of bounds"));
312f561c2ecSJohn Baldwin phys_copydata(mem->u.md_paddr, off, size, dst);
313f561c2ecSJohn Baldwin break;
314f561c2ecSJohn Baldwin case MEMDESC_VLIST:
315f561c2ecSJohn Baldwin vlist_copydata(mem->u.md_list, mem->md_nseg, off, size, dst);
316f561c2ecSJohn Baldwin break;
317f561c2ecSJohn Baldwin case MEMDESC_PLIST:
318f561c2ecSJohn Baldwin plist_copydata(mem->u.md_list, mem->md_nseg, off, size, dst);
319f561c2ecSJohn Baldwin break;
320f561c2ecSJohn Baldwin case MEMDESC_UIO:
321f561c2ecSJohn Baldwin panic("Use uiomove instead");
322f561c2ecSJohn Baldwin break;
323f561c2ecSJohn Baldwin case MEMDESC_MBUF:
324f561c2ecSJohn Baldwin m_copydata(mem->u.md_mbuf, off, size, dst);
325f561c2ecSJohn Baldwin break;
326f561c2ecSJohn Baldwin case MEMDESC_VMPAGES:
327f561c2ecSJohn Baldwin KASSERT(off + size <= mem->md_len, ("copy out of bounds"));
328f561c2ecSJohn Baldwin vmpages_copydata(mem->u.md_ma, mem->md_offset + off, size,
329f561c2ecSJohn Baldwin dst);
330f561c2ecSJohn Baldwin break;
331f561c2ecSJohn Baldwin default:
332f561c2ecSJohn Baldwin __assert_unreachable();
333f561c2ecSJohn Baldwin }
334f561c2ecSJohn Baldwin }
335*a9b61512SJohn Baldwin
336*a9b61512SJohn Baldwin /*
337*a9b61512SJohn Baldwin * memdesc_alloc_ext_mbufs allocates a chain of external mbufs backed
338*a9b61512SJohn Baldwin * by the storage of a memory descriptor's data buffer.
339*a9b61512SJohn Baldwin */
340*a9b61512SJohn Baldwin static struct mbuf *
vaddr_ext_mbuf(memdesc_alloc_ext_mbuf_t * ext_alloc,void * cb_arg,int how,void * buf,size_t len,size_t * actual_len)341*a9b61512SJohn Baldwin vaddr_ext_mbuf(memdesc_alloc_ext_mbuf_t *ext_alloc, void *cb_arg, int how,
342*a9b61512SJohn Baldwin void *buf, size_t len, size_t *actual_len)
343*a9b61512SJohn Baldwin {
344*a9b61512SJohn Baldwin *actual_len = len;
345*a9b61512SJohn Baldwin return (ext_alloc(cb_arg, how, buf, len));
346*a9b61512SJohn Baldwin }
347*a9b61512SJohn Baldwin
348*a9b61512SJohn Baldwin static bool
can_append_paddr(struct mbuf * m,vm_paddr_t pa)349*a9b61512SJohn Baldwin can_append_paddr(struct mbuf *m, vm_paddr_t pa)
350*a9b61512SJohn Baldwin {
351*a9b61512SJohn Baldwin u_int last_len;
352*a9b61512SJohn Baldwin
353*a9b61512SJohn Baldwin /* Can always append to an empty mbuf. */
354*a9b61512SJohn Baldwin if (m->m_epg_npgs == 0)
355*a9b61512SJohn Baldwin return (true);
356*a9b61512SJohn Baldwin
357*a9b61512SJohn Baldwin /* Can't append to a full mbuf. */
358*a9b61512SJohn Baldwin if (m->m_epg_npgs == MBUF_PEXT_MAX_PGS)
359*a9b61512SJohn Baldwin return (false);
360*a9b61512SJohn Baldwin
361*a9b61512SJohn Baldwin /* Can't append a non-page-aligned address to a non-empty mbuf. */
362*a9b61512SJohn Baldwin if ((pa & PAGE_MASK) != 0)
363*a9b61512SJohn Baldwin return (false);
364*a9b61512SJohn Baldwin
365*a9b61512SJohn Baldwin /* Can't append if the last page is not a full page. */
366*a9b61512SJohn Baldwin last_len = m->m_epg_last_len;
367*a9b61512SJohn Baldwin if (m->m_epg_npgs == 1)
368*a9b61512SJohn Baldwin last_len += m->m_epg_1st_off;
369*a9b61512SJohn Baldwin return (last_len == PAGE_SIZE);
370*a9b61512SJohn Baldwin }
371*a9b61512SJohn Baldwin
372*a9b61512SJohn Baldwin /*
373*a9b61512SJohn Baldwin * Returns amount of data added to an M_EXTPG mbuf.
374*a9b61512SJohn Baldwin */
375*a9b61512SJohn Baldwin static size_t
append_paddr_range(struct mbuf * m,vm_paddr_t pa,size_t len)376*a9b61512SJohn Baldwin append_paddr_range(struct mbuf *m, vm_paddr_t pa, size_t len)
377*a9b61512SJohn Baldwin {
378*a9b61512SJohn Baldwin size_t appended;
379*a9b61512SJohn Baldwin
380*a9b61512SJohn Baldwin appended = 0;
381*a9b61512SJohn Baldwin
382*a9b61512SJohn Baldwin /* Append the first page. */
383*a9b61512SJohn Baldwin if (m->m_epg_npgs == 0) {
384*a9b61512SJohn Baldwin m->m_epg_pa[0] = trunc_page(pa);
385*a9b61512SJohn Baldwin m->m_epg_npgs = 1;
386*a9b61512SJohn Baldwin m->m_epg_1st_off = pa & PAGE_MASK;
387*a9b61512SJohn Baldwin m->m_epg_last_len = PAGE_SIZE - m->m_epg_1st_off;
388*a9b61512SJohn Baldwin if (m->m_epg_last_len > len)
389*a9b61512SJohn Baldwin m->m_epg_last_len = len;
390*a9b61512SJohn Baldwin m->m_len = m->m_epg_last_len;
391*a9b61512SJohn Baldwin len -= m->m_epg_last_len;
392*a9b61512SJohn Baldwin pa += m->m_epg_last_len;
393*a9b61512SJohn Baldwin appended += m->m_epg_last_len;
394*a9b61512SJohn Baldwin }
395*a9b61512SJohn Baldwin KASSERT(len == 0 || (pa & PAGE_MASK) == 0,
396*a9b61512SJohn Baldwin ("PA not aligned before full pages"));
397*a9b61512SJohn Baldwin
398*a9b61512SJohn Baldwin /* Full pages. */
399*a9b61512SJohn Baldwin while (len >= PAGE_SIZE && m->m_epg_npgs < MBUF_PEXT_MAX_PGS) {
400*a9b61512SJohn Baldwin m->m_epg_pa[m->m_epg_npgs] = pa;
401*a9b61512SJohn Baldwin m->m_epg_npgs++;
402*a9b61512SJohn Baldwin m->m_epg_last_len = PAGE_SIZE;
403*a9b61512SJohn Baldwin m->m_len += PAGE_SIZE;
404*a9b61512SJohn Baldwin pa += PAGE_SIZE;
405*a9b61512SJohn Baldwin len -= PAGE_SIZE;
406*a9b61512SJohn Baldwin appended += PAGE_SIZE;
407*a9b61512SJohn Baldwin }
408*a9b61512SJohn Baldwin
409*a9b61512SJohn Baldwin /* Final partial page. */
410*a9b61512SJohn Baldwin if (len > 0 && m->m_epg_npgs < MBUF_PEXT_MAX_PGS) {
411*a9b61512SJohn Baldwin KASSERT(len < PAGE_SIZE, ("final page is full page"));
412*a9b61512SJohn Baldwin m->m_epg_pa[m->m_epg_npgs] = pa;
413*a9b61512SJohn Baldwin m->m_epg_npgs++;
414*a9b61512SJohn Baldwin m->m_epg_last_len = len;
415*a9b61512SJohn Baldwin m->m_len += len;
416*a9b61512SJohn Baldwin appended += len;
417*a9b61512SJohn Baldwin }
418*a9b61512SJohn Baldwin
419*a9b61512SJohn Baldwin return (appended);
420*a9b61512SJohn Baldwin }
421*a9b61512SJohn Baldwin
422*a9b61512SJohn Baldwin static struct mbuf *
paddr_ext_mbuf(memdesc_alloc_extpg_mbuf_t * extpg_alloc,void * cb_arg,int how,vm_paddr_t pa,size_t len,size_t * actual_len,bool can_truncate)423*a9b61512SJohn Baldwin paddr_ext_mbuf(memdesc_alloc_extpg_mbuf_t *extpg_alloc, void *cb_arg, int how,
424*a9b61512SJohn Baldwin vm_paddr_t pa, size_t len, size_t *actual_len, bool can_truncate)
425*a9b61512SJohn Baldwin {
426*a9b61512SJohn Baldwin struct mbuf *m, *tail;
427*a9b61512SJohn Baldwin size_t appended;
428*a9b61512SJohn Baldwin
429*a9b61512SJohn Baldwin if (can_truncate) {
430*a9b61512SJohn Baldwin vm_paddr_t end;
431*a9b61512SJohn Baldwin
432*a9b61512SJohn Baldwin /*
433*a9b61512SJohn Baldwin * Trim any partial page at the end, but not if it's
434*a9b61512SJohn Baldwin * the only page.
435*a9b61512SJohn Baldwin */
436*a9b61512SJohn Baldwin end = trunc_page(pa + len);
437*a9b61512SJohn Baldwin if (end > pa)
438*a9b61512SJohn Baldwin len = end - pa;
439*a9b61512SJohn Baldwin }
440*a9b61512SJohn Baldwin *actual_len = len;
441*a9b61512SJohn Baldwin
442*a9b61512SJohn Baldwin m = tail = extpg_alloc(cb_arg, how);
443*a9b61512SJohn Baldwin if (m == NULL)
444*a9b61512SJohn Baldwin return (NULL);
445*a9b61512SJohn Baldwin while (len > 0) {
446*a9b61512SJohn Baldwin if (!can_append_paddr(tail, pa)) {
447*a9b61512SJohn Baldwin MBUF_EXT_PGS_ASSERT_SANITY(tail);
448*a9b61512SJohn Baldwin tail->m_next = extpg_alloc(cb_arg, how);
449*a9b61512SJohn Baldwin if (tail->m_next == NULL)
450*a9b61512SJohn Baldwin goto error;
451*a9b61512SJohn Baldwin tail = tail->m_next;
452*a9b61512SJohn Baldwin }
453*a9b61512SJohn Baldwin
454*a9b61512SJohn Baldwin appended = append_paddr_range(tail, pa, len);
455*a9b61512SJohn Baldwin KASSERT(appended > 0, ("did not append anything"));
456*a9b61512SJohn Baldwin KASSERT(appended <= len, ("appended too much"));
457*a9b61512SJohn Baldwin
458*a9b61512SJohn Baldwin pa += appended;
459*a9b61512SJohn Baldwin len -= appended;
460*a9b61512SJohn Baldwin }
461*a9b61512SJohn Baldwin
462*a9b61512SJohn Baldwin MBUF_EXT_PGS_ASSERT_SANITY(tail);
463*a9b61512SJohn Baldwin return (m);
464*a9b61512SJohn Baldwin error:
465*a9b61512SJohn Baldwin m_freem(m);
466*a9b61512SJohn Baldwin return (NULL);
467*a9b61512SJohn Baldwin }
468*a9b61512SJohn Baldwin
469*a9b61512SJohn Baldwin static struct mbuf *
vlist_ext_mbuf(memdesc_alloc_ext_mbuf_t * ext_alloc,void * cb_arg,int how,struct bus_dma_segment * vlist,u_int sglist_cnt,size_t offset,size_t len,size_t * actual_len)470*a9b61512SJohn Baldwin vlist_ext_mbuf(memdesc_alloc_ext_mbuf_t *ext_alloc, void *cb_arg, int how,
471*a9b61512SJohn Baldwin struct bus_dma_segment *vlist, u_int sglist_cnt, size_t offset,
472*a9b61512SJohn Baldwin size_t len, size_t *actual_len)
473*a9b61512SJohn Baldwin {
474*a9b61512SJohn Baldwin struct mbuf *m, *n, *tail;
475*a9b61512SJohn Baldwin size_t todo;
476*a9b61512SJohn Baldwin
477*a9b61512SJohn Baldwin *actual_len = len;
478*a9b61512SJohn Baldwin
479*a9b61512SJohn Baldwin while (vlist->ds_len <= offset) {
480*a9b61512SJohn Baldwin KASSERT(sglist_cnt > 1, ("out of sglist entries"));
481*a9b61512SJohn Baldwin
482*a9b61512SJohn Baldwin offset -= vlist->ds_len;
483*a9b61512SJohn Baldwin vlist++;
484*a9b61512SJohn Baldwin sglist_cnt--;
485*a9b61512SJohn Baldwin }
486*a9b61512SJohn Baldwin
487*a9b61512SJohn Baldwin m = tail = NULL;
488*a9b61512SJohn Baldwin while (len > 0) {
489*a9b61512SJohn Baldwin KASSERT(sglist_cnt >= 1, ("out of sglist entries"));
490*a9b61512SJohn Baldwin
491*a9b61512SJohn Baldwin todo = len;
492*a9b61512SJohn Baldwin if (todo > vlist->ds_len - offset)
493*a9b61512SJohn Baldwin todo = vlist->ds_len - offset;
494*a9b61512SJohn Baldwin
495*a9b61512SJohn Baldwin n = ext_alloc(cb_arg, how, (char *)(uintptr_t)vlist->ds_addr +
496*a9b61512SJohn Baldwin offset, todo);
497*a9b61512SJohn Baldwin if (n == NULL)
498*a9b61512SJohn Baldwin goto error;
499*a9b61512SJohn Baldwin
500*a9b61512SJohn Baldwin if (m == NULL) {
501*a9b61512SJohn Baldwin m = n;
502*a9b61512SJohn Baldwin tail = m;
503*a9b61512SJohn Baldwin } else {
504*a9b61512SJohn Baldwin tail->m_next = n;
505*a9b61512SJohn Baldwin tail = n;
506*a9b61512SJohn Baldwin }
507*a9b61512SJohn Baldwin
508*a9b61512SJohn Baldwin offset = 0;
509*a9b61512SJohn Baldwin vlist++;
510*a9b61512SJohn Baldwin sglist_cnt--;
511*a9b61512SJohn Baldwin len -= todo;
512*a9b61512SJohn Baldwin }
513*a9b61512SJohn Baldwin
514*a9b61512SJohn Baldwin return (m);
515*a9b61512SJohn Baldwin error:
516*a9b61512SJohn Baldwin m_freem(m);
517*a9b61512SJohn Baldwin return (NULL);
518*a9b61512SJohn Baldwin }
519*a9b61512SJohn Baldwin
520*a9b61512SJohn Baldwin static struct mbuf *
plist_ext_mbuf(memdesc_alloc_extpg_mbuf_t * extpg_alloc,void * cb_arg,int how,struct bus_dma_segment * plist,u_int sglist_cnt,size_t offset,size_t len,size_t * actual_len,bool can_truncate)521*a9b61512SJohn Baldwin plist_ext_mbuf(memdesc_alloc_extpg_mbuf_t *extpg_alloc, void *cb_arg, int how,
522*a9b61512SJohn Baldwin struct bus_dma_segment *plist, u_int sglist_cnt, size_t offset, size_t len,
523*a9b61512SJohn Baldwin size_t *actual_len, bool can_truncate)
524*a9b61512SJohn Baldwin {
525*a9b61512SJohn Baldwin vm_paddr_t pa;
526*a9b61512SJohn Baldwin struct mbuf *m, *tail;
527*a9b61512SJohn Baldwin size_t appended, totlen, todo;
528*a9b61512SJohn Baldwin
529*a9b61512SJohn Baldwin while (plist->ds_len <= offset) {
530*a9b61512SJohn Baldwin KASSERT(sglist_cnt > 1, ("out of sglist entries"));
531*a9b61512SJohn Baldwin
532*a9b61512SJohn Baldwin offset -= plist->ds_len;
533*a9b61512SJohn Baldwin plist++;
534*a9b61512SJohn Baldwin sglist_cnt--;
535*a9b61512SJohn Baldwin }
536*a9b61512SJohn Baldwin
537*a9b61512SJohn Baldwin totlen = 0;
538*a9b61512SJohn Baldwin m = tail = extpg_alloc(cb_arg, how);
539*a9b61512SJohn Baldwin if (m == NULL)
540*a9b61512SJohn Baldwin return (NULL);
541*a9b61512SJohn Baldwin while (len > 0) {
542*a9b61512SJohn Baldwin KASSERT(sglist_cnt >= 1, ("out of sglist entries"));
543*a9b61512SJohn Baldwin
544*a9b61512SJohn Baldwin pa = plist->ds_addr + offset;
545*a9b61512SJohn Baldwin todo = len;
546*a9b61512SJohn Baldwin if (todo > plist->ds_len - offset)
547*a9b61512SJohn Baldwin todo = plist->ds_len - offset;
548*a9b61512SJohn Baldwin
549*a9b61512SJohn Baldwin /*
550*a9b61512SJohn Baldwin * If truncation is enabled, avoid sending a final
551*a9b61512SJohn Baldwin * partial page, but only if there is more data
552*a9b61512SJohn Baldwin * available in the current segment. Also, at least
553*a9b61512SJohn Baldwin * some data must be sent, so only drop the final page
554*a9b61512SJohn Baldwin * for this segment if the segment spans multiple
555*a9b61512SJohn Baldwin * pages or some other data is already queued.
556*a9b61512SJohn Baldwin */
557*a9b61512SJohn Baldwin else if (can_truncate) {
558*a9b61512SJohn Baldwin vm_paddr_t end;
559*a9b61512SJohn Baldwin
560*a9b61512SJohn Baldwin end = trunc_page(pa + len);
561*a9b61512SJohn Baldwin if (end <= pa && totlen != 0) {
562*a9b61512SJohn Baldwin /*
563*a9b61512SJohn Baldwin * This last segment is only a partial
564*a9b61512SJohn Baldwin * page.
565*a9b61512SJohn Baldwin */
566*a9b61512SJohn Baldwin len = 0;
567*a9b61512SJohn Baldwin break;
568*a9b61512SJohn Baldwin }
569*a9b61512SJohn Baldwin todo = end - pa;
570*a9b61512SJohn Baldwin }
571*a9b61512SJohn Baldwin
572*a9b61512SJohn Baldwin offset = 0;
573*a9b61512SJohn Baldwin len -= todo;
574*a9b61512SJohn Baldwin totlen += todo;
575*a9b61512SJohn Baldwin
576*a9b61512SJohn Baldwin while (todo > 0) {
577*a9b61512SJohn Baldwin if (!can_append_paddr(tail, pa)) {
578*a9b61512SJohn Baldwin MBUF_EXT_PGS_ASSERT_SANITY(tail);
579*a9b61512SJohn Baldwin tail->m_next = extpg_alloc(cb_arg, how);
580*a9b61512SJohn Baldwin if (tail->m_next == NULL)
581*a9b61512SJohn Baldwin goto error;
582*a9b61512SJohn Baldwin tail = tail->m_next;
583*a9b61512SJohn Baldwin }
584*a9b61512SJohn Baldwin
585*a9b61512SJohn Baldwin appended = append_paddr_range(tail, pa, todo);
586*a9b61512SJohn Baldwin KASSERT(appended > 0, ("did not append anything"));
587*a9b61512SJohn Baldwin
588*a9b61512SJohn Baldwin pa += appended;
589*a9b61512SJohn Baldwin todo -= appended;
590*a9b61512SJohn Baldwin }
591*a9b61512SJohn Baldwin }
592*a9b61512SJohn Baldwin
593*a9b61512SJohn Baldwin MBUF_EXT_PGS_ASSERT_SANITY(tail);
594*a9b61512SJohn Baldwin *actual_len = totlen;
595*a9b61512SJohn Baldwin return (m);
596*a9b61512SJohn Baldwin error:
597*a9b61512SJohn Baldwin m_freem(m);
598*a9b61512SJohn Baldwin return (NULL);
599*a9b61512SJohn Baldwin }
600*a9b61512SJohn Baldwin
601*a9b61512SJohn Baldwin static struct mbuf *
vmpages_ext_mbuf(memdesc_alloc_extpg_mbuf_t * extpg_alloc,void * cb_arg,int how,vm_page_t * ma,size_t offset,size_t len,size_t * actual_len,bool can_truncate)602*a9b61512SJohn Baldwin vmpages_ext_mbuf(memdesc_alloc_extpg_mbuf_t *extpg_alloc, void *cb_arg, int how,
603*a9b61512SJohn Baldwin vm_page_t *ma, size_t offset, size_t len, size_t *actual_len,
604*a9b61512SJohn Baldwin bool can_truncate)
605*a9b61512SJohn Baldwin {
606*a9b61512SJohn Baldwin struct mbuf *m, *tail;
607*a9b61512SJohn Baldwin
608*a9b61512SJohn Baldwin while (offset >= PAGE_SIZE) {
609*a9b61512SJohn Baldwin ma++;
610*a9b61512SJohn Baldwin offset -= PAGE_SIZE;
611*a9b61512SJohn Baldwin }
612*a9b61512SJohn Baldwin
613*a9b61512SJohn Baldwin if (can_truncate) {
614*a9b61512SJohn Baldwin size_t end;
615*a9b61512SJohn Baldwin
616*a9b61512SJohn Baldwin /*
617*a9b61512SJohn Baldwin * Trim any partial page at the end, but not if it's
618*a9b61512SJohn Baldwin * the only page.
619*a9b61512SJohn Baldwin */
620*a9b61512SJohn Baldwin end = trunc_page(offset + len);
621*a9b61512SJohn Baldwin if (end > offset)
622*a9b61512SJohn Baldwin len = end - offset;
623*a9b61512SJohn Baldwin }
624*a9b61512SJohn Baldwin *actual_len = len;
625*a9b61512SJohn Baldwin
626*a9b61512SJohn Baldwin m = tail = extpg_alloc(cb_arg, how);
627*a9b61512SJohn Baldwin if (m == NULL)
628*a9b61512SJohn Baldwin return (NULL);
629*a9b61512SJohn Baldwin
630*a9b61512SJohn Baldwin /* First page. */
631*a9b61512SJohn Baldwin m->m_epg_pa[0] = VM_PAGE_TO_PHYS(*ma);
632*a9b61512SJohn Baldwin ma++;
633*a9b61512SJohn Baldwin m->m_epg_npgs = 1;
634*a9b61512SJohn Baldwin m->m_epg_1st_off = offset;
635*a9b61512SJohn Baldwin m->m_epg_last_len = PAGE_SIZE - offset;
636*a9b61512SJohn Baldwin if (m->m_epg_last_len > len)
637*a9b61512SJohn Baldwin m->m_epg_last_len = len;
638*a9b61512SJohn Baldwin m->m_len = m->m_epg_last_len;
639*a9b61512SJohn Baldwin len -= m->m_epg_last_len;
640*a9b61512SJohn Baldwin
641*a9b61512SJohn Baldwin /* Full pages. */
642*a9b61512SJohn Baldwin while (len >= PAGE_SIZE) {
643*a9b61512SJohn Baldwin if (tail->m_epg_npgs == MBUF_PEXT_MAX_PGS) {
644*a9b61512SJohn Baldwin MBUF_EXT_PGS_ASSERT_SANITY(tail);
645*a9b61512SJohn Baldwin tail->m_next = extpg_alloc(cb_arg, how);
646*a9b61512SJohn Baldwin if (tail->m_next == NULL)
647*a9b61512SJohn Baldwin goto error;
648*a9b61512SJohn Baldwin tail = tail->m_next;
649*a9b61512SJohn Baldwin }
650*a9b61512SJohn Baldwin
651*a9b61512SJohn Baldwin tail->m_epg_pa[tail->m_epg_npgs] = VM_PAGE_TO_PHYS(*ma);
652*a9b61512SJohn Baldwin ma++;
653*a9b61512SJohn Baldwin tail->m_epg_npgs++;
654*a9b61512SJohn Baldwin tail->m_epg_last_len = PAGE_SIZE;
655*a9b61512SJohn Baldwin tail->m_len += PAGE_SIZE;
656*a9b61512SJohn Baldwin len -= PAGE_SIZE;
657*a9b61512SJohn Baldwin }
658*a9b61512SJohn Baldwin
659*a9b61512SJohn Baldwin /* Last partial page. */
660*a9b61512SJohn Baldwin if (len > 0) {
661*a9b61512SJohn Baldwin if (tail->m_epg_npgs == MBUF_PEXT_MAX_PGS) {
662*a9b61512SJohn Baldwin MBUF_EXT_PGS_ASSERT_SANITY(tail);
663*a9b61512SJohn Baldwin tail->m_next = extpg_alloc(cb_arg, how);
664*a9b61512SJohn Baldwin if (tail->m_next == NULL)
665*a9b61512SJohn Baldwin goto error;
666*a9b61512SJohn Baldwin tail = tail->m_next;
667*a9b61512SJohn Baldwin }
668*a9b61512SJohn Baldwin
669*a9b61512SJohn Baldwin tail->m_epg_pa[tail->m_epg_npgs] = VM_PAGE_TO_PHYS(*ma);
670*a9b61512SJohn Baldwin ma++;
671*a9b61512SJohn Baldwin tail->m_epg_npgs++;
672*a9b61512SJohn Baldwin tail->m_epg_last_len = len;
673*a9b61512SJohn Baldwin tail->m_len += len;
674*a9b61512SJohn Baldwin }
675*a9b61512SJohn Baldwin
676*a9b61512SJohn Baldwin MBUF_EXT_PGS_ASSERT_SANITY(tail);
677*a9b61512SJohn Baldwin return (m);
678*a9b61512SJohn Baldwin error:
679*a9b61512SJohn Baldwin m_freem(m);
680*a9b61512SJohn Baldwin return (NULL);
681*a9b61512SJohn Baldwin }
682*a9b61512SJohn Baldwin
683*a9b61512SJohn Baldwin /*
684*a9b61512SJohn Baldwin * Somewhat similar to m_copym but optionally avoids a partial mbuf at
685*a9b61512SJohn Baldwin * the end.
686*a9b61512SJohn Baldwin */
687*a9b61512SJohn Baldwin static struct mbuf *
mbuf_subchain(struct mbuf * m0,size_t offset,size_t len,size_t * actual_len,bool can_truncate,int how)688*a9b61512SJohn Baldwin mbuf_subchain(struct mbuf *m0, size_t offset, size_t len,
689*a9b61512SJohn Baldwin size_t *actual_len, bool can_truncate, int how)
690*a9b61512SJohn Baldwin {
691*a9b61512SJohn Baldwin struct mbuf *m, *tail;
692*a9b61512SJohn Baldwin size_t totlen;
693*a9b61512SJohn Baldwin
694*a9b61512SJohn Baldwin while (offset >= m0->m_len) {
695*a9b61512SJohn Baldwin offset -= m0->m_len;
696*a9b61512SJohn Baldwin m0 = m0->m_next;
697*a9b61512SJohn Baldwin }
698*a9b61512SJohn Baldwin
699*a9b61512SJohn Baldwin /* Always return at least one mbuf. */
700*a9b61512SJohn Baldwin totlen = m0->m_len - offset;
701*a9b61512SJohn Baldwin if (totlen > len)
702*a9b61512SJohn Baldwin totlen = len;
703*a9b61512SJohn Baldwin
704*a9b61512SJohn Baldwin m = m_get(how, MT_DATA);
705*a9b61512SJohn Baldwin if (m == NULL)
706*a9b61512SJohn Baldwin return (NULL);
707*a9b61512SJohn Baldwin m->m_len = totlen;
708*a9b61512SJohn Baldwin if (m0->m_flags & (M_EXT | M_EXTPG)) {
709*a9b61512SJohn Baldwin m->m_data = m0->m_data + offset;
710*a9b61512SJohn Baldwin mb_dupcl(m, m0);
711*a9b61512SJohn Baldwin } else
712*a9b61512SJohn Baldwin memcpy(mtod(m, void *), mtodo(m0, offset), m->m_len);
713*a9b61512SJohn Baldwin
714*a9b61512SJohn Baldwin tail = m;
715*a9b61512SJohn Baldwin m0 = m0->m_next;
716*a9b61512SJohn Baldwin len -= totlen;
717*a9b61512SJohn Baldwin while (len > 0) {
718*a9b61512SJohn Baldwin /*
719*a9b61512SJohn Baldwin * If truncation is enabled, don't send any partial
720*a9b61512SJohn Baldwin * mbufs besides the first one.
721*a9b61512SJohn Baldwin */
722*a9b61512SJohn Baldwin if (can_truncate && m0->m_len > len)
723*a9b61512SJohn Baldwin break;
724*a9b61512SJohn Baldwin
725*a9b61512SJohn Baldwin tail->m_next = m_get(how, MT_DATA);
726*a9b61512SJohn Baldwin if (tail->m_next == NULL)
727*a9b61512SJohn Baldwin goto error;
728*a9b61512SJohn Baldwin tail = tail->m_next;
729*a9b61512SJohn Baldwin tail->m_len = m0->m_len;
730*a9b61512SJohn Baldwin if (m0->m_flags & (M_EXT | M_EXTPG)) {
731*a9b61512SJohn Baldwin tail->m_data = m0->m_data;
732*a9b61512SJohn Baldwin mb_dupcl(tail, m0);
733*a9b61512SJohn Baldwin } else
734*a9b61512SJohn Baldwin memcpy(mtod(tail, void *), mtod(m0, void *),
735*a9b61512SJohn Baldwin tail->m_len);
736*a9b61512SJohn Baldwin
737*a9b61512SJohn Baldwin totlen += tail->m_len;
738*a9b61512SJohn Baldwin m0 = m0->m_next;
739*a9b61512SJohn Baldwin len -= tail->m_len;
740*a9b61512SJohn Baldwin }
741*a9b61512SJohn Baldwin *actual_len = totlen;
742*a9b61512SJohn Baldwin return (m);
743*a9b61512SJohn Baldwin error:
744*a9b61512SJohn Baldwin m_freem(m);
745*a9b61512SJohn Baldwin return (NULL);
746*a9b61512SJohn Baldwin }
747*a9b61512SJohn Baldwin
748*a9b61512SJohn Baldwin struct mbuf *
memdesc_alloc_ext_mbufs(struct memdesc * mem,memdesc_alloc_ext_mbuf_t * ext_alloc,memdesc_alloc_extpg_mbuf_t * extpg_alloc,void * cb_arg,int how,size_t offset,size_t len,size_t * actual_len,bool can_truncate)749*a9b61512SJohn Baldwin memdesc_alloc_ext_mbufs(struct memdesc *mem,
750*a9b61512SJohn Baldwin memdesc_alloc_ext_mbuf_t *ext_alloc,
751*a9b61512SJohn Baldwin memdesc_alloc_extpg_mbuf_t *extpg_alloc, void *cb_arg, int how,
752*a9b61512SJohn Baldwin size_t offset, size_t len, size_t *actual_len, bool can_truncate)
753*a9b61512SJohn Baldwin {
754*a9b61512SJohn Baldwin struct mbuf *m;
755*a9b61512SJohn Baldwin size_t done;
756*a9b61512SJohn Baldwin
757*a9b61512SJohn Baldwin switch (mem->md_type) {
758*a9b61512SJohn Baldwin case MEMDESC_VADDR:
759*a9b61512SJohn Baldwin m = vaddr_ext_mbuf(ext_alloc, cb_arg, how,
760*a9b61512SJohn Baldwin (char *)mem->u.md_vaddr + offset, len, &done);
761*a9b61512SJohn Baldwin break;
762*a9b61512SJohn Baldwin case MEMDESC_PADDR:
763*a9b61512SJohn Baldwin m = paddr_ext_mbuf(extpg_alloc, cb_arg, how, mem->u.md_paddr +
764*a9b61512SJohn Baldwin offset, len, &done, can_truncate);
765*a9b61512SJohn Baldwin break;
766*a9b61512SJohn Baldwin case MEMDESC_VLIST:
767*a9b61512SJohn Baldwin m = vlist_ext_mbuf(ext_alloc, cb_arg, how, mem->u.md_list,
768*a9b61512SJohn Baldwin mem->md_nseg, offset, len, &done);
769*a9b61512SJohn Baldwin break;
770*a9b61512SJohn Baldwin case MEMDESC_PLIST:
771*a9b61512SJohn Baldwin m = plist_ext_mbuf(extpg_alloc, cb_arg, how, mem->u.md_list,
772*a9b61512SJohn Baldwin mem->md_nseg, offset, len, &done, can_truncate);
773*a9b61512SJohn Baldwin break;
774*a9b61512SJohn Baldwin case MEMDESC_UIO:
775*a9b61512SJohn Baldwin panic("uio not supported");
776*a9b61512SJohn Baldwin case MEMDESC_MBUF:
777*a9b61512SJohn Baldwin m = mbuf_subchain(mem->u.md_mbuf, offset, len, &done,
778*a9b61512SJohn Baldwin can_truncate, how);
779*a9b61512SJohn Baldwin break;
780*a9b61512SJohn Baldwin case MEMDESC_VMPAGES:
781*a9b61512SJohn Baldwin m = vmpages_ext_mbuf(extpg_alloc, cb_arg, how, mem->u.md_ma,
782*a9b61512SJohn Baldwin mem->md_offset + offset, len, &done, can_truncate);
783*a9b61512SJohn Baldwin break;
784*a9b61512SJohn Baldwin default:
785*a9b61512SJohn Baldwin __assert_unreachable();
786*a9b61512SJohn Baldwin }
787*a9b61512SJohn Baldwin if (m == NULL)
788*a9b61512SJohn Baldwin return (NULL);
789*a9b61512SJohn Baldwin
790*a9b61512SJohn Baldwin if (can_truncate) {
791*a9b61512SJohn Baldwin KASSERT(done <= len, ("chain too long"));
792*a9b61512SJohn Baldwin } else {
793*a9b61512SJohn Baldwin KASSERT(done == len, ("short chain with no limit"));
794*a9b61512SJohn Baldwin }
795*a9b61512SJohn Baldwin KASSERT(m_length(m, NULL) == done, ("length mismatch"));
796*a9b61512SJohn Baldwin if (actual_len != NULL)
797*a9b61512SJohn Baldwin *actual_len = done;
798*a9b61512SJohn Baldwin return (m);
799*a9b61512SJohn Baldwin }
800