xref: /nrf52832-nimble/packages/NimBLE-latest/porting/nimble/src/os_mbuf.c (revision 042d53a763ad75cb1465103098bb88c245d95138)
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