Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kernel: Add MSIx interrupt mechanism for PCI devices #18580

Merged
merged 14 commits into from
May 7, 2023

Conversation

Panky-codes
Copy link
Contributor

@Panky-codes Panky-codes commented Apr 29, 2023

This adds support to MSI(x) based interrupt mechanism, an alternative to pin-based interrupts.

The first 10 commits add the necessary infrastructure to use MSIx interrupt mechanism. The next three commits add MSIx support to the NVMe device. This PR only adds MSIx support. TODOs have been added for the old MSI-based interrupts which could be extended later.

Test:

$ SERENITY_NVME_ENABLE=1 Meta/serenity.sh run

Trace from QEMU for the NVMe device:

...
pci_nvme_io_cmd cid 54 nsid 0x1 sqid 1 opc 0x2 opname 'NVME_NVM_CMD_READ'
<snip>
pci_nvme_irq_msix raising MSI-X IRQ vector 1
pci_nvme_mmio_write addr 0x100c data 0x36 size 4
pci_nvme_mmio_doorbell_cq cqid 1 new_head 54
...

@github-actions github-actions bot added the 👀 pr-needs-review PR needs review from a maintainer or community member label Apr 29, 2023
Copy link
Collaborator

@kleinesfilmroellchen kleinesfilmroellchen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly style comments, exciting changes!

Kernel/Bus/PCI/Device.cpp Outdated Show resolved Hide resolved
Kernel/Arch/PCIMSI.h Outdated Show resolved Hide resolved
Kernel/Arch/x86_64/PCI/MSI.h Outdated Show resolved Hide resolved
Kernel/Bus/PCI/Device.cpp Outdated Show resolved Hide resolved
Kernel/Bus/PCI/Device.h Outdated Show resolved Hide resolved
Kernel/Bus/PCI/Device.h Outdated Show resolved Hide resolved
Kernel/Bus/PCI/Device.h Outdated Show resolved Hide resolved
Kernel/Bus/PCI/Device.h Outdated Show resolved Hide resolved
Kernel/Interrupts/PCIIRQHandler.cpp Outdated Show resolved Hide resolved
Kernel/Storage/NVMe/NVMeQueue.h Outdated Show resolved Hide resolved
@supercomputer7
Copy link
Member

I will look into this today or tomorrow.
Regardless, adding MSI-x support is a good thing, thank you for doing this!

Copy link
Member

@supercomputer7 supercomputer7 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Besides my comment on that explicit keyword, this looks wonderful!
I was not sure about the whole PCIIRQHandler thing, but it does seem correct to me, so let's try this out and if we happen to have something better we can always fix this :)

@Panky-codes Panky-codes force-pushed the msix-v3 branch 2 times, most recently from f9a53f6 to f65d4a6 Compare May 1, 2023 08:58
Copy link
Collaborator

@kleinesfilmroellchen kleinesfilmroellchen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just two more things

Kernel/Bus/PCI/Device.cpp Outdated Show resolved Hide resolved
Kernel/Bus/PCI/Device.cpp Outdated Show resolved Hide resolved
@Panky-codes Panky-codes force-pushed the msix-v3 branch 2 times, most recently from c049cc3 to f9a1cb3 Compare May 4, 2023 10:40
Kernel/Interrupts/GenericInterruptHandler.h Outdated Show resolved Hide resolved
Kernel/Interrupts/GenericInterruptHandler.h Show resolved Hide resolved
Kernel/Arch/x86_64/PCI/Initializer.cpp Show resolved Hide resolved
Kernel/Arch/x86_64/PCI/Initializer.cpp Outdated Show resolved Hide resolved
Kernel/Arch/Interrupts.h Show resolved Hide resolved
Kernel/Bus/PCI/Device.h Outdated Show resolved Hide resolved
Kernel/Interrupts/PCIIRQHandler.cpp Outdated Show resolved Hide resolved
Kernel/Interrupts/PCIIRQHandler.cpp Outdated Show resolved Hide resolved
Kernel/Storage/NVMe/NVMeController.cpp Show resolved Hide resolved
Kernel/Storage/NVMe/NVMeController.cpp Show resolved Hide resolved
Kernel/Interrupts/GenericInterruptHandler.h Outdated Show resolved Hide resolved
Kernel/Interrupts/PCIIRQHandler.cpp Outdated Show resolved Hide resolved
Kernel/Arch/x86_64/Interrupts.cpp Outdated Show resolved Hide resolved
Kernel/Arch/aarch64/Interrupts.cpp Show resolved Hide resolved
Kernel/Arch/PCIMSI.h Show resolved Hide resolved
Kernel/Storage/NVMe/NVMeInterruptQueue.cpp Show resolved Hide resolved
Pin-based PCI device are allocated an IRQ, and it could be shared with
multiple devices. An interrupt handler with an IRQ for a PCI device
will get registered only during the driver initialization.

