counter.h revision 044183fa
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
59typedef 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*/
77always_inline void
78vlib_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*/
107always_inline u64
108vlib_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*/
142always_inline void
143vlib_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 */
164typedef 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
175always_inline void
176vlib_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*/
186always_inline void
187vlib_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*/
198always_inline void
199vlib_counter_zero (vlib_counter_t * a)
200{
201  a->packets = a->bytes = 0;
202}
203
204/** Mini combined counter */
205typedef struct
206{
207  u16 packets;			/**< Packet count */
208  i16 bytes;			/**< Byte count */
209} vlib_mini_counter_t;
210
211/** A collection of combined counters */
212typedef 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*/
225void 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*/
230void 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
240always_inline void
241vlib_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
300static inline void
301vlib_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*/
334always_inline void
335vlib_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
359void 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
367void 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
380serialize_function_t serialize_vlib_simple_counter_main,
381  unserialize_vlib_simple_counter_main;
382serialize_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 */
394