1474f4d78SMaxime Coquelin /* SPDX-License-Identifier: BSD-3-Clause 2474f4d78SMaxime Coquelin * Copyright (c) 2023 Red Hat, Inc. 3474f4d78SMaxime Coquelin */ 4474f4d78SMaxime Coquelin 5474f4d78SMaxime Coquelin #include <stdint.h> 6474f4d78SMaxime Coquelin #include <stdio.h> 7474f4d78SMaxime Coquelin #include <unistd.h> 8474f4d78SMaxime Coquelin 9474f4d78SMaxime Coquelin #include "iotlb.h" 10474f4d78SMaxime Coquelin #include "vhost.h" 11474f4d78SMaxime Coquelin #include "virtio_net_ctrl.h" 12474f4d78SMaxime Coquelin 13474f4d78SMaxime Coquelin struct virtio_net_ctrl { 14474f4d78SMaxime Coquelin uint8_t class; 15474f4d78SMaxime Coquelin uint8_t command; 16474f4d78SMaxime Coquelin uint8_t command_data[]; 17474f4d78SMaxime Coquelin }; 18474f4d78SMaxime Coquelin 19474f4d78SMaxime Coquelin struct virtio_net_ctrl_elem { 20474f4d78SMaxime Coquelin struct virtio_net_ctrl *ctrl_req; 21474f4d78SMaxime Coquelin uint16_t head_idx; 22474f4d78SMaxime Coquelin uint16_t n_descs; 23474f4d78SMaxime Coquelin uint8_t *desc_ack; 24474f4d78SMaxime Coquelin }; 25474f4d78SMaxime Coquelin 26474f4d78SMaxime Coquelin static int 27474f4d78SMaxime Coquelin virtio_net_ctrl_pop(struct virtio_net *dev, struct vhost_virtqueue *cvq, 28474f4d78SMaxime Coquelin struct virtio_net_ctrl_elem *ctrl_elem) 29474f4d78SMaxime Coquelin __rte_shared_locks_required(&cvq->iotlb_lock) 30474f4d78SMaxime Coquelin { 31474f4d78SMaxime Coquelin uint16_t avail_idx, desc_idx, n_descs = 0; 32474f4d78SMaxime Coquelin uint64_t desc_len, desc_addr, desc_iova, data_len = 0; 33474f4d78SMaxime Coquelin uint8_t *ctrl_req; 34474f4d78SMaxime Coquelin struct vring_desc *descs; 35474f4d78SMaxime Coquelin 365147b641STyler Retzlaff avail_idx = rte_atomic_load_explicit((unsigned short __rte_atomic *)&cvq->avail->idx, 375147b641STyler Retzlaff rte_memory_order_acquire); 38474f4d78SMaxime Coquelin if (avail_idx == cvq->last_avail_idx) { 390e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, DEBUG, "Control queue empty"); 40474f4d78SMaxime Coquelin return 0; 41474f4d78SMaxime Coquelin } 42474f4d78SMaxime Coquelin 43474f4d78SMaxime Coquelin desc_idx = cvq->avail->ring[cvq->last_avail_idx]; 44474f4d78SMaxime Coquelin if (desc_idx >= cvq->size) { 450e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, ERR, "Out of range desc index, dropping"); 46474f4d78SMaxime Coquelin goto err; 47474f4d78SMaxime Coquelin } 48474f4d78SMaxime Coquelin 49474f4d78SMaxime Coquelin ctrl_elem->head_idx = desc_idx; 50474f4d78SMaxime Coquelin 51474f4d78SMaxime Coquelin if (cvq->desc[desc_idx].flags & VRING_DESC_F_INDIRECT) { 52474f4d78SMaxime Coquelin desc_len = cvq->desc[desc_idx].len; 53474f4d78SMaxime Coquelin desc_iova = cvq->desc[desc_idx].addr; 54474f4d78SMaxime Coquelin 55474f4d78SMaxime Coquelin descs = (struct vring_desc *)(uintptr_t)vhost_iova_to_vva(dev, cvq, 56474f4d78SMaxime Coquelin desc_iova, &desc_len, VHOST_ACCESS_RO); 57474f4d78SMaxime Coquelin if (!descs || desc_len != cvq->desc[desc_idx].len) { 580e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to map ctrl indirect descs"); 59474f4d78SMaxime Coquelin goto err; 60474f4d78SMaxime Coquelin } 61474f4d78SMaxime Coquelin 62474f4d78SMaxime Coquelin desc_idx = 0; 63474f4d78SMaxime Coquelin } else { 64474f4d78SMaxime Coquelin descs = cvq->desc; 65474f4d78SMaxime Coquelin } 66474f4d78SMaxime Coquelin 67474f4d78SMaxime Coquelin while (1) { 68474f4d78SMaxime Coquelin desc_len = descs[desc_idx].len; 69474f4d78SMaxime Coquelin desc_iova = descs[desc_idx].addr; 70474f4d78SMaxime Coquelin 71474f4d78SMaxime Coquelin n_descs++; 72474f4d78SMaxime Coquelin 73474f4d78SMaxime Coquelin if (descs[desc_idx].flags & VRING_DESC_F_WRITE) { 74474f4d78SMaxime Coquelin if (ctrl_elem->desc_ack) { 750e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, ERR, 760e21c7c0SDavid Marchand "Unexpected ctrl chain layout"); 77474f4d78SMaxime Coquelin goto err; 78474f4d78SMaxime Coquelin } 79474f4d78SMaxime Coquelin 80474f4d78SMaxime Coquelin if (desc_len != sizeof(uint8_t)) { 810e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, ERR, 820e21c7c0SDavid Marchand "Invalid ack size for ctrl req, dropping"); 83474f4d78SMaxime Coquelin goto err; 84474f4d78SMaxime Coquelin } 85474f4d78SMaxime Coquelin 86474f4d78SMaxime Coquelin ctrl_elem->desc_ack = (uint8_t *)(uintptr_t)vhost_iova_to_vva(dev, cvq, 87474f4d78SMaxime Coquelin desc_iova, &desc_len, VHOST_ACCESS_WO); 88474f4d78SMaxime Coquelin if (!ctrl_elem->desc_ack || desc_len != sizeof(uint8_t)) { 890e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, ERR, 900e21c7c0SDavid Marchand "Failed to map ctrl ack descriptor"); 91474f4d78SMaxime Coquelin goto err; 92474f4d78SMaxime Coquelin } 93474f4d78SMaxime Coquelin } else { 94474f4d78SMaxime Coquelin if (ctrl_elem->desc_ack) { 950e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, ERR, 960e21c7c0SDavid Marchand "Unexpected ctrl chain layout"); 97474f4d78SMaxime Coquelin goto err; 98474f4d78SMaxime Coquelin } 99474f4d78SMaxime Coquelin 100474f4d78SMaxime Coquelin data_len += desc_len; 101474f4d78SMaxime Coquelin } 102474f4d78SMaxime Coquelin 103474f4d78SMaxime Coquelin if (!(descs[desc_idx].flags & VRING_DESC_F_NEXT)) 104474f4d78SMaxime Coquelin break; 105474f4d78SMaxime Coquelin 106474f4d78SMaxime Coquelin desc_idx = descs[desc_idx].next; 107474f4d78SMaxime Coquelin } 108474f4d78SMaxime Coquelin 109474f4d78SMaxime Coquelin desc_idx = ctrl_elem->head_idx; 110474f4d78SMaxime Coquelin 111474f4d78SMaxime Coquelin if (cvq->desc[desc_idx].flags & VRING_DESC_F_INDIRECT) 112474f4d78SMaxime Coquelin ctrl_elem->n_descs = 1; 113474f4d78SMaxime Coquelin else 114474f4d78SMaxime Coquelin ctrl_elem->n_descs = n_descs; 115474f4d78SMaxime Coquelin 116474f4d78SMaxime Coquelin if (!ctrl_elem->desc_ack) { 1170e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, ERR, "Missing ctrl ack descriptor"); 118474f4d78SMaxime Coquelin goto err; 119474f4d78SMaxime Coquelin } 120474f4d78SMaxime Coquelin 121474f4d78SMaxime Coquelin if (data_len < sizeof(ctrl_elem->ctrl_req->class) + sizeof(ctrl_elem->ctrl_req->command)) { 1220e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, ERR, "Invalid control header size"); 123474f4d78SMaxime Coquelin goto err; 124474f4d78SMaxime Coquelin } 125474f4d78SMaxime Coquelin 126474f4d78SMaxime Coquelin ctrl_elem->ctrl_req = malloc(data_len); 127474f4d78SMaxime Coquelin if (!ctrl_elem->ctrl_req) { 1280e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to alloc ctrl request"); 129474f4d78SMaxime Coquelin goto err; 130474f4d78SMaxime Coquelin } 131474f4d78SMaxime Coquelin 132474f4d78SMaxime Coquelin ctrl_req = (uint8_t *)ctrl_elem->ctrl_req; 133474f4d78SMaxime Coquelin 134474f4d78SMaxime Coquelin if (cvq->desc[desc_idx].flags & VRING_DESC_F_INDIRECT) { 135474f4d78SMaxime Coquelin desc_len = cvq->desc[desc_idx].len; 136474f4d78SMaxime Coquelin desc_iova = cvq->desc[desc_idx].addr; 137474f4d78SMaxime Coquelin 138474f4d78SMaxime Coquelin descs = (struct vring_desc *)(uintptr_t)vhost_iova_to_vva(dev, cvq, 139474f4d78SMaxime Coquelin desc_iova, &desc_len, VHOST_ACCESS_RO); 140474f4d78SMaxime Coquelin if (!descs || desc_len != cvq->desc[desc_idx].len) { 1410e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to map ctrl indirect descs"); 142474f4d78SMaxime Coquelin goto free_err; 143474f4d78SMaxime Coquelin } 144474f4d78SMaxime Coquelin 145474f4d78SMaxime Coquelin desc_idx = 0; 146474f4d78SMaxime Coquelin } else { 147474f4d78SMaxime Coquelin descs = cvq->desc; 148474f4d78SMaxime Coquelin } 149474f4d78SMaxime Coquelin 150474f4d78SMaxime Coquelin while (!(descs[desc_idx].flags & VRING_DESC_F_WRITE)) { 151474f4d78SMaxime Coquelin desc_len = descs[desc_idx].len; 152474f4d78SMaxime Coquelin desc_iova = descs[desc_idx].addr; 153474f4d78SMaxime Coquelin 154474f4d78SMaxime Coquelin desc_addr = vhost_iova_to_vva(dev, cvq, desc_iova, &desc_len, VHOST_ACCESS_RO); 155474f4d78SMaxime Coquelin if (!desc_addr || desc_len < descs[desc_idx].len) { 1560e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to map ctrl descriptor"); 157474f4d78SMaxime Coquelin goto free_err; 158474f4d78SMaxime Coquelin } 159474f4d78SMaxime Coquelin 160474f4d78SMaxime Coquelin memcpy(ctrl_req, (void *)(uintptr_t)desc_addr, desc_len); 161474f4d78SMaxime Coquelin ctrl_req += desc_len; 162474f4d78SMaxime Coquelin 163474f4d78SMaxime Coquelin if (!(descs[desc_idx].flags & VRING_DESC_F_NEXT)) 164474f4d78SMaxime Coquelin break; 165474f4d78SMaxime Coquelin 166474f4d78SMaxime Coquelin desc_idx = descs[desc_idx].next; 167474f4d78SMaxime Coquelin } 168474f4d78SMaxime Coquelin 169474f4d78SMaxime Coquelin cvq->last_avail_idx++; 170474f4d78SMaxime Coquelin if (cvq->last_avail_idx >= cvq->size) 171474f4d78SMaxime Coquelin cvq->last_avail_idx -= cvq->size; 172*15677ca2SMaxime Coquelin vhost_virtqueue_reconnect_log_split(cvq); 173474f4d78SMaxime Coquelin 174474f4d78SMaxime Coquelin if (dev->features & (1ULL << VIRTIO_RING_F_EVENT_IDX)) 175474f4d78SMaxime Coquelin vhost_avail_event(cvq) = cvq->last_avail_idx; 176474f4d78SMaxime Coquelin 177474f4d78SMaxime Coquelin return 1; 178474f4d78SMaxime Coquelin 179474f4d78SMaxime Coquelin free_err: 180474f4d78SMaxime Coquelin free(ctrl_elem->ctrl_req); 181474f4d78SMaxime Coquelin err: 182474f4d78SMaxime Coquelin cvq->last_avail_idx++; 183474f4d78SMaxime Coquelin if (cvq->last_avail_idx >= cvq->size) 184474f4d78SMaxime Coquelin cvq->last_avail_idx -= cvq->size; 185*15677ca2SMaxime Coquelin vhost_virtqueue_reconnect_log_split(cvq); 186474f4d78SMaxime Coquelin 187474f4d78SMaxime Coquelin if (dev->features & (1ULL << VIRTIO_RING_F_EVENT_IDX)) 188474f4d78SMaxime Coquelin vhost_avail_event(cvq) = cvq->last_avail_idx; 189474f4d78SMaxime Coquelin 190474f4d78SMaxime Coquelin return -1; 191474f4d78SMaxime Coquelin } 192474f4d78SMaxime Coquelin 193474f4d78SMaxime Coquelin static uint8_t 194474f4d78SMaxime Coquelin virtio_net_ctrl_handle_req(struct virtio_net *dev, struct virtio_net_ctrl *ctrl_req) 195474f4d78SMaxime Coquelin { 196474f4d78SMaxime Coquelin uint8_t ret = VIRTIO_NET_ERR; 197474f4d78SMaxime Coquelin 198474f4d78SMaxime Coquelin if (ctrl_req->class == VIRTIO_NET_CTRL_MQ && 199474f4d78SMaxime Coquelin ctrl_req->command == VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET) { 200474f4d78SMaxime Coquelin uint16_t queue_pairs; 201474f4d78SMaxime Coquelin uint32_t i; 202474f4d78SMaxime Coquelin 203474f4d78SMaxime Coquelin queue_pairs = *(uint16_t *)(uintptr_t)ctrl_req->command_data; 2040e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, INFO, "Ctrl req: MQ %u queue pairs", queue_pairs); 205474f4d78SMaxime Coquelin ret = VIRTIO_NET_OK; 206474f4d78SMaxime Coquelin 207474f4d78SMaxime Coquelin for (i = 0; i < dev->nr_vring; i++) { 208474f4d78SMaxime Coquelin struct vhost_virtqueue *vq = dev->virtqueue[i]; 209474f4d78SMaxime Coquelin bool enable; 210474f4d78SMaxime Coquelin 211474f4d78SMaxime Coquelin if (vq == dev->cvq) 212474f4d78SMaxime Coquelin continue; 213474f4d78SMaxime Coquelin 214474f4d78SMaxime Coquelin if (i < queue_pairs * 2) 215474f4d78SMaxime Coquelin enable = true; 216474f4d78SMaxime Coquelin else 217474f4d78SMaxime Coquelin enable = false; 218474f4d78SMaxime Coquelin 219474f4d78SMaxime Coquelin vq->enabled = enable; 220474f4d78SMaxime Coquelin if (dev->notify_ops->vring_state_changed) 221474f4d78SMaxime Coquelin dev->notify_ops->vring_state_changed(dev->vid, i, enable); 222474f4d78SMaxime Coquelin } 223474f4d78SMaxime Coquelin } 224474f4d78SMaxime Coquelin 225474f4d78SMaxime Coquelin return ret; 226474f4d78SMaxime Coquelin } 227474f4d78SMaxime Coquelin 228474f4d78SMaxime Coquelin static int 229474f4d78SMaxime Coquelin virtio_net_ctrl_push(struct virtio_net *dev, struct virtio_net_ctrl_elem *ctrl_elem) 230474f4d78SMaxime Coquelin { 231474f4d78SMaxime Coquelin struct vhost_virtqueue *cvq = dev->cvq; 232474f4d78SMaxime Coquelin struct vring_used_elem *used_elem; 233474f4d78SMaxime Coquelin 234474f4d78SMaxime Coquelin used_elem = &cvq->used->ring[cvq->last_used_idx]; 235474f4d78SMaxime Coquelin used_elem->id = ctrl_elem->head_idx; 236474f4d78SMaxime Coquelin used_elem->len = ctrl_elem->n_descs; 237474f4d78SMaxime Coquelin 238474f4d78SMaxime Coquelin cvq->last_used_idx++; 239474f4d78SMaxime Coquelin if (cvq->last_used_idx >= cvq->size) 240474f4d78SMaxime Coquelin cvq->last_used_idx -= cvq->size; 241474f4d78SMaxime Coquelin 2425147b641STyler Retzlaff rte_atomic_store_explicit((unsigned short __rte_atomic *)&cvq->used->idx, 2435147b641STyler Retzlaff cvq->last_used_idx, rte_memory_order_release); 244474f4d78SMaxime Coquelin 245474f4d78SMaxime Coquelin vhost_vring_call_split(dev, dev->cvq); 246474f4d78SMaxime Coquelin 247474f4d78SMaxime Coquelin free(ctrl_elem->ctrl_req); 248474f4d78SMaxime Coquelin 249474f4d78SMaxime Coquelin return 0; 250474f4d78SMaxime Coquelin } 251474f4d78SMaxime Coquelin 252474f4d78SMaxime Coquelin int 253474f4d78SMaxime Coquelin virtio_net_ctrl_handle(struct virtio_net *dev) 254474f4d78SMaxime Coquelin { 255474f4d78SMaxime Coquelin int ret = 0; 256474f4d78SMaxime Coquelin 257474f4d78SMaxime Coquelin if (dev->features & (1ULL << VIRTIO_F_RING_PACKED)) { 2580e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, ERR, "Packed ring not supported yet"); 259474f4d78SMaxime Coquelin return -1; 260474f4d78SMaxime Coquelin } 261474f4d78SMaxime Coquelin 262474f4d78SMaxime Coquelin if (!dev->cvq) { 2630e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, ERR, "missing control queue"); 264474f4d78SMaxime Coquelin return -1; 265474f4d78SMaxime Coquelin } 266474f4d78SMaxime Coquelin 267474f4d78SMaxime Coquelin rte_rwlock_read_lock(&dev->cvq->access_lock); 268474f4d78SMaxime Coquelin vhost_user_iotlb_rd_lock(dev->cvq); 269474f4d78SMaxime Coquelin 270474f4d78SMaxime Coquelin while (1) { 271474f4d78SMaxime Coquelin struct virtio_net_ctrl_elem ctrl_elem; 272474f4d78SMaxime Coquelin 273474f4d78SMaxime Coquelin memset(&ctrl_elem, 0, sizeof(struct virtio_net_ctrl_elem)); 274474f4d78SMaxime Coquelin 275474f4d78SMaxime Coquelin ret = virtio_net_ctrl_pop(dev, dev->cvq, &ctrl_elem); 276474f4d78SMaxime Coquelin if (ret <= 0) 277474f4d78SMaxime Coquelin break; 278474f4d78SMaxime Coquelin 279474f4d78SMaxime Coquelin *ctrl_elem.desc_ack = virtio_net_ctrl_handle_req(dev, ctrl_elem.ctrl_req); 280474f4d78SMaxime Coquelin 281474f4d78SMaxime Coquelin ret = virtio_net_ctrl_push(dev, &ctrl_elem); 282474f4d78SMaxime Coquelin if (ret < 0) 283474f4d78SMaxime Coquelin break; 284474f4d78SMaxime Coquelin } 285474f4d78SMaxime Coquelin 286474f4d78SMaxime Coquelin vhost_user_iotlb_rd_unlock(dev->cvq); 287474f4d78SMaxime Coquelin rte_rwlock_read_unlock(&dev->cvq->access_lock); 288474f4d78SMaxime Coquelin 289474f4d78SMaxime Coquelin return ret; 290474f4d78SMaxime Coquelin } 291