For MSI(x) interrupts, the driver has to allocate IRQs and this field
can be used to skip IRQs that have already been reserved by pin-based
interrupts so that we don't have to share IRQs, which generally will
reduce the performance.
Set pin-based interrupt handler as reserved during PCI bus init.
This is required so that MSI(x) based interrupts can avoid sharing the
IRQ which has been marked as reserved.
MSI(x) interrupts need to reserve IRQs so that it can be programmed by
the device. Add an API to reserve contiguous ranges of interrupt
handlers so that it can used by PCI devices that use MSI(x) mechanism.

This API needs to be implemented by aarch64 architecture.
MSI(x) mechanism requires the device to write to its Capability
structure. Add write{8,16,32} similar to read{8,16,32}.
Add a struct named MSIxInfo that stores all the relevant MSIx
information as a part of PCI DeviceIdentifier struct.

Populate the MSIx struct during the PCI device init. As the
DeviceIdentifier struct need to populate MSIx info, don't mark
DeviceIdentifier as const in the PCI::Device class.
Instead of iterating through the capabilities, use the
is_msix_capable() API from the PCIIdentifier class that belongs to the
device.
Implement enabling and disabling MSIx interrupts for a PCI device.

Removes two TODO()s from PCI::Device.cpp :^)
MSIx table entry is used to program interrupt vectors and it is
architecture specific. Add helper functions declaration in
Arch/PCIMSI.h. The definition of the function is placed in the
respective arch specific code.
Add reserve_irqs, allocate_irq, enable_interrupt and disable_interrupt
API to a PCI device.

reserve_irqs() can be used by a device driver that would like to
reserve irqs for MSI(x) interrupts. The API returns the type of IRQ
that was reserved by the PCI device. If the PCI device does not support
MSI(x), then it is a noop.

allocate_irq() API can be used to allocate an IRQ at an index. For
MSIx the driver needs to map the vector table into the memory and add
the corresponding IRQ at the given index. This API will return the
actual IRQ that was used so that the driver can use it create interrupt
handler for that IRQ.

{enable, disable}_interrupt API is used to enable or disable a
particular IRQ at the given index. It is a noop for pin-based
interrupts. This could be used by IRQHandler to enable or disable an
interrupt.
@gmta
Copy link
Member

gmta commented May 7, 2023

@Panky-codes result of running lsirq after SERENITY_NVME_ENABLE=1 Meta/serenity.sh run:

image

