[PATCH v1 2/2] virtio, qtest: Add functionality to handle interrupt
From: Tetsuya Mukawa <hidden>
Date: 2016-06-02 03:30:38
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/virtio_ethdev.c | 7 +- drivers/net/virtio/virtio_qtest/qtest.h | 3 +- drivers/net/virtio/virtio_qtest/qtest_utils.c | 225 ++++++++++++++++++++- drivers/net/virtio/virtio_qtest/qtest_utils.h | 68 ++++++- drivers/net/virtio/virtio_qtest/virtio_qtest_dev.c | 23 ++- drivers/net/virtio/virtio_qtest/virtio_qtest_pci.c | 64 ++++-- 6 files changed, 360 insertions(+), 30 deletions(-)
diff --git a/drivers/net/virtio/virtio_ethdev.c b/drivers/net/virtio/virtio_ethdev.c
index 8b5fb66..e8737ab 100644
--- a/drivers/net/virtio/virtio_ethdev.c
+++ b/drivers/net/virtio/virtio_ethdev.c@@ -1043,7 +1043,6 @@ eth_virtio_dev_init(struct rte_eth_dev *eth_dev) struct virtio_net_config *config; struct virtio_net_config local_config; struct rte_pci_device *pci_dev; - uint32_t dev_flags = RTE_ETH_DEV_DETACHABLE; int ret; RTE_BUILD_BUG_ON(RTE_PKTMBUF_HEADROOM < sizeof(struct virtio_net_hdr_mrg_rxbuf));
@@ -1067,8 +1066,9 @@ eth_virtio_dev_init(struct rte_eth_dev *eth_dev) pci_dev = eth_dev->pci_dev; + eth_dev->data->dev_flags |= RTE_ETH_DEV_DETACHABLE; if (pci_dev) { - ret = vtpci_init(pci_dev, hw, &dev_flags); + ret = vtpci_init(pci_dev, hw, ð_dev->data->dev_flags); if (ret) return ret; }
@@ -1086,10 +1086,9 @@ eth_virtio_dev_init(struct rte_eth_dev *eth_dev) /* If host does not support status then disable LSC */ if (!vtpci_with_feature(hw, VIRTIO_NET_F_STATUS)) - dev_flags &= ~RTE_ETH_DEV_INTR_LSC; + eth_dev->data->dev_flags &= ~RTE_ETH_DEV_INTR_LSC; rte_eth_copy_pci_info(eth_dev, pci_dev); - eth_dev->data->dev_flags = dev_flags; rx_func_get(eth_dev);
diff --git a/drivers/net/virtio/virtio_qtest/qtest.h b/drivers/net/virtio/virtio_qtest/qtest.h
index 534c5a0..7e8c093 100644
--- a/drivers/net/virtio/virtio_qtest/qtest.h
+++ b/drivers/net/virtio/virtio_qtest/qtest.h@@ -35,7 +35,7 @@ #define _VIRTIO_QTEST_H_ #define QTEST_DRV_NAME "eth_virtio_qtest" -#define QTEST_DEVICE_NUM 2 +#define QTEST_DEVICE_NUM 3 #include <rte_pci.h> #include <linux/pci_regs.h>
@@ -43,6 +43,7 @@ /* Device information */ #define VIRTIO_NET_DEVICE_ID 0x1000 #define VIRTIO_NET_VENDOR_ID 0x1af4 +#define VIRTIO_NET_IRQ_NUM 10 #define IVSHMEM_DEVICE_ID 0x1110 #define IVSHMEM_VENDOR_ID 0x1af4 #define PIIX3_DEVICE_ID 0x7000
diff --git a/drivers/net/virtio/virtio_qtest/qtest_utils.c b/drivers/net/virtio/virtio_qtest/qtest_utils.c
index 27118fb..c5a3a7a 100644
--- a/drivers/net/virtio/virtio_qtest/qtest_utils.c
+++ b/drivers/net/virtio/virtio_qtest/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)
@@ -466,15 +504,112 @@ qtest_get_bar_size(struct qtest_session *s, const char *name, return 0; } +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
@@ -492,6 +627,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); }
@@ -595,6 +731,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", + 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.
@@ -1011,6 +1198,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);
@@ -1018,7 +1211,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;
@@ -1042,12 +1235,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"); + goto error; + } + ret = qtest_register_target_devices(s, devices, devnum); if (ret != 0) { PMD_DRV_LOG(ERR, "Failed to initialize qtest session"); 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", ivshmem_path);
@@ -1067,6 +1269,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"); + goto error; + } + s->intr_th_started = 1; + + ret = qtest_intr_initialize(s); + if (ret != 0) { + PMD_DRV_LOG(ERR, "Failed to initialize interrupt"); + goto error; + } + ret = qtest_setup_shared_memory(s); if (ret != 0) { PMD_DRV_LOG(ERR, "Failed to setup shared memory");
diff --git a/drivers/net/virtio/virtio_qtest/qtest_utils.h b/drivers/net/virtio/virtio_qtest/qtest_utils.h
index e41374f..c1abc39 100644
--- a/drivers/net/virtio/virtio_qtest/qtest_utils.h
+++ b/drivers/net/virtio/virtio_qtest/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
@@ -274,6 +326,20 @@ int qtest_get_bar_size(struct qtest_session *s, const char *name, /** * @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
diff --git a/drivers/net/virtio/virtio_qtest/virtio_qtest_dev.c b/drivers/net/virtio/virtio_qtest/virtio_qtest_dev.c
index dec38ff..78a87b5 100644
--- a/drivers/net/virtio/virtio_qtest/virtio_qtest_dev.c
+++ b/drivers/net/virtio/virtio_qtest/virtio_qtest_dev.c@@ -51,12 +51,14 @@ #define ETH_VIRTIO_NET_ARG_IVSHMEM_PATH "ivshmem" #define ETH_VIRTIO_NET_ARG_VIRTIO_NET_ADDR "virtio-net-addr" #define ETH_VIRTIO_NET_ARG_IVSHMEM_ADDR "ivshmem-addr" +#define ETH_VIRTIO_NET_ARG_PIIX3_ADDR "piix3-addr" static const char *valid_qtest_args[] = { ETH_VIRTIO_NET_ARG_QTEST_PATH, ETH_VIRTIO_NET_ARG_IVSHMEM_PATH, ETH_VIRTIO_NET_ARG_VIRTIO_NET_ADDR, ETH_VIRTIO_NET_ARG_IVSHMEM_ADDR, + ETH_VIRTIO_NET_ARG_PIIX3_ADDR, NULL };
@@ -197,7 +199,7 @@ static int virtio_prepare_target_devices(struct qtest_pci_device *devices, struct rte_kvargs *kvlist) { - struct qtest_pci_device *virtio_net, *ivshmem; + struct qtest_pci_device *virtio_net, *ivshmem, *piix3; struct rte_pci_addr default_addr; const struct rte_memseg *ms; int ret;
@@ -224,6 +226,7 @@ virtio_prepare_target_devices(struct qtest_pci_device *devices, virtio_net = &devices[0]; ivshmem = &devices[1]; + piix3 = &devices[2]; virtio_net->name = "virtio-net"; virtio_net->device_id = VIRTIO_NET_DEVICE_ID;
@@ -251,6 +254,12 @@ virtio_prepare_target_devices(struct qtest_pci_device *devices, /* In host mode, only one memory segment is vaild */ ivshmem->bar[2].region_start = (uint64_t)ms[0].addr; + /* piix3 is needed to route irqs from virtio-net to ioapic */ + piix3->name = "piix3"; + piix3->device_id = PIIX3_DEVICE_ID; + piix3->vendor_id = PIIX3_VENDOR_ID; + piix3->init = qtest_init_piix3_device; + /* * Set pci addresses specified by command line. * QTest utils will only check specified pci address.
@@ -274,6 +283,13 @@ virtio_prepare_target_devices(struct qtest_pci_device *devices, if (ret < 0) return -1; + default_addr.devid = 1; + ret = virtio_net_eth_pmd_parse_pci_addr(kvlist, + ETH_VIRTIO_NET_ARG_PIIX3_ADDR, + &piix3->specified_addr, &default_addr); + if (ret < 0) + return -1; + return 0; } /*
@@ -318,7 +334,7 @@ rte_qtest_virtio_pmd_init(const char *name, const char *params) hw = eth_dev->data->dev_private; hw->virtio_user_dev = qtest_vdev_init(qtest_path, ivshmem_path, - devices, QTEST_DEVICE_NUM); + VIRTIO_NET_IRQ_NUM, devices, QTEST_DEVICE_NUM); if (hw->virtio_user_dev == NULL) goto error;
@@ -331,8 +347,9 @@ rte_qtest_virtio_pmd_init(const char *name, const char *params) if (ret < 0) goto error; + TAILQ_INIT(ð_dev->link_intr_cbs); + eth_dev->driver = NULL; - eth_dev->data->dev_flags |= RTE_ETH_DEV_DETACHABLE; eth_dev->data->kdrv = RTE_KDRV_NONE; eth_dev->data->drv_name = QTEST_DRV_NAME;
diff --git a/drivers/net/virtio/virtio_qtest/virtio_qtest_pci.c b/drivers/net/virtio/virtio_qtest/virtio_qtest_pci.c
index d715b13..048cca2 100644
--- a/drivers/net/virtio/virtio_qtest/virtio_qtest_pci.c
+++ b/drivers/net/virtio/virtio_qtest/virtio_qtest_pci.c@@ -58,6 +58,34 @@ check_vq_phys_addr_ok(struct virtqueue *vq) return 1; } +static int +intr_cb_register(struct virtio_hw *hw, + rte_intr_callback_fn cb, void *cb_arg) +{ + qtest_intr_callback_register(hw->virtio_user_dev, cb, cb_arg); + return 0; +} + +static int +intr_cb_unregister(struct virtio_hw *hw, + rte_intr_callback_fn cb, void *cb_arg) +{ + qtest_intr_callback_register(hw->virtio_user_dev, cb, cb_arg); + return 0; +} + +static int +intr_enable(struct virtio_hw *hw) +{ + return qtest_intr_enable(hw->virtio_user_dev); +} + +static int +intr_disable(struct virtio_hw *hw) +{ + return qtest_intr_disable(hw->virtio_user_dev); +} + static inline uint8_t qtest_read8(struct virtio_hw *hw, uint8_t *addr) {
@@ -259,19 +287,23 @@ qtest_notify_queue(struct virtio_hw *hw __rte_unused, struct virtqueue *vq) } const struct virtio_pci_ops modern_qtest_ops = { - .read_dev_cfg = qtest_read_dev_config, - .write_dev_cfg = qtest_write_dev_config, - .reset = qtest_reset, - .get_status = qtest_get_status, - .set_status = qtest_set_status, - .get_features = qtest_get_features, - .set_features = qtest_set_features, - .get_isr = qtest_get_isr, - .set_config_irq = qtest_set_config_irq, - .get_queue_num = qtest_get_queue_num, - .setup_queue = qtest_setup_queue, - .del_queue = qtest_del_queue, - .notify_queue = qtest_notify_queue, + .read_dev_cfg = qtest_read_dev_config, + .write_dev_cfg = qtest_write_dev_config, + .reset = qtest_reset, + .get_status = qtest_get_status, + .set_status = qtest_set_status, + .get_features = qtest_get_features, + .set_features = qtest_set_features, + .get_isr = qtest_get_isr, + .set_config_irq = qtest_set_config_irq, + .get_queue_num = qtest_get_queue_num, + .setup_queue = qtest_setup_queue, + .del_queue = qtest_del_queue, + .notify_queue = qtest_notify_queue, + .intr_cb_register = intr_cb_register, + .intr_cb_unregister = intr_cb_unregister, + .intr_enable = intr_enable, + .intr_disable = intr_disable, }; static void *
@@ -396,9 +428,9 @@ qtest_vtpci_init(struct virtio_hw *hw, uint32_t *dev_flags) if (virtio_read_caps(hw) == 0) { PMD_INIT_LOG(INFO, "modern virtio pci detected."); hw->vtpci_ops = &modern_qtest_ops; - hw->modern = 1; - /* So far, we don't support LSC interrupt */ - *dev_flags = 0; + hw->use_msix = 0; + hw->modern = 1; + *dev_flags |= RTE_ETH_DEV_INTR_LSC; return 0; }
--
2.7.4