The patch adds functionality to handle interrupt from pci device of
QEMU guest. To handle the interrupts, the patch adds to initialize piix3
pci device.

Signed-off-by: Tetsuya Mukawa <mukawa at igel.co.jp>
---
 drivers/net/virtio/qtest_utils.c | 225 ++++++++++++++++++++++++++++++++++++++-
 drivers/net/virtio/qtest_utils.h |  68 +++++++++++-
 2 files changed, 287 insertions(+), 6 deletions(-)

diff --git a/drivers/net/virtio/qtest_utils.c b/drivers/net/virtio/qtest_utils.c
index 338224a..337546a 100644
--- a/drivers/net/virtio/qtest_utils.c
+++ b/drivers/net/virtio/qtest_utils.c
@@ -36,6 +36,7 @@
 #include <sys/un.h>
 #include <pthread.h>
 #include <fcntl.h>
+#include <sys/eventfd.h>

 #include <rte_malloc.h>

@@ -43,6 +44,12 @@
 #include "virtio_ethdev.h"
 #include "qtest_utils.h"

+/* PIIX3 configuration registers */
+#define PIIX3_REG_ADDR_PIRQA            0x60
+#define PIIX3_REG_ADDR_PIRQB            0x61
+#define PIIX3_REG_ADDR_PIRQC            0x62
+#define PIIX3_REG_ADDR_PIRQD            0x63
+
 /* ivshmem configuration */
 #define IVSHMEM_PROTOCOL_VERSION        0

@@ -74,6 +81,14 @@ struct qtest_session {
        size_t evq_total_len;

        union qtest_pipefds msgfds;
+
+       int irqno;
+       pthread_t intr_th;
+       int intr_th_started;
+       int eventfd;
+       rte_atomic16_t enable_intr;
+       rte_intr_callback_fn cb;
+       void *cb_arg;
 };

 static int
@@ -230,6 +245,29 @@ qtest_pci_inb(struct qtest_session *s, uint8_t bus, 
uint8_t device,
        return (tmp >> ((offset & 0x3) * 8)) & 0xff;
 }

+static void
+qtest_pci_outb(struct qtest_session *s, uint8_t bus, uint8_t device,
+               uint8_t function, uint8_t offset, uint8_t value)
+{
+       uint32_t addr, tmp, pos;
+
+       addr = PCI_CONFIG_ADDR(bus, device, function, offset);
+       pos = (offset % 4) * 8;
+
+       if (pthread_mutex_lock(&s->qtest_session_lock) < 0)
+               rte_panic("Cannot lock mutex\n");
+
+       qtest_raw_out(s, 0xcf8, addr, 'l');
+       tmp = qtest_raw_in(s, 0xcfc, 'l');
+       tmp = (tmp & ~(0xff << pos)) | (value << pos);
+
+       qtest_raw_out(s, 0xcf8, addr, 'l');
+       qtest_raw_out(s, 0xcfc, tmp, 'l');
+
+       if (pthread_mutex_unlock(&s->qtest_session_lock) < 0)
+               rte_panic("Cannot unlock mutex\n");
+}
+
 static uint32_t
 qtest_pci_inl(struct qtest_session *s, uint8_t bus, uint8_t device,
                uint8_t function, uint8_t offset)
@@ -389,15 +427,112 @@ qtest_find_device(struct qtest_session *s, const char 
*name)
        return NULL;
 }

