PageRenderTime 118ms CodeModel.GetById 35ms RepoModel.GetById 0ms app.codeStats 0ms

/tools/testing/selftests/timers/freq-step.c

https://github.com/kvaneesh/linux
C | 263 lines | 194 code | 59 blank | 10 comment | 21 complexity | 67e14de020306c5f19bae0322a85112c MD5 | raw file
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * This test checks the response of the system clock to frequency
  4. * steps made with adjtimex(). The frequency error and stability of
  5. * the CLOCK_MONOTONIC clock relative to the CLOCK_MONOTONIC_RAW clock
  6. * is measured in two intervals following the step. The test fails if
  7. * values from the second interval exceed specified limits.
  8. *
  9. * Copyright (C) Miroslav Lichvar <mlichvar@redhat.com> 2017
  10. */
  11. #include <math.h>
  12. #include <stdio.h>
  13. #include <sys/timex.h>
  14. #include <time.h>
  15. #include <unistd.h>
  16. #include "../kselftest.h"
  17. #define SAMPLES 100
  18. #define SAMPLE_READINGS 10
  19. #define MEAN_SAMPLE_INTERVAL 0.1
  20. #define STEP_INTERVAL 1.0
  21. #define MAX_PRECISION 500e-9
  22. #define MAX_FREQ_ERROR 0.02e-6
  23. #define MAX_STDDEV 50e-9
  24. #ifndef ADJ_SETOFFSET
  25. #define ADJ_SETOFFSET 0x0100
  26. #endif
  27. struct sample {
  28. double offset;
  29. double time;
  30. };
  31. static time_t mono_raw_base;
  32. static time_t mono_base;
  33. static long user_hz;
  34. static double precision;
  35. static double mono_freq_offset;
  36. static double diff_timespec(struct timespec *ts1, struct timespec *ts2)
  37. {
  38. return ts1->tv_sec - ts2->tv_sec + (ts1->tv_nsec - ts2->tv_nsec) / 1e9;
  39. }
  40. static double get_sample(struct sample *sample)
  41. {
  42. double delay, mindelay = 0.0;
  43. struct timespec ts1, ts2, ts3;
  44. int i;
  45. for (i = 0; i < SAMPLE_READINGS; i++) {
  46. clock_gettime(CLOCK_MONOTONIC_RAW, &ts1);
  47. clock_gettime(CLOCK_MONOTONIC, &ts2);
  48. clock_gettime(CLOCK_MONOTONIC_RAW, &ts3);
  49. ts1.tv_sec -= mono_raw_base;
  50. ts2.tv_sec -= mono_base;
  51. ts3.tv_sec -= mono_raw_base;
  52. delay = diff_timespec(&ts3, &ts1);
  53. if (delay <= 1e-9) {
  54. i--;
  55. continue;
  56. }
  57. if (!i || delay < mindelay) {
  58. sample->offset = diff_timespec(&ts2, &ts1);
  59. sample->offset -= delay / 2.0;
  60. sample->time = ts1.tv_sec + ts1.tv_nsec / 1e9;
  61. mindelay = delay;
  62. }
  63. }
  64. return mindelay;
  65. }
  66. static void reset_ntp_error(void)
  67. {
  68. struct timex txc;
  69. txc.modes = ADJ_SETOFFSET;
  70. txc.time.tv_sec = 0;
  71. txc.time.tv_usec = 0;
  72. if (adjtimex(&txc) < 0) {
  73. perror("[FAIL] adjtimex");
  74. ksft_exit_fail();
  75. }
  76. }
  77. static void set_frequency(double freq)
  78. {
  79. struct timex txc;
  80. int tick_offset;
  81. tick_offset = 1e6 * freq / user_hz;
  82. txc.modes = ADJ_TICK | ADJ_FREQUENCY;
  83. txc.tick = 1000000 / user_hz + tick_offset;
  84. txc.freq = (1e6 * freq - user_hz * tick_offset) * (1 << 16);
  85. if (adjtimex(&txc) < 0) {
  86. perror("[FAIL] adjtimex");
  87. ksft_exit_fail();
  88. }
  89. }
  90. static void regress(struct sample *samples, int n, double *intercept,
  91. double *slope, double *r_stddev, double *r_max)
  92. {
  93. double x, y, r, x_sum, y_sum, xy_sum, x2_sum, r2_sum;
  94. int i;
  95. x_sum = 0.0, y_sum = 0.0, xy_sum = 0.0, x2_sum = 0.0;
  96. for (i = 0; i < n; i++) {
  97. x = samples[i].time;
  98. y = samples[i].offset;
  99. x_sum += x;
  100. y_sum += y;
  101. xy_sum += x * y;
  102. x2_sum += x * x;
  103. }
  104. *slope = (xy_sum - x_sum * y_sum / n) / (x2_sum - x_sum * x_sum / n);
  105. *intercept = (y_sum - *slope * x_sum) / n;
  106. *r_max = 0.0, r2_sum = 0.0;
  107. for (i = 0; i < n; i++) {
  108. x = samples[i].time;
  109. y = samples[i].offset;
  110. r = fabs(x * *slope + *intercept - y);
  111. if (*r_max < r)
  112. *r_max = r;
  113. r2_sum += r * r;
  114. }
  115. *r_stddev = sqrt(r2_sum / n);
  116. }
  117. static int run_test(int calibration, double freq_base, double freq_step)
  118. {
  119. struct sample samples[SAMPLES];
  120. double intercept, slope, stddev1, max1, stddev2, max2;
  121. double freq_error1, freq_error2;
  122. int i;
  123. set_frequency(freq_base);
  124. for (i = 0; i < 10; i++)
  125. usleep(1e6 * MEAN_SAMPLE_INTERVAL / 10);
  126. reset_ntp_error();
  127. set_frequency(freq_base + freq_step);
  128. for (i = 0; i < 10; i++)
  129. usleep(rand() % 2000000 * STEP_INTERVAL / 10);
  130. set_frequency(freq_base);
  131. for (i = 0; i < SAMPLES; i++) {
  132. usleep(rand() % 2000000 * MEAN_SAMPLE_INTERVAL);
  133. get_sample(&samples[i]);
  134. }
  135. if (calibration) {
  136. regress(samples, SAMPLES, &intercept, &slope, &stddev1, &max1);
  137. mono_freq_offset = slope;
  138. printf("CLOCK_MONOTONIC_RAW frequency offset: %11.3f ppm\n",
  139. 1e6 * mono_freq_offset);
  140. return 0;
  141. }
  142. regress(samples, SAMPLES / 2, &intercept, &slope, &stddev1, &max1);
  143. freq_error1 = slope * (1.0 - mono_freq_offset) - mono_freq_offset -
  144. freq_base;
  145. regress(samples + SAMPLES / 2, SAMPLES / 2, &intercept, &slope,
  146. &stddev2, &max2);
  147. freq_error2 = slope * (1.0 - mono_freq_offset) - mono_freq_offset -
  148. freq_base;
  149. printf("%6.0f %+10.3f %6.0f %7.0f %+10.3f %6.0f %7.0f\t",
  150. 1e6 * freq_step,
  151. 1e6 * freq_error1, 1e9 * stddev1, 1e9 * max1,
  152. 1e6 * freq_error2, 1e9 * stddev2, 1e9 * max2);
  153. if (fabs(freq_error2) > MAX_FREQ_ERROR || stddev2 > MAX_STDDEV) {
  154. printf("[FAIL]\n");
  155. return 1;
  156. }
  157. printf("[OK]\n");
  158. return 0;
  159. }
  160. static void init_test(void)
  161. {
  162. struct timespec ts;
  163. struct sample sample;
  164. if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts)) {
  165. perror("[FAIL] clock_gettime(CLOCK_MONOTONIC_RAW)");
  166. ksft_exit_fail();
  167. }
  168. mono_raw_base = ts.tv_sec;
  169. if (clock_gettime(CLOCK_MONOTONIC, &ts)) {
  170. perror("[FAIL] clock_gettime(CLOCK_MONOTONIC)");
  171. ksft_exit_fail();
  172. }
  173. mono_base = ts.tv_sec;
  174. user_hz = sysconf(_SC_CLK_TCK);
  175. precision = get_sample(&sample) / 2.0;
  176. printf("CLOCK_MONOTONIC_RAW+CLOCK_MONOTONIC precision: %.0f ns\t\t",
  177. 1e9 * precision);
  178. if (precision > MAX_PRECISION)
  179. ksft_exit_skip("precision: %.0f ns > MAX_PRECISION: %.0f ns\n",
  180. 1e9 * precision, 1e9 * MAX_PRECISION);
  181. printf("[OK]\n");
  182. srand(ts.tv_sec ^ ts.tv_nsec);
  183. run_test(1, 0.0, 0.0);
  184. }
  185. int main(int argc, char **argv)
  186. {
  187. double freq_base, freq_step;
  188. int i, j, fails = 0;
  189. init_test();
  190. printf("Checking response to frequency step:\n");
  191. printf(" Step 1st interval 2nd interval\n");
  192. printf(" Freq Dev Max Freq Dev Max\n");
  193. for (i = 2; i >= 0; i--) {
  194. for (j = 0; j < 5; j++) {
  195. freq_base = (rand() % (1 << 24) - (1 << 23)) / 65536e6;
  196. freq_step = 10e-6 * (1 << (6 * i));
  197. fails += run_test(0, freq_base, freq_step);
  198. }
  199. }
  200. set_frequency(0.0);
  201. if (fails)
  202. return ksft_exit_fail();
  203. return ksft_exit_pass();
  204. }