vlib: add buffer and thread callbacks
[vpp.git] / src / vlib / buffer.c
1 /*
2  * Copyright (c) 2015 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 /*
16  * buffer.c: allocate/free network buffers.
17  *
18  * Copyright (c) 2008 Eliot Dresselhaus
19  *
20  * Permission is hereby granted, free of charge, to any person obtaining
21  * a copy of this software and associated documentation files (the
22  * "Software"), to deal in the Software without restriction, including
23  * without limitation the rights to use, copy, modify, merge, publish,
24  * distribute, sublicense, and/or sell copies of the Software, and to
25  * permit persons to whom the Software is furnished to do so, subject to
26  * the following conditions:
27  *
28  * The above copyright notice and this permission notice shall be
29  * included in all copies or substantial portions of the Software.
30  *
31  *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
32  *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
33  *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
34  *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
35  *  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
36  *  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
37  *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
38  */
39
40 /**
41  * @file
42  *
43  * Allocate/free network buffers.
44  */
45
46 #include <vlib/vlib.h>
47
48 uword
49 vlib_buffer_length_in_chain_slow_path (vlib_main_t * vm,
50                                        vlib_buffer_t * b_first)
51 {
52   vlib_buffer_t *b = b_first;
53   uword l_first = b_first->current_length;
54   uword l = 0;
55   while (b->flags & VLIB_BUFFER_NEXT_PRESENT)
56     {
57       b = vlib_get_buffer (vm, b->next_buffer);
58       l += b->current_length;
59     }
60   b_first->total_length_not_including_first_buffer = l;
61   b_first->flags |= VLIB_BUFFER_TOTAL_LENGTH_VALID;
62   return l + l_first;
63 }
64
65 u8 *
66 format_vlib_buffer (u8 * s, va_list * args)
67 {
68   vlib_buffer_t *b = va_arg (*args, vlib_buffer_t *);
69   uword indent = format_get_indent (s);
70
71   s = format (s, "current data %d, length %d, free-list %d",
72               b->current_data, b->current_length, b->free_list_index);
73
74   if (b->flags & VLIB_BUFFER_TOTAL_LENGTH_VALID)
75     s = format (s, ", totlen-nifb %d",
76                 b->total_length_not_including_first_buffer);
77
78   if (b->flags & VLIB_BUFFER_IS_TRACED)
79     s = format (s, ", trace 0x%x", b->trace_index);
80
81   while (b->flags & VLIB_BUFFER_NEXT_PRESENT)
82     {
83       vlib_main_t *vm = vlib_get_main ();
84       u32 next_buffer = b->next_buffer;
85       b = vlib_get_buffer (vm, next_buffer);
86
87       s = format (s, "\n%Unext-buffer 0x%x, segment length %d",
88                   format_white_space, indent, next_buffer, b->current_length);
89     }
90
91   return s;
92 }
93
94 u8 *
95 format_vlib_buffer_and_data (u8 * s, va_list * args)
96 {
97   vlib_buffer_t *b = va_arg (*args, vlib_buffer_t *);
98
99   s = format (s, "%U, %U",
100               format_vlib_buffer, b,
101               format_hex_bytes, vlib_buffer_get_current (b), 64);
102
103   return s;
104 }
105
106 static u8 *
107 format_vlib_buffer_known_state (u8 * s, va_list * args)
108 {
109   vlib_buffer_known_state_t state = va_arg (*args, vlib_buffer_known_state_t);
110   char *t;
111
112   switch (state)
113     {
114     case VLIB_BUFFER_UNKNOWN:
115       t = "unknown";
116       break;
117
118     case VLIB_BUFFER_KNOWN_ALLOCATED:
119       t = "known-allocated";
120       break;
121
122     case VLIB_BUFFER_KNOWN_FREE:
123       t = "known-free";
124       break;
125
126     default:
127       t = "invalid";
128       break;
129     }
130
131   return format (s, "%s", t);
132 }
133
134 u8 *
135 format_vlib_buffer_contents (u8 * s, va_list * va)
136 {
137   vlib_main_t *vm = va_arg (*va, vlib_main_t *);
138   vlib_buffer_t *b = va_arg (*va, vlib_buffer_t *);
139
140   while (1)
141     {
142       vec_add (s, vlib_buffer_get_current (b), b->current_length);
143       if (!(b->flags & VLIB_BUFFER_NEXT_PRESENT))
144         break;
145       b = vlib_get_buffer (vm, b->next_buffer);
146     }
147
148   return s;
149 }
150
151 static u8 *
152 vlib_validate_buffer_helper (vlib_main_t * vm,
153                              u32 bi,
154                              uword follow_buffer_next, uword ** unique_hash)
155 {
156   vlib_buffer_t *b = vlib_get_buffer (vm, bi);
157   vlib_buffer_main_t *bm = vm->buffer_main;
158   vlib_buffer_free_list_t *fl;
159
160   if (pool_is_free_index (bm->buffer_free_list_pool, b->free_list_index))
161     return format (0, "unknown free list 0x%x", b->free_list_index);
162
163   fl = pool_elt_at_index (bm->buffer_free_list_pool, b->free_list_index);
164
165   if ((signed) b->current_data < (signed) -VLIB_BUFFER_PRE_DATA_SIZE)
166     return format (0, "current data %d before pre-data", b->current_data);
167
168   if (b->current_data + b->current_length > fl->n_data_bytes)
169     return format (0, "%d-%d beyond end of buffer %d",
170                    b->current_data, b->current_length, fl->n_data_bytes);
171
172   if (follow_buffer_next && (b->flags & VLIB_BUFFER_NEXT_PRESENT))
173     {
174       vlib_buffer_known_state_t k;
175       u8 *msg, *result;
176
177       k = vlib_buffer_is_known (vm, b->next_buffer);
178       if (k != VLIB_BUFFER_KNOWN_ALLOCATED)
179         return format (0, "next 0x%x: %U",
180                        b->next_buffer, format_vlib_buffer_known_state, k);
181
182       if (unique_hash)
183         {
184           if (hash_get (*unique_hash, b->next_buffer))
185             return format (0, "duplicate buffer 0x%x", b->next_buffer);
186
187           hash_set1 (*unique_hash, b->next_buffer);
188         }
189
190       msg = vlib_validate_buffer (vm, b->next_buffer, follow_buffer_next);
191       if (msg)
192         {
193           result = format (0, "next 0x%x: %v", b->next_buffer, msg);
194           vec_free (msg);
195           return result;
196         }
197     }
198
199   return 0;
200 }
201
202 u8 *
203 vlib_validate_buffer (vlib_main_t * vm, u32 bi, uword follow_buffer_next)
204 {
205   return vlib_validate_buffer_helper (vm, bi, follow_buffer_next,
206                                       /* unique_hash */ 0);
207 }
208
209 u8 *
210 vlib_validate_buffers (vlib_main_t * vm,
211                        u32 * buffers,
212                        uword next_buffer_stride,
213                        uword n_buffers,
214                        vlib_buffer_known_state_t known_state,
215                        uword follow_buffer_next)
216 {
217   uword i, *hash;
218   u32 bi, *b = buffers;
219   vlib_buffer_known_state_t k;
220   u8 *msg = 0, *result = 0;
221
222   hash = hash_create (0, 0);
223   for (i = 0; i < n_buffers; i++)
224     {
225       bi = b[0];
226       b += next_buffer_stride;
227
228       /* Buffer is not unique. */
229       if (hash_get (hash, bi))
230         {
231           msg = format (0, "not unique");
232           goto done;
233         }
234
235       k = vlib_buffer_is_known (vm, bi);
236       if (k != known_state)
237         {
238           msg = format (0, "is %U; expected %U",
239                         format_vlib_buffer_known_state, k,
240                         format_vlib_buffer_known_state, known_state);
241           goto done;
242         }
243
244       msg = vlib_validate_buffer_helper (vm, bi, follow_buffer_next, &hash);
245       if (msg)
246         goto done;
247
248       hash_set1 (hash, bi);
249     }
250
251 done:
252   if (msg)
253     {
254       result = format (0, "0x%x: %v", bi, msg);
255       vec_free (msg);
256     }
257   hash_free (hash);
258   return result;
259 }
260
261 vlib_main_t **vlib_mains;
262
263 /* When dubugging validate that given buffers are either known allocated
264    or known free. */
265 static void __attribute__ ((unused))
266 vlib_buffer_validate_alloc_free (vlib_main_t * vm,
267                                  u32 * buffers,
268                                  uword n_buffers,
269                                  vlib_buffer_known_state_t expected_state)
270 {
271   u32 *b;
272   uword i, bi, is_free;
273
274   if (CLIB_DEBUG == 0)
275     return;
276
277   ASSERT (os_get_cpu_number () == 0);
278
279   /* smp disaster check */
280   if (vlib_mains)
281     ASSERT (vm == vlib_mains[0]);
282
283   is_free = expected_state == VLIB_BUFFER_KNOWN_ALLOCATED;
284   b = buffers;
285   for (i = 0; i < n_buffers; i++)
286     {
287       vlib_buffer_known_state_t known;
288
289       bi = b[0];
290       b += 1;
291       known = vlib_buffer_is_known (vm, bi);
292       if (known != expected_state)
293         {
294           ASSERT (0);
295           vlib_panic_with_msg
296             (vm, "%s %U buffer 0x%x",
297              is_free ? "freeing" : "allocating",
298              format_vlib_buffer_known_state, known, bi);
299         }
300
301       vlib_buffer_set_known_state
302         (vm, bi,
303          is_free ? VLIB_BUFFER_KNOWN_FREE : VLIB_BUFFER_KNOWN_ALLOCATED);
304     }
305 }
306
307 #define BUFFERS_PER_COPY (sizeof (vlib_copy_unit_t) / sizeof (u32))
308
309 /* Make sure we have at least given number of unaligned buffers. */
310 static void
311 fill_unaligned (vlib_main_t * vm,
312                 vlib_buffer_free_list_t * free_list,
313                 uword n_unaligned_buffers)
314 {
315   word la = vec_len (free_list->aligned_buffers);
316   word lu = vec_len (free_list->unaligned_buffers);
317
318   /* Aligned come in aligned copy-sized chunks. */
319   ASSERT (la % BUFFERS_PER_COPY == 0);
320
321   ASSERT (la >= n_unaligned_buffers);
322
323   while (lu < n_unaligned_buffers)
324     {
325       /* Copy 4 buffers from end of aligned vector to unaligned vector. */
326       vec_add (free_list->unaligned_buffers,
327                free_list->aligned_buffers + la - BUFFERS_PER_COPY,
328                BUFFERS_PER_COPY);
329       la -= BUFFERS_PER_COPY;
330       lu += BUFFERS_PER_COPY;
331     }
332   _vec_len (free_list->aligned_buffers) = la;
333 }
334
335 /* After free aligned buffers may not contain even sized chunks. */
336 static void
337 trim_aligned (vlib_buffer_free_list_t * f)
338 {
339   uword l, n_trim;
340
341   /* Add unaligned to aligned before trim. */
342   l = vec_len (f->unaligned_buffers);
343   if (l > 0)
344     {
345       vec_add_aligned (f->aligned_buffers, f->unaligned_buffers, l,
346                        /* align */ sizeof (vlib_copy_unit_t));
347
348       _vec_len (f->unaligned_buffers) = 0;
349     }
350
351   /* Remove unaligned buffers from end of aligned vector and save for next trim. */
352   l = vec_len (f->aligned_buffers);
353   n_trim = l % BUFFERS_PER_COPY;
354   if (n_trim)
355     {
356       /* Trim aligned -> unaligned. */
357       vec_add (f->unaligned_buffers, f->aligned_buffers + l - n_trim, n_trim);
358
359       /* Remove from aligned. */
360       _vec_len (f->aligned_buffers) = l - n_trim;
361     }
362 }
363
364 static void
365 merge_free_lists (vlib_buffer_free_list_t * dst,
366                   vlib_buffer_free_list_t * src)
367 {
368   uword l;
369   u32 *d;
370
371   trim_aligned (src);
372   trim_aligned (dst);
373
374   l = vec_len (src->aligned_buffers);
375   if (l > 0)
376     {
377       vec_add2_aligned (dst->aligned_buffers, d, l,
378                         /* align */ sizeof (vlib_copy_unit_t));
379       clib_memcpy (d, src->aligned_buffers, l * sizeof (d[0]));
380       vec_free (src->aligned_buffers);
381     }
382
383   l = vec_len (src->unaligned_buffers);
384   if (l > 0)
385     {
386       vec_add (dst->unaligned_buffers, src->unaligned_buffers, l);
387       vec_free (src->unaligned_buffers);
388     }
389 }
390
391 always_inline u32
392 vlib_buffer_get_free_list_with_size (vlib_main_t * vm, u32 size)
393 {
394   vlib_buffer_main_t *bm = vm->buffer_main;
395
396   size = vlib_buffer_round_size (size);
397   uword *p = hash_get (bm->free_list_by_size, size);
398   return p ? p[0] : ~0;
399 }
400
401 /* Add buffer free list. */
402 static u32
403 vlib_buffer_create_free_list_helper (vlib_main_t * vm,
404                                      u32 n_data_bytes,
405                                      u32 is_public, u32 is_default, u8 * name)
406 {
407   vlib_buffer_main_t *bm = vm->buffer_main;
408   vlib_buffer_free_list_t *f;
409   int i;
410
411   ASSERT (os_get_cpu_number () == 0);
412
413   if (!is_default && pool_elts (bm->buffer_free_list_pool) == 0)
414     {
415       u32 default_free_free_list_index;
416
417       /* *INDENT-OFF* */
418       default_free_free_list_index =
419         vlib_buffer_create_free_list_helper
420         (vm,
421          /* default buffer size */ VLIB_BUFFER_DEFAULT_FREE_LIST_BYTES,
422          /* is_public */ 1,
423          /* is_default */ 1,
424          (u8 *) "default");
425       /* *INDENT-ON* */
426       ASSERT (default_free_free_list_index ==
427               VLIB_BUFFER_DEFAULT_FREE_LIST_INDEX);
428
429       if (n_data_bytes == VLIB_BUFFER_DEFAULT_FREE_LIST_BYTES && is_public)
430         return default_free_free_list_index;
431     }
432
433   pool_get_aligned (bm->buffer_free_list_pool, f, CLIB_CACHE_LINE_BYTES);
434
435   memset (f, 0, sizeof (f[0]));
436   f->index = f - bm->buffer_free_list_pool;
437   f->n_data_bytes = vlib_buffer_round_size (n_data_bytes);
438   f->min_n_buffers_each_physmem_alloc = 16;
439   f->name = clib_mem_is_heap_object (name) ? name : format (0, "%s", name);
440
441   /* Setup free buffer template. */
442   f->buffer_init_template.free_list_index = f->index;
443
444   if (is_public)
445     {
446       uword *p = hash_get (bm->free_list_by_size, f->n_data_bytes);
447       if (!p)
448         hash_set (bm->free_list_by_size, f->n_data_bytes, f->index);
449     }
450
451   for (i = 1; i < vec_len (vlib_mains); i++)
452     {
453       vlib_buffer_main_t *wbm = vlib_mains[i]->buffer_main;
454       vlib_buffer_free_list_t *wf;
455       pool_get_aligned (wbm->buffer_free_list_pool,
456                         wf, CLIB_CACHE_LINE_BYTES);
457       ASSERT (f - bm->buffer_free_list_pool ==
458               wf - wbm->buffer_free_list_pool);
459       wf[0] = f[0];
460       wf->aligned_buffers = 0;
461       wf->unaligned_buffers = 0;
462       wf->n_alloc = 0;
463     }
464
465   return f->index;
466 }
467
468 u32
469 vlib_buffer_create_free_list (vlib_main_t * vm, u32 n_data_bytes,
470                               char *fmt, ...)
471 {
472   va_list va;
473   u8 *name;
474
475   va_start (va, fmt);
476   name = va_format (0, fmt, &va);
477   va_end (va);
478
479   return vlib_buffer_create_free_list_helper (vm, n_data_bytes,
480                                               /* is_public */ 0,
481                                               /* is_default */ 0,
482                                               name);
483 }
484
485 u32
486 vlib_buffer_get_or_create_free_list (vlib_main_t * vm, u32 n_data_bytes,
487                                      char *fmt, ...)
488 {
489   u32 i = vlib_buffer_get_free_list_with_size (vm, n_data_bytes);
490
491   if (i == ~0)
492     {
493       va_list va;
494       u8 *name;
495
496       va_start (va, fmt);
497       name = va_format (0, fmt, &va);
498       va_end (va);
499
500       i = vlib_buffer_create_free_list_helper (vm, n_data_bytes,
501                                                /* is_public */ 1,
502                                                /* is_default */ 0,
503                                                name);
504     }
505
506   return i;
507 }
508
509 static void
510 del_free_list (vlib_main_t * vm, vlib_buffer_free_list_t * f)
511 {
512   u32 i;
513
514   for (i = 0; i < vec_len (f->buffer_memory_allocated); i++)
515     vm->os_physmem_free (f->buffer_memory_allocated[i]);
516   vec_free (f->name);
517   vec_free (f->buffer_memory_allocated);
518   vec_free (f->unaligned_buffers);
519   vec_free (f->aligned_buffers);
520 }
521
522 /* Add buffer free list. */
523 void
524 vlib_buffer_delete_free_list_internal (vlib_main_t * vm, u32 free_list_index)
525 {
526   vlib_buffer_main_t *bm = vm->buffer_main;
527   vlib_buffer_free_list_t *f;
528   u32 merge_index;
529   int i;
530
531   ASSERT (os_get_cpu_number () == 0);
532
533   f = vlib_buffer_get_free_list (vm, free_list_index);
534
535   ASSERT (vec_len (f->unaligned_buffers) + vec_len (f->aligned_buffers) ==
536           f->n_alloc);
537   merge_index = vlib_buffer_get_free_list_with_size (vm, f->n_data_bytes);
538   if (merge_index != ~0 && merge_index != free_list_index)
539     {
540       merge_free_lists (pool_elt_at_index (bm->buffer_free_list_pool,
541                                            merge_index), f);
542     }
543
544   del_free_list (vm, f);
545
546   /* Poison it. */
547   memset (f, 0xab, sizeof (f[0]));
548
549   pool_put (bm->buffer_free_list_pool, f);
550
551   for (i = 1; i < vec_len (vlib_mains); i++)
552     {
553       bm = vlib_mains[i]->buffer_main;
554       f = vlib_buffer_get_free_list (vlib_mains[i], free_list_index);;
555       memset (f, 0xab, sizeof (f[0]));
556       pool_put (bm->buffer_free_list_pool, f);
557     }
558 }
559
560 /* Make sure free list has at least given number of free buffers. */
561 static uword
562 fill_free_list (vlib_main_t * vm,
563                 vlib_buffer_free_list_t * fl, uword min_free_buffers)
564 {
565   vlib_buffer_t *buffers, *b;
566   int n, n_bytes, i;
567   u32 *bi;
568   u32 n_remaining, n_alloc, n_this_chunk;
569
570   trim_aligned (fl);
571
572   /* Already have enough free buffers on free list? */
573   n = min_free_buffers - vec_len (fl->aligned_buffers);
574   if (n <= 0)
575     return min_free_buffers;
576
577   /* Always allocate round number of buffers. */
578   n = round_pow2 (n, BUFFERS_PER_COPY);
579
580   /* Always allocate new buffers in reasonably large sized chunks. */
581   n = clib_max (n, fl->min_n_buffers_each_physmem_alloc);
582
583   n_remaining = n;
584   n_alloc = 0;
585   while (n_remaining > 0)
586     {
587       n_this_chunk = clib_min (n_remaining, 16);
588
589       n_bytes = n_this_chunk * (sizeof (b[0]) + fl->n_data_bytes);
590
591       /* drb: removed power-of-2 ASSERT */
592       buffers = vm->os_physmem_alloc_aligned (&vm->physmem_main,
593                                               n_bytes,
594                                               sizeof (vlib_buffer_t));
595       if (!buffers)
596         return n_alloc;
597
598       /* Record chunk as being allocated so we can free it later. */
599       vec_add1 (fl->buffer_memory_allocated, buffers);
600
601       fl->n_alloc += n_this_chunk;
602       n_alloc += n_this_chunk;
603       n_remaining -= n_this_chunk;
604
605       b = buffers;
606       vec_add2_aligned (fl->aligned_buffers, bi, n_this_chunk,
607                         sizeof (vlib_copy_unit_t));
608       for (i = 0; i < n_this_chunk; i++)
609         {
610           bi[i] = vlib_get_buffer_index (vm, b);
611
612           if (CLIB_DEBUG > 0)
613             vlib_buffer_set_known_state (vm, bi[i], VLIB_BUFFER_KNOWN_FREE);
614           b = vlib_buffer_next_contiguous (b, fl->n_data_bytes);
615         }
616
617       memset (buffers, 0, n_bytes);
618
619       /* Initialize all new buffers. */
620       b = buffers;
621       for (i = 0; i < n_this_chunk; i++)
622         {
623           vlib_buffer_init_for_free_list (b, fl);
624           b = vlib_buffer_next_contiguous (b, fl->n_data_bytes);
625         }
626
627       if (fl->buffer_init_function)
628         fl->buffer_init_function (vm, fl, bi, n_this_chunk);
629     }
630   return n_alloc;
631 }
632
633 always_inline uword
634 copy_alignment (u32 * x)
635 {
636   return (pointer_to_uword (x) / sizeof (x[0])) % BUFFERS_PER_COPY;
637 }
638
639
640 static u32
641 alloc_from_free_list (vlib_main_t * vm,
642                       vlib_buffer_free_list_t * free_list,
643                       u32 * alloc_buffers, u32 n_alloc_buffers)
644 {
645   u32 *dst, *u_src;
646   uword u_len, n_left;
647   uword n_unaligned_start, n_unaligned_end, n_filled;
648
649   n_left = n_alloc_buffers;
650   dst = alloc_buffers;
651   n_unaligned_start = ((BUFFERS_PER_COPY - copy_alignment (dst))
652                        & (BUFFERS_PER_COPY - 1));
653
654   n_filled = fill_free_list (vm, free_list, n_alloc_buffers);
655   if (n_filled == 0)
656     return 0;
657
658   n_left = n_filled < n_left ? n_filled : n_left;
659   n_alloc_buffers = n_left;
660
661   if (n_unaligned_start >= n_left)
662     {
663       n_unaligned_start = n_left;
664       n_unaligned_end = 0;
665     }
666   else
667     n_unaligned_end = copy_alignment (dst + n_alloc_buffers);
668
669   fill_unaligned (vm, free_list, n_unaligned_start + n_unaligned_end);
670
671   u_len = vec_len (free_list->unaligned_buffers);
672   u_src = free_list->unaligned_buffers + u_len - 1;
673
674   if (n_unaligned_start)
675     {
676       uword n_copy = n_unaligned_start;
677       if (n_copy > n_left)
678         n_copy = n_left;
679       n_left -= n_copy;
680
681       while (n_copy > 0)
682         {
683           *dst++ = *u_src--;
684           n_copy--;
685           u_len--;
686         }
687
688       /* Now dst should be aligned. */
689       if (n_left > 0)
690         ASSERT (pointer_to_uword (dst) % sizeof (vlib_copy_unit_t) == 0);
691     }
692
693   /* Aligned copy. */
694   {
695     vlib_copy_unit_t *d, *s;
696     uword n_copy;
697
698     if (vec_len (free_list->aligned_buffers) <
699         ((n_left / BUFFERS_PER_COPY) * BUFFERS_PER_COPY))
700       abort ();
701
702     n_copy = n_left / BUFFERS_PER_COPY;
703     n_left = n_left % BUFFERS_PER_COPY;
704
705     /* Remove buffers from aligned free list. */
706     _vec_len (free_list->aligned_buffers) -= n_copy * BUFFERS_PER_COPY;
707
708     s = (vlib_copy_unit_t *) vec_end (free_list->aligned_buffers);
709     d = (vlib_copy_unit_t *) dst;
710
711     /* Fast path loop. */
712     while (n_copy >= 4)
713       {
714         d[0] = s[0];
715         d[1] = s[1];
716         d[2] = s[2];
717         d[3] = s[3];
718         n_copy -= 4;
719         s += 4;
720         d += 4;
721       }
722
723     while (n_copy >= 1)
724       {
725         d[0] = s[0];
726         n_copy -= 1;
727         s += 1;
728         d += 1;
729       }
730
731     dst = (void *) d;
732   }
733
734   /* Unaligned copy. */
735   ASSERT (n_unaligned_end == n_left);
736   while (n_left > 0)
737     {
738       *dst++ = *u_src--;
739       n_left--;
740       u_len--;
741     }
742
743   if (!free_list->unaligned_buffers)
744     ASSERT (u_len == 0);
745   else
746     _vec_len (free_list->unaligned_buffers) = u_len;
747
748   /* Verify that buffers are known free. */
749   vlib_buffer_validate_alloc_free (vm, alloc_buffers,
750                                    n_alloc_buffers, VLIB_BUFFER_KNOWN_FREE);
751
752   return n_alloc_buffers;
753 }
754
755
756 /* Allocate a given number of buffers into given array.
757    Returns number actually allocated which will be either zero or
758    number requested. */
759 static u32
760 vlib_buffer_alloc_internal (vlib_main_t * vm, u32 * buffers, u32 n_buffers)
761 {
762   vlib_buffer_main_t *bm = vm->buffer_main;
763
764   return alloc_from_free_list
765     (vm,
766      pool_elt_at_index (bm->buffer_free_list_pool,
767                         VLIB_BUFFER_DEFAULT_FREE_LIST_INDEX),
768      buffers, n_buffers);
769 }
770
771 static u32
772 vlib_buffer_alloc_from_free_list_internal (vlib_main_t * vm,
773                                            u32 * buffers,
774                                            u32 n_buffers, u32 free_list_index)
775 {
776   vlib_buffer_main_t *bm = vm->buffer_main;
777   vlib_buffer_free_list_t *f;
778   f = pool_elt_at_index (bm->buffer_free_list_pool, free_list_index);
779   return alloc_from_free_list (vm, f, buffers, n_buffers);
780 }
781
782 always_inline void
783 add_buffer_to_free_list (vlib_main_t * vm,
784                          vlib_buffer_free_list_t * f,
785                          u32 buffer_index, u8 do_init)
786 {
787   vlib_buffer_t *b;
788   b = vlib_get_buffer (vm, buffer_index);
789   if (PREDICT_TRUE (do_init))
790     vlib_buffer_init_for_free_list (b, f);
791   vec_add1_aligned (f->aligned_buffers, buffer_index,
792                     sizeof (vlib_copy_unit_t));
793 }
794
795 always_inline vlib_buffer_free_list_t *
796 buffer_get_free_list (vlib_main_t * vm, vlib_buffer_t * b, u32 * index)
797 {
798   vlib_buffer_main_t *bm = vm->buffer_main;
799   u32 i;
800
801   *index = i = b->free_list_index;
802   return pool_elt_at_index (bm->buffer_free_list_pool, i);
803 }
804
805 void *
806 vlib_set_buffer_free_callback (vlib_main_t * vm, void *fp)
807 {
808   vlib_buffer_main_t *bm = vm->buffer_main;
809   void *rv = bm->buffer_free_callback;
810
811   bm->buffer_free_callback = fp;
812   return rv;
813 }
814
815 static_always_inline void
816 vlib_buffer_free_inline (vlib_main_t * vm,
817                          u32 * buffers, u32 n_buffers, u32 follow_buffer_next)
818 {
819   vlib_buffer_main_t *bm = vm->buffer_main;
820   vlib_buffer_free_list_t *fl;
821   static u32 *next_to_free[2];  /* smp bad */
822   u32 i_next_to_free, *b, *n, *f, fi;
823   uword n_left;
824   int i;
825   static vlib_buffer_free_list_t **announce_list;
826   vlib_buffer_free_list_t *fl0 = 0, *fl1 = 0;
827   u32 bi0 = (u32) ~ 0, bi1 = (u32) ~ 0, fi0, fi1 = (u32) ~ 0;
828   u8 free0, free1 = 0, free_next0, free_next1;
829   u32 (*cb) (vlib_main_t * vm, u32 * buffers, u32 n_buffers,
830              u32 follow_buffer_next);
831
832   ASSERT (os_get_cpu_number () == 0);
833
834   cb = bm->buffer_free_callback;
835
836   if (PREDICT_FALSE (cb != 0))
837     n_buffers = (*cb) (vm, buffers, n_buffers, follow_buffer_next);
838
839   if (!n_buffers)
840     return;
841
842   /* Use first buffer to get default free list. */
843   {
844     u32 bi0 = buffers[0];
845     vlib_buffer_t *b0;
846
847     b0 = vlib_get_buffer (vm, bi0);
848     fl = buffer_get_free_list (vm, b0, &fi);
849     if (fl->buffers_added_to_freelist_function)
850       vec_add1 (announce_list, fl);
851   }
852
853   vec_validate (next_to_free[0], n_buffers - 1);
854   vec_validate (next_to_free[1], n_buffers - 1);
855
856   i_next_to_free = 0;
857   n_left = n_buffers;
858   b = buffers;
859
860 again:
861   /* Verify that buffers are known allocated. */
862   vlib_buffer_validate_alloc_free (vm, b,
863                                    n_left, VLIB_BUFFER_KNOWN_ALLOCATED);
864
865   vec_add2_aligned (fl->aligned_buffers, f, n_left,
866                     /* align */ sizeof (vlib_copy_unit_t));
867
868   n = next_to_free[i_next_to_free];
869   while (n_left >= 4)
870     {
871       vlib_buffer_t *b0, *b1, *binit0, *binit1, dummy_buffers[2];
872
873       bi0 = b[0];
874       bi1 = b[1];
875
876       f[0] = bi0;
877       f[1] = bi1;
878       f += 2;
879       b += 2;
880       n_left -= 2;
881
882       /* Prefetch buffers for next iteration. */
883       vlib_prefetch_buffer_with_index (vm, b[0], WRITE);
884       vlib_prefetch_buffer_with_index (vm, b[1], WRITE);
885
886       b0 = vlib_get_buffer (vm, bi0);
887       b1 = vlib_get_buffer (vm, bi1);
888
889       free0 = (b0->flags & VLIB_BUFFER_RECYCLE) == 0;
890       free1 = (b1->flags & VLIB_BUFFER_RECYCLE) == 0;
891
892       /* Must be before init which will over-write buffer flags. */
893       if (follow_buffer_next)
894         {
895           n[0] = b0->next_buffer;
896           free_next0 = free0 && (b0->flags & VLIB_BUFFER_NEXT_PRESENT) != 0;
897           n += free_next0;
898
899           n[0] = b1->next_buffer;
900           free_next1 = free1 && (b1->flags & VLIB_BUFFER_NEXT_PRESENT) != 0;
901           n += free_next1;
902         }
903       else
904         free_next0 = free_next1 = 0;
905
906       /* Must be before init which will over-write buffer free list. */
907       fi0 = b0->free_list_index;
908       fi1 = b1->free_list_index;
909
910       if (PREDICT_FALSE (fi0 != fi || fi1 != fi))
911         goto slow_path_x2;
912
913       binit0 = free0 ? b0 : &dummy_buffers[0];
914       binit1 = free1 ? b1 : &dummy_buffers[1];
915
916       vlib_buffer_init_two_for_free_list (binit0, binit1, fl);
917       continue;
918
919     slow_path_x2:
920       /* Backup speculation. */
921       f -= 2;
922       n -= free_next0 + free_next1;
923
924       _vec_len (fl->aligned_buffers) = f - fl->aligned_buffers;
925
926       fl0 = pool_elt_at_index (bm->buffer_free_list_pool, fi0);
927       fl1 = pool_elt_at_index (bm->buffer_free_list_pool, fi1);
928
929       add_buffer_to_free_list (vm, fl0, bi0, free0);
930       if (PREDICT_FALSE (fl0->buffers_added_to_freelist_function != 0))
931         {
932           int i;
933           for (i = 0; i < vec_len (announce_list); i++)
934             if (fl0 == announce_list[i])
935               goto no_fl0;
936           vec_add1 (announce_list, fl0);
937         }
938     no_fl0:
939       if (PREDICT_FALSE (fl1->buffers_added_to_freelist_function != 0))
940         {
941           int i;
942           for (i = 0; i < vec_len (announce_list); i++)
943             if (fl1 == announce_list[i])
944               goto no_fl1;
945           vec_add1 (announce_list, fl1);
946         }
947
948     no_fl1:
949       add_buffer_to_free_list (vm, fl1, bi1, free1);
950
951       /* Possibly change current free list. */
952       if (fi0 != fi && fi1 != fi)
953         {
954           fi = fi1;
955           fl = pool_elt_at_index (bm->buffer_free_list_pool, fi);
956         }
957
958       vec_add2_aligned (fl->aligned_buffers, f, n_left,
959                         /* align */ sizeof (vlib_copy_unit_t));
960     }
961
962   while (n_left >= 1)
963     {
964       vlib_buffer_t *b0, *binit0, dummy_buffers[1];
965
966       bi0 = b[0];
967       f[0] = bi0;
968       f += 1;
969       b += 1;
970       n_left -= 1;
971
972       b0 = vlib_get_buffer (vm, bi0);
973
974       free0 = (b0->flags & VLIB_BUFFER_RECYCLE) == 0;
975
976       /* Must be before init which will over-write buffer flags. */
977       if (follow_buffer_next)
978         {
979           n[0] = b0->next_buffer;
980           free_next0 = free0 && (b0->flags & VLIB_BUFFER_NEXT_PRESENT) != 0;
981           n += free_next0;
982         }
983       else
984         free_next0 = 0;
985
986       /* Must be before init which will over-write buffer free list. */
987       fi0 = b0->free_list_index;
988
989       if (PREDICT_FALSE (fi0 != fi))
990         goto slow_path_x1;
991
992       binit0 = free0 ? b0 : &dummy_buffers[0];
993
994       vlib_buffer_init_for_free_list (binit0, fl);
995       continue;
996
997     slow_path_x1:
998       /* Backup speculation. */
999       f -= 1;
1000       n -= free_next0;
1001
1002       _vec_len (fl->aligned_buffers) = f - fl->aligned_buffers;
1003
1004       fl0 = pool_elt_at_index (bm->buffer_free_list_pool, fi0);
1005
1006       add_buffer_to_free_list (vm, fl0, bi0, free0);
1007       if (PREDICT_FALSE (fl0->buffers_added_to_freelist_function != 0))
1008         {
1009           int i;
1010           for (i = 0; i < vec_len (announce_list); i++)
1011             if (fl0 == announce_list[i])
1012               goto no_fl00;
1013           vec_add1 (announce_list, fl0);
1014         }
1015
1016     no_fl00:
1017       fi = fi0;
1018       fl = pool_elt_at_index (bm->buffer_free_list_pool, fi);
1019
1020       vec_add2_aligned (fl->aligned_buffers, f, n_left,
1021                         /* align */ sizeof (vlib_copy_unit_t));
1022     }
1023
1024   if (follow_buffer_next && ((n_left = n - next_to_free[i_next_to_free]) > 0))
1025     {
1026       b = next_to_free[i_next_to_free];
1027       i_next_to_free ^= 1;
1028       goto again;
1029     }
1030
1031   _vec_len (fl->aligned_buffers) = f - fl->aligned_buffers;
1032
1033   if (vec_len (announce_list))
1034     {
1035       vlib_buffer_free_list_t *fl;
1036       for (i = 0; i < vec_len (announce_list); i++)
1037         {
1038           fl = announce_list[i];
1039           fl->buffers_added_to_freelist_function (vm, fl);
1040         }
1041       _vec_len (announce_list) = 0;
1042     }
1043 }
1044
1045 static void
1046 vlib_buffer_free_internal (vlib_main_t * vm, u32 * buffers, u32 n_buffers)
1047 {
1048   vlib_buffer_free_inline (vm, buffers, n_buffers,      /* follow_buffer_next */
1049                            1);
1050 }
1051
1052 static void
1053 vlib_buffer_free_no_next_internal (vlib_main_t * vm, u32 * buffers,
1054                                    u32 n_buffers)
1055 {
1056   vlib_buffer_free_inline (vm, buffers, n_buffers,      /* follow_buffer_next */
1057                            0);
1058 }
1059
1060 /* Copy template packet data into buffers as they are allocated. */
1061 static void __attribute__ ((unused))
1062 vlib_packet_template_buffer_init (vlib_main_t * vm,
1063                                   vlib_buffer_free_list_t * fl,
1064                                   u32 * buffers, u32 n_buffers)
1065 {
1066   vlib_packet_template_t *t =
1067     uword_to_pointer (fl->buffer_init_function_opaque,
1068                       vlib_packet_template_t *);
1069   uword i;
1070
1071   for (i = 0; i < n_buffers; i++)
1072     {
1073       vlib_buffer_t *b = vlib_get_buffer (vm, buffers[i]);
1074       ASSERT (b->current_length == vec_len (t->packet_data));
1075       clib_memcpy (vlib_buffer_get_current (b), t->packet_data,
1076                    b->current_length);
1077     }
1078 }
1079
1080 void
1081 vlib_packet_template_init (vlib_main_t * vm,
1082                            vlib_packet_template_t * t,
1083                            void *packet_data,
1084                            uword n_packet_data_bytes,
1085                            uword min_n_buffers_each_physmem_alloc,
1086                            char *fmt, ...)
1087 {
1088   vlib_buffer_main_t *bm = vm->buffer_main;
1089   va_list va;
1090   __attribute__ ((unused)) u8 *name;
1091   vlib_buffer_free_list_t *fl;
1092
1093   va_start (va, fmt);
1094   name = va_format (0, fmt, &va);
1095   va_end (va);
1096
1097   if (bm->cb.vlib_packet_template_init_cb)
1098     bm->cb.vlib_packet_template_init_cb (vm, (void *) t, packet_data,
1099                                          n_packet_data_bytes,
1100                                          min_n_buffers_each_physmem_alloc,
1101                                          name);
1102
1103   vlib_worker_thread_barrier_sync (vm);
1104
1105   memset (t, 0, sizeof (t[0]));
1106
1107   vec_add (t->packet_data, packet_data, n_packet_data_bytes);
1108   t->min_n_buffers_each_physmem_alloc = min_n_buffers_each_physmem_alloc;
1109
1110   t->free_list_index = vlib_buffer_create_free_list_helper
1111     (vm, n_packet_data_bytes,
1112      /* is_public */ 1,
1113      /* is_default */ 0,
1114      name);
1115
1116   ASSERT (t->free_list_index != 0);
1117   fl = vlib_buffer_get_free_list (vm, t->free_list_index);
1118   fl->min_n_buffers_each_physmem_alloc = t->min_n_buffers_each_physmem_alloc;
1119
1120   fl->buffer_init_function = vlib_packet_template_buffer_init;
1121   fl->buffer_init_function_opaque = pointer_to_uword (t);
1122
1123   fl->buffer_init_template.current_data = 0;
1124   fl->buffer_init_template.current_length = n_packet_data_bytes;
1125   fl->buffer_init_template.flags = 0;
1126   vlib_worker_thread_barrier_release (vm);
1127 }
1128
1129 void *
1130 vlib_packet_template_get_packet (vlib_main_t * vm,
1131                                  vlib_packet_template_t * t, u32 * bi_result)
1132 {
1133   u32 bi;
1134   vlib_buffer_t *b;
1135
1136   if (vlib_buffer_alloc (vm, &bi, 1) != 1)
1137     return 0;
1138
1139   *bi_result = bi;
1140
1141   b = vlib_get_buffer (vm, bi);
1142   clib_memcpy (vlib_buffer_get_current (b),
1143                t->packet_data, vec_len (t->packet_data));
1144   b->current_length = vec_len (t->packet_data);
1145
1146   return b->data;
1147 }
1148
1149 void
1150 vlib_packet_template_get_packet_helper (vlib_main_t * vm,
1151                                         vlib_packet_template_t * t)
1152 {
1153   word n = t->min_n_buffers_each_physmem_alloc;
1154   word l = vec_len (t->packet_data);
1155   word n_alloc;
1156
1157   ASSERT (l > 0);
1158   ASSERT (vec_len (t->free_buffers) == 0);
1159
1160   vec_validate (t->free_buffers, n - 1);
1161   n_alloc = vlib_buffer_alloc_from_free_list (vm, t->free_buffers,
1162                                               n, t->free_list_index);
1163   _vec_len (t->free_buffers) = n_alloc;
1164 }
1165
1166 /* Append given data to end of buffer, possibly allocating new buffers. */
1167 u32
1168 vlib_buffer_add_data (vlib_main_t * vm,
1169                       u32 free_list_index,
1170                       u32 buffer_index, void *data, u32 n_data_bytes)
1171 {
1172   u32 n_buffer_bytes, n_left, n_left_this_buffer, bi;
1173   vlib_buffer_t *b;
1174   void *d;
1175
1176   bi = buffer_index;
1177   if (bi == 0
1178       && 1 != vlib_buffer_alloc_from_free_list (vm, &bi, 1, free_list_index))
1179     goto out_of_buffers;
1180
1181   d = data;
1182   n_left = n_data_bytes;
1183   n_buffer_bytes = vlib_buffer_free_list_buffer_size (vm, free_list_index);
1184
1185   b = vlib_get_buffer (vm, bi);
1186   b->flags &= ~VLIB_BUFFER_TOTAL_LENGTH_VALID;
1187
1188   /* Get to the end of the chain before we try to append data... */
1189   while (b->flags & VLIB_BUFFER_NEXT_PRESENT)
1190     b = vlib_get_buffer (vm, b->next_buffer);
1191
1192   while (1)
1193     {
1194       u32 n;
1195
1196       ASSERT (n_buffer_bytes >= b->current_length);
1197       n_left_this_buffer =
1198         n_buffer_bytes - (b->current_data + b->current_length);
1199       n = clib_min (n_left_this_buffer, n_left);
1200       clib_memcpy (vlib_buffer_get_current (b) + b->current_length, d, n);
1201       b->current_length += n;
1202       n_left -= n;
1203       if (n_left == 0)
1204         break;
1205
1206       d += n;
1207       if (1 !=
1208           vlib_buffer_alloc_from_free_list (vm, &b->next_buffer, 1,
1209                                             free_list_index))
1210         goto out_of_buffers;
1211
1212       b->flags |= VLIB_BUFFER_NEXT_PRESENT;
1213
1214       b = vlib_get_buffer (vm, b->next_buffer);
1215     }
1216
1217   return bi;
1218
1219 out_of_buffers:
1220   clib_error ("out of buffers");
1221   return bi;
1222 }
1223
1224 u16
1225 vlib_buffer_chain_append_data_with_alloc (vlib_main_t * vm,
1226                                           u32 free_list_index,
1227                                           vlib_buffer_t * first,
1228                                           vlib_buffer_t ** last,
1229                                           void *data, u16 data_len)
1230 {
1231   vlib_buffer_t *l = *last;
1232   u32 n_buffer_bytes =
1233     vlib_buffer_free_list_buffer_size (vm, free_list_index);
1234   u16 copied = 0;
1235   ASSERT (n_buffer_bytes >= l->current_length + l->current_data);
1236   while (data_len)
1237     {
1238       u16 max = n_buffer_bytes - l->current_length - l->current_data;
1239       if (max == 0)
1240         {
1241           if (1 !=
1242               vlib_buffer_alloc_from_free_list (vm, &l->next_buffer, 1,
1243                                                 free_list_index))
1244             return copied;
1245           *last = l = vlib_buffer_chain_buffer (vm, first, l, l->next_buffer);
1246           max = n_buffer_bytes - l->current_length - l->current_data;
1247         }
1248
1249       u16 len = (data_len > max) ? max : data_len;
1250       clib_memcpy (vlib_buffer_get_current (l) + l->current_length,
1251                    data + copied, len);
1252       vlib_buffer_chain_increase_length (first, l, len);
1253       data_len -= len;
1254       copied += len;
1255     }
1256   return copied;
1257 }
1258
1259
1260 static u8 *
1261 format_vlib_buffer_free_list (u8 * s, va_list * va)
1262 {
1263   vlib_buffer_free_list_t *f = va_arg (*va, vlib_buffer_free_list_t *);
1264   u32 threadnum = va_arg (*va, u32);
1265   uword bytes_alloc, bytes_free, n_free, size;
1266
1267   if (!f)
1268     return format (s, "%=7s%=30s%=12s%=12s%=12s%=12s%=12s%=12s",
1269                    "Thread", "Name", "Index", "Size", "Alloc", "Free",
1270                    "#Alloc", "#Free");
1271
1272   size = sizeof (vlib_buffer_t) + f->n_data_bytes;
1273   n_free = vec_len (f->aligned_buffers) + vec_len (f->unaligned_buffers);
1274   bytes_alloc = size * f->n_alloc;
1275   bytes_free = size * n_free;
1276
1277   s = format (s, "%7d%30s%12d%12d%=12U%=12U%=12d%=12d", threadnum,
1278               f->name, f->index, f->n_data_bytes,
1279               format_memory_size, bytes_alloc,
1280               format_memory_size, bytes_free, f->n_alloc, n_free);
1281
1282   return s;
1283 }
1284
1285 static clib_error_t *
1286 show_buffers (vlib_main_t * vm,
1287               unformat_input_t * input, vlib_cli_command_t * cmd)
1288 {
1289   vlib_buffer_main_t *bm;
1290   vlib_buffer_free_list_t *f;
1291   vlib_main_t *curr_vm;
1292   u32 vm_index = 0;
1293
1294   vlib_cli_output (vm, "%U", format_vlib_buffer_free_list, 0, 0);
1295
1296   do
1297     {
1298       curr_vm = vec_len (vlib_mains) ? vlib_mains[vm_index] : vm;
1299       bm = curr_vm->buffer_main;
1300
1301     /* *INDENT-OFF* */
1302     pool_foreach (f, bm->buffer_free_list_pool, ({
1303       vlib_cli_output (vm, "%U", format_vlib_buffer_free_list, f, vm_index);
1304     }));
1305     /* *INDENT-ON* */
1306
1307       vm_index++;
1308     }
1309   while (vm_index < vec_len (vlib_mains));
1310
1311   return 0;
1312 }
1313
1314 /* *INDENT-OFF* */
1315 VLIB_CLI_COMMAND (show_buffers_command, static) = {
1316   .path = "show buffers",
1317   .short_help = "Show packet buffer allocation",
1318   .function = show_buffers,
1319 };
1320 /* *INDENT-ON* */
1321
1322 void
1323 vlib_buffer_cb_init (struct vlib_main_t *vm)
1324 {
1325   vlib_buffer_main_t *bm = vm->buffer_main;
1326   bm->cb.vlib_buffer_alloc_cb = &vlib_buffer_alloc_internal;
1327   bm->cb.vlib_buffer_alloc_from_free_list_cb =
1328     &vlib_buffer_alloc_from_free_list_internal;
1329   bm->cb.vlib_buffer_free_cb = &vlib_buffer_free_internal;
1330   bm->cb.vlib_buffer_free_no_next_cb = &vlib_buffer_free_no_next_internal;
1331   bm->cb.vlib_buffer_delete_free_list_cb =
1332     &vlib_buffer_delete_free_list_internal;
1333   bm->extern_buffer_mgmt = 0;
1334 }
1335
1336 int
1337 vlib_buffer_cb_register (struct vlib_main_t *vm, vlib_buffer_callbacks_t * cb)
1338 {
1339   vlib_buffer_main_t *bm = vm->buffer_main;
1340   if (bm->extern_buffer_mgmt)
1341     return -1;
1342
1343 #define _(x) bm->cb.x = cb->x
1344   _(vlib_buffer_alloc_cb);
1345   _(vlib_buffer_alloc_from_free_list_cb);
1346   _(vlib_buffer_free_cb);
1347   _(vlib_buffer_free_no_next_cb);
1348   _(vlib_buffer_delete_free_list_cb);
1349 #undef _
1350   bm->extern_buffer_mgmt = 1;
1351   return 0;
1352 }
1353
1354 /** @endcond */
1355 /*
1356  * fd.io coding-style-patch-verification: ON
1357  *
1358  * Local Variables:
1359  * eval: (c-set-style "gnu")
1360  * End:
1361  */