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