aboutsummaryrefslogtreecommitdiff
path: root/arch/arm/kernel/iblock.c
blob: 48632738b5f055f7f4a56745a963c46aba1a79d6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
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;
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();
}
EXPORT_SYMBOL_GPL(iblock_start);

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);
}
EXPORT_SYMBOL_GPL(iblock_end);

void iblock_end_maybe(unsigned long flags)
{
	if (raw_irqs_disabled_flags(flags))
		return;
	iblock_end();
}
EXPORT_SYMBOL_GPL(iblock_end_maybe);

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);