135e7012eSMaxime Coquelin /* SPDX-License-Identifier: BSD-3-Clause 235e7012eSMaxime Coquelin * Copyright(c) 2010-2016 Intel Corporation 335e7012eSMaxime Coquelin * Copyright(c) 2022 Red Hat Inc, 435e7012eSMaxime Coquelin */ 535e7012eSMaxime Coquelin 635e7012eSMaxime Coquelin #include <unistd.h> 735e7012eSMaxime Coquelin 835e7012eSMaxime Coquelin #include <rte_common.h> 935e7012eSMaxime Coquelin #include <rte_eal.h> 1035e7012eSMaxime Coquelin #include <rte_errno.h> 1135e7012eSMaxime Coquelin 1235e7012eSMaxime Coquelin #include "virtio_cvq.h" 1335e7012eSMaxime Coquelin #include "virtqueue.h" 1435e7012eSMaxime Coquelin 1535e7012eSMaxime Coquelin static struct virtio_pmd_ctrl * 1635e7012eSMaxime Coquelin virtio_send_command_packed(struct virtnet_ctl *cvq, 1735e7012eSMaxime Coquelin struct virtio_pmd_ctrl *ctrl, 1835e7012eSMaxime Coquelin int *dlen, int pkt_num) 1935e7012eSMaxime Coquelin { 2035e7012eSMaxime Coquelin struct virtqueue *vq = virtnet_cq_to_vq(cvq); 2135e7012eSMaxime Coquelin int head; 2235e7012eSMaxime Coquelin struct vring_packed_desc *desc = vq->vq_packed.ring.desc; 2335e7012eSMaxime Coquelin struct virtio_pmd_ctrl *result; 2435e7012eSMaxime Coquelin uint16_t flags; 2535e7012eSMaxime Coquelin int sum = 0; 2635e7012eSMaxime Coquelin int nb_descs = 0; 2735e7012eSMaxime Coquelin int k; 2835e7012eSMaxime Coquelin 2935e7012eSMaxime Coquelin /* 3035e7012eSMaxime Coquelin * Format is enforced in qemu code: 3135e7012eSMaxime Coquelin * One TX packet for header; 3235e7012eSMaxime Coquelin * At least one TX packet per argument; 3335e7012eSMaxime Coquelin * One RX packet for ACK. 3435e7012eSMaxime Coquelin */ 3535e7012eSMaxime Coquelin head = vq->vq_avail_idx; 3635e7012eSMaxime Coquelin flags = vq->vq_packed.cached_flags; 37a632f0f6SMaxime Coquelin desc[head].addr = cvq->hdr_mem; 3835e7012eSMaxime Coquelin desc[head].len = sizeof(struct virtio_net_ctrl_hdr); 3935e7012eSMaxime Coquelin vq->vq_free_cnt--; 4035e7012eSMaxime Coquelin nb_descs++; 4135e7012eSMaxime Coquelin if (++vq->vq_avail_idx >= vq->vq_nentries) { 4235e7012eSMaxime Coquelin vq->vq_avail_idx -= vq->vq_nentries; 4335e7012eSMaxime Coquelin vq->vq_packed.cached_flags ^= VRING_PACKED_DESC_F_AVAIL_USED; 4435e7012eSMaxime Coquelin } 4535e7012eSMaxime Coquelin 4635e7012eSMaxime Coquelin for (k = 0; k < pkt_num; k++) { 47a632f0f6SMaxime Coquelin desc[vq->vq_avail_idx].addr = cvq->hdr_mem 4835e7012eSMaxime Coquelin + sizeof(struct virtio_net_ctrl_hdr) 4935e7012eSMaxime Coquelin + sizeof(ctrl->status) + sizeof(uint8_t) * sum; 5035e7012eSMaxime Coquelin desc[vq->vq_avail_idx].len = dlen[k]; 5135e7012eSMaxime Coquelin desc[vq->vq_avail_idx].flags = VRING_DESC_F_NEXT | 5235e7012eSMaxime Coquelin vq->vq_packed.cached_flags; 5335e7012eSMaxime Coquelin sum += dlen[k]; 5435e7012eSMaxime Coquelin vq->vq_free_cnt--; 5535e7012eSMaxime Coquelin nb_descs++; 5635e7012eSMaxime Coquelin if (++vq->vq_avail_idx >= vq->vq_nentries) { 5735e7012eSMaxime Coquelin vq->vq_avail_idx -= vq->vq_nentries; 5835e7012eSMaxime Coquelin vq->vq_packed.cached_flags ^= 5935e7012eSMaxime Coquelin VRING_PACKED_DESC_F_AVAIL_USED; 6035e7012eSMaxime Coquelin } 6135e7012eSMaxime Coquelin } 6235e7012eSMaxime Coquelin 63a632f0f6SMaxime Coquelin desc[vq->vq_avail_idx].addr = cvq->hdr_mem 6435e7012eSMaxime Coquelin + sizeof(struct virtio_net_ctrl_hdr); 6535e7012eSMaxime Coquelin desc[vq->vq_avail_idx].len = sizeof(ctrl->status); 6635e7012eSMaxime Coquelin desc[vq->vq_avail_idx].flags = VRING_DESC_F_WRITE | 6735e7012eSMaxime Coquelin vq->vq_packed.cached_flags; 6835e7012eSMaxime Coquelin vq->vq_free_cnt--; 6935e7012eSMaxime Coquelin nb_descs++; 7035e7012eSMaxime Coquelin if (++vq->vq_avail_idx >= vq->vq_nentries) { 7135e7012eSMaxime Coquelin vq->vq_avail_idx -= vq->vq_nentries; 7235e7012eSMaxime Coquelin vq->vq_packed.cached_flags ^= VRING_PACKED_DESC_F_AVAIL_USED; 7335e7012eSMaxime Coquelin } 7435e7012eSMaxime Coquelin 7535e7012eSMaxime Coquelin virtqueue_store_flags_packed(&desc[head], VRING_DESC_F_NEXT | flags, 7635e7012eSMaxime Coquelin vq->hw->weak_barriers); 7735e7012eSMaxime Coquelin 7835e7012eSMaxime Coquelin virtio_wmb(vq->hw->weak_barriers); 794dd3477cSMaxime Coquelin cvq->notify_queue(vq, cvq->notify_cookie); 8035e7012eSMaxime Coquelin 8135e7012eSMaxime Coquelin /* wait for used desc in virtqueue 8235e7012eSMaxime Coquelin * desc_is_used has a load-acquire or rte_io_rmb inside 8335e7012eSMaxime Coquelin */ 8435e7012eSMaxime Coquelin while (!desc_is_used(&desc[head], vq)) 8535e7012eSMaxime Coquelin usleep(100); 8635e7012eSMaxime Coquelin 8735e7012eSMaxime Coquelin /* now get used descriptors */ 8835e7012eSMaxime Coquelin vq->vq_free_cnt += nb_descs; 8935e7012eSMaxime Coquelin vq->vq_used_cons_idx += nb_descs; 9035e7012eSMaxime Coquelin if (vq->vq_used_cons_idx >= vq->vq_nentries) { 9135e7012eSMaxime Coquelin vq->vq_used_cons_idx -= vq->vq_nentries; 9235e7012eSMaxime Coquelin vq->vq_packed.used_wrap_counter ^= 1; 9335e7012eSMaxime Coquelin } 9435e7012eSMaxime Coquelin 95*1af8b0b2SDavid Marchand PMD_INIT_LOG(DEBUG, "vq->vq_free_cnt=%d", vq->vq_free_cnt); 96*1af8b0b2SDavid Marchand PMD_INIT_LOG(DEBUG, "vq->vq_avail_idx=%d", vq->vq_avail_idx); 97*1af8b0b2SDavid Marchand PMD_INIT_LOG(DEBUG, "vq->vq_used_cons_idx=%d", vq->vq_used_cons_idx); 98*1af8b0b2SDavid Marchand PMD_INIT_LOG(DEBUG, "vq->vq_packed.cached_flags=0x%x", vq->vq_packed.cached_flags); 99*1af8b0b2SDavid Marchand PMD_INIT_LOG(DEBUG, "vq->vq_packed.used_wrap_counter=%d", vq->vq_packed.used_wrap_counter); 10035e7012eSMaxime Coquelin 101a632f0f6SMaxime Coquelin result = cvq->hdr_mz->addr; 10235e7012eSMaxime Coquelin return result; 10335e7012eSMaxime Coquelin } 10435e7012eSMaxime Coquelin 10535e7012eSMaxime Coquelin static struct virtio_pmd_ctrl * 10635e7012eSMaxime Coquelin virtio_send_command_split(struct virtnet_ctl *cvq, 10735e7012eSMaxime Coquelin struct virtio_pmd_ctrl *ctrl, 10835e7012eSMaxime Coquelin int *dlen, int pkt_num) 10935e7012eSMaxime Coquelin { 11035e7012eSMaxime Coquelin struct virtio_pmd_ctrl *result; 11135e7012eSMaxime Coquelin struct virtqueue *vq = virtnet_cq_to_vq(cvq); 11235e7012eSMaxime Coquelin uint32_t head, i; 11335e7012eSMaxime Coquelin int k, sum = 0; 11435e7012eSMaxime Coquelin 11535e7012eSMaxime Coquelin head = vq->vq_desc_head_idx; 11635e7012eSMaxime Coquelin 11735e7012eSMaxime Coquelin /* 11835e7012eSMaxime Coquelin * Format is enforced in qemu code: 11935e7012eSMaxime Coquelin * One TX packet for header; 12035e7012eSMaxime Coquelin * At least one TX packet per argument; 12135e7012eSMaxime Coquelin * One RX packet for ACK. 12235e7012eSMaxime Coquelin */ 12335e7012eSMaxime Coquelin vq->vq_split.ring.desc[head].flags = VRING_DESC_F_NEXT; 124a632f0f6SMaxime Coquelin vq->vq_split.ring.desc[head].addr = cvq->hdr_mem; 12535e7012eSMaxime Coquelin vq->vq_split.ring.desc[head].len = sizeof(struct virtio_net_ctrl_hdr); 12635e7012eSMaxime Coquelin vq->vq_free_cnt--; 12735e7012eSMaxime Coquelin i = vq->vq_split.ring.desc[head].next; 12835e7012eSMaxime Coquelin 12935e7012eSMaxime Coquelin for (k = 0; k < pkt_num; k++) { 13035e7012eSMaxime Coquelin vq->vq_split.ring.desc[i].flags = VRING_DESC_F_NEXT; 131a632f0f6SMaxime Coquelin vq->vq_split.ring.desc[i].addr = cvq->hdr_mem 13235e7012eSMaxime Coquelin + sizeof(struct virtio_net_ctrl_hdr) 13335e7012eSMaxime Coquelin + sizeof(ctrl->status) + sizeof(uint8_t) * sum; 13435e7012eSMaxime Coquelin vq->vq_split.ring.desc[i].len = dlen[k]; 13535e7012eSMaxime Coquelin sum += dlen[k]; 13635e7012eSMaxime Coquelin vq->vq_free_cnt--; 13735e7012eSMaxime Coquelin i = vq->vq_split.ring.desc[i].next; 13835e7012eSMaxime Coquelin } 13935e7012eSMaxime Coquelin 14035e7012eSMaxime Coquelin vq->vq_split.ring.desc[i].flags = VRING_DESC_F_WRITE; 141a632f0f6SMaxime Coquelin vq->vq_split.ring.desc[i].addr = cvq->hdr_mem 14235e7012eSMaxime Coquelin + sizeof(struct virtio_net_ctrl_hdr); 14335e7012eSMaxime Coquelin vq->vq_split.ring.desc[i].len = sizeof(ctrl->status); 14435e7012eSMaxime Coquelin vq->vq_free_cnt--; 14535e7012eSMaxime Coquelin 14635e7012eSMaxime Coquelin vq->vq_desc_head_idx = vq->vq_split.ring.desc[i].next; 14735e7012eSMaxime Coquelin 14835e7012eSMaxime Coquelin vq_update_avail_ring(vq, head); 14935e7012eSMaxime Coquelin vq_update_avail_idx(vq); 15035e7012eSMaxime Coquelin 15135e7012eSMaxime Coquelin PMD_INIT_LOG(DEBUG, "vq->vq_queue_index = %d", vq->vq_queue_index); 15235e7012eSMaxime Coquelin 1534dd3477cSMaxime Coquelin cvq->notify_queue(vq, cvq->notify_cookie); 15435e7012eSMaxime Coquelin 15535e7012eSMaxime Coquelin while (virtqueue_nused(vq) == 0) 15635e7012eSMaxime Coquelin usleep(100); 15735e7012eSMaxime Coquelin 15835e7012eSMaxime Coquelin while (virtqueue_nused(vq)) { 15935e7012eSMaxime Coquelin uint32_t idx, desc_idx, used_idx; 16035e7012eSMaxime Coquelin struct vring_used_elem *uep; 16135e7012eSMaxime Coquelin 16235e7012eSMaxime Coquelin used_idx = (uint32_t)(vq->vq_used_cons_idx 16335e7012eSMaxime Coquelin & (vq->vq_nentries - 1)); 16435e7012eSMaxime Coquelin uep = &vq->vq_split.ring.used->ring[used_idx]; 16535e7012eSMaxime Coquelin idx = (uint32_t)uep->id; 16635e7012eSMaxime Coquelin desc_idx = idx; 16735e7012eSMaxime Coquelin 16835e7012eSMaxime Coquelin while (vq->vq_split.ring.desc[desc_idx].flags & 16935e7012eSMaxime Coquelin VRING_DESC_F_NEXT) { 17035e7012eSMaxime Coquelin desc_idx = vq->vq_split.ring.desc[desc_idx].next; 17135e7012eSMaxime Coquelin vq->vq_free_cnt++; 17235e7012eSMaxime Coquelin } 17335e7012eSMaxime Coquelin 17435e7012eSMaxime Coquelin vq->vq_split.ring.desc[desc_idx].next = vq->vq_desc_head_idx; 17535e7012eSMaxime Coquelin vq->vq_desc_head_idx = idx; 17635e7012eSMaxime Coquelin 17735e7012eSMaxime Coquelin vq->vq_used_cons_idx++; 17835e7012eSMaxime Coquelin vq->vq_free_cnt++; 17935e7012eSMaxime Coquelin } 18035e7012eSMaxime Coquelin 181*1af8b0b2SDavid Marchand PMD_INIT_LOG(DEBUG, "vq->vq_free_cnt=%d", vq->vq_free_cnt); 182*1af8b0b2SDavid Marchand PMD_INIT_LOG(DEBUG, "vq->vq_desc_head_idx=%d", vq->vq_desc_head_idx); 18335e7012eSMaxime Coquelin 184a632f0f6SMaxime Coquelin result = cvq->hdr_mz->addr; 18535e7012eSMaxime Coquelin return result; 18635e7012eSMaxime Coquelin } 18735e7012eSMaxime Coquelin 18835e7012eSMaxime Coquelin int 18935e7012eSMaxime Coquelin virtio_send_command(struct virtnet_ctl *cvq, struct virtio_pmd_ctrl *ctrl, int *dlen, int pkt_num) 19035e7012eSMaxime Coquelin { 19135e7012eSMaxime Coquelin virtio_net_ctrl_ack status = ~0; 19235e7012eSMaxime Coquelin struct virtio_pmd_ctrl *result; 19335e7012eSMaxime Coquelin struct virtqueue *vq; 19435e7012eSMaxime Coquelin 19535e7012eSMaxime Coquelin ctrl->status = status; 19635e7012eSMaxime Coquelin 19735e7012eSMaxime Coquelin if (!cvq) { 19835e7012eSMaxime Coquelin PMD_INIT_LOG(ERR, "Control queue is not supported."); 19935e7012eSMaxime Coquelin return -1; 20035e7012eSMaxime Coquelin } 20135e7012eSMaxime Coquelin 20235e7012eSMaxime Coquelin rte_spinlock_lock(&cvq->lock); 20335e7012eSMaxime Coquelin vq = virtnet_cq_to_vq(cvq); 20435e7012eSMaxime Coquelin 20535e7012eSMaxime Coquelin PMD_INIT_LOG(DEBUG, "vq->vq_desc_head_idx = %d, status = %d, " 20635e7012eSMaxime Coquelin "vq->hw->cvq = %p vq = %p", 20735e7012eSMaxime Coquelin vq->vq_desc_head_idx, status, vq->hw->cvq, vq); 20835e7012eSMaxime Coquelin 20935e7012eSMaxime Coquelin if (vq->vq_free_cnt < pkt_num + 2 || pkt_num < 1) { 21035e7012eSMaxime Coquelin rte_spinlock_unlock(&cvq->lock); 21135e7012eSMaxime Coquelin return -1; 21235e7012eSMaxime Coquelin } 21335e7012eSMaxime Coquelin 214a632f0f6SMaxime Coquelin memcpy(cvq->hdr_mz->addr, ctrl, sizeof(struct virtio_pmd_ctrl)); 21535e7012eSMaxime Coquelin 21635e7012eSMaxime Coquelin if (virtio_with_packed_queue(vq->hw)) 21735e7012eSMaxime Coquelin result = virtio_send_command_packed(cvq, ctrl, dlen, pkt_num); 21835e7012eSMaxime Coquelin else 21935e7012eSMaxime Coquelin result = virtio_send_command_split(cvq, ctrl, dlen, pkt_num); 22035e7012eSMaxime Coquelin 22135e7012eSMaxime Coquelin rte_spinlock_unlock(&cvq->lock); 22235e7012eSMaxime Coquelin return result->status; 22335e7012eSMaxime Coquelin } 22435e7012eSMaxime Coquelin 225