From d38ff30d88adbe51d50e0f4e66a819b31bd3b234 Mon Sep 17 00:00:00 2001 From: Werner Almesberger Date: Thu, 29 Jan 2009 14:27:33 +0000 Subject: Quick and dirty interrupt blocking time detector. --- arch/arm/kernel/Makefile | 1 + arch/arm/kernel/iblock.c | 158 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 159 insertions(+) create mode 100644 arch/arm/kernel/iblock.c (limited to 'arch/arm/kernel') diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index 4305345987d..29d70d2ac86 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile @@ -44,5 +44,6 @@ endif head-y := head$(MMUEXT).o obj-$(CONFIG_DEBUG_LL) += debug.o +obj-y += iblock.o extra-y := $(head-y) init_task.o vmlinux.lds diff --git a/arch/arm/kernel/iblock.c b/arch/arm/kernel/iblock.c new file mode 100644 index 00000000000..d869e790ec2 --- /dev/null +++ b/arch/arm/kernel/iblock.c @@ -0,0 +1,158 @@ +/* + * + * /sys/kernel/iblock/ + * All times are in microseconds (us). + * + * - limit + * Interrupt blocking time reporting limit, in microseconds. + * 0 disables reporting. Auto-resets to zero after reporting. + * + * - max + * Maximum blocking time recorded. Reset to zero by writing anything. + * + * - test + * Force a delay with interrupts disabled. + * + */ + + +#include +#include +#include +#include +#include +#include +#include + + +unsigned long s3c2410_gettimeoffset(void); + + +static unsigned long iblock_t0; +static int iblock_limit; +static int iblock_max; + + +void iblock_start(void) +{ + unsigned long flags; + + raw_local_save_flags(flags); + if (raw_irqs_disabled_flags(flags)) + return; + iblock_t0 = s3c2410_gettimeoffset(); +} + + +void iblock_end(void) +{ + unsigned long flags; + unsigned long t, us; + + raw_local_save_flags(flags); + if (!raw_irqs_disabled_flags(flags)) + return; + if (!iblock_t0) + return; + t = s3c2410_gettimeoffset(); + us = t-iblock_t0; + if (us > 40000000) + return; + if (us > iblock_max) + iblock_max = us; + if (!iblock_limit) + return; + if (us < iblock_limit) + return; + iblock_limit = 0; + printk(KERN_ERR "interrupts were disabled for %lu us !\n", us); + WARN_ON(1); +} + + +void iblock_end_maybe(unsigned long flags) +{ + if (raw_irqs_disabled_flags(flags)) + return; + iblock_end(); +} + + +static ssize_t limit_read(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%u us\n", iblock_limit); +} + + +static ssize_t limit_write(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long tmp; + char *end; + + tmp = simple_strtoul(buf, &end, 0); + if (end == buf) + return -EINVAL; + iblock_limit = tmp; + return count; +} + + +static ssize_t max_read(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%u us\n", iblock_max); +} + + +static ssize_t max_write(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + iblock_max = 0; + return count; +} + + +static ssize_t test_write(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned long tmp, flags; + char *end; + + tmp = simple_strtoul(buf, &end, 0); + if (end == buf) + return -EINVAL; + local_irq_save(flags); + udelay(tmp); + local_irq_restore(flags); + return count; +} + + +static DEVICE_ATTR(limit, 0644, limit_read, limit_write); +static DEVICE_ATTR(max, 0644, max_read, max_write); +static DEVICE_ATTR(test, 0200, NULL, test_write); + + +static struct attribute *sysfs_entries[] = { + &dev_attr_limit.attr, + &dev_attr_max.attr, + &dev_attr_test.attr, + NULL +}; + + +static struct attribute_group attr_group = { + .name = "iblock", + .attrs = sysfs_entries, +}; + + +static int __devinit iblock_init(void) +{ + return sysfs_create_group(kernel_kobj, &attr_group); +} + + +module_init(iblock_init); -- cgit v1.2.3