summaryrefslogtreecommitdiffstats
path: root/kernel/irq
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/irq')
-rw-r--r--kernel/irq/Kconfig11
-rw-r--r--kernel/irq/Makefile1
-rw-r--r--kernel/irq/debugfs.c215
-rw-r--r--kernel/irq/internals.h22
-rw-r--r--kernel/irq/irqdesc.c1
-rw-r--r--kernel/irq/irqdomain.c87
-rw-r--r--kernel/irq/manage.c1
7 files changed, 337 insertions, 1 deletions
diff --git a/kernel/irq/Kconfig b/kernel/irq/Kconfig
index 3bbfd6a9c475..8d9498e51585 100644
--- a/kernel/irq/Kconfig
+++ b/kernel/irq/Kconfig
@@ -108,4 +108,15 @@ config SPARSE_IRQ
108 108
109 If you don't know what to do here, say N. 109 If you don't know what to do here, say N.
110 110
111config 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
111endmenu 122endmenu
diff --git a/kernel/irq/Makefile b/kernel/irq/Makefile
index 1d3ee3169202..c61fc9c2d1f7 100644
--- a/kernel/irq/Makefile
+++ b/kernel/irq/Makefile
@@ -10,3 +10,4 @@ obj-$(CONFIG_PM_SLEEP) += pm.o
10obj-$(CONFIG_GENERIC_MSI_IRQ) += msi.o 10obj-$(CONFIG_GENERIC_MSI_IRQ) += msi.o
11obj-$(CONFIG_GENERIC_IRQ_IPI) += ipi.o 11obj-$(CONFIG_GENERIC_IRQ_IPI) += ipi.o
12obj-$(CONFIG_SMP) += affinity.o 12obj-$(CONFIG_SMP) += affinity.o
13obj-$(CONFIG_GENERIC_IRQ_DEBUGFS) += debugfs.o
diff --git a/kernel/irq/debugfs.c b/kernel/irq/debugfs.c
new file mode 100644
index 000000000000..50ee2f6593e8
--- /dev/null
+++ b/kernel/irq/debugfs.c
@@ -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
12static struct dentry *irq_dir;
13
14struct irq_bit_descr {
15 unsigned int mask;
16 char *name;
17};
18#define BIT_MASK_DESCR(m) { .mask = m, .name = #m }
19
20static 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
32static 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
45static void irq_debug_show_masks(struct seq_file *m, struct irq_desc *desc) { }
46#endif
47
48static 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
58static void
59irq_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
73static void
74irq_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
88static 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
116static 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
127static 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
139static 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
165static int irq_debug_open(struct inode *inode, struct file *file)
166{
167 return single_open(file, irq_debug_show, inode->i_private);
168}
169
170static 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
177void 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
189void irq_remove_debugfs_entry(struct irq_desc *desc)
190{
191 if (desc->debugfs_file)
192 debugfs_remove(desc->debugfs_file);
193}
194
195static 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);
diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h
index 921a2419720c..094db5bfb83f 100644
--- a/kernel/irq/internals.h
+++ b/kernel/irq/internals.h
@@ -169,6 +169,11 @@ irq_put_desc_unlock(struct irq_desc *desc, unsigned long flags)
169 169
170#define __irqd_to_state(d) ACCESS_PRIVATE((d)->common, state_use_accessors) 170#define __irqd_to_state(d) ACCESS_PRIVATE((d)->common, state_use_accessors)
171 171
172static inline unsigned int irqd_get(struct irq_data *d)
173{
174 return __irqd_to_state(d);
175}
176
172/* 177/*
173 * Manipulation functions for irq_data.state 178 * Manipulation functions for irq_data.state
174 */ 179 */
@@ -237,3 +242,20 @@ irq_init_generic_chip(struct irq_chip_generic *gc, const char *name,
237 int num_ct, unsigned int irq_base, 242 int num_ct, unsigned int irq_base,
238 void __iomem *reg_base, irq_flow_handler_t handler) { } 243 void __iomem *reg_base, irq_flow_handler_t handler) { }
239#endif /* CONFIG_GENERIC_IRQ_CHIP */ 244#endif /* CONFIG_GENERIC_IRQ_CHIP */
245
246#ifdef CONFIG_GENERIC_IRQ_DEBUGFS
247void irq_add_debugfs_entry(unsigned int irq, struct irq_desc *desc);
248void irq_remove_debugfs_entry(struct irq_desc *desc);
249# ifdef CONFIG_IRQ_DOMAIN
250void irq_domain_debugfs_init(struct dentry *root);
251# else
252static inline void irq_domain_debugfs_init(struct dentry *root);
253# endif
254#else /* CONFIG_GENERIC_IRQ_DEBUGFS */
255static inline void irq_add_debugfs_entry(unsigned int irq, struct irq_desc *d)
256{
257}
258static inline void irq_remove_debugfs_entry(struct irq_desc *d)
259{
260}
261#endif /* CONFIG_GENERIC_IRQ_DEBUGFS */
diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c
index 09abce2ea8f0..feade536b6d1 100644
--- a/kernel/irq/irqdesc.c
+++ b/kernel/irq/irqdesc.c
@@ -394,6 +394,7 @@ static void free_desc(unsigned int irq)
394{ 394{
395 struct irq_desc *desc = irq_to_desc(irq); 395 struct irq_desc *desc = irq_to_desc(irq);
396 396
397 irq_remove_debugfs_entry(desc);
397 unregister_irq_proc(irq, desc); 398 unregister_irq_proc(irq, desc);
398 399
399 /* 400 /*
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 8d5805c655b6..75e1f0851c33 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -29,9 +29,17 @@ struct irqchip_fwid {
29 struct fwnode_handle fwnode; 29 struct fwnode_handle fwnode;
30 unsigned int type; 30 unsigned int type;
31 char *name; 31 char *name;
32 void *data; 32 void *data;
33}; 33};
34 34
35#ifdef CONFIG_GENERIC_IRQ_DEBUGFS
36static void debugfs_add_domain_dir(struct irq_domain *d);
37static void debugfs_remove_domain_dir(struct irq_domain *d);
38#else
39static inline void debugfs_add_domain_dir(struct irq_domain *d) { }
40static inline void debugfs_remove_domain_dir(struct irq_domain *d) { }
41#endif
42
35/** 43/**
36 * irq_domain_alloc_fwnode - Allocate a fwnode_handle suitable for 44 * irq_domain_alloc_fwnode - Allocate a fwnode_handle suitable for
37 * identifying an irq domain 45 * identifying an irq domain
@@ -194,6 +202,7 @@ struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, int size,
194 irq_domain_check_hierarchy(domain); 202 irq_domain_check_hierarchy(domain);
195 203
196 mutex_lock(&irq_domain_mutex); 204 mutex_lock(&irq_domain_mutex);
205 debugfs_add_domain_dir(domain);
197 list_add(&domain->link, &irq_domain_list); 206 list_add(&domain->link, &irq_domain_list);
198 mutex_unlock(&irq_domain_mutex); 207 mutex_unlock(&irq_domain_mutex);
199 208
@@ -213,6 +222,7 @@ EXPORT_SYMBOL_GPL(__irq_domain_add);
213void irq_domain_remove(struct irq_domain *domain) 222void irq_domain_remove(struct irq_domain *domain)
214{ 223{
215 mutex_lock(&irq_domain_mutex); 224 mutex_lock(&irq_domain_mutex);
225 debugfs_remove_domain_dir(domain);
216 226
217 WARN_ON(!radix_tree_empty(&domain->revmap_tree)); 227 WARN_ON(!radix_tree_empty(&domain->revmap_tree));
218 228
@@ -1599,3 +1609,78 @@ static void irq_domain_check_hierarchy(struct irq_domain *domain)
1599{ 1609{
1600} 1610}
1601#endif /* CONFIG_IRQ_DOMAIN_HIERARCHY */ 1611#endif /* CONFIG_IRQ_DOMAIN_HIERARCHY */
1612
1613#ifdef CONFIG_GENERIC_IRQ_DEBUGFS
1614static struct dentry *domain_dir;
1615
1616static void
1617irq_domain_debug_show_one(struct seq_file *m, struct irq_domain *d, int ind)
1618{
1619 seq_printf(m, "%*sname: %s\n", ind, "", d->name);
1620 seq_printf(m, "%*ssize: %u\n", ind + 1, "",
1621 d->revmap_size + d->revmap_direct_max_irq);
1622 seq_printf(m, "%*smapped: %u\n", ind + 1, "", d->mapcount);
1623 seq_printf(m, "%*sflags: 0x%08x\n", ind +1 , "", d->flags);
1624#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
1625 if (!d->parent)
1626 return;
1627 seq_printf(m, "%*sparent: %s\n", ind + 1, "", d->parent->name);
1628 irq_domain_debug_show_one(m, d->parent, ind + 4);
1629#endif
1630}
1631
1632static int irq_domain_debug_show(struct seq_file *m, void *p)
1633{
1634 struct irq_domain *d = m->private;
1635
1636 /* Default domain? Might be NULL */
1637 if (!d) {
1638 if (!irq_default_domain)
1639 return 0;
1640 d = irq_default_domain;
1641 }
1642 irq_domain_debug_show_one(m, d, 0);
1643 return 0;
1644}
1645
1646static int irq_domain_debug_open(struct inode *inode, struct file *file)
1647{
1648 return single_open(file, irq_domain_debug_show, inode->i_private);
1649}
1650
1651static const struct file_operations dfs_domain_ops = {
1652 .open = irq_domain_debug_open,
1653 .read = seq_read,
1654 .llseek = seq_lseek,
1655 .release = single_release,
1656};
1657
1658static void debugfs_add_domain_dir(struct irq_domain *d)
1659{
1660 if (!d->name || !domain_dir || d->debugfs_file)
1661 return;
1662 d->debugfs_file = debugfs_create_file(d->name, 0444, domain_dir, d,
1663 &dfs_domain_ops);
1664}
1665
1666static void debugfs_remove_domain_dir(struct irq_domain *d)
1667{
1668 if (d->debugfs_file)
1669 debugfs_remove(d->debugfs_file);
1670}
1671
1672void __init irq_domain_debugfs_init(struct dentry *root)
1673{
1674 struct irq_domain *d;
1675
1676 domain_dir = debugfs_create_dir("domains", root);
1677 if (!domain_dir)
1678 return;
1679
1680 debugfs_create_file("default", 0444, domain_dir, NULL, &dfs_domain_ops);
1681 mutex_lock(&irq_domain_mutex);
1682 list_for_each_entry(d, &irq_domain_list, link)
1683 debugfs_add_domain_dir(d);
1684 mutex_unlock(&irq_domain_mutex);
1685}
1686#endif
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c
index 4c34696ca575..284f4eb1ffbe 100644
--- a/kernel/irq/manage.c
+++ b/kernel/irq/manage.c
@@ -1398,6 +1398,7 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
1398 wake_up_process(new->secondary->thread); 1398 wake_up_process(new->secondary->thread);
1399 1399
1400 register_irq_proc(irq, desc); 1400 register_irq_proc(irq, desc);
1401 irq_add_debugfs_entry(irq, desc);
1401 new->dir = NULL; 1402 new->dir = NULL;
1402 register_handler_proc(irq, new); 1403 register_handler_proc(irq, new);
1403 free_cpumask_var(mask); 1404 free_cpumask_var(mask);