c46d441a2971c7774b2b5e784fb255946bb92fb7
[vpp.git] / src / 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 <vpp/stats/stats.h>
16 #include <signal.h>
17 #include <vlib/threads.h>
18 #include <vnet/fib/fib_entry.h>
19 #include <vnet/fib/fib_table.h>
20 #include <vnet/dpo/load_balance.h>
21
22 #define STATS_DEBUG 0
23
24 stats_main_t stats_main;
25
26 #include <vnet/ip/ip.h>
27
28 #include <vpp/api/vpe_msg_enum.h>
29
30 #define f64_endian(a)
31 #define f64_print(a,b)
32
33 #define vl_typedefs             /* define message structures */
34 #include <vpp/api/vpe_all_api_h.h>
35 #undef vl_typedefs
36
37 #define vl_endianfun            /* define message structures */
38 #include <vpp/api/vpe_all_api_h.h>
39 #undef vl_endianfun
40
41 /* instantiate all the print functions we know about */
42 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
43 #define vl_printfun
44 #include <vpp/api/vpe_all_api_h.h>
45 #undef vl_printfun
46
47 #define foreach_stats_msg                               \
48 _(WANT_STATS, want_stats)                               \
49 _(VNET_INTERFACE_COUNTERS, vnet_interface_counters)     \
50 _(VNET_IP4_FIB_COUNTERS, vnet_ip4_fib_counters)         \
51 _(VNET_IP6_FIB_COUNTERS, vnet_ip6_fib_counters)         \
52 _(VNET_IP4_NBR_COUNTERS, vnet_ip4_nbr_counters)         \
53 _(VNET_IP6_NBR_COUNTERS, vnet_ip6_nbr_counters)
54
55 /* These constants ensure msg sizes <= 1024, aka ring allocation */
56 #define SIMPLE_COUNTER_BATCH_SIZE       126
57 #define COMBINED_COUNTER_BATCH_SIZE     63
58 #define IP4_FIB_COUNTER_BATCH_SIZE      48
59 #define IP6_FIB_COUNTER_BATCH_SIZE      30
60
61 /* 5ms */
62 #define STATS_RELEASE_DELAY_NS (1000 * 1000 * 5)
63 /*                              ns/us  us/ms        */
64
65 void
66 dslock (stats_main_t * sm, int release_hint, int tag)
67 {
68   u32 thread_id;
69   data_structure_lock_t *l = sm->data_structure_lock;
70
71   if (PREDICT_FALSE (l == 0))
72     return;
73
74   thread_id = os_get_cpu_number ();
75   if (l->lock && l->thread_id == thread_id)
76     {
77       l->count++;
78       return;
79     }
80
81   if (release_hint)
82     l->release_hint++;
83
84   while (__sync_lock_test_and_set (&l->lock, 1))
85     /* zzzz */ ;
86   l->tag = tag;
87   l->thread_id = thread_id;
88   l->count = 1;
89 }
90
91 void
92 stats_dslock_with_hint (int hint, int tag)
93 {
94   stats_main_t *sm = &stats_main;
95   dslock (sm, hint, tag);
96 }
97
98 void
99 dsunlock (stats_main_t * sm)
100 {
101   u32 thread_id;
102   data_structure_lock_t *l = sm->data_structure_lock;
103
104   if (PREDICT_FALSE (l == 0))
105     return;
106
107   thread_id = os_get_cpu_number ();
108   ASSERT (l->lock && l->thread_id == thread_id);
109   l->count--;
110   if (l->count == 0)
111     {
112       l->tag = -l->tag;
113       l->release_hint = 0;
114       CLIB_MEMORY_BARRIER ();
115       l->lock = 0;
116     }
117 }
118
119 void
120 stats_dsunlock (int hint, int tag)
121 {
122   stats_main_t *sm = &stats_main;
123   dsunlock (sm);
124 }
125
126 static void
127 do_simple_interface_counters (stats_main_t * sm)
128 {
129   vl_api_vnet_interface_counters_t *mp = 0;
130   vnet_interface_main_t *im = sm->interface_main;
131   api_main_t *am = sm->api_main;
132   vl_shmem_hdr_t *shmem_hdr = am->shmem_hdr;
133   unix_shared_memory_queue_t *q = shmem_hdr->vl_input_queue;
134   vlib_simple_counter_main_t *cm;
135   u32 items_this_message = 0;
136   u64 v, *vp = 0;
137   int i;
138
139   /*
140    * Prevent interface registration from expanding / moving the vectors...
141    * That tends never to happen, so we can hold this lock for a while.
142    */
143   vnet_interface_counter_lock (im);
144
145   vec_foreach (cm, im->sw_if_counters)
146   {
147
148     for (i = 0; i < vec_len (cm->maxi); i++)
149       {
150         if (mp == 0)
151           {
152             items_this_message = clib_min (SIMPLE_COUNTER_BATCH_SIZE,
153                                            vec_len (cm->maxi) - i);
154
155             mp = vl_msg_api_alloc_as_if_client
156               (sizeof (*mp) + items_this_message * sizeof (v));
157             mp->_vl_msg_id = ntohs (VL_API_VNET_INTERFACE_COUNTERS);
158             mp->vnet_counter_type = cm - im->sw_if_counters;
159             mp->is_combined = 0;
160             mp->first_sw_if_index = htonl (i);
161             mp->count = 0;
162             vp = (u64 *) mp->data;
163           }
164         v = vlib_get_simple_counter (cm, i);
165         clib_mem_unaligned (vp, u64) = clib_host_to_net_u64 (v);
166         vp++;
167         mp->count++;
168         if (mp->count == items_this_message)
169           {
170             mp->count = htonl (items_this_message);
171             /* Send to the main thread... */
172             vl_msg_api_send_shmem (q, (u8 *) & mp);
173             mp = 0;
174           }
175       }
176     ASSERT (mp == 0);
177   }
178   vnet_interface_counter_unlock (im);
179 }
180
181 static void
182 do_combined_interface_counters (stats_main_t * sm)
183 {
184   vl_api_vnet_interface_counters_t *mp = 0;
185   vnet_interface_main_t *im = sm->interface_main;
186   api_main_t *am = sm->api_main;
187   vl_shmem_hdr_t *shmem_hdr = am->shmem_hdr;
188   unix_shared_memory_queue_t *q = shmem_hdr->vl_input_queue;
189   vlib_combined_counter_main_t *cm;
190   u32 items_this_message = 0;
191   vlib_counter_t v, *vp = 0;
192   int i;
193
194   vnet_interface_counter_lock (im);
195
196   vec_foreach (cm, im->combined_sw_if_counters)
197   {
198
199     for (i = 0; i < vec_len (cm->maxi); i++)
200       {
201         if (mp == 0)
202           {
203             items_this_message = clib_min (COMBINED_COUNTER_BATCH_SIZE,
204                                            vec_len (cm->maxi) - i);
205
206             mp = vl_msg_api_alloc_as_if_client
207               (sizeof (*mp) + items_this_message * sizeof (v));
208             mp->_vl_msg_id = ntohs (VL_API_VNET_INTERFACE_COUNTERS);
209             mp->vnet_counter_type = cm - im->combined_sw_if_counters;
210             mp->is_combined = 1;
211             mp->first_sw_if_index = htonl (i);
212             mp->count = 0;
213             vp = (vlib_counter_t *) mp->data;
214           }
215         vlib_get_combined_counter (cm, i, &v);
216         clib_mem_unaligned (&vp->packets, u64)
217           = clib_host_to_net_u64 (v.packets);
218         clib_mem_unaligned (&vp->bytes, u64) = clib_host_to_net_u64 (v.bytes);
219         vp++;
220         mp->count++;
221         if (mp->count == items_this_message)
222           {
223             mp->count = htonl (items_this_message);
224             /* Send to the main thread... */
225             vl_msg_api_send_shmem (q, (u8 *) & mp);
226             mp = 0;
227           }
228       }
229     ASSERT (mp == 0);
230   }
231   vnet_interface_counter_unlock (im);
232 }
233
234 /* from .../vnet/vnet/ip/lookup.c. Yuck */
235 typedef CLIB_PACKED (struct
236                      {
237                      ip4_address_t address;
238 u32 address_length: 6;
239 u32 index:           26;
240                      }) ip4_route_t;
241
242 static void
243 ip46_fib_stats_delay (stats_main_t * sm, u32 sec, u32 nsec)
244 {
245   struct timespec _req, *req = &_req;
246   struct timespec _rem, *rem = &_rem;
247
248   req->tv_sec = sec;
249   req->tv_nsec = nsec;
250   while (1)
251     {
252       if (nanosleep (req, rem) == 0)
253         break;
254       *req = *rem;
255       if (errno == EINTR)
256         continue;
257       clib_unix_warning ("nanosleep");
258       break;
259     }
260 }
261
262 /**
263  * @brief The context passed when collecting adjacency counters
264  */
265 typedef struct ip4_nbr_stats_ctx_t_
266 {
267   /**
268    * The SW IF index all these adjs belong to
269    */
270   u32 sw_if_index;
271
272   /**
273    * A vector of ip4 nbr counters
274    */
275   vl_api_ip4_nbr_counter_t *counters;
276 } ip4_nbr_stats_ctx_t;
277
278 static adj_walk_rc_t
279 ip4_nbr_stats_cb (adj_index_t ai, void *arg)
280 {
281   vl_api_ip4_nbr_counter_t *vl_counter;
282   vlib_counter_t adj_counter;
283   ip4_nbr_stats_ctx_t *ctx;
284   ip_adjacency_t *adj;
285
286   ctx = arg;
287   vlib_get_combined_counter (&adjacency_counters, ai, &adj_counter);
288
289   if (0 != adj_counter.packets)
290     {
291       vec_add2 (ctx->counters, vl_counter, 1);
292       adj = adj_get (ai);
293
294       vl_counter->packets = clib_host_to_net_u64 (adj_counter.packets);
295       vl_counter->bytes = clib_host_to_net_u64 (adj_counter.bytes);
296       vl_counter->address = adj->sub_type.nbr.next_hop.ip4.as_u32;
297       vl_counter->link_type = adj->ia_link;
298     }
299   return (ADJ_WALK_RC_CONTINUE);
300 }
301
302 #define MIN(x,y) (((x)<(y))?(x):(y))
303
304 static void
305 ip4_nbr_ship (stats_main_t * sm, ip4_nbr_stats_ctx_t * ctx)
306 {
307   api_main_t *am = sm->api_main;
308   vl_shmem_hdr_t *shmem_hdr = am->shmem_hdr;
309   unix_shared_memory_queue_t *q = shmem_hdr->vl_input_queue;
310   vl_api_vnet_ip4_nbr_counters_t *mp = 0;
311   int first = 0;
312
313   /*
314    * If the walk context has counters, which may be left over from the last
315    * suspend, then we continue from there.
316    */
317   while (0 != vec_len (ctx->counters))
318     {
319       u32 n_items = MIN (vec_len (ctx->counters),
320                          IP4_FIB_COUNTER_BATCH_SIZE);
321       u8 pause = 0;
322
323       dslock (sm, 0 /* release hint */ , 1 /* tag */ );
324
325       mp = vl_msg_api_alloc_as_if_client (sizeof (*mp) +
326                                           (n_items *
327                                            sizeof
328                                            (vl_api_ip4_nbr_counter_t)));
329       mp->_vl_msg_id = ntohs (VL_API_VNET_IP4_NBR_COUNTERS);
330       mp->count = ntohl (n_items);
331       mp->sw_if_index = ntohl (ctx->sw_if_index);
332       mp->begin = first;
333       first = 0;
334
335       /*
336        * copy the counters from the back of the context, then we can easily
337        * 'erase' them by resetting the vector length.
338        * The order we push the stats to the caller is not important.
339        */
340       clib_memcpy (mp->c,
341                    &ctx->counters[vec_len (ctx->counters) - n_items],
342                    n_items * sizeof (*ctx->counters));
343
344       _vec_len (ctx->counters) = vec_len (ctx->counters) - n_items;
345
346       /*
347        * send to the shm q
348        */
349       unix_shared_memory_queue_lock (q);
350       pause = unix_shared_memory_queue_is_full (q);
351
352       vl_msg_api_send_shmem_nolock (q, (u8 *) & mp);
353       unix_shared_memory_queue_unlock (q);
354       dsunlock (sm);
355
356       if (pause)
357         ip46_fib_stats_delay (sm, 0 /* sec */ ,
358                               STATS_RELEASE_DELAY_NS);
359     }
360 }
361
362 static void
363 do_ip4_nbrs (stats_main_t * sm)
364 {
365   vnet_main_t *vnm = vnet_get_main ();
366   vnet_interface_main_t *im = &vnm->interface_main;
367   vnet_sw_interface_t *si;
368
369   ip4_nbr_stats_ctx_t ctx = {
370     .sw_if_index = 0,
371     .counters = NULL,
372   };
373
374   /* *INDENT-OFF* */
375   pool_foreach (si, im->sw_interfaces,
376   ({
377     /*
378      * update the interface we are now concerned with
379      */
380     ctx.sw_if_index = si->sw_if_index;
381
382     /*
383      * we are about to walk another interface, so we shouldn't have any pending
384      * stats to export.
385      */
386     ASSERT(ctx.counters == NULL);
387
388     /*
389      * visit each neighbour adjacency on the interface and collect
390      * its current stats.
391      * Because we hold the lock the walk is synchronous, so safe to routing
392      * updates. It's limited in work by the number of adjacenies on an
393      * interface, which is typically not huge.
394      */
395     dslock (sm, 0 /* release hint */ , 1 /* tag */ );
396     adj_nbr_walk (si->sw_if_index,
397                   FIB_PROTOCOL_IP4,
398                   ip4_nbr_stats_cb,
399                   &ctx);
400     dsunlock (sm);
401
402     /*
403      * if this interface has some adjacencies with counters then ship them,
404      * else continue to the next interface.
405      */
406     if (NULL != ctx.counters)
407       {
408         ip4_nbr_ship(sm, &ctx);
409       }
410   }));
411   /* *INDENT-OFF* */
412 }
413
414 /**
415  * @brief The context passed when collecting adjacency counters
416  */
417 typedef struct ip6_nbr_stats_ctx_t_
418 {
419   /**
420    * The SW IF index all these adjs belong to
421    */
422   u32 sw_if_index;
423
424   /**
425    * A vector of ip6 nbr counters
426    */
427   vl_api_ip6_nbr_counter_t *counters;
428 } ip6_nbr_stats_ctx_t;
429
430 static adj_walk_rc_t
431 ip6_nbr_stats_cb (adj_index_t ai,
432                   void *arg)
433 {
434   vl_api_ip6_nbr_counter_t *vl_counter;
435   vlib_counter_t adj_counter;
436   ip6_nbr_stats_ctx_t *ctx;
437   ip_adjacency_t *adj;
438
439   ctx = arg;
440   vlib_get_combined_counter(&adjacency_counters, ai, &adj_counter);
441
442   if (0 != adj_counter.packets)
443     {
444       vec_add2(ctx->counters, vl_counter, 1);
445       adj = adj_get(ai);
446
447       vl_counter->packets = clib_host_to_net_u64(adj_counter.packets);
448       vl_counter->bytes   = clib_host_to_net_u64(adj_counter.bytes);
449       vl_counter->address[0] = adj->sub_type.nbr.next_hop.ip6.as_u64[0];
450       vl_counter->address[1] = adj->sub_type.nbr.next_hop.ip6.as_u64[1];
451       vl_counter->link_type = adj->ia_link;
452     }
453   return (ADJ_WALK_RC_CONTINUE);
454 }
455
456 #define MIN(x,y) (((x)<(y))?(x):(y))
457
458 static void
459 ip6_nbr_ship (stats_main_t * sm,
460               ip6_nbr_stats_ctx_t *ctx)
461 {
462   api_main_t *am = sm->api_main;
463   vl_shmem_hdr_t *shmem_hdr = am->shmem_hdr;
464   unix_shared_memory_queue_t *q = shmem_hdr->vl_input_queue;
465   vl_api_vnet_ip6_nbr_counters_t *mp = 0;
466   int first = 0;
467
468   /*
469    * If the walk context has counters, which may be left over from the last
470    * suspend, then we continue from there.
471    */
472   while (0 != vec_len(ctx->counters))
473     {
474       u32 n_items = MIN (vec_len (ctx->counters),
475                          IP6_FIB_COUNTER_BATCH_SIZE);
476       u8 pause = 0;
477
478       dslock (sm, 0 /* release hint */ , 1 /* tag */ );
479
480       mp = vl_msg_api_alloc_as_if_client (sizeof (*mp) +
481                                           (n_items *
482                                            sizeof
483                                            (vl_api_ip6_nbr_counter_t)));
484       mp->_vl_msg_id = ntohs (VL_API_VNET_IP6_NBR_COUNTERS);
485       mp->count = ntohl (n_items);
486       mp->sw_if_index = ntohl (ctx->sw_if_index);
487       mp->begin = first;
488       first = 0;
489
490       /*
491        * copy the counters from the back of the context, then we can easily
492        * 'erase' them by resetting the vector length.
493        * The order we push the stats to the caller is not important.
494        */
495       clib_memcpy (mp->c,
496                    &ctx->counters[vec_len (ctx->counters) - n_items],
497                    n_items * sizeof (*ctx->counters));
498
499       _vec_len (ctx->counters) = vec_len (ctx->counters) - n_items;
500
501       /*
502        * send to the shm q
503        */
504       unix_shared_memory_queue_lock (q);
505       pause = unix_shared_memory_queue_is_full (q);
506
507       vl_msg_api_send_shmem_nolock (q, (u8 *) & mp);
508       unix_shared_memory_queue_unlock (q);
509       dsunlock (sm);
510
511       if (pause)
512         ip46_fib_stats_delay (sm, 0 /* sec */ ,
513                               STATS_RELEASE_DELAY_NS);
514     }
515 }
516
517 static void
518 do_ip6_nbrs (stats_main_t * sm)
519 {
520   vnet_main_t *vnm = vnet_get_main ();
521   vnet_interface_main_t *im = &vnm->interface_main;
522   vnet_sw_interface_t *si;
523
524   ip6_nbr_stats_ctx_t ctx = {
525     .sw_if_index = 0,
526     .counters = NULL,
527   };
528
529   /* *INDENT-OFF* */
530   pool_foreach (si, im->sw_interfaces,
531   ({
532     /*
533      * update the interface we are now concerned with
534      */
535     ctx.sw_if_index = si->sw_if_index;
536
537     /*
538      * we are about to walk another interface, so we shouldn't have any pending
539      * stats to export.
540      */
541     ASSERT(ctx.counters == NULL);
542
543     /*
544      * visit each neighbour adjacency on the interface and collect
545      * its current stats.
546      * Because we hold the lock the walk is synchronous, so safe to routing
547      * updates. It's limited in work by the number of adjacenies on an
548      * interface, which is typically not huge.
549      */
550     dslock (sm, 0 /* release hint */ , 1 /* tag */ );
551     adj_nbr_walk (si->sw_if_index,
552                   FIB_PROTOCOL_IP6,
553                   ip6_nbr_stats_cb,
554                   &ctx);
555     dsunlock (sm);
556
557     /*
558      * if this interface has some adjacencies with counters then ship them,
559      * else continue to the next interface.
560      */
561     if (NULL != ctx.counters)
562       {
563         ip6_nbr_ship(sm, &ctx);
564       }
565   }));
566   /* *INDENT-OFF* */
567 }
568
569 static void
570 do_ip4_fibs (stats_main_t * sm)
571 {
572   ip4_main_t *im4 = &ip4_main;
573   api_main_t *am = sm->api_main;
574   vl_shmem_hdr_t *shmem_hdr = am->shmem_hdr;
575   unix_shared_memory_queue_t *q = shmem_hdr->vl_input_queue;
576   static ip4_route_t *routes;
577   ip4_route_t *r;
578   fib_table_t *fib;
579   ip_lookup_main_t *lm = &im4->lookup_main;
580   static uword *results;
581   vl_api_vnet_ip4_fib_counters_t *mp = 0;
582   u32 items_this_message;
583   vl_api_ip4_fib_counter_t *ctrp = 0;
584   u32 start_at_fib_index = 0;
585   int i;
586
587 again:
588   /* *INDENT-OFF* */
589   pool_foreach (fib, im4->fibs,
590   ({
591     /* We may have bailed out due to control-plane activity */
592     while ((fib - im4->fibs) < start_at_fib_index)
593       continue;
594
595     if (mp == 0)
596       {
597         items_this_message = IP4_FIB_COUNTER_BATCH_SIZE;
598         mp = vl_msg_api_alloc_as_if_client
599           (sizeof (*mp) +
600            items_this_message * sizeof (vl_api_ip4_fib_counter_t));
601         mp->_vl_msg_id = ntohs (VL_API_VNET_IP4_FIB_COUNTERS);
602         mp->count = 0;
603         mp->vrf_id = ntohl (fib->ft_table_id);
604         ctrp = (vl_api_ip4_fib_counter_t *) mp->c;
605       }
606     else
607       {
608         /* happens if the last FIB was empty... */
609         ASSERT (mp->count == 0);
610         mp->vrf_id = ntohl (fib->ft_table_id);
611       }
612
613     dslock (sm, 0 /* release hint */ , 1 /* tag */ );
614
615     vec_reset_length (routes);
616     vec_reset_length (results);
617
618     for (i = 0; i < ARRAY_LEN (fib->v4.fib_entry_by_dst_address); i++)
619       {
620         uword *hash = fib->v4.fib_entry_by_dst_address[i];
621         hash_pair_t *p;
622         ip4_route_t x;
623
624         x.address_length = i;
625
626         hash_foreach_pair (p, hash,
627         ({
628           x.address.data_u32 = p->key;
629           x.index = p->value[0];
630
631           vec_add1 (routes, x);
632           if (sm->data_structure_lock->release_hint)
633             {
634               start_at_fib_index = fib - im4->fibs;
635               dsunlock (sm);
636               ip46_fib_stats_delay (sm, 0 /* sec */,
637                                     STATS_RELEASE_DELAY_NS);
638               mp->count = 0;
639               ctrp = (vl_api_ip4_fib_counter_t *)mp->c;
640               goto again;
641             }
642         }));
643       }
644
645     vec_foreach (r, routes)
646       {
647         vlib_counter_t c;
648
649         vlib_get_combined_counter (&load_balance_main.lbm_to_counters,
650                                    r->index, &c);
651         /*
652          * If it has actually
653          * seen at least one packet, send it.
654          */
655         if (c.packets > 0)
656           {
657
658             /* already in net byte order */
659             ctrp->address = r->address.as_u32;
660             ctrp->address_length = r->address_length;
661             ctrp->packets = clib_host_to_net_u64 (c.packets);
662             ctrp->bytes = clib_host_to_net_u64 (c.bytes);
663             mp->count++;
664             ctrp++;
665
666             if (mp->count == items_this_message)
667               {
668                 mp->count = htonl (items_this_message);
669                 /*
670                  * If the main thread's input queue is stuffed,
671                  * drop the data structure lock (which the main thread
672                  * may want), and take a pause.
673                  */
674                 unix_shared_memory_queue_lock (q);
675                 if (unix_shared_memory_queue_is_full (q))
676                   {
677                     dsunlock (sm);
678                     vl_msg_api_send_shmem_nolock (q, (u8 *) & mp);
679                     unix_shared_memory_queue_unlock (q);
680                     mp = 0;
681                     ip46_fib_stats_delay (sm, 0 /* sec */ ,
682                                           STATS_RELEASE_DELAY_NS);
683                     goto again;
684                   }
685                 vl_msg_api_send_shmem_nolock (q, (u8 *) & mp);
686                 unix_shared_memory_queue_unlock (q);
687
688                 items_this_message = IP4_FIB_COUNTER_BATCH_SIZE;
689                 mp = vl_msg_api_alloc_as_if_client
690                   (sizeof (*mp) +
691                    items_this_message * sizeof (vl_api_ip4_fib_counter_t));
692                 mp->_vl_msg_id = ntohs (VL_API_VNET_IP4_FIB_COUNTERS);
693                 mp->count = 0;
694                 mp->vrf_id = ntohl (fib->ft_table_id);
695                 ctrp = (vl_api_ip4_fib_counter_t *) mp->c;
696               }
697           }                     /* for each (mp or single) adj */
698         if (sm->data_structure_lock->release_hint)
699           {
700             start_at_fib_index = fib - im4->fibs;
701             dsunlock (sm);
702             ip46_fib_stats_delay (sm, 0 /* sec */ , STATS_RELEASE_DELAY_NS);
703             mp->count = 0;
704             ctrp = (vl_api_ip4_fib_counter_t *) mp->c;
705             goto again;
706           }
707       }                         /* vec_foreach (routes) */
708
709     dsunlock (sm);
710
711     /* Flush any data from this fib */
712     if (mp->count)
713       {
714         mp->count = htonl (mp->count);
715         vl_msg_api_send_shmem (q, (u8 *) & mp);
716         mp = 0;
717       }
718   }));
719   /* *INDENT-ON* */
720
721   /* If e.g. the last FIB had no reportable routes, free the buffer */
722   if (mp)
723     vl_msg_api_free (mp);
724 }
725
726 typedef struct
727 {
728   ip6_address_t address;
729   u32 address_length;
730   u32 index;
731 } ip6_route_t;
732
733 typedef struct
734 {
735   u32 fib_index;
736   ip6_route_t **routep;
737   stats_main_t *sm;
738 } add_routes_in_fib_arg_t;
739
740 static void
741 add_routes_in_fib (BVT (clib_bihash_kv) * kvp, void *arg)
742 {
743   add_routes_in_fib_arg_t *ap = arg;
744   stats_main_t *sm = ap->sm;
745
746   if (sm->data_structure_lock->release_hint)
747     clib_longjmp (&sm->jmp_buf, 1);
748
749   if (kvp->key[2] >> 32 == ap->fib_index)
750     {
751       ip6_address_t *addr;
752       ip6_route_t *r;
753       addr = (ip6_address_t *) kvp;
754       vec_add2 (*ap->routep, r, 1);
755       r->address = addr[0];
756       r->address_length = kvp->key[2] & 0xFF;
757       r->index = kvp->value;
758     }
759 }
760
761 static void
762 do_ip6_fibs (stats_main_t * sm)
763 {
764   ip6_main_t *im6 = &ip6_main;
765   api_main_t *am = sm->api_main;
766   vl_shmem_hdr_t *shmem_hdr = am->shmem_hdr;
767   unix_shared_memory_queue_t *q = shmem_hdr->vl_input_queue;
768   static ip6_route_t *routes;
769   ip6_route_t *r;
770   fib_table_t *fib;
771   static uword *results;
772   vl_api_vnet_ip6_fib_counters_t *mp = 0;
773   u32 items_this_message;
774   vl_api_ip6_fib_counter_t *ctrp = 0;
775   u32 start_at_fib_index = 0;
776   BVT (clib_bihash) * h = &im6->ip6_table[IP6_FIB_TABLE_FWDING].ip6_hash;
777   add_routes_in_fib_arg_t _a, *a = &_a;
778
779 again:
780   /* *INDENT-OFF* */
781   pool_foreach (fib, im6->fibs,
782   ({
783     /* We may have bailed out due to control-plane activity */
784     while ((fib - im6->fibs) < start_at_fib_index)
785       continue;
786
787     if (mp == 0)
788       {
789         items_this_message = IP6_FIB_COUNTER_BATCH_SIZE;
790         mp = vl_msg_api_alloc_as_if_client
791           (sizeof (*mp) +
792            items_this_message * sizeof (vl_api_ip6_fib_counter_t));
793         mp->_vl_msg_id = ntohs (VL_API_VNET_IP6_FIB_COUNTERS);
794         mp->count = 0;
795         mp->vrf_id = ntohl (fib->ft_table_id);
796         ctrp = (vl_api_ip6_fib_counter_t *) mp->c;
797       }
798
799     dslock (sm, 0 /* release hint */ , 1 /* tag */ );
800
801     vec_reset_length (routes);
802     vec_reset_length (results);
803
804     a->fib_index = fib - im6->fibs;
805     a->routep = &routes;
806     a->sm = sm;
807
808     if (clib_setjmp (&sm->jmp_buf, 0) == 0)
809       {
810         start_at_fib_index = fib - im6->fibs;
811         BV (clib_bihash_foreach_key_value_pair) (h, add_routes_in_fib, a);
812       }
813     else
814       {
815         dsunlock (sm);
816         ip46_fib_stats_delay (sm, 0 /* sec */ ,
817                               STATS_RELEASE_DELAY_NS);
818         mp->count = 0;
819         ctrp = (vl_api_ip6_fib_counter_t *) mp->c;
820         goto again;
821       }
822
823     vec_foreach (r, routes)
824     {
825         vlib_counter_t c;
826
827         vlib_get_combined_counter (&load_balance_main.lbm_to_counters,
828                                    r->index, &c);
829         /*
830          * If it has actually
831          * seen at least one packet, send it.
832          */
833         if (c.packets > 0)
834           {
835             /* already in net byte order */
836             ctrp->address[0] = r->address.as_u64[0];
837             ctrp->address[1] = r->address.as_u64[1];
838             ctrp->address_length = (u8) r->address_length;
839             ctrp->packets = clib_host_to_net_u64 (c.packets);
840             ctrp->bytes = clib_host_to_net_u64 (c.bytes);
841             mp->count++;
842             ctrp++;
843
844             if (mp->count == items_this_message)
845               {
846                 mp->count = htonl (items_this_message);
847                 /*
848                  * If the main thread's input queue is stuffed,
849                  * drop the data structure lock (which the main thread
850                  * may want), and take a pause.
851                  */
852                 unix_shared_memory_queue_lock (q);
853                 if (unix_shared_memory_queue_is_full (q))
854                   {
855                     dsunlock (sm);
856                     vl_msg_api_send_shmem_nolock (q, (u8 *) & mp);
857                     unix_shared_memory_queue_unlock (q);
858                     mp = 0;
859                     ip46_fib_stats_delay (sm, 0 /* sec */ ,
860                                           STATS_RELEASE_DELAY_NS);
861                     goto again;
862                   }
863                 vl_msg_api_send_shmem_nolock (q, (u8 *) & mp);
864                 unix_shared_memory_queue_unlock (q);
865
866                 items_this_message = IP6_FIB_COUNTER_BATCH_SIZE;
867                 mp = vl_msg_api_alloc_as_if_client
868                   (sizeof (*mp) +
869                    items_this_message * sizeof (vl_api_ip6_fib_counter_t));
870                 mp->_vl_msg_id = ntohs (VL_API_VNET_IP6_FIB_COUNTERS);
871                 mp->count = 0;
872                 mp->vrf_id = ntohl (fib->ft_table_id);
873                 ctrp = (vl_api_ip6_fib_counter_t *) mp->c;
874               }
875           }
876
877         if (sm->data_structure_lock->release_hint)
878           {
879             start_at_fib_index = fib - im6->fibs;
880             dsunlock (sm);
881             ip46_fib_stats_delay (sm, 0 /* sec */ , STATS_RELEASE_DELAY_NS);
882             mp->count = 0;
883             ctrp = (vl_api_ip6_fib_counter_t *) mp->c;
884             goto again;
885           }
886     }                           /* vec_foreach (routes) */
887
888     dsunlock (sm);
889
890     /* Flush any data from this fib */
891     if (mp->count)
892       {
893         mp->count = htonl (mp->count);
894         vl_msg_api_send_shmem (q, (u8 *) & mp);
895         mp = 0;
896       }
897   }));
898   /* *INDENT-ON* */
899
900   /* If e.g. the last FIB had no reportable routes, free the buffer */
901   if (mp)
902     vl_msg_api_free (mp);
903 }
904
905 static void
906 stats_thread_fn (void *arg)
907 {
908   stats_main_t *sm = &stats_main;
909   vlib_worker_thread_t *w = (vlib_worker_thread_t *) arg;
910   vlib_thread_main_t *tm = vlib_get_thread_main ();
911
912   /* stats thread wants no signals. */
913   {
914     sigset_t s;
915     sigfillset (&s);
916     pthread_sigmask (SIG_SETMASK, &s, 0);
917   }
918
919   if (vec_len (tm->thread_prefix))
920     vlib_set_thread_name ((char *)
921                           format (0, "%v_stats%c", tm->thread_prefix, '\0'));
922
923   clib_mem_set_heap (w->thread_mheap);
924
925   while (1)
926     {
927       /* 10 second poll interval */
928       ip46_fib_stats_delay (sm, 10 /* secs */ , 0 /* nsec */ );
929
930       if (!(sm->enable_poller))
931         continue;
932       do_simple_interface_counters (sm);
933       do_combined_interface_counters (sm);
934       do_ip4_fibs (sm);
935       do_ip6_fibs (sm);
936       do_ip4_nbrs (sm);
937       do_ip6_nbrs (sm);
938     }
939 }
940
941 static void
942 vl_api_vnet_interface_counters_t_handler (vl_api_vnet_interface_counters_t *
943                                           mp)
944 {
945   vpe_client_registration_t *reg;
946   stats_main_t *sm = &stats_main;
947   unix_shared_memory_queue_t *q, *q_prev = NULL;
948   vl_api_vnet_interface_counters_t *mp_copy = NULL;
949   u32 mp_size;
950
951 #if STATS_DEBUG > 0
952   char *counter_name;
953   u32 count, sw_if_index;
954   int i;
955 #endif
956
957   mp_size = sizeof (*mp) + (ntohl (mp->count) *
958                             (mp->is_combined ? sizeof (vlib_counter_t) :
959                              sizeof (u64)));
960
961   /* *INDENT-OFF* */
962   pool_foreach(reg, sm->stats_registrations,
963   ({
964     q = vl_api_client_index_to_input_queue (reg->client_index);
965     if (q)
966       {
967         if (q_prev && (q_prev->cursize < q_prev->maxsize))
968           {
969             mp_copy = vl_msg_api_alloc_as_if_client(mp_size);
970             clib_memcpy(mp_copy, mp, mp_size);
971             vl_msg_api_send_shmem (q_prev, (u8 *)&mp);
972             mp = mp_copy;
973           }
974         q_prev = q;
975       }
976   }));
977   /* *INDENT-ON* */
978
979 #if STATS_DEBUG > 0
980   count = ntohl (mp->count);
981   sw_if_index = ntohl (mp->first_sw_if_index);
982   if (mp->is_combined == 0)
983     {
984       u64 *vp, v;
985       vp = (u64 *) mp->data;
986
987       switch (mp->vnet_counter_type)
988         {
989         case VNET_INTERFACE_COUNTER_DROP:
990           counter_name = "drop";
991           break;
992         case VNET_INTERFACE_COUNTER_PUNT:
993           counter_name = "punt";
994           break;
995         case VNET_INTERFACE_COUNTER_IP4:
996           counter_name = "ip4";
997           break;
998         case VNET_INTERFACE_COUNTER_IP6:
999           counter_name = "ip6";
1000           break;
1001         case VNET_INTERFACE_COUNTER_RX_NO_BUF:
1002           counter_name = "rx-no-buff";
1003           break;
1004         case VNET_INTERFACE_COUNTER_RX_MISS:
1005           , counter_name = "rx-miss";
1006           break;
1007         case VNET_INTERFACE_COUNTER_RX_ERROR:
1008           , counter_name = "rx-error (fifo-full)";
1009           break;
1010         case VNET_INTERFACE_COUNTER_TX_ERROR:
1011           , counter_name = "tx-error (fifo-full)";
1012           break;
1013         default:
1014           counter_name = "bogus";
1015           break;
1016         }
1017       for (i = 0; i < count; i++)
1018         {
1019           v = clib_mem_unaligned (vp, u64);
1020           v = clib_net_to_host_u64 (v);
1021           vp++;
1022           fformat (stdout, "%U.%s %lld\n", format_vnet_sw_if_index_name,
1023                    sm->vnet_main, sw_if_index, counter_name, v);
1024           sw_if_index++;
1025         }
1026     }
1027   else
1028     {
1029       vlib_counter_t *vp;
1030       u64 packets, bytes;
1031       vp = (vlib_counter_t *) mp->data;
1032
1033       switch (mp->vnet_counter_type)
1034         {
1035         case VNET_INTERFACE_COUNTER_RX:
1036           counter_name = "rx";
1037           break;
1038         case VNET_INTERFACE_COUNTER_TX:
1039           counter_name = "tx";
1040           break;
1041         default:
1042           counter_name = "bogus";
1043           break;
1044         }
1045       for (i = 0; i < count; i++)
1046         {
1047           packets = clib_mem_unaligned (&vp->packets, u64);
1048           packets = clib_net_to_host_u64 (packets);
1049           bytes = clib_mem_unaligned (&vp->bytes, u64);
1050           bytes = clib_net_to_host_u64 (bytes);
1051           vp++;
1052           fformat (stdout, "%U.%s.packets %lld\n",
1053                    format_vnet_sw_if_index_name,
1054                    sm->vnet_main, sw_if_index, counter_name, packets);
1055           fformat (stdout, "%U.%s.bytes %lld\n",
1056                    format_vnet_sw_if_index_name,
1057                    sm->vnet_main, sw_if_index, counter_name, bytes);
1058           sw_if_index++;
1059         }
1060     }
1061 #endif
1062   if (q_prev && (q_prev->cursize < q_prev->maxsize))
1063     {
1064       vl_msg_api_send_shmem (q_prev, (u8 *) & mp);
1065     }
1066   else
1067     {
1068       vl_msg_api_free (mp);
1069     }
1070 }
1071
1072 static void
1073 vl_api_vnet_ip4_fib_counters_t_handler (vl_api_vnet_ip4_fib_counters_t * mp)
1074 {
1075   vpe_client_registration_t *reg;
1076   stats_main_t *sm = &stats_main;
1077   unix_shared_memory_queue_t *q, *q_prev = NULL;
1078   vl_api_vnet_ip4_fib_counters_t *mp_copy = NULL;
1079   u32 mp_size;
1080
1081   mp_size = sizeof (*mp_copy) +
1082     ntohl (mp->count) * sizeof (vl_api_ip4_fib_counter_t);
1083
1084   /* *INDENT-OFF* */
1085   pool_foreach(reg, sm->stats_registrations,
1086   ({
1087     q = vl_api_client_index_to_input_queue (reg->client_index);
1088     if (q)
1089       {
1090         if (q_prev && (q_prev->cursize < q_prev->maxsize))
1091           {
1092             mp_copy = vl_msg_api_alloc_as_if_client(mp_size);
1093             clib_memcpy(mp_copy, mp, mp_size);
1094             vl_msg_api_send_shmem (q_prev, (u8 *)&mp);
1095             mp = mp_copy;
1096           }
1097         q_prev = q;
1098       }
1099   }));
1100   /* *INDENT-ON* */
1101   if (q_prev && (q_prev->cursize < q_prev->maxsize))
1102     {
1103       vl_msg_api_send_shmem (q_prev, (u8 *) & mp);
1104     }
1105   else
1106     {
1107       vl_msg_api_free (mp);
1108     }
1109 }
1110
1111 static void
1112 vl_api_vnet_ip4_nbr_counters_t_handler (vl_api_vnet_ip4_nbr_counters_t * mp)
1113 {
1114   vpe_client_registration_t *reg;
1115   stats_main_t *sm = &stats_main;
1116   unix_shared_memory_queue_t *q, *q_prev = NULL;
1117   vl_api_vnet_ip4_nbr_counters_t *mp_copy = NULL;
1118   u32 mp_size;
1119
1120   mp_size = sizeof (*mp_copy) +
1121     ntohl (mp->count) * sizeof (vl_api_ip4_nbr_counter_t);
1122
1123   /* *INDENT-OFF* */
1124   pool_foreach(reg, sm->stats_registrations,
1125   ({
1126     q = vl_api_client_index_to_input_queue (reg->client_index);
1127     if (q)
1128       {
1129         if (q_prev && (q_prev->cursize < q_prev->maxsize))
1130           {
1131             mp_copy = vl_msg_api_alloc_as_if_client(mp_size);
1132             clib_memcpy(mp_copy, mp, mp_size);
1133             vl_msg_api_send_shmem (q_prev, (u8 *)&mp);
1134             mp = mp_copy;
1135           }
1136         q_prev = q;
1137       }
1138   }));
1139   /* *INDENT-ON* */
1140   if (q_prev && (q_prev->cursize < q_prev->maxsize))
1141     {
1142       vl_msg_api_send_shmem (q_prev, (u8 *) & mp);
1143     }
1144   else
1145     {
1146       vl_msg_api_free (mp);
1147     }
1148 }
1149
1150 static void
1151 vl_api_vnet_ip6_fib_counters_t_handler (vl_api_vnet_ip6_fib_counters_t * mp)
1152 {
1153   vpe_client_registration_t *reg;
1154   stats_main_t *sm = &stats_main;
1155   unix_shared_memory_queue_t *q, *q_prev = NULL;
1156   vl_api_vnet_ip6_fib_counters_t *mp_copy = NULL;
1157   u32 mp_size;
1158
1159   mp_size = sizeof (*mp_copy) +
1160     ntohl (mp->count) * sizeof (vl_api_ip6_fib_counter_t);
1161
1162   /* *INDENT-OFF* */
1163   pool_foreach(reg, sm->stats_registrations,
1164   ({
1165     q = vl_api_client_index_to_input_queue (reg->client_index);
1166     if (q)
1167       {
1168         if (q_prev && (q_prev->cursize < q_prev->maxsize))
1169           {
1170             mp_copy = vl_msg_api_alloc_as_if_client(mp_size);
1171             clib_memcpy(mp_copy, mp, mp_size);
1172             vl_msg_api_send_shmem (q_prev, (u8 *)&mp);
1173             mp = mp_copy;
1174           }
1175         q_prev = q;
1176       }
1177   }));
1178   /* *INDENT-ON* */
1179   if (q_prev && (q_prev->cursize < q_prev->maxsize))
1180     {
1181       vl_msg_api_send_shmem (q_prev, (u8 *) & mp);
1182     }
1183   else
1184     {
1185       vl_msg_api_free (mp);
1186     }
1187 }
1188
1189 static void
1190 vl_api_vnet_ip6_nbr_counters_t_handler (vl_api_vnet_ip6_nbr_counters_t * mp)
1191 {
1192   vpe_client_registration_t *reg;
1193   stats_main_t *sm = &stats_main;
1194   unix_shared_memory_queue_t *q, *q_prev = NULL;
1195   vl_api_vnet_ip6_nbr_counters_t *mp_copy = NULL;
1196   u32 mp_size;
1197
1198   mp_size = sizeof (*mp_copy) +
1199     ntohl (mp->count) * sizeof (vl_api_ip6_nbr_counter_t);
1200
1201   /* *INDENT-OFF* */
1202   pool_foreach(reg, sm->stats_registrations,
1203   ({
1204     q = vl_api_client_index_to_input_queue (reg->client_index);
1205     if (q)
1206       {
1207         if (q_prev && (q_prev->cursize < q_prev->maxsize))
1208           {
1209             mp_copy = vl_msg_api_alloc_as_if_client(mp_size);
1210             clib_memcpy(mp_copy, mp, mp_size);
1211             vl_msg_api_send_shmem (q_prev, (u8 *)&mp);
1212             mp = mp_copy;
1213           }
1214         q_prev = q;
1215       }
1216   }));
1217   /* *INDENT-ON* */
1218   if (q_prev && (q_prev->cursize < q_prev->maxsize))
1219     {
1220       vl_msg_api_send_shmem (q_prev, (u8 *) & mp);
1221     }
1222   else
1223     {
1224       vl_msg_api_free (mp);
1225     }
1226 }
1227
1228 static void
1229 vl_api_want_stats_t_handler (vl_api_want_stats_t * mp)
1230 {
1231   stats_main_t *sm = &stats_main;
1232   vpe_client_registration_t *rp;
1233   vl_api_want_stats_reply_t *rmp;
1234   uword *p;
1235   i32 retval = 0;
1236   unix_shared_memory_queue_t *q;
1237
1238   p = hash_get (sm->stats_registration_hash, mp->client_index);
1239   if (p)
1240     {
1241       if (mp->enable_disable)
1242         {
1243           clib_warning ("pid %d: already enabled...", mp->pid);
1244           retval = -2;
1245           goto reply;
1246         }
1247       else
1248         {
1249           rp = pool_elt_at_index (sm->stats_registrations, p[0]);
1250           pool_put (sm->stats_registrations, rp);
1251           hash_unset (sm->stats_registration_hash, mp->client_index);
1252           goto reply;
1253         }
1254     }
1255   if (mp->enable_disable == 0)
1256     {
1257       clib_warning ("pid %d: already disabled...", mp->pid);
1258       retval = -3;
1259       goto reply;
1260     }
1261   pool_get (sm->stats_registrations, rp);
1262   rp->client_index = mp->client_index;
1263   rp->client_pid = mp->pid;
1264   hash_set (sm->stats_registration_hash, rp->client_index,
1265             rp - sm->stats_registrations);
1266
1267 reply:
1268   if (pool_elts (sm->stats_registrations))
1269     sm->enable_poller = 1;
1270   else
1271     sm->enable_poller = 0;
1272
1273   q = vl_api_client_index_to_input_queue (mp->client_index);
1274
1275   if (!q)
1276     return;
1277
1278   rmp = vl_msg_api_alloc (sizeof (*rmp));
1279   rmp->_vl_msg_id = ntohs (VL_API_WANT_STATS_REPLY);
1280   rmp->context = mp->context;
1281   rmp->retval = retval;
1282
1283   vl_msg_api_send_shmem (q, (u8 *) & rmp);
1284 }
1285
1286 int
1287 stats_memclnt_delete_callback (u32 client_index)
1288 {
1289   vpe_client_registration_t *rp;
1290   stats_main_t *sm = &stats_main;
1291   uword *p;
1292
1293   p = hash_get (sm->stats_registration_hash, client_index);
1294   if (p)
1295     {
1296       rp = pool_elt_at_index (sm->stats_registrations, p[0]);
1297       pool_put (sm->stats_registrations, rp);
1298       hash_unset (sm->stats_registration_hash, client_index);
1299     }
1300
1301   return 0;
1302 }
1303
1304 #define vl_api_vnet_ip4_fib_counters_t_endian vl_noop_handler
1305 #define vl_api_vnet_ip4_fib_counters_t_print vl_noop_handler
1306 #define vl_api_vnet_ip6_fib_counters_t_endian vl_noop_handler
1307 #define vl_api_vnet_ip6_fib_counters_t_print vl_noop_handler
1308 #define vl_api_vnet_ip4_nbr_counters_t_endian vl_noop_handler
1309 #define vl_api_vnet_ip4_nbr_counters_t_print vl_noop_handler
1310 #define vl_api_vnet_ip6_nbr_counters_t_endian vl_noop_handler
1311 #define vl_api_vnet_ip6_nbr_counters_t_print vl_noop_handler
1312
1313 static clib_error_t *
1314 stats_init (vlib_main_t * vm)
1315 {
1316   stats_main_t *sm = &stats_main;
1317   api_main_t *am = &api_main;
1318   void *vlib_worker_thread_bootstrap_fn (void *arg);
1319
1320   sm->vlib_main = vm;
1321   sm->vnet_main = vnet_get_main ();
1322   sm->interface_main = &vnet_get_main ()->interface_main;
1323   sm->api_main = am;
1324   sm->stats_poll_interval_in_seconds = 10;
1325   sm->data_structure_lock =
1326     clib_mem_alloc_aligned (sizeof (data_structure_lock_t),
1327                             CLIB_CACHE_LINE_BYTES);
1328   memset (sm->data_structure_lock, 0, sizeof (*sm->data_structure_lock));
1329
1330 #define _(N,n)                                                  \
1331     vl_msg_api_set_handlers(VL_API_##N, #n,                     \
1332                            vl_api_##n##_t_handler,              \
1333                            vl_noop_handler,                     \
1334                            vl_api_##n##_t_endian,               \
1335                            vl_api_##n##_t_print,                \
1336                            sizeof(vl_api_##n##_t), 0 /* do NOT trace! */);
1337   foreach_stats_msg;
1338 #undef _
1339
1340   /* tell the msg infra not to free these messages... */
1341   am->message_bounce[VL_API_VNET_INTERFACE_COUNTERS] = 1;
1342   am->message_bounce[VL_API_VNET_IP4_FIB_COUNTERS] = 1;
1343   am->message_bounce[VL_API_VNET_IP6_FIB_COUNTERS] = 1;
1344   am->message_bounce[VL_API_VNET_IP4_NBR_COUNTERS] = 1;
1345   am->message_bounce[VL_API_VNET_IP6_NBR_COUNTERS] = 1;
1346
1347   return 0;
1348 }
1349
1350 VLIB_INIT_FUNCTION (stats_init);
1351
1352 /* *INDENT-OFF* */
1353 VLIB_REGISTER_THREAD (stats_thread_reg, static) = {
1354   .name = "stats",
1355   .function = stats_thread_fn,
1356   .fixed_count = 1,
1357   .count = 1,
1358   .no_data_structure_clone = 1,
1359   .use_pthreads = 1,
1360 };
1361 /* *INDENT-ON* */
1362
1363 /*
1364  * fd.io coding-style-patch-verification: ON
1365  *
1366  * Local Variables:
1367  * eval: (c-set-style "gnu")
1368  * End:
1369  */