xref: /dragonfly/sys/kern/subr_sglist.c (revision 76f1911e)
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