Search code examples
linuxlinux-device-driverinterruptdmapci

How does MSI interrupt work in linux driver?


Currently working on a PCI device driver. And the device is programmed like this:
When a DMA transmission is done, the device send a MSI interrupt to PC with MSI data "001" binary.

Now I'm writing a driver for this pci device, and for the MSI part, I have some questions.

In wikipedia, it says:

MSI allows the device to write a small amount of interrupt-describing data to a special memory-mapped I/O address, and the chipset then delivers the corresponding interrupt to a processor.

Q1: So in my case, the small amount of interrupt-describing data is the "001" sent from pci device to PC?

In my driver code, the MSI irq is registered like this:

err = pci_enable_msi(my_pci_dev);  
err = request_irq(my_pci_dev->irq, irq_handler, 0, "PCI_FPGA_CARD", NULL);  

and the irq_handler is defined like this:

static irqreturn_t irq_handler(int irq, void *dev_id)  
{  
  printk(KERN_INFO "(irq_handler):  Called\n");  
  return IRQ_HANDLED;
}  

Q2: With the 3 kernel functions above, how can we get the message "001"?
Q3: The PCI device support up to 8 MSI vectors, so to use all those 8 vectors, which code should I use below or neither is correct:

err = pci_enable_msi_block(my_pci_dev,8);
err = request_irq(my_pci_dev->irq, irq_handler, 0, "PCI_FPGA_CARD", NULL);

or

err = pci_enable_msi(my_pci_dev);
err = request_irq(my_pci_dev->irq, irq_handler_0, 0, "PCI_FPGA_CARD", NULL);
err = request_irq(my_pci_dev->irq, irq_handler_1, 0, "PCI_FPGA_CARD", NULL);
err = request_irq(my_pci_dev->irq, irq_handler_2, 0, "PCI_FPGA_CARD", NULL);
err = request_irq(my_pci_dev->irq, irq_handler_3, 0, "PCI_FPGA_CARD", NULL);
err = request_irq(my_pci_dev->irq, irq_handler_4, 0, "PCI_FPGA_CARD", NULL);
err = request_irq(my_pci_dev->irq, irq_handler_5, 0, "PCI_FPGA_CARD", NULL);
err = request_irq(my_pci_dev->irq, irq_handler_6, 0, "PCI_FPGA_CARD", NULL);
err = request_irq(my_pci_dev->irq, irq_handler_7, 0, "PCI_FPGA_CARD", NULL);

3 questions ~~ thx for your help.


Solution

  • Late answer. Hope it still helps.

    A1: Yes. MSI is a posted memory write from the device to CPU. The TLP targets the MSI address allocated by the CPU and the payload is the MSI DATA, which is the "001" in this case. The address and data (offset) combined define a unique interrupt vector. So with different "data", you can have multiple interrupts and handlers.

    A2: Usually you don't need it. Below from Wikipedia.

    A common misconception with MSI is that it allows the device to send data to a processor as part of the interrupt. The data that is sent as part of the memory write transaction is used by the chipset to determine which interrupt to trigger on which processor; that data is not available for the device to communicate additional information to the interrupt handler.

    If you are still curious, check the MSI DATA register of MSI capability. It may contain the "001", but I haven't verified. This "001" should not be relevant to the ISR anyways.

    A3: You should register multiple handlers. With MSI, you have a consecutive vectors, and MSI-X gives a table of individual addresses and data for each interrupt vector.

    For MSI:

    request_irq(my_pci_dev->irq, irq_handler_0, ...);
    request_irq(my_pci_dev->irq + 1, irq_handler_1, ...);
    request_irq(my_pci_dev->irq + 2, irq_handler_2, ...);
    

    For MSI-X:

    request_irq(my_pci_dev->pMsixEntries[0].vector, irq_handler_0, ...);
    request_irq(my_pci_dev->pMsixEntries[1].vector, irq_handler_1, ...);
    request_irq(my_pci_dev->pMsixEntries[2].vector, irq_handler_2, ...);