+int
+qtest_intr_enable(struct qtest_session *s)
+{
+       rte_atomic16_set(&s->enable_intr, 1);
+
+       return 0;
+}
+
+int
+qtest_intr_disable(struct qtest_session *s)
+{
+       rte_atomic16_set(&s->enable_intr, 0);
+
+       return 0;
+}
+
+void
+qtest_intr_callback_register(struct qtest_session *s,
+               rte_intr_callback_fn cb, void *cb_arg)
+{
+       s->cb = cb;
+       s->cb_arg = cb_arg;
+       rte_atomic16_set(&s->enable_intr, 1);
+}
+
+void
+qtest_intr_callback_unregister(struct qtest_session *s,
+               rte_intr_callback_fn cb __rte_unused,
+               void *cb_arg __rte_unused)
+{
+       rte_atomic16_set(&s->enable_intr, 0);
+       s->cb = NULL;
+       s->cb_arg = NULL;
+}
+
+static void *
+qtest_intr_handler(void *data) {
+       struct qtest_session *s = (struct qtest_session *)data;
+       eventfd_t value;
+       int ret;
+
+       for (;;) {
+               ret = eventfd_read(s->eventfd, &value);
+               if (ret < 0)
+                       return NULL;
+               s->cb(NULL, s->cb_arg);
+       }
+       return NULL;
+}
+
+static int
+qtest_intr_initialize(struct qtest_session *s)
+{
+       char buf[64];
+       int ret;
+
+       snprintf(buf, sizeof(buf), "irq_intercept_in ioapic\n");
+
+       if (pthread_mutex_lock(&s->qtest_session_lock) < 0)
+               rte_panic("Cannot lock mutex\n");
+
+       /* To enable interrupt, send "irq_intercept_in" message to QEMU */
+       ret = qtest_raw_send(s->qtest_socket, buf, strlen(buf));
+       if (ret < 0) {
+               pthread_mutex_unlock(&s->qtest_session_lock);
+               return -1;
+       }
+
+       /* just ignore QEMU response */
+       ret = qtest_raw_recv(s->msgfds.readfd, buf, sizeof(buf));
+       if (ret < 0) {
+               pthread_mutex_unlock(&s->qtest_session_lock);
+               return -1;
+       }
+
+       if (pthread_mutex_unlock(&s->qtest_session_lock) < 0)
+               rte_panic("Cannot lock mutex\n");
+
+       return 0;
+}
+
 static void
 qtest_event_send(struct qtest_session *s, char *buf)
 {
+       char interrupt_message[32];
        int ret;

-       /* relay normal message to pipe */
-       ret = qtest_raw_send(s->msgfds.writefd, buf, strlen(buf));
-       if (ret < 0)
-               rte_panic("cannot relay normal message\n");
+       /* This message will come when interrupt occurs */
+       snprintf(interrupt_message, sizeof(interrupt_message),
+                       "IRQ raise %d", s->irqno);
+
+       if (strncmp(buf, interrupt_message,
+                               strlen(interrupt_message)) == 0) {
+               if (rte_atomic16_read(&s->enable_intr) == 0)
+                       return;
+
+               /* relay interrupt to eventfd */
+               ret = eventfd_write(s->eventfd, 1);
+               if (ret < 0)
+                       rte_panic("cannot relay interrupt\n");
+       } else {
+               /* relay normal message to pipe */
+               ret = qtest_raw_send(s->msgfds.writefd, buf, strlen(buf));
+               if (ret < 0)
+                       rte_panic("cannot relay normal message\n");
+       }
 }

 static void
@@ -415,6 +550,7 @@ qtest_close_sockets(struct qtest_session *s)
        qtest_close_one_socket(&s->qtest_socket);
        qtest_close_one_socket(&s->msgfds.readfd);
        qtest_close_one_socket(&s->msgfds.writefd);
+       qtest_close_one_socket(&s->eventfd);
        qtest_close_one_socket(&s->ivshmem_socket);
 }

@@ -518,6 +654,57 @@ qtest_event_handler(void *data) {
        return NULL;
 }

