15566a3e3SBruce Richardson /* SPDX-License-Identifier: BSD-3-Clause 25566a3e3SBruce Richardson * Copyright(c) 2010-2016 Intel Corporation 36a84c37eSJianfeng Tan */ 46a84c37eSJianfeng Tan 56a84c37eSJianfeng Tan #include <sys/socket.h> 66a84c37eSJianfeng Tan #include <sys/types.h> 76a84c37eSJianfeng Tan #include <sys/stat.h> 86a84c37eSJianfeng Tan #include <unistd.h> 96a84c37eSJianfeng Tan #include <fcntl.h> 106a84c37eSJianfeng Tan #include <sys/un.h> 1172b452c5SDmitry Kozlyuk #include <stdlib.h> 126a84c37eSJianfeng Tan #include <string.h> 136a84c37eSJianfeng Tan #include <errno.h> 146a84c37eSJianfeng Tan 1594973531SMaxime Coquelin #include <rte_alarm.h> 166723c0fcSBruce Richardson #include <rte_string_fns.h> 17534bccbaSTiwei Bie #include <rte_fbarray.h> 18534bccbaSTiwei Bie 196a84c37eSJianfeng Tan #include "vhost.h" 2033d24d65SJianfeng Tan #include "virtio_user_dev.h" 216a84c37eSJianfeng Tan 22748e5ea5SMaxime Coquelin struct vhost_user_data { 2394973531SMaxime Coquelin int vhostfd; 2494973531SMaxime Coquelin int listenfd; 25748e5ea5SMaxime Coquelin uint64_t protocol_features; 26748e5ea5SMaxime Coquelin }; 275b75b63cSMaxime Coquelin 285b75b63cSMaxime Coquelin #ifndef VHOST_USER_F_PROTOCOL_FEATURES 295b75b63cSMaxime Coquelin #define VHOST_USER_F_PROTOCOL_FEATURES 30 305b75b63cSMaxime Coquelin #endif 315b75b63cSMaxime Coquelin 325b75b63cSMaxime Coquelin /** Protocol features. */ 335b75b63cSMaxime Coquelin #ifndef VHOST_USER_PROTOCOL_F_MQ 345b75b63cSMaxime Coquelin #define VHOST_USER_PROTOCOL_F_MQ 0 355b75b63cSMaxime Coquelin #endif 365b75b63cSMaxime Coquelin 375b75b63cSMaxime Coquelin #ifndef VHOST_USER_PROTOCOL_F_REPLY_ACK 385b75b63cSMaxime Coquelin #define VHOST_USER_PROTOCOL_F_REPLY_ACK 3 395b75b63cSMaxime Coquelin #endif 405b75b63cSMaxime Coquelin 415b75b63cSMaxime Coquelin #ifndef VHOST_USER_PROTOCOL_F_STATUS 425b75b63cSMaxime Coquelin #define VHOST_USER_PROTOCOL_F_STATUS 16 435b75b63cSMaxime Coquelin #endif 445b75b63cSMaxime Coquelin 455b75b63cSMaxime Coquelin #define VHOST_USER_SUPPORTED_PROTOCOL_FEATURES \ 465b75b63cSMaxime Coquelin (1ULL << VHOST_USER_PROTOCOL_F_MQ | \ 475b75b63cSMaxime Coquelin 1ULL << VHOST_USER_PROTOCOL_F_REPLY_ACK | \ 485b75b63cSMaxime Coquelin 1ULL << VHOST_USER_PROTOCOL_F_STATUS) 495b75b63cSMaxime Coquelin 505526b0cbSJianfeng Tan /* The version of the protocol we support */ 515526b0cbSJianfeng Tan #define VHOST_USER_VERSION 0x1 525526b0cbSJianfeng Tan 535526b0cbSJianfeng Tan #define VHOST_MEMORY_MAX_NREGIONS 8 545526b0cbSJianfeng Tan struct vhost_memory { 555526b0cbSJianfeng Tan uint32_t nregions; 565526b0cbSJianfeng Tan uint32_t padding; 575526b0cbSJianfeng Tan struct vhost_memory_region regions[VHOST_MEMORY_MAX_NREGIONS]; 585526b0cbSJianfeng Tan }; 595526b0cbSJianfeng Tan 6047e2ad07SMaxime Coquelin enum vhost_user_request { 6147e2ad07SMaxime Coquelin VHOST_USER_NONE = 0, 6247e2ad07SMaxime Coquelin VHOST_USER_GET_FEATURES = 1, 6347e2ad07SMaxime Coquelin VHOST_USER_SET_FEATURES = 2, 6447e2ad07SMaxime Coquelin VHOST_USER_SET_OWNER = 3, 6547e2ad07SMaxime Coquelin VHOST_USER_RESET_OWNER = 4, 6647e2ad07SMaxime Coquelin VHOST_USER_SET_MEM_TABLE = 5, 6747e2ad07SMaxime Coquelin VHOST_USER_SET_LOG_BASE = 6, 6847e2ad07SMaxime Coquelin VHOST_USER_SET_LOG_FD = 7, 6947e2ad07SMaxime Coquelin VHOST_USER_SET_VRING_NUM = 8, 7047e2ad07SMaxime Coquelin VHOST_USER_SET_VRING_ADDR = 9, 7147e2ad07SMaxime Coquelin VHOST_USER_SET_VRING_BASE = 10, 7247e2ad07SMaxime Coquelin VHOST_USER_GET_VRING_BASE = 11, 7347e2ad07SMaxime Coquelin VHOST_USER_SET_VRING_KICK = 12, 7447e2ad07SMaxime Coquelin VHOST_USER_SET_VRING_CALL = 13, 7547e2ad07SMaxime Coquelin VHOST_USER_SET_VRING_ERR = 14, 7647e2ad07SMaxime Coquelin VHOST_USER_GET_PROTOCOL_FEATURES = 15, 7747e2ad07SMaxime Coquelin VHOST_USER_SET_PROTOCOL_FEATURES = 16, 7847e2ad07SMaxime Coquelin VHOST_USER_GET_QUEUE_NUM = 17, 7947e2ad07SMaxime Coquelin VHOST_USER_SET_VRING_ENABLE = 18, 8047e2ad07SMaxime Coquelin VHOST_USER_SET_STATUS = 39, 8147e2ad07SMaxime Coquelin VHOST_USER_GET_STATUS = 40, 8247e2ad07SMaxime Coquelin }; 8347e2ad07SMaxime Coquelin 84*e7750639SAndre Muezerie struct __rte_packed_begin vhost_user_msg { 855526b0cbSJianfeng Tan enum vhost_user_request request; 865526b0cbSJianfeng Tan 875526b0cbSJianfeng Tan #define VHOST_USER_VERSION_MASK 0x3 885526b0cbSJianfeng Tan #define VHOST_USER_REPLY_MASK (0x1 << 2) 89c60208ddSMaxime Coquelin #define VHOST_USER_NEED_REPLY_MASK (0x1 << 3) 905526b0cbSJianfeng Tan uint32_t flags; 915526b0cbSJianfeng Tan uint32_t size; /* the following payload size */ 925526b0cbSJianfeng Tan union { 935526b0cbSJianfeng Tan #define VHOST_USER_VRING_IDX_MASK 0xff 945526b0cbSJianfeng Tan #define VHOST_USER_VRING_NOFD_MASK (0x1 << 8) 955526b0cbSJianfeng Tan uint64_t u64; 965526b0cbSJianfeng Tan struct vhost_vring_state state; 975526b0cbSJianfeng Tan struct vhost_vring_addr addr; 985526b0cbSJianfeng Tan struct vhost_memory memory; 995526b0cbSJianfeng Tan } payload; 100*e7750639SAndre Muezerie } __rte_packed_end; 1015526b0cbSJianfeng Tan 1025526b0cbSJianfeng Tan #define VHOST_USER_HDR_SIZE offsetof(struct vhost_user_msg, payload.u64) 1035526b0cbSJianfeng Tan #define VHOST_USER_PAYLOAD_SIZE \ 1045526b0cbSJianfeng Tan (sizeof(struct vhost_user_msg) - VHOST_USER_HDR_SIZE) 1055526b0cbSJianfeng Tan 1066a84c37eSJianfeng Tan static int 10772f64d10SMaxime Coquelin vhost_user_write(int fd, struct vhost_user_msg *msg, int *fds, int fd_num) 1086a84c37eSJianfeng Tan { 1096a84c37eSJianfeng Tan int r; 1106a84c37eSJianfeng Tan struct msghdr msgh; 1116a84c37eSJianfeng Tan struct iovec iov; 1126a84c37eSJianfeng Tan size_t fd_size = fd_num * sizeof(int); 1136a84c37eSJianfeng Tan char control[CMSG_SPACE(fd_size)]; 1146a84c37eSJianfeng Tan struct cmsghdr *cmsg; 1156a84c37eSJianfeng Tan 1166a84c37eSJianfeng Tan memset(&msgh, 0, sizeof(msgh)); 1176a84c37eSJianfeng Tan memset(control, 0, sizeof(control)); 1186a84c37eSJianfeng Tan 11972f64d10SMaxime Coquelin iov.iov_base = (uint8_t *)msg; 12072f64d10SMaxime Coquelin iov.iov_len = VHOST_USER_HDR_SIZE + msg->size; 1216a84c37eSJianfeng Tan 1226a84c37eSJianfeng Tan msgh.msg_iov = &iov; 1236a84c37eSJianfeng Tan msgh.msg_iovlen = 1; 1246a84c37eSJianfeng Tan msgh.msg_control = control; 1256a84c37eSJianfeng Tan msgh.msg_controllen = sizeof(control); 1266a84c37eSJianfeng Tan 1276a84c37eSJianfeng Tan cmsg = CMSG_FIRSTHDR(&msgh); 1286a84c37eSJianfeng Tan cmsg->cmsg_len = CMSG_LEN(fd_size); 1296a84c37eSJianfeng Tan cmsg->cmsg_level = SOL_SOCKET; 1306a84c37eSJianfeng Tan cmsg->cmsg_type = SCM_RIGHTS; 1310ad96138SStephen Hemminger if (fd_size > 0) 1326a84c37eSJianfeng Tan memcpy(CMSG_DATA(cmsg), fds, fd_size); 1336a84c37eSJianfeng Tan 1346a84c37eSJianfeng Tan do { 1356a84c37eSJianfeng Tan r = sendmsg(fd, &msgh, 0); 1366a84c37eSJianfeng Tan } while (r < 0 && errno == EINTR); 1376a84c37eSJianfeng Tan 138539d910cSMaxime Coquelin if (r < 0) 139539d910cSMaxime Coquelin PMD_DRV_LOG(ERR, "Failed to send msg: %s", strerror(errno)); 140539d910cSMaxime Coquelin 1416a84c37eSJianfeng Tan return r; 1426a84c37eSJianfeng Tan } 1436a84c37eSJianfeng Tan 1446a84c37eSJianfeng Tan static int 1456a84c37eSJianfeng Tan vhost_user_read(int fd, struct vhost_user_msg *msg) 1466a84c37eSJianfeng Tan { 1476a84c37eSJianfeng Tan uint32_t valid_flags = VHOST_USER_REPLY_MASK | VHOST_USER_VERSION; 1486a84c37eSJianfeng Tan int ret, sz_hdr = VHOST_USER_HDR_SIZE, sz_payload; 1496a84c37eSJianfeng Tan 1506a84c37eSJianfeng Tan ret = recv(fd, (void *)msg, sz_hdr, 0); 151432a195fSMaxime Coquelin if (ret < 0) { 152432a195fSMaxime Coquelin PMD_DRV_LOG(ERR, "Failed to recv msg header: %s", strerror(errno)); 153432a195fSMaxime Coquelin return -1; 154432a195fSMaxime Coquelin } else if (ret < sz_hdr) { 1556a84c37eSJianfeng Tan PMD_DRV_LOG(ERR, "Failed to recv msg hdr: %d instead of %d.", 1566a84c37eSJianfeng Tan ret, sz_hdr); 157432a195fSMaxime Coquelin return -1; 1586a84c37eSJianfeng Tan } 1596a84c37eSJianfeng Tan 1606a84c37eSJianfeng Tan /* validate msg flags */ 1616a84c37eSJianfeng Tan if (msg->flags != (valid_flags)) { 162432a195fSMaxime Coquelin PMD_DRV_LOG(ERR, "Failed to recv msg: flags 0x%x instead of 0x%x.", 1636a84c37eSJianfeng Tan msg->flags, valid_flags); 164432a195fSMaxime Coquelin return -1; 1656a84c37eSJianfeng Tan } 1666a84c37eSJianfeng Tan 1676a84c37eSJianfeng Tan sz_payload = msg->size; 1687b3249c5SDaniel Mrzyglod 169432a195fSMaxime Coquelin if ((size_t)sz_payload > sizeof(msg->payload)) { 170f3854ebaSThomas Monjalon PMD_DRV_LOG(ERR, "Payload size overflow, header says %d but max %zu", 171432a195fSMaxime Coquelin sz_payload, sizeof(msg->payload)); 172432a195fSMaxime Coquelin return -1; 173432a195fSMaxime Coquelin } 1747b3249c5SDaniel Mrzyglod 1756a84c37eSJianfeng Tan if (sz_payload) { 1766a84c37eSJianfeng Tan ret = recv(fd, (void *)((char *)msg + sz_hdr), sz_payload, 0); 177432a195fSMaxime Coquelin if (ret < 0) { 178432a195fSMaxime Coquelin PMD_DRV_LOG(ERR, "Failed to recv msg payload: %s", strerror(errno)); 179432a195fSMaxime Coquelin return -1; 180432a195fSMaxime Coquelin } else if (ret < sz_payload) { 181432a195fSMaxime Coquelin PMD_DRV_LOG(ERR, "Failed to recv msg payload: %d instead of %u.", 1826a84c37eSJianfeng Tan ret, msg->size); 183432a195fSMaxime Coquelin return -1; 1846a84c37eSJianfeng Tan } 1856a84c37eSJianfeng Tan } 1866a84c37eSJianfeng Tan 1876a84c37eSJianfeng Tan return 0; 1886a84c37eSJianfeng Tan } 1896a84c37eSJianfeng Tan 19006856cabSMaxime Coquelin static int 191539d910cSMaxime Coquelin vhost_user_check_reply_ack(struct virtio_user_dev *dev, struct vhost_user_msg *msg) 192539d910cSMaxime Coquelin { 19394973531SMaxime Coquelin struct vhost_user_data *data = dev->backend_data; 194539d910cSMaxime Coquelin enum vhost_user_request req = msg->request; 195539d910cSMaxime Coquelin int ret; 196539d910cSMaxime Coquelin 197539d910cSMaxime Coquelin if (!(msg->flags & VHOST_USER_NEED_REPLY_MASK)) 198539d910cSMaxime Coquelin return 0; 199539d910cSMaxime Coquelin 20094973531SMaxime Coquelin ret = vhost_user_read(data->vhostfd, msg); 201539d910cSMaxime Coquelin if (ret < 0) { 202539d910cSMaxime Coquelin PMD_DRV_LOG(ERR, "Failed to read reply-ack"); 203539d910cSMaxime Coquelin return -1; 204539d910cSMaxime Coquelin } 205539d910cSMaxime Coquelin 206539d910cSMaxime Coquelin if (req != msg->request) { 207539d910cSMaxime Coquelin PMD_DRV_LOG(ERR, "Unexpected reply-ack request type (%d)", msg->request); 208539d910cSMaxime Coquelin return -1; 209539d910cSMaxime Coquelin } 210539d910cSMaxime Coquelin 211539d910cSMaxime Coquelin if (msg->size != sizeof(msg->payload.u64)) { 212539d910cSMaxime Coquelin PMD_DRV_LOG(ERR, "Unexpected reply-ack payload size (%u)", msg->size); 213539d910cSMaxime Coquelin return -1; 214539d910cSMaxime Coquelin } 215539d910cSMaxime Coquelin 216539d910cSMaxime Coquelin if (msg->payload.u64) { 217539d910cSMaxime Coquelin PMD_DRV_LOG(ERR, "Slave replied NACK to request type (%d)", msg->request); 218539d910cSMaxime Coquelin return -1; 219539d910cSMaxime Coquelin } 220539d910cSMaxime Coquelin 221539d910cSMaxime Coquelin return 0; 222539d910cSMaxime Coquelin } 223539d910cSMaxime Coquelin 224539d910cSMaxime Coquelin static int 22506856cabSMaxime Coquelin vhost_user_set_owner(struct virtio_user_dev *dev) 22606856cabSMaxime Coquelin { 22706856cabSMaxime Coquelin int ret; 22894973531SMaxime Coquelin struct vhost_user_data *data = dev->backend_data; 22906856cabSMaxime Coquelin struct vhost_user_msg msg = { 23006856cabSMaxime Coquelin .request = VHOST_USER_SET_OWNER, 23106856cabSMaxime Coquelin .flags = VHOST_USER_VERSION, 23206856cabSMaxime Coquelin }; 23306856cabSMaxime Coquelin 23494973531SMaxime Coquelin ret = vhost_user_write(data->vhostfd, &msg, NULL, 0); 23506856cabSMaxime Coquelin if (ret < 0) { 23606856cabSMaxime Coquelin PMD_DRV_LOG(ERR, "Failed to set owner"); 23706856cabSMaxime Coquelin return -1; 23806856cabSMaxime Coquelin } 23906856cabSMaxime Coquelin 24006856cabSMaxime Coquelin return 0; 24106856cabSMaxime Coquelin } 24206856cabSMaxime Coquelin 243cc0151b3SMaxime Coquelin static int 2446c251919SMaxime Coquelin vhost_user_get_protocol_features(struct virtio_user_dev *dev, uint64_t *features) 2456c251919SMaxime Coquelin { 2466c251919SMaxime Coquelin int ret; 24794973531SMaxime Coquelin struct vhost_user_data *data = dev->backend_data; 2486c251919SMaxime Coquelin struct vhost_user_msg msg = { 2496c251919SMaxime Coquelin .request = VHOST_USER_GET_PROTOCOL_FEATURES, 2506c251919SMaxime Coquelin .flags = VHOST_USER_VERSION, 2516c251919SMaxime Coquelin }; 2526c251919SMaxime Coquelin 25394973531SMaxime Coquelin ret = vhost_user_write(data->vhostfd, &msg, NULL, 0); 2546c251919SMaxime Coquelin if (ret < 0) 2556c251919SMaxime Coquelin goto err; 2566c251919SMaxime Coquelin 25794973531SMaxime Coquelin ret = vhost_user_read(data->vhostfd, &msg); 2586c251919SMaxime Coquelin if (ret < 0) 2596c251919SMaxime Coquelin goto err; 2606c251919SMaxime Coquelin 2616c251919SMaxime Coquelin if (msg.request != VHOST_USER_GET_PROTOCOL_FEATURES) { 2626c251919SMaxime Coquelin PMD_DRV_LOG(ERR, "Unexpected request type (%d)", msg.request); 2636c251919SMaxime Coquelin goto err; 2646c251919SMaxime Coquelin } 2656c251919SMaxime Coquelin 2666c251919SMaxime Coquelin if (msg.size != sizeof(*features)) { 2676c251919SMaxime Coquelin PMD_DRV_LOG(ERR, "Unexpected payload size (%u)", msg.size); 2686c251919SMaxime Coquelin goto err; 2696c251919SMaxime Coquelin } 2706c251919SMaxime Coquelin 2716c251919SMaxime Coquelin *features = msg.payload.u64; 2726c251919SMaxime Coquelin 2736c251919SMaxime Coquelin return 0; 2746c251919SMaxime Coquelin err: 2756c251919SMaxime Coquelin PMD_DRV_LOG(ERR, "Failed to get backend protocol features"); 2766c251919SMaxime Coquelin 2776c251919SMaxime Coquelin return -1; 2786c251919SMaxime Coquelin } 2796c251919SMaxime Coquelin 2806c251919SMaxime Coquelin static int 2816c251919SMaxime Coquelin vhost_user_set_protocol_features(struct virtio_user_dev *dev, uint64_t features) 2826c251919SMaxime Coquelin { 2836c251919SMaxime Coquelin int ret; 28494973531SMaxime Coquelin struct vhost_user_data *data = dev->backend_data; 2856c251919SMaxime Coquelin struct vhost_user_msg msg = { 2866c251919SMaxime Coquelin .request = VHOST_USER_SET_PROTOCOL_FEATURES, 2876c251919SMaxime Coquelin .flags = VHOST_USER_VERSION, 2886c251919SMaxime Coquelin .size = sizeof(features), 2896c251919SMaxime Coquelin .payload.u64 = features, 2906c251919SMaxime Coquelin }; 2916c251919SMaxime Coquelin 29294973531SMaxime Coquelin ret = vhost_user_write(data->vhostfd, &msg, NULL, 0); 2936c251919SMaxime Coquelin if (ret < 0) { 2946c251919SMaxime Coquelin PMD_DRV_LOG(ERR, "Failed to set protocol features"); 2956c251919SMaxime Coquelin return -1; 2966c251919SMaxime Coquelin } 2976c251919SMaxime Coquelin 2986c251919SMaxime Coquelin return 0; 2996c251919SMaxime Coquelin } 3006c251919SMaxime Coquelin 3015b75b63cSMaxime Coquelin static int 3025b75b63cSMaxime Coquelin vhost_user_get_features(struct virtio_user_dev *dev, uint64_t *features) 3035b75b63cSMaxime Coquelin { 3045b75b63cSMaxime Coquelin int ret; 305748e5ea5SMaxime Coquelin struct vhost_user_data *data = dev->backend_data; 3065b75b63cSMaxime Coquelin struct vhost_user_msg msg = { 3075b75b63cSMaxime Coquelin .request = VHOST_USER_GET_FEATURES, 3085b75b63cSMaxime Coquelin .flags = VHOST_USER_VERSION, 3095b75b63cSMaxime Coquelin }; 3105b75b63cSMaxime Coquelin 31194973531SMaxime Coquelin ret = vhost_user_write(data->vhostfd, &msg, NULL, 0); 3125b75b63cSMaxime Coquelin if (ret < 0) 3135b75b63cSMaxime Coquelin goto err; 3145b75b63cSMaxime Coquelin 31594973531SMaxime Coquelin ret = vhost_user_read(data->vhostfd, &msg); 3165b75b63cSMaxime Coquelin if (ret < 0) 3175b75b63cSMaxime Coquelin goto err; 3185b75b63cSMaxime Coquelin 3195b75b63cSMaxime Coquelin if (msg.request != VHOST_USER_GET_FEATURES) { 3205b75b63cSMaxime Coquelin PMD_DRV_LOG(ERR, "Unexpected request type (%d)", msg.request); 3215b75b63cSMaxime Coquelin goto err; 3225b75b63cSMaxime Coquelin } 3235b75b63cSMaxime Coquelin 3245b75b63cSMaxime Coquelin if (msg.size != sizeof(*features)) { 3255b75b63cSMaxime Coquelin PMD_DRV_LOG(ERR, "Unexpected payload size (%u)", msg.size); 3265b75b63cSMaxime Coquelin goto err; 3275b75b63cSMaxime Coquelin } 3285b75b63cSMaxime Coquelin 3295b75b63cSMaxime Coquelin *features = msg.payload.u64; 3305b75b63cSMaxime Coquelin 3315b75b63cSMaxime Coquelin if (!(*features & (1ULL << VHOST_USER_F_PROTOCOL_FEATURES))) 3325b75b63cSMaxime Coquelin return 0; 3335b75b63cSMaxime Coquelin 3345b75b63cSMaxime Coquelin /* Negotiate protocol features */ 335748e5ea5SMaxime Coquelin ret = vhost_user_get_protocol_features(dev, &data->protocol_features); 3365b75b63cSMaxime Coquelin if (ret < 0) 3375b75b63cSMaxime Coquelin goto err; 3385b75b63cSMaxime Coquelin 339748e5ea5SMaxime Coquelin data->protocol_features &= VHOST_USER_SUPPORTED_PROTOCOL_FEATURES; 3405b75b63cSMaxime Coquelin 341748e5ea5SMaxime Coquelin ret = vhost_user_set_protocol_features(dev, data->protocol_features); 3425b75b63cSMaxime Coquelin if (ret < 0) 3435b75b63cSMaxime Coquelin goto err; 3445b75b63cSMaxime Coquelin 345748e5ea5SMaxime Coquelin if (!(data->protocol_features & (1ULL << VHOST_USER_PROTOCOL_F_MQ))) 3465b75b63cSMaxime Coquelin dev->unsupported_features |= (1ull << VIRTIO_NET_F_MQ); 3475b75b63cSMaxime Coquelin 3485b75b63cSMaxime Coquelin return 0; 3495b75b63cSMaxime Coquelin err: 3505b75b63cSMaxime Coquelin PMD_DRV_LOG(ERR, "Failed to get backend features"); 3515b75b63cSMaxime Coquelin 3525b75b63cSMaxime Coquelin return -1; 3535b75b63cSMaxime Coquelin } 3545b75b63cSMaxime Coquelin 3555b75b63cSMaxime Coquelin static int 3565b75b63cSMaxime Coquelin vhost_user_set_features(struct virtio_user_dev *dev, uint64_t features) 3575b75b63cSMaxime Coquelin { 3585b75b63cSMaxime Coquelin int ret; 35994973531SMaxime Coquelin struct vhost_user_data *data = dev->backend_data; 3605b75b63cSMaxime Coquelin struct vhost_user_msg msg = { 3615b75b63cSMaxime Coquelin .request = VHOST_USER_SET_FEATURES, 3625b75b63cSMaxime Coquelin .flags = VHOST_USER_VERSION, 3635b75b63cSMaxime Coquelin .size = sizeof(features), 3645b75b63cSMaxime Coquelin .payload.u64 = features, 3655b75b63cSMaxime Coquelin }; 3665b75b63cSMaxime Coquelin 3675b75b63cSMaxime Coquelin msg.payload.u64 |= dev->device_features & (1ULL << VHOST_USER_F_PROTOCOL_FEATURES); 3685b75b63cSMaxime Coquelin 36994973531SMaxime Coquelin ret = vhost_user_write(data->vhostfd, &msg, NULL, 0); 3705b75b63cSMaxime Coquelin if (ret < 0) { 3715b75b63cSMaxime Coquelin PMD_DRV_LOG(ERR, "Failed to set features"); 3725b75b63cSMaxime Coquelin return -1; 3735b75b63cSMaxime Coquelin } 3745b75b63cSMaxime Coquelin 3755b75b63cSMaxime Coquelin return 0; 3765b75b63cSMaxime Coquelin } 3775b75b63cSMaxime Coquelin 378534bccbaSTiwei Bie struct walk_arg { 379534bccbaSTiwei Bie struct vhost_memory *vm; 380534bccbaSTiwei Bie int *fds; 381534bccbaSTiwei Bie int region_nr; 3826a84c37eSJianfeng Tan }; 3836a84c37eSJianfeng Tan 3846a84c37eSJianfeng Tan static int 385534bccbaSTiwei Bie update_memory_region(const struct rte_memseg_list *msl __rte_unused, 386534bccbaSTiwei Bie const struct rte_memseg *ms, void *arg) 3876a84c37eSJianfeng Tan { 388534bccbaSTiwei Bie struct walk_arg *wa = arg; 389534bccbaSTiwei Bie struct vhost_memory_region *mr; 390534bccbaSTiwei Bie uint64_t start_addr, end_addr; 391534bccbaSTiwei Bie size_t offset; 392534bccbaSTiwei Bie int i, fd; 3936a84c37eSJianfeng Tan 394534bccbaSTiwei Bie fd = rte_memseg_get_fd_thread_unsafe(ms); 395534bccbaSTiwei Bie if (fd < 0) { 396534bccbaSTiwei Bie PMD_DRV_LOG(ERR, "Failed to get fd, ms=%p rte_errno=%d", 397534bccbaSTiwei Bie ms, rte_errno); 3986a84c37eSJianfeng Tan return -1; 3996a84c37eSJianfeng Tan } 4006a84c37eSJianfeng Tan 401534bccbaSTiwei Bie if (rte_memseg_get_fd_offset_thread_unsafe(ms, &offset) < 0) { 402534bccbaSTiwei Bie PMD_DRV_LOG(ERR, "Failed to get offset, ms=%p rte_errno=%d", 403534bccbaSTiwei Bie ms, rte_errno); 4046a84c37eSJianfeng Tan return -1; 4056a84c37eSJianfeng Tan } 4066a84c37eSJianfeng Tan 407534bccbaSTiwei Bie start_addr = (uint64_t)(uintptr_t)ms->addr; 408534bccbaSTiwei Bie end_addr = start_addr + ms->len; 409534bccbaSTiwei Bie 410534bccbaSTiwei Bie for (i = 0; i < wa->region_nr; i++) { 411534bccbaSTiwei Bie if (wa->fds[i] != fd) 412534bccbaSTiwei Bie continue; 413534bccbaSTiwei Bie 414534bccbaSTiwei Bie mr = &wa->vm->regions[i]; 415534bccbaSTiwei Bie 416534bccbaSTiwei Bie if (mr->userspace_addr + mr->memory_size < end_addr) 417534bccbaSTiwei Bie mr->memory_size = end_addr - mr->userspace_addr; 418534bccbaSTiwei Bie 419534bccbaSTiwei Bie if (mr->userspace_addr > start_addr) { 420534bccbaSTiwei Bie mr->userspace_addr = start_addr; 421534bccbaSTiwei Bie mr->guest_phys_addr = start_addr; 422534bccbaSTiwei Bie } 423534bccbaSTiwei Bie 424534bccbaSTiwei Bie if (mr->mmap_offset > offset) 425534bccbaSTiwei Bie mr->mmap_offset = offset; 426534bccbaSTiwei Bie 427534bccbaSTiwei Bie PMD_DRV_LOG(DEBUG, "index=%d fd=%d offset=0x%" PRIx64 428534bccbaSTiwei Bie " addr=0x%" PRIx64 " len=%" PRIu64, i, fd, 429534bccbaSTiwei Bie mr->mmap_offset, mr->userspace_addr, 430534bccbaSTiwei Bie mr->memory_size); 431534bccbaSTiwei Bie 432534bccbaSTiwei Bie return 0; 433534bccbaSTiwei Bie } 434534bccbaSTiwei Bie 435534bccbaSTiwei Bie if (i >= VHOST_MEMORY_MAX_NREGIONS) { 436534bccbaSTiwei Bie PMD_DRV_LOG(ERR, "Too many memory regions"); 437534bccbaSTiwei Bie return -1; 438534bccbaSTiwei Bie } 439534bccbaSTiwei Bie 440534bccbaSTiwei Bie mr = &wa->vm->regions[i]; 441534bccbaSTiwei Bie wa->fds[i] = fd; 442534bccbaSTiwei Bie 443534bccbaSTiwei Bie mr->guest_phys_addr = start_addr; 444534bccbaSTiwei Bie mr->userspace_addr = start_addr; 445534bccbaSTiwei Bie mr->memory_size = ms->len; 446534bccbaSTiwei Bie mr->mmap_offset = offset; 447534bccbaSTiwei Bie 448534bccbaSTiwei Bie PMD_DRV_LOG(DEBUG, "index=%d fd=%d offset=0x%" PRIx64 449534bccbaSTiwei Bie " addr=0x%" PRIx64 " len=%" PRIu64, i, fd, 450534bccbaSTiwei Bie mr->mmap_offset, mr->userspace_addr, 451534bccbaSTiwei Bie mr->memory_size); 452534bccbaSTiwei Bie 453534bccbaSTiwei Bie wa->region_nr++; 454534bccbaSTiwei Bie 455534bccbaSTiwei Bie return 0; 456534bccbaSTiwei Bie } 457534bccbaSTiwei Bie 4586a84c37eSJianfeng Tan static int 459539d910cSMaxime Coquelin vhost_user_set_memory_table(struct virtio_user_dev *dev) 4606a84c37eSJianfeng Tan { 461534bccbaSTiwei Bie struct walk_arg wa; 462539d910cSMaxime Coquelin int fds[VHOST_MEMORY_MAX_NREGIONS]; 463539d910cSMaxime Coquelin int ret, fd_num; 464748e5ea5SMaxime Coquelin struct vhost_user_data *data = dev->backend_data; 465539d910cSMaxime Coquelin struct vhost_user_msg msg = { 466539d910cSMaxime Coquelin .request = VHOST_USER_SET_MEM_TABLE, 467539d910cSMaxime Coquelin .flags = VHOST_USER_VERSION, 468539d910cSMaxime Coquelin }; 469539d910cSMaxime Coquelin 470748e5ea5SMaxime Coquelin if (data->protocol_features & (1ULL << VHOST_USER_PROTOCOL_F_REPLY_ACK)) 471539d910cSMaxime Coquelin msg.flags |= VHOST_USER_NEED_REPLY_MASK; 4726a84c37eSJianfeng Tan 473534bccbaSTiwei Bie wa.region_nr = 0; 474539d910cSMaxime Coquelin wa.vm = &msg.payload.memory; 475534bccbaSTiwei Bie wa.fds = fds; 476534bccbaSTiwei Bie 477534bccbaSTiwei Bie /* 478534bccbaSTiwei Bie * The memory lock has already been taken by memory subsystem 479534bccbaSTiwei Bie * or virtio_user_start_device(). 480534bccbaSTiwei Bie */ 481539d910cSMaxime Coquelin ret = rte_memseg_walk_thread_unsafe(update_memory_region, &wa); 482539d910cSMaxime Coquelin if (ret < 0) 483539d910cSMaxime Coquelin goto err; 484539d910cSMaxime Coquelin 485539d910cSMaxime Coquelin fd_num = wa.region_nr; 486539d910cSMaxime Coquelin msg.payload.memory.nregions = wa.region_nr; 487539d910cSMaxime Coquelin msg.payload.memory.padding = 0; 488539d910cSMaxime Coquelin 489539d910cSMaxime Coquelin msg.size = sizeof(msg.payload.memory.nregions); 490539d910cSMaxime Coquelin msg.size += sizeof(msg.payload.memory.padding); 491539d910cSMaxime Coquelin msg.size += fd_num * sizeof(struct vhost_memory_region); 492539d910cSMaxime Coquelin 49394973531SMaxime Coquelin ret = vhost_user_write(data->vhostfd, &msg, fds, fd_num); 494539d910cSMaxime Coquelin if (ret < 0) 495539d910cSMaxime Coquelin goto err; 496539d910cSMaxime Coquelin 497539d910cSMaxime Coquelin return vhost_user_check_reply_ack(dev, &msg); 498539d910cSMaxime Coquelin err: 499539d910cSMaxime Coquelin PMD_DRV_LOG(ERR, "Failed to set memory table"); 5006a84c37eSJianfeng Tan return -1; 5016a84c37eSJianfeng Tan } 5026a84c37eSJianfeng Tan 503ab9098d2SMaxime Coquelin static int 504ab9098d2SMaxime Coquelin vhost_user_set_vring(struct virtio_user_dev *dev, enum vhost_user_request req, 505ab9098d2SMaxime Coquelin struct vhost_vring_state *state) 506ab9098d2SMaxime Coquelin { 507ab9098d2SMaxime Coquelin int ret; 50894973531SMaxime Coquelin struct vhost_user_data *data = dev->backend_data; 509ab9098d2SMaxime Coquelin struct vhost_user_msg msg = { 510ab9098d2SMaxime Coquelin .request = req, 511ab9098d2SMaxime Coquelin .flags = VHOST_USER_VERSION, 512ab9098d2SMaxime Coquelin .size = sizeof(*state), 513ab9098d2SMaxime Coquelin .payload.state = *state, 514ab9098d2SMaxime Coquelin }; 515ab9098d2SMaxime Coquelin 51694973531SMaxime Coquelin ret = vhost_user_write(data->vhostfd, &msg, NULL, 0); 517ab9098d2SMaxime Coquelin if (ret < 0) { 518ab9098d2SMaxime Coquelin PMD_DRV_LOG(ERR, "Failed to set vring state (request %d)", req); 519ab9098d2SMaxime Coquelin return -1; 520ab9098d2SMaxime Coquelin } 521ab9098d2SMaxime Coquelin 522ab9098d2SMaxime Coquelin return 0; 523ab9098d2SMaxime Coquelin } 524ab9098d2SMaxime Coquelin 525ab9098d2SMaxime Coquelin static int 526ab9098d2SMaxime Coquelin vhost_user_set_vring_enable(struct virtio_user_dev *dev, struct vhost_vring_state *state) 527ab9098d2SMaxime Coquelin { 528ab9098d2SMaxime Coquelin return vhost_user_set_vring(dev, VHOST_USER_SET_VRING_ENABLE, state); 529ab9098d2SMaxime Coquelin } 530ab9098d2SMaxime Coquelin 531ab9098d2SMaxime Coquelin static int 532ab9098d2SMaxime Coquelin vhost_user_set_vring_num(struct virtio_user_dev *dev, struct vhost_vring_state *state) 533ab9098d2SMaxime Coquelin { 534ab9098d2SMaxime Coquelin return vhost_user_set_vring(dev, VHOST_USER_SET_VRING_NUM, state); 535ab9098d2SMaxime Coquelin } 536ab9098d2SMaxime Coquelin 537ab9098d2SMaxime Coquelin static int 538ab9098d2SMaxime Coquelin vhost_user_set_vring_base(struct virtio_user_dev *dev, struct vhost_vring_state *state) 539ab9098d2SMaxime Coquelin { 540ab9098d2SMaxime Coquelin return vhost_user_set_vring(dev, VHOST_USER_SET_VRING_BASE, state); 541ab9098d2SMaxime Coquelin } 542ab9098d2SMaxime Coquelin 543ab9098d2SMaxime Coquelin static int 544ab9098d2SMaxime Coquelin vhost_user_get_vring_base(struct virtio_user_dev *dev, struct vhost_vring_state *state) 545ab9098d2SMaxime Coquelin { 546ab9098d2SMaxime Coquelin int ret; 547ab9098d2SMaxime Coquelin struct vhost_user_msg msg; 54894973531SMaxime Coquelin struct vhost_user_data *data = dev->backend_data; 549ab9098d2SMaxime Coquelin unsigned int index = state->index; 550ab9098d2SMaxime Coquelin 551ab9098d2SMaxime Coquelin ret = vhost_user_set_vring(dev, VHOST_USER_GET_VRING_BASE, state); 552ab9098d2SMaxime Coquelin if (ret < 0) { 553ab9098d2SMaxime Coquelin PMD_DRV_LOG(ERR, "Failed to send request"); 554ab9098d2SMaxime Coquelin goto err; 555ab9098d2SMaxime Coquelin } 556ab9098d2SMaxime Coquelin 55794973531SMaxime Coquelin ret = vhost_user_read(data->vhostfd, &msg); 558ab9098d2SMaxime Coquelin if (ret < 0) { 559ab9098d2SMaxime Coquelin PMD_DRV_LOG(ERR, "Failed to read reply"); 560ab9098d2SMaxime Coquelin goto err; 561ab9098d2SMaxime Coquelin } 562ab9098d2SMaxime Coquelin 563ab9098d2SMaxime Coquelin if (msg.request != VHOST_USER_GET_VRING_BASE) { 564ab9098d2SMaxime Coquelin PMD_DRV_LOG(ERR, "Unexpected request type (%d)", msg.request); 565ab9098d2SMaxime Coquelin goto err; 566ab9098d2SMaxime Coquelin } 567ab9098d2SMaxime Coquelin 568ab9098d2SMaxime Coquelin if (msg.size != sizeof(*state)) { 569ab9098d2SMaxime Coquelin PMD_DRV_LOG(ERR, "Unexpected payload size (%u)", msg.size); 570ab9098d2SMaxime Coquelin goto err; 571ab9098d2SMaxime Coquelin } 572ab9098d2SMaxime Coquelin 573ab9098d2SMaxime Coquelin if (msg.payload.state.index != index) { 574ab9098d2SMaxime Coquelin PMD_DRV_LOG(ERR, "Unexpected ring index (%u)", state->index); 575ab9098d2SMaxime Coquelin goto err; 576ab9098d2SMaxime Coquelin } 577ab9098d2SMaxime Coquelin 578ab9098d2SMaxime Coquelin *state = msg.payload.state; 579ab9098d2SMaxime Coquelin 580ab9098d2SMaxime Coquelin return 0; 581ab9098d2SMaxime Coquelin err: 582ab9098d2SMaxime Coquelin PMD_DRV_LOG(ERR, "Failed to get vring base"); 583ab9098d2SMaxime Coquelin return -1; 584ab9098d2SMaxime Coquelin } 585ab9098d2SMaxime Coquelin 586ce399c36SMaxime Coquelin static int 587ce399c36SMaxime Coquelin vhost_user_set_vring_file(struct virtio_user_dev *dev, enum vhost_user_request req, 588ce399c36SMaxime Coquelin struct vhost_vring_file *file) 589ce399c36SMaxime Coquelin { 590ce399c36SMaxime Coquelin int ret; 591ce399c36SMaxime Coquelin int fd = file->fd; 592ce399c36SMaxime Coquelin int num_fd = 0; 59394973531SMaxime Coquelin struct vhost_user_data *data = dev->backend_data; 594ce399c36SMaxime Coquelin struct vhost_user_msg msg = { 595ce399c36SMaxime Coquelin .request = req, 596ce399c36SMaxime Coquelin .flags = VHOST_USER_VERSION, 597ce399c36SMaxime Coquelin .size = sizeof(msg.payload.u64), 598ce399c36SMaxime Coquelin .payload.u64 = file->index & VHOST_USER_VRING_IDX_MASK, 599ce399c36SMaxime Coquelin }; 600ce399c36SMaxime Coquelin 601ce399c36SMaxime Coquelin if (fd >= 0) 602ce399c36SMaxime Coquelin num_fd++; 603ce399c36SMaxime Coquelin else 604ce399c36SMaxime Coquelin msg.payload.u64 |= VHOST_USER_VRING_NOFD_MASK; 605ce399c36SMaxime Coquelin 60694973531SMaxime Coquelin ret = vhost_user_write(data->vhostfd, &msg, &fd, num_fd); 607ce399c36SMaxime Coquelin if (ret < 0) { 608ce399c36SMaxime Coquelin PMD_DRV_LOG(ERR, "Failed to set vring file (request %d)", req); 609ce399c36SMaxime Coquelin return -1; 610ce399c36SMaxime Coquelin } 611ce399c36SMaxime Coquelin 612ce399c36SMaxime Coquelin return 0; 613ce399c36SMaxime Coquelin } 614ce399c36SMaxime Coquelin 615ce399c36SMaxime Coquelin static int 616ce399c36SMaxime Coquelin vhost_user_set_vring_call(struct virtio_user_dev *dev, struct vhost_vring_file *file) 617ce399c36SMaxime Coquelin { 618ce399c36SMaxime Coquelin return vhost_user_set_vring_file(dev, VHOST_USER_SET_VRING_CALL, file); 619ce399c36SMaxime Coquelin } 620ce399c36SMaxime Coquelin 621ce399c36SMaxime Coquelin static int 622ce399c36SMaxime Coquelin vhost_user_set_vring_kick(struct virtio_user_dev *dev, struct vhost_vring_file *file) 623ce399c36SMaxime Coquelin { 624ce399c36SMaxime Coquelin return vhost_user_set_vring_file(dev, VHOST_USER_SET_VRING_KICK, file); 625ce399c36SMaxime Coquelin } 626ce399c36SMaxime Coquelin 627dc65db73SMaxime Coquelin 628dc65db73SMaxime Coquelin static int 629dc65db73SMaxime Coquelin vhost_user_set_vring_addr(struct virtio_user_dev *dev, struct vhost_vring_addr *addr) 630dc65db73SMaxime Coquelin { 631dc65db73SMaxime Coquelin int ret; 63294973531SMaxime Coquelin struct vhost_user_data *data = dev->backend_data; 633dc65db73SMaxime Coquelin struct vhost_user_msg msg = { 634dc65db73SMaxime Coquelin .request = VHOST_USER_SET_VRING_ADDR, 635dc65db73SMaxime Coquelin .flags = VHOST_USER_VERSION, 636dc65db73SMaxime Coquelin .size = sizeof(*addr), 637dc65db73SMaxime Coquelin .payload.addr = *addr, 638dc65db73SMaxime Coquelin }; 639dc65db73SMaxime Coquelin 64094973531SMaxime Coquelin ret = vhost_user_write(data->vhostfd, &msg, NULL, 0); 641dc65db73SMaxime Coquelin if (ret < 0) { 642dc65db73SMaxime Coquelin PMD_DRV_LOG(ERR, "Failed to send vring addresses"); 643dc65db73SMaxime Coquelin return -1; 644dc65db73SMaxime Coquelin } 645dc65db73SMaxime Coquelin 646dc65db73SMaxime Coquelin return 0; 647dc65db73SMaxime Coquelin } 648dc65db73SMaxime Coquelin 6498723c894SMaxime Coquelin static int 6508723c894SMaxime Coquelin vhost_user_get_status(struct virtio_user_dev *dev, uint8_t *status) 6518723c894SMaxime Coquelin { 6528723c894SMaxime Coquelin int ret; 653748e5ea5SMaxime Coquelin struct vhost_user_data *data = dev->backend_data; 6548723c894SMaxime Coquelin struct vhost_user_msg msg = { 6558723c894SMaxime Coquelin .request = VHOST_USER_GET_STATUS, 6568723c894SMaxime Coquelin .flags = VHOST_USER_VERSION, 6578723c894SMaxime Coquelin }; 6588723c894SMaxime Coquelin 6598723c894SMaxime Coquelin /* 6608723c894SMaxime Coquelin * If features have not been negotiated, we don't know if the backend 6618723c894SMaxime Coquelin * supports protocol features 6628723c894SMaxime Coquelin */ 6638723c894SMaxime Coquelin if (!(dev->status & VIRTIO_CONFIG_STATUS_FEATURES_OK)) 6648723c894SMaxime Coquelin return -ENOTSUP; 6658723c894SMaxime Coquelin 6668723c894SMaxime Coquelin /* Status protocol feature requires protocol features support */ 6678723c894SMaxime Coquelin if (!(dev->device_features & (1ULL << VHOST_USER_F_PROTOCOL_FEATURES))) 6688723c894SMaxime Coquelin return -ENOTSUP; 6698723c894SMaxime Coquelin 670748e5ea5SMaxime Coquelin if (!(data->protocol_features & (1ULL << VHOST_USER_PROTOCOL_F_STATUS))) 6718723c894SMaxime Coquelin return -ENOTSUP; 6728723c894SMaxime Coquelin 67394973531SMaxime Coquelin ret = vhost_user_write(data->vhostfd, &msg, NULL, 0); 6748723c894SMaxime Coquelin if (ret < 0) { 6758723c894SMaxime Coquelin PMD_DRV_LOG(ERR, "Failed to send request"); 6768723c894SMaxime Coquelin goto err; 6778723c894SMaxime Coquelin } 6788723c894SMaxime Coquelin 67994973531SMaxime Coquelin ret = vhost_user_read(data->vhostfd, &msg); 6808723c894SMaxime Coquelin if (ret < 0) { 6818723c894SMaxime Coquelin PMD_DRV_LOG(ERR, "Failed to recv request"); 6828723c894SMaxime Coquelin goto err; 6838723c894SMaxime Coquelin } 6848723c894SMaxime Coquelin 6858723c894SMaxime Coquelin if (msg.request != VHOST_USER_GET_STATUS) { 6868723c894SMaxime Coquelin PMD_DRV_LOG(ERR, "Unexpected request type (%d)", msg.request); 6878723c894SMaxime Coquelin goto err; 6888723c894SMaxime Coquelin } 6898723c894SMaxime Coquelin 6908723c894SMaxime Coquelin if (msg.size != sizeof(msg.payload.u64)) { 6918723c894SMaxime Coquelin PMD_DRV_LOG(ERR, "Unexpected payload size (%u)", msg.size); 6928723c894SMaxime Coquelin goto err; 6938723c894SMaxime Coquelin } 6948723c894SMaxime Coquelin 6958723c894SMaxime Coquelin *status = (uint8_t)msg.payload.u64; 6968723c894SMaxime Coquelin 6978723c894SMaxime Coquelin return 0; 6988723c894SMaxime Coquelin err: 6998723c894SMaxime Coquelin PMD_DRV_LOG(ERR, "Failed to get device status"); 7008723c894SMaxime Coquelin return -1; 7018723c894SMaxime Coquelin } 7028723c894SMaxime Coquelin 7038723c894SMaxime Coquelin static int 7048723c894SMaxime Coquelin vhost_user_set_status(struct virtio_user_dev *dev, uint8_t status) 7058723c894SMaxime Coquelin { 7068723c894SMaxime Coquelin int ret; 707748e5ea5SMaxime Coquelin struct vhost_user_data *data = dev->backend_data; 7088723c894SMaxime Coquelin struct vhost_user_msg msg = { 7098723c894SMaxime Coquelin .request = VHOST_USER_SET_STATUS, 7108723c894SMaxime Coquelin .flags = VHOST_USER_VERSION, 7118723c894SMaxime Coquelin .size = sizeof(msg.payload.u64), 7128723c894SMaxime Coquelin .payload.u64 = status, 7138723c894SMaxime Coquelin }; 7148723c894SMaxime Coquelin 7158723c894SMaxime Coquelin /* 7168723c894SMaxime Coquelin * If features have not been negotiated, we don't know if the backend 7178723c894SMaxime Coquelin * supports protocol features 7188723c894SMaxime Coquelin */ 7198723c894SMaxime Coquelin if (!(dev->status & VIRTIO_CONFIG_STATUS_FEATURES_OK)) 7208723c894SMaxime Coquelin return -ENOTSUP; 7218723c894SMaxime Coquelin 7228723c894SMaxime Coquelin /* Status protocol feature requires protocol features support */ 7238723c894SMaxime Coquelin if (!(dev->device_features & (1ULL << VHOST_USER_F_PROTOCOL_FEATURES))) 7248723c894SMaxime Coquelin return -ENOTSUP; 7258723c894SMaxime Coquelin 726748e5ea5SMaxime Coquelin if (!(data->protocol_features & (1ULL << VHOST_USER_PROTOCOL_F_STATUS))) 7278723c894SMaxime Coquelin return -ENOTSUP; 7288723c894SMaxime Coquelin 729748e5ea5SMaxime Coquelin if (data->protocol_features & (1ULL << VHOST_USER_PROTOCOL_F_REPLY_ACK)) 7308723c894SMaxime Coquelin msg.flags |= VHOST_USER_NEED_REPLY_MASK; 7318723c894SMaxime Coquelin 73294973531SMaxime Coquelin ret = vhost_user_write(data->vhostfd, &msg, NULL, 0); 7338723c894SMaxime Coquelin if (ret < 0) { 7348723c894SMaxime Coquelin PMD_DRV_LOG(ERR, "Failed to send get status request"); 7358723c894SMaxime Coquelin return -1; 7368723c894SMaxime Coquelin } 7378723c894SMaxime Coquelin 7388723c894SMaxime Coquelin return vhost_user_check_reply_ack(dev, &msg); 7398723c894SMaxime Coquelin } 740dc65db73SMaxime Coquelin 741bd8f50a4SZhiyong Yang #define MAX_VIRTIO_USER_BACKLOG 1 742bd8f50a4SZhiyong Yang static int 74394973531SMaxime Coquelin vhost_user_start_server(struct virtio_user_dev *dev, struct sockaddr_un *un) 744bd8f50a4SZhiyong Yang { 745bd8f50a4SZhiyong Yang int ret; 746bd8f50a4SZhiyong Yang int flag; 74794973531SMaxime Coquelin struct vhost_user_data *data = dev->backend_data; 74894973531SMaxime Coquelin int fd = data->listenfd; 749bd8f50a4SZhiyong Yang 750bd8f50a4SZhiyong Yang ret = bind(fd, (struct sockaddr *)un, sizeof(*un)); 751bd8f50a4SZhiyong Yang if (ret < 0) { 752f3854ebaSThomas Monjalon PMD_DRV_LOG(ERR, "failed to bind to %s: %s; remove it and try again", 753bd8f50a4SZhiyong Yang dev->path, strerror(errno)); 754bd8f50a4SZhiyong Yang return -1; 755bd8f50a4SZhiyong Yang } 756bd8f50a4SZhiyong Yang ret = listen(fd, MAX_VIRTIO_USER_BACKLOG); 757bd8f50a4SZhiyong Yang if (ret < 0) 758bd8f50a4SZhiyong Yang return -1; 759bd8f50a4SZhiyong Yang 7609af79db2SMaxime Coquelin PMD_DRV_LOG(NOTICE, "(%s) waiting for client connection...", dev->path); 76194973531SMaxime Coquelin data->vhostfd = accept(fd, NULL, NULL); 76294973531SMaxime Coquelin if (data->vhostfd < 0) { 7639af79db2SMaxime Coquelin PMD_DRV_LOG(ERR, "Failed to accept initial client connection (%s)", 7649af79db2SMaxime Coquelin strerror(errno)); 7659af79db2SMaxime Coquelin return -1; 7669af79db2SMaxime Coquelin } 7679af79db2SMaxime Coquelin 768bd8f50a4SZhiyong Yang flag = fcntl(fd, F_GETFL); 7691e986888SChenbo Xia if (fcntl(fd, F_SETFL, flag | O_NONBLOCK) < 0) { 7701e986888SChenbo Xia PMD_DRV_LOG(ERR, "fcntl failed, %s", strerror(errno)); 7711e986888SChenbo Xia return -1; 7721e986888SChenbo Xia } 773bd8f50a4SZhiyong Yang 774bd8f50a4SZhiyong Yang return 0; 775bd8f50a4SZhiyong Yang } 776bd8f50a4SZhiyong Yang 77794973531SMaxime Coquelin static int 77894973531SMaxime Coquelin vhost_user_server_disconnect(struct virtio_user_dev *dev) 77994973531SMaxime Coquelin { 78094973531SMaxime Coquelin struct vhost_user_data *data = dev->backend_data; 78194973531SMaxime Coquelin 78294973531SMaxime Coquelin if (data->vhostfd < 0) { 78394973531SMaxime Coquelin PMD_DRV_LOG(ERR, "(%s) Expected valid Vhost FD", dev->path); 78494973531SMaxime Coquelin return -1; 78594973531SMaxime Coquelin } 78694973531SMaxime Coquelin 78794973531SMaxime Coquelin close(data->vhostfd); 78894973531SMaxime Coquelin data->vhostfd = -1; 78994973531SMaxime Coquelin 79094973531SMaxime Coquelin return 0; 79194973531SMaxime Coquelin } 79294973531SMaxime Coquelin 79394973531SMaxime Coquelin static int 79494973531SMaxime Coquelin vhost_user_server_reconnect(struct virtio_user_dev *dev) 79594973531SMaxime Coquelin { 79694973531SMaxime Coquelin struct vhost_user_data *data = dev->backend_data; 79794973531SMaxime Coquelin int fd; 79894973531SMaxime Coquelin 79994973531SMaxime Coquelin fd = accept(data->listenfd, NULL, NULL); 80094973531SMaxime Coquelin if (fd < 0) 80194973531SMaxime Coquelin return -1; 80294973531SMaxime Coquelin 80394973531SMaxime Coquelin data->vhostfd = fd; 80494973531SMaxime Coquelin 80594973531SMaxime Coquelin return 0; 80694973531SMaxime Coquelin } 80794973531SMaxime Coquelin 8086a84c37eSJianfeng Tan /** 8096a84c37eSJianfeng Tan * Set up environment to talk with a vhost user backend. 8106a84c37eSJianfeng Tan * 8116a84c37eSJianfeng Tan * @return 81233d24d65SJianfeng Tan * - (-1) if fail; 81333d24d65SJianfeng Tan * - (0) if succeed. 8146a84c37eSJianfeng Tan */ 81533d24d65SJianfeng Tan static int 81633d24d65SJianfeng Tan vhost_user_setup(struct virtio_user_dev *dev) 8176a84c37eSJianfeng Tan { 8186a84c37eSJianfeng Tan int fd; 8196a84c37eSJianfeng Tan int flag; 8206a84c37eSJianfeng Tan struct sockaddr_un un; 821748e5ea5SMaxime Coquelin struct vhost_user_data *data; 822748e5ea5SMaxime Coquelin 823748e5ea5SMaxime Coquelin data = malloc(sizeof(*data)); 824748e5ea5SMaxime Coquelin if (!data) { 825f3854ebaSThomas Monjalon PMD_DRV_LOG(ERR, "(%s) Failed to allocate Vhost-user data", dev->path); 826748e5ea5SMaxime Coquelin return -1; 827748e5ea5SMaxime Coquelin } 828748e5ea5SMaxime Coquelin 829748e5ea5SMaxime Coquelin memset(data, 0, sizeof(*data)); 830748e5ea5SMaxime Coquelin 831748e5ea5SMaxime Coquelin dev->backend_data = data; 8326a84c37eSJianfeng Tan 83394973531SMaxime Coquelin data->vhostfd = -1; 8340ea5be8dSMaxime Coquelin data->listenfd = -1; 83594973531SMaxime Coquelin 8366a84c37eSJianfeng Tan fd = socket(AF_UNIX, SOCK_STREAM, 0); 8376a84c37eSJianfeng Tan if (fd < 0) { 8386a84c37eSJianfeng Tan PMD_DRV_LOG(ERR, "socket() error, %s", strerror(errno)); 839748e5ea5SMaxime Coquelin goto err_data; 8406a84c37eSJianfeng Tan } 8416a84c37eSJianfeng Tan 8426a84c37eSJianfeng Tan flag = fcntl(fd, F_GETFD); 8436abf10a2SYunjian Wang if (flag == -1) 8446abf10a2SYunjian Wang PMD_DRV_LOG(WARNING, "fcntl get fd failed, %s", strerror(errno)); 8456abf10a2SYunjian Wang else if (fcntl(fd, F_SETFD, flag | FD_CLOEXEC) < 0) 8466abf10a2SYunjian Wang PMD_DRV_LOG(WARNING, "fcntl set fd failed, %s", strerror(errno)); 8476a84c37eSJianfeng Tan 8486a84c37eSJianfeng Tan memset(&un, 0, sizeof(un)); 8496a84c37eSJianfeng Tan un.sun_family = AF_UNIX; 8506723c0fcSBruce Richardson strlcpy(un.sun_path, dev->path, sizeof(un.sun_path)); 851bd8f50a4SZhiyong Yang 852bd8f50a4SZhiyong Yang if (dev->is_server) { 85394973531SMaxime Coquelin data->listenfd = fd; 85494973531SMaxime Coquelin if (vhost_user_start_server(dev, &un) < 0) { 855bd8f50a4SZhiyong Yang PMD_DRV_LOG(ERR, "virtio-user startup fails in server mode"); 856748e5ea5SMaxime Coquelin goto err_socket; 857bd8f50a4SZhiyong Yang } 858bd8f50a4SZhiyong Yang } else { 8596a84c37eSJianfeng Tan if (connect(fd, (struct sockaddr *)&un, sizeof(un)) < 0) { 8606a84c37eSJianfeng Tan PMD_DRV_LOG(ERR, "connect error, %s", strerror(errno)); 861748e5ea5SMaxime Coquelin goto err_socket; 8626a84c37eSJianfeng Tan } 86394973531SMaxime Coquelin data->vhostfd = fd; 864bd8f50a4SZhiyong Yang } 865bd8f50a4SZhiyong Yang 86633d24d65SJianfeng Tan return 0; 867748e5ea5SMaxime Coquelin 868748e5ea5SMaxime Coquelin err_socket: 869748e5ea5SMaxime Coquelin close(fd); 870748e5ea5SMaxime Coquelin err_data: 871748e5ea5SMaxime Coquelin free(data); 872748e5ea5SMaxime Coquelin dev->backend_data = NULL; 873748e5ea5SMaxime Coquelin 874748e5ea5SMaxime Coquelin return -1; 875748e5ea5SMaxime Coquelin } 876748e5ea5SMaxime Coquelin 877748e5ea5SMaxime Coquelin static int 878748e5ea5SMaxime Coquelin vhost_user_destroy(struct virtio_user_dev *dev) 879748e5ea5SMaxime Coquelin { 88094973531SMaxime Coquelin struct vhost_user_data *data = dev->backend_data; 88194973531SMaxime Coquelin 88294973531SMaxime Coquelin if (!data) 88394973531SMaxime Coquelin return 0; 88494973531SMaxime Coquelin 88594973531SMaxime Coquelin if (data->vhostfd >= 0) { 88694973531SMaxime Coquelin close(data->vhostfd); 88794973531SMaxime Coquelin data->vhostfd = -1; 888748e5ea5SMaxime Coquelin } 889748e5ea5SMaxime Coquelin 89094973531SMaxime Coquelin if (data->listenfd >= 0) { 89194973531SMaxime Coquelin close(data->listenfd); 89294973531SMaxime Coquelin data->listenfd = -1; 89394973531SMaxime Coquelin } 89494973531SMaxime Coquelin 89594973531SMaxime Coquelin free(data); 89694973531SMaxime Coquelin dev->backend_data = NULL; 89794973531SMaxime Coquelin 898748e5ea5SMaxime Coquelin return 0; 8996a84c37eSJianfeng Tan } 9000b6df936SJianfeng Tan 90133d24d65SJianfeng Tan static int 90233d24d65SJianfeng Tan vhost_user_enable_queue_pair(struct virtio_user_dev *dev, 90333d24d65SJianfeng Tan uint16_t pair_idx, 90433d24d65SJianfeng Tan int enable) 9050b6df936SJianfeng Tan { 90694973531SMaxime Coquelin struct vhost_user_data *data = dev->backend_data; 9070b6df936SJianfeng Tan int i; 9080b6df936SJianfeng Tan 90994973531SMaxime Coquelin if (data->vhostfd < 0) 91094973531SMaxime Coquelin return 0; 91194973531SMaxime Coquelin 91247ac9661STiwei Bie if (dev->qp_enabled[pair_idx] == enable) 91347ac9661STiwei Bie return 0; 91447ac9661STiwei Bie 9150b6df936SJianfeng Tan for (i = 0; i < 2; ++i) { 9160b6df936SJianfeng Tan struct vhost_vring_state state = { 9170b6df936SJianfeng Tan .index = pair_idx * 2 + i, 9180b6df936SJianfeng Tan .num = enable, 9190b6df936SJianfeng Tan }; 9200b6df936SJianfeng Tan 921ab9098d2SMaxime Coquelin if (vhost_user_set_vring_enable(dev, &state)) 9220b6df936SJianfeng Tan return -1; 9230b6df936SJianfeng Tan } 9240b6df936SJianfeng Tan 92547ac9661STiwei Bie dev->qp_enabled[pair_idx] = enable; 9260b6df936SJianfeng Tan return 0; 9270b6df936SJianfeng Tan } 92833d24d65SJianfeng Tan 9295b75b63cSMaxime Coquelin static int 9305b75b63cSMaxime Coquelin vhost_user_get_backend_features(uint64_t *features) 9315b75b63cSMaxime Coquelin { 9325b75b63cSMaxime Coquelin *features = 1ULL << VHOST_USER_F_PROTOCOL_FEATURES; 9335b75b63cSMaxime Coquelin 9345b75b63cSMaxime Coquelin return 0; 9355b75b63cSMaxime Coquelin } 9365b75b63cSMaxime Coquelin 93794973531SMaxime Coquelin static int 93894973531SMaxime Coquelin vhost_user_update_link_state(struct virtio_user_dev *dev) 93994973531SMaxime Coquelin { 94094973531SMaxime Coquelin struct vhost_user_data *data = dev->backend_data; 94194973531SMaxime Coquelin char buf[128]; 94294973531SMaxime Coquelin 94394973531SMaxime Coquelin if (data->vhostfd >= 0) { 94494973531SMaxime Coquelin int r; 94594973531SMaxime Coquelin 94641f9a175SYuan Wang r = recv(data->vhostfd, buf, 128, MSG_PEEK | MSG_DONTWAIT); 94794973531SMaxime Coquelin if (r == 0 || (r < 0 && errno != EAGAIN)) { 94894973531SMaxime Coquelin dev->net_status &= (~VIRTIO_NET_S_LINK_UP); 9496564ddcdSDavid Marchand PMD_DRV_LOG(ERR, "virtio-user port %u is down", dev->hw.port_id); 95094973531SMaxime Coquelin 95194973531SMaxime Coquelin /* This function could be called in the process 95294973531SMaxime Coquelin * of interrupt handling, callback cannot be 95394973531SMaxime Coquelin * unregistered here, set an alarm to do it. 95494973531SMaxime Coquelin */ 95523abee9dSIlya Maximets rte_eal_alarm_set(1, 95623abee9dSIlya Maximets virtio_user_dev_delayed_disconnect_handler, 95723abee9dSIlya Maximets (void *)dev); 95894973531SMaxime Coquelin } else { 95994973531SMaxime Coquelin dev->net_status |= VIRTIO_NET_S_LINK_UP; 96094973531SMaxime Coquelin } 96194973531SMaxime Coquelin } else if (dev->is_server) { 96294973531SMaxime Coquelin dev->net_status &= (~VIRTIO_NET_S_LINK_UP); 96394973531SMaxime Coquelin if (virtio_user_dev_server_reconnect(dev) >= 0) 96494973531SMaxime Coquelin dev->net_status |= VIRTIO_NET_S_LINK_UP; 96594973531SMaxime Coquelin } 96694973531SMaxime Coquelin 96794973531SMaxime Coquelin return 0; 96894973531SMaxime Coquelin } 96994973531SMaxime Coquelin 97094973531SMaxime Coquelin static int 97194973531SMaxime Coquelin vhost_user_get_intr_fd(struct virtio_user_dev *dev) 97294973531SMaxime Coquelin { 97394973531SMaxime Coquelin struct vhost_user_data *data = dev->backend_data; 97494973531SMaxime Coquelin 97594973531SMaxime Coquelin if (dev->is_server && data->vhostfd == -1) 97694973531SMaxime Coquelin return data->listenfd; 97794973531SMaxime Coquelin 97894973531SMaxime Coquelin return data->vhostfd; 97994973531SMaxime Coquelin } 98094973531SMaxime Coquelin 981520dd992SFerruh Yigit struct virtio_user_backend_ops virtio_ops_user = { 98233d24d65SJianfeng Tan .setup = vhost_user_setup, 983748e5ea5SMaxime Coquelin .destroy = vhost_user_destroy, 9845b75b63cSMaxime Coquelin .get_backend_features = vhost_user_get_backend_features, 98506856cabSMaxime Coquelin .set_owner = vhost_user_set_owner, 986cc0151b3SMaxime Coquelin .get_features = vhost_user_get_features, 987cc0151b3SMaxime Coquelin .set_features = vhost_user_set_features, 988539d910cSMaxime Coquelin .set_memory_table = vhost_user_set_memory_table, 989ab9098d2SMaxime Coquelin .set_vring_num = vhost_user_set_vring_num, 990ab9098d2SMaxime Coquelin .set_vring_base = vhost_user_set_vring_base, 991ab9098d2SMaxime Coquelin .get_vring_base = vhost_user_get_vring_base, 992ce399c36SMaxime Coquelin .set_vring_call = vhost_user_set_vring_call, 993ce399c36SMaxime Coquelin .set_vring_kick = vhost_user_set_vring_kick, 994dc65db73SMaxime Coquelin .set_vring_addr = vhost_user_set_vring_addr, 9958723c894SMaxime Coquelin .get_status = vhost_user_get_status, 9968723c894SMaxime Coquelin .set_status = vhost_user_set_status, 99794973531SMaxime Coquelin .enable_qp = vhost_user_enable_queue_pair, 99894973531SMaxime Coquelin .update_link_state = vhost_user_update_link_state, 99994973531SMaxime Coquelin .server_disconnect = vhost_user_server_disconnect, 100094973531SMaxime Coquelin .server_reconnect = vhost_user_server_reconnect, 100194973531SMaxime Coquelin .get_intr_fd = vhost_user_get_intr_fd, 100233d24d65SJianfeng Tan }; 1003