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