[haiku-commits] haiku: hrev45849 - src/add-ons/kernel/busses/virtio

  • From: korli@xxxxxxxxxxxxxxxx
  • To: haiku-commits@xxxxxxxxxxxxx
  • Date: Thu, 11 Jul 2013 20:42:20 +0200 (CEST)

hrev45849 adds 1 changeset to branch 'master'
old head: d058a4aed272a9fe79ef9e8443fad3bb5352e433
new head: 8dfd68e0f8d23a978549ad54b088a384d7214210
overview: http://cgit.haiku-os.org/haiku/log/?qt=range&q=8dfd68e+%5Ed058a4a

----------------------------------------------------------------------------

8dfd68e: Virtio PCI: added support for MSI-X interrupts
  
  * make use of MSI/MSI-X PCI x86 API
  * MSI support untested because QEmu only offers MSI-X
  * changed a bit the Virtio bus API by adding a queue count parameter
  for the setup_interrupt() hook.

                                   [ Jérôme Duval <jerome.duval@xxxxxxxxx> ]

----------------------------------------------------------------------------

Revision:    hrev45849
Commit:      8dfd68e0f8d23a978549ad54b088a384d7214210
URL:         http://cgit.haiku-os.org/haiku/commit/?id=8dfd68e
Author:      Jérôme Duval <jerome.duval@xxxxxxxxx>
Date:        Thu Jul 11 18:28:21 2013 UTC

----------------------------------------------------------------------------

4 files changed, 217 insertions(+), 27 deletions(-)
headers/private/virtio/virtio.h                  |   2 +-
.../kernel/bus_managers/virtio/VirtioDevice.cpp  |   2 +-
src/add-ons/kernel/busses/virtio/virtio_pci.cpp  | 236 +++++++++++++++++--
src/add-ons/kernel/busses/virtio/virtio_pci.h    |   4 +-

----------------------------------------------------------------------------

diff --git a/headers/private/virtio/virtio.h b/headers/private/virtio/virtio.h
index 3b2b299..94642c9 100644
--- a/headers/private/virtio/virtio.h
+++ b/headers/private/virtio/virtio.h
@@ -84,7 +84,7 @@ typedef struct {
        
        uint16  (*get_queue_ring_size)(void* cookie, uint16 queue);
        status_t (*setup_queue)(void* cookie, uint16 queue, phys_addr_t phy);
-       status_t (*setup_interrupt)(void* cookie);
+       status_t (*setup_interrupt)(void* cookie, uint16 queueCount);
        void    (*notify_queue)(void* cookie, uint16 queue);
 } virtio_sim_interface;
 
diff --git a/src/add-ons/kernel/bus_managers/virtio/VirtioDevice.cpp 
b/src/add-ons/kernel/bus_managers/virtio/VirtioDevice.cpp
index 606c77e..03c514a 100644
--- a/src/add-ons/kernel/bus_managers/virtio/VirtioDevice.cpp
+++ b/src/add-ons/kernel/bus_managers/virtio/VirtioDevice.cpp
@@ -177,7 +177,7 @@ VirtioDevice::SetupInterrupt(virtio_intr_func configHandler,
 {
        fConfigHandler = configHandler;
        fConfigCookie = configCookie;
-       status_t status = fController->setup_interrupt(fCookie);
+       status_t status = fController->setup_interrupt(fCookie, fQueueCount);
        if (status != B_OK)
                return status;
 
diff --git a/src/add-ons/kernel/busses/virtio/virtio_pci.cpp 
b/src/add-ons/kernel/busses/virtio/virtio_pci.cpp
index 8d2ff0a..a9a7943 100644
--- a/src/add-ons/kernel/busses/virtio/virtio_pci.cpp
+++ b/src/add-ons/kernel/busses/virtio/virtio_pci.cpp
@@ -9,6 +9,7 @@
 #include <string.h>
 
 #include <bus/PCI.h>
+#include <PCI_x86.h>
 #include <virtio.h>
 
 #include "virtio_pci.h"
@@ -20,8 +21,9 @@
 #else
 #      define TRACE(x...) ;
 #endif
+#define TRACE_ALWAYS(x...)     dprintf("\33[33mvirtio_pci:\33[0m " x)
 #define ERROR(x...)                    dprintf("\33[33mvirtio_pci:\33[0m " x)
-#define CALLED()                       TRACE("CALLED %s\n", 
__PRETTY_FUNCTION__)
+#define CALLED(x...)           TRACE("CALLED %s\n", __PRETTY_FUNCTION__)
 
 
 #define VIRTIO_PCI_DEVICE_MODULE_NAME "busses/virtio/virtio_pci/driver_v1"
@@ -29,21 +31,37 @@
 
 #define VIRTIO_PCI_CONTROLLER_TYPE_NAME "virtio pci controller"
 
+typedef enum {
+       VIRTIO_IRQ_LEGACY,
+       VIRTIO_IRQ_MSI,
+       VIRTIO_IRQ_MSI_X_SHARED,
+       VIRTIO_IRQ_MSI_X,
+} virtio_irq_type;
+
+typedef struct {
+       virtio_sim sim;
+       uint16 queue;
+} virtio_pci_queue_cookie;
 
 typedef struct {
        pci_device_module_info* pci;
        pci_device* device;
-       uint16 config_base;
        addr_t base_addr;
        uint8 irq;
+       virtio_irq_type irq_type;
        virtio_sim sim;
+       uint16 queue_count;
 
        device_node* node;
+       pci_info info;
+
+       virtio_pci_queue_cookie *cookies;
 } virtio_pci_sim_info;
 
 
 device_manager_info* gDeviceManager;
 virtio_for_controller_interface* gVirtio;
+static pci_x86_module_info* sPCIx86Module;
 
 
 int32
@@ -65,6 +83,61 @@ virtio_pci_interrupt(void *data)
 }
 
 
