UT: Repaired broken C unit tests (--enable-tests)
[vpp.git] / src / vpp / stats / stats.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 #include <vpp/stats/stats.h>
16 #include <signal.h>
17 #include <vnet/fib/ip4_fib.h>
18 #include <vnet/fib/fib_entry.h>
19 #include <vnet/dpo/load_balance.h>
20
21 #define STATS_DEBUG 0
22
23 stats_main_t stats_main;
24
25 #include <vnet/ip/ip.h>
26
27 #include <vpp/api/vpe_msg_enum.h>
28
29 #define f64_endian(a)
30 #define f64_print(a,b)
31
32 #define vl_typedefs             /* define message structures */
33 #include <vpp/api/vpe_all_api_h.h>
34 #undef vl_typedefs
35
36 #define vl_endianfun            /* define message structures */
37 #include <vpp/api/vpe_all_api_h.h>
38 #undef vl_endianfun
39
40 /* instantiate all the print functions we know about */
41 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
42 #define vl_printfun
43 #include <vpp/api/vpe_all_api_h.h>
44 #undef vl_printfun
45
46 #define foreach_stats_msg                                               \
47 _(WANT_STATS, want_stats)                                               \
48 _(VNET_INTERFACE_SIMPLE_COUNTERS, vnet_interface_simple_counters)       \
49 _(WANT_INTERFACE_SIMPLE_STATS, want_interface_simple_stats)     \
50 _(VNET_INTERFACE_COMBINED_COUNTERS, vnet_interface_combined_counters)   \
51 _(WANT_INTERFACE_COMBINED_STATS, want_interface_combined_stats) \
52 _(WANT_PER_INTERFACE_COMBINED_STATS, want_per_interface_combined_stats) \
53 _(WANT_PER_INTERFACE_SIMPLE_STATS, want_per_interface_simple_stats) \
54 _(VNET_IP4_FIB_COUNTERS, vnet_ip4_fib_counters)                         \
55 _(WANT_IP4_FIB_STATS, want_ip4_fib_stats)            \
56 _(VNET_IP6_FIB_COUNTERS, vnet_ip6_fib_counters)                         \
57 _(WANT_IP6_FIB_STATS, want_ip6_fib_stats)        \
58 _(VNET_IP4_NBR_COUNTERS, vnet_ip4_nbr_counters)                         \
59 _(WANT_IP4_NBR_STATS, want_ip4_nbr_stats)            \
60 _(VNET_IP6_NBR_COUNTERS, vnet_ip6_nbr_counters) \
61 _(WANT_IP6_NBR_STATS, want_ip6_nbr_stats) \
62 _(VNET_GET_SUMMARY_STATS, vnet_get_summary_stats)
63
64
65 #define vl_msg_name_crc_list
66 #include <vpp/stats/stats.api.h>
67 #undef vl_msg_name_crc_list
68
69 static void
70 setup_message_id_table (api_main_t * am)
71 {
72 #define _(id,n,crc) \
73   vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id);
74   foreach_vl_msg_name_crc_stats;
75 #undef _
76 }
77
78 /* These constants ensure msg sizes <= 1024, aka ring allocation */
79 #define SIMPLE_COUNTER_BATCH_SIZE       126
80 #define COMBINED_COUNTER_BATCH_SIZE     63
81 #define IP4_FIB_COUNTER_BATCH_SIZE      48
82 #define IP6_FIB_COUNTER_BATCH_SIZE      30
83
84 /* 5ms */
85 #define STATS_RELEASE_DELAY_NS (1000 * 1000 * 5)
86 /*                              ns/us  us/ms        */
87
88 u8 *
89 format_vnet_interface_combined_counters (u8 * s, va_list * args)
90 {
91   stats_main_t *sm = &stats_main;
92   vl_api_vnet_interface_combined_counters_t *mp =
93     va_arg (*args, vl_api_vnet_interface_combined_counters_t *);
94
95   char *counter_name;
96   u32 count, sw_if_index;
97   int i;
98   count = ntohl (mp->count);
99   sw_if_index = ntohl (mp->first_sw_if_index);
100
101   vlib_counter_t *vp;
102   u64 packets, bytes;
103   vp = (vlib_counter_t *) mp->data;
104
105   switch (mp->vnet_counter_type)
106     {
107     case VNET_INTERFACE_COUNTER_RX:
108       counter_name = "rx";
109       break;
110     case VNET_INTERFACE_COUNTER_TX:
111       counter_name = "tx";
112       break;
113     default:
114       counter_name = "bogus";
115       break;
116     }
117   for (i = 0; i < count; i++)
118     {
119       packets = clib_mem_unaligned (&vp->packets, u64);
120       packets = clib_net_to_host_u64 (packets);
121       bytes = clib_mem_unaligned (&vp->bytes, u64);
122       bytes = clib_net_to_host_u64 (bytes);
123       vp++;
124       s = format (s, "%U.%s.packets %lld\n",
125                   format_vnet_sw_if_index_name,
126                   sm->vnet_main, sw_if_index, counter_name, packets);
127       s = format (s, "%U.%s.bytes %lld\n",
128                   format_vnet_sw_if_index_name,
129                   sm->vnet_main, sw_if_index, counter_name, bytes);
130       sw_if_index++;
131     }
132   return s;
133 }
134
135 u8 *
136 format_vnet_interface_simple_counters (u8 * s, va_list * args)
137 {
138   stats_main_t *sm = &stats_main;
139   vl_api_vnet_interface_simple_counters_t *mp =
140     va_arg (*args, vl_api_vnet_interface_simple_counters_t *);
141   char *counter_name;
142   u32 count, sw_if_index;
143   count = ntohl (mp->count);
144   sw_if_index = ntohl (mp->first_sw_if_index);
145   u64 *vp, v;
146   vp = (u64 *) mp->data;
147   int i;
148
149   switch (mp->vnet_counter_type)
150     {
151     case VNET_INTERFACE_COUNTER_DROP:
152       counter_name = "drop";
153       break;
154     case VNET_INTERFACE_COUNTER_PUNT:
155       counter_name = "punt";
156       break;
157     case VNET_INTERFACE_COUNTER_IP4:
158       counter_name = "ip4";
159       break;
160     case VNET_INTERFACE_COUNTER_IP6:
161       counter_name = "ip6";
162       break;
163     case VNET_INTERFACE_COUNTER_RX_NO_BUF:
164       counter_name = "rx-no-buff";
165       break;
166     case VNET_INTERFACE_COUNTER_RX_MISS:
167       counter_name = "rx-miss";
168       break;
169     case VNET_INTERFACE_COUNTER_RX_ERROR:
170       counter_name = "rx-error (fifo-full)";
171       break;
172     case VNET_INTERFACE_COUNTER_TX_ERROR:
173       counter_name = "tx-error (fifo-full)";
174       break;
175     default:
176       counter_name = "bogus";
177       break;
178     }
179   for (i = 0; i < count; i++)
180     {
181       v = clib_mem_unaligned (vp, u64);
182       v = clib_net_to_host_u64 (v);
183       vp++;
184       s = format (s, "%U.%s %lld\n", format_vnet_sw_if_index_name,
185                   sm->vnet_main, sw_if_index, counter_name, v);
186       sw_if_index++;
187     }
188
189   return s;
190 }
191
192 static void
193 dslock (stats_main_t * sm, int release_hint, int tag)
194 {
195   u32 thread_index;
196   data_structure_lock_t *l = sm->data_structure_lock;
197
198   if (PREDICT_FALSE (l == 0))
199     return;
200
201   thread_index = vlib_get_thread_index ();
202   if (l->lock && l->thread_index == thread_index)
203     {
204       l->count++;
205       return;
206     }
207
208   if (release_hint)
209     l->release_hint++;
210
211   while (__sync_lock_test_and_set (&l->lock, 1))
212     /* zzzz */ ;
213   l->tag = tag;
214   l->thread_index = thread_index;
215   l->count = 1;
216 }
217
218 void
219 stats_dslock_with_hint (int hint, int tag)
220 {
221   stats_main_t *sm = &stats_main;
222   dslock (sm, hint, tag);
223 }
224
225 static void
226 dsunlock (stats_main_t * sm)
227 {
228   u32 thread_index;
229   data_structure_lock_t *l = sm->data_structure_lock;
230
231   if (PREDICT_FALSE (l == 0))
232     return;
233
234   thread_index = vlib_get_thread_index ();
235   ASSERT (l->lock && l->thread_index == thread_index);
236   l->count--;
237   if (l->count == 0)
238     {
239       l->tag = -l->tag;
240       l->release_hint = 0;
241       CLIB_MEMORY_BARRIER ();
242       l->lock = 0;
243     }
244 }
245
246 void
247 stats_dsunlock (int hint, int tag)
248 {
249   stats_main_t *sm = &stats_main;
250   dsunlock (sm);
251 }
252
253 static vpe_client_registration_t *
254 get_client_for_stat (u32 reg, u32 item, u32 client_index)
255 {
256   stats_main_t *sm = &stats_main;
257   vpe_client_stats_registration_t *registration;
258   uword *p;
259
260   /* Is there anything listening for item in that reg */
261   p = hash_get (sm->stats_registration_hash[reg], item);
262
263   if (!p)
264     return 0;                   // Fail
265
266   /* If there is, is our client_index one of them */
267   registration = pool_elt_at_index (sm->stats_registrations[reg], p[0]);
268   p = hash_get (registration->client_hash, client_index);
269
270   if (!p)
271     return 0;                   // Fail
272
273   return pool_elt_at_index (registration->clients, p[0]);
274
275 }
276
277 static int
278 set_client_for_stat (u32 reg, u32 item, vpe_client_registration_t * client)
279 {
280   stats_main_t *sm = &stats_main;
281   vpe_client_stats_registration_t *registration;
282   vpe_client_registration_t *cr;
283   uword *p;
284
285   /* Is there anything listening for item in that reg */
286   p = hash_get (sm->stats_registration_hash[reg], item);
287
288   if (!p)
289     {
290       pool_get (sm->stats_registrations[reg], registration);
291       registration->item = item;
292       hash_set (sm->stats_registration_hash[reg], item,
293                 registration - sm->stats_registrations[reg]);
294     }
295   else
296     {
297       registration = pool_elt_at_index (sm->stats_registrations[reg], p[0]);
298     }
299
300   p = hash_get (registration->client_hash, client->client_index);
301
302   if (!p)
303     {
304       pool_get (registration->clients, cr);
305       cr->client_index = client->client_index;
306       cr->client_pid = client->client_pid;
307       hash_set (registration->client_hash, cr->client_index,
308                 cr - registration->clients);
309     }
310
311   return 1;                     //At least one client is doing something ... poll
312 }
313
314 int
315 clear_client_for_stat (u32 reg, u32 item, u32 client_index)
316 {
317   stats_main_t *sm = &stats_main;
318   vpe_client_stats_registration_t *registration;
319   vpe_client_registration_t *client;
320   uword *p;
321   int i, elts;
322
323   /* Clear the client first */
324   /* Is there anything listening for item in that reg */
325   p = hash_get (sm->stats_registration_hash[reg], item);
326
327   if (!p)
328     goto exit;
329
330   /* If there is, is our client_index one of them */
331   registration = pool_elt_at_index (sm->stats_registrations[reg], p[0]);
332   p = hash_get (registration->client_hash, client_index);
333
334   if (!p)
335     goto exit;
336
337   client = pool_elt_at_index (registration->clients, p[0]);
338   hash_unset (registration->client_hash, client->client_index);
339   pool_put (registration->clients, client);
340
341   /* Now check if that was the last client for that item */
342   if (0 == pool_elts (registration->clients))
343     {
344       hash_unset (sm->stats_registration_hash[reg], item);
345       pool_put (sm->stats_registrations[reg], registration);
346     }
347
348 exit:
349   elts = 0;
350   /* Now check if that was the last item in any of the listened to stats */
351   for (i = 0; i < STATS_REG_N_IDX; i++)
352     {
353       elts += pool_elts (sm->stats_registrations[i]);
354     }
355   return elts;
356 }
357
358 vpe_client_registration_t *
359 get_clients_for_stat (u32 reg, u32 item)
360 {
361   stats_main_t *sm = &stats_main;
362   vpe_client_registration_t *client, *clients = 0;
363   vpe_client_stats_registration_t *registration;
364   uword *p;
365
366   /* Is there anything listening for item in that reg */
367   p = hash_get (sm->stats_registration_hash[reg], item);
368
369   if (!p)
370     return 0;                   // Fail
371
372   /* If there is, is our client_index one of them */
373   registration = pool_elt_at_index (sm->stats_registrations[reg], p[0]);
374
375   vec_reset_length (clients);
376   pool_foreach (client, registration->clients, (
377                                                  {
378                                                  vec_add1 (clients, *client);}
379                 ));
380   return clients;
381 }
382
383
384 static void
385 clear_client_reg (u32 ** registrations)
386 {
387   /* When registrations[x] is a vector of pool indices
388      here is a good place to clean up the pools
389    */
390 #define stats_reg(n) vec_free(registrations[IDX_##n]);
391 #include <vpp/stats/stats.reg>
392 #undef stats_reg
393
394   vec_free (registrations);
395 }
396
397 u32 **
398 init_client_reg (u32 ** registrations)
399 {
400
401   /*
402      Initialise the stats registrations for each
403      type of stat a client can register for as well as
404      a vector of "interested" indexes.
405      Initially this is a u32 of either sw_if_index or fib_index
406      but eventually this should migrate to a pool_index (u32)
407      with a type specific pool that can include more complex things
408      such as timing and structured events.
409    */
410   vec_validate (registrations, STATS_REG_N_IDX);
411 #define stats_reg(n) \
412   vec_reset_length(registrations[IDX_##n]);
413 #include <vpp/stats/stats.reg>
414 #undef stats_reg
415
416   /*
417      When registrations[x] is a vector of pool indices, here
418      is a good place to init the pools.
419    */
420   return registrations;
421 }
422
423 u32 **
424 enable_all_client_reg (u32 ** registrations)
425 {
426
427   /*
428      Enable all stats known by adding
429      ~0 to the index vector. Eventually this
430      should be deprecated.
431    */
432 #define stats_reg(n)                            \
433   vec_add1(registrations[IDX_##n], ~0);
434 #include <vpp/stats/stats.reg>
435 #undef stats_reg
436   return registrations;
437 }
438
439 static void
440 do_simple_interface_counters (stats_main_t * sm)
441 {
442   vl_api_vnet_interface_simple_counters_t *mp = 0;
443   vnet_interface_main_t *im = sm->interface_main;
444   api_main_t *am = sm->api_main;
445   vl_shmem_hdr_t *shmem_hdr = am->shmem_hdr;
446   unix_shared_memory_queue_t *q = shmem_hdr->vl_input_queue;
447   vlib_simple_counter_main_t *cm;
448   u32 items_this_message = 0;
449   u64 v, *vp = 0;
450   int i, n_counts;
451
452   /*
453    * Prevent interface registration from expanding / moving the vectors...
454    * That tends never to happen, so we can hold this lock for a while.
455    */
456   vnet_interface_counter_lock (im);
457
458   vec_foreach (cm, im->sw_if_counters)
459   {
460     n_counts = vlib_simple_counter_n_counters (cm);
461     for (i = 0; i < n_counts; i++)
462       {
463         if (mp == 0)
464           {
465             items_this_message = clib_min (SIMPLE_COUNTER_BATCH_SIZE,
466                                            n_counts - i);
467
468             mp = vl_msg_api_alloc_as_if_client
469               (sizeof (*mp) + items_this_message * sizeof (v));
470             mp->_vl_msg_id = ntohs (VL_API_VNET_INTERFACE_SIMPLE_COUNTERS);
471             mp->vnet_counter_type = cm - im->sw_if_counters;
472             mp->first_sw_if_index = htonl (i);
473             mp->count = 0;
474             vp = (u64 *) mp->data;
475           }
476         v = vlib_get_simple_counter (cm, i);
477         clib_mem_unaligned (vp, u64) = clib_host_to_net_u64 (v);
478         vp++;
479         mp->count++;
480         if (mp->count == items_this_message)
481           {
482             mp->count = htonl (items_this_message);
483             /* Send to the main thread... */
484             vl_msg_api_send_shmem (q, (u8 *) & mp);
485             mp = 0;
486           }
487       }
488     ASSERT (mp == 0);
489   }
490   vnet_interface_counter_unlock (im);
491 }
492
493 void
494 handle_client_registration (vpe_client_registration_t * client, u32 stat,
495                             u32 item, int enable_disable)
496 {
497   stats_main_t *sm = &stats_main;
498   vpe_client_registration_t *rp, _rp;
499
500   rp = get_client_for_stat (stat, item, client->client_index);
501
502   /* Disable case */
503   if (enable_disable == 0)
504     {
505       if (!rp)                  // No client to disable
506         {
507           clib_warning ("pid %d: already disabled for stats...",
508                         client->client_pid);
509           return;
510         }
511       sm->enable_poller =
512         clear_client_for_stat (stat, item, client->client_index);
513       return;
514     }
515   /* Enable case */
516   if (!rp)
517     {
518       rp = &_rp;
519       rp->client_index = client->client_index;
520       rp->client_pid = client->client_pid;
521       sm->enable_poller = set_client_for_stat (stat, item, rp);
522     }
523 }
524
525
526 /**********************************
527  * ALL Interface Combined stats - to be deprecated
528  **********************************/
529
530 /*
531  * This API should be deprecated as _per_interface_ works with ~0 as sw_if_index.
532  */
533 static void
534   vl_api_want_interface_combined_stats_t_handler
535   (vl_api_want_interface_combined_stats_t * mp)
536 {
537   stats_main_t *sm = &stats_main;
538   vpe_client_registration_t rp;
539   vl_api_want_interface_combined_stats_reply_t *rmp;
540   uword *p;
541   i32 retval = 0;
542   unix_shared_memory_queue_t *q;
543   u32 swif;
544
545   swif = ~0;                    //Using same mechanism as _per_interface_
546   rp.client_index = mp->client_index;
547   rp.client_pid = mp->pid;
548
549   handle_client_registration (&rp, IDX_PER_INTERFACE_COMBINED_COUNTERS, swif,
550                               mp->enable_disable);
551
552 reply:
553   q = vl_api_client_index_to_input_queue (mp->client_index);
554
555   if (!q)
556     {
557       sm->enable_poller =
558         clear_client_for_stat (IDX_PER_INTERFACE_COMBINED_COUNTERS, swif,
559                                mp->client_index);
560       return;
561     }
562
563   rmp = vl_msg_api_alloc (sizeof (*rmp));
564   rmp->_vl_msg_id = ntohs (VL_API_WANT_INTERFACE_COMBINED_STATS_REPLY);
565   rmp->context = mp->context;
566   rmp->retval = retval;
567
568   vl_msg_api_send_shmem (q, (u8 *) & rmp);
569 }
570
571 static void
572   vl_api_vnet_interface_combined_counters_t_handler
573   (vl_api_vnet_interface_combined_counters_t * mp)
574 {
575   vpe_client_registration_t *clients, client;
576   stats_main_t *sm = &stats_main;
577   unix_shared_memory_queue_t *q, *q_prev = NULL;
578   vl_api_vnet_interface_combined_counters_t *mp_copy = NULL;
579   u32 mp_size;
580   int i;
581
582   mp_size = sizeof (*mp) + (ntohl (mp->count) * sizeof (vlib_counter_t));
583
584   clients =
585     get_clients_for_stat (IDX_PER_INTERFACE_COMBINED_COUNTERS,
586                           ~0 /*flag for all */ );
587
588   for (i = 0; i < vec_len (clients); i++)
589     {
590       client = clients[i];
591       q = vl_api_client_index_to_input_queue (client.client_index);
592       if (q)
593         {
594           if (q_prev && (q_prev->cursize < q_prev->maxsize))
595             {
596               mp_copy = vl_msg_api_alloc_as_if_client (mp_size);
597               clib_memcpy (mp_copy, mp, mp_size);
598               vl_msg_api_send_shmem (q_prev, (u8 *) & mp);
599               mp = mp_copy;
600             }
601           q_prev = q;
602         }
603     }
604 #if STATS_DEBUG > 0
605   fformat (stdout, "%U\n", format_vnet_combined_counters, mp);
606 #endif
607
608   if (q_prev && (q_prev->cursize < q_prev->maxsize))
609     {
610       vl_msg_api_send_shmem (q_prev, (u8 *) & mp);
611     }
612   else
613     {
614       vl_msg_api_free (mp);
615     }
616 }
617
618 static void
619 do_combined_interface_counters (stats_main_t * sm)
620 {
621   vl_api_vnet_interface_combined_counters_t *mp = 0;
622   vnet_interface_main_t *im = sm->interface_main;
623   api_main_t *am = sm->api_main;
624   vl_shmem_hdr_t *shmem_hdr = am->shmem_hdr;
625   unix_shared_memory_queue_t *q = shmem_hdr->vl_input_queue;
626   vlib_combined_counter_main_t *cm;
627   u32 items_this_message = 0;
628   vlib_counter_t v, *vp = 0;
629   int i, n_counts;
630
631   vnet_interface_counter_lock (im);
632
633   vec_foreach (cm, im->combined_sw_if_counters)
634   {
635     n_counts = vlib_combined_counter_n_counters (cm);
636     for (i = 0; i < n_counts; i++)
637       {
638         if (mp == 0)
639           {
640             items_this_message = clib_min (COMBINED_COUNTER_BATCH_SIZE,
641                                            n_counts - i);
642
643             mp = vl_msg_api_alloc_as_if_client
644               (sizeof (*mp) + items_this_message * sizeof (v));
645             mp->_vl_msg_id = ntohs (VL_API_VNET_INTERFACE_COMBINED_COUNTERS);
646             mp->vnet_counter_type = cm - im->combined_sw_if_counters;
647             mp->first_sw_if_index = htonl (i);
648             mp->count = 0;
649             vp = (vlib_counter_t *) mp->data;
650           }
651         vlib_get_combined_counter (cm, i, &v);
652         clib_mem_unaligned (&vp->packets, u64)
653           = clib_host_to_net_u64 (v.packets);
654         clib_mem_unaligned (&vp->bytes, u64) = clib_host_to_net_u64 (v.bytes);
655         vp++;
656         mp->count++;
657         if (mp->count == items_this_message)
658           {
659             mp->count = htonl (items_this_message);
660             /* Send to the main thread... */
661             vl_msg_api_send_shmem (q, (u8 *) & mp);
662             mp = 0;
663           }
664       }
665     ASSERT (mp == 0);
666   }
667   vnet_interface_counter_unlock (im);
668 }
669
670 /**********************************
671  * Per Interface Combined stats
672  **********************************/
673
674 /* Request from client registering interfaces it wants */
675 static void
676   vl_api_want_per_interface_combined_stats_t_handler
677   (vl_api_want_per_interface_combined_stats_t * mp)
678 {
679   stats_main_t *sm = &stats_main;
680   vpe_client_registration_t rp;
681   vl_api_want_per_interface_combined_stats_reply_t *rmp;
682   vlib_combined_counter_main_t *cm;
683   uword *p;
684   i32 retval = 0;
685   unix_shared_memory_queue_t *q;
686   int i;
687   u32 swif;
688
689   // Validate we have good sw_if_indexes before registering
690   for (i = 0; i < mp->num; i++)
691     {
692       swif = mp->sw_ifs[i];
693
694       /* Check its a real sw_if_index that the client is allowed to see */
695       if (swif != ~0)
696         {
697           if (pool_is_free_index (sm->interface_main->sw_interfaces, swif))
698             {
699               retval = VNET_API_ERROR_INVALID_SW_IF_INDEX;
700               goto reply;
701             }
702         }
703     }
704
705   for (i = 0; i < mp->num; i++)
706     {
707       swif = mp->sw_ifs[i];
708
709       rp.client_index = mp->client_index;
710       rp.client_pid = mp->pid;
711       handle_client_registration (&rp, IDX_PER_INTERFACE_COMBINED_COUNTERS,
712                                   swif, mp->enable_disable);
713     }
714
715 reply:
716   q = vl_api_client_index_to_input_queue (mp->client_index);
717
718   if (!q)
719     {
720       for (i = 0; i < mp->num; i++)
721         {
722           swif = mp->sw_ifs[i];
723           sm->enable_poller =
724             clear_client_for_stat (IDX_PER_INTERFACE_COMBINED_COUNTERS, swif,
725                                    mp->client_index);
726         }
727       return;
728     }
729
730   rmp = vl_msg_api_alloc (sizeof (*rmp));
731   rmp->_vl_msg_id = ntohs (VL_API_WANT_PER_INTERFACE_COMBINED_STATS_REPLY);
732   rmp->context = mp->context;
733   rmp->retval = retval;
734
735   vl_msg_api_send_shmem (q, (u8 *) & rmp);
736 }
737
738 /* Per Interface Combined distribution to client */
739 static void
740 do_combined_per_interface_counters (stats_main_t * sm)
741 {
742   vl_api_vnet_per_interface_combined_counters_t *mp = 0;
743   vnet_interface_main_t *im = sm->interface_main;
744   api_main_t *am = sm->api_main;
745   vl_shmem_hdr_t *shmem_hdr = am->shmem_hdr;
746   unix_shared_memory_queue_t *q = NULL;
747   vlib_combined_counter_main_t *cm;
748   /*
749    * items_this_message will eventually be used to optimise the batching
750    * of per client messages for each stat. For now setting this to 1 then
751    * iterate. This will not affect API.
752    *
753    * FIXME instead of enqueueing here, this should be sent to a batch
754    * storer for per-client transmission. Each "mp" sent would be a single entry
755    * and if a client is listening to other sw_if_indexes for same, it would be
756    * appended to that *mp
757    */
758   u32 items_this_message = 1;
759   vnet_combined_counter_t *vp = 0;
760   vlib_counter_t v;
761   int i, j;
762   u32 timestamp;
763   vpe_client_stats_registration_t *reg;
764   vpe_client_registration_t *client;
765   u32 *sw_if_index = 0;
766
767   /*
768      FIXME(s):
769      - capturing the timestamp of the counters "when VPP knew them" is important.
770      Less so is that the timing of the delivery to the control plane be in the same
771      timescale.
772
773      i.e. As long as the control plane can delta messages from VPP and work out
774      velocity etc based on the timestamp, it can do so in a more "batch mode".
775
776      It would be beneficial to keep a "per-client" message queue, and then
777      batch all the stat messages for a client into one message, with
778      discrete timestamps.
779
780      Given this particular API is for "per interface" one assumes that the scale
781      is less than the ~0 case, which the prior API is suited for.
782    */
783   vnet_interface_counter_lock (im);
784
785   timestamp = vlib_time_now (sm->vlib_main);
786
787   vec_reset_length (sm->regs_tmp);
788   pool_foreach (reg,
789                 sm->stats_registrations[IDX_PER_INTERFACE_COMBINED_COUNTERS],
790                 (
791                     {
792                     vec_add1 (sm->regs_tmp, reg);}));
793
794   for (i = 0; i < vec_len (sm->regs_tmp); i++)
795     {
796       reg = sm->regs_tmp[i];
797       if (reg->item == ~0)
798         {
799           vnet_interface_counter_unlock (im);
800           do_combined_interface_counters (sm);
801           vnet_interface_counter_lock (im);
802           continue;
803         }
804       vec_reset_length (sm->clients_tmp);
805       pool_foreach (client, reg->clients, (
806                                             {
807                                             vec_add1 (sm->clients_tmp,
808                                                       client);}
809                     ));
810
811       //FIXME - should be doing non-variant part of mp here and managing
812       // any alloc per client in that vec_foreach
813       for (j = 0; j < vec_len (sm->clients_tmp); j++)
814         {
815           client = sm->clients_tmp[j];
816           q = vl_api_client_index_to_input_queue (client->client_index);
817
818           //Client may have disconnected abrubtly, clean up so we don't poll nothing.
819           if (!q)
820             {
821               sm->enable_poller =
822                 clear_client_for_stat (IDX_PER_INTERFACE_COMBINED_COUNTERS,
823                                        reg->item, client->client_index);
824               continue;
825             }
826
827           mp = vl_msg_api_alloc (sizeof (*mp) +
828                                  (items_this_message *
829                                   (sizeof (*vp) /* rx */ )));
830
831           // FIXME when optimising for items_this_message > 1 need to include a
832           // SIMPLE_INTERFACE_BATCH_SIZE check.
833           mp->_vl_msg_id =
834             ntohs (VL_API_VNET_PER_INTERFACE_COMBINED_COUNTERS);
835
836           mp->count = items_this_message;
837           mp->timestamp = timestamp;
838           vp = (vnet_combined_counter_t *) mp->data;
839
840           vp->sw_if_index = htonl (reg->item);
841
842           cm = im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_RX;
843           vlib_get_combined_counter (cm, reg->item, &v);
844           clib_mem_unaligned (&vp->rx_packets, u64)
845             = clib_host_to_net_u64 (v.packets);
846           clib_mem_unaligned (&vp->rx_bytes, u64) =
847             clib_host_to_net_u64 (v.bytes);
848
849
850           /* TX vlib_counter_t packets/bytes */
851           cm = im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_TX;
852           vlib_get_combined_counter (cm, reg->item, &v);
853           clib_mem_unaligned (&vp->tx_packets, u64)
854             = clib_host_to_net_u64 (v.packets);
855           clib_mem_unaligned (&vp->tx_bytes, u64) =
856             clib_host_to_net_u64 (v.bytes);
857
858           vl_msg_api_send_shmem (q, (u8 *) & mp);
859         }
860     }
861
862   vnet_interface_counter_unlock (im);
863 }
864
865 /**********************************
866  * Per Interface simple stats
867  **********************************/
868
869 /* Request from client registering interfaces it wants */
870 static void
871   vl_api_want_per_interface_simple_stats_t_handler
872   (vl_api_want_per_interface_simple_stats_t * mp)
873 {
874   stats_main_t *sm = &stats_main;
875   vpe_client_registration_t rp;
876   vl_api_want_per_interface_simple_stats_reply_t *rmp;
877   vlib_simple_counter_main_t *cm;
878   uword *p;
879   i32 retval = 0;
880   unix_shared_memory_queue_t *q;
881   int i;
882   u32 swif;
883
884   for (i = 0; i < mp->num; i++)
885     {
886       swif = mp->sw_ifs[i];
887
888       /* Check its a real sw_if_index that the client is allowed to see */
889       if (swif != ~0)
890         {
891           if (pool_is_free_index (sm->interface_main->sw_interfaces, swif))
892             {
893               retval = VNET_API_ERROR_INVALID_SW_IF_INDEX;
894               goto reply;
895             }
896         }
897     }
898
899   for (i = 0; i < mp->num; i++)
900     {
901       swif = mp->sw_ifs[i];
902
903       rp.client_index = mp->client_index;
904       rp.client_pid = mp->pid;
905       handle_client_registration (&rp, IDX_PER_INTERFACE_SIMPLE_COUNTERS,
906                                   swif, mp->enable_disable);
907     }
908
909 reply:
910   q = vl_api_client_index_to_input_queue (mp->client_index);
911
912   //Client may have disconnected abrubtly, clean up so we don't poll nothing.
913   if (!q)
914     {
915       for (i = 0; i < mp->num; i++)
916         {
917           swif = mp->sw_ifs[i];
918           sm->enable_poller =
919             clear_client_for_stat (IDX_PER_INTERFACE_SIMPLE_COUNTERS, swif,
920                                    mp->client_index);
921         }
922
923       return;
924     }
925
926
927   rmp = vl_msg_api_alloc (sizeof (*rmp));
928   rmp->_vl_msg_id = ntohs (VL_API_WANT_PER_INTERFACE_SIMPLE_STATS_REPLY);
929   rmp->context = mp->context;
930   rmp->retval = retval;
931
932   vl_msg_api_send_shmem (q, (u8 *) & rmp);
933 }
934
935 /* Per Interface Simple distribution to client */
936 static void
937 do_simple_per_interface_counters (stats_main_t * sm)
938 {
939   vl_api_vnet_per_interface_simple_counters_t *mp = 0;
940   vnet_interface_main_t *im = sm->interface_main;
941   api_main_t *am = sm->api_main;
942   vl_shmem_hdr_t *shmem_hdr = am->shmem_hdr;
943   unix_shared_memory_queue_t *q = NULL;
944   vlib_simple_counter_main_t *cm;
945   /*
946    * items_this_message will eventually be used to optimise the batching
947    * of per client messages for each stat. For now setting this to 1 then
948    * iterate. This will not affect API.
949    *
950    * FIXME instead of enqueueing here, this should be sent to a batch
951    * storer for per-client transmission. Each "mp" sent would be a single entry
952    * and if a client is listening to other sw_if_indexes for same, it would be
953    * appended to that *mp
954    */
955   u32 items_this_message = 1;
956   int i, j, size;
957   vpe_client_stats_registration_t *reg;
958   vpe_client_registration_t *client;
959   u32 timestamp;
960   u32 count;
961   vnet_simple_counter_t *vp = 0;
962   counter_t v;
963
964   /*
965      FIXME(s):
966      - capturing the timestamp of the counters "when VPP knew them" is important.
967      Less so is that the timing of the delivery to the control plane be in the same
968      timescale.
969
970      i.e. As long as the control plane can delta messages from VPP and work out
971      velocity etc based on the timestamp, it can do so in a more "batch mode".
972
973      It would be beneficial to keep a "per-client" message queue, and then
974      batch all the stat messages for a client into one message, with
975      discrete timestamps.
976
977      Given this particular API is for "per interface" one assumes that the scale
978      is less than the ~0 case, which the prior API is suited for.
979    */
980   vnet_interface_counter_lock (im);
981
982   timestamp = vlib_time_now (sm->vlib_main);
983
984   vec_reset_length (sm->regs_tmp);
985   pool_foreach (reg,
986                 sm->stats_registrations[IDX_PER_INTERFACE_SIMPLE_COUNTERS], (
987                                                                                {
988                                                                                vec_add1
989                                                                                (sm->regs_tmp,
990                                                                                 reg);}));
991
992   for (i = 0; i < vec_len (sm->regs_tmp); i++)
993     {
994       reg = sm->regs_tmp[i];
995       if (reg->item == ~0)
996         {
997           vnet_interface_counter_unlock (im);
998           do_simple_interface_counters (sm);
999           vnet_interface_counter_lock (im);
1000           continue;
1001         }
1002       vec_reset_length (sm->clients_tmp);
1003       pool_foreach (client, reg->clients, (
1004                                             {
1005                                             vec_add1 (sm->clients_tmp,
1006                                                       client);}
1007                     ));
1008
1009       //FIXME - should be doing non-variant part of mp here and managing
1010       // any alloc per client in that vec_foreach
1011       for (j = 0; j < vec_len (sm->clients_tmp); j++)
1012         {
1013           client = sm->clients_tmp[j];
1014           q = vl_api_client_index_to_input_queue (client->client_index);
1015
1016           //Client may have disconnected abrubtly, clean up so we don't poll nothing.
1017           if (!q)
1018             {
1019               sm->enable_poller =
1020                 clear_client_for_stat (IDX_PER_INTERFACE_SIMPLE_COUNTERS,
1021                                        reg->item, client->client_index);
1022               continue;
1023             }
1024
1025           size = (sizeof (*mp) + (items_this_message * (sizeof (u64) * 10)));
1026           mp = vl_msg_api_alloc (size);
1027           // FIXME when optimising for items_this_message > 1 need to include a
1028           // SIMPLE_INTERFACE_BATCH_SIZE check.
1029           mp->_vl_msg_id = ntohs (VL_API_VNET_PER_INTERFACE_SIMPLE_COUNTERS);
1030
1031           mp->count = items_this_message;
1032           mp->timestamp = timestamp;
1033           vp = (vnet_simple_counter_t *) mp->data;
1034
1035           vp->sw_if_index = htonl (reg->item);
1036
1037           //FIXME will be simpler with a preprocessor macro
1038           // VNET_INTERFACE_COUNTER_DROP
1039           cm = im->sw_if_counters + VNET_INTERFACE_COUNTER_DROP;
1040           v = vlib_get_simple_counter (cm, reg->item);
1041           clib_mem_unaligned (&vp->drop, u64) = clib_host_to_net_u64 (v);
1042
1043           // VNET_INTERFACE_COUNTER_PUNT
1044           cm = im->sw_if_counters + VNET_INTERFACE_COUNTER_PUNT;
1045           v = vlib_get_simple_counter (cm, reg->item);
1046           clib_mem_unaligned (&vp->punt, u64) = clib_host_to_net_u64 (v);
1047
1048           // VNET_INTERFACE_COUNTER_IP4
1049           cm = im->sw_if_counters + VNET_INTERFACE_COUNTER_IP4;
1050           v = vlib_get_simple_counter (cm, reg->item);
1051           clib_mem_unaligned (&vp->rx_ip4, u64) = clib_host_to_net_u64 (v);
1052
1053           //VNET_INTERFACE_COUNTER_IP6
1054           cm = im->sw_if_counters + VNET_INTERFACE_COUNTER_IP6;
1055           v = vlib_get_simple_counter (cm, reg->item);
1056           clib_mem_unaligned (&vp->rx_ip6, u64) = clib_host_to_net_u64 (v);
1057
1058           //VNET_INTERFACE_COUNTER_RX_NO_BUF
1059           cm = im->sw_if_counters + VNET_INTERFACE_COUNTER_RX_NO_BUF;
1060           v = vlib_get_simple_counter (cm, reg->item);
1061           clib_mem_unaligned (&vp->rx_no_buffer, u64) =
1062             clib_host_to_net_u64 (v);
1063
1064           //VNET_INTERFACE_COUNTER_RX_MISS
1065           cm = im->sw_if_counters + VNET_INTERFACE_COUNTER_RX_MISS;
1066           v = vlib_get_simple_counter (cm, reg->item);
1067           clib_mem_unaligned (&vp->rx_miss, u64) = clib_host_to_net_u64 (v);
1068
1069           //VNET_INTERFACE_COUNTER_RX_ERROR
1070           cm = im->sw_if_counters + VNET_INTERFACE_COUNTER_RX_ERROR;
1071           v = vlib_get_simple_counter (cm, reg->item);
1072           clib_mem_unaligned (&vp->rx_error, u64) = clib_host_to_net_u64 (v);
1073
1074           //VNET_INTERFACE_COUNTER_TX_ERROR
1075           cm = im->sw_if_counters + VNET_INTERFACE_COUNTER_TX_ERROR;
1076           v = vlib_get_simple_counter (cm, reg->item);
1077           clib_mem_unaligned (&vp->tx_error, u64) = clib_host_to_net_u64 (v);
1078
1079           //VNET_INTERFACE_COUNTER_MPLS
1080           cm = im->sw_if_counters + VNET_INTERFACE_COUNTER_MPLS;
1081           v = vlib_get_simple_counter (cm, reg->item);
1082           clib_mem_unaligned (&vp->rx_mpls, u64) = clib_host_to_net_u64 (v);
1083
1084           vl_msg_api_send_shmem (q, (u8 *) & mp);
1085         }
1086     }
1087
1088   vnet_interface_counter_unlock (im);
1089 }
1090
1091 /**********************************
1092  * Per FIB IP4 stats
1093  **********************************/
1094
1095 static void
1096 ip46_fib_stats_delay (stats_main_t * sm, u32 sec, u32 nsec)
1097 {
1098   struct timespec _req, *req = &_req;
1099   struct timespec _rem, *rem = &_rem;
1100
1101   req->tv_sec = sec;
1102   req->tv_nsec = nsec;
1103   while (1)
1104     {
1105       if (nanosleep (req, rem) == 0)
1106         break;
1107       *req = *rem;
1108       if (errno == EINTR)
1109         continue;
1110       clib_unix_warning ("nanosleep");
1111       break;
1112     }
1113 }
1114
1115 /**
1116  * @brief The context passed when collecting adjacency counters
1117  */
1118 typedef struct ip4_nbr_stats_ctx_t_
1119 {
1120   /**
1121    * The SW IF index all these adjs belong to
1122    */
1123   u32 sw_if_index;
1124
1125   /**
1126    * A vector of ip4 nbr counters
1127    */
1128   vl_api_ip4_nbr_counter_t *counters;
1129 } ip4_nbr_stats_ctx_t;
1130
1131 static adj_walk_rc_t
1132 ip4_nbr_stats_cb (adj_index_t ai, void *arg)
1133 {
1134   vl_api_ip4_nbr_counter_t *vl_counter;
1135   vlib_counter_t adj_counter;
1136   ip4_nbr_stats_ctx_t *ctx;
1137   ip_adjacency_t *adj;
1138
1139   ctx = arg;
1140   vlib_get_combined_counter (&adjacency_counters, ai, &adj_counter);
1141
1142   if (0 != adj_counter.packets)
1143     {
1144       vec_add2 (ctx->counters, vl_counter, 1);
1145       adj = adj_get (ai);
1146
1147       vl_counter->packets = clib_host_to_net_u64 (adj_counter.packets);
1148       vl_counter->bytes = clib_host_to_net_u64 (adj_counter.bytes);
1149       vl_counter->address = adj->sub_type.nbr.next_hop.ip4.as_u32;
1150       vl_counter->link_type = adj->ia_link;
1151     }
1152   return (ADJ_WALK_RC_CONTINUE);
1153 }
1154
1155 #define MIN(x,y) (((x)<(y))?(x):(y))
1156
1157 static void
1158 ip4_nbr_ship (stats_main_t * sm, ip4_nbr_stats_ctx_t * ctx)
1159 {
1160   api_main_t *am = sm->api_main;
1161   vl_shmem_hdr_t *shmem_hdr = am->shmem_hdr;
1162   unix_shared_memory_queue_t *q = shmem_hdr->vl_input_queue;
1163   vl_api_vnet_ip4_nbr_counters_t *mp = 0;
1164   int first = 0;
1165
1166   /*
1167    * If the walk context has counters, which may be left over from the last
1168    * suspend, then we continue from there.
1169    */
1170   while (0 != vec_len (ctx->counters))
1171     {
1172       u32 n_items = MIN (vec_len (ctx->counters),
1173                          IP4_FIB_COUNTER_BATCH_SIZE);
1174       u8 pause = 0;
1175
1176       dslock (sm, 0 /* release hint */ , 1 /* tag */ );
1177
1178       mp = vl_msg_api_alloc_as_if_client (sizeof (*mp) +
1179                                           (n_items *
1180                                            sizeof
1181                                            (vl_api_ip4_nbr_counter_t)));
1182       mp->_vl_msg_id = ntohs (VL_API_VNET_IP4_NBR_COUNTERS);
1183       mp->count = ntohl (n_items);
1184       mp->sw_if_index = ntohl (ctx->sw_if_index);
1185       mp->begin = first;
1186       first = 0;
1187
1188       /*
1189        * copy the counters from the back of the context, then we can easily
1190        * 'erase' them by resetting the vector length.
1191        * The order we push the stats to the caller is not important.
1192        */
1193       clib_memcpy (mp->c,
1194                    &ctx->counters[vec_len (ctx->counters) - n_items],
1195                    n_items * sizeof (*ctx->counters));
1196
1197       _vec_len (ctx->counters) = vec_len (ctx->counters) - n_items;
1198
1199       /*
1200        * send to the shm q
1201        */
1202       unix_shared_memory_queue_lock (q);
1203       pause = unix_shared_memory_queue_is_full (q);
1204
1205       vl_msg_api_send_shmem_nolock (q, (u8 *) & mp);
1206       unix_shared_memory_queue_unlock (q);
1207       dsunlock (sm);
1208
1209       if (pause)
1210         ip46_fib_stats_delay (sm, 0 /* sec */ ,
1211                               STATS_RELEASE_DELAY_NS);
1212     }
1213 }
1214
1215 static void
1216 do_ip4_nbr_counters (stats_main_t * sm)
1217 {
1218   vnet_main_t *vnm = vnet_get_main ();
1219   vnet_interface_main_t *im = &vnm->interface_main;
1220   vnet_sw_interface_t *si;
1221
1222   ip4_nbr_stats_ctx_t ctx = {
1223     .sw_if_index = 0,
1224     .counters = NULL,
1225   };
1226
1227   /* *INDENT-OFF* */
1228   pool_foreach (si, im->sw_interfaces,
1229   ({
1230     /*
1231      * update the interface we are now concerned with
1232      */
1233     ctx.sw_if_index = si->sw_if_index;
1234
1235     /*
1236      * we are about to walk another interface, so we shouldn't have any pending
1237      * stats to export.
1238      */
1239     ASSERT(ctx.counters == NULL);
1240
1241     /*
1242      * visit each neighbour adjacency on the interface and collect
1243      * its current stats.
1244      * Because we hold the lock the walk is synchronous, so safe to routing
1245      * updates. It's limited in work by the number of adjacenies on an
1246      * interface, which is typically not huge.
1247      */
1248     dslock (sm, 0 /* release hint */ , 1 /* tag */ );
1249     adj_nbr_walk (si->sw_if_index,
1250                   FIB_PROTOCOL_IP4,
1251                   ip4_nbr_stats_cb,
1252                   &ctx);
1253     dsunlock (sm);
1254
1255     /*
1256      * if this interface has some adjacencies with counters then ship them,
1257      * else continue to the next interface.
1258      */
1259     if (NULL != ctx.counters)
1260       {
1261         ip4_nbr_ship(sm, &ctx);
1262       }
1263   }));
1264   /* *INDENT-OFF* */
1265 }
1266
1267 /**
1268  * @brief The context passed when collecting adjacency counters
1269  */
1270 typedef struct ip6_nbr_stats_ctx_t_
1271 {
1272   /**
1273    * The SW IF index all these adjs belong to
1274    */
1275   u32 sw_if_index;
1276
1277   /**
1278    * A vector of ip6 nbr counters
1279    */
1280   vl_api_ip6_nbr_counter_t *counters;
1281 } ip6_nbr_stats_ctx_t;
1282
1283 static adj_walk_rc_t
1284 ip6_nbr_stats_cb (adj_index_t ai,
1285                   void *arg)
1286 {
1287   vl_api_ip6_nbr_counter_t *vl_counter;
1288   vlib_counter_t adj_counter;
1289   ip6_nbr_stats_ctx_t *ctx;
1290   ip_adjacency_t *adj;
1291
1292   ctx = arg;
1293   vlib_get_combined_counter(&adjacency_counters, ai, &adj_counter);
1294
1295   if (0 != adj_counter.packets)
1296     {
1297       vec_add2(ctx->counters, vl_counter, 1);
1298       adj = adj_get(ai);
1299
1300       vl_counter->packets = clib_host_to_net_u64(adj_counter.packets);
1301       vl_counter->bytes   = clib_host_to_net_u64(adj_counter.bytes);
1302       vl_counter->address[0] = adj->sub_type.nbr.next_hop.ip6.as_u64[0];
1303       vl_counter->address[1] = adj->sub_type.nbr.next_hop.ip6.as_u64[1];
1304       vl_counter->link_type = adj->ia_link;
1305     }
1306   return (ADJ_WALK_RC_CONTINUE);
1307 }
1308
1309 #define MIN(x,y) (((x)<(y))?(x):(y))
1310
1311 static void
1312 ip6_nbr_ship (stats_main_t * sm,
1313               ip6_nbr_stats_ctx_t *ctx)
1314 {
1315   api_main_t *am = sm->api_main;
1316   vl_shmem_hdr_t *shmem_hdr = am->shmem_hdr;
1317   unix_shared_memory_queue_t *q = shmem_hdr->vl_input_queue;
1318   vl_api_vnet_ip6_nbr_counters_t *mp = 0;
1319   int first = 0;
1320
1321   /*
1322    * If the walk context has counters, which may be left over from the last
1323    * suspend, then we continue from there.
1324    */
1325   while (0 != vec_len(ctx->counters))
1326     {
1327       u32 n_items = MIN (vec_len (ctx->counters),
1328                          IP6_FIB_COUNTER_BATCH_SIZE);
1329       u8 pause = 0;
1330
1331       dslock (sm, 0 /* release hint */ , 1 /* tag */ );
1332
1333       mp = vl_msg_api_alloc_as_if_client (sizeof (*mp) +
1334                                           (n_items *
1335                                            sizeof
1336                                            (vl_api_ip6_nbr_counter_t)));
1337       mp->_vl_msg_id = ntohs (VL_API_VNET_IP6_NBR_COUNTERS);
1338       mp->count = ntohl (n_items);
1339       mp->sw_if_index = ntohl (ctx->sw_if_index);
1340       mp->begin = first;
1341       first = 0;
1342
1343       /*
1344        * copy the counters from the back of the context, then we can easily
1345        * 'erase' them by resetting the vector length.
1346        * The order we push the stats to the caller is not important.
1347        */
1348       clib_memcpy (mp->c,
1349                    &ctx->counters[vec_len (ctx->counters) - n_items],
1350                    n_items * sizeof (*ctx->counters));
1351
1352       _vec_len (ctx->counters) = vec_len (ctx->counters) - n_items;
1353
1354       /*
1355        * send to the shm q
1356        */
1357       unix_shared_memory_queue_lock (q);
1358       pause = unix_shared_memory_queue_is_full (q);
1359
1360       vl_msg_api_send_shmem_nolock (q, (u8 *) & mp);
1361       unix_shared_memory_queue_unlock (q);
1362       dsunlock (sm);
1363
1364       if (pause)
1365         ip46_fib_stats_delay (sm, 0 /* sec */ ,
1366                               STATS_RELEASE_DELAY_NS);
1367     }
1368 }
1369
1370 static void
1371 do_ip6_nbr_counters (stats_main_t * sm)
1372 {
1373   vnet_main_t *vnm = vnet_get_main ();
1374   vnet_interface_main_t *im = &vnm->interface_main;
1375   vnet_sw_interface_t *si;
1376
1377   ip6_nbr_stats_ctx_t ctx = {
1378     .sw_if_index = 0,
1379     .counters = NULL,
1380   };
1381
1382   /* *INDENT-OFF* */
1383   pool_foreach (si, im->sw_interfaces,
1384   ({
1385     /*
1386      * update the interface we are now concerned with
1387      */
1388     ctx.sw_if_index = si->sw_if_index;
1389
1390     /*
1391      * we are about to walk another interface, so we shouldn't have any pending
1392      * stats to export.
1393      */
1394     ASSERT(ctx.counters == NULL);
1395
1396     /*
1397      * visit each neighbour adjacency on the interface and collect
1398      * its current stats.
1399      * Because we hold the lock the walk is synchronous, so safe to routing
1400      * updates. It's limited in work by the number of adjacenies on an
1401      * interface, which is typically not huge.
1402      */
1403     dslock (sm, 0 /* release hint */ , 1 /* tag */ );
1404     adj_nbr_walk (si->sw_if_index,
1405                   FIB_PROTOCOL_IP6,
1406                   ip6_nbr_stats_cb,
1407                   &ctx);
1408     dsunlock (sm);
1409
1410     /*
1411      * if this interface has some adjacencies with counters then ship them,
1412      * else continue to the next interface.
1413      */
1414     if (NULL != ctx.counters)
1415       {
1416         ip6_nbr_ship(sm, &ctx);
1417       }
1418   }));
1419   /* *INDENT-OFF* */
1420 }
1421
1422 static void
1423 do_ip4_fib_counters (stats_main_t * sm)
1424 {
1425   ip4_main_t *im4 = &ip4_main;
1426   api_main_t *am = sm->api_main;
1427   vl_shmem_hdr_t *shmem_hdr = am->shmem_hdr;
1428   unix_shared_memory_queue_t *q = shmem_hdr->vl_input_queue;
1429   ip4_route_t *r;
1430   fib_table_t *fib;
1431   ip4_fib_t *v4_fib;
1432   do_ip46_fibs_t *do_fibs;
1433   vl_api_vnet_ip4_fib_counters_t *mp = 0;
1434   u32 items_this_message;
1435   vl_api_ip4_fib_counter_t *ctrp = 0;
1436   u32 start_at_fib_index = 0;
1437   int i, j, k;
1438
1439   do_fibs = &sm->do_ip46_fibs;
1440
1441 again:
1442   vec_reset_length (do_fibs->fibs);
1443   /* *INDENT-OFF* */
1444   pool_foreach (fib, im4->fibs,
1445                 ({vec_add1(do_fibs->fibs,fib);}));
1446
1447   /* *INDENT-ON* */
1448
1449   for (j = 0; j < vec_len (do_fibs->fibs); j++)
1450     {
1451       fib = do_fibs->fibs[j];
1452       /* We may have bailed out due to control-plane activity */
1453       while ((fib - im4->fibs) < start_at_fib_index)
1454         continue;
1455
1456       v4_fib = pool_elt_at_index (im4->v4_fibs, fib->ft_index);
1457
1458       if (mp == 0)
1459         {
1460           items_this_message = IP4_FIB_COUNTER_BATCH_SIZE;
1461           mp = vl_msg_api_alloc_as_if_client
1462             (sizeof (*mp) +
1463              items_this_message * sizeof (vl_api_ip4_fib_counter_t));
1464           mp->_vl_msg_id = ntohs (VL_API_VNET_IP4_FIB_COUNTERS);
1465           mp->count = 0;
1466           mp->vrf_id = ntohl (fib->ft_table_id);
1467           ctrp = (vl_api_ip4_fib_counter_t *) mp->c;
1468         }
1469       else
1470         {
1471           /* happens if the last FIB was empty... */
1472           ASSERT (mp->count == 0);
1473           mp->vrf_id = ntohl (fib->ft_table_id);
1474         }
1475
1476       dslock (sm, 0 /* release hint */ , 1 /* tag */ );
1477
1478       vec_reset_length (do_fibs->ip4routes);
1479       vec_reset_length (do_fibs->results);
1480
1481       for (i = 0; i < ARRAY_LEN (v4_fib->fib_entry_by_dst_address); i++)
1482         {
1483           uword *hash = v4_fib->fib_entry_by_dst_address[i];
1484           hash_pair_t *p;
1485           ip4_route_t x;
1486
1487           vec_reset_length (do_fibs->pvec);
1488
1489           x.address_length = i;
1490
1491           hash_foreach_pair (p, hash, (
1492                                         {
1493                                         vec_add1 (do_fibs->pvec, p);}
1494                              ));
1495           for (k = 0; k < vec_len (do_fibs->pvec); k++)
1496             {
1497               p = do_fibs->pvec[k];
1498               x.address.data_u32 = p->key;
1499               x.index = p->value[0];
1500
1501               vec_add1 (do_fibs->ip4routes, x);
1502               if (sm->data_structure_lock->release_hint)
1503                 {
1504                   start_at_fib_index = fib - im4->fibs;
1505                   dsunlock (sm);
1506                   ip46_fib_stats_delay (sm, 0 /* sec */ ,
1507                                         STATS_RELEASE_DELAY_NS);
1508                   mp->count = 0;
1509                   ctrp = (vl_api_ip4_fib_counter_t *) mp->c;
1510                   goto again;
1511                 }
1512             }
1513         }
1514
1515       vec_foreach (r, do_fibs->ip4routes)
1516       {
1517         vlib_counter_t c;
1518         const dpo_id_t *dpo_id;
1519         u32 index;
1520
1521         dpo_id = fib_entry_contribute_ip_forwarding (r->index);
1522         index = (u32) dpo_id->dpoi_index;
1523
1524         vlib_get_combined_counter (&load_balance_main.lbm_to_counters,
1525                                    index, &c);
1526         /*
1527          * If it has actually
1528          * seen at least one packet, send it.
1529          */
1530         if (c.packets > 0)
1531           {
1532
1533             /* already in net byte order */
1534             ctrp->address = r->address.as_u32;
1535             ctrp->address_length = r->address_length;
1536             ctrp->packets = clib_host_to_net_u64 (c.packets);
1537             ctrp->bytes = clib_host_to_net_u64 (c.bytes);
1538             mp->count++;
1539             ctrp++;
1540
1541             if (mp->count == items_this_message)
1542               {
1543                 mp->count = htonl (items_this_message);
1544                 /*
1545                  * If the main thread's input queue is stuffed,
1546                  * drop the data structure lock (which the main thread
1547                  * may want), and take a pause.
1548                  */
1549                 unix_shared_memory_queue_lock (q);
1550                 if (unix_shared_memory_queue_is_full (q))
1551                   {
1552                     dsunlock (sm);
1553                     vl_msg_api_send_shmem_nolock (q, (u8 *) & mp);
1554                     unix_shared_memory_queue_unlock (q);
1555                     mp = 0;
1556                     ip46_fib_stats_delay (sm, 0 /* sec */ ,
1557                                           STATS_RELEASE_DELAY_NS);
1558                     goto again;
1559                   }
1560                 vl_msg_api_send_shmem_nolock (q, (u8 *) & mp);
1561                 unix_shared_memory_queue_unlock (q);
1562
1563                 items_this_message = IP4_FIB_COUNTER_BATCH_SIZE;
1564                 mp = vl_msg_api_alloc_as_if_client
1565                   (sizeof (*mp) +
1566                    items_this_message * sizeof (vl_api_ip4_fib_counter_t));
1567                 mp->_vl_msg_id = ntohs (VL_API_VNET_IP4_FIB_COUNTERS);
1568                 mp->count = 0;
1569                 mp->vrf_id = ntohl (fib->ft_table_id);
1570                 ctrp = (vl_api_ip4_fib_counter_t *) mp->c;
1571               }
1572           }                     /* for each (mp or single) adj */
1573         if (sm->data_structure_lock->release_hint)
1574           {
1575             start_at_fib_index = fib - im4->fibs;
1576             dsunlock (sm);
1577             ip46_fib_stats_delay (sm, 0 /* sec */ , STATS_RELEASE_DELAY_NS);
1578             mp->count = 0;
1579             ctrp = (vl_api_ip4_fib_counter_t *) mp->c;
1580             goto again;
1581           }
1582       }                         /* vec_foreach (routes) */
1583
1584       dsunlock (sm);
1585
1586       /* Flush any data from this fib */
1587       if (mp->count)
1588         {
1589           mp->count = htonl (mp->count);
1590           vl_msg_api_send_shmem (q, (u8 *) & mp);
1591           mp = 0;
1592         }
1593     }
1594
1595   /* If e.g. the last FIB had no reportable routes, free the buffer */
1596   if (mp)
1597     vl_msg_api_free (mp);
1598 }
1599
1600 typedef struct
1601 {
1602   u32 fib_index;
1603   ip6_route_t **routep;
1604   stats_main_t *sm;
1605 } add_routes_in_fib_arg_t;
1606
1607 static void
1608 add_routes_in_fib (BVT (clib_bihash_kv) * kvp, void *arg)
1609 {
1610   add_routes_in_fib_arg_t *ap = arg;
1611   stats_main_t *sm = ap->sm;
1612
1613   if (sm->data_structure_lock->release_hint)
1614     clib_longjmp (&sm->jmp_buf, 1);
1615
1616   if (kvp->key[2] >> 32 == ap->fib_index)
1617     {
1618       ip6_address_t *addr;
1619       ip6_route_t *r;
1620       addr = (ip6_address_t *) kvp;
1621       vec_add2 (*ap->routep, r, 1);
1622       r->address = addr[0];
1623       r->address_length = kvp->key[2] & 0xFF;
1624       r->index = kvp->value;
1625     }
1626 }
1627
1628 static void
1629 do_ip6_fib_counters (stats_main_t * sm)
1630 {
1631   ip6_main_t *im6 = &ip6_main;
1632   api_main_t *am = sm->api_main;
1633   vl_shmem_hdr_t *shmem_hdr = am->shmem_hdr;
1634   unix_shared_memory_queue_t *q = shmem_hdr->vl_input_queue;
1635   ip6_route_t *r;
1636   fib_table_t *fib;
1637   do_ip46_fibs_t *do_fibs;
1638   vl_api_vnet_ip6_fib_counters_t *mp = 0;
1639   u32 items_this_message;
1640   vl_api_ip6_fib_counter_t *ctrp = 0;
1641   u32 start_at_fib_index = 0;
1642   BVT (clib_bihash) * h = &im6->ip6_table[IP6_FIB_TABLE_FWDING].ip6_hash;
1643   add_routes_in_fib_arg_t _a, *a = &_a;
1644   int i;
1645
1646   do_fibs = &sm->do_ip46_fibs;
1647 again:
1648   vec_reset_length (do_fibs->fibs);
1649   /* *INDENT-OFF* */
1650   pool_foreach (fib, im6->fibs,
1651                 ({vec_add1(do_fibs->fibs,fib);}));
1652   /* *INDENT-ON* */
1653
1654
1655   for (i = 0; i < vec_len (do_fibs->fibs); i++)
1656     {
1657       fib = do_fibs->fibs[i];
1658       /* We may have bailed out due to control-plane activity */
1659       while ((fib - im6->fibs) < start_at_fib_index)
1660         continue;
1661
1662       if (mp == 0)
1663         {
1664           items_this_message = IP6_FIB_COUNTER_BATCH_SIZE;
1665           mp = vl_msg_api_alloc_as_if_client
1666             (sizeof (*mp) +
1667              items_this_message * sizeof (vl_api_ip6_fib_counter_t));
1668           mp->_vl_msg_id = ntohs (VL_API_VNET_IP6_FIB_COUNTERS);
1669           mp->count = 0;
1670           mp->vrf_id = ntohl (fib->ft_table_id);
1671           ctrp = (vl_api_ip6_fib_counter_t *) mp->c;
1672         }
1673
1674       dslock (sm, 0 /* release hint */ , 1 /* tag */ );
1675
1676       vec_reset_length (do_fibs->ip6routes);
1677       vec_reset_length (do_fibs->results);
1678
1679       a->fib_index = fib - im6->fibs;
1680       a->routep = &do_fibs->ip6routes;
1681       a->sm = sm;
1682
1683       if (clib_setjmp (&sm->jmp_buf, 0) == 0)
1684         {
1685           start_at_fib_index = fib - im6->fibs;
1686           BV (clib_bihash_foreach_key_value_pair) (h, add_routes_in_fib, a);
1687         }
1688       else
1689         {
1690           dsunlock (sm);
1691           ip46_fib_stats_delay (sm, 0 /* sec */ ,
1692                                 STATS_RELEASE_DELAY_NS);
1693           mp->count = 0;
1694           ctrp = (vl_api_ip6_fib_counter_t *) mp->c;
1695           goto again;
1696         }
1697
1698       vec_foreach (r, do_fibs->ip6routes)
1699       {
1700         vlib_counter_t c;
1701
1702         vlib_get_combined_counter (&load_balance_main.lbm_to_counters,
1703                                    r->index, &c);
1704         /*
1705          * If it has actually
1706          * seen at least one packet, send it.
1707          */
1708         if (c.packets > 0)
1709           {
1710             /* already in net byte order */
1711             ctrp->address[0] = r->address.as_u64[0];
1712             ctrp->address[1] = r->address.as_u64[1];
1713             ctrp->address_length = (u8) r->address_length;
1714             ctrp->packets = clib_host_to_net_u64 (c.packets);
1715             ctrp->bytes = clib_host_to_net_u64 (c.bytes);
1716             mp->count++;
1717             ctrp++;
1718
1719             if (mp->count == items_this_message)
1720               {
1721                 mp->count = htonl (items_this_message);
1722                 /*
1723                  * If the main thread's input queue is stuffed,
1724                  * drop the data structure lock (which the main thread
1725                  * may want), and take a pause.
1726                  */
1727                 unix_shared_memory_queue_lock (q);
1728                 if (unix_shared_memory_queue_is_full (q))
1729                   {
1730                     dsunlock (sm);
1731                     vl_msg_api_send_shmem_nolock (q, (u8 *) & mp);
1732                     unix_shared_memory_queue_unlock (q);
1733                     mp = 0;
1734                     ip46_fib_stats_delay (sm, 0 /* sec */ ,
1735                                           STATS_RELEASE_DELAY_NS);
1736                     goto again;
1737                   }
1738                 vl_msg_api_send_shmem_nolock (q, (u8 *) & mp);
1739                 unix_shared_memory_queue_unlock (q);
1740
1741                 items_this_message = IP6_FIB_COUNTER_BATCH_SIZE;
1742                 mp = vl_msg_api_alloc_as_if_client
1743                   (sizeof (*mp) +
1744                    items_this_message * sizeof (vl_api_ip6_fib_counter_t));
1745                 mp->_vl_msg_id = ntohs (VL_API_VNET_IP6_FIB_COUNTERS);
1746                 mp->count = 0;
1747                 mp->vrf_id = ntohl (fib->ft_table_id);
1748                 ctrp = (vl_api_ip6_fib_counter_t *) mp->c;
1749               }
1750           }
1751
1752         if (sm->data_structure_lock->release_hint)
1753           {
1754             start_at_fib_index = fib - im6->fibs;
1755             dsunlock (sm);
1756             ip46_fib_stats_delay (sm, 0 /* sec */ , STATS_RELEASE_DELAY_NS);
1757             mp->count = 0;
1758             ctrp = (vl_api_ip6_fib_counter_t *) mp->c;
1759             goto again;
1760           }
1761       }                         /* vec_foreach (routes) */
1762
1763       dsunlock (sm);
1764
1765       /* Flush any data from this fib */
1766       if (mp->count)
1767         {
1768           mp->count = htonl (mp->count);
1769           vl_msg_api_send_shmem (q, (u8 *) & mp);
1770           mp = 0;
1771         }
1772     }
1773
1774   /* If e.g. the last FIB had no reportable routes, free the buffer */
1775   if (mp)
1776     vl_msg_api_free (mp);
1777 }
1778
1779 static void
1780 stats_thread_fn (void *arg)
1781 {
1782   stats_main_t *sm = &stats_main;
1783   vlib_worker_thread_t *w = (vlib_worker_thread_t *) arg;
1784   vlib_thread_main_t *tm = vlib_get_thread_main ();
1785
1786   /* stats thread wants no signals. */
1787   {
1788     sigset_t s;
1789     sigfillset (&s);
1790     pthread_sigmask (SIG_SETMASK, &s, 0);
1791   }
1792
1793   if (vec_len (tm->thread_prefix))
1794     vlib_set_thread_name ((char *)
1795                           format (0, "%v_stats%c", tm->thread_prefix, '\0'));
1796
1797   clib_mem_set_heap (w->thread_mheap);
1798
1799   while (1)
1800     {
1801       /* 10 second poll interval */
1802       ip46_fib_stats_delay (sm, 10 /* secs */ , 0 /* nsec */ );
1803
1804       if (!(sm->enable_poller))
1805         {
1806           continue;
1807         }
1808       if (pool_elts
1809           (sm->stats_registrations[IDX_PER_INTERFACE_COMBINED_COUNTERS]))
1810         do_combined_per_interface_counters (sm);
1811
1812       if (pool_elts
1813           (sm->stats_registrations[IDX_PER_INTERFACE_SIMPLE_COUNTERS]))
1814         do_simple_per_interface_counters (sm);
1815
1816       if (pool_elts (sm->stats_registrations[IDX_IP4_FIB_COUNTERS]))
1817         do_ip4_fib_counters (sm);
1818
1819       if (pool_elts (sm->stats_registrations[IDX_IP6_FIB_COUNTERS]))
1820         do_ip6_fib_counters (sm);
1821
1822       if (pool_elts (sm->stats_registrations[IDX_IP4_NBR_COUNTERS]))
1823         do_ip4_nbr_counters (sm);
1824
1825       if (pool_elts (sm->stats_registrations[IDX_IP6_NBR_COUNTERS]))
1826         do_ip6_nbr_counters (sm);
1827     }
1828 }
1829
1830 static void
1831   vl_api_vnet_interface_simple_counters_t_handler
1832   (vl_api_vnet_interface_simple_counters_t * mp)
1833 {
1834   vpe_client_registration_t *clients, client;
1835   stats_main_t *sm = &stats_main;
1836   unix_shared_memory_queue_t *q, *q_prev = NULL;
1837   vl_api_vnet_interface_simple_counters_t *mp_copy = NULL;
1838   u32 mp_size;
1839   int i;
1840
1841   mp_size = sizeof (*mp) + (ntohl (mp->count) * sizeof (u64));
1842
1843   clients =
1844     get_clients_for_stat (IDX_PER_INTERFACE_SIMPLE_COUNTERS,
1845                           ~0 /*flag for all */ );
1846
1847   for (i = 0; i < vec_len (clients); i++)
1848     {
1849       client = clients[i];
1850       q = vl_api_client_index_to_input_queue (client.client_index);
1851       if (q)
1852         {
1853           if (q_prev && (q_prev->cursize < q_prev->maxsize))
1854             {
1855               mp_copy = vl_msg_api_alloc_as_if_client (mp_size);
1856               clib_memcpy (mp_copy, mp, mp_size);
1857               vl_msg_api_send_shmem (q_prev, (u8 *) & mp);
1858               mp = mp_copy;
1859             }
1860           q_prev = q;
1861         }
1862       else
1863         {
1864           sm->enable_poller =
1865             clear_client_for_stat (IDX_PER_INTERFACE_SIMPLE_COUNTERS, ~0,
1866                                    client.client_index);
1867           continue;
1868         }
1869     }
1870
1871 #if STATS_DEBUG > 0
1872   fformat (stdout, "%U\n", format_vnet_simple_counters, mp);
1873 #endif
1874
1875   if (q_prev && (q_prev->cursize < q_prev->maxsize))
1876     {
1877       vl_msg_api_send_shmem (q_prev, (u8 *) & mp);
1878     }
1879   else
1880     {
1881       vl_msg_api_free (mp);
1882     }
1883 }
1884
1885
1886
1887
1888
1889 static void
1890 vl_api_vnet_ip4_fib_counters_t_handler (vl_api_vnet_ip4_fib_counters_t * mp)
1891 {
1892   stats_main_t *sm = &stats_main;
1893   unix_shared_memory_queue_t *q, *q_prev = NULL;
1894   vl_api_vnet_ip4_fib_counters_t *mp_copy = NULL;
1895   u32 mp_size;
1896   vpe_client_registration_t *clients, client;
1897   int i;
1898
1899   mp_size = sizeof (*mp_copy) +
1900     ntohl (mp->count) * sizeof (vl_api_ip4_fib_counter_t);
1901
1902   clients =
1903     get_clients_for_stat (IDX_IP4_FIB_COUNTERS, ~0 /*flag for all */ );
1904
1905   for (i = 0; i < vec_len (clients); i++)
1906     {
1907       client = clients[i];
1908       q = vl_api_client_index_to_input_queue (client.client_index);
1909       if (q)
1910         {
1911           if (q_prev && (q_prev->cursize < q_prev->maxsize))
1912             {
1913               mp_copy = vl_msg_api_alloc_as_if_client (mp_size);
1914               clib_memcpy (mp_copy, mp, mp_size);
1915               vl_msg_api_send_shmem (q_prev, (u8 *) & mp);
1916               mp = mp_copy;
1917             }
1918           q_prev = q;
1919         }
1920       else
1921         {
1922           sm->enable_poller = clear_client_for_stat (IDX_IP4_FIB_COUNTERS,
1923                                                      ~0, client.client_index);
1924           continue;
1925         }
1926     }
1927
1928   if (q_prev && (q_prev->cursize < q_prev->maxsize))
1929     {
1930       vl_msg_api_send_shmem (q_prev, (u8 *) & mp);
1931     }
1932   else
1933     {
1934       vl_msg_api_free (mp);
1935     }
1936 }
1937
1938 static void
1939 vl_api_vnet_ip4_nbr_counters_t_handler (vl_api_vnet_ip4_nbr_counters_t * mp)
1940 {
1941   stats_main_t *sm = &stats_main;
1942   unix_shared_memory_queue_t *q, *q_prev = NULL;
1943   vl_api_vnet_ip4_nbr_counters_t *mp_copy = NULL;
1944   u32 mp_size;
1945   vpe_client_registration_t *clients, client;
1946   int i;
1947
1948   mp_size = sizeof (*mp_copy) +
1949     ntohl (mp->count) * sizeof (vl_api_ip4_nbr_counter_t);
1950
1951   clients =
1952     get_clients_for_stat (IDX_IP4_NBR_COUNTERS, ~0 /*flag for all */ );
1953
1954   for (i = 0; i < vec_len (clients); i++)
1955     {
1956       client = clients[i];
1957       q = vl_api_client_index_to_input_queue (client.client_index);
1958       if (q)
1959         {
1960           if (q_prev && (q_prev->cursize < q_prev->maxsize))
1961             {
1962               mp_copy = vl_msg_api_alloc_as_if_client (mp_size);
1963               clib_memcpy (mp_copy, mp, mp_size);
1964               vl_msg_api_send_shmem (q_prev, (u8 *) & mp);
1965               mp = mp_copy;
1966             }
1967           q_prev = q;
1968         }
1969       else
1970         {
1971           sm->enable_poller = clear_client_for_stat (IDX_IP4_NBR_COUNTERS,
1972                                                      ~0, client.client_index);
1973           continue;
1974         }
1975     }
1976
1977   /* *INDENT-ON* */
1978   if (q_prev && (q_prev->cursize < q_prev->maxsize))
1979     {
1980       vl_msg_api_send_shmem (q_prev, (u8 *) & mp);
1981     }
1982   else
1983     {
1984       vl_msg_api_free (mp);
1985     }
1986 }
1987
1988 static void
1989 vl_api_vnet_ip6_fib_counters_t_handler (vl_api_vnet_ip6_fib_counters_t * mp)
1990 {
1991   stats_main_t *sm = &stats_main;
1992   unix_shared_memory_queue_t *q, *q_prev = NULL;
1993   vl_api_vnet_ip6_fib_counters_t *mp_copy = NULL;
1994   u32 mp_size;
1995   vpe_client_registration_t *clients, client;
1996   int i;
1997
1998   mp_size = sizeof (*mp_copy) +
1999     ntohl (mp->count) * sizeof (vl_api_ip6_fib_counter_t);
2000
2001   clients =
2002     get_clients_for_stat (IDX_IP6_FIB_COUNTERS, ~0 /*flag for all */ );
2003
2004   for (i = 0; i < vec_len (clients); i++)
2005     {
2006       client = clients[i];
2007       q = vl_api_client_index_to_input_queue (client.client_index);
2008       if (q)
2009         {
2010           if (q_prev && (q_prev->cursize < q_prev->maxsize))
2011             {
2012               mp_copy = vl_msg_api_alloc_as_if_client (mp_size);
2013               clib_memcpy (mp_copy, mp, mp_size);
2014               vl_msg_api_send_shmem (q_prev, (u8 *) & mp);
2015               mp = mp_copy;
2016             }
2017           q_prev = q;
2018         }
2019       else
2020         {
2021           sm->enable_poller = clear_client_for_stat (IDX_IP6_FIB_COUNTERS,
2022                                                      ~0, client.client_index);
2023           continue;
2024         }
2025     }
2026   /* *INDENT-ON* */
2027   if (q_prev && (q_prev->cursize < q_prev->maxsize))
2028     {
2029       vl_msg_api_send_shmem (q_prev, (u8 *) & mp);
2030     }
2031   else
2032     {
2033       vl_msg_api_free (mp);
2034     }
2035 }
2036
2037 static void
2038 vl_api_vnet_ip6_nbr_counters_t_handler (vl_api_vnet_ip6_nbr_counters_t * mp)
2039 {
2040   stats_main_t *sm = &stats_main;
2041   unix_shared_memory_queue_t *q, *q_prev = NULL;
2042   vl_api_vnet_ip6_nbr_counters_t *mp_copy = NULL;
2043   u32 mp_size;
2044   vpe_client_registration_t *clients, client;
2045   int i;
2046
2047   mp_size = sizeof (*mp_copy) +
2048     ntohl (mp->count) * sizeof (vl_api_ip6_nbr_counter_t);
2049
2050   clients =
2051     get_clients_for_stat (IDX_IP6_NBR_COUNTERS, ~0 /*flag for all */ );
2052
2053   for (i = 0; i < vec_len (clients); i++)
2054     {
2055       client = clients[i];
2056       q = vl_api_client_index_to_input_queue (client.client_index);
2057       if (q)
2058         {
2059           if (q_prev && (q_prev->cursize < q_prev->maxsize))
2060             {
2061               mp_copy = vl_msg_api_alloc_as_if_client (mp_size);
2062               clib_memcpy (mp_copy, mp, mp_size);
2063               vl_msg_api_send_shmem (q_prev, (u8 *) & mp);
2064               mp = mp_copy;
2065             }
2066           q_prev = q;
2067         }
2068       else
2069         {
2070           sm->enable_poller = clear_client_for_stat (IDX_IP6_NBR_COUNTERS,
2071                                                      ~0, client.client_index);
2072           continue;
2073         }
2074     }
2075   /* *INDENT-ON* */
2076   if (q_prev && (q_prev->cursize < q_prev->maxsize))
2077     {
2078       vl_msg_api_send_shmem (q_prev, (u8 *) & mp);
2079     }
2080   else
2081     {
2082       vl_msg_api_free (mp);
2083     }
2084 }
2085
2086 static void
2087 vl_api_want_stats_t_handler (vl_api_want_stats_t * mp)
2088 {
2089   stats_main_t *sm = &stats_main;
2090   vpe_client_registration_t rp;
2091   vl_api_want_stats_reply_t *rmp;
2092   uword *p;
2093   i32 retval = 0;
2094   u32 item;
2095   unix_shared_memory_queue_t *q;
2096
2097   item = ~0;                    //"ALL THE THINGS IN THE THINGS
2098   rp.client_index = mp->client_index;
2099   rp.client_pid = mp->pid;
2100
2101   handle_client_registration (&rp, IDX_PER_INTERFACE_SIMPLE_COUNTERS,
2102                               item, mp->enable_disable);
2103
2104   handle_client_registration (&rp, IDX_PER_INTERFACE_COMBINED_COUNTERS,
2105                               item, mp->enable_disable);
2106
2107   handle_client_registration (&rp, IDX_IP4_FIB_COUNTERS,
2108                               item, mp->enable_disable);
2109
2110   handle_client_registration (&rp, IDX_IP4_NBR_COUNTERS,
2111                               item, mp->enable_disable);
2112
2113   handle_client_registration (&rp, IDX_IP6_FIB_COUNTERS,
2114                               item, mp->enable_disable);
2115
2116   handle_client_registration (&rp, IDX_IP6_NBR_COUNTERS,
2117                               item, mp->enable_disable);
2118
2119 reply:
2120   q = vl_api_client_index_to_input_queue (mp->client_index);
2121
2122   if (!q)
2123     return;
2124
2125   rmp = vl_msg_api_alloc (sizeof (*rmp));
2126   rmp->_vl_msg_id = ntohs (VL_API_WANT_STATS_REPLY);
2127   rmp->context = mp->context;
2128   rmp->retval = retval;
2129
2130   vl_msg_api_send_shmem (q, (u8 *) & rmp);
2131 }
2132
2133 static void
2134   vl_api_want_interface_simple_stats_t_handler
2135   (vl_api_want_interface_simple_stats_t * mp)
2136 {
2137   stats_main_t *sm = &stats_main;
2138   vpe_client_registration_t rp;
2139   vl_api_want_interface_simple_stats_reply_t *rmp;
2140   uword *p;
2141   i32 retval = 0;
2142   u32 swif;
2143   unix_shared_memory_queue_t *q;
2144
2145   swif = ~0;                    //Using same mechanism as _per_interface_
2146   rp.client_index = mp->client_index;
2147   rp.client_pid = mp->pid;
2148
2149   handle_client_registration (&rp, IDX_PER_INTERFACE_SIMPLE_COUNTERS, swif,
2150                               mp->enable_disable);
2151
2152 reply:
2153   q = vl_api_client_index_to_input_queue (mp->client_index);
2154
2155   if (!q)
2156     {
2157       sm->enable_poller =
2158         clear_client_for_stat (IDX_PER_INTERFACE_SIMPLE_COUNTERS, swif,
2159                                mp->client_index);
2160       return;
2161     }
2162
2163   rmp = vl_msg_api_alloc (sizeof (*rmp));
2164   rmp->_vl_msg_id = ntohs (VL_API_WANT_INTERFACE_SIMPLE_STATS_REPLY);
2165   rmp->context = mp->context;
2166   rmp->retval = retval;
2167
2168   vl_msg_api_send_shmem (q, (u8 *) & rmp);
2169 }
2170
2171
2172 static void
2173 vl_api_want_ip4_fib_stats_t_handler (vl_api_want_ip4_fib_stats_t * mp)
2174 {
2175   stats_main_t *sm = &stats_main;
2176   vpe_client_registration_t rp;
2177   vl_api_want_ip4_fib_stats_reply_t *rmp;
2178   uword *p;
2179   i32 retval = 0;
2180   unix_shared_memory_queue_t *q;
2181   u32 fib;
2182
2183   fib = ~0;                     //Using same mechanism as _per_interface_
2184   rp.client_index = mp->client_index;
2185   rp.client_pid = mp->pid;
2186
2187   handle_client_registration (&rp, IDX_IP4_FIB_COUNTERS, fib,
2188                               mp->enable_disable);
2189
2190 reply:
2191   q = vl_api_client_index_to_input_queue (mp->client_index);
2192
2193   if (!q)
2194     {
2195       sm->enable_poller = clear_client_for_stat (IDX_IP4_FIB_COUNTERS,
2196                                                  fib, mp->client_index);
2197       return;
2198     }
2199
2200   rmp = vl_msg_api_alloc (sizeof (*rmp));
2201   rmp->_vl_msg_id = ntohs (VL_API_WANT_IP4_FIB_STATS_REPLY);
2202   rmp->context = mp->context;
2203   rmp->retval = retval;
2204
2205   vl_msg_api_send_shmem (q, (u8 *) & rmp);
2206 }
2207
2208 static void
2209 vl_api_want_ip6_fib_stats_t_handler (vl_api_want_ip6_fib_stats_t * mp)
2210 {
2211   stats_main_t *sm = &stats_main;
2212   vpe_client_registration_t rp;
2213   vl_api_want_ip4_fib_stats_reply_t *rmp;
2214   uword *p;
2215   i32 retval = 0;
2216   unix_shared_memory_queue_t *q;
2217   u32 fib;
2218
2219   fib = ~0;                     //Using same mechanism as _per_interface_
2220   rp.client_index = mp->client_index;
2221   rp.client_pid = mp->pid;
2222
2223   handle_client_registration (&rp, IDX_IP6_FIB_COUNTERS, fib,
2224                               mp->enable_disable);
2225
2226 reply:
2227   q = vl_api_client_index_to_input_queue (mp->client_index);
2228
2229   if (!q)
2230     {
2231       sm->enable_poller = clear_client_for_stat (IDX_IP6_FIB_COUNTERS,
2232                                                  fib, mp->client_index);
2233       return;
2234     }
2235
2236   rmp = vl_msg_api_alloc (sizeof (*rmp));
2237   rmp->_vl_msg_id = ntohs (VL_API_WANT_IP6_FIB_STATS_REPLY);
2238   rmp->context = mp->context;
2239   rmp->retval = retval;
2240
2241   vl_msg_api_send_shmem (q, (u8 *) & rmp);
2242 }
2243
2244 /* FIXME - NBR stats broken - this will be fixed in subsequent patch */
2245 static void
2246 vl_api_want_ip4_nbr_stats_t_handler (vl_api_want_ip4_nbr_stats_t * mp)
2247 {
2248 }
2249
2250 static void
2251 vl_api_want_ip6_nbr_stats_t_handler (vl_api_want_ip6_nbr_stats_t * mp)
2252 {
2253 }
2254
2255 static void
2256 vl_api_vnet_get_summary_stats_t_handler (vl_api_vnet_get_summary_stats_t * mp)
2257 {
2258   stats_main_t *sm = &stats_main;
2259   vnet_interface_main_t *im = sm->interface_main;
2260   vl_api_vnet_get_summary_stats_reply_t *rmp;
2261   vlib_combined_counter_main_t *cm;
2262   vlib_counter_t v;
2263   int i, which;
2264   u64 total_pkts[VLIB_N_RX_TX];
2265   u64 total_bytes[VLIB_N_RX_TX];
2266
2267   unix_shared_memory_queue_t *q =
2268     vl_api_client_index_to_input_queue (mp->client_index);
2269
2270   if (!q)
2271     {
2272       return;
2273     }
2274
2275   rmp = vl_msg_api_alloc (sizeof (*rmp));
2276   rmp->_vl_msg_id = ntohs (VL_API_VNET_GET_SUMMARY_STATS_REPLY);
2277   rmp->context = mp->context;
2278   rmp->retval = 0;
2279
2280   memset (total_pkts, 0, sizeof (total_pkts));
2281   memset (total_bytes, 0, sizeof (total_bytes));
2282
2283   vnet_interface_counter_lock (im);
2284
2285   vec_foreach (cm, im->combined_sw_if_counters)
2286   {
2287     which = cm - im->combined_sw_if_counters;
2288
2289     for (i = 0; i < vlib_combined_counter_n_counters (cm); i++)
2290       {
2291         vlib_get_combined_counter (cm, i, &v);
2292         total_pkts[which] += v.packets;
2293         total_bytes[which] += v.bytes;
2294       }
2295   }
2296   vnet_interface_counter_unlock (im);
2297
2298   rmp->total_pkts[VLIB_RX] = clib_host_to_net_u64 (total_pkts[VLIB_RX]);
2299   rmp->total_bytes[VLIB_RX] = clib_host_to_net_u64 (total_bytes[VLIB_RX]);
2300   rmp->total_pkts[VLIB_TX] = clib_host_to_net_u64 (total_pkts[VLIB_TX]);
2301   rmp->total_bytes[VLIB_TX] = clib_host_to_net_u64 (total_bytes[VLIB_TX]);
2302   rmp->vector_rate =
2303     clib_host_to_net_u64 (vlib_last_vector_length_per_node (sm->vlib_main));
2304
2305   vl_msg_api_send_shmem (q, (u8 *) & rmp);
2306 }
2307
2308 int
2309 stats_memclnt_delete_callback (u32 client_index)
2310 {
2311   vpe_client_stats_registration_t *rp;
2312   stats_main_t *sm = &stats_main;
2313   uword *p;
2314
2315   // FIXME
2316   /* p = hash_get (sm->stats_registration_hash, client_index); */
2317   /* if (p) */
2318   /*   { */
2319   /*     rp = pool_elt_at_index (sm->stats_registrations, p[0]); */
2320   /*     pool_put (sm->stats_registrations, rp); */
2321   /*     hash_unset (sm->stats_registration_hash, client_index); */
2322   /*   } */
2323
2324   return 0;
2325 }
2326
2327 #define vl_api_vnet_interface_simple_counters_t_endian vl_noop_handler
2328 #define vl_api_vnet_interface_simple_counters_t_print vl_noop_handler
2329 #define vl_api_vnet_interface_combined_counters_t_endian vl_noop_handler
2330 #define vl_api_vnet_interface_combined_counters_t_print vl_noop_handler
2331 #define vl_api_vnet_ip4_fib_counters_t_endian vl_noop_handler
2332 #define vl_api_vnet_ip4_fib_counters_t_print vl_noop_handler
2333 #define vl_api_vnet_ip6_fib_counters_t_endian vl_noop_handler
2334 #define vl_api_vnet_ip6_fib_counters_t_print vl_noop_handler
2335 #define vl_api_vnet_ip4_nbr_counters_t_endian vl_noop_handler
2336 #define vl_api_vnet_ip4_nbr_counters_t_print vl_noop_handler
2337 #define vl_api_vnet_ip6_nbr_counters_t_endian vl_noop_handler
2338 #define vl_api_vnet_ip6_nbr_counters_t_print vl_noop_handler
2339
2340 static clib_error_t *
2341 stats_init (vlib_main_t * vm)
2342 {
2343   stats_main_t *sm = &stats_main;
2344   api_main_t *am = &api_main;
2345   void *vlib_worker_thread_bootstrap_fn (void *arg);
2346
2347   sm->vlib_main = vm;
2348   sm->vnet_main = vnet_get_main ();
2349   sm->interface_main = &vnet_get_main ()->interface_main;
2350   sm->api_main = am;
2351   sm->stats_poll_interval_in_seconds = 10;
2352   sm->data_structure_lock =
2353     clib_mem_alloc_aligned (sizeof (data_structure_lock_t),
2354                             CLIB_CACHE_LINE_BYTES);
2355   memset (sm->data_structure_lock, 0, sizeof (*sm->data_structure_lock));
2356
2357 #define _(N,n)                                                  \
2358     vl_msg_api_set_handlers(VL_API_##N, #n,                     \
2359                            vl_api_##n##_t_handler,              \
2360                            vl_noop_handler,                     \
2361                            vl_api_##n##_t_endian,               \
2362                            vl_api_##n##_t_print,                \
2363                            sizeof(vl_api_##n##_t), 0 /* do NOT trace! */);
2364   foreach_stats_msg;
2365 #undef _
2366
2367   /* tell the msg infra not to free these messages... */
2368   am->message_bounce[VL_API_VNET_INTERFACE_SIMPLE_COUNTERS] = 1;
2369   am->message_bounce[VL_API_VNET_INTERFACE_COMBINED_COUNTERS] = 1;
2370   am->message_bounce[VL_API_VNET_IP4_FIB_COUNTERS] = 1;
2371   am->message_bounce[VL_API_VNET_IP6_FIB_COUNTERS] = 1;
2372   am->message_bounce[VL_API_VNET_IP4_NBR_COUNTERS] = 1;
2373   am->message_bounce[VL_API_VNET_IP6_NBR_COUNTERS] = 1;
2374
2375   /*
2376    * Set up the (msg_name, crc, message-id) table
2377    */
2378   setup_message_id_table (am);
2379
2380   vec_validate (sm->stats_registrations, STATS_REG_N_IDX);
2381   vec_validate (sm->stats_registration_hash, STATS_REG_N_IDX);
2382 #define stats_reg(n)                            \
2383   sm->stats_registrations[IDX_##n] = 0; \
2384   sm->stats_registration_hash[IDX_##n] = 0;
2385 #include <vpp/stats/stats.reg>
2386 #undef stats_reg
2387
2388   return 0;
2389 }
2390
2391 VLIB_INIT_FUNCTION (stats_init);
2392
2393 /* *INDENT-OFF* */
2394 VLIB_REGISTER_THREAD (stats_thread_reg, static) = {
2395   .name = "stats",
2396   .function = stats_thread_fn,
2397   .fixed_count = 1,
2398   .count = 1,
2399   .no_data_structure_clone = 1,
2400   .use_pthreads = 1,
2401 };
2402 /* *INDENT-ON* */
2403
2404 /*
2405  * fd.io coding-style-patch-verification: ON
2406  *
2407  * Local Variables:
2408  * eval: (c-set-style "gnu")
2409  * End:
2410  */