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