FD.io VPP  v21.06-3-gbb25fbf28
Vector Packet Processing
time.c
Go to the documentation of this file.
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  Copyright (c) 2005 Eliot Dresselhaus
17 
18  Permission is hereby granted, free of charge, to any person obtaining
19  a copy of this software and associated documentation files (the
20  "Software"), to deal in the Software without restriction, including
21  without limitation the rights to use, copy, modify, merge, publish,
22  distribute, sublicense, and/or sell copies of the Software, and to
23  permit persons to whom the Software is furnished to do so, subject to
24  the following conditions:
25 
26  The above copyright notice and this permission notice shall be
27  included in all copies or substantial portions of the Software.
28 
29  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
30  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
31  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
32  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
33  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
34  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
35  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 */
37 
38 #include <vppinfra/os.h>
39 #include <vppinfra/time.h>
40 #include <vppinfra/format.h>
41 #include <vppinfra/cpu.h>
42 #include <math.h>
43 
44 #ifdef CLIB_UNIX
45 
46 #include <math.h>
47 #include <sys/time.h>
48 #include <fcntl.h>
49 
50 /* Not very accurate way of determining cpu clock frequency
51  for unix. Better to use /proc/cpuinfo on linux. */
52 static f64
54 {
55  f64 time_now, time_start, time_limit, freq;
56  u64 t[2];
57 
58  time_start = time_now = unix_time_now ();
59  time_limit = time_now + sample_time;
60  t[0] = clib_cpu_time_now ();
61  while (time_now < time_limit)
62  time_now = unix_time_now ();
63  t[1] = clib_cpu_time_now ();
64 
65  freq = (t[1] - t[0]) / (time_now - time_start);
66 
67  return freq;
68 }
69 
70 /* Fetch cpu frequency via parseing /proc/cpuinfo.
71  Only works for Linux. */
72 static f64
74 {
75  f64 cpu_freq = 1e9; /* better than 40... */
76  f64 ppc_timebase = 0; /* warnings be gone */
77  int fd;
78  unformat_input_t input;
79 
80 /* $$$$ aarch64 kernel doesn't report "cpu MHz" */
81 #if defined(__aarch64__)
82  return 0.0;
83 #endif
84 
85  cpu_freq = 0;
86  fd = open ("/proc/cpuinfo", 0);
87  if (fd < 0)
88  return cpu_freq;
89 
90  unformat_init_clib_file (&input, fd);
91 
92  ppc_timebase = 0;
94  {
95  if (unformat (&input, "cpu MHz : %f", &cpu_freq))
96  cpu_freq *= 1e6;
97  else if (unformat (&input, "timebase : %f", &ppc_timebase))
98  ;
99  else
100  unformat_skip_line (&input);
101  }
102 
103  unformat_free (&input);
104 
105  close (fd);
106 
107  /* Override CPU frequency with time base for PPC. */
108  if (ppc_timebase != 0)
109  cpu_freq = ppc_timebase;
110 
111  return cpu_freq;
112 }
113 
114 /* Fetch cpu frequency via reading /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq
115  Only works for Linux. */
116 static f64
118 {
119  f64 cpu_freq = 0.0;
120  int fd;
121  unformat_input_t input;
122 
123  /* Time stamp always runs at max frequency. */
124  cpu_freq = 0;
125  fd = open ("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq", 0);
126  if (fd < 0)
127  goto done;
128 
129  unformat_init_clib_file (&input, fd);
130  (void) unformat (&input, "%f", &cpu_freq);
131  cpu_freq *= 1e3; /* measured in kHz */
132  unformat_free (&input);
133  close (fd);
134 done:
135  return cpu_freq;
136 }
137 
138 __clib_export f64
140 {
141 #if defined (__aarch64__)
142  /* The system counter increments at a fixed frequency. It is distributed
143  * to each core which has registers for reading the current counter value
144  * as well as the clock frequency. The system counter is not clocked at
145  * the same frequency as the core. */
146  u64 hz;
147  asm volatile ("mrs %0, cntfrq_el0":"=r" (hz));
148  return (f64) hz;
149 #endif
150  f64 cpu_freq;
151 
152 #ifdef __x86_64__
153  u32 __clib_unused eax = 0, ebx = 0, ecx = 0, edx = 0;
154  clib_get_cpuid (0x00, &eax, &ebx, &ecx, &edx);
155  if (eax >= 0x15)
156  {
157  u32 max_leaf = eax;
158  /*
159  CPUID Leaf 0x15 - Time Stamp Counter and Nominal Core Crystal Clock Info
160  eax - denominator of the TSC/”core crystal clock” ratio
161  ebx - numerator of the TSC/”core crystal clock” ratio
162  ecx - nominal frequency of the core crystal clock in Hz
163  edx - reseved
164  */
165 
166  clib_get_cpuid (0x15, &eax, &ebx, &ecx, &edx);
167  if (ebx && ecx)
168  return (u64) ecx *ebx / eax;
169 
170  if (max_leaf >= 0x16)
171  {
172  /*
173  CPUID Leaf 0x16 - Processor Frequency Information Leaf
174  eax - Bits 15 - 00: Processor Base Frequency (in MHz).
175  */
176 
177  clib_get_cpuid (0x16, &eax, &ebx, &ecx, &edx);
178  if (eax)
179  return 1e6 * (eax & 0xffff);
180  }
181  }
182 #endif
183 
184  /* If we have an invariant TSC, use it to estimate the clock frequency */
185  if (clib_cpu_supports_invariant_tsc ())
186  return estimate_clock_frequency (1e-3);
187 
188  /* Next, try /sys version. */
190  if (cpu_freq != 0)
191  return cpu_freq;
192 
193  /* Next try /proc version. */
195  if (cpu_freq != 0)
196  return cpu_freq;
197 
198  /* If /proc/cpuinfo fails (e.g. not running on Linux) fall back to
199  gettimeofday based estimated clock frequency. */
200  return estimate_clock_frequency (1e-3);
201 }
202 
203 #endif /* CLIB_UNIX */
204 
205 /* Initialize time. */
206 __clib_export void
208 {
209  clib_memset (c, 0, sizeof (c[0]));
210  c->clocks_per_second = os_cpu_clock_frequency ();
211  /*
212  * Sporadic reports of os_cpu_clock_frequency() returning 0.0
213  * in highly parallel container environments.
214  * To avoid immediate division by zero:
215  * Step 1: try estimate_clock_frequency().
216  * Step 2: give up. Pretend we have a 2gHz clock.
217  */
218  if (PREDICT_FALSE (c->clocks_per_second == 0.0))
219  {
220  c->clocks_per_second = estimate_clock_frequency (1e-3);
221  if (c->clocks_per_second == 0.0)
222  {
223  clib_warning ("os_cpu_clock_frequency() returned 0.0, use 2e9...");
224  c->clocks_per_second = 2e9;
225  }
226  }
227  c->seconds_per_clock = 1 / c->clocks_per_second;
228  c->log2_clocks_per_second = min_log2_u64 ((u64) c->clocks_per_second);
229 
230  /* Verify frequency every 16 sec */
231  c->log2_clocks_per_frequency_verify = c->log2_clocks_per_second + 4;
232 
233  c->last_verify_reference_time = unix_time_now ();
234  c->init_reference_time = c->last_verify_reference_time;
235  c->last_cpu_time = clib_cpu_time_now ();
236  c->init_cpu_time = c->last_verify_cpu_time = c->last_cpu_time;
237  c->total_cpu_time = 0ULL;
238 
239  /*
240  * Use exponential smoothing, with a half-life of 1 minute
241  * reported_rate(t) = reported_rate(t-1) * K + rate(t)*(1-K)
242  * where K = e**(-1.0/3.75);
243  * 15 samples in 4 minutes
244  * 7.5 samples in 2 minutes,
245  * 3.75 samples in 1 minute, etc.
246  */
247  c->damping_constant = exp (-1.0 / 3.75);
248 }
249 
250 __clib_export void
252 {
253  f64 now_reference, delta_reference, delta_reference_max;
254  f64 delta_clock_in_seconds;
255  u64 now_clock, delta_clock;
256  f64 new_clocks_per_second, delta;
257 
258  /* Ask the kernel and the CPU what time it is... */
259  now_reference = unix_time_now ();
260  now_clock = clib_cpu_time_now ();
261 
262  /* Compute change in the reference clock */
263  delta_reference = now_reference - c->last_verify_reference_time;
264 
265  /* And change in the CPU clock */
266  delta_clock_in_seconds = (f64) (now_clock - c->last_verify_cpu_time) *
267  c->seconds_per_clock;
268 
269  /*
270  * Recompute vpp start time reference, and total clocks
271  * using the current clock rate
272  */
273  c->init_reference_time += (delta_reference - delta_clock_in_seconds);
274  c->total_cpu_time = (now_reference - c->init_reference_time)
275  * c->clocks_per_second;
276 
277  c->last_cpu_time = now_clock;
278 
279  /* Calculate a new clock rate sample */
280  delta_clock = c->last_cpu_time - c->last_verify_cpu_time;
281 
282  c->last_verify_cpu_time = c->last_cpu_time;
283  c->last_verify_reference_time = now_reference;
284 
285  /*
286  * Is the reported reference interval non-positive,
287  * or off by a factor of two - or 8 seconds - whichever is larger?
288  * Someone reset the clock behind our back.
289  */
290  delta_reference_max = (f64) (2ULL << c->log2_clocks_per_frequency_verify) /
291  (f64) (1ULL << c->log2_clocks_per_second);
292  delta_reference_max = delta_reference_max > 8.0 ? delta_reference_max : 8.0;
293 
294  /* Ignore this sample */
295  if (delta_reference <= 0.0 || delta_reference > delta_reference_max)
296  return;
297 
298  /*
299  * Reject large frequency changes, another consequence of
300  * system clock changes particularly with old kernels.
301  */
302  new_clocks_per_second = ((f64) delta_clock) / delta_reference;
303 
304  /* Compute abs(rate change) */
305  delta = new_clocks_per_second - c->clocks_per_second;
306  if (delta < 0.0)
307  delta = -delta;
308 
309  /* If rate change > 1%, reject this sample */
310  if (PREDICT_FALSE ((delta / c->clocks_per_second) > .01))
311  {
312  clib_warning ("Rejecting large frequency change of %.2f%%",
313  (delta / c->clocks_per_second) * 100.0);
314  return;
315  }
316 
317  /* Add sample to the exponentially-smoothed rate */
318  c->clocks_per_second = c->clocks_per_second * c->damping_constant +
319  (1.0 - c->damping_constant) * new_clocks_per_second;
320  c->seconds_per_clock = 1.0 / c->clocks_per_second;
321 
322  /*
323  * Recalculate total_cpu_time based on the kernel timebase, and
324  * the calculated clock rate
325  */
326  c->total_cpu_time =
327  (now_reference - c->init_reference_time) * c->clocks_per_second;
328 }
329 
330 
331 __clib_export u8 *
332 format_clib_time (u8 * s, va_list * args)
333 {
334  clib_time_t *c = va_arg (*args, clib_time_t *);
335  int verbose = va_arg (*args, int);
336  f64 now, reftime, delta_reftime_in_seconds, error;
337 
338  /* Compute vpp elapsed time from the CPU clock */
339  reftime = unix_time_now ();
340  now = clib_time_now (c);
341 
342  s = format (s, "Time now %.6f", now);
343  if (verbose == 0)
344  return s;
345 
346  /* And also from the kernel */
347  delta_reftime_in_seconds = reftime - c->init_reference_time;
348 
349  error = now - delta_reftime_in_seconds;
350 
351  s = format (s, ", reftime %.6f, error %.6f, clocks/sec %.6f",
352  delta_reftime_in_seconds, error, c->clocks_per_second);
353  return (s);
354 }
355 
356 /*
357  * fd.io coding-style-patch-verification: ON
358  *
359  * Local Variables:
360  * eval: (c-set-style "gnu")
361  * End:
362  */
os.h
unformat_init_clib_file
void unformat_init_clib_file(unformat_input_t *input, int file_descriptor)
Definition: unformat.c:1064
min_log2_u64
static u64 min_log2_u64(u64 x)
Definition: clib.h:232
unix_time_now
static f64 unix_time_now(void)
Definition: time.h:255
exp
u8 exp
Definition: fib_types.api:27
unformat_input_t
struct _unformat_input_t unformat_input_t
clock_frequency_from_sys_filesystem
static f64 clock_frequency_from_sys_filesystem(void)
Definition: time.c:117
error
Definition: cJSON.c:88
clib_time_init
__clib_export void clib_time_init(clib_time_t *c)
Definition: time.c:207
unformat
uword unformat(unformat_input_t *i, const char *fmt,...)
Definition: unformat.c:978
unformat_free
static void unformat_free(unformat_input_t *i)
Definition: format.h:155
math.h
PREDICT_FALSE
#define PREDICT_FALSE(x)
Definition: clib.h:124
estimate_clock_frequency
static f64 estimate_clock_frequency(f64 sample_time)
Definition: time.c:53
unformat_check_input
static uword unformat_check_input(unformat_input_t *i)
Definition: format.h:163
os_cpu_clock_frequency
__clib_export f64 os_cpu_clock_frequency(void)
Definition: time.c:139
c
svmdb_client_t * c
Definition: vpp_get_metrics.c:48
time.h
f64
double f64
Definition: types.h:142
clib_get_cpuid
static int clib_get_cpuid(const u32 lev, u32 *eax, u32 *ebx, u32 *ecx, u32 *edx)
Definition: cpu.h:171
format.h
clib_time_now
static f64 clib_time_now(clib_time_t *c)
Definition: time.h:230
cpu.h
clib_time_t
Definition: time.h:44
u64
unsigned long u64
Definition: types.h:89
format
description fragment has unexpected format
Definition: map.api:433
u32
unsigned int u32
Definition: types.h:88
unformat_skip_line
static void unformat_skip_line(unformat_input_t *i)
Definition: format.h:214
now
f64 now
Definition: nat44_ei_out2in.c:710
clib_time_verify_frequency
__clib_export void clib_time_verify_frequency(clib_time_t *c)
Definition: time.c:251
clib_memset
clib_memset(h->entries, 0, sizeof(h->entries[0]) *entries)
u8
unsigned char u8
Definition: types.h:56
clock_frequency_from_proc_filesystem
static f64 clock_frequency_from_proc_filesystem(void)
Definition: time.c:73
clib_warning
#define clib_warning(format, args...)
Definition: error.h:59
clib_cpu_time_now
static u64 clib_cpu_time_now(void)
Definition: time.h:81
UNFORMAT_END_OF_INPUT
#define UNFORMAT_END_OF_INPUT
Definition: format.h:137
format_clib_time
__clib_export u8 * format_clib_time(u8 *s, va_list *args)
Definition: time.c:332