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