abfa89eea6d271d879649fe9c3b6f2628236629a
[vpp.git] / src / vlib / counter.h
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 /*
16  * counter.h: simple and packet/byte counters
17  *
18  * Copyright (c) 2008 Eliot Dresselhaus
19  *
20  * Permission is hereby granted, free of charge, to any person obtaining
21  * a copy of this software and associated documentation files (the
22  * "Software"), to deal in the Software without restriction, including
23  * without limitation the rights to use, copy, modify, merge, publish,
24  * distribute, sublicense, and/or sell copies of the Software, and to
25  * permit persons to whom the Software is furnished to do so, subject to
26  * the following conditions:
27  *
28  * The above copyright notice and this permission notice shall be
29  * included in all copies or substantial portions of the Software.
30  *
31  *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
32  *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
33  *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
34  *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
35  *  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
36  *  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
37  *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
38  */
39
40 #ifndef included_vlib_counter_h
41 #define included_vlib_counter_h
42
43 /** \file
44
45     Optimized thread-safe counters.
46
47     Each vlib_[simple|combined]_counter_main_t consists of a single
48     vector of thread-safe / atomically-updated u64 counters [the
49     "maxi" vector], and a (u16 **) per-thread vector [the "minis"
50     vector] of narrow, per-thread counters.
51
52     The idea is to drastically reduce the number of atomic operations.
53     In the case of packet counts, we divide the number of atomic ops
54     by 2**16, etc.
55 */
56
57 /** A collection of simple counters */
58
59 typedef struct
60 {
61   u16 **minis;   /**< Per-thread u16 non-atomic counters */
62   u64 *maxi;     /**< Shared wide counters */
63   u64 *value_at_last_clear; /**< Counter values as of last clear. */
64   u64 *value_at_last_serialize; /**< Values as of last serialize. */
65   u32 last_incremental_serialize_index; /**< Last counter index
66                                            serialized incrementally. */
67
68   char *name;                   /**< The counter collection's name. */
69 } vlib_simple_counter_main_t;
70
71 /** Increment a simple counter
72     @param cm - (vlib_simple_counter_main_t *) simple counter main pointer
73     @param cpu_index - (u32) the current cpu index
74     @param index - (u32) index of the counter to increment
75     @param increment - (u32) quantitiy to add to the counter
76 */
77 always_inline void
78 vlib_increment_simple_counter (vlib_simple_counter_main_t * cm,
79                                u32 cpu_index, u32 index, u32 increment)
80 {
81   u16 *my_minis;
82   u16 *mini;
83   u32 old, new;
84
85   my_minis = cm->minis[cpu_index];
86   mini = vec_elt_at_index (my_minis, index);
87   old = mini[0];
88   new = old + increment;
89   mini[0] = new;
90
91   if (PREDICT_FALSE (mini[0] != new))
92     {
93       __sync_fetch_and_add (&cm->maxi[index], new);
94       my_minis[index] = 0;
95     }
96 }
97
98 /** Get the value of a simple counter
99     Scrapes the entire set of mini counters. Innacurate unless
100     worker threads which might increment the counter are
101     barrier-synchronized
102
103     @param cm - (vlib_simple_counter_main_t *) simple counter main pointer
104     @param index - (u32) index of the counter to fetch
105     @returns - (u64) current counter value
106 */
107 always_inline u64
108 vlib_get_simple_counter (vlib_simple_counter_main_t * cm, u32 index)
109 {
110   u16 *my_minis, *mini;
111   u64 v;
112   int i;
113
114   ASSERT (index < vec_len (cm->maxi));
115
116   v = 0;
117
118   for (i = 0; i < vec_len (cm->minis); i++)
119     {
120       my_minis = cm->minis[i];
121       mini = vec_elt_at_index (my_minis, index);
122       v += mini[0];
123     }
124
125   v += cm->maxi[index];
126
127   if (index < vec_len (cm->value_at_last_clear))
128     {
129       ASSERT (v >= cm->value_at_last_clear[index]);
130       v -= cm->value_at_last_clear[index];
131     }
132
133   return v;
134 }
135
136 /** Clear a simple counter
137     Clears the set of per-thread u16 counters, and the u64 counter
138
139     @param cm - (vlib_simple_counter_main_t *) simple counter main pointer
140     @param index - (u32) index of the counter to clear
141 */
142 always_inline void
143 vlib_zero_simple_counter (vlib_simple_counter_main_t * cm, u32 index)
144 {
145   u16 *my_minis;
146   int i;
147
148   ASSERT (index < vec_len (cm->maxi));
149
150   for (i = 0; i < vec_len (cm->minis); i++)
151     {
152       my_minis = cm->minis[i];
153       my_minis[index] = 0;
154     }
155
156   cm->maxi[index] = 0;
157
158   if (index < vec_len (cm->value_at_last_clear))
159     cm->value_at_last_clear[index] = 0;
160 }
161
162 /** Combined counter to hold both packets and byte differences.
163  */
164 typedef struct
165 {
166   u64 packets;                  /**< packet counter */
167   u64 bytes;                    /**< byte counter  */
168 } vlib_counter_t;
169
170 /** Add two combined counters, results in the first counter
171     @param [in,out] a - (vlib_counter_t *) dst counter
172     @param b - (vlib_counter_t *) src counter
173 */
174
175 always_inline void
176 vlib_counter_add (vlib_counter_t * a, vlib_counter_t * b)
177 {
178   a->packets += b->packets;
179   a->bytes += b->bytes;
180 }
181
182 /** Subtract combined counters, results in the first counter
183     @param [in,out] a - (vlib_counter_t *) dst counter
184     @param b - (vlib_counter_t *) src counter
185 */
186 always_inline void
187 vlib_counter_sub (vlib_counter_t * a, vlib_counter_t * b)
188 {
189   ASSERT (a->packets >= b->packets);
190   ASSERT (a->bytes >= b->bytes);
191   a->packets -= b->packets;
192   a->bytes -= b->bytes;
193 }
194
195 /** Clear a combined counter
196     @param a - (vlib_counter_t *) counter to clear
197 */
198 always_inline void
199 vlib_counter_zero (vlib_counter_t * a)
200 {
201   a->packets = a->bytes = 0;
202 }
203
204 /** Mini combined counter */
205 typedef struct
206 {
207   u16 packets;                  /**< Packet count */
208   i16 bytes;                    /**< Byte count */
209 } vlib_mini_counter_t;
210
211 /** A collection of combined counters */
212 typedef struct
213 {
214   vlib_mini_counter_t **minis;  /**< Per-thread u16 non-atomic counter pairs */
215   vlib_counter_t *maxi;         /**< Shared wide counter pairs */
216   vlib_counter_t *value_at_last_clear;  /**< Counter values as of last clear. */
217   vlib_counter_t *value_at_last_serialize; /**< Counter values as of last serialize. */
218   u32 last_incremental_serialize_index; /**< Last counter index serialized incrementally. */
219   char *name; /**< The counter collection's name. */
220 } vlib_combined_counter_main_t;
221
222 /** Clear a collection of simple counters
223     @param cm - (vlib_simple_counter_main_t *) collection to clear
224 */
225 void vlib_clear_simple_counters (vlib_simple_counter_main_t * cm);
226
227 /** Clear a collection of combined counters
228     @param cm - (vlib_combined_counter_main_t *) collection to clear
229 */
230 void vlib_clear_combined_counters (vlib_combined_counter_main_t * cm);
231
232 /** Increment a combined counter
233     @param cm - (vlib_combined_counter_main_t *) comined counter main pointer
234     @param cpu_index - (u32) the current cpu index
235     @param index - (u32) index of the counter to increment
236     @param packet_increment - (u32) number of packets to add to the counter
237     @param byte_increment - (u32) number of bytes to add to the counter
238 */
239
240 always_inline void
241 vlib_increment_combined_counter (vlib_combined_counter_main_t * cm,
242                                  u32 cpu_index,
243                                  u32 index,
244                                  u32 packet_increment, u32 byte_increment)
245 {
246   vlib_mini_counter_t *my_minis, *mini;
247   u32 old_packets, new_packets;
248   i32 old_bytes, new_bytes;
249
250   /* Use this CPU's mini counter array */
251   my_minis = cm->minis[cpu_index];
252
253   mini = vec_elt_at_index (my_minis, index);
254   old_packets = mini->packets;
255   old_bytes = mini->bytes;
256
257   new_packets = old_packets + packet_increment;
258   new_bytes = old_bytes + byte_increment;
259
260   mini->packets = new_packets;
261   mini->bytes = new_bytes;
262
263   /* Bytes always overflow before packets.. */
264   if (PREDICT_FALSE (mini->bytes != new_bytes))
265     {
266       vlib_counter_t *maxi = vec_elt_at_index (cm->maxi, index);
267
268       __sync_fetch_and_add (&maxi->packets, new_packets);
269       __sync_fetch_and_add (&maxi->bytes, new_bytes);
270
271       mini->packets = 0;
272       mini->bytes = 0;
273     }
274 }
275
276 #define vlib_prefetch_combined_counter(_cm, _cpu_index, _index)  \
277 {                                                                \
278     vlib_mini_counter_t *_cpu_minis;                             \
279                                                                  \
280     /*                                                           \
281      * This CPU's mini index is assumed to already be in cache   \
282      */                                                          \
283     _cpu_minis = (_cm)->minis[(_cpu_index)];                     \
284     CLIB_PREFETCH(_cpu_minis + (_index),                         \
285                   sizeof(*_cpu_minis),                           \
286                   STORE);                                        \
287 }
288
289
290 /** Get the value of a combined counter, never called in the speed path
291     Scrapes the entire set of mini counters. Innacurate unless
292     worker threads which might increment the counter are
293     barrier-synchronized
294
295     @param cm - (vlib_combined_counter_main_t *) combined counter main pointer
296     @param index - (u32) index of the combined counter to fetch
297     @param result [out] - (vlib_counter_t *) result stored here
298 */
299
300 static inline void
301 vlib_get_combined_counter (vlib_combined_counter_main_t * cm,
302                            u32 index, vlib_counter_t * result)
303 {
304   vlib_mini_counter_t *my_minis, *mini;
305   vlib_counter_t *maxi;
306   int i;
307
308   result->packets = 0;
309   result->bytes = 0;
310
311   for (i = 0; i < vec_len (cm->minis); i++)
312     {
313       my_minis = cm->minis[i];
314
315       mini = vec_elt_at_index (my_minis, index);
316       result->packets += mini->packets;
317       result->bytes += mini->bytes;
318     }
319
320   maxi = vec_elt_at_index (cm->maxi, index);
321   result->packets += maxi->packets;
322   result->bytes += maxi->bytes;
323
324   if (index < vec_len (cm->value_at_last_clear))
325     vlib_counter_sub (result, &cm->value_at_last_clear[index]);
326 }
327
328 /** Clear a combined counter
329     Clears the set of per-thread u16 counters, and the shared vlib_counter_t
330
331     @param cm - (vlib_combined_counter_main_t *) combined counter main pointer
332     @param index - (u32) index of the counter to clear
333 */
334 always_inline void
335 vlib_zero_combined_counter (vlib_combined_counter_main_t * cm, u32 index)
336 {
337   vlib_mini_counter_t *mini, *my_minis;
338   int i;
339
340   for (i = 0; i < vec_len (cm->minis); i++)
341     {
342       my_minis = cm->minis[i];
343
344       mini = vec_elt_at_index (my_minis, index);
345       mini->packets = 0;
346       mini->bytes = 0;
347     }
348
349   vlib_counter_zero (&cm->maxi[index]);
350   if (index < vec_len (cm->value_at_last_clear))
351     vlib_counter_zero (&cm->value_at_last_clear[index]);
352 }
353
354 /** validate a simple counter
355     @param cm - (vlib_simple_counter_main_t *) pointer to the counter collection
356     @param index - (u32) index of the counter to validate
357 */
358
359 void vlib_validate_simple_counter (vlib_simple_counter_main_t * cm,
360                                    u32 index);
361 /** validate a combined counter
362     @param cm - (vlib_combined_counter_main_t *) pointer to the counter
363     collection
364     @param index - (u32) index of the counter to validate
365 */
366
367 void vlib_validate_combined_counter (vlib_combined_counter_main_t * cm,
368                                      u32 index);
369
370 /** Obtain the number of simple or combined counters allocated.
371     A macro which reduces to to vec_len(cm->maxi), the answer in either
372     case.
373
374     @param cm - (vlib_simple_counter_main_t) or
375     (vlib_combined_counter_main_t) the counter collection to interrogate
376     @returns vec_len(cm->maxi)
377 */
378 #define vlib_counter_len(cm) vec_len((cm)->maxi)
379
380 serialize_function_t serialize_vlib_simple_counter_main,
381   unserialize_vlib_simple_counter_main;
382 serialize_function_t serialize_vlib_combined_counter_main,
383   unserialize_vlib_combined_counter_main;
384
385 #endif /* included_vlib_counter_h */
386
387 /*
388  * fd.io coding-style-patch-verification: ON
389  *
390  * Local Variables:
391  * eval: (c-set-style "gnu")
392  * End:
393  */