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/include/asm/irqflags.h | 12 ++- arch/arm/kernel/Makefile | 1 + arch/arm/kernel/iblock.c | 158 ++++++++++++++++++++++++++++++++++++++++ arch/arm/plat-s3c/time.c | 2 +- arch/arm/plat-s3c24xx/time.c | 2 +- 5 files changed, 172 insertions(+), 3 deletions(-) create mode 100644 arch/arm/kernel/iblock.c diff --git a/arch/arm/include/asm/irqflags.h b/arch/arm/include/asm/irqflags.h index 6d09974e664..3b01c151507 100644 --- a/arch/arm/include/asm/irqflags.h +++ b/arch/arm/include/asm/irqflags.h @@ -5,6 +5,12 @@ #include + +void iblock_start(void); +void iblock_end(void); +void iblock_end_maybe(unsigned long flags); + + /* * CPU interrupt mask handling. */ @@ -31,6 +37,7 @@ #define raw_local_irq_save(x) \ ({ \ unsigned long temp; \ + iblock_start(); \ (void) (&temp == &x); \ __asm__ __volatile__( \ "mrs %0, cpsr @ local_irq_save\n" \ @@ -47,6 +54,7 @@ #define raw_local_irq_enable() \ ({ \ unsigned long temp; \ + iblock_start(); \ __asm__ __volatile__( \ "mrs %0, cpsr @ local_irq_enable\n" \ " bic %0, %0, #128\n" \ @@ -62,6 +70,7 @@ #define raw_local_irq_disable() \ ({ \ unsigned long temp; \ + iblock_start(); \ __asm__ __volatile__( \ "mrs %0, cpsr @ local_irq_disable\n" \ " orr %0, %0, #128\n" \ @@ -117,11 +126,12 @@ * restore saved IRQ & FIQ state */ #define raw_local_irq_restore(x) \ + ({ iblock_end_maybe(x); \ __asm__ __volatile__( \ "msr cpsr_c, %0 @ local_irq_restore\n" \ : \ : "r" (x) \ - : "memory", "cc") + : "memory", "cc"); }) #define raw_irqs_disabled_flags(flags) \ ({ \ 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); diff --git a/arch/arm/plat-s3c/time.c b/arch/arm/plat-s3c/time.c index 3b27b29da47..3941f75abbd 100644 --- a/arch/arm/plat-s3c/time.c +++ b/arch/arm/plat-s3c/time.c @@ -97,7 +97,7 @@ static inline unsigned long timer_ticks_to_usec(unsigned long ticks) * IRQs are disabled before entering here from do_gettimeofday() */ -static unsigned long s3c2410_gettimeoffset (void) +unsigned long s3c2410_gettimeoffset (void) { unsigned long tdone; unsigned long tval; diff --git a/arch/arm/plat-s3c24xx/time.c b/arch/arm/plat-s3c24xx/time.c index a8e05a83095..713a6bc040f 100644 --- a/arch/arm/plat-s3c24xx/time.c +++ b/arch/arm/plat-s3c24xx/time.c @@ -99,7 +99,7 @@ static inline unsigned long timer_ticks_to_usec(unsigned long ticks) #define SRCPND_TIMER4 (1<<(IRQ_TIMER4 - IRQ_EINT0)) -static unsigned long s3c2410_gettimeoffset (void) +unsigned long s3c2410_gettimeoffset (void) { unsigned long tdone; unsigned long irqpend; -- cgit v1.2.3