xref: /dpdk/drivers/net/virtio/virtio_user/vhost_user.c (revision e77506397fc8005c5129e22e9e2d15d5876790fd)
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