xref: /spdk/lib/virtio/virtio.c (revision a26b8bd349f99be5300350c5b7655159de66d869)
1488570ebSJim Harris /*   SPDX-License-Identifier: BSD-3-Clause
2a6dbe372Spaul luse  *   Copyright (C) 2010-2016 Intel Corporation. All rights reserved.
39dde5e5cSDariusz Stojaczyk  *   All rights reserved.
49dde5e5cSDariusz Stojaczyk  */
59dde5e5cSDariusz Stojaczyk 
69dde5e5cSDariusz Stojaczyk #include "spdk/stdinc.h"
79dde5e5cSDariusz Stojaczyk 
89dde5e5cSDariusz Stojaczyk #include "spdk/env.h"
9060d9b85SChangpeng Liu #include "spdk/util.h"
109dde5e5cSDariusz Stojaczyk #include "spdk/barrier.h"
119dde5e5cSDariusz Stojaczyk 
129dde5e5cSDariusz Stojaczyk #include "spdk_internal/virtio.h"
139dde5e5cSDariusz Stojaczyk 
149dde5e5cSDariusz Stojaczyk /* We use SMP memory barrier variants as all virtio_pci devices
159dde5e5cSDariusz Stojaczyk  * are purely virtual. All MMIO is executed on a CPU core, so
169dde5e5cSDariusz Stojaczyk  * there's no need to do full MMIO synchronization.
179dde5e5cSDariusz Stojaczyk  */
189dde5e5cSDariusz Stojaczyk #define virtio_mb()	spdk_smp_mb()
199dde5e5cSDariusz Stojaczyk #define virtio_rmb()	spdk_smp_rmb()
209dde5e5cSDariusz Stojaczyk #define virtio_wmb()	spdk_smp_wmb()
219dde5e5cSDariusz Stojaczyk 
229dde5e5cSDariusz Stojaczyk /* Chain all the descriptors in the ring with an END */
239dde5e5cSDariusz Stojaczyk static inline void
vring_desc_init(struct vring_desc * dp,uint16_t n)249dde5e5cSDariusz Stojaczyk vring_desc_init(struct vring_desc *dp, uint16_t n)
259dde5e5cSDariusz Stojaczyk {
269dde5e5cSDariusz Stojaczyk 	uint16_t i;
279dde5e5cSDariusz Stojaczyk 
289dde5e5cSDariusz Stojaczyk 	for (i = 0; i < n - 1; i++) {
299dde5e5cSDariusz Stojaczyk 		dp[i].next = (uint16_t)(i + 1);
309dde5e5cSDariusz Stojaczyk 	}
319dde5e5cSDariusz Stojaczyk 	dp[i].next = VQ_RING_DESC_CHAIN_END;
329dde5e5cSDariusz Stojaczyk }
339dde5e5cSDariusz Stojaczyk 
349dde5e5cSDariusz Stojaczyk static void
virtio_init_vring(struct virtqueue * vq)359dde5e5cSDariusz Stojaczyk virtio_init_vring(struct virtqueue *vq)
369dde5e5cSDariusz Stojaczyk {
379dde5e5cSDariusz Stojaczyk 	int size = vq->vq_nentries;
389dde5e5cSDariusz Stojaczyk 	struct vring *vr = &vq->vq_ring;
399dde5e5cSDariusz Stojaczyk 	uint8_t *ring_mem = vq->vq_ring_virt_mem;
409dde5e5cSDariusz Stojaczyk 
419dde5e5cSDariusz Stojaczyk 	/*
429dde5e5cSDariusz Stojaczyk 	 * Reinitialise since virtio port might have been stopped and restarted
439dde5e5cSDariusz Stojaczyk 	 */
449dde5e5cSDariusz Stojaczyk 	memset(ring_mem, 0, vq->vq_ring_size);
459dde5e5cSDariusz Stojaczyk 	vring_init(vr, size, ring_mem, VIRTIO_PCI_VRING_ALIGN);
469dde5e5cSDariusz Stojaczyk 	vq->vq_used_cons_idx = 0;
479dde5e5cSDariusz Stojaczyk 	vq->vq_desc_head_idx = 0;
489dde5e5cSDariusz Stojaczyk 	vq->vq_avail_idx = 0;
499dde5e5cSDariusz Stojaczyk 	vq->vq_desc_tail_idx = (uint16_t)(vq->vq_nentries - 1);
509dde5e5cSDariusz Stojaczyk 	vq->vq_free_cnt = vq->vq_nentries;
519dde5e5cSDariusz Stojaczyk 	vq->req_start = VQ_RING_DESC_CHAIN_END;
529dde5e5cSDariusz Stojaczyk 	vq->req_end = VQ_RING_DESC_CHAIN_END;
5303b12a98SDariusz Stojaczyk 	vq->reqs_finished = 0;
549dde5e5cSDariusz Stojaczyk 	memset(vq->vq_descx, 0, sizeof(struct vq_desc_extra) * vq->vq_nentries);
559dde5e5cSDariusz Stojaczyk 
569dde5e5cSDariusz Stojaczyk 	vring_desc_init(vr->desc, size);
579dde5e5cSDariusz Stojaczyk 
5803b12a98SDariusz Stojaczyk 	/* Tell the backend not to interrupt us.
5903b12a98SDariusz Stojaczyk 	 * If F_EVENT_IDX is negotiated, we will always set incredibly high
6003b12a98SDariusz Stojaczyk 	 * used event idx, so that we will practically never receive an
6103b12a98SDariusz Stojaczyk 	 * interrupt. See virtqueue_req_flush()
6203b12a98SDariusz Stojaczyk 	 */
6303b12a98SDariusz Stojaczyk 	if (vq->vdev->negotiated_features & (1ULL << VIRTIO_RING_F_EVENT_IDX)) {
6403b12a98SDariusz Stojaczyk 		vring_used_event(&vq->vq_ring) = UINT16_MAX;
6503b12a98SDariusz Stojaczyk 	} else {
6659266471SDariusz Stojaczyk 		vq->vq_ring.avail->flags |= VRING_AVAIL_F_NO_INTERRUPT;
679dde5e5cSDariusz Stojaczyk 	}
6803b12a98SDariusz Stojaczyk }
699dde5e5cSDariusz Stojaczyk 
709dde5e5cSDariusz Stojaczyk static int
virtio_init_queue(struct virtio_dev * dev,uint16_t vtpci_queue_idx)719dde5e5cSDariusz Stojaczyk virtio_init_queue(struct virtio_dev *dev, uint16_t vtpci_queue_idx)
729dde5e5cSDariusz Stojaczyk {
739dde5e5cSDariusz Stojaczyk 	unsigned int vq_size, size;
749dde5e5cSDariusz Stojaczyk 	struct virtqueue *vq;
75eeb1650cSDariusz Stojaczyk 	int rc;
769dde5e5cSDariusz Stojaczyk 
772172c432STomasz Zawadzki 	SPDK_DEBUGLOG(virtio_dev, "setting up queue: %"PRIu16"\n", vtpci_queue_idx);
789dde5e5cSDariusz Stojaczyk 
799dde5e5cSDariusz Stojaczyk 	/*
809dde5e5cSDariusz Stojaczyk 	 * Read the virtqueue size from the Queue Size field
819dde5e5cSDariusz Stojaczyk 	 * Always power of 2 and if 0 virtqueue does not exist
829dde5e5cSDariusz Stojaczyk 	 */
830a1a4ce1SDariusz Stojaczyk 	vq_size = virtio_dev_backend_ops(dev)->get_queue_size(dev, vtpci_queue_idx);
842172c432STomasz Zawadzki 	SPDK_DEBUGLOG(virtio_dev, "vq_size: %u\n", vq_size);
859dde5e5cSDariusz Stojaczyk 	if (vq_size == 0) {
869dde5e5cSDariusz Stojaczyk 		SPDK_ERRLOG("virtqueue %"PRIu16" does not exist\n", vtpci_queue_idx);
879dde5e5cSDariusz Stojaczyk 		return -EINVAL;
889dde5e5cSDariusz Stojaczyk 	}
899dde5e5cSDariusz Stojaczyk 
90060d9b85SChangpeng Liu 	if (!spdk_u32_is_pow2(vq_size)) {
919dde5e5cSDariusz Stojaczyk 		SPDK_ERRLOG("virtqueue %"PRIu16" size (%u) is not powerof 2\n",
929dde5e5cSDariusz Stojaczyk 			    vtpci_queue_idx, vq_size);
939dde5e5cSDariusz Stojaczyk 		return -EINVAL;
949dde5e5cSDariusz Stojaczyk 	}
959dde5e5cSDariusz Stojaczyk 
96794b2533SDarek Stojaczyk 	size = sizeof(*vq) + vq_size * sizeof(struct vq_desc_extra);
979dde5e5cSDariusz Stojaczyk 
98060d9b85SChangpeng Liu 	if (posix_memalign((void **)&vq, SPDK_CACHE_LINE_SIZE, size)) {
999dde5e5cSDariusz Stojaczyk 		SPDK_ERRLOG("can not allocate vq\n");
1009dde5e5cSDariusz Stojaczyk 		return -ENOMEM;
1019dde5e5cSDariusz Stojaczyk 	}
10212f622b7SDarek Stojaczyk 	memset(vq, 0, size);
1039dde5e5cSDariusz Stojaczyk 	dev->vqs[vtpci_queue_idx] = vq;
1049dde5e5cSDariusz Stojaczyk 
1059dde5e5cSDariusz Stojaczyk 	vq->vdev = dev;
1069dde5e5cSDariusz Stojaczyk 	vq->vq_queue_index = vtpci_queue_idx;
1079dde5e5cSDariusz Stojaczyk 	vq->vq_nentries = vq_size;
1089dde5e5cSDariusz Stojaczyk 
1099dde5e5cSDariusz Stojaczyk 	/*
1109dde5e5cSDariusz Stojaczyk 	 * Reserve a memzone for vring elements
1119dde5e5cSDariusz Stojaczyk 	 */
1129dde5e5cSDariusz Stojaczyk 	size = vring_size(vq_size, VIRTIO_PCI_VRING_ALIGN);
113060d9b85SChangpeng Liu 	vq->vq_ring_size = SPDK_ALIGN_CEIL(size, VIRTIO_PCI_VRING_ALIGN);
1142172c432STomasz Zawadzki 	SPDK_DEBUGLOG(virtio_dev, "vring_size: %u, rounded_vring_size: %u\n",
1159dde5e5cSDariusz Stojaczyk 		      size, vq->vq_ring_size);
1169dde5e5cSDariusz Stojaczyk 
1179dde5e5cSDariusz Stojaczyk 	vq->owner_thread = NULL;
1189dde5e5cSDariusz Stojaczyk 
119eeb1650cSDariusz Stojaczyk 	rc = virtio_dev_backend_ops(dev)->setup_queue(dev, vq);
120eeb1650cSDariusz Stojaczyk 	if (rc < 0) {
1219dde5e5cSDariusz Stojaczyk 		SPDK_ERRLOG("setup_queue failed\n");
12201103b2eSDarek Stojaczyk 		free(vq);
12365165d79SDariusz Stojaczyk 		dev->vqs[vtpci_queue_idx] = NULL;
124eeb1650cSDariusz Stojaczyk 		return rc;
1259dde5e5cSDariusz Stojaczyk 	}
1269dde5e5cSDariusz Stojaczyk 
1272172c432STomasz Zawadzki 	SPDK_DEBUGLOG(virtio_dev, "vq->vq_ring_mem:      0x%" PRIx64 "\n",
1283a15b49bSDariusz Stojaczyk 		      vq->vq_ring_mem);
1292172c432STomasz Zawadzki 	SPDK_DEBUGLOG(virtio_dev, "vq->vq_ring_virt_mem: 0x%" PRIx64 "\n",
1303a15b49bSDariusz Stojaczyk 		      (uint64_t)(uintptr_t)vq->vq_ring_virt_mem);
1313a15b49bSDariusz Stojaczyk 
1323a15b49bSDariusz Stojaczyk 	virtio_init_vring(vq);
1339dde5e5cSDariusz Stojaczyk 	return 0;
1349dde5e5cSDariusz Stojaczyk }
1359dde5e5cSDariusz Stojaczyk 
1369dde5e5cSDariusz Stojaczyk static void
virtio_free_queues(struct virtio_dev * dev)1379dde5e5cSDariusz Stojaczyk virtio_free_queues(struct virtio_dev *dev)
1389dde5e5cSDariusz Stojaczyk {
1399dde5e5cSDariusz Stojaczyk 	uint16_t nr_vq = dev->max_queues;
1409dde5e5cSDariusz Stojaczyk 	struct virtqueue *vq;
1419dde5e5cSDariusz Stojaczyk 	uint16_t i;
1429dde5e5cSDariusz Stojaczyk 
1439dde5e5cSDariusz Stojaczyk 	if (dev->vqs == NULL) {
1449dde5e5cSDariusz Stojaczyk 		return;
1459dde5e5cSDariusz Stojaczyk 	}
1469dde5e5cSDariusz Stojaczyk 
1479dde5e5cSDariusz Stojaczyk 	for (i = 0; i < nr_vq; i++) {
1489dde5e5cSDariusz Stojaczyk 		vq = dev->vqs[i];
1499dde5e5cSDariusz Stojaczyk 		if (!vq) {
1509dde5e5cSDariusz Stojaczyk 			continue;
1519dde5e5cSDariusz Stojaczyk 		}
1529dde5e5cSDariusz Stojaczyk 
153e724452eSDariusz Stojaczyk 		virtio_dev_backend_ops(dev)->del_queue(dev, vq);
1549dde5e5cSDariusz Stojaczyk 
15501103b2eSDarek Stojaczyk 		free(vq);
1569dde5e5cSDariusz Stojaczyk 		dev->vqs[i] = NULL;
1579dde5e5cSDariusz Stojaczyk 	}
1589dde5e5cSDariusz Stojaczyk 
15901103b2eSDarek Stojaczyk 	free(dev->vqs);
1609dde5e5cSDariusz Stojaczyk 	dev->vqs = NULL;
1619dde5e5cSDariusz Stojaczyk }
1629dde5e5cSDariusz Stojaczyk 
1639dde5e5cSDariusz Stojaczyk static int
virtio_alloc_queues(struct virtio_dev * dev,uint16_t max_queues,uint16_t fixed_vq_num)164a02483e6SChangpeng Liu virtio_alloc_queues(struct virtio_dev *dev, uint16_t max_queues, uint16_t fixed_vq_num)
1659dde5e5cSDariusz Stojaczyk {
1669dde5e5cSDariusz Stojaczyk 	uint16_t i;
1679dde5e5cSDariusz Stojaczyk 	int ret;
1689dde5e5cSDariusz Stojaczyk 
169a02483e6SChangpeng Liu 	if (max_queues == 0) {
1707755bed3SDariusz Stojaczyk 		/* perfectly fine to have a device with no virtqueues. */
1719dde5e5cSDariusz Stojaczyk 		return 0;
1729dde5e5cSDariusz Stojaczyk 	}
1739dde5e5cSDariusz Stojaczyk 
1747755bed3SDariusz Stojaczyk 	assert(dev->vqs == NULL);
175a02483e6SChangpeng Liu 	dev->vqs = calloc(1, sizeof(struct virtqueue *) * max_queues);
1769dde5e5cSDariusz Stojaczyk 	if (!dev->vqs) {
177a02483e6SChangpeng Liu 		SPDK_ERRLOG("failed to allocate %"PRIu16" vqs\n", max_queues);
1789dde5e5cSDariusz Stojaczyk 		return -ENOMEM;
1799dde5e5cSDariusz Stojaczyk 	}
1809dde5e5cSDariusz Stojaczyk 
181a02483e6SChangpeng Liu 	for (i = 0; i < max_queues; i++) {
1829dde5e5cSDariusz Stojaczyk 		ret = virtio_init_queue(dev, i);
1839dde5e5cSDariusz Stojaczyk 		if (ret < 0) {
1849dde5e5cSDariusz Stojaczyk 			virtio_free_queues(dev);
1859dde5e5cSDariusz Stojaczyk 			return ret;
1869dde5e5cSDariusz Stojaczyk 		}
1879dde5e5cSDariusz Stojaczyk 	}
1889dde5e5cSDariusz Stojaczyk 
189a02483e6SChangpeng Liu 	dev->max_queues = max_queues;
1907755bed3SDariusz Stojaczyk 	dev->fixed_queues_num = fixed_vq_num;
1919dde5e5cSDariusz Stojaczyk 	return 0;
1929dde5e5cSDariusz Stojaczyk }
1939dde5e5cSDariusz Stojaczyk 
1949dde5e5cSDariusz Stojaczyk /**
1959dde5e5cSDariusz Stojaczyk  * Negotiate virtio features. For virtio_user this will also set
1969dde5e5cSDariusz Stojaczyk  * dev->modern flag if VIRTIO_F_VERSION_1 flag is negotiated.
1979dde5e5cSDariusz Stojaczyk  */
1989dde5e5cSDariusz Stojaczyk static int
virtio_negotiate_features(struct virtio_dev * dev,uint64_t req_features)1999dde5e5cSDariusz Stojaczyk virtio_negotiate_features(struct virtio_dev *dev, uint64_t req_features)
2009dde5e5cSDariusz Stojaczyk {
2019dde5e5cSDariusz Stojaczyk 	uint64_t host_features = virtio_dev_backend_ops(dev)->get_features(dev);
2029dde5e5cSDariusz Stojaczyk 	int rc;
2039dde5e5cSDariusz Stojaczyk 
2042172c432STomasz Zawadzki 	SPDK_DEBUGLOG(virtio_dev, "guest features = %" PRIx64 "\n", req_features);
2052172c432STomasz Zawadzki 	SPDK_DEBUGLOG(virtio_dev, "device features = %" PRIx64 "\n", host_features);
2069dde5e5cSDariusz Stojaczyk 
2079dde5e5cSDariusz Stojaczyk 	rc = virtio_dev_backend_ops(dev)->set_features(dev, req_features & host_features);
2089dde5e5cSDariusz Stojaczyk 	if (rc != 0) {
2099dde5e5cSDariusz Stojaczyk 		SPDK_ERRLOG("failed to negotiate device features.\n");
210eeb1650cSDariusz Stojaczyk 		return rc;
2119dde5e5cSDariusz Stojaczyk 	}
2129dde5e5cSDariusz Stojaczyk 
2132172c432STomasz Zawadzki 	SPDK_DEBUGLOG(virtio_dev, "negotiated features = %" PRIx64 "\n",
2149dde5e5cSDariusz Stojaczyk 		      dev->negotiated_features);
2159dde5e5cSDariusz Stojaczyk 
2169dde5e5cSDariusz Stojaczyk 	virtio_dev_set_status(dev, VIRTIO_CONFIG_S_FEATURES_OK);
2179dde5e5cSDariusz Stojaczyk 	if (!(virtio_dev_get_status(dev) & VIRTIO_CONFIG_S_FEATURES_OK)) {
2189dde5e5cSDariusz Stojaczyk 		SPDK_ERRLOG("failed to set FEATURES_OK status!\n");
219c9e5777eSDariusz Stojaczyk 		/* either the device failed, or we offered some features that
220c9e5777eSDariusz Stojaczyk 		 * depend on other, not offered features.
221c9e5777eSDariusz Stojaczyk 		 */
222eeb1650cSDariusz Stojaczyk 		return -EINVAL;
2239dde5e5cSDariusz Stojaczyk 	}
2249dde5e5cSDariusz Stojaczyk 
2259dde5e5cSDariusz Stojaczyk 	return 0;
2269dde5e5cSDariusz Stojaczyk }
2279dde5e5cSDariusz Stojaczyk 
2289dde5e5cSDariusz Stojaczyk int
virtio_dev_construct(struct virtio_dev * vdev,const char * name,const struct virtio_dev_ops * ops,void * ctx)229fb12bbecSDariusz Stojaczyk virtio_dev_construct(struct virtio_dev *vdev, const char *name,
230fb12bbecSDariusz Stojaczyk 		     const struct virtio_dev_ops *ops, void *ctx)
2319dde5e5cSDariusz Stojaczyk {
232fb12bbecSDariusz Stojaczyk 	int rc;
233fb12bbecSDariusz Stojaczyk 
234fb12bbecSDariusz Stojaczyk 	vdev->name = strdup(name);
235fb12bbecSDariusz Stojaczyk 	if (vdev->name == NULL) {
236fb12bbecSDariusz Stojaczyk 		return -ENOMEM;
237fb12bbecSDariusz Stojaczyk 	}
238fb12bbecSDariusz Stojaczyk 
239fb12bbecSDariusz Stojaczyk 	rc = pthread_mutex_init(&vdev->mutex, NULL);
240fb12bbecSDariusz Stojaczyk 	if (rc != 0) {
241fb12bbecSDariusz Stojaczyk 		free(vdev->name);
242fb12bbecSDariusz Stojaczyk 		return -rc;
243fb12bbecSDariusz Stojaczyk 	}
244fb12bbecSDariusz Stojaczyk 
2459dde5e5cSDariusz Stojaczyk 	vdev->backend_ops = ops;
2469dde5e5cSDariusz Stojaczyk 	vdev->ctx = ctx;
2479dde5e5cSDariusz Stojaczyk 
2489dde5e5cSDariusz Stojaczyk 	return 0;
2499dde5e5cSDariusz Stojaczyk }
2509dde5e5cSDariusz Stojaczyk 
2519dde5e5cSDariusz Stojaczyk int
virtio_dev_reset(struct virtio_dev * dev,uint64_t req_features)2527755bed3SDariusz Stojaczyk virtio_dev_reset(struct virtio_dev *dev, uint64_t req_features)
2539dde5e5cSDariusz Stojaczyk {
2549dde5e5cSDariusz Stojaczyk 	req_features |= (1ULL << VIRTIO_F_VERSION_1);
2559dde5e5cSDariusz Stojaczyk 
2569dde5e5cSDariusz Stojaczyk 	virtio_dev_stop(dev);
2579dde5e5cSDariusz Stojaczyk 
2589dde5e5cSDariusz Stojaczyk 	virtio_dev_set_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE);
2599728d6b0SDariusz Stojaczyk 	if (!(virtio_dev_get_status(dev) & VIRTIO_CONFIG_S_ACKNOWLEDGE)) {
2609728d6b0SDariusz Stojaczyk 		SPDK_ERRLOG("Failed to set VIRTIO_CONFIG_S_ACKNOWLEDGE status.\n");
261eeb1650cSDariusz Stojaczyk 		return -EIO;
2629728d6b0SDariusz Stojaczyk 	}
2639dde5e5cSDariusz Stojaczyk 
2649dde5e5cSDariusz Stojaczyk 	virtio_dev_set_status(dev, VIRTIO_CONFIG_S_DRIVER);
2659728d6b0SDariusz Stojaczyk 	if (!(virtio_dev_get_status(dev) & VIRTIO_CONFIG_S_DRIVER)) {
2669728d6b0SDariusz Stojaczyk 		SPDK_ERRLOG("Failed to set VIRTIO_CONFIG_S_DRIVER status.\n");
267eeb1650cSDariusz Stojaczyk 		return -EIO;
2689728d6b0SDariusz Stojaczyk 	}
2697755bed3SDariusz Stojaczyk 
2707755bed3SDariusz Stojaczyk 	return virtio_negotiate_features(dev, req_features);
2719dde5e5cSDariusz Stojaczyk }
2729dde5e5cSDariusz Stojaczyk 
2737755bed3SDariusz Stojaczyk int
virtio_dev_start(struct virtio_dev * vdev,uint16_t max_queues,uint16_t fixed_queue_num)2747755bed3SDariusz Stojaczyk virtio_dev_start(struct virtio_dev *vdev, uint16_t max_queues, uint16_t fixed_queue_num)
2757755bed3SDariusz Stojaczyk {
2767755bed3SDariusz Stojaczyk 	int ret;
2777755bed3SDariusz Stojaczyk 
2787755bed3SDariusz Stojaczyk 	ret = virtio_alloc_queues(vdev, max_queues, fixed_queue_num);
2799dde5e5cSDariusz Stojaczyk 	if (ret < 0) {
2809dde5e5cSDariusz Stojaczyk 		return ret;
2819dde5e5cSDariusz Stojaczyk 	}
2829dde5e5cSDariusz Stojaczyk 
2837755bed3SDariusz Stojaczyk 	virtio_dev_set_status(vdev, VIRTIO_CONFIG_S_DRIVER_OK);
2849728d6b0SDariusz Stojaczyk 	if (!(virtio_dev_get_status(vdev) & VIRTIO_CONFIG_S_DRIVER_OK)) {
2859728d6b0SDariusz Stojaczyk 		SPDK_ERRLOG("Failed to set VIRTIO_CONFIG_S_DRIVER_OK status.\n");
2869728d6b0SDariusz Stojaczyk 		return -1;
2879728d6b0SDariusz Stojaczyk 	}
2889728d6b0SDariusz Stojaczyk 
2899dde5e5cSDariusz Stojaczyk 	return 0;
2909dde5e5cSDariusz Stojaczyk }
2919dde5e5cSDariusz Stojaczyk 
2929dde5e5cSDariusz Stojaczyk void
virtio_dev_destruct(struct virtio_dev * dev)2939dde5e5cSDariusz Stojaczyk virtio_dev_destruct(struct virtio_dev *dev)
2949dde5e5cSDariusz Stojaczyk {
2959dde5e5cSDariusz Stojaczyk 	virtio_dev_backend_ops(dev)->destruct_dev(dev);
2969dde5e5cSDariusz Stojaczyk 	pthread_mutex_destroy(&dev->mutex);
297fb12bbecSDariusz Stojaczyk 	free(dev->name);
2989dde5e5cSDariusz Stojaczyk }
2999dde5e5cSDariusz Stojaczyk 
3009dde5e5cSDariusz Stojaczyk static void
vq_ring_free_chain(struct virtqueue * vq,uint16_t desc_idx)3019dde5e5cSDariusz Stojaczyk vq_ring_free_chain(struct virtqueue *vq, uint16_t desc_idx)
3029dde5e5cSDariusz Stojaczyk {
3039dde5e5cSDariusz Stojaczyk 	struct vring_desc *dp, *dp_tail;
3049dde5e5cSDariusz Stojaczyk 	struct vq_desc_extra *dxp;
3059dde5e5cSDariusz Stojaczyk 	uint16_t desc_idx_last = desc_idx;
3069dde5e5cSDariusz Stojaczyk 
3079dde5e5cSDariusz Stojaczyk 	dp  = &vq->vq_ring.desc[desc_idx];
3089dde5e5cSDariusz Stojaczyk 	dxp = &vq->vq_descx[desc_idx];
3099dde5e5cSDariusz Stojaczyk 	vq->vq_free_cnt = (uint16_t)(vq->vq_free_cnt + dxp->ndescs);
3109dde5e5cSDariusz Stojaczyk 	if ((dp->flags & VRING_DESC_F_INDIRECT) == 0) {
3119dde5e5cSDariusz Stojaczyk 		while (dp->flags & VRING_DESC_F_NEXT) {
3129dde5e5cSDariusz Stojaczyk 			desc_idx_last = dp->next;
3139dde5e5cSDariusz Stojaczyk 			dp = &vq->vq_ring.desc[dp->next];
3149dde5e5cSDariusz Stojaczyk 		}
3159dde5e5cSDariusz Stojaczyk 	}
3169dde5e5cSDariusz Stojaczyk 	dxp->ndescs = 0;
3179dde5e5cSDariusz Stojaczyk 
3189dde5e5cSDariusz Stojaczyk 	/*
3199dde5e5cSDariusz Stojaczyk 	 * We must append the existing free chain, if any, to the end of
3209dde5e5cSDariusz Stojaczyk 	 * newly freed chain. If the virtqueue was completely used, then
3219dde5e5cSDariusz Stojaczyk 	 * head would be VQ_RING_DESC_CHAIN_END (ASSERTed above).
3229dde5e5cSDariusz Stojaczyk 	 */
3239dde5e5cSDariusz Stojaczyk 	if (vq->vq_desc_tail_idx == VQ_RING_DESC_CHAIN_END) {
3249dde5e5cSDariusz Stojaczyk 		vq->vq_desc_head_idx = desc_idx;
3259dde5e5cSDariusz Stojaczyk 	} else {
3269dde5e5cSDariusz Stojaczyk 		dp_tail = &vq->vq_ring.desc[vq->vq_desc_tail_idx];
3279dde5e5cSDariusz Stojaczyk 		dp_tail->next = desc_idx;
3289dde5e5cSDariusz Stojaczyk 	}
3299dde5e5cSDariusz Stojaczyk 
3309dde5e5cSDariusz Stojaczyk 	vq->vq_desc_tail_idx = desc_idx_last;
3319dde5e5cSDariusz Stojaczyk 	dp->next = VQ_RING_DESC_CHAIN_END;
3329dde5e5cSDariusz Stojaczyk }
3339dde5e5cSDariusz Stojaczyk 
3349dde5e5cSDariusz Stojaczyk static uint16_t
virtqueue_dequeue_burst_rx(struct virtqueue * vq,void ** rx_pkts,uint32_t * len,uint16_t num)3359dde5e5cSDariusz Stojaczyk virtqueue_dequeue_burst_rx(struct virtqueue *vq, void **rx_pkts,
3369dde5e5cSDariusz Stojaczyk 			   uint32_t *len, uint16_t num)
3379dde5e5cSDariusz Stojaczyk {
3389dde5e5cSDariusz Stojaczyk 	struct vring_used_elem *uep;
339016d9337SZiye Yang 	void *cookie;
3409dde5e5cSDariusz Stojaczyk 	uint16_t used_idx, desc_idx;
3419dde5e5cSDariusz Stojaczyk 	uint16_t i;
3429dde5e5cSDariusz Stojaczyk 
3439dde5e5cSDariusz Stojaczyk 	/*  Caller does the check */
3449dde5e5cSDariusz Stojaczyk 	for (i = 0; i < num ; i++) {
3459dde5e5cSDariusz Stojaczyk 		used_idx = (uint16_t)(vq->vq_used_cons_idx & (vq->vq_nentries - 1));
3469dde5e5cSDariusz Stojaczyk 		uep = &vq->vq_ring.used->ring[used_idx];
3479dde5e5cSDariusz Stojaczyk 		desc_idx = (uint16_t) uep->id;
3489dde5e5cSDariusz Stojaczyk 		len[i] = uep->len;
349016d9337SZiye Yang 		cookie = vq->vq_descx[desc_idx].cookie;
3509dde5e5cSDariusz Stojaczyk 
3519dde5e5cSDariusz Stojaczyk 		if (spdk_unlikely(cookie == NULL)) {
3529dde5e5cSDariusz Stojaczyk 			SPDK_WARNLOG("vring descriptor with no mbuf cookie at %"PRIu16"\n",
3539dde5e5cSDariusz Stojaczyk 				     vq->vq_used_cons_idx);
3549dde5e5cSDariusz Stojaczyk 			break;
3559dde5e5cSDariusz Stojaczyk 		}
3569dde5e5cSDariusz Stojaczyk 
357060d9b85SChangpeng Liu 		__builtin_prefetch(cookie);
358060d9b85SChangpeng Liu 
3599dde5e5cSDariusz Stojaczyk 		rx_pkts[i]  = cookie;
3609dde5e5cSDariusz Stojaczyk 		vq->vq_used_cons_idx++;
3619dde5e5cSDariusz Stojaczyk 		vq_ring_free_chain(vq, desc_idx);
3629dde5e5cSDariusz Stojaczyk 		vq->vq_descx[desc_idx].cookie = NULL;
3639dde5e5cSDariusz Stojaczyk 	}
3649dde5e5cSDariusz Stojaczyk 
3659dde5e5cSDariusz Stojaczyk 	return i;
3669dde5e5cSDariusz Stojaczyk }
3679dde5e5cSDariusz Stojaczyk 
368587002f5SDariusz Stojaczyk static void
finish_req(struct virtqueue * vq)369587002f5SDariusz Stojaczyk finish_req(struct virtqueue *vq)
370587002f5SDariusz Stojaczyk {
371587002f5SDariusz Stojaczyk 	struct vring_desc *desc;
372587002f5SDariusz Stojaczyk 	uint16_t avail_idx;
373587002f5SDariusz Stojaczyk 
374587002f5SDariusz Stojaczyk 	desc = &vq->vq_ring.desc[vq->req_end];
375587002f5SDariusz Stojaczyk 	desc->flags &= ~VRING_DESC_F_NEXT;
376587002f5SDariusz Stojaczyk 
377587002f5SDariusz Stojaczyk 	/*
378587002f5SDariusz Stojaczyk 	 * Place the head of the descriptor chain into the next slot and make
379587002f5SDariusz Stojaczyk 	 * it usable to the host. The chain is made available now rather than
380587002f5SDariusz Stojaczyk 	 * deferring to virtqueue_req_flush() in the hopes that if the host is
381587002f5SDariusz Stojaczyk 	 * currently running on another CPU, we can keep it processing the new
382587002f5SDariusz Stojaczyk 	 * descriptor.
383587002f5SDariusz Stojaczyk 	 */
384587002f5SDariusz Stojaczyk 	avail_idx = (uint16_t)(vq->vq_avail_idx & (vq->vq_nentries - 1));
385587002f5SDariusz Stojaczyk 	vq->vq_ring.avail->ring[avail_idx] = vq->req_start;
386587002f5SDariusz Stojaczyk 	vq->vq_avail_idx++;
387587002f5SDariusz Stojaczyk 	vq->req_end = VQ_RING_DESC_CHAIN_END;
388587002f5SDariusz Stojaczyk 	virtio_wmb();
389587002f5SDariusz Stojaczyk 	vq->vq_ring.avail->idx = vq->vq_avail_idx;
39003b12a98SDariusz Stojaczyk 	vq->reqs_finished++;
391587002f5SDariusz Stojaczyk }
392587002f5SDariusz Stojaczyk 
3939dde5e5cSDariusz Stojaczyk int
virtqueue_req_start(struct virtqueue * vq,void * cookie,int iovcnt)3949dde5e5cSDariusz Stojaczyk virtqueue_req_start(struct virtqueue *vq, void *cookie, int iovcnt)
3959dde5e5cSDariusz Stojaczyk {
3969dde5e5cSDariusz Stojaczyk 	struct vq_desc_extra *dxp;
3979dde5e5cSDariusz Stojaczyk 
398*a26b8bd3SVasilii Ivanov 	/* Reserve enough entries to handle iov split */
399*a26b8bd3SVasilii Ivanov 	if (2 * iovcnt > vq->vq_free_cnt) {
4008b94174aSDariusz Stojaczyk 		return iovcnt > vq->vq_nentries ? -EINVAL : -ENOMEM;
4019dde5e5cSDariusz Stojaczyk 	}
4029dde5e5cSDariusz Stojaczyk 
403587002f5SDariusz Stojaczyk 	if (vq->req_end != VQ_RING_DESC_CHAIN_END) {
404587002f5SDariusz Stojaczyk 		finish_req(vq);
4059dde5e5cSDariusz Stojaczyk 	}
4069dde5e5cSDariusz Stojaczyk 
4079dde5e5cSDariusz Stojaczyk 	vq->req_start = vq->vq_desc_head_idx;
4089dde5e5cSDariusz Stojaczyk 	dxp = &vq->vq_descx[vq->req_start];
4099dde5e5cSDariusz Stojaczyk 	dxp->cookie = cookie;
4109dde5e5cSDariusz Stojaczyk 	dxp->ndescs = 0;
4119dde5e5cSDariusz Stojaczyk 
4129dde5e5cSDariusz Stojaczyk 	return 0;
4139dde5e5cSDariusz Stojaczyk }
4149dde5e5cSDariusz Stojaczyk 
4159dde5e5cSDariusz Stojaczyk void
virtqueue_req_flush(struct virtqueue * vq)4169dde5e5cSDariusz Stojaczyk virtqueue_req_flush(struct virtqueue *vq)
4179dde5e5cSDariusz Stojaczyk {
41803b12a98SDariusz Stojaczyk 	uint16_t reqs_finished;
41903b12a98SDariusz Stojaczyk 
420587002f5SDariusz Stojaczyk 	if (vq->req_end == VQ_RING_DESC_CHAIN_END) {
421587002f5SDariusz Stojaczyk 		/* no non-empty requests have been started */
4229dde5e5cSDariusz Stojaczyk 		return;
4239dde5e5cSDariusz Stojaczyk 	}
4249dde5e5cSDariusz Stojaczyk 
425587002f5SDariusz Stojaczyk 	finish_req(vq);
426face9eb2SDariusz Stojaczyk 	virtio_mb();
427587002f5SDariusz Stojaczyk 
42803b12a98SDariusz Stojaczyk 	reqs_finished = vq->reqs_finished;
42903b12a98SDariusz Stojaczyk 	vq->reqs_finished = 0;
43003b12a98SDariusz Stojaczyk 
43103b12a98SDariusz Stojaczyk 	if (vq->vdev->negotiated_features & (1ULL << VIRTIO_RING_F_EVENT_IDX)) {
43203b12a98SDariusz Stojaczyk 		/* Set used event idx to a value the device will never reach.
43303b12a98SDariusz Stojaczyk 		 * This effectively disables interrupts.
43403b12a98SDariusz Stojaczyk 		 */
43503b12a98SDariusz Stojaczyk 		vring_used_event(&vq->vq_ring) = vq->vq_used_cons_idx - vq->vq_nentries - 1;
43603b12a98SDariusz Stojaczyk 
43703b12a98SDariusz Stojaczyk 		if (!vring_need_event(vring_avail_event(&vq->vq_ring),
43803b12a98SDariusz Stojaczyk 				      vq->vq_avail_idx,
43903b12a98SDariusz Stojaczyk 				      vq->vq_avail_idx - reqs_finished)) {
44003b12a98SDariusz Stojaczyk 			return;
44103b12a98SDariusz Stojaczyk 		}
44203b12a98SDariusz Stojaczyk 	} else if (vq->vq_ring.used->flags & VRING_USED_F_NO_NOTIFY) {
44303b12a98SDariusz Stojaczyk 		return;
44403b12a98SDariusz Stojaczyk 	}
44503b12a98SDariusz Stojaczyk 
4469dde5e5cSDariusz Stojaczyk 	virtio_dev_backend_ops(vq->vdev)->notify_queue(vq->vdev, vq);
4472172c432STomasz Zawadzki 	SPDK_DEBUGLOG(virtio_dev, "Notified backend after xmit\n");
4489dde5e5cSDariusz Stojaczyk }
4499dde5e5cSDariusz Stojaczyk 
4509dde5e5cSDariusz Stojaczyk void
virtqueue_req_abort(struct virtqueue * vq)4519dde5e5cSDariusz Stojaczyk virtqueue_req_abort(struct virtqueue *vq)
4529dde5e5cSDariusz Stojaczyk {
4539dde5e5cSDariusz Stojaczyk 	struct vring_desc *desc;
4549dde5e5cSDariusz Stojaczyk 
4559dde5e5cSDariusz Stojaczyk 	if (vq->req_start == VQ_RING_DESC_CHAIN_END) {
4569dde5e5cSDariusz Stojaczyk 		/* no requests have been started */
4579dde5e5cSDariusz Stojaczyk 		return;
4589dde5e5cSDariusz Stojaczyk 	}
4599dde5e5cSDariusz Stojaczyk 
4609dde5e5cSDariusz Stojaczyk 	desc = &vq->vq_ring.desc[vq->req_end];
4619dde5e5cSDariusz Stojaczyk 	desc->flags &= ~VRING_DESC_F_NEXT;
4629dde5e5cSDariusz Stojaczyk 
4639dde5e5cSDariusz Stojaczyk 	vq_ring_free_chain(vq, vq->req_start);
4649dde5e5cSDariusz Stojaczyk 	vq->req_start = VQ_RING_DESC_CHAIN_END;
4659dde5e5cSDariusz Stojaczyk }
4669dde5e5cSDariusz Stojaczyk 
4679dde5e5cSDariusz Stojaczyk void
virtqueue_req_add_iovs(struct virtqueue * vq,struct iovec * iovs,uint16_t iovcnt,enum spdk_virtio_desc_type desc_type)4689dde5e5cSDariusz Stojaczyk virtqueue_req_add_iovs(struct virtqueue *vq, struct iovec *iovs, uint16_t iovcnt,
4699dde5e5cSDariusz Stojaczyk 		       enum spdk_virtio_desc_type desc_type)
4709dde5e5cSDariusz Stojaczyk {
4719dde5e5cSDariusz Stojaczyk 	struct vring_desc *desc;
4729dde5e5cSDariusz Stojaczyk 	struct vq_desc_extra *dxp;
4739dde5e5cSDariusz Stojaczyk 	uint16_t i, prev_head, new_head;
474*a26b8bd3SVasilii Ivanov 	uint64_t processed_length, iovec_length, current_length;
475*a26b8bd3SVasilii Ivanov 	void *current_base;
476*a26b8bd3SVasilii Ivanov 	uint16_t used_desc_count = 0;
4779dde5e5cSDariusz Stojaczyk 
4789dde5e5cSDariusz Stojaczyk 	assert(vq->req_start != VQ_RING_DESC_CHAIN_END);
4799dde5e5cSDariusz Stojaczyk 	assert(iovcnt <= vq->vq_free_cnt);
4809dde5e5cSDariusz Stojaczyk 
4819dde5e5cSDariusz Stojaczyk 	/* TODO use indirect descriptors if iovcnt is high enough
4829dde5e5cSDariusz Stojaczyk 	 * or the caller specifies SPDK_VIRTIO_DESC_F_INDIRECT
4839dde5e5cSDariusz Stojaczyk 	 */
4849dde5e5cSDariusz Stojaczyk 
485a1ca7a12SDariusz Stojaczyk 	prev_head = vq->req_end;
486a1ca7a12SDariusz Stojaczyk 	new_head = vq->vq_desc_head_idx;
4879dde5e5cSDariusz Stojaczyk 	for (i = 0; i < iovcnt; ++i) {
488*a26b8bd3SVasilii Ivanov 		processed_length = 0;
489*a26b8bd3SVasilii Ivanov 		iovec_length = iovs[i].iov_len;
490*a26b8bd3SVasilii Ivanov 		current_base = iovs[i].iov_base;
491*a26b8bd3SVasilii Ivanov 
492*a26b8bd3SVasilii Ivanov 		while (processed_length < iovec_length) {
4939dde5e5cSDariusz Stojaczyk 			desc = &vq->vq_ring.desc[new_head];
494*a26b8bd3SVasilii Ivanov 			current_length = iovec_length - processed_length;
4959dde5e5cSDariusz Stojaczyk 
4969dde5e5cSDariusz Stojaczyk 			if (!vq->vdev->is_hw) {
497*a26b8bd3SVasilii Ivanov 				desc->addr  = (uintptr_t)current_base;
4989dde5e5cSDariusz Stojaczyk 			} else {
499*a26b8bd3SVasilii Ivanov 				desc->addr = spdk_vtophys(current_base, &current_length);
5009dde5e5cSDariusz Stojaczyk 			}
5019dde5e5cSDariusz Stojaczyk 
502*a26b8bd3SVasilii Ivanov 			desc->len = current_length;
5039dde5e5cSDariusz Stojaczyk 			/* always set NEXT flag. unset it on the last descriptor
5049dde5e5cSDariusz Stojaczyk 			 * in the request-ending function.
5059dde5e5cSDariusz Stojaczyk 			 */
5069dde5e5cSDariusz Stojaczyk 			desc->flags = desc_type | VRING_DESC_F_NEXT;
5079dde5e5cSDariusz Stojaczyk 
5089dde5e5cSDariusz Stojaczyk 			prev_head = new_head;
5099dde5e5cSDariusz Stojaczyk 			new_head = desc->next;
510*a26b8bd3SVasilii Ivanov 			used_desc_count++;
511*a26b8bd3SVasilii Ivanov 
512*a26b8bd3SVasilii Ivanov 			processed_length += current_length;
513*a26b8bd3SVasilii Ivanov 			current_base += current_length;
514*a26b8bd3SVasilii Ivanov 		}
5159dde5e5cSDariusz Stojaczyk 	}
5169dde5e5cSDariusz Stojaczyk 
5179dde5e5cSDariusz Stojaczyk 	dxp = &vq->vq_descx[vq->req_start];
518*a26b8bd3SVasilii Ivanov 	dxp->ndescs += used_desc_count;
5199dde5e5cSDariusz Stojaczyk 
5209dde5e5cSDariusz Stojaczyk 	vq->req_end = prev_head;
5219dde5e5cSDariusz Stojaczyk 	vq->vq_desc_head_idx = new_head;
522*a26b8bd3SVasilii Ivanov 	vq->vq_free_cnt = (uint16_t)(vq->vq_free_cnt - used_desc_count);
5239dde5e5cSDariusz Stojaczyk 	if (vq->vq_desc_head_idx == VQ_RING_DESC_CHAIN_END) {
5249dde5e5cSDariusz Stojaczyk 		assert(vq->vq_free_cnt == 0);
5259dde5e5cSDariusz Stojaczyk 		vq->vq_desc_tail_idx = VQ_RING_DESC_CHAIN_END;
5269dde5e5cSDariusz Stojaczyk 	}
5279dde5e5cSDariusz Stojaczyk }
5289dde5e5cSDariusz Stojaczyk 
529060d9b85SChangpeng Liu #define DESC_PER_CACHELINE (SPDK_CACHE_LINE_SIZE / sizeof(struct vring_desc))
5309dde5e5cSDariusz Stojaczyk uint16_t
virtio_recv_pkts(struct virtqueue * vq,void ** io,uint32_t * len,uint16_t nb_pkts)5319dde5e5cSDariusz Stojaczyk virtio_recv_pkts(struct virtqueue *vq, void **io, uint32_t *len, uint16_t nb_pkts)
5329dde5e5cSDariusz Stojaczyk {
5339dde5e5cSDariusz Stojaczyk 	uint16_t nb_used, num;
5349dde5e5cSDariusz Stojaczyk 
53559266471SDariusz Stojaczyk 	nb_used = vq->vq_ring.used->idx - vq->vq_used_cons_idx;
5369dde5e5cSDariusz Stojaczyk 	virtio_rmb();
5379dde5e5cSDariusz Stojaczyk 
5389dde5e5cSDariusz Stojaczyk 	num = (uint16_t)(spdk_likely(nb_used <= nb_pkts) ? nb_used : nb_pkts);
5399dde5e5cSDariusz Stojaczyk 	if (spdk_likely(num > DESC_PER_CACHELINE)) {
5409dde5e5cSDariusz Stojaczyk 		num = num - ((vq->vq_used_cons_idx + num) % DESC_PER_CACHELINE);
5419dde5e5cSDariusz Stojaczyk 	}
5429dde5e5cSDariusz Stojaczyk 
543ba43426cSDariusz Stojaczyk 	return virtqueue_dequeue_burst_rx(vq, io, len, num);
5449dde5e5cSDariusz Stojaczyk }
5459dde5e5cSDariusz Stojaczyk 
5469dde5e5cSDariusz Stojaczyk int
virtio_dev_acquire_queue(struct virtio_dev * vdev,uint16_t index)5479dde5e5cSDariusz Stojaczyk virtio_dev_acquire_queue(struct virtio_dev *vdev, uint16_t index)
5489dde5e5cSDariusz Stojaczyk {
5499dde5e5cSDariusz Stojaczyk 	struct virtqueue *vq = NULL;
5509dde5e5cSDariusz Stojaczyk 
5519dde5e5cSDariusz Stojaczyk 	if (index >= vdev->max_queues) {
5529dde5e5cSDariusz Stojaczyk 		SPDK_ERRLOG("requested vq index %"PRIu16" exceeds max queue count %"PRIu16".\n",
5539dde5e5cSDariusz Stojaczyk 			    index, vdev->max_queues);
5549dde5e5cSDariusz Stojaczyk 		return -1;
5559dde5e5cSDariusz Stojaczyk 	}
5569dde5e5cSDariusz Stojaczyk 
5579dde5e5cSDariusz Stojaczyk 	pthread_mutex_lock(&vdev->mutex);
5589dde5e5cSDariusz Stojaczyk 	vq = vdev->vqs[index];
5599dde5e5cSDariusz Stojaczyk 	if (vq == NULL || vq->owner_thread != NULL) {
5609dde5e5cSDariusz Stojaczyk 		pthread_mutex_unlock(&vdev->mutex);
5619dde5e5cSDariusz Stojaczyk 		return -1;
5629dde5e5cSDariusz Stojaczyk 	}
5639dde5e5cSDariusz Stojaczyk 
5649dde5e5cSDariusz Stojaczyk 	vq->owner_thread = spdk_get_thread();
5659dde5e5cSDariusz Stojaczyk 	pthread_mutex_unlock(&vdev->mutex);
5669dde5e5cSDariusz Stojaczyk 	return 0;
5679dde5e5cSDariusz Stojaczyk }
5689dde5e5cSDariusz Stojaczyk 
5699dde5e5cSDariusz Stojaczyk int32_t
virtio_dev_find_and_acquire_queue(struct virtio_dev * vdev,uint16_t start_index)5709dde5e5cSDariusz Stojaczyk virtio_dev_find_and_acquire_queue(struct virtio_dev *vdev, uint16_t start_index)
5719dde5e5cSDariusz Stojaczyk {
5729dde5e5cSDariusz Stojaczyk 	struct virtqueue *vq = NULL;
5739dde5e5cSDariusz Stojaczyk 	uint16_t i;
5749dde5e5cSDariusz Stojaczyk 
5759dde5e5cSDariusz Stojaczyk 	pthread_mutex_lock(&vdev->mutex);
5769dde5e5cSDariusz Stojaczyk 	for (i = start_index; i < vdev->max_queues; ++i) {
5779dde5e5cSDariusz Stojaczyk 		vq = vdev->vqs[i];
5789dde5e5cSDariusz Stojaczyk 		if (vq != NULL && vq->owner_thread == NULL) {
5799dde5e5cSDariusz Stojaczyk 			break;
5809dde5e5cSDariusz Stojaczyk 		}
5819dde5e5cSDariusz Stojaczyk 	}
5829dde5e5cSDariusz Stojaczyk 
5839dde5e5cSDariusz Stojaczyk 	if (vq == NULL || i == vdev->max_queues) {
5849dde5e5cSDariusz Stojaczyk 		SPDK_ERRLOG("no more unused virtio queues with idx >= %"PRIu16".\n", start_index);
5859dde5e5cSDariusz Stojaczyk 		pthread_mutex_unlock(&vdev->mutex);
5869dde5e5cSDariusz Stojaczyk 		return -1;
5879dde5e5cSDariusz Stojaczyk 	}
5889dde5e5cSDariusz Stojaczyk 
5899dde5e5cSDariusz Stojaczyk 	vq->owner_thread = spdk_get_thread();
5909dde5e5cSDariusz Stojaczyk 	pthread_mutex_unlock(&vdev->mutex);
5919dde5e5cSDariusz Stojaczyk 	return i;
5929dde5e5cSDariusz Stojaczyk }
5939dde5e5cSDariusz Stojaczyk 
5949dde5e5cSDariusz Stojaczyk struct spdk_thread *
virtio_dev_queue_get_thread(struct virtio_dev * vdev,uint16_t index)5959dde5e5cSDariusz Stojaczyk virtio_dev_queue_get_thread(struct virtio_dev *vdev, uint16_t index)
5969dde5e5cSDariusz Stojaczyk {
5979dde5e5cSDariusz Stojaczyk 	struct spdk_thread *thread = NULL;
5989dde5e5cSDariusz Stojaczyk 
5999dde5e5cSDariusz Stojaczyk 	if (index >= vdev->max_queues) {
6009dde5e5cSDariusz Stojaczyk 		SPDK_ERRLOG("given vq index %"PRIu16" exceeds max queue count %"PRIu16"\n",
6019dde5e5cSDariusz Stojaczyk 			    index, vdev->max_queues);
602a00b6819SBen Walker 		abort(); /* This is not recoverable */
6039dde5e5cSDariusz Stojaczyk 	}
6049dde5e5cSDariusz Stojaczyk 
6059dde5e5cSDariusz Stojaczyk 	pthread_mutex_lock(&vdev->mutex);
606a00b6819SBen Walker 	thread = vdev->vqs[index]->owner_thread;
6079dde5e5cSDariusz Stojaczyk 	pthread_mutex_unlock(&vdev->mutex);
6089dde5e5cSDariusz Stojaczyk 
6099dde5e5cSDariusz Stojaczyk 	return thread;
6109dde5e5cSDariusz Stojaczyk }
6119dde5e5cSDariusz Stojaczyk 
6129dde5e5cSDariusz Stojaczyk bool
virtio_dev_queue_is_acquired(struct virtio_dev * vdev,uint16_t index)6139dde5e5cSDariusz Stojaczyk virtio_dev_queue_is_acquired(struct virtio_dev *vdev, uint16_t index)
6149dde5e5cSDariusz Stojaczyk {
6159dde5e5cSDariusz Stojaczyk 	return virtio_dev_queue_get_thread(vdev, index) != NULL;
6169dde5e5cSDariusz Stojaczyk }
6179dde5e5cSDariusz Stojaczyk 
6189dde5e5cSDariusz Stojaczyk void
virtio_dev_release_queue(struct virtio_dev * vdev,uint16_t index)6199dde5e5cSDariusz Stojaczyk virtio_dev_release_queue(struct virtio_dev *vdev, uint16_t index)
6209dde5e5cSDariusz Stojaczyk {
6219dde5e5cSDariusz Stojaczyk 	struct virtqueue *vq = NULL;
6229dde5e5cSDariusz Stojaczyk 
6239dde5e5cSDariusz Stojaczyk 	if (index >= vdev->max_queues) {
6249dde5e5cSDariusz Stojaczyk 		SPDK_ERRLOG("given vq index %"PRIu16" exceeds max queue count %"PRIu16".\n",
6259dde5e5cSDariusz Stojaczyk 			    index, vdev->max_queues);
6269dde5e5cSDariusz Stojaczyk 		return;
6279dde5e5cSDariusz Stojaczyk 	}
6289dde5e5cSDariusz Stojaczyk 
6299dde5e5cSDariusz Stojaczyk 	pthread_mutex_lock(&vdev->mutex);
6309dde5e5cSDariusz Stojaczyk 	vq = vdev->vqs[index];
6319dde5e5cSDariusz Stojaczyk 	if (vq == NULL) {
6329dde5e5cSDariusz Stojaczyk 		SPDK_ERRLOG("virtqueue at index %"PRIu16" is not initialized.\n", index);
6339dde5e5cSDariusz Stojaczyk 		pthread_mutex_unlock(&vdev->mutex);
6349dde5e5cSDariusz Stojaczyk 		return;
6359dde5e5cSDariusz Stojaczyk 	}
6369dde5e5cSDariusz Stojaczyk 
6379dde5e5cSDariusz Stojaczyk 	assert(vq->owner_thread == spdk_get_thread());
6389dde5e5cSDariusz Stojaczyk 	vq->owner_thread = NULL;
6399dde5e5cSDariusz Stojaczyk 	pthread_mutex_unlock(&vdev->mutex);
6409dde5e5cSDariusz Stojaczyk }
6419dde5e5cSDariusz Stojaczyk 
64262844ae3SDariusz Stojaczyk int
virtio_dev_read_dev_config(struct virtio_dev * dev,size_t offset,void * dst,int length)6439dde5e5cSDariusz Stojaczyk virtio_dev_read_dev_config(struct virtio_dev *dev, size_t offset,
6449dde5e5cSDariusz Stojaczyk 			   void *dst, int length)
6459dde5e5cSDariusz Stojaczyk {
64662844ae3SDariusz Stojaczyk 	return virtio_dev_backend_ops(dev)->read_dev_cfg(dev, offset, dst, length);
6479dde5e5cSDariusz Stojaczyk }
6489dde5e5cSDariusz Stojaczyk 
64962844ae3SDariusz Stojaczyk int
virtio_dev_write_dev_config(struct virtio_dev * dev,size_t offset,const void * src,int length)6509dde5e5cSDariusz Stojaczyk virtio_dev_write_dev_config(struct virtio_dev *dev, size_t offset,
6519dde5e5cSDariusz Stojaczyk 			    const void *src, int length)
6529dde5e5cSDariusz Stojaczyk {
65362844ae3SDariusz Stojaczyk 	return virtio_dev_backend_ops(dev)->write_dev_cfg(dev, offset, src, length);
6549dde5e5cSDariusz Stojaczyk }
6559dde5e5cSDariusz Stojaczyk 
6569dde5e5cSDariusz Stojaczyk void
virtio_dev_stop(struct virtio_dev * dev)6579dde5e5cSDariusz Stojaczyk virtio_dev_stop(struct virtio_dev *dev)
6589dde5e5cSDariusz Stojaczyk {
6599dde5e5cSDariusz Stojaczyk 	virtio_dev_backend_ops(dev)->set_status(dev, VIRTIO_CONFIG_S_RESET);
6609dde5e5cSDariusz Stojaczyk 	/* flush status write */
6619dde5e5cSDariusz Stojaczyk 	virtio_dev_backend_ops(dev)->get_status(dev);
6627755bed3SDariusz Stojaczyk 	virtio_free_queues(dev);
6639dde5e5cSDariusz Stojaczyk }
6649dde5e5cSDariusz Stojaczyk 
6659dde5e5cSDariusz Stojaczyk void
virtio_dev_set_status(struct virtio_dev * dev,uint8_t status)6669dde5e5cSDariusz Stojaczyk virtio_dev_set_status(struct virtio_dev *dev, uint8_t status)
6679dde5e5cSDariusz Stojaczyk {
6689dde5e5cSDariusz Stojaczyk 	if (status != VIRTIO_CONFIG_S_RESET) {
6699dde5e5cSDariusz Stojaczyk 		status |= virtio_dev_backend_ops(dev)->get_status(dev);
6709dde5e5cSDariusz Stojaczyk 	}
6719dde5e5cSDariusz Stojaczyk 
6729dde5e5cSDariusz Stojaczyk 	virtio_dev_backend_ops(dev)->set_status(dev, status);
6739dde5e5cSDariusz Stojaczyk }
6749dde5e5cSDariusz Stojaczyk 
6759dde5e5cSDariusz Stojaczyk uint8_t
virtio_dev_get_status(struct virtio_dev * dev)6769dde5e5cSDariusz Stojaczyk virtio_dev_get_status(struct virtio_dev *dev)
6779dde5e5cSDariusz Stojaczyk {
6789dde5e5cSDariusz Stojaczyk 	return virtio_dev_backend_ops(dev)->get_status(dev);
6799dde5e5cSDariusz Stojaczyk }
6809dde5e5cSDariusz Stojaczyk 
6819dde5e5cSDariusz Stojaczyk const struct virtio_dev_ops *
virtio_dev_backend_ops(struct virtio_dev * dev)6829dde5e5cSDariusz Stojaczyk virtio_dev_backend_ops(struct virtio_dev *dev)
6839dde5e5cSDariusz Stojaczyk {
6849dde5e5cSDariusz Stojaczyk 	return dev->backend_ops;
6859dde5e5cSDariusz Stojaczyk }
6869dde5e5cSDariusz Stojaczyk 
6879dde5e5cSDariusz Stojaczyk void
virtio_dev_dump_json_info(struct virtio_dev * hw,struct spdk_json_write_ctx * w)6881a6dac40SPawel Wodkowski virtio_dev_dump_json_info(struct virtio_dev *hw, struct spdk_json_write_ctx *w)
6899dde5e5cSDariusz Stojaczyk {
690ab0f787eSShuhei Matsumoto 	spdk_json_write_named_object_begin(w, "virtio");
6919dde5e5cSDariusz Stojaczyk 
692ab0f787eSShuhei Matsumoto 	spdk_json_write_named_uint32(w, "vq_count", hw->max_queues);
6939dde5e5cSDariusz Stojaczyk 
694ab0f787eSShuhei Matsumoto 	spdk_json_write_named_uint32(w, "vq_size",
695ab0f787eSShuhei Matsumoto 				     virtio_dev_backend_ops(hw)->get_queue_size(hw, 0));
6969dde5e5cSDariusz Stojaczyk 
6971a6dac40SPawel Wodkowski 	virtio_dev_backend_ops(hw)->dump_json_info(hw, w);
6989dde5e5cSDariusz Stojaczyk 
6999dde5e5cSDariusz Stojaczyk 	spdk_json_write_object_end(w);
7009dde5e5cSDariusz Stojaczyk }
7019dde5e5cSDariusz Stojaczyk 
7022172c432STomasz Zawadzki SPDK_LOG_REGISTER_COMPONENT(virtio_dev)
703