In the last part we discussed evolution of the interrupt delivery process from the devices in the x86 system (PIC → APIC → MSI), general theory, and all the necessary terminology.
In this practical part we will look at how to roll back to the use of obsolete methods of interrupt delivery in Linux, and in particular we will look at Linux kernel boot options:
- pci=nomsi
- noapic
- nolapic
Also we will look at the order in which the OS looks for interrupt routing tables (ACPI/MPtable/$PIR) and what the impact is from the following boot options:
- pci=noacpi
- acpi=noirq
- acpi=off
You've probably used some combination of these options when one of the devices in your system hasn't worked correctly because of an interrupt problem. We'll go through these options and find out what they do and how they change the kernel '/proc/interrupts' interface output.
Boot without any extra options
In this article for our interrupt investigation we will be using custom board with the Intel Haswell i7 CPU with the LynxPoint-LP chipset which runs coreboot.
We will be getting information about interrupts in the Linux system through the command:
cat /proc/interrupts
Here is the output when the kernel was booted without any external options:
CPU0 CPU1 CPU2 CPU3
0: 15 0 0 0 IO-APIC-edge timer
1: 0 1 0 1 IO-APIC-edge i8042
8: 0 0 0 1 IO-APIC-edge rtc0
9: 0 0 0 0 IO-APIC-fasteoi acpi
12: 0 0 0 1 IO-APIC-edge
23: 16 247 7 10 IO-APIC-fasteoi ehci_hcd:usb1
56: 0 0 0 0 PCI-MSI-edge aerdrv,PCIe PME
57: 0 0 0 0 PCI-MSI-edge aerdrv,PCIe PME
58: 0 0 0 0 PCI-MSI-edge aerdrv,PCIe PME
59: 0 0 0 0 PCI-MSI-edge aerdrv,PCIe PME
60: 0 0 0 0 PCI-MSI-edge aerdrv,PCIe PME
61: 0 0 0 0 PCI-MSI-edge aerdrv,PCIe PME
62: 3118 1984 972 3454 PCI-MSI-edge ahci
63: 1 0 0 0 PCI-MSI-edge eth59
64: 2095 57 4 832 PCI-MSI-edge eth59-rx-0
65: 6 18 1 1309 PCI-MSI-edge eth59-rx-1
66: 13 512 2 1 PCI-MSI-edge eth59-rx-2
67: 10 61 232 2 PCI-MSI-edge eth59-rx-3
68: 169 0 0 0 PCI-MSI-edge eth59-tx-0
69: 14 14 4 205 PCI-MSI-edge eth59-tx-1
70: 11 491 3 0 PCI-MSI-edge eth59-tx-2
71: 20 19 134 50 PCI-MSI-edge eth59-tx-3
72: 0 0 0 0 PCI-MSI-edge eth58
73: 2 1 0 152 PCI-MSI-edge eth58-rx-0
74: 3 150 2 0 PCI-MSI-edge eth58-rx-1
75: 2 34 117 2 PCI-MSI-edge eth58-rx-2
76: 153 0 2 0 PCI-MSI-edge eth58-rx-3
77: 4 0 2 149 PCI-MSI-edge eth58-tx-0
78: 4 149 2 0 PCI-MSI-edge eth58-tx-1
79: 4 0 117 34 PCI-MSI-edge eth58-tx-2
80: 153 0 2 0 PCI-MSI-edge eth58-tx-3
81: 66 106 2 101 PCI-MSI-edge snd_hda_intel
82: 928 5657 262 224 PCI-MSI-edge i915
83: 545 56 32 15 PCI-MSI-edge snd_hda_intel
NMI: 0 0 0 0 Non-maskable interrupts
LOC: 4193 3644 3326 3499 Local timer interrupts
SPU: 0 0 0 0 Spurious interrupts
PMI: 0 0 0 0 Performance monitoring interrupts
IWI: 290 233 590 111 IRQ work interrupts
RTR: 3 0 0 0 APIC ICR read retries
RES: 1339 2163 2404 1946 Rescheduling interrupts
CAL: 607 537 475 559 Function call interrupts
TLB: 163 202 164 251 TLB shootdowns
TRM: 48 48 48 48 Thermal event interrupts
THR: 0 0 0 0 Threshold APIC interrupts
MCE: 0 0 0 0 Machine check exceptions
MCP: 3 3 3 3 Machine check polls
ERR: 0
MIS: 0
File '/proc/interrupts' is the procfs Linux interface to the interrupt subsystem, and it presents a table about the number of interrupts on every CPU core in the system in the following form:
- First column: interrupt number
- CPUx columns: interrupt counters for every CPU core in the system
- Next column: interrupt type:
- IO-APIC-edge — edge-triggered interrupt for the I/O APIC controller
- IO-APIC-fasteoi — level-triggered interrupt for the I/O APIC controller
- PCI-MSI-edge — MSI interrupt
- XT-PIC-XT-PIC — interrupt for the PIC controller (we will see it later)
- Last column: device (driver) associated with this interrupt
Everything here is like it is supposed to be in the modern system. For the devices and drivers which support MSI/MSI-X, this is the type of interrupt that they use. The rest of the interrupt routing is done through the APIC controller.
Simplistically, the interrupt routing schematics can be drawn like this: (red lines are active routing paths and black lines are unused routing paths)
A device that supports MSI/MSI-X interrupts should have that particular capability listed in its PCI configuration space.
As an example of that let's look at a little fragment of the lspci output for the devices that declare they use MSI/MSI-X. In our case it is a SATA controller (interrupt 'ahci'), two ethernet controllers (interrupts 'eth58*' and 'eth59*'), graphical controller ('i915'), and two HD Audio controllers ('snd_hda_intel').
lspci -v
00:02.0 VGA compatible controller: Intel Corporation Haswell-ULT Integrated Graphics Controller (rev 09) (prog-if 00 [VGA controller])
...
Capabilities: [90] MSI: Enable+ Count=1/1 Maskable- 64bit-
Capabilities: [d0] Power Management version 2
Capabilities: [a4] PCI Advanced Features
Kernel driver in use: i915
00:03.0 Audio device: Intel Corporation Haswell-ULT HD Audio Controller (rev 09
...
Capabilities: [60] MSI: Enable+ Count=1/1 Maskable- 64bit-
Capabilities: [70] Express Root Complex Integrated Endpoint, MSI 00
Kernel driver in use: snd_hda_intel
00:1b.0 Audio device: Intel Corporation 8 Series HD Audio Controller (rev 04)
...
Capabilities: [60] MSI: Enable+ Count=1/1 Maskable- 64bit+
Capabilities: [70] Express Root Complex Integrated Endpoint, MSI 00
Capabilities: [100] Virtual Channel
Kernel driver in use: snd_hda_intel
00:1f.2 SATA controller: Intel Corporation 8 Series SATA Controller 1 [AHCI mode] (rev 04) (prog-if 01 [AHCI 1.0])
...
Capabilities: [80] MSI: Enable+ Count=1/1 Maskable- 64bit-
Capabilities: [70] Power Management version 3
Capabilities: [a8] SATA HBA v1.0
Kernel driver in use: ahci
05:00.0 Ethernet controller: Intel Corporation I350 Gigabit Network Connection (rev 01)
...
Capabilities: [50] MSI: Enable- Count=1/1 Maskable+ 64bit+
Capabilities: [70] MSI-X: Enable+ Count=10 Masked-
Capabilities: [a0] Express Endpoint, MSI 00
Kernel driver in use: igb
05:00.1 Ethernet controller: Intel Corporation I350 Gigabit Network Connection (rev 01)
...
Capabilities: [50] MSI: Enable- Count=1/1 Maskable+ 64bit+
Capabilities: [70] MSI-X: Enable+ Count=10 Masked-
Capabilities: [a0] Express Endpoint, MSI 00
Kernel driver in use: igb
As we see, all of these devices either have a string «MSI: Enable+» or «MSI-X: Enable+».
Let's downgrade our system! For a start let's boot with the kernel option 'pci=nomsi'.
pci=nomsi
Because of this option MSI interrupts become IO-APIC/XT-PIC depending on the interrupt controller in use.
In this case the priority choice is still modern APIC controller, so the interrupt picture will be:
Output of /proc/interrupts:
CPU0 CPU1 CPU2 CPU3
0: 15 0 0 0 IO-APIC-edge timer
1: 0 1 0 1 IO-APIC-edge i8042
8: 0 0 1 0 IO-APIC-edge rtc0
9: 0 0 0 0 IO-APIC-fasteoi acpi
12: 0 0 0 1 IO-APIC-edge
16: 1314 5625 342 555 IO-APIC-fasteoi i915, snd_hda_intel, eth59
17: 5 0 1 34 IO-APIC-fasteoi eth58
21: 2882 2558 963 2088 IO-APIC-fasteoi ahci
22: 26 81 2 170 IO-APIC-fasteoi snd_hda_intel
23: 23 369 8 8 IO-APIC-fasteoi ehci_hcd:usb1
NMI: 0 0 0 0 Non-maskable interrupts
LOC: 3011 3331 2435 2617 Local timer interrupts
SPU: 0 0 0 0 Spurious interrupts
PMI: 0 0 0 0 Performance monitoring interrupts
IWI: 197 228 544 85 IRQ work interrupts
RTR: 3 0 0 0 APIC ICR read retries
RES: 1708 2349 1821 1569 Rescheduling interrupts
CAL: 520 554 509 555 Function call interrupts
TLB: 187 181 205 179 TLB shootdowns
TRM: 102 102 102 102 Thermal event interrupts
THR: 0 0 0 0 Threshold APIC interrupts
MCE: 0 0 0 0 Machine check exceptions
MCP: 2 2 2 2 Machine check polls
ERR: 0
MIS: 0
As expected, all MSI/MSI-X interrupts have disappeared. Instead of them devices now use interrupts of 'IO-APIC-fasteoi' type.
Let us draw our attention to the fact that earlier, before enabling this kernel boot option, each of the 'eth58' and 'eth59' had nine interrupts! But now each of them has only one interrupt. Recall that without the MSI, one function in the PCI device can have only one interrupt!
Here is a little info from the 'dmesg' command about the ethernet controllers' initialization:
— boot without the 'pci=nomsi' option:
igb: Intel(R) Gigabit Ethernet Network Driver - version 5.0.5-k
igb: Copyright (c) 2007-2013 Intel Corporation.
acpi:acpi_pci_irq_enable: igb 0000:05:00.0: PCI INT A -> GSI 16 (level, low) -> IRQ 16
igb 0000:05:00.0: irq 63 for MSI/MSI-X
igb 0000:05:00.0: irq 64 for MSI/MSI-X
igb 0000:05:00.0: irq 65 for MSI/MSI-X
igb 0000:05:00.0: irq 66 for MSI/MSI-X
igb 0000:05:00.0: irq 67 for MSI/MSI-X
igb 0000:05:00.0: irq 68 for MSI/MSI-X
igb 0000:05:00.0: irq 69 for MSI/MSI-X
igb 0000:05:00.0: irq 70 for MSI/MSI-X
igb 0000:05:00.0: irq 71 for MSI/MSI-X
igb 0000:05:00.0: irq 63 for MSI/MSI-X
igb 0000:05:00.0: irq 64 for MSI/MSI-X
igb 0000:05:00.0: irq 65 for MSI/MSI-X
igb 0000:05:00.0: irq 66 for MSI/MSI-X
igb 0000:05:00.0: irq 67 for MSI/MSI-X
igb 0000:05:00.0: irq 68 for MSI/MSI-X
igb 0000:05:00.0: irq 69 for MSI/MSI-X
igb 0000:05:00.0: irq 70 for MSI/MSI-X
igb 0000:05:00.0: irq 71 for MSI/MSI-X
igb 0000:05:00.0: added PHC on eth0
igb 0000:05:00.0: Intel(R) Gigabit Ethernet Network Connection
igb 0000:05:00.0: eth0: (PCIe:5.0Gb/s:Width x1) 00:15:d5:03:00:2a
igb 0000:05:00.0: eth0: PBA No: 106300-000
igb 0000:05:00.0: Using MSI-X interrupts. 4 rx queue(s), 4 tx queue(s)
acpi:acpi_pci_irq_enable: igb 0000:05:00.1: PCI INT B -> GSI 17 (level, low) -> IRQ 17
igb 0000:05:00.1: irq 72 for MSI/MSI-X
igb 0000:05:00.1: irq 73 for MSI/MSI-X
igb 0000:05:00.1: irq 74 for MSI/MSI-X
igb 0000:05:00.1: irq 75 for MSI/MSI-X
igb 0000:05:00.1: irq 76 for MSI/MSI-X
igb 0000:05:00.1: irq 77 for MSI/MSI-X
igb 0000:05:00.1: irq 78 for MSI/MSI-X
igb 0000:05:00.1: irq 79 for MSI/MSI-X
igb 0000:05:00.1: irq 80 for MSI/MSI-X
igb 0000:05:00.1: irq 72 for MSI/MSI-X
igb 0000:05:00.1: irq 73 for MSI/MSI-X
igb 0000:05:00.1: irq 74 for MSI/MSI-X
igb 0000:05:00.1: irq 75 for MSI/MSI-X
igb 0000:05:00.1: irq 76 for MSI/MSI-X
igb 0000:05:00.1: irq 77 for MSI/MSI-X
igb 0000:05:00.1: irq 78 for MSI/MSI-X
igb 0000:05:00.1: irq 79 for MSI/MSI-X
igb 0000:05:00.1: irq 80 for MSI/MSI-X
igb 0000:05:00.1: added PHC on eth1
igb 0000:05:00.1: Intel(R) Gigabit Ethernet Network Connection
igb 0000:05:00.1: eth1: (PCIe:5.0Gb/s:Width x1) 00:15:d5:03:00:2b
igb 0000:05:00.1: eth1: PBA No: 106300-000
igb 0000:05:00.1: Using MSI-X interrupts. 4 rx queue(s), 4 tx queue(s)
— boot with the 'pci=nomsi' option:
igb: Intel(R) Gigabit Ethernet Network Driver - version 5.0.5-k
igb: Copyright (c) 2007-2013 Intel Corporation.
acpi:acpi_pci_irq_enable: igb 0000:05:00.0: PCI INT A -> GSI 16 (level, low) -> IRQ 16
igb 0000:05:00.0: added PHC on eth0
igb 0000:05:00.0: Intel(R) Gigabit Ethernet Network Connection
igb 0000:05:00.0: eth0: (PCIe:5.0Gb/s:Width x1) 00:15:d5:03:00:2a
igb 0000:05:00.0: eth0: PBA No: 106300-000
igb 0000:05:00.0: Using legacy interrupts. 1 rx queue(s), 1 tx queue(s)
acpi:acpi_pci_irq_enable: igb 0000:05:00.1: PCI INT B -> GSI 17 (level, low) -> IRQ 17
igb 0000:05:00.1: added PHC on eth1
igb 0000:05:00.1: Intel(R) Gigabit Ethernet Network Connection
igb 0000:05:00.1: eth1: (PCIe:5.0Gb/s:Width x1) 00:15:d5:03:00:2b
igb 0000:05:00.1: eth1: PBA No: 106300-000
igb 0000:05:00.1: Using legacy interrupts. 1 rx queue(s), 1 tx queue(s)
Because of the decreased number of interrupts per device, enabling this option can lead to a significant performance limitation of the device driver, and that is not even counting that according to the Intel research 'Reducing Interrupt Latency Through the Use of Message Signaled Interrupts', MSI interrupts 3 times faster than the IO-APIC interrupts and 5 times faster than the PIC interrupts.
noapic
This option disables I/O APIC. MSI interrupts can still find their way to all of the CPUs, but the rest of interrupts from the devices can go only to CPU0, because PIC is only connected to CPU0. However, LAPIC is working and all other CPUs can still work and handle interrupts.
CPU0 CPU1 CPU2 CPU3
0: 5 0 0 0 XT-PIC-XT-PIC timer
1: 2 0 0 0 XT-PIC-XT-PIC i8042
2: 0 0 0 0 XT-PIC-XT-PIC cascade
8: 1 0 0 0 XT-PIC-XT-PIC rtc0
9: 0 0 0 0 XT-PIC-XT-PIC acpi
12: 172 0 0 0 XT-PIC-XT-PIC ehci_hcd:usb1
56: 0 0 0 0 PCI-MSI-edge aerdrv, PCIe PME
57: 0 0 0 0 PCI-MSI-edge aerdrv, PCIe PME
58: 0 0 0 0 PCI-MSI-edge aerdrv, PCIe PME
59: 0 0 0 0 PCI-MSI-edge aerdrv, PCIe PME
60: 0 0 0 0 PCI-MSI-edge aerdrv, PCIe PME
61: 0 0 0 0 PCI-MSI-edge aerdrv, PCIe PME
62: 2833 2989 1021 811 PCI-MSI-edge ahci
63: 0 1 0 0 PCI-MSI-edge eth59
64: 301 52 9 3 PCI-MSI-edge eth59-rx-0
65: 12 24 3 178 PCI-MSI-edge eth59-rx-1
66: 14 85 6 2 PCI-MSI-edge eth59-rx-2
67: 17 24 307 1 PCI-MSI-edge eth59-rx-3
68: 70 18 8 10 PCI-MSI-edge eth59-tx-0
69: 7 0 0 23 PCI-MSI-edge eth59-tx-1
70: 15 227 2 2 PCI-MSI-edge eth59-tx-2
71: 18 6 27 2 PCI-MSI-edge eth59-tx-3
72: 0 0 0 0 PCI-MSI-edge eth58
73: 1 0 0 27 PCI-MSI-edge eth58-rx-0
74: 1 22 0 5 PCI-MSI-edge eth58-rx-1
75: 1 0 22 5 PCI-MSI-edge eth58-rx-2
76: 23 0 0 5 PCI-MSI-edge eth58-rx-3
77: 1 0 0 27 PCI-MSI-edge eth58-tx-0
78: 1 22 0 5 PCI-MSI-edge eth58-tx-1
79: 1 0 22 5 PCI-MSI-edge eth58-tx-2
80: 23 0 0 5 PCI-MSI-edge eth58-tx-3
81: 187 17 70 7 PCI-MSI-edge snd_hda_intel
82: 698 1647 247 129 PCI-MSI-edge i915
83: 438 135 16 59 PCI-MSI-edge snd_hda_intel
NMI: 0 0 0 0 Non-maskable interrupts
LOC: 1975 2499 2245 1474 Local timer interrupts
SPU: 0 0 0 0 Spurious interrupts
PMI: 0 0 0 0 Performance monitoring interrupts
IWI: 132 67 429 91 IRQ work interrupts
RTR: 3 0 0 0 APIC ICR read retries
RES: 1697 2178 1903 1541 Rescheduling interrupts
CAL: 561 496 534 567 Function call interrupts
TLB: 229 254 170 137 TLB shootdowns
TRM: 78 78 78 78 Thermal event interrupts
THR: 0 0 0 0 Threshold APIC interrupts
MCE: 0 0 0 0 Machine check exceptions
MCP: 2 2 2 2 Machine check polls
ERR: 0
MIS: 0
As we see, all IO-APIC-* interrupts have turned into XT-PIC-XT-PIC, and all of these interrupts have been routed to CPU0 only. MSI interrupts on the other hand have remained unchanged and go to all of the CPUs.
nolapic
This kernel boot option disables LAPIC. MSI interrupts can't work without LAPIC, and I/O APIC can't work without LAPIC either. All of the device interrupts can only go to the PIC, and it works with the CPU0 only. And without LAPIC the rest of the CPUs besides CPU0 won't work.
Output of /proc/interrupts:
CPU0
0: 6416 XT-PIC-XT-PIC timer
1: 2 XT-PIC-XT-PIC i8042
2: 0 XT-PIC-XT-PIC cascade
3: 5067 XT-PIC-XT-PIC aerdrv, aerdrv, PCIe PME, PCIe PME, i915, snd_hda_intel, eth59
4: 32 XT-PIC-XT-PIC aerdrv, aerdrv, PCIe PME, PCIe PME, eth58
5: 0 XT-PIC-XT-PIC aerdrv, PCIe PME
6: 0 XT-PIC-XT-PIC aerdrv, PCIe PME
8: 1 XT-PIC-XT-PIC rtc0
9: 0 XT-PIC-XT-PIC acpi
11: 274 XT-PIC-XT-PIC snd_hda_intel
12: 202 XT-PIC-XT-PIC ehci_hcd:usb1
15: 7903 XT-PIC-XT-PIC ahci
NMI: 0 Non-maskable interrupts
LOC: 0 Local timer interrupts
SPU: 0 Spurious interrupts
PMI: 0 Performance monitoring interrupts
IWI: 0 IRQ work interrupts
RTR: 0 APIC ICR read retries
RES: 0 Rescheduling interrupts
CAL: 0 Function call interrupts
TLB: 0 TLB shootdowns
TRM: 0 Thermal event interrupts
THR: 0 Threshold APIC interrupts
MCE: 0 Machine check exceptions
MCP: 1 Machine check polls
ERR: 0
MIS: 0
Combinations of options:
Actually there is only one combination for the new variant of routing: «noapic pci=nomsi». In this case all interrupts from the devices only go to the CPU0 through the PIC controller. But the LAPIC system is still working, so all the other CPUs can work and handle interrupts.
You cannot combine any other options with «nolapic» since it makes I/O APIC and MSI unaccessible. Therefore, if you've ever added Linux kernel boot options like «noapic nolapic» (or the most common case «acpi=off noapic nolapic») it seems like you've written some extra letters.
Finally, here is the result of the options «noapic pci=nomsi» to our interrupt routing picture:
And the output of /proc/interrupts is:
CPU0 CPU1 CPU2 CPU3
0: 5 0 0 0 XT-PIC-XT-PIC timer
1: 2 0 0 0 XT-PIC-XT-PIC i8042
2: 0 0 0 0 XT-PIC-XT-PIC cascade
3: 5072 0 0 0 XT-PIC-XT-PIC i915, snd_hda_intel, eth59
4: 32 0 0 0 XT-PIC-XT-PIC eth58
8: 1 0 0 0 XT-PIC-XT-PIC rtc0
9: 0 0 0 0 XT-PIC-XT-PIC acpi
11: 281 0 0 0 XT-PIC-XT-PIC snd_hda_intel
12: 200 0 0 0 XT-PIC-XT-PIC ehci_hcd:usb1
15: 7930 0 0 0 XT-PIC-XT-PIC ahci
NMI: 0 0 0 0 Non-maskable interrupts
LOC: 2595 2387 2129 1697 Local timer interrupts
SPU: 0 0 0 0 Spurious interrupts
PMI: 0 0 0 0 Performance monitoring interrupts
IWI: 159 90 482 135 IRQ work interrupts
RTR: 3 0 0 0 APIC ICR read retries
RES: 1568 1666 1810 1833 Rescheduling interrupts
CAL: 431 556 549 558 Function call interrupts
TLB: 124 184 156 274 TLB shootdowns
TRM: 116 116 116 116 Thermal event interrupts
THR: 0 0 0 0 Threshold APIC interrupts
MCE: 0 0 0 0 Machine check exceptions
MCP: 2 2 2 2 Machine check polls
ERR: 0
MIS: 0
Interrupt routing tables and the options «acpi=noirq», «pci=noacpi», «acpi=off»
How does the operating system get information about the device interrupt routing? The BIOS prepares such info for the OS in the form of:
- ACPI tables (_PIC/_PRT functions)
- _MP_ table (MPtable)
- $PIR table
- Registers 0x3C/0x3D of the device's PCI configuration space
It is worth to note for the MSI interrupts declaration that the BIOS doesn't need to do anything extra (beside declaring the use of the LAPIC): all the aforementioned routing information is needed only for the APIC/PIC interrupt lines.
Tables in the list above are presented in the order of priority. Let's examine it in detail.
Let's assume the BIOS has presented all this data and we boot our OS without any extra boot options:
- OS finds ACPI tables.
- ОS executes ACPI function "_PIC", passing it the argument stating that the boot should happen in APIC mode. Here there is function code that usually saves the chosen mode in a variable (for example, PICM=1).
- To access interrupt routing info the OS calls ACPI function "_PRT". This checks the PICM variable and returns routing for the APIC mode case.
In the case when we boot with the option noapic:
- OS finds ACPI tables
- ОS executes ACPI function "_PIC", passing it the argument stating that the boot should happen in PIC mode. Here there is function code that usually saves the chosen mode in a variable (for example, PICM=0)
- To access interrupt routing info the OS calls ACPI function "_PRT". This checks the PICM variable and returns routing for the PIC mode case.
If ACPI tables aren't present or interrupt routing with ACPI is disabled through the option acpi=noirq or pci=noacpi (or ACPI subsystem is completely disabled with the acpi=off option), then the OS looks for the MPtable (_MP_) to get all the interrupt routing information:
- OS can't find/doesn't look at the ACPI tables
- OS finds MPtable (_MP_)
If ACPI tables aren't present or interrupt routing with ACPI is disabled through the option acpi=noirq or pci=noacpi (or ACPI subsystem is completely disabled with the acpi=off option), and if the MPtable (_MP_) is not present either (or there is a boot option noapic or nolapic):
- OS can't find/doesn't look at the ACPI tables
- OS can't find/doesn't look at the MPtable (_MP_)
- OS finds $PIR table
If there is no $PIR table or it is not full, then the OS will look at the registers 0x3C/0x3D of the device's PCI configuration space to guess interrupt routing.
Here is a picture summarizing all of this:
One should remember that not every BIOS provides all of these three tables (ACPI/MPtable/$PIR), so if you've passed an option to your bootloader (e.g. GRUB) that disables the use of ACPI or ACPI and MPtable for the interrupt routing, it is possible that your system won't boot.
Note 1: In the case when we try to boot in APIC mode with the option 'acpi=noirq' and without MPtable present, the picture of interrupts will be like in the case of normal booting with only the 'noapic' option. The operating system will go to PIC mode by itself. In the case when you try to boot without any ACPI tables at all ('acpi=off') and without MPtable present, then the picture will be like this:
CPU0
0: 6 XT-PIC-XT-PIC timer
1: 2 XT-PIC-XT-PIC i8042
2: 0 XT-PIC-XT-PIC cascade
8: 0 XT-PIC-XT-PIC rtc0
12: 373 XT-PIC-XT-PIC ehci_hcd:usb1
16: 0 PCI-MSI-edge PCIe PME
17: 0 PCI-MSI-edge PCIe PME
18: 0 PCI-MSI-edge PCIe PME
19: 0 PCI-MSI-edge PCIe PME
20: 0 PCI-MSI-edge PCIe PME
21: 0 PCI-MSI-edge PCIe PME
22: 8728 PCI-MSI-edge ahci
23: 1 PCI-MSI-edge eth59
24: 1301 PCI-MSI-edge eth59-rx-0
25: 113 PCI-MSI-edge eth59-tx-0
26: 0 PCI-MSI-edge eth58
27: 45 PCI-MSI-edge eth58-rx-0
28: 45 PCI-MSI-edge eth58-tx-0
29: 1280 PCI-MSI-edge snd_hda_intel
NMI: 2 Non-maskable interrupts
LOC: 24076 Local timer interrupts
SPU: 0 Spurious interrupts
PMI: 2 Performance monitoring interrupts
IWI: 2856 IRQ work interrupts
RTR: 0 APIC ICR read retries
RES: 0 Rescheduling interrupts
CAL: 0 Function call interrupts
TLB: 0 TLB shootdowns
TRM: 34 Thermal event interrupts
THR: 0 Threshold APIC interrupts
MCE: 0 Machine check exceptions
MCP: 2 Machine check polls
ERR: 0
MIS: 0
This happens because without the ACPI MADT table (Multiple APIC Description Table) and the necessary info from the MPtable, the operating system doesn't know APIC identifiers (APIC IDs) for the other CPUs and can't work with them. But the LAPIC of the main CPU0 works because we haven't disabled it, and MSI interrupts can still go to it. So the interrupt picture would be:
Note 2: In general, interrupt routing with the use of ACPI in an APIC case should match the interrupt routing with the MPtable. Also, the interrupt routing with the use of ACPI in a PIC case should match the interrupt routing with the $PIR table. Therefore the '/proc/interrupts' output should not differ. But in my investigation I've noticed one strange fact. For some reason in the case of interrupt routing through the MPtable there is a cascade interrupt «XT-PIC-XT-PIC cascade» in the output:
CPU0 CPU1 CPU2 CPU3
0: 15 0 0 0 IO-APIC-edge timer
1: 2 0 0 0 IO-APIC-edge i8042
2: 0 0 0 0 XT-PIC-XT-PIC cascade
8: 0 1 0 0 IO-APIC-edge rtc0
9: 0 0 0 0 IO-APIC-edge acpi
...
It is a little bit strange that it happens like that, but it seems like the kernel source documentation says that it is OK.
Сonclusion
In conclusion we review for one more time the discussed options.
Interrupt controller choice options:
- pci=nomsi — MSI interrupts become IO-APIC/XT-PIC depending on the interrupt controller in use.
- noapic — Disables I/O APIC. MSI interrupts can still go to all the other CPUs, the rest of the device interrupts can only go to the PIC, and it works with the CPU0 only. But LAPIC still works and other CPUs can work and handle interrupts.
- noapic pci=nomsi — All of the device interrupts can only go to the PIC, and it works with the CPU0 only. But LAPIC works and other CPUs can work and handle interrupts.
- nolapic — Disables LAPIC. MSI interrupts can't work without LAPIC, and I/O APIC can't work without LAPIC. All of the device interrupts can only go to the PIC, and it works with the CPU0 only. And without LAPIC the rest of the CPUs besides CPU0 won't work.
Interrupt tables priority options:
- no options — routing through the APIC with the help of ACPI tables
- noapic — routing through the PIC with the help of ACPI tables
- acpi=noirq (pci=noacpi/acpi=off) — routing through the APIC with the help of MPtable
- acpi=noirq (pci=noacpi/acpi=off) noapic (nolapic) — routing through the PIC with the help of $PIR
In the next part we will look at how coreboot configures the chipset for the interrupt routing.
Acknowledgments
Special thanks to Jacob Garber from the coreboot community for helping me with this article translation