X-Git-Url: http://git.droids-corp.org/?a=blobdiff_plain;f=drivers%2Fvdpa%2Fmlx5%2Fmlx5_vdpa_virtq.c;h=bd48460b5bedbbee1ea4aadfe79c95edc6061c26;hb=796ae7bb6a41fd87c2a975763f26775c2fe7d8b3;hp=781bccfd6b704b9fc3b29005c928d4fd3b93998c;hpb=bff735011078f90ff3f37e00413728dacc26d2e6;p=dpdk.git diff --git a/drivers/vdpa/mlx5/mlx5_vdpa_virtq.c b/drivers/vdpa/mlx5/mlx5_vdpa_virtq.c index 781bccfd6b..bd48460b5b 100644 --- a/drivers/vdpa/mlx5/mlx5_vdpa_virtq.c +++ b/drivers/vdpa/mlx5/mlx5_vdpa_virtq.c @@ -2,9 +2,12 @@ * Copyright 2019 Mellanox Technologies, Ltd */ #include +#include +#include #include #include +#include #include @@ -12,16 +15,56 @@ #include "mlx5_vdpa.h" +static void +mlx5_vdpa_virtq_handler(void *cb_arg) +{ + struct mlx5_vdpa_virtq *virtq = cb_arg; + struct mlx5_vdpa_priv *priv = virtq->priv; + uint64_t buf; + int nbytes; + + do { + nbytes = read(virtq->intr_handle.fd, &buf, 8); + if (nbytes < 0) { + if (errno == EINTR || + errno == EWOULDBLOCK || + errno == EAGAIN) + continue; + DRV_LOG(ERR, "Failed to read kickfd of virtq %d: %s", + virtq->index, strerror(errno)); + } + break; + } while (1); + rte_write32(virtq->index, priv->virtq_db_addr); + DRV_LOG(DEBUG, "Ring virtq %u doorbell.", virtq->index); +} + static int mlx5_vdpa_virtq_unset(struct mlx5_vdpa_virtq *virtq) { - int i; + unsigned int i; + int retries = MLX5_VDPA_INTR_RETRIES; + int ret = -EAGAIN; - if (virtq->virtq) { - claim_zero(mlx5_devx_cmd_destroy(virtq->virtq)); - virtq->virtq = NULL; + if (virtq->intr_handle.fd != -1) { + while (retries-- && ret == -EAGAIN) { + ret = rte_intr_callback_unregister(&virtq->intr_handle, + mlx5_vdpa_virtq_handler, + virtq); + if (ret == -EAGAIN) { + DRV_LOG(DEBUG, "Try again to unregister fd %d " + "of virtq %d interrupt, retries = %d.", + virtq->intr_handle.fd, + (int)virtq->index, retries); + usleep(MLX5_VDPA_INTR_RETRIES_USEC); + } + } + virtq->intr_handle.fd = -1; } - for (i = 0; i < 3; ++i) { + if (virtq->virtq) + claim_zero(mlx5_devx_cmd_destroy(virtq->virtq)); + virtq->virtq = NULL; + for (i = 0; i < RTE_DIM(virtq->umems); ++i) { if (virtq->umems[i].obj) claim_zero(mlx5_glue->devx_umem_dereg (virtq->umems[i].obj)); @@ -37,18 +80,12 @@ mlx5_vdpa_virtq_unset(struct mlx5_vdpa_virtq *virtq) void mlx5_vdpa_virtqs_release(struct mlx5_vdpa_priv *priv) { - struct mlx5_vdpa_virtq *entry; - struct mlx5_vdpa_virtq *next; - - entry = SLIST_FIRST(&priv->virtq_list); - while (entry) { - next = SLIST_NEXT(entry, next); - mlx5_vdpa_virtq_unset(entry); - SLIST_REMOVE(&priv->virtq_list, entry, mlx5_vdpa_virtq, next); - rte_free(entry); - entry = next; - } - SLIST_INIT(&priv->virtq_list); + int i; + + for (i = 0; i < priv->nr_virtqs; i++) { + mlx5_vdpa_virtq_unset(&priv->virtqs[i]); + priv->virtqs[i].enable = 0; + } if (priv->tis) { claim_zero(mlx5_devx_cmd_destroy(priv->tis)); priv->tis = NULL; @@ -57,6 +94,51 @@ mlx5_vdpa_virtqs_release(struct mlx5_vdpa_priv *priv) claim_zero(mlx5_devx_cmd_destroy(priv->td)); priv->td = NULL; } + if (priv->virtq_db_addr) { + claim_zero(munmap(priv->virtq_db_addr, priv->var->length)); + priv->virtq_db_addr = NULL; + } + priv->features = 0; + priv->nr_virtqs = 0; +} + +int +mlx5_vdpa_virtq_modify(struct mlx5_vdpa_virtq *virtq, int state) +{ + struct mlx5_devx_virtq_attr attr = { + .type = MLX5_VIRTQ_MODIFY_TYPE_STATE, + .state = state ? MLX5_VIRTQ_STATE_RDY : + MLX5_VIRTQ_STATE_SUSPEND, + .queue_index = virtq->index, + }; + + return mlx5_devx_cmd_modify_virtq(virtq->virtq, &attr); +} + +int +mlx5_vdpa_virtq_stop(struct mlx5_vdpa_priv *priv, int index) +{ + struct mlx5_devx_virtq_attr attr = {0}; + struct mlx5_vdpa_virtq *virtq = &priv->virtqs[index]; + int ret = mlx5_vdpa_virtq_modify(virtq, 0); + + if (ret) + return -1; + if (mlx5_devx_cmd_query_virtq(virtq->virtq, &attr)) { + DRV_LOG(ERR, "Failed to query virtq %d.", index); + return -1; + } + DRV_LOG(INFO, "Query vid %d vring %d: hw_available_idx=%d, " + "hw_used_index=%d", priv->vid, index, + attr.hw_available_index, attr.hw_used_index); + ret = rte_vhost_set_vring_base(priv->vid, index, + attr.hw_available_index, + attr.hw_used_index); + if (ret) { + DRV_LOG(ERR, "Failed to set virtq %d base.", index); + return -1; + } + return 0; } static uint64_t @@ -78,14 +160,14 @@ mlx5_vdpa_hva_to_gpa(struct rte_vhost_memory *mem, uint64_t hva) } static int -mlx5_vdpa_virtq_setup(struct mlx5_vdpa_priv *priv, - struct mlx5_vdpa_virtq *virtq, int index) +mlx5_vdpa_virtq_setup(struct mlx5_vdpa_priv *priv, int index) { + struct mlx5_vdpa_virtq *virtq = &priv->virtqs[index]; struct rte_vhost_vring vq; struct mlx5_devx_virtq_attr attr = {0}; uint64_t gpa; int ret; - int i; + unsigned int i; uint16_t last_avail_idx; uint16_t last_used_idx; @@ -94,6 +176,14 @@ mlx5_vdpa_virtq_setup(struct mlx5_vdpa_priv *priv, return -1; virtq->index = index; virtq->vq_size = vq.size; + attr.tso_ipv4 = !!(priv->features & (1ULL << VIRTIO_NET_F_HOST_TSO4)); + attr.tso_ipv6 = !!(priv->features & (1ULL << VIRTIO_NET_F_HOST_TSO6)); + attr.tx_csum = !!(priv->features & (1ULL << VIRTIO_NET_F_CSUM)); + attr.rx_csum = !!(priv->features & (1ULL << VIRTIO_NET_F_GUEST_CSUM)); + attr.virtio_version_1_0 = !!(priv->features & (1ULL << + VIRTIO_F_VERSION_1)); + attr.type = (priv->features & (1ULL << VIRTIO_F_RING_PACKED)) ? + MLX5_VIRTQ_TYPE_PACKED : MLX5_VIRTQ_TYPE_SPLIT; /* * No need event QPs creation when the guest in poll mode or when the * capability allows it. @@ -116,7 +206,7 @@ mlx5_vdpa_virtq_setup(struct mlx5_vdpa_priv *priv, " need event QPs and event mechanism.", index); } /* Setup 3 UMEMs for each virtq. */ - for (i = 0; i < 3; ++i) { + for (i = 0; i < RTE_DIM(virtq->umems); ++i) { virtq->umems[i].size = priv->caps.umems[i].a * vq.size + priv->caps.umems[i].b; virtq->umems[i].buf = rte_zmalloc(__func__, @@ -139,28 +229,40 @@ mlx5_vdpa_virtq_setup(struct mlx5_vdpa_priv *priv, attr.umems[i].offset = 0; attr.umems[i].size = virtq->umems[i].size; } - gpa = mlx5_vdpa_hva_to_gpa(priv->vmem, (uint64_t)(uintptr_t)vq.desc); - if (!gpa) { - DRV_LOG(ERR, "Fail to get GPA for descriptor ring."); - goto error; - } - attr.desc_addr = gpa; - gpa = mlx5_vdpa_hva_to_gpa(priv->vmem, (uint64_t)(uintptr_t)vq.used); - if (!gpa) { - DRV_LOG(ERR, "Fail to get GPA for used ring."); - goto error; - } - attr.used_addr = gpa; - gpa = mlx5_vdpa_hva_to_gpa(priv->vmem, (uint64_t)(uintptr_t)vq.avail); - if (!gpa) { - DRV_LOG(ERR, "Fail to get GPA for available ring."); - goto error; + if (attr.type == MLX5_VIRTQ_TYPE_SPLIT) { + gpa = mlx5_vdpa_hva_to_gpa(priv->vmem, + (uint64_t)(uintptr_t)vq.desc); + if (!gpa) { + DRV_LOG(ERR, "Failed to get descriptor ring GPA."); + goto error; + } + attr.desc_addr = gpa; + gpa = mlx5_vdpa_hva_to_gpa(priv->vmem, + (uint64_t)(uintptr_t)vq.used); + if (!gpa) { + DRV_LOG(ERR, "Failed to get GPA for used ring."); + goto error; + } + attr.used_addr = gpa; + gpa = mlx5_vdpa_hva_to_gpa(priv->vmem, + (uint64_t)(uintptr_t)vq.avail); + if (!gpa) { + DRV_LOG(ERR, "Failed to get GPA for available ring."); + goto error; + } + attr.available_addr = gpa; } - attr.available_addr = gpa; - rte_vhost_get_vring_base(priv->vid, index, &last_avail_idx, + ret = rte_vhost_get_vring_base(priv->vid, index, &last_avail_idx, &last_used_idx); - DRV_LOG(INFO, "vid %d: Init last_avail_idx=%d, last_used_idx=%d for " - "virtq %d.", priv->vid, last_avail_idx, last_used_idx, index); + if (ret) { + last_avail_idx = 0; + last_used_idx = 0; + DRV_LOG(WARNING, "Couldn't get vring base, idx are set to 0"); + } else { + DRV_LOG(INFO, "vid %d: Init last_avail_idx=%d, last_used_idx=%d for " + "virtq %d.", priv->vid, last_avail_idx, + last_used_idx, index); + } attr.hw_available_index = last_avail_idx; attr.hw_used_index = last_used_idx; attr.q_size = vq.size; @@ -168,22 +270,126 @@ mlx5_vdpa_virtq_setup(struct mlx5_vdpa_priv *priv, attr.tis_id = priv->tis->id; attr.queue_index = index; virtq->virtq = mlx5_devx_cmd_create_virtq(priv->ctx, &attr); + virtq->priv = priv; if (!virtq->virtq) goto error; + if (mlx5_vdpa_virtq_modify(virtq, 1)) + goto error; + virtq->priv = priv; + rte_write32(virtq->index, priv->virtq_db_addr); + /* Setup doorbell mapping. */ + virtq->intr_handle.fd = vq.kickfd; + if (virtq->intr_handle.fd == -1) { + DRV_LOG(WARNING, "Virtq %d kickfd is invalid.", index); + if (!priv->direct_notifier) { + DRV_LOG(ERR, "Virtq %d cannot be notified.", index); + goto error; + } + } else { + virtq->intr_handle.type = RTE_INTR_HANDLE_EXT; + if (rte_intr_callback_register(&virtq->intr_handle, + mlx5_vdpa_virtq_handler, + virtq)) { + virtq->intr_handle.fd = -1; + DRV_LOG(ERR, "Failed to register virtq %d interrupt.", + index); + goto error; + } else { + DRV_LOG(DEBUG, "Register fd %d interrupt for virtq %d.", + virtq->intr_handle.fd, index); + } + } return 0; error: mlx5_vdpa_virtq_unset(virtq); return -1; } +static int +mlx5_vdpa_features_validate(struct mlx5_vdpa_priv *priv) +{ + if (priv->features & (1ULL << VIRTIO_F_RING_PACKED)) { + if (!(priv->caps.virtio_queue_type & (1 << + MLX5_VIRTQ_TYPE_PACKED))) { + DRV_LOG(ERR, "Failed to configur PACKED mode for vdev " + "%d - it was not reported by HW/driver" + " capability.", priv->vid); + return -ENOTSUP; + } + } + if (priv->features & (1ULL << VIRTIO_NET_F_HOST_TSO4)) { + if (!priv->caps.tso_ipv4) { + DRV_LOG(ERR, "Failed to enable TSO4 for vdev %d - TSO4" + " was not reported by HW/driver capability.", + priv->vid); + return -ENOTSUP; + } + } + if (priv->features & (1ULL << VIRTIO_NET_F_HOST_TSO6)) { + if (!priv->caps.tso_ipv6) { + DRV_LOG(ERR, "Failed to enable TSO6 for vdev %d - TSO6" + " was not reported by HW/driver capability.", + priv->vid); + return -ENOTSUP; + } + } + if (priv->features & (1ULL << VIRTIO_NET_F_CSUM)) { + if (!priv->caps.tx_csum) { + DRV_LOG(ERR, "Failed to enable CSUM for vdev %d - CSUM" + " was not reported by HW/driver capability.", + priv->vid); + return -ENOTSUP; + } + } + if (priv->features & (1ULL << VIRTIO_NET_F_GUEST_CSUM)) { + if (!priv->caps.rx_csum) { + DRV_LOG(ERR, "Failed to enable GUEST CSUM for vdev %d" + " GUEST CSUM was not reported by HW/driver " + "capability.", priv->vid); + return -ENOTSUP; + } + } + if (priv->features & (1ULL << VIRTIO_F_VERSION_1)) { + if (!priv->caps.virtio_version_1_0) { + DRV_LOG(ERR, "Failed to enable version 1 for vdev %d " + "version 1 was not reported by HW/driver" + " capability.", priv->vid); + return -ENOTSUP; + } + } + return 0; +} + int mlx5_vdpa_virtqs_prepare(struct mlx5_vdpa_priv *priv) { struct mlx5_devx_tis_attr tis_attr = {0}; - struct mlx5_vdpa_virtq *virtq; uint32_t i; uint16_t nr_vring = rte_vhost_get_vring_num(priv->vid); + int ret = rte_vhost_get_negotiated_features(priv->vid, &priv->features); + if (ret || mlx5_vdpa_features_validate(priv)) { + DRV_LOG(ERR, "Failed to configure negotiated features."); + return -1; + } + if (nr_vring > priv->caps.max_num_virtio_queues * 2) { + DRV_LOG(ERR, "Do not support more than %d virtqs(%d).", + (int)priv->caps.max_num_virtio_queues * 2, + (int)nr_vring); + return -1; + } + /* Always map the entire page. */ + priv->virtq_db_addr = mmap(NULL, priv->var->length, PROT_READ | + PROT_WRITE, MAP_SHARED, priv->ctx->cmd_fd, + priv->var->mmap_off); + if (priv->virtq_db_addr == MAP_FAILED) { + DRV_LOG(ERR, "Failed to map doorbell page %u.", errno); + priv->virtq_db_addr = NULL; + goto error; + } else { + DRV_LOG(DEBUG, "VAR address of doorbell mapping is %p.", + priv->virtq_db_addr); + } priv->td = mlx5_devx_cmd_create_td(priv->ctx); if (!priv->td) { DRV_LOG(ERR, "Failed to create transport domain."); @@ -195,18 +401,57 @@ mlx5_vdpa_virtqs_prepare(struct mlx5_vdpa_priv *priv) DRV_LOG(ERR, "Failed to create TIS."); goto error; } + priv->nr_virtqs = nr_vring; for (i = 0; i < nr_vring; i++) { - virtq = rte_zmalloc(__func__, sizeof(*virtq), 0); - if (!virtq || mlx5_vdpa_virtq_setup(priv, virtq, i)) { - if (virtq) - rte_free(virtq); + claim_zero(rte_vhost_enable_guest_notification(priv->vid, i, + 1)); + if (mlx5_vdpa_virtq_setup(priv, i)) goto error; - } - SLIST_INSERT_HEAD(&priv->virtq_list, virtq, next); } - priv->nr_virtqs = nr_vring; return 0; error: mlx5_vdpa_virtqs_release(priv); return -1; } + +int +mlx5_vdpa_virtq_enable(struct mlx5_vdpa_priv *priv, int index, int enable) +{ + struct mlx5_vdpa_virtq *virtq = &priv->virtqs[index]; + int ret; + + DRV_LOG(INFO, "Update virtq %d status %sable -> %sable.", index, + virtq->enable ? "en" : "dis", enable ? "en" : "dis"); + if (virtq->enable == !!enable) + return 0; + if (!priv->configured) { + virtq->enable = !!enable; + return 0; + } + if (enable) { + /* Configuration might have been updated - reconfigure virtq. */ + if (virtq->virtq) { + ret = mlx5_vdpa_virtq_stop(priv, index); + if (ret) + DRV_LOG(WARNING, "Failed to stop virtq %d.", + index); + mlx5_vdpa_virtq_unset(virtq); + } + ret = mlx5_vdpa_virtq_setup(priv, index); + if (ret) { + DRV_LOG(ERR, "Failed to setup virtq %d.", index); + return ret; + /* The only case virtq can stay invalid. */ + } + } + virtq->enable = !!enable; + if (is_virtq_recvq(virtq->index, priv->nr_virtqs)) { + /* Need to add received virtq to the RQT table of the TIRs. */ + ret = mlx5_vdpa_steer_update(priv); + if (ret) { + virtq->enable = !enable; + return ret; + } + } + return 0; +}