From d1579e030ce7a52ecc49e86f437586bac7bec2ce Mon Sep 17 00:00:00 2001 From: Daniel Thompson Date: Mon, 16 Nov 2015 18:53:19 +0000 Subject: misc: debugfs microbenchmark wrapper Signed-off-by: Daniel Thompson --- drivers/misc/Makefile | 1 + drivers/misc/ubench.c | 467 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 468 insertions(+) create mode 100644 drivers/misc/ubench.c diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 1abfb5a3878d..a8df0926f2b6 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -55,6 +55,7 @@ obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o obj-$(CONFIG_CXL_BASE) += cxl/ obj-$(CONFIG_PANEL) += panel.o obj-y += lockup.o +obj-y += ubench.o lkdtm-$(CONFIG_LKDTM) += lkdtm_core.o lkdtm-$(CONFIG_LKDTM) += lkdtm_bugs.o diff --git a/drivers/misc/ubench.c b/drivers/misc/ubench.c new file mode 100644 index 000000000000..2c8b8e9f0eff --- /dev/null +++ b/drivers/misc/ubench.c @@ -0,0 +1,467 @@ +/* + * ubench.c + * + * The sole purpose of this module is to help with debugging of system + * debug tools. + * + * Copyright (C) 2015 Linaro Limited + * Daniel Thompson + */ + +#define pr_fmt(fmt) "ubench[%u]: " fmt, raw_smp_processor_id() + +#include +#include +#include +#include +#include +#include +#include +#include + + +static int ubench_do_action_on_cpu(int cpu, long (*action)(void *)) +{ + if (cpu == -1) + (void) action(NULL); + + if (cpu < 0 || cpu > num_possible_cpus()) + return -EINVAL; + + pr_info("About to run %pf on cpu %d\n", action, cpu); + + /* work_on_cpu() ends up performing an uninterruptible + * wait-for-completion. This means we'll lose the prompt + * whilst things run regardless of the CPU we send the + * work to. + */ + work_on_cpu(cpu, action, NULL); + + return 0; +} + +#define DEFINE_UBENCH_ATTRIBUTE(__fops, __action) \ + static int __fops##_get(void *data, u64 *val) \ + { \ + *val = __action(NULL); \ + return 0; \ + } \ + static int __fops##_set(void *data, u64 val) \ + { \ + ubench_do_action_on_cpu(val, __action); \ + return 0; \ + } \ + DEFINE_SIMPLE_ATTRIBUTE(__fops, __fops##_get, __fops##_set, "%llu\n") + +static long do_ubench_local_irq_disable(void *info) +{ + int i; + unsigned long long t = sched_clock(); + + for (i = 0; i < 1000000; i++) { + local_irq_disable(); + local_irq_enable(); + } + + pr_info("local_irq_disable %llu\n", sched_clock() - t); + return 0; +} + +DEFINE_UBENCH_ATTRIBUTE(ubench_local_irq_disable_fops, + do_ubench_local_irq_disable); + +static long do_ubench_local_irq_save(void *info) +{ + int i; + unsigned long flags; + unsigned long long t = sched_clock(); + + for (i = 0; i < 1000000; i++) { + local_irq_save(flags); + local_irq_restore(flags); + } + + pr_info("local_irq_save %llu\n", sched_clock() - t); + return 0; +} + +DEFINE_UBENCH_ATTRIBUTE(ubench_local_irq_save_fops, do_ubench_local_irq_save); + +static DEFINE_SPINLOCK(ubench_lock); + +static long do_ubench_spin_lock_irq(void *info) +{ + int i; + unsigned long long t = sched_clock(); + + for (i = 0; i < 1000000; i++) { + spin_lock_irq(&ubench_lock); + spin_unlock_irq(&ubench_lock); + } + + pr_info("spin_lock_irq %llu\n", sched_clock() - t); + return 0; +} + +DEFINE_UBENCH_ATTRIBUTE(ubench_spin_lock_irq_fops, do_ubench_spin_lock_irq); + +static long do_ubench_spin_lock_irqsave(void *info) +{ + int i; + unsigned long flags; + unsigned long long t = sched_clock(); + + for (i = 0; i < 1000000; i++) { + spin_lock_irqsave(&ubench_lock, flags); + spin_unlock_irqrestore(&ubench_lock, flags); + } + + pr_info("spin_lock_irqsave %llu\n", sched_clock() - t); + return 0; +} + +DEFINE_UBENCH_ATTRIBUTE(ubench_spin_lock_irqsave_fops, + do_ubench_spin_lock_irqsave); + +static DEFINE_RWLOCK(ubench_rwlock); + +static long do_ubench_read_lock_irq(void *info) +{ + int i; + unsigned long long t = sched_clock(); + + for (i = 0; i < 1000000; i++) { + read_lock_irq(&ubench_rwlock); + read_unlock_irq(&ubench_rwlock); + } + + pr_info("read_lock_irq %llu\n", sched_clock() - t); + return 0; +} + +DEFINE_UBENCH_ATTRIBUTE(ubench_read_lock_irq_fops, do_ubench_read_lock_irq); + +static long do_ubench_read_lock_irqsave(void *info) +{ + int i; + unsigned long flags; + unsigned long long t = sched_clock(); + + for (i = 0; i < 1000000; i++) { + read_lock_irqsave(&ubench_rwlock, flags); + read_unlock_irqrestore(&ubench_rwlock, flags); + } + + pr_info("read_lock_irqsave %llu\n", sched_clock() - t); + return 0; +} + +DEFINE_UBENCH_ATTRIBUTE(ubench_read_lock_irqsave_fops, + do_ubench_read_lock_irqsave); + +static long do_ubench_write_lock_irq(void *info) +{ + int i; + unsigned long long t = sched_clock(); + + for (i = 0; i < 1000000; i++) { + write_lock_irq(&ubench_rwlock); + write_unlock_irq(&ubench_rwlock); + } + + pr_info("write_lock_irq %llu\n", sched_clock() - t); + return 0; +} + +DEFINE_UBENCH_ATTRIBUTE(ubench_write_lock_irq_fops, do_ubench_write_lock_irq); + +static long do_ubench_write_lock_irqsave(void *info) +{ + int i; + unsigned long flags; + unsigned long long t = sched_clock(); + + for (i = 0; i < 1000000; i++) { + write_lock_irqsave(&ubench_rwlock, flags); + write_unlock_irqrestore(&ubench_rwlock, flags); + } + + pr_info("write_lock_irqsave %llu\n", sched_clock() - t); + return 0; +} + +DEFINE_UBENCH_ATTRIBUTE(ubench_write_lock_irqsave_fops, + do_ubench_write_lock_irqsave); + +static long do_ubench_msr_daif(void *info) +{ + int i; + unsigned long long t = sched_clock(); + + for (i = 0; i < 1000000; i++) { + asm volatile( + " msr daifset, #2\n" + " msr daifclr, #2" + : + : + : "r1", "r2", "memory"); + } + + t = sched_clock() - t; + pr_info("msr daifset/clr %llu\n", t); + return t; +} + +DEFINE_UBENCH_ATTRIBUTE(ubench_msr_daif_fops, + do_ubench_msr_daif); + +static long do_ubench_msr_pmr(void *info) +{ + int i; + unsigned long long t = sched_clock(); + + for (i = 0; i < 1000000; i++) { + asm volatile( + " mov x1, #0xef\n" + " msr_s " __stringify(ICC_PMR_EL1) ",x1\n" + " dsb sy\n" + " mov x1, #0xf0\n" + " msr_s " __stringify(ICC_PMR_EL1) ",x1\n" + " dsb sy\n" + : + : + : "r1", "r2", "memory"); + } + + t = sched_clock() - t; + pr_info("msr pmr %llu\n", t); + return t; +} + +DEFINE_UBENCH_ATTRIBUTE(ubench_msr_pmr_fops, + do_ubench_msr_pmr); + +static long do_ubench_msr_pmr_fast(void *info) +{ + int i; + unsigned long long t = sched_clock(); + + for (i = 0; i < 1000000; i++) { + asm volatile( + " mov x1, #0xb0\n" + " msr_s " __stringify(ICC_PMR_EL1) ",x1\n" + " mov x1, #0xf0\n" + " msr_s " __stringify(ICC_PMR_EL1) ",x1\n" + " dsb sy\n" + : + : + : "r1", "r2", "memory"); + } + + t = sched_clock() - t; + pr_info("msr pmr fast %llu\n", t); + return t; +} + +DEFINE_UBENCH_ATTRIBUTE(ubench_msr_pmr_fast_fops, + do_ubench_msr_pmr_fast); + +static long do_ubench_tif_flag(void *info) +{ + int i; + unsigned long long t = sched_clock(); + + for (i = 0; i < 1000000; i++) { + asm volatile( + " mov x2, sp\n" + " and x2, x2, #-16384\n" + "1: ldxr x1, [x2]\n" + " orr x1, x1, #(1<<25)\n" + " stlxr w1, x1, [x2]\n" + " cbnz w1, 1b\n" + " mov x2, sp\n" + " and x2, x2, #-16384\n" + "1: ldxr x1, [x2]\n" + " and x1, x1, #~(1<<25)\n" + " stlxr w1, x1, [x2]\n" + " cbnz w1, 1b\n" + " mrs_s x1, " __stringify(ICC_PMR_EL1) "\n" + " and x1, x1, #(1<<6)\n" + " cbnz x1, 2f\n" + " mov x1, #0xf0\n" + " msr_s " __stringify(ICC_PMR_EL1) ",x1\n" + " dsb sy\n" + "2:" + : + : + : "r1", "r2", "memory"); + } + + t = sched_clock() - t; + pr_info("tif_flag %llu\n", t); + return t; +} + +DEFINE_UBENCH_ATTRIBUTE(ubench_tif_flag_fops, + do_ubench_tif_flag); + +static long do_ubench_tif_direct(void *info) +{ + int i; + unsigned long long t = sched_clock(); + + for (i = 0; i < 1000000; i++) { + asm volatile( + " mov x2, sp\n" + " and x2, x2, #-16384\n" + " add x2, x2, #4\n" + " mov x1, #1\n" + " str x1, [x2]\n" + " mov x2, sp\n" + " and x2, x2, #-16384\n" + " add x2, x2, #4\n" + " mov x1, #0\n" + " str x1, [x2]\n" + " mrs_s x1, " __stringify(ICC_PMR_EL1) "\n" + " and x1, x1, #(1<<6)\n" + " cbnz x1, 1f\n" + " mov x1, #0xf0\n" + " msr_s " __stringify(ICC_PMR_EL1) ",x1\n" + " dsb sy\n" + "1:" + : + : + : "r1", "r2", "memory"); + } + + t = sched_clock() - t; + pr_info("tif direct %llu\n", t); + return t; +} + +DEFINE_UBENCH_ATTRIBUTE(ubench_tif_direct_fops, + do_ubench_tif_direct); +static long do_ubench_nops(void *info) +{ + int i; + unsigned long long t = sched_clock(); + + for (i = 0; i < 1000000; i++) { + asm volatile( + " nop\n" + " nop\n" + " nop\n" + " nop\n" + " nop\n" + " nop\n" + " nop\n" + " nop\n" + " nop\n" + " nop\n" + " nop\n" + " nop\n" + " nop\n" + " nop\n" + " nop\n" + " nop\n" + : + : + : "r1", "r2", "memory"); + } + + t = sched_clock() - t; + pr_info("nops %llu\n", t); + return t; +} + +DEFINE_UBENCH_ATTRIBUTE(ubench_nops_fops, + do_ubench_nops); + +static long do_ubench_empty(void *info) +{ + int i; + unsigned long long t = sched_clock(); + + for (i = 0; i < 1000000; i++) { + asm volatile( + "" + : + : + : "r1", "r2", "memory"); + } + + t = sched_clock() - t; + pr_info("empty %llu\n", t); + return t; +} + +DEFINE_UBENCH_ATTRIBUTE(ubench_empty_fops, + do_ubench_empty); + +static long do_ubench_all(void *info) +{ + //do_ubench_local_irq_disable(NULL); + //do_ubench_local_irq_save(NULL); + //do_ubench_spin_lock_irq(NULL); + //do_ubench_spin_lock_irqsave(NULL); + //do_ubench_read_lock_irq(NULL); + //do_ubench_read_lock_irqsave(NULL); + //do_ubench_write_lock_irq(NULL); + //do_ubench_write_lock_irqsave(NULL); + do_ubench_msr_daif(NULL); + do_ubench_msr_pmr(NULL); + do_ubench_msr_pmr_fast(NULL); + do_ubench_tif_flag(NULL); + do_ubench_tif_direct(NULL); + do_ubench_empty(NULL); + do_ubench_nops(NULL); + + return 0; +} + +DEFINE_UBENCH_ATTRIBUTE(ubench_all_fops, do_ubench_all); + +#define E(x) { #x, &ubench_##x##_fops } +const struct +{ + const char *name; + const struct file_operations *fops; +} ubench_fops_table[] = { + E(local_irq_disable), + E(local_irq_save), + E(spin_lock_irq), + E(spin_lock_irqsave), + E(read_lock_irq), + E(read_lock_irqsave), + E(write_lock_irq), + E(write_lock_irqsave), + E(msr_daif), + E(msr_pmr), + E(msr_pmr_fast), + E(tif_flag), + E(tif_direct), + E(nops), + E(empty), + E(all), +}; +#undef E + +static int __init ubench_init(void) +{ + struct dentry *dir; + unsigned int i; + + dir = debugfs_create_dir("ubench", NULL); + if (dir) + for (i = 0; i < ARRAY_SIZE(ubench_fops_table); i++) + (void)debugfs_create_file(ubench_fops_table[i].name, + S_IRUGO | S_IWUSR, dir, NULL, + ubench_fops_table[i].fops); + + return 0; +} + +module_init(ubench_init); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Daniel Thompson"); -- cgit v1.2.3