xref: /freebsd/sys/kern/subr_mchain.c (revision 7660b554bc59a07be0431c17e0e33815818baa69)
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 
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
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 = htobe16(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 = htole16(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 = htobe32(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 = htole32(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 = htobe64(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 = htole64(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 			} else
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 	if (x != NULL)
380 		*x = le16toh(v);
381 	return error;
382 }
383 
384 int
385 md_get_uint16be(struct mdchain *mdp, u_int16_t *x) {
386 	u_int16_t v;
387 	int error = md_get_uint16(mdp, &v);
388 
389 	if (x != NULL)
390 		*x = be16toh(v);
391 	return error;
392 }
393 
394 int
395 md_get_uint32(struct mdchain *mdp, u_int32_t *x)
396 {
397 	return md_get_mem(mdp, (caddr_t)x, 4, MB_MINLINE);
398 }
399 
400 int
401 md_get_uint32be(struct mdchain *mdp, u_int32_t *x)
402 {
403 	u_int32_t v;
404 	int error;
405 
406 	error = md_get_uint32(mdp, &v);
407 	if (x != NULL)
408 		*x = be32toh(v);
409 	return error;
410 }
411 
412 int
413 md_get_uint32le(struct mdchain *mdp, u_int32_t *x)
414 {
415 	u_int32_t v;
416 	int error;
417 
418 	error = md_get_uint32(mdp, &v);
419 	if (x != NULL)
420 		*x = le32toh(v);
421 	return error;
422 }
423 
424 int
425 md_get_int64(struct mdchain *mdp, int64_t *x)
426 {
427 	return md_get_mem(mdp, (caddr_t)x, 8, MB_MINLINE);
428 }
429 
430 int
431 md_get_int64be(struct mdchain *mdp, int64_t *x)
432 {
433 	int64_t v;
434 	int error;
435 
436 	error = md_get_int64(mdp, &v);
437 	if (x != NULL)
438 		*x = be64toh(v);
439 	return error;
440 }
441 
442 int
443 md_get_int64le(struct mdchain *mdp, int64_t *x)
444 {
445 	int64_t v;
446 	int error;
447 
448 	error = md_get_int64(mdp, &v);
449 	if (x != NULL)
450 		*x = le64toh(v);
451 	return error;
452 }
453 
454 int
455 md_get_mem(struct mdchain *mdp, caddr_t target, int size, int type)
456 {
457 	struct mbuf *m = mdp->md_cur;
458 	int error;
459 	u_int count;
460 	u_char *s;
461 
462 	while (size > 0) {
463 		if (m == NULL) {
464 			MBERROR("incomplete copy\n");
465 			return EBADRPC;
466 		}
467 		s = mdp->md_pos;
468 		count = mtod(m, u_char*) + m->m_len - s;
469 		if (count == 0) {
470 			mdp->md_cur = m = m->m_next;
471 			if (m)
472 				s = mdp->md_pos = mtod(m, caddr_t);
473 			continue;
474 		}
475 		if (count > size)
476 			count = size;
477 		size -= count;
478 		mdp->md_pos += count;
479 		if (target == NULL)
480 			continue;
481 		switch (type) {
482 		    case MB_MUSER:
483 			error = copyout(s, target, count);
484 			if (error)
485 				return error;
486 			break;
487 		    case MB_MSYSTEM:
488 			bcopy(s, target, count);
489 			break;
490 		    case MB_MINLINE:
491 			while (count--)
492 				*target++ = *s++;
493 			continue;
494 		}
495 		target += count;
496 	}
497 	return 0;
498 }
499 
500 int
501 md_get_mbuf(struct mdchain *mdp, int size, struct mbuf **ret)
502 {
503 	struct mbuf *m = mdp->md_cur, *rm;
504 
505 	rm = m_copym(m, mdp->md_pos - mtod(m, u_char*), size, M_TRYWAIT);
506 	if (rm == NULL)
507 		return EBADRPC;
508 	md_get_mem(mdp, NULL, size, MB_MZERO);
509 	*ret = rm;
510 	return 0;
511 }
512 
513 int
514 md_get_uio(struct mdchain *mdp, struct uio *uiop, int size)
515 {
516 	char *uiocp;
517 	long left;
518 	int mtype, error;
519 
520 	mtype = (uiop->uio_segflg == UIO_SYSSPACE) ? MB_MSYSTEM : MB_MUSER;
521 	while (size > 0 && uiop->uio_resid) {
522 		if (uiop->uio_iovcnt <= 0 || uiop->uio_iov == NULL)
523 			return EFBIG;
524 		left = uiop->uio_iov->iov_len;
525 		if (left == 0) {
526 			uiop->uio_iov++;
527 			uiop->uio_iovcnt--;
528 			continue;
529 		}
530 		uiocp = uiop->uio_iov->iov_base;
531 		if (left > size)
532 			left = size;
533 		error = md_get_mem(mdp, uiocp, left, mtype);
534 		if (error)
535 			return error;
536 		uiop->uio_offset += left;
537 		uiop->uio_resid -= left;
538 		uiop->uio_iov->iov_base =
539 		    (char *)uiop->uio_iov->iov_base + left;
540 		uiop->uio_iov->iov_len -= left;
541 		size -= left;
542 	}
543 	return 0;
544 }
545