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 int udelay_test_usecs; 25 static int udelay_test_iterations = DEFAULT_ITERATIONS; 26 27 static 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 67 static 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 94 static int udelay_test_open(struct inode *inode, struct file *file) 95 { 96 return single_open(file, udelay_test_show, inode->i_private); 97 } 98 99 static 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 128 static 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 137 static 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 147 module_init(udelay_test_init); 148 149 static 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 156 module_exit(udelay_test_exit); 157 158 MODULE_AUTHOR("David Riley <davidriley@chromium.org>"); 159 MODULE_LICENSE("GPL"); 160