vlib: add buffer cloning support
[vpp.git] / src / vlib / buffer.c
1 /*
2  * Copyright (c) 2015 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 /*
16  * buffer.c: allocate/free network buffers.
17  *
18  * Copyright (c) 2008 Eliot Dresselhaus
19  *
20  * Permission is hereby granted, free of charge, to any person obtaining
21  * a copy of this software and associated documentation files (the
22  * "Software"), to deal in the Software without restriction, including
23  * without limitation the rights to use, copy, modify, merge, publish,
24  * distribute, sublicense, and/or sell copies of the Software, and to
25  * permit persons to whom the Software is furnished to do so, subject to
26  * the following conditions:
27  *
28  * The above copyright notice and this permission notice shall be
29  * included in all copies or substantial portions of the Software.
30  *
31  *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
32  *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
33  *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
34  *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
35  *  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
36  *  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
37  *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
38  */
39
40 /**
41  * @file
42  *
43  * Allocate/free network buffers.
44  */
45
46 #include <vlib/vlib.h>
47
48 uword
49 vlib_buffer_length_in_chain_slow_path (vlib_main_t * vm,
50                                        vlib_buffer_t * b_first)
51 {
52   vlib_buffer_t *b = b_first;
53   uword l_first = b_first->current_length;
54   uword l = 0;
55   while (b->flags & VLIB_BUFFER_NEXT_PRESENT)
56     {
57       b = vlib_get_buffer (vm, b->next_buffer);
58       l += b->current_length;
59     }
60   b_first->total_length_not_including_first_buffer = l;
61   b_first->flags |= VLIB_BUFFER_TOTAL_LENGTH_VALID;
62   return l + l_first;
63 }
64
65 u8 *
66 format_vlib_buffer (u8 * s, va_list * args)
67 {
68   vlib_buffer_t *b = va_arg (*args, vlib_buffer_t *);
69   uword indent = format_get_indent (s);
70
71   s = format (s, "current data %d, length %d, free-list %d, clone-count %u",
72               b->current_data, b->current_length, b->free_list_index,
73               b->n_add_refs);
74
75   if (b->flags & VLIB_BUFFER_TOTAL_LENGTH_VALID)
76     s = format (s, ", totlen-nifb %d",
77                 b->total_length_not_including_first_buffer);
78
79   if (b->flags & VLIB_BUFFER_IS_TRACED)
80     s = format (s, ", trace 0x%x", b->trace_index);
81
82   while (b->flags & VLIB_BUFFER_NEXT_PRESENT)
83     {
84       vlib_main_t *vm = vlib_get_main ();
85       u32 next_buffer = b->next_buffer;
86       b = vlib_get_buffer (vm, next_buffer);
87
88       s =
89         format (s, "\n%Unext-buffer 0x%x, segment length %d, clone-count %u",
90                 format_white_space, indent, next_buffer, b->current_length,
91                 b->n_add_refs);
92     }
93
94   return s;
95 }
96
97 u8 *
98 format_vlib_buffer_and_data (u8 * s, va_list * args)
99 {
100   vlib_buffer_t *b = va_arg (*args, vlib_buffer_t *);
101
102   s = format (s, "%U, %U",
103               format_vlib_buffer, b,
104               format_hex_bytes, vlib_buffer_get_current (b), 64);
105
106   return s;
107 }
108
109 static u8 *
110 format_vlib_buffer_known_state (u8 * s, va_list * args)
111 {
112   vlib_buffer_known_state_t state = va_arg (*args, vlib_buffer_known_state_t);
113   char *t;
114
115   switch (state)
116     {
117     case VLIB_BUFFER_UNKNOWN:
118       t = "unknown";
119       break;
120
121     case VLIB_BUFFER_KNOWN_ALLOCATED:
122       t = "known-allocated";
123       break;
124
125     case VLIB_BUFFER_KNOWN_FREE:
126       t = "known-free";
127       break;
128
129     default:
130       t = "invalid";
131       break;
132     }
133
134   return format (s, "%s", t);
135 }
136
137 u8 *
138 format_vlib_buffer_contents (u8 * s, va_list * va)
139 {
140   vlib_main_t *vm = va_arg (*va, vlib_main_t *);
141   vlib_buffer_t *b = va_arg (*va, vlib_buffer_t *);
142
143   while (1)
144     {
145       vec_add (s, vlib_buffer_get_current (b), b->current_length);
146       if (!(b->flags & VLIB_BUFFER_NEXT_PRESENT))
147         break;
148       b = vlib_get_buffer (vm, b->next_buffer);
149     }
150
151   return s;
152 }
153
154 static u8 *
155 vlib_validate_buffer_helper (vlib_main_t * vm,
156                              u32 bi,
157                              uword follow_buffer_next, uword ** unique_hash)
158 {
159   vlib_buffer_t *b = vlib_get_buffer (vm, bi);
160   vlib_buffer_main_t *bm = vm->buffer_main;
161   vlib_buffer_free_list_t *fl;
162
163   if (pool_is_free_index (bm->buffer_free_list_pool, b->free_list_index))
164     return format (0, "unknown free list 0x%x", b->free_list_index);
165
166   fl = pool_elt_at_index (bm->buffer_free_list_pool, b->free_list_index);
167
168   if ((signed) b->current_data < (signed) -VLIB_BUFFER_PRE_DATA_SIZE)
169     return format (0, "current data %d before pre-data", b->current_data);
170
171   if (b->current_data + b->current_length > fl->n_data_bytes)
172     return format (0, "%d-%d beyond end of buffer %d",
173                    b->current_data, b->current_length, fl->n_data_bytes);
174
175   if (follow_buffer_next && (b->flags & VLIB_BUFFER_NEXT_PRESENT))
176     {
177       vlib_buffer_known_state_t k;
178       u8 *msg, *result;
179
180       k = vlib_buffer_is_known (vm, b->next_buffer);
181       if (k != VLIB_BUFFER_KNOWN_ALLOCATED)
182         return format (0, "next 0x%x: %U",
183                        b->next_buffer, format_vlib_buffer_known_state, k);
184
185       if (unique_hash)
186         {
187           if (hash_get (*unique_hash, b->next_buffer))
188             return format (0, "duplicate buffer 0x%x", b->next_buffer);
189
190           hash_set1 (*unique_hash, b->next_buffer);
191         }
192
193       msg = vlib_validate_buffer (vm, b->next_buffer, follow_buffer_next);
194       if (msg)
195         {
196           result = format (0, "next 0x%x: %v", b->next_buffer, msg);
197           vec_free (msg);
198           return result;
199         }
200     }
201
202   return 0;
203 }
204
205 u8 *
206 vlib_validate_buffer (vlib_main_t * vm, u32 bi, uword follow_buffer_next)
207 {
208   return vlib_validate_buffer_helper (vm, bi, follow_buffer_next,
209                                       /* unique_hash */ 0);
210 }
211
212 u8 *
213 vlib_validate_buffers (vlib_main_t * vm,
214                        u32 * buffers,
215                        uword next_buffer_stride,
216                        uword n_buffers,
217                        vlib_buffer_known_state_t known_state,
218                        uword follow_buffer_next)
219 {
220   uword i, *hash;
221   u32 bi, *b = buffers;
222   vlib_buffer_known_state_t k;
223   u8 *msg = 0, *result = 0;
224
225   hash = hash_create (0, 0);
226   for (i = 0; i < n_buffers; i++)
227     {
228       bi = b[0];
229       b += next_buffer_stride;
230
231       /* Buffer is not unique. */
232       if (hash_get (hash, bi))
233         {
234           msg = format (0, "not unique");
235           goto done;
236         }
237
238       k = vlib_buffer_is_known (vm, bi);
239       if (k != known_state)
240         {
241           msg = format (0, "is %U; expected %U",
242                         format_vlib_buffer_known_state, k,
243                         format_vlib_buffer_known_state, known_state);
244           goto done;
245         }
246
247       msg = vlib_validate_buffer_helper (vm, bi, follow_buffer_next, &hash);
248       if (msg)
249         goto done;
250
251       hash_set1 (hash, bi);
252     }
253
254 done:
255   if (msg)
256     {
257       result = format (0, "0x%x: %v", bi, msg);
258       vec_free (msg);
259     }
260   hash_free (hash);
261   return result;
262 }
263
264 vlib_main_t **vlib_mains;
265
266 /* When dubugging validate that given buffers are either known allocated
267    or known free. */
268 static void
269 vlib_buffer_validate_alloc_free (vlib_main_t * vm,
270                                  u32 * buffers,
271                                  uword n_buffers,
272                                  vlib_buffer_known_state_t expected_state)
273 {
274   u32 *b;
275   uword i, bi, is_free;
276
277   if (CLIB_DEBUG == 0)
278     return;
279
280   ASSERT (os_get_cpu_number () == 0);
281
282   /* smp disaster check */
283   if (vlib_mains)
284     ASSERT (vm == vlib_mains[0]);
285
286   is_free = expected_state == VLIB_BUFFER_KNOWN_ALLOCATED;
287   b = buffers;
288   for (i = 0; i < n_buffers; i++)
289     {
290       vlib_buffer_known_state_t known;
291
292       bi = b[0];
293       b += 1;
294       known = vlib_buffer_is_known (vm, bi);
295       if (known != expected_state)
296         {
297           ASSERT (0);
298           vlib_panic_with_msg
299             (vm, "%s %U buffer 0x%x",
300              is_free ? "freeing" : "allocating",
301              format_vlib_buffer_known_state, known, bi);
302         }
303
304       vlib_buffer_set_known_state
305         (vm, bi,
306          is_free ? VLIB_BUFFER_KNOWN_FREE : VLIB_BUFFER_KNOWN_ALLOCATED);
307     }
308 }
309
310 void
311 vlib_buffer_merge_free_lists (vlib_buffer_free_list_t * dst,
312                               vlib_buffer_free_list_t * src)
313 {
314   uword l;
315   u32 *d;
316
317   l = vec_len (src->buffers);
318   if (l > 0)
319     {
320       vec_add2_aligned (dst->buffers, d, l, CLIB_CACHE_LINE_BYTES);
321       clib_memcpy (d, src->buffers, l * sizeof (d[0]));
322       vec_free (src->buffers);
323     }
324 }
325
326 /* Add buffer free list. */
327 static u32
328 vlib_buffer_create_free_list_helper (vlib_main_t * vm,
329                                      u32 n_data_bytes,
330                                      u32 is_public, u32 is_default, u8 * name)
331 {
332   vlib_buffer_main_t *bm = vm->buffer_main;
333   vlib_buffer_free_list_t *f;
334   int i;
335
336   ASSERT (os_get_cpu_number () == 0);
337
338   if (!is_default && pool_elts (bm->buffer_free_list_pool) == 0)
339     {
340       u32 default_free_free_list_index;
341
342       /* *INDENT-OFF* */
343       default_free_free_list_index =
344         vlib_buffer_create_free_list_helper
345         (vm,
346          /* default buffer size */ VLIB_BUFFER_DEFAULT_FREE_LIST_BYTES,
347          /* is_public */ 1,
348          /* is_default */ 1,
349          (u8 *) "default");
350       /* *INDENT-ON* */
351       ASSERT (default_free_free_list_index ==
352               VLIB_BUFFER_DEFAULT_FREE_LIST_INDEX);
353
354       if (n_data_bytes == VLIB_BUFFER_DEFAULT_FREE_LIST_BYTES && is_public)
355         return default_free_free_list_index;
356     }
357
358   pool_get_aligned (bm->buffer_free_list_pool, f, CLIB_CACHE_LINE_BYTES);
359
360   memset (f, 0, sizeof (f[0]));
361   f->index = f - bm->buffer_free_list_pool;
362   f->n_data_bytes = vlib_buffer_round_size (n_data_bytes);
363   f->min_n_buffers_each_physmem_alloc = 16;
364   f->name = clib_mem_is_heap_object (name) ? name : format (0, "%s", name);
365
366   /* Setup free buffer template. */
367   f->buffer_init_template.free_list_index = f->index;
368   f->buffer_init_template.n_add_refs = 0;
369
370   if (is_public)
371     {
372       uword *p = hash_get (bm->free_list_by_size, f->n_data_bytes);
373       if (!p)
374         hash_set (bm->free_list_by_size, f->n_data_bytes, f->index);
375     }
376
377   for (i = 1; i < vec_len (vlib_mains); i++)
378     {
379       vlib_buffer_main_t *wbm = vlib_mains[i]->buffer_main;
380       vlib_buffer_free_list_t *wf;
381       pool_get_aligned (wbm->buffer_free_list_pool,
382                         wf, CLIB_CACHE_LINE_BYTES);
383       ASSERT (f - bm->buffer_free_list_pool ==
384               wf - wbm->buffer_free_list_pool);
385       wf[0] = f[0];
386       wf->buffers = 0;
387       wf->n_alloc = 0;
388     }
389
390   return f->index;
391 }
392
393 u32
394 vlib_buffer_create_free_list (vlib_main_t * vm, u32 n_data_bytes,
395                               char *fmt, ...)
396 {
397   va_list va;
398   u8 *name;
399
400   va_start (va, fmt);
401   name = va_format (0, fmt, &va);
402   va_end (va);
403
404   return vlib_buffer_create_free_list_helper (vm, n_data_bytes,
405                                               /* is_public */ 0,
406                                               /* is_default */ 0,
407                                               name);
408 }
409
410 u32
411 vlib_buffer_get_or_create_free_list (vlib_main_t * vm, u32 n_data_bytes,
412                                      char *fmt, ...)
413 {
414   u32 i = vlib_buffer_get_free_list_with_size (vm, n_data_bytes);
415
416   if (i == ~0)
417     {
418       va_list va;
419       u8 *name;
420
421       va_start (va, fmt);
422       name = va_format (0, fmt, &va);
423       va_end (va);
424
425       i = vlib_buffer_create_free_list_helper (vm, n_data_bytes,
426                                                /* is_public */ 1,
427                                                /* is_default */ 0,
428                                                name);
429     }
430
431   return i;
432 }
433
434 static void
435 del_free_list (vlib_main_t * vm, vlib_buffer_free_list_t * f)
436 {
437   u32 i;
438
439   for (i = 0; i < vec_len (f->buffer_memory_allocated); i++)
440     vm->os_physmem_free (f->buffer_memory_allocated[i]);
441   vec_free (f->name);
442   vec_free (f->buffer_memory_allocated);
443   vec_free (f->buffers);
444 }
445
446 /* Add buffer free list. */
447 void
448 vlib_buffer_delete_free_list_internal (vlib_main_t * vm, u32 free_list_index)
449 {
450   vlib_buffer_main_t *bm = vm->buffer_main;
451   vlib_buffer_free_list_t *f;
452   u32 merge_index;
453   int i;
454
455   ASSERT (os_get_cpu_number () == 0);
456
457   f = vlib_buffer_get_free_list (vm, free_list_index);
458
459   ASSERT (vec_len (f->buffers) == f->n_alloc);
460   merge_index = vlib_buffer_get_free_list_with_size (vm, f->n_data_bytes);
461   if (merge_index != ~0 && merge_index != free_list_index)
462     {
463       vlib_buffer_merge_free_lists (pool_elt_at_index
464                                     (bm->buffer_free_list_pool, merge_index),
465                                     f);
466     }
467
468   del_free_list (vm, f);
469
470   /* Poison it. */
471   memset (f, 0xab, sizeof (f[0]));
472
473   pool_put (bm->buffer_free_list_pool, f);
474
475   for (i = 1; i < vec_len (vlib_mains); i++)
476     {
477       bm = vlib_mains[i]->buffer_main;
478       f = vlib_buffer_get_free_list (vlib_mains[i], free_list_index);;
479       memset (f, 0xab, sizeof (f[0]));
480       pool_put (bm->buffer_free_list_pool, f);
481     }
482 }
483
484 /* Make sure free list has at least given number of free buffers. */
485 static uword
486 fill_free_list (vlib_main_t * vm,
487                 vlib_buffer_free_list_t * fl, uword min_free_buffers)
488 {
489   vlib_buffer_t *buffers, *b;
490   int n, n_bytes, i;
491   u32 *bi;
492   u32 n_remaining, n_alloc, n_this_chunk;
493
494   /* Already have enough free buffers on free list? */
495   n = min_free_buffers - vec_len (fl->buffers);
496   if (n <= 0)
497     return min_free_buffers;
498
499   /* Always allocate round number of buffers. */
500   n = round_pow2 (n, CLIB_CACHE_LINE_BYTES / sizeof (u32));
501
502   /* Always allocate new buffers in reasonably large sized chunks. */
503   n = clib_max (n, fl->min_n_buffers_each_physmem_alloc);
504
505   n_remaining = n;
506   n_alloc = 0;
507   while (n_remaining > 0)
508     {
509       n_this_chunk = clib_min (n_remaining, 16);
510
511       n_bytes = n_this_chunk * (sizeof (b[0]) + fl->n_data_bytes);
512
513       /* drb: removed power-of-2 ASSERT */
514       buffers = vm->os_physmem_alloc_aligned (&vm->physmem_main,
515                                               n_bytes,
516                                               sizeof (vlib_buffer_t));
517       if (!buffers)
518         return n_alloc;
519
520       /* Record chunk as being allocated so we can free it later. */
521       vec_add1 (fl->buffer_memory_allocated, buffers);
522
523       fl->n_alloc += n_this_chunk;
524       n_alloc += n_this_chunk;
525       n_remaining -= n_this_chunk;
526
527       b = buffers;
528       vec_add2_aligned (fl->buffers, bi, n_this_chunk, CLIB_CACHE_LINE_BYTES);
529       for (i = 0; i < n_this_chunk; i++)
530         {
531           bi[i] = vlib_get_buffer_index (vm, b);
532
533           if (CLIB_DEBUG > 0)
534             vlib_buffer_set_known_state (vm, bi[i], VLIB_BUFFER_KNOWN_FREE);
535           b = vlib_buffer_next_contiguous (b, fl->n_data_bytes);
536         }
537
538       memset (buffers, 0, n_bytes);
539
540       /* Initialize all new buffers. */
541       b = buffers;
542       for (i = 0; i < n_this_chunk; i++)
543         {
544           vlib_buffer_init_for_free_list (b, fl);
545           b = vlib_buffer_next_contiguous (b, fl->n_data_bytes);
546         }
547
548       if (fl->buffer_init_function)
549         fl->buffer_init_function (vm, fl, bi, n_this_chunk);
550     }
551   return n_alloc;
552 }
553
554 static u32
555 alloc_from_free_list (vlib_main_t * vm,
556                       vlib_buffer_free_list_t * free_list,
557                       u32 * alloc_buffers, u32 n_alloc_buffers)
558 {
559   u32 *dst, *src;
560   uword len;
561   uword n_filled;
562
563   dst = alloc_buffers;
564
565   n_filled = fill_free_list (vm, free_list, n_alloc_buffers);
566   if (n_filled == 0)
567     return 0;
568
569   len = vec_len (free_list->buffers);
570   ASSERT (len >= n_alloc_buffers);
571
572   src = free_list->buffers + len - n_alloc_buffers;
573   clib_memcpy (dst, src, n_alloc_buffers * sizeof (u32));
574
575   _vec_len (free_list->buffers) -= n_alloc_buffers;
576
577   /* Verify that buffers are known free. */
578   vlib_buffer_validate_alloc_free (vm, alloc_buffers,
579                                    n_alloc_buffers, VLIB_BUFFER_KNOWN_FREE);
580
581   return n_alloc_buffers;
582 }
583
584
585 /* Allocate a given number of buffers into given array.
586    Returns number actually allocated which will be either zero or
587    number requested. */
588 static u32
589 vlib_buffer_alloc_internal (vlib_main_t * vm, u32 * buffers, u32 n_buffers)
590 {
591   vlib_buffer_main_t *bm = vm->buffer_main;
592
593   return alloc_from_free_list
594     (vm,
595      pool_elt_at_index (bm->buffer_free_list_pool,
596                         VLIB_BUFFER_DEFAULT_FREE_LIST_INDEX),
597      buffers, n_buffers);
598 }
599
600 static u32
601 vlib_buffer_alloc_from_free_list_internal (vlib_main_t * vm,
602                                            u32 * buffers,
603                                            u32 n_buffers, u32 free_list_index)
604 {
605   vlib_buffer_main_t *bm = vm->buffer_main;
606   vlib_buffer_free_list_t *f;
607   f = pool_elt_at_index (bm->buffer_free_list_pool, free_list_index);
608   return alloc_from_free_list (vm, f, buffers, n_buffers);
609 }
610
611 void *
612 vlib_set_buffer_free_callback (vlib_main_t * vm, void *fp)
613 {
614   vlib_buffer_main_t *bm = vm->buffer_main;
615   void *rv = bm->buffer_free_callback;
616
617   bm->buffer_free_callback = fp;
618   return rv;
619 }
620
621 static_always_inline void
622 vlib_buffer_free_inline (vlib_main_t * vm,
623                          u32 * buffers, u32 n_buffers, u32 follow_buffer_next)
624 {
625   vlib_buffer_main_t *bm = vm->buffer_main;
626   vlib_buffer_free_list_t *fl;
627   u32 fi;
628   int i;
629   u32 (*cb) (vlib_main_t * vm, u32 * buffers, u32 n_buffers,
630              u32 follow_buffer_next);
631
632   cb = bm->buffer_free_callback;
633
634   if (PREDICT_FALSE (cb != 0))
635     n_buffers = (*cb) (vm, buffers, n_buffers, follow_buffer_next);
636
637   if (!n_buffers)
638     return;
639
640   for (i = 0; i < n_buffers; i++)
641     {
642       vlib_buffer_t *b;
643       u32 bi = buffers[i];
644
645       b = vlib_get_buffer (vm, bi);
646
647       fl = vlib_buffer_get_buffer_free_list (vm, b, &fi);
648
649       /* The only current use of this callback: multicast recycle */
650       if (PREDICT_FALSE (fl->buffers_added_to_freelist_function != 0))
651         {
652           int j;
653
654           vlib_buffer_add_to_free_list
655             (vm, fl, buffers[i], (b->flags & VLIB_BUFFER_RECYCLE) == 0);
656
657           for (j = 0; j < vec_len (bm->announce_list); j++)
658             {
659               if (fl == bm->announce_list[j])
660                 goto already_announced;
661             }
662           vec_add1 (bm->announce_list, fl);
663         already_announced:
664           ;
665         }
666       else
667         {
668           if (PREDICT_TRUE ((b->flags & VLIB_BUFFER_RECYCLE) == 0))
669             {
670               u32 flags, next;
671
672               do
673                 {
674                   vlib_buffer_t *nb = vlib_get_buffer (vm, bi);
675                   flags = nb->flags;
676                   next = nb->next_buffer;
677                   if (nb->n_add_refs)
678                     nb->n_add_refs--;
679                   else
680                     {
681                       vlib_buffer_validate_alloc_free (vm, &bi, 1,
682                                                        VLIB_BUFFER_KNOWN_ALLOCATED);
683                       vlib_buffer_add_to_free_list (vm, fl, bi, 1);
684                     }
685                   bi = next;
686                 }
687               while (follow_buffer_next
688                      && (flags & VLIB_BUFFER_NEXT_PRESENT));
689
690             }
691         }
692     }
693   if (vec_len (bm->announce_list))
694     {
695       vlib_buffer_free_list_t *fl;
696       for (i = 0; i < vec_len (bm->announce_list); i++)
697         {
698           fl = bm->announce_list[i];
699           fl->buffers_added_to_freelist_function (vm, fl);
700         }
701       _vec_len (bm->announce_list) = 0;
702     }
703 }
704
705 static void
706 vlib_buffer_free_internal (vlib_main_t * vm, u32 * buffers, u32 n_buffers)
707 {
708   vlib_buffer_free_inline (vm, buffers, n_buffers,      /* follow_buffer_next */
709                            1);
710 }
711
712 static void
713 vlib_buffer_free_no_next_internal (vlib_main_t * vm, u32 * buffers,
714                                    u32 n_buffers)
715 {
716   vlib_buffer_free_inline (vm, buffers, n_buffers,      /* follow_buffer_next */
717                            0);
718 }
719
720 /* Copy template packet data into buffers as they are allocated. */
721 static void __attribute__ ((unused))
722 vlib_packet_template_buffer_init (vlib_main_t * vm,
723                                   vlib_buffer_free_list_t * fl,
724                                   u32 * buffers, u32 n_buffers)
725 {
726   vlib_packet_template_t *t =
727     uword_to_pointer (fl->buffer_init_function_opaque,
728                       vlib_packet_template_t *);
729   uword i;
730
731   for (i = 0; i < n_buffers; i++)
732     {
733       vlib_buffer_t *b = vlib_get_buffer (vm, buffers[i]);
734       ASSERT (b->current_length == vec_len (t->packet_data));
735       clib_memcpy (vlib_buffer_get_current (b), t->packet_data,
736                    b->current_length);
737     }
738 }
739
740 void
741 vlib_packet_template_init (vlib_main_t * vm,
742                            vlib_packet_template_t * t,
743                            void *packet_data,
744                            uword n_packet_data_bytes,
745                            uword min_n_buffers_each_physmem_alloc,
746                            char *fmt, ...)
747 {
748   vlib_buffer_main_t *bm = vm->buffer_main;
749   va_list va;
750   __attribute__ ((unused)) u8 *name;
751   vlib_buffer_free_list_t *fl;
752
753   va_start (va, fmt);
754   name = va_format (0, fmt, &va);
755   va_end (va);
756
757   if (bm->cb.vlib_packet_template_init_cb)
758     bm->cb.vlib_packet_template_init_cb (vm, (void *) t, packet_data,
759                                          n_packet_data_bytes,
760                                          min_n_buffers_each_physmem_alloc,
761                                          name);
762
763   vlib_worker_thread_barrier_sync (vm);
764
765   memset (t, 0, sizeof (t[0]));
766
767   vec_add (t->packet_data, packet_data, n_packet_data_bytes);
768   t->min_n_buffers_each_physmem_alloc = min_n_buffers_each_physmem_alloc;
769
770   t->free_list_index = vlib_buffer_create_free_list_helper
771     (vm, n_packet_data_bytes,
772      /* is_public */ 1,
773      /* is_default */ 0,
774      name);
775
776   ASSERT (t->free_list_index != 0);
777   fl = vlib_buffer_get_free_list (vm, t->free_list_index);
778   fl->min_n_buffers_each_physmem_alloc = t->min_n_buffers_each_physmem_alloc;
779
780   fl->buffer_init_function = vlib_packet_template_buffer_init;
781   fl->buffer_init_function_opaque = pointer_to_uword (t);
782
783   fl->buffer_init_template.current_data = 0;
784   fl->buffer_init_template.current_length = n_packet_data_bytes;
785   fl->buffer_init_template.flags = 0;
786   fl->buffer_init_template.n_add_refs = 0;
787   vlib_worker_thread_barrier_release (vm);
788 }
789
790 void *
791 vlib_packet_template_get_packet (vlib_main_t * vm,
792                                  vlib_packet_template_t * t, u32 * bi_result)
793 {
794   u32 bi;
795   vlib_buffer_t *b;
796
797   if (vlib_buffer_alloc (vm, &bi, 1) != 1)
798     return 0;
799
800   *bi_result = bi;
801
802   b = vlib_get_buffer (vm, bi);
803   clib_memcpy (vlib_buffer_get_current (b),
804                t->packet_data, vec_len (t->packet_data));
805   b->current_length = vec_len (t->packet_data);
806
807   return b->data;
808 }
809
810 void
811 vlib_packet_template_get_packet_helper (vlib_main_t * vm,
812                                         vlib_packet_template_t * t)
813 {
814   word n = t->min_n_buffers_each_physmem_alloc;
815   word l = vec_len (t->packet_data);
816   word n_alloc;
817
818   ASSERT (l > 0);
819   ASSERT (vec_len (t->free_buffers) == 0);
820
821   vec_validate (t->free_buffers, n - 1);
822   n_alloc = vlib_buffer_alloc_from_free_list (vm, t->free_buffers,
823                                               n, t->free_list_index);
824   _vec_len (t->free_buffers) = n_alloc;
825 }
826
827 /* Append given data to end of buffer, possibly allocating new buffers. */
828 u32
829 vlib_buffer_add_data (vlib_main_t * vm,
830                       u32 free_list_index,
831                       u32 buffer_index, void *data, u32 n_data_bytes)
832 {
833   u32 n_buffer_bytes, n_left, n_left_this_buffer, bi;
834   vlib_buffer_t *b;
835   void *d;
836
837   bi = buffer_index;
838   if (bi == 0
839       && 1 != vlib_buffer_alloc_from_free_list (vm, &bi, 1, free_list_index))
840     goto out_of_buffers;
841
842   d = data;
843   n_left = n_data_bytes;
844   n_buffer_bytes = vlib_buffer_free_list_buffer_size (vm, free_list_index);
845
846   b = vlib_get_buffer (vm, bi);
847   b->flags &= ~VLIB_BUFFER_TOTAL_LENGTH_VALID;
848
849   /* Get to the end of the chain before we try to append data... */
850   while (b->flags & VLIB_BUFFER_NEXT_PRESENT)
851     b = vlib_get_buffer (vm, b->next_buffer);
852
853   while (1)
854     {
855       u32 n;
856
857       ASSERT (n_buffer_bytes >= b->current_length);
858       n_left_this_buffer =
859         n_buffer_bytes - (b->current_data + b->current_length);
860       n = clib_min (n_left_this_buffer, n_left);
861       clib_memcpy (vlib_buffer_get_current (b) + b->current_length, d, n);
862       b->current_length += n;
863       n_left -= n;
864       if (n_left == 0)
865         break;
866
867       d += n;
868       if (1 !=
869           vlib_buffer_alloc_from_free_list (vm, &b->next_buffer, 1,
870                                             free_list_index))
871         goto out_of_buffers;
872
873       b->flags |= VLIB_BUFFER_NEXT_PRESENT;
874
875       b = vlib_get_buffer (vm, b->next_buffer);
876     }
877
878   return bi;
879
880 out_of_buffers:
881   clib_error ("out of buffers");
882   return bi;
883 }
884
885 u16
886 vlib_buffer_chain_append_data_with_alloc (vlib_main_t * vm,
887                                           u32 free_list_index,
888                                           vlib_buffer_t * first,
889                                           vlib_buffer_t ** last,
890                                           void *data, u16 data_len)
891 {
892   vlib_buffer_t *l = *last;
893   u32 n_buffer_bytes =
894     vlib_buffer_free_list_buffer_size (vm, free_list_index);
895   u16 copied = 0;
896   ASSERT (n_buffer_bytes >= l->current_length + l->current_data);
897   while (data_len)
898     {
899       u16 max = n_buffer_bytes - l->current_length - l->current_data;
900       if (max == 0)
901         {
902           if (1 !=
903               vlib_buffer_alloc_from_free_list (vm, &l->next_buffer, 1,
904                                                 free_list_index))
905             return copied;
906           *last = l = vlib_buffer_chain_buffer (vm, first, l, l->next_buffer);
907           max = n_buffer_bytes - l->current_length - l->current_data;
908         }
909
910       u16 len = (data_len > max) ? max : data_len;
911       clib_memcpy (vlib_buffer_get_current (l) + l->current_length,
912                    data + copied, len);
913       vlib_buffer_chain_increase_length (first, l, len);
914       data_len -= len;
915       copied += len;
916     }
917   return copied;
918 }
919
920
921 static u8 *
922 format_vlib_buffer_free_list (u8 * s, va_list * va)
923 {
924   vlib_buffer_free_list_t *f = va_arg (*va, vlib_buffer_free_list_t *);
925   u32 threadnum = va_arg (*va, u32);
926   uword bytes_alloc, bytes_free, n_free, size;
927
928   if (!f)
929     return format (s, "%=7s%=30s%=12s%=12s%=12s%=12s%=12s%=12s",
930                    "Thread", "Name", "Index", "Size", "Alloc", "Free",
931                    "#Alloc", "#Free");
932
933   size = sizeof (vlib_buffer_t) + f->n_data_bytes;
934   n_free = vec_len (f->buffers);
935   bytes_alloc = size * f->n_alloc;
936   bytes_free = size * n_free;
937
938   s = format (s, "%7d%30s%12d%12d%=12U%=12U%=12d%=12d", threadnum,
939               f->name, f->index, f->n_data_bytes,
940               format_memory_size, bytes_alloc,
941               format_memory_size, bytes_free, f->n_alloc, n_free);
942
943   return s;
944 }
945
946 static clib_error_t *
947 show_buffers (vlib_main_t * vm,
948               unformat_input_t * input, vlib_cli_command_t * cmd)
949 {
950   vlib_buffer_main_t *bm;
951   vlib_buffer_free_list_t *f;
952   vlib_main_t *curr_vm;
953   u32 vm_index = 0;
954
955   vlib_cli_output (vm, "%U", format_vlib_buffer_free_list, 0, 0);
956
957   do
958     {
959       curr_vm = vec_len (vlib_mains) ? vlib_mains[vm_index] : vm;
960       bm = curr_vm->buffer_main;
961
962     /* *INDENT-OFF* */
963     pool_foreach (f, bm->buffer_free_list_pool, ({
964       vlib_cli_output (vm, "%U", format_vlib_buffer_free_list, f, vm_index);
965     }));
966     /* *INDENT-ON* */
967
968       vm_index++;
969     }
970   while (vm_index < vec_len (vlib_mains));
971
972   return 0;
973 }
974
975 /* *INDENT-OFF* */
976 VLIB_CLI_COMMAND (show_buffers_command, static) = {
977   .path = "show buffers",
978   .short_help = "Show packet buffer allocation",
979   .function = show_buffers,
980 };
981 /* *INDENT-ON* */
982
983 void
984 vlib_buffer_cb_init (struct vlib_main_t *vm)
985 {
986   vlib_buffer_main_t *bm = vm->buffer_main;
987   bm->cb.vlib_buffer_alloc_cb = &vlib_buffer_alloc_internal;
988   bm->cb.vlib_buffer_alloc_from_free_list_cb =
989     &vlib_buffer_alloc_from_free_list_internal;
990   bm->cb.vlib_buffer_free_cb = &vlib_buffer_free_internal;
991   bm->cb.vlib_buffer_free_no_next_cb = &vlib_buffer_free_no_next_internal;
992   bm->cb.vlib_buffer_delete_free_list_cb =
993     &vlib_buffer_delete_free_list_internal;
994   bm->extern_buffer_mgmt = 0;
995 }
996
997 int
998 vlib_buffer_cb_register (struct vlib_main_t *vm, vlib_buffer_callbacks_t * cb)
999 {
1000   vlib_buffer_main_t *bm = vm->buffer_main;
1001   if (bm->extern_buffer_mgmt)
1002     return -1;
1003
1004 #define _(x) bm->cb.x = cb->x
1005   _(vlib_buffer_alloc_cb);
1006   _(vlib_buffer_alloc_from_free_list_cb);
1007   _(vlib_buffer_free_cb);
1008   _(vlib_buffer_free_no_next_cb);
1009   _(vlib_buffer_delete_free_list_cb);
1010 #undef _
1011   bm->extern_buffer_mgmt = 1;
1012   return 0;
1013 }
1014
1015 /** @endcond */
1016 /*
1017  * fd.io coding-style-patch-verification: ON
1018  *
1019  * Local Variables:
1020  * eval: (c-set-style "gnu")
1021  * End:
1022  */