Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * udelay() test kernel module
0004  *
0005  * Test is executed by writing and reading to /sys/kernel/debug/udelay_test
0006  * Tests are configured by writing: USECS ITERATIONS
0007  * Tests are executed by reading from the same file.
0008  * Specifying usecs of 0 or negative values will run multiples tests.
0009  *
0010  * Copyright (C) 2014 Google, Inc.
0011  */
0012 
0013 #include <linux/debugfs.h>
0014 #include <linux/delay.h>
0015 #include <linux/ktime.h>
0016 #include <linux/module.h>
0017 #include <linux/uaccess.h>
0018 
0019 #define DEFAULT_ITERATIONS 100
0020 
0021 #define DEBUGFS_FILENAME "udelay_test"
0022 
0023 static DEFINE_MUTEX(udelay_test_lock);
0024 static int udelay_test_usecs;
0025 static int udelay_test_iterations = DEFAULT_ITERATIONS;
0026 
0027 static int udelay_test_single(struct seq_file *s, int usecs, uint32_t iters)
0028 {
0029     int min = 0, max = 0, fail_count = 0;
0030     uint64_t sum = 0;
0031     uint64_t avg;
0032     int i;
0033     /* Allow udelay to be up to 0.5% fast */
0034     int allowed_error_ns = usecs * 5;
0035 
0036     for (i = 0; i < iters; ++i) {
0037         s64 kt1, kt2;
0038         int time_passed;
0039 
0040         kt1 = ktime_get_ns();
0041         udelay(usecs);
0042         kt2 = ktime_get_ns();
0043         time_passed = kt2 - kt1;
0044 
0045         if (i == 0 || time_passed < min)
0046             min = time_passed;
0047         if (i == 0 || time_passed > max)
0048             max = time_passed;
0049         if ((time_passed + allowed_error_ns) / 1000 < usecs)
0050             ++fail_count;
0051         WARN_ON(time_passed < 0);
0052         sum += time_passed;
0053     }
0054 
0055     avg = sum;
0056     do_div(avg, iters);
0057     seq_printf(s, "%d usecs x %d: exp=%d allowed=%d min=%d avg=%lld max=%d",
0058             usecs, iters, usecs * 1000,
0059             (usecs * 1000) - allowed_error_ns, min, avg, max);
0060     if (fail_count)
0061         seq_printf(s, " FAIL=%d", fail_count);
0062     seq_puts(s, "\n");
0063 
0064     return 0;
0065 }
0066 
0067 static int udelay_test_show(struct seq_file *s, void *v)
0068 {
0069     int usecs;
0070     int iters;
0071     int ret = 0;
0072 
0073     mutex_lock(&udelay_test_lock);
0074     usecs = udelay_test_usecs;
0075     iters = udelay_test_iterations;
0076     mutex_unlock(&udelay_test_lock);
0077 
0078     if (usecs > 0 && iters > 0) {
0079         return udelay_test_single(s, usecs, iters);
0080     } else if (usecs == 0) {
0081         struct timespec64 ts;
0082 
0083         ktime_get_ts64(&ts);
0084         seq_printf(s, "udelay() test (lpj=%ld kt=%lld.%09ld)\n",
0085                 loops_per_jiffy, (s64)ts.tv_sec, ts.tv_nsec);
0086         seq_puts(s, "usage:\n");
0087         seq_puts(s, "echo USECS [ITERS] > " DEBUGFS_FILENAME "\n");
0088         seq_puts(s, "cat " DEBUGFS_FILENAME "\n");
0089     }
0090 
0091     return ret;
0092 }
0093 
0094 static int udelay_test_open(struct inode *inode, struct file *file)
0095 {
0096     return single_open(file, udelay_test_show, inode->i_private);
0097 }
0098 
0099 static ssize_t udelay_test_write(struct file *file, const char __user *buf,
0100         size_t count, loff_t *pos)
0101 {
0102     char lbuf[32];
0103     int ret;
0104     int usecs;
0105     int iters;
0106 
0107     if (count >= sizeof(lbuf))
0108         return -EINVAL;
0109 
0110     if (copy_from_user(lbuf, buf, count))
0111         return -EFAULT;
0112     lbuf[count] = '\0';
0113 
0114     ret = sscanf(lbuf, "%d %d", &usecs, &iters);
0115     if (ret < 1)
0116         return -EINVAL;
0117     else if (ret < 2)
0118         iters = DEFAULT_ITERATIONS;
0119 
0120     mutex_lock(&udelay_test_lock);
0121     udelay_test_usecs = usecs;
0122     udelay_test_iterations = iters;
0123     mutex_unlock(&udelay_test_lock);
0124 
0125     return count;
0126 }
0127 
0128 static const struct file_operations udelay_test_debugfs_ops = {
0129     .owner = THIS_MODULE,
0130     .open = udelay_test_open,
0131     .read = seq_read,
0132     .write = udelay_test_write,
0133     .llseek = seq_lseek,
0134     .release = single_release,
0135 };
0136 
0137 static int __init udelay_test_init(void)
0138 {
0139     mutex_lock(&udelay_test_lock);
0140     debugfs_create_file(DEBUGFS_FILENAME, S_IRUSR, NULL, NULL,
0141                 &udelay_test_debugfs_ops);
0142     mutex_unlock(&udelay_test_lock);
0143 
0144     return 0;
0145 }
0146 
0147 module_init(udelay_test_init);
0148 
0149 static void __exit udelay_test_exit(void)
0150 {
0151     mutex_lock(&udelay_test_lock);
0152     debugfs_remove(debugfs_lookup(DEBUGFS_FILENAME, NULL));
0153     mutex_unlock(&udelay_test_lock);
0154 }
0155 
0156 module_exit(udelay_test_exit);
0157 
0158 MODULE_AUTHOR("David Riley <davidriley@chromium.org>");
0159 MODULE_LICENSE("GPL");