xref: /dragonfly/sys/kern/uipc_mbuf2.c (revision 9c600e7d)
1 /*	$FreeBSD: src/sys/kern/uipc_mbuf2.c,v 1.2.2.5 2003/01/23 21:06:44 sam Exp $	*/
2 /*	$DragonFly: src/sys/kern/uipc_mbuf2.c,v 1.3 2003/07/23 02:30:20 dillon Exp $	*/
3 /*	$KAME: uipc_mbuf2.c,v 1.31 2001/11/28 11:08:53 itojun Exp $	*/
4 /*	$NetBSD: uipc_mbuf.c,v 1.40 1999/04/01 00:23:25 thorpej Exp $	*/
5 
6 /*
7  * Copyright (C) 1999 WIDE Project.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of the project nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 /*
36  * Copyright (c) 1982, 1986, 1988, 1991, 1993
37  *	The Regents of the University of California.  All rights reserved.
38  *
39  * Redistribution and use in source and binary forms, with or without
40  * modification, are permitted provided that the following conditions
41  * are met:
42  * 1. Redistributions of source code must retain the above copyright
43  *    notice, this list of conditions and the following disclaimer.
44  * 2. Redistributions in binary form must reproduce the above copyright
45  *    notice, this list of conditions and the following disclaimer in the
46  *    documentation and/or other materials provided with the distribution.
47  * 3. All advertising materials mentioning features or use of this software
48  *    must display the following acknowledgement:
49  *	This product includes software developed by the University of
50  *	California, Berkeley and its contributors.
51  * 4. Neither the name of the University nor the names of its contributors
52  *    may be used to endorse or promote products derived from this software
53  *    without specific prior written permission.
54  *
55  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
56  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
57  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
58  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
59  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
60  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
61  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
62  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
63  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
64  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
65  * SUCH DAMAGE.
66  *
67  *	@(#)uipc_mbuf.c	8.4 (Berkeley) 2/14/95
68  */
69 
70 /*#define PULLDOWN_DEBUG*/
71 
72 #include <sys/param.h>
73 #include <sys/systm.h>
74 #include <sys/kernel.h>
75 #include <sys/proc.h>
76 #include <sys/malloc.h>
77 #include <sys/mbuf.h>
78 
79 #define M_SHAREDCLUSTER(m) \
80 	(((m)->m_flags & M_EXT) != 0 && \
81 	 ((m)->m_ext.ext_free || mclrefcnt[mtocl((m)->m_ext.ext_buf)] > 1))
82 
83 MALLOC_DEFINE(M_PACKET_TAGS, "tag", "packet-attached information");
84 
85 /* can't call it m_dup(), as freebsd[34] uses m_dup() with different arg */
86 static struct mbuf *m_dup1 __P((struct mbuf *, int, int, int));
87 
88 /*
89  * ensure that [off, off + len) is contiguous on the mbuf chain "m".
90  * packet chain before "off" is kept untouched.
91  * if offp == NULL, the target will start at <retval, 0> on resulting chain.
92  * if offp != NULL, the target will start at <retval, *offp> on resulting chain.
93  *
94  * on error return (NULL return value), original "m" will be freed.
95  *
96  * XXX M_TRAILINGSPACE/M_LEADINGSPACE on shared cluster (sharedcluster)
97  */
98 struct mbuf *
99 m_pulldown(m, off, len, offp)
100 	struct mbuf *m;
101 	int off, len;
102 	int *offp;
103 {
104 	struct mbuf *n, *o;
105 	int hlen, tlen, olen;
106 	int sharedcluster;
107 
108 	/* check invalid arguments. */
109 	if (m == NULL)
110 		panic("m == NULL in m_pulldown()");
111 	if (len > MCLBYTES) {
112 		m_freem(m);
113 		return NULL;	/* impossible */
114 	}
115 
116 #ifdef PULLDOWN_DEBUG
117     {
118 	struct mbuf *t;
119 	printf("before:");
120 	for (t = m; t; t = t->m_next)
121 		printf(" %d", t->m_len);
122 	printf("\n");
123     }
124 #endif
125 	n = m;
126 	while (n != NULL && off > 0) {
127 		if (n->m_len > off)
128 			break;
129 		off -= n->m_len;
130 		n = n->m_next;
131 	}
132 	/* be sure to point non-empty mbuf */
133 	while (n != NULL && n->m_len == 0)
134 		n = n->m_next;
135 	if (!n) {
136 		m_freem(m);
137 		return NULL;	/* mbuf chain too short */
138 	}
139 
140 	sharedcluster = M_SHAREDCLUSTER(n);
141 
142 	/*
143 	 * the target data is on <n, off>.
144 	 * if we got enough data on the mbuf "n", we're done.
145 	 */
146 	if ((off == 0 || offp) && len <= n->m_len - off && !sharedcluster)
147 		goto ok;
148 
149 	/*
150 	 * when len <= n->m_len - off and off != 0, it is a special case.
151 	 * len bytes from <n, off> sits in single mbuf, but the caller does
152 	 * not like the starting position (off).
153 	 * chop the current mbuf into two pieces, set off to 0.
154 	 */
155 	if (len <= n->m_len - off) {
156 		o = m_dup1(n, off, n->m_len - off, M_DONTWAIT);
157 		if (o == NULL) {
158 			m_freem(m);
159 			return NULL;	/* ENOBUFS */
160 		}
161 		n->m_len = off;
162 		o->m_next = n->m_next;
163 		n->m_next = o;
164 		n = n->m_next;
165 		off = 0;
166 		goto ok;
167 	}
168 
169 	/*
170 	 * we need to take hlen from <n, off> and tlen from <n->m_next, 0>,
171 	 * and construct contiguous mbuf with m_len == len.
172 	 * note that hlen + tlen == len, and tlen > 0.
173 	 */
174 	hlen = n->m_len - off;
175 	tlen = len - hlen;
176 
177 	/*
178 	 * ensure that we have enough trailing data on mbuf chain.
179 	 * if not, we can do nothing about the chain.
180 	 */
181 	olen = 0;
182 	for (o = n->m_next; o != NULL; o = o->m_next)
183 		olen += o->m_len;
184 	if (hlen + olen < len) {
185 		m_freem(m);
186 		return NULL;	/* mbuf chain too short */
187 	}
188 
189 	/*
190 	 * easy cases first.
191 	 * we need to use m_copydata() to get data from <n->m_next, 0>.
192 	 */
193 	if ((off == 0 || offp) && M_TRAILINGSPACE(n) >= tlen &&
194 	    !sharedcluster) {
195 		m_copydata(n->m_next, 0, tlen, mtod(n, caddr_t) + n->m_len);
196 		n->m_len += tlen;
197 		m_adj(n->m_next, tlen);
198 		goto ok;
199 	}
200 	if ((off == 0 || offp) && M_LEADINGSPACE(n->m_next) >= hlen &&
201 	    !sharedcluster) {
202 		n->m_next->m_data -= hlen;
203 		n->m_next->m_len += hlen;
204 		bcopy(mtod(n, caddr_t) + off, mtod(n->m_next, caddr_t), hlen);
205 		n->m_len -= hlen;
206 		n = n->m_next;
207 		off = 0;
208 		goto ok;
209 	}
210 
211 	/*
212 	 * now, we need to do the hard way.  don't m_copy as there's no room
213 	 * on both end.
214 	 */
215 	MGET(o, M_DONTWAIT, m->m_type);
216 	if (o && len > MLEN) {
217 		MCLGET(o, M_DONTWAIT);
218 		if ((o->m_flags & M_EXT) == 0) {
219 			m_free(o);
220 			o = NULL;
221 		}
222 	}
223 	if (!o) {
224 		m_freem(m);
225 		return NULL;	/* ENOBUFS */
226 	}
227 	/* get hlen from <n, off> into <o, 0> */
228 	o->m_len = hlen;
229 	bcopy(mtod(n, caddr_t) + off, mtod(o, caddr_t), hlen);
230 	n->m_len -= hlen;
231 	/* get tlen from <n->m_next, 0> into <o, hlen> */
232 	m_copydata(n->m_next, 0, tlen, mtod(o, caddr_t) + o->m_len);
233 	o->m_len += tlen;
234 	m_adj(n->m_next, tlen);
235 	o->m_next = n->m_next;
236 	n->m_next = o;
237 	n = o;
238 	off = 0;
239 
240 ok:
241 #ifdef PULLDOWN_DEBUG
242     {
243 	struct mbuf *t;
244 	printf("after:");
245 	for (t = m; t; t = t->m_next)
246 		printf("%c%d", t == n ? '*' : ' ', t->m_len);
247 	printf(" (off=%d)\n", off);
248     }
249 #endif
250 	if (offp)
251 		*offp = off;
252 	return n;
253 }
254 
255 static struct mbuf *
256 m_dup1(m, off, len, wait)
257 	struct mbuf *m;
258 	int off;
259 	int len;
260 	int wait;
261 {
262 	struct mbuf *n;
263 	int l;
264 	int copyhdr;
265 
266 	if (len > MCLBYTES)
267 		return NULL;
268 	if (off == 0 && (m->m_flags & M_PKTHDR) != 0) {
269 		copyhdr = 1;
270 		MGETHDR(n, wait, m->m_type);
271 		l = MHLEN;
272 	} else {
273 		copyhdr = 0;
274 		MGET(n, wait, m->m_type);
275 		l = MLEN;
276 	}
277 	if (n && len > l) {
278 		MCLGET(n, wait);
279 		if ((n->m_flags & M_EXT) == 0) {
280 			m_free(n);
281 			n = NULL;
282 		}
283 	}
284 	if (!n)
285 		return NULL;
286 
287 	if (copyhdr && !m_dup_pkthdr(n, m, wait)) {
288 		m_free(n);
289 		return NULL;
290 	}
291 	m_copydata(m, off, len, mtod(n, caddr_t));
292 	return n;
293 }
294 
295 /* Get a packet tag structure along with specified data following. */
296 struct m_tag *
297 m_tag_alloc(u_int32_t cookie, int type, int len, int wait)
298 {
299 	struct m_tag *t;
300 
301 	if (len < 0)
302 		return NULL;
303 	t = malloc(len + sizeof(struct m_tag), M_PACKET_TAGS, wait);
304 	if (t == NULL)
305 		return NULL;
306 	t->m_tag_id = type;
307 	t->m_tag_len = len;
308 	t->m_tag_cookie = cookie;
309 	return t;
310 }
311 
312 
313 /* Free a packet tag. */
314 void
315 m_tag_free(struct m_tag *t)
316 {
317 	free(t, M_PACKET_TAGS);
318 }
319 
320 /* Prepend a packet tag. */
321 void
322 m_tag_prepend(struct mbuf *m, struct m_tag *t)
323 {
324 	KASSERT(m && t, ("m_tag_prepend: null argument, m %p t %p", m, t));
325 	SLIST_INSERT_HEAD(&m->m_pkthdr.tags, t, m_tag_link);
326 }
327 
328 /* Unlink a packet tag. */
329 void
330 m_tag_unlink(struct mbuf *m, struct m_tag *t)
331 {
332 	KASSERT(m && t, ("m_tag_unlink: null argument, m %p t %p", m, t));
333 	SLIST_REMOVE(&m->m_pkthdr.tags, t, m_tag, m_tag_link);
334 }
335 
336 /* Unlink and free a packet tag. */
337 void
338 m_tag_delete(struct mbuf *m, struct m_tag *t)
339 {
340 	KASSERT(m && t, ("m_tag_delete: null argument, m %p t %p", m, t));
341 	m_tag_unlink(m, t);
342 	m_tag_free(t);
343 }
344 
345 /* Unlink and free a packet tag chain, starting from given tag. */
346 void
347 m_tag_delete_chain(struct mbuf *m, struct m_tag *t)
348 {
349 	struct m_tag *p, *q;
350 
351 	KASSERT(m, ("m_tag_delete_chain: null mbuf"));
352 	if (t != NULL)
353 		p = t;
354 	else
355 		p = SLIST_FIRST(&m->m_pkthdr.tags);
356 	if (p == NULL)
357 		return;
358 	while ((q = SLIST_NEXT(p, m_tag_link)) != NULL)
359 		m_tag_delete(m, q);
360 	m_tag_delete(m, p);
361 }
362 
363 /* Find a tag, starting from a given position. */
364 struct m_tag *
365 m_tag_locate(struct mbuf *m, u_int32_t cookie, int type, struct m_tag *t)
366 {
367 	struct m_tag *p;
368 
369 	KASSERT(m, ("m_tag_find: null mbuf"));
370 	if (t == NULL)
371 		p = SLIST_FIRST(&m->m_pkthdr.tags);
372 	else
373 		p = SLIST_NEXT(t, m_tag_link);
374 	while (p != NULL) {
375 		if (p->m_tag_cookie == cookie && p->m_tag_id == type)
376 			return p;
377 		p = SLIST_NEXT(p, m_tag_link);
378 	}
379 	return NULL;
380 }
381 
382 /* Copy a single tag. */
383 struct m_tag *
384 m_tag_copy(struct m_tag *t, int how)
385 {
386 	struct m_tag *p;
387 
388 	KASSERT(t, ("m_tag_copy: null tag"));
389 	p = m_tag_alloc(t->m_tag_cookie, t->m_tag_id, t->m_tag_len, how);
390 	if (p == NULL)
391 		return (NULL);
392 	bcopy(t + 1, p + 1, t->m_tag_len); /* Copy the data */
393 	return p;
394 }
395 
396 /*
397  * Copy two tag chains. The destination mbuf (to) loses any attached
398  * tags even if the operation fails. This should not be a problem, as
399  * m_tag_copy_chain() is typically called with a newly-allocated
400  * destination mbuf.
401  */
402 int
403 m_tag_copy_chain(struct mbuf *to, const struct mbuf *from, int how)
404 {
405 	struct m_tag *p, *t, *tprev = NULL;
406 
407 	KASSERT(to && from,
408 		("m_tag_copy: null argument, to %p from %p", to, from));
409 	m_tag_delete_chain(to, NULL);
410 	SLIST_FOREACH(p, &from->m_pkthdr.tags, m_tag_link) {
411 		t = m_tag_copy(p, how);
412 		if (t == NULL) {
413 			m_tag_delete_chain(to, NULL);
414 			return 0;
415 		}
416 		if (tprev == NULL)
417 			SLIST_INSERT_HEAD(&to->m_pkthdr.tags, t, m_tag_link);
418 		else {
419 			SLIST_INSERT_AFTER(tprev, t, m_tag_link);
420 			tprev = t;
421 		}
422 	}
423 	return 1;
424 }
425 
426 /* Initialize tags on an mbuf. */
427 void
428 m_tag_init(struct mbuf *m)
429 {
430 	SLIST_INIT(&m->m_pkthdr.tags);
431 }
432 
433 /* Get first tag in chain. */
434 struct m_tag *
435 m_tag_first(struct mbuf *m)
436 {
437 	return SLIST_FIRST(&m->m_pkthdr.tags);
438 }
439 
440 /* Get next tag in chain. */
441 struct m_tag *
442 m_tag_next(struct mbuf *m, struct m_tag *t)
443 {
444 	return SLIST_NEXT(t, m_tag_link);
445 }
446