diff options
Diffstat (limited to 'kernel/irq')
-rw-r--r-- | kernel/irq/Kconfig | 11 | ||||
-rw-r--r-- | kernel/irq/Makefile | 1 | ||||
-rw-r--r-- | kernel/irq/debugfs.c | 215 | ||||
-rw-r--r-- | kernel/irq/internals.h | 22 | ||||
-rw-r--r-- | kernel/irq/irqdesc.c | 1 | ||||
-rw-r--r-- | kernel/irq/irqdomain.c | 87 | ||||
-rw-r--r-- | kernel/irq/manage.c | 1 |
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 | ||
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 | |||
111 | endmenu | 122 | endmenu |
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 | |||
10 | obj-$(CONFIG_GENERIC_MSI_IRQ) += msi.o | 10 | obj-$(CONFIG_GENERIC_MSI_IRQ) += msi.o |
11 | obj-$(CONFIG_GENERIC_IRQ_IPI) += ipi.o | 11 | obj-$(CONFIG_GENERIC_IRQ_IPI) += ipi.o |
12 | obj-$(CONFIG_SMP) += affinity.o | 12 | obj-$(CONFIG_SMP) += affinity.o |
13 | obj-$(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 | |||
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); | ||
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 | ||
172 | static 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 | ||
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 */ | ||
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 | ||
36 | static void debugfs_add_domain_dir(struct irq_domain *d); | ||
37 | static void debugfs_remove_domain_dir(struct irq_domain *d); | ||
38 | #else | ||
39 | static inline void debugfs_add_domain_dir(struct irq_domain *d) { } | ||
40 | static 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); | |||
213 | void irq_domain_remove(struct irq_domain *domain) | 222 | void 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 | ||
1614 | static struct dentry *domain_dir; | ||
1615 | |||
1616 | static void | ||
1617 | irq_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 | |||
1632 | static 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 | |||
1646 | static 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 | |||
1651 | static 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 | |||
1658 | static 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 | |||
1666 | static void debugfs_remove_domain_dir(struct irq_domain *d) | ||
1667 | { | ||
1668 | if (d->debugfs_file) | ||
1669 | debugfs_remove(d->debugfs_file); | ||
1670 | } | ||
1671 | |||
1672 | void __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); |