1 /*
2 * Software in this file is based heavily on code written in the FreeBSD source
3 * code repostiory. While the code is written from scratch, it contains
4 * many of the ideas and logic flow in the original source, this is a
5 * derivative work, and the following license applies as well:
6 *
7 * Copyright (c) 1982, 1986, 1988, 1991, 1993
8 * The Regents of the University of California. 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 * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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 #include "os/os.h"
37
38 #include <assert.h>
39 #include <stddef.h>
40 #include <string.h>
41 #include <limits.h>
42
43 /**
44 * @addtogroup OSKernel
45 * @{
46 * @defgroup OSMqueue Queue of Mbufs
47 * @{
48 */
49
50 STAILQ_HEAD(, os_mbuf_pool) g_msys_pool_list =
51 STAILQ_HEAD_INITIALIZER(g_msys_pool_list);
52
53
54 int
os_mqueue_init(struct os_mqueue * mq,ble_npl_event_fn * ev_cb,void * arg)55 os_mqueue_init(struct os_mqueue *mq, ble_npl_event_fn *ev_cb, void *arg)
56 {
57 struct ble_npl_event *ev;
58
59 STAILQ_INIT(&mq->mq_head);
60
61 ev = &mq->mq_ev;
62 ble_npl_event_init(ev, ev_cb, arg);
63
64 return (0);
65 }
66
67 struct os_mbuf *
os_mqueue_get(struct os_mqueue * mq)68 os_mqueue_get(struct os_mqueue *mq)
69 {
70 struct os_mbuf_pkthdr *mp;
71 struct os_mbuf *m;
72 os_sr_t sr;
73
74 OS_ENTER_CRITICAL(sr);
75 mp = STAILQ_FIRST(&mq->mq_head);
76 if (mp) {
77 STAILQ_REMOVE_HEAD(&mq->mq_head, omp_next);
78 }
79 OS_EXIT_CRITICAL(sr);
80
81 if (mp) {
82 m = OS_MBUF_PKTHDR_TO_MBUF(mp);
83 } else {
84 m = NULL;
85 }
86
87 return (m);
88 }
89
90 int
os_mqueue_put(struct os_mqueue * mq,struct ble_npl_eventq * evq,struct os_mbuf * m)91 os_mqueue_put(struct os_mqueue *mq, struct ble_npl_eventq *evq, struct os_mbuf *m)
92 {
93 struct os_mbuf_pkthdr *mp;
94 os_sr_t sr;
95 int rc;
96
97 /* Can only place the head of a chained mbuf on the queue. */
98 if (!OS_MBUF_IS_PKTHDR(m)) {
99 rc = OS_EINVAL;
100 goto err;
101 }
102
103 mp = OS_MBUF_PKTHDR(m);
104
105 OS_ENTER_CRITICAL(sr);
106 STAILQ_INSERT_TAIL(&mq->mq_head, mp, omp_next);
107 OS_EXIT_CRITICAL(sr);
108
109 /* Only post an event to the queue if its specified */
110 if (evq) {
111 ble_npl_eventq_put(evq, &mq->mq_ev);
112 }
113
114 return (0);
115 err:
116 return (rc);
117 }
118
119 int
os_msys_register(struct os_mbuf_pool * new_pool)120 os_msys_register(struct os_mbuf_pool *new_pool)
121 {
122 struct os_mbuf_pool *pool;
123
124 pool = NULL;
125 STAILQ_FOREACH(pool, &g_msys_pool_list, omp_next) {
126 if (new_pool->omp_databuf_len > pool->omp_databuf_len) {
127 break;
128 }
129 }
130
131 if (pool) {
132 STAILQ_INSERT_AFTER(&g_msys_pool_list, pool, new_pool, omp_next);
133 } else {
134 STAILQ_INSERT_TAIL(&g_msys_pool_list, new_pool, omp_next);
135 }
136
137 return (0);
138 }
139
140 void
os_msys_reset(void)141 os_msys_reset(void)
142 {
143 STAILQ_INIT(&g_msys_pool_list);
144 }
145
146 static struct os_mbuf_pool *
_os_msys_find_pool(uint16_t dsize)147 _os_msys_find_pool(uint16_t dsize)
148 {
149 struct os_mbuf_pool *pool;
150
151 pool = NULL;
152 STAILQ_FOREACH(pool, &g_msys_pool_list, omp_next) {
153 if (dsize <= pool->omp_databuf_len) {
154 break;
155 }
156 }
157
158 if (!pool) {
159 pool = STAILQ_LAST(&g_msys_pool_list, os_mbuf_pool, omp_next);
160 }
161
162 return (pool);
163 }
164
165
166 struct os_mbuf *
os_msys_get(uint16_t dsize,uint16_t leadingspace)167 os_msys_get(uint16_t dsize, uint16_t leadingspace)
168 {
169 struct os_mbuf *m;
170 struct os_mbuf_pool *pool;
171
172 pool = _os_msys_find_pool(dsize);
173 if (!pool) {
174 goto err;
175 }
176
177 m = os_mbuf_get(pool, leadingspace);
178 return (m);
179 err:
180 return (NULL);
181 }
182
183 struct os_mbuf *
os_msys_get_pkthdr(uint16_t dsize,uint16_t user_hdr_len)184 os_msys_get_pkthdr(uint16_t dsize, uint16_t user_hdr_len)
185 {
186 uint16_t total_pkthdr_len;
187 struct os_mbuf *m;
188 struct os_mbuf_pool *pool;
189
190 total_pkthdr_len = user_hdr_len + sizeof(struct os_mbuf_pkthdr);
191 pool = _os_msys_find_pool(dsize + total_pkthdr_len);
192 if (!pool) {
193 goto err;
194 }
195
196 m = os_mbuf_get_pkthdr(pool, user_hdr_len);
197 return (m);
198 err:
199 return (NULL);
200 }
201
202 int
os_msys_count(void)203 os_msys_count(void)
204 {
205 struct os_mbuf_pool *omp;
206 int total;
207
208 total = 0;
209 STAILQ_FOREACH(omp, &g_msys_pool_list, omp_next) {
210 total += omp->omp_pool->mp_num_blocks;
211 }
212
213 return total;
214 }
215
216 int
os_msys_num_free(void)217 os_msys_num_free(void)
218 {
219 struct os_mbuf_pool *omp;
220 int total;
221
222 total = 0;
223 STAILQ_FOREACH(omp, &g_msys_pool_list, omp_next) {
224 total += omp->omp_pool->mp_num_free;
225 }
226
227 return total;
228 }
229
230
231 int
os_mbuf_pool_init(struct os_mbuf_pool * omp,struct os_mempool * mp,uint16_t buf_len,uint16_t nbufs)232 os_mbuf_pool_init(struct os_mbuf_pool *omp, struct os_mempool *mp,
233 uint16_t buf_len, uint16_t nbufs)
234 {
235 omp->omp_databuf_len = buf_len - sizeof(struct os_mbuf);
236 omp->omp_pool = mp;
237
238 return (0);
239 }
240
241 struct os_mbuf *
os_mbuf_get(struct os_mbuf_pool * omp,uint16_t leadingspace)242 os_mbuf_get(struct os_mbuf_pool *omp, uint16_t leadingspace)
243 {
244 struct os_mbuf *om;
245
246 if (leadingspace > omp->omp_databuf_len) {
247 goto err;
248 }
249
250 om = os_memblock_get(omp->omp_pool);
251 if (!om) {
252 goto err;
253 }
254
255 SLIST_NEXT(om, om_next) = NULL;
256 om->om_flags = 0;
257 om->om_pkthdr_len = 0;
258 om->om_len = 0;
259 om->om_data = (&om->om_databuf[0] + leadingspace);
260 om->om_omp = omp;
261
262 return (om);
263 err:
264 return (NULL);
265 }
266
267 struct os_mbuf *
os_mbuf_get_pkthdr(struct os_mbuf_pool * omp,uint8_t user_pkthdr_len)268 os_mbuf_get_pkthdr(struct os_mbuf_pool *omp, uint8_t user_pkthdr_len)
269 {
270 uint16_t pkthdr_len;
271 struct os_mbuf_pkthdr *pkthdr;
272 struct os_mbuf *om;
273
274 /* User packet header must fit inside mbuf */
275 pkthdr_len = user_pkthdr_len + sizeof(struct os_mbuf_pkthdr);
276 if ((pkthdr_len > omp->omp_databuf_len) || (pkthdr_len > 255)) {
277 return NULL;
278 }
279
280 om = os_mbuf_get(omp, 0);
281 if (om) {
282 om->om_pkthdr_len = pkthdr_len;
283 om->om_data += pkthdr_len;
284
285 pkthdr = OS_MBUF_PKTHDR(om);
286 pkthdr->omp_len = 0;
287 pkthdr->omp_flags = 0;
288 STAILQ_NEXT(pkthdr, omp_next) = NULL;
289 }
290
291 return om;
292 }
293
294 int
os_mbuf_free(struct os_mbuf * om)295 os_mbuf_free(struct os_mbuf *om)
296 {
297 int rc;
298
299 if (om->om_omp != NULL) {
300 rc = os_memblock_put(om->om_omp->omp_pool, om);
301 if (rc != 0) {
302 goto err;
303 }
304 }
305
306 return (0);
307 err:
308 return (rc);
309 }
310
311 int
os_mbuf_free_chain(struct os_mbuf * om)312 os_mbuf_free_chain(struct os_mbuf *om)
313 {
314 struct os_mbuf *next;
315 int rc;
316
317 while (om != NULL) {
318 next = SLIST_NEXT(om, om_next);
319
320 rc = os_mbuf_free(om);
321 if (rc != 0) {
322 goto err;
323 }
324
325 om = next;
326 }
327
328 return (0);
329 err:
330 return (rc);
331 }
332
333 /**
334 * Copy a packet header from one mbuf to another.
335 *
336 * @param omp The mbuf pool associated with these buffers
337 * @param new_buf The new buffer to copy the packet header into
338 * @param old_buf The old buffer to copy the packet header from
339 */
340 static inline void
_os_mbuf_copypkthdr(struct os_mbuf * new_buf,struct os_mbuf * old_buf)341 _os_mbuf_copypkthdr(struct os_mbuf *new_buf, struct os_mbuf *old_buf)
342 {
343 assert(new_buf->om_len == 0);
344
345 memcpy(&new_buf->om_databuf[0], &old_buf->om_databuf[0],
346 old_buf->om_pkthdr_len);
347 new_buf->om_pkthdr_len = old_buf->om_pkthdr_len;
348 new_buf->om_data = new_buf->om_databuf + old_buf->om_pkthdr_len;
349 }
350
351 int
os_mbuf_append(struct os_mbuf * om,const void * data,uint16_t len)352 os_mbuf_append(struct os_mbuf *om, const void *data, uint16_t len)
353 {
354 struct os_mbuf_pool *omp;
355 struct os_mbuf *last;
356 struct os_mbuf *new;
357 int remainder;
358 int space;
359 int rc;
360
361 if (om == NULL) {
362 rc = OS_EINVAL;
363 goto err;
364 }
365
366 omp = om->om_omp;
367
368 /* Scroll to last mbuf in the chain */
369 last = om;
370 while (SLIST_NEXT(last, om_next) != NULL) {
371 last = SLIST_NEXT(last, om_next);
372 }
373
374 remainder = len;
375 space = OS_MBUF_TRAILINGSPACE(last);
376
377 /* If room in current mbuf, copy the first part of the data into the
378 * remaining space in that mbuf.
379 */
380 if (space > 0) {
381 if (space > remainder) {
382 space = remainder;
383 }
384
385 memcpy(OS_MBUF_DATA(last, uint8_t *) + last->om_len , data, space);
386
387 last->om_len += space;
388 data += space;
389 remainder -= space;
390 }
391
392 /* Take the remaining data, and keep allocating new mbufs and copying
393 * data into it, until data is exhausted.
394 */
395 while (remainder > 0) {
396 new = os_mbuf_get(omp, 0);
397 if (!new) {
398 break;
399 }
400
401 new->om_len = min(omp->omp_databuf_len, remainder);
402 memcpy(OS_MBUF_DATA(new, void *), data, new->om_len);
403 data += new->om_len;
404 remainder -= new->om_len;
405 SLIST_NEXT(last, om_next) = new;
406 last = new;
407 }
408
409 /* Adjust the packet header length in the buffer */
410 if (OS_MBUF_IS_PKTHDR(om)) {
411 OS_MBUF_PKTHDR(om)->omp_len += len - remainder;
412 }
413
414 if (remainder != 0) {
415 rc = OS_ENOMEM;
416 goto err;
417 }
418
419
420 return (0);
421 err:
422 return (rc);
423 }
424
425 int
os_mbuf_appendfrom(struct os_mbuf * dst,const struct os_mbuf * src,uint16_t src_off,uint16_t len)426 os_mbuf_appendfrom(struct os_mbuf *dst, const struct os_mbuf *src,
427 uint16_t src_off, uint16_t len)
428 {
429 const struct os_mbuf *src_cur_om;
430 uint16_t src_cur_off;
431 uint16_t chunk_sz;
432 int rc;
433
434 src_cur_om = os_mbuf_off(src, src_off, &src_cur_off);
435 while (len > 0) {
436 if (src_cur_om == NULL) {
437 return OS_EINVAL;
438 }
439
440 chunk_sz = min(len, src_cur_om->om_len - src_cur_off);
441 rc = os_mbuf_append(dst, src_cur_om->om_data + src_cur_off, chunk_sz);
442 if (rc != 0) {
443 return rc;
444 }
445
446 len -= chunk_sz;
447 src_cur_om = SLIST_NEXT(src_cur_om, om_next);
448 src_cur_off = 0;
449 }
450
451 return 0;
452 }
453
454 struct os_mbuf *
os_mbuf_dup(struct os_mbuf * om)455 os_mbuf_dup(struct os_mbuf *om)
456 {
457 struct os_mbuf_pool *omp;
458 struct os_mbuf *head;
459 struct os_mbuf *copy;
460
461 omp = om->om_omp;
462
463 head = NULL;
464 copy = NULL;
465
466 for (; om != NULL; om = SLIST_NEXT(om, om_next)) {
467 if (head) {
468 SLIST_NEXT(copy, om_next) = os_mbuf_get(omp,
469 OS_MBUF_LEADINGSPACE(om));
470 if (!SLIST_NEXT(copy, om_next)) {
471 os_mbuf_free_chain(head);
472 goto err;
473 }
474
475 copy = SLIST_NEXT(copy, om_next);
476 } else {
477 head = os_mbuf_get(omp, OS_MBUF_LEADINGSPACE(om));
478 if (!head) {
479 goto err;
480 }
481
482 if (OS_MBUF_IS_PKTHDR(om)) {
483 _os_mbuf_copypkthdr(head, om);
484 }
485 copy = head;
486 }
487 copy->om_flags = om->om_flags;
488 copy->om_len = om->om_len;
489 memcpy(OS_MBUF_DATA(copy, uint8_t *), OS_MBUF_DATA(om, uint8_t *),
490 om->om_len);
491 }
492
493 return (head);
494 err:
495 return (NULL);
496 }
497
498 struct os_mbuf *
os_mbuf_off(const struct os_mbuf * om,int off,uint16_t * out_off)499 os_mbuf_off(const struct os_mbuf *om, int off, uint16_t *out_off)
500 {
501 struct os_mbuf *next;
502 struct os_mbuf *cur;
503
504 /* Cast away const. */
505 cur = (struct os_mbuf *)om;
506
507 while (1) {
508 if (cur == NULL) {
509 return NULL;
510 }
511
512 next = SLIST_NEXT(cur, om_next);
513
514 if (cur->om_len > off ||
515 (cur->om_len == off && next == NULL)) {
516
517 *out_off = off;
518 return cur;
519 }
520
521 off -= cur->om_len;
522 cur = next;
523 }
524 }
525
526 int
os_mbuf_copydata(const struct os_mbuf * m,int off,int len,void * dst)527 os_mbuf_copydata(const struct os_mbuf *m, int off, int len, void *dst)
528 {
529 unsigned int count;
530 uint8_t *udst;
531
532 if (!len) {
533 return 0;
534 }
535
536 udst = dst;
537
538 while (off > 0) {
539 if (!m) {
540 return (-1);
541 }
542
543 if (off < m->om_len)
544 break;
545 off -= m->om_len;
546 m = SLIST_NEXT(m, om_next);
547 }
548 while (len > 0 && m != NULL) {
549 count = min(m->om_len - off, len);
550 memcpy(udst, m->om_data + off, count);
551 len -= count;
552 udst += count;
553 off = 0;
554 m = SLIST_NEXT(m, om_next);
555 }
556
557 return (len > 0 ? -1 : 0);
558 }
559
560 void
os_mbuf_adj(struct os_mbuf * mp,int req_len)561 os_mbuf_adj(struct os_mbuf *mp, int req_len)
562 {
563 int len = req_len;
564 struct os_mbuf *m;
565 int count;
566
567 if ((m = mp) == NULL)
568 return;
569 if (len >= 0) {
570 /*
571 * Trim from head.
572 */
573 while (m != NULL && len > 0) {
574 if (m->om_len <= len) {
575 len -= m->om_len;
576 m->om_len = 0;
577 m = SLIST_NEXT(m, om_next);
578 } else {
579 m->om_len -= len;
580 m->om_data += len;
581 len = 0;
582 }
583 }
584 if (OS_MBUF_IS_PKTHDR(mp))
585 OS_MBUF_PKTHDR(mp)->omp_len -= (req_len - len);
586 } else {
587 /*
588 * Trim from tail. Scan the mbuf chain,
589 * calculating its length and finding the last mbuf.
590 * If the adjustment only affects this mbuf, then just
591 * adjust and return. Otherwise, rescan and truncate
592 * after the remaining size.
593 */
594 len = -len;
595 count = 0;
596 for (;;) {
597 count += m->om_len;
598 if (SLIST_NEXT(m, om_next) == (struct os_mbuf *)0)
599 break;
600 m = SLIST_NEXT(m, om_next);
601 }
602 if (m->om_len >= len) {
603 m->om_len -= len;
604 if (OS_MBUF_IS_PKTHDR(mp))
605 OS_MBUF_PKTHDR(mp)->omp_len -= len;
606 return;
607 }
608 count -= len;
609 if (count < 0)
610 count = 0;
611 /*
612 * Correct length for chain is "count".
613 * Find the mbuf with last data, adjust its length,
614 * and toss data from remaining mbufs on chain.
615 */
616 m = mp;
617 if (OS_MBUF_IS_PKTHDR(m))
618 OS_MBUF_PKTHDR(m)->omp_len = count;
619 for (; m; m = SLIST_NEXT(m, om_next)) {
620 if (m->om_len >= count) {
621 m->om_len = count;
622 if (SLIST_NEXT(m, om_next) != NULL) {
623 os_mbuf_free_chain(SLIST_NEXT(m, om_next));
624 SLIST_NEXT(m, om_next) = NULL;
625 }
626 break;
627 }
628 count -= m->om_len;
629 }
630 }
631 }
632
633 int
os_mbuf_cmpf(const struct os_mbuf * om,int off,const void * data,int len)634 os_mbuf_cmpf(const struct os_mbuf *om, int off, const void *data, int len)
635 {
636 uint16_t chunk_sz;
637 uint16_t data_off;
638 uint16_t om_off;
639 int rc;
640
641 if (len <= 0) {
642 return 0;
643 }
644
645 data_off = 0;
646 om = os_mbuf_off(om, off, &om_off);
647 while (1) {
648 if (om == NULL) {
649 return INT_MAX;
650 }
651
652 chunk_sz = min(om->om_len - om_off, len - data_off);
653 if (chunk_sz > 0) {
654 rc = memcmp(om->om_data + om_off, data + data_off, chunk_sz);
655 if (rc != 0) {
656 return rc;
657 }
658 }
659
660 data_off += chunk_sz;
661 if (data_off == len) {
662 return 0;
663 }
664
665 om = SLIST_NEXT(om, om_next);
666 om_off = 0;
667
668 if (om == NULL) {
669 return INT_MAX;
670 }
671 }
672 }
673
674 int
os_mbuf_cmpm(const struct os_mbuf * om1,uint16_t offset1,const struct os_mbuf * om2,uint16_t offset2,uint16_t len)675 os_mbuf_cmpm(const struct os_mbuf *om1, uint16_t offset1,
676 const struct os_mbuf *om2, uint16_t offset2,
677 uint16_t len)
678 {
679 const struct os_mbuf *cur1;
680 const struct os_mbuf *cur2;
681 uint16_t bytes_remaining;
682 uint16_t chunk_sz;
683 uint16_t om1_left;
684 uint16_t om2_left;
685 uint16_t om1_off;
686 uint16_t om2_off;
687 int rc;
688
689 om1_off = 0;
690 om2_off = 0;
691
692 cur1 = os_mbuf_off(om1, offset1, &om1_off);
693 cur2 = os_mbuf_off(om2, offset2, &om2_off);
694
695 bytes_remaining = len;
696 while (1) {
697 if (bytes_remaining == 0) {
698 return 0;
699 }
700
701 while (cur1 != NULL && om1_off >= cur1->om_len) {
702 cur1 = SLIST_NEXT(cur1, om_next);
703 om1_off = 0;
704 }
705 while (cur2 != NULL && om2_off >= cur2->om_len) {
706 cur2 = SLIST_NEXT(cur2, om_next);
707 om2_off = 0;
708 }
709
710 if (cur1 == NULL || cur2 == NULL) {
711 return INT_MAX;
712 }
713
714 om1_left = cur1->om_len - om1_off;
715 om2_left = cur2->om_len - om2_off;
716 chunk_sz = min(min(om1_left, om2_left), bytes_remaining);
717
718 rc = memcmp(cur1->om_data + om1_off, cur2->om_data + om2_off,
719 chunk_sz);
720 if (rc != 0) {
721 return rc;
722 }
723
724 om1_off += chunk_sz;
725 om2_off += chunk_sz;
726 bytes_remaining -= chunk_sz;
727 }
728 }
729
730 struct os_mbuf *
os_mbuf_prepend(struct os_mbuf * om,int len)731 os_mbuf_prepend(struct os_mbuf *om, int len)
732 {
733 struct os_mbuf *p;
734 int leading;
735
736 while (1) {
737 /* Fill the available space at the front of the head of the chain, as
738 * needed.
739 */
740 leading = min(len, OS_MBUF_LEADINGSPACE(om));
741
742 om->om_data -= leading;
743 om->om_len += leading;
744 if (OS_MBUF_IS_PKTHDR(om)) {
745 OS_MBUF_PKTHDR(om)->omp_len += leading;
746 }
747
748 len -= leading;
749 if (len == 0) {
750 break;
751 }
752
753 /* The current head didn't have enough space; allocate a new head. */
754 if (OS_MBUF_IS_PKTHDR(om)) {
755 p = os_mbuf_get_pkthdr(om->om_omp,
756 om->om_pkthdr_len - sizeof (struct os_mbuf_pkthdr));
757 } else {
758 p = os_mbuf_get(om->om_omp, 0);
759 }
760 if (p == NULL) {
761 os_mbuf_free_chain(om);
762 om = NULL;
763 break;
764 }
765
766 if (OS_MBUF_IS_PKTHDR(om)) {
767 _os_mbuf_copypkthdr(p, om);
768 om->om_pkthdr_len = 0;
769 }
770
771 /* Move the new head's data pointer to the end so that data can be
772 * prepended.
773 */
774 p->om_data += OS_MBUF_TRAILINGSPACE(p);
775
776 SLIST_NEXT(p, om_next) = om;
777 om = p;
778 }
779
780 return om;
781 }
782
783 struct os_mbuf *
os_mbuf_prepend_pullup(struct os_mbuf * om,uint16_t len)784 os_mbuf_prepend_pullup(struct os_mbuf *om, uint16_t len)
785 {
786 om = os_mbuf_prepend(om, len);
787 if (om == NULL) {
788 return NULL;
789 }
790
791 om = os_mbuf_pullup(om, len);
792 if (om == NULL) {
793 return NULL;
794 }
795
796 return om;
797 }
798
799 int
os_mbuf_copyinto(struct os_mbuf * om,int off,const void * src,int len)800 os_mbuf_copyinto(struct os_mbuf *om, int off, const void *src, int len)
801 {
802 struct os_mbuf *next;
803 struct os_mbuf *cur;
804 const uint8_t *sptr;
805 uint16_t cur_off;
806 int copylen;
807 int rc;
808
809 /* Find the mbuf,offset pair for the start of the destination. */
810 cur = os_mbuf_off(om, off, &cur_off);
811 if (cur == NULL) {
812 return -1;
813 }
814
815 /* Overwrite existing data until we reach the end of the chain. */
816 sptr = src;
817 while (1) {
818 copylen = min(cur->om_len - cur_off, len);
819 if (copylen > 0) {
820 memcpy(cur->om_data + cur_off, sptr, copylen);
821 sptr += copylen;
822 len -= copylen;
823
824 copylen = 0;
825 }
826
827 if (len == 0) {
828 /* All the source data fit in the existing mbuf chain. */
829 return 0;
830 }
831
832 next = SLIST_NEXT(cur, om_next);
833 if (next == NULL) {
834 break;
835 }
836
837 cur = next;
838 cur_off = 0;
839 }
840
841 /* Append the remaining data to the end of the chain. */
842 rc = os_mbuf_append(cur, sptr, len);
843 if (rc != 0) {
844 return rc;
845 }
846
847 /* Fix up the packet header, if one is present. */
848 if (OS_MBUF_IS_PKTHDR(om)) {
849 OS_MBUF_PKTHDR(om)->omp_len =
850 max(OS_MBUF_PKTHDR(om)->omp_len, off + len);
851 }
852
853 return 0;
854 }
855
856 void
os_mbuf_concat(struct os_mbuf * first,struct os_mbuf * second)857 os_mbuf_concat(struct os_mbuf *first, struct os_mbuf *second)
858 {
859 struct os_mbuf *next;
860 struct os_mbuf *cur;
861
862 /* Point 'cur' to the last buffer in the first chain. */
863 cur = first;
864 while (1) {
865 next = SLIST_NEXT(cur, om_next);
866 if (next == NULL) {
867 break;
868 }
869
870 cur = next;
871 }
872
873 /* Attach the second chain to the end of the first. */
874 SLIST_NEXT(cur, om_next) = second;
875
876 /* If the first chain has a packet header, calculate the length of the
877 * second chain and add it to the header length.
878 */
879 if (OS_MBUF_IS_PKTHDR(first)) {
880 if (OS_MBUF_IS_PKTHDR(second)) {
881 OS_MBUF_PKTHDR(first)->omp_len += OS_MBUF_PKTHDR(second)->omp_len;
882 } else {
883 for (cur = second; cur != NULL; cur = SLIST_NEXT(cur, om_next)) {
884 OS_MBUF_PKTHDR(first)->omp_len += cur->om_len;
885 }
886 }
887 }
888
889 second->om_pkthdr_len = 0;
890 }
891
892 void *
os_mbuf_extend(struct os_mbuf * om,uint16_t len)893 os_mbuf_extend(struct os_mbuf *om, uint16_t len)
894 {
895 struct os_mbuf *newm;
896 struct os_mbuf *last;
897 void *data;
898
899 if (len > om->om_omp->omp_databuf_len) {
900 return NULL;
901 }
902
903 /* Scroll to last mbuf in the chain */
904 last = om;
905 while (SLIST_NEXT(last, om_next) != NULL) {
906 last = SLIST_NEXT(last, om_next);
907 }
908
909 if (OS_MBUF_TRAILINGSPACE(last) < len) {
910 newm = os_mbuf_get(om->om_omp, 0);
911 if (newm == NULL) {
912 return NULL;
913 }
914
915 SLIST_NEXT(last, om_next) = newm;
916 last = newm;
917 }
918
919 data = last->om_data + last->om_len;
920 last->om_len += len;
921
922 if (OS_MBUF_IS_PKTHDR(om)) {
923 OS_MBUF_PKTHDR(om)->omp_len += len;
924 }
925
926 return data;
927 }
928
929
930 struct os_mbuf *
os_mbuf_pullup(struct os_mbuf * om,uint16_t len)931 os_mbuf_pullup(struct os_mbuf *om, uint16_t len)
932 {
933 struct os_mbuf_pool *omp;
934 struct os_mbuf *next;
935 struct os_mbuf *om2;
936 int count;
937 int space;
938
939 omp = om->om_omp;
940
941 /*
942 * If first mbuf has no cluster, and has room for len bytes
943 * without shifting current data, pullup into it,
944 * otherwise allocate a new mbuf to prepend to the chain.
945 */
946 if (om->om_len >= len) {
947 return (om);
948 }
949 if (om->om_len + OS_MBUF_TRAILINGSPACE(om) >= len &&
950 SLIST_NEXT(om, om_next)) {
951 om2 = om;
952 om = SLIST_NEXT(om, om_next);
953 len -= om2->om_len;
954 } else {
955 if (len > omp->omp_databuf_len - om->om_pkthdr_len) {
956 goto bad;
957 }
958
959 om2 = os_mbuf_get(omp, 0);
960 if (om2 == NULL) {
961 goto bad;
962 }
963
964 if (OS_MBUF_IS_PKTHDR(om)) {
965 _os_mbuf_copypkthdr(om2, om);
966 }
967 }
968 space = OS_MBUF_TRAILINGSPACE(om2);
969 do {
970 count = min(min(len, space), om->om_len);
971 memcpy(om2->om_data + om2->om_len, om->om_data, count);
972 len -= count;
973 om2->om_len += count;
974 om->om_len -= count;
975 space -= count;
976 if (om->om_len) {
977 om->om_data += count;
978 } else {
979 next = SLIST_NEXT(om, om_next);
980 os_mbuf_free(om);
981 om = next;
982 }
983 } while (len > 0 && om);
984 if (len > 0) {
985 os_mbuf_free(om2);
986 goto bad;
987 }
988 SLIST_NEXT(om2, om_next) = om;
989 return (om2);
990 bad:
991 os_mbuf_free_chain(om);
992 return (NULL);
993 }
994
995 struct os_mbuf *
os_mbuf_trim_front(struct os_mbuf * om)996 os_mbuf_trim_front(struct os_mbuf *om)
997 {
998 struct os_mbuf *next;
999 struct os_mbuf *cur;
1000
1001 /* Abort early if there is nothing to trim. */
1002 if (om->om_len != 0) {
1003 return om;
1004 }
1005
1006 /* Starting with the second mbuf in the chain, continue removing and
1007 * freeing mbufs until an non-empty one is encountered.
1008 */
1009 cur = SLIST_NEXT(om, om_next);
1010 while (cur != NULL && cur->om_len == 0) {
1011 next = SLIST_NEXT(cur, om_next);
1012
1013 SLIST_NEXT(om, om_next) = next;
1014 os_mbuf_free(cur);
1015
1016 cur = next;
1017 }
1018
1019 if (cur == NULL) {
1020 /* All buffers after the first have been freed. */
1021 return om;
1022 }
1023
1024 /* Try to remove the first mbuf in the chain. If this buffer contains a
1025 * packet header, make sure the second buffer can accommodate it.
1026 */
1027 if (OS_MBUF_LEADINGSPACE(cur) >= om->om_pkthdr_len) {
1028 /* Second buffer has room; copy packet header. */
1029 cur->om_pkthdr_len = om->om_pkthdr_len;
1030 memcpy(OS_MBUF_PKTHDR(cur), OS_MBUF_PKTHDR(om), om->om_pkthdr_len);
1031
1032 /* Free first buffer. */
1033 os_mbuf_free(om);
1034 om = cur;
1035 }
1036
1037 return om;
1038 }
1039
1040