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