aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/include/asm/irqflags.h12
-rw-r--r--arch/arm/kernel/Makefile1
-rw-r--r--arch/arm/kernel/iblock.c158
-rw-r--r--arch/arm/plat-s3c/time.c2
-rw-r--r--arch/arm/plat-s3c24xx/time.c2
5 files changed, 172 insertions, 3 deletions
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 <asm/ptrace.h>
+
+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 <linux/kernel.h>
+#include <linux/sysfs.h>
+#include <linux/kobject.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <asm/irqflags.h>
+
+
+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;