PCI_MSI(9)
π PCI_MSI(9) π
okay bestie, so this is a page from the Kernel Developer's Manual β yes, the actual manual, not some random discord server. it's all about PCI MSI and MSI-X manipulation functions. buckle up, we're getting into interrupt vibes. π
β¨ NAME β¨
pci_msi, pci_msix, pci_msi_count, pci_msi_alloc, pci_msi_alloc_exact, pci_msix_count, pci_msix_alloc, pci_msix_alloc_exact, pci_msix_alloc_map, pci_intx_alloc, pci_intr_alloc, pci_intr_release, pci_intr_type β these are the PCI MSI{,-X} manipulation functions, and they're literally the main characters of this page. slay. π
πΈ SYNOPSIS πΈ
int
pci_msi_count(pci_chipset_tag_t pc, pcitag_t tag);
int
pci_msi_alloc(const struct pci_attach_args *pa,
pci_intr_handle_t **ihps, int *count);
int
pci_msi_alloc_exact(const struct pci_attach_args *pa,
pci_intr_handle_t **ihps, int count);
int
pci_msix_count(pci_chipset_tag_t pc, pcitag_t tag);
int
pci_msix_alloc(const struct pci_attach_args *pa,
pci_intr_handle_t **ihps, int *count);
int
pci_msix_alloc_exact(const struct pci_attach_args *pa,
pci_intr_handle_t **ihps, int count);
int
pci_msix_alloc_map(const struct pci_attach_args *pa,
pci_intr_handle_t **ihps, u_int *table_indexes, int count);
int
pci_intx_alloc(const struct pci_attach_args *pa,
pci_intr_handle_t **ihp);
int
pci_intr_alloc(const struct pci_attach_args *pa,
pci_intr_handle_t **ihp, int *counts, pci_intr_type_t max_type);
void
pci_intr_release(pci_chipset_tag_t pc, pci_intr_handle_t *pih,
int count);
pci_intr_type_t
pci_intr_type(pci_chipset_tag_t pc, pci_intr_handle_t ih);
π DESCRIPTION π
so basically, the pci_msi functions exist so that device drivers can use MSI/MSI-X. that's the whole point. when the system uses MSI/MSI-X, it must define the __HAVE_PCI_MSI_MSIX build option. no cap, that's required. π₯
every driver has an attach() function with a bus-specific attach_args structure. each driver for a PCI device gets a pointer to an object of type struct pci_attach_args, which contains β among other things β info about where the device sits in the PCI bus topology, enough to let interrupts from the device be handled. it's giving "I know where you live" energy but for hardware. π¦
pci_msi_count() returns the max number of the device's MSI. if the device can't use MSI, pci_msi_count() returns zero. pci_msix_count() works the same way for MSI-X. bestie, it's literally the same vibe, different interrupt flavor. β¨
if a driver wants to establish an MSI handler for the device, it should pass the struct pci_attach_args * and count to pci_msi_alloc() or pci_msi_alloc_exact(). these return zero on success and nonzero on failure. when they're successful, they return the pointer to the allocated handle array in pihs whose size is count or less. the difference between pci_msi_alloc() and pci_msi_alloc_exact() is whether count can be decremented or not. pci_msi_alloc() can decrement count, and it's similar to FreeBSD's pci_alloc_msi(). in contrast, pci_msi_alloc_exact() cannot decrement count. that's the tea. β
if the driver wants to refer to the MSI source in an attach or error message, it should use the value returned by pci_intr_string() the same as INTx. the buffer passed to pci_intr_string() should be at least PCI_INTRSTR_LEN bytes long. don't skimp on the buffer, bestie, it's not cute. π
subsequently, when the driver is prepared to receive MSIs, it should call pci_intr_establish() the same as INTx to actually establish the handler. when the device interrupts, intrhand will be called with a single argument intrarg, and it will run at the interrupt priority level ipl. iconic behavior, honestly. π
the return value of pci_intr_establish() may be saved and passed to pci_intr_disestablish() to disable the interrupt handler the same as INTx when the driver is no longer interested in MSIs from the device. after that, the driver should also call pci_intr_release() to free resources about MSI as well as INTx and MSI-X. if pih is NULL, pci_intr_release() does nothing. it's literally the "no thoughts, head empty" of functions. π
if a driver wants to establish an MSI-X handler for the device, it's almost the same as MSI. the only difference is pci_msix_alloc_map(). this function can assign separate handlers for each MSI-X table entry. i.e., if the driver wants to assign the handlers in the following way:
msix_handler0 => MSI-X table index: 4
msix_handler1 => MSI-X table index: 5
msix_handler2 => MSI-X table index: 0
the driver should set table_indexes this way:
table_indexes[0] = 4;
table_indexes[1] = 5;
table_indexes[2] = 0;
if the driver wants to fall back to INTx, the driver should use pci_intx_alloc() and pci_intr_release() instead of pci_intr_map() to resolve the contradiction of interrupt handler ownership. i.e., pci_intr_map() does not have the ownership (the function just calculates value), in contrast, pci_msi_alloc() and pci_msix_alloc() have it (the functions allocate memory for interrupt handlers). ownership is real, bestie. don't come for what's not yours. π«Ά
pci_intr_alloc() is a wrapper function which selects and automatically falls back between allocation functions according to the argument counts. the elements of the counts array mean each required interrupt count for INTx, MSI, and MSI-X. the index count of counts must be PCI_INTR_TYPE_SIZE. max_type must be PCI_INTR_TYPE_MSIX, PCI_INTR_TYPE_MSI, or PCI_INTR_TYPE_INTX. the parameter does not mean array index counts of counts. the parameter means the interrupt type which pci_intr_alloc() tries to allocate first. i.e., if the driver wants to allocate interrupts in the following way:
5 MSI-X
1 MSI (if MSI-X allocation failed)
INTx (if MSI allocation failed either)
the driver should call pci_intr_alloc() in the following way:
int counts[PCI_INTR_TYPE_SIZE];
counts[PCI_INTR_TYPE_MSIX] = 5;
counts[PCI_INTR_TYPE_MSI] = 1;
counts[PCI_INTR_TYPE_INTX] = 1;
error = pci_intr_alloc(pa, ihps, counts,
PCI_INTR_TYPE_MSIX);
if the driver wants to allocate interrupts in the following way:
hardware max number MSI-X
1 MSI (if MSI-X allocation failed)
that is, the driver does not use INTx, the driver should call pci_intr_alloc() in the following way:
int counts[PCI_INTR_TYPE_SIZE];
counts[PCI_INTR_TYPE_MSIX] = -1; /* -1 means max */
counts[PCI_INTR_TYPE_MSI] = 1;
counts[PCI_INTR_TYPE_INTX] = 0; /* 0 means not use */
error = pci_intr_alloc(pa, ihps, counts,
PCI_INTR_TYPE_MSIX);
if the driver wants to allocate interrupts in the following way:
3 MSI
INTx (if MSI allocation failed)
that is, the driver does not use MSI-X, the driver should call pci_intr_alloc() in the following way:
int counts[PCI_INTR_TYPE_SIZE];
counts[PCI_INTR_TYPE_MSIX] = 0; /* 0 means not use */
counts[PCI_INTR_TYPE_MSI] = 3;
counts[PCI_INTR_TYPE_INTX] = 1;
error = pci_intr_alloc(pa, ihps, counts,
PCI_INTR_TYPE_MSI);
if the driver wants to allocate interrupts in the following way:
1 MSI-X
1 MSI
INTx (if MSI/MSI-X allocation failed)
that is, general usage, the driver should call simply pci_intr_alloc() in the following way:
error = pci_intr_alloc(pa, ihps, NULL, 0);
max_type is ignored in this case. pci_intr_alloc() returns zero on any allocation function success, and non-zero on all allocation function failures. on success, counts is overwritten by a really allocated count. i.e., if 5 MSI-X is allocated, counts is
counts[PCI_INTR_TYPE_MSIX] == 5
counts[PCI_INTR_TYPE_MSI] == 0
counts[PCI_INTR_TYPE_INTX] == 0
on return. the way that counts gets rewritten is giving "plot twist" but make it allocated. π₯
pci_intr_type() returns the interrupt type of ih. the return value is PCI_INTR_TYPE_MSIX for MSI-X, PCI_INTR_TYPE_MSI for MSI, and PCI_INTR_TYPE_INTX for others. that's it, that's the function. short and iconic. π
π SEE ALSO π
pci_intr(9) β go read that one too, bestie, it's the companion piece. iykyk. π
π¦ HISTORY π¦
pci_msi support first appeared in NetBSD 8.0. support is present on i386, amd64 and aarch64 architectures. so if you're on one of those, you're literally in the right place at the right time. periodt. β
π AUTHORS π
the pci_msi interfaces were designed and implemented by Kengo Nakahara β¨knakahara@NetBSD.orgβ©. we love a legend who ships interrupt code. π«Ά
this cutie of a page came from Debian, dated January 12, 2021, and it's the PCI_MSI(9) manual page. stay slay, sysadmins! πβ¨
π¦ where this page comes from
- nixpkgs unstable: netbsd-source 11.0
- Copyright (c) 2015 Internet Initiative Japan Inc
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms