xref: /dragonfly/sys/kern/libmchain/subr_mchain.c (revision 984263bc)
1 /*
2  * Copyright (c) 2000, 2001 Boris Popov
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *    This product includes software developed by Boris Popov.
16  * 4. Neither the name of the author nor the names of any co-contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * $FreeBSD: src/sys/kern/subr_mchain.c,v 1.2.2.2 2002/04/13 12:46:40 bp Exp $
33  */
34 
35 
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/kernel.h>
39 #include <sys/errno.h>
40 #include <sys/malloc.h>
41 #include <sys/mbuf.h>
42 #include <sys/module.h>
43 #include <sys/uio.h>
44 
45 #include <sys/mchain.h>
46 
47 MODULE_VERSION(libmchain, 1);
48 
49 #define MBERROR(format, args...) printf("%s(%d): "format, __FUNCTION__ , \
50 				    __LINE__ ,## args)
51 
52 #define MBPANIC(format, args...) printf("%s(%d): "format, __FUNCTION__ , \
53 				    __LINE__ ,## args)
54 
55 /*
56  * Various helper functions
57  */
58 int
59 m_fixhdr(struct mbuf *m0)
60 {
61 	struct mbuf *m = m0;
62 	int len = 0;
63 
64 	while (m) {
65 		len += m->m_len;
66 		m = m->m_next;
67 	}
68 	m0->m_pkthdr.len = len;
69 	return len;
70 }
71 
72 int
73 mb_init(struct mbchain *mbp)
74 {
75 	struct mbuf *m;
76 
77 	m = m_gethdr(M_WAITOK, MT_DATA);
78 	if (m == NULL)
79 		return ENOBUFS;
80 	m->m_pkthdr.rcvif = NULL;
81 	m->m_len = 0;
82 	mb_initm(mbp, m);
83 	return 0;
84 }
85 
86 void
87 mb_initm(struct mbchain *mbp, struct mbuf *m)
88 {
89 	bzero(mbp, sizeof(*mbp));
90 	mbp->mb_top = mbp->mb_cur = m;
91 	mbp->mb_mleft = M_TRAILINGSPACE(m);
92 }
93 
94 void
95 mb_done(struct mbchain *mbp)
96 {
97 	if (mbp->mb_top) {
98 		m_freem(mbp->mb_top);
99 		mbp->mb_top = NULL;
100 	}
101 }
102 
103 struct mbuf *
104 mb_detach(struct mbchain *mbp)
105 {
106 	struct mbuf *m;
107 
108 	m = mbp->mb_top;
109 	mbp->mb_top = NULL;
110 	return m;
111 }
112 
113 int
114 mb_fixhdr(struct mbchain *mbp)
115 {
116 	return mbp->mb_top->m_pkthdr.len = m_fixhdr(mbp->mb_top);
117 }
118 
119 /*
120  * Check if object of size 'size' fit to the current position and
121  * allocate new mbuf if not. Advance pointers and increase length of mbuf(s).
122  * Return pointer to the object placeholder or NULL if any error occured.
123  * Note: size should be <= MLEN
124  */
125 caddr_t
126 mb_reserve(struct mbchain *mbp, int size)
127 {
128 	struct mbuf *m, *mn;
129 	caddr_t bpos;
130 
131 	if (size > MLEN)
132 		panic("mb_reserve: size = %d\n", size);
133 	m = mbp->mb_cur;
134 	if (mbp->mb_mleft < size) {
135 		mn = m_get(M_WAITOK, MT_DATA);
136 		if (mn == NULL)
137 			return NULL;
138 		mbp->mb_cur = m->m_next = mn;
139 		m = mn;
140 		m->m_len = 0;
141 		mbp->mb_mleft = M_TRAILINGSPACE(m);
142 	}
143 	mbp->mb_mleft -= size;
144 	mbp->mb_count += size;
145 	bpos = mtod(m, caddr_t) + m->m_len;
146 	m->m_len += size;
147 	return bpos;
148 }
149 
150 int
151 mb_put_uint8(struct mbchain *mbp, u_int8_t x)
152 {
153 	return mb_put_mem(mbp, (caddr_t)&x, sizeof(x), MB_MSYSTEM);
154 }
155 
156 int
157 mb_put_uint16be(struct mbchain *mbp, u_int16_t x)
158 {
159 	x = htobes(x);
160 	return mb_put_mem(mbp, (caddr_t)&x, sizeof(x), MB_MSYSTEM);
161 }
162 
163 int
164 mb_put_uint16le(struct mbchain *mbp, u_int16_t x)
165 {
166 	x = htoles(x);
167 	return mb_put_mem(mbp, (caddr_t)&x, sizeof(x), MB_MSYSTEM);
168 }
169 
170 int
171 mb_put_uint32be(struct mbchain *mbp, u_int32_t x)
172 {
173 	x = htobel(x);
174 	return mb_put_mem(mbp, (caddr_t)&x, sizeof(x), MB_MSYSTEM);
175 }
176 
177 int
178 mb_put_uint32le(struct mbchain *mbp, u_int32_t x)
179 {
180 	x = htolel(x);
181 	return mb_put_mem(mbp, (caddr_t)&x, sizeof(x), MB_MSYSTEM);
182 }
183 
184 int
185 mb_put_int64be(struct mbchain *mbp, int64_t x)
186 {
187 	x = htobeq(x);
188 	return mb_put_mem(mbp, (caddr_t)&x, sizeof(x), MB_MSYSTEM);
189 }
190 
191 int
192 mb_put_int64le(struct mbchain *mbp, int64_t x)
193 {
194 	x = htoleq(x);
195 	return mb_put_mem(mbp, (caddr_t)&x, sizeof(x), MB_MSYSTEM);
196 }
197 
198 int
199 mb_put_mem(struct mbchain *mbp, c_caddr_t source, int size, int type)
200 {
201 	struct mbuf *m;
202 	caddr_t dst;
203 	c_caddr_t src;
204 	int cplen, error, mleft, count;
205 
206 	m = mbp->mb_cur;
207 	mleft = mbp->mb_mleft;
208 
209 	while (size > 0) {
210 		if (mleft == 0) {
211 			if (m->m_next == NULL) {
212 				m = m_getm(m, size, M_WAITOK, MT_DATA);
213 				if (m == NULL)
214 					return ENOBUFS;
215 			}
216 			m = m->m_next;
217 			mleft = M_TRAILINGSPACE(m);
218 			continue;
219 		}
220 		cplen = mleft > size ? size : mleft;
221 		dst = mtod(m, caddr_t) + m->m_len;
222 		switch (type) {
223 		    case MB_MCUSTOM:
224 			error = mbp->mb_copy(mbp, source, dst, cplen);
225 			if (error)
226 				return error;
227 			break;
228 		    case MB_MINLINE:
229 			for (src = source, count = cplen; count; count--)
230 				*dst++ = *src++;
231 			break;
232 		    case MB_MSYSTEM:
233 			bcopy(source, dst, cplen);
234 			break;
235 		    case MB_MUSER:
236 			error = copyin(source, dst, cplen);
237 			if (error)
238 				return error;
239 			break;
240 		    case MB_MZERO:
241 			bzero(dst, cplen);
242 			break;
243 		}
244 		size -= cplen;
245 		source += cplen;
246 		m->m_len += cplen;
247 		mleft -= cplen;
248 		mbp->mb_count += cplen;
249 	}
250 	mbp->mb_cur = m;
251 	mbp->mb_mleft = mleft;
252 	return 0;
253 }
254 
255 int
256 mb_put_mbuf(struct mbchain *mbp, struct mbuf *m)
257 {
258 	mbp->mb_cur->m_next = m;
259 	while (m) {
260 		mbp->mb_count += m->m_len;
261 		if (m->m_next == NULL)
262 			break;
263 		m = m->m_next;
264 	}
265 	mbp->mb_mleft = M_TRAILINGSPACE(m);
266 	mbp->mb_cur = m;
267 	return 0;
268 }
269 
270 /*
271  * copies a uio scatter/gather list to an mbuf chain.
272  */
273 int
274 mb_put_uio(struct mbchain *mbp, struct uio *uiop, int size)
275 {
276 	long left;
277 	int mtype, error;
278 
279 	mtype = (uiop->uio_segflg == UIO_SYSSPACE) ? MB_MSYSTEM : MB_MUSER;
280 
281 	while (size > 0 && uiop->uio_resid) {
282 		if (uiop->uio_iovcnt <= 0 || uiop->uio_iov == NULL)
283 			return EFBIG;
284 		left = uiop->uio_iov->iov_len;
285 		if (left == 0) {
286 			uiop->uio_iov++;
287 			uiop->uio_iovcnt--;
288 			continue;
289 		}
290 		if (left > size)
291 			left = size;
292 		error = mb_put_mem(mbp, uiop->uio_iov->iov_base, left, mtype);
293 		if (error)
294 			return error;
295 		uiop->uio_offset += left;
296 		uiop->uio_resid -= left;
297 		uiop->uio_iov->iov_base += left;
298 		uiop->uio_iov->iov_len -= left;
299 		size -= left;
300 	}
301 	return 0;
302 }
303 
304 /*
305  * Routines for fetching data from an mbuf chain
306  */
307 int
308 md_init(struct mdchain *mdp)
309 {
310 	struct mbuf *m;
311 
312 	m = m_gethdr(M_WAITOK, MT_DATA);
313 	if (m == NULL)
314 		return ENOBUFS;
315 	m->m_pkthdr.rcvif = NULL;
316 	m->m_len = 0;
317 	md_initm(mdp, m);
318 	return 0;
319 }
320 
321 void
322 md_initm(struct mdchain *mdp, struct mbuf *m)
323 {
324 	bzero(mdp, sizeof(*mdp));
325 	mdp->md_top = mdp->md_cur = m;
326 	mdp->md_pos = mtod(m, u_char*);
327 }
328 
329 void
330 md_done(struct mdchain *mdp)
331 {
332 	if (mdp->md_top) {
333 		m_freem(mdp->md_top);
334 		mdp->md_top = NULL;
335 	}
336 }
337 
338 /*
339  * Append a separate mbuf chain. It is caller responsibility to prevent
340  * multiple calls to fetch/record routines.
341  */
342 void
343 md_append_record(struct mdchain *mdp, struct mbuf *top)
344 {
345 	struct mbuf *m;
346 
347 	if (mdp->md_top == NULL) {
348 		md_initm(mdp, top);
349 		return;
350 	}
351 	m = mdp->md_top;
352 	while (m->m_nextpkt)
353 		m = m->m_nextpkt;
354 	m->m_nextpkt = top;
355 	top->m_nextpkt = NULL;
356 	return;
357 }
358 
359 /*
360  * Put next record in place of existing
361  */
362 int
363 md_next_record(struct mdchain *mdp)
364 {
365 	struct mbuf *m;
366 
367 	if (mdp->md_top == NULL)
368 		return ENOENT;
369 	m = mdp->md_top->m_nextpkt;
370 	md_done(mdp);
371 	if (m == NULL)
372 		return ENOENT;
373 	md_initm(mdp, m);
374 	return 0;
375 }
376 
377 int
378 md_get_uint8(struct mdchain *mdp, u_int8_t *x)
379 {
380 	return md_get_mem(mdp, x, 1, MB_MINLINE);
381 }
382 
383 int
384 md_get_uint16(struct mdchain *mdp, u_int16_t *x)
385 {
386 	return md_get_mem(mdp, (caddr_t)x, 2, MB_MINLINE);
387 }
388 
389 int
390 md_get_uint16le(struct mdchain *mdp, u_int16_t *x)
391 {
392 	u_int16_t v;
393 	int error = md_get_uint16(mdp, &v);
394 
395 	*x = letohs(v);
396 	return error;
397 }
398 
399 int
400 md_get_uint16be(struct mdchain *mdp, u_int16_t *x) {
401 	u_int16_t v;
402 	int error = md_get_uint16(mdp, &v);
403 
404 	*x = betohs(v);
405 	return error;
406 }
407 
408 int
409 md_get_uint32(struct mdchain *mdp, u_int32_t *x)
410 {
411 	return md_get_mem(mdp, (caddr_t)x, 4, MB_MINLINE);
412 }
413 
414 int
415 md_get_uint32be(struct mdchain *mdp, u_int32_t *x)
416 {
417 	u_int32_t v;
418 	int error;
419 
420 	error = md_get_uint32(mdp, &v);
421 	*x = betohl(v);
422 	return error;
423 }
424 
425 int
426 md_get_uint32le(struct mdchain *mdp, u_int32_t *x)
427 {
428 	u_int32_t v;
429 	int error;
430 
431 	error = md_get_uint32(mdp, &v);
432 	*x = letohl(v);
433 	return error;
434 }
435 
436 int
437 md_get_int64(struct mdchain *mdp, int64_t *x)
438 {
439 	return md_get_mem(mdp, (caddr_t)x, 8, MB_MINLINE);
440 }
441 
442 int
443 md_get_int64be(struct mdchain *mdp, int64_t *x)
444 {
445 	int64_t v;
446 	int error;
447 
448 	error = md_get_int64(mdp, &v);
449 	*x = betohq(v);
450 	return error;
451 }
452 
453 int
454 md_get_int64le(struct mdchain *mdp, int64_t *x)
455 {
456 	int64_t v;
457 	int error;
458 
459 	error = md_get_int64(mdp, &v);
460 	*x = letohq(v);
461 	return error;
462 }
463 
464 int
465 md_get_mem(struct mdchain *mdp, caddr_t target, int size, int type)
466 {
467 	struct mbuf *m = mdp->md_cur;
468 	int error;
469 	u_int count;
470 	u_char *s;
471 
472 	while (size > 0) {
473 		if (m == NULL) {
474 			MBERROR("incomplete copy\n");
475 			return EBADRPC;
476 		}
477 		s = mdp->md_pos;
478 		count = mtod(m, u_char*) + m->m_len - s;
479 		if (count == 0) {
480 			mdp->md_cur = m = m->m_next;
481 			if (m)
482 				s = mdp->md_pos = mtod(m, caddr_t);
483 			continue;
484 		}
485 		if (count > size)
486 			count = size;
487 		size -= count;
488 		mdp->md_pos += count;
489 		if (target == NULL)
490 			continue;
491 		switch (type) {
492 		    case MB_MUSER:
493 			error = copyout(s, target, count);
494 			if (error)
495 				return error;
496 			break;
497 		    case MB_MSYSTEM:
498 			bcopy(s, target, count);
499 			break;
500 		    case MB_MINLINE:
501 			while (count--)
502 				*target++ = *s++;
503 			continue;
504 		}
505 		target += count;
506 	}
507 	return 0;
508 }
509 
510 int
511 md_get_mbuf(struct mdchain *mdp, int size, struct mbuf **ret)
512 {
513 	struct mbuf *m = mdp->md_cur, *rm;
514 
515 	rm = m_copym(m, mdp->md_pos - mtod(m, u_char*), size, M_WAITOK);
516 	if (rm == NULL)
517 		return EBADRPC;
518 	md_get_mem(mdp, NULL, size, MB_MZERO);
519 	*ret = rm;
520 	return 0;
521 }
522 
523 int
524 md_get_uio(struct mdchain *mdp, struct uio *uiop, int size)
525 {
526 	char *uiocp;
527 	long left;
528 	int mtype, error;
529 
530 	mtype = (uiop->uio_segflg == UIO_SYSSPACE) ? MB_MSYSTEM : MB_MUSER;
531 	while (size > 0 && uiop->uio_resid) {
532 		if (uiop->uio_iovcnt <= 0 || uiop->uio_iov == NULL)
533 			return EFBIG;
534 		left = uiop->uio_iov->iov_len;
535 		if (left == 0) {
536 			uiop->uio_iov++;
537 			uiop->uio_iovcnt--;
538 			continue;
539 		}
540 		uiocp = uiop->uio_iov->iov_base;
541 		if (left > size)
542 			left = size;
543 		error = md_get_mem(mdp, uiocp, left, mtype);
544 		if (error)
545 			return error;
546 		uiop->uio_offset += left;
547 		uiop->uio_resid -= left;
548 		uiop->uio_iov->iov_base += left;
549 		uiop->uio_iov->iov_len -= left;
550 		size -= left;
551 	}
552 	return 0;
553 }
554