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