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 struct dentry *udelay_test_debugfs_file;
  25static int udelay_test_usecs;
  26static int udelay_test_iterations = DEFAULT_ITERATIONS;
  27
  28static int udelay_test_single(struct seq_file *s, int usecs, uint32_t iters)
  29{
  30        int min = 0, max = 0, fail_count = 0;
  31        uint64_t sum = 0;
  32        uint64_t avg;
  33        int i;
  34        /* Allow udelay to be up to 0.5% fast */
  35        int allowed_error_ns = usecs * 5;
  36
  37        for (i = 0; i < iters; ++i) {
  38                s64 kt1, kt2;
  39                int time_passed;
  40
  41                kt1 = ktime_get_ns();
  42                udelay(usecs);
  43                kt2 = ktime_get_ns();
  44                time_passed = kt2 - kt1;
  45
  46                if (i == 0 || time_passed < min)
  47                        min = time_passed;
  48                if (i == 0 || time_passed > max)
  49                        max = time_passed;
  50                if ((time_passed + allowed_error_ns) / 1000 < usecs)
  51                        ++fail_count;
  52                WARN_ON(time_passed < 0);
  53                sum += time_passed;
  54        }
  55
  56        avg = sum;
  57        do_div(avg, iters);
  58        seq_printf(s, "%d usecs x %d: exp=%d allowed=%d min=%d avg=%lld max=%d",
  59                        usecs, iters, usecs * 1000,
  60                        (usecs * 1000) - allowed_error_ns, min, avg, max);
  61        if (fail_count)
  62                seq_printf(s, " FAIL=%d", fail_count);
  63        seq_puts(s, "\n");
  64
  65        return 0;
  66}
  67
  68static int udelay_test_show(struct seq_file *s, void *v)
  69{
  70        int usecs;
  71        int iters;
  72        int ret = 0;
  73
  74        mutex_lock(&udelay_test_lock);
  75        usecs = udelay_test_usecs;
  76        iters = udelay_test_iterations;
  77        mutex_unlock(&udelay_test_lock);
  78
  79        if (usecs > 0 && iters > 0) {
  80                return udelay_test_single(s, usecs, iters);
  81        } else if (usecs == 0) {
  82                struct timespec64 ts;
  83
  84                ktime_get_ts64(&ts);
  85                seq_printf(s, "udelay() test (lpj=%ld kt=%lld.%09ld)\n",
  86                                loops_per_jiffy, (s64)ts.tv_sec, ts.tv_nsec);
  87                seq_puts(s, "usage:\n");
  88                seq_puts(s, "echo USECS [ITERS] > " DEBUGFS_FILENAME "\n");
  89                seq_puts(s, "cat " DEBUGFS_FILENAME "\n");
  90        }
  91
  92        return ret;
  93}
  94
  95static int udelay_test_open(struct inode *inode, struct file *file)
  96{
  97        return single_open(file, udelay_test_show, inode->i_private);
  98}
  99
 100static ssize_t udelay_test_write(struct file *file, const char __user *buf,
 101                size_t count, loff_t *pos)
 102{
 103        char lbuf[32];
 104        int ret;
 105        int usecs;
 106        int iters;
 107
 108        if (count >= sizeof(lbuf))
 109                return -EINVAL;
 110
 111        if (copy_from_user(lbuf, buf, count))
 112                return -EFAULT;
 113        lbuf[count] = '\0';
 114
 115        ret = sscanf(lbuf, "%d %d", &usecs, &iters);
 116        if (ret < 1)
 117                return -EINVAL;
 118        else if (ret < 2)
 119                iters = DEFAULT_ITERATIONS;
 120
 121        mutex_lock(&udelay_test_lock);
 122        udelay_test_usecs = usecs;
 123        udelay_test_iterations = iters;
 124        mutex_unlock(&udelay_test_lock);
 125
 126        return count;
 127}
 128
 129static const struct file_operations udelay_test_debugfs_ops = {
 130        .owner = THIS_MODULE,
 131        .open = udelay_test_open,
 132        .read = seq_read,
 133        .write = udelay_test_write,
 134        .llseek = seq_lseek,
 135        .release = single_release,
 136};
 137
 138static int __init udelay_test_init(void)
 139{
 140        mutex_lock(&udelay_test_lock);
 141        udelay_test_debugfs_file = debugfs_create_file(DEBUGFS_FILENAME,
 142                        S_IRUSR, NULL, NULL, &udelay_test_debugfs_ops);
 143        mutex_unlock(&udelay_test_lock);
 144
 145        return 0;
 146}
 147
 148module_init(udelay_test_init);
 149
 150static void __exit udelay_test_exit(void)
 151{
 152        mutex_lock(&udelay_test_lock);
 153        debugfs_remove(udelay_test_debugfs_file);
 154        mutex_unlock(&udelay_test_lock);
 155}
 156
 157module_exit(udelay_test_exit);
 158
 159MODULE_AUTHOR("David Riley <davidriley@chromium.org>");
 160MODULE_LICENSE("GPL");
 161