之前为了确认 /proc/interrupts 文件第一列的缩进方式,看了一下相关源码,在这里做一些记录。
Contents
系统一共有多少个中断?
系统可用的中断数量主要由架构决定,x86 的具体数量可以参考以下定义。
/* kernel/irq/irqdesc.c */ 96 int nr_irqs = NR_IRQS; 97 EXPORT_SYMBOL_GPL(nr_irqs);
/* arch/x86/include/asm/irq_vectors.h */ 152 #define NR_IRQS_LEGACY 16 153 154 #define IO_APIC_VECTOR_LIMIT ( 32 * MAX_IO_APICS ) 155 156 #ifdef CONFIG_X86_IO_APIC 157 # define CPU_VECTOR_LIMIT (64 * NR_CPUS) 158 # define NR_IRQS \ 159 (CPU_VECTOR_LIMIT > IO_APIC_VECTOR_LIMIT ? \ 160 (NR_VECTORS + CPU_VECTOR_LIMIT) : \ 161 (NR_VECTORS + IO_APIC_VECTOR_LIMIT)) 162 #else /* !CONFIG_X86_IO_APIC: */ 163 # define NR_IRQS NR_IRQS_LEGACY 164 #endif
打印/显示 /proc/interrupts 文件
/proc/interrupts 是一个 sequence file. 关于 sequence file 的接口定义,可以参考[2].
对于 /proc/interrupts,简单地说,在读取这个文件时,系统会遍历 0 ~ nr_irq (包含 nr_irq)个中断号,对每个中断号都调用 show_interrupts() 来获取该中断的信息。
/* fs/proc/interrupts.c */
8 /*
9 * /proc/interrupts
10 */
11 static void *int_seq_start(struct seq_file *f, loff_t *pos)
12 {
13 return (*pos <= nr_irqs) ? pos : NULL;
14 }
15
16 static void *int_seq_next(struct seq_file *f, void *v, loff_t *pos)
17 {
18 (*pos)++;
19 if (*pos > nr_irqs)
20 return NULL;
21 return pos;
22 }
23
24 static void int_seq_stop(struct seq_file *f, void *v)
25 {
26 /* Nothing to do */
27 }
28
29 static const struct seq_operations int_seq_ops = {
30 .start = int_seq_start,
31 .next = int_seq_next,
32 .stop = int_seq_stop,
33 .show = show_interrupts
34 };
35
36 static int interrupts_open(struct inode *inode, struct file *filp)
37 {
38 return seq_open(filp, &int_seq_ops);
39 }
40
41 static const struct file_operations proc_interrupts_operations = {
42 .open = interrupts_open,
43 .read = seq_read,
44 .llseek = seq_lseek,
45 .release = seq_release,
46 };
47
48 static int __init proc_interrupts_init(void)
49 {
50 proc_create("interrupts", 0, NULL, &proc_interrupts_operations);
51 return 0;
52 }
53 module_init(proc_interrupts_init);
show_interrupts()
很惭愧, show_interrupts() 就做了一点微小的工作,大概三件事:
/* kernel/irq/proc.c */
414 int show_interrupts(struct seq_file *p, void *v)
415 {
416 static int prec;
417
418 unsigned long flags, any_count = 0;
419 int i = *(loff_t *) v, j;
420 struct irqaction *action;
421 struct irq_desc *desc;
422
423 if (i > ACTUAL_NR_IRQS)
424 return 0;
425
426 if (i == ACTUAL_NR_IRQS)
427 return arch_show_interrupts(p, prec);
428
429 /* print header and calculate the width of the first column */
430 if (i == 0) {
431 for (prec = 3, j = 1000; prec < 10 && j <= nr_irqs; ++prec)
432 j *= 10;
433
434 seq_printf(p, "%*s", prec + 8, "");
435 for_each_online_cpu(j)
436 seq_printf(p, "CPU%-8d", j);
437 seq_putc(p, '\n');
438 }
439
440 desc = irq_to_desc(i);
441 if (!desc)
442 return 0;
443
444 raw_spin_lock_irqsave(&desc->lock, flags);
445 for_each_online_cpu(j)
446 any_count |= kstat_irqs_cpu(i, j);
447 action = desc->action;
448 if (!action && !any_count)
449 goto out;
450
451 seq_printf(p, "%*d: ", prec, i);
452 for_each_online_cpu(j)
453 seq_printf(p, "%10u ", kstat_irqs_cpu(i, j));
454
455 if (desc->irq_data.chip) {
456 if (desc->irq_data.chip->irq_print_chip)
457 desc->irq_data.chip->irq_print_chip(&desc->irq_data, p);
458 else if (desc->irq_data.chip->name)
459 seq_printf(p, " %8s", desc->irq_data.chip->name);
460 else
461 seq_printf(p, " %8s", "-");
462 } else {
463 seq_printf(p, " %8s", "None");
464 }
465 #ifdef CONFIG_GENERIC_IRQ_SHOW_LEVEL
466 seq_printf(p, " %-8s", irqd_is_level_type(&desc->irq_data) ? "Level" : "Edge");
467 #endif
468 if (desc->name)
469 seq_printf(p, "-%-8s", desc->name);
470
471 if (action) {
472 seq_printf(p, " %s", action->name);
473 while ((action = action->next) != NULL)
474 seq_printf(p, ", %s", action->name);
475 }
476
477 seq_putc(p, '\n');
478 out:
479 raw_spin_unlock_irqrestore(&desc->lock, flags);
480 return 0;
481 }
/* arch/x86/kernel/irq.c */
50 #define irq_stats(x) (&per_cpu(irq_stat, x))
51 #define rh_irq_stats(x) (&per_cpu(rh_irq_stat, x))
52 /*
53 * /proc/interrupts printing for arch specific interrupts
54 */
55 int arch_show_interrupts(struct seq_file *p, int prec)
56 {
57 int j;
58
59 seq_printf(p, "%*s: ", prec, "NMI");
60 for_each_online_cpu(j)
61 seq_printf(p, "%10u ", irq_stats(j)->__nmi_count);
62 seq_printf(p, " Non-maskable interrupts\n");
63 #ifdef CONFIG_X86_LOCAL_APIC
64 seq_printf(p, "%*s: ", prec, "LOC");
65 for_each_online_cpu(j)
66 seq_printf(p, "%10u ", irq_stats(j)->apic_timer_irqs);
67 seq_printf(p, " Local timer interrupts\n");
68
69 seq_printf(p, "%*s: ", prec, "SPU");
70 for_each_online_cpu(j)
71 seq_printf(p, "%10u ", irq_stats(j)->irq_spurious_count);
72 seq_printf(p, " Spurious interrupts\n");
73 seq_printf(p, "%*s: ", prec, "PMI");
74 for_each_online_cpu(j)
75 seq_printf(p, "%10u ", irq_stats(j)->apic_perf_irqs);
76 seq_printf(p, " Performance monitoring interrupts\n");
77 seq_printf(p, "%*s: ", prec, "IWI");
78 for_each_online_cpu(j)
79 seq_printf(p, "%10u ", irq_stats(j)->apic_irq_work_irqs);
80 seq_printf(p, " IRQ work interrupts\n");
81 seq_printf(p, "%*s: ", prec, "RTR");
82 for_each_online_cpu(j)
83 seq_printf(p, "%10u ", irq_stats(j)->icr_read_retry_count);
84 seq_printf(p, " APIC ICR read retries\n");
85 #endif
86 if (x86_platform_ipi_callback) {
87 seq_printf(p, "%*s: ", prec, "PLT");
88 for_each_online_cpu(j)
89 seq_printf(p, "%10u ", irq_stats(j)->x86_platform_ipis);
90 seq_printf(p, " Platform interrupts\n");
91 }
92 #ifdef CONFIG_SMP
93 seq_printf(p, "%*s: ", prec, "RES");
94 for_each_online_cpu(j)
95 seq_printf(p, "%10u ", irq_stats(j)->irq_resched_count);
96 seq_printf(p, " Rescheduling interrupts\n");
97 seq_printf(p, "%*s: ", prec, "CAL");
98 for_each_online_cpu(j)
99 seq_printf(p, "%10u ", irq_stats(j)->irq_call_count -
100 irq_stats(j)->irq_tlb_count);
101 seq_printf(p, " Function call interrupts\n");
102 seq_printf(p, "%*s: ", prec, "TLB");
103 for_each_online_cpu(j)
104 seq_printf(p, "%10u ", irq_stats(j)->irq_tlb_count);
105 seq_printf(p, " TLB shootdowns\n");
106 #endif
107 #ifdef CONFIG_X86_THERMAL_VECTOR
108 seq_printf(p, "%*s: ", prec, "TRM");
109 for_each_online_cpu(j)
110 seq_printf(p, "%10u ", irq_stats(j)->irq_thermal_count);
111 seq_printf(p, " Thermal event interrupts\n");
112 #endif
113 #ifdef CONFIG_X86_MCE_THRESHOLD
114 seq_printf(p, "%*s: ", prec, "THR");
115 for_each_online_cpu(j)
116 seq_printf(p, "%10u ", irq_stats(j)->irq_threshold_count);
117 seq_printf(p, " Threshold APIC interrupts\n");
118 #endif
119 #ifdef CONFIG_X86_MCE
120 seq_printf(p, "%*s: ", prec, "MCE");
121 for_each_online_cpu(j)
122 seq_printf(p, "%10u ", per_cpu(mce_exception_count, j));
123 seq_printf(p, " Machine check exceptions\n");
124 seq_printf(p, "%*s: ", prec, "MCP");
125 for_each_online_cpu(j)
126 seq_printf(p, "%10u ", per_cpu(mce_poll_count, j));
127 seq_printf(p, " Machine check polls\n");
128 #endif
129 if (test_bit(HYPERVISOR_CALLBACK_VECTOR, used_vectors)) {
130 seq_printf(p, "%*s: ", prec, "HYP");
131 for_each_online_cpu(j)
132 seq_printf(p, "%10u ",
133 ¦ rh_irq_stats(j)->irq_hv_callback_count);
134 seq_printf(p, " Hypervisor callback interrupts\n");
135 }
136 seq_printf(p, "%*s: %10u\n", prec, "ERR", atomic_read(&irq_err_count));
137 #if defined(CONFIG_X86_IO_APIC)
138 seq_printf(p, "%*s: %10u\n", prec, "MIS", atomic_read(&irq_mis_count));
139 #endif
140 return 0;
141 }
中断描述符 – interrupt descriptor
说到底, /proc/interrupts 的统计数据是从每个中断号对应的 interrupt descriptor 得到的。
interrupt descriptor 的定义如下。本人水平有限,就暂时不解释每一个值的含义了。
/* include/linux/irqdesc.h */
/**
* struct irq_desc - interrupt descriptor
* @irq_data: per irq and chip data passed down to chip functions
* @kstat_irqs: irq stats per cpu
* @handle_irq: highlevel irq-events handler
* @preflow_handler: handler called before the flow handler (currently used by sparc)
* @action: the irq action chain
* @status: status information
* @core_internal_state__do_not_mess_with_it: core internal status information
* @depth: disable-depth, for nested irq_disable() calls
* @wake_depth: enable depth, for multiple irq_set_irq_wake() callers
* @irq_count: stats field to detect stalled irqs
* @last_unhandled: aging timer for unhandled count
* @irqs_unhandled: stats field for spurious unhandled interrupts
* @lock: locking for SMP
* @affinity_hint: hint to user space for preferred irq affinity
* @affinity_notify: context for notification of affinity changes
* @pending_mask: pending rebalanced interrupts
* @threads_oneshot: bitfield to handle shared oneshot threads
* @threads_active: number of irqaction threads currently running
* @wait_for_threads: wait queue for sync_irq to wait for threaded handlers
* @dir: /proc/irq/ procfs entry
* @name: flow handler name for /proc/interrupts output
*/
struct irq_desc {
struct irq_data irq_data;
unsigned int __percpu *kstat_irqs;
irq_flow_handler_t handle_irq;
#ifdef CONFIG_IRQ_PREFLOW_FASTEOI
irq_preflow_handler_t preflow_handler;
#endif
struct irqaction *action; /* IRQ action list */
unsigned int status_use_accessors;
unsigned int core_internal_state__do_not_mess_with_it;
unsigned int depth; /* nested irq disables */
unsigned int wake_depth; /* nested wake enables */
unsigned int irq_count; /* For detecting broken IRQs */
unsigned long last_unhandled; /* Aging timer for unhandled count */
unsigned int irqs_unhandled;
raw_spinlock_t lock;
struct cpumask *percpu_enabled;
#ifdef CONFIG_SMP
const struct cpumask *affinity_hint;
struct irq_affinity_notify *affinity_notify;
#ifdef CONFIG_GENERIC_PENDING_IRQ
cpumask_var_t pending_mask;
#endif
#endif
unsigned long threads_oneshot;
atomic_t threads_active;
wait_queue_head_t wait_for_threads;
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *dir;
#endif
int parent_irq;
struct module *owner;
const char *name;
} ____cacheline_internodealigned_in_smp;
参考资料
1. [Proc interrupts] http://www.crashcourse.ca/wiki/index.php/Proc_interrupts
本文主要搬运这篇文档。
2. [5.4. Manage /proc file with seq_file] http://www.tldp.org/LDP/lkmpg/2.6/html/x861.html
描述 sequence file 接口。
3. [Lesson 13: Proc files and sequence files] http://www.crashcourse.ca/introduction-linux-kernel-programming/lesson-13-proc-files-and-sequence-files-part-3
关于 sequence file 更详细的描述。