aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCraig Gallek <kraig@google.com>2016-09-13 12:14:51 -0400
committerThomas Gleixner <tglx@linutronix.de>2016-09-14 09:28:15 -0400
commitecb3f394c5dba897d215a5422f1b363e93e2ce4e (patch)
treee6807b6c7eddf7b7579e01fb0edcd0c0a87c1dca
parent00b992deaa08495ab958da5950c9ebbba27d0ddc (diff)
genirq: Expose interrupt information through sysfs
Information about interrupts is exposed via /proc/interrupts, but the format of that file has changed over kernel versions and differs across architectures. It also has varying column numbers depending on hardware. That all makes it hard for tools to parse. To solve this, expose the information through sysfs so each irq attribute is in a separate file in a consistent, machine parsable way. This feature is only available when both CONFIG_SPARSE_IRQ and CONFIG_SYSFS are enabled. Examples: /sys/kernel/irq/18/actions: i801_smbus,ehci_hcd:usb1,uhci_hcd:usb7 /sys/kernel/irq/18/chip_name: IR-IO-APIC /sys/kernel/irq/18/hwirq: 18 /sys/kernel/irq/18/name: fasteoi /sys/kernel/irq/18/per_cpu_count: 0,0 /sys/kernel/irq/18/type: level /sys/kernel/irq/25/actions: ahci0 /sys/kernel/irq/25/chip_name: IR-PCI-MSI /sys/kernel/irq/25/hwirq: 512000 /sys/kernel/irq/25/name: edge /sys/kernel/irq/25/per_cpu_count: 29036,0 /sys/kernel/irq/25/type: edge [ tglx: Moved kobject_del() under sparse_irq_lock, massaged code comments and changelog ] Signed-off-by: Craig Gallek <kraig@google.com> Cc: David Decotigny <decot@google.com> Link: http://lkml.kernel.org/r/1473783291-122873-1-git-send-email-kraigatgoog@gmail.com Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
-rw-r--r--Documentation/ABI/testing/sysfs-kernel-irq53
-rw-r--r--include/linux/irqdesc.h3
-rw-r--r--kernel/irq/irqdesc.c193
3 files changed, 247 insertions, 2 deletions
diff --git a/Documentation/ABI/testing/sysfs-kernel-irq b/Documentation/ABI/testing/sysfs-kernel-irq
new file mode 100644
index 000000000000..eb074b100986
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-kernel-irq
@@ -0,0 +1,53 @@
1What: /sys/kernel/irq
2Date: September 2016
3KernelVersion: 4.9
4Contact: Craig Gallek <kraig@google.com>
5Description: Directory containing information about the system's IRQs.
6 Specifically, data from the associated struct irq_desc.
7 The information here is similar to that in /proc/interrupts
8 but in a more machine-friendly format. This directory contains
9 one subdirectory for each Linux IRQ number.
10
11What: /sys/kernel/irq/<irq>/actions
12Date: September 2016
13KernelVersion: 4.9
14Contact: Craig Gallek <kraig@google.com>
15Description: The IRQ action chain. A comma-separated list of zero or more
16 device names associated with this interrupt.
17
18What: /sys/kernel/irq/<irq>/chip_name
19Date: September 2016
20KernelVersion: 4.9
21Contact: Craig Gallek <kraig@google.com>
22Description: Human-readable chip name supplied by the associated device
23 driver.
24
25What: /sys/kernel/irq/<irq>/hwirq
26Date: September 2016
27KernelVersion: 4.9
28Contact: Craig Gallek <kraig@google.com>
29Description: When interrupt translation domains are used, this file contains
30 the underlying hardware IRQ number used for this Linux IRQ.
31
32What: /sys/kernel/irq/<irq>/name
33Date: September 2016
34KernelVersion: 4.9
35Contact: Craig Gallek <kraig@google.com>
36Description: Human-readable flow handler name as defined by the irq chip
37 driver.
38
39What: /sys/kernel/irq/<irq>/per_cpu_count
40Date: September 2016
41KernelVersion: 4.9
42Contact: Craig Gallek <kraig@google.com>
43Description: The number of times the interrupt has fired since boot. This
44 is a comma-separated list of counters; one per CPU in CPU id
45 order. NOTE: This file consistently shows counters for all
46 CPU ids. This differs from the behavior of /proc/interrupts
47 which only shows counters for online CPUs.
48
49What: /sys/kernel/irq/<irq>/type
50Date: September 2016
51KernelVersion: 4.9
52Contact: Craig Gallek <kraig@google.com>
53Description: The type of the interrupt. Either the string 'level' or 'edge'.
diff --git a/include/linux/irqdesc.h b/include/linux/irqdesc.h
index b51beebf9804..c9be57931b58 100644
--- a/include/linux/irqdesc.h
+++ b/include/linux/irqdesc.h
@@ -2,6 +2,7 @@
2#define _LINUX_IRQDESC_H 2#define _LINUX_IRQDESC_H
3 3
4#include <linux/rcupdate.h> 4#include <linux/rcupdate.h>
5#include <linux/kobject.h>
5 6
6/* 7/*
7 * Core internal functions to deal with irq descriptors 8 * Core internal functions to deal with irq descriptors
@@ -43,6 +44,7 @@ struct pt_regs;
43 * @force_resume_depth: number of irqactions on a irq descriptor with 44 * @force_resume_depth: number of irqactions on a irq descriptor with
44 * IRQF_FORCE_RESUME set 45 * IRQF_FORCE_RESUME set
45 * @rcu: rcu head for delayed free 46 * @rcu: rcu head for delayed free
47 * @kobj: kobject used to represent this struct in sysfs
46 * @dir: /proc/irq/ procfs entry 48 * @dir: /proc/irq/ procfs entry
47 * @name: flow handler name for /proc/interrupts output 49 * @name: flow handler name for /proc/interrupts output
48 */ 50 */
@@ -88,6 +90,7 @@ struct irq_desc {
88#endif 90#endif
89#ifdef CONFIG_SPARSE_IRQ 91#ifdef CONFIG_SPARSE_IRQ
90 struct rcu_head rcu; 92 struct rcu_head rcu;
93 struct kobject kobj;
91#endif 94#endif
92 int parent_irq; 95 int parent_irq;
93 struct module *owner; 96 struct module *owner;
diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c
index a623b44f2d4b..93b51727abaa 100644
--- a/kernel/irq/irqdesc.c
+++ b/kernel/irq/irqdesc.c
@@ -15,6 +15,7 @@
15#include <linux/radix-tree.h> 15#include <linux/radix-tree.h>
16#include <linux/bitmap.h> 16#include <linux/bitmap.h>
17#include <linux/irqdomain.h> 17#include <linux/irqdomain.h>
18#include <linux/sysfs.h>
18 19
19#include "internals.h" 20#include "internals.h"
20 21
@@ -123,6 +124,181 @@ static DECLARE_BITMAP(allocated_irqs, IRQ_BITMAP_BITS);
123 124
124#ifdef CONFIG_SPARSE_IRQ 125#ifdef CONFIG_SPARSE_IRQ
125 126
127static void irq_kobj_release(struct kobject *kobj);
128
129#ifdef CONFIG_SYSFS
130static struct kobject *irq_kobj_base;
131
132#define IRQ_ATTR_RO(_name) \
133static struct kobj_attribute _name##_attr = __ATTR_RO(_name)
134
135static ssize_t per_cpu_count_show(struct kobject *kobj,
136 struct kobj_attribute *attr, char *buf)
137{
138 struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
139 int cpu, irq = desc->irq_data.irq;
140 ssize_t ret = 0;
141 char *p = "";
142
143 for_each_possible_cpu(cpu) {
144 unsigned int c = kstat_irqs_cpu(irq, cpu);
145
146 ret += scnprintf(buf + ret, PAGE_SIZE - ret, "%s%u", p, c);
147 p = ",";
148 }
149
150 ret += scnprintf(buf + ret, PAGE_SIZE - ret, "\n");
151 return ret;
152}
153IRQ_ATTR_RO(per_cpu_count);
154
155static ssize_t chip_name_show(struct kobject *kobj,
156 struct kobj_attribute *attr, char *buf)
157{
158 struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
159 ssize_t ret = 0;
160
161 raw_spin_lock_irq(&desc->lock);
162 if (desc->irq_data.chip && desc->irq_data.chip->name) {
163 ret = scnprintf(buf, PAGE_SIZE, "%s\n",
164 desc->irq_data.chip->name);
165 }
166 raw_spin_unlock_irq(&desc->lock);
167
168 return ret;
169}
170IRQ_ATTR_RO(chip_name);
171
172static ssize_t hwirq_show(struct kobject *kobj,
173 struct kobj_attribute *attr, char *buf)
174{
175 struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
176 ssize_t ret = 0;
177
178 raw_spin_lock_irq(&desc->lock);
179 if (desc->irq_data.domain)
180 ret = sprintf(buf, "%d\n", (int)desc->irq_data.hwirq);
181 raw_spin_unlock_irq(&desc->lock);
182
183 return ret;
184}
185IRQ_ATTR_RO(hwirq);
186
187static ssize_t type_show(struct kobject *kobj,
188 struct kobj_attribute *attr, char *buf)
189{
190 struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
191 ssize_t ret = 0;
192
193 raw_spin_lock_irq(&desc->lock);
194 ret = sprintf(buf, "%s\n",
195 irqd_is_level_type(&desc->irq_data) ? "level" : "edge");
196 raw_spin_unlock_irq(&desc->lock);
197
198 return ret;
199
200}
201IRQ_ATTR_RO(type);
202
203static ssize_t name_show(struct kobject *kobj,
204 struct kobj_attribute *attr, char *buf)
205{
206 struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
207 ssize_t ret = 0;
208
209 raw_spin_lock_irq(&desc->lock);
210 if (desc->name)
211 ret = scnprintf(buf, PAGE_SIZE, "%s\n", desc->name);
212 raw_spin_unlock_irq(&desc->lock);
213
214 return ret;
215}
216IRQ_ATTR_RO(name);
217
218static ssize_t actions_show(struct kobject *kobj,
219 struct kobj_attribute *attr, char *buf)
220{
221 struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
222 struct irqaction *action;
223 ssize_t ret = 0;
224 char *p = "";
225
226 raw_spin_lock_irq(&desc->lock);
227 for (action = desc->action; action != NULL; action = action->next) {
228 ret += scnprintf(buf + ret, PAGE_SIZE - ret, "%s%s",
229 p, action->name);
230 p = ",";
231 }
232 raw_spin_unlock_irq(&desc->lock);
233
234 if (ret)
235 ret += scnprintf(buf + ret, PAGE_SIZE - ret, "\n");
236
237 return ret;
238}
239IRQ_ATTR_RO(actions);
240
241static struct attribute *irq_attrs[] = {
242 &per_cpu_count_attr.attr,
243 &chip_name_attr.attr,
244 &hwirq_attr.attr,
245 &type_attr.attr,
246 &name_attr.attr,
247 &actions_attr.attr,
248 NULL
249};
250
251static struct kobj_type irq_kobj_type = {
252 .release = irq_kobj_release,
253 .sysfs_ops = &kobj_sysfs_ops,
254 .default_attrs = irq_attrs,
255};
256
257static void irq_sysfs_add(int irq, struct irq_desc *desc)
258{
259 if (irq_kobj_base) {
260 /*
261 * Continue even in case of failure as this is nothing
262 * crucial.
263 */
264 if (kobject_add(&desc->kobj, irq_kobj_base, "%d", irq))
265 pr_warn("Failed to add kobject for irq %d\n", irq);
266 }
267}
268
269static int __init irq_sysfs_init(void)
270{
271 struct irq_desc *desc;
272 int irq;
273
274 /* Prevent concurrent irq alloc/free */
275 irq_lock_sparse();
276
277 irq_kobj_base = kobject_create_and_add("irq", kernel_kobj);
278 if (!irq_kobj_base) {
279 irq_unlock_sparse();
280 return -ENOMEM;
281 }
282
283 /* Add the already allocated interrupts */
284 for_each_irq_desc(irq, desc)
285 irq_sysfs_add(irq, desc);
286 irq_unlock_sparse();
287
288 return 0;
289}
290postcore_initcall(irq_sysfs_init);
291
292#else /* !CONFIG_SYSFS */
293
294static struct kobj_type irq_kobj_type = {
295 .release = irq_kobj_release,
296};
297
298static void irq_sysfs_add(int irq, struct irq_desc *desc) {}
299
300#endif /* CONFIG_SYSFS */
301
126static RADIX_TREE(irq_desc_tree, GFP_KERNEL); 302static RADIX_TREE(irq_desc_tree, GFP_KERNEL);
127 303
128static void irq_insert_desc(unsigned int irq, struct irq_desc *desc) 304static void irq_insert_desc(unsigned int irq, struct irq_desc *desc)
@@ -187,6 +363,7 @@ static struct irq_desc *alloc_desc(int irq, int node, unsigned int flags,
187 363
188 desc_set_defaults(irq, desc, node, affinity, owner); 364 desc_set_defaults(irq, desc, node, affinity, owner);
189 irqd_set(&desc->irq_data, flags); 365 irqd_set(&desc->irq_data, flags);
366 kobject_init(&desc->kobj, &irq_kobj_type);
190 367
191 return desc; 368 return desc;
192 369
@@ -197,15 +374,22 @@ err_desc:
197 return NULL; 374 return NULL;
198} 375}
199 376
200static void delayed_free_desc(struct rcu_head *rhp) 377static void irq_kobj_release(struct kobject *kobj)
201{ 378{
202 struct irq_desc *desc = container_of(rhp, struct irq_desc, rcu); 379 struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
203 380
204 free_masks(desc); 381 free_masks(desc);
205 free_percpu(desc->kstat_irqs); 382 free_percpu(desc->kstat_irqs);
206 kfree(desc); 383 kfree(desc);
207} 384}
208 385
386static void delayed_free_desc(struct rcu_head *rhp)
387{
388 struct irq_desc *desc = container_of(rhp, struct irq_desc, rcu);
389
390 kobject_put(&desc->kobj);
391}
392
209static void free_desc(unsigned int irq) 393static void free_desc(unsigned int irq)
210{ 394{
211 struct irq_desc *desc = irq_to_desc(irq); 395 struct irq_desc *desc = irq_to_desc(irq);
@@ -217,8 +401,12 @@ static void free_desc(unsigned int irq)
217 * kstat_irq_usr(). Once we deleted the descriptor from the 401 * kstat_irq_usr(). Once we deleted the descriptor from the
218 * sparse tree we can free it. Access in proc will fail to 402 * sparse tree we can free it. Access in proc will fail to
219 * lookup the descriptor. 403 * lookup the descriptor.
404 *
405 * The sysfs entry must be serialized against a concurrent
406 * irq_sysfs_init() as well.
220 */ 407 */
221 mutex_lock(&sparse_irq_lock); 408 mutex_lock(&sparse_irq_lock);
409 kobject_del(&desc->kobj);
222 delete_irq_desc(irq); 410 delete_irq_desc(irq);
223 mutex_unlock(&sparse_irq_lock); 411 mutex_unlock(&sparse_irq_lock);
224 412
@@ -261,6 +449,7 @@ static int alloc_descs(unsigned int start, unsigned int cnt, int node,
261 goto err; 449 goto err;
262 mutex_lock(&sparse_irq_lock); 450 mutex_lock(&sparse_irq_lock);
263 irq_insert_desc(start + i, desc); 451 irq_insert_desc(start + i, desc);
452 irq_sysfs_add(start + i, desc);
264 mutex_unlock(&sparse_irq_lock); 453 mutex_unlock(&sparse_irq_lock);
265 } 454 }
266 return start; 455 return start;