From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-pa0-f45.google.com (mail-pa0-f45.google.com [209.85.220.45]) by dpdk.org (Postfix) with ESMTP id 9402E8DAF for ; Thu, 19 Nov 2015 11:57:57 +0100 (CET) Received: by padhx2 with SMTP id hx2so78034240pad.1 for ; Thu, 19 Nov 2015 02:57:56 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=igel-co-jp.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=lvK4jg1pA5VVN9qgHvW9kRsPjhKVnrptKZYYSc6hEXc=; b=Dvf8zZ4Rb4TPKoMuxChPUvdBGzorw4nZKl/y3SpPc8aZAsdoLPFseLxcq2l8HIOX6n gC9AKLadUrXJyh2bLneVjAEtCws5ATmc/79jGTG4N105Wv43Sarbw+Vr/hWZUBNt4+yx LxbNk7Yv7QEeKWd/CBTi9UeX6XuipelagjrgBzqfnLikA2Duo2fF9XIQ28kDMwjGFsm8 k9dyUUV9hFQNIAlPH7wPUBsBOUyZ+vE0TZW8qDrA79d/oIXJRZk25MPDWuxeZTRx6+43 m0XyCRlWFnH0OiaGnp7tjy8TcZzJ6+ScFv6rVDdfnXc0IM+H3zArQHh999Injk6m+vBa E0iA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=lvK4jg1pA5VVN9qgHvW9kRsPjhKVnrptKZYYSc6hEXc=; b=GbH0J39dLMNUJpzHYvFLlcwRScwgxCPmescJHZw8jG9LJkCFnPJQESoGb/0+36M768 dWItGVpYsZ0GJRlSUqjvlY85wwvyJqSVB6AbUQ2mVoBQDxui4HO9MCm8BWNTB2daQmQ+ P/LSQjr41V2JikIEqNX/OzmcA09YR8GI8Cy2UfMfkguNqT/KQmFcSagvDY72jwJU4tN2 Gu+AyzfvOg7aS+4r4E8o0zXYnFie3ffSC3nI0N4MacRhDoITFt1MAYzM7vrYOnRX5z+6 ecVgd/x4nJcZMs2hEoxIqvxRzgDwkrSicn/GJeRC7ZsWxqJ69YKSIZH9Yr20k0JJf3su hYiw== X-Gm-Message-State: ALoCoQlBwAm5Ky1/XIiPFr7dUaogMwaezSCeJOeTfy2lDCW8C3NnE4ts4EaeHDIVYhvwBwP6skTZ X-Received: by 10.68.178.1 with SMTP id cu1mr9529503pbc.115.1447930676764; Thu, 19 Nov 2015 02:57:56 -0800 (PST) Received: from localhost.localdomain (napt.igel.co.jp. [219.106.231.132]) by smtp.gmail.com with ESMTPSA id yg2sm9867565pbb.79.2015.11.19.02.57.53 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 19 Nov 2015 02:57:56 -0800 (PST) From: Tetsuya Mukawa To: dev@dpdk.org Date: Thu, 19 Nov 2015 19:57:30 +0900 Message-Id: <1447930650-26023-3-git-send-email-mukawa@igel.co.jp> X-Mailer: git-send-email 2.1.4 In-Reply-To: <1447930650-26023-1-git-send-email-mukawa@igel.co.jp> References: <1447930650-26023-1-git-send-email-mukawa@igel.co.jp> Cc: nakajima.yoshihiro@lab.ntt.co.jp, zhbzg@huawei.com, mst@redhat.com, gaoxiaoqiu@huawei.com, oscar.zhangbo@huawei.com, ann.zhuangyanying@huawei.com, zhoujingbin@huawei.com, guohongzhen@huawei.com Subject: [dpdk-dev] [RFC PATCH 2/2] virtio: Extend virtio-net PMD to support container environment X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: patches and discussions about DPDK List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 19 Nov 2015 10:57:58 -0000 The patch extends virtio-net PMD to work on host. Actually, we don't have virtio-net devices on host. Thus, this PMD called "cvio PMD" is for virtual device. To prepare virtio-net device on host, the users need to invoke QEMU process in special qtest mode. In this mode, no guest runs. Also, this mode is mainly used for testing QEMU devices from outer process. Here is example of command line. $ qemu-system-x86_64 -machine accel=qtest -display none \ -qtest unix:/tmp/qtest.sock,server \ -netdev type=tap,script=/etc/qemu-ifup,id=net0 \ -device virtio-net-pci,netdev=net0 \ -chardev socket,id=chr0,path=/tmp/ivshmem.sock,server \ -device ivshmem,size=1G,chardev=chr0,vectors=1 After invoking QEMU, cvio PMD can connect to QEMU process using unix domain sockets. Over these sockets, virtio-net device and ivshmem device in QEMU are probed by cvio PMD. Here is example of command line. $ testpmd -c f -n 1 -m 1024 --shm \ --vdev="eth_cvio0,qtest=/tmp/qtest.sock,ivshmem=/tmp/ivshmem.sock" \ -- --disable-hw-vlan --txqflags=0xf00 -i Please specify same unix domain sockets and memory size in both command lines like above. Also, "--shm" option is needed for cvio PMD like above. This option creates one hugepage file on hugetlbfs. It means we need enough contiguous memory. If there is no enough contiguous memory, initialization will be failed. This contiguous memory is used for sharing memory between DPDK application and ivshmem device in QEMU. Signed-off-by: Tetsuya Mukawa --- config/common_linuxapp | 5 + drivers/net/virtio/Makefile | 4 + drivers/net/virtio/qtest.c | 590 +++++++++++++++++++++++++++++++++++++ drivers/net/virtio/virtio_ethdev.c | 214 ++++++++++++-- drivers/net/virtio/virtio_ethdev.h | 16 + drivers/net/virtio/virtio_pci.h | 25 ++ 6 files changed, 833 insertions(+), 21 deletions(-) create mode 100644 drivers/net/virtio/qtest.c diff --git a/config/common_linuxapp b/config/common_linuxapp index 7248262..11e8fd1 100644 --- a/config/common_linuxapp +++ b/config/common_linuxapp @@ -478,3 +478,8 @@ CONFIG_RTE_APP_TEST=y CONFIG_RTE_TEST_PMD=y CONFIG_RTE_TEST_PMD_RECORD_CORE_CYCLES=n CONFIG_RTE_TEST_PMD_RECORD_BURST_STATS=n + +# +# Enable virtio support for container +# +CONFIG_RTE_VIRTIO_VDEV=n diff --git a/drivers/net/virtio/Makefile b/drivers/net/virtio/Makefile index 43835ba..5d6f69c 100644 --- a/drivers/net/virtio/Makefile +++ b/drivers/net/virtio/Makefile @@ -52,6 +52,10 @@ SRCS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += virtio_rxtx.c SRCS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += virtio_ethdev.c SRCS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += virtio_rxtx_simple.c +ifeq ($(CONFIG_RTE_VIRTIO_VDEV),y) + SRCS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += qtest.c +endif + # this lib depends upon: DEPDIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += lib/librte_eal lib/librte_ether DEPDIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += lib/librte_mempool lib/librte_mbuf diff --git a/drivers/net/virtio/qtest.c b/drivers/net/virtio/qtest.c new file mode 100644 index 0000000..005fa24 --- /dev/null +++ b/drivers/net/virtio/qtest.c @@ -0,0 +1,590 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2015 IGEL Co., Ltd. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of IGEL Co., Ltd. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "virtio_pci.h" +#include "virtio_logs.h" +#include "virtio_ethdev.h" + +#define IVSHMEM_PROTOCOL_VERSION 0 + +#define NB_BUS 256 +#define NB_DEVICE 32 +#define NB_PCI_BAR 6 + +#define REG_ADDR_VENDOR_ID 0x0 +#define REG_ADDR_DEVICE_ID 0x2 +#define REG_ADDR_COMMAND 0x4 +#define REG_ADDR_STATUS 0x6 +#define REG_ADDR_REVISION_ID 0x8 +#define REG_ADDR_CLASS_CODE 0x9 +#define REG_ADDR_CACHE_LINE_S 0xc +#define REG_ADDR_LAT_TIMER 0xd +#define REG_ADDR_HEADER_TYPE 0xe +#define REG_ADDR_BIST 0xf +#define REG_ADDR_BAR0 0x10 +#define REG_ADDR_BAR1 0x14 +#define REG_ADDR_BAR2 0x18 +#define REG_ADDR_BAR3 0x1c +#define REG_ADDR_BAR4 0x20 +#define REG_ADDR_BAR5 0x24 + +#define REG_VAL_COMMAND_IO 0x1 +#define REG_VAL_COMMAND_MEMORY 0x2 +#define REG_VAL_COMMAND_MASTER 0x4 + +#define REG_VAL_HEADER_TYPE_ENDPOINT 0x0 + +#define REG_VAL_BAR_MEMORY 0x0 +#define REG_VAL_BAR_IO 0x1 +#define REG_VAL_BAR_LOCATE_32 0x0 +#define REG_VAL_BAR_LOCATE_UNDER_1MB 0x2 +#define REG_VAL_BAR_LOCATE_64 0x4 + +#define VIRTIO_NET_IO_START 0xc000 + +enum qtest_pci_bar_type { + QTEST_PCI_BAR_DISABLE = 0, + QTEST_PCI_BAR_IO, + QTEST_PCI_BAR_MEMORY_UNDER_1MB, + QTEST_PCI_BAR_MEMORY_32, + QTEST_PCI_BAR_MEMORY_64 +}; + +struct qtest_pci_bar { + enum qtest_pci_bar_type type; + uint8_t addr; + uint64_t region_start; + uint64_t region_size; +}; + +TAILQ_HEAD(qtest_pci_device_list, qtest_pci_device); +struct qtest_pci_device { + TAILQ_ENTRY(qtest_pci_device) next; + const char *name; + uint16_t device_id; + uint16_t vendor_id; + uint8_t bus_addr; + uint8_t device_addr; + struct qtest_pci_bar bar[NB_PCI_BAR]; + int (*init)(int fd, struct qtest_pci_device *dev); +}; + +struct qtest_session { + int qtest_socket; + int ivshmem_socket; + int shm_fd; + struct qtest_pci_device_list head; +}; + +static uint32_t +qtest_inl(int fd, uint16_t addr) +{ + char buf[1024]; + int ret; + + snprintf(buf, sizeof(buf), "inl 0x%x\n", addr); + ret = write(fd, buf, strlen(buf)); + ret = read(fd, buf, sizeof(buf)); + buf[ret] = '\0'; + return strtoul(buf + strlen("OK "), NULL, 16); +} + +static void +qtest_outl(int fd, uint16_t addr, uint32_t val) +{ + char buf[1024]; + int ret; + + snprintf(buf, sizeof(buf), "outl 0x%x 0x%x\n", addr, val); + ret = write(fd, buf, strlen(buf)); + if (ret < 0) + return; + + ret = read(fd, buf, sizeof(buf)); + if (ret < 0) + return; +} + +static int +qtest_pci_readb(int fd, uint8_t bus, uint8_t device, + uint8_t function, uint8_t offset) +{ + uint32_t tmp; + + tmp = 0x80000000 | (bus & 0xff) << 16 | (device & 0x1f) << 11 | + (function & 0xf) << 8 | (offset & 0xfc); + + qtest_outl(fd, 0xcf8, tmp); + tmp = qtest_inl(fd, 0xcfc); + + return (tmp >> ((offset & 0x3) * 8)) & 0xff; +} + +static uint32_t +qtest_pci_readl(int fd, uint8_t bus, uint8_t device, + uint8_t function, uint8_t offset) +{ + uint32_t tmp; + + tmp = 0x80000000 | (bus & 0xff) << 16 | (device & 0x1f) << 11 | + (function & 0xf) << 8 | (offset & 0xfc); + + qtest_outl(fd, 0xcf8, tmp); + return qtest_inl(fd, 0xcfc); +} + +static void +qtest_pci_writel(int fd, uint8_t bus, uint8_t device, + uint8_t function, uint8_t offset, uint32_t value) +{ + uint32_t tmp; + + tmp = 0x80000000 | (bus & 0xff) << 16 | (device & 0x1f) << 11 | + (function & 0xf) << 8 | (offset & 0xfc); + + qtest_outl(fd, 0xcf8, tmp); + qtest_outl(fd, 0xcfc, value); + return; +} + +static uint64_t +qtest_pci_readq(int fd, uint8_t bus, uint8_t device, + uint8_t function, uint8_t offset) +{ + uint32_t tmp; + uint64_t val; + + tmp = 0x80000000 | (bus & 0xff) << 16 | (device & 0x1f) << 11 | + (function & 0xf) << 8 | (offset & 0xfc); + + qtest_outl(fd, 0xcf8, tmp); + val = (uint64_t)qtest_inl(fd, 0xcfc); + + tmp = 0x80000000 | (bus & 0xff) << 16 | (device & 0x1f) << 11 | + (function & 0xf) << 8 | ((offset + 4)& 0xfc); + + qtest_outl(fd, 0xcf8, tmp); + val |= (uint64_t)qtest_inl(fd, 0xcfc) << 32; + + return val; +} + +static void +qtest_pci_writeq(int fd, uint8_t bus, uint8_t device, + uint8_t function, uint8_t offset, uint64_t value) +{ + uint32_t tmp; + + tmp = 0x80000000 | (bus & 0xff) << 16 | (device & 0x1f) << 11 | + (function & 0xf) << 8 | (offset & 0xfc); + + qtest_outl(fd, 0xcf8, tmp); + qtest_outl(fd, 0xcfc, (uint32_t)(value & 0xffffffff)); + + tmp = 0x80000000 | (bus & 0xff) << 16 | (device & 0x1f) << 11 | + (function & 0xf) << 8 | ((offset + 4) & 0xfc); + + qtest_outl(fd, 0xcf8, tmp); + qtest_outl(fd, 0xcfc, (uint32_t)(value >> 32)); + return; +} + +static int +qtest_init_pci_device(int fd, struct qtest_pci_device *dev) +{ + uint8_t i, bus, device; + uint32_t val; + uint64_t val64; + + 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); + + /* Check header type */ + val = qtest_pci_readb(fd, bus, device, 0, REG_ADDR_HEADER_TYPE); + if (val != REG_VAL_HEADER_TYPE_ENDPOINT) { + PMD_DRV_LOG(ERR, "Unexpected header type %d\n", val); + return -1; + } + + /* Check BAR type */ + for (i = 0; i < NB_PCI_BAR; i++) { + val = qtest_pci_readl(fd, bus, device, 0, dev->bar[i].addr); + + switch (dev->bar[i].type) { + case QTEST_PCI_BAR_IO: + if ((val & 0x1) != REG_VAL_BAR_IO) + goto error; + break; + case QTEST_PCI_BAR_MEMORY_UNDER_1MB: + if ((val & 0x1) != REG_VAL_BAR_MEMORY) + goto error; + if ((val & 0x6) != REG_VAL_BAR_LOCATE_UNDER_1MB) + goto error; + break; + case QTEST_PCI_BAR_MEMORY_32: + if ((val & 0x1) != REG_VAL_BAR_MEMORY) + goto error; + if ((val & 0x6) != REG_VAL_BAR_LOCATE_32) + goto error; + break; + case QTEST_PCI_BAR_MEMORY_64: + if ((val & 0x1) != REG_VAL_BAR_MEMORY) + goto error; + if ((val & 0x6) != REG_VAL_BAR_LOCATE_64) + goto error; + break; + case QTEST_PCI_BAR_DISABLE: + break; + } + } + + /* Enable device */ + val = qtest_pci_readl(fd, bus, device, 0, REG_ADDR_COMMAND); + val |= REG_VAL_COMMAND_IO | REG_VAL_COMMAND_MEMORY | REG_VAL_COMMAND_MASTER; + qtest_pci_writel(fd, bus, device, 0, REG_ADDR_COMMAND, val); + + /* Calculate BAR size */ + for (i = 0; i < NB_PCI_BAR; i++) { + switch (dev->bar[i].type) { + case QTEST_PCI_BAR_IO: + case QTEST_PCI_BAR_MEMORY_UNDER_1MB: + case QTEST_PCI_BAR_MEMORY_32: + qtest_pci_writel(fd, bus, device, 0, + dev->bar[i].addr, 0xffffffff); + val = qtest_pci_readl(fd, bus, device, + 0, dev->bar[i].addr); + dev->bar[i].region_size = ~(val & 0xfffffff0) + 1; + break; + case QTEST_PCI_BAR_MEMORY_64: + qtest_pci_writeq(fd, bus, device, 0, + dev->bar[i].addr, 0xffffffffffffffff); + val64 = qtest_pci_readq(fd, bus, device, + 0, dev->bar[i].addr); + dev->bar[i].region_size = + ~(val64 & 0xfffffffffffffff0) + 1; + break; + case QTEST_PCI_BAR_DISABLE: + break; + } + } + + /* Set BAR region */ + for (i = 0; i < NB_PCI_BAR; i++) { + switch (dev->bar[i].type) { + case QTEST_PCI_BAR_IO: + case QTEST_PCI_BAR_MEMORY_UNDER_1MB: + case QTEST_PCI_BAR_MEMORY_32: + qtest_pci_writel(fd, bus, device, 0, dev->bar[i].addr, + dev->bar[i].region_start); + PMD_DRV_LOG(INFO, "Set BAR of %s device: 0x%lx - 0x%lx\n", + dev->name, dev->bar[i].region_start, + dev->bar[i].region_start + dev->bar[i].region_size); + break; + case QTEST_PCI_BAR_MEMORY_64: + qtest_pci_writeq(fd, bus, device, 0, dev->bar[i].addr, + dev->bar[i].region_start); + PMD_DRV_LOG(INFO, "Set BAR of %s device: 0x%lx - 0x%lx\n", + dev->name, dev->bar[i].region_start, + dev->bar[i].region_start + dev->bar[i].region_size); + break; + case QTEST_PCI_BAR_DISABLE: + break; + } + + } + + return 0; + +error: + PMD_DRV_LOG(ERR, "Unexpected BAR type\n"); + return -1; +} + +static int +qtest_try_init_pci_device(struct qtest_session *s, uint16_t bus, uint8_t device) +{ + struct qtest_pci_device *dev; + uint32_t val; + + val = qtest_pci_readl(s->qtest_socket, bus, device, 0, 0); + TAILQ_FOREACH(dev, &s->head, next) { + if (val == ((uint32_t)dev->device_id << 16 | dev->vendor_id)) { + dev->bus_addr = bus; + dev->device_addr = device; + return dev->init(s->qtest_socket, dev); + } + } + + return 0; +} + +static int +qtest_init_pci_devices(struct qtest_session *s) +{ + uint16_t bus; + uint8_t device; + int ret; + + bus = 0; + do { + device = 0; + do { + ret = qtest_try_init_pci_device(s, bus, device); + if (ret != 0) + return ret; + } + while (device++ != NB_DEVICE - 1); + } while (bus++ != NB_BUS - 1); + + return 0; +} + +static void +qtest_close_session(struct qtest_session *s) +{ + close(s->qtest_socket); + close(s->ivshmem_socket); +} + +static int +qtest_register_target_devices(struct qtest_session *s) +{ + struct qtest_pci_device *virtio_net, *ivshmem; + void *addr; + + virtio_net = malloc(sizeof(*virtio_net)); + if (virtio_net == NULL) + return -1; + + ivshmem = malloc(sizeof(*ivshmem)); + if (ivshmem == NULL) + return -1; + + memset(virtio_net, 0, sizeof(*virtio_net)); + memset(ivshmem, 0, sizeof(*ivshmem)); + + TAILQ_INIT(&s->head); + + virtio_net->name = "virtio-net"; + virtio_net->device_id = 0x1000; + virtio_net->vendor_id = 0x1af4; + virtio_net->init = qtest_init_pci_device; + virtio_net->bar[0].addr = REG_ADDR_BAR0; + virtio_net->bar[0].type = QTEST_PCI_BAR_IO; + virtio_net->bar[0].region_start = VIRTIO_NET_IO_START; + + TAILQ_INSERT_TAIL(&s->head, virtio_net, next); + + ivshmem->name = "ivshmem"; + ivshmem->device_id = 0x1110; + ivshmem->vendor_id = 0x1af4; + ivshmem->init = qtest_init_pci_device; + ivshmem->bar[0].addr = REG_ADDR_BAR0; + ivshmem->bar[0].type = QTEST_PCI_BAR_MEMORY_32; + ivshmem->bar[0].region_start = 0; + ivshmem->bar[1].addr = REG_ADDR_BAR2; + ivshmem->bar[1].type = QTEST_PCI_BAR_MEMORY_64; + + rte_memseg_info_get(0, NULL, NULL, &addr); + ivshmem->bar[1].region_start = rte_mem_virt2phy(addr); + + TAILQ_INSERT_TAIL(&s->head, ivshmem, next); + + return 0; +} + +static int +qtest_send_message_to_ivshmem(int sock_fd, uint64_t client_id, int shm_fd) +{ + struct iovec iov; + struct msghdr msgh; + size_t fdsize = sizeof(int); + char control[CMSG_SPACE(fdsize)]; + struct cmsghdr *cmsg; + int ret; + + memset(&msgh, 0, sizeof(msgh)); + iov.iov_base = &client_id; + iov.iov_len = sizeof(client_id); + + msgh.msg_iov = &iov; + msgh.msg_iovlen = 1; + + if (shm_fd >= 0) { + msgh.msg_control = &control; + msgh.msg_controllen = sizeof(control); + cmsg = CMSG_FIRSTHDR(&msgh); + cmsg->cmsg_len = CMSG_LEN(fdsize); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + memcpy(CMSG_DATA(cmsg), &shm_fd, fdsize); + } + + do { + ret = sendmsg(sock_fd, &msgh, 0); + } while (ret < 0 && errno == EINTR); + + if (ret < 0) { + PMD_DRV_LOG(ERR, "sendmsg error\n"); + return ret; + } + + return ret; +} + +static int +qtest_setup_shared_memory(struct qtest_session *s) +{ + int ret; + + /* send our protocol version first */ + ret = qtest_send_message_to_ivshmem(s->ivshmem_socket, + IVSHMEM_PROTOCOL_VERSION, -1); + if (ret < 0) { + PMD_DRV_LOG(ERR, + "Failed to send protocol version to ivshmem\n"); + return -1; + } + + /* send cliend id */ + ret = qtest_send_message_to_ivshmem(s->ivshmem_socket, 0, -1); + if (ret < 0) { + PMD_DRV_LOG(ERR, "Failed to send VMID to ivshmem\n"); + return -1; + } + + /* send shm_fd */ + ret = qtest_send_message_to_ivshmem(s->ivshmem_socket, -1, s->shm_fd); + if (ret < 0) { + PMD_DRV_LOG(ERR, "Failed to file descriptor to ivshmem\n"); + return -1; + } + + return 0; +} + +static void +qtest_remove_target_devices(struct qtest_session *s) +{ + struct qtest_pci_device *dev, *next; + + for (dev = TAILQ_FIRST(&s->head); dev != NULL; dev = next) { + next = TAILQ_NEXT(dev, next); + TAILQ_REMOVE(&s->head, dev, next); + free(dev); + } +} + +int +qtest_vdev_init(struct rte_eth_dev_data *data, + int qtest_socket, int ivshmem_socket) +{ + struct virtio_hw *hw = data->dev_private; + struct qtest_session *s; + uint64_t size = 0; + int ret; + int shm_fd; + + s = malloc(sizeof(*s)); + + ret = qtest_register_target_devices(s); + if (ret != 0) { + PMD_DRV_LOG(ERR, "Failed to initialize qtest session\n"); + return -1; + } + + rte_memseg_info_get(0, &shm_fd, &size, NULL); + + s->qtest_socket = qtest_socket; + s->ivshmem_socket = ivshmem_socket; + s->shm_fd = shm_fd; + + ret = qtest_setup_shared_memory(s); + if (ret != 0) { + PMD_DRV_LOG(ERR, "Failed to setup shared memory\n"); + return -1; + } + + ret = qtest_init_pci_devices(s); + if (ret != 0) { + PMD_DRV_LOG(ERR, "Failed to initialize devices\n"); + return -1; + } + + hw->qsession = (void *)s; + + return 0; +} + +void +qtest_vdev_uninit(struct rte_eth_dev_data *data) +{ + struct virtio_hw *hw = data->dev_private; + struct qtest_session *s; + + s = (struct qtest_session *)hw->qsession; + qtest_close_session(s); + qtest_remove_target_devices(s); +} + +void +virtio_ioport_write(struct virtio_hw *hw, uint64_t addr, uint64_t val) +{ + struct qtest_session *s = (struct qtest_session *)hw->qsession; + + return qtest_outl(s->qtest_socket, + VIRTIO_NET_IO_START + (uint16_t)addr, val); +} + +uint32_t +virtio_ioport_read(struct virtio_hw *hw, uint64_t addr) +{ + struct qtest_session *s = (struct qtest_session *)hw->qsession; + + return qtest_inl(s->qtest_socket, + VIRTIO_NET_IO_START + (uint16_t)addr); +} + diff --git a/drivers/net/virtio/virtio_ethdev.c b/drivers/net/virtio/virtio_ethdev.c index 74c00ee..ea42ef1 100644 --- a/drivers/net/virtio/virtio_ethdev.c +++ b/drivers/net/virtio/virtio_ethdev.c @@ -36,6 +36,11 @@ #include #include #include +#ifdef RTE_VIRTIO_VDEV +#include +#include +#include +#endif #ifdef RTE_EXEC_ENV_LINUXAPP #include #include @@ -56,6 +61,9 @@ #include #include #include +#ifdef RTE_VIRTIO_VDEV +#include +#endif #include "virtio_ethdev.h" #include "virtio_pci.h" @@ -491,8 +499,10 @@ virtio_dev_close(struct rte_eth_dev *dev) PMD_INIT_LOG(DEBUG, "virtio_dev_close"); /* reset the NIC */ - if (pci_dev->driver->drv_flags & RTE_PCI_DRV_INTR_LSC) + if ((dev->dev_type == RTE_ETH_DEV_PCI) && + (pci_dev->driver->drv_flags & RTE_PCI_DRV_INTR_LSC)) { vtpci_irq_config(hw, VIRTIO_MSI_NO_VECTOR); + } vtpci_reset(hw); hw->started = 0; virtio_dev_free_mbufs(dev); @@ -1266,7 +1276,7 @@ eth_virtio_dev_init(struct rte_eth_dev *eth_dev) struct virtio_hw *hw = eth_dev->data->dev_private; struct virtio_net_config *config; struct virtio_net_config local_config; - struct rte_pci_device *pci_dev; + struct rte_pci_device *pci_dev = eth_dev->pci_dev; RTE_BUILD_BUG_ON(RTE_PKTMBUF_HEADROOM < sizeof(struct virtio_net_hdr)); @@ -1287,15 +1297,18 @@ eth_virtio_dev_init(struct rte_eth_dev *eth_dev) return -ENOMEM; } - pci_dev = eth_dev->pci_dev; + if (eth_dev->dev_type == RTE_ETH_DEV_PCI) { + rte_eth_copy_pci_info(eth_dev, pci_dev); - rte_eth_copy_pci_info(eth_dev, pci_dev); + if (virtio_resource_init(pci_dev) < 0) + return -1; - if (virtio_resource_init(pci_dev) < 0) - return -1; - - hw->use_msix = virtio_has_msix(&pci_dev->addr); - hw->io_base = (uint32_t)(uintptr_t)pci_dev->mem_resource[0].addr; + hw->use_msix = virtio_has_msix(&pci_dev->addr); + hw->io_base = (uint32_t)(uintptr_t)pci_dev->mem_resource[0].addr; + } else { + hw->use_msix = 0; + hw->io_base = 0; + } /* Reset the device although not necessary at startup */ vtpci_reset(hw); @@ -1308,8 +1321,10 @@ eth_virtio_dev_init(struct rte_eth_dev *eth_dev) virtio_negotiate_features(hw); /* If host does not support status then disable LSC */ - if (!vtpci_with_feature(hw, VIRTIO_NET_F_STATUS)) + if ((eth_dev->dev_type == RTE_ETH_DEV_PCI) && + (!vtpci_with_feature(hw, VIRTIO_NET_F_STATUS))) { pci_dev->driver->drv_flags &= ~RTE_PCI_DRV_INTR_LSC; + } rx_func_get(eth_dev); @@ -1385,14 +1400,16 @@ eth_virtio_dev_init(struct rte_eth_dev *eth_dev) PMD_INIT_LOG(DEBUG, "hw->max_rx_queues=%d hw->max_tx_queues=%d", hw->max_rx_queues, hw->max_tx_queues); - PMD_INIT_LOG(DEBUG, "port %d vendorID=0x%x deviceID=0x%x", - eth_dev->data->port_id, pci_dev->id.vendor_id, - pci_dev->id.device_id); + if (eth_dev->dev_type == RTE_ETH_DEV_PCI) { + PMD_INIT_LOG(DEBUG, "port %d vendorID=0x%x deviceID=0x%x", + eth_dev->data->port_id, pci_dev->id.vendor_id, + pci_dev->id.device_id); - /* Setup interrupt callback */ - if (pci_dev->driver->drv_flags & RTE_PCI_DRV_INTR_LSC) - rte_intr_callback_register(&pci_dev->intr_handle, - virtio_interrupt_handler, eth_dev); + /* Setup interrupt callback */ + if (pci_dev->driver->drv_flags & RTE_PCI_DRV_INTR_LSC) + rte_intr_callback_register(&pci_dev->intr_handle, + virtio_interrupt_handler, eth_dev); + } virtio_dev_cq_start(eth_dev); @@ -1426,10 +1443,12 @@ eth_virtio_dev_uninit(struct rte_eth_dev *eth_dev) eth_dev->data->mac_addrs = NULL; /* reset interrupt callback */ - if (pci_dev->driver->drv_flags & RTE_PCI_DRV_INTR_LSC) + if ((eth_dev->dev_type == RTE_ETH_DEV_PCI) && + (pci_dev->driver->drv_flags & RTE_PCI_DRV_INTR_LSC)) { rte_intr_callback_unregister(&pci_dev->intr_handle, virtio_interrupt_handler, eth_dev); + } PMD_INIT_LOG(DEBUG, "dev_uninit completed"); @@ -1493,11 +1512,13 @@ virtio_dev_configure(struct rte_eth_dev *dev) return -ENOTSUP; } - if (pci_dev->driver->drv_flags & RTE_PCI_DRV_INTR_LSC) + if ((dev->dev_type == RTE_ETH_DEV_PCI) && + (pci_dev->driver->drv_flags & RTE_PCI_DRV_INTR_LSC)) { if (vtpci_irq_config(hw, 0) == VIRTIO_MSI_NO_VECTOR) { PMD_DRV_LOG(ERR, "failed to set config vector"); return -EBUSY; } + } return 0; } @@ -1511,7 +1532,8 @@ virtio_dev_start(struct rte_eth_dev *dev) struct rte_pci_device *pci_dev = dev->pci_dev; /* check if lsc interrupt feature is enabled */ - if (dev->data->dev_conf.intr_conf.lsc) { + if ((dev->dev_type == RTE_ETH_DEV_PCI) && + (dev->data->dev_conf.intr_conf.lsc)) { if (!(pci_dev->driver->drv_flags & RTE_PCI_DRV_INTR_LSC)) { PMD_DRV_LOG(ERR, "link status not supported by host"); return -ENOTSUP; @@ -1617,8 +1639,10 @@ virtio_dev_stop(struct rte_eth_dev *dev) PMD_INIT_LOG(DEBUG, "stop"); - if (dev->data->dev_conf.intr_conf.lsc) + if ((dev->dev_type == RTE_ETH_DEV_PCI) && + (dev->data->dev_conf.intr_conf.lsc)) { rte_intr_disable(&dev->pci_dev->intr_handle); + } memset(&link, 0, sizeof(link)); virtio_dev_atomic_write_link_status(dev, &link); @@ -1691,3 +1715,151 @@ static struct rte_driver rte_virtio_driver = { }; PMD_REGISTER_DRIVER(rte_virtio_driver); + +#ifdef RTE_VIRTIO_VDEV + +#define ETH_CVIO_ARG_QTEST_PATH "qtest" +#define ETH_CVIO_ARG_IVSHMEM_PATH "ivshmem" + +/*TODO: specify mac addr */ +static const char *valid_args[] = { + ETH_CVIO_ARG_QTEST_PATH, + ETH_CVIO_ARG_IVSHMEM_PATH, + NULL +}; + +static int +get_string_arg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + int ret, fd, loop = 3; + int *pfd = extra_args; + struct sockaddr_un sa = {0}; + + if ((value == NULL) || (extra_args == NULL)) + return -EINVAL; + + fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd < 0) + return -1; + + sa.sun_family = AF_UNIX; + strncpy(sa.sun_path, value, sizeof(sa.sun_path)); + + /* We need to wait QEMU until socket will be prepared */ + while (loop--) { + ret = connect(fd, (struct sockaddr*)&sa, + sizeof(struct sockaddr_un)); + if (ret != 0) + sleep(1); + else + break; + } + + if (ret != 0) { + close(fd); + return -1; + } + + *pfd = fd; + + return 0; +} + +static struct rte_eth_dev * +cvio_eth_dev_alloc(const char *name) +{ + struct rte_eth_dev *eth_dev; + struct rte_eth_dev_data *data; + struct virtio_hw *hw; + + eth_dev = rte_eth_dev_allocate(name, RTE_ETH_DEV_VIRTUAL); + if (eth_dev == NULL) + rte_panic("cannot alloc rte_eth_dev\n"); + + data = eth_dev->data; + + hw = rte_zmalloc(NULL, sizeof(*hw), 0); + if (!hw) + rte_panic("malloc virtio_hw failed\n"); + + data->dev_private = hw; + /* will be used in virtio_dev_info_get() */ + eth_dev->driver = &rte_virtio_pmd; + /* TAILQ_INIT(&(eth_dev->link_intr_cbs)); */ + return eth_dev; +} + +/* + * Dev initialization routine. + * Invoked once for each virtio vdev at EAL init time, + * See rte_eal_dev_init(). + * Returns 0 on success. + */ +static int +rte_cvio_pmd_devinit(const char *name, const char *params) +{ + struct rte_kvargs *kvlist = NULL; + struct rte_eth_dev *eth_dev = NULL; + int ret, qtest_sock, ivshmem_sock; + + if (params == NULL || params[0] == '\0') { + rte_panic("param is null\n"); + } + + kvlist = rte_kvargs_parse(params, valid_args); + if (!kvlist) + rte_panic("error when parsing param\n"); + + if (rte_kvargs_count(kvlist, ETH_CVIO_ARG_IVSHMEM_PATH) == 1) { + ret = rte_kvargs_process(kvlist, ETH_CVIO_ARG_IVSHMEM_PATH, + &get_string_arg, &ivshmem_sock); + if (ret != 0) { + PMD_INIT_LOG(ERR, + "Failed to connect to ivshmem socket"); + return -1; + } + } else { + rte_panic("no arg: %s\n", ETH_CVIO_ARG_IVSHMEM_PATH); + } + + if (rte_kvargs_count(kvlist, ETH_CVIO_ARG_QTEST_PATH) == 1) { + ret = rte_kvargs_process(kvlist, ETH_CVIO_ARG_QTEST_PATH, + &get_string_arg, &qtest_sock); + if (ret != 0) { + PMD_INIT_LOG(ERR, + "Failed to connect to qtest socket"); + return -1; + } + } else { + rte_panic("no arg: %s\n", ETH_CVIO_ARG_QTEST_PATH); + } + + eth_dev = cvio_eth_dev_alloc(name); + + qtest_vdev_init(eth_dev->data, qtest_sock, ivshmem_sock); + + /* originally, this will be called in rte_eal_pci_probe() */ + eth_virtio_dev_init(eth_dev); + + return 0; +} + +static int +rte_cvio_pmd_devuninit(const char *name) +{ + /* TODO: Support port hotlug */ + rte_panic("%s", name); + return 0; +} + +static struct rte_driver rte_cvio_driver = { + .name = "eth_cvio", + .type = PMD_VDEV, + .init = rte_cvio_pmd_devinit, + .uninit = rte_cvio_pmd_devuninit, +}; + +PMD_REGISTER_DRIVER(rte_cvio_driver); + +#endif /* RTE_VIRTIO_VDEV */ diff --git a/drivers/net/virtio/virtio_ethdev.h b/drivers/net/virtio/virtio_ethdev.h index ae2d47d..4f12c53 100644 --- a/drivers/net/virtio/virtio_ethdev.h +++ b/drivers/net/virtio/virtio_ethdev.h @@ -56,6 +56,16 @@ #define VIRTIO_MAX_RX_PKTLEN 9728 /* Features desired/implemented by this driver. */ +#ifdef RTE_VIRTIO_VDEV +#define VIRTIO_PMD_GUEST_FEATURES \ + (1u << VIRTIO_NET_F_MAC | \ + 1u << VIRTIO_NET_F_MQ | \ + 1u << VIRTIO_NET_F_CTRL_MAC_ADDR | \ + 1u << VIRTIO_NET_F_CTRL_VQ | \ + 1u << VIRTIO_NET_F_CTRL_RX | \ + 1u << VIRTIO_NET_F_CTRL_VLAN | \ + 1u << VIRTIO_NET_F_MRG_RXBUF) +#else /* RTE_VIRTIO_VDEV */ #define VIRTIO_PMD_GUEST_FEATURES \ (1u << VIRTIO_NET_F_MAC | \ 1u << VIRTIO_NET_F_STATUS | \ @@ -65,6 +75,7 @@ 1u << VIRTIO_NET_F_CTRL_RX | \ 1u << VIRTIO_NET_F_CTRL_VLAN | \ 1u << VIRTIO_NET_F_MRG_RXBUF) +#endif /* RTE_VIRTIO_VDEV */ /* * CQ function prototype @@ -122,5 +133,10 @@ uint16_t virtio_xmit_pkts_simple(void *tx_queue, struct rte_mbuf **tx_pkts, #define VTNET_LRO_FEATURES (VIRTIO_NET_F_GUEST_TSO4 | \ VIRTIO_NET_F_GUEST_TSO6 | VIRTIO_NET_F_GUEST_ECN) +#ifdef RTE_VIRTIO_VDEV +int qtest_vdev_init(struct rte_eth_dev_data *data, + int qtest_socket, int ivshmem_socket); +void qtest_vdev_uninit(struct rte_eth_dev_data *data); +#endif #endif /* _VIRTIO_ETHDEV_H_ */ diff --git a/drivers/net/virtio/virtio_pci.h b/drivers/net/virtio/virtio_pci.h index 47f722a..fe884de 100644 --- a/drivers/net/virtio/virtio_pci.h +++ b/drivers/net/virtio/virtio_pci.h @@ -165,6 +165,9 @@ struct virtqueue; struct virtio_hw { struct virtqueue *cvq; +#ifdef RTE_VIRTIO_VDEV + void *qsession; +#endif uint32_t io_base; uint32_t guest_features; uint32_t max_tx_queues; @@ -226,6 +229,26 @@ outl_p(unsigned int data, unsigned int port) } #endif +#ifdef RTE_VIRTIO_VDEV + +uint32_t virtio_ioport_read(struct virtio_hw *, uint64_t); +void virtio_ioport_write(struct virtio_hw *, uint64_t, uint64_t); + +#define VIRTIO_READ_REG_1(hw, reg) \ + virtio_ioport_read(hw, reg) +#define VIRTIO_WRITE_REG_1(hw, reg, value) \ + virtio_ioport_write(hw, reg, value) +#define VIRTIO_READ_REG_2(hw, reg) \ + virtio_ioport_read(hw, reg) +#define VIRTIO_WRITE_REG_2(hw, reg, value) \ + virtio_ioport_write(hw, reg, value) +#define VIRTIO_READ_REG_4(hw, reg) \ + virtio_ioport_read(hw, reg) +#define VIRTIO_WRITE_REG_4(hw, reg, value) \ + virtio_ioport_write(hw, reg, value) + +#else /* RTE_VIRTIO_VDEV */ + #define VIRTIO_PCI_REG_ADDR(hw, reg) \ (unsigned short)((hw)->io_base + (reg)) @@ -244,6 +267,8 @@ outl_p(unsigned int data, unsigned int port) #define VIRTIO_WRITE_REG_4(hw, reg, value) \ outl_p((unsigned int)(value), (VIRTIO_PCI_REG_ADDR((hw), (reg)))) +#endif /* RTE_VIRTIO_VDEV */ + static inline int vtpci_with_feature(struct virtio_hw *hw, uint32_t bit) { -- 2.1.4