xref: /haiku/src/libs/compat/freebsd_network/mbuf.c (revision 5115ca085884f7b604a3d607688f0ca20fb7cf57)
1 /*
2  * Copyright 2007, Hugo Santos. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *      Hugo Santos, hugosantos@gmail.com
7  *
8  * Some of this code is based on previous work by Marcus Overhagen.
9  *
10  * `m_defrag' and friends are straight from FreeBSD 6.2.
11  */
12 
13 #include "device.h"
14 
15 #include <stdint.h>
16 #include <string.h>
17 #include <slab/Slab.h>
18 
19 #include <compat/sys/mbuf.h>
20 #include <compat/sys/kernel.h>
21 
22 
23 #define MBUF_CHECKSLEEP(how) do { } while (0)
24 #define MBTOM(how) (how)
25 
26 
27 #define CHUNK_SIZE	2048
28 
29 static object_cache *sMBufCache;
30 static object_cache *sChunkCache;
31 
32 
33 static int
34 m_to_oc_flags(int how)
35 {
36 	if (how & M_NOWAIT)
37 		return CACHE_DONT_SLEEP;
38 
39 	return 0;
40 }
41 
42 
43 static int
44 construct_mbuf(struct mbuf *mb, short type, int flags)
45 {
46 	mb->m_next = NULL;
47 	mb->m_nextpkt = NULL;
48 	mb->m_len = 0;
49 	mb->m_flags = flags;
50 	mb->m_type = type;
51 
52 	if (flags & M_PKTHDR) {
53 		mb->m_data = mb->m_pktdat;
54 		memset(&mb->m_pkthdr, 0, sizeof(mb->m_pkthdr));
55 		/* SLIST_INIT(&m->m_pkthdr.tags); */
56 	} else {
57 		mb->m_data = mb->m_dat;
58 	}
59 
60 	return 0;
61 }
62 
63 
64 static int
65 construct_ext_mbuf(struct mbuf *mb, int how)
66 {
67 	mb->m_ext.ext_buf = object_cache_alloc(sChunkCache, m_to_oc_flags(how));
68 	if (mb->m_ext.ext_buf == NULL)
69 		return B_NO_MEMORY;
70 
71 	mb->m_data = mb->m_ext.ext_buf;
72 	mb->m_flags |= M_EXT;
73 	/* mb->m_ext.ext_free = NULL; */
74 	/* mb->m_ext.ext_args = NULL; */
75 	mb->m_ext.ext_size = MCLBYTES;
76 	mb->m_ext.ext_type = EXT_CLUSTER;
77 	/* mb->m_ext.ref_cnt = NULL; */
78 
79 	return 0;
80 }
81 
82 
83 static int
84 construct_pkt_mbuf(int how, struct mbuf *mb, short type, int flags)
85 {
86 	construct_mbuf(mb, type, flags);
87 	if (construct_ext_mbuf(mb, how) < 0)
88 		return -1;
89 	mb->m_ext.ext_type = EXT_PACKET;
90 	return 0;
91 }
92 
93 
94 static void
95 destruct_pkt_mbuf(struct mbuf *mb)
96 {
97 	object_cache_free(sChunkCache, mb->m_ext.ext_buf);
98 	mb->m_ext.ext_buf = NULL;
99 }
100 
101 
102 struct mbuf *
103 m_getcl(int how, short type, int flags)
104 {
105 	struct mbuf *mb =
106 		(struct mbuf *)object_cache_alloc(sMBufCache, m_to_oc_flags(how));
107 	if (mb == NULL)
108 		return NULL;
109 
110 	if (construct_pkt_mbuf(how, mb, type, flags) < 0) {
111 		object_cache_free(sMBufCache, mb);
112 		return NULL;
113 	}
114 
115 	return mb;
116 }
117 
118 
119 static struct mbuf *
120 _m_get(int how, short type, int flags)
121 {
122 	struct mbuf *mb =
123 		(struct mbuf *)object_cache_alloc(sMBufCache, m_to_oc_flags(how));
124 	if (mb == NULL)
125 		return NULL;
126 
127 	construct_mbuf(mb, type, flags);
128 
129 	return mb;
130 }
131 
132 
133 struct mbuf *
134 m_get(int how, short type)
135 {
136 	return _m_get(how, type, 0);
137 }
138 
139 
140 struct mbuf *
141 m_gethdr(int how, short type)
142 {
143 	return _m_get(how, type, M_PKTHDR);
144 }
145 
146 
147 void
148 m_clget(struct mbuf *m, int how)
149 {
150 	m->m_ext.ext_buf = NULL;
151 	/* called checks for errors by looking for M_EXT */
152 	construct_ext_mbuf(m, how);
153 }
154 
155 
156 void
157 m_freem(struct mbuf *mb)
158 {
159 	while (mb)
160 		mb = m_free(mb);
161 }
162 
163 
164 static void
165 mb_free_ext(struct mbuf *m)
166 {
167 	/*
168 	if (m->m_ext.ref_count != NULL)
169 		panic("unsupported");
170 		*/
171 
172 	if (m->m_ext.ext_type == EXT_PACKET)
173 		destruct_pkt_mbuf(m);
174 	else if (m->m_ext.ext_type == EXT_CLUSTER) {
175 		object_cache_free(sChunkCache, m->m_ext.ext_buf);
176 		m->m_ext.ext_buf = NULL;
177 	} else
178 		panic("unknown type");
179 
180 	object_cache_free(sMBufCache, m);
181 }
182 
183 
184 struct mbuf *
185 m_free(struct mbuf *m)
186 {
187 	struct mbuf *next = m->m_next;
188 
189 	if (m->m_flags & M_EXT)
190 		mb_free_ext(m);
191 	else
192 		object_cache_free(sMBufCache, m);
193 
194 	return next;
195 }
196 
197 
198 /*
199  * Copy data from an mbuf chain starting "off" bytes from the beginning,
200  * continuing for "len" bytes, into the indicated buffer.
201  */
202 void
203 m_copydata(const struct mbuf *m, int off, int len, caddr_t cp)
204 {
205 	u_int count;
206 
207 	KASSERT(off >= 0, ("m_copydata, negative off %d", off));
208 	KASSERT(len >= 0, ("m_copydata, negative len %d", len));
209 	while (off > 0) {
210 		KASSERT(m != NULL, ("m_copydata, offset > size of mbuf chain"));
211 		if (off < m->m_len)
212 			break;
213 		off -= m->m_len;
214 		m = m->m_next;
215 	}
216 	while (len > 0) {
217 		KASSERT(m != NULL, ("m_copydata, length > size of mbuf chain"));
218 		count = min(m->m_len - off, len);
219 		bcopy(mtod(m, caddr_t) + off, cp, count);
220 		len -= count;
221 		cp += count;
222 		off = 0;
223 		m = m->m_next;
224 	}
225 }
226 
227 
228 /*
229  * Concatenate mbuf chain n to m.
230  * Both chains must be of the same type (e.g. MT_DATA).
231  * Any m_pkthdr is not updated.
232  */
233 void
234 m_cat(struct mbuf *m, struct mbuf *n)
235 {
236 	while (m->m_next)
237 		m = m->m_next;
238 	while (n) {
239 		if (m->m_flags & M_EXT ||
240 		    m->m_data + m->m_len + n->m_len >= &m->m_dat[MLEN]) {
241 			/* just join the two chains */
242 			m->m_next = n;
243 			return;
244 		}
245 		/* splat the data from one into the other */
246 		bcopy(mtod(n, caddr_t), mtod(m, caddr_t) + m->m_len,
247 		    (u_int)n->m_len);
248 		m->m_len += n->m_len;
249 		n = m_free(n);
250 	}
251 }
252 
253 
254 u_int
255 m_length(struct mbuf *m0, struct mbuf **last)
256 {
257 	struct mbuf *m;
258 	u_int len;
259 
260 	len = 0;
261 	for (m = m0; m != NULL; m = m->m_next) {
262 		len += m->m_len;
263 		if (m->m_next == NULL)
264 			break;
265 	}
266 	if (last != NULL)
267 		*last = m;
268 	return (len);
269 }
270 
271 
272 u_int
273 m_fixhdr(struct mbuf *m0)
274 {
275 	u_int len;
276 
277 	len = m_length(m0, NULL);
278 	m0->m_pkthdr.len = len;
279 	return (len);
280 }
281 
282 
283 static int
284 m_tag_copy_chain(struct mbuf *to, struct mbuf *from, int how)
285 {
286 	return 1;
287 }
288 
289 
290 /*
291  * Duplicate "from"'s mbuf pkthdr in "to".
292  * "from" must have M_PKTHDR set, and "to" must be empty.
293  * In particular, this does a deep copy of the packet tags.
294  */
295 static int
296 m_dup_pkthdr(struct mbuf *to, struct mbuf *from, int how)
297 {
298 	MBUF_CHECKSLEEP(how);
299 	/* to->m_flags = (from->m_flags & M_COPYFLAGS) | (to->m_flags & M_EXT); */
300 	to->m_flags = (to->m_flags & M_EXT);
301 	if ((to->m_flags & M_EXT) == 0)
302 		to->m_data = to->m_pktdat;
303 	to->m_pkthdr = from->m_pkthdr;
304 	/* SLIST_INIT(&to->m_pkthdr.tags); */
305 	return (m_tag_copy_chain(to, from, MBTOM(how)));
306 }
307 
308 
309 /*
310  * Defragment a mbuf chain, returning the shortest possible
311  * chain of mbufs and clusters.  If allocation fails and
312  * this cannot be completed, NULL will be returned, but
313  * the passed in chain will be unchanged.  Upon success,
314  * the original chain will be freed, and the new chain
315  * will be returned.
316  *
317  * If a non-packet header is passed in, the original
318  * mbuf (chain?) will be returned unharmed.
319  */
320 struct mbuf *
321 m_defrag(struct mbuf *m0, int how)
322 {
323 	struct mbuf *m_new = NULL, *m_final = NULL;
324 	int progress = 0, length;
325 
326 	MBUF_CHECKSLEEP(how);
327 	if (!(m0->m_flags & M_PKTHDR))
328 		return (m0);
329 
330 	m_fixhdr(m0); /* Needed sanity check */
331 
332 	if (m0->m_pkthdr.len > MHLEN)
333 		m_final = m_getcl(how, MT_DATA, M_PKTHDR);
334 	else
335 		m_final = m_gethdr(how, MT_DATA);
336 
337 	if (m_final == NULL)
338 		goto nospace;
339 
340 	if (m_dup_pkthdr(m_final, m0, how) == 0)
341 		goto nospace;
342 
343 	m_new = m_final;
344 
345 	while (progress < m0->m_pkthdr.len) {
346 		length = m0->m_pkthdr.len - progress;
347 		if (length > MCLBYTES)
348 			length = MCLBYTES;
349 
350 		if (m_new == NULL) {
351 			if (length > MLEN)
352 				m_new = m_getcl(how, MT_DATA, 0);
353 			else
354 				m_new = m_get(how, MT_DATA);
355 			if (m_new == NULL)
356 				goto nospace;
357 		}
358 
359 		m_copydata(m0, progress, length, mtod(m_new, caddr_t));
360 		progress += length;
361 		m_new->m_len = length;
362 		if (m_new != m_final)
363 			m_cat(m_final, m_new);
364 		m_new = NULL;
365 	}
366 
367 	m_freem(m0);
368 	m0 = m_final;
369 	return (m0);
370 nospace:
371 	if (m_final)
372 		m_freem(m_final);
373 	return (NULL);
374 }
375 
376 
377 status_t
378 init_mbufs()
379 {
380 	sMBufCache = create_object_cache("mbufs", MSIZE, 8, NULL, NULL, NULL);
381 	if (sMBufCache == NULL)
382 		return B_NO_MEMORY;
383 
384 	sChunkCache = create_object_cache("mbuf chunks", MCLBYTES, 0, NULL, NULL,
385 		NULL);
386 	if (sChunkCache == NULL) {
387 		delete_object_cache(sMBufCache);
388 		return B_NO_MEMORY;
389 	}
390 
391 	return B_OK;
392 }
393 
394 
395 void
396 uninit_mbufs()
397 {
398 	delete_object_cache(sMBufCache);
399 	delete_object_cache(sChunkCache);
400 }
401 
402