1a5f7a14bSFrançois Tigeot /*-
2a5f7a14bSFrançois Tigeot * Copyright (c) 2008 Yahoo!, Inc.
3a5f7a14bSFrançois Tigeot * All rights reserved.
4a5f7a14bSFrançois Tigeot * Written by: John Baldwin <jhb@FreeBSD.org>
5a5f7a14bSFrançois Tigeot *
6a5f7a14bSFrançois Tigeot * Redistribution and use in source and binary forms, with or without
7a5f7a14bSFrançois Tigeot * modification, are permitted provided that the following conditions
8a5f7a14bSFrançois Tigeot * are met:
9a5f7a14bSFrançois Tigeot * 1. Redistributions of source code must retain the above copyright
10a5f7a14bSFrançois Tigeot * notice, this list of conditions and the following disclaimer.
11a5f7a14bSFrançois Tigeot * 2. Redistributions in binary form must reproduce the above copyright
12a5f7a14bSFrançois Tigeot * notice, this list of conditions and the following disclaimer in the
13a5f7a14bSFrançois Tigeot * documentation and/or other materials provided with the distribution.
14a5f7a14bSFrançois Tigeot * 3. Neither the name of the author nor the names of any co-contributors
15a5f7a14bSFrançois Tigeot * may be used to endorse or promote products derived from this software
16a5f7a14bSFrançois Tigeot * without specific prior written permission.
17a5f7a14bSFrançois Tigeot *
18a5f7a14bSFrançois Tigeot * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19a5f7a14bSFrançois Tigeot * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20a5f7a14bSFrançois Tigeot * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21a5f7a14bSFrançois Tigeot * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22a5f7a14bSFrançois Tigeot * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23a5f7a14bSFrançois Tigeot * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24a5f7a14bSFrançois Tigeot * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25a5f7a14bSFrançois Tigeot * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26a5f7a14bSFrançois Tigeot * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27a5f7a14bSFrançois Tigeot * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28a5f7a14bSFrançois Tigeot * SUCH DAMAGE.
29a5f7a14bSFrançois Tigeot *
30a5f7a14bSFrançois Tigeot * $FreeBSD: src/sys/kern/subr_sglist.c,v 1.3 2009/08/21 02:59:07 jhb Exp $
31a5f7a14bSFrançois Tigeot */
32a5f7a14bSFrançois Tigeot
33a5f7a14bSFrançois Tigeot
34a5f7a14bSFrançois Tigeot #include <sys/param.h>
35a5f7a14bSFrançois Tigeot #include <sys/kernel.h>
36a5f7a14bSFrançois Tigeot #include <sys/malloc.h>
37a5f7a14bSFrançois Tigeot #include <sys/mbuf.h>
38a5f7a14bSFrançois Tigeot #include <sys/proc.h>
39a5f7a14bSFrançois Tigeot #include <sys/sglist.h>
40a5f7a14bSFrançois Tigeot #include <sys/uio.h>
41a5f7a14bSFrançois Tigeot
42a5f7a14bSFrançois Tigeot #include <vm/vm.h>
43a5f7a14bSFrançois Tigeot #include <vm/pmap.h>
44a5f7a14bSFrançois Tigeot #include <vm/vm_map.h>
45a5f7a14bSFrançois Tigeot
46a5f7a14bSFrançois Tigeot #include <sys/ktr.h>
47a5f7a14bSFrançois Tigeot
48a5f7a14bSFrançois Tigeot static MALLOC_DEFINE(M_SGLIST, "sglist", "scatter/gather lists");
49a5f7a14bSFrançois Tigeot
50a5f7a14bSFrançois Tigeot /*
51a5f7a14bSFrançois Tigeot * Convenience macros to save the state of an sglist so it can be restored
52a5f7a14bSFrançois Tigeot * if an append attempt fails. Since sglist's only grow we only need to
53a5f7a14bSFrançois Tigeot * save the current count of segments and the length of the ending segment.
54a5f7a14bSFrançois Tigeot * Earlier segments will not be changed by an append, and the only change
55a5f7a14bSFrançois Tigeot * that can occur to the ending segment is that it can be extended.
56a5f7a14bSFrançois Tigeot */
57a5f7a14bSFrançois Tigeot struct sgsave {
58a5f7a14bSFrançois Tigeot u_short sg_nseg;
59a5f7a14bSFrançois Tigeot size_t ss_len;
60a5f7a14bSFrançois Tigeot };
61a5f7a14bSFrançois Tigeot
62a5f7a14bSFrançois Tigeot #define SGLIST_SAVE(sg, sgsave) do { \
63a5f7a14bSFrançois Tigeot (sgsave).sg_nseg = (sg)->sg_nseg; \
64a5f7a14bSFrançois Tigeot if ((sgsave).sg_nseg > 0) \
65a5f7a14bSFrançois Tigeot (sgsave).ss_len = (sg)->sg_segs[(sgsave).sg_nseg - 1].ss_len; \
66a5f7a14bSFrançois Tigeot else \
67a5f7a14bSFrançois Tigeot (sgsave).ss_len = 0; \
68a5f7a14bSFrançois Tigeot } while (0)
69a5f7a14bSFrançois Tigeot
70a5f7a14bSFrançois Tigeot #define SGLIST_RESTORE(sg, sgsave) do { \
71a5f7a14bSFrançois Tigeot (sg)->sg_nseg = (sgsave).sg_nseg; \
72a5f7a14bSFrançois Tigeot if ((sgsave).sg_nseg > 0) \
73a5f7a14bSFrançois Tigeot (sg)->sg_segs[(sgsave).sg_nseg - 1].ss_len = (sgsave).ss_len; \
74a5f7a14bSFrançois Tigeot } while (0)
75a5f7a14bSFrançois Tigeot
76a5f7a14bSFrançois Tigeot /*
77a5f7a14bSFrançois Tigeot * Append a single (paddr, len) to a sglist. sg is the list and ss is
78a5f7a14bSFrançois Tigeot * the current segment in the list. If we run out of segments then
79a5f7a14bSFrançois Tigeot * EFBIG will be returned.
80a5f7a14bSFrançois Tigeot */
81a5f7a14bSFrançois Tigeot static __inline int
_sglist_append_range(struct sglist * sg,struct sglist_seg ** ssp,vm_paddr_t paddr,size_t len)82a5f7a14bSFrançois Tigeot _sglist_append_range(struct sglist *sg, struct sglist_seg **ssp,
83a5f7a14bSFrançois Tigeot vm_paddr_t paddr, size_t len)
84a5f7a14bSFrançois Tigeot {
85a5f7a14bSFrançois Tigeot struct sglist_seg *ss;
86a5f7a14bSFrançois Tigeot
87a5f7a14bSFrançois Tigeot ss = *ssp;
88a5f7a14bSFrançois Tigeot if (ss->ss_paddr + ss->ss_len == paddr)
89a5f7a14bSFrançois Tigeot ss->ss_len += len;
90a5f7a14bSFrançois Tigeot else {
91a5f7a14bSFrançois Tigeot if (sg->sg_nseg == sg->sg_maxseg)
92a5f7a14bSFrançois Tigeot return (EFBIG);
93a5f7a14bSFrançois Tigeot ss++;
94a5f7a14bSFrançois Tigeot ss->ss_paddr = paddr;
95a5f7a14bSFrançois Tigeot ss->ss_len = len;
96a5f7a14bSFrançois Tigeot sg->sg_nseg++;
97a5f7a14bSFrançois Tigeot *ssp = ss;
98a5f7a14bSFrançois Tigeot }
99a5f7a14bSFrançois Tigeot return (0);
100a5f7a14bSFrançois Tigeot }
101a5f7a14bSFrançois Tigeot
102a5f7a14bSFrançois Tigeot /*
103a5f7a14bSFrançois Tigeot * Worker routine to append a virtual address range (either kernel or
104a5f7a14bSFrançois Tigeot * user) to a scatter/gather list.
105a5f7a14bSFrançois Tigeot */
106a5f7a14bSFrançois Tigeot static __inline int
_sglist_append_buf(struct sglist * sg,void * buf,size_t len,pmap_t pmap,size_t * donep)107a5f7a14bSFrançois Tigeot _sglist_append_buf(struct sglist *sg, void *buf, size_t len, pmap_t pmap,
108a5f7a14bSFrançois Tigeot size_t *donep)
109a5f7a14bSFrançois Tigeot {
110a5f7a14bSFrançois Tigeot struct sglist_seg *ss;
111a5f7a14bSFrançois Tigeot vm_offset_t vaddr, offset;
112a5f7a14bSFrançois Tigeot vm_paddr_t paddr;
113*76f1911eSMatthew Dillon void *handle;
114a5f7a14bSFrançois Tigeot size_t seglen;
115a5f7a14bSFrançois Tigeot int error;
116a5f7a14bSFrançois Tigeot
117a5f7a14bSFrançois Tigeot if (donep)
118a5f7a14bSFrançois Tigeot *donep = 0;
119a5f7a14bSFrançois Tigeot if (len == 0)
120a5f7a14bSFrançois Tigeot return (0);
121a5f7a14bSFrançois Tigeot
122a5f7a14bSFrançois Tigeot /* Do the first page. It may have an offset. */
123a5f7a14bSFrançois Tigeot vaddr = (vm_offset_t)buf;
124a5f7a14bSFrançois Tigeot offset = vaddr & PAGE_MASK;
125*76f1911eSMatthew Dillon if (pmap != NULL) {
126*76f1911eSMatthew Dillon paddr = pmap_extract(pmap, vaddr, &handle);
127*76f1911eSMatthew Dillon } else {
128a5f7a14bSFrançois Tigeot paddr = pmap_kextract(vaddr);
129*76f1911eSMatthew Dillon handle = NULL;
130*76f1911eSMatthew Dillon }
131a5f7a14bSFrançois Tigeot seglen = MIN(len, PAGE_SIZE - offset);
132a5f7a14bSFrançois Tigeot if (sg->sg_nseg == 0) {
133a5f7a14bSFrançois Tigeot ss = sg->sg_segs;
134a5f7a14bSFrançois Tigeot ss->ss_paddr = paddr;
135a5f7a14bSFrançois Tigeot ss->ss_len = seglen;
136a5f7a14bSFrançois Tigeot sg->sg_nseg = 1;
137a5f7a14bSFrançois Tigeot } else {
138a5f7a14bSFrançois Tigeot ss = &sg->sg_segs[sg->sg_nseg - 1];
139a5f7a14bSFrançois Tigeot error = _sglist_append_range(sg, &ss, paddr, seglen);
140*76f1911eSMatthew Dillon if (error) {
141*76f1911eSMatthew Dillon pmap_extract_done(handle);
142a5f7a14bSFrançois Tigeot return (error);
143a5f7a14bSFrançois Tigeot }
144*76f1911eSMatthew Dillon }
145*76f1911eSMatthew Dillon pmap_extract_done(handle);
146a5f7a14bSFrançois Tigeot vaddr += seglen;
147a5f7a14bSFrançois Tigeot len -= seglen;
148a5f7a14bSFrançois Tigeot if (donep)
149a5f7a14bSFrançois Tigeot *donep += seglen;
150a5f7a14bSFrançois Tigeot
151a5f7a14bSFrançois Tigeot while (len > 0) {
152a5f7a14bSFrançois Tigeot seglen = MIN(len, PAGE_SIZE);
153*76f1911eSMatthew Dillon if (pmap != NULL) {
154*76f1911eSMatthew Dillon paddr = pmap_extract(pmap, vaddr, &handle);
155*76f1911eSMatthew Dillon error = _sglist_append_range(sg, &ss, paddr, seglen);
156*76f1911eSMatthew Dillon pmap_extract_done(handle);
157*76f1911eSMatthew Dillon } else {
158a5f7a14bSFrançois Tigeot paddr = pmap_kextract(vaddr);
159a5f7a14bSFrançois Tigeot error = _sglist_append_range(sg, &ss, paddr, seglen);
160*76f1911eSMatthew Dillon }
161a5f7a14bSFrançois Tigeot if (error)
162a5f7a14bSFrançois Tigeot return (error);
163a5f7a14bSFrançois Tigeot vaddr += seglen;
164a5f7a14bSFrançois Tigeot len -= seglen;
165a5f7a14bSFrançois Tigeot if (donep)
166a5f7a14bSFrançois Tigeot *donep += seglen;
167a5f7a14bSFrançois Tigeot }
168a5f7a14bSFrançois Tigeot
169a5f7a14bSFrançois Tigeot return (0);
170a5f7a14bSFrançois Tigeot }
171a5f7a14bSFrançois Tigeot
172a5f7a14bSFrançois Tigeot /*
173a5f7a14bSFrançois Tigeot * Determine the number of scatter/gather list elements needed to
174a5f7a14bSFrançois Tigeot * describe a kernel virtual address range.
175a5f7a14bSFrançois Tigeot */
176a5f7a14bSFrançois Tigeot int
sglist_count(void * buf,size_t len)177a5f7a14bSFrançois Tigeot sglist_count(void *buf, size_t len)
178a5f7a14bSFrançois Tigeot {
179a5f7a14bSFrançois Tigeot vm_offset_t vaddr, vendaddr;
180a5f7a14bSFrançois Tigeot vm_paddr_t lastaddr, paddr;
181a5f7a14bSFrançois Tigeot int nsegs;
182a5f7a14bSFrançois Tigeot
183a5f7a14bSFrançois Tigeot if (len == 0)
184a5f7a14bSFrançois Tigeot return (0);
185a5f7a14bSFrançois Tigeot
186a5f7a14bSFrançois Tigeot vaddr = trunc_page((vm_offset_t)buf);
187a5f7a14bSFrançois Tigeot vendaddr = (vm_offset_t)buf + len;
188a5f7a14bSFrançois Tigeot nsegs = 1;
189a5f7a14bSFrançois Tigeot lastaddr = pmap_kextract(vaddr);
190a5f7a14bSFrançois Tigeot vaddr += PAGE_SIZE;
191a5f7a14bSFrançois Tigeot while (vaddr < vendaddr) {
192a5f7a14bSFrançois Tigeot paddr = pmap_kextract(vaddr);
193a5f7a14bSFrançois Tigeot if (lastaddr + PAGE_SIZE != paddr)
194a5f7a14bSFrançois Tigeot nsegs++;
195a5f7a14bSFrançois Tigeot lastaddr = paddr;
196a5f7a14bSFrançois Tigeot vaddr += PAGE_SIZE;
197a5f7a14bSFrançois Tigeot }
198a5f7a14bSFrançois Tigeot return (nsegs);
199a5f7a14bSFrançois Tigeot }
200a5f7a14bSFrançois Tigeot
201a5f7a14bSFrançois Tigeot /*
202a5f7a14bSFrançois Tigeot * Allocate a scatter/gather list along with 'nsegs' segments. The
203a5f7a14bSFrançois Tigeot * 'mflags' parameters are the same as passed to kmalloc(9). The caller
204a5f7a14bSFrançois Tigeot * should use sglist_free() to free this list.
205a5f7a14bSFrançois Tigeot */
206a5f7a14bSFrançois Tigeot struct sglist *
sglist_alloc(int nsegs,int mflags)207a5f7a14bSFrançois Tigeot sglist_alloc(int nsegs, int mflags)
208a5f7a14bSFrançois Tigeot {
209a5f7a14bSFrançois Tigeot struct sglist *sg;
210a5f7a14bSFrançois Tigeot
211a5f7a14bSFrançois Tigeot sg = kmalloc(sizeof(struct sglist) + nsegs * sizeof(struct sglist_seg),
212a5f7a14bSFrançois Tigeot M_SGLIST, mflags);
213a5f7a14bSFrançois Tigeot if (sg == NULL)
214a5f7a14bSFrançois Tigeot return (NULL);
215a5f7a14bSFrançois Tigeot sglist_init(sg, nsegs, (struct sglist_seg *)(sg + 1));
216a5f7a14bSFrançois Tigeot return (sg);
217a5f7a14bSFrançois Tigeot }
218a5f7a14bSFrançois Tigeot
219a5f7a14bSFrançois Tigeot /*
220a5f7a14bSFrançois Tigeot * Free a scatter/gather list allocated via sglist_allc().
221a5f7a14bSFrançois Tigeot */
222a5f7a14bSFrançois Tigeot void
sglist_free(struct sglist * sg)223a5f7a14bSFrançois Tigeot sglist_free(struct sglist *sg)
224a5f7a14bSFrançois Tigeot {
225a5f7a14bSFrançois Tigeot
226a5f7a14bSFrançois Tigeot if (refcount_release(&sg->sg_refs))
227a5f7a14bSFrançois Tigeot kfree(sg, M_SGLIST);
228a5f7a14bSFrançois Tigeot }
229a5f7a14bSFrançois Tigeot
230a5f7a14bSFrançois Tigeot /*
231a5f7a14bSFrançois Tigeot * Append the segments to describe a single kernel virtual address
232a5f7a14bSFrançois Tigeot * range to a scatter/gather list. If there are insufficient
233a5f7a14bSFrançois Tigeot * segments, then this fails with EFBIG.
234a5f7a14bSFrançois Tigeot */
235a5f7a14bSFrançois Tigeot int
sglist_append(struct sglist * sg,void * buf,size_t len)236a5f7a14bSFrançois Tigeot sglist_append(struct sglist *sg, void *buf, size_t len)
237a5f7a14bSFrançois Tigeot {
238a5f7a14bSFrançois Tigeot struct sgsave save;
239a5f7a14bSFrançois Tigeot int error;
240a5f7a14bSFrançois Tigeot
241a5f7a14bSFrançois Tigeot if (sg->sg_maxseg == 0)
242a5f7a14bSFrançois Tigeot return (EINVAL);
243a5f7a14bSFrançois Tigeot SGLIST_SAVE(sg, save);
244a5f7a14bSFrançois Tigeot error = _sglist_append_buf(sg, buf, len, NULL, NULL);
245a5f7a14bSFrançois Tigeot if (error)
246a5f7a14bSFrançois Tigeot SGLIST_RESTORE(sg, save);
247a5f7a14bSFrançois Tigeot return (error);
248a5f7a14bSFrançois Tigeot }
249a5f7a14bSFrançois Tigeot
250a5f7a14bSFrançois Tigeot /*
251a5f7a14bSFrançois Tigeot * Append a single physical address range to a scatter/gather list.
252a5f7a14bSFrançois Tigeot * If there are insufficient segments, then this fails with EFBIG.
253a5f7a14bSFrançois Tigeot */
254a5f7a14bSFrançois Tigeot int
sglist_append_phys(struct sglist * sg,vm_paddr_t paddr,size_t len)255a5f7a14bSFrançois Tigeot sglist_append_phys(struct sglist *sg, vm_paddr_t paddr, size_t len)
256a5f7a14bSFrançois Tigeot {
257a5f7a14bSFrançois Tigeot struct sglist_seg *ss;
258a5f7a14bSFrançois Tigeot struct sgsave save;
259a5f7a14bSFrançois Tigeot int error;
260a5f7a14bSFrançois Tigeot
261a5f7a14bSFrançois Tigeot if (sg->sg_maxseg == 0)
262a5f7a14bSFrançois Tigeot return (EINVAL);
263a5f7a14bSFrançois Tigeot if (len == 0)
264a5f7a14bSFrançois Tigeot return (0);
265a5f7a14bSFrançois Tigeot
266a5f7a14bSFrançois Tigeot if (sg->sg_nseg == 0) {
267a5f7a14bSFrançois Tigeot sg->sg_segs[0].ss_paddr = paddr;
268a5f7a14bSFrançois Tigeot sg->sg_segs[0].ss_len = len;
269a5f7a14bSFrançois Tigeot sg->sg_nseg = 1;
270a5f7a14bSFrançois Tigeot return (0);
271a5f7a14bSFrançois Tigeot }
272a5f7a14bSFrançois Tigeot ss = &sg->sg_segs[sg->sg_nseg - 1];
273a5f7a14bSFrançois Tigeot SGLIST_SAVE(sg, save);
274a5f7a14bSFrançois Tigeot error = _sglist_append_range(sg, &ss, paddr, len);
275a5f7a14bSFrançois Tigeot if (error)
276a5f7a14bSFrançois Tigeot SGLIST_RESTORE(sg, save);
277a5f7a14bSFrançois Tigeot return (error);
278a5f7a14bSFrançois Tigeot }
279a5f7a14bSFrançois Tigeot
280a5f7a14bSFrançois Tigeot /*
281a5f7a14bSFrançois Tigeot * Append the segments that describe a single mbuf chain to a
282a5f7a14bSFrançois Tigeot * scatter/gather list. If there are insufficient segments, then this
283a5f7a14bSFrançois Tigeot * fails with EFBIG.
284a5f7a14bSFrançois Tigeot */
285a5f7a14bSFrançois Tigeot int
sglist_append_mbuf(struct sglist * sg,struct mbuf * m0)286a5f7a14bSFrançois Tigeot sglist_append_mbuf(struct sglist *sg, struct mbuf *m0)
287a5f7a14bSFrançois Tigeot {
288a5f7a14bSFrançois Tigeot struct sgsave save;
289a5f7a14bSFrançois Tigeot struct mbuf *m;
290a5f7a14bSFrançois Tigeot int error;
291a5f7a14bSFrançois Tigeot
292a5f7a14bSFrançois Tigeot if (sg->sg_maxseg == 0)
293a5f7a14bSFrançois Tigeot return (EINVAL);
294a5f7a14bSFrançois Tigeot
295a5f7a14bSFrançois Tigeot error = 0;
296a5f7a14bSFrançois Tigeot SGLIST_SAVE(sg, save);
297a5f7a14bSFrançois Tigeot for (m = m0; m != NULL; m = m->m_next) {
298a5f7a14bSFrançois Tigeot if (m->m_len > 0) {
299a5f7a14bSFrançois Tigeot error = sglist_append(sg, m->m_data, m->m_len);
300a5f7a14bSFrançois Tigeot if (error) {
301a5f7a14bSFrançois Tigeot SGLIST_RESTORE(sg, save);
302a5f7a14bSFrançois Tigeot return (error);
303a5f7a14bSFrançois Tigeot }
304a5f7a14bSFrançois Tigeot }
305a5f7a14bSFrançois Tigeot }
306a5f7a14bSFrançois Tigeot return (0);
307a5f7a14bSFrançois Tigeot }
308a5f7a14bSFrançois Tigeot
309a5f7a14bSFrançois Tigeot /*
310a5f7a14bSFrançois Tigeot * Append the segments that describe a single user address range to a
311a5f7a14bSFrançois Tigeot * scatter/gather list. If there are insufficient segments, then this
312a5f7a14bSFrançois Tigeot * fails with EFBIG.
313a5f7a14bSFrançois Tigeot */
314a5f7a14bSFrançois Tigeot int
sglist_append_user(struct sglist * sg,void * buf,size_t len,struct thread * td)315a5f7a14bSFrançois Tigeot sglist_append_user(struct sglist *sg, void *buf, size_t len, struct thread *td)
316a5f7a14bSFrançois Tigeot {
317a5f7a14bSFrançois Tigeot struct sgsave save;
318a5f7a14bSFrançois Tigeot int error;
319a5f7a14bSFrançois Tigeot
320a5f7a14bSFrançois Tigeot if (sg->sg_maxseg == 0)
321a5f7a14bSFrançois Tigeot return (EINVAL);
322a5f7a14bSFrançois Tigeot SGLIST_SAVE(sg, save);
323a5f7a14bSFrançois Tigeot error = _sglist_append_buf(sg, buf, len,
324a5f7a14bSFrançois Tigeot vmspace_pmap(td->td_proc->p_vmspace), NULL);
325a5f7a14bSFrançois Tigeot if (error)
326a5f7a14bSFrançois Tigeot SGLIST_RESTORE(sg, save);
327a5f7a14bSFrançois Tigeot return (error);
328a5f7a14bSFrançois Tigeot }
329a5f7a14bSFrançois Tigeot
330a5f7a14bSFrançois Tigeot /*
331a5f7a14bSFrançois Tigeot * Append the segments that describe a single uio to a scatter/gather
332a5f7a14bSFrançois Tigeot * list. If there are insufficient segments, then this fails with
333a5f7a14bSFrançois Tigeot * EFBIG.
334a5f7a14bSFrançois Tigeot */
335a5f7a14bSFrançois Tigeot int
sglist_append_uio(struct sglist * sg,struct uio * uio)336a5f7a14bSFrançois Tigeot sglist_append_uio(struct sglist *sg, struct uio *uio)
337a5f7a14bSFrançois Tigeot {
338a5f7a14bSFrançois Tigeot struct iovec *iov;
339a5f7a14bSFrançois Tigeot struct sgsave save;
340a5f7a14bSFrançois Tigeot size_t resid, minlen;
341a5f7a14bSFrançois Tigeot pmap_t pmap;
342a5f7a14bSFrançois Tigeot int error, i;
343a5f7a14bSFrançois Tigeot
344a5f7a14bSFrançois Tigeot if (sg->sg_maxseg == 0)
345a5f7a14bSFrançois Tigeot return (EINVAL);
346a5f7a14bSFrançois Tigeot
347a5f7a14bSFrançois Tigeot resid = uio->uio_resid;
348a5f7a14bSFrançois Tigeot iov = uio->uio_iov;
349a5f7a14bSFrançois Tigeot
350a5f7a14bSFrançois Tigeot if (uio->uio_segflg == UIO_USERSPACE) {
351a5f7a14bSFrançois Tigeot KASSERT(uio->uio_td != NULL,
352a5f7a14bSFrançois Tigeot ("sglist_append_uio: USERSPACE but no thread"));
353a5f7a14bSFrançois Tigeot pmap = vmspace_pmap(uio->uio_td->td_proc->p_vmspace);
354a5f7a14bSFrançois Tigeot } else
355a5f7a14bSFrançois Tigeot pmap = NULL;
356a5f7a14bSFrançois Tigeot
357a5f7a14bSFrançois Tigeot error = 0;
358a5f7a14bSFrançois Tigeot SGLIST_SAVE(sg, save);
359a5f7a14bSFrançois Tigeot for (i = 0; i < uio->uio_iovcnt && resid != 0; i++) {
360a5f7a14bSFrançois Tigeot /*
361a5f7a14bSFrançois Tigeot * Now at the first iovec to load. Load each iovec
362a5f7a14bSFrançois Tigeot * until we have exhausted the residual count.
363a5f7a14bSFrançois Tigeot */
364a5f7a14bSFrançois Tigeot minlen = MIN(resid, iov[i].iov_len);
365a5f7a14bSFrançois Tigeot if (minlen > 0) {
366a5f7a14bSFrançois Tigeot error = _sglist_append_buf(sg, iov[i].iov_base, minlen,
367a5f7a14bSFrançois Tigeot pmap, NULL);
368a5f7a14bSFrançois Tigeot if (error) {
369a5f7a14bSFrançois Tigeot SGLIST_RESTORE(sg, save);
370a5f7a14bSFrançois Tigeot return (error);
371a5f7a14bSFrançois Tigeot }
372a5f7a14bSFrançois Tigeot resid -= minlen;
373a5f7a14bSFrançois Tigeot }
374a5f7a14bSFrançois Tigeot }
375a5f7a14bSFrançois Tigeot return (0);
376a5f7a14bSFrançois Tigeot }
377a5f7a14bSFrançois Tigeot
378a5f7a14bSFrançois Tigeot /*
379a5f7a14bSFrançois Tigeot * Append the segments that describe at most 'resid' bytes from a
380a5f7a14bSFrançois Tigeot * single uio to a scatter/gather list. If there are insufficient
381a5f7a14bSFrançois Tigeot * segments, then only the amount that fits is appended.
382a5f7a14bSFrançois Tigeot */
383a5f7a14bSFrançois Tigeot int
sglist_consume_uio(struct sglist * sg,struct uio * uio,size_t resid)384a5f7a14bSFrançois Tigeot sglist_consume_uio(struct sglist *sg, struct uio *uio, size_t resid)
385a5f7a14bSFrançois Tigeot {
386a5f7a14bSFrançois Tigeot struct iovec *iov;
387a5f7a14bSFrançois Tigeot size_t done;
388a5f7a14bSFrançois Tigeot pmap_t pmap;
389a5f7a14bSFrançois Tigeot int error, len;
390a5f7a14bSFrançois Tigeot
391a5f7a14bSFrançois Tigeot if (sg->sg_maxseg == 0)
392a5f7a14bSFrançois Tigeot return (EINVAL);
393a5f7a14bSFrançois Tigeot
394a5f7a14bSFrançois Tigeot if (uio->uio_segflg == UIO_USERSPACE) {
395a5f7a14bSFrançois Tigeot KASSERT(uio->uio_td != NULL,
396a5f7a14bSFrançois Tigeot ("sglist_consume_uio: USERSPACE but no thread"));
397a5f7a14bSFrançois Tigeot pmap = vmspace_pmap(uio->uio_td->td_proc->p_vmspace);
398a5f7a14bSFrançois Tigeot } else
399a5f7a14bSFrançois Tigeot pmap = NULL;
400a5f7a14bSFrançois Tigeot
401a5f7a14bSFrançois Tigeot error = 0;
402a5f7a14bSFrançois Tigeot while (resid > 0 && uio->uio_resid) {
403a5f7a14bSFrançois Tigeot iov = uio->uio_iov;
404a5f7a14bSFrançois Tigeot len = iov->iov_len;
405a5f7a14bSFrançois Tigeot if (len == 0) {
406a5f7a14bSFrançois Tigeot uio->uio_iov++;
407a5f7a14bSFrançois Tigeot uio->uio_iovcnt--;
408a5f7a14bSFrançois Tigeot continue;
409a5f7a14bSFrançois Tigeot }
410a5f7a14bSFrançois Tigeot if (len > resid)
411a5f7a14bSFrançois Tigeot len = resid;
412a5f7a14bSFrançois Tigeot
413a5f7a14bSFrançois Tigeot /*
414a5f7a14bSFrançois Tigeot * Try to append this iovec. If we run out of room,
415a5f7a14bSFrançois Tigeot * then break out of the loop.
416a5f7a14bSFrançois Tigeot */
417a5f7a14bSFrançois Tigeot error = _sglist_append_buf(sg, iov->iov_base, len, pmap, &done);
418a5f7a14bSFrançois Tigeot iov->iov_base = (char *)iov->iov_base + done;
419a5f7a14bSFrançois Tigeot iov->iov_len -= done;
420a5f7a14bSFrançois Tigeot uio->uio_resid -= done;
421a5f7a14bSFrançois Tigeot uio->uio_offset += done;
422a5f7a14bSFrançois Tigeot resid -= done;
423a5f7a14bSFrançois Tigeot if (error)
424a5f7a14bSFrançois Tigeot break;
425a5f7a14bSFrançois Tigeot }
426a5f7a14bSFrançois Tigeot return (0);
427a5f7a14bSFrançois Tigeot }
428a5f7a14bSFrançois Tigeot
429a5f7a14bSFrançois Tigeot /*
430a5f7a14bSFrançois Tigeot * Allocate and populate a scatter/gather list to describe a single
431a5f7a14bSFrançois Tigeot * kernel virtual address range.
432a5f7a14bSFrançois Tigeot */
433a5f7a14bSFrançois Tigeot struct sglist *
sglist_build(void * buf,size_t len,int mflags)434a5f7a14bSFrançois Tigeot sglist_build(void *buf, size_t len, int mflags)
435a5f7a14bSFrançois Tigeot {
436a5f7a14bSFrançois Tigeot struct sglist *sg;
437a5f7a14bSFrançois Tigeot int nsegs;
438a5f7a14bSFrançois Tigeot
439a5f7a14bSFrançois Tigeot if (len == 0)
440a5f7a14bSFrançois Tigeot return (NULL);
441a5f7a14bSFrançois Tigeot
442a5f7a14bSFrançois Tigeot nsegs = sglist_count(buf, len);
443a5f7a14bSFrançois Tigeot sg = sglist_alloc(nsegs, mflags);
444a5f7a14bSFrançois Tigeot if (sg == NULL)
445a5f7a14bSFrançois Tigeot return (NULL);
446a5f7a14bSFrançois Tigeot if (sglist_append(sg, buf, len) != 0) {
447a5f7a14bSFrançois Tigeot sglist_free(sg);
448a5f7a14bSFrançois Tigeot return (NULL);
449a5f7a14bSFrançois Tigeot }
450a5f7a14bSFrançois Tigeot return (sg);
451a5f7a14bSFrançois Tigeot }
452a5f7a14bSFrançois Tigeot
453a5f7a14bSFrançois Tigeot /*
454a5f7a14bSFrançois Tigeot * Clone a new copy of a scatter/gather list.
455a5f7a14bSFrançois Tigeot */
456a5f7a14bSFrançois Tigeot struct sglist *
sglist_clone(struct sglist * sg,int mflags)457a5f7a14bSFrançois Tigeot sglist_clone(struct sglist *sg, int mflags)
458a5f7a14bSFrançois Tigeot {
459a5f7a14bSFrançois Tigeot struct sglist *new;
460a5f7a14bSFrançois Tigeot
461a5f7a14bSFrançois Tigeot if (sg == NULL)
462a5f7a14bSFrançois Tigeot return (NULL);
463a5f7a14bSFrançois Tigeot new = sglist_alloc(sg->sg_maxseg, mflags);
464a5f7a14bSFrançois Tigeot if (new == NULL)
465a5f7a14bSFrançois Tigeot return (NULL);
466a5f7a14bSFrançois Tigeot new->sg_nseg = sg->sg_nseg;
467a5f7a14bSFrançois Tigeot bcopy(sg->sg_segs, new->sg_segs, sizeof(struct sglist_seg) *
468a5f7a14bSFrançois Tigeot sg->sg_nseg);
469a5f7a14bSFrançois Tigeot return (new);
470a5f7a14bSFrançois Tigeot }
471a5f7a14bSFrançois Tigeot
472a5f7a14bSFrançois Tigeot /*
473a5f7a14bSFrançois Tigeot * Calculate the total length of the segments described in a
474a5f7a14bSFrançois Tigeot * scatter/gather list.
475a5f7a14bSFrançois Tigeot */
476a5f7a14bSFrançois Tigeot size_t
sglist_length(struct sglist * sg)477a5f7a14bSFrançois Tigeot sglist_length(struct sglist *sg)
478a5f7a14bSFrançois Tigeot {
479a5f7a14bSFrançois Tigeot size_t space;
480a5f7a14bSFrançois Tigeot int i;
481a5f7a14bSFrançois Tigeot
482a5f7a14bSFrançois Tigeot space = 0;
483a5f7a14bSFrançois Tigeot for (i = 0; i < sg->sg_nseg; i++)
484a5f7a14bSFrançois Tigeot space += sg->sg_segs[i].ss_len;
485a5f7a14bSFrançois Tigeot return (space);
486a5f7a14bSFrançois Tigeot }
487a5f7a14bSFrançois Tigeot
488a5f7a14bSFrançois Tigeot /*
489a5f7a14bSFrançois Tigeot * Split a scatter/gather list into two lists. The scatter/gather
490a5f7a14bSFrançois Tigeot * entries for the first 'length' bytes of the 'original' list are
491a5f7a14bSFrançois Tigeot * stored in the '*head' list and are removed from 'original'.
492a5f7a14bSFrançois Tigeot *
493a5f7a14bSFrançois Tigeot * If '*head' is NULL, then a new list will be allocated using
494a5f7a14bSFrançois Tigeot * 'mflags'. If M_NOWAIT is specified and the allocation fails,
495a5f7a14bSFrançois Tigeot * ENOMEM will be returned.
496a5f7a14bSFrançois Tigeot *
497a5f7a14bSFrançois Tigeot * If '*head' is not NULL, it should point to an empty sglist. If it
498a5f7a14bSFrançois Tigeot * does not have enough room for the remaining space, then EFBIG will
499a5f7a14bSFrançois Tigeot * be returned. If '*head' is not empty, then EINVAL will be
500a5f7a14bSFrançois Tigeot * returned.
501a5f7a14bSFrançois Tigeot *
502a5f7a14bSFrançois Tigeot * If 'original' is shared (refcount > 1), then EDOOFUS will be
503a5f7a14bSFrançois Tigeot * returned.
504a5f7a14bSFrançois Tigeot */
505a5f7a14bSFrançois Tigeot int
sglist_split(struct sglist * original,struct sglist ** head,size_t length,int mflags)506a5f7a14bSFrançois Tigeot sglist_split(struct sglist *original, struct sglist **head, size_t length,
507a5f7a14bSFrançois Tigeot int mflags)
508a5f7a14bSFrançois Tigeot {
509a5f7a14bSFrançois Tigeot struct sglist *sg;
510a5f7a14bSFrançois Tigeot size_t space, split;
511a5f7a14bSFrançois Tigeot int count, i;
512a5f7a14bSFrançois Tigeot
513a5f7a14bSFrançois Tigeot if (original->sg_refs > 1)
514a5f7a14bSFrançois Tigeot return (EDOOFUS);
515a5f7a14bSFrançois Tigeot
516a5f7a14bSFrançois Tigeot /* Figure out how big of a sglist '*head' has to hold. */
517a5f7a14bSFrançois Tigeot count = 0;
518a5f7a14bSFrançois Tigeot space = 0;
519a5f7a14bSFrançois Tigeot split = 0;
520a5f7a14bSFrançois Tigeot for (i = 0; i < original->sg_nseg; i++) {
521a5f7a14bSFrançois Tigeot space += original->sg_segs[i].ss_len;
522a5f7a14bSFrançois Tigeot count++;
523a5f7a14bSFrançois Tigeot if (space >= length) {
524a5f7a14bSFrançois Tigeot /*
525a5f7a14bSFrançois Tigeot * If 'length' falls in the middle of a
526a5f7a14bSFrançois Tigeot * scatter/gather list entry, then 'split'
527a5f7a14bSFrançois Tigeot * holds how much of that entry will remain in
528a5f7a14bSFrançois Tigeot * 'original'.
529a5f7a14bSFrançois Tigeot */
530a5f7a14bSFrançois Tigeot split = space - length;
531a5f7a14bSFrançois Tigeot break;
532a5f7a14bSFrançois Tigeot }
533a5f7a14bSFrançois Tigeot }
534a5f7a14bSFrançois Tigeot
535a5f7a14bSFrançois Tigeot /* Nothing to do, so leave head empty. */
536a5f7a14bSFrançois Tigeot if (count == 0)
537a5f7a14bSFrançois Tigeot return (0);
538a5f7a14bSFrançois Tigeot
539a5f7a14bSFrançois Tigeot if (*head == NULL) {
540a5f7a14bSFrançois Tigeot sg = sglist_alloc(count, mflags);
541a5f7a14bSFrançois Tigeot if (sg == NULL)
542a5f7a14bSFrançois Tigeot return (ENOMEM);
543a5f7a14bSFrançois Tigeot *head = sg;
544a5f7a14bSFrançois Tigeot } else {
545a5f7a14bSFrançois Tigeot sg = *head;
546a5f7a14bSFrançois Tigeot if (sg->sg_maxseg < count)
547a5f7a14bSFrançois Tigeot return (EFBIG);
548a5f7a14bSFrançois Tigeot if (sg->sg_nseg != 0)
549a5f7a14bSFrançois Tigeot return (EINVAL);
550a5f7a14bSFrançois Tigeot }
551a5f7a14bSFrançois Tigeot
552a5f7a14bSFrançois Tigeot /* Copy 'count' entries to 'sg' from 'original'. */
553a5f7a14bSFrançois Tigeot bcopy(original->sg_segs, sg->sg_segs, count *
554a5f7a14bSFrançois Tigeot sizeof(struct sglist_seg));
555a5f7a14bSFrançois Tigeot sg->sg_nseg = count;
556a5f7a14bSFrançois Tigeot
557a5f7a14bSFrançois Tigeot /*
558a5f7a14bSFrançois Tigeot * If we had to split a list entry, fixup the last entry in
559a5f7a14bSFrançois Tigeot * 'sg' and the new first entry in 'original'. We also
560a5f7a14bSFrançois Tigeot * decrement 'count' by 1 since we will only be removing
561a5f7a14bSFrançois Tigeot * 'count - 1' segments from 'original' now.
562a5f7a14bSFrançois Tigeot */
563a5f7a14bSFrançois Tigeot if (split != 0) {
564a5f7a14bSFrançois Tigeot count--;
565a5f7a14bSFrançois Tigeot sg->sg_segs[count].ss_len -= split;
566a5f7a14bSFrançois Tigeot original->sg_segs[count].ss_paddr =
567a5f7a14bSFrançois Tigeot sg->sg_segs[count].ss_paddr + split;
568a5f7a14bSFrançois Tigeot original->sg_segs[count].ss_len = split;
569a5f7a14bSFrançois Tigeot }
570a5f7a14bSFrançois Tigeot
571a5f7a14bSFrançois Tigeot /* Trim 'count' entries from the front of 'original'. */
572a5f7a14bSFrançois Tigeot original->sg_nseg -= count;
573a5f7a14bSFrançois Tigeot bcopy(original->sg_segs + count, original->sg_segs, count *
574a5f7a14bSFrançois Tigeot sizeof(struct sglist_seg));
575a5f7a14bSFrançois Tigeot return (0);
576a5f7a14bSFrançois Tigeot }
577a5f7a14bSFrançois Tigeot
578a5f7a14bSFrançois Tigeot /*
579a5f7a14bSFrançois Tigeot * Append the scatter/gather list elements in 'second' to the
580a5f7a14bSFrançois Tigeot * scatter/gather list 'first'. If there is not enough space in
581a5f7a14bSFrançois Tigeot * 'first', EFBIG is returned.
582a5f7a14bSFrançois Tigeot */
583a5f7a14bSFrançois Tigeot int
sglist_join(struct sglist * first,struct sglist * second)584a5f7a14bSFrançois Tigeot sglist_join(struct sglist *first, struct sglist *second)
585a5f7a14bSFrançois Tigeot {
586a5f7a14bSFrançois Tigeot struct sglist_seg *flast, *sfirst;
587a5f7a14bSFrançois Tigeot int append;
588a5f7a14bSFrançois Tigeot
589a5f7a14bSFrançois Tigeot /* If 'second' is empty, there is nothing to do. */
590a5f7a14bSFrançois Tigeot if (second->sg_nseg == 0)
591a5f7a14bSFrançois Tigeot return (0);
592a5f7a14bSFrançois Tigeot
593a5f7a14bSFrançois Tigeot /*
594a5f7a14bSFrançois Tigeot * If the first entry in 'second' can be appended to the last entry
595a5f7a14bSFrançois Tigeot * in 'first' then set append to '1'.
596a5f7a14bSFrançois Tigeot */
597a5f7a14bSFrançois Tigeot append = 0;
598a5f7a14bSFrançois Tigeot flast = &first->sg_segs[first->sg_nseg - 1];
599a5f7a14bSFrançois Tigeot sfirst = &second->sg_segs[0];
600a5f7a14bSFrançois Tigeot if (first->sg_nseg != 0 &&
601a5f7a14bSFrançois Tigeot flast->ss_paddr + flast->ss_len == sfirst->ss_paddr)
602a5f7a14bSFrançois Tigeot append = 1;
603a5f7a14bSFrançois Tigeot
604a5f7a14bSFrançois Tigeot /* Make sure 'first' has enough room. */
605a5f7a14bSFrançois Tigeot if (first->sg_nseg + second->sg_nseg - append > first->sg_maxseg)
606a5f7a14bSFrançois Tigeot return (EFBIG);
607a5f7a14bSFrançois Tigeot
608a5f7a14bSFrançois Tigeot /* Merge last in 'first' and first in 'second' if needed. */
609a5f7a14bSFrançois Tigeot if (append)
610a5f7a14bSFrançois Tigeot flast->ss_len += sfirst->ss_len;
611a5f7a14bSFrançois Tigeot
612a5f7a14bSFrançois Tigeot /* Append new segments from 'second' to 'first'. */
613a5f7a14bSFrançois Tigeot bcopy(first->sg_segs + first->sg_nseg, second->sg_segs + append,
614a5f7a14bSFrançois Tigeot (second->sg_nseg - append) * sizeof(struct sglist_seg));
615a5f7a14bSFrançois Tigeot first->sg_nseg += second->sg_nseg - append;
616a5f7a14bSFrançois Tigeot sglist_reset(second);
617a5f7a14bSFrançois Tigeot return (0);
618a5f7a14bSFrançois Tigeot }
619a5f7a14bSFrançois Tigeot
620a5f7a14bSFrançois Tigeot /*
621a5f7a14bSFrançois Tigeot * Generate a new scatter/gather list from a range of an existing
622a5f7a14bSFrançois Tigeot * scatter/gather list. The 'offset' and 'length' parameters specify
623a5f7a14bSFrançois Tigeot * the logical range of the 'original' list to extract. If that range
624a5f7a14bSFrançois Tigeot * is not a subset of the length of 'original', then EINVAL is
625a5f7a14bSFrançois Tigeot * returned. The new scatter/gather list is stored in '*slice'.
626a5f7a14bSFrançois Tigeot *
627a5f7a14bSFrançois Tigeot * If '*slice' is NULL, then a new list will be allocated using
628a5f7a14bSFrançois Tigeot * 'mflags'. If M_NOWAIT is specified and the allocation fails,
629a5f7a14bSFrançois Tigeot * ENOMEM will be returned.
630a5f7a14bSFrançois Tigeot *
631a5f7a14bSFrançois Tigeot * If '*slice' is not NULL, it should point to an empty sglist. If it
632a5f7a14bSFrançois Tigeot * does not have enough room for the remaining space, then EFBIG will
633a5f7a14bSFrançois Tigeot * be returned. If '*slice' is not empty, then EINVAL will be
634a5f7a14bSFrançois Tigeot * returned.
635a5f7a14bSFrançois Tigeot */
636a5f7a14bSFrançois Tigeot int
sglist_slice(struct sglist * original,struct sglist ** slice,size_t offset,size_t length,int mflags)637a5f7a14bSFrançois Tigeot sglist_slice(struct sglist *original, struct sglist **slice, size_t offset,
638a5f7a14bSFrançois Tigeot size_t length, int mflags)
639a5f7a14bSFrançois Tigeot {
640a5f7a14bSFrançois Tigeot struct sglist *sg;
641a5f7a14bSFrançois Tigeot size_t space, end, foffs, loffs;
642a5f7a14bSFrançois Tigeot int count, i, fseg;
643a5f7a14bSFrançois Tigeot
644a5f7a14bSFrançois Tigeot /* Nothing to do. */
645a5f7a14bSFrançois Tigeot if (length == 0)
646a5f7a14bSFrançois Tigeot return (0);
647a5f7a14bSFrançois Tigeot
648a5f7a14bSFrançois Tigeot /* Figure out how many segments '*slice' needs to have. */
649a5f7a14bSFrançois Tigeot end = offset + length;
650a5f7a14bSFrançois Tigeot space = 0;
651a5f7a14bSFrançois Tigeot count = 0;
652a5f7a14bSFrançois Tigeot fseg = 0;
653a5f7a14bSFrançois Tigeot foffs = loffs = 0;
654a5f7a14bSFrançois Tigeot for (i = 0; i < original->sg_nseg; i++) {
655a5f7a14bSFrançois Tigeot space += original->sg_segs[i].ss_len;
656a5f7a14bSFrançois Tigeot if (space > offset) {
657a5f7a14bSFrançois Tigeot /*
658a5f7a14bSFrançois Tigeot * When we hit the first segment, store its index
659a5f7a14bSFrançois Tigeot * in 'fseg' and the offset into the first segment
660a5f7a14bSFrançois Tigeot * of 'offset' in 'foffs'.
661a5f7a14bSFrançois Tigeot */
662a5f7a14bSFrançois Tigeot if (count == 0) {
663a5f7a14bSFrançois Tigeot fseg = i;
664a5f7a14bSFrançois Tigeot foffs = offset - (space -
665a5f7a14bSFrançois Tigeot original->sg_segs[i].ss_len);
666a5f7a14bSFrançois Tigeot }
667a5f7a14bSFrançois Tigeot count++;
668a5f7a14bSFrançois Tigeot
669a5f7a14bSFrançois Tigeot /*
670a5f7a14bSFrançois Tigeot * When we hit the last segment, break out of
671a5f7a14bSFrançois Tigeot * the loop. Store the amount of extra space
672a5f7a14bSFrançois Tigeot * at the end of this segment in 'loffs'.
673a5f7a14bSFrançois Tigeot */
674a5f7a14bSFrançois Tigeot if (space >= end) {
675a5f7a14bSFrançois Tigeot loffs = space - end;
676a5f7a14bSFrançois Tigeot break;
677a5f7a14bSFrançois Tigeot }
678a5f7a14bSFrançois Tigeot }
679a5f7a14bSFrançois Tigeot }
680a5f7a14bSFrançois Tigeot
681a5f7a14bSFrançois Tigeot /* If we never hit 'end', then 'length' ran off the end, so fail. */
682a5f7a14bSFrançois Tigeot if (space < end)
683a5f7a14bSFrançois Tigeot return (EINVAL);
684a5f7a14bSFrançois Tigeot
685a5f7a14bSFrançois Tigeot if (*slice == NULL) {
686a5f7a14bSFrançois Tigeot sg = sglist_alloc(count, mflags);
687a5f7a14bSFrançois Tigeot if (sg == NULL)
688a5f7a14bSFrançois Tigeot return (ENOMEM);
689a5f7a14bSFrançois Tigeot *slice = sg;
690a5f7a14bSFrançois Tigeot } else {
691a5f7a14bSFrançois Tigeot sg = *slice;
692a5f7a14bSFrançois Tigeot if (sg->sg_maxseg < count)
693a5f7a14bSFrançois Tigeot return (EFBIG);
694a5f7a14bSFrançois Tigeot if (sg->sg_nseg != 0)
695a5f7a14bSFrançois Tigeot return (EINVAL);
696a5f7a14bSFrançois Tigeot }
697a5f7a14bSFrançois Tigeot
698a5f7a14bSFrançois Tigeot /*
699a5f7a14bSFrançois Tigeot * Copy over 'count' segments from 'original' starting at
700a5f7a14bSFrançois Tigeot * 'fseg' to 'sg'.
701a5f7a14bSFrançois Tigeot */
702a5f7a14bSFrançois Tigeot bcopy(original->sg_segs + fseg, sg->sg_segs,
703a5f7a14bSFrançois Tigeot count * sizeof(struct sglist_seg));
704a5f7a14bSFrançois Tigeot sg->sg_nseg = count;
705a5f7a14bSFrançois Tigeot
706a5f7a14bSFrançois Tigeot /* Fixup first and last segments if needed. */
707a5f7a14bSFrançois Tigeot if (foffs != 0) {
708a5f7a14bSFrançois Tigeot sg->sg_segs[0].ss_paddr += foffs;
709a5f7a14bSFrançois Tigeot sg->sg_segs[0].ss_len -= foffs;
710a5f7a14bSFrançois Tigeot }
711a5f7a14bSFrançois Tigeot if (loffs != 0) {
712a5f7a14bSFrançois Tigeot sg->sg_segs[count - 1].ss_len -= loffs;
713a5f7a14bSFrançois Tigeot }
714a5f7a14bSFrançois Tigeot return (0);
715a5f7a14bSFrançois Tigeot }
716