+/* This function should be fixed when multiple target devices are supported */
+int
+qtest_init_piix3_device(struct qtest_session *s, struct qtest_pci_device *dev)
+{
+       uint8_t bus, device, slot = 0;
+       struct qtest_pci_device *tmpdev;
+       uint8_t pcislot2regaddr[] = {   0xff,
+                                       0xff,
+                                       0xff,
+                                       PIIX3_REG_ADDR_PIRQC,
+                                       PIIX3_REG_ADDR_PIRQD,
+                                       PIIX3_REG_ADDR_PIRQA,
+                                       PIIX3_REG_ADDR_PIRQB};
+
+       bus = dev->bus_addr;
+       device = dev->device_addr;
+
+       PMD_DRV_LOG(INFO,
+               "Find %s on virtual PCI bus: %04x:%02x:00.0\n",
+               dev->name, bus, device);
+
+       /* Get slot id that is connected to target device(virtio-net device) */
+       TAILQ_FOREACH(tmpdev, &s->head, next) {
+               if (strcmp(tmpdev->name, "piix3") != 0 &&
+                               strcmp(tmpdev->name, "ivshmem") != 0) {
+                       slot = tmpdev->device_addr;
+                       break;
+               }
+       }
+
+       if (slot == 0)
+               return -1;
+
+       /*
+        * Set interrupt routing for target device.
+        * Here is i440fx/piix3 connection settings
+        * ---------------------------------------
+        * PCI Slot3 -> PIRQC
+        * PCI Slot4 -> PIRQD
+        * PCI Slot5 -> PIRQA
+        * PCI Slot6 -> PIRQB
+        */
+       if (pcislot2regaddr[slot] != 0xff) {
+               qtest_pci_outb(s, bus, device, 0,
+                               pcislot2regaddr[slot],
+                               s->irqno);
+       }
+
+       return 0;
+}
+
 /*
  * Common initialization of PCI device.
  * To know detail, see pci specification.
@@ -855,6 +1042,12 @@ qtest_vdev_uninit(struct qtest_session *s)
                s->event_th_started = 0;
        }

+       if (s->intr_th_started) {
+               pthread_cancel(s->intr_th);
+               pthread_join(s->intr_th, NULL);
+               s->intr_th_started = 0;
+       }
+
        pthread_mutex_destroy(&s->qtest_session_lock);
        qtest_remove_target_devices(s);
        rte_free(s);
@@ -862,7 +1055,7 @@ qtest_vdev_uninit(struct qtest_session *s)

 struct qtest_session *
 qtest_vdev_init(char *qtest_path, char *ivshmem_path,
-               struct qtest_pci_device *devices, int devnum)
+               int irqno, struct qtest_pci_device *devices, int devnum)
 {
        struct qtest_session *s;
        int ret;
@@ -886,12 +1079,21 @@ qtest_vdev_init(char *qtest_path, char *ivshmem_path,
                goto error;
        }

+       s->eventfd = eventfd(0, 0);
+       if (s->eventfd < 0) {
+               PMD_DRV_LOG(ERR, "Failed to open eventfd\n");
+               goto error;
+       }
+
        ret = qtest_register_target_devices(s, devices, devnum);
        if (ret != 0) {
                PMD_DRV_LOG(ERR, "Failed to initialize qtest session\n");
                goto error;
        }

+       rte_atomic16_set(&s->enable_intr, 0);
+       s->irqno = irqno;
+
        s->ivshmem_socket = qtest_open_socket(ivshmem_path);
        if (s->ivshmem_socket < 0) {
                PMD_DRV_LOG(ERR, "Failed to open %s\n", ivshmem_path);
@@ -911,6 +1113,19 @@ qtest_vdev_init(char *qtest_path, char *ivshmem_path,
        }
        s->event_th_started = 1;

+       ret = pthread_create(&s->intr_th, NULL, qtest_intr_handler, s);
+       if (ret != 0) {
+               PMD_DRV_LOG(ERR, "Failed to create interrupt handler\n");
+               goto error;
+       }
+       s->intr_th_started = 1;
+
+       ret = qtest_intr_initialize(s);
+       if (ret != 0) {
+               PMD_DRV_LOG(ERR, "Failed to initialize interrupt\n");
+               goto error;
+       }
+
        ret = qtest_setup_shared_memory(s);
        if (ret != 0) {
                PMD_DRV_LOG(ERR, "Failed to setup shared memory\n");
diff --git a/drivers/net/virtio/qtest_utils.h b/drivers/net/virtio/qtest_utils.h
index 26994b1..0717ee9 100644
--- a/drivers/net/virtio/qtest_utils.h
+++ b/drivers/net/virtio/qtest_utils.h
@@ -134,6 +134,8 @@ struct qtest_pci_device {
  *   Path of qtest socket.
  * @param ivshmem_path
  *   Path of ivshmem socket.
+ * @param irqno
+ *   Interrupt number of the target device(virtio-net device).
  * @param devices
  *   Array of device information. It should contain piix3, ivshmem and target
  *   device(virtio-net device).
@@ -143,7 +145,7 @@ struct qtest_pci_device {
  *   The pointer to qtest session structure.
  */
 struct qtest_session *qtest_vdev_init(char *qtest_path, char *ivshmem_path,
-               struct qtest_pci_device *devices, int devnum);
+               int irqno, struct qtest_pci_device *devices, int devnum);

 /**
  * @internal
@@ -156,6 +158,56 @@ void qtest_vdev_uninit(struct qtest_session *s);

 /**
  * @internal
+ * Register interrupt callback.
+ *
+ * @param s
+ *   The pointer to qtest session structure.
+ * @param cb
+ *   The pointer to callback.
+ * @param cb_arg
+ *   The pointer to callback argument.
+ */
+void qtest_intr_callback_register(struct qtest_session *s,
+               rte_intr_callback_fn cb, void *cb_arg);
+
+/**
+ * @internal
+ * Unregister interrupt callback.
+ *
+ * @param s
+ *   The pointer to qtest session structure.
+ * @param cb
+ *   The pointer to callback.
+ * @param cb_arg
+ *   The pointer to callback argument.
+ */
+void qtest_intr_callback_unregister(struct qtest_session *s,
+               rte_intr_callback_fn cb, void *cb_arg);
+
+/**
+ * @internal
+ * Enable interrupt callback.
+ *
+ * @param s
+ *   The pointer to qtest session structure.
+ * @return
+ *   0 on success, negative on error
+ */
+int qtest_intr_enable(struct qtest_session *s);
+
+/**
+ * @internal
+ * Disable interrupt callback.
+ *
+ * @param s
+ *   The pointer to qtest session structure.
+ * @return
+ *   0 on success, negative on error
+ */
+int qtest_intr_disable(struct qtest_session *s);
+
+/**
+ * @internal
  * Read a port of QEMU guest.
  *
  * @param s
@@ -218,6 +270,20 @@ void qtest_write(struct qtest_session *s, uint64_t addr,

 /**
  * @internal
+ * Initialization function of piix3 device.
+ *
+ * @param s
+ *   The pointer to qtest session structure.
+ * @param dev
+ *   The pointer of pci device.
+ * @return
+ *   0 on success, negative on error
+ */
+int qtest_init_piix3_device(struct qtest_session *s,
+                       struct qtest_pci_device *dev);
+
+/**
+ * @internal
  * Initialization function of general device.
  *
  * @param s
-- 
2.1.4

Reply via email to