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