Blame view
kernel/irq/proc.c
11.2 KB
1da177e4c
|
1 2 3 4 5 6 7 8 9 |
/* * linux/kernel/irq/proc.c * * Copyright (C) 1992, 1998-2004 Linus Torvalds, Ingo Molnar * * This file contains the /proc/irq/ handling code. */ #include <linux/irq.h> |
5a0e3ad6a
|
10 |
#include <linux/gfp.h> |
1da177e4c
|
11 |
#include <linux/proc_fs.h> |
f18e439d1
|
12 |
#include <linux/seq_file.h> |
1da177e4c
|
13 |
#include <linux/interrupt.h> |
c78b9b65f
|
14 |
#include <linux/kernel_stat.h> |
1da177e4c
|
15 |
|
97a41e261
|
16 |
#include "internals.h" |
4a733ee12
|
17 |
static struct proc_dir_entry *root_irq_dir; |
1da177e4c
|
18 19 |
#ifdef CONFIG_SMP |
4b060420a
|
20 |
static int show_irq_affinity(int type, struct seq_file *m, void *v) |
1da177e4c
|
21 |
{ |
08678b084
|
22 |
struct irq_desc *desc = irq_to_desc((long)m->private); |
6b8ff3120
|
23 |
const struct cpumask *mask = desc->irq_data.affinity; |
42ee2b741
|
24 25 |
#ifdef CONFIG_GENERIC_PENDING_IRQ |
f230b6d5c
|
26 |
if (irqd_is_setaffinity_pending(&desc->irq_data)) |
7f7ace0cd
|
27 |
mask = desc->pending_mask; |
42ee2b741
|
28 |
#endif |
4b060420a
|
29 30 31 32 |
if (type) seq_cpumask_list(m, mask); else seq_cpumask(m, mask); |
f18e439d1
|
33 34 35 |
seq_putc(m, ' '); return 0; |
1da177e4c
|
36 |
} |
e7a297b0d
|
37 38 39 40 41 |
static int irq_affinity_hint_proc_show(struct seq_file *m, void *v) { struct irq_desc *desc = irq_to_desc((long)m->private); unsigned long flags; cpumask_var_t mask; |
4308ad801
|
42 |
if (!zalloc_cpumask_var(&mask, GFP_KERNEL)) |
e7a297b0d
|
43 44 45 46 47 |
return -ENOMEM; raw_spin_lock_irqsave(&desc->lock, flags); if (desc->affinity_hint) cpumask_copy(mask, desc->affinity_hint); |
e7a297b0d
|
48 49 50 51 52 53 54 55 56 |
raw_spin_unlock_irqrestore(&desc->lock, flags); seq_cpumask(m, mask); seq_putc(m, ' '); free_cpumask_var(mask); return 0; } |
25d61578d
|
57 58 59 |
#ifndef is_affinity_mask_valid #define is_affinity_mask_valid(val) 1 #endif |
1da177e4c
|
60 |
int no_irq_affinity; |
4b060420a
|
61 62 63 64 65 66 67 68 69 70 71 72 |
static int irq_affinity_proc_show(struct seq_file *m, void *v) { return show_irq_affinity(0, m, v); } static int irq_affinity_list_proc_show(struct seq_file *m, void *v) { return show_irq_affinity(1, m, v); } static ssize_t write_irq_affinity(int type, struct file *file, |
f18e439d1
|
73 |
const char __user *buffer, size_t count, loff_t *pos) |
1da177e4c
|
74 |
{ |
f18e439d1
|
75 |
unsigned int irq = (int)(long)PDE(file->f_path.dentry->d_inode)->data; |
0de26520c
|
76 |
cpumask_var_t new_value; |
f18e439d1
|
77 |
int err; |
1da177e4c
|
78 |
|
bce43032a
|
79 |
if (!irq_can_set_affinity(irq) || no_irq_affinity) |
1da177e4c
|
80 |
return -EIO; |
0de26520c
|
81 82 |
if (!alloc_cpumask_var(&new_value, GFP_KERNEL)) return -ENOMEM; |
4b060420a
|
83 84 85 86 |
if (type) err = cpumask_parselist_user(buffer, count, new_value); else err = cpumask_parse_user(buffer, count, new_value); |
1da177e4c
|
87 |
if (err) |
0de26520c
|
88 |
goto free_cpumask; |
1da177e4c
|
89 |
|
6bdf197b0
|
90 |
if (!is_affinity_mask_valid(new_value)) { |
0de26520c
|
91 92 93 |
err = -EINVAL; goto free_cpumask; } |
25d61578d
|
94 |
|
1da177e4c
|
95 96 97 98 99 |
/* * Do not allow disabling IRQs completely - it's a too easy * way to make the system unusable accidentally :-) At least * one online CPU still has to be targeted. */ |
0de26520c
|
100 |
if (!cpumask_intersects(new_value, cpu_online_mask)) { |
eee45269b
|
101 102 |
/* Special case for empty set - allow the architecture code to set default SMP affinity. */ |
3b8249e75
|
103 |
err = irq_select_affinity_usr(irq, new_value) ? -EINVAL : count; |
0de26520c
|
104 105 106 107 108 109 110 111 |
} else { irq_set_affinity(irq, new_value); err = count; } free_cpumask: free_cpumask_var(new_value); return err; |
1da177e4c
|
112 |
} |
4b060420a
|
113 114 115 116 117 118 119 120 121 122 123 |
static ssize_t irq_affinity_proc_write(struct file *file, const char __user *buffer, size_t count, loff_t *pos) { return write_irq_affinity(0, file, buffer, count, pos); } static ssize_t irq_affinity_list_proc_write(struct file *file, const char __user *buffer, size_t count, loff_t *pos) { return write_irq_affinity(1, file, buffer, count, pos); } |
f18e439d1
|
124 |
static int irq_affinity_proc_open(struct inode *inode, struct file *file) |
184047567
|
125 |
{ |
f18e439d1
|
126 |
return single_open(file, irq_affinity_proc_show, PDE(inode)->data); |
184047567
|
127 |
} |
4b060420a
|
128 129 130 131 |
static int irq_affinity_list_proc_open(struct inode *inode, struct file *file) { return single_open(file, irq_affinity_list_proc_show, PDE(inode)->data); } |
e7a297b0d
|
132 133 134 135 |
static int irq_affinity_hint_proc_open(struct inode *inode, struct file *file) { return single_open(file, irq_affinity_hint_proc_show, PDE(inode)->data); } |
f18e439d1
|
136 137 138 139 140 141 142 |
static const struct file_operations irq_affinity_proc_fops = { .open = irq_affinity_proc_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, .write = irq_affinity_proc_write, }; |
e7a297b0d
|
143 144 145 146 147 148 |
static const struct file_operations irq_affinity_hint_proc_fops = { .open = irq_affinity_hint_proc_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; |
4b060420a
|
149 150 151 152 153 154 155 |
static const struct file_operations irq_affinity_list_proc_fops = { .open = irq_affinity_list_proc_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, .write = irq_affinity_list_proc_write, }; |
f18e439d1
|
156 157 |
static int default_affinity_show(struct seq_file *m, void *v) { |
d036e67b4
|
158 |
seq_cpumask(m, irq_default_affinity); |
f18e439d1
|
159 160 161 162 163 164 165 |
seq_putc(m, ' '); return 0; } static ssize_t default_affinity_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) |
184047567
|
166 |
{ |
d036e67b4
|
167 |
cpumask_var_t new_value; |
f18e439d1
|
168 |
int err; |
184047567
|
169 |
|
d036e67b4
|
170 171 172 173 |
if (!alloc_cpumask_var(&new_value, GFP_KERNEL)) return -ENOMEM; err = cpumask_parse_user(buffer, count, new_value); |
184047567
|
174 |
if (err) |
d036e67b4
|
175 |
goto out; |
184047567
|
176 |
|
d036e67b4
|
177 178 179 180 |
if (!is_affinity_mask_valid(new_value)) { err = -EINVAL; goto out; } |
184047567
|
181 182 183 184 185 186 |
/* * Do not allow disabling IRQs completely - it's a too easy * way to make the system unusable accidentally :-) At least * one online CPU still has to be targeted. */ |
d036e67b4
|
187 188 189 190 |
if (!cpumask_intersects(new_value, cpu_online_mask)) { err = -EINVAL; goto out; } |
184047567
|
191 |
|
d036e67b4
|
192 193 |
cpumask_copy(irq_default_affinity, new_value); err = count; |
184047567
|
194 |
|
d036e67b4
|
195 196 197 |
out: free_cpumask_var(new_value); return err; |
184047567
|
198 |
} |
f18e439d1
|
199 200 201 |
static int default_affinity_open(struct inode *inode, struct file *file) { |
34769945f
|
202 |
return single_open(file, default_affinity_show, PDE(inode)->data); |
f18e439d1
|
203 204 205 206 207 208 209 210 211 |
} static const struct file_operations default_affinity_proc_fops = { .open = default_affinity_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, .write = default_affinity_write, }; |
92d6b71ab
|
212 213 214 215 |
static int irq_node_proc_show(struct seq_file *m, void *v) { struct irq_desc *desc = irq_to_desc((long) m->private); |
6b8ff3120
|
216 217 |
seq_printf(m, "%d ", desc->irq_data.node); |
92d6b71ab
|
218 219 220 221 222 223 224 225 226 227 228 229 230 231 |
return 0; } static int irq_node_proc_open(struct inode *inode, struct file *file) { return single_open(file, irq_node_proc_show, PDE(inode)->data); } static const struct file_operations irq_node_proc_fops = { .open = irq_node_proc_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; |
1da177e4c
|
232 |
#endif |
a1afb6371
|
233 |
static int irq_spurious_proc_show(struct seq_file *m, void *v) |
96d97cf03
|
234 |
{ |
a1afb6371
|
235 236 237 238 239 240 241 242 243 244 245 246 247 |
struct irq_desc *desc = irq_to_desc((long) m->private); seq_printf(m, "count %u " "unhandled %u " "last_unhandled %u ms ", desc->irq_count, desc->irqs_unhandled, jiffies_to_msecs(desc->last_unhandled)); return 0; } static int irq_spurious_proc_open(struct inode *inode, struct file *file) { |
25c9170ed
|
248 |
return single_open(file, irq_spurious_proc_show, PDE(inode)->data); |
96d97cf03
|
249 |
} |
a1afb6371
|
250 251 252 253 254 255 |
static const struct file_operations irq_spurious_proc_fops = { .open = irq_spurious_proc_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; |
1da177e4c
|
256 257 258 259 |
#define MAX_NAMELEN 128 static int name_unique(unsigned int irq, struct irqaction *new_action) { |
08678b084
|
260 |
struct irq_desc *desc = irq_to_desc(irq); |
1da177e4c
|
261 |
struct irqaction *action; |
d2d9433a4
|
262 263 |
unsigned long flags; int ret = 1; |
1da177e4c
|
264 |
|
239007b84
|
265 |
raw_spin_lock_irqsave(&desc->lock, flags); |
d2d9433a4
|
266 |
for (action = desc->action ; action; action = action->next) { |
1da177e4c
|
267 |
if ((action != new_action) && action->name && |
d2d9433a4
|
268 269 270 271 272 |
!strcmp(new_action->name, action->name)) { ret = 0; break; } } |
239007b84
|
273 |
raw_spin_unlock_irqrestore(&desc->lock, flags); |
d2d9433a4
|
274 |
return ret; |
1da177e4c
|
275 276 277 278 279 |
} void register_handler_proc(unsigned int irq, struct irqaction *action) { char name [MAX_NAMELEN]; |
08678b084
|
280 |
struct irq_desc *desc = irq_to_desc(irq); |
1da177e4c
|
281 |
|
08678b084
|
282 |
if (!desc->dir || action->dir || !action->name || |
1da177e4c
|
283 284 285 286 287 288 289 |
!name_unique(irq, action)) return; memset(name, 0, MAX_NAMELEN); snprintf(name, MAX_NAMELEN, "%s", action->name); /* create /proc/irq/1234/handler/ */ |
08678b084
|
290 |
action->dir = proc_mkdir(name, desc->dir); |
1da177e4c
|
291 292 293 294 295 |
} #undef MAX_NAMELEN #define MAX_NAMELEN 10 |
2c6927a38
|
296 |
void register_irq_proc(unsigned int irq, struct irq_desc *desc) |
1da177e4c
|
297 298 |
{ char name [MAX_NAMELEN]; |
6b8ff3120
|
299 |
if (!root_irq_dir || (desc->irq_data.chip == &no_irq_chip) || desc->dir) |
1da177e4c
|
300 301 302 303 304 305 |
return; memset(name, 0, MAX_NAMELEN); sprintf(name, "%d", irq); /* create /proc/irq/1234 */ |
08678b084
|
306 |
desc->dir = proc_mkdir(name, root_irq_dir); |
c82a43d40
|
307 308 |
if (!desc->dir) return; |
1da177e4c
|
309 310 |
#ifdef CONFIG_SMP |
f18e439d1
|
311 |
/* create /proc/irq/<irq>/smp_affinity */ |
08678b084
|
312 |
proc_create_data("smp_affinity", 0600, desc->dir, |
f18e439d1
|
313 |
&irq_affinity_proc_fops, (void *)(long)irq); |
92d6b71ab
|
314 |
|
e7a297b0d
|
315 316 317 |
/* create /proc/irq/<irq>/affinity_hint */ proc_create_data("affinity_hint", 0400, desc->dir, &irq_affinity_hint_proc_fops, (void *)(long)irq); |
4b060420a
|
318 319 320 |
/* create /proc/irq/<irq>/smp_affinity_list */ proc_create_data("smp_affinity_list", 0600, desc->dir, &irq_affinity_list_proc_fops, (void *)(long)irq); |
92d6b71ab
|
321 322 |
proc_create_data("node", 0444, desc->dir, &irq_node_proc_fops, (void *)(long)irq); |
1da177e4c
|
323 |
#endif |
96d97cf03
|
324 |
|
a1afb6371
|
325 326 |
proc_create_data("spurious", 0444, desc->dir, &irq_spurious_proc_fops, (void *)(long)irq); |
1da177e4c
|
327 |
} |
13bfe99e0
|
328 329 330 331 332 333 334 335 336 |
void unregister_irq_proc(unsigned int irq, struct irq_desc *desc) { char name [MAX_NAMELEN]; if (!root_irq_dir || !desc->dir) return; #ifdef CONFIG_SMP remove_proc_entry("smp_affinity", desc->dir); remove_proc_entry("affinity_hint", desc->dir); |
def945eeb
|
337 |
remove_proc_entry("smp_affinity_list", desc->dir); |
13bfe99e0
|
338 339 340 341 342 343 344 345 |
remove_proc_entry("node", desc->dir); #endif remove_proc_entry("spurious", desc->dir); memset(name, 0, MAX_NAMELEN); sprintf(name, "%u", irq); remove_proc_entry(name, root_irq_dir); } |
1da177e4c
|
346 347 348 349 |
#undef MAX_NAMELEN void unregister_handler_proc(unsigned int irq, struct irqaction *action) { |
08678b084
|
350 351 |
if (action->dir) { struct irq_desc *desc = irq_to_desc(irq); |
d3c60047b
|
352 |
|
08678b084
|
353 354 |
remove_proc_entry(action->dir->name, desc->dir); } |
1da177e4c
|
355 |
} |
3786fc710
|
356 |
static void register_default_affinity_proc(void) |
184047567
|
357 358 |
{ #ifdef CONFIG_SMP |
f18e439d1
|
359 360 |
proc_create("irq/default_smp_affinity", 0600, NULL, &default_affinity_proc_fops); |
184047567
|
361 362 |
#endif } |
1da177e4c
|
363 364 |
void init_irq_proc(void) { |
2c6927a38
|
365 366 |
unsigned int irq; struct irq_desc *desc; |
1da177e4c
|
367 368 369 370 371 |
/* create /proc/irq */ root_irq_dir = proc_mkdir("irq", NULL); if (!root_irq_dir) return; |
184047567
|
372 |
register_default_affinity_proc(); |
1da177e4c
|
373 374 375 |
/* * Create entries for all existing IRQs. */ |
0b8f1efad
|
376 377 378 |
for_each_irq_desc(irq, desc) { if (!desc) continue; |
2c6927a38
|
379 |
register_irq_proc(irq, desc); |
0b8f1efad
|
380 |
} |
1da177e4c
|
381 |
} |
c78b9b65f
|
382 383 384 385 386 387 |
#ifdef CONFIG_GENERIC_IRQ_SHOW int __weak arch_show_interrupts(struct seq_file *p, int prec) { return 0; } |
a6e120ed4
|
388 389 390 |
#ifndef ACTUAL_NR_IRQS # define ACTUAL_NR_IRQS nr_irqs #endif |
c78b9b65f
|
391 392 393 394 395 396 397 398 |
int show_interrupts(struct seq_file *p, void *v) { static int prec; unsigned long flags, any_count = 0; int i = *(loff_t *) v, j; struct irqaction *action; struct irq_desc *desc; |
a6e120ed4
|
399 |
if (i > ACTUAL_NR_IRQS) |
c78b9b65f
|
400 |
return 0; |
a6e120ed4
|
401 |
if (i == ACTUAL_NR_IRQS) |
c78b9b65f
|
402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 |
return arch_show_interrupts(p, prec); /* print header and calculate the width of the first column */ if (i == 0) { for (prec = 3, j = 1000; prec < 10 && j <= nr_irqs; ++prec) j *= 10; seq_printf(p, "%*s", prec + 8, ""); for_each_online_cpu(j) seq_printf(p, "CPU%-8d", j); seq_putc(p, ' '); } desc = irq_to_desc(i); if (!desc) return 0; raw_spin_lock_irqsave(&desc->lock, flags); for_each_online_cpu(j) any_count |= kstat_irqs_cpu(i, j); action = desc->action; if (!action && !any_count) goto out; seq_printf(p, "%*d: ", prec, i); for_each_online_cpu(j) seq_printf(p, "%10u ", kstat_irqs_cpu(i, j)); |
ab7798ffc
|
430 431 432 433 434 435 436 437 438 439 440 |
if (desc->irq_data.chip) { if (desc->irq_data.chip->irq_print_chip) desc->irq_data.chip->irq_print_chip(&desc->irq_data, p); else if (desc->irq_data.chip->name) seq_printf(p, " %8s", desc->irq_data.chip->name); else seq_printf(p, " %8s", "-"); } else { seq_printf(p, " %8s", "None"); } |
94b2c363d
|
441 |
#ifdef CONFIG_GENERIC_IRQ_SHOW_LEVEL |
ab7798ffc
|
442 443 |
seq_printf(p, " %-8s", irqd_is_level_type(&desc->irq_data) ? "Level" : "Edge"); #endif |
ee0401ec1
|
444 445 |
if (desc->name) seq_printf(p, "-%-8s", desc->name); |
c78b9b65f
|
446 447 448 449 450 451 452 453 454 455 456 457 458 459 |
if (action) { seq_printf(p, " %s", action->name); while ((action = action->next) != NULL) seq_printf(p, ", %s", action->name); } seq_putc(p, ' '); out: raw_spin_unlock_irqrestore(&desc->lock, flags); return 0; } #endif |