LCOV - code coverage report
Current view: top level - lib/vhost - virtio_net_ctrl.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 0 128 0.0 %
Date: 2026-04-01 20:02:27 Functions: 0 4 0.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 0 90 0.0 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: BSD-3-Clause
       2                 :            :  * Copyright (c) 2023 Red Hat, Inc.
       3                 :            :  */
       4                 :            : 
       5                 :            : #include <stdint.h>
       6                 :            : #include <stdio.h>
       7                 :            : #include <unistd.h>
       8                 :            : 
       9                 :            : #include "iotlb.h"
      10                 :            : #include "vhost.h"
      11                 :            : #include "virtio_net_ctrl.h"
      12                 :            : 
      13                 :            : struct virtio_net_ctrl {
      14                 :            :         uint8_t class;
      15                 :            :         uint8_t command;
      16                 :            :         uint8_t command_data[];
      17                 :            : };
      18                 :            : 
      19                 :            : struct virtio_net_ctrl_elem {
      20                 :            :         struct virtio_net_ctrl *ctrl_req;
      21                 :            :         uint16_t head_idx;
      22                 :            :         uint16_t n_descs;
      23                 :            :         uint8_t *desc_ack;
      24                 :            : };
      25                 :            : 
      26                 :            : static int
      27                 :          0 : virtio_net_ctrl_pop(struct virtio_net *dev, struct vhost_virtqueue *cvq,
      28                 :            :                 struct virtio_net_ctrl_elem *ctrl_elem)
      29                 :            :         __rte_requires_shared_capability(&cvq->iotlb_lock)
      30                 :            : {
      31                 :            :         uint16_t avail_idx, desc_idx, n_descs = 0, nr_descs, cnt = 0;
      32                 :            :         uint64_t desc_len, desc_addr, desc_iova, data_len = 0;
      33                 :            :         uint8_t *ctrl_req;
      34                 :            :         struct vring_desc *descs;
      35                 :            : 
      36                 :          0 :         avail_idx = rte_atomic_load_explicit((unsigned short __rte_atomic *)&cvq->avail->idx,
      37                 :            :                 rte_memory_order_acquire);
      38         [ #  # ]:          0 :         if (avail_idx == cvq->last_avail_idx) {
      39                 :          0 :                 VHOST_CONFIG_LOG(dev->ifname, DEBUG, "Control queue empty");
      40                 :          0 :                 return 0;
      41                 :            :         }
      42                 :            : 
      43                 :          0 :         desc_idx = cvq->avail->ring[cvq->last_avail_idx & (cvq->size - 1)];
      44         [ #  # ]:          0 :         if (desc_idx >= cvq->size) {
      45                 :          0 :                 VHOST_CONFIG_LOG(dev->ifname, ERR, "Out of range desc index, dropping");
      46                 :          0 :                 goto err;
      47                 :            :         }
      48                 :            : 
      49                 :          0 :         ctrl_elem->head_idx = desc_idx;
      50                 :            : 
      51         [ #  # ]:          0 :         if (cvq->desc[desc_idx].flags & VRING_DESC_F_INDIRECT) {
      52                 :          0 :                 desc_len = cvq->desc[desc_idx].len;
      53         [ #  # ]:          0 :                 desc_iova = cvq->desc[desc_idx].addr;
      54                 :            : 
      55                 :          0 :                 descs = (struct vring_desc *)(uintptr_t)vhost_iova_to_vva(dev, cvq,
      56                 :            :                                         desc_iova, &desc_len, VHOST_ACCESS_RO);
      57   [ #  #  #  # ]:          0 :                 if (!descs || desc_len != cvq->desc[desc_idx].len) {
      58                 :          0 :                         VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to map ctrl indirect descs");
      59                 :          0 :                         goto err;
      60                 :            :                 }
      61                 :            : 
      62                 :          0 :                 nr_descs = desc_len / sizeof(struct vring_desc);
      63                 :            :                 desc_idx = 0;
      64                 :            :         } else {
      65                 :            :                 descs = cvq->desc;
      66                 :          0 :                 nr_descs = cvq->size;
      67                 :            :         }
      68                 :            : 
      69                 :            :         while (1) {
      70   [ #  #  #  # ]:          0 :                 if (unlikely(desc_idx >= nr_descs || cnt++ >= nr_descs)) {
      71                 :          0 :                         VHOST_CONFIG_LOG(dev->ifname, ERR, "Invalid ctrl descriptor chain");
      72                 :          0 :                         goto err;
      73                 :            :                 }
      74                 :            : 
      75                 :          0 :                 desc_len = descs[desc_idx].len;
      76                 :          0 :                 desc_iova = descs[desc_idx].addr;
      77                 :            : 
      78                 :            :                 n_descs++;
      79                 :            : 
      80         [ #  # ]:          0 :                 if (descs[desc_idx].flags & VRING_DESC_F_WRITE) {
      81         [ #  # ]:          0 :                         if (ctrl_elem->desc_ack) {
      82                 :          0 :                                 VHOST_CONFIG_LOG(dev->ifname, ERR,
      83                 :            :                                                 "Unexpected ctrl chain layout");
      84                 :          0 :                                 goto err;
      85                 :            :                         }
      86                 :            : 
      87         [ #  # ]:          0 :                         if (desc_len != sizeof(uint8_t)) {
      88                 :          0 :                                 VHOST_CONFIG_LOG(dev->ifname, ERR,
      89                 :            :                                                 "Invalid ack size for ctrl req, dropping");
      90                 :          0 :                                 goto err;
      91                 :            :                         }
      92                 :            : 
      93                 :          0 :                         ctrl_elem->desc_ack = (uint8_t *)(uintptr_t)vhost_iova_to_vva(dev, cvq,
      94                 :            :                                         desc_iova, &desc_len, VHOST_ACCESS_WO);
      95   [ #  #  #  # ]:          0 :                         if (!ctrl_elem->desc_ack || desc_len != sizeof(uint8_t)) {
      96                 :          0 :                                 VHOST_CONFIG_LOG(dev->ifname, ERR,
      97                 :            :                                                 "Failed to map ctrl ack descriptor");
      98                 :          0 :                                 goto err;
      99                 :            :                         }
     100                 :            :                 } else {
     101         [ #  # ]:          0 :                         if (ctrl_elem->desc_ack) {
     102                 :          0 :                                 VHOST_CONFIG_LOG(dev->ifname, ERR,
     103                 :            :                                                 "Unexpected ctrl chain layout");
     104                 :          0 :                                 goto err;
     105                 :            :                         }
     106                 :            : 
     107                 :          0 :                         data_len += desc_len;
     108                 :            :                 }
     109                 :            : 
     110         [ #  # ]:          0 :                 if (!(descs[desc_idx].flags & VRING_DESC_F_NEXT))
     111                 :            :                         break;
     112                 :            : 
     113                 :          0 :                 desc_idx = descs[desc_idx].next;
     114                 :            :         }
     115                 :            : 
     116                 :          0 :         desc_idx = ctrl_elem->head_idx;
     117                 :            : 
     118         [ #  # ]:          0 :         if (cvq->desc[desc_idx].flags & VRING_DESC_F_INDIRECT)
     119                 :          0 :                 ctrl_elem->n_descs = 1;
     120                 :            :         else
     121                 :          0 :                 ctrl_elem->n_descs = n_descs;
     122                 :            : 
     123         [ #  # ]:          0 :         if (!ctrl_elem->desc_ack) {
     124                 :          0 :                 VHOST_CONFIG_LOG(dev->ifname, ERR, "Missing ctrl ack descriptor");
     125                 :          0 :                 goto err;
     126                 :            :         }
     127                 :            : 
     128         [ #  # ]:          0 :         if (data_len < sizeof(ctrl_elem->ctrl_req->class) + sizeof(ctrl_elem->ctrl_req->command)) {
     129                 :          0 :                 VHOST_CONFIG_LOG(dev->ifname, ERR, "Invalid control header size");
     130                 :          0 :                 goto err;
     131                 :            :         }
     132                 :            : 
     133                 :          0 :         ctrl_elem->ctrl_req = malloc(data_len);
     134         [ #  # ]:          0 :         if (!ctrl_elem->ctrl_req) {
     135                 :          0 :                 VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to alloc ctrl request");
     136                 :          0 :                 goto err;
     137                 :            :         }
     138                 :            : 
     139                 :            :         ctrl_req = (uint8_t *)ctrl_elem->ctrl_req;
     140                 :            : 
     141         [ #  # ]:          0 :         if (cvq->desc[desc_idx].flags & VRING_DESC_F_INDIRECT) {
     142                 :          0 :                 desc_len = cvq->desc[desc_idx].len;
     143         [ #  # ]:          0 :                 desc_iova = cvq->desc[desc_idx].addr;
     144                 :            : 
     145                 :          0 :                 descs = (struct vring_desc *)(uintptr_t)vhost_iova_to_vva(dev, cvq,
     146                 :            :                                         desc_iova, &desc_len, VHOST_ACCESS_RO);
     147   [ #  #  #  # ]:          0 :                 if (!descs || desc_len != cvq->desc[desc_idx].len) {
     148                 :          0 :                         VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to map ctrl indirect descs");
     149                 :          0 :                         goto free_err;
     150                 :            :                 }
     151                 :            : 
     152                 :          0 :                 nr_descs = desc_len / sizeof(struct vring_desc);
     153                 :            :                 desc_idx = 0;
     154                 :            :         } else {
     155                 :            :                 descs = cvq->desc;
     156                 :          0 :                 nr_descs = cvq->size;
     157                 :            :         }
     158                 :            : 
     159                 :            :         cnt = 0;
     160                 :            :         while (1) {
     161   [ #  #  #  # ]:          0 :                 if (unlikely(desc_idx >= nr_descs || cnt++ >= nr_descs)) {
     162                 :          0 :                         VHOST_CONFIG_LOG(dev->ifname, ERR, "Invalid ctrl descriptor chain");
     163                 :          0 :                         goto free_err;
     164                 :            :                 }
     165                 :            : 
     166         [ #  # ]:          0 :                 if (descs[desc_idx].flags & VRING_DESC_F_WRITE)
     167                 :            :                         break;
     168                 :            : 
     169                 :          0 :                 desc_len = descs[desc_idx].len;
     170         [ #  # ]:          0 :                 desc_iova = descs[desc_idx].addr;
     171                 :            : 
     172                 :            :                 desc_addr = vhost_iova_to_vva(dev, cvq, desc_iova, &desc_len, VHOST_ACCESS_RO);
     173   [ #  #  #  # ]:          0 :                 if (!desc_addr || desc_len < descs[desc_idx].len) {
     174                 :          0 :                         VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to map ctrl descriptor");
     175                 :          0 :                         goto free_err;
     176                 :            :                 }
     177                 :            : 
     178         [ #  # ]:          0 :                 memcpy(ctrl_req, (void *)(uintptr_t)desc_addr, desc_len);
     179                 :          0 :                 ctrl_req += desc_len;
     180                 :            : 
     181         [ #  # ]:          0 :                 if (!(descs[desc_idx].flags & VRING_DESC_F_NEXT))
     182                 :            :                         break;
     183                 :            : 
     184                 :          0 :                 desc_idx = descs[desc_idx].next;
     185                 :            :         }
     186                 :            : 
     187         [ #  # ]:          0 :         cvq->last_avail_idx++;
     188                 :            :         vhost_virtqueue_reconnect_log_split(cvq);
     189                 :            : 
     190         [ #  # ]:          0 :         if (dev->features & (1ULL << VIRTIO_RING_F_EVENT_IDX))
     191                 :          0 :                 vhost_avail_event(cvq) = cvq->last_avail_idx;
     192                 :            : 
     193                 :            :         return 1;
     194                 :            : 
     195                 :          0 : free_err:
     196                 :          0 :         free(ctrl_elem->ctrl_req);
     197                 :          0 : err:
     198         [ #  # ]:          0 :         cvq->last_avail_idx++;
     199                 :            :         vhost_virtqueue_reconnect_log_split(cvq);
     200                 :            : 
     201         [ #  # ]:          0 :         if (dev->features & (1ULL << VIRTIO_RING_F_EVENT_IDX))
     202                 :          0 :                 vhost_avail_event(cvq) = cvq->last_avail_idx;
     203                 :            : 
     204                 :            :         return -1;
     205                 :            : }
     206                 :            : 
     207                 :            : static uint8_t
     208                 :          0 : virtio_net_ctrl_handle_req(struct virtio_net *dev, struct virtio_net_ctrl *ctrl_req)
     209                 :            : {
     210                 :            :         uint8_t ret = VIRTIO_NET_ERR;
     211                 :            : 
     212         [ #  # ]:          0 :         if (ctrl_req->class == VIRTIO_NET_CTRL_MQ &&
     213         [ #  # ]:          0 :                         ctrl_req->command == VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET) {
     214                 :            :                 uint16_t queue_pairs;
     215                 :            :                 uint32_t i;
     216                 :            : 
     217                 :          0 :                 queue_pairs = *(uint16_t *)(uintptr_t)ctrl_req->command_data;
     218                 :          0 :                 VHOST_CONFIG_LOG(dev->ifname, INFO, "Ctrl req: MQ %u queue pairs", queue_pairs);
     219                 :            :                 ret = VIRTIO_NET_OK;
     220                 :            : 
     221         [ #  # ]:          0 :                 for (i = 0; i < dev->nr_vring; i++) {
     222                 :          0 :                         struct vhost_virtqueue *vq = dev->virtqueue[i];
     223                 :            :                         bool enable;
     224                 :            : 
     225         [ #  # ]:          0 :                         if (vq == dev->cvq)
     226                 :          0 :                                 continue;
     227                 :            : 
     228         [ #  # ]:          0 :                         if (i < queue_pairs * 2)
     229                 :            :                                 enable = true;
     230                 :            :                         else
     231                 :            :                                 enable = false;
     232                 :            : 
     233                 :          0 :                         vq->enabled = enable;
     234         [ #  # ]:          0 :                         if (dev->notify_ops->vring_state_changed)
     235                 :          0 :                                 dev->notify_ops->vring_state_changed(dev->vid, i, enable);
     236                 :            :                 }
     237                 :            :         }
     238                 :            : 
     239                 :          0 :         return ret;
     240                 :            : }
     241                 :            : 
     242                 :            : static int
     243                 :          0 : virtio_net_ctrl_push(struct virtio_net *dev, struct virtio_net_ctrl_elem *ctrl_elem)
     244                 :            : {
     245                 :          0 :         struct vhost_virtqueue *cvq = dev->cvq;
     246                 :            :         struct vring_used_elem *used_elem;
     247                 :            : 
     248                 :          0 :         used_elem = &cvq->used->ring[cvq->last_used_idx & (cvq->size - 1)];
     249                 :          0 :         used_elem->id = ctrl_elem->head_idx;
     250                 :          0 :         used_elem->len = sizeof(*ctrl_elem->desc_ack);
     251                 :            : 
     252                 :          0 :         cvq->last_used_idx++;
     253                 :            : 
     254                 :          0 :         rte_atomic_store_explicit((unsigned short __rte_atomic *)&cvq->used->idx,
     255                 :            :                 cvq->last_used_idx, rte_memory_order_release);
     256                 :            : 
     257                 :          0 :         vhost_vring_call_split(dev, dev->cvq);
     258                 :            : 
     259                 :          0 :         free(ctrl_elem->ctrl_req);
     260                 :            : 
     261                 :          0 :         return 0;
     262                 :            : }
     263                 :            : 
     264                 :            : int
     265                 :          0 : virtio_net_ctrl_handle(struct virtio_net *dev)
     266                 :            : {
     267                 :            :         int ret = 0;
     268                 :            : 
     269         [ #  # ]:          0 :         if (dev->features & (1ULL << VIRTIO_F_RING_PACKED)) {
     270                 :          0 :                 VHOST_CONFIG_LOG(dev->ifname, ERR, "Packed ring not supported yet");
     271                 :          0 :                 return -1;
     272                 :            :         }
     273                 :            : 
     274         [ #  # ]:          0 :         if (!dev->cvq) {
     275                 :          0 :                 VHOST_CONFIG_LOG(dev->ifname, ERR, "missing control queue");
     276                 :          0 :                 return -1;
     277                 :            :         }
     278                 :            : 
     279                 :          0 :         rte_rwlock_read_lock(&dev->cvq->access_lock);
     280                 :          0 :         vhost_user_iotlb_rd_lock(dev->cvq);
     281                 :            : 
     282                 :          0 :         while (1) {
     283                 :            :                 struct virtio_net_ctrl_elem ctrl_elem;
     284                 :            : 
     285                 :            :                 memset(&ctrl_elem, 0, sizeof(struct virtio_net_ctrl_elem));
     286                 :            : 
     287                 :          0 :                 ret = virtio_net_ctrl_pop(dev, dev->cvq, &ctrl_elem);
     288         [ #  # ]:          0 :                 if (ret <= 0)
     289                 :            :                         break;
     290                 :            : 
     291                 :          0 :                 *ctrl_elem.desc_ack = virtio_net_ctrl_handle_req(dev, ctrl_elem.ctrl_req);
     292                 :            : 
     293                 :          0 :                 ret = virtio_net_ctrl_push(dev, &ctrl_elem);
     294         [ #  # ]:          0 :                 if (ret < 0)
     295                 :            :                         break;
     296                 :            :         }
     297                 :            : 
     298                 :          0 :         vhost_user_iotlb_rd_unlock(dev->cvq);
     299                 :          0 :         rte_rwlock_read_unlock(&dev->cvq->access_lock);
     300                 :            : 
     301                 :          0 :         return ret;
     302                 :            : }

Generated by: LCOV version 1.14