This patch implements basic MSI-X support for virtio-rng.

The device uses the virtio preferred method of working with MSI-X by
creating one vector for configuration and one vector for each vq in the
device.

Signed-off-by: Sasha Levin <[email protected]>
---
 tools/kvm/include/kvm/pci.h |   16 +++++++++++++
 tools/kvm/virtio/rng.c      |   53 ++++++++++++++++++++++++++++++++++++++----
 2 files changed, 64 insertions(+), 5 deletions(-)

diff --git a/tools/kvm/include/kvm/pci.h b/tools/kvm/include/kvm/pci.h
index a7532e3..27fa349 100644
--- a/tools/kvm/include/kvm/pci.h
+++ b/tools/kvm/include/kvm/pci.h
@@ -24,6 +24,21 @@ struct pci_config_address {
        unsigned        enable_bit      : 1;            /* 31       */
 };
 
+struct msix_table {
+       u32 low;
+       u32 high;
+       u32 data;
+       u32 ctrl;
+};
+
+struct msix_cap {
+       u8 cap;
+       u8 next;
+       u16 table_size;
+       u32 table_offset;
+       struct msix_table table[3 * PCI_MSIX_ENTRY_SIZE];
+};
+
 struct pci_device_header {
        u16             vendor_id;
        u16             device_id;
@@ -47,6 +62,7 @@ struct pci_device_header {
        u8              irq_pin;
        u8              min_gnt;
        u8              max_lat;
+       struct msix_cap msix;
 };
 
 void pci__init(void);
diff --git a/tools/kvm/virtio/rng.c b/tools/kvm/virtio/rng.c
index 1a7f569..5f29ded 100644
--- a/tools/kvm/virtio/rng.c
+++ b/tools/kvm/virtio/rng.c
@@ -39,6 +39,8 @@ struct rng_dev {
        u8                      isr;
        u16                     config_vector;
        int                     fd;
+       u32                     vq_vector[NUM_VIRT_QUEUES];
+       u32                     msix_io_block;
 
        /* virtio queue */
        u16                     queue_selector;
@@ -81,6 +83,9 @@ static bool virtio_rng_pci_io_in(struct ioport *ioport, 
struct kvm *kvm, u16 por
        case VIRTIO_MSI_CONFIG_VECTOR:
                ioport__write16(data, rdev->config_vector);
                break;
+       case VIRTIO_MSI_QUEUE_VECTOR:
+               ioport__write16(data, rdev->vq_vector[rdev->queue_selector]);
+               break;
        default:
                ret             = false;
                break;
@@ -109,10 +114,10 @@ static void virtio_rng_do_io(struct kvm *kvm, void *param)
        struct virt_queue *vq = job->vq;
        struct rng_dev *rdev = job->rdev;
 
-       while (virt_queue__available(vq)) {
+       while (virt_queue__available(vq))
                virtio_rng_do_io_request(kvm, rdev, vq);
-               virt_queue__trigger_irq(vq, rdev->pci_hdr.irq_line, &rdev->isr, 
kvm);
-       }
+
+       kvm__irq_line(kvm, rdev->pci_hdr.irq_line, VIRTIO_IRQ_HIGH);
 }
 
 static bool virtio_rng_pci_io_out(struct ioport *ioport, struct kvm *kvm, u16 
port, void *data, int size, u32 count)
@@ -125,7 +130,6 @@ static bool virtio_rng_pci_io_out(struct ioport *ioport, 
struct kvm *kvm, u16 po
        offset = port - rdev->base_addr;
 
        switch (offset) {
-       case VIRTIO_MSI_QUEUE_VECTOR:
        case VIRTIO_PCI_GUEST_FEATURES:
                break;
        case VIRTIO_PCI_QUEUE_PFN: {
@@ -163,8 +167,21 @@ static bool virtio_rng_pci_io_out(struct ioport *ioport, 
struct kvm *kvm, u16 po
                rdev->status            = ioport__read8(data);
                break;
        case VIRTIO_MSI_CONFIG_VECTOR:
-               rdev->config_vector     = VIRTIO_MSI_NO_VECTOR;
+               rdev->config_vector     = ioport__read16(data);
+               break;
+       case VIRTIO_MSI_QUEUE_VECTOR: {
+               u32 gsi;
+               u32 vec;
+
+               vec = rdev->vq_vector[rdev->queue_selector] = 
ioport__read16(data);
+
+               gsi = irq__add_msix_route(kvm,
+                                         rdev->pci_hdr.msix.table[vec].low,
+                                         rdev->pci_hdr.msix.table[vec].high,
+                                         rdev->pci_hdr.msix.table[vec].data);
+               rdev->pci_hdr.irq_line = gsi;
                break;
+       }
        default:
                ret                     = false;
                break;
@@ -185,6 +202,16 @@ static void ioevent_callback(struct kvm *kvm, void *param)
        thread_pool__do_job(&job->job_id);
 }
 
+static void callback_mmio(u64 addr, u8 *data, u32 len, u8 is_write, void *ptr)
+{
+       struct rng_dev *rdev = ptr;
+       void *table = &rdev->pci_hdr.msix.table;
+       if (is_write)
+               memcpy(table + addr - rdev->msix_io_block, data, len);
+       else
+               memcpy(data, table + addr - rdev->msix_io_block, len);
+}
+
 void virtio_rng__init(struct kvm *kvm)
 {
        u8 pin, line, dev, i;
@@ -196,7 +223,10 @@ void virtio_rng__init(struct kvm *kvm)
        if (rdev == NULL)
                return;
 
+       rdev->msix_io_block = pci_get_io_space_block();
+
        rdev_base_addr = ioport__register(IOPORT_EMPTY, &virtio_rng_io_ops, 
IOPORT_SIZE, rdev);
+       kvm__register_mmio(kvm, rdev->msix_io_block, 0x100, callback_mmio, 
rdev);
 
        rdev->pci_hdr = (struct pci_device_header) {
                .vendor_id              = PCI_VENDOR_ID_REDHAT_QUMRANET,
@@ -207,8 +237,21 @@ void virtio_rng__init(struct kvm *kvm)
                .subsys_vendor_id       = 
PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET,
                .subsys_id              = VIRTIO_ID_RNG,
                .bar[0]                 = rdev_base_addr | 
PCI_BASE_ADDRESS_SPACE_IO,
+               .bar[1]                 = rdev->msix_io_block |
+                                       PCI_BASE_ADDRESS_SPACE_MEMORY |
+                                       PCI_BASE_ADDRESS_MEM_TYPE_64,
+               /* bar[2] is the continuation of bar[1] for 64bit addressing */
+               .bar[2]                 = 0,
+               .status                 = PCI_STATUS_CAP_LIST,
+               .capabilities           = (void *)&rdev->pci_hdr.msix - (void 
*)&rdev->pci_hdr,
        };
 
+       rdev->pci_hdr.msix.cap = PCI_CAP_ID_MSIX;
+       rdev->pci_hdr.msix.next = 0;
+       rdev->pci_hdr.msix.table_size = (NUM_VIRT_QUEUES + 1) | 
PCI_MSIX_FLAGS_ENABLE;
+       rdev->pci_hdr.msix.table_offset = 1; /* Use BAR 1 */
+
+       rdev->config_vector = 0;
        rdev->base_addr = rdev_base_addr;
        rdev->fd = open("/dev/urandom", O_RDONLY);
        if (rdev->fd < 0)
-- 
1.7.6

--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to