xref: /haiku/src/libs/compat/freebsd_network/mbuf.c (revision 12dba4e70f831d6d27a7f769cc9dab19c19a155d)
1 /*
2  * Copyright 2007, Hugo Santos. All Rights Reserved.
3  * Copyright 2004, Marcus Overhagen. All Rights Reserved.
4  * Distributed under the terms of the MIT License.
5  */
6 
7 
8 #include "device.h"
9 
10 #include <stdint.h>
11 #include <string.h>
12 #include <slab/Slab.h>
13 
14 #include <compat/sys/malloc.h>
15 #include <compat/sys/mbuf.h>
16 #include <compat/sys/kernel.h>
17 
18 
19 static object_cache *sMBufCache;
20 static object_cache *sChunkCache;
21 static object_cache *sJumbo9ChunkCache;
22 static object_cache *sJumboPageSizeCache;
23 
24 
25 int max_linkhdr = 16;
26 int max_protohdr = 40 + 20; /* ip6 + tcp */
27 
28 /* max_linkhdr + max_protohdr, but that's not allowed by gcc. */
29 int max_hdr = 16 + 40 + 20;
30 
31 /* MHLEN - max_hdr */
32 int max_datalen = MHLEN - (16 + 40 + 20);
33 
34 
35 static int
36 m_to_oc_flags(int how)
37 {
38 	if (how & M_NOWAIT)
39 		return CACHE_DONT_WAIT_FOR_MEMORY;
40 
41 	return 0;
42 }
43 
44 
45 static int
46 construct_mbuf(struct mbuf *memoryBuffer, short type, int flags)
47 {
48 	memoryBuffer->m_next = NULL;
49 	memoryBuffer->m_nextpkt = NULL;
50 	memoryBuffer->m_len = 0;
51 	memoryBuffer->m_flags = flags;
52 	memoryBuffer->m_type = type;
53 
54 	if (flags & M_PKTHDR) {
55 		memoryBuffer->m_data = memoryBuffer->m_pktdat;
56 		memset(&memoryBuffer->m_pkthdr, 0, sizeof(memoryBuffer->m_pkthdr));
57 		SLIST_INIT(&memoryBuffer->m_pkthdr.tags);
58 	} else {
59 		memoryBuffer->m_data = memoryBuffer->m_dat;
60 	}
61 
62 	return 0;
63 }
64 
65 
66 static int
67 construct_ext_sized_mbuf(struct mbuf *memoryBuffer, int how, int size)
68 {
69 	object_cache *cache;
70 	int extType;
71 	if (size != MCLBYTES && size != MJUM9BYTES && size != MJUMPAGESIZE)
72 		panic("unsupported size");
73 
74 	if (size == MCLBYTES) {
75 		cache = sChunkCache;
76 		extType = EXT_CLUSTER;
77 	} else if (size == MJUM9BYTES) {
78 		cache = sJumbo9ChunkCache;
79 		extType = EXT_JUMBO9;
80 	} else {
81 		cache = sJumboPageSizeCache;
82 		extType = EXT_JUMBOP;
83 	}
84 
85 	memoryBuffer->m_ext.ext_buf = object_cache_alloc(cache, m_to_oc_flags(how));
86 	if (memoryBuffer->m_ext.ext_buf == NULL)
87 		return B_NO_MEMORY;
88 
89 	memoryBuffer->m_data = memoryBuffer->m_ext.ext_buf;
90 	memoryBuffer->m_flags |= M_EXT;
91 	memoryBuffer->m_ext.ext_size = size;
92 	memoryBuffer->m_ext.ext_type = extType;
93 	memoryBuffer->m_ext.ext_flags = EXT_FLAG_EMBREF;
94 	memoryBuffer->m_ext.ext_count = 1;
95 
96 	return 0;
97 }
98 
99 
100 static inline int
101 construct_ext_mbuf(struct mbuf *memoryBuffer, int how)
102 {
103 	return construct_ext_sized_mbuf(memoryBuffer, how, MCLBYTES);
104 }
105 
106 
107 static int
108 construct_pkt_mbuf(int how, struct mbuf *memoryBuffer, short type, int flags)
109 {
110 	construct_mbuf(memoryBuffer, type, flags);
111 	if (construct_ext_mbuf(memoryBuffer, how) < 0)
112 		return -1;
113 	memoryBuffer->m_ext.ext_type = EXT_CLUSTER;
114 	return 0;
115 }
116 
117 
118 struct mbuf *
119 m_getcl(int how, short type, int flags)
120 {
121 	struct mbuf *memoryBuffer =
122 		(struct mbuf *)object_cache_alloc(sMBufCache, m_to_oc_flags(how));
123 	if (memoryBuffer == NULL)
124 		return NULL;
125 
126 	if (construct_pkt_mbuf(how, memoryBuffer, type, flags) < 0) {
127 		object_cache_free(sMBufCache, memoryBuffer, 0);
128 		return NULL;
129 	}
130 
131 	return memoryBuffer;
132 }
133 
134 
135 static struct mbuf *
136 _m_get(int how, short type, int flags)
137 {
138 	struct mbuf *memoryBuffer =
139 		(struct mbuf *)object_cache_alloc(sMBufCache, m_to_oc_flags(how));
140 	if (memoryBuffer == NULL)
141 		return NULL;
142 
143 	construct_mbuf(memoryBuffer, type, flags);
144 
145 	return memoryBuffer;
146 }
147 
148 
149 struct mbuf *
150 m_get(int how, short type)
151 {
152 	return _m_get(how, type, 0);
153 }
154 
155 
156 struct mbuf *
157 m_get2(int size, int how, short type, int flags)
158 {
159 	if (size <= MHLEN || (size <= MLEN && (flags & M_PKTHDR) == 0)) {
160 		size = MCLBYTES;
161 	} else if (size <= MJUMPAGESIZE) {
162 		size = MJUMPAGESIZE;
163 	} else if (size <= MJUM9BYTES) {
164 		size = MJUM9BYTES;
165 	} else /* (size > MJUM9BYTES) */ {
166 		return NULL;
167 	}
168 
169 	return m_getjcl(how, type, flags, size);
170 }
171 
172 
173 struct mbuf *
174 m_gethdr(int how, short type)
175 {
176 	return _m_get(how, type, M_PKTHDR);
177 }
178 
179 
180 struct mbuf *
181 m_getjcl(int how, short type, int flags, int size)
182 {
183 	struct mbuf *memoryBuffer =
184 		(struct mbuf *)object_cache_alloc(sMBufCache, m_to_oc_flags(how));
185 	if (memoryBuffer == NULL)
186 		return NULL;
187 	construct_mbuf(memoryBuffer, type, flags);
188 	if (construct_ext_sized_mbuf(memoryBuffer, how, size) < 0) {
189 		object_cache_free(sMBufCache, memoryBuffer, 0);
190 		return NULL;
191 	}
192 	return memoryBuffer;
193 }
194 
195 
196 int
197 m_clget(struct mbuf *memoryBuffer, int how)
198 {
199 	memoryBuffer->m_ext.ext_buf = NULL;
200 	/* called checks for errors by looking for M_EXT */
201 	construct_ext_mbuf(memoryBuffer, how);
202 	return memoryBuffer->m_flags & M_EXT;
203 }
204 
205 
206 void *
207 m_cljget(struct mbuf *memoryBuffer, int how, int size)
208 {
209 	if (memoryBuffer == NULL)
210 		panic("m_cljget doesn't support allocate mbuf");
211 	memoryBuffer->m_ext.ext_buf = NULL;
212 	construct_ext_sized_mbuf(memoryBuffer, how, size);
213 	/* shouldn't be used */
214 	return NULL;
215 }
216 
217 
218 void
219 m_freem(struct mbuf *memoryBuffer)
220 {
221 	while (memoryBuffer)
222 		memoryBuffer = m_free(memoryBuffer);
223 }
224 
225 
226 static void
227 mb_free_ext(struct mbuf *memoryBuffer)
228 {
229 	object_cache *cache = NULL;
230 	volatile u_int *refcnt;
231 	struct mbuf *mref;
232 	int freembuf;
233 
234 	KASSERT(memoryBuffer->m_flags & M_EXT, ("%s: M_EXT not set on %p",
235 		__func__, memoryBuffer));
236 
237 	/* See if this is the mbuf that holds the embedded refcount. */
238 	if (memoryBuffer->m_ext.ext_flags & EXT_FLAG_EMBREF) {
239 		refcnt = &memoryBuffer->m_ext.ext_count;
240 		mref = memoryBuffer;
241 	} else {
242 		KASSERT(memoryBuffer->m_ext.ext_cnt != NULL,
243 			("%s: no refcounting pointer on %p", __func__, memoryBuffer));
244 		refcnt = memoryBuffer->m_ext.ext_cnt;
245 		mref = __containerof(refcnt, struct mbuf, m_ext.ext_count);
246 	}
247 
248 	/*
249 	 * Check if the header is embedded in the cluster.  It is
250 	 * important that we can't touch any of the mbuf fields
251 	 * after we have freed the external storage, since mbuf
252 	 * could have been embedded in it.  For now, the mbufs
253 	 * embedded into the cluster are always of type EXT_EXTREF,
254 	 * and for this type we won't free the mref.
255 	 */
256 	if (memoryBuffer->m_flags & M_NOFREE) {
257 		freembuf = 0;
258 		KASSERT(memoryBuffer->m_ext.ext_type == EXT_EXTREF,
259 			("%s: no-free mbuf %p has wrong type", __func__, memoryBuffer));
260 	} else
261 		freembuf = 1;
262 
263 	/* Free attached storage only if this mbuf is the only reference to it. */
264 	if (!(*refcnt == 1 || atomic_add(refcnt, -1) == 1)
265 			&& !(freembuf && memoryBuffer != mref))
266 		return;
267 
268 	if (memoryBuffer->m_ext.ext_type == EXT_CLUSTER)
269 		cache = sChunkCache;
270 	else if (memoryBuffer->m_ext.ext_type == EXT_JUMBO9)
271 		cache = sJumbo9ChunkCache;
272 	else if (memoryBuffer->m_ext.ext_type == EXT_JUMBOP)
273 		cache = sJumboPageSizeCache;
274 	else
275 		panic("unknown type");
276 
277 	object_cache_free(cache, memoryBuffer->m_ext.ext_buf, 0);
278 	memoryBuffer->m_ext.ext_buf = NULL;
279 	object_cache_free(sMBufCache, memoryBuffer, 0);
280 }
281 
282 
283 struct mbuf *
284 m_free(struct mbuf *memoryBuffer)
285 {
286 	struct mbuf *next = memoryBuffer->m_next;
287 
288 	if (memoryBuffer->m_flags & M_EXT)
289 		mb_free_ext(memoryBuffer);
290 	else
291 		object_cache_free(sMBufCache, memoryBuffer, 0);
292 
293 	return next;
294 }
295 
296 
297 void
298 m_extadd(struct mbuf *memoryBuffer, caddr_t buffer, u_int size,
299     void (*freeHook)(void *, void *), void *arg1, void *arg2, int flags, int type)
300 {
301 	// TODO: implement?
302 	panic("m_extadd() called.");
303 }
304 
305 
306 status_t
307 init_mbufs()
308 {
309 	sMBufCache = create_object_cache("mbufs", MSIZE, 8, NULL, NULL, NULL);
310 	if (sMBufCache == NULL)
311 		goto clean;
312 	sChunkCache = create_object_cache("mbuf chunks", MCLBYTES, 0, NULL, NULL,
313 		NULL);
314 	if (sChunkCache == NULL)
315 		goto clean;
316 	sJumbo9ChunkCache = create_object_cache("mbuf jumbo9 chunks", MJUM9BYTES, 0,
317 		NULL, NULL, NULL);
318 	if (sJumbo9ChunkCache == NULL)
319 		goto clean;
320 	sJumboPageSizeCache = create_object_cache("mbuf jumbo page size chunks",
321 		MJUMPAGESIZE, 0, NULL, NULL, NULL);
322 	if (sJumboPageSizeCache == NULL)
323 		goto clean;
324 	return B_OK;
325 
326 clean:
327 	if (sJumbo9ChunkCache != NULL)
328 		delete_object_cache(sJumbo9ChunkCache);
329 	if (sChunkCache != NULL)
330 		delete_object_cache(sChunkCache);
331 	if (sMBufCache != NULL)
332 		delete_object_cache(sMBufCache);
333 	return B_NO_MEMORY;
334 }
335 
336 
337 void
338 uninit_mbufs()
339 {
340 	delete_object_cache(sMBufCache);
341 	delete_object_cache(sChunkCache);
342 	delete_object_cache(sJumbo9ChunkCache);
343 	delete_object_cache(sJumboPageSizeCache);
344 }
345