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/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