Initial commit of vpp code.
[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 <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 <api/vpe_all_api_h.h> 
32 #undef vl_typedefs
33
34 #define vl_endianfun             /* define message structures */
35 #include <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 <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     
618     /* stats thread wants no signals. */
619     {
620         sigset_t s;
621         sigfillset (&s);
622         pthread_sigmask (SIG_SETMASK, &s, 0);
623     }
624
625     clib_mem_set_heap (w->thread_mheap);
626
627     while (1) {
628         /* 10 second poll interval */
629         ip46_fib_stats_delay (sm, 10 /* secs */, 0 /* nsec */);
630
631         if (! (sm->enable_poller))
632             continue;
633         do_simple_interface_counters (sm);
634         do_combined_interface_counters (sm);
635         do_ip4_fibs (sm);
636         do_ip6_fibs (sm);
637     }
638 }
639
640 static void vl_api_vnet_interface_counters_t_handler (
641     vl_api_vnet_interface_counters_t *mp)
642 {
643     vpe_client_registration_t *reg;
644     stats_main_t * sm = &stats_main;
645     unix_shared_memory_queue_t *q, *q_prev = NULL;
646     vl_api_vnet_interface_counters_t *mp_copy = NULL;
647     u32 mp_size;
648
649 #if STATS_DEBUG > 0
650     char *counter_name;
651     u32 count, sw_if_index;
652     int i;
653 #endif
654     
655     mp_size = sizeof (*mp) + (ntohl(mp->count) *
656             (mp->is_combined ? sizeof (vlib_counter_t) : sizeof (u64)));
657
658     pool_foreach(reg, sm->stats_registrations, 
659     ({
660         q = vl_api_client_index_to_input_queue (reg->client_index);
661         if (q) {
662             if (q_prev && (q_prev->cursize < q_prev->maxsize)) {
663                 mp_copy = vl_msg_api_alloc_as_if_client(mp_size);
664                 memcpy(mp_copy, mp, mp_size);
665                 vl_msg_api_send_shmem (q_prev, (u8 *)&mp);
666                 mp = mp_copy;
667             }
668             q_prev = q;
669         }
670     }));
671
672 #if STATS_DEBUG > 0
673     count = ntohl (mp->count);
674     sw_if_index = ntohl (mp->first_sw_if_index);
675     if (mp->is_combined == 0) {
676         u64 * vp, v;
677         vp = (u64 *) mp->data;
678
679         switch (mp->vnet_counter_type) {
680         case  VNET_INTERFACE_COUNTER_DROP:
681             counter_name = "drop";
682             break;
683         case  VNET_INTERFACE_COUNTER_PUNT:
684             counter_name = "punt";
685             break;
686         case  VNET_INTERFACE_COUNTER_IP4:
687             counter_name = "ip4";
688             break;
689         case  VNET_INTERFACE_COUNTER_IP6:
690             counter_name = "ip6";
691             break;
692         case VNET_INTERFACE_COUNTER_RX_NO_BUF:
693             counter_name = "rx-no-buff";
694             break;
695         case VNET_INTERFACE_COUNTER_RX_MISS:,
696             counter_name = "rx-miss";
697             break;
698         case VNET_INTERFACE_COUNTER_RX_ERROR:,
699             counter_name = "rx-error (fifo-full)";
700             break;
701         case VNET_INTERFACE_COUNTER_TX_ERROR:,
702             counter_name = "tx-error (fifo-full)";
703             break;
704         default:
705             counter_name = "bogus";
706             break;
707         }
708         for (i = 0; i < count; i++) {
709             v = clib_mem_unaligned (vp, u64);
710             v = clib_net_to_host_u64 (v);
711             vp++;
712             fformat (stdout, "%U.%s %lld\n", format_vnet_sw_if_index_name, 
713                      sm->vnet_main, sw_if_index, counter_name, v);
714             sw_if_index++;
715         }
716     } else {
717         vlib_counter_t *vp;
718         u64 packets, bytes;
719         vp = (vlib_counter_t *) mp->data;
720
721         switch (mp->vnet_counter_type) {
722         case  VNET_INTERFACE_COUNTER_RX:
723             counter_name = "rx";
724             break;
725         case  VNET_INTERFACE_COUNTER_TX:
726             counter_name = "tx";
727             break;
728         default:
729             counter_name = "bogus";
730             break;
731         }
732         for (i = 0; i < count; i++) {
733             packets = clib_mem_unaligned (&vp->packets, u64);
734             packets = clib_net_to_host_u64 (packets);
735             bytes = clib_mem_unaligned (&vp->bytes, u64);
736             bytes = clib_net_to_host_u64 (bytes);
737             vp++;
738             fformat (stdout, "%U.%s.packets %lld\n", 
739                      format_vnet_sw_if_index_name, 
740                      sm->vnet_main, sw_if_index, counter_name, packets);
741             fformat (stdout, "%U.%s.bytes %lld\n", 
742                      format_vnet_sw_if_index_name, 
743                      sm->vnet_main, sw_if_index, counter_name, bytes);
744             sw_if_index++;
745         }
746     }
747 #endif
748     if (q_prev &&
749         (q_prev->cursize < q_prev->maxsize)) {
750             vl_msg_api_send_shmem (q_prev, (u8 *)&mp);
751     } else {
752         vl_msg_api_free (mp);
753     }
754 }
755
756 static void vl_api_vnet_ip4_fib_counters_t_handler (
757     vl_api_vnet_ip4_fib_counters_t *mp)
758 {
759     vpe_client_registration_t *reg;
760     stats_main_t * sm = &stats_main;
761     unix_shared_memory_queue_t *q, *q_prev = NULL;
762     vl_api_vnet_ip4_fib_counters_t *mp_copy = NULL;
763     u32 mp_size;
764
765     mp_size = sizeof(*mp_copy) +
766             ntohl(mp->count) * sizeof(vl_api_ip4_fib_counter_t);
767
768     pool_foreach(reg, sm->stats_registrations,
769     ({
770         q = vl_api_client_index_to_input_queue (reg->client_index);
771         if (q) {
772             if (q_prev && (q_prev->cursize < q_prev->maxsize)) {
773                 mp_copy = vl_msg_api_alloc_as_if_client(mp_size);
774                 memcpy(mp_copy, mp, mp_size);
775                 vl_msg_api_send_shmem (q_prev, (u8 *)&mp);
776                 mp = mp_copy;
777             }
778             q_prev = q;
779         }
780     }));
781     if (q_prev &&
782         (q_prev->cursize < q_prev->maxsize)) {
783             vl_msg_api_send_shmem (q_prev, (u8 *)&mp);
784     } else {
785         vl_msg_api_free (mp);
786     }
787 }
788
789 static void vl_api_vnet_ip6_fib_counters_t_handler (
790     vl_api_vnet_ip6_fib_counters_t *mp)
791 {
792     vpe_client_registration_t *reg;
793     stats_main_t * sm = &stats_main;
794     unix_shared_memory_queue_t *q, *q_prev = NULL;
795     vl_api_vnet_ip6_fib_counters_t *mp_copy = NULL;
796     u32 mp_size;
797
798     mp_size = sizeof(*mp_copy) +
799             ntohl(mp->count) * sizeof(vl_api_ip6_fib_counter_t);
800     
801     pool_foreach(reg, sm->stats_registrations, 
802     ({
803         q = vl_api_client_index_to_input_queue (reg->client_index);
804         if (q) {
805             if (q_prev && (q_prev->cursize < q_prev->maxsize)) {
806                 mp_copy = vl_msg_api_alloc_as_if_client(mp_size);
807                 memcpy(mp_copy, mp, mp_size);
808                 vl_msg_api_send_shmem (q_prev, (u8 *)&mp);
809                 mp = mp_copy;
810             }
811             q_prev = q;
812         }
813     }));
814     if (q_prev &&
815         (q_prev->cursize < q_prev->maxsize)) {
816             vl_msg_api_send_shmem (q_prev, (u8 *)&mp);
817     } else {
818         vl_msg_api_free (mp);
819     }
820 }
821
822 static void vl_api_want_stats_reply_t_handler (vl_api_want_stats_reply_t *mp)
823 { clib_warning ("BUG"); }
824
825 static void vl_api_want_stats_t_handler (
826     vl_api_want_stats_t *mp)
827 {
828     stats_main_t *sm = &stats_main;
829     vpe_client_registration_t *rp;
830     vl_api_want_stats_reply_t *rmp;
831     uword *p;
832     i32 retval = 0;
833     unix_shared_memory_queue_t *q;
834
835     p = hash_get (sm->stats_registration_hash, mp->client_index);
836     if (p) {
837         if (mp->enable_disable) {
838             clib_warning ("pid %d: already enabled...", mp->pid);
839             retval = -2;
840             goto reply;
841         } else {
842             rp = pool_elt_at_index (sm->stats_registrations, p[0]);
843             pool_put (sm->stats_registrations, rp);
844             hash_unset (sm->stats_registration_hash, mp->client_index);
845             goto reply;
846         }
847     }
848     if (mp->enable_disable == 0) {
849         clib_warning ("pid %d: already disabled...", mp->pid);
850         retval = -3;
851         goto reply;
852     }
853     pool_get (sm->stats_registrations, rp);
854     rp->client_index = mp->client_index;
855     rp->client_pid = mp->pid;
856     hash_set (sm->stats_registration_hash, rp->client_index, 
857               rp - sm->stats_registrations);
858
859 reply:
860     if (pool_elts(sm->stats_registrations))
861         sm->enable_poller = 1;
862     else
863         sm->enable_poller = 0;
864         
865     q = vl_api_client_index_to_input_queue (mp->client_index);
866
867     if (!q)
868         return;
869
870     rmp = vl_msg_api_alloc (sizeof (*rmp));
871     rmp->_vl_msg_id = ntohs(VL_API_WANT_STATS_REPLY);
872     rmp->context = mp->context;
873     rmp->retval = retval;
874
875     vl_msg_api_send_shmem (q, (u8 *)&rmp);
876 }
877
878 int stats_memclnt_delete_callback (u32 client_index) 
879 {
880     vpe_client_registration_t *rp;
881     stats_main_t *sm = &stats_main;
882     uword * p;
883
884     p = hash_get (sm->stats_registration_hash, client_index);
885     if (p) {
886         rp = pool_elt_at_index (sm->stats_registrations, p[0]);
887         pool_put (sm->stats_registrations, rp);
888         hash_unset (sm->stats_registration_hash, client_index);
889     }
890
891     return 0;
892 }
893
894 #define vl_api_vnet_ip4_fib_counters_t_endian vl_noop_handler
895 #define vl_api_vnet_ip4_fib_counters_t_print vl_noop_handler
896 #define vl_api_vnet_ip6_fib_counters_t_endian vl_noop_handler
897 #define vl_api_vnet_ip6_fib_counters_t_print vl_noop_handler
898
899 static clib_error_t * stats_init (vlib_main_t * vm)
900 {
901     stats_main_t * sm = &stats_main;
902     api_main_t * am = &api_main;
903     void *vlib_worker_thread_bootstrap_fn (void *arg);
904
905     sm->vlib_main = vm;
906     sm->vnet_main = vnet_get_main();
907     sm->interface_main = &vnet_get_main()->interface_main;
908     sm->api_main = am;
909     sm->stats_poll_interval_in_seconds = 10;
910     sm->data_structure_lock = 
911         clib_mem_alloc_aligned (sizeof(data_structure_lock_t),
912                                 CLIB_CACHE_LINE_BYTES);
913     memset(sm->data_structure_lock, 0, sizeof (*sm->data_structure_lock));
914
915 #define _(N,n)                                                  \
916     vl_msg_api_set_handlers(VL_API_##N, #n,                     \
917                            vl_api_##n##_t_handler,              \
918                            vl_noop_handler,                     \
919                            vl_api_##n##_t_endian,               \
920                            vl_api_##n##_t_print,                \
921                            sizeof(vl_api_##n##_t), 0 /* do NOT trace! */); 
922     foreach_stats_msg;
923 #undef _
924
925     /* tell the msg infra not to free these messages... */
926     am->message_bounce [VL_API_VNET_INTERFACE_COUNTERS] = 1;
927     am->message_bounce [VL_API_VNET_IP4_FIB_COUNTERS] = 1;
928     am->message_bounce [VL_API_VNET_IP6_FIB_COUNTERS] = 1;
929
930     return 0;
931 }
932
933 VLIB_INIT_FUNCTION (stats_init);
934
935 VLIB_REGISTER_THREAD (stats_thread_reg, static) = {
936     .name = "stats",
937     .function = stats_thread_fn,
938     .fixed_count = 1,
939     .count = 1,
940     .no_data_structure_clone = 1,
941     .use_pthreads = 1,
942     .mheap_size = 2<<20,
943 };