diff options
author | Daniel Thompson <daniel.thompson@linaro.org> | 2014-06-11 14:08:27 +0100 |
---|---|---|
committer | Daniel Thompson <daniel.thompson@linaro.org> | 2015-01-05 10:19:10 +0000 |
commit | 58c643f7add1cedf0821ee144d45f9f7f9fe97f1 (patch) | |
tree | 89bea79ae58355f1199e2f2c6ea705803b73656c /drivers/misc/lockup.c | |
parent | 70691b13d2b7876ab50f68ccba1c604d9b181fb2 (diff) |
misc: Add a lockup driver to get the CPUs wedged
Signed-off-by: Daniel Thompson <daniel.thompson@linaro.org>
Diffstat (limited to 'drivers/misc/lockup.c')
-rw-r--r-- | drivers/misc/lockup.c | 102 |
1 files changed, 102 insertions, 0 deletions
diff --git a/drivers/misc/lockup.c b/drivers/misc/lockup.c new file mode 100644 index 000000000000..674a1a228a8a --- /dev/null +++ b/drivers/misc/lockup.c @@ -0,0 +1,102 @@ +/* + * lockup.c + * + * Deliberately do "stupid" things to see if we can debug them + * properly. + * + * The sole purpose of this module is to help with debugging of system + * debug tools. + * + * Copyright (C) 2014 Linaro Limited + * Daniel Thompson <daniel.thompson@linaro.org> + */ + +#include <linux/module.h> +#include <linux/debugfs.h> +#include <linux/debug_locks.h> + +static DEFINE_SPINLOCK(lockup_lock); + +static int lockup_do_action_on_cpu(int cpu, long (*action)(void *)) +{ + if (cpu == -1) + (void) action(NULL); + + if (cpu < 0 || cpu > num_possible_cpus()) + return -EINVAL; + + /* work_on_cpu() ends up performing an uninterruptible + * wait-for-completion. This means we'll lose the prompt + * regardless of the CPU we send the work to. + */ + work_on_cpu(cpu, action, NULL); + + return 0; +} + +#define DEFINE_LOCKUP_ATTRIBUTE(__fops, __action) \ +static int __fops ## _set(void *data, u64 val) \ +{ \ + lockup_do_action_on_cpu(val, __action); \ + return 0; \ +} \ +DEFINE_SIMPLE_ATTRIBUTE(__fops, NULL, __fops ## _set, "%llu\n") + + +static long do_lockup_spin_lock(void *info) +{ + pr_warn("lockup[%u]: About to wedge in spin_lock\n", get_cpu()); + spin_lock(&lockup_lock); + debug_locks_off(); + spin_lock(&lockup_lock); + spin_unlock(&lockup_lock); + spin_unlock(&lockup_lock); + return 0; +} +DEFINE_LOCKUP_ATTRIBUTE(lockup_spin_lock_fops, do_lockup_spin_lock); + +static long do_lockup_spin_lock_irqsave(void *info) +{ + unsigned long flags1, flags2; + + pr_warn("lockup[%u]: About to wedge in spin_lock_irqsave\n", get_cpu()); + spin_lock_irqsave(&lockup_lock, flags1); + debug_locks_off(); + spin_lock_irqsave(&lockup_lock, flags2); + spin_unlock_irqrestore(&lockup_lock, flags2); + spin_unlock_irqrestore(&lockup_lock, flags1); + return 0; +} +DEFINE_LOCKUP_ATTRIBUTE(lockup_spin_lock_irqsave_fops, + do_lockup_spin_lock_irqsave); + +static int __init lockup_init(void) +{ +#define E(x) { #x, &lockup_##x##_fops } + const struct { + const char *name; + const struct file_operations *fops; + } fops_table[] = { + E(spin_lock), + E(spin_lock_irqsave) + }; +#undef E + + struct dentry *dir; + unsigned int i; + + dir = debugfs_create_dir("lockup", NULL); + if (dir) + for (i = 0; i < ARRAY_SIZE(fops_table); i++) + (void)debugfs_create_file(fops_table[i].name, + S_IRUGO | S_IWUSR, dir, NULL, + fops_table[i].fops); + + pr_info("lockup: create 'lockup' attribute\n"); + return 0; +} + +module_init(lockup_init); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Daniel Thompson"); |