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, ¤t_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