]> git.saurik.com Git - apple/xnu.git/blob - bsd/kern/uipc_mbuf2.c
a8c8652b2309f61ce953efab95c288d15d8de3ab
[apple/xnu.git] / bsd / kern / uipc_mbuf2.c
1 /*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22 /* $NetBSD: uipc_mbuf.c,v 1.40 1999/04/01 00:23:25 thorpej Exp $ */
23
24 /*
25 * Copyright (C) 1999 WIDE Project.
26 * All rights reserved.
27 *
28 * Redistribution and use in source and binary forms, with or without
29 * modification, are permitted provided that the following conditions
30 * are met:
31 * 1. Redistributions of source code must retain the above copyright
32 * notice, this list of conditions and the following disclaimer.
33 * 2. Redistributions in binary form must reproduce the above copyright
34 * notice, this list of conditions and the following disclaimer in the
35 * documentation and/or other materials provided with the distribution.
36 * 3. Neither the name of the project nor the names of its contributors
37 * may be used to endorse or promote products derived from this software
38 * without specific prior written permission.
39 *
40 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
41 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
42 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
43 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
44 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
45 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
46 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
47 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
48 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
49 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
50 * SUCH DAMAGE.
51 */
52
53 /*
54 * Copyright (c) 1982, 1986, 1988, 1991, 1993
55 * The Regents of the University of California. All rights reserved.
56 *
57 * Redistribution and use in source and binary forms, with or without
58 * modification, are permitted provided that the following conditions
59 * are met:
60 * 1. Redistributions of source code must retain the above copyright
61 * notice, this list of conditions and the following disclaimer.
62 * 2. Redistributions in binary form must reproduce the above copyright
63 * notice, this list of conditions and the following disclaimer in the
64 * documentation and/or other materials provided with the distribution.
65 * 3. All advertising materials mentioning features or use of this software
66 * must display the following acknowledgement:
67 * This product includes software developed by the University of
68 * California, Berkeley and its contributors.
69 * 4. Neither the name of the University nor the names of its contributors
70 * may be used to endorse or promote products derived from this software
71 * without specific prior written permission.
72 *
73 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
74 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
75 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
76 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
77 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
78 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
79 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
80 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
81 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
82 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
83 * SUCH DAMAGE.
84 *
85 * @(#)uipc_mbuf.c 8.4 (Berkeley) 2/14/95
86 */
87
88
89 /*#define PULLDOWN_DEBUG*/
90
91 #include <sys/param.h>
92 #include <sys/systm.h>
93 #include <sys/proc_internal.h>
94 #include <sys/malloc.h>
95 #include <sys/mbuf.h>
96 #if defined(PULLDOWN_STAT) && defined(INET6)
97 #include <netinet/in.h>
98 #include <netinet/ip6.h>
99 #include <netinet6/ip6_var.h>
100 #endif
101
102 /*
103 * ensure that [off, off + len) is contiguous on the mbuf chain "m".
104 * packet chain before "off" is kept untouched.
105 * if offp == NULL, the target will start at <retval, 0> on resulting chain.
106 * if offp != NULL, the target will start at <retval, *offp> on resulting chain.
107 *
108 * on error return (NULL return value), original "m" will be freed.
109 *
110 * XXX M_TRAILINGSPACE/M_LEADINGSPACE on shared cluster (sharedcluster)
111 */
112 struct mbuf *
113 m_pulldown(m, off, len, offp)
114 struct mbuf *m;
115 int off, len;
116 int *offp;
117 {
118 struct mbuf *n, *o;
119 int hlen, tlen, olen;
120 int sharedcluster;
121 #if defined(PULLDOWN_STAT) && defined(INET6)
122 static struct mbuf *prev = NULL;
123 int prevlen = 0, prevmlen = 0;
124 #endif
125
126 /* check invalid arguments. */
127 if (m == NULL)
128 panic("m == NULL in m_pulldown()");
129 if (len > MCLBYTES) {
130 m_freem(m);
131 return NULL; /* impossible */
132 }
133
134 #if defined(PULLDOWN_STAT) && defined(INET6)
135 ip6stat.ip6s_pulldown++;
136 #endif
137
138 #if defined(PULLDOWN_STAT) && defined(INET6)
139 /* statistics for m_pullup */
140 ip6stat.ip6s_pullup++;
141 if (off + len > MHLEN)
142 ip6stat.ip6s_pullup_fail++;
143 else {
144 int dlen, mlen;
145
146 dlen = (prev == m) ? prevlen : m->m_len;
147 mlen = (prev == m) ? prevmlen : m->m_len + M_TRAILINGSPACE(m);
148
149 if (dlen >= off + len)
150 ip6stat.ip6s_pullup--; /* call will not be made! */
151 else if ((m->m_flags & M_EXT) != 0) {
152 ip6stat.ip6s_pullup_alloc++;
153 ip6stat.ip6s_pullup_copy++;
154 } else {
155 if (mlen >= off + len)
156 ip6stat.ip6s_pullup_copy++;
157 else {
158 ip6stat.ip6s_pullup_alloc++;
159 ip6stat.ip6s_pullup_copy++;
160 }
161 }
162
163 prevlen = off + len;
164 prevmlen = MHLEN;
165 }
166
167 /* statistics for m_pullup2 */
168 ip6stat.ip6s_pullup2++;
169 if (off + len > MCLBYTES)
170 ip6stat.ip6s_pullup2_fail++;
171 else {
172 int dlen, mlen;
173
174 dlen = (prev == m) ? prevlen : m->m_len;
175 mlen = (prev == m) ? prevmlen : m->m_len + M_TRAILINGSPACE(m);
176 prevlen = off + len;
177 prevmlen = mlen;
178
179 if (dlen >= off + len)
180 ip6stat.ip6s_pullup2--; /* call will not be made! */
181 else if ((m->m_flags & M_EXT) != 0) {
182 ip6stat.ip6s_pullup2_alloc++;
183 ip6stat.ip6s_pullup2_copy++;
184 prevmlen = (off + len > MHLEN) ? MCLBYTES : MHLEN;
185 } else {
186 if (mlen >= off + len)
187 ip6stat.ip6s_pullup2_copy++;
188 else {
189 ip6stat.ip6s_pullup2_alloc++;
190 ip6stat.ip6s_pullup2_copy++;
191 prevmlen = (off + len > MHLEN) ? MCLBYTES
192 : MHLEN;
193 }
194 }
195 }
196
197 prev = m;
198 #endif
199
200 #ifdef PULLDOWN_DEBUG
201 {
202 struct mbuf *t;
203 printf("before:");
204 for (t = m; t; t = t->m_next)
205 printf(" %d", t->m_len);
206 printf("\n");
207 }
208 #endif
209 n = m;
210 while (n != NULL && off > 0) {
211 if (n->m_len > off)
212 break;
213 off -= n->m_len;
214 n = n->m_next;
215 }
216 /* be sure to point non-empty mbuf */
217 while (n != NULL && n->m_len == 0)
218 n = n->m_next;
219 if (!n) {
220 m_freem(m);
221 return NULL; /* mbuf chain too short */
222 }
223
224 /*
225 * the target data is on <n, off>.
226 * if we got enough data on the mbuf "n", we're done.
227 */
228 if ((off == 0 || offp) && len <= n->m_len - off)
229 goto ok;
230
231 #if defined(PULLDOWN_STAT) && defined(INET6)
232 ip6stat.ip6s_pulldown_copy++;
233 #endif
234
235 /*
236 * when len < n->m_len - off and off != 0, it is a special case.
237 * len bytes from <n, off> sits in single mbuf, but the caller does
238 * not like the starting position (off).
239 * chop the current mbuf into two pieces, set off to 0.
240 */
241 if (len < n->m_len - off) {
242 o = m_copym(n, off, n->m_len - off, M_DONTWAIT);
243 if (o == NULL) {
244 m_freem(m);
245 return NULL; /* ENOBUFS */
246 }
247 n->m_len = off;
248 o->m_next = n->m_next;
249 n->m_next = o;
250 n = n->m_next;
251 off = 0;
252 goto ok;
253 }
254
255 /*
256 * we need to take hlen from <n, off> and tlen from <n->m_next, 0>,
257 * and construct contiguous mbuf with m_len == len.
258 * note that hlen + tlen == len, and tlen > 0.
259 */
260 hlen = n->m_len - off;
261 tlen = len - hlen;
262
263 /*
264 * ensure that we have enough trailing data on mbuf chain.
265 * if not, we can do nothing about the chain.
266 */
267 olen = 0;
268 for (o = n->m_next; o != NULL; o = o->m_next)
269 olen += o->m_len;
270 if (hlen + olen < len) {
271 m_freem(m);
272 return NULL; /* mbuf chain too short */
273 }
274
275 /*
276 * easy cases first.
277 * we need to use m_copydata() to get data from <n->m_next, 0>.
278 */
279 if ((n->m_flags & M_EXT) == 0)
280 sharedcluster = 0;
281 else {
282 if (n->m_ext.ext_free)
283 sharedcluster = 1;
284 else if (m_mclhasreference(n))
285 sharedcluster = 1;
286 else
287 sharedcluster = 0;
288 }
289 if ((off == 0 || offp) && M_TRAILINGSPACE(n) >= tlen
290 && !sharedcluster) {
291 m_copydata(n->m_next, 0, tlen, mtod(n, caddr_t) + n->m_len);
292 n->m_len += tlen;
293 m_adj(n->m_next, tlen);
294 goto ok;
295 }
296 if ((off == 0 || offp) && M_LEADINGSPACE(n->m_next) >= hlen
297 && !sharedcluster) {
298 n->m_next->m_data -= hlen;
299 n->m_next->m_len += hlen;
300 bcopy(mtod(n, caddr_t) + off, mtod(n->m_next, caddr_t), hlen);
301 n->m_len -= hlen;
302 n = n->m_next;
303 off = 0;
304 goto ok;
305 }
306
307 /*
308 * now, we need to do the hard way. don't m_copy as there's no room
309 * on both end.
310 */
311 #if defined(PULLDOWN_STAT) && defined(INET6)
312 ip6stat.ip6s_pulldown_alloc++;
313 #endif
314 MGET(o, M_DONTWAIT, m->m_type);
315 if (o == NULL) {
316 m_freem(m);
317 return NULL; /* ENOBUFS */
318 }
319 if (len > MHLEN) { /* use MHLEN just for safety */
320 MCLGET(o, M_DONTWAIT);
321 if ((o->m_flags & M_EXT) == 0) {
322 m_freem(m);
323 m_free(o);
324 return NULL; /* ENOBUFS */
325 }
326 }
327 /* get hlen from <n, off> into <o, 0> */
328 o->m_len = hlen;
329 bcopy(mtod(n, caddr_t) + off, mtod(o, caddr_t), hlen);
330 n->m_len -= hlen;
331 /* get tlen from <n->m_next, 0> into <o, hlen> */
332 m_copydata(n->m_next, 0, tlen, mtod(o, caddr_t) + o->m_len);
333 o->m_len += tlen;
334 m_adj(n->m_next, tlen);
335 o->m_next = n->m_next;
336 n->m_next = o;
337 n = o;
338 off = 0;
339
340 ok:
341 #ifdef PULLDOWN_DEBUG
342 {
343 struct mbuf *t;
344 printf("after:");
345 for (t = m; t; t = t->m_next)
346 printf("%c%d", t == n ? '*' : ' ', t->m_len);
347 printf(" (off=%d)\n", off);
348 }
349 #endif
350 if (offp)
351 *offp = off;
352 return n;
353 }
354
355 /*
356 * pkthdr.aux chain manipulation.
357 * we don't allow clusters at this moment.
358 */
359 struct mbuf *
360 m_aux_add(m, af, type)
361 struct mbuf *m;
362 int af, type;
363 {
364 struct mbuf *n;
365 struct mauxtag *t;
366
367 if ((m->m_flags & M_PKTHDR) == 0)
368 return NULL;
369
370 n = m_aux_find(m, af, type);
371 if (n)
372 return n;
373
374 MGET(n, M_DONTWAIT, m->m_type);
375 if (n == NULL)
376 return NULL;
377
378 t = mtod(n, struct mauxtag *);
379 t->af = af;
380 t->type = type;
381 n->m_data += sizeof(struct mauxtag);
382 n->m_len = 0;
383 n->m_next = m->m_pkthdr.aux;
384 m->m_pkthdr.aux = n;
385 return n;
386 }
387
388 struct mbuf *
389 m_aux_find(m, af, type)
390 struct mbuf *m;
391 int af, type;
392 {
393 struct mbuf *n;
394 struct mauxtag *t;
395
396 if ((m->m_flags & M_PKTHDR) == 0)
397 return NULL;
398
399 for (n = m->m_pkthdr.aux; n; n = n->m_next) {
400 t = (struct mauxtag *)n->m_dat;
401 if (t->af == af && t->type == type)
402 return n;
403 }
404 return NULL;
405 }
406
407 void
408 m_aux_delete(m, victim)
409 struct mbuf *m;
410 struct mbuf *victim;
411 {
412 struct mbuf *n, *prev, *next;
413 struct mauxtag *t;
414
415 if ((m->m_flags & M_PKTHDR) == 0)
416 return;
417
418 prev = NULL;
419 n = m->m_pkthdr.aux;
420 while (n) {
421 t = (struct mauxtag *)n->m_dat;
422 next = n->m_next;
423 if (n == victim) {
424 if (prev)
425 prev->m_next = n->m_next;
426 else
427 m->m_pkthdr.aux = n->m_next;
428 n->m_next = NULL;
429 m_free(n);
430 } else
431 prev = n;
432 n = next;
433 }
434 }
435
436 /* Get a packet tag structure along with specified data following. */
437 struct m_tag *
438 m_tag_alloc(u_int32_t id, u_int16_t type, int len, int wait)
439 {
440 struct m_tag *t;
441
442 if (len < 0)
443 return NULL;
444 #ifndef __APPLE__
445 t = malloc(len + sizeof(struct m_tag), M_PACKET_TAGS, wait);
446 #else
447 /*MALLOC(t, struct m_tag *, len + sizeof(struct m_tag), M_TEMP, M_WAITOK);*/
448 if (len + sizeof(struct m_tag) <= MLEN) {
449 struct mbuf *m = m_get(wait, MT_TAG);
450 if (m == NULL)
451 return NULL;
452 t = (struct m_tag *) m->m_dat;
453 } else if (len + sizeof(struct m_tag) <= MCLBYTES) {
454 MCLALLOC((caddr_t)t, wait);
455 } else
456 t = NULL;
457 #endif
458 if (t == NULL)
459 return NULL;
460 t->m_tag_type = type;
461 t->m_tag_len = len;
462 t->m_tag_id = id;
463 return t;
464 }
465
466
467 /* Free a packet tag. */
468 void
469 m_tag_free(struct m_tag *t)
470 {
471 #ifndef __APPLE__
472 free(t, M_PACKET_TAGS);
473 #else
474 /* FREE(t, M_TEMP); */
475 if (t == NULL)
476 return;
477 if (t->m_tag_len <= MLEN) {
478 struct mbuf * m = m_dtom(t);
479 m_free(m);
480 } else {
481 MCLFREE((caddr_t)t);
482 }
483 #endif
484 }
485
486 /* Prepend a packet tag. */
487 void
488 m_tag_prepend(struct mbuf *m, struct m_tag *t)
489 {
490 KASSERT(m && t, ("m_tag_prepend: null argument, m %p t %p", m, t));
491 SLIST_INSERT_HEAD(&m->m_pkthdr.tags, t, m_tag_link);
492 }
493
494 /* Unlink a packet tag. */
495 void
496 m_tag_unlink(struct mbuf *m, struct m_tag *t)
497 {
498 KASSERT(m && t, ("m_tag_unlink: null argument, m %p t %p", m, t));
499 SLIST_REMOVE(&m->m_pkthdr.tags, t, m_tag, m_tag_link);
500 }
501
502 /* Unlink and free a packet tag. */
503 void
504 m_tag_delete(struct mbuf *m, struct m_tag *t)
505 {
506 KASSERT(m && t, ("m_tag_delete: null argument, m %p t %p", m, t));
507 m_tag_unlink(m, t);
508 m_tag_free(t);
509 }
510
511 /* Unlink and free a packet tag chain, starting from given tag. */
512 void
513 m_tag_delete_chain(struct mbuf *m, struct m_tag *t)
514 {
515 struct m_tag *p, *q;
516
517 KASSERT(m, ("m_tag_delete_chain: null mbuf"));
518 if (t != NULL)
519 p = t;
520 else
521 p = SLIST_FIRST(&m->m_pkthdr.tags);
522 if (p == NULL)
523 return;
524 while ((q = SLIST_NEXT(p, m_tag_link)) != NULL)
525 m_tag_delete(m, q);
526 m_tag_delete(m, p);
527 }
528
529 /* Find a tag, starting from a given position. */
530 struct m_tag *
531 m_tag_locate(struct mbuf *m, u_int32_t id, u_int16_t type, struct m_tag *t)
532 {
533 struct m_tag *p;
534
535 KASSERT(m, ("m_tag_find: null mbuf"));
536 if (t == NULL)
537 p = SLIST_FIRST(&m->m_pkthdr.tags);
538 else
539 p = SLIST_NEXT(t, m_tag_link);
540 while (p != NULL) {
541 if (p->m_tag_id == id && p->m_tag_type == type)
542 return p;
543 p = SLIST_NEXT(p, m_tag_link);
544 }
545 return NULL;
546 }
547
548 /* Copy a single tag. */
549 struct m_tag *
550 m_tag_copy(struct m_tag *t, int how)
551 {
552 struct m_tag *p;
553
554 KASSERT(t, ("m_tag_copy: null tag"));
555 p = m_tag_alloc(t->m_tag_type, t->m_tag_id, t->m_tag_len, how);
556 if (p == NULL)
557 return (NULL);
558 bcopy(t + 1, p + 1, t->m_tag_len); /* Copy the data */
559 return p;
560 }
561
562 /*
563 * Copy two tag chains. The destination mbuf (to) loses any attached
564 * tags even if the operation fails. This should not be a problem, as
565 * m_tag_copy_chain() is typically called with a newly-allocated
566 * destination mbuf.
567 */
568 int
569 m_tag_copy_chain(struct mbuf *to, struct mbuf *from, int how)
570 {
571 struct m_tag *p, *t, *tprev = NULL;
572
573 KASSERT(to && from,
574 ("m_tag_copy: null argument, to %p from %p", to, from));
575 m_tag_delete_chain(to, NULL);
576 SLIST_FOREACH(p, &from->m_pkthdr.tags, m_tag_link) {
577 t = m_tag_copy(p, how);
578 if (t == NULL) {
579 m_tag_delete_chain(to, NULL);
580 return 0;
581 }
582 if (tprev == NULL)
583 SLIST_INSERT_HEAD(&to->m_pkthdr.tags, t, m_tag_link);
584 else {
585 SLIST_INSERT_AFTER(tprev, t, m_tag_link);
586 tprev = t;
587 }
588 }
589 return 1;
590 }
591
592 /* Initialize tags on an mbuf. */
593 void
594 m_tag_init(struct mbuf *m)
595 {
596 SLIST_INIT(&m->m_pkthdr.tags);
597 }
598
599 /* Get first tag in chain. */
600 struct m_tag *
601 m_tag_first(struct mbuf *m)
602 {
603 return SLIST_FIRST(&m->m_pkthdr.tags);
604 }
605
606 /* Get next tag in chain. */
607 struct m_tag *
608 m_tag_next(__unused struct mbuf *m, struct m_tag *t)
609 {
610 return SLIST_NEXT(t, m_tag_link);
611 }