get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/1157/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 1157,
    "url": "https://patches.dpdk.org/api/patches/1157/?format=api",
    "web_url": "https://patches.dpdk.org/project/dpdk/patch/1415272471-3299-8-git-send-email-mukawa@igel.co.jp/",
    "project": {
        "id": 1,
        "url": "https://patches.dpdk.org/api/projects/1/?format=api",
        "name": "DPDK",
        "link_name": "dpdk",
        "list_id": "dev.dpdk.org",
        "list_email": "dev@dpdk.org",
        "web_url": "http://core.dpdk.org",
        "scm_url": "git://dpdk.org/dpdk",
        "webscm_url": "http://git.dpdk.org/dpdk",
        "list_archive_url": "https://inbox.dpdk.org/dev",
        "list_archive_url_format": "https://inbox.dpdk.org/dev/{}",
        "commit_url_format": ""
    },
    "msgid": "<1415272471-3299-8-git-send-email-mukawa@igel.co.jp>",
    "list_archive_url": "https://inbox.dpdk.org/dev/1415272471-3299-8-git-send-email-mukawa@igel.co.jp",
    "date": "2014-11-06T11:14:31",
    "name": "[dpdk-dev,RFC,7/7] lib/librte_vhost: Add vhost-user implementation",
    "commit_ref": null,
    "pull_url": null,
    "state": "rfc",
    "archived": true,
    "hash": "7997cca6afb2840df2a9d5e74196f2a02f46bbfb",
    "submitter": {
        "id": 64,
        "url": "https://patches.dpdk.org/api/people/64/?format=api",
        "name": "Tetsuya Mukawa",
        "email": "mukawa@igel.co.jp"
    },
    "delegate": null,
    "mbox": "https://patches.dpdk.org/project/dpdk/patch/1415272471-3299-8-git-send-email-mukawa@igel.co.jp/mbox/",
    "series": [],
    "comments": "https://patches.dpdk.org/api/patches/1157/comments/",
    "check": "pending",
    "checks": "https://patches.dpdk.org/api/patches/1157/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<dev-bounces@dpdk.org>",
        "X-Original-To": "patchwork@dpdk.org",
        "Delivered-To": "patchwork@dpdk.org",
        "Received": [
            "from [92.243.14.124] (localhost [IPv6:::1])\n\tby dpdk.org (Postfix) with ESMTP id C01DC7F4C;\n\tThu,  6 Nov 2014 12:05:53 +0100 (CET)",
            "from mail-pa0-f48.google.com (mail-pa0-f48.google.com\n\t[209.85.220.48]) by dpdk.org (Postfix) with ESMTP id E1D167F34\n\tfor <dev@dpdk.org>; Thu,  6 Nov 2014 12:05:43 +0100 (CET)",
            "by mail-pa0-f48.google.com with SMTP id ey11so1051510pad.35\n\tfor <dev@dpdk.org>; Thu, 06 Nov 2014 03:15:10 -0800 (PST)",
            "from localhost.localdomain (napt.igel.co.jp. [219.106.231.132])\n\tby mx.google.com with ESMTPSA id\n\tjc3sm5652315pbb.49.2014.11.06.03.15.08 for <multiple recipients>\n\t(version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128);\n\tThu, 06 Nov 2014 03:15:09 -0800 (PST)"
        ],
        "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20130820;\n\th=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to\n\t:references;\n\tbh=COHvDcmJbsKIjeE5T7uJRO4F9BJ7MbSqiSsOOmhci4I=;\n\tb=F5fIv8Uq048mYN8KouY0s3FqMWhCnE0d+p3rLXgpQBRYqAH/Bc+1jJAd3JMAinVdrC\n\tsF0gO1OH8+N1AbEdIIhVgtljIWPMTodzh0zZpZUfoesPEUgKGnacLHuf7JLeOEikgUur\n\t1Gv4iSn6U/QNpFbJDFjQZHzjaH8XKrIIhgCbQPnbrReJbsSwkXdC9niLuQufgh30GFnL\n\tA0Uali2FTvw//usjxn48wAiBKDG6RWqRLSGSEqNcmUS251Rt4e/NUrrqynFu1YO0Zl7O\n\toDKVE/vJAuV2ivMmQGBGYr5KXj8GbwYrbb0ybP7Sp/FGK+37WhT7swnBWYytKnyFh3Xj\n\t4F7Q==",
        "X-Gm-Message-State": "ALoCoQmCArbbRk0gUy6FEpbGbUtjEFlX2OLr8OvntdOBwq1PA5sMdT5ebSw7ERFkvqSiT9lPmtlc",
        "X-Received": "by 10.70.10.195 with SMTP id k3mr3599324pdb.41.1415272510350;\n\tThu, 06 Nov 2014 03:15:10 -0800 (PST)",
        "From": "Tetsuya Mukawa <mukawa@igel.co.jp>",
        "To": "dev@dpdk.org",
        "Date": "Thu,  6 Nov 2014 20:14:31 +0900",
        "Message-Id": "<1415272471-3299-8-git-send-email-mukawa@igel.co.jp>",
        "X-Mailer": "git-send-email 1.9.1",
        "In-Reply-To": "<1415272471-3299-1-git-send-email-mukawa@igel.co.jp>",
        "References": "<1415272471-3299-1-git-send-email-mukawa@igel.co.jp>",
        "Cc": "nakajima.yoshihiro@lab.ntt.co.jp, masutani.hitoshi@lab.ntt.co.jp",
        "Subject": "[dpdk-dev] [RFC PATCH 7/7] lib/librte_vhost: Add vhost-user\n\timplementation",
        "X-BeenThere": "dev@dpdk.org",
        "X-Mailman-Version": "2.1.15",
        "Precedence": "list",
        "List-Id": "patches and discussions about DPDK <dev.dpdk.org>",
        "List-Unsubscribe": "<http://dpdk.org/ml/options/dev>,\n\t<mailto:dev-request@dpdk.org?subject=unsubscribe>",
        "List-Archive": "<http://dpdk.org/ml/archives/dev/>",
        "List-Post": "<mailto:dev@dpdk.org>",
        "List-Help": "<mailto:dev-request@dpdk.org?subject=help>",
        "List-Subscribe": "<http://dpdk.org/ml/listinfo/dev>,\n\t<mailto:dev-request@dpdk.org?subject=subscribe>",
        "Errors-To": "dev-bounces@dpdk.org",
        "Sender": "\"dev\" <dev-bounces@dpdk.org>"
    },
    "content": "This patch adds vhost-user implementation to librte_vhost.\nTo communicate with vhost-user of QEMU, speficy VHOST_DRV_USER as\na vhost_driver_type_t variable in rte_vhost_driver_register().\n\nSigned-off-by: Tetsuya Mukawa <mukawa@igel.co.jp>\n---\n lib/librte_vhost/rte_virtio_net.h  |  19 +-\n lib/librte_vhost/vhost-net-user.c  | 541 +++++++++++++++++++++++++++++++++++++\n lib/librte_vhost/vhost-net.c       |  39 ++-\n lib/librte_vhost/vhost-net.h       |   7 +\n lib/librte_vhost/virtio-net-user.c | 410 ++++++++++++++++++++++++++++\n lib/librte_vhost/virtio-net.c      |  64 ++++-\n 6 files changed, 1073 insertions(+), 7 deletions(-)\n create mode 100644 lib/librte_vhost/vhost-net-user.c\n create mode 100644 lib/librte_vhost/virtio-net-user.c",
    "diff": "diff --git a/lib/librte_vhost/rte_virtio_net.h b/lib/librte_vhost/rte_virtio_net.h\nindex a9e20ea..af07900 100644\n--- a/lib/librte_vhost/rte_virtio_net.h\n+++ b/lib/librte_vhost/rte_virtio_net.h\n@@ -75,17 +75,32 @@ struct buf_vector {\n  */\n typedef enum {\n \tVHOST_DRV_CUSE, /* cuse driver */\n+\tVHOST_DRV_USER, /* vhost-user driver */\n \tVHOST_DRV_NUM\t/* the number of vhost driver types */\n } vhost_driver_type_t;\n \n+\n+/**\n+ * Structure contains vhost-user session specific information\n+ */\n+struct vhost_user_session {\n+\tint\t\tfh;\t\t/**< session identifier */\n+\tpthread_t\ttid;\t\t/**< thread id of session handler */\n+\tint\t\tsocketfd;\t/**< fd of socket */\n+\tint\t\tinterval;\t/**< reconnection interval of session */\n+};\n+\n /**\n  * Structure contains information relating vhost driver.\n  */\n struct vhost_driver {\n \tvhost_driver_type_t\ttype;\t\t/**< driver type. */\n \tconst char\t\t*dev_name;\t/**< accessing device name. */\n+\tvoid\t\t\t*priv;\t\t/**< private data. */\n \tunion {\n \t\tstruct fuse_session *cuse_session;\t/**< fuse session. */\n+\t\tstruct vhost_user_session *user_session;\n+\t\t\t\t\t\t/**< vhost-user session. */\n \t};\n };\n \n@@ -199,9 +214,11 @@ struct vhost_driver *rte_vhost_driver_register(\n \t\tconst char *dev_name, vhost_driver_type_t type);\n \n /* Register callbacks. */\n-int rte_vhost_driver_callback_register(struct virtio_net_device_ops const * const);\n+int rte_vhost_driver_callback_register(struct vhost_driver *drv,\n+\t\t\tstruct virtio_net_device_ops const * const, void *priv);\n /* Start vhost driver session blocking loop. */\n int rte_vhost_driver_session_start(struct vhost_driver *drv);\n+void rte_vhost_driver_session_stop(struct vhost_driver *drv);\n \n /**\n  * This function adds buffers to the virtio devices RX virtqueue. Buffers can\ndiff --git a/lib/librte_vhost/vhost-net-user.c b/lib/librte_vhost/vhost-net-user.c\nnew file mode 100644\nindex 0000000..434f20f\n--- /dev/null\n+++ b/lib/librte_vhost/vhost-net-user.c\n@@ -0,0 +1,541 @@\n+/*-\n+ *   BSD LICENSE\n+ *\n+ *   Copyright (c) 2014 IGEL Co/.Ltd.\n+ *   All rights reserved.\n+ *\n+ *   Redistribution and use in source and binary forms, with or without\n+ *   modification, are permitted provided that the following conditions\n+ *   are met:\n+ *\n+ *     * Redistributions of source code must retain the above copyright\n+ *       notice, this list of conditions and the following disclaimer.\n+ *     * Redistributions in binary form must reproduce the above copyright\n+ *       notice, this list of conditions and the following disclaimer in\n+ *       the documentation and/or other materials provided with the\n+ *       distribution.\n+ *     * Neither the name of IGEL nor the names of its\n+ *       contributors may be used to endorse or promote products derived\n+ *       from this software without specific prior written permission.\n+ *\n+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n+ *   \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+ */\n+\n+#include <sys/types.h>\n+#include <sys/socket.h>\n+#include <linux/un.h>\n+\n+#define VHOST_USER_MAX_DEVICE\t\t(32)\n+#define VHOST_USER_MAX_FD_NUM\t\t(3)\n+\n+/* start id of vhost user device */\n+rte_atomic16_t vhost_user_device_id;\n+\n+static struct vhost_net_device_ops const *ops;\n+\n+typedef enum VhostUserRequest {\n+\tVHOST_USER_NONE = 0,\n+\tVHOST_USER_GET_FEATURES = 1,\n+\tVHOST_USER_SET_FEATURES = 2,\n+\tVHOST_USER_SET_OWNER = 3,\n+\tVHOST_USER_RESET_OWNER = 4,\n+\tVHOST_USER_SET_MEM_TABLE = 5,\n+\tVHOST_USER_SET_LOG_BASE = 6,\n+\tVHOST_USER_SET_LOG_FD = 7,\n+\tVHOST_USER_SET_VRING_NUM = 8,\n+\tVHOST_USER_SET_VRING_ADDR = 9,\n+\tVHOST_USER_SET_VRING_BASE = 10,\n+\tVHOST_USER_GET_VRING_BASE = 11,\n+\tVHOST_USER_SET_VRING_KICK = 12,\n+\tVHOST_USER_SET_VRING_CALL = 13,\n+\tVHOST_USER_SET_VRING_ERR = 14,\n+\tVHOST_USER_MAX\n+} VhostUserRequest;\n+\n+#define VHOST_MEMORY_MAX_NREGIONS\t8\n+\n+typedef struct VhostUserMemoryRegion {\n+\tuint64_t guest_phys_addr;\n+\tuint64_t memory_size;\n+\tuint64_t userspace_addr;\n+\tuint64_t mmap_offset;\n+} VhostUserMemoryRegion;\n+\n+typedef struct VhostUserMemory {\n+\tuint32_t nregions;\n+\tuint32_t padding;\n+\tVhostUserMemoryRegion regions[VHOST_MEMORY_MAX_NREGIONS];\n+} VhostUserMemory;\n+\n+typedef struct VhostUserMsg {\n+\tVhostUserRequest request;\n+\n+#define VHOST_USER_VERSION_MASK\t\t(0x3)\n+#define VHOST_USER_REPLY_MASK\t\t(0x1<<2)\n+\tuint32_t flags;\n+\tuint32_t size; /* the following payload size */\n+\tunion {\n+#define VHOST_USER_VRING_IDX_MASK\t(0xff)\n+#define VHOST_USER_VRING_NOFD_MASK\t(0x1<<8)\n+\t\tuint64_t u64;\n+\t\tstruct vhost_vring_state state;\n+\t\tstruct vhost_vring_addr addr;\n+\t\tVhostUserMemory memory;\n+\t};\n+} __attribute__((packed)) VhostUserMsg;\n+\n+static VhostUserMsg m __attribute__ ((unused));\n+#define VHOST_USER_HDR_SIZE\t(sizeof(m.request) \\\n+\t\t+ sizeof(m.flags) + sizeof(m.size))\n+\n+/* The version of the protocol we support */\n+#define VHOST_USER_VERSION\t\t(0x1)\n+\n+static unsigned long int ioctl_to_vhost_user_request[VHOST_USER_MAX] = {\n+\t-1,\t\t\t/* VHOST_USER_NONE */\n+\tVHOST_GET_FEATURES,\t/* VHOST_USER_GET_FEATURES */\n+\tVHOST_SET_FEATURES,\t/* VHOST_USER_SET_FEATURES */\n+\tVHOST_SET_OWNER,\t/* VHOST_USER_SET_OWNER */\n+\tVHOST_RESET_OWNER,\t/* VHOST_USER_RESET_OWNER */\n+\tVHOST_SET_MEM_TABLE,\t/* VHOST_USER_SET_MEM_TABLE */\n+\tVHOST_SET_LOG_BASE,\t/* VHOST_USER_SET_LOG_BASE */\n+\tVHOST_SET_LOG_FD,\t/* VHOST_USER_SET_LOG_FD */\n+\tVHOST_SET_VRING_NUM,\t/* VHOST_USER_SET_VRING_NUM */\n+\tVHOST_SET_VRING_ADDR,\t/* VHOST_USER_SET_VRING_ADDR */\n+\tVHOST_SET_VRING_BASE,\t/* VHOST_USER_SET_VRING_BASE */\n+\tVHOST_GET_VRING_BASE,\t/* VHOST_USER_GET_VRING_BASE */\n+\tVHOST_SET_VRING_KICK,\t/* VHOST_USER_SET_VRING_KICK */\n+\tVHOST_SET_VRING_CALL,\t/* VHOST_USER_SET_VRING_CALL */\n+\tVHOST_SET_VRING_ERR\t/* VHOST_USER_SET_VRING_ERR */\n+};\n+\n+/**\n+ * Returns vhost_device_ctx from given fuse_req_t. The index is populated later when\n+ * the device is added to the device linked list.\n+ */\n+static struct vhost_device_ctx\n+vhost_driver_to_vhost_ctx(struct vhost_driver *drv)\n+{\n+\tstruct vhost_device_ctx ctx;\n+\tint device_id = drv->user_session->fh;\n+\n+\tctx.type = VHOST_DRV_USER;\n+\tctx.fh = device_id;\n+\tctx.user.drv = drv;\n+\n+\treturn ctx;\n+}\n+\n+/**\n+ * When the device is created in QEMU it gets initialised here and added to the device linked list.\n+ */\n+static int\n+vhost_user_open(struct vhost_driver *drv)\n+{\n+\tstruct vhost_device_ctx ctx = vhost_driver_to_vhost_ctx(drv);\n+\n+\tint ret;\n+\n+\tret = ops->new_device(ctx);\n+\tif (ret == -1)\n+\t\treturn -1;\n+\n+\tRTE_LOG(INFO, VHOST_CONFIG, \"(%\"PRIu64\") Device configuration started\\n\", ctx.fh);\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * When QEMU is shutdown or killed the device gets released.\n+ */\n+static void\n+vhost_user_release(struct vhost_driver *drv)\n+{\n+\tstruct vhost_device_ctx ctx = vhost_driver_to_vhost_ctx(drv);\n+\n+\tops->destroy_device(ctx);\n+\tRTE_LOG(INFO, VHOST_CONFIG, \"(%\"PRIu64\") Device released\\n\", ctx.fh);\n+}\n+\n+/**\n+ * Send data to vhost-user device on a QEMU.\n+ */\n+static int\n+vhost_user_write(struct vhost_driver *drv, VhostUserMsg *msg,\n+\t\tint *fds, size_t fd_num)\n+{\n+\tint fd, len;\n+\tsize_t fd_size = fd_num * sizeof(int);\n+\tchar control[CMSG_SPACE(fd_size)];\n+\tstruct msghdr msg_header;\n+\tstruct iovec iov[1];\n+\tstruct cmsghdr *cmsg_header;\n+\tstruct vhost_device_ctx ctx = vhost_driver_to_vhost_ctx(drv);\n+\n+\tif ((drv == NULL) || (msg == NULL))\n+\t\treturn -EINVAL;\n+\n+\tfd = drv->user_session->socketfd;\n+\n+\tmemset(&msg_header, 0, sizeof(msg_header));\n+\tmemset(control, 0, sizeof(control));\n+\n+\t/* set the payload */\n+\tiov[0].iov_base = (void *)msg;\n+\tiov[0].iov_len = VHOST_USER_HDR_SIZE + msg->size;\n+\n+\tmsg_header.msg_iov = iov;\n+\tmsg_header.msg_iovlen = 1;\n+\n+\tif (fd_num) {\n+\t\tmsg_header.msg_control = control;\n+\t\tmsg_header.msg_controllen = sizeof(control);\n+\t\tcmsg_header = CMSG_FIRSTHDR(&msg_header);\n+\t\tcmsg_header->cmsg_len = CMSG_LEN(fd_size);\n+\t\tcmsg_header->cmsg_level = SOL_SOCKET;\n+\t\tcmsg_header->cmsg_type = SCM_RIGHTS;\n+\t\tmemcpy(CMSG_DATA(cmsg_header), fds, fd_size);\n+\t} else {\n+\t\tmsg_header.msg_control = 0;\n+\t\tmsg_header.msg_controllen = 0;\n+\t}\n+\n+\tdo {\n+\t\tlen = sendmsg(fd, &msg_header, 0);\n+\t} while (len < 0 && errno == EINTR);\n+\n+\tif (len < 0)\n+\t\tgoto error;\n+\n+\treturn 0;\n+\n+error:\n+\tRTE_LOG(INFO, VHOST_CONFIG, \"(%\"PRIu64\") Device cannot send message\\n\", ctx.fh);\n+\treturn -EFAULT;\n+}\n+\n+/**\n+ * Receive data from vhost-user device on a QEMU.\n+ */\n+static int\n+vhost_user_read(struct vhost_driver *drv, VhostUserMsg *msg,\n+\t\tint *fds, size_t *fd_num)\n+{\n+\tint fd, len;\n+\tsize_t fd_size = (*fd_num) * sizeof(int);\n+\tchar control[CMSG_SPACE(fd_size)];\n+\tstruct msghdr msg_header;\n+\tstruct iovec iov[1];\n+\tstruct cmsghdr *cmsg_header;\n+\tstruct vhost_device_ctx ctx = vhost_driver_to_vhost_ctx(drv);\n+\n+\tif ((drv == NULL) || (msg == NULL))\n+\t\treturn -EINVAL;\n+\n+\tfd = drv->user_session->socketfd;\n+\n+\tmemset(&msg_header, 0, sizeof(msg_header));\n+\tmemset(control, 0, sizeof(control));\n+\t*fd_num = 0;\n+\n+\t/* set the payload */\n+\tiov[0].iov_base = (void *)msg;\n+\tiov[0].iov_len = VHOST_USER_HDR_SIZE;\n+\n+\tmsg_header.msg_iov = iov;\n+\tmsg_header.msg_iovlen = 1;\n+\tmsg_header.msg_control = control;\n+\tmsg_header.msg_controllen = sizeof(control);\n+\n+\tif ((len = recvmsg(fd, &msg_header, 0)) <= 0)\n+\t\tgoto error;\n+\n+\tif (msg_header.msg_flags & (MSG_TRUNC | MSG_CTRUNC))\n+\t\tgoto error;\n+\n+\tcmsg_header = CMSG_FIRSTHDR(&msg_header);\n+\tif (cmsg_header && cmsg_header->cmsg_len > 0 &&\n+\t\t\tcmsg_header->cmsg_level == SOL_SOCKET &&\n+\t\t\tcmsg_header->cmsg_type == SCM_RIGHTS) {\n+\t\tif (fd_size >= cmsg_header->cmsg_len - CMSG_LEN(0)) {\n+\t\t\tfd_size = cmsg_header->cmsg_len - CMSG_LEN(0);\n+\t\t\tmemcpy(fds, CMSG_DATA(cmsg_header), fd_size);\n+\t\t\t*fd_num = fd_size / sizeof(int);\n+\t\t}\n+\t}\n+\n+\tif (read(fd, ((char *)msg) + len, msg->size) < 0)\n+\t\tgoto error;\n+\n+\treturn 0;\n+\n+error:\n+\tRTE_LOG(INFO, VHOST_CONFIG, \"(%\"PRIu64\") Device cannot receive message\\n\", ctx.fh);\n+\treturn -EFAULT;\n+}\n+\n+/*\n+ * Boilerplate code for vhost-user IOCTL\n+ * Implicit arguments: ctx, req, result.\n+ */\n+#define VHOST_USER_IOCTL(func) do {\t\\\n+\tresult = (func)(ctx);\t\t\\\n+} while (0)\n+\n+/*\n+ * Boilerplate code for vhost-user Read IOCTL\n+ * Implicit arguments: ctx, req, result, in_bufsz, in_buf.\n+ */\n+#define VHOST_USER_IOCTL_R(type, var, func) do {\\\n+\tresult = func(ctx, &(var));\t\t\\\n+} while (0)\n+\n+/*\n+ * Boilerplate code for vhost-user Write IOCTL\n+ * Implicit arguments: ctx, req, result, out_bufsz.\n+ */\n+#define\tVHOST_USER_IOCTL_W(type, var, func) do {\\\n+\tresult = (func)(ctx, &(var));\t\t\\\n+\tmsg->flags |= VHOST_USER_REPLY_MASK;\t\\\n+\tmsg->size = sizeof(type);\t\t\\\n+\tvhost_user_write(drv, msg, NULL, 0);\t\\\n+} while (0)\n+\n+/*\n+ * Boilerplate code for vhost-user Read/Write IOCTL\n+ * Implicit arguments: ctx, req, result, in_bufsz, in_buf.\n+ */\n+#define VHOST_USER_IOCTL_RW(type1, var1, type2, var2, func) do {\\\n+\tresult = (func)(ctx, (var1), &(var2));\t\t\t\\\n+\tmsg->flags |= VHOST_USER_REPLY_MASK;\t\t\t\\\n+\tmsg->size = sizeof(type2);\t\t\t\t\\\n+\tvhost_user_write(drv, msg, NULL, 0);\t\t\t\\\n+} while (0)\n+\n+/**\n+ * The IOCTLs are handled using unix domain socket in userspace.\n+ */\n+\tstatic int\n+vhost_user_ioctl(struct vhost_driver *drv, VhostUserMsg *msg,\n+\t\tint *fds, int fd_num)\n+{\n+\tstruct vhost_device_ctx ctx = vhost_driver_to_vhost_ctx(drv);\n+\tstruct vhost_vring_file file;\n+\tint result = 0;\n+\n+\tswitch (ioctl_to_vhost_user_request[msg->request]) {\n+\tcase VHOST_GET_FEATURES:\n+\t\tLOG_DEBUG(VHOST_CONFIG, \"(%\"PRIu64\") IOCTL: VHOST_GET_FEATURES\\n\", ctx.fh);\n+\t\tVHOST_USER_IOCTL_W(uint64_t, msg->u64, ops->get_features);\n+\t\tbreak;\n+\n+\tcase VHOST_SET_FEATURES:\n+\t\tLOG_DEBUG(VHOST_CONFIG, \"(%\"PRIu64\") IOCTL: VHOST_SET_FEATURES\\n\", ctx.fh);\n+\t\tVHOST_USER_IOCTL_R(uint64_t, msg->u64, ops->set_features);\n+\t\tbreak;\n+\n+\tcase VHOST_RESET_OWNER:\n+\t\tLOG_DEBUG(VHOST_CONFIG, \"(%\"PRIu64\") IOCTL: VHOST_RESET_OWNER\\n\", ctx.fh);\n+\t\tVHOST_USER_IOCTL(ops->reset_owner);\n+\t\tbreak;\n+\n+\tcase VHOST_SET_OWNER:\n+\t\tLOG_DEBUG(VHOST_CONFIG, \"(%\"PRIu64\") IOCTL: VHOST_SET_OWNER\\n\", ctx.fh);\n+\t\tVHOST_USER_IOCTL(ops->set_owner);\n+\t\tbreak;\n+\n+\tcase VHOST_SET_MEM_TABLE:\n+\t\t/*TODO fix race condition.*/\n+\t\tLOG_DEBUG(VHOST_CONFIG, \"(%\"PRIu64\") IOCTL: VHOST_SET_MEM_TABLE\\n\", ctx.fh);\n+\t\t/* all fds should be same, because physical memory consist of an one file */\n+\t\tctx.user.fds = fds;\n+\t\tctx.user.fd_num = fd_num;\n+\t\tresult = ops->set_mem_table(ctx, &msg->memory, msg->memory.nregions);\n+\t\tbreak;\n+\n+\tcase VHOST_SET_VRING_NUM:\n+\t\tLOG_DEBUG(VHOST_CONFIG, \"(%\"PRIu64\") IOCTL: VHOST_SET_VRING_NUM\\n\", ctx.fh);\n+\t\tVHOST_USER_IOCTL_R(struct vhost_vring_state, msg->state, ops->set_vring_num);\n+\t\tbreak;\n+\n+\tcase VHOST_SET_VRING_BASE:\n+\t\tLOG_DEBUG(VHOST_CONFIG, \"(%\"PRIu64\") IOCTL: VHOST_SET_VRING_BASE\\n\", ctx.fh);\n+\t\tVHOST_USER_IOCTL_R(struct vhost_vring_state, msg->state, ops->set_vring_base);\n+\t\tbreak;\n+\n+\tcase VHOST_GET_VRING_BASE:\n+\t\tLOG_DEBUG(VHOST_CONFIG, \"(%\"PRIu64\") IOCTL: VHOST_GET_VRING_BASE\\n\", ctx.fh);\n+\t\tVHOST_USER_IOCTL_RW(uint32_t, msg->addr.index, struct vhost_vring_state, msg->state, ops->get_vring_base);\n+\t\tbreak;\n+\n+\tcase VHOST_SET_VRING_ADDR:\n+\t\tLOG_DEBUG(VHOST_CONFIG, \"(%\"PRIu64\") IOCTL: VHOST_SET_VRING_ADDR\\n\", ctx.fh);\n+\t\tVHOST_USER_IOCTL_R(struct vhost_vring_addr, msg->addr, ops->set_vring_addr);\n+\t\tbreak;\n+\n+\tcase VHOST_SET_VRING_KICK:\n+\t\tLOG_DEBUG(VHOST_CONFIG, \"(%\"PRIu64\") IOCTL: VHOST_SET_VRING_KICK\\n\", ctx.fh);\n+\t\tctx.user.fds = fds;\n+\t\tctx.user.fd_num = fd_num;\n+\t\tfile.index = msg->u64;\n+\t\tVHOST_USER_IOCTL_R(struct vhost_vring_file, file, ops->set_vring_kick);\n+\t\tbreak;\n+\n+\tcase VHOST_SET_VRING_CALL:\n+\t\tLOG_DEBUG(VHOST_CONFIG, \"(%\"PRIu64\") IOCTL: VHOST_SET_VRING_CALL\\n\", ctx.fh);\n+\t\tctx.user.fds = fds;\n+\t\tctx.user.fd_num = fd_num;\n+\t\tfile.index = msg->u64;\n+\t\tVHOST_USER_IOCTL_R(struct vhost_vring_file, file, ops->set_vring_call);\n+\t\tbreak;\n+\n+\tcase VHOST_NET_SET_BACKEND:\n+\tdefault:\n+\t\tRTE_LOG(ERR, VHOST_CONFIG, \"(%\"PRIu64\") IOCTL: DOESN NOT EXIST\\n\", ctx.fh);\n+\t\tresult = -1;\n+\t}\n+\n+\tif (result < 0)\n+\t\tLOG_DEBUG(VHOST_CONFIG, \"(%\"PRIu64\") IOCTL: FAIL\\n\", ctx.fh);\n+\telse\n+\t\tLOG_DEBUG(VHOST_CONFIG, \"(%\"PRIu64\") IOCTL: SUCCESS\\n\", ctx.fh);\n+\n+\treturn result;\n+}\n+\n+/**\n+ * vhost-user specific registration.\n+ */\n+static int\n+vhost_user_driver_register(struct vhost_driver *drv)\n+{\n+\tif ((drv == NULL) || (drv->dev_name == NULL) ||\n+\t\t\t(strlen(drv->dev_name) > UNIX_PATH_MAX - 1))\n+\t\treturn -1;\n+\n+\tops = get_virtio_net_callbacks(drv->type);\n+\n+\tdrv->user_session = rte_malloc(NULL, sizeof(struct vhost_user_session), CACHE_LINE_SIZE);\n+\tif (drv->user_session == NULL)\n+\t\treturn -1;\n+\n+\tdrv->user_session->fh =\n+\t\trte_atomic16_add_return(&vhost_user_device_id, 1) - 1; /* fh of first device is zero */\n+\tdrv->user_session->interval = 1;\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * When vhost-user driver starts, the session handler communicates with vhost-user\n+ * device on a QEMU using a unix domain sokcet.\n+ */\n+static void *\n+vhost_user_session_handler(void *data)\n+{\n+\tstruct vhost_driver *drv = data;\n+\tint ret;\n+\tstruct sockaddr_un caddr;\n+\tVhostUserMsg msg;\n+\tint fds[VHOST_USER_MAX_FD_NUM];\n+\tint socketfd;\n+\tint interval;\n+\tsize_t fd_num;\n+\n+\tif ((drv == NULL) || (drv->dev_name == NULL))\n+\t\treturn NULL;\n+\n+\tbzero(&caddr, sizeof(caddr));\n+\tcaddr.sun_family = AF_LOCAL;\n+\tstrncpy((char *)&caddr.sun_path, drv->dev_name, strlen(drv->dev_name));\n+\n+reconnect:\n+\tdrv->user_session->socketfd = socket(AF_UNIX, SOCK_STREAM, 0);\n+\tif (drv->user_session->socketfd < 0)\n+\t\treturn NULL;\n+\n+\tsocketfd = drv->user_session->socketfd;\n+\tinterval = drv->user_session->interval;\n+\twhile (1) {\n+\t\tret = connect(socketfd, (struct sockaddr *)&caddr, sizeof(caddr));\n+\t\tif (ret == 0)\n+\t\t\tbreak; /* success */\n+\t\tsleep(interval);\n+\t}\n+\n+\tret = vhost_user_open(drv);\n+\tif (ret != 0) {\n+\t\tRTE_LOG(ERR, VHOST_CONFIG, \"(Socket %s) open failuer\\n\", drv->dev_name);\n+\t\treturn NULL;\n+\t}\n+\n+\tfor (;;) {\n+\t\tfd_num = VHOST_USER_MAX_FD_NUM;\n+\t\tbzero(&msg, sizeof(VhostUserMsg));\n+\t\tret = vhost_user_read(drv, &msg, fds, &fd_num);\n+\t\tif (ret != 0) {\n+\t\t\tRTE_LOG(ERR, VHOST_CONFIG, \"(Socket %s) read failuer\\n\", drv->dev_name);\n+\t\t\tvhost_user_release(drv);\n+\t\t\tgoto reconnect;\n+\t\t}\n+\n+\t\tret = vhost_user_ioctl(drv, &msg, fds, fd_num);\n+\t\tif (ret != 0) {\n+\t\t\tRTE_LOG(ERR, VHOST_CONFIG, \"(Socket %s) request failuer\\n\", drv->dev_name);\n+\t\t\tvhost_user_release(drv);\n+\t\t\tgoto reconnect;\n+\t\t}\n+\t}\n+\n+\treturn NULL;\n+}\n+\n+/**\n+ * Create session handler\n+ */\n+static int\n+vhost_user_driver_start(struct vhost_driver *drv)\n+{\n+\tif (pthread_create(&drv->user_session->tid, NULL, vhost_user_session_handler, drv)) {\n+\t\tRTE_LOG(ERR, VHOST_CONFIG,\n+\t\t\t\t\"(Socket %s) starting event handler failuer\\n\", drv->dev_name);\n+\t\treturn -1;\n+\t}\n+\n+\t/* TODO: The event handler thread may need to run on a core user speficied. */\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * Destroy session handler\n+ */\n+static void\n+vhost_user_driver_stop(struct vhost_driver *drv)\n+{\n+\tpthread_t *tid = &drv->user_session->tid;\n+\n+\tif (pthread_create(tid, NULL, vhost_user_session_handler, drv)) {\n+\t\tRTE_LOG(ERR, VHOST_CONFIG,\n+\t\t\t\t\"(Socket %s) starting event handler failuer\\n\", drv->dev_name);\n+\t\treturn;\n+\t}\n+\n+\t/* stop event thread and wait until connection is closed */\n+\tif (*tid) {\n+\t\tpthread_cancel(*tid);\n+\t\tpthread_join(*tid, NULL);\n+\t}\n+\n+\tvhost_user_release(drv);\n+}\ndiff --git a/lib/librte_vhost/vhost-net.c b/lib/librte_vhost/vhost-net.c\nindex b0de5fd..10f41e9 100644\n--- a/lib/librte_vhost/vhost-net.c\n+++ b/lib/librte_vhost/vhost-net.c\n@@ -42,6 +42,11 @@\n  */\n #include \"vhost-net-cdev.c\"\n \n+/*\n+ * Include vhost-user depend functions and definitions\n+ */\n+#include \"vhost-net-user.c\"\n+\n /**\n  * This function abstracts cuse and vhost-user driver registration.\n  */\n@@ -65,10 +70,17 @@ rte_vhost_driver_register(const char *dev_name, vhost_driver_type_t type)\n \t\tif (ret != 0)\n \t\t\tgoto err;\n \t\tbreak;\n+\tcase VHOST_DRV_USER:\n+\t\tret = vhost_user_driver_register(drv);\n+\t\tbreak;\n \tdefault:\n+\t\tret = -EINVAL;\n \t\tbreak;\n \t}\n \n+\tif (ret != 0)\n+\t\tgoto err;\n+\n \treturn drv;\n err:\n \tfree(drv);\n@@ -81,17 +93,40 @@ err:\n int\n rte_vhost_driver_session_start(struct vhost_driver *drv)\n {\n+\tint ret;\n+\n \tif (drv == NULL)\n \t\treturn -ENODEV;\n \n \tswitch (drv->type) {\n \tcase VHOST_DRV_CUSE:\n-\t\tvhost_cuse_driver_session_start(drv);\n+\t\tret = vhost_cuse_driver_session_start(drv);\n+\t\tbreak;\n+\tcase VHOST_DRV_USER:\n+\t\tret = vhost_user_driver_start(drv);\n \t\tbreak;\n \tdefault:\n+\t\tret = -EINVAL;\n \t\tbreak;\n \t}\n \n-\treturn 0;\n+\treturn ret;\n }\n \n+/**\n+ * The vhost session is closed, only allow for vhost-user.\n+ */\n+void\n+rte_vhost_driver_session_stop(struct vhost_driver *drv)\n+{\n+\tif (drv == NULL)\n+\t\treturn;\n+\n+\tswitch (drv->type) {\n+\tcase VHOST_DRV_USER:\n+\t\tvhost_user_driver_stop(drv);\n+\t\tbreak;\n+\tdefault:\n+\t\tbreak;\n+\t}\n+}\ndiff --git a/lib/librte_vhost/vhost-net.h b/lib/librte_vhost/vhost-net.h\nindex ef04832..0e36ba0 100644\n--- a/lib/librte_vhost/vhost-net.h\n+++ b/lib/librte_vhost/vhost-net.h\n@@ -76,6 +76,12 @@ struct vhost_device_cuse_ctx {\n \tpid_t   pid;\t/* PID of process calling the IOCTL. */\n };\n \n+struct vhost_device_user_ctx {\n+\tint\t\t\t*fds;\n+\tint\t\t\tfd_num;\n+\tstruct vhost_driver\t*drv;\n+};\n+\n /*\n  * Structure used to identify device context.\n  */\n@@ -83,6 +89,7 @@ struct vhost_device_ctx {\n \tvhost_driver_type_t\ttype;\t/* driver type. */\n \tuint64_t\t\tfh;\t/* Populated with fi->fh to track the device index. */\n \tunion {\n+\t\tstruct vhost_device_user_ctx user;\n \t\tstruct vhost_device_cuse_ctx cdev;\n \t};\n };\ndiff --git a/lib/librte_vhost/virtio-net-user.c b/lib/librte_vhost/virtio-net-user.c\nnew file mode 100644\nindex 0000000..1e78f98\n--- /dev/null\n+++ b/lib/librte_vhost/virtio-net-user.c\n@@ -0,0 +1,410 @@\n+/*-\n+ *   BSD LICENSE\n+ *\n+ *   Copyright (c) 2014 IGEL Co.,Ltd.\n+ *   All rights reserved.\n+ *\n+ *   Redistribution and use in source and binary forms, with or without\n+ *   modification, are permitted provided that the following conditions\n+ *   are met:\n+ *\n+ *     * Redistributions of source code must retain the above copyright\n+ *       notice, this list of conditions and the following disclaimer.\n+ *     * Redistributions in binary form must reproduce the above copyright\n+ *       notice, this list of conditions and the following disclaimer in\n+ *       the documentation and/or other materials provided with the\n+ *       distribution.\n+ *     * Neither the name of IGEL nor the names of its\n+ *       contributors may be used to endorse or promote products derived\n+ *       from this software without specific prior written permission.\n+ *\n+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n+ *   \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+ */\n+\n+#include <sys/types.h>\n+#include <sys/stat.h>\n+#include <fcntl.h>\n+#include <unistd.h>\n+#include <sys/mman.h>\n+#include <stdlib.h>\n+\n+/* Functions defined in virtio_net.c */\n+static void init_device(struct vhost_device_ctx ctx, struct virtio_net *dev);\n+static void cleanup_device(struct virtio_net *dev);\n+static void free_device(struct virtio_net_config_ll *ll_dev);\n+static int new_device(struct vhost_device_ctx ctx);\n+static void destroy_device(struct vhost_device_ctx ctx);\n+static int set_owner(struct vhost_device_ctx ctx);\n+static int reset_owner(struct vhost_device_ctx ctx);\n+static int get_features(struct vhost_device_ctx ctx, uint64_t *pu);\n+static int set_features(struct vhost_device_ctx ctx, uint64_t *pu);\n+static int set_vring_num(struct vhost_device_ctx ctx, struct vhost_vring_state *state);\n+static int set_vring_addr(struct vhost_device_ctx ctx, struct vhost_vring_addr *addr);\n+static int set_vring_base(struct vhost_device_ctx ctx, struct vhost_vring_state *state);\n+static int set_backend(struct vhost_device_ctx ctx, struct vhost_vring_file *file);\n+\n+/* Root address of the linked list in the configuration core. */\n+static struct virtio_net_config_ll *user_ll_root;\n+\n+/**\n+ * Retrieves an entry from the devices configuration linked list.\n+ */\n+static struct virtio_net_config_ll *\n+user_get_config_ll_entry(struct vhost_device_ctx ctx)\n+{\n+\tstruct virtio_net_config_ll *ll_dev = user_ll_root;\n+\n+\t/* Loop through linked list until the device_fh is found. */\n+\twhile (ll_dev != NULL) {\n+\t\tif (ll_dev->dev.device_fh == ctx.fh)\n+\t\t\treturn ll_dev;\n+\t\tll_dev = ll_dev->next;\n+\t}\n+\n+\treturn NULL;\n+}\n+\n+/**\n+ * Searches the configuration core linked list and retrieves the device if it exists.\n+ */\n+static struct virtio_net *\n+user_get_device(struct vhost_device_ctx ctx)\n+{\n+\tstruct virtio_net_config_ll *ll_dev;\n+\n+\tll_dev = user_get_config_ll_entry(ctx);\n+\n+\t/* If a matching entry is found in the linked list, return the device in that entry. */\n+\tif (ll_dev)\n+\t\treturn &ll_dev->dev;\n+\n+\tRTE_LOG(ERR, VHOST_CONFIG, \"(%\"PRIu64\") Device not found in linked list.\\n\", ctx.fh);\n+\treturn NULL;\n+}\n+\n+/**\n+ * Add entry containing a device to the device configuration linked list.\n+ */\n+static void\n+user_add_config_ll_entry(struct virtio_net_config_ll *new_ll_dev)\n+{\n+\tstruct virtio_net_config_ll *ll_dev = user_ll_root;\n+\n+\t/* If ll_dev == NULL then this is the first device so go to else */\n+\tif (ll_dev) {\n+\t\t/* If the 1st device_fh != 0 then we insert our device here. */\n+\t\tif (ll_dev->dev.device_fh != 0)\t{\n+\t\t\tnew_ll_dev->dev.device_fh = 0;\n+\t\t\tnew_ll_dev->next = ll_dev;\n+\t\t\tuser_ll_root = new_ll_dev;\n+\t\t} else {\n+\t\t\t/* Increment through the ll until we find un unused device_fh. Insert the device at that entry*/\n+\t\t\twhile ((ll_dev->next != NULL) && (ll_dev->dev.device_fh == (ll_dev->next->dev.device_fh - 1)))\n+\t\t\t\tll_dev = ll_dev->next;\n+\n+\t\t\tnew_ll_dev->dev.device_fh = ll_dev->dev.device_fh + 1;\n+\t\t\tnew_ll_dev->next = ll_dev->next;\n+\t\t\tll_dev->next = new_ll_dev;\n+\t\t}\n+\t} else {\n+\t\tuser_ll_root = new_ll_dev;\n+\t\tuser_ll_root->dev.device_fh = 0;\n+\t}\n+\n+}\n+\n+/**\n+ * Remove an entry from the device configuration linked list.\n+ */\n+static struct virtio_net_config_ll *\n+user_rm_config_ll_entry(struct virtio_net_config_ll *ll_dev, struct virtio_net_config_ll *ll_dev_last)\n+{\n+\t/* First remove the device and then clean it up. */\n+\tif (ll_dev == user_ll_root) {\n+\t\tuser_ll_root = ll_dev->next;\n+\t\tcleanup_device(&ll_dev->dev);\n+\t\tfree_device(ll_dev);\n+\t\treturn user_ll_root;\n+\t} else {\n+\t\tif (likely(ll_dev_last != NULL)) {\n+\t\t\tll_dev_last->next = ll_dev->next;\n+\t\t\tcleanup_device(&ll_dev->dev);\n+\t\t\tfree_device(ll_dev);\n+\t\t\treturn ll_dev_last->next;\n+\t\t} else {\n+\t\t\tcleanup_device(&ll_dev->dev);\n+\t\t\tfree_device(ll_dev);\n+\t\t\tRTE_LOG(ERR, VHOST_CONFIG, \"Remove entry from config_ll failed\\n\");\n+\t\t\treturn NULL;\n+\t\t}\n+\t}\n+}\n+\n+/**\n+ * Returns the root entry of linked list\n+ */\n+static struct virtio_net_config_ll *\n+user_get_config_ll_root(void)\n+{\n+\treturn user_ll_root;\n+}\n+\n+/**\n+ * vhost-user specific device initialization.\n+ */\n+static void\n+user_init_device(struct vhost_device_ctx ctx, struct virtio_net *dev)\n+{\n+\tdev->priv = ctx.user.drv->priv;\n+}\n+\n+/**\n+ * Locate the file containing QEMU's memory space and map it to our address space.\n+ */\n+static int\n+user_host_memory_map(struct virtio_net *dev, struct virtio_memory *mem, int fd, size_t size)\n+{\n+\tvoid *map;\n+\n+\tmap = mmap(0, size, PROT_READ|PROT_WRITE , MAP_POPULATE|MAP_SHARED, fd, 0);\n+\tclose(fd);\n+\n+\tif (map == MAP_FAILED) {\n+\t\tRTE_LOG(ERR, VHOST_CONFIG, \"(%\"PRIu64\") Error mapping the file fd %d\\n\",  dev->device_fh, fd);\n+\t\treturn -1;\n+\t}\n+\n+\t/* Store the memory address and size in the device data structure */\n+\tmem->mapped_address = (uint64_t)(uintptr_t)map;\n+\tmem->mapped_size = size;\n+\n+\tLOG_DEBUG(VHOST_CONFIG, \"(%\"PRIu64\") Mem File: fd: %d - Size: %llu - VA: %p\\n\", dev->device_fh,\n+\t\t\tfd, (long long unsigned)mem->mapped_size, map);\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * Called from IOCTL: VHOST_SET_MEM_TABLE\n+ * This function creates and populates the memory structure for the device. This includes\n+ * storing offsets used to translate buffer addresses.\n+ */\n+static int\n+user_set_mem_table(struct vhost_device_ctx ctx, const void *mem_regions_addr, uint32_t nregions)\n+{\n+\tstruct virtio_net *dev;\n+\tstruct vhost_memory_region *mem_regions;\n+\tstruct virtio_memory *mem;\n+\tuint64_t size = offsetof(struct vhost_memory, regions);\n+\tuint32_t regionidx, valid_regions;\n+\tsize_t guest_memory_size = 0;\n+\n+\tdev = user_get_device(ctx);\n+\tif (dev == NULL)\n+\t\treturn -1;\n+\n+\tif (dev->mem) {\n+\t\tmunmap((void *)(uintptr_t)dev->mem->mapped_address, (size_t)dev->mem->mapped_size);\n+\t\tfree(dev->mem);\n+\t}\n+\n+\t/* Malloc the memory structure depending on the number of regions. */\n+\tmem = calloc(1, sizeof(struct virtio_memory) + (sizeof(struct virtio_memory_regions) * nregions));\n+\tif (mem == NULL) {\n+\t\tRTE_LOG(ERR, VHOST_CONFIG, \"(%\"PRIu64\") Failed to allocate memory for dev->mem.\\n\", dev->device_fh);\n+\t\treturn -1;\n+\t}\n+\n+\tmem->nregions = nregions;\n+\n+\tmem_regions = (void*)(uintptr_t)((uint64_t)(uintptr_t)mem_regions_addr + size);\n+\n+\tfor (regionidx = 0; regionidx < mem->nregions; regionidx++) {\n+\t\t/* Populate the region structure for each region. */\n+\t\tmem->regions[regionidx].guest_phys_address = mem_regions[regionidx].guest_phys_addr;\n+\t\tmem->regions[regionidx].guest_phys_address_end = mem->regions[regionidx].guest_phys_address +\n+\t\t\tmem_regions[regionidx].memory_size;\n+\t\tmem->regions[regionidx].memory_size = mem_regions[regionidx].memory_size;\n+\t\tmem->regions[regionidx].userspace_address = mem_regions[regionidx].userspace_addr;\n+\t\tguest_memory_size += mem_regions[regionidx].memory_size;\n+\n+\t\tLOG_DEBUG(VHOST_CONFIG, \"(%\"PRIu64\") REGION: %u - GPA: %p - QEMU VA: %p - SIZE (%\"PRIu64\")\\n\", dev->device_fh,\n+\t\t\t\tregionidx, (void*)(uintptr_t)mem->regions[regionidx].guest_phys_address,\n+\t\t\t\t(void*)(uintptr_t)mem->regions[regionidx].userspace_address,\n+\t\t\t\tmem->regions[regionidx].memory_size);\n+\t}\n+\n+\tfor (regionidx = 0; regionidx < mem->nregions; regionidx++) {\n+\t\t/*set the base address mapping*/\n+\t\tif (mem->regions[regionidx].guest_phys_address == 0x0) {\n+\t\t\tmem->base_address = mem->regions[regionidx].userspace_address;\n+\t\t\t/* Map VM memory file */\n+\t\t\tif (user_host_memory_map(dev, mem, ctx.user.fds[regionidx], guest_memory_size) != 0) {\n+\t\t\t\tfree(mem);\n+\t\t\t\treturn -1;\n+\t\t\t}\n+\t\t} else\n+\t\t\tclose(ctx.user.fds[regionidx]);\n+\t}\n+\n+\t/* Check that we have a valid base address. */\n+\tif (mem->base_address == 0) {\n+\t\tRTE_LOG(ERR, VHOST_CONFIG, \"(%\"PRIu64\") Failed to find base address of qemu memory file.\\n\", dev->device_fh);\n+\t\tfree(mem);\n+\t\treturn -1;\n+\t}\n+\n+\t/* Check if all of our regions have valid mappings. Usually one does not exist in the QEMU memory file. */\n+\tvalid_regions = mem->nregions;\n+\tfor (regionidx = 0; regionidx < mem->nregions; regionidx++) {\n+\t\tif ((mem->regions[regionidx].userspace_address < mem->base_address) ||\n+\t\t\t\t(mem->regions[regionidx].userspace_address > (mem->base_address + mem->mapped_size)))\n+\t\t\tvalid_regions--;\n+\t}\n+\n+\t/* If a region does not have a valid mapping we rebuild our memory struct to contain only valid entries. */\n+\tif (valid_regions != mem->nregions) {\n+\t\tLOG_DEBUG(VHOST_CONFIG, \"(%\"PRIu64\") Not all memory regions exist in the QEMU mem file. Re-populating mem structure\\n\",\n+\t\t\t\tdev->device_fh);\n+\n+\t\t/* Re-populate the memory structure with only valid regions. Invalid regions are over-written with memmove. */\n+\t\tvalid_regions = 0;\n+\n+\t\tfor (regionidx = mem->nregions; 0 != regionidx--;) {\n+\t\t\tif ((mem->regions[regionidx].userspace_address < mem->base_address) ||\n+\t\t\t\t\t(mem->regions[regionidx].userspace_address > (mem->base_address + mem->mapped_size))) {\n+\t\t\t\tmemmove(&mem->regions[regionidx], &mem->regions[regionidx + 1],\n+\t\t\t\t\t\tsizeof(struct virtio_memory_regions) * valid_regions);\n+\t\t\t} else {\n+\t\t\t\tvalid_regions++;\n+\t\t\t}\n+\t\t}\n+\t}\n+\tmem->nregions = valid_regions;\n+\tdev->mem = mem;\n+\n+\t/*\n+\t * Calculate the address offset for each region. This offset is used to identify the vhost virtual address\n+\t * corresponding to a QEMU guest physical address.\n+\t */\n+\tfor (regionidx = 0; regionidx < dev->mem->nregions; regionidx++)\n+\t\tdev->mem->regions[regionidx].address_offset = dev->mem->regions[regionidx].userspace_address - dev->mem->base_address\n+\t\t\t+ dev->mem->mapped_address - dev->mem->regions[regionidx].guest_phys_address;\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * Called from IOCTL: VHOST_GET_VRING_BASE\n+ * We send the virtio device our available ring last used index.\n+ */\n+static int\n+user_get_vring_base(struct vhost_device_ctx ctx, uint32_t index, struct vhost_vring_state *state)\n+{\n+\tstruct virtio_net *dev;\n+\n+\tdev = user_get_device(ctx);\n+\tif (dev == NULL)\n+\t\treturn -1;\n+\n+\tstate->index = index;\n+\t/* State->index refers to the queue index. The TX queue is 1, RX queue is 0. */\n+\tstate->num = dev->virtqueue[state->index]->last_used_idx;\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * Called from IOCTL: VHOST_SET_VRING_CALL\n+ * The virtio device sends an eventfd to interrupt the guest. This fd gets copied in\n+ * to our process space.\n+ * Also this message is sent when virtio-net device is reset by device driver on QEMU.\n+ */\n+static int\n+user_set_vring_call(struct vhost_device_ctx ctx, struct vhost_vring_file *file)\n+{\n+\tstruct virtio_net *dev;\n+\tstruct vhost_virtqueue *vq;\n+\n+\tdev = user_get_device(ctx);\n+\tif (dev == NULL)\n+\t\treturn -1;\n+\n+\t/* file->index refers to the queue index. The TX queue is 1, RX queue is 0. */\n+\tvq = dev->virtqueue[file->index];\n+\n+\tif (vq->kickfd)\n+\t\tclose((int)vq->kickfd);\n+\n+\t/* Populate the eventfd_copy structure and call eventfd_copy. */\n+\tvq->kickfd = ctx.user.fds[0];\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * Called from IOCTL: VHOST_SET_VRING_KICK\n+ * The virtio device sends an eventfd that it can use to notify us. This fd gets copied in\n+ * to our process space.\n+ */\n+static int\n+user_set_vring_kick(struct vhost_device_ctx ctx, struct vhost_vring_file *file)\n+{\n+\tstruct virtio_net *dev;\n+\tstruct vhost_virtqueue *vq;\n+\n+\tdev = user_get_device(ctx);\n+\tif (dev == NULL)\n+\t\treturn -1;\n+\n+\t/* file->index refers to the queue index. The TX queue is 1, RX queue is 0. */\n+\tvq = dev->virtqueue[file->index];\n+\n+\tif (vq->callfd)\n+\t\tclose((int)vq->callfd);\n+\n+\t/* Populate the eventfd_copy structure and call eventfd_copy. */\n+\tvq->callfd = ctx.user.fds[0];\n+\n+\tif ((dev->virtqueue[VIRTIO_RXQ] != NULL) && (dev->virtqueue[VIRTIO_TXQ]) != NULL)\n+\t\treturn set_backend(ctx, file);\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * Function pointers are set for the device operations to allow to call functions\n+ * when an IOCTL, device_add or device_release is received.\n+ */\n+static const struct vhost_net_device_ops vhost_user_device_ops = {\n+\t.new_device = new_device,\n+\t.destroy_device = destroy_device,\n+\n+\t.get_features = get_features,\n+\t.set_features = set_features,\n+\n+\t.set_mem_table = user_set_mem_table,\n+\n+\t.set_vring_num = set_vring_num,\n+\t.set_vring_addr = set_vring_addr,\n+\t.set_vring_base = set_vring_base,\n+\t.get_vring_base = user_get_vring_base,\n+\n+\t.set_vring_kick = user_set_vring_kick,\n+\t.set_vring_call = user_set_vring_call,\n+\n+\t.set_backend = set_backend,\n+\n+\t.set_owner = set_owner,\n+\t.reset_owner = reset_owner,\n+};\ndiff --git a/lib/librte_vhost/virtio-net.c b/lib/librte_vhost/virtio-net.c\nindex 13fbb6f..db810e7 100644\n--- a/lib/librte_vhost/virtio-net.c\n+++ b/lib/librte_vhost/virtio-net.c\n@@ -96,6 +96,12 @@ qva_to_vva(struct virtio_net *dev, uint64_t qemu_va)\n  */\n #include \"virtio-net-cdev.c\"\n \n+/**\n+ * Include vhost-user depend functions and definitions.\n+ */\n+#include \"virtio-net-user.c\"\n+\n+\n /*\n  * Retrieves an entry from the devices configuration linked list.\n  */\n@@ -105,6 +111,8 @@ get_config_ll_entry(struct vhost_device_ctx ctx)\n \tswitch (ctx.type) {\n \tcase VHOST_DRV_CUSE:\n \t\treturn cdev_get_config_ll_entry(ctx);\n+\tcase VHOST_DRV_USER:\n+\t\treturn user_get_config_ll_entry(ctx);\n \tdefault:\n \t\tbreak;\n \t}\n@@ -120,6 +128,8 @@ get_device(struct vhost_device_ctx ctx)\n \tswitch (ctx.type) {\n \tcase VHOST_DRV_CUSE:\n \t\treturn cdev_get_device(ctx);\n+\tcase VHOST_DRV_USER:\n+\t\treturn user_get_device(ctx);\n \tdefault:\n \t\tbreak;\n \t}\n@@ -136,6 +146,8 @@ add_config_ll_entry(vhost_driver_type_t type,\n \tswitch (type) {\n \tcase VHOST_DRV_CUSE:\n \t\treturn cdev_add_config_ll_entry(new_ll_dev);\n+\tcase VHOST_DRV_USER:\n+\t\treturn user_add_config_ll_entry(new_ll_dev);\n \tdefault:\n \t\tbreak;\n \t}\n@@ -149,8 +161,39 @@ cleanup_device(struct virtio_net *dev)\n {\n \t/* Unmap QEMU memory file if mapped. */\n \tif (dev->mem) {\n-\t\tmunmap((void *)(uintptr_t)dev->mem->mapped_address,\n-\t\t\t(size_t)dev->mem->mapped_size);\n+\t\t{\n+\t\t\t/*\n+\t\t\t * 'munmap()' will be failed when mapped_size isn't\n+\t\t\t * aligned with hugepage size.\n+\t\t\t * Usually a file size of QEMU physical memory is\n+\t\t\t * aligned by hugepage size. So In a case of CUSE,\n+\t\t\t * there is no problem. But with vhost-user, there is\n+\t\t\t * no way to get physical memory size.\n+\t\t\t *\n+\t\t\t * Let's assume hugepage size is 2MB or 1GB here.\n+\t\t\t * BTW, 'mmap()' automatically fixed size parameter\n+\t\t\t * to be aligned. Why does 'munmap()' do like so?\n+\t\t\t */\n+\t\t\tint ret = 0;\n+\t\t\tsize_t hugepagesize, size = dev->mem->mapped_size;\n+\n+\t\t\t/* assume hugepage size is 2MB */\n+\t\t\thugepagesize = 2 * 1024 * 1024;\n+\t\t\tsize = (size + hugepagesize - 1) /\n+\t\t\t\t\t\thugepagesize * hugepagesize;\n+\t\t\tret = munmap((void *)(uintptr_t)\n+\t\t\t\t\t\tdev->mem->mapped_address,\n+\t\t\t\t\t\tsize);\n+\t\t\tif (ret) {\n+\t\t\t\t/* assume hugepage size is 1GB, try again */\n+\t\t\t\thugepagesize = 1024 * 1024 * 1024;\n+\t\t\t\tsize = (size + hugepagesize - 1) /\n+\t\t\t\t\t\thugepagesize * hugepagesize;\n+\t\t\t\tmunmap((void *)(uintptr_t)\n+\t\t\t\t\t\tdev->mem->mapped_address,\n+\t\t\t\t\t\tsize);\n+\t\t\t}\n+\t\t}\n \t\tfree(dev->mem);\n \t}\n \n@@ -187,6 +230,8 @@ rm_config_ll_entry(vhost_driver_type_t type,\n \tswitch (type) {\n \tcase VHOST_DRV_CUSE:\n \t\treturn cdev_rm_config_ll_entry(ll_dev, ll_dev_last);\n+\tcase VHOST_DRV_USER:\n+\t\treturn user_rm_config_ll_entry(ll_dev, ll_dev_last);\n \tdefault:\n \t\tbreak;\n \t}\n@@ -201,7 +246,9 @@ get_config_ll_root(struct vhost_device_ctx ctx)\n {\n \tswitch (ctx.type) {\n \tcase VHOST_DRV_CUSE:\n-\t\treturn cdev_get_config_ll_root(ctx);\n+\t\treturn cdev_get_config_ll_root();\n+\tcase VHOST_DRV_USER:\n+\t\treturn user_get_config_ll_root();\n \tdefault:\n \t\tbreak;\n \t}\n@@ -232,6 +279,8 @@ init_device(struct vhost_device_ctx ctx, struct virtio_net *dev)\n \tswitch (ctx.type) {\n \tcase VHOST_DRV_CUSE:\n \t\treturn cdev_init_device(ctx, dev);\n+\tcase VHOST_DRV_USER:\n+\t\treturn user_init_device(ctx, dev);\n \tdefault:\n \t\tbreak;\n \t}\n@@ -527,6 +576,8 @@ get_virtio_net_callbacks(vhost_driver_type_t type)\n \tswitch (type) {\n \tcase VHOST_DRV_CUSE:\n \t\treturn &vhost_cuse_device_ops;\n+\tcase VHOST_DRV_USER:\n+\t\treturn &vhost_user_device_ops;\n \tdefault:\n \t\tbreak;\n \t}\n@@ -570,9 +621,14 @@ int rte_vhost_feature_enable(uint64_t feature_mask)\n  * Register ops so that we can add/remove device to data core.\n  */\n int\n-rte_vhost_driver_callback_register(struct virtio_net_device_ops const * const ops)\n+rte_vhost_driver_callback_register(struct vhost_driver *drv,\n+\t\tstruct virtio_net_device_ops const * const ops, void *priv)\n {\n+\tif (drv == NULL || ops == NULL)\n+\t\treturn -1;\n+\n \tnotify_ops = ops;\n+\tdrv->priv = priv;\n \n \treturn 0;\n }\n",
    "prefixes": [
        "dpdk-dev",
        "RFC",
        "7/7"
    ]
}