linux/kernel/time/test_udelay.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * udelay() test kernel module
   4 *
   5 * Test is executed by writing and reading to /sys/kernel/debug/udelay_test
   6 * Tests are configured by writing: USECS ITERATIONS
   7 * Tests are executed by reading from the same file.
   8 * Specifying usecs of 0 or negative values will run multiples tests.
   9 *
  10 * Copyright (C) 2014 Google, Inc.
  11 */
  12
  13#include <linux/debugfs.h>
  14#include <linux/delay.h>
  15#include <linux/ktime.h>
  16#include <linux/module.h>
  17#include <linux/uaccess.h>
  18
  19#define DEFAULT_ITERATIONS 100
  20
  21#define DEBUGFS_FILENAME "udelay_test"
  22
  23static DEFINE_MUTEX(udelay_test_lock);
  24static int udelay_test_usecs;
  25static int udelay_test_iterations = DEFAULT_ITERATIONS;
  26
  27static int udelay_test_single(struct seq_file *s, int usecs, uint32_t iters)
  28{
  29        int min = 0, max = 0, fail_count = 0;
  30        uint64_t sum = 0;
  31        uint64_t avg;
  32        int i;
  33        /* Allow udelay to be up to 0.5% fast */
  34        int allowed_error_ns = usecs * 5;
  35
  36        for (i = 0; i < iters; ++i) {
  37                s64 kt1, kt2;
  38                int time_passed;
  39
  40                kt1 = ktime_get_ns();
  41                udelay(usecs);
  42                kt2 = ktime_get_ns();
  43                time_passed = kt2 - kt1;
  44
  45                if (i == 0 || time_passed < min)
  46                        min = time_passed;
  47                if (i == 0 || time_passed > max)
  48                        max = time_passed;
  49                if ((time_passed + allowed_error_ns) / 1000 < usecs)
  50                        ++fail_count;
  51                WARN_ON(time_passed < 0);
  52                sum += time_passed;
  53        }
  54
  55        avg = sum;
  56        do_div(avg, iters);
  57        seq_printf(s, "%d usecs x %d: exp=%d allowed=%d min=%d avg=%lld max=%d",
  58                        usecs, iters, usecs * 1000,
  59                        (usecs * 1000) - allowed_error_ns, min, avg, max);
  60        if (fail_count)
  61                seq_printf(s, " FAIL=%d", fail_count);
  62        seq_puts(s, "\n");
  63
  64        return 0;
  65}
  66
  67static int udelay_test_show(struct seq_file *s, void *v)
  68{
  69        int usecs;
  70        int iters;
  71        int ret = 0;
  72
  73        mutex_lock(&udelay_test_lock);
  74        usecs = udelay_test_usecs;
  75        iters = udelay_test_iterations;
  76        mutex_unlock(&udelay_test_lock);
  77
  78        if (usecs > 0 && iters > 0) {
  79                return udelay_test_single(s, usecs, iters);
  80        } else if (usecs == 0) {
  81                struct timespec64 ts;
  82
  83                ktime_get_ts64(&ts);
  84                seq_printf(s, "udelay() test (lpj=%ld kt=%lld.%09ld)\n",
  85                                loops_per_jiffy, (s64)ts.tv_sec, ts.tv_nsec);
  86                seq_puts(s, "usage:\n");
  87                seq_puts(s, "echo USECS [ITERS] > " DEBUGFS_FILENAME "\n");
  88                seq_puts(s, "cat " DEBUGFS_FILENAME "\n");
  89        }
  90
  91        return ret;
  92}
  93
  94static int udelay_test_open(struct inode *inode, struct file *file)
  95{
  96        return single_open(file, udelay_test_show, inode->i_private);
  97}
  98
  99static ssize_t udelay_test_write(struct file *file, const char __user *buf,
 100                size_t count, loff_t *pos)
 101{
 102        char lbuf[32];
 103        int ret;
 104        int usecs;
 105        int iters;
 106
 107        if (count >= sizeof(lbuf))
 108                return -EINVAL;
 109
 110        if (copy_from_user(lbuf, buf, count))
 111                return -EFAULT;
 112        lbuf[count] = '\0';
 113
 114        ret = sscanf(lbuf, "%d %d", &usecs, &iters);
 115        if (ret < 1)
 116                return -EINVAL;
 117        else if (ret < 2)
 118                iters = DEFAULT_ITERATIONS;
 119
 120        mutex_lock(&udelay_test_lock);
 121        udelay_test_usecs = usecs;
 122        udelay_test_iterations = iters;
 123        mutex_unlock(&udelay_test_lock);
 124
 125        return count;
 126}
 127
 128static const struct file_operations udelay_test_debugfs_ops = {
 129        .owner = THIS_MODULE,
 130        .open = udelay_test_open,
 131        .read = seq_read,
 132        .write = udelay_test_write,
 133        .llseek = seq_lseek,
 134        .release = single_release,
 135};
 136
 137static int __init udelay_test_init(void)
 138{
 139        mutex_lock(&udelay_test_lock);
 140        debugfs_create_file(DEBUGFS_FILENAME, S_IRUSR, NULL, NULL,
 141                            &udelay_test_debugfs_ops);
 142        mutex_unlock(&udelay_test_lock);
 143
 144        return 0;
 145}
 146
 147module_init(udelay_test_init);
 148
 149static void __exit udelay_test_exit(void)
 150{
 151        mutex_lock(&udelay_test_lock);
 152        debugfs_remove(debugfs_lookup(DEBUGFS_FILENAME, NULL));
 153        mutex_unlock(&udelay_test_lock);
 154}
 155
 156module_exit(udelay_test_exit);
 157
 158MODULE_AUTHOR("David Riley <davidriley@chromium.org>");
 159MODULE_LICENSE("GPL");
 160