vppinfra: add unformat_init_path
[vpp.git] / src / vppinfra / time.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 /*
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
53 estimate_clock_frequency (f64 sample_time)
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
73 clock_frequency_from_proc_filesystem (void)
74 {
75   f64 cpu_freq = 1e9;           /* better than 40... */
76   f64 ppc_timebase = 0;         /* warnings be gone */
77   unformat_input_t input;
78
79 /* $$$$ aarch64 kernel doesn't report "cpu MHz" */
80 #if defined(__aarch64__)
81   return 0.0;
82 #endif
83
84   cpu_freq = 0;
85
86   ppc_timebase = 0;
87   if (unformat_init_file (&input, "/proc/cpuinfo"))
88     {
89       while (unformat_check_input (&input) != UNFORMAT_END_OF_INPUT)
90         {
91           if (unformat (&input, "cpu MHz : %f", &cpu_freq))
92             cpu_freq *= 1e6;
93           else if (unformat (&input, "timebase : %f", &ppc_timebase))
94             ;
95           else
96             unformat_skip_line (&input);
97         }
98
99       unformat_free (&input);
100     }
101   else
102     return cpu_freq;
103
104   /* Override CPU frequency with time base for PPC. */
105   if (ppc_timebase != 0)
106     cpu_freq = ppc_timebase;
107
108   return cpu_freq;
109 }
110
111 /* Fetch cpu frequency via reading /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq
112    Only works for Linux. */
113 static f64
114 clock_frequency_from_sys_filesystem (void)
115 {
116   f64 cpu_freq = 0.0;
117   unformat_input_t input;
118
119   /* Time stamp always runs at max frequency. */
120   cpu_freq = 0;
121
122   if (unformat_init_file (
123         &input, "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq"))
124     {
125       if (unformat (&input, "%f", &cpu_freq))
126         cpu_freq *= 1e3; /* measured in kHz */
127       unformat_free (&input);
128     }
129
130   return cpu_freq;
131 }
132
133 __clib_export f64
134 os_cpu_clock_frequency (void)
135 {
136 #if defined (__aarch64__)
137   /* The system counter increments at a fixed frequency. It is distributed
138    * to each core which has registers for reading the current counter value
139    * as well as the clock frequency. The system counter is not clocked at
140    * the same frequency as the core. */
141   u64 hz;
142   asm volatile ("mrs %0, cntfrq_el0":"=r" (hz));
143   return (f64) hz;
144 #endif
145   f64 cpu_freq;
146
147 #ifdef __x86_64__
148   u32 __clib_unused eax = 0, ebx = 0, ecx = 0, edx = 0;
149   clib_get_cpuid (0x00, &eax, &ebx, &ecx, &edx);
150   if (eax >= 0x15)
151     {
152       u32 max_leaf = eax;
153       /*
154          CPUID Leaf 0x15 - Time Stamp Counter and Nominal Core Crystal Clock Info
155          eax - denominator of the TSC/”core crystal clock” ratio
156          ebx - numerator of the TSC/”core crystal clock” ratio
157          ecx - nominal frequency of the core crystal clock in Hz
158          edx - reseved
159        */
160
161       clib_get_cpuid (0x15, &eax, &ebx, &ecx, &edx);
162       if (ebx && ecx)
163         return (u64) ecx *ebx / eax;
164
165       if (max_leaf >= 0x16)
166         {
167           /*
168              CPUID Leaf 0x16 - Processor Frequency Information Leaf
169              eax - Bits 15 - 00: Processor Base Frequency (in MHz).
170            */
171
172           clib_get_cpuid (0x16, &eax, &ebx, &ecx, &edx);
173           if (eax)
174             return 1e6 * (eax & 0xffff);
175         }
176     }
177 #endif
178
179   /* If we have an invariant TSC, use it to estimate the clock frequency */
180   if (clib_cpu_supports_invariant_tsc ())
181     return estimate_clock_frequency (1e-3);
182
183   /* Next, try /sys version. */
184   cpu_freq = clock_frequency_from_sys_filesystem ();
185   if (cpu_freq != 0)
186     return cpu_freq;
187
188   /* Next try /proc version. */
189   cpu_freq = clock_frequency_from_proc_filesystem ();
190   if (cpu_freq != 0)
191     return cpu_freq;
192
193   /* If /proc/cpuinfo fails (e.g. not running on Linux) fall back to
194      gettimeofday based estimated clock frequency. */
195   return estimate_clock_frequency (1e-3);
196 }
197
198 #endif /* CLIB_UNIX */
199
200 /* Initialize time. */
201 __clib_export void
202 clib_time_init (clib_time_t * c)
203 {
204   clib_memset (c, 0, sizeof (c[0]));
205   c->clocks_per_second = os_cpu_clock_frequency ();
206   /*
207    * Sporadic reports of os_cpu_clock_frequency() returning 0.0
208    * in highly parallel container environments.
209    * To avoid immediate division by zero:
210    *   Step 1: try estimate_clock_frequency().
211    *   Step 2: give up. Pretend we have a 2gHz clock.
212    */
213   if (PREDICT_FALSE (c->clocks_per_second == 0.0))
214     {
215       c->clocks_per_second = estimate_clock_frequency (1e-3);
216       if (c->clocks_per_second == 0.0)
217         {
218           clib_warning ("os_cpu_clock_frequency() returned 0.0, use 2e9...");
219           c->clocks_per_second = 2e9;
220         }
221     }
222   c->seconds_per_clock = 1 / c->clocks_per_second;
223   c->log2_clocks_per_second = min_log2_u64 ((u64) c->clocks_per_second);
224
225   /* Verify frequency every 16 sec */
226   c->log2_clocks_per_frequency_verify = c->log2_clocks_per_second + 4;
227
228   c->last_verify_reference_time = unix_time_now ();
229   c->init_reference_time = c->last_verify_reference_time;
230   c->last_cpu_time = clib_cpu_time_now ();
231   c->init_cpu_time = c->last_verify_cpu_time = c->last_cpu_time;
232   c->total_cpu_time = 0ULL;
233
234   /*
235    * Use exponential smoothing, with a half-life of 1 minute
236    * reported_rate(t) = reported_rate(t-1) * K + rate(t)*(1-K)
237    * where K = e**(-1.0/3.75);
238    * 15 samples in 4 minutes
239    * 7.5 samples in 2 minutes,
240    * 3.75 samples in 1 minute, etc.
241    */
242   c->damping_constant = exp (-1.0 / 3.75);
243 }
244
245 __clib_export void
246 clib_time_verify_frequency (clib_time_t * c)
247 {
248   f64 now_reference, delta_reference, delta_reference_max;
249   f64 delta_clock_in_seconds;
250   u64 now_clock, delta_clock;
251   f64 new_clocks_per_second, delta;
252
253   /* Ask the kernel and the CPU what time it is... */
254   now_reference = unix_time_now ();
255   now_clock = clib_cpu_time_now ();
256
257   /* Compute change in the reference clock */
258   delta_reference = now_reference - c->last_verify_reference_time;
259
260   /* And change in the CPU clock */
261   delta_clock_in_seconds = (f64) (now_clock - c->last_verify_cpu_time) *
262     c->seconds_per_clock;
263
264   /*
265    * Recompute vpp start time reference, and total clocks
266    * using the current clock rate
267    */
268   c->init_reference_time += (delta_reference - delta_clock_in_seconds);
269   c->total_cpu_time = (now_reference - c->init_reference_time)
270     * c->clocks_per_second;
271
272   c->last_cpu_time = now_clock;
273
274   /* Calculate a new clock rate sample */
275   delta_clock = c->last_cpu_time - c->last_verify_cpu_time;
276
277   c->last_verify_cpu_time = c->last_cpu_time;
278   c->last_verify_reference_time = now_reference;
279
280   /*
281    * Is the reported reference interval non-positive,
282    * or off by a factor of two - or 8 seconds - whichever is larger?
283    * Someone reset the clock behind our back.
284    */
285   delta_reference_max = (f64) (2ULL << c->log2_clocks_per_frequency_verify) /
286     (f64) (1ULL << c->log2_clocks_per_second);
287   delta_reference_max = delta_reference_max > 8.0 ? delta_reference_max : 8.0;
288
289   /* Ignore this sample */
290   if (delta_reference <= 0.0 || delta_reference > delta_reference_max)
291     return;
292
293   /*
294    * Reject large frequency changes, another consequence of
295    * system clock changes particularly with old kernels.
296    */
297   new_clocks_per_second = ((f64) delta_clock) / delta_reference;
298
299   /* Compute abs(rate change) */
300   delta = new_clocks_per_second - c->clocks_per_second;
301   if (delta < 0.0)
302     delta = -delta;
303
304   /* If rate change > 1%, reject this sample */
305   if (PREDICT_FALSE ((delta / c->clocks_per_second) > .01))
306     {
307       clib_warning ("Rejecting large frequency change of %.2f%%",
308                     (delta / c->clocks_per_second) * 100.0);
309       return;
310     }
311
312   /* Add sample to the exponentially-smoothed rate */
313   c->clocks_per_second = c->clocks_per_second * c->damping_constant +
314     (1.0 - c->damping_constant) * new_clocks_per_second;
315   c->seconds_per_clock = 1.0 / c->clocks_per_second;
316
317   /*
318    * Recalculate total_cpu_time based on the kernel timebase, and
319    * the calculated clock rate
320    */
321   c->total_cpu_time =
322     (now_reference - c->init_reference_time) * c->clocks_per_second;
323 }
324
325
326 __clib_export u8 *
327 format_clib_time (u8 * s, va_list * args)
328 {
329   clib_time_t *c = va_arg (*args, clib_time_t *);
330   int verbose = va_arg (*args, int);
331   f64 now, reftime, delta_reftime_in_seconds, error;
332
333   /* Compute vpp elapsed time from the CPU clock */
334   reftime = unix_time_now ();
335   now = clib_time_now (c);
336
337   s = format (s, "Time now %.6f", now);
338   if (verbose == 0)
339     return s;
340
341   /* And also from the kernel */
342   delta_reftime_in_seconds = reftime - c->init_reference_time;
343
344   error = now - delta_reftime_in_seconds;
345
346   s = format (s, ", reftime %.6f, error %.6f, clocks/sec %.6f",
347               delta_reftime_in_seconds, error, c->clocks_per_second);
348   return (s);
349 }
350
351 /*
352  * fd.io coding-style-patch-verification: ON
353  *
354  * Local Variables:
355  * eval: (c-set-style "gnu")
356  * End:
357  */