xref: /spdk/lib/virtio/virtio_vhost_user.c (revision 34edd9f1bf5fda4c987f4500ddc3c9f50be32e7d)
14e6e7eafSChangpeng Liu /*   SPDX-License-Identifier: BSD-3-Clause
2a6dbe372Spaul luse  *   Copyright (C) 2010-2016 Intel Corporation. All rights reserved.
34e6e7eafSChangpeng Liu  *   All rights reserved.
44e6e7eafSChangpeng Liu  */
54e6e7eafSChangpeng Liu 
64e6e7eafSChangpeng Liu #include "spdk/stdinc.h"
74e6e7eafSChangpeng Liu 
84e6e7eafSChangpeng Liu #include <sys/eventfd.h>
94e6e7eafSChangpeng Liu 
104e6e7eafSChangpeng Liu #include "spdk/string.h"
114e6e7eafSChangpeng Liu #include "spdk/config.h"
124e6e7eafSChangpeng Liu #include "spdk/util.h"
134e6e7eafSChangpeng Liu 
144e6e7eafSChangpeng Liu #include "spdk_internal/virtio.h"
154e6e7eafSChangpeng Liu #include "spdk_internal/vhost_user.h"
164e6e7eafSChangpeng Liu 
174e6e7eafSChangpeng Liu /* The version of the protocol we support */
184e6e7eafSChangpeng Liu #define VHOST_USER_VERSION    0x1
194e6e7eafSChangpeng Liu 
204e6e7eafSChangpeng Liu #define VIRTIO_USER_SUPPORTED_PROTOCOL_FEATURES \
214e6e7eafSChangpeng Liu 	((1ULL << VHOST_USER_PROTOCOL_F_MQ) | \
224e6e7eafSChangpeng Liu 	(1ULL << VHOST_USER_PROTOCOL_F_CONFIG))
234e6e7eafSChangpeng Liu 
244e6e7eafSChangpeng Liu struct virtio_user_dev {
254e6e7eafSChangpeng Liu 	int		vhostfd;
264e6e7eafSChangpeng Liu 
274e6e7eafSChangpeng Liu 	int		callfds[SPDK_VIRTIO_MAX_VIRTQUEUES];
284e6e7eafSChangpeng Liu 	int		kickfds[SPDK_VIRTIO_MAX_VIRTQUEUES];
294e6e7eafSChangpeng Liu 	uint32_t	queue_size;
304e6e7eafSChangpeng Liu 
314e6e7eafSChangpeng Liu 	uint8_t		status;
32f869197bSJim Harris 	bool		is_stopping;
334e6e7eafSChangpeng Liu 	char		path[PATH_MAX];
344e6e7eafSChangpeng Liu 	uint64_t	protocol_features;
354e6e7eafSChangpeng Liu 	struct vring	vrings[SPDK_VIRTIO_MAX_VIRTQUEUES];
364e6e7eafSChangpeng Liu 	struct spdk_mem_map *mem_map;
374e6e7eafSChangpeng Liu };
384e6e7eafSChangpeng Liu 
394e6e7eafSChangpeng Liu static int
404e6e7eafSChangpeng Liu vhost_user_write(int fd, void *buf, int len, int *fds, int fd_num)
414e6e7eafSChangpeng Liu {
424e6e7eafSChangpeng Liu 	int r;
434e6e7eafSChangpeng Liu 	struct msghdr msgh;
444e6e7eafSChangpeng Liu 	struct iovec iov;
454e6e7eafSChangpeng Liu 	size_t fd_size = fd_num * sizeof(int);
464e6e7eafSChangpeng Liu 	char control[CMSG_SPACE(fd_size)];
474e6e7eafSChangpeng Liu 	struct cmsghdr *cmsg;
484e6e7eafSChangpeng Liu 
494e6e7eafSChangpeng Liu 	memset(&msgh, 0, sizeof(msgh));
504e6e7eafSChangpeng Liu 	memset(control, 0, sizeof(control));
514e6e7eafSChangpeng Liu 
524e6e7eafSChangpeng Liu 	iov.iov_base = (uint8_t *)buf;
534e6e7eafSChangpeng Liu 	iov.iov_len = len;
544e6e7eafSChangpeng Liu 
554e6e7eafSChangpeng Liu 	msgh.msg_iov = &iov;
564e6e7eafSChangpeng Liu 	msgh.msg_iovlen = 1;
574e6e7eafSChangpeng Liu 
584e6e7eafSChangpeng Liu 	if (fds && fd_num > 0) {
594e6e7eafSChangpeng Liu 		msgh.msg_control = control;
604e6e7eafSChangpeng Liu 		msgh.msg_controllen = sizeof(control);
614e6e7eafSChangpeng Liu 		cmsg = CMSG_FIRSTHDR(&msgh);
62b50af42bSGangCao 		if (!cmsg) {
63b50af42bSGangCao 			SPDK_WARNLOG("First HDR is NULL\n");
64b50af42bSGangCao 			return -EIO;
65b50af42bSGangCao 		}
664e6e7eafSChangpeng Liu 		cmsg->cmsg_len = CMSG_LEN(fd_size);
674e6e7eafSChangpeng Liu 		cmsg->cmsg_level = SOL_SOCKET;
684e6e7eafSChangpeng Liu 		cmsg->cmsg_type = SCM_RIGHTS;
694e6e7eafSChangpeng Liu 		memcpy(CMSG_DATA(cmsg), fds, fd_size);
704e6e7eafSChangpeng Liu 	} else {
714e6e7eafSChangpeng Liu 		msgh.msg_control = NULL;
724e6e7eafSChangpeng Liu 		msgh.msg_controllen = 0;
734e6e7eafSChangpeng Liu 	}
744e6e7eafSChangpeng Liu 
754e6e7eafSChangpeng Liu 	do {
764e6e7eafSChangpeng Liu 		r = sendmsg(fd, &msgh, 0);
774e6e7eafSChangpeng Liu 	} while (r < 0 && errno == EINTR);
784e6e7eafSChangpeng Liu 
794e6e7eafSChangpeng Liu 	if (r == -1) {
804e6e7eafSChangpeng Liu 		return -errno;
814e6e7eafSChangpeng Liu 	}
824e6e7eafSChangpeng Liu 
834e6e7eafSChangpeng Liu 	return 0;
844e6e7eafSChangpeng Liu }
854e6e7eafSChangpeng Liu 
864e6e7eafSChangpeng Liu static int
874e6e7eafSChangpeng Liu vhost_user_read(int fd, struct vhost_user_msg *msg)
884e6e7eafSChangpeng Liu {
894e6e7eafSChangpeng Liu 	uint32_t valid_flags = VHOST_USER_REPLY_MASK | VHOST_USER_VERSION;
904e6e7eafSChangpeng Liu 	ssize_t ret;
914e6e7eafSChangpeng Liu 	size_t sz_hdr = VHOST_USER_HDR_SIZE, sz_payload;
924e6e7eafSChangpeng Liu 
934e6e7eafSChangpeng Liu 	ret = recv(fd, (void *)msg, sz_hdr, 0);
944e6e7eafSChangpeng Liu 	if ((size_t)ret != sz_hdr) {
954e6e7eafSChangpeng Liu 		SPDK_WARNLOG("Failed to recv msg hdr: %zd instead of %zu.\n",
964e6e7eafSChangpeng Liu 			     ret, sz_hdr);
974e6e7eafSChangpeng Liu 		if (ret == -1) {
984e6e7eafSChangpeng Liu 			return -errno;
994e6e7eafSChangpeng Liu 		} else {
1004e6e7eafSChangpeng Liu 			return -EBUSY;
1014e6e7eafSChangpeng Liu 		}
1024e6e7eafSChangpeng Liu 	}
1034e6e7eafSChangpeng Liu 
1044e6e7eafSChangpeng Liu 	/* validate msg flags */
1054e6e7eafSChangpeng Liu 	if (msg->flags != (valid_flags)) {
1064e6e7eafSChangpeng Liu 		SPDK_WARNLOG("Failed to recv msg: flags %"PRIx32" instead of %"PRIx32".\n",
1074e6e7eafSChangpeng Liu 			     msg->flags, valid_flags);
1084e6e7eafSChangpeng Liu 		return -EIO;
1094e6e7eafSChangpeng Liu 	}
1104e6e7eafSChangpeng Liu 
1114e6e7eafSChangpeng Liu 	sz_payload = msg->size;
1124e6e7eafSChangpeng Liu 
1134e6e7eafSChangpeng Liu 	if (sz_payload > VHOST_USER_PAYLOAD_SIZE) {
1144e6e7eafSChangpeng Liu 		SPDK_WARNLOG("Received oversized msg: payload size %zu > available space %zu\n",
1154e6e7eafSChangpeng Liu 			     sz_payload, VHOST_USER_PAYLOAD_SIZE);
1164e6e7eafSChangpeng Liu 		return -EIO;
1174e6e7eafSChangpeng Liu 	}
1184e6e7eafSChangpeng Liu 
1194e6e7eafSChangpeng Liu 	if (sz_payload) {
1204e6e7eafSChangpeng Liu 		ret = recv(fd, (void *)((char *)msg + sz_hdr), sz_payload, 0);
1214e6e7eafSChangpeng Liu 		if ((size_t)ret != sz_payload) {
1224e6e7eafSChangpeng Liu 			SPDK_WARNLOG("Failed to recv msg payload: %zd instead of %"PRIu32".\n",
1234e6e7eafSChangpeng Liu 				     ret, msg->size);
1244e6e7eafSChangpeng Liu 			if (ret == -1) {
1254e6e7eafSChangpeng Liu 				return -errno;
1264e6e7eafSChangpeng Liu 			} else {
1274e6e7eafSChangpeng Liu 				return -EBUSY;
1284e6e7eafSChangpeng Liu 			}
1294e6e7eafSChangpeng Liu 		}
1304e6e7eafSChangpeng Liu 	}
1314e6e7eafSChangpeng Liu 
1324e6e7eafSChangpeng Liu 	return 0;
1334e6e7eafSChangpeng Liu }
1344e6e7eafSChangpeng Liu 
1354e6e7eafSChangpeng Liu struct hugepage_file_info {
1364e6e7eafSChangpeng Liu 	uint64_t addr;            /**< virtual addr */
1374e6e7eafSChangpeng Liu 	size_t   size;            /**< the file size */
1384e6e7eafSChangpeng Liu 	char     path[PATH_MAX];  /**< path to backing file */
1394e6e7eafSChangpeng Liu };
1404e6e7eafSChangpeng Liu 
1414e6e7eafSChangpeng Liu /* Two possible options:
1424e6e7eafSChangpeng Liu  * 1. Match HUGEPAGE_INFO_FMT to find the file storing struct hugepage_file
1434e6e7eafSChangpeng Liu  * array. This is simple but cannot be used in secondary process because
1444e6e7eafSChangpeng Liu  * secondary process will close and munmap that file.
1454e6e7eafSChangpeng Liu  * 2. Match HUGEFILE_FMT to find hugepage files directly.
1464e6e7eafSChangpeng Liu  *
1474e6e7eafSChangpeng Liu  * We choose option 2.
1484e6e7eafSChangpeng Liu  */
1494e6e7eafSChangpeng Liu static int
1504e6e7eafSChangpeng Liu get_hugepage_file_info(struct hugepage_file_info hugepages[], int max)
1514e6e7eafSChangpeng Liu {
1524e6e7eafSChangpeng Liu 	int idx, rc;
1534e6e7eafSChangpeng Liu 	FILE *f;
1544e6e7eafSChangpeng Liu 	char buf[BUFSIZ], *tmp, *tail;
1554e6e7eafSChangpeng Liu 	char *str_underline, *str_start;
1564e6e7eafSChangpeng Liu 	int huge_index;
1574e6e7eafSChangpeng Liu 	uint64_t v_start, v_end;
1584e6e7eafSChangpeng Liu 
1594e6e7eafSChangpeng Liu 	f = fopen("/proc/self/maps", "r");
1604e6e7eafSChangpeng Liu 	if (!f) {
1614e6e7eafSChangpeng Liu 		SPDK_ERRLOG("cannot open /proc/self/maps\n");
1624e6e7eafSChangpeng Liu 		rc = -errno;
1634e6e7eafSChangpeng Liu 		assert(rc < 0); /* scan-build hack */
1644e6e7eafSChangpeng Liu 		return rc;
1654e6e7eafSChangpeng Liu 	}
1664e6e7eafSChangpeng Liu 
1674e6e7eafSChangpeng Liu 	idx = 0;
1684e6e7eafSChangpeng Liu 	while (fgets(buf, sizeof(buf), f) != NULL) {
1694e6e7eafSChangpeng Liu 		if (sscanf(buf, "%" PRIx64 "-%" PRIx64, &v_start, &v_end) < 2) {
1704e6e7eafSChangpeng Liu 			SPDK_ERRLOG("Failed to parse address\n");
1714e6e7eafSChangpeng Liu 			rc = -EIO;
1724e6e7eafSChangpeng Liu 			goto out;
1734e6e7eafSChangpeng Liu 		}
1744e6e7eafSChangpeng Liu 
1754e6e7eafSChangpeng Liu 		tmp = strchr(buf, ' ') + 1; /** skip address */
1764e6e7eafSChangpeng Liu 		tmp = strchr(tmp, ' ') + 1; /** skip perm */
1774e6e7eafSChangpeng Liu 		tmp = strchr(tmp, ' ') + 1; /** skip offset */
1784e6e7eafSChangpeng Liu 		tmp = strchr(tmp, ' ') + 1; /** skip dev */
1794e6e7eafSChangpeng Liu 		tmp = strchr(tmp, ' ') + 1; /** skip inode */
1804e6e7eafSChangpeng Liu 		while (*tmp == ' ') {       /** skip spaces */
1814e6e7eafSChangpeng Liu 			tmp++;
1824e6e7eafSChangpeng Liu 		}
1834e6e7eafSChangpeng Liu 		tail = strrchr(tmp, '\n');  /** remove newline if exists */
1844e6e7eafSChangpeng Liu 		if (tail) {
1854e6e7eafSChangpeng Liu 			*tail = '\0';
1864e6e7eafSChangpeng Liu 		}
1874e6e7eafSChangpeng Liu 
1884e6e7eafSChangpeng Liu 		/* Match HUGEFILE_FMT, aka "%s/%smap_%d",
1894e6e7eafSChangpeng Liu 		 * which is defined in eal_filesystem.h
1904e6e7eafSChangpeng Liu 		 */
1914e6e7eafSChangpeng Liu 		str_underline = strrchr(tmp, '_');
1924e6e7eafSChangpeng Liu 		if (!str_underline) {
1934e6e7eafSChangpeng Liu 			continue;
1944e6e7eafSChangpeng Liu 		}
1954e6e7eafSChangpeng Liu 
1964e6e7eafSChangpeng Liu 		str_start = str_underline - strlen("map");
1974e6e7eafSChangpeng Liu 		if (str_start < tmp) {
1984e6e7eafSChangpeng Liu 			continue;
1994e6e7eafSChangpeng Liu 		}
2004e6e7eafSChangpeng Liu 
2014e6e7eafSChangpeng Liu 		if (sscanf(str_start, "map_%d", &huge_index) != 1) {
2024e6e7eafSChangpeng Liu 			continue;
2034e6e7eafSChangpeng Liu 		}
2044e6e7eafSChangpeng Liu 
2054e6e7eafSChangpeng Liu 		if (idx >= max) {
2064e6e7eafSChangpeng Liu 			SPDK_ERRLOG("Exceed maximum of %d\n", max);
2074e6e7eafSChangpeng Liu 			rc = -ENOSPC;
2084e6e7eafSChangpeng Liu 			goto out;
2094e6e7eafSChangpeng Liu 		}
2104e6e7eafSChangpeng Liu 
2114e6e7eafSChangpeng Liu 		if (idx > 0 &&
2124e6e7eafSChangpeng Liu 		    strncmp(tmp, hugepages[idx - 1].path, PATH_MAX) == 0 &&
2134e6e7eafSChangpeng Liu 		    v_start == hugepages[idx - 1].addr + hugepages[idx - 1].size) {
2144e6e7eafSChangpeng Liu 			hugepages[idx - 1].size += (v_end - v_start);
2154e6e7eafSChangpeng Liu 			continue;
2164e6e7eafSChangpeng Liu 		}
2174e6e7eafSChangpeng Liu 
2184e6e7eafSChangpeng Liu 		hugepages[idx].addr = v_start;
2194e6e7eafSChangpeng Liu 		hugepages[idx].size = v_end - v_start;
2204e6e7eafSChangpeng Liu 		snprintf(hugepages[idx].path, PATH_MAX, "%s", tmp);
2214e6e7eafSChangpeng Liu 		idx++;
2224e6e7eafSChangpeng Liu 	}
2234e6e7eafSChangpeng Liu 
2244e6e7eafSChangpeng Liu 	rc = idx;
2254e6e7eafSChangpeng Liu out:
2264e6e7eafSChangpeng Liu 	fclose(f);
2274e6e7eafSChangpeng Liu 	return rc;
2284e6e7eafSChangpeng Liu }
2294e6e7eafSChangpeng Liu 
2304e6e7eafSChangpeng Liu static int
2314e6e7eafSChangpeng Liu prepare_vhost_memory_user(struct vhost_user_msg *msg, int fds[])
2324e6e7eafSChangpeng Liu {
2334e6e7eafSChangpeng Liu 	int i, num;
2344e6e7eafSChangpeng Liu 	struct hugepage_file_info hugepages[VHOST_USER_MEMORY_MAX_NREGIONS];
2354e6e7eafSChangpeng Liu 
2364e6e7eafSChangpeng Liu 	num = get_hugepage_file_info(hugepages, VHOST_USER_MEMORY_MAX_NREGIONS);
2374e6e7eafSChangpeng Liu 	if (num < 0) {
2384e6e7eafSChangpeng Liu 		SPDK_ERRLOG("Failed to prepare memory for vhost-user\n");
2394e6e7eafSChangpeng Liu 		return num;
2404e6e7eafSChangpeng Liu 	}
2414e6e7eafSChangpeng Liu 
2424e6e7eafSChangpeng Liu 	for (i = 0; i < num; ++i) {
2434e6e7eafSChangpeng Liu 		/* the memory regions are unaligned */
2444e6e7eafSChangpeng Liu 		msg->payload.memory.regions[i].guest_phys_addr = hugepages[i].addr; /* use vaddr! */
2454e6e7eafSChangpeng Liu 		msg->payload.memory.regions[i].userspace_addr = hugepages[i].addr;
2464e6e7eafSChangpeng Liu 		msg->payload.memory.regions[i].memory_size = hugepages[i].size;
2474e6e7eafSChangpeng Liu 		msg->payload.memory.regions[i].flags_padding = 0;
2484e6e7eafSChangpeng Liu 		fds[i] = open(hugepages[i].path, O_RDWR);
2494e6e7eafSChangpeng Liu 	}
2504e6e7eafSChangpeng Liu 
2514e6e7eafSChangpeng Liu 	msg->payload.memory.nregions = num;
2524e6e7eafSChangpeng Liu 	msg->payload.memory.padding = 0;
2534e6e7eafSChangpeng Liu 
2544e6e7eafSChangpeng Liu 	return 0;
2554e6e7eafSChangpeng Liu }
2564e6e7eafSChangpeng Liu 
2574e6e7eafSChangpeng Liu static const char *const vhost_msg_strings[VHOST_USER_MAX] = {
2584e6e7eafSChangpeng Liu 	[VHOST_USER_SET_OWNER] = "VHOST_SET_OWNER",
2594e6e7eafSChangpeng Liu 	[VHOST_USER_RESET_OWNER] = "VHOST_RESET_OWNER",
2604e6e7eafSChangpeng Liu 	[VHOST_USER_SET_FEATURES] = "VHOST_SET_FEATURES",
2614e6e7eafSChangpeng Liu 	[VHOST_USER_GET_FEATURES] = "VHOST_GET_FEATURES",
2624e6e7eafSChangpeng Liu 	[VHOST_USER_SET_VRING_CALL] = "VHOST_SET_VRING_CALL",
2634e6e7eafSChangpeng Liu 	[VHOST_USER_GET_PROTOCOL_FEATURES] = "VHOST_USER_GET_PROTOCOL_FEATURES",
2644e6e7eafSChangpeng Liu 	[VHOST_USER_SET_PROTOCOL_FEATURES] = "VHOST_USER_SET_PROTOCOL_FEATURES",
2654e6e7eafSChangpeng Liu 	[VHOST_USER_SET_VRING_NUM] = "VHOST_SET_VRING_NUM",
2664e6e7eafSChangpeng Liu 	[VHOST_USER_SET_VRING_BASE] = "VHOST_SET_VRING_BASE",
2674e6e7eafSChangpeng Liu 	[VHOST_USER_GET_VRING_BASE] = "VHOST_GET_VRING_BASE",
2684e6e7eafSChangpeng Liu 	[VHOST_USER_SET_VRING_ADDR] = "VHOST_SET_VRING_ADDR",
2694e6e7eafSChangpeng Liu 	[VHOST_USER_SET_VRING_KICK] = "VHOST_SET_VRING_KICK",
2704e6e7eafSChangpeng Liu 	[VHOST_USER_SET_MEM_TABLE] = "VHOST_SET_MEM_TABLE",
2714e6e7eafSChangpeng Liu 	[VHOST_USER_SET_VRING_ENABLE] = "VHOST_SET_VRING_ENABLE",
2724e6e7eafSChangpeng Liu 	[VHOST_USER_GET_QUEUE_NUM] = "VHOST_USER_GET_QUEUE_NUM",
2734e6e7eafSChangpeng Liu 	[VHOST_USER_GET_CONFIG] = "VHOST_USER_GET_CONFIG",
2744e6e7eafSChangpeng Liu 	[VHOST_USER_SET_CONFIG] = "VHOST_USER_SET_CONFIG",
2754e6e7eafSChangpeng Liu };
2764e6e7eafSChangpeng Liu 
2774e6e7eafSChangpeng Liu static int
2784e6e7eafSChangpeng Liu vhost_user_sock(struct virtio_user_dev *dev,
2794e6e7eafSChangpeng Liu 		enum vhost_user_request req,
2804e6e7eafSChangpeng Liu 		void *arg)
2814e6e7eafSChangpeng Liu {
2824e6e7eafSChangpeng Liu 	struct vhost_user_msg msg;
2834e6e7eafSChangpeng Liu 	struct vhost_vring_file *file = 0;
2844e6e7eafSChangpeng Liu 	int need_reply = 0;
2854e6e7eafSChangpeng Liu 	int fds[VHOST_USER_MEMORY_MAX_NREGIONS];
2864e6e7eafSChangpeng Liu 	int fd_num = 0;
2874e6e7eafSChangpeng Liu 	int i, len, rc;
2884e6e7eafSChangpeng Liu 	int vhostfd = dev->vhostfd;
2894e6e7eafSChangpeng Liu 
2904e6e7eafSChangpeng Liu 	SPDK_DEBUGLOG(virtio_user, "sent message %d = %s\n", req, vhost_msg_strings[req]);
2914e6e7eafSChangpeng Liu 
2924e6e7eafSChangpeng Liu 	msg.request = req;
2934e6e7eafSChangpeng Liu 	msg.flags = VHOST_USER_VERSION;
2944e6e7eafSChangpeng Liu 	msg.size = 0;
2954e6e7eafSChangpeng Liu 
2964e6e7eafSChangpeng Liu 	switch (req) {
2974e6e7eafSChangpeng Liu 	case VHOST_USER_GET_FEATURES:
2984e6e7eafSChangpeng Liu 	case VHOST_USER_GET_PROTOCOL_FEATURES:
2994e6e7eafSChangpeng Liu 	case VHOST_USER_GET_QUEUE_NUM:
3004e6e7eafSChangpeng Liu 		need_reply = 1;
3014e6e7eafSChangpeng Liu 		break;
3024e6e7eafSChangpeng Liu 
3034e6e7eafSChangpeng Liu 	case VHOST_USER_SET_FEATURES:
3044e6e7eafSChangpeng Liu 	case VHOST_USER_SET_LOG_BASE:
3054e6e7eafSChangpeng Liu 	case VHOST_USER_SET_PROTOCOL_FEATURES:
3064e6e7eafSChangpeng Liu 		msg.payload.u64 = *((__u64 *)arg);
3074e6e7eafSChangpeng Liu 		msg.size = sizeof(msg.payload.u64);
3084e6e7eafSChangpeng Liu 		break;
3094e6e7eafSChangpeng Liu 
3104e6e7eafSChangpeng Liu 	case VHOST_USER_SET_OWNER:
3114e6e7eafSChangpeng Liu 	case VHOST_USER_RESET_OWNER:
3124e6e7eafSChangpeng Liu 		break;
3134e6e7eafSChangpeng Liu 
3144e6e7eafSChangpeng Liu 	case VHOST_USER_SET_MEM_TABLE:
3154e6e7eafSChangpeng Liu 		rc = prepare_vhost_memory_user(&msg, fds);
3164e6e7eafSChangpeng Liu 		if (rc < 0) {
3174e6e7eafSChangpeng Liu 			return rc;
3184e6e7eafSChangpeng Liu 		}
3194e6e7eafSChangpeng Liu 		fd_num = msg.payload.memory.nregions;
3204e6e7eafSChangpeng Liu 		msg.size = sizeof(msg.payload.memory.nregions);
3214e6e7eafSChangpeng Liu 		msg.size += sizeof(msg.payload.memory.padding);
3224e6e7eafSChangpeng Liu 		msg.size += fd_num * sizeof(struct vhost_memory_region);
3234e6e7eafSChangpeng Liu 		break;
3244e6e7eafSChangpeng Liu 
3254e6e7eafSChangpeng Liu 	case VHOST_USER_SET_LOG_FD:
3264e6e7eafSChangpeng Liu 		fds[fd_num++] = *((int *)arg);
3274e6e7eafSChangpeng Liu 		break;
3284e6e7eafSChangpeng Liu 
3294e6e7eafSChangpeng Liu 	case VHOST_USER_SET_VRING_NUM:
3304e6e7eafSChangpeng Liu 	case VHOST_USER_SET_VRING_BASE:
3314e6e7eafSChangpeng Liu 	case VHOST_USER_SET_VRING_ENABLE:
3324e6e7eafSChangpeng Liu 		memcpy(&msg.payload.state, arg, sizeof(msg.payload.state));
3334e6e7eafSChangpeng Liu 		msg.size = sizeof(msg.payload.state);
3344e6e7eafSChangpeng Liu 		break;
3354e6e7eafSChangpeng Liu 
3364e6e7eafSChangpeng Liu 	case VHOST_USER_GET_VRING_BASE:
3374e6e7eafSChangpeng Liu 		memcpy(&msg.payload.state, arg, sizeof(msg.payload.state));
3384e6e7eafSChangpeng Liu 		msg.size = sizeof(msg.payload.state);
3394e6e7eafSChangpeng Liu 		need_reply = 1;
3404e6e7eafSChangpeng Liu 		break;
3414e6e7eafSChangpeng Liu 
3424e6e7eafSChangpeng Liu 	case VHOST_USER_SET_VRING_ADDR:
3434e6e7eafSChangpeng Liu 		memcpy(&msg.payload.addr, arg, sizeof(msg.payload.addr));
3444e6e7eafSChangpeng Liu 		msg.size = sizeof(msg.payload.addr);
3454e6e7eafSChangpeng Liu 		break;
3464e6e7eafSChangpeng Liu 
3474e6e7eafSChangpeng Liu 	case VHOST_USER_SET_VRING_KICK:
3484e6e7eafSChangpeng Liu 	case VHOST_USER_SET_VRING_CALL:
3494e6e7eafSChangpeng Liu 	case VHOST_USER_SET_VRING_ERR:
3504e6e7eafSChangpeng Liu 		file = arg;
3514e6e7eafSChangpeng Liu 		msg.payload.u64 = file->index & VHOST_USER_VRING_IDX_MASK;
3524e6e7eafSChangpeng Liu 		msg.size = sizeof(msg.payload.u64);
3534e6e7eafSChangpeng Liu 		if (file->fd > 0) {
3544e6e7eafSChangpeng Liu 			fds[fd_num++] = file->fd;
3554e6e7eafSChangpeng Liu 		} else {
3564e6e7eafSChangpeng Liu 			msg.payload.u64 |= VHOST_USER_VRING_NOFD_MASK;
3574e6e7eafSChangpeng Liu 		}
3584e6e7eafSChangpeng Liu 		break;
3594e6e7eafSChangpeng Liu 
3604e6e7eafSChangpeng Liu 	case VHOST_USER_GET_CONFIG:
3614e6e7eafSChangpeng Liu 		memcpy(&msg.payload.cfg, arg, sizeof(msg.payload.cfg));
3624e6e7eafSChangpeng Liu 		msg.size = sizeof(msg.payload.cfg);
3634e6e7eafSChangpeng Liu 		need_reply = 1;
3644e6e7eafSChangpeng Liu 		break;
3654e6e7eafSChangpeng Liu 
3664e6e7eafSChangpeng Liu 	case VHOST_USER_SET_CONFIG:
3674e6e7eafSChangpeng Liu 		memcpy(&msg.payload.cfg, arg, sizeof(msg.payload.cfg));
3684e6e7eafSChangpeng Liu 		msg.size = sizeof(msg.payload.cfg);
3694e6e7eafSChangpeng Liu 		break;
3704e6e7eafSChangpeng Liu 
3714e6e7eafSChangpeng Liu 	default:
3724e6e7eafSChangpeng Liu 		SPDK_ERRLOG("trying to send unknown msg\n");
3734e6e7eafSChangpeng Liu 		return -EINVAL;
3744e6e7eafSChangpeng Liu 	}
3754e6e7eafSChangpeng Liu 
3764e6e7eafSChangpeng Liu 	len = VHOST_USER_HDR_SIZE + msg.size;
3774e6e7eafSChangpeng Liu 	rc = vhost_user_write(vhostfd, &msg, len, fds, fd_num);
3784e6e7eafSChangpeng Liu 	if (rc < 0) {
3794e6e7eafSChangpeng Liu 		SPDK_ERRLOG("%s failed: %s\n",
3804e6e7eafSChangpeng Liu 			    vhost_msg_strings[req], spdk_strerror(-rc));
3814e6e7eafSChangpeng Liu 		return rc;
3824e6e7eafSChangpeng Liu 	}
3834e6e7eafSChangpeng Liu 
3844e6e7eafSChangpeng Liu 	if (req == VHOST_USER_SET_MEM_TABLE)
3854e6e7eafSChangpeng Liu 		for (i = 0; i < fd_num; ++i) {
3864e6e7eafSChangpeng Liu 			close(fds[i]);
3874e6e7eafSChangpeng Liu 		}
3884e6e7eafSChangpeng Liu 
3894e6e7eafSChangpeng Liu 	if (need_reply) {
3904e6e7eafSChangpeng Liu 		rc = vhost_user_read(vhostfd, &msg);
3914e6e7eafSChangpeng Liu 		if (rc < 0) {
3924e6e7eafSChangpeng Liu 			SPDK_WARNLOG("Received msg failed: %s\n", spdk_strerror(-rc));
3934e6e7eafSChangpeng Liu 			return rc;
3944e6e7eafSChangpeng Liu 		}
3954e6e7eafSChangpeng Liu 
3964e6e7eafSChangpeng Liu 		if (req != msg.request) {
3974e6e7eafSChangpeng Liu 			SPDK_WARNLOG("Received unexpected msg type\n");
3984e6e7eafSChangpeng Liu 			return -EIO;
3994e6e7eafSChangpeng Liu 		}
4004e6e7eafSChangpeng Liu 
4014e6e7eafSChangpeng Liu 		switch (req) {
4024e6e7eafSChangpeng Liu 		case VHOST_USER_GET_FEATURES:
4034e6e7eafSChangpeng Liu 		case VHOST_USER_GET_PROTOCOL_FEATURES:
4044e6e7eafSChangpeng Liu 		case VHOST_USER_GET_QUEUE_NUM:
4054e6e7eafSChangpeng Liu 			if (msg.size != sizeof(msg.payload.u64)) {
4064e6e7eafSChangpeng Liu 				SPDK_WARNLOG("Received bad msg size\n");
4074e6e7eafSChangpeng Liu 				return -EIO;
4084e6e7eafSChangpeng Liu 			}
4094e6e7eafSChangpeng Liu 			*((__u64 *)arg) = msg.payload.u64;
4104e6e7eafSChangpeng Liu 			break;
4114e6e7eafSChangpeng Liu 		case VHOST_USER_GET_VRING_BASE:
4124e6e7eafSChangpeng Liu 			if (msg.size != sizeof(msg.payload.state)) {
4134e6e7eafSChangpeng Liu 				SPDK_WARNLOG("Received bad msg size\n");
4144e6e7eafSChangpeng Liu 				return -EIO;
4154e6e7eafSChangpeng Liu 			}
4164e6e7eafSChangpeng Liu 			memcpy(arg, &msg.payload.state,
4174e6e7eafSChangpeng Liu 			       sizeof(struct vhost_vring_state));
4184e6e7eafSChangpeng Liu 			break;
4194e6e7eafSChangpeng Liu 		case VHOST_USER_GET_CONFIG:
4204e6e7eafSChangpeng Liu 			if (msg.size != sizeof(msg.payload.cfg)) {
4214e6e7eafSChangpeng Liu 				SPDK_WARNLOG("Received bad msg size\n");
4224e6e7eafSChangpeng Liu 				return -EIO;
4234e6e7eafSChangpeng Liu 			}
4244e6e7eafSChangpeng Liu 			memcpy(arg, &msg.payload.cfg, sizeof(msg.payload.cfg));
4254e6e7eafSChangpeng Liu 			break;
4264e6e7eafSChangpeng Liu 		default:
4274e6e7eafSChangpeng Liu 			SPDK_WARNLOG("Received unexpected msg type\n");
4284e6e7eafSChangpeng Liu 			return -EBADMSG;
4294e6e7eafSChangpeng Liu 		}
4304e6e7eafSChangpeng Liu 	}
4314e6e7eafSChangpeng Liu 
4324e6e7eafSChangpeng Liu 	return 0;
4334e6e7eafSChangpeng Liu }
4344e6e7eafSChangpeng Liu 
4354e6e7eafSChangpeng Liu /**
4364e6e7eafSChangpeng Liu  * Set up environment to talk with a vhost user backend.
4374e6e7eafSChangpeng Liu  *
4384e6e7eafSChangpeng Liu  * @return
4394e6e7eafSChangpeng Liu  *   - (-1) if fail;
4404e6e7eafSChangpeng Liu  *   - (0) if succeed.
4414e6e7eafSChangpeng Liu  */
4424e6e7eafSChangpeng Liu static int
4434e6e7eafSChangpeng Liu vhost_user_setup(struct virtio_user_dev *dev)
4444e6e7eafSChangpeng Liu {
4454e6e7eafSChangpeng Liu 	int fd;
4464e6e7eafSChangpeng Liu 	int flag;
4474e6e7eafSChangpeng Liu 	struct sockaddr_un un;
4484e6e7eafSChangpeng Liu 	ssize_t rc;
4494e6e7eafSChangpeng Liu 
4504e6e7eafSChangpeng Liu 	fd = socket(AF_UNIX, SOCK_STREAM, 0);
4514e6e7eafSChangpeng Liu 	if (fd < 0) {
4524e6e7eafSChangpeng Liu 		SPDK_ERRLOG("socket() error, %s\n", spdk_strerror(errno));
4534e6e7eafSChangpeng Liu 		return -errno;
4544e6e7eafSChangpeng Liu 	}
4554e6e7eafSChangpeng Liu 
4564e6e7eafSChangpeng Liu 	flag = fcntl(fd, F_GETFD);
4574e6e7eafSChangpeng Liu 	if (fcntl(fd, F_SETFD, flag | FD_CLOEXEC) < 0) {
4584e6e7eafSChangpeng Liu 		SPDK_ERRLOG("fcntl failed, %s\n", spdk_strerror(errno));
4594e6e7eafSChangpeng Liu 	}
4604e6e7eafSChangpeng Liu 
4614e6e7eafSChangpeng Liu 	memset(&un, 0, sizeof(un));
4624e6e7eafSChangpeng Liu 	un.sun_family = AF_UNIX;
4634e6e7eafSChangpeng Liu 	rc = snprintf(un.sun_path, sizeof(un.sun_path), "%s", dev->path);
4644e6e7eafSChangpeng Liu 	if (rc < 0 || (size_t)rc >= sizeof(un.sun_path)) {
4654e6e7eafSChangpeng Liu 		SPDK_ERRLOG("socket path too long\n");
4664e6e7eafSChangpeng Liu 		close(fd);
4674e6e7eafSChangpeng Liu 		if (rc < 0) {
4684e6e7eafSChangpeng Liu 			return -errno;
4694e6e7eafSChangpeng Liu 		} else {
4704e6e7eafSChangpeng Liu 			return -EINVAL;
4714e6e7eafSChangpeng Liu 		}
4724e6e7eafSChangpeng Liu 	}
4734e6e7eafSChangpeng Liu 	if (connect(fd, (struct sockaddr *)&un, sizeof(un)) < 0) {
4744e6e7eafSChangpeng Liu 		SPDK_ERRLOG("connect error, %s\n", spdk_strerror(errno));
4754e6e7eafSChangpeng Liu 		close(fd);
4764e6e7eafSChangpeng Liu 		return -errno;
4774e6e7eafSChangpeng Liu 	}
4784e6e7eafSChangpeng Liu 
4794e6e7eafSChangpeng Liu 	dev->vhostfd = fd;
4804e6e7eafSChangpeng Liu 	return 0;
4814e6e7eafSChangpeng Liu }
4824e6e7eafSChangpeng Liu 
4834e6e7eafSChangpeng Liu static int
4844e6e7eafSChangpeng Liu virtio_user_create_queue(struct virtio_dev *vdev, uint32_t queue_sel)
4854e6e7eafSChangpeng Liu {
4864e6e7eafSChangpeng Liu 	struct virtio_user_dev *dev = vdev->ctx;
4874e6e7eafSChangpeng Liu 
4884e6e7eafSChangpeng Liu 	/* Of all per virtqueue MSGs, make sure VHOST_SET_VRING_CALL come
4894e6e7eafSChangpeng Liu 	 * firstly because vhost depends on this msg to allocate virtqueue
4904e6e7eafSChangpeng Liu 	 * pair.
4914e6e7eafSChangpeng Liu 	 */
4924e6e7eafSChangpeng Liu 	struct vhost_vring_file file;
4934e6e7eafSChangpeng Liu 
4944e6e7eafSChangpeng Liu 	file.index = queue_sel;
4954e6e7eafSChangpeng Liu 	file.fd = dev->callfds[queue_sel];
49684ac072eSChangpeng Liu 	return vhost_user_sock(dev, VHOST_USER_SET_VRING_CALL, &file);
4974e6e7eafSChangpeng Liu }
4984e6e7eafSChangpeng Liu 
4994e6e7eafSChangpeng Liu static int
5004e6e7eafSChangpeng Liu virtio_user_set_vring_addr(struct virtio_dev *vdev, uint32_t queue_sel)
5014e6e7eafSChangpeng Liu {
5024e6e7eafSChangpeng Liu 	struct virtio_user_dev *dev = vdev->ctx;
5034e6e7eafSChangpeng Liu 	struct vring *vring = &dev->vrings[queue_sel];
5044e6e7eafSChangpeng Liu 	struct vhost_vring_addr addr = {
5054e6e7eafSChangpeng Liu 		.index = queue_sel,
5064e6e7eafSChangpeng Liu 		.desc_user_addr = (uint64_t)(uintptr_t)vring->desc,
5074e6e7eafSChangpeng Liu 		.avail_user_addr = (uint64_t)(uintptr_t)vring->avail,
5084e6e7eafSChangpeng Liu 		.used_user_addr = (uint64_t)(uintptr_t)vring->used,
5094e6e7eafSChangpeng Liu 		.log_guest_addr = 0,
5104e6e7eafSChangpeng Liu 		.flags = 0, /* disable log */
5114e6e7eafSChangpeng Liu 	};
5124e6e7eafSChangpeng Liu 
51384ac072eSChangpeng Liu 	return vhost_user_sock(dev, VHOST_USER_SET_VRING_ADDR, &addr);
5144e6e7eafSChangpeng Liu }
5154e6e7eafSChangpeng Liu 
5164e6e7eafSChangpeng Liu static int
5174e6e7eafSChangpeng Liu virtio_user_kick_queue(struct virtio_dev *vdev, uint32_t queue_sel)
5184e6e7eafSChangpeng Liu {
5194e6e7eafSChangpeng Liu 	struct virtio_user_dev *dev = vdev->ctx;
5204e6e7eafSChangpeng Liu 	struct vhost_vring_file file;
5214e6e7eafSChangpeng Liu 	struct vhost_vring_state state;
5224e6e7eafSChangpeng Liu 	struct vring *vring = &dev->vrings[queue_sel];
5234e6e7eafSChangpeng Liu 	int rc;
5244e6e7eafSChangpeng Liu 
5254e6e7eafSChangpeng Liu 	state.index = queue_sel;
5264e6e7eafSChangpeng Liu 	state.num = vring->num;
52784ac072eSChangpeng Liu 	rc = vhost_user_sock(dev, VHOST_USER_SET_VRING_NUM, &state);
5284e6e7eafSChangpeng Liu 	if (rc < 0) {
5294e6e7eafSChangpeng Liu 		return rc;
5304e6e7eafSChangpeng Liu 	}
5314e6e7eafSChangpeng Liu 
5324e6e7eafSChangpeng Liu 	state.index = queue_sel;
5334e6e7eafSChangpeng Liu 	state.num = 0; /* no reservation */
53484ac072eSChangpeng Liu 	rc = vhost_user_sock(dev, VHOST_USER_SET_VRING_BASE, &state);
5354e6e7eafSChangpeng Liu 	if (rc < 0) {
5364e6e7eafSChangpeng Liu 		return rc;
5374e6e7eafSChangpeng Liu 	}
5384e6e7eafSChangpeng Liu 
5394e6e7eafSChangpeng Liu 	virtio_user_set_vring_addr(vdev, queue_sel);
5404e6e7eafSChangpeng Liu 
5414e6e7eafSChangpeng Liu 	/* Of all per virtqueue MSGs, make sure VHOST_USER_SET_VRING_KICK comes
5424e6e7eafSChangpeng Liu 	 * lastly because vhost depends on this msg to judge if
5434e6e7eafSChangpeng Liu 	 * virtio is ready.
5444e6e7eafSChangpeng Liu 	 */
5454e6e7eafSChangpeng Liu 	file.index = queue_sel;
5464e6e7eafSChangpeng Liu 	file.fd = dev->kickfds[queue_sel];
54784ac072eSChangpeng Liu 	return vhost_user_sock(dev, VHOST_USER_SET_VRING_KICK, &file);
5484e6e7eafSChangpeng Liu }
5494e6e7eafSChangpeng Liu 
5504e6e7eafSChangpeng Liu static int
5514e6e7eafSChangpeng Liu virtio_user_stop_queue(struct virtio_dev *vdev, uint32_t queue_sel)
5524e6e7eafSChangpeng Liu {
5534e6e7eafSChangpeng Liu 	struct virtio_user_dev *dev = vdev->ctx;
5544e6e7eafSChangpeng Liu 	struct vhost_vring_state state;
5554e6e7eafSChangpeng Liu 
5564e6e7eafSChangpeng Liu 	state.index = queue_sel;
5574e6e7eafSChangpeng Liu 	state.num = 0;
5584e6e7eafSChangpeng Liu 
55984ac072eSChangpeng Liu 	return vhost_user_sock(dev, VHOST_USER_GET_VRING_BASE, &state);
5604e6e7eafSChangpeng Liu }
5614e6e7eafSChangpeng Liu 
5624e6e7eafSChangpeng Liu static int
5634e6e7eafSChangpeng Liu virtio_user_queue_setup(struct virtio_dev *vdev,
5644e6e7eafSChangpeng Liu 			int (*fn)(struct virtio_dev *, uint32_t))
5654e6e7eafSChangpeng Liu {
5664e6e7eafSChangpeng Liu 	uint32_t i;
5674e6e7eafSChangpeng Liu 	int rc;
5684e6e7eafSChangpeng Liu 
5694e6e7eafSChangpeng Liu 	for (i = 0; i < vdev->max_queues; ++i) {
5704e6e7eafSChangpeng Liu 		rc = fn(vdev, i);
5714e6e7eafSChangpeng Liu 		if (rc < 0) {
5724e6e7eafSChangpeng Liu 			SPDK_ERRLOG("setup tx vq fails: %"PRIu32".\n", i);
5734e6e7eafSChangpeng Liu 			return rc;
5744e6e7eafSChangpeng Liu 		}
5754e6e7eafSChangpeng Liu 	}
5764e6e7eafSChangpeng Liu 
5774e6e7eafSChangpeng Liu 	return 0;
5784e6e7eafSChangpeng Liu }
5794e6e7eafSChangpeng Liu 
5804e6e7eafSChangpeng Liu static int
5814e6e7eafSChangpeng Liu virtio_user_map_notify(void *cb_ctx, struct spdk_mem_map *map,
5824e6e7eafSChangpeng Liu 		       enum spdk_mem_map_notify_action action,
5834e6e7eafSChangpeng Liu 		       void *vaddr, size_t size)
5844e6e7eafSChangpeng Liu {
5854e6e7eafSChangpeng Liu 	struct virtio_dev *vdev = cb_ctx;
5864e6e7eafSChangpeng Liu 	struct virtio_user_dev *dev = vdev->ctx;
5874e6e7eafSChangpeng Liu 	uint64_t features;
5884e6e7eafSChangpeng Liu 	int ret;
5894e6e7eafSChangpeng Liu 
590f869197bSJim Harris 	/* We do not support dynamic memory allocation with virtio-user.  If this is the
591f869197bSJim Harris 	 * initial notification when the device is started, dev->mem_map will be NULL.  If
592f869197bSJim Harris 	 * this is the final notification when the device is stopped, dev->is_stopping will
593f869197bSJim Harris 	 * be true.  All other cases are unsupported.
594f869197bSJim Harris 	 */
595f869197bSJim Harris 	if (dev->mem_map != NULL && !dev->is_stopping) {
596f869197bSJim Harris 		assert(false);
597f869197bSJim Harris 		SPDK_ERRLOG("Memory map change with active virtio_user_devs not allowed.\n");
598f869197bSJim Harris 		SPDK_ERRLOG("Pre-allocate memory for application using -s (mem_size) option.\n");
599f869197bSJim Harris 		return -1;
600f869197bSJim Harris 	}
601f869197bSJim Harris 
6024e6e7eafSChangpeng Liu 	/* We have to resend all mappings anyway, so don't bother with any
6034e6e7eafSChangpeng Liu 	 * page tracking.
6044e6e7eafSChangpeng Liu 	 */
60584ac072eSChangpeng Liu 	ret = vhost_user_sock(dev, VHOST_USER_SET_MEM_TABLE, NULL);
6064e6e7eafSChangpeng Liu 	if (ret < 0) {
6074e6e7eafSChangpeng Liu 		return ret;
6084e6e7eafSChangpeng Liu 	}
6094e6e7eafSChangpeng Liu 
6104e6e7eafSChangpeng Liu 	/* Since we might want to use that mapping straight away, we have to
6114e6e7eafSChangpeng Liu 	 * make sure the guest has already processed our SET_MEM_TABLE message.
6124e6e7eafSChangpeng Liu 	 * F_REPLY_ACK is just a feature and the host is not obliged to
6134e6e7eafSChangpeng Liu 	 * support it, so we send a simple message that always has a response
6144e6e7eafSChangpeng Liu 	 * and we wait for that response. Messages are always processed in order.
6154e6e7eafSChangpeng Liu 	 */
61684ac072eSChangpeng Liu 	return vhost_user_sock(dev, VHOST_USER_GET_FEATURES, &features);
6174e6e7eafSChangpeng Liu }
6184e6e7eafSChangpeng Liu 
6194e6e7eafSChangpeng Liu static int
6204e6e7eafSChangpeng Liu virtio_user_register_mem(struct virtio_dev *vdev)
6214e6e7eafSChangpeng Liu {
6224e6e7eafSChangpeng Liu 	struct virtio_user_dev *dev = vdev->ctx;
6234e6e7eafSChangpeng Liu 	const struct spdk_mem_map_ops virtio_user_map_ops = {
6244e6e7eafSChangpeng Liu 		.notify_cb = virtio_user_map_notify,
6254e6e7eafSChangpeng Liu 		.are_contiguous = NULL
6264e6e7eafSChangpeng Liu 	};
6274e6e7eafSChangpeng Liu 
6284e6e7eafSChangpeng Liu 	dev->mem_map = spdk_mem_map_alloc(0, &virtio_user_map_ops, vdev);
6294e6e7eafSChangpeng Liu 	if (dev->mem_map == NULL) {
6304e6e7eafSChangpeng Liu 		SPDK_ERRLOG("spdk_mem_map_alloc() failed\n");
6314e6e7eafSChangpeng Liu 		return -1;
6324e6e7eafSChangpeng Liu 	}
6334e6e7eafSChangpeng Liu 
6344e6e7eafSChangpeng Liu 	return 0;
6354e6e7eafSChangpeng Liu }
6364e6e7eafSChangpeng Liu 
6374e6e7eafSChangpeng Liu static void
6384e6e7eafSChangpeng Liu virtio_user_unregister_mem(struct virtio_dev *vdev)
6394e6e7eafSChangpeng Liu {
6404e6e7eafSChangpeng Liu 	struct virtio_user_dev *dev = vdev->ctx;
6414e6e7eafSChangpeng Liu 
642f869197bSJim Harris 	dev->is_stopping = true;
6434e6e7eafSChangpeng Liu 	spdk_mem_map_free(&dev->mem_map);
6444e6e7eafSChangpeng Liu }
6454e6e7eafSChangpeng Liu 
6464e6e7eafSChangpeng Liu static int
6474e6e7eafSChangpeng Liu virtio_user_start_device(struct virtio_dev *vdev)
6484e6e7eafSChangpeng Liu {
6494e6e7eafSChangpeng Liu 	struct virtio_user_dev *dev = vdev->ctx;
6504e6e7eafSChangpeng Liu 	uint64_t host_max_queues;
6514e6e7eafSChangpeng Liu 	int ret;
6524e6e7eafSChangpeng Liu 
6534e6e7eafSChangpeng Liu 	if ((dev->protocol_features & (1ULL << VHOST_USER_PROTOCOL_F_MQ)) == 0 &&
6544e6e7eafSChangpeng Liu 	    vdev->max_queues > 1 + vdev->fixed_queues_num) {
6554e6e7eafSChangpeng Liu 		SPDK_WARNLOG("%s: requested %"PRIu16" request queues, but the "
6564e6e7eafSChangpeng Liu 			     "host doesn't support VHOST_USER_PROTOCOL_F_MQ. "
6574e6e7eafSChangpeng Liu 			     "Only one request queue will be used.\n",
6584e6e7eafSChangpeng Liu 			     vdev->name, vdev->max_queues - vdev->fixed_queues_num);
6594e6e7eafSChangpeng Liu 		vdev->max_queues = 1 + vdev->fixed_queues_num;
6604e6e7eafSChangpeng Liu 	}
6614e6e7eafSChangpeng Liu 
6624e6e7eafSChangpeng Liu 	/* negotiate the number of I/O queues. */
66384ac072eSChangpeng Liu 	ret = vhost_user_sock(dev, VHOST_USER_GET_QUEUE_NUM, &host_max_queues);
6644e6e7eafSChangpeng Liu 	if (ret < 0) {
6654e6e7eafSChangpeng Liu 		return ret;
6664e6e7eafSChangpeng Liu 	}
6674e6e7eafSChangpeng Liu 
6684e6e7eafSChangpeng Liu 	if (vdev->max_queues > host_max_queues + vdev->fixed_queues_num) {
6694e6e7eafSChangpeng Liu 		SPDK_WARNLOG("%s: requested %"PRIu16" request queues"
6704e6e7eafSChangpeng Liu 			     "but only %"PRIu64" available\n",
6714e6e7eafSChangpeng Liu 			     vdev->name, vdev->max_queues - vdev->fixed_queues_num,
6724e6e7eafSChangpeng Liu 			     host_max_queues);
6734e6e7eafSChangpeng Liu 		vdev->max_queues = host_max_queues;
6744e6e7eafSChangpeng Liu 	}
6754e6e7eafSChangpeng Liu 
6764e6e7eafSChangpeng Liu 	/* tell vhost to create queues */
6774e6e7eafSChangpeng Liu 	ret = virtio_user_queue_setup(vdev, virtio_user_create_queue);
6784e6e7eafSChangpeng Liu 	if (ret < 0) {
6794e6e7eafSChangpeng Liu 		return ret;
6804e6e7eafSChangpeng Liu 	}
6814e6e7eafSChangpeng Liu 
6824e6e7eafSChangpeng Liu 	ret = virtio_user_register_mem(vdev);
6834e6e7eafSChangpeng Liu 	if (ret < 0) {
6844e6e7eafSChangpeng Liu 		return ret;
6854e6e7eafSChangpeng Liu 	}
6864e6e7eafSChangpeng Liu 
6874e6e7eafSChangpeng Liu 	return virtio_user_queue_setup(vdev, virtio_user_kick_queue);
6884e6e7eafSChangpeng Liu }
6894e6e7eafSChangpeng Liu 
6904e6e7eafSChangpeng Liu static int
6914e6e7eafSChangpeng Liu virtio_user_stop_device(struct virtio_dev *vdev)
6924e6e7eafSChangpeng Liu {
6934e6e7eafSChangpeng Liu 	int ret;
6944e6e7eafSChangpeng Liu 
6954e6e7eafSChangpeng Liu 	ret = virtio_user_queue_setup(vdev, virtio_user_stop_queue);
6964e6e7eafSChangpeng Liu 	/* a queue might fail to stop for various reasons, e.g. socket
6974e6e7eafSChangpeng Liu 	 * connection going down, but this mustn't prevent us from freeing
6984e6e7eafSChangpeng Liu 	 * the mem map.
6994e6e7eafSChangpeng Liu 	 */
7004e6e7eafSChangpeng Liu 	virtio_user_unregister_mem(vdev);
7014e6e7eafSChangpeng Liu 	return ret;
7024e6e7eafSChangpeng Liu }
7034e6e7eafSChangpeng Liu 
7044e6e7eafSChangpeng Liu static int
7054e6e7eafSChangpeng Liu virtio_user_dev_setup(struct virtio_dev *vdev)
7064e6e7eafSChangpeng Liu {
7074e6e7eafSChangpeng Liu 	struct virtio_user_dev *dev = vdev->ctx;
7084e6e7eafSChangpeng Liu 	uint16_t i;
7094e6e7eafSChangpeng Liu 
7104e6e7eafSChangpeng Liu 	dev->vhostfd = -1;
7114e6e7eafSChangpeng Liu 
7124e6e7eafSChangpeng Liu 	for (i = 0; i < SPDK_VIRTIO_MAX_VIRTQUEUES; ++i) {
7134e6e7eafSChangpeng Liu 		dev->callfds[i] = -1;
7144e6e7eafSChangpeng Liu 		dev->kickfds[i] = -1;
7154e6e7eafSChangpeng Liu 	}
7164e6e7eafSChangpeng Liu 
71784ac072eSChangpeng Liu 	return vhost_user_setup(dev);
7184e6e7eafSChangpeng Liu }
7194e6e7eafSChangpeng Liu 
7204e6e7eafSChangpeng Liu static int
7214e6e7eafSChangpeng Liu virtio_user_read_dev_config(struct virtio_dev *vdev, size_t offset,
7224e6e7eafSChangpeng Liu 			    void *dst, int length)
7234e6e7eafSChangpeng Liu {
7244e6e7eafSChangpeng Liu 	struct virtio_user_dev *dev = vdev->ctx;
7254e6e7eafSChangpeng Liu 	struct vhost_user_config cfg = {0};
7264e6e7eafSChangpeng Liu 	int rc;
7274e6e7eafSChangpeng Liu 
7284e6e7eafSChangpeng Liu 	if ((dev->protocol_features & (1ULL << VHOST_USER_PROTOCOL_F_CONFIG)) == 0) {
7294e6e7eafSChangpeng Liu 		return -ENOTSUP;
7304e6e7eafSChangpeng Liu 	}
7314e6e7eafSChangpeng Liu 
7324e6e7eafSChangpeng Liu 	cfg.offset = 0;
7334e6e7eafSChangpeng Liu 	cfg.size = VHOST_USER_MAX_CONFIG_SIZE;
7344e6e7eafSChangpeng Liu 
73584ac072eSChangpeng Liu 	rc = vhost_user_sock(dev, VHOST_USER_GET_CONFIG, &cfg);
7364e6e7eafSChangpeng Liu 	if (rc < 0) {
7374e6e7eafSChangpeng Liu 		SPDK_ERRLOG("get_config failed: %s\n", spdk_strerror(-rc));
7384e6e7eafSChangpeng Liu 		return rc;
7394e6e7eafSChangpeng Liu 	}
7404e6e7eafSChangpeng Liu 
7414e6e7eafSChangpeng Liu 	memcpy(dst, cfg.region + offset, length);
7424e6e7eafSChangpeng Liu 	return 0;
7434e6e7eafSChangpeng Liu }
7444e6e7eafSChangpeng Liu 
7454e6e7eafSChangpeng Liu static int
7464e6e7eafSChangpeng Liu virtio_user_write_dev_config(struct virtio_dev *vdev, size_t offset,
7474e6e7eafSChangpeng Liu 			     const void *src, int length)
7484e6e7eafSChangpeng Liu {
7494e6e7eafSChangpeng Liu 	struct virtio_user_dev *dev = vdev->ctx;
7504e6e7eafSChangpeng Liu 	struct vhost_user_config cfg = {0};
7514e6e7eafSChangpeng Liu 	int rc;
7524e6e7eafSChangpeng Liu 
7534e6e7eafSChangpeng Liu 	if ((dev->protocol_features & (1ULL << VHOST_USER_PROTOCOL_F_CONFIG)) == 0) {
7544e6e7eafSChangpeng Liu 		return -ENOTSUP;
7554e6e7eafSChangpeng Liu 	}
7564e6e7eafSChangpeng Liu 
7574e6e7eafSChangpeng Liu 	cfg.offset = offset;
7584e6e7eafSChangpeng Liu 	cfg.size = length;
7594e6e7eafSChangpeng Liu 	memcpy(cfg.region, src, length);
7604e6e7eafSChangpeng Liu 
76184ac072eSChangpeng Liu 	rc = vhost_user_sock(dev, VHOST_USER_SET_CONFIG, &cfg);
7624e6e7eafSChangpeng Liu 	if (rc < 0) {
7634e6e7eafSChangpeng Liu 		SPDK_ERRLOG("set_config failed: %s\n", spdk_strerror(-rc));
7644e6e7eafSChangpeng Liu 		return rc;
7654e6e7eafSChangpeng Liu 	}
7664e6e7eafSChangpeng Liu 
7674e6e7eafSChangpeng Liu 	return 0;
7684e6e7eafSChangpeng Liu }
7694e6e7eafSChangpeng Liu 
7704e6e7eafSChangpeng Liu static void
7714e6e7eafSChangpeng Liu virtio_user_set_status(struct virtio_dev *vdev, uint8_t status)
7724e6e7eafSChangpeng Liu {
7734e6e7eafSChangpeng Liu 	struct virtio_user_dev *dev = vdev->ctx;
7744e6e7eafSChangpeng Liu 	int rc = 0;
7754e6e7eafSChangpeng Liu 
7764e6e7eafSChangpeng Liu 	if ((dev->status & VIRTIO_CONFIG_S_NEEDS_RESET) &&
7774e6e7eafSChangpeng Liu 	    status != VIRTIO_CONFIG_S_RESET) {
7784e6e7eafSChangpeng Liu 		rc = -1;
7794e6e7eafSChangpeng Liu 	} else if (status & VIRTIO_CONFIG_S_DRIVER_OK) {
7804e6e7eafSChangpeng Liu 		rc = virtio_user_start_device(vdev);
7814e6e7eafSChangpeng Liu 	} else if (status == VIRTIO_CONFIG_S_RESET &&
7824e6e7eafSChangpeng Liu 		   (dev->status & VIRTIO_CONFIG_S_DRIVER_OK)) {
7834e6e7eafSChangpeng Liu 		rc = virtio_user_stop_device(vdev);
7844e6e7eafSChangpeng Liu 	}
7854e6e7eafSChangpeng Liu 
7864e6e7eafSChangpeng Liu 	if (rc != 0) {
7874e6e7eafSChangpeng Liu 		dev->status |= VIRTIO_CONFIG_S_NEEDS_RESET;
7884e6e7eafSChangpeng Liu 	} else {
7894e6e7eafSChangpeng Liu 		dev->status = status;
7904e6e7eafSChangpeng Liu 	}
7914e6e7eafSChangpeng Liu }
7924e6e7eafSChangpeng Liu 
7934e6e7eafSChangpeng Liu static uint8_t
7944e6e7eafSChangpeng Liu virtio_user_get_status(struct virtio_dev *vdev)
7954e6e7eafSChangpeng Liu {
7964e6e7eafSChangpeng Liu 	struct virtio_user_dev *dev = vdev->ctx;
7974e6e7eafSChangpeng Liu 
7984e6e7eafSChangpeng Liu 	return dev->status;
7994e6e7eafSChangpeng Liu }
8004e6e7eafSChangpeng Liu 
8014e6e7eafSChangpeng Liu static uint64_t
8024e6e7eafSChangpeng Liu virtio_user_get_features(struct virtio_dev *vdev)
8034e6e7eafSChangpeng Liu {
8044e6e7eafSChangpeng Liu 	struct virtio_user_dev *dev = vdev->ctx;
8054e6e7eafSChangpeng Liu 	uint64_t features;
8064e6e7eafSChangpeng Liu 	int rc;
8074e6e7eafSChangpeng Liu 
80884ac072eSChangpeng Liu 	rc = vhost_user_sock(dev, VHOST_USER_GET_FEATURES, &features);
8094e6e7eafSChangpeng Liu 	if (rc < 0) {
8104e6e7eafSChangpeng Liu 		SPDK_ERRLOG("get_features failed: %s\n", spdk_strerror(-rc));
8114e6e7eafSChangpeng Liu 		return 0;
8124e6e7eafSChangpeng Liu 	}
8134e6e7eafSChangpeng Liu 
8144e6e7eafSChangpeng Liu 	return features;
8154e6e7eafSChangpeng Liu }
8164e6e7eafSChangpeng Liu 
8174e6e7eafSChangpeng Liu static int
8184e6e7eafSChangpeng Liu virtio_user_set_features(struct virtio_dev *vdev, uint64_t features)
8194e6e7eafSChangpeng Liu {
8204e6e7eafSChangpeng Liu 	struct virtio_user_dev *dev = vdev->ctx;
8214e6e7eafSChangpeng Liu 	uint64_t protocol_features;
8224e6e7eafSChangpeng Liu 	int ret;
8234e6e7eafSChangpeng Liu 
82484ac072eSChangpeng Liu 	ret = vhost_user_sock(dev, VHOST_USER_SET_FEATURES, &features);
8254e6e7eafSChangpeng Liu 	if (ret < 0) {
8264e6e7eafSChangpeng Liu 		return ret;
8274e6e7eafSChangpeng Liu 	}
8284e6e7eafSChangpeng Liu 
8294e6e7eafSChangpeng Liu 	vdev->negotiated_features = features;
8304e6e7eafSChangpeng Liu 	vdev->modern = virtio_dev_has_feature(vdev, VIRTIO_F_VERSION_1);
8314e6e7eafSChangpeng Liu 
8324e6e7eafSChangpeng Liu 	if (!virtio_dev_has_feature(vdev, VHOST_USER_F_PROTOCOL_FEATURES)) {
8334e6e7eafSChangpeng Liu 		/* nothing else to do */
8344e6e7eafSChangpeng Liu 		return 0;
8354e6e7eafSChangpeng Liu 	}
8364e6e7eafSChangpeng Liu 
83784ac072eSChangpeng Liu 	ret = vhost_user_sock(dev, VHOST_USER_GET_PROTOCOL_FEATURES, &protocol_features);
8384e6e7eafSChangpeng Liu 	if (ret < 0) {
8394e6e7eafSChangpeng Liu 		return ret;
8404e6e7eafSChangpeng Liu 	}
8414e6e7eafSChangpeng Liu 
8424e6e7eafSChangpeng Liu 	protocol_features &= VIRTIO_USER_SUPPORTED_PROTOCOL_FEATURES;
84384ac072eSChangpeng Liu 	ret = vhost_user_sock(dev, VHOST_USER_SET_PROTOCOL_FEATURES, &protocol_features);
8444e6e7eafSChangpeng Liu 	if (ret < 0) {
8454e6e7eafSChangpeng Liu 		return ret;
8464e6e7eafSChangpeng Liu 	}
8474e6e7eafSChangpeng Liu 
8484e6e7eafSChangpeng Liu 	dev->protocol_features = protocol_features;
8494e6e7eafSChangpeng Liu 	return 0;
8504e6e7eafSChangpeng Liu }
8514e6e7eafSChangpeng Liu 
8524e6e7eafSChangpeng Liu static uint16_t
8534e6e7eafSChangpeng Liu virtio_user_get_queue_size(struct virtio_dev *vdev, uint16_t queue_id)
8544e6e7eafSChangpeng Liu {
8554e6e7eafSChangpeng Liu 	struct virtio_user_dev *dev = vdev->ctx;
8564e6e7eafSChangpeng Liu 
8574e6e7eafSChangpeng Liu 	/* Currently each queue has same queue size */
8584e6e7eafSChangpeng Liu 	return dev->queue_size;
8594e6e7eafSChangpeng Liu }
8604e6e7eafSChangpeng Liu 
8614e6e7eafSChangpeng Liu static int
8624e6e7eafSChangpeng Liu virtio_user_setup_queue(struct virtio_dev *vdev, struct virtqueue *vq)
8634e6e7eafSChangpeng Liu {
8644e6e7eafSChangpeng Liu 	struct virtio_user_dev *dev = vdev->ctx;
8654e6e7eafSChangpeng Liu 	struct vhost_vring_state state;
8664e6e7eafSChangpeng Liu 	uint16_t queue_idx = vq->vq_queue_index;
8674e6e7eafSChangpeng Liu 	void *queue_mem;
8684e6e7eafSChangpeng Liu 	uint64_t desc_addr, avail_addr, used_addr;
8694e6e7eafSChangpeng Liu 	int callfd, kickfd, rc;
8704e6e7eafSChangpeng Liu 
8714e6e7eafSChangpeng Liu 	if (dev->callfds[queue_idx] != -1 || dev->kickfds[queue_idx] != -1) {
8724e6e7eafSChangpeng Liu 		SPDK_ERRLOG("queue %"PRIu16" already exists\n", queue_idx);
8734e6e7eafSChangpeng Liu 		return -EEXIST;
8744e6e7eafSChangpeng Liu 	}
8754e6e7eafSChangpeng Liu 
8764e6e7eafSChangpeng Liu 	/* May use invalid flag, but some backend uses kickfd and
8774e6e7eafSChangpeng Liu 	 * callfd as criteria to judge if dev is alive. so finally we
8784e6e7eafSChangpeng Liu 	 * use real event_fd.
8794e6e7eafSChangpeng Liu 	 */
8804e6e7eafSChangpeng Liu 	callfd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
8814e6e7eafSChangpeng Liu 	if (callfd < 0) {
8824e6e7eafSChangpeng Liu 		SPDK_ERRLOG("callfd error, %s\n", spdk_strerror(errno));
8834e6e7eafSChangpeng Liu 		return -errno;
8844e6e7eafSChangpeng Liu 	}
8854e6e7eafSChangpeng Liu 
8864e6e7eafSChangpeng Liu 	kickfd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
8874e6e7eafSChangpeng Liu 	if (kickfd < 0) {
8884e6e7eafSChangpeng Liu 		SPDK_ERRLOG("kickfd error, %s\n", spdk_strerror(errno));
8894e6e7eafSChangpeng Liu 		close(callfd);
8904e6e7eafSChangpeng Liu 		return -errno;
8914e6e7eafSChangpeng Liu 	}
8924e6e7eafSChangpeng Liu 
8934e6e7eafSChangpeng Liu 	queue_mem = spdk_zmalloc(vq->vq_ring_size, VIRTIO_PCI_VRING_ALIGN, NULL,
8944e6e7eafSChangpeng Liu 				 SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA);
8954e6e7eafSChangpeng Liu 	if (queue_mem == NULL) {
8964e6e7eafSChangpeng Liu 		close(kickfd);
8974e6e7eafSChangpeng Liu 		close(callfd);
8984e6e7eafSChangpeng Liu 		return -ENOMEM;
8994e6e7eafSChangpeng Liu 	}
9004e6e7eafSChangpeng Liu 
9014e6e7eafSChangpeng Liu 	vq->vq_ring_mem = SPDK_VTOPHYS_ERROR;
9024e6e7eafSChangpeng Liu 	vq->vq_ring_virt_mem = queue_mem;
9034e6e7eafSChangpeng Liu 
9044e6e7eafSChangpeng Liu 	state.index = vq->vq_queue_index;
905e8003b3aSXiaoguang Wang 	state.num = 1;
9064e6e7eafSChangpeng Liu 
9074e6e7eafSChangpeng Liu 	if (virtio_dev_has_feature(vdev, VHOST_USER_F_PROTOCOL_FEATURES)) {
90884ac072eSChangpeng Liu 		rc = vhost_user_sock(dev, VHOST_USER_SET_VRING_ENABLE, &state);
9094e6e7eafSChangpeng Liu 		if (rc < 0) {
9104e6e7eafSChangpeng Liu 			SPDK_ERRLOG("failed to send VHOST_USER_SET_VRING_ENABLE: %s\n",
9114e6e7eafSChangpeng Liu 				    spdk_strerror(-rc));
9124e6e7eafSChangpeng Liu 			close(kickfd);
9134e6e7eafSChangpeng Liu 			close(callfd);
9144e6e7eafSChangpeng Liu 			spdk_free(queue_mem);
9154e6e7eafSChangpeng Liu 			return -rc;
9164e6e7eafSChangpeng Liu 		}
9174e6e7eafSChangpeng Liu 	}
9184e6e7eafSChangpeng Liu 
9194e6e7eafSChangpeng Liu 	dev->callfds[queue_idx] = callfd;
9204e6e7eafSChangpeng Liu 	dev->kickfds[queue_idx] = kickfd;
9214e6e7eafSChangpeng Liu 
9224e6e7eafSChangpeng Liu 	desc_addr = (uintptr_t)vq->vq_ring_virt_mem;
9234e6e7eafSChangpeng Liu 	avail_addr = desc_addr + vq->vq_nentries * sizeof(struct vring_desc);
9244e6e7eafSChangpeng Liu 	used_addr = SPDK_ALIGN_CEIL(avail_addr + offsetof(struct vring_avail,
9254e6e7eafSChangpeng Liu 				    ring[vq->vq_nentries]),
9264e6e7eafSChangpeng Liu 				    VIRTIO_PCI_VRING_ALIGN);
9274e6e7eafSChangpeng Liu 
9284e6e7eafSChangpeng Liu 	dev->vrings[queue_idx].num = vq->vq_nentries;
9294e6e7eafSChangpeng Liu 	dev->vrings[queue_idx].desc = (void *)(uintptr_t)desc_addr;
9304e6e7eafSChangpeng Liu 	dev->vrings[queue_idx].avail = (void *)(uintptr_t)avail_addr;
9314e6e7eafSChangpeng Liu 	dev->vrings[queue_idx].used = (void *)(uintptr_t)used_addr;
9324e6e7eafSChangpeng Liu 
9334e6e7eafSChangpeng Liu 	return 0;
9344e6e7eafSChangpeng Liu }
9354e6e7eafSChangpeng Liu 
9364e6e7eafSChangpeng Liu static void
9374e6e7eafSChangpeng Liu virtio_user_del_queue(struct virtio_dev *vdev, struct virtqueue *vq)
9384e6e7eafSChangpeng Liu {
9394e6e7eafSChangpeng Liu 	/* For legacy devices, write 0 to VIRTIO_PCI_QUEUE_PFN port, QEMU
9404e6e7eafSChangpeng Liu 	 * correspondingly stops the ioeventfds, and reset the status of
9414e6e7eafSChangpeng Liu 	 * the device.
9424e6e7eafSChangpeng Liu 	 * For modern devices, set queue desc, avail, used in PCI bar to 0,
9434e6e7eafSChangpeng Liu 	 * not see any more behavior in QEMU.
9444e6e7eafSChangpeng Liu 	 *
9454e6e7eafSChangpeng Liu 	 * Here we just care about what information to deliver to vhost-user.
9464e6e7eafSChangpeng Liu 	 * So we just close ioeventfd for now.
9474e6e7eafSChangpeng Liu 	 */
9484e6e7eafSChangpeng Liu 	struct virtio_user_dev *dev = vdev->ctx;
9494e6e7eafSChangpeng Liu 
9504e6e7eafSChangpeng Liu 	close(dev->callfds[vq->vq_queue_index]);
9514e6e7eafSChangpeng Liu 	close(dev->kickfds[vq->vq_queue_index]);
9524e6e7eafSChangpeng Liu 	dev->callfds[vq->vq_queue_index] = -1;
9534e6e7eafSChangpeng Liu 	dev->kickfds[vq->vq_queue_index] = -1;
9544e6e7eafSChangpeng Liu 
9554e6e7eafSChangpeng Liu 	spdk_free(vq->vq_ring_virt_mem);
9564e6e7eafSChangpeng Liu }
9574e6e7eafSChangpeng Liu 
9584e6e7eafSChangpeng Liu static void
9594e6e7eafSChangpeng Liu virtio_user_notify_queue(struct virtio_dev *vdev, struct virtqueue *vq)
9604e6e7eafSChangpeng Liu {
9614e6e7eafSChangpeng Liu 	uint64_t buf = 1;
9624e6e7eafSChangpeng Liu 	struct virtio_user_dev *dev = vdev->ctx;
9634e6e7eafSChangpeng Liu 
9644e6e7eafSChangpeng Liu 	if (write(dev->kickfds[vq->vq_queue_index], &buf, sizeof(buf)) < 0) {
9654e6e7eafSChangpeng Liu 		SPDK_ERRLOG("failed to kick backend: %s.\n", spdk_strerror(errno));
9664e6e7eafSChangpeng Liu 	}
9674e6e7eafSChangpeng Liu }
9684e6e7eafSChangpeng Liu 
9694e6e7eafSChangpeng Liu static void
9704e6e7eafSChangpeng Liu virtio_user_destroy(struct virtio_dev *vdev)
9714e6e7eafSChangpeng Liu {
9724e6e7eafSChangpeng Liu 	struct virtio_user_dev *dev = vdev->ctx;
9734e6e7eafSChangpeng Liu 
974cebb63a7SGangCao 	if (dev) {
9754e6e7eafSChangpeng Liu 		close(dev->vhostfd);
9764e6e7eafSChangpeng Liu 		free(dev);
9774e6e7eafSChangpeng Liu 	}
978cebb63a7SGangCao }
9794e6e7eafSChangpeng Liu 
9804e6e7eafSChangpeng Liu static void
9814e6e7eafSChangpeng Liu virtio_user_dump_json_info(struct virtio_dev *vdev, struct spdk_json_write_ctx *w)
9824e6e7eafSChangpeng Liu {
9834e6e7eafSChangpeng Liu 	struct virtio_user_dev *dev = vdev->ctx;
9844e6e7eafSChangpeng Liu 
9854e6e7eafSChangpeng Liu 	spdk_json_write_named_string(w, "type", "user");
9864e6e7eafSChangpeng Liu 	spdk_json_write_named_string(w, "socket", dev->path);
9874e6e7eafSChangpeng Liu }
9884e6e7eafSChangpeng Liu 
9894e6e7eafSChangpeng Liu static void
9904e6e7eafSChangpeng Liu virtio_user_write_json_config(struct virtio_dev *vdev, struct spdk_json_write_ctx *w)
9914e6e7eafSChangpeng Liu {
9924e6e7eafSChangpeng Liu 	struct virtio_user_dev *dev = vdev->ctx;
9934e6e7eafSChangpeng Liu 
9944e6e7eafSChangpeng Liu 	spdk_json_write_named_string(w, "trtype", "user");
9954e6e7eafSChangpeng Liu 	spdk_json_write_named_string(w, "traddr", dev->path);
9964e6e7eafSChangpeng Liu 	spdk_json_write_named_uint32(w, "vq_count", vdev->max_queues - vdev->fixed_queues_num);
9974e6e7eafSChangpeng Liu 	spdk_json_write_named_uint32(w, "vq_size", virtio_dev_backend_ops(vdev)->get_queue_size(vdev, 0));
9984e6e7eafSChangpeng Liu }
9994e6e7eafSChangpeng Liu 
10004e6e7eafSChangpeng Liu static const struct virtio_dev_ops virtio_user_ops = {
10014e6e7eafSChangpeng Liu 	.read_dev_cfg	= virtio_user_read_dev_config,
10024e6e7eafSChangpeng Liu 	.write_dev_cfg	= virtio_user_write_dev_config,
10034e6e7eafSChangpeng Liu 	.get_status	= virtio_user_get_status,
10044e6e7eafSChangpeng Liu 	.set_status	= virtio_user_set_status,
10054e6e7eafSChangpeng Liu 	.get_features	= virtio_user_get_features,
10064e6e7eafSChangpeng Liu 	.set_features	= virtio_user_set_features,
10074e6e7eafSChangpeng Liu 	.destruct_dev	= virtio_user_destroy,
10084e6e7eafSChangpeng Liu 	.get_queue_size	= virtio_user_get_queue_size,
10094e6e7eafSChangpeng Liu 	.setup_queue	= virtio_user_setup_queue,
10104e6e7eafSChangpeng Liu 	.del_queue	= virtio_user_del_queue,
10114e6e7eafSChangpeng Liu 	.notify_queue	= virtio_user_notify_queue,
10124e6e7eafSChangpeng Liu 	.dump_json_info = virtio_user_dump_json_info,
10134e6e7eafSChangpeng Liu 	.write_json_config = virtio_user_write_json_config,
10144e6e7eafSChangpeng Liu };
10154e6e7eafSChangpeng Liu 
10164e6e7eafSChangpeng Liu int
10174e6e7eafSChangpeng Liu virtio_user_dev_init(struct virtio_dev *vdev, const char *name, const char *path,
10184e6e7eafSChangpeng Liu 		     uint32_t queue_size)
10194e6e7eafSChangpeng Liu {
10204e6e7eafSChangpeng Liu 	struct virtio_user_dev *dev;
10214e6e7eafSChangpeng Liu 	int rc;
10224e6e7eafSChangpeng Liu 
10234e6e7eafSChangpeng Liu 	if (name == NULL) {
1024*34edd9f1SKamil Godzwon 		SPDK_ERRLOG("No name given for controller: %s\n", path);
10254e6e7eafSChangpeng Liu 		return -EINVAL;
10264e6e7eafSChangpeng Liu 	}
10274e6e7eafSChangpeng Liu 
10284e6e7eafSChangpeng Liu 	dev = calloc(1, sizeof(*dev));
10294e6e7eafSChangpeng Liu 	if (dev == NULL) {
10304e6e7eafSChangpeng Liu 		return -ENOMEM;
10314e6e7eafSChangpeng Liu 	}
10324e6e7eafSChangpeng Liu 
10334e6e7eafSChangpeng Liu 	rc = virtio_dev_construct(vdev, name, &virtio_user_ops, dev);
10344e6e7eafSChangpeng Liu 	if (rc != 0) {
10354e6e7eafSChangpeng Liu 		SPDK_ERRLOG("Failed to init device: %s\n", path);
10364e6e7eafSChangpeng Liu 		free(dev);
10374e6e7eafSChangpeng Liu 		return rc;
10384e6e7eafSChangpeng Liu 	}
10394e6e7eafSChangpeng Liu 
10404e6e7eafSChangpeng Liu 	vdev->is_hw = 0;
10414e6e7eafSChangpeng Liu 
10424e6e7eafSChangpeng Liu 	snprintf(dev->path, PATH_MAX, "%s", path);
10434e6e7eafSChangpeng Liu 	dev->queue_size = queue_size;
10444e6e7eafSChangpeng Liu 
10454e6e7eafSChangpeng Liu 	rc = virtio_user_dev_setup(vdev);
10464e6e7eafSChangpeng Liu 	if (rc < 0) {
10474e6e7eafSChangpeng Liu 		SPDK_ERRLOG("backend set up fails\n");
10484e6e7eafSChangpeng Liu 		goto err;
10494e6e7eafSChangpeng Liu 	}
10504e6e7eafSChangpeng Liu 
105184ac072eSChangpeng Liu 	rc = vhost_user_sock(dev, VHOST_USER_SET_OWNER, NULL);
10524e6e7eafSChangpeng Liu 	if (rc < 0) {
10534e6e7eafSChangpeng Liu 		SPDK_ERRLOG("set_owner fails: %s\n", spdk_strerror(-rc));
10544e6e7eafSChangpeng Liu 		goto err;
10554e6e7eafSChangpeng Liu 	}
10564e6e7eafSChangpeng Liu 
10574e6e7eafSChangpeng Liu 	return 0;
10584e6e7eafSChangpeng Liu 
10594e6e7eafSChangpeng Liu err:
10604e6e7eafSChangpeng Liu 	virtio_dev_destruct(vdev);
10614e6e7eafSChangpeng Liu 	return rc;
10624e6e7eafSChangpeng Liu }
10634e6e7eafSChangpeng Liu SPDK_LOG_REGISTER_COMPONENT(virtio_user)
1064