xref: /freebsd/sys/kern/subr_mchain.c (revision 78704ef45793e56c8e064611c05c9bb8a0067e9f)
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, ...) printf("%s(%d): "format, __func__ , \
49 				    __LINE__ , ## __VA_ARGS__)
50 
51 #define MBPANIC(format, ...) printf("%s(%d): "format, __func__ , \
52 				    __LINE__ , ## __VA_ARGS__)
53 
54 /*
55  * Various helper functions
56  */
57 int
58 mb_init(struct mbchain *mbp)
59 {
60 	struct mbuf *m;
61 
62 	m = m_gethdr(M_TRYWAIT, MT_DATA);
63 	if (m == NULL)
64 		return ENOBUFS;
65 	m->m_len = 0;
66 	mb_initm(mbp, m);
67 	return 0;
68 }
69 
70 void
71 mb_initm(struct mbchain *mbp, struct mbuf *m)
72 {
73 	bzero(mbp, sizeof(*mbp));
74 	mbp->mb_top = mbp->mb_cur = m;
75 	mbp->mb_mleft = M_TRAILINGSPACE(m);
76 }
77 
78 void
79 mb_done(struct mbchain *mbp)
80 {
81 	if (mbp->mb_top) {
82 		m_freem(mbp->mb_top);
83 		mbp->mb_top = NULL;
84 	}
85 }
86 
87 struct mbuf *
88 mb_detach(struct mbchain *mbp)
89 {
90 	struct mbuf *m;
91 
92 	m = mbp->mb_top;
93 	mbp->mb_top = NULL;
94 	return m;
95 }
96 
97 int
98 mb_fixhdr(struct mbchain *mbp)
99 {
100 	return mbp->mb_top->m_pkthdr.len = m_fixhdr(mbp->mb_top);
101 }
102 
103 /*
104  * Check if object of size 'size' fit to the current position and
105  * allocate new mbuf if not. Advance pointers and increase length of mbuf(s).
106  * Return pointer to the object placeholder or NULL if any error occured.
107  * Note: size should be <= MLEN
108  */
109 caddr_t
110 mb_reserve(struct mbchain *mbp, int size)
111 {
112 	struct mbuf *m, *mn;
113 	caddr_t bpos;
114 
115 	if (size > MLEN)
116 		panic("mb_reserve: size = %d\n", size);
117 	m = mbp->mb_cur;
118 	if (mbp->mb_mleft < size) {
119 		mn = m_get(M_TRYWAIT, MT_DATA);
120 		if (mn == NULL)
121 			return NULL;
122 		mbp->mb_cur = m->m_next = mn;
123 		m = mn;
124 		m->m_len = 0;
125 		mbp->mb_mleft = M_TRAILINGSPACE(m);
126 	}
127 	mbp->mb_mleft -= size;
128 	mbp->mb_count += size;
129 	bpos = mtod(m, caddr_t) + m->m_len;
130 	m->m_len += size;
131 	return bpos;
132 }
133 
134 int
135 mb_put_uint8(struct mbchain *mbp, u_int8_t x)
136 {
137 	return mb_put_mem(mbp, (caddr_t)&x, sizeof(x), MB_MSYSTEM);
138 }
139 
140 int
141 mb_put_uint16be(struct mbchain *mbp, u_int16_t x)
142 {
143 	x = htobes(x);
144 	return mb_put_mem(mbp, (caddr_t)&x, sizeof(x), MB_MSYSTEM);
145 }
146 
147 int
148 mb_put_uint16le(struct mbchain *mbp, u_int16_t x)
149 {
150 	x = htoles(x);
151 	return mb_put_mem(mbp, (caddr_t)&x, sizeof(x), MB_MSYSTEM);
152 }
153 
154 int
155 mb_put_uint32be(struct mbchain *mbp, u_int32_t x)
156 {
157 	x = htobel(x);
158 	return mb_put_mem(mbp, (caddr_t)&x, sizeof(x), MB_MSYSTEM);
159 }
160 
161 int
162 mb_put_uint32le(struct mbchain *mbp, u_int32_t x)
163 {
164 	x = htolel(x);
165 	return mb_put_mem(mbp, (caddr_t)&x, sizeof(x), MB_MSYSTEM);
166 }
167 
168 int
169 mb_put_int64be(struct mbchain *mbp, int64_t x)
170 {
171 	x = htobeq(x);
172 	return mb_put_mem(mbp, (caddr_t)&x, sizeof(x), MB_MSYSTEM);
173 }
174 
175 int
176 mb_put_int64le(struct mbchain *mbp, int64_t x)
177 {
178 	x = htoleq(x);
179 	return mb_put_mem(mbp, (caddr_t)&x, sizeof(x), MB_MSYSTEM);
180 }
181 
182 int
183 mb_put_mem(struct mbchain *mbp, c_caddr_t source, int size, int type)
184 {
185 	struct mbuf *m;
186 	caddr_t dst;
187 	c_caddr_t src;
188 	int cplen, error, mleft, count;
189 
190 	m = mbp->mb_cur;
191 	mleft = mbp->mb_mleft;
192 
193 	while (size > 0) {
194 		if (mleft == 0) {
195 			if (m->m_next == NULL) {
196 				m = m_getm(m, size, M_TRYWAIT, MT_DATA);
197 				if (m == NULL)
198 					return ENOBUFS;
199 			}
200 			m = m->m_next;
201 			mleft = M_TRAILINGSPACE(m);
202 			continue;
203 		}
204 		cplen = mleft > size ? size : mleft;
205 		dst = mtod(m, caddr_t) + m->m_len;
206 		switch (type) {
207 		    case MB_MCUSTOM:
208 			error = mbp->mb_copy(mbp, source, dst, cplen);
209 			if (error)
210 				return error;
211 			break;
212 		    case MB_MINLINE:
213 			for (src = source, count = cplen; count; count--)
214 				*dst++ = *src++;
215 			break;
216 		    case MB_MSYSTEM:
217 			bcopy(source, dst, cplen);
218 			break;
219 		    case MB_MUSER:
220 			error = copyin(source, dst, cplen);
221 			if (error)
222 				return error;
223 			break;
224 		    case MB_MZERO:
225 			bzero(dst, cplen);
226 			break;
227 		}
228 		size -= cplen;
229 		source += cplen;
230 		m->m_len += cplen;
231 		mleft -= cplen;
232 		mbp->mb_count += cplen;
233 	}
234 	mbp->mb_cur = m;
235 	mbp->mb_mleft = mleft;
236 	return 0;
237 }
238 
239 int
240 mb_put_mbuf(struct mbchain *mbp, struct mbuf *m)
241 {
242 	mbp->mb_cur->m_next = m;
243 	while (m) {
244 		mbp->mb_count += m->m_len;
245 		if (m->m_next == NULL)
246 			break;
247 		m = m->m_next;
248 	}
249 	mbp->mb_mleft = M_TRAILINGSPACE(m);
250 	mbp->mb_cur = m;
251 	return 0;
252 }
253 
254 /*
255  * copies a uio scatter/gather list to an mbuf chain.
256  */
257 int
258 mb_put_uio(struct mbchain *mbp, struct uio *uiop, int size)
259 {
260 	long left;
261 	int mtype, error;
262 
263 	mtype = (uiop->uio_segflg == UIO_SYSSPACE) ? MB_MSYSTEM : MB_MUSER;
264 
265 	while (size > 0 && uiop->uio_resid) {
266 		if (uiop->uio_iovcnt <= 0 || uiop->uio_iov == NULL)
267 			return EFBIG;
268 		left = uiop->uio_iov->iov_len;
269 		if (left == 0) {
270 			uiop->uio_iov++;
271 			uiop->uio_iovcnt--;
272 			continue;
273 		}
274 		if (left > size)
275 			left = size;
276 		error = mb_put_mem(mbp, uiop->uio_iov->iov_base, left, mtype);
277 		if (error)
278 			return error;
279 		uiop->uio_offset += left;
280 		uiop->uio_resid -= left;
281 		uiop->uio_iov->iov_base =
282 		    (char *)uiop->uio_iov->iov_base + left;
283 		uiop->uio_iov->iov_len -= left;
284 		size -= left;
285 	}
286 	return 0;
287 }
288 
289 /*
290  * Routines for fetching data from an mbuf chain
291  */
292 int
293 md_init(struct mdchain *mdp)
294 {
295 	struct mbuf *m;
296 
297 	m = m_gethdr(M_TRYWAIT, MT_DATA);
298 	if (m == NULL)
299 		return ENOBUFS;
300 	m->m_len = 0;
301 	md_initm(mdp, m);
302 	return 0;
303 }
304 
305 void
306 md_initm(struct mdchain *mdp, struct mbuf *m)
307 {
308 	bzero(mdp, sizeof(*mdp));
309 	mdp->md_top = mdp->md_cur = m;
310 	mdp->md_pos = mtod(m, u_char*);
311 }
312 
313 void
314 md_done(struct mdchain *mdp)
315 {
316 	if (mdp->md_top) {
317 		m_freem(mdp->md_top);
318 		mdp->md_top = NULL;
319 	}
320 }
321 
322 /*
323  * Append a separate mbuf chain. It is caller responsibility to prevent
324  * multiple calls to fetch/record routines.
325  */
326 void
327 md_append_record(struct mdchain *mdp, struct mbuf *top)
328 {
329 	struct mbuf *m;
330 
331 	if (mdp->md_top == NULL) {
332 		md_initm(mdp, top);
333 		return;
334 	}
335 	m = mdp->md_top;
336 	while (m->m_nextpkt)
337 		m = m->m_nextpkt;
338 	m->m_nextpkt = top;
339 	top->m_nextpkt = NULL;
340 	return;
341 }
342 
343 /*
344  * Put next record in place of existing
345  */
346 int
347 md_next_record(struct mdchain *mdp)
348 {
349 	struct mbuf *m;
350 
351 	if (mdp->md_top == NULL)
352 		return ENOENT;
353 	m = mdp->md_top->m_nextpkt;
354 	md_done(mdp);
355 	if (m == NULL)
356 		return ENOENT;
357 	md_initm(mdp, m);
358 	return 0;
359 }
360 
361 int
362 md_get_uint8(struct mdchain *mdp, u_int8_t *x)
363 {
364 	return md_get_mem(mdp, x, 1, MB_MINLINE);
365 }
366 
367 int
368 md_get_uint16(struct mdchain *mdp, u_int16_t *x)
369 {
370 	return md_get_mem(mdp, (caddr_t)x, 2, MB_MINLINE);
371 }
372 
373 int
374 md_get_uint16le(struct mdchain *mdp, u_int16_t *x)
375 {
376 	u_int16_t v;
377 	int error = md_get_uint16(mdp, &v);
378 
379 	*x = letohs(v);
380 	return error;
381 }
382 
383 int
384 md_get_uint16be(struct mdchain *mdp, u_int16_t *x) {
385 	u_int16_t v;
386 	int error = md_get_uint16(mdp, &v);
387 
388 	*x = betohs(v);
389 	return error;
390 }
391 
392 int
393 md_get_uint32(struct mdchain *mdp, u_int32_t *x)
394 {
395 	return md_get_mem(mdp, (caddr_t)x, 4, MB_MINLINE);
396 }
397 
398 int
399 md_get_uint32be(struct mdchain *mdp, u_int32_t *x)
400 {
401 	u_int32_t v;
402 	int error;
403 
404 	error = md_get_uint32(mdp, &v);
405 	*x = betohl(v);
406 	return error;
407 }
408 
409 int
410 md_get_uint32le(struct mdchain *mdp, u_int32_t *x)
411 {
412 	u_int32_t v;
413 	int error;
414 
415 	error = md_get_uint32(mdp, &v);
416 	*x = letohl(v);
417 	return error;
418 }
419 
420 int
421 md_get_int64(struct mdchain *mdp, int64_t *x)
422 {
423 	return md_get_mem(mdp, (caddr_t)x, 8, MB_MINLINE);
424 }
425 
426 int
427 md_get_int64be(struct mdchain *mdp, int64_t *x)
428 {
429 	int64_t v;
430 	int error;
431 
432 	error = md_get_int64(mdp, &v);
433 	*x = betohq(v);
434 	return error;
435 }
436 
437 int
438 md_get_int64le(struct mdchain *mdp, int64_t *x)
439 {
440 	int64_t v;
441 	int error;
442 
443 	error = md_get_int64(mdp, &v);
444 	*x = letohq(v);
445 	return error;
446 }
447 
448 int
449 md_get_mem(struct mdchain *mdp, caddr_t target, int size, int type)
450 {
451 	struct mbuf *m = mdp->md_cur;
452 	int error;
453 	u_int count;
454 	u_char *s;
455 
456 	while (size > 0) {
457 		if (m == NULL) {
458 			MBERROR("incomplete copy\n");
459 			return EBADRPC;
460 		}
461 		s = mdp->md_pos;
462 		count = mtod(m, u_char*) + m->m_len - s;
463 		if (count == 0) {
464 			mdp->md_cur = m = m->m_next;
465 			if (m)
466 				s = mdp->md_pos = mtod(m, caddr_t);
467 			continue;
468 		}
469 		if (count > size)
470 			count = size;
471 		size -= count;
472 		mdp->md_pos += count;
473 		if (target == NULL)
474 			continue;
475 		switch (type) {
476 		    case MB_MUSER:
477 			error = copyout(s, target, count);
478 			if (error)
479 				return error;
480 			break;
481 		    case MB_MSYSTEM:
482 			bcopy(s, target, count);
483 			break;
484 		    case MB_MINLINE:
485 			while (count--)
486 				*target++ = *s++;
487 			continue;
488 		}
489 		target += count;
490 	}
491 	return 0;
492 }
493 
494 int
495 md_get_mbuf(struct mdchain *mdp, int size, struct mbuf **ret)
496 {
497 	struct mbuf *m = mdp->md_cur, *rm;
498 
499 	rm = m_copym(m, mdp->md_pos - mtod(m, u_char*), size, M_TRYWAIT);
500 	if (rm == NULL)
501 		return EBADRPC;
502 	md_get_mem(mdp, NULL, size, MB_MZERO);
503 	*ret = rm;
504 	return 0;
505 }
506 
507 int
508 md_get_uio(struct mdchain *mdp, struct uio *uiop, int size)
509 {
510 	char *uiocp;
511 	long left;
512 	int mtype, error;
513 
514 	mtype = (uiop->uio_segflg == UIO_SYSSPACE) ? MB_MSYSTEM : MB_MUSER;
515 	while (size > 0 && uiop->uio_resid) {
516 		if (uiop->uio_iovcnt <= 0 || uiop->uio_iov == NULL)
517 			return EFBIG;
518 		left = uiop->uio_iov->iov_len;
519 		if (left == 0) {
520 			uiop->uio_iov++;
521 			uiop->uio_iovcnt--;
522 			continue;
523 		}
524 		uiocp = uiop->uio_iov->iov_base;
525 		if (left > size)
526 			left = size;
527 		error = md_get_mem(mdp, uiocp, left, mtype);
528 		if (error)
529 			return error;
530 		uiop->uio_offset += left;
531 		uiop->uio_resid -= left;
532 		uiop->uio_iov->iov_base =
533 		    (char *)uiop->uio_iov->iov_base + left;
534 		uiop->uio_iov->iov_len -= left;
535 		size -= left;
536 	}
537 	return 0;
538 }
539