lpfs/proc/interrupts.rs
1
2//! https://github.com/torvalds/linux/blob/cef7298262e9af841fb70d8673af45caf55300a1/kernel/irq/proc.c
3//!
4//! > <pre>
5//! > 5.2.11. /proc/interrupts
6//! > This file records the number of interrupts per IRQ on the x86 architecture. A standard /proc/interrupts looks similar to the following:
7//! > CPU0
8//! > 0: 80448940 XT-PIC timer
9//! > 1: 174412 XT-PIC keyboard
10//! > 2: 0 XT-PIC cascade
11//! > 8: 1 XT-PIC rtc
12//! > 10: 410964 XT-PIC eth0
13//! > 12: 60330 XT-PIC PS/2 Mouse
14//! > 14: 1314121 XT-PIC ide0
15//! > 15: 5195422 XT-PIC ide1
16//! > NMI: 0
17//! > ERR: 0
18//! > For a multi-processor machine, this file may look slightly different:
19//! > CPU0 CPU1
20//! > 0: 1366814704 0 XT-PIC timer
21//! > 1: 128 340 IO-APIC-edge keyboard
22//! > 2: 0 0 XT-PIC cascade
23//! > 8: 0 1 IO-APIC-edge rtc
24//! > 12: 5323 5793 IO-APIC-edge PS/2 Mouse
25//! > 13: 1 0 XT-PIC fpu
26//! > 16: 11184294 15940594 IO-APIC-level Intel EtherExpress Pro 10/100 Ethernet
27//! > 20: 8450043 11120093 IO-APIC-level megaraid
28//! > 30: 10432 10722 IO-APIC-level aic7xxx
29//! > 31: 23 22 IO-APIC-level aic7xxx
30//! > NMI: 0
31//! > ERR: 0
32//! > The first column refers to the IRQ number. Each CPU in the system has its own column and its own number of interrupts per IRQ. The next column reports the type of interrupt, and the last column contains the name of the device that is located at that IRQ.
33//! > Each of the types of interrupts seen in this file, which are architecture-specific, mean something different. For x86 machines, the following values are common:
34//! > XT-PIC — This is the old AT computer interrupts.
35//! > IO-APIC-edge — The voltage signal on this interrupt transitions from low to high, creating an edge, where the interrupt occurs and is only signaled once. This kind of interrupt, as well as the IO-APIC-level interrupt, are only seen on systems with processors from the 586 family and higher.
36//! > IO-APIC-level — Generates interrupts when its voltage signal is high until the signal is low again.
37//! </pre>
38//!
39//! -- https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/5/html/deployment_guide/s1-proc-topfiles#s2-proc-interrupts
40//!
41//! > /proc/interrupts
42//! > This is used to record the number of interrupts per CPU per IO
43//! > device. Since Linux 2.6.24, for the i386 and x86-64 architec‐
44//! > tures, at least, this also includes interrupts internal to the
45//! > system (that is, not associated with a device as such), such
46//! > as NMI (nonmaskable interrupt), LOC (local timer interrupt),
47//! > and for SMP systems, TLB (TLB flush interrupt), RES
48//! > (rescheduling interrupt), CAL (remote function call inter‐
49//! > rupt), and possibly others. Very easy to read formatting,
50//! > done in ASCII.
51//!
52//! -- http://man7.org/linux/man-pages/man5/proc.5.html
53
54define_struct! {
55 pub struct InternalInterrupt {
56 name: String,
57 /// The length of Vector equals the CPU numbers.
58 /// The first element is for CPU0, second for CPU1, and so on.
59 counts: Vec<usize>,
60 details: String,
61 }
62}
63
64define_struct! {
65 pub struct DeviceInterrupt {
66 irq_number: usize,
67 /// The length of Vector equals the CPU numbers.
68 /// The first element is for CPU0, second for CPU1, and so on.
69 counts: Vec<usize>,
70 type_device: String,
71 }
72}
73
74define_struct! {
75 /// returned by [`interrupts()`](fn.interrupts.html)
76 pub struct Interrupts {
77 cpu_num: usize,
78 internals: Vec<InternalInterrupt>,
79 devices: Vec<DeviceInterrupt>,
80 }
81}
82
83use std::str::FromStr;
84
85impl FromStr for InternalInterrupt {
86 type Err = crate::ProcErr;
87
88 fn from_str(s: &str) -> Result<InternalInterrupt, crate::ProcErr> {
89 let columns: Vec<&str> = s.split_ascii_whitespace().collect();
90 if columns.len() < 2 {
91 return Err("require at least two clolums to parse an InternalInterrupt".into())
92 }
93 let name = columns[0].trim_end_matches(':').to_string();
94 let mut cpu_num = 0;
95 let mut counts = vec![];
96 for item in columns[1..].iter() {
97 if let Ok(n) = item.parse::<usize>() {
98 cpu_num += 1;
99 counts.push(n);
100 }else {
101 break;
102 }
103 }
104 if counts.is_empty() {
105 return Err("interrupt count not found".into());
106 }
107 let details = columns[1+cpu_num..].join(" ");
108 Ok(InternalInterrupt{
109 name, counts, details
110 })
111 }
112}
113
114impl FromStr for DeviceInterrupt {
115 type Err = crate::ProcErr;
116
117 fn from_str(s: &str) -> Result<DeviceInterrupt, Self::Err> {
118 let columns: Vec<&str> = s.split_ascii_whitespace().collect();
119 if columns.len() < 4{
120 return Err("require at least four clolums to parse an InternalInterrupt".into())
121 }
122 let irq_number = columns[0].trim_end_matches(':').parse::<usize>()?;
123 let mut cpu_num = 0;
124 let mut counts = vec![];
125 for item in columns[1..].iter() {
126 if let Ok(n) = item.parse::<usize>() {
127 cpu_num += 1;
128 counts.push(n);
129 }else {
130 break;
131 }
132 }
133 if counts.is_empty() {
134 return Err("interrupt count not found".into());
135 }
136 let type_device = columns[1+cpu_num..].join(" ");
137 Ok(DeviceInterrupt{
138 irq_number, counts, type_device
139 })
140 }
141}
142
143impl FromStr for Interrupts {
144 type Err = crate::ProcErr;
145
146 fn from_str(s: &str) -> Result<Interrupts, Self::Err> {
147 let lines: Vec<&str> = s.lines().collect();
148 if lines.is_empty() {
149 return Err("no enough lines to parse Interrupts".into());
150 }
151 let cpu_num = lines[0].split_ascii_whitespace().count();
152 let mut internals = vec![];
153 let mut devices = vec![];
154 let mut skip_lines = 1;
155 for line in lines.iter().skip(skip_lines) {
156 if let Ok(itnl) = line.parse::<InternalInterrupt>() {
157 internals.push(itnl);
158 skip_lines += 1;
159 }else {
160 break;
161 }
162 }
163 for line in lines.iter().skip(skip_lines) {
164 let dvs = line.parse::<DeviceInterrupt>()?;
165 devices.push(dvs);
166 }
167 Ok(Interrupts{
168 cpu_num, internals, devices
169 })
170 }
171}
172
173instance_impl! {
174 interrupts, "/proc/interrupts", Interrupts
175}
176
177#[cfg(test)]
178mod test {
179 use super::*;
180
181 #[test]
182 fn test_parse_internal1() {
183 let source = "MCP: 250 Machine check polls";
184 let correct = InternalInterrupt {
185 name: "MCP".to_string(),
186 counts: vec![250],
187 details: "Machine check polls".to_string()
188 };
189 assert_eq!(correct, source.parse::<InternalInterrupt>().unwrap())
190 }
191
192 #[test]
193 fn test_parse_internal2() {
194 let source = "MIS: 0";
195 let correct = InternalInterrupt {
196 name: "MIS".to_string(),
197 counts: vec![0],
198 details: "".to_string()
199 };
200 assert_eq!(correct, source.parse::<InternalInterrupt>().unwrap())
201 }
202
203 #[test]
204 fn test_parse_device() {
205 let source = "1: 9 IO-APIC 1-edge i8042";
206 let correct = DeviceInterrupt {
207 irq_number: 1,
208 counts: vec![9],
209 type_device: "IO-APIC 1-edge i8042".to_string()
210 };
211 assert_eq!(correct, source.parse::<DeviceInterrupt>().unwrap());
212 }
213}