337fca9ade720f88a7ba60ebcc0798645cf5b74a
[vpp.git] / vlib / vlib / dpdk_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 #include <rte_config.h>
41
42 #include <rte_common.h>
43 #include <rte_log.h>
44 #include <rte_memory.h>
45 #include <rte_memzone.h>
46 #include <rte_tailq.h>
47 #include <rte_eal.h>
48 #include <rte_per_lcore.h>
49 #include <rte_launch.h>
50 #include <rte_atomic.h>
51 #include <rte_cycles.h>
52 #include <rte_prefetch.h>
53 #include <rte_lcore.h>
54 #include <rte_per_lcore.h>
55 #include <rte_branch_prediction.h>
56 #include <rte_interrupts.h>
57 #include <rte_pci.h>
58 #include <rte_random.h>
59 #include <rte_debug.h>
60 #include <rte_ether.h>
61 #include <rte_ethdev.h>
62 #include <rte_ring.h>
63 #include <rte_mempool.h>
64 #include <rte_mbuf.h>
65 #include <rte_version.h>
66
67 #include <vlib/vlib.h>
68
69 #pragma weak rte_mem_virt2phy
70 #pragma weak rte_eal_has_hugepages
71 #pragma weak rte_socket_id
72 #pragma weak rte_pktmbuf_pool_create
73
74 uword
75 vlib_buffer_length_in_chain_slow_path (vlib_main_t * vm,
76                                        vlib_buffer_t * b_first)
77 {
78   vlib_buffer_t *b = b_first;
79   uword l_first = b_first->current_length;
80   uword l = 0;
81   while (b->flags & VLIB_BUFFER_NEXT_PRESENT)
82     {
83       b = vlib_get_buffer (vm, b->next_buffer);
84       l += b->current_length;
85     }
86   b_first->total_length_not_including_first_buffer = l;
87   b_first->flags |= VLIB_BUFFER_TOTAL_LENGTH_VALID;
88   return l + l_first;
89 }
90
91 u8 *
92 format_vlib_buffer (u8 * s, va_list * args)
93 {
94   vlib_buffer_t *b = va_arg (*args, vlib_buffer_t *);
95   uword indent = format_get_indent (s);
96
97   s = format (s, "current data %d, length %d, free-list %d",
98               b->current_data, b->current_length, b->free_list_index);
99
100   if (b->flags & VLIB_BUFFER_TOTAL_LENGTH_VALID)
101     s = format (s, ", totlen-nifb %d",
102                 b->total_length_not_including_first_buffer);
103
104   if (b->flags & VLIB_BUFFER_IS_TRACED)
105     s = format (s, ", trace 0x%x", b->trace_index);
106
107   while (b->flags & VLIB_BUFFER_NEXT_PRESENT)
108     {
109       vlib_main_t *vm = vlib_get_main ();
110       u32 next_buffer = b->next_buffer;
111       b = vlib_get_buffer (vm, next_buffer);
112
113       s = format (s, "\n%Unext-buffer 0x%x, segment length %d",
114                   format_white_space, indent, next_buffer, b->current_length);
115     }
116
117
118   return s;
119 }
120
121 u8 *
122 format_vlib_buffer_and_data (u8 * s, va_list * args)
123 {
124   vlib_buffer_t *b = va_arg (*args, vlib_buffer_t *);
125
126   s = format (s, "%U, %U",
127               format_vlib_buffer, b,
128               format_hex_bytes, vlib_buffer_get_current (b), 64);
129
130   return s;
131 }
132
133 u8 *
134 format_vlib_buffer_contents (u8 * s, va_list * va)
135 {
136   vlib_main_t *vm = va_arg (*va, vlib_main_t *);
137   vlib_buffer_t *b = va_arg (*va, vlib_buffer_t *);
138
139   while (1)
140     {
141       vec_add (s, vlib_buffer_get_current (b), b->current_length);
142       if (!(b->flags & VLIB_BUFFER_NEXT_PRESENT))
143         break;
144       b = vlib_get_buffer (vm, b->next_buffer);
145     }
146
147   return s;
148 }
149
150 vlib_main_t **vlib_mains;
151
152 /* Aligned copy routine. */
153 void
154 vlib_aligned_memcpy (void *_dst, void *_src, int n_bytes)
155 {
156   vlib_copy_unit_t *dst = _dst;
157   vlib_copy_unit_t *src = _src;
158
159   /* Arguments must be naturally aligned. */
160   ASSERT (pointer_to_uword (dst) % sizeof (dst[0]) == 0);
161   ASSERT (pointer_to_uword (src) % sizeof (src[0]) == 0);
162   ASSERT (n_bytes % sizeof (dst[0]) == 0);
163
164   if (4 * sizeof (dst[0]) == CLIB_CACHE_LINE_BYTES)
165     {
166       CLIB_PREFETCH (dst + 0, 4 * sizeof (dst[0]), WRITE);
167       CLIB_PREFETCH (src + 0, 4 * sizeof (src[0]), READ);
168
169       while (n_bytes >= 4 * sizeof (dst[0]))
170         {
171           dst += 4;
172           src += 4;
173           n_bytes -= 4 * sizeof (dst[0]);
174           CLIB_PREFETCH (dst, 4 * sizeof (dst[0]), WRITE);
175           CLIB_PREFETCH (src, 4 * sizeof (src[0]), READ);
176           dst[-4] = src[-4];
177           dst[-3] = src[-3];
178           dst[-2] = src[-2];
179           dst[-1] = src[-1];
180         }
181     }
182   else if (8 * sizeof (dst[0]) == CLIB_CACHE_LINE_BYTES)
183     {
184       CLIB_PREFETCH (dst + 0, 8 * sizeof (dst[0]), WRITE);
185       CLIB_PREFETCH (src + 0, 8 * sizeof (src[0]), READ);
186
187       while (n_bytes >= 8 * sizeof (dst[0]))
188         {
189           dst += 8;
190           src += 8;
191           n_bytes -= 8 * sizeof (dst[0]);
192           CLIB_PREFETCH (dst, 8 * sizeof (dst[0]), WRITE);
193           CLIB_PREFETCH (src, 8 * sizeof (src[0]), READ);
194           dst[-8] = src[-8];
195           dst[-7] = src[-7];
196           dst[-6] = src[-6];
197           dst[-5] = src[-5];
198           dst[-4] = src[-4];
199           dst[-3] = src[-3];
200           dst[-2] = src[-2];
201           dst[-1] = src[-1];
202         }
203     }
204   else
205     /* Cache line size unknown: fall back to slow version. */ ;
206
207   while (n_bytes > 0)
208     {
209       *dst++ = *src++;
210       n_bytes -= 1 * sizeof (dst[0]);
211     }
212 }
213
214 #define BUFFERS_PER_COPY (sizeof (vlib_copy_unit_t) / sizeof (u32))
215
216 /* Make sure we have at least given number of unaligned buffers. */
217 static void
218 fill_unaligned (vlib_main_t * vm,
219                 vlib_buffer_free_list_t * free_list,
220                 uword n_unaligned_buffers)
221 {
222   word la = vec_len (free_list->aligned_buffers);
223   word lu = vec_len (free_list->unaligned_buffers);
224
225   /* Aligned come in aligned copy-sized chunks. */
226   ASSERT (la % BUFFERS_PER_COPY == 0);
227
228   ASSERT (la >= n_unaligned_buffers);
229
230   while (lu < n_unaligned_buffers)
231     {
232       /* Copy 4 buffers from end of aligned vector to unaligned vector. */
233       vec_add (free_list->unaligned_buffers,
234                free_list->aligned_buffers + la - BUFFERS_PER_COPY,
235                BUFFERS_PER_COPY);
236       la -= BUFFERS_PER_COPY;
237       lu += BUFFERS_PER_COPY;
238     }
239   _vec_len (free_list->aligned_buffers) = la;
240 }
241
242 /* After free aligned buffers may not contain even sized chunks. */
243 static void
244 trim_aligned (vlib_buffer_free_list_t * f)
245 {
246   uword l, n_trim;
247
248   /* Add unaligned to aligned before trim. */
249   l = vec_len (f->unaligned_buffers);
250   if (l > 0)
251     {
252       vec_add_aligned (f->aligned_buffers, f->unaligned_buffers, l,
253                        /* align */ sizeof (vlib_copy_unit_t));
254
255       _vec_len (f->unaligned_buffers) = 0;
256     }
257
258   /* Remove unaligned buffers from end of aligned vector and save for next trim. */
259   l = vec_len (f->aligned_buffers);
260   n_trim = l % BUFFERS_PER_COPY;
261   if (n_trim)
262     {
263       /* Trim aligned -> unaligned. */
264       vec_add (f->unaligned_buffers, f->aligned_buffers + l - n_trim, n_trim);
265
266       /* Remove from aligned. */
267       _vec_len (f->aligned_buffers) = l - n_trim;
268     }
269 }
270
271 static void
272 merge_free_lists (vlib_buffer_free_list_t * dst,
273                   vlib_buffer_free_list_t * src)
274 {
275   uword l;
276   u32 *d;
277
278   trim_aligned (src);
279   trim_aligned (dst);
280
281   l = vec_len (src->aligned_buffers);
282   if (l > 0)
283     {
284       vec_add2_aligned (dst->aligned_buffers, d, l,
285                         /* align */ sizeof (vlib_copy_unit_t));
286       vlib_aligned_memcpy (d, src->aligned_buffers, l * sizeof (d[0]));
287       vec_free (src->aligned_buffers);
288     }
289
290   l = vec_len (src->unaligned_buffers);
291   if (l > 0)
292     {
293       vec_add (dst->unaligned_buffers, src->unaligned_buffers, l);
294       vec_free (src->unaligned_buffers);
295     }
296 }
297
298 always_inline u32
299 vlib_buffer_get_free_list_with_size (vlib_main_t * vm, u32 size)
300 {
301   vlib_buffer_main_t *bm = vm->buffer_main;
302
303   size = vlib_buffer_round_size (size);
304   uword *p = hash_get (bm->free_list_by_size, size);
305   return p ? p[0] : ~0;
306 }
307
308 /* Add buffer free list. */
309 static u32
310 vlib_buffer_create_free_list_helper (vlib_main_t * vm,
311                                      u32 n_data_bytes,
312                                      u32 is_public, u32 is_default, u8 * name)
313 {
314   vlib_buffer_main_t *bm = vm->buffer_main;
315   vlib_buffer_free_list_t *f;
316
317   if (!is_default && pool_elts (bm->buffer_free_list_pool) == 0)
318     {
319       u32 default_free_free_list_index;
320
321       /* *INDENT-OFF* */
322       default_free_free_list_index =
323         vlib_buffer_create_free_list_helper
324         (vm,
325          /* default buffer size */ VLIB_BUFFER_DEFAULT_FREE_LIST_BYTES,
326          /* is_public */ 1,
327          /* is_default */ 1,
328          (u8 *) "default");
329       /* *INDENT-ON* */
330       ASSERT (default_free_free_list_index ==
331               VLIB_BUFFER_DEFAULT_FREE_LIST_INDEX);
332
333       if (n_data_bytes == VLIB_BUFFER_DEFAULT_FREE_LIST_BYTES && is_public)
334         return default_free_free_list_index;
335     }
336
337   pool_get_aligned (bm->buffer_free_list_pool, f, CLIB_CACHE_LINE_BYTES);
338
339   memset (f, 0, sizeof (f[0]));
340   f->index = f - bm->buffer_free_list_pool;
341   f->n_data_bytes = vlib_buffer_round_size (n_data_bytes);
342   f->min_n_buffers_each_physmem_alloc = 16;
343   f->name = clib_mem_is_heap_object (name) ? name : format (0, "%s", name);
344
345   /* Setup free buffer template. */
346   f->buffer_init_template.free_list_index = f->index;
347
348   if (is_public)
349     {
350       uword *p = hash_get (bm->free_list_by_size, f->n_data_bytes);
351       if (!p)
352         hash_set (bm->free_list_by_size, f->n_data_bytes, f->index);
353     }
354
355   return f->index;
356 }
357
358 u32
359 vlib_buffer_create_free_list (vlib_main_t * vm, u32 n_data_bytes,
360                               char *fmt, ...)
361 {
362   va_list va;
363   u8 *name;
364
365   va_start (va, fmt);
366   name = va_format (0, fmt, &va);
367   va_end (va);
368
369   return vlib_buffer_create_free_list_helper (vm, n_data_bytes,
370                                               /* is_public */ 0,
371                                               /* is_default */ 0,
372                                               name);
373 }
374
375 u32
376 vlib_buffer_get_or_create_free_list (vlib_main_t * vm, u32 n_data_bytes,
377                                      char *fmt, ...)
378 {
379   u32 i = vlib_buffer_get_free_list_with_size (vm, n_data_bytes);
380
381   if (i == ~0)
382     {
383       va_list va;
384       u8 *name;
385
386       va_start (va, fmt);
387       name = va_format (0, fmt, &va);
388       va_end (va);
389
390       i = vlib_buffer_create_free_list_helper (vm, n_data_bytes,
391                                                /* is_public */ 1,
392                                                /* is_default */ 0,
393                                                name);
394     }
395
396   return i;
397 }
398
399 static void
400 del_free_list (vlib_main_t * vm, vlib_buffer_free_list_t * f)
401 {
402   u32 i;
403   struct rte_mbuf *mb;
404   vlib_buffer_t *b;
405
406   for (i = 0; i < vec_len (f->unaligned_buffers); i++)
407     {
408       b = vlib_get_buffer (vm, f->unaligned_buffers[i]);
409       mb = rte_mbuf_from_vlib_buffer (b);
410       ASSERT (rte_mbuf_refcnt_read (mb) == 1);
411       rte_pktmbuf_free (mb);
412     }
413   for (i = 0; i < vec_len (f->aligned_buffers); i++)
414     {
415       b = vlib_get_buffer (vm, f->aligned_buffers[i]);
416       mb = rte_mbuf_from_vlib_buffer (b);
417       ASSERT (rte_mbuf_refcnt_read (mb) == 1);
418       rte_pktmbuf_free (mb);
419     }
420   vec_free (f->name);
421   vec_free (f->unaligned_buffers);
422   vec_free (f->aligned_buffers);
423 }
424
425 /* Add buffer free list. */
426 void
427 vlib_buffer_delete_free_list (vlib_main_t * vm, u32 free_list_index)
428 {
429   vlib_buffer_main_t *bm = vm->buffer_main;
430   vlib_buffer_free_list_t *f;
431   u32 merge_index;
432
433   f = vlib_buffer_get_free_list (vm, free_list_index);
434
435   merge_index = vlib_buffer_get_free_list_with_size (vm, f->n_data_bytes);
436   if (merge_index != ~0 && merge_index != free_list_index)
437     {
438       merge_free_lists (pool_elt_at_index (bm->buffer_free_list_pool,
439                                            merge_index), f);
440     }
441
442   del_free_list (vm, f);
443
444   /* Poison it. */
445   memset (f, 0xab, sizeof (f[0]));
446
447   pool_put (bm->buffer_free_list_pool, f);
448 }
449
450 /* Make sure free list has at least given number of free buffers. */
451 static uword
452 fill_free_list (vlib_main_t * vm,
453                 vlib_buffer_free_list_t * fl, uword min_free_buffers)
454 {
455   vlib_buffer_t *b;
456   int n, i;
457   u32 bi;
458   u32 n_remaining = 0, n_alloc = 0;
459   unsigned socket_id = rte_socket_id ? rte_socket_id () : 0;
460   struct rte_mempool *rmp = vm->buffer_main->pktmbuf_pools[socket_id];
461   struct rte_mbuf *mb;
462
463   /* Too early? */
464   if (PREDICT_FALSE (rmp == 0))
465     return 0;
466
467   trim_aligned (fl);
468
469   /* Already have enough free buffers on free list? */
470   n = min_free_buffers - vec_len (fl->aligned_buffers);
471   if (n <= 0)
472     return min_free_buffers;
473
474   /* Always allocate round number of buffers. */
475   n = round_pow2 (n, BUFFERS_PER_COPY);
476
477   /* Always allocate new buffers in reasonably large sized chunks. */
478   n = clib_max (n, fl->min_n_buffers_each_physmem_alloc);
479
480   vec_validate (vm->mbuf_alloc_list, n - 1);
481
482   if (rte_mempool_get_bulk (rmp, vm->mbuf_alloc_list, n) < 0)
483     return 0;
484
485   _vec_len (vm->mbuf_alloc_list) = n;
486
487   for (i = 0; i < n; i++)
488     {
489       mb = vm->mbuf_alloc_list[i];
490
491       ASSERT (rte_mbuf_refcnt_read (mb) == 0);
492       rte_mbuf_refcnt_set (mb, 1);
493       mb->next = NULL;
494       mb->data_off = RTE_PKTMBUF_HEADROOM;
495       mb->nb_segs = 1;
496
497       b = vlib_buffer_from_rte_mbuf (mb);
498       bi = vlib_get_buffer_index (vm, b);
499
500       vec_add1_aligned (fl->aligned_buffers, bi, sizeof (vlib_copy_unit_t));
501       n_alloc++;
502       n_remaining--;
503
504       vlib_buffer_init_for_free_list (b, fl);
505
506       if (fl->buffer_init_function)
507         fl->buffer_init_function (vm, fl, &bi, 1);
508     }
509
510   fl->n_alloc += n;
511
512   return n;
513 }
514
515 always_inline uword
516 copy_alignment (u32 * x)
517 {
518   return (pointer_to_uword (x) / sizeof (x[0])) % BUFFERS_PER_COPY;
519 }
520
521 static u32
522 alloc_from_free_list (vlib_main_t * vm,
523                       vlib_buffer_free_list_t * free_list,
524                       u32 * alloc_buffers, u32 n_alloc_buffers)
525 {
526   u32 *dst, *u_src;
527   uword u_len, n_left;
528   uword n_unaligned_start, n_unaligned_end, n_filled;
529
530   n_left = n_alloc_buffers;
531   dst = alloc_buffers;
532   n_unaligned_start = ((BUFFERS_PER_COPY - copy_alignment (dst))
533                        & (BUFFERS_PER_COPY - 1));
534
535   n_filled = fill_free_list (vm, free_list, n_alloc_buffers);
536   if (n_filled == 0)
537     return 0;
538
539   n_left = n_filled < n_left ? n_filled : n_left;
540   n_alloc_buffers = n_left;
541
542   if (n_unaligned_start >= n_left)
543     {
544       n_unaligned_start = n_left;
545       n_unaligned_end = 0;
546     }
547   else
548     n_unaligned_end = copy_alignment (dst + n_alloc_buffers);
549
550   fill_unaligned (vm, free_list, n_unaligned_start + n_unaligned_end);
551
552   u_len = vec_len (free_list->unaligned_buffers);
553   u_src = free_list->unaligned_buffers + u_len - 1;
554
555   if (n_unaligned_start)
556     {
557       uword n_copy = n_unaligned_start;
558       if (n_copy > n_left)
559         n_copy = n_left;
560       n_left -= n_copy;
561
562       while (n_copy > 0)
563         {
564           *dst++ = *u_src--;
565           n_copy--;
566           u_len--;
567         }
568
569       /* Now dst should be aligned. */
570       if (n_left > 0)
571         ASSERT (pointer_to_uword (dst) % sizeof (vlib_copy_unit_t) == 0);
572     }
573
574   /* Aligned copy. */
575   {
576     vlib_copy_unit_t *d, *s;
577     uword n_copy;
578
579     if (vec_len (free_list->aligned_buffers) <
580         ((n_left / BUFFERS_PER_COPY) * BUFFERS_PER_COPY))
581       abort ();
582
583     n_copy = n_left / BUFFERS_PER_COPY;
584     n_left = n_left % BUFFERS_PER_COPY;
585
586     /* Remove buffers from aligned free list. */
587     _vec_len (free_list->aligned_buffers) -= n_copy * BUFFERS_PER_COPY;
588
589     s = (vlib_copy_unit_t *) vec_end (free_list->aligned_buffers);
590     d = (vlib_copy_unit_t *) dst;
591
592     /* Fast path loop. */
593     while (n_copy >= 4)
594       {
595         d[0] = s[0];
596         d[1] = s[1];
597         d[2] = s[2];
598         d[3] = s[3];
599         n_copy -= 4;
600         s += 4;
601         d += 4;
602       }
603
604     while (n_copy >= 1)
605       {
606         d[0] = s[0];
607         n_copy -= 1;
608         s += 1;
609         d += 1;
610       }
611
612     dst = (void *) d;
613   }
614
615   /* Unaligned copy. */
616   ASSERT (n_unaligned_end == n_left);
617   while (n_left > 0)
618     {
619       *dst++ = *u_src--;
620       n_left--;
621       u_len--;
622     }
623
624   if (!free_list->unaligned_buffers)
625     ASSERT (u_len == 0);
626   else
627     _vec_len (free_list->unaligned_buffers) = u_len;
628
629   return n_alloc_buffers;
630 }
631
632 /* Allocate a given number of buffers into given array.
633    Returns number actually allocated which will be either zero or
634    number requested. */
635 u32
636 vlib_buffer_alloc (vlib_main_t * vm, u32 * buffers, u32 n_buffers)
637 {
638   vlib_buffer_main_t *bm = vm->buffer_main;
639
640   return alloc_from_free_list
641     (vm,
642      pool_elt_at_index (bm->buffer_free_list_pool,
643                         VLIB_BUFFER_DEFAULT_FREE_LIST_INDEX),
644      buffers, n_buffers);
645 }
646
647 u32
648 vlib_buffer_alloc_from_free_list (vlib_main_t * vm,
649                                   u32 * buffers,
650                                   u32 n_buffers, u32 free_list_index)
651 {
652   vlib_buffer_main_t *bm = vm->buffer_main;
653   vlib_buffer_free_list_t *f;
654   f = pool_elt_at_index (bm->buffer_free_list_pool, free_list_index);
655   return alloc_from_free_list (vm, f, buffers, n_buffers);
656 }
657
658 always_inline void
659 add_buffer_to_free_list (vlib_main_t * vm,
660                          vlib_buffer_free_list_t * f,
661                          u32 buffer_index, u8 do_init)
662 {
663   vlib_buffer_t *b;
664   b = vlib_get_buffer (vm, buffer_index);
665   if (PREDICT_TRUE (do_init))
666     vlib_buffer_init_for_free_list (b, f);
667   vec_add1_aligned (f->aligned_buffers, buffer_index,
668                     sizeof (vlib_copy_unit_t));
669 }
670
671 always_inline vlib_buffer_free_list_t *
672 buffer_get_free_list (vlib_main_t * vm, vlib_buffer_t * b, u32 * index)
673 {
674   vlib_buffer_main_t *bm = vm->buffer_main;
675   u32 i;
676
677   *index = i = b->free_list_index;
678   return pool_elt_at_index (bm->buffer_free_list_pool, i);
679 }
680
681 void *
682 vlib_set_buffer_free_callback (vlib_main_t * vm, void *fp)
683 {
684   vlib_buffer_main_t *bm = vm->buffer_main;
685   void *rv = bm->buffer_free_callback;
686
687   bm->buffer_free_callback = fp;
688   return rv;
689 }
690
691 static_always_inline void
692 vlib_buffer_free_inline (vlib_main_t * vm,
693                          u32 * buffers, u32 n_buffers, u32 follow_buffer_next)
694 {
695   vlib_buffer_main_t *bm = vm->buffer_main;
696   vlib_buffer_free_list_t *fl;
697   u32 fi;
698   int i;
699   u32 (*cb) (vlib_main_t * vm, u32 * buffers, u32 n_buffers,
700              u32 follow_buffer_next);
701
702   cb = bm->buffer_free_callback;
703
704   if (PREDICT_FALSE (cb != 0))
705     n_buffers = (*cb) (vm, buffers, n_buffers, follow_buffer_next);
706
707   if (!n_buffers)
708     return;
709
710   for (i = 0; i < n_buffers; i++)
711     {
712       vlib_buffer_t *b;
713       struct rte_mbuf *mb;
714
715       b = vlib_get_buffer (vm, buffers[i]);
716
717       fl = buffer_get_free_list (vm, b, &fi);
718
719       /* The only current use of this callback: multicast recycle */
720       if (PREDICT_FALSE (fl->buffers_added_to_freelist_function != 0))
721         {
722           int j;
723
724           add_buffer_to_free_list
725             (vm, fl, buffers[i], (b->flags & VLIB_BUFFER_RECYCLE) == 0);
726
727           for (j = 0; j < vec_len (bm->announce_list); j++)
728             {
729               if (fl == bm->announce_list[j])
730                 goto already_announced;
731             }
732           vec_add1 (bm->announce_list, fl);
733         already_announced:
734           ;
735         }
736       else
737         {
738           if (PREDICT_TRUE ((b->flags & VLIB_BUFFER_RECYCLE) == 0))
739             {
740               mb = rte_mbuf_from_vlib_buffer (b);
741               ASSERT (rte_mbuf_refcnt_read (mb) == 1);
742               rte_pktmbuf_free (mb);
743             }
744         }
745     }
746   if (vec_len (bm->announce_list))
747     {
748       vlib_buffer_free_list_t *fl;
749       for (i = 0; i < vec_len (bm->announce_list); i++)
750         {
751           fl = bm->announce_list[i];
752           fl->buffers_added_to_freelist_function (vm, fl);
753         }
754       _vec_len (bm->announce_list) = 0;
755     }
756 }
757
758 void
759 vlib_buffer_free (vlib_main_t * vm, u32 * buffers, u32 n_buffers)
760 {
761   vlib_buffer_free_inline (vm, buffers, n_buffers,      /* follow_buffer_next */
762                            1);
763 }
764
765 void
766 vlib_buffer_free_no_next (vlib_main_t * vm, u32 * buffers, u32 n_buffers)
767 {
768   vlib_buffer_free_inline (vm, buffers, n_buffers,      /* follow_buffer_next */
769                            0);
770 }
771
772 /* Copy template packet data into buffers as they are allocated. */
773 __attribute__ ((unused))
774      static void
775        vlib_packet_template_buffer_init (vlib_main_t * vm,
776                                          vlib_buffer_free_list_t * fl,
777                                          u32 * buffers, u32 n_buffers)
778 {
779   vlib_packet_template_t *t =
780     uword_to_pointer (fl->buffer_init_function_opaque,
781                       vlib_packet_template_t *);
782   uword i;
783
784   for (i = 0; i < n_buffers; i++)
785     {
786       vlib_buffer_t *b = vlib_get_buffer (vm, buffers[i]);
787       ASSERT (b->current_length == vec_len (t->packet_data));
788       clib_memcpy (vlib_buffer_get_current (b), t->packet_data,
789                    b->current_length);
790     }
791 }
792
793 void
794 vlib_packet_template_init (vlib_main_t * vm,
795                            vlib_packet_template_t * t,
796                            void *packet_data,
797                            uword n_packet_data_bytes,
798                            uword min_n_buffers_each_physmem_alloc,
799                            char *fmt, ...)
800 {
801   va_list va;
802   __attribute__ ((unused)) u8 *name;
803
804   va_start (va, fmt);
805   name = va_format (0, fmt, &va);
806   va_end (va);
807
808   vlib_worker_thread_barrier_sync (vm);
809   memset (t, 0, sizeof (t[0]));
810
811   vec_add (t->packet_data, packet_data, n_packet_data_bytes);
812
813   vlib_worker_thread_barrier_release (vm);
814 }
815
816 void *
817 vlib_packet_template_get_packet (vlib_main_t * vm,
818                                  vlib_packet_template_t * t, u32 * bi_result)
819 {
820   u32 bi;
821   vlib_buffer_t *b;
822
823   if (vlib_buffer_alloc (vm, &bi, 1) != 1)
824     return 0;
825
826   *bi_result = bi;
827
828   b = vlib_get_buffer (vm, bi);
829   clib_memcpy (vlib_buffer_get_current (b),
830                t->packet_data, vec_len (t->packet_data));
831   b->current_length = vec_len (t->packet_data);
832
833   /* Fix up mbuf header length fields */
834   struct rte_mbuf *mb;
835   mb = rte_mbuf_from_vlib_buffer (b);
836   mb->data_len = b->current_length;
837   mb->pkt_len = b->current_length;
838
839   return b->data;
840 }
841
842 /* Append given data to end of buffer, possibly allocating new buffers. */
843 u32
844 vlib_buffer_add_data (vlib_main_t * vm,
845                       u32 free_list_index,
846                       u32 buffer_index, void *data, u32 n_data_bytes)
847 {
848   u32 n_buffer_bytes, n_left, n_left_this_buffer, bi;
849   vlib_buffer_t *b;
850   void *d;
851
852   bi = buffer_index;
853   if (bi == 0
854       && 1 != vlib_buffer_alloc_from_free_list (vm, &bi, 1, free_list_index))
855     goto out_of_buffers;
856
857   d = data;
858   n_left = n_data_bytes;
859   n_buffer_bytes = vlib_buffer_free_list_buffer_size (vm, free_list_index);
860
861   b = vlib_get_buffer (vm, bi);
862   b->flags &= ~VLIB_BUFFER_TOTAL_LENGTH_VALID;
863
864   /* Get to the end of the chain before we try to append data... */
865   while (b->flags & VLIB_BUFFER_NEXT_PRESENT)
866     b = vlib_get_buffer (vm, b->next_buffer);
867
868   while (1)
869     {
870       u32 n;
871
872       ASSERT (n_buffer_bytes >= b->current_length);
873       n_left_this_buffer =
874         n_buffer_bytes - (b->current_data + b->current_length);
875       n = clib_min (n_left_this_buffer, n_left);
876       clib_memcpy (vlib_buffer_get_current (b) + b->current_length, d, n);
877       b->current_length += n;
878       n_left -= n;
879       if (n_left == 0)
880         break;
881
882       d += n;
883       if (1 !=
884           vlib_buffer_alloc_from_free_list (vm, &b->next_buffer, 1,
885                                             free_list_index))
886         goto out_of_buffers;
887
888       b->flags |= VLIB_BUFFER_NEXT_PRESENT;
889
890       b = vlib_get_buffer (vm, b->next_buffer);
891     }
892
893   return bi;
894
895 out_of_buffers:
896   clib_error ("out of buffers");
897   return bi;
898 }
899
900 u16
901 vlib_buffer_chain_append_data_with_alloc (vlib_main_t * vm,
902                                           u32 free_list_index,
903                                           vlib_buffer_t * first,
904                                           vlib_buffer_t ** last,
905                                           void *data, u16 data_len)
906 {
907   vlib_buffer_t *l = *last;
908   u32 n_buffer_bytes =
909     vlib_buffer_free_list_buffer_size (vm, free_list_index);
910   u16 copied = 0;
911   ASSERT (n_buffer_bytes >= l->current_length + l->current_data);
912   while (data_len)
913     {
914       u16 max = n_buffer_bytes - l->current_length - l->current_data;
915       if (max == 0)
916         {
917           if (1 !=
918               vlib_buffer_alloc_from_free_list (vm, &l->next_buffer, 1,
919                                                 free_list_index))
920             return copied;
921           *last = l = vlib_buffer_chain_buffer (vm, first, l, l->next_buffer);
922           max = n_buffer_bytes - l->current_length - l->current_data;
923         }
924
925       u16 len = (data_len > max) ? max : data_len;
926       clib_memcpy (vlib_buffer_get_current (l) + l->current_length,
927                    data + copied, len);
928       vlib_buffer_chain_increase_length (first, l, len);
929       data_len -= len;
930       copied += len;
931     }
932   return copied;
933 }
934
935 /*
936  * Fills in the required rte_mbuf fields for chained buffers given a VLIB chain.
937  */
938 void
939 vlib_buffer_chain_validate (vlib_main_t * vm, vlib_buffer_t * b_first)
940 {
941   vlib_buffer_t *b = b_first, *prev = b_first;
942   struct rte_mbuf *mb_prev, *mb, *mb_first;
943
944   mb_first = rte_mbuf_from_vlib_buffer (b_first);
945
946   mb_first->pkt_len = mb_first->data_len = b_first->current_length;
947   while (b->flags & VLIB_BUFFER_NEXT_PRESENT)
948     {
949       b = vlib_get_buffer (vm, b->next_buffer);
950       mb = rte_mbuf_from_vlib_buffer (b);
951       mb_prev = rte_mbuf_from_vlib_buffer (prev);
952       mb_first->nb_segs++;
953       mb_first->pkt_len += b->current_length;
954       mb_prev->next = mb;
955       mb->data_len = b->current_length;
956       prev = b;
957     }
958 }
959
960 clib_error_t *
961 vlib_buffer_pool_create (vlib_main_t * vm, unsigned num_mbufs,
962                          unsigned socket_id)
963 {
964   vlib_buffer_main_t *bm = vm->buffer_main;
965   vlib_physmem_main_t *vpm = &vm->physmem_main;
966   struct rte_mempool *rmp;
967 #if RTE_VERSION < RTE_VERSION_NUM(16, 7, 0, 0)
968   uword new_start, new_size;
969 #endif
970   int i;
971
972   if (!rte_pktmbuf_pool_create)
973     return clib_error_return (0, "not linked with DPDK");
974
975   vec_validate_aligned (bm->pktmbuf_pools, socket_id, CLIB_CACHE_LINE_BYTES);
976
977   /* pool already exists, nothing to do */
978   if (bm->pktmbuf_pools[socket_id])
979     return 0;
980
981   u8 *pool_name = format (0, "mbuf_pool_socket%u%c", socket_id, 0);
982
983   rmp = rte_pktmbuf_pool_create ((char *) pool_name,    /* pool name */
984                                  num_mbufs,     /* number of mbufs */
985                                  512,   /* cache size */
986                                  VLIB_BUFFER_HDR_SIZE,  /* priv size */
987                                  VLIB_BUFFER_PRE_DATA_SIZE + VLIB_BUFFER_DATA_SIZE,     /* dataroom size */
988                                  socket_id);    /* cpu socket */
989
990   if (rmp)
991     {
992 #if RTE_VERSION >= RTE_VERSION_NUM(16, 7, 0, 0)
993       {
994         uword this_pool_end;
995         uword this_pool_start;
996         uword this_pool_size;
997         uword save_vpm_start, save_vpm_end, save_vpm_size;
998         struct rte_mempool_memhdr *memhdr;
999         
1000         this_pool_start = ~0ULL;
1001         this_pool_end = 0LL;
1002
1003         STAILQ_FOREACH (memhdr, &rmp->mem_list, next) 
1004           {
1005             if (((uword)(memhdr->addr + memhdr->len)) > this_pool_end)
1006               this_pool_end = (uword)(memhdr->addr + memhdr->len);
1007             if (((uword)memhdr->addr) < this_pool_start)
1008               this_pool_start = (uword)(memhdr->addr);
1009           }
1010         ASSERT (this_pool_start < ~0ULL && this_pool_end > 0);
1011         this_pool_size = this_pool_end - this_pool_start;
1012
1013         if (CLIB_DEBUG > 1)
1014           {
1015             clib_warning ("%s: pool start %llx pool end %llx pool size %lld",
1016                           pool_name, this_pool_start, this_pool_end, 
1017                           this_pool_size);
1018             clib_warning 
1019               ("before: virtual.start %llx virtual.end %llx virtual.size %lld",
1020                vpm->virtual.start, vpm->virtual.end, vpm->virtual.size);
1021           }
1022         
1023         save_vpm_start = vpm->virtual.start;
1024         save_vpm_end = vpm->virtual.end;
1025         save_vpm_size = vpm->virtual.size;
1026
1027         if ((this_pool_start < vpm->virtual.start) || vpm->virtual.start == 0)
1028           vpm->virtual.start = this_pool_start;
1029         if (this_pool_end > vpm->virtual.end)
1030           vpm->virtual.end = this_pool_end;
1031
1032         vpm->virtual.size = vpm->virtual.end - vpm->virtual.start;
1033         
1034         if (CLIB_DEBUG > 1)
1035           {
1036             clib_warning 
1037               ("after: virtual.start %llx virtual.end %llx virtual.size %lld",
1038                vpm->virtual.start, vpm->virtual.end, vpm->virtual.size);
1039           }
1040
1041         /* check if fits into buffer index range */
1042         if ((u64) vpm->virtual.size > 
1043             ((u64) 1 << (32 + CLIB_LOG2_CACHE_LINE_BYTES)))
1044           {
1045             clib_warning ("physmem: virtual size out of range!");
1046             vpm->virtual.start = save_vpm_start;
1047             vpm->virtual.end = save_vpm_end;
1048             vpm->virtual.size = save_vpm_size;
1049             rmp = 0;
1050           }
1051       }
1052       if (rmp)
1053         {
1054           bm->pktmbuf_pools[socket_id] = rmp;
1055           vec_free(pool_name);
1056           return 0;
1057         }
1058     }
1059 #else
1060       new_start = pointer_to_uword (rmp);
1061       new_size = rmp->elt_va_end - new_start;
1062
1063       if (vpm->virtual.size > 0)
1064         {
1065           ASSERT (new_start != vpm->virtual.start);
1066           if (new_start < vpm->virtual.start)
1067             {
1068               new_size = vpm->virtual.size + vpm->virtual.start - new_start;
1069             }
1070           else
1071             {
1072               new_size += new_start - vpm->virtual.start;
1073               new_start = vpm->virtual.start;
1074             }
1075
1076           /* check if fits into buffer index range */
1077           if ((u64) new_size > ((u64) 1 << (32 + CLIB_LOG2_CACHE_LINE_BYTES)))
1078             rmp = 0;
1079         }
1080     }
1081
1082   if (rmp)
1083     {
1084       bm->pktmbuf_pools[socket_id] = rmp;
1085       vpm->virtual.start = new_start;
1086       vpm->virtual.size = new_size;
1087       vpm->virtual.end = new_start + new_size;
1088       vec_free(pool_name);
1089       return 0;
1090     }
1091 #endif
1092
1093   vec_free (pool_name);
1094
1095   /* no usable pool for this socket, try to use pool from another one */
1096   for (i = 0; i < vec_len (bm->pktmbuf_pools); i++)
1097     {
1098       if (bm->pktmbuf_pools[i])
1099         {
1100           clib_warning
1101             ("WARNING: Failed to allocate mempool for CPU socket %u. "
1102              "Threads running on socket %u will use socket %u mempool.",
1103              socket_id, socket_id, i);
1104           bm->pktmbuf_pools[socket_id] = bm->pktmbuf_pools[i];
1105           return 0;
1106         }
1107     }
1108
1109   return clib_error_return (0, "failed to allocate mempool on socket %u",
1110                             socket_id);
1111 }
1112
1113
1114 static void
1115 vlib_serialize_tx (serialize_main_header_t * m, serialize_stream_t * s)
1116 {
1117   vlib_main_t *vm;
1118   vlib_serialize_buffer_main_t *sm;
1119   uword n, n_bytes_to_write;
1120   vlib_buffer_t *last;
1121
1122   n_bytes_to_write = s->current_buffer_index;
1123   sm =
1124     uword_to_pointer (s->data_function_opaque,
1125                       vlib_serialize_buffer_main_t *);
1126   vm = sm->vlib_main;
1127
1128   ASSERT (sm->tx.max_n_data_bytes_per_chain > 0);
1129   if (serialize_stream_is_end_of_stream (s)
1130       || sm->tx.n_total_data_bytes + n_bytes_to_write >
1131       sm->tx.max_n_data_bytes_per_chain)
1132     {
1133       vlib_process_t *p = vlib_get_current_process (vm);
1134
1135       last = vlib_get_buffer (vm, sm->last_buffer);
1136       last->current_length = n_bytes_to_write;
1137
1138       vlib_set_next_frame_buffer (vm, &p->node_runtime, sm->tx.next_index,
1139                                   sm->first_buffer);
1140
1141       sm->first_buffer = sm->last_buffer = ~0;
1142       sm->tx.n_total_data_bytes = 0;
1143     }
1144
1145   else if (n_bytes_to_write == 0 && s->n_buffer_bytes == 0)
1146     {
1147       ASSERT (sm->first_buffer == ~0);
1148       ASSERT (sm->last_buffer == ~0);
1149       n =
1150         vlib_buffer_alloc_from_free_list (vm, &sm->first_buffer, 1,
1151                                           sm->tx.free_list_index);
1152       if (n != 1)
1153         serialize_error (m,
1154                          clib_error_create
1155                          ("vlib_buffer_alloc_from_free_list fails"));
1156       sm->last_buffer = sm->first_buffer;
1157       s->n_buffer_bytes =
1158         vlib_buffer_free_list_buffer_size (vm, sm->tx.free_list_index);
1159     }
1160
1161   if (n_bytes_to_write > 0)
1162     {
1163       vlib_buffer_t *prev = vlib_get_buffer (vm, sm->last_buffer);
1164       n =
1165         vlib_buffer_alloc_from_free_list (vm, &sm->last_buffer, 1,
1166                                           sm->tx.free_list_index);
1167       if (n != 1)
1168         serialize_error (m,
1169                          clib_error_create
1170                          ("vlib_buffer_alloc_from_free_list fails"));
1171       sm->tx.n_total_data_bytes += n_bytes_to_write;
1172       prev->current_length = n_bytes_to_write;
1173       prev->next_buffer = sm->last_buffer;
1174       prev->flags |= VLIB_BUFFER_NEXT_PRESENT;
1175     }
1176
1177   if (sm->last_buffer != ~0)
1178     {
1179       last = vlib_get_buffer (vm, sm->last_buffer);
1180       s->buffer = vlib_buffer_get_current (last);
1181       s->current_buffer_index = 0;
1182       ASSERT (last->current_data == s->current_buffer_index);
1183     }
1184 }
1185
1186 static void
1187 vlib_serialize_rx (serialize_main_header_t * m, serialize_stream_t * s)
1188 {
1189   vlib_main_t *vm;
1190   vlib_serialize_buffer_main_t *sm;
1191   vlib_buffer_t *last;
1192
1193   sm =
1194     uword_to_pointer (s->data_function_opaque,
1195                       vlib_serialize_buffer_main_t *);
1196   vm = sm->vlib_main;
1197
1198   if (serialize_stream_is_end_of_stream (s))
1199     return;
1200
1201   if (sm->last_buffer != ~0)
1202     {
1203       last = vlib_get_buffer (vm, sm->last_buffer);
1204
1205       if (last->flags & VLIB_BUFFER_NEXT_PRESENT)
1206         sm->last_buffer = last->next_buffer;
1207       else
1208         {
1209           vlib_buffer_free (vm, &sm->first_buffer, /* count */ 1);
1210           sm->first_buffer = sm->last_buffer = ~0;
1211         }
1212     }
1213
1214   if (sm->last_buffer == ~0)
1215     {
1216       while (clib_fifo_elts (sm->rx.buffer_fifo) == 0)
1217         {
1218           sm->rx.ready_one_time_event =
1219             vlib_process_create_one_time_event (vm, vlib_current_process (vm),
1220                                                 ~0);
1221           vlib_process_wait_for_one_time_event (vm, /* no event data */ 0,
1222                                                 sm->rx.ready_one_time_event);
1223         }
1224
1225       clib_fifo_sub1 (sm->rx.buffer_fifo, sm->first_buffer);
1226       sm->last_buffer = sm->first_buffer;
1227     }
1228
1229   ASSERT (sm->last_buffer != ~0);
1230
1231   last = vlib_get_buffer (vm, sm->last_buffer);
1232   s->current_buffer_index = 0;
1233   s->buffer = vlib_buffer_get_current (last);
1234   s->n_buffer_bytes = last->current_length;
1235 }
1236
1237 static void
1238 serialize_open_vlib_helper (serialize_main_t * m,
1239                             vlib_main_t * vm,
1240                             vlib_serialize_buffer_main_t * sm, uword is_read)
1241 {
1242   /* Initialize serialize main but save overflow buffer for re-use between calls. */
1243   {
1244     u8 *save = m->stream.overflow_buffer;
1245     memset (m, 0, sizeof (m[0]));
1246     m->stream.overflow_buffer = save;
1247     if (save)
1248       _vec_len (save) = 0;
1249   }
1250
1251   sm->first_buffer = sm->last_buffer = ~0;
1252   if (is_read)
1253     clib_fifo_reset (sm->rx.buffer_fifo);
1254   else
1255     sm->tx.n_total_data_bytes = 0;
1256   sm->vlib_main = vm;
1257   m->header.data_function = is_read ? vlib_serialize_rx : vlib_serialize_tx;
1258   m->stream.data_function_opaque = pointer_to_uword (sm);
1259 }
1260
1261 void
1262 serialize_open_vlib_buffer (serialize_main_t * m, vlib_main_t * vm,
1263                             vlib_serialize_buffer_main_t * sm)
1264 {
1265   serialize_open_vlib_helper (m, vm, sm, /* is_read */ 0);
1266 }
1267
1268 void
1269 unserialize_open_vlib_buffer (serialize_main_t * m, vlib_main_t * vm,
1270                               vlib_serialize_buffer_main_t * sm)
1271 {
1272   serialize_open_vlib_helper (m, vm, sm, /* is_read */ 1);
1273 }
1274
1275 u32
1276 serialize_close_vlib_buffer (serialize_main_t * m)
1277 {
1278   vlib_serialize_buffer_main_t *sm
1279     = uword_to_pointer (m->stream.data_function_opaque,
1280                         vlib_serialize_buffer_main_t *);
1281   vlib_buffer_t *last;
1282   serialize_stream_t *s = &m->stream;
1283
1284   last = vlib_get_buffer (sm->vlib_main, sm->last_buffer);
1285   last->current_length = s->current_buffer_index;
1286
1287   if (vec_len (s->overflow_buffer) > 0)
1288     {
1289       sm->last_buffer
1290         = vlib_buffer_add_data (sm->vlib_main, sm->tx.free_list_index,
1291                                 sm->last_buffer == ~0 ? 0 : sm->last_buffer,
1292                                 s->overflow_buffer,
1293                                 vec_len (s->overflow_buffer));
1294       _vec_len (s->overflow_buffer) = 0;
1295     }
1296
1297   return sm->first_buffer;
1298 }
1299
1300 void
1301 unserialize_close_vlib_buffer (serialize_main_t * m)
1302 {
1303   vlib_serialize_buffer_main_t *sm
1304     = uword_to_pointer (m->stream.data_function_opaque,
1305                         vlib_serialize_buffer_main_t *);
1306   if (sm->first_buffer != ~0)
1307     vlib_buffer_free_one (sm->vlib_main, sm->first_buffer);
1308   clib_fifo_reset (sm->rx.buffer_fifo);
1309   if (m->stream.overflow_buffer)
1310     _vec_len (m->stream.overflow_buffer) = 0;
1311 }
1312
1313 static u8 *
1314 format_vlib_buffer_free_list (u8 * s, va_list * va)
1315 {
1316   vlib_buffer_free_list_t *f = va_arg (*va, vlib_buffer_free_list_t *);
1317   u32 threadnum = va_arg (*va, u32);
1318   uword bytes_alloc, bytes_free, n_free, size;
1319
1320   if (!f)
1321     return format (s, "%=7s%=30s%=12s%=12s%=12s%=12s%=12s%=12s",
1322                    "Thread", "Name", "Index", "Size", "Alloc", "Free",
1323                    "#Alloc", "#Free");
1324
1325   size = sizeof (vlib_buffer_t) + f->n_data_bytes;
1326   n_free = vec_len (f->aligned_buffers) + vec_len (f->unaligned_buffers);
1327   bytes_alloc = size * f->n_alloc;
1328   bytes_free = size * n_free;
1329
1330   s = format (s, "%7d%30s%12d%12d%=12U%=12U%=12d%=12d",
1331               threadnum,
1332               f->name, f->index, f->n_data_bytes,
1333               format_memory_size, bytes_alloc,
1334               format_memory_size, bytes_free, f->n_alloc, n_free);
1335
1336   return s;
1337 }
1338
1339 static clib_error_t *
1340 show_buffers (vlib_main_t * vm,
1341               unformat_input_t * input, vlib_cli_command_t * cmd)
1342 {
1343   vlib_buffer_main_t *bm;
1344   vlib_buffer_free_list_t *f;
1345   vlib_main_t *curr_vm;
1346   u32 vm_index = 0;
1347
1348   vlib_cli_output (vm, "%U", format_vlib_buffer_free_list, 0, 0);
1349
1350   do
1351     {
1352       curr_vm = vec_len (vlib_mains) ? vlib_mains[vm_index] : vm;
1353       bm = curr_vm->buffer_main;
1354
1355     /* *INDENT-OFF* */
1356     pool_foreach (f, bm->buffer_free_list_pool, ({
1357       vlib_cli_output (vm, "%U", format_vlib_buffer_free_list, f, vm_index);
1358     }));
1359     /* *INDENT-ON* */
1360
1361       vm_index++;
1362     }
1363   while (vm_index < vec_len (vlib_mains));
1364
1365   return 0;
1366 }
1367
1368 /* *INDENT-OFF* */
1369 VLIB_CLI_COMMAND (show_buffers_command, static) = {
1370   .path = "show buffers",
1371   .short_help = "Show packet buffer allocation",
1372   .function = show_buffers,
1373 };
1374 /* *INDENT-ON* */
1375
1376 #if CLIB_DEBUG > 0
1377
1378 u32 *vlib_buffer_state_validation_lock;
1379 uword *vlib_buffer_state_validation_hash;
1380 void *vlib_buffer_state_heap;
1381
1382 static clib_error_t *
1383 buffer_state_validation_init (vlib_main_t * vm)
1384 {
1385   void *oldheap;
1386
1387   vlib_buffer_state_heap = mheap_alloc (0, 10 << 20);
1388
1389   oldheap = clib_mem_set_heap (vlib_buffer_state_heap);
1390
1391   vlib_buffer_state_validation_hash = hash_create (0, sizeof (uword));
1392   vec_validate_aligned (vlib_buffer_state_validation_lock, 0,
1393                         CLIB_CACHE_LINE_BYTES);
1394   clib_mem_set_heap (oldheap);
1395   return 0;
1396 }
1397
1398 VLIB_INIT_FUNCTION (buffer_state_validation_init);
1399 #endif
1400
1401 /*
1402  * fd.io coding-style-patch-verification: ON
1403  *
1404  * Local Variables:
1405  * eval: (c-set-style "gnu")
1406  * End:
1407  */