[lsirq(45:45)]: ASSERTION FAILED: !PtrTraits::is_null(bits)
[lsirq(45:45)]: ././Kernel/Library/LockRefPtr.h:447 in T* AK::LockRefPtr<T, PtrTraits>::as_nonnull_ptr(FlatPtr) const [with T = Kernel::IRQController; PtrTraits = AK::LockRefPtrTraits<Kernel::IRQController>; FlatPtr = long unsigned int]
[lsirq(45:45)]: KERNEL PANIC! :^(
[lsirq(45:45)]: Aborted
[lsirq(45:45)]: at ./Kernel/Arch/x86_64/CPU.cpp:35 in void abort()
[lsirq(45:45)]: Kernel + 0x00000000016e462f  Kernel::__panic(char const*, unsigned int, char const*) +0x16f
[lsirq(45:45)]: Kernel + 0x00000000016e5b41  abort.localalias +0x35a
[lsirq(45:45)]: Kernel + 0x00000000016e57e7  abort.localalias +0x0
[lsirq(45:45)]: Kernel + 0x00000000005c5206  Kernel::PCIIRQHandler::controller() const +0x166
[lsirq(45:45)]: Kernel + 0x0000000000b0e357  Kernel::SysFSInterrupts::try_generate(Kernel::KBufferBuilder&)::{lambda(Kernel::GenericInterruptHandler&)#1}::operator()(Kernel::GenericInterruptHandler&) const::{lambda()#1}::operator()() const +0x587
[lsirq(45:45)]: Kernel + 0x0000000000b1182d  AK::Function<void (Kernel::GenericInterruptHandler&)>::CallableWrapper<Kernel::SysFSInterrupts::try_generate(Kernel::KBufferBuilder&)::{lambda(Kernel::GenericInterruptHandler&)#1}>::call(Kernel::GenericInterruptHandler&) +0x12d
[lsirq(45:45)]: Kernel + 0x00000000016fa61d  Kernel::InterruptManagement::enumerate_interrupt_handlers(AK::Function<void (Kernel::GenericInterruptHandler&)>) +0x15d
[lsirq(45:45)]: Kernel + 0x0000000000b0c81a  Kernel::SysFSInterrupts::try_generate(Kernel::KBufferBuilder&) [clone .localalias] +0x21a
[lsirq(45:45)]: Kernel + 0x0000000000ba4119  Kernel::SysFSGlobalInformation::refresh_data(Kernel::OpenFileDescription&) const [clone .localalias] +0x689
[lsirq(45:45)]: Kernel + 0x0000000000a39d93  Kernel::SysFSInode::attach(Kernel::OpenFileDescription&) [clone .localalias] +0x283
[lsirq(45:45)]: Kernel + 0x00000000008ca620  Kernel::OpenFileDescription::attach() [clone .localalias] +0x2e0
[lsirq(45:45)]: Kernel + 0x00000000008dd480  Kernel::OpenFileDescription::try_create(Kernel::Custody&) +0x7f0
[lsirq(45:45)]: Kernel + 0x0000000000ca5f63  Kernel::VirtualFileSystem::open(Kernel::Process const&, Kernel::Credentials const&, AK::StringView, int, unsigned short, Kernel::Custody&, AK::Optional<Kernel::UidAndGid>) [clone .localalias] +0x633
[lsirq(45:45)]: Kernel + 0x0000000000caa3f3  Kernel::VirtualFileSystem::open(Kernel::Credentials const&, AK::StringView, int, unsigned short, Kernel::Custody&, AK::Optional<Kernel::UidAndGid>) +0x173
[lsirq(45:45)]: Kernel + 0x000000000133d18b  Kernel::Process::sys$open(AK::Userspace<Kernel::Syscall::SC_open_params const*>) +0x104b
[lsirq(45:45)]: Kernel + 0x000000000118c37f  Kernel::Syscall::handle(Kernel::RegisterState&, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long) [clone .localalias] +0x93f
[lsirq(45:45)]: Kernel + 0x000000000118e4e2  syscall_handler +0x902
[lsirq(45:45)]: Kernel + 0x0000000001749a81  syscall_entry +0x51

@gmta gmta added ⏳ pr-waiting-for-author PR is blocked by feedback / code changes from the author and removed 👀 pr-needs-review PR needs review from a maintainer or community member labels May 7, 2023
PCIIRQHandler is a generic IRQ handler that the device driver can
inherit to use either Pin or MSI(x) based interrupt mechanism.

The PCIIRQHandler can do what the existing IRQHandler can do for pin
based interrupts but also deal with MSI based interrupts. We can
hopefully convert all the PCI based devices to use this handler so that
MSI(x) can be used.
Add an explicit QueueType enum which could be used to create a poll or
an interrupt queue. This is better than passing an Optional<irq>.

This refactoring is in preparation for adding MSIx support to NVMe.
This is in preparation for adding MSI(x) support to the NVMe device.
NVMeInterruptQueue needs access to the PCI device to deal with MSI(x)
interrupts. It is ok to pass the NVMeController as a reference to the
NVMeQueue as NVMeController is the one that owns the NVMeQueue.

This is very similar to how AHCIController passes its reference to its
interrupt handler.
Add MSIx support to NVMe. Prefer MSIx over pin-based interrupts as they
are more efficient and all modern hardware support them.
Now we support MSIx for NVMe. Retain the information about using
nvme_poll until MSIx is tested on a Bare metal system.
@github-actions github-actions bot added 👀 pr-needs-review PR needs review from a maintainer or community member and removed ⏳ pr-waiting-for-author PR is blocked by feedback / code changes from the author labels May 7, 2023
@Panky-codes
Copy link
Contributor Author

Panky-codes commented May 7, 2023

Thanks for the report @gmta . I missed one place to check for null ptr in PCIIRQHandler. Updated the code to return PCI-MSI similar to what Linux does.

image

@gmta
Copy link
Member

gmta commented May 7, 2023

Thanks @Panky-codes, that fixed it!

@gmta gmta added ✅ pr-maintainer-approved-but-awaiting-ci PR has been approved by a maintainer and can be merged after CI has passed and removed 👀 pr-needs-review PR needs review from a maintainer or community member labels May 7, 2023
@gmta gmta merged commit 53d47c6 into SerenityOS:master May 7, 2023
12 checks passed
@github-actions github-actions bot removed the ✅ pr-maintainer-approved-but-awaiting-ci PR has been approved by a maintainer and can be merged after CI has passed label May 7, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants