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 23 static DEFINE_MUTEX(udelay_test_lock); 24 static struct dentry *udelay_test_debugfs_file; 25 static int udelay_test_usecs; 26 static int udelay_test_iterations = DEFAULT_ITERATIONS; 27 28 static 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 68 static 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 95 static int udelay_test_open(struct inode *inode, struct file *file) 96 { 97 return single_open(file, udelay_test_show, inode->i_private); 98 } 99 100 static 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 129 static 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 138 static 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 148 module_init(udelay_test_init); 149 150 static 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 157 module_exit(udelay_test_exit); 158 159 MODULE_AUTHOR("David Riley <davidriley@chromium.org>"); 160 MODULE_LICENSE("GPL"); 161