dpdk: Add support for Mellanox ConnectX-4 devices
[vpp.git] / 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 <stats/stats.h>
16 #include <signal.h>
17 #include <vlib/threads.h>
18 #include <vnet/fib/fib_entry.h>
19 #include <vnet/fib/fib_table.h>
20 #include <vnet/dpo/load_balance.h>
21
22 #define STATS_DEBUG 0
23
24 stats_main_t stats_main;
25
26 #include <vnet/ip/ip.h>
27
28 #include <vpp-api/vpe_msg_enum.h>
29
30 #define f64_endian(a)
31 #define f64_print(a,b)
32
33 #define vl_typedefs             /* define message structures */
34 #include <vpp-api/vpe_all_api_h.h>
35 #undef vl_typedefs
36
37 #define vl_endianfun            /* define message structures */
38 #include <vpp-api/vpe_all_api_h.h>
39 #undef vl_endianfun
40
41 /* instantiate all the print functions we know about */
42 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
43 #define vl_printfun
44 #include <vpp-api/vpe_all_api_h.h>
45 #undef vl_printfun
46
47 #define foreach_stats_msg                               \
48 _(WANT_STATS, want_stats)                               \
49 _(WANT_STATS_REPLY, want_stats_reply)                   \
50 _(VNET_INTERFACE_COUNTERS, vnet_interface_counters)     \
51 _(VNET_IP4_FIB_COUNTERS, vnet_ip4_fib_counters)         \
52 _(VNET_IP6_FIB_COUNTERS, vnet_ip6_fib_counters)
53
54 /* These constants ensure msg sizes <= 1024, aka ring allocation */
55 #define SIMPLE_COUNTER_BATCH_SIZE       126
56 #define COMBINED_COUNTER_BATCH_SIZE     63
57 #define IP4_FIB_COUNTER_BATCH_SIZE      48
58 #define IP6_FIB_COUNTER_BATCH_SIZE      30
59
60 /* 5ms */
61 #define STATS_RELEASE_DELAY_NS (1000 * 1000 * 5)
62 /*                              ns/us  us/ms        */
63
64 void
65 dslock (stats_main_t * sm, int release_hint, int tag)
66 {
67   u32 thread_id;
68   data_structure_lock_t *l = sm->data_structure_lock;
69
70   if (PREDICT_FALSE (l == 0))
71     return;
72
73   thread_id = os_get_cpu_number ();
74   if (l->lock && l->thread_id == thread_id)
75     {
76       l->count++;
77       return;
78     }
79
80   if (release_hint)
81     l->release_hint++;
82
83   while (__sync_lock_test_and_set (&l->lock, 1))
84     /* zzzz */ ;
85   l->tag = tag;
86   l->thread_id = thread_id;
87   l->count = 1;
88 }
89
90 void
91 stats_dslock_with_hint (int hint, int tag)
92 {
93   stats_main_t *sm = &stats_main;
94   dslock (sm, hint, tag);
95 }
96
97 void
98 dsunlock (stats_main_t * sm)
99 {
100   u32 thread_id;
101   data_structure_lock_t *l = sm->data_structure_lock;
102
103   if (PREDICT_FALSE (l == 0))
104     return;
105
106   thread_id = os_get_cpu_number ();
107   ASSERT (l->lock && l->thread_id == thread_id);
108   l->count--;
109   if (l->count == 0)
110     {
111       l->tag = -l->tag;
112       l->release_hint = 0;
113       CLIB_MEMORY_BARRIER ();
114       l->lock = 0;
115     }
116 }
117
118 void
119 stats_dsunlock (int hint, int tag)
120 {
121   stats_main_t *sm = &stats_main;
122   dsunlock (sm);
123 }
124
125 static void
126 do_simple_interface_counters (stats_main_t * sm)
127 {
128   vl_api_vnet_interface_counters_t *mp = 0;
129   vnet_interface_main_t *im = sm->interface_main;
130   api_main_t *am = sm->api_main;
131   vl_shmem_hdr_t *shmem_hdr = am->shmem_hdr;
132   unix_shared_memory_queue_t *q = shmem_hdr->vl_input_queue;
133   vlib_simple_counter_main_t *cm;
134   u32 items_this_message = 0;
135   u64 v, *vp = 0;
136   int i;
137
138   /*
139    * Prevent interface registration from expanding / moving the vectors...
140    * That tends never to happen, so we can hold this lock for a while.
141    */
142   vnet_interface_counter_lock (im);
143
144   vec_foreach (cm, im->sw_if_counters)
145   {
146
147     for (i = 0; i < vec_len (cm->maxi); i++)
148       {
149         if (mp == 0)
150           {
151             items_this_message = clib_min (SIMPLE_COUNTER_BATCH_SIZE,
152                                            vec_len (cm->maxi) - i);
153
154             mp = vl_msg_api_alloc_as_if_client
155               (sizeof (*mp) + items_this_message * sizeof (v));
156             mp->_vl_msg_id = ntohs (VL_API_VNET_INTERFACE_COUNTERS);
157             mp->vnet_counter_type = cm - im->sw_if_counters;
158             mp->is_combined = 0;
159             mp->first_sw_if_index = htonl (i);
160             mp->count = 0;
161             vp = (u64 *) mp->data;
162           }
163         v = vlib_get_simple_counter (cm, i);
164         clib_mem_unaligned (vp, u64) = clib_host_to_net_u64 (v);
165         vp++;
166         mp->count++;
167         if (mp->count == items_this_message)
168           {
169             mp->count = htonl (items_this_message);
170             /* Send to the main thread... */
171             vl_msg_api_send_shmem (q, (u8 *) & mp);
172             mp = 0;
173           }
174       }
175     ASSERT (mp == 0);
176   }
177   vnet_interface_counter_unlock (im);
178 }
179
180 static void
181 do_combined_interface_counters (stats_main_t * sm)
182 {
183   vl_api_vnet_interface_counters_t *mp = 0;
184   vnet_interface_main_t *im = sm->interface_main;
185   api_main_t *am = sm->api_main;
186   vl_shmem_hdr_t *shmem_hdr = am->shmem_hdr;
187   unix_shared_memory_queue_t *q = shmem_hdr->vl_input_queue;
188   vlib_combined_counter_main_t *cm;
189   u32 items_this_message = 0;
190   vlib_counter_t v, *vp = 0;
191   int i;
192
193   vnet_interface_counter_lock (im);
194
195   vec_foreach (cm, im->combined_sw_if_counters)
196   {
197
198     for (i = 0; i < vec_len (cm->maxi); i++)
199       {
200         if (mp == 0)
201           {
202             items_this_message = clib_min (COMBINED_COUNTER_BATCH_SIZE,
203                                            vec_len (cm->maxi) - i);
204
205             mp = vl_msg_api_alloc_as_if_client
206               (sizeof (*mp) + items_this_message * sizeof (v));
207             mp->_vl_msg_id = ntohs (VL_API_VNET_INTERFACE_COUNTERS);
208             mp->vnet_counter_type = cm - im->combined_sw_if_counters;
209             mp->is_combined = 1;
210             mp->first_sw_if_index = htonl (i);
211             mp->count = 0;
212             vp = (vlib_counter_t *) mp->data;
213           }
214         vlib_get_combined_counter (cm, i, &v);
215         clib_mem_unaligned (&vp->packets, u64)
216           = clib_host_to_net_u64 (v.packets);
217         clib_mem_unaligned (&vp->bytes, u64) = clib_host_to_net_u64 (v.bytes);
218         vp++;
219         mp->count++;
220         if (mp->count == items_this_message)
221           {
222             mp->count = htonl (items_this_message);
223             /* Send to the main thread... */
224             vl_msg_api_send_shmem (q, (u8 *) & mp);
225             mp = 0;
226           }
227       }
228     ASSERT (mp == 0);
229   }
230   vnet_interface_counter_unlock (im);
231 }
232
233 /* from .../vnet/vnet/ip/lookup.c. Yuck */
234 typedef CLIB_PACKED (struct
235                      {
236                      ip4_address_t address;
237 u32 address_length: 6;
238 u32 index:           26;
239                      }) ip4_route_t;
240
241 static void
242 ip46_fib_stats_delay (stats_main_t * sm, u32 sec, u32 nsec)
243 {
244   struct timespec _req, *req = &_req;
245   struct timespec _rem, *rem = &_rem;
246
247   req->tv_sec = sec;
248   req->tv_nsec = nsec;
249   while (1)
250     {
251       if (nanosleep (req, rem) == 0)
252         break;
253       *req = *rem;
254       if (errno == EINTR)
255         continue;
256       clib_unix_warning ("nanosleep");
257       break;
258     }
259 }
260
261 static void
262 do_ip4_fibs (stats_main_t * sm)
263 {
264   ip4_main_t *im4 = &ip4_main;
265   api_main_t *am = sm->api_main;
266   vl_shmem_hdr_t *shmem_hdr = am->shmem_hdr;
267   unix_shared_memory_queue_t *q = shmem_hdr->vl_input_queue;
268   static ip4_route_t *routes;
269   ip4_route_t *r;
270   fib_table_t *fib;
271   ip_lookup_main_t *lm = &im4->lookup_main;
272   static uword *results;
273   vl_api_vnet_ip4_fib_counters_t *mp = 0;
274   u32 items_this_message;
275   vl_api_ip4_fib_counter_t *ctrp = 0;
276   u32 start_at_fib_index = 0;
277   int i;
278
279 again:
280   /* *INDENT-OFF* */
281   pool_foreach (fib, im4->fibs,
282   ({
283     /* We may have bailed out due to control-plane activity */
284     while ((fib - im4->fibs) < start_at_fib_index)
285       continue;
286
287     if (mp == 0)
288       {
289         items_this_message = IP4_FIB_COUNTER_BATCH_SIZE;
290         mp = vl_msg_api_alloc_as_if_client
291           (sizeof (*mp) +
292            items_this_message * sizeof (vl_api_ip4_fib_counter_t));
293         mp->_vl_msg_id = ntohs (VL_API_VNET_IP4_FIB_COUNTERS);
294         mp->count = 0;
295         mp->vrf_id = ntohl (fib->ft_table_id);
296         ctrp = (vl_api_ip4_fib_counter_t *) mp->c;
297       }
298     else
299       {
300         /* happens if the last FIB was empty... */
301         ASSERT (mp->count == 0);
302         mp->vrf_id = ntohl (fib->ft_table_id);
303       }
304
305     dslock (sm, 0 /* release hint */ , 1 /* tag */ );
306
307     vec_reset_length (routes);
308     vec_reset_length (results);
309
310     for (i = 0; i < ARRAY_LEN (fib->v4.fib_entry_by_dst_address); i++)
311       {
312         uword *hash = fib->v4.fib_entry_by_dst_address[i];
313         hash_pair_t *p;
314         ip4_route_t x;
315
316         x.address_length = i;
317
318         hash_foreach_pair (p, hash,
319         ({
320           x.address.data_u32 = p->key;
321           if (lm->fib_result_n_words > 1)
322             {
323               x.index = vec_len (results);
324               vec_add (results, p->value, lm->fib_result_n_words);
325             }
326           else
327             x.index = p->value[0];
328
329           vec_add1 (routes, x);
330           if (sm->data_structure_lock->release_hint)
331             {
332               start_at_fib_index = fib - im4->fibs;
333               dsunlock (sm);
334               ip46_fib_stats_delay (sm, 0 /* sec */,
335                                     STATS_RELEASE_DELAY_NS);
336               mp->count = 0;
337               ctrp = (vl_api_ip4_fib_counter_t *)mp->c;
338               goto again;
339             }
340         }));
341       }
342
343     vec_foreach (r, routes)
344       {
345         vlib_counter_t c;
346
347         vlib_get_combined_counter (&load_balance_main.lbm_to_counters,
348                                    r->index, &c);
349         /*
350          * If it has actually
351          * seen at least one packet, send it.
352          */
353         if (c.packets > 0)
354           {
355
356             /* already in net byte order */
357             ctrp->address = r->address.as_u32;
358             ctrp->address_length = r->address_length;
359             ctrp->packets = clib_host_to_net_u64 (c.packets);
360             ctrp->bytes = clib_host_to_net_u64 (c.bytes);
361             mp->count++;
362             ctrp++;
363
364             if (mp->count == items_this_message)
365               {
366                 mp->count = htonl (items_this_message);
367                 /*
368                  * If the main thread's input queue is stuffed,
369                  * drop the data structure lock (which the main thread
370                  * may want), and take a pause.
371                  */
372                 unix_shared_memory_queue_lock (q);
373                 if (unix_shared_memory_queue_is_full (q))
374                   {
375                     dsunlock (sm);
376                     vl_msg_api_send_shmem_nolock (q, (u8 *) & mp);
377                     unix_shared_memory_queue_unlock (q);
378                     mp = 0;
379                     ip46_fib_stats_delay (sm, 0 /* sec */ ,
380                                           STATS_RELEASE_DELAY_NS);
381                     goto again;
382                   }
383                 vl_msg_api_send_shmem_nolock (q, (u8 *) & mp);
384                 unix_shared_memory_queue_unlock (q);
385
386                 items_this_message = IP4_FIB_COUNTER_BATCH_SIZE;
387                 mp = vl_msg_api_alloc_as_if_client
388                   (sizeof (*mp) +
389                    items_this_message * sizeof (vl_api_ip4_fib_counter_t));
390                 mp->_vl_msg_id = ntohs (VL_API_VNET_IP4_FIB_COUNTERS);
391                 mp->count = 0;
392                 mp->vrf_id = ntohl (fib->ft_table_id);
393                 ctrp = (vl_api_ip4_fib_counter_t *) mp->c;
394               }
395           }                     /* for each (mp or single) adj */
396         if (sm->data_structure_lock->release_hint)
397           {
398             start_at_fib_index = fib - im4->fibs;
399             dsunlock (sm);
400             ip46_fib_stats_delay (sm, 0 /* sec */ , STATS_RELEASE_DELAY_NS);
401             mp->count = 0;
402             ctrp = (vl_api_ip4_fib_counter_t *) mp->c;
403             goto again;
404           }
405       }                         /* vec_foreach (routes) */
406
407     dsunlock (sm);
408
409     /* Flush any data from this fib */
410     if (mp->count)
411       {
412         mp->count = htonl (mp->count);
413         vl_msg_api_send_shmem (q, (u8 *) & mp);
414         mp = 0;
415       }
416   }));
417   /* *INDENT-ON* */
418
419   /* If e.g. the last FIB had no reportable routes, free the buffer */
420   if (mp)
421     vl_msg_api_free (mp);
422 }
423
424 typedef struct
425 {
426   ip6_address_t address;
427   u32 address_length;
428   u32 index;
429 } ip6_route_t;
430
431 typedef struct
432 {
433   u32 fib_index;
434   ip6_route_t **routep;
435   stats_main_t *sm;
436 } add_routes_in_fib_arg_t;
437
438 static void
439 add_routes_in_fib (BVT (clib_bihash_kv) * kvp, void *arg)
440 {
441   add_routes_in_fib_arg_t *ap = arg;
442   stats_main_t *sm = ap->sm;
443
444   if (sm->data_structure_lock->release_hint)
445     clib_longjmp (&sm->jmp_buf, 1);
446
447   if (kvp->key[2] >> 32 == ap->fib_index)
448     {
449       ip6_address_t *addr;
450       ip6_route_t *r;
451       addr = (ip6_address_t *) kvp;
452       vec_add2 (*ap->routep, r, 1);
453       r->address = addr[0];
454       r->address_length = kvp->key[2] & 0xFF;
455       r->index = kvp->value;
456     }
457 }
458
459 static void
460 do_ip6_fibs (stats_main_t * sm)
461 {
462   ip6_main_t *im6 = &ip6_main;
463   api_main_t *am = sm->api_main;
464   vl_shmem_hdr_t *shmem_hdr = am->shmem_hdr;
465   unix_shared_memory_queue_t *q = shmem_hdr->vl_input_queue;
466   static ip6_route_t *routes;
467   ip6_route_t *r;
468   fib_table_t *fib;
469   static uword *results;
470   vl_api_vnet_ip6_fib_counters_t *mp = 0;
471   u32 items_this_message;
472   vl_api_ip6_fib_counter_t *ctrp = 0;
473   u32 start_at_fib_index = 0;
474   BVT (clib_bihash) * h = &im6->ip6_table[IP6_FIB_TABLE_FWDING].ip6_hash;
475   add_routes_in_fib_arg_t _a, *a = &_a;
476
477 again:
478   /* *INDENT-OFF* */
479   pool_foreach (fib, im6->fibs,
480   ({
481     /* We may have bailed out due to control-plane activity */
482     while ((fib - im6->fibs) < start_at_fib_index)
483       continue;
484
485     if (mp == 0)
486       {
487         items_this_message = IP6_FIB_COUNTER_BATCH_SIZE;
488         mp = vl_msg_api_alloc_as_if_client
489           (sizeof (*mp) +
490            items_this_message * sizeof (vl_api_ip6_fib_counter_t));
491         mp->_vl_msg_id = ntohs (VL_API_VNET_IP6_FIB_COUNTERS);
492         mp->count = 0;
493         mp->vrf_id = ntohl (fib->ft_table_id);
494         ctrp = (vl_api_ip6_fib_counter_t *) mp->c;
495       }
496
497     dslock (sm, 0 /* release hint */ , 1 /* tag */ );
498
499     vec_reset_length (routes);
500     vec_reset_length (results);
501
502     a->fib_index = fib - im6->fibs;
503     a->routep = &routes;
504     a->sm = sm;
505
506     if (clib_setjmp (&sm->jmp_buf, 0) == 0)
507       {
508         start_at_fib_index = fib - im6->fibs;
509         BV (clib_bihash_foreach_key_value_pair) (h, add_routes_in_fib, a);
510       }
511     else
512       {
513         dsunlock (sm);
514         ip46_fib_stats_delay (sm, 0 /* sec */ ,
515                               STATS_RELEASE_DELAY_NS);
516         mp->count = 0;
517         ctrp = (vl_api_ip6_fib_counter_t *) mp->c;
518         goto again;
519       }
520
521     vec_foreach (r, routes)
522     {
523         vlib_counter_t c;
524
525         vlib_get_combined_counter (&load_balance_main.lbm_to_counters,
526                                    r->index, &c);
527         /*
528          * If it has actually
529          * seen at least one packet, send it.
530          */
531         if (c.packets > 0)
532           {
533             /* already in net byte order */
534             ctrp->address[0] = r->address.as_u64[0];
535             ctrp->address[1] = r->address.as_u64[1];
536             ctrp->address_length = (u8) r->address_length;
537             ctrp->packets = clib_host_to_net_u64 (c.packets);
538             ctrp->bytes = clib_host_to_net_u64 (c.bytes);
539             mp->count++;
540             ctrp++;
541
542             if (mp->count == items_this_message)
543               {
544                 mp->count = htonl (items_this_message);
545                 /*
546                  * If the main thread's input queue is stuffed,
547                  * drop the data structure lock (which the main thread
548                  * may want), and take a pause.
549                  */
550                 unix_shared_memory_queue_lock (q);
551                 if (unix_shared_memory_queue_is_full (q))
552                   {
553                     dsunlock (sm);
554                     vl_msg_api_send_shmem_nolock (q, (u8 *) & mp);
555                     unix_shared_memory_queue_unlock (q);
556                     mp = 0;
557                     ip46_fib_stats_delay (sm, 0 /* sec */ ,
558                                           STATS_RELEASE_DELAY_NS);
559                     goto again;
560                   }
561                 vl_msg_api_send_shmem_nolock (q, (u8 *) & mp);
562                 unix_shared_memory_queue_unlock (q);
563
564                 items_this_message = IP6_FIB_COUNTER_BATCH_SIZE;
565                 mp = vl_msg_api_alloc_as_if_client
566                   (sizeof (*mp) +
567                    items_this_message * sizeof (vl_api_ip6_fib_counter_t));
568                 mp->_vl_msg_id = ntohs (VL_API_VNET_IP6_FIB_COUNTERS);
569                 mp->count = 0;
570                 mp->vrf_id = ntohl (fib->ft_table_id);
571                 ctrp = (vl_api_ip6_fib_counter_t *) mp->c;
572               }
573           }
574
575         if (sm->data_structure_lock->release_hint)
576           {
577             start_at_fib_index = fib - im6->fibs;
578             dsunlock (sm);
579             ip46_fib_stats_delay (sm, 0 /* sec */ , STATS_RELEASE_DELAY_NS);
580             mp->count = 0;
581             ctrp = (vl_api_ip6_fib_counter_t *) mp->c;
582             goto again;
583           }
584     }                           /* vec_foreach (routes) */
585
586     dsunlock (sm);
587
588     /* Flush any data from this fib */
589     if (mp->count)
590       {
591         mp->count = htonl (mp->count);
592         vl_msg_api_send_shmem (q, (u8 *) & mp);
593         mp = 0;
594       }
595   }));
596   /* *INDENT-ON* */
597
598   /* If e.g. the last FIB had no reportable routes, free the buffer */
599   if (mp)
600     vl_msg_api_free (mp);
601 }
602
603 static void
604 stats_thread_fn (void *arg)
605 {
606   stats_main_t *sm = &stats_main;
607   vlib_worker_thread_t *w = (vlib_worker_thread_t *) arg;
608   vlib_thread_main_t *tm = vlib_get_thread_main ();
609
610   /* stats thread wants no signals. */
611   {
612     sigset_t s;
613     sigfillset (&s);
614     pthread_sigmask (SIG_SETMASK, &s, 0);
615   }
616
617   if (vec_len (tm->thread_prefix))
618     vlib_set_thread_name ((char *)
619                           format (0, "%v_stats%c", tm->thread_prefix, '\0'));
620
621   clib_mem_set_heap (w->thread_mheap);
622
623   while (1)
624     {
625       /* 10 second poll interval */
626       ip46_fib_stats_delay (sm, 10 /* secs */ , 0 /* nsec */ );
627
628       if (!(sm->enable_poller))
629         continue;
630       do_simple_interface_counters (sm);
631       do_combined_interface_counters (sm);
632       do_ip4_fibs (sm);
633       do_ip6_fibs (sm);
634     }
635 }
636
637 static void
638 vl_api_vnet_interface_counters_t_handler (vl_api_vnet_interface_counters_t *
639                                           mp)
640 {
641   vpe_client_registration_t *reg;
642   stats_main_t *sm = &stats_main;
643   unix_shared_memory_queue_t *q, *q_prev = NULL;
644   vl_api_vnet_interface_counters_t *mp_copy = NULL;
645   u32 mp_size;
646
647 #if STATS_DEBUG > 0
648   char *counter_name;
649   u32 count, sw_if_index;
650   int i;
651 #endif
652
653   mp_size = sizeof (*mp) + (ntohl (mp->count) *
654                             (mp->is_combined ? sizeof (vlib_counter_t) :
655                              sizeof (u64)));
656
657   /* *INDENT-OFF* */
658   pool_foreach(reg, sm->stats_registrations,
659   ({
660     q = vl_api_client_index_to_input_queue (reg->client_index);
661     if (q)
662       {
663         if (q_prev && (q_prev->cursize < q_prev->maxsize))
664           {
665             mp_copy = vl_msg_api_alloc_as_if_client(mp_size);
666             clib_memcpy(mp_copy, mp, mp_size);
667             vl_msg_api_send_shmem (q_prev, (u8 *)&mp);
668             mp = mp_copy;
669           }
670         q_prev = q;
671       }
672   }));
673   /* *INDENT-ON* */
674
675 #if STATS_DEBUG > 0
676   count = ntohl (mp->count);
677   sw_if_index = ntohl (mp->first_sw_if_index);
678   if (mp->is_combined == 0)
679     {
680       u64 *vp, v;
681       vp = (u64 *) mp->data;
682
683       switch (mp->vnet_counter_type)
684         {
685         case VNET_INTERFACE_COUNTER_DROP:
686           counter_name = "drop";
687           break;
688         case VNET_INTERFACE_COUNTER_PUNT:
689           counter_name = "punt";
690           break;
691         case VNET_INTERFACE_COUNTER_IP4:
692           counter_name = "ip4";
693           break;
694         case VNET_INTERFACE_COUNTER_IP6:
695           counter_name = "ip6";
696           break;
697         case VNET_INTERFACE_COUNTER_RX_NO_BUF:
698           counter_name = "rx-no-buff";
699           break;
700         case VNET_INTERFACE_COUNTER_RX_MISS:
701           , counter_name = "rx-miss";
702           break;
703         case VNET_INTERFACE_COUNTER_RX_ERROR:
704           , counter_name = "rx-error (fifo-full)";
705           break;
706         case VNET_INTERFACE_COUNTER_TX_ERROR:
707           , counter_name = "tx-error (fifo-full)";
708           break;
709         default:
710           counter_name = "bogus";
711           break;
712         }
713       for (i = 0; i < count; i++)
714         {
715           v = clib_mem_unaligned (vp, u64);
716           v = clib_net_to_host_u64 (v);
717           vp++;
718           fformat (stdout, "%U.%s %lld\n", format_vnet_sw_if_index_name,
719                    sm->vnet_main, sw_if_index, counter_name, v);
720           sw_if_index++;
721         }
722     }
723   else
724     {
725       vlib_counter_t *vp;
726       u64 packets, bytes;
727       vp = (vlib_counter_t *) mp->data;
728
729       switch (mp->vnet_counter_type)
730         {
731         case VNET_INTERFACE_COUNTER_RX:
732           counter_name = "rx";
733           break;
734         case VNET_INTERFACE_COUNTER_TX:
735           counter_name = "tx";
736           break;
737         default:
738           counter_name = "bogus";
739           break;
740         }
741       for (i = 0; i < count; i++)
742         {
743           packets = clib_mem_unaligned (&vp->packets, u64);
744           packets = clib_net_to_host_u64 (packets);
745           bytes = clib_mem_unaligned (&vp->bytes, u64);
746           bytes = clib_net_to_host_u64 (bytes);
747           vp++;
748           fformat (stdout, "%U.%s.packets %lld\n",
749                    format_vnet_sw_if_index_name,
750                    sm->vnet_main, sw_if_index, counter_name, packets);
751           fformat (stdout, "%U.%s.bytes %lld\n",
752                    format_vnet_sw_if_index_name,
753                    sm->vnet_main, sw_if_index, counter_name, bytes);
754           sw_if_index++;
755         }
756     }
757 #endif
758   if (q_prev && (q_prev->cursize < q_prev->maxsize))
759     {
760       vl_msg_api_send_shmem (q_prev, (u8 *) & mp);
761     }
762   else
763     {
764       vl_msg_api_free (mp);
765     }
766 }
767
768 static void
769 vl_api_vnet_ip4_fib_counters_t_handler (vl_api_vnet_ip4_fib_counters_t * mp)
770 {
771   vpe_client_registration_t *reg;
772   stats_main_t *sm = &stats_main;
773   unix_shared_memory_queue_t *q, *q_prev = NULL;
774   vl_api_vnet_ip4_fib_counters_t *mp_copy = NULL;
775   u32 mp_size;
776
777   mp_size = sizeof (*mp_copy) +
778     ntohl (mp->count) * sizeof (vl_api_ip4_fib_counter_t);
779
780   /* *INDENT-OFF* */
781   pool_foreach(reg, sm->stats_registrations,
782   ({
783     q = vl_api_client_index_to_input_queue (reg->client_index);
784     if (q)
785       {
786         if (q_prev && (q_prev->cursize < q_prev->maxsize))
787           {
788             mp_copy = vl_msg_api_alloc_as_if_client(mp_size);
789             clib_memcpy(mp_copy, mp, mp_size);
790             vl_msg_api_send_shmem (q_prev, (u8 *)&mp);
791             mp = mp_copy;
792           }
793         q_prev = q;
794       }
795   }));
796   /* *INDENT-ON* */
797   if (q_prev && (q_prev->cursize < q_prev->maxsize))
798     {
799       vl_msg_api_send_shmem (q_prev, (u8 *) & mp);
800     }
801   else
802     {
803       vl_msg_api_free (mp);
804     }
805 }
806
807 static void
808 vl_api_vnet_ip6_fib_counters_t_handler (vl_api_vnet_ip6_fib_counters_t * mp)
809 {
810   vpe_client_registration_t *reg;
811   stats_main_t *sm = &stats_main;
812   unix_shared_memory_queue_t *q, *q_prev = NULL;
813   vl_api_vnet_ip6_fib_counters_t *mp_copy = NULL;
814   u32 mp_size;
815
816   mp_size = sizeof (*mp_copy) +
817     ntohl (mp->count) * sizeof (vl_api_ip6_fib_counter_t);
818
819   /* *INDENT-OFF* */
820   pool_foreach(reg, sm->stats_registrations,
821   ({
822     q = vl_api_client_index_to_input_queue (reg->client_index);
823     if (q)
824       {
825         if (q_prev && (q_prev->cursize < q_prev->maxsize))
826           {
827             mp_copy = vl_msg_api_alloc_as_if_client(mp_size);
828             clib_memcpy(mp_copy, mp, mp_size);
829             vl_msg_api_send_shmem (q_prev, (u8 *)&mp);
830             mp = mp_copy;
831           }
832         q_prev = q;
833       }
834   }));
835   /* *INDENT-ON* */
836   if (q_prev && (q_prev->cursize < q_prev->maxsize))
837     {
838       vl_msg_api_send_shmem (q_prev, (u8 *) & mp);
839     }
840   else
841     {
842       vl_msg_api_free (mp);
843     }
844 }
845
846 static void
847 vl_api_want_stats_reply_t_handler (vl_api_want_stats_reply_t * mp)
848 {
849   clib_warning ("BUG");
850 }
851
852 static void
853 vl_api_want_stats_t_handler (vl_api_want_stats_t * mp)
854 {
855   stats_main_t *sm = &stats_main;
856   vpe_client_registration_t *rp;
857   vl_api_want_stats_reply_t *rmp;
858   uword *p;
859   i32 retval = 0;
860   unix_shared_memory_queue_t *q;
861
862   p = hash_get (sm->stats_registration_hash, mp->client_index);
863   if (p)
864     {
865       if (mp->enable_disable)
866         {
867           clib_warning ("pid %d: already enabled...", mp->pid);
868           retval = -2;
869           goto reply;
870         }
871       else
872         {
873           rp = pool_elt_at_index (sm->stats_registrations, p[0]);
874           pool_put (sm->stats_registrations, rp);
875           hash_unset (sm->stats_registration_hash, mp->client_index);
876           goto reply;
877         }
878     }
879   if (mp->enable_disable == 0)
880     {
881       clib_warning ("pid %d: already disabled...", mp->pid);
882       retval = -3;
883       goto reply;
884     }
885   pool_get (sm->stats_registrations, rp);
886   rp->client_index = mp->client_index;
887   rp->client_pid = mp->pid;
888   hash_set (sm->stats_registration_hash, rp->client_index,
889             rp - sm->stats_registrations);
890
891 reply:
892   if (pool_elts (sm->stats_registrations))
893     sm->enable_poller = 1;
894   else
895     sm->enable_poller = 0;
896
897   q = vl_api_client_index_to_input_queue (mp->client_index);
898
899   if (!q)
900     return;
901
902   rmp = vl_msg_api_alloc (sizeof (*rmp));
903   rmp->_vl_msg_id = ntohs (VL_API_WANT_STATS_REPLY);
904   rmp->context = mp->context;
905   rmp->retval = retval;
906
907   vl_msg_api_send_shmem (q, (u8 *) & rmp);
908 }
909
910 int
911 stats_memclnt_delete_callback (u32 client_index)
912 {
913   vpe_client_registration_t *rp;
914   stats_main_t *sm = &stats_main;
915   uword *p;
916
917   p = hash_get (sm->stats_registration_hash, client_index);
918   if (p)
919     {
920       rp = pool_elt_at_index (sm->stats_registrations, p[0]);
921       pool_put (sm->stats_registrations, rp);
922       hash_unset (sm->stats_registration_hash, client_index);
923     }
924
925   return 0;
926 }
927
928 #define vl_api_vnet_ip4_fib_counters_t_endian vl_noop_handler
929 #define vl_api_vnet_ip4_fib_counters_t_print vl_noop_handler
930 #define vl_api_vnet_ip6_fib_counters_t_endian vl_noop_handler
931 #define vl_api_vnet_ip6_fib_counters_t_print vl_noop_handler
932
933 static clib_error_t *
934 stats_init (vlib_main_t * vm)
935 {
936   stats_main_t *sm = &stats_main;
937   api_main_t *am = &api_main;
938   void *vlib_worker_thread_bootstrap_fn (void *arg);
939
940   sm->vlib_main = vm;
941   sm->vnet_main = vnet_get_main ();
942   sm->interface_main = &vnet_get_main ()->interface_main;
943   sm->api_main = am;
944   sm->stats_poll_interval_in_seconds = 10;
945   sm->data_structure_lock =
946     clib_mem_alloc_aligned (sizeof (data_structure_lock_t),
947                             CLIB_CACHE_LINE_BYTES);
948   memset (sm->data_structure_lock, 0, sizeof (*sm->data_structure_lock));
949
950 #define _(N,n)                                                  \
951     vl_msg_api_set_handlers(VL_API_##N, #n,                     \
952                            vl_api_##n##_t_handler,              \
953                            vl_noop_handler,                     \
954                            vl_api_##n##_t_endian,               \
955                            vl_api_##n##_t_print,                \
956                            sizeof(vl_api_##n##_t), 0 /* do NOT trace! */);
957   foreach_stats_msg;
958 #undef _
959
960   /* tell the msg infra not to free these messages... */
961   am->message_bounce[VL_API_VNET_INTERFACE_COUNTERS] = 1;
962   am->message_bounce[VL_API_VNET_IP4_FIB_COUNTERS] = 1;
963   am->message_bounce[VL_API_VNET_IP6_FIB_COUNTERS] = 1;
964
965   return 0;
966 }
967
968 VLIB_INIT_FUNCTION (stats_init);
969
970 /* *INDENT-OFF* */
971 VLIB_REGISTER_THREAD (stats_thread_reg, static) = {
972   .name = "stats",
973   .function = stats_thread_fn,
974   .fixed_count = 1,
975   .count = 1,
976   .no_data_structure_clone = 1,
977   .use_pthreads = 1,
978 };
979 /* *INDENT-ON* */
980
981 /*
982  * fd.io coding-style-patch-verification: ON
983  *
984  * Local Variables:
985  * eval: (c-set-style "gnu")
986  * End:
987  */