[PATCH v4 08/12] virtio, qtest: Add functionality to handle interrupt
From: Tetsuya Mukawa <hidden>
Date: 2016-03-09 08:34:01
Subsystem:
networking drivers, the rest · Maintainers:
Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds
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 <redacted> --- 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