stats: buffer gauge callbacks use index instead of name.
[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 <vppinfra/linux/sysfs.h>
47 #include <vlib/vlib.h>
48 #include <vlib/unix/unix.h>
49 #include <vpp/stats/stat_segment.h>
50
51 #define VLIB_BUFFER_DEFAULT_BUFFERS_PER_NUMA 16384
52 #define VLIB_BUFFER_DEFAULT_BUFFERS_PER_NUMA_UNPRIV 8192
53
54 #ifdef CLIB_HAVE_VEC128
55 /* Assumptions by vlib_buffer_free_inline: */
56 STATIC_ASSERT_FITS_IN (vlib_buffer_t, flags, 16);
57 STATIC_ASSERT_FITS_IN (vlib_buffer_t, ref_count, 16);
58 STATIC_ASSERT_FITS_IN (vlib_buffer_t, buffer_pool_index, 16);
59 #endif
60
61 /* Make sure that buffer template size is not accidentally changed */
62 STATIC_ASSERT_OFFSET_OF (vlib_buffer_t, template_end, 64);
63
64 u16 __vlib_buffer_external_hdr_size = 0;
65
66 uword
67 vlib_buffer_length_in_chain_slow_path (vlib_main_t * vm,
68                                        vlib_buffer_t * b_first)
69 {
70   vlib_buffer_t *b = b_first;
71   uword l_first = b_first->current_length;
72   uword l = 0;
73   while (b->flags & VLIB_BUFFER_NEXT_PRESENT)
74     {
75       b = vlib_get_buffer (vm, b->next_buffer);
76       l += b->current_length;
77     }
78   b_first->total_length_not_including_first_buffer = l;
79   b_first->flags |= VLIB_BUFFER_TOTAL_LENGTH_VALID;
80   return l + l_first;
81 }
82
83 u8 *
84 format_vlib_buffer (u8 * s, va_list * args)
85 {
86   vlib_buffer_t *b = va_arg (*args, vlib_buffer_t *);
87   u32 indent = format_get_indent (s);
88   u8 *a = 0;
89
90 #define _(bit, name, v) \
91   if (v && (b->flags & VLIB_BUFFER_##name)) \
92     a = format (a, "%s ", v);
93   foreach_vlib_buffer_flag
94 #undef _
95     s = format (s, "current data %d, length %d, buffer-pool %d, "
96                 "ref-count %u", b->current_data, b->current_length,
97                 b->buffer_pool_index, b->ref_count);
98
99   if (b->flags & VLIB_BUFFER_TOTAL_LENGTH_VALID)
100     s = format (s, ", totlen-nifb %d",
101                 b->total_length_not_including_first_buffer);
102
103   if (b->flags & VLIB_BUFFER_IS_TRACED)
104     s = format (s, ", trace 0x%x", b->trace_index);
105
106   if (a)
107     s = format (s, "\n%U%v", format_white_space, indent, a);
108   vec_free (a);
109
110   while (b->flags & VLIB_BUFFER_NEXT_PRESENT)
111     {
112       vlib_main_t *vm = vlib_get_main ();
113       u32 next_buffer = b->next_buffer;
114       b = vlib_get_buffer (vm, next_buffer);
115
116       s =
117         format (s, "\n%Unext-buffer 0x%x, segment length %d, ref-count %u",
118                 format_white_space, indent, next_buffer, b->current_length,
119                 b->ref_count);
120     }
121
122   return s;
123 }
124
125 u8 *
126 format_vlib_buffer_and_data (u8 * s, va_list * args)
127 {
128   vlib_buffer_t *b = va_arg (*args, vlib_buffer_t *);
129
130   s = format (s, "%U, %U",
131               format_vlib_buffer, b,
132               format_hex_bytes, vlib_buffer_get_current (b), 64);
133
134   return s;
135 }
136
137 static u8 *
138 format_vlib_buffer_known_state (u8 * s, va_list * args)
139 {
140   vlib_buffer_known_state_t state = va_arg (*args, vlib_buffer_known_state_t);
141   char *t;
142
143   switch (state)
144     {
145     case VLIB_BUFFER_UNKNOWN:
146       t = "unknown";
147       break;
148
149     case VLIB_BUFFER_KNOWN_ALLOCATED:
150       t = "known-allocated";
151       break;
152
153     case VLIB_BUFFER_KNOWN_FREE:
154       t = "known-free";
155       break;
156
157     default:
158       t = "invalid";
159       break;
160     }
161
162   return format (s, "%s", t);
163 }
164
165 u8 *
166 format_vlib_buffer_contents (u8 * s, va_list * va)
167 {
168   vlib_main_t *vm = va_arg (*va, vlib_main_t *);
169   vlib_buffer_t *b = va_arg (*va, vlib_buffer_t *);
170
171   while (1)
172     {
173       vec_add (s, vlib_buffer_get_current (b), b->current_length);
174       if (!(b->flags & VLIB_BUFFER_NEXT_PRESENT))
175         break;
176       b = vlib_get_buffer (vm, b->next_buffer);
177     }
178
179   return s;
180 }
181
182 static u8 *
183 vlib_validate_buffer_helper (vlib_main_t * vm,
184                              u32 bi,
185                              uword follow_buffer_next, uword ** unique_hash)
186 {
187   vlib_buffer_main_t *bm = vm->buffer_main;
188   vlib_buffer_t *b = vlib_get_buffer (vm, bi);
189
190   if (vec_len (bm->buffer_pools) <= b->buffer_pool_index)
191     return format (0, "unknown buffer pool 0x%x", b->buffer_pool_index);
192
193   if ((signed) b->current_data < (signed) -VLIB_BUFFER_PRE_DATA_SIZE)
194     return format (0, "current data %d before pre-data", b->current_data);
195
196   if (b->current_data + b->current_length >
197       vlib_buffer_get_default_data_size (vm))
198     return format (0, "%d-%d beyond end of buffer %d", b->current_data,
199                    b->current_length, vlib_buffer_get_default_data_size (vm));
200
201   if (follow_buffer_next && (b->flags & VLIB_BUFFER_NEXT_PRESENT))
202     {
203       vlib_buffer_known_state_t k;
204       u8 *msg, *result;
205
206       k = vlib_buffer_is_known (vm, b->next_buffer);
207       if (k != VLIB_BUFFER_KNOWN_ALLOCATED)
208         return format (0, "next 0x%x: %U",
209                        b->next_buffer, format_vlib_buffer_known_state, k);
210
211       if (unique_hash)
212         {
213           if (hash_get (*unique_hash, b->next_buffer))
214             return format (0, "duplicate buffer 0x%x", b->next_buffer);
215
216           hash_set1 (*unique_hash, b->next_buffer);
217         }
218
219       msg = vlib_validate_buffer (vm, b->next_buffer, follow_buffer_next);
220       if (msg)
221         {
222           result = format (0, "next 0x%x: %v", b->next_buffer, msg);
223           vec_free (msg);
224           return result;
225         }
226     }
227
228   return 0;
229 }
230
231 u8 *
232 vlib_validate_buffer (vlib_main_t * vm, u32 bi, uword follow_buffer_next)
233 {
234   return vlib_validate_buffer_helper (vm, bi, follow_buffer_next,
235                                       /* unique_hash */ 0);
236 }
237
238 u8 *
239 vlib_validate_buffers (vlib_main_t * vm,
240                        u32 * buffers,
241                        uword next_buffer_stride,
242                        uword n_buffers,
243                        vlib_buffer_known_state_t known_state,
244                        uword follow_buffer_next)
245 {
246   uword i, *hash;
247   u32 bi, *b = buffers;
248   vlib_buffer_known_state_t k;
249   u8 *msg = 0, *result = 0;
250
251   hash = hash_create (0, 0);
252   for (i = 0; i < n_buffers; i++)
253     {
254       bi = b[0];
255       b += next_buffer_stride;
256
257       /* Buffer is not unique. */
258       if (hash_get (hash, bi))
259         {
260           msg = format (0, "not unique");
261           goto done;
262         }
263
264       k = vlib_buffer_is_known (vm, bi);
265       if (k != known_state)
266         {
267           msg = format (0, "is %U; expected %U",
268                         format_vlib_buffer_known_state, k,
269                         format_vlib_buffer_known_state, known_state);
270           goto done;
271         }
272
273       msg = vlib_validate_buffer_helper (vm, bi, follow_buffer_next, &hash);
274       if (msg)
275         goto done;
276
277       hash_set1 (hash, bi);
278     }
279
280 done:
281   if (msg)
282     {
283       result = format (0, "0x%x: %v", bi, msg);
284       vec_free (msg);
285     }
286   hash_free (hash);
287   return result;
288 }
289
290 /*
291  * Hand-craft a static vector w/ length 1, so vec_len(vlib_mains) =1
292  * and vlib_mains[0] = &vlib_global_main from the beginning of time.
293  *
294  * The only place which should ever expand vlib_mains is start_workers()
295  * in threads.c. It knows about the bootstrap vector.
296  */
297 /* *INDENT-OFF* */
298 static struct
299 {
300   vec_header_t h;
301   vlib_main_t *vm;
302 } __attribute__ ((packed)) __bootstrap_vlib_main_vector
303   __attribute__ ((aligned (CLIB_CACHE_LINE_BYTES))) =
304 {
305   .h.len = 1,
306   .vm = &vlib_global_main,
307 };
308 /* *INDENT-ON* */
309
310 vlib_main_t **vlib_mains = &__bootstrap_vlib_main_vector.vm;
311
312
313 /* When dubugging validate that given buffers are either known allocated
314    or known free. */
315 void
316 vlib_buffer_validate_alloc_free (vlib_main_t * vm,
317                                  u32 * buffers,
318                                  uword n_buffers,
319                                  vlib_buffer_known_state_t expected_state)
320 {
321   vlib_buffer_main_t *bm = vm->buffer_main;
322   u32 *b;
323   uword i, bi, is_free;
324
325   if (CLIB_DEBUG == 0)
326     return;
327
328   is_free = expected_state == VLIB_BUFFER_KNOWN_ALLOCATED;
329   b = buffers;
330   for (i = 0; i < n_buffers; i++)
331     {
332       vlib_buffer_known_state_t known;
333
334       bi = b[0];
335       b += 1;
336       known = vlib_buffer_is_known (vm, bi);
337
338       if (known == VLIB_BUFFER_UNKNOWN &&
339           expected_state == VLIB_BUFFER_KNOWN_FREE)
340         known = VLIB_BUFFER_KNOWN_FREE;
341
342       if (known != expected_state)
343         {
344           clib_panic ("%s %U buffer 0x%x", is_free ? "freeing" : "allocating",
345                       format_vlib_buffer_known_state, known, bi);
346         }
347
348       clib_spinlock_lock (&bm->buffer_known_hash_lockp);
349       hash_set (bm->buffer_known_hash, bi, is_free ? VLIB_BUFFER_KNOWN_FREE :
350                 VLIB_BUFFER_KNOWN_ALLOCATED);
351       clib_spinlock_unlock (&bm->buffer_known_hash_lockp);
352     }
353 }
354
355 void
356 vlib_packet_template_init (vlib_main_t * vm,
357                            vlib_packet_template_t * t,
358                            void *packet_data,
359                            uword n_packet_data_bytes,
360                            uword min_n_buffers_each_alloc, char *fmt, ...)
361 {
362   va_list va;
363
364   va_start (va, fmt);
365   t->name = va_format (0, fmt, &va);
366   va_end (va);
367
368   vlib_worker_thread_barrier_sync (vm);
369
370   clib_memset (t, 0, sizeof (t[0]));
371
372   vec_add (t->packet_data, packet_data, n_packet_data_bytes);
373   t->min_n_buffers_each_alloc = min_n_buffers_each_alloc;
374   vlib_worker_thread_barrier_release (vm);
375 }
376
377 void *
378 vlib_packet_template_get_packet (vlib_main_t * vm,
379                                  vlib_packet_template_t * t, u32 * bi_result)
380 {
381   u32 bi;
382   vlib_buffer_t *b;
383
384   if (vlib_buffer_alloc (vm, &bi, 1) != 1)
385     return 0;
386
387   *bi_result = bi;
388
389   b = vlib_get_buffer (vm, bi);
390   clib_memcpy_fast (vlib_buffer_get_current (b),
391                     t->packet_data, vec_len (t->packet_data));
392   b->current_length = vec_len (t->packet_data);
393
394   return b->data;
395 }
396
397 /* Append given data to end of buffer, possibly allocating new buffers. */
398 int
399 vlib_buffer_add_data (vlib_main_t * vm, u32 * buffer_index, void *data,
400                       u32 n_data_bytes)
401 {
402   u32 n_buffer_bytes, n_left, n_left_this_buffer, bi;
403   vlib_buffer_t *b;
404   void *d;
405
406   bi = *buffer_index;
407   if (bi == ~0 && 1 != vlib_buffer_alloc (vm, &bi, 1))
408     goto out_of_buffers;
409
410   d = data;
411   n_left = n_data_bytes;
412   n_buffer_bytes = vlib_buffer_get_default_data_size (vm);
413
414   b = vlib_get_buffer (vm, bi);
415   b->flags &= ~VLIB_BUFFER_TOTAL_LENGTH_VALID;
416
417   /* Get to the end of the chain before we try to append data... */
418   while (b->flags & VLIB_BUFFER_NEXT_PRESENT)
419     b = vlib_get_buffer (vm, b->next_buffer);
420
421   while (1)
422     {
423       u32 n;
424
425       ASSERT (n_buffer_bytes >= b->current_length);
426       n_left_this_buffer =
427         n_buffer_bytes - (b->current_data + b->current_length);
428       n = clib_min (n_left_this_buffer, n_left);
429       clib_memcpy_fast (vlib_buffer_get_current (b) + b->current_length, d,
430                         n);
431       b->current_length += n;
432       n_left -= n;
433       if (n_left == 0)
434         break;
435
436       d += n;
437       if (1 != vlib_buffer_alloc (vm, &b->next_buffer, 1))
438         goto out_of_buffers;
439
440       b->flags |= VLIB_BUFFER_NEXT_PRESENT;
441
442       b = vlib_get_buffer (vm, b->next_buffer);
443     }
444
445   *buffer_index = bi;
446   return 0;
447
448 out_of_buffers:
449   clib_warning ("out of buffers");
450   return 1;
451 }
452
453 u16
454 vlib_buffer_chain_append_data_with_alloc (vlib_main_t * vm,
455                                           vlib_buffer_t * first,
456                                           vlib_buffer_t ** last, void *data,
457                                           u16 data_len)
458 {
459   vlib_buffer_t *l = *last;
460   u32 n_buffer_bytes = vlib_buffer_get_default_data_size (vm);
461   u16 copied = 0;
462   ASSERT (n_buffer_bytes >= l->current_length + l->current_data);
463   while (data_len)
464     {
465       u16 max = n_buffer_bytes - l->current_length - l->current_data;
466       if (max == 0)
467         {
468           if (1 != vlib_buffer_alloc_from_pool (vm, &l->next_buffer, 1,
469                                                 first->buffer_pool_index))
470             return copied;
471           *last = l = vlib_buffer_chain_buffer (vm, l, l->next_buffer);
472           max = n_buffer_bytes - l->current_length - l->current_data;
473         }
474
475       u16 len = (data_len > max) ? max : data_len;
476       clib_memcpy_fast (vlib_buffer_get_current (l) + l->current_length,
477                         data + copied, len);
478       vlib_buffer_chain_increase_length (first, l, len);
479       data_len -= len;
480       copied += len;
481     }
482   return copied;
483 }
484
485 clib_error_t *
486 vlib_buffer_pool_create (vlib_main_t * vm, u8 index, char *name,
487                          u32 data_size, u32 physmem_map_index)
488 {
489   vlib_buffer_main_t *bm = vm->buffer_main;
490   vlib_buffer_pool_t *bp;
491   vlib_physmem_map_t *m = vlib_physmem_get_map (vm, physmem_map_index);
492   uword start = pointer_to_uword (m->base);
493   uword size = (uword) m->n_pages << m->log2_page_size;
494   uword i, j;
495   u32 alloc_size, n_alloc_per_page;;
496
497   vec_validate_aligned (bm->buffer_pools, index, CLIB_CACHE_LINE_BYTES);
498   bp = vec_elt_at_index (bm->buffer_pools, index);
499
500   if (bp->start)
501     return clib_error_return (0, "buffer with index %u already exists",
502                               index);
503
504   if (index >= 255)
505     return clib_error_return (0, "buffer index must be < 255", index);
506
507   if (bm->buffer_mem_size == 0)
508     {
509       bm->buffer_mem_start = start;
510       bm->buffer_mem_size = size;
511     }
512   else if (start < bm->buffer_mem_start)
513     {
514       bm->buffer_mem_size += bm->buffer_mem_start - start;
515       bm->buffer_mem_start = start;
516       if (size > bm->buffer_mem_size)
517         bm->buffer_mem_size = size;
518     }
519   else if (start > bm->buffer_mem_start)
520     {
521       uword new_size = start - bm->buffer_mem_start + size;
522       if (new_size > bm->buffer_mem_size)
523         bm->buffer_mem_size = new_size;
524     }
525
526   if ((u64) bm->buffer_mem_size >
527       ((u64) 1 << (32 + CLIB_LOG2_CACHE_LINE_BYTES)))
528     {
529       clib_panic ("buffer memory size out of range!");
530     }
531
532   bp->start = start;
533   bp->size = size;
534   bp->index = bp - bm->buffer_pools;
535   bp->buffer_template.buffer_pool_index = bp->index;
536   bp->buffer_template.ref_count = 1;
537   bp->physmem_map_index = physmem_map_index;
538   bp->name = format (0, "%s%c", name, 0);
539   bp->data_size = data_size;
540   bp->numa_node = m->numa_node;
541
542   vec_validate_aligned (bp->threads, vec_len (vlib_mains) - 1,
543                         CLIB_CACHE_LINE_BYTES);
544
545   alloc_size = data_size + sizeof (vlib_buffer_t) + bm->ext_hdr_size;
546   n_alloc_per_page = (1ULL << m->log2_page_size) / alloc_size;
547
548   /* preallocate buffer indices memory */
549   vec_validate_aligned (bp->buffers, m->n_pages * n_alloc_per_page,
550                         CLIB_CACHE_LINE_BYTES);
551   vec_reset_length (bp->buffers);
552
553   clib_spinlock_init (&bp->lock);
554
555   for (j = 0; j < m->n_pages; j++)
556     for (i = 0; i < n_alloc_per_page; i++)
557       {
558         u8 *p;
559         u32 bi;
560
561         p = m->base + (j << m->log2_page_size) + i * alloc_size;
562         p += bm->ext_hdr_size;
563
564         vlib_buffer_copy_template ((vlib_buffer_t *) p, &bp->buffer_template);
565
566         bi = vlib_get_buffer_index (vm, (vlib_buffer_t *) p);
567
568         vec_add1_aligned (bp->buffers, bi, CLIB_CACHE_LINE_BYTES);
569         vlib_get_buffer (vm, bi);
570       }
571
572   bp->n_buffers = vec_len (bp->buffers);
573
574   return 0;
575 }
576
577 static u8 *
578 format_vlib_buffer_pool (u8 * s, va_list * va)
579 {
580   vlib_main_t *vm = va_arg (*va, vlib_main_t *);
581   vlib_buffer_pool_t *bp = va_arg (*va, vlib_buffer_pool_t *);
582   vlib_buffer_pool_thread_t *bpt;
583   u32 cached = 0;
584
585   if (!bp)
586     return format (s, "%-20s%=6s%=6s%=6s%=11s%=6s%=8s%=8s%=8s",
587                    "Pool Name", "Index", "NUMA", "Size", "Data Size",
588                    "Total", "Avail", "Cached", "Used");
589
590   /* *INDENT-OFF* */
591   vec_foreach (bpt, bp->threads)
592     cached += vec_len (bpt->cached_buffers);
593   /* *INDENT-ON* */
594
595   s = format (s, "%-20s%=6d%=6d%=6u%=11u%=6u%=8u%=8u%=8u",
596               bp->name, bp->index, bp->numa_node, bp->data_size +
597               sizeof (vlib_buffer_t) + vm->buffer_main->ext_hdr_size,
598               bp->data_size, bp->n_buffers, vec_len (bp->buffers), cached,
599               bp->n_buffers - vec_len (bp->buffers) - cached);
600
601   return s;
602 }
603
604 static clib_error_t *
605 show_buffers (vlib_main_t * vm,
606               unformat_input_t * input, vlib_cli_command_t * cmd)
607 {
608   vlib_buffer_main_t *bm = vm->buffer_main;
609   vlib_buffer_pool_t *bp;
610
611   vlib_cli_output (vm, "%U", format_vlib_buffer_pool, vm, 0);
612
613   /* *INDENT-OFF* */
614   vec_foreach (bp, bm->buffer_pools)
615     vlib_cli_output (vm, "%U", format_vlib_buffer_pool, vm, bp);
616   /* *INDENT-ON* */
617
618   return 0;
619 }
620
621 /* *INDENT-OFF* */
622 VLIB_CLI_COMMAND (show_buffers_command, static) = {
623   .path = "show buffers",
624   .short_help = "Show packet buffer allocation",
625   .function = show_buffers,
626 };
627 /* *INDENT-ON* */
628
629 clib_error_t *
630 vlib_buffer_worker_init (vlib_main_t * vm)
631 {
632   vlib_buffer_main_t *bm = vm->buffer_main;
633   vlib_buffer_pool_t *bp;
634
635   /* *INDENT-OFF* */
636   vec_foreach (bp, bm->buffer_pools)
637     {
638       clib_spinlock_lock (&bp->lock);
639       vec_validate_aligned (bp->threads, vec_len (vlib_mains) - 1,
640                             CLIB_CACHE_LINE_BYTES);
641       clib_spinlock_unlock (&bp->lock);
642     }
643   /* *INDENT-ON* */
644
645   return 0;
646 }
647
648 VLIB_WORKER_INIT_FUNCTION (vlib_buffer_worker_init);
649
650 static clib_error_t *
651 vlib_buffer_main_init_numa_node (struct vlib_main_t *vm, u32 numa_node)
652 {
653   vlib_buffer_main_t *bm = vm->buffer_main;
654   clib_error_t *error;
655   u32 physmem_map_index;
656   uword n_pages, pagesize;
657   u32 buffers_per_numa;
658   u32 buffer_size = CLIB_CACHE_LINE_ROUND (bm->ext_hdr_size +
659                                            sizeof (vlib_buffer_t) +
660                                            vlib_buffer_get_default_data_size
661                                            (vm));
662   u8 *name;
663
664   pagesize = clib_mem_get_default_hugepage_size ();
665   name = format (0, "buffers-numa-%d%c", numa_node, 0);
666
667   buffers_per_numa = bm->buffers_per_numa ? bm->buffers_per_numa :
668     VLIB_BUFFER_DEFAULT_BUFFERS_PER_NUMA;
669
670 retry:
671   n_pages = (buffers_per_numa - 1) / (pagesize / buffer_size) + 1;
672   error = vlib_physmem_shared_map_create (vm, (char *) name,
673                                           n_pages * pagesize,
674                                           min_log2 (pagesize), numa_node,
675                                           &physmem_map_index);
676
677   if (error && pagesize != clib_mem_get_page_size ())
678     {
679       vlib_log_warn (bm->log_default, "%U", format_clib_error, error);
680       clib_error_free (error);
681       vlib_log_warn (bm->log_default, "falling back to non-hugepage "
682                      "backed buffer pool");
683       pagesize = clib_mem_get_page_size ();
684       buffers_per_numa = bm->buffers_per_numa ? bm->buffers_per_numa :
685         VLIB_BUFFER_DEFAULT_BUFFERS_PER_NUMA_UNPRIV;
686       goto retry;
687     }
688
689   if (error)
690     return error;
691
692   vec_reset_length (name);
693   name = format (name, "default-numa-%d%c", numa_node, 0);
694
695   return vlib_buffer_pool_create (vm, numa_node, (char *) name,
696                                   vlib_buffer_get_default_data_size (vm),
697                                   physmem_map_index);
698 }
699
700 void
701 vlib_buffer_main_alloc (vlib_main_t * vm)
702 {
703   vlib_buffer_main_t *bm;
704
705   if (vm->buffer_main)
706     return;
707
708   vm->buffer_main = bm = clib_mem_alloc (sizeof (bm[0]));
709   clib_memset (vm->buffer_main, 0, sizeof (bm[0]));
710   bm->default_data_size = VLIB_BUFFER_DEFAULT_DATA_SIZE;
711 }
712
713 static u32
714 buffer_get_cached (vlib_buffer_pool_t * bp)
715 {
716   u32 cached = 0;
717   vlib_buffer_pool_thread_t *bpt;
718
719   /* *INDENT-OFF* */
720   vec_foreach (bpt, bp->threads)
721     cached += vec_len (bpt->cached_buffers);
722   /* *INDENT-ON* */
723
724   return cached;
725 }
726
727 static vlib_buffer_pool_t *
728 buffer_get_by_index (vlib_buffer_main_t * bm, u32 index)
729 {
730   vlib_buffer_pool_t *bp;
731   if (!bm->buffer_pools || vec_len (bm->buffer_pools) < index)
732     return 0;
733   bp = vec_elt_at_index (bm->buffer_pools, index);
734   if (!bp)
735     return 0;
736   return bp;
737 }
738
739 static void
740 buffer_gauges_update_used_fn (stat_segment_directory_entry_t * e, u32 index)
741 {
742   vlib_main_t *vm = vlib_get_main ();
743   vlib_buffer_pool_t *bp = buffer_get_by_index (vm->buffer_main, index);
744   if (!bp)
745     return;
746
747   e->value = bp->n_buffers - vec_len (bp->buffers) - buffer_get_cached (bp);
748 }
749
750 static void
751 buffer_gauges_update_available_fn (stat_segment_directory_entry_t * e,
752                                    u32 index)
753 {
754   vlib_main_t *vm = vlib_get_main ();
755   vlib_buffer_pool_t *bp = buffer_get_by_index (vm->buffer_main, index);
756   if (!bp)
757     return;
758
759   e->value = vec_len (bp->buffers);
760 }
761
762 static void
763 buffer_gauges_update_cached_fn (stat_segment_directory_entry_t * e, u32 index)
764 {
765   vlib_main_t *vm = vlib_get_main ();
766   vlib_buffer_pool_t *bp = buffer_get_by_index (vm->buffer_main, index);
767   if (!bp)
768     return;
769
770   e->value = buffer_get_cached (bp);
771 }
772
773 clib_error_t *
774 vlib_buffer_main_init (struct vlib_main_t * vm)
775 {
776   vlib_buffer_main_t *bm;
777   clib_error_t *err;
778   clib_bitmap_t *bmp = 0;
779   u32 numa_node;
780   vlib_buffer_pool_t *bp;
781   u8 *name;
782
783   vlib_buffer_main_alloc (vm);
784
785   bm = vm->buffer_main;
786   bm->log_default = vlib_log_register_class ("buffer", 0);
787   bm->ext_hdr_size = __vlib_buffer_external_hdr_size;
788
789   clib_spinlock_init (&bm->buffer_known_hash_lockp);
790
791   err = clib_sysfs_read ("/sys/devices/system/node/possible", "%U",
792                          unformat_bitmap_list, &bmp);
793   if (err)
794     {
795       /* no info from sysfs, assuming that only numa 0 exists */
796       clib_error_free (err);
797       bmp = clib_bitmap_set (bmp, 0, 1);
798     }
799
800   /* *INDENT-OFF* */
801   clib_bitmap_foreach (numa_node, bmp, {
802       if ((err = vlib_buffer_main_init_numa_node(vm, numa_node)))
803           goto done;
804     });
805   /* *INDENT-ON* */
806
807   bm->n_numa_nodes = clib_bitmap_last_set (bmp) + 1;
808
809   vec_foreach (bp, bm->buffer_pools)
810   {
811     name = format (0, "/buffer/cached/%s%c", bp->name, 0);
812     stat_segment_register_gauge (name, buffer_gauges_update_cached_fn,
813                                  bp - bm->buffer_pools);
814     vec_free (name);
815     name = format (0, "/buffer/used/%s%c", bp->name, 0);
816     stat_segment_register_gauge (name, buffer_gauges_update_used_fn,
817                                  bp - bm->buffer_pools);
818     vec_free (name);
819     name = format (0, "/buffer/available/%s%c", bp->name, 0);
820     stat_segment_register_gauge (name, buffer_gauges_update_available_fn,
821                                  bp - bm->buffer_pools);
822     vec_free (name);
823   }
824
825
826 done:
827   vec_free (bmp);
828   return err;
829 }
830
831 static clib_error_t *
832 vlib_buffers_configure (vlib_main_t * vm, unformat_input_t * input)
833 {
834   vlib_buffer_main_t *bm;
835
836   vlib_buffer_main_alloc (vm);
837
838   bm = vm->buffer_main;
839
840   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
841     {
842       if (unformat (input, "buffers-per-numa %u", &bm->buffers_per_numa))
843         ;
844       else if (unformat (input, "default data-size %u",
845                          &bm->default_data_size))
846         ;
847       else
848         return unformat_parse_error (input);
849     }
850
851   unformat_free (input);
852   return 0;
853 }
854
855 VLIB_EARLY_CONFIG_FUNCTION (vlib_buffers_configure, "buffers");
856
857
858 /** @endcond */
859 /*
860  * fd.io coding-style-patch-verification: ON
861  *
862  * Local Variables:
863  * eval: (c-set-style "gnu")
864  * End:
865  */