diff options
author | Manuel Bentele | 2020-10-23 15:18:01 +0200 |
---|---|---|
committer | Manuel Bentele | 2020-10-23 15:18:01 +0200 |
commit | dbb41ce2b7f309d394054a6bd1e33afd578798a5 (patch) | |
tree | 6a31092063d9f2fb5ac5720ec6759040e793c3d5 /kernel/tests/lib/tst_timer_test.c | |
parent | Set Linux kernel version to unknown if it is not detectable (diff) | |
download | xloop-dbb41ce2b7f309d394054a6bd1e33afd578798a5.tar.gz xloop-dbb41ce2b7f309d394054a6bd1e33afd578798a5.tar.xz xloop-dbb41ce2b7f309d394054a6bd1e33afd578798a5.zip |
Move the source code of all xloop components to the common 'src' directory
Diffstat (limited to 'kernel/tests/lib/tst_timer_test.c')
-rw-r--r-- | kernel/tests/lib/tst_timer_test.c | 472 |
1 files changed, 0 insertions, 472 deletions
diff --git a/kernel/tests/lib/tst_timer_test.c b/kernel/tests/lib/tst_timer_test.c deleted file mode 100644 index 196c512..0000000 --- a/kernel/tests/lib/tst_timer_test.c +++ /dev/null @@ -1,472 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (c) 2017 Cyril Hrubis <chrubis@suse.cz> - */ - -#include <sys/prctl.h> -#include <stdlib.h> -#include <stdio.h> -#include <limits.h> -#include <string.h> - -#define TST_NO_DEFAULT_MAIN -#include "tst_test.h" -#include "tst_clocks.h" -#include "tst_timer_test.h" - -#define MAX_SAMPLES 500 - -static const char *scall; -static void (*setup)(void); -static void (*cleanup)(void); -static int (*sample)(int clk_id, long long usec); -static struct tst_test *test; - -static long long *samples; -static unsigned int cur_sample; -static unsigned int monotonic_resolution; -static unsigned int timerslack; -static int virt_env; - -static char *print_frequency_plot; -static char *file_name; -static char *str_sleep_time; -static char *str_sample_cnt; -static int sleep_time = -1; -static int sample_cnt; - -static void print_line(char c, int len) -{ - while (len-- > 0) - fputc(c, stderr); -} - -static unsigned int ceilu(float f) -{ - if (f - (int)f > 0) - return (unsigned int)f + 1; - - return (unsigned int)f; -} - -static unsigned int flooru(float f) -{ - return (unsigned int)f; -} - -static float bucket_len(unsigned int bucket, unsigned int max_bucket, - unsigned int cols) -{ - return 1.00 * bucket * cols / max_bucket; -} - -static const char *table_heading = " Time: us "; - -/* - * Line Header: '10023 | ' - */ -static unsigned int header_len(long long max_sample) -{ - unsigned int l = 1; - - while (max_sample/=10) - l++; - - return MAX(strlen(table_heading) + 2, l + 3); -} - -static void frequency_plot(void) -{ - unsigned int cols = 80; - unsigned int rows = 20; - unsigned int i, buckets[rows]; - long long max_sample = samples[0]; - long long min_sample = samples[cur_sample-1]; - unsigned int line_header_len = header_len(max_sample); - unsigned int plot_line_len = cols - line_header_len; - unsigned int bucket_size; - - memset(buckets, 0, sizeof(buckets)); - - /* - * We work with discrete data buckets smaller than 1 does not make - * sense as well as it's a good idea to keep buckets integer sized - * to avoid scaling artifacts. - */ - bucket_size = MAX(1u, ceilu(1.00 * (max_sample - min_sample)/(rows-1))); - - for (i = 0; i < cur_sample; i++) { - unsigned int bucket; - bucket = flooru(1.00 * (samples[i] - min_sample)/bucket_size); - buckets[bucket]++; - } - - unsigned int max_bucket = buckets[0]; - for (i = 1; i < rows; i++) - max_bucket = MAX(max_bucket, buckets[i]); - - fprintf(stderr, "\n%*s| Frequency\n", line_header_len - 2, table_heading); - - print_line('-', cols); - fputc('\n', stderr); - - unsigned int l, r; - - for (l = 0; l < rows; l++) { - if (buckets[l]) - break; - } - - for (r = rows-1; r > l; r--) { - if (buckets[r]) - break; - } - - for (i = l; i <= r; i++) { - float len = bucket_len(buckets[i], max_bucket, plot_line_len); - - fprintf(stderr, "%*lli | ", - line_header_len - 3, min_sample + bucket_size*i); - print_line('*', len); - - if ((len - (int)len) >= 0.5) - fputc('+', stderr); - else if ((len - (int)len) >= 0.25) - fputc('-', stderr); - else if (len < 0.25 && buckets[i]) - fputc('.', stderr); - - fputc('\n', stderr); - } - - print_line('-', cols); - fputc('\n', stderr); - - float scale = 1.00 * plot_line_len / max_bucket; - - fprintf(stderr, - "%*uus | 1 sample = %.5f '*', %.5f '+', %.5f '-', non-zero '.'\n", - line_header_len - 5, bucket_size, scale, scale * 2, scale * 4); - - fputc('\n', stderr); -} - -void tst_timer_sample(void) -{ - samples[cur_sample++] = tst_timer_elapsed_us(); -} - -static int cmp(const void *a, const void *b) -{ - const long long *aa = a, *bb = b; - - return (*bb - *aa); -} - -/* - * The threshold per one syscall is computed as a sum of: - * - * 400 us - accomodates for context switches, process - * migrations between CPUs on SMP, etc. - * 2*monotonic_resolution - accomodates for granurality of the CLOCK_MONOTONIC - * slack_per_scall - max of 0.1% of the sleep capped on 100ms or - * current->timer_slack_ns, which is slack allowed - * in kernel - * - * The formula for slack_per_scall applies to select() and *poll*() syscalls, - * the futex and *nanosleep() use only the timer_slack_ns, so we are a bit - * less strict here that we could be for these two for longer sleep times... - * - * We also allow for outliners, i.e. add some number to the threshold in case - * that the number of iteration is small. For large enoung number of iterations - * outliners are discarded and averaged out. - */ -static long long compute_threshold(long long requested_us, - unsigned int nsamples) -{ - unsigned int slack_per_scall = MIN(100000, requested_us / 1000); - - slack_per_scall = MAX(slack_per_scall, timerslack); - - return (400 + 2 * monotonic_resolution + slack_per_scall) * nsamples - + 3000/nsamples; -} - -/* - * Returns number of samples to discard. - * - * We set it to either at least 1 if number of samples > 1 or 5%. - */ -static unsigned int compute_discard(unsigned int nsamples) -{ - if (nsamples == 1) - return 0; - - return MAX(1u, nsamples / 20); -} - -static void write_to_file(void) -{ - unsigned int i; - FILE *f; - - if (!file_name) - return; - - f = fopen(file_name, "w"); - - if (!f) { - tst_res(TWARN | TERRNO, - "Failed to open '%s'", file_name); - return; - } - - for (i = 0; i < cur_sample; i++) - fprintf(f, "%lli\n", samples[i]); - - if (fclose(f)) { - tst_res(TWARN | TERRNO, - "Failed to close file '%s'", file_name); - } -} - - -/* - * Timer testing function. - * - * What we do here is: - * - * * Take nsamples measurements of the timer function, the function - * to be sampled is defined in the the actual test. - * - * * We sort the array of samples, then: - * - * - look for outliners which are samples where the sleep time has exceeded - * requested sleep time by an order of magnitude and, at the same time, are - * greater than clock resolution multiplied by three. - * - * - check for samples where the call has woken up too early which is a plain - * old bug - * - * - then we compute truncated mean and compare that with the requested sleep - * time increased by a threshold - */ -void do_timer_test(long long usec, unsigned int nsamples) -{ - long long trunc_mean, median; - unsigned int discard = compute_discard(nsamples); - unsigned int keep_samples = nsamples - discard; - long long threshold = compute_threshold(usec, keep_samples); - int i; - int failed = 0; - - tst_res(TINFO, - "%s sleeping for %llius %u iterations, threshold %.2fus", - scall, usec, nsamples, 1.00 * threshold / (keep_samples)); - - cur_sample = 0; - for (i = 0; i < (int)nsamples; i++) { - if (sample(CLOCK_MONOTONIC, usec)) { - tst_res(TINFO, "sampling function failed, exitting"); - return; - } - } - - qsort(samples, nsamples, sizeof(samples[0]), cmp); - - write_to_file(); - - for (i = 0; samples[i] > 10 * usec && i < (int)nsamples; i++) { - if (samples[i] <= 3 * monotonic_resolution) - break; - } - - if (i > 0) { - tst_res(TINFO, "Found %i outliners in [%lli,%lli] range", - i, samples[0], samples[i-1]); - } - - for (i = nsamples - 1; samples[i] < usec && i > -1; i--); - - if (i < (int)nsamples - 1) { - tst_res(TFAIL, "%s woken up early %u times range: [%lli,%lli]", - scall, nsamples - 1 - i, - samples[i+1], samples[nsamples-1]); - failed = 1; - } - - median = samples[nsamples/2]; - - trunc_mean = 0; - - for (i = discard; i < (int)nsamples; i++) - trunc_mean += samples[i]; - - tst_res(TINFO, - "min %llius, max %llius, median %llius, trunc mean %.2fus (discarded %u)", - samples[nsamples-1], samples[0], median, - 1.00 * trunc_mean / keep_samples, discard); - - if (virt_env) { - tst_res(TINFO, - "Virtualisation detected, skipping oversleep checks"); - } else if (trunc_mean > (nsamples - discard) * usec + threshold) { - tst_res(TFAIL, "%s slept for too long", scall); - - if (!print_frequency_plot) - frequency_plot(); - - failed = 1; - } - - if (print_frequency_plot) - frequency_plot(); - - if (!failed) - tst_res(TPASS, "Measured times are within thresholds"); -} - -static void parse_timer_opts(void); - -static int set_latency(void) -{ - int fd, latency = 0; - - fd = open("/dev/cpu_dma_latency", O_WRONLY); - if (fd < 0) - return fd; - - return write(fd, &latency, sizeof(latency)); -} - -static void timer_setup(void) -{ - struct timespec t; - int ret; - - if (setup) - setup(); - - /* - * Running tests in VM may cause timing issues, disable upper bound - * checks if any hypervisor is detected. - */ - virt_env = tst_is_virt(VIRT_ANY); - tst_clock_getres(CLOCK_MONOTONIC, &t); - - tst_res(TINFO, "CLOCK_MONOTONIC resolution %lins", (long)t.tv_nsec); - - monotonic_resolution = t.tv_nsec / 1000; - timerslack = 50; - -#ifdef PR_GET_TIMERSLACK - ret = prctl(PR_GET_TIMERSLACK); - if (ret < 0) { - tst_res(TINFO, "prctl(PR_GET_TIMERSLACK) = -1, using %uus", - timerslack); - } else { - timerslack = ret / 1000; - tst_res(TINFO, "prctl(PR_GET_TIMERSLACK) = %ius", timerslack); - } -#else - tst_res(TINFO, "PR_GET_TIMERSLACK not defined, using %uus", - timerslack); -#endif /* PR_GET_TIMERSLACK */ - parse_timer_opts(); - - samples = SAFE_MALLOC(sizeof(long long) * MAX(MAX_SAMPLES, sample_cnt)); - if (set_latency() < 0) - tst_res(TINFO, "Failed to set zero latency constraint: %m"); -} - -static void timer_cleanup(void) -{ - free(samples); - - if (cleanup) - cleanup(); -} - -static struct tst_timer_tcase { - long long usec; - unsigned int samples; -} tcases[] = { - {1000, 500}, - {2000, 500}, - {5000, 300}, - {10000, 100}, - {25000, 50}, - {100000, 10}, - {1000000, 2}, -}; - -static void timer_test_fn(unsigned int n) -{ - do_timer_test(tcases[n].usec, tcases[n].samples); -} - -static void single_timer_test(void) -{ - do_timer_test(sleep_time, sample_cnt); -} - -static struct tst_option options[] = { - {"p", &print_frequency_plot, "-p Print frequency plot"}, - {"s:", &str_sleep_time, "-s us Sleep time"}, - {"n:", &str_sample_cnt, "-n uint Number of samples to take"}, - {"f:", &file_name, "-f fname Write measured samples into a file"}, - {NULL, NULL, NULL} -}; - -static void parse_timer_opts(void) -{ - if (str_sleep_time) { - if (tst_parse_int(str_sleep_time, &sleep_time, 0, INT_MAX)) { - tst_brk(TBROK, - "Invalid sleep time '%s'", str_sleep_time); - } - } - - if (str_sample_cnt) { - if (tst_parse_int(str_sample_cnt, &sample_cnt, 1, INT_MAX)) { - tst_brk(TBROK, - "Invalid sample count '%s'", str_sample_cnt); - } - } - - if (str_sleep_time || str_sample_cnt) { - if (sleep_time < 0) - sleep_time = 10000; - - if (!sample_cnt) - sample_cnt = 500; - - long long timeout = sleep_time * sample_cnt / 1000000; - - tst_set_timeout(timeout + timeout/10); - - test->test_all = single_timer_test; - test->test = NULL; - test->tcnt = 0; - } -} - -struct tst_test *tst_timer_test_setup(struct tst_test *timer_test) -{ - setup = timer_test->setup; - cleanup = timer_test->cleanup; - scall = timer_test->scall; - sample = timer_test->sample; - - timer_test->scall = NULL; - timer_test->setup = timer_setup; - timer_test->cleanup = timer_cleanup; - timer_test->test = timer_test_fn; - timer_test->tcnt = ARRAY_SIZE(tcases); - timer_test->sample = NULL; - timer_test->options = options; - - test = timer_test; - - return timer_test; -} |