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