+int32
+virtio_pci_config_interrupt(void *data)
+{
+       virtio_pci_sim_info* bus = (virtio_pci_sim_info*)data;
+       gVirtio->config_interrupt_handler(bus->sim);
+
+       return B_HANDLED_INTERRUPT;
+}
+
+
+int32
+virtio_pci_queue_interrupt(void *data)
+{
+       virtio_pci_queue_cookie* cookie = (virtio_pci_queue_cookie*)data;
+       gVirtio->queue_interrupt_handler(cookie->sim, cookie->queue);
+
+       return B_HANDLED_INTERRUPT;
+}
+
+
+status_t
+virtio_pci_setup_msix_interrupts(virtio_pci_sim_info* bus)
+{
+       CALLED();
+       uint8 irq = 0; // first irq slot
+       bus->pci->write_io_16(bus->device, bus->base_addr
+               + VIRTIO_MSI_CONFIG_VECTOR, irq);
+       if (bus->pci->read_io_16(bus->device, bus->base_addr
+               + VIRTIO_MSI_CONFIG_VECTOR) == VIRTIO_MSI_NO_VECTOR) {
+               ERROR("msix config vector incorrect\n");
+               return B_BAD_VALUE;
+       }
+       if (bus->irq_type == VIRTIO_IRQ_MSI_X)
+               irq++;
+
+       for (uint16 queue = 0; queue < bus->queue_count; queue++) {
+               bus->pci->write_io_16(bus->device, bus->base_addr
+                       + VIRTIO_PCI_QUEUE_SEL, queue);
+               bus->pci->write_io_16(bus->device, bus->base_addr
+                       + VIRTIO_MSI_QUEUE_VECTOR, irq);
+
+               if (bus->pci->read_io_16(bus->device, bus->base_addr
+                       + VIRTIO_MSI_QUEUE_VECTOR) == VIRTIO_MSI_NO_VECTOR) {
+                       ERROR("msix queue vector incorrect\n");
+                       return B_BAD_VALUE;
+               }
+       
+               if (bus->irq_type == VIRTIO_IRQ_MSI_X)
+                       irq++;
+       }
+
+       return B_OK;
+}
+
+
 static void
 set_sim(void* cookie, virtio_sim sim)
 {
@@ -127,7 +200,7 @@ read_device_config(void* cookie, uint8 _offset, void* 
_buffer,
        CALLED();
        virtio_pci_sim_info* bus = (virtio_pci_sim_info*)cookie;
 
-       addr_t offset = bus->base_addr + bus->config_base + _offset;
+       addr_t offset = bus->base_addr + VIRTIO_PCI_CONFIG(bus) + _offset;
        uint8* buffer = (uint8*)_buffer;
        while (bufferSize > 0) {
                uint8 size = 4;
@@ -159,7 +232,7 @@ write_device_config(void* cookie, uint8 _offset, const 
void* _buffer,
        CALLED();
        virtio_pci_sim_info* bus = (virtio_pci_sim_info*)cookie;
 
-       addr_t offset = bus->base_addr + bus->config_base + _offset;
+       addr_t offset = bus->base_addr + VIRTIO_PCI_CONFIG(bus) + _offset;
        const uint8* buffer = (const uint8*)_buffer;
        while (bufferSize > 0) {
                uint8 size = 4;
@@ -209,17 +282,108 @@ setup_queue(void* cookie, uint16 queue, phys_addr_t phy)
 
 
 status_t 
-setup_interrupt(void* cookie)
+setup_interrupt(void* cookie, uint16 queueCount)
 {
        CALLED();
        virtio_pci_sim_info* bus = (virtio_pci_sim_info*)cookie;
-       
-       // setup interrupt handler
-       status_t status = install_io_interrupt_handler(bus->irq,
-               virtio_pci_interrupt, bus, 0);
-       if (status != B_OK) {
-               ERROR("can't install interrupt handler\n");
-               return status;
+       pci_info *pciInfo = &bus->info;
+
+       bus->queue_count = queueCount;
+
+       if (sPCIx86Module != NULL) {
+               // try MSI-X
+               uint8 msixCount = sPCIx86Module->get_msix_count(
+                       pciInfo->bus, pciInfo->device, pciInfo->function);
+               if (msixCount >= 1) {
+                       if (msixCount >= (queueCount + 1)) {
+                               uint8 vector;
+                               bus->cookies = new(std::nothrow)
+                                       virtio_pci_queue_cookie[queueCount];
+                               if (bus->cookies != NULL
+                                       && 
sPCIx86Module->configure_msix(pciInfo->bus,
+                                               pciInfo->device, 
pciInfo->function, queueCount + 1,
+                                               &vector) == B_OK
+                                       && 
sPCIx86Module->enable_msix(pciInfo->bus, pciInfo->device,
+                                               pciInfo->function) == B_OK) {
+                                       TRACE_ALWAYS("using MSI-X count %u 
starting at %d\n",
+                                               queueCount + 1, vector);
+                                       bus->irq = vector;
+                                       bus->irq_type = VIRTIO_IRQ_MSI_X;
+                               } else {
+                                       ERROR("couldn't use MSI-X\n");
+                               }
+                       } else {
+                               uint8 vector;
+                               if (sPCIx86Module->configure_msix(pciInfo->bus, 
pciInfo->device,
+                                               pciInfo->function, queueCount + 
1, &vector) == B_OK
+                                       && 
sPCIx86Module->enable_msix(pciInfo->bus, pciInfo->device,
+                                               pciInfo->function) == B_OK) {
+                                       TRACE_ALWAYS("using MSI-X vector shared 
%u\n", 1);
+                                       bus->irq = vector;
+                                       bus->irq_type = VIRTIO_IRQ_MSI_X_SHARED;
+                               } else {
+                                       ERROR("couldn't use MSI-X SHARED\n");
+                               }
+                       }
+               } else if (sPCIx86Module->get_msi_count(
+                       pciInfo->bus, pciInfo->device, pciInfo->function) >= 1) 
{
+                       // try MSI 
+                       uint8 vector;
+                       if (sPCIx86Module->configure_msi(pciInfo->bus, 
pciInfo->device,
+                                       pciInfo->function, 1, &vector) == B_OK
+                               && sPCIx86Module->enable_msi(pciInfo->bus, 
pciInfo->device,
+                                       pciInfo->function) == B_OK) {
+                               TRACE_ALWAYS("using MSI vector %u\n", vector);
+                               bus->irq = vector;
+                               bus->irq_type = VIRTIO_IRQ_MSI;
+                       } else {
+                               ERROR("couldn't use MSI\n");
+                       }
+               }
+       }
+       if (bus->irq_type == VIRTIO_IRQ_LEGACY) {
+               bus->irq = pciInfo->u.h0.interrupt_line;
+               TRACE_ALWAYS("using legacy interrupt %u\n", bus->irq);
+       }
+       if (bus->irq == 0 || bus->irq == 0xff) {
+               ERROR("PCI IRQ not assigned\n");
+               if (sPCIx86Module != NULL) {
+                       put_module(B_PCI_X86_MODULE_NAME);
+                       sPCIx86Module = NULL;
+               }
+               delete bus;
+               return B_ERROR;
+       }
+
+       if (bus->irq_type == VIRTIO_IRQ_MSI_X) {
+               status_t status = install_io_interrupt_handler(bus->irq,
+                       virtio_pci_config_interrupt, bus, 0);
+               if (status != B_OK) {
+                       ERROR("can't install interrupt handler\n");
+                       return status;
+               }
+               int32 irq = bus->irq + 1;
+               for (int32 queue = 0; queue < queueCount; queue++, irq++) {
+                       bus->cookies[queue].sim = bus->sim;
+                       bus->cookies[queue].queue = queue;
+                       status_t status = install_io_interrupt_handler(irq,
+                               virtio_pci_queue_interrupt, 
&bus->cookies[queue], 0);
+                       if (status != B_OK) {
+                               ERROR("can't install interrupt handler\n");
+                               return status;
+                       }
+               }
+               TRACE("setup_interrupt() installed MSI-X interrupt handlers\n");
+               virtio_pci_setup_msix_interrupts(bus);
+       } else {
+               // setup interrupt handler
+               status_t status = install_io_interrupt_handler(bus->irq,
+                       virtio_pci_interrupt, bus, 0);
+               if (status != B_OK) {
+                       ERROR("can't install interrupt handler\n");
+                       return status;
+               }
+               TRACE("setup_interrupt() installed legacy interrupt handler\n");
        }
 
        return B_OK;
@@ -261,23 +425,22 @@ init_bus(device_node* node, void** bus_cookie)
                gDeviceManager->put_node(parent);
        }
 
+       if (get_module(B_PCI_X86_MODULE_NAME, (module_info**)&sPCIx86Module)
+                       != B_OK) {
+               sPCIx86Module = NULL;
+       }
+
        bus->node = node;
        bus->pci = pci;
        bus->device = device;
-       // TODO MSI implies 24
-       bus->config_base = 20;
+       bus->cookies = NULL;
+       bus->irq_type = VIRTIO_IRQ_LEGACY;
 
-       pci_info pciInfo;
-       pci->get_pci_info(device, &pciInfo);
+       pci_info *pciInfo = &bus->info;
+       pci->get_pci_info(device, pciInfo);
 
        // legacy interrupt
-       bus->base_addr = pciInfo.u.h0.base_registers[0];
-       bus->irq = pciInfo.u.h0.interrupt_line;
-       if (bus->irq == 0 || bus->irq == 0xff) {
-               ERROR("PCI IRQ not assigned\n");
-               delete bus;
-               return B_ERROR;
-       }
+       bus->base_addr = pciInfo->u.h0.base_registers[0];
 
        // enable bus master and io
        uint16 pcicmd = pci->read_pci_config(device, PCI_command, 2);
@@ -300,6 +463,33 @@ static void
 uninit_bus(void* bus_cookie)
 {
        virtio_pci_sim_info* bus = (virtio_pci_sim_info*)bus_cookie;
+       if (bus->irq_type != VIRTIO_IRQ_LEGACY) {
+               if (bus->irq_type == VIRTIO_IRQ_MSI) {
+                       remove_io_interrupt_handler(bus->irq, 
virtio_pci_interrupt, bus);
+                       sPCIx86Module->disable_msi(bus->info.bus,
+                               bus->info.device, bus->info.function);
+                       sPCIx86Module->unconfigure_msi(bus->info.bus,
+                               bus->info.device, bus->info.function);
+               } else {
+                       int32 irq = bus->irq + 1;
+                       for (uint16 queue = 0; queue < bus->queue_count; 
queue++, irq++) {
+                               remove_io_interrupt_handler(irq, 
virtio_pci_queue_interrupt,
+                                       &bus->cookies[queue]);
+                       }
+                       remove_io_interrupt_handler(bus->irq, 
virtio_pci_config_interrupt,
+                                       bus);
+                       sPCIx86Module->disable_msix(bus->info.bus,
+                               bus->info.device, bus->info.function);
+                       sPCIx86Module->unconfigure_msix(bus->info.bus,
+                               bus->info.device, bus->info.function);
+               }
+       } else
+               remove_io_interrupt_handler(bus->irq, virtio_pci_interrupt, 
bus);
+       if (sPCIx86Module != NULL) {
+               put_module(B_PCI_X86_MODULE_NAME);
+               sPCIx86Module = NULL;
+       }
+       delete[] bus->cookies;
        delete bus;
 }
 
diff --git a/src/add-ons/kernel/busses/virtio/virtio_pci.h 
b/src/add-ons/kernel/busses/virtio/virtio_pci.h
index 485cf4f..981ab66 100644
--- a/src/add-ons/kernel/busses/virtio/virtio_pci.h
+++ b/src/add-ons/kernel/busses/virtio/virtio_pci.h
@@ -72,8 +72,8 @@
  * The remaining space is defined by each driver as the per-driver
  * configuration space.
  */
-#define VIRTIO_PCI_CONFIG(sc) \
-    (((sc)->vtpci_flags & VTPCI_FLAG_MSIX) ? 24 : 20)
+#define VIRTIO_PCI_CONFIG(bus) \
+    ((bus->irq_type != VIRTIO_IRQ_LEGACY) ? 24 : 20)
 
 /*
  * How many bits to shift physical queue address written to QUEUE_PFN.


Other related posts:

  • » [haiku-commits] haiku: hrev45849 - src/add-ons/kernel/busses/virtio - korli