xref: /freebsd/sys/kern/subr_mchain.c (revision ee2ea5ceafed78a5bd9810beb9e3ca927180c226)
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$
33  */
34 
35 
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/endian.h>
39 #include <sys/errno.h>
40 #include <sys/mbuf.h>
41 #include <sys/module.h>
42 #include <sys/uio.h>
43 
44 #include <sys/mchain.h>
45 
46 MODULE_VERSION(libmchain, 1);
47 
48 #define MBERROR(format, args...) printf("%s(%d): "format, __func__ , \
49 				    __LINE__ ,## args)
50 
51 #define MBPANIC(format, args...) printf("%s(%d): "format, __func__ , \
52 				    __LINE__ ,## args)
53 
54 /*
55  * Various helper functions
56  */
57 int
58 m_fixhdr(struct mbuf *m0)
59 {
60 	struct mbuf *m = m0;
61 	int len = 0;
62 
63 	while (m) {
64 		len += m->m_len;
65 		m = m->m_next;
66 	}
67 	m0->m_pkthdr.len = len;
68 	return len;
69 }
70 
71 int
72 mb_init(struct mbchain *mbp)
73 {
74 	struct mbuf *m;
75 
76 	m = m_gethdr(M_TRYWAIT, MT_DATA);
77 	if (m == NULL)
78 		return ENOBUFS;
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\n", size);
131 	m = mbp->mb_cur;
132 	if (mbp->mb_mleft < size) {
133 		mn = m_get(M_TRYWAIT, 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 = htobes(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 = htoles(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 = htobel(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 = htolel(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 = htobeq(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 = htoleq(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 cplen, error, mleft, count;
203 
204 	m = mbp->mb_cur;
205 	mleft = mbp->mb_mleft;
206 
207 	while (size > 0) {
208 		if (mleft == 0) {
209 			if (m->m_next == NULL) {
210 				m = m_getm(m, size, M_TRYWAIT, MT_DATA);
211 				if (m == NULL)
212 					return ENOBUFS;
213 			}
214 			m = m->m_next;
215 			mleft = M_TRAILINGSPACE(m);
216 			continue;
217 		}
218 		cplen = mleft > size ? size : mleft;
219 		dst = mtod(m, caddr_t) + m->m_len;
220 		switch (type) {
221 		    case MB_MCUSTOM:
222 			error = mbp->mb_copy(mbp, source, dst, cplen);
223 			if (error)
224 				return error;
225 			break;
226 		    case MB_MINLINE:
227 			for (src = source, count = cplen; count; count--)
228 				*dst++ = *src++;
229 			break;
230 		    case MB_MSYSTEM:
231 			bcopy(source, dst, cplen);
232 			break;
233 		    case MB_MUSER:
234 			error = copyin(source, dst, cplen);
235 			if (error)
236 				return error;
237 			break;
238 		    case MB_MZERO:
239 			bzero(dst, cplen);
240 			break;
241 		}
242 		size -= cplen;
243 		source += cplen;
244 		m->m_len += cplen;
245 		mleft -= cplen;
246 		mbp->mb_count += cplen;
247 	}
248 	mbp->mb_cur = m;
249 	mbp->mb_mleft = mleft;
250 	return 0;
251 }
252 
253 int
254 mb_put_mbuf(struct mbchain *mbp, struct mbuf *m)
255 {
256 	mbp->mb_cur->m_next = m;
257 	while (m) {
258 		mbp->mb_count += m->m_len;
259 		if (m->m_next == NULL)
260 			break;
261 		m = m->m_next;
262 	}
263 	mbp->mb_mleft = M_TRAILINGSPACE(m);
264 	mbp->mb_cur = m;
265 	return 0;
266 }
267 
268 /*
269  * copies a uio scatter/gather list to an mbuf chain.
270  */
271 int
272 mb_put_uio(struct mbchain *mbp, struct uio *uiop, int size)
273 {
274 	long left;
275 	int mtype, error;
276 
277 	mtype = (uiop->uio_segflg == UIO_SYSSPACE) ? MB_MSYSTEM : MB_MUSER;
278 
279 	while (size > 0 && uiop->uio_resid) {
280 		if (uiop->uio_iovcnt <= 0 || uiop->uio_iov == NULL)
281 			return EFBIG;
282 		left = uiop->uio_iov->iov_len;
283 		if (left == 0) {
284 			uiop->uio_iov++;
285 			uiop->uio_iovcnt--;
286 			continue;
287 		}
288 		if (left > size)
289 			left = size;
290 		error = mb_put_mem(mbp, uiop->uio_iov->iov_base, left, mtype);
291 		if (error)
292 			return error;
293 		uiop->uio_offset += left;
294 		uiop->uio_resid -= left;
295 		uiop->uio_iov->iov_base += left;
296 		uiop->uio_iov->iov_len -= left;
297 		size -= left;
298 	}
299 	return 0;
300 }
301 
302 /*
303  * Routines for fetching data from an mbuf chain
304  */
305 int
306 md_init(struct mdchain *mdp)
307 {
308 	struct mbuf *m;
309 
310 	m = m_gethdr(M_TRYWAIT, MT_DATA);
311 	if (m == NULL)
312 		return ENOBUFS;
313 	m->m_len = 0;
314 	md_initm(mdp, m);
315 	return 0;
316 }
317 
318 void
319 md_initm(struct mdchain *mdp, struct mbuf *m)
320 {
321 	bzero(mdp, sizeof(*mdp));
322 	mdp->md_top = mdp->md_cur = m;
323 	mdp->md_pos = mtod(m, u_char*);
324 }
325 
326 void
327 md_done(struct mdchain *mdp)
328 {
329 	if (mdp->md_top) {
330 		m_freem(mdp->md_top);
331 		mdp->md_top = NULL;
332 	}
333 }
334 
335 /*
336  * Append a separate mbuf chain. It is caller responsibility to prevent
337  * multiple calls to fetch/record routines.
338  */
339 void
340 md_append_record(struct mdchain *mdp, struct mbuf *top)
341 {
342 	struct mbuf *m;
343 
344 	if (mdp->md_top == NULL) {
345 		md_initm(mdp, top);
346 		return;
347 	}
348 	m = mdp->md_top;
349 	while (m->m_nextpkt)
350 		m = m->m_nextpkt;
351 	m->m_nextpkt = top;
352 	top->m_nextpkt = NULL;
353 	return;
354 }
355 
356 /*
357  * Put next record in place of existing
358  */
359 int
360 md_next_record(struct mdchain *mdp)
361 {
362 	struct mbuf *m;
363 
364 	if (mdp->md_top == NULL)
365 		return ENOENT;
366 	m = mdp->md_top->m_nextpkt;
367 	md_done(mdp);
368 	if (m == NULL)
369 		return ENOENT;
370 	md_initm(mdp, m);
371 	return 0;
372 }
373 
374 int
375 md_get_uint8(struct mdchain *mdp, u_int8_t *x)
376 {
377 	return md_get_mem(mdp, x, 1, MB_MINLINE);
378 }
379 
380 int
381 md_get_uint16(struct mdchain *mdp, u_int16_t *x)
382 {
383 	return md_get_mem(mdp, (caddr_t)x, 2, MB_MINLINE);
384 }
385 
386 int
387 md_get_uint16le(struct mdchain *mdp, u_int16_t *x)
388 {
389 	u_int16_t v;
390 	int error = md_get_uint16(mdp, &v);
391 
392 	*x = letohs(v);
393 	return error;
394 }
395 
396 int
397 md_get_uint16be(struct mdchain *mdp, u_int16_t *x) {
398 	u_int16_t v;
399 	int error = md_get_uint16(mdp, &v);
400 
401 	*x = betohs(v);
402 	return error;
403 }
404 
405 int
406 md_get_uint32(struct mdchain *mdp, u_int32_t *x)
407 {
408 	return md_get_mem(mdp, (caddr_t)x, 4, MB_MINLINE);
409 }
410 
411 int
412 md_get_uint32be(struct mdchain *mdp, u_int32_t *x)
413 {
414 	u_int32_t v;
415 	int error;
416 
417 	error = md_get_uint32(mdp, &v);
418 	*x = betohl(v);
419 	return error;
420 }
421 
422 int
423 md_get_uint32le(struct mdchain *mdp, u_int32_t *x)
424 {
425 	u_int32_t v;
426 	int error;
427 
428 	error = md_get_uint32(mdp, &v);
429 	*x = letohl(v);
430 	return error;
431 }
432 
433 int
434 md_get_int64(struct mdchain *mdp, int64_t *x)
435 {
436 	return md_get_mem(mdp, (caddr_t)x, 8, MB_MINLINE);
437 }
438 
439 int
440 md_get_int64be(struct mdchain *mdp, int64_t *x)
441 {
442 	int64_t v;
443 	int error;
444 
445 	error = md_get_int64(mdp, &v);
446 	*x = betohq(v);
447 	return error;
448 }
449 
450 int
451 md_get_int64le(struct mdchain *mdp, int64_t *x)
452 {
453 	int64_t v;
454 	int error;
455 
456 	error = md_get_int64(mdp, &v);
457 	*x = letohq(v);
458 	return error;
459 }
460 
461 int
462 md_get_mem(struct mdchain *mdp, caddr_t target, int size, int type)
463 {
464 	struct mbuf *m = mdp->md_cur;
465 	int error;
466 	u_int count;
467 	u_char *s;
468 
469 	while (size > 0) {
470 		if (m == NULL) {
471 			MBERROR("incomplete copy\n");
472 			return EBADRPC;
473 		}
474 		s = mdp->md_pos;
475 		count = mtod(m, u_char*) + m->m_len - s;
476 		if (count == 0) {
477 			mdp->md_cur = m = m->m_next;
478 			if (m)
479 				s = mdp->md_pos = mtod(m, caddr_t);
480 			continue;
481 		}
482 		if (count > size)
483 			count = size;
484 		size -= count;
485 		mdp->md_pos += count;
486 		if (target == NULL)
487 			continue;
488 		switch (type) {
489 		    case MB_MUSER:
490 			error = copyout(s, target, count);
491 			if (error)
492 				return error;
493 			break;
494 		    case MB_MSYSTEM:
495 			bcopy(s, target, count);
496 			break;
497 		    case MB_MINLINE:
498 			while (count--)
499 				*target++ = *s++;
500 			continue;
501 		}
502 		target += count;
503 	}
504 	return 0;
505 }
506 
507 int
508 md_get_mbuf(struct mdchain *mdp, int size, struct mbuf **ret)
509 {
510 	struct mbuf *m = mdp->md_cur, *rm;
511 
512 	rm = m_copym(m, mdp->md_pos - mtod(m, u_char*), size, M_TRYWAIT);
513 	if (rm == NULL)
514 		return EBADRPC;
515 	md_get_mem(mdp, NULL, size, MB_MZERO);
516 	*ret = rm;
517 	return 0;
518 }
519 
520 int
521 md_get_uio(struct mdchain *mdp, struct uio *uiop, int size)
522 {
523 	char *uiocp;
524 	long left;
525 	int mtype, error;
526 
527 	mtype = (uiop->uio_segflg == UIO_SYSSPACE) ? MB_MSYSTEM : MB_MUSER;
528 	while (size > 0 && uiop->uio_resid) {
529 		if (uiop->uio_iovcnt <= 0 || uiop->uio_iov == NULL)
530 			return EFBIG;
531 		left = uiop->uio_iov->iov_len;
532 		if (left == 0) {
533 			uiop->uio_iov++;
534 			uiop->uio_iovcnt--;
535 			continue;
536 		}
537 		uiocp = uiop->uio_iov->iov_base;
538 		if (left > size)
539 			left = size;
540 		error = md_get_mem(mdp, uiocp, left, mtype);
541 		if (error)
542 			return error;
543 		uiop->uio_offset += left;
544 		uiop->uio_resid -= left;
545 		uiop->uio_iov->iov_base += left;
546 		uiop->uio_iov->iov_len -= left;
547 		size -= left;
548 	}
549 	return 0;
550 }
551