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