Skip to content

Commit 087cdfb

Browse files
committed
genirq/debugfs: Add proper debugfs interface
Debugging (hierarchical) interupt domains is tedious as there is no information about the hierarchy and no information about states of interrupts in the various domain levels. Add a debugfs directory 'irq' and subdirectories 'domains' and 'irqs'. The domains directory contains the domain files. The content is information about the domain. If the domain is part of a hierarchy then the parent domains are printed as well. # ls /sys/kernel/debug/irq/domains/ default INTEL-IR-2 INTEL-IR-MSI-2 IO-APIC-IR-2 PCI-MSI DMAR-MSI INTEL-IR-3 INTEL-IR-MSI-3 IO-APIC-IR-3 unknown-1 INTEL-IR-0 INTEL-IR-MSI-0 IO-APIC-IR-0 IO-APIC-IR-4 VECTOR INTEL-IR-1 INTEL-IR-MSI-1 IO-APIC-IR-1 PCI-HT # cat /sys/kernel/debug/irq/domains/VECTOR name: VECTOR size: 0 mapped: 216 flags: 0x00000041 # cat /sys/kernel/debug/irq/domains/IO-APIC-IR-0 name: IO-APIC-IR-0 size: 24 mapped: 19 flags: 0x00000041 parent: INTEL-IR-3 name: INTEL-IR-3 size: 65536 mapped: 167 flags: 0x00000041 parent: VECTOR name: VECTOR size: 0 mapped: 216 flags: 0x00000041 Unfortunately there is no per cpu information about the VECTOR domain (yet). The irqs directory contains detailed information about mapped interrupts. # cat /sys/kernel/debug/irq/irqs/3 handler: handle_edge_irq status: 0x00004000 istate: 0x00000000 ddepth: 1 wdepth: 0 dstate: 0x01018000 IRQD_IRQ_DISABLED IRQD_SINGLE_TARGET IRQD_MOVE_PCNTXT node: 0 affinity: 0-143 effectiv: 0 pending: domain: IO-APIC-IR-0 hwirq: 0x3 chip: IR-IO-APIC flags: 0x10 IRQCHIP_SKIP_SET_WAKE parent: domain: INTEL-IR-3 hwirq: 0x20000 chip: INTEL-IR flags: 0x0 parent: domain: VECTOR hwirq: 0x3 chip: APIC flags: 0x0 This was developed to simplify the debugging of the managed affinity changes. Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Acked-by: Marc Zyngier <marc.zyngier@arm.com> Cc: Jens Axboe <axboe@kernel.dk> Cc: Michael Ellerman <mpe@ellerman.id.au> Cc: Keith Busch <keith.busch@intel.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Christoph Hellwig <hch@lst.de> Link: http://lkml.kernel.org/r/20170619235444.537566163@linutronix.de Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
1 parent 9dc6be3 commit 087cdfb

File tree

9 files changed

+345
-1
lines changed

9 files changed

+345
-1
lines changed

include/linux/irqdesc.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ struct pt_regs;
4646
* @rcu: rcu head for delayed free
4747
* @kobj: kobject used to represent this struct in sysfs
4848
* @dir: /proc/irq/ procfs entry
49+
* @debugfs_file: dentry for the debugfs file
4950
* @name: flow handler name for /proc/interrupts output
5051
*/
5152
struct irq_desc {
@@ -88,6 +89,9 @@ struct irq_desc {
8889
#ifdef CONFIG_PROC_FS
8990
struct proc_dir_entry *dir;
9091
#endif
92+
#ifdef CONFIG_GENERIC_IRQ_DEBUGFS
93+
struct dentry *debugfs_file;
94+
#endif
9195
#ifdef CONFIG_SPARSE_IRQ
9296
struct rcu_head rcu;
9397
struct kobject kobj;

include/linux/irqdomain.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ struct irq_domain_chip_generic;
139139
* setting up one or more generic chips for interrupt controllers
140140
* drivers using the generic chip library which uses this pointer.
141141
* @parent: Pointer to parent irq_domain to support hierarchy irq_domains
142+
* @debugfs_file: dentry for the domain debugfs file
142143
*
143144
* Revmap data, used internally by irq_domain
144145
* @revmap_direct_max_irq: The largest hwirq that can be set for controllers that
@@ -162,6 +163,9 @@ struct irq_domain {
162163
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
163164
struct irq_domain *parent;
164165
#endif
166+
#ifdef CONFIG_GENERIC_IRQ_DEBUGFS
167+
struct dentry *debugfs_file;
168+
#endif
165169

166170
/* reverse map data. The linear map gets appended to the irq_domain */
167171
irq_hw_number_t hwirq_max;

kernel/irq/Kconfig

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,4 +108,15 @@ config SPARSE_IRQ
108108

109109
If you don't know what to do here, say N.
110110

111+
config GENERIC_IRQ_DEBUGFS
112+
bool "Expose irq internals in debugfs"
113+
depends on DEBUG_FS
114+
default n
115+
---help---
116+
117+
Exposes internal state information through debugfs. Mostly for
118+
developers and debugging of hard to diagnose interrupt problems.
119+
120+
If you don't know what to do here, say N.
121+
111122
endmenu

kernel/irq/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ obj-$(CONFIG_PM_SLEEP) += pm.o
1010
obj-$(CONFIG_GENERIC_MSI_IRQ) += msi.o
1111
obj-$(CONFIG_GENERIC_IRQ_IPI) += ipi.o
1212
obj-$(CONFIG_SMP) += affinity.o
13+
obj-$(CONFIG_GENERIC_IRQ_DEBUGFS) += debugfs.o

kernel/irq/debugfs.c

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
/*
2+
* Copyright 2017 Thomas Gleixner <tglx@linutronix.de>
3+
*
4+
* This file is licensed under the GPL V2.
5+
*/
6+
#include <linux/debugfs.h>
7+
#include <linux/irqdomain.h>
8+
#include <linux/irq.h>
9+
10+
#include "internals.h"
11+
12+
static struct dentry *irq_dir;
13+
14+
struct irq_bit_descr {
15+
unsigned int mask;
16+
char *name;
17+
};
18+
#define BIT_MASK_DESCR(m) { .mask = m, .name = #m }
19+
20+
static void irq_debug_show_bits(struct seq_file *m, int ind, unsigned int state,
21+
const struct irq_bit_descr *sd, int size)
22+
{
23+
int i;
24+
25+
for (i = 0; i < size; i++, sd++) {
26+
if (state & sd->mask)
27+
seq_printf(m, "%*s%s\n", ind + 12, "", sd->name);
28+
}
29+
}
30+
31+
#ifdef CONFIG_SMP
32+
static void irq_debug_show_masks(struct seq_file *m, struct irq_desc *desc)
33+
{
34+
struct irq_data *data = irq_desc_get_irq_data(desc);
35+
struct cpumask *msk;
36+
37+
msk = irq_data_get_affinity_mask(data);
38+
seq_printf(m, "affinity: %*pbl\n", cpumask_pr_args(msk));
39+
#ifdef CONFIG_GENERIC_PENDING_IRQ
40+
msk = desc->pending_mask;
41+
seq_printf(m, "pending: %*pbl\n", cpumask_pr_args(msk));
42+
#endif
43+
}
44+
#else
45+
static void irq_debug_show_masks(struct seq_file *m, struct irq_desc *desc) { }
46+
#endif
47+
48+
static const struct irq_bit_descr irqchip_flags[] = {
49+
BIT_MASK_DESCR(IRQCHIP_SET_TYPE_MASKED),
50+
BIT_MASK_DESCR(IRQCHIP_EOI_IF_HANDLED),
51+
BIT_MASK_DESCR(IRQCHIP_MASK_ON_SUSPEND),
52+
BIT_MASK_DESCR(IRQCHIP_ONOFFLINE_ENABLED),
53+
BIT_MASK_DESCR(IRQCHIP_SKIP_SET_WAKE),
54+
BIT_MASK_DESCR(IRQCHIP_ONESHOT_SAFE),
55+
BIT_MASK_DESCR(IRQCHIP_EOI_THREADED),
56+
};
57+
58+
static void
59+
irq_debug_show_chip(struct seq_file *m, struct irq_data *data, int ind)
60+
{
61+
struct irq_chip *chip = data->chip;
62+
63+
if (!chip) {
64+
seq_printf(m, "chip: None\n");
65+
return;
66+
}
67+
seq_printf(m, "%*schip: %s\n", ind, "", chip->name);
68+
seq_printf(m, "%*sflags: 0x%lx\n", ind + 1, "", chip->flags);
69+
irq_debug_show_bits(m, ind, chip->flags, irqchip_flags,
70+
ARRAY_SIZE(irqchip_flags));
71+
}
72+
73+
static void
74+
irq_debug_show_data(struct seq_file *m, struct irq_data *data, int ind)
75+
{
76+
seq_printf(m, "%*sdomain: %s\n", ind, "",
77+
data->domain ? data->domain->name : "");
78+
seq_printf(m, "%*shwirq: 0x%lx\n", ind + 1, "", data->hwirq);
79+
irq_debug_show_chip(m, data, ind + 1);
80+
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
81+
if (!data->parent_data)
82+
return;
83+
seq_printf(m, "%*sparent:\n", ind + 1, "");
84+
irq_debug_show_data(m, data->parent_data, ind + 4);
85+
#endif
86+
}
87+
88+
static const struct irq_bit_descr irqdata_states[] = {
89+
BIT_MASK_DESCR(IRQ_TYPE_EDGE_RISING),
90+
BIT_MASK_DESCR(IRQ_TYPE_EDGE_FALLING),
91+
BIT_MASK_DESCR(IRQ_TYPE_LEVEL_HIGH),
92+
BIT_MASK_DESCR(IRQ_TYPE_LEVEL_LOW),
93+
BIT_MASK_DESCR(IRQD_LEVEL),
94+
95+
BIT_MASK_DESCR(IRQD_ACTIVATED),
96+
BIT_MASK_DESCR(IRQD_IRQ_STARTED),
97+
BIT_MASK_DESCR(IRQD_IRQ_DISABLED),
98+
BIT_MASK_DESCR(IRQD_IRQ_MASKED),
99+
BIT_MASK_DESCR(IRQD_IRQ_INPROGRESS),
100+
101+
BIT_MASK_DESCR(IRQD_PER_CPU),
102+
BIT_MASK_DESCR(IRQD_NO_BALANCING),
103+
104+
BIT_MASK_DESCR(IRQD_MOVE_PCNTXT),
105+
BIT_MASK_DESCR(IRQD_AFFINITY_SET),
106+
BIT_MASK_DESCR(IRQD_SETAFFINITY_PENDING),
107+
BIT_MASK_DESCR(IRQD_AFFINITY_MANAGED),
108+
BIT_MASK_DESCR(IRQD_MANAGED_SHUTDOWN),
109+
110+
BIT_MASK_DESCR(IRQD_FORWARDED_TO_VCPU),
111+
112+
BIT_MASK_DESCR(IRQD_WAKEUP_STATE),
113+
BIT_MASK_DESCR(IRQD_WAKEUP_ARMED),
114+
};
115+
116+
static const struct irq_bit_descr irqdesc_states[] = {
117+
BIT_MASK_DESCR(_IRQ_NOPROBE),
118+
BIT_MASK_DESCR(_IRQ_NOREQUEST),
119+
BIT_MASK_DESCR(_IRQ_NOTHREAD),
120+
BIT_MASK_DESCR(_IRQ_NOAUTOEN),
121+
BIT_MASK_DESCR(_IRQ_NESTED_THREAD),
122+
BIT_MASK_DESCR(_IRQ_PER_CPU_DEVID),
123+
BIT_MASK_DESCR(_IRQ_IS_POLLED),
124+
BIT_MASK_DESCR(_IRQ_DISABLE_UNLAZY),
125+
};
126+
127+
static const struct irq_bit_descr irqdesc_istates[] = {
128+
BIT_MASK_DESCR(IRQS_AUTODETECT),
129+
BIT_MASK_DESCR(IRQS_SPURIOUS_DISABLED),
130+
BIT_MASK_DESCR(IRQS_POLL_INPROGRESS),
131+
BIT_MASK_DESCR(IRQS_ONESHOT),
132+
BIT_MASK_DESCR(IRQS_REPLAY),
133+
BIT_MASK_DESCR(IRQS_WAITING),
134+
BIT_MASK_DESCR(IRQS_PENDING),
135+
BIT_MASK_DESCR(IRQS_SUSPENDED),
136+
};
137+
138+
139+
static int irq_debug_show(struct seq_file *m, void *p)
140+
{
141+
struct irq_desc *desc = m->private;
142+
struct irq_data *data;
143+
144+
raw_spin_lock_irq(&desc->lock);
145+
data = irq_desc_get_irq_data(desc);
146+
seq_printf(m, "handler: %pf\n", desc->handle_irq);
147+
seq_printf(m, "status: 0x%08x\n", desc->status_use_accessors);
148+
irq_debug_show_bits(m, 0, desc->status_use_accessors, irqdesc_states,
149+
ARRAY_SIZE(irqdesc_states));
150+
seq_printf(m, "istate: 0x%08x\n", desc->istate);
151+
irq_debug_show_bits(m, 0, desc->istate, irqdesc_istates,
152+
ARRAY_SIZE(irqdesc_istates));
153+
seq_printf(m, "ddepth: %u\n", desc->depth);
154+
seq_printf(m, "wdepth: %u\n", desc->wake_depth);
155+
seq_printf(m, "dstate: 0x%08x\n", irqd_get(data));
156+
irq_debug_show_bits(m, 0, irqd_get(data), irqdata_states,
157+
ARRAY_SIZE(irqdata_states));
158+
seq_printf(m, "node: %d\n", irq_data_get_node(data));
159+
irq_debug_show_masks(m, desc);
160+
irq_debug_show_data(m, data, 0);
161+
raw_spin_unlock_irq(&desc->lock);
162+
return 0;
163+
}
164+
165+
static int irq_debug_open(struct inode *inode, struct file *file)
166+
{
167+
return single_open(file, irq_debug_show, inode->i_private);
168+
}
169+
170+
static const struct file_operations dfs_irq_ops = {
171+
.open = irq_debug_open,
172+
.read = seq_read,
173+
.llseek = seq_lseek,
174+
.release = single_release,
175+
};
176+
177+
void irq_add_debugfs_entry(unsigned int irq, struct irq_desc *desc)
178+
{
179+
char name [10];
180+
181+
if (!irq_dir || !desc || desc->debugfs_file)
182+
return;
183+
184+
sprintf(name, "%d", irq);
185+
desc->debugfs_file = debugfs_create_file(name, 0444, irq_dir, desc,
186+
&dfs_irq_ops);
187+
}
188+
189+
void irq_remove_debugfs_entry(struct irq_desc *desc)
190+
{
191+
if (desc->debugfs_file)
192+
debugfs_remove(desc->debugfs_file);
193+
}
194+
195+
static int __init irq_debugfs_init(void)
196+
{
197+
struct dentry *root_dir;
198+
int irq;
199+
200+
root_dir = debugfs_create_dir("irq", NULL);
201+
if (!root_dir)
202+
return -ENOMEM;
203+
204+
irq_domain_debugfs_init(root_dir);
205+
206+
irq_dir = debugfs_create_dir("irqs", root_dir);
207+
208+
irq_lock_sparse();
209+
for_each_active_irq(irq)
210+
irq_add_debugfs_entry(irq, irq_to_desc(irq));
211+
irq_unlock_sparse();
212+
213+
return 0;
214+
}
215+
__initcall(irq_debugfs_init);

kernel/irq/internals.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,11 @@ irq_put_desc_unlock(struct irq_desc *desc, unsigned long flags)
169169

170170
#define __irqd_to_state(d) ACCESS_PRIVATE((d)->common, state_use_accessors)
171171

172+
static inline unsigned int irqd_get(struct irq_data *d)
173+
{
174+
return __irqd_to_state(d);
175+
}
176+
172177
/*
173178
* Manipulation functions for irq_data.state
174179
*/
@@ -237,3 +242,20 @@ irq_init_generic_chip(struct irq_chip_generic *gc, const char *name,
237242
int num_ct, unsigned int irq_base,
238243
void __iomem *reg_base, irq_flow_handler_t handler) { }
239244
#endif /* CONFIG_GENERIC_IRQ_CHIP */
245+
246+
#ifdef CONFIG_GENERIC_IRQ_DEBUGFS
247+
void irq_add_debugfs_entry(unsigned int irq, struct irq_desc *desc);
248+
void irq_remove_debugfs_entry(struct irq_desc *desc);
249+
# ifdef CONFIG_IRQ_DOMAIN
250+
void irq_domain_debugfs_init(struct dentry *root);
251+
# else
252+
static inline void irq_domain_debugfs_init(struct dentry *root);
253+
# endif
254+
#else /* CONFIG_GENERIC_IRQ_DEBUGFS */
255+
static inline void irq_add_debugfs_entry(unsigned int irq, struct irq_desc *d)
256+
{
257+
}
258+
static inline void irq_remove_debugfs_entry(struct irq_desc *d)
259+
{
260+
}
261+
#endif /* CONFIG_GENERIC_IRQ_DEBUGFS */

kernel/irq/irqdesc.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,7 @@ static void free_desc(unsigned int irq)
394394
{
395395
struct irq_desc *desc = irq_to_desc(irq);
396396

397+
irq_remove_debugfs_entry(desc);
397398
unregister_irq_proc(irq, desc);
398399

399400
/*

0 commit comments

Comments
 (0)