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