X-Git-Url: http://git.droids-corp.org/?a=blobdiff_plain;f=drivers%2Fvdpa%2Fmlx5%2Fmlx5_vdpa.c;h=9ea032d57b5906c02a666176f88e16aa2e5682df;hb=81a6b7fe068f64d1381ea4d6fce5088a1bcc7f81;hp=97d914a473246cd18e69d4fabb1dfd90041ee0e8;hpb=58b4a2b13ec0d28a3ffa70d79dff21e9a16b7ae7;p=dpdk.git diff --git a/drivers/vdpa/mlx5/mlx5_vdpa.c b/drivers/vdpa/mlx5/mlx5_vdpa.c index 97d914a473..9ea032d57b 100644 --- a/drivers/vdpa/mlx5/mlx5_vdpa.c +++ b/drivers/vdpa/mlx5/mlx5_vdpa.c @@ -2,12 +2,18 @@ * Copyright 2019 Mellanox Technologies, Ltd */ #include +#include +#include +#include +#include +#include #include #include #include #include #include +#include #include #include @@ -24,14 +30,19 @@ (1ULL << VIRTIO_NET_F_MQ) | \ (1ULL << VIRTIO_NET_F_GUEST_ANNOUNCE) | \ (1ULL << VIRTIO_F_ORDER_PLATFORM) | \ - (1ULL << VHOST_F_LOG_ALL)) + (1ULL << VHOST_F_LOG_ALL) | \ + (1ULL << VIRTIO_NET_F_MTU)) #define MLX5_VDPA_PROTOCOL_FEATURES \ ((1ULL << VHOST_USER_PROTOCOL_F_SLAVE_REQ) | \ (1ULL << VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD) | \ (1ULL << VHOST_USER_PROTOCOL_F_HOST_NOTIFIER) | \ (1ULL << VHOST_USER_PROTOCOL_F_LOG_SHMFD) | \ - (1ULL << VHOST_USER_PROTOCOL_F_MQ)) + (1ULL << VHOST_USER_PROTOCOL_F_MQ) | \ + (1ULL << VHOST_USER_PROTOCOL_F_NET_MTU)) + +#define MLX5_VDPA_MAX_RETRIES 20 +#define MLX5_VDPA_USEC 1000 TAILQ_HEAD(mlx5_vdpa_privs, mlx5_vdpa_priv) priv_list = TAILQ_HEAD_INITIALIZER(priv_list); @@ -39,21 +50,21 @@ static pthread_mutex_t priv_list_lock = PTHREAD_MUTEX_INITIALIZER; int mlx5_vdpa_logtype; static struct mlx5_vdpa_priv * -mlx5_vdpa_find_priv_resource_by_did(int did) +mlx5_vdpa_find_priv_resource_by_vdev(struct rte_vdpa_device *vdev) { struct mlx5_vdpa_priv *priv; int found = 0; pthread_mutex_lock(&priv_list_lock); TAILQ_FOREACH(priv, &priv_list, next) { - if (did == priv->id) { + if (vdev == priv->vdev) { found = 1; break; } } pthread_mutex_unlock(&priv_list_lock); if (!found) { - DRV_LOG(ERR, "Invalid device id: %d.", did); + DRV_LOG(ERR, "Invalid vDPA device: %s.", vdev->device->name); rte_errno = EINVAL; return NULL; } @@ -61,12 +72,13 @@ mlx5_vdpa_find_priv_resource_by_did(int did) } static int -mlx5_vdpa_get_queue_num(int did, uint32_t *queue_num) +mlx5_vdpa_get_queue_num(struct rte_vdpa_device *vdev, uint32_t *queue_num) { - struct mlx5_vdpa_priv *priv = mlx5_vdpa_find_priv_resource_by_did(did); + struct mlx5_vdpa_priv *priv = + mlx5_vdpa_find_priv_resource_by_vdev(vdev); if (priv == NULL) { - DRV_LOG(ERR, "Invalid device id: %d.", did); + DRV_LOG(ERR, "Invalid vDPA device: %s.", vdev->device->name); return -1; } *queue_num = priv->caps.max_num_virtio_queues; @@ -74,12 +86,13 @@ mlx5_vdpa_get_queue_num(int did, uint32_t *queue_num) } static int -mlx5_vdpa_get_vdpa_features(int did, uint64_t *features) +mlx5_vdpa_get_vdpa_features(struct rte_vdpa_device *vdev, uint64_t *features) { - struct mlx5_vdpa_priv *priv = mlx5_vdpa_find_priv_resource_by_did(did); + struct mlx5_vdpa_priv *priv = + mlx5_vdpa_find_priv_resource_by_vdev(vdev); if (priv == NULL) { - DRV_LOG(ERR, "Invalid device id: %d.", did); + DRV_LOG(ERR, "Invalid vDPA device: %s.", vdev->device->name); return -1; } *features = MLX5_VDPA_DEFAULT_FEATURES; @@ -99,12 +112,14 @@ mlx5_vdpa_get_vdpa_features(int did, uint64_t *features) } static int -mlx5_vdpa_get_protocol_features(int did, uint64_t *features) +mlx5_vdpa_get_protocol_features(struct rte_vdpa_device *vdev, + uint64_t *features) { - struct mlx5_vdpa_priv *priv = mlx5_vdpa_find_priv_resource_by_did(did); + struct mlx5_vdpa_priv *priv = + mlx5_vdpa_find_priv_resource_by_vdev(vdev); if (priv == NULL) { - DRV_LOG(ERR, "Invalid device id: %d.", did); + DRV_LOG(ERR, "Invalid vDPA device: %s.", vdev->device->name); return -1; } *features = MLX5_VDPA_PROTOCOL_FEATURES; @@ -114,35 +129,58 @@ mlx5_vdpa_get_protocol_features(int did, uint64_t *features) static int mlx5_vdpa_set_vring_state(int vid, int vring, int state) { - int did = rte_vhost_get_vdpa_device_id(vid); - struct mlx5_vdpa_priv *priv = mlx5_vdpa_find_priv_resource_by_did(did); - struct mlx5_vdpa_virtq *virtq = NULL; + struct rte_vdpa_device *vdev = rte_vdpa_get_device( + rte_vhost_get_vdpa_device_id(vid)); + struct mlx5_vdpa_priv *priv = + mlx5_vdpa_find_priv_resource_by_vdev(vdev); if (priv == NULL) { - DRV_LOG(ERR, "Invalid device id: %d.", did); + DRV_LOG(ERR, "Invalid vDPA device: %s.", vdev->device->name); return -EINVAL; } - SLIST_FOREACH(virtq, &priv->virtq_list, next) - if (virtq->index == vring) - break; - if (!virtq) { - DRV_LOG(ERR, "Invalid or unconfigured vring id: %d.", vring); - return -EINVAL; + if (vring >= (int)priv->caps.max_num_virtio_queues * 2) { + DRV_LOG(ERR, "Too big vring id: %d.", vring); + return -E2BIG; + } + return mlx5_vdpa_virtq_enable(priv, vring, state); +} + +static int +mlx5_vdpa_direct_db_prepare(struct mlx5_vdpa_priv *priv) +{ + int ret; + + if (priv->direct_notifier) { + ret = rte_vhost_host_notifier_ctrl(priv->vid, false); + if (ret != 0) { + DRV_LOG(INFO, "Direct HW notifier FD cannot be " + "destroyed for device %d: %d.", priv->vid, ret); + return -1; + } + priv->direct_notifier = 0; } - return mlx5_vdpa_virtq_enable(virtq, state); + ret = rte_vhost_host_notifier_ctrl(priv->vid, true); + if (ret != 0) + DRV_LOG(INFO, "Direct HW notifier FD cannot be configured for" + " device %d: %d.", priv->vid, ret); + else + priv->direct_notifier = 1; + return 0; } static int mlx5_vdpa_features_set(int vid) { - int did = rte_vhost_get_vdpa_device_id(vid); - struct mlx5_vdpa_priv *priv = mlx5_vdpa_find_priv_resource_by_did(did); + struct rte_vdpa_device *vdev = rte_vdpa_get_device( + rte_vhost_get_vdpa_device_id(vid)); + struct mlx5_vdpa_priv *priv = + mlx5_vdpa_find_priv_resource_by_vdev(vdev); uint64_t log_base, log_size; uint64_t features; int ret; if (priv == NULL) { - DRV_LOG(ERR, "Invalid device id: %d.", did); + DRV_LOG(ERR, "Invalid vDPA device: %s.", vdev->device->name); return -EINVAL; } ret = rte_vhost_get_negotiated_features(vid, &features); @@ -171,37 +209,126 @@ mlx5_vdpa_features_set(int vid) return 0; } +static int +mlx5_vdpa_pd_create(struct mlx5_vdpa_priv *priv) +{ +#ifdef HAVE_IBV_FLOW_DV_SUPPORT + priv->pd = mlx5_glue->alloc_pd(priv->ctx); + if (priv->pd == NULL) { + DRV_LOG(ERR, "Failed to allocate PD."); + return errno ? -errno : -ENOMEM; + } + struct mlx5dv_obj obj; + struct mlx5dv_pd pd_info; + int ret = 0; + + obj.pd.in = priv->pd; + obj.pd.out = &pd_info; + ret = mlx5_glue->dv_init_obj(&obj, MLX5DV_OBJ_PD); + if (ret) { + DRV_LOG(ERR, "Fail to get PD object info."); + mlx5_glue->dealloc_pd(priv->pd); + priv->pd = NULL; + return -errno; + } + priv->pdn = pd_info.pdn; + return 0; +#else + (void)priv; + DRV_LOG(ERR, "Cannot get pdn - no DV support."); + return -ENOTSUP; +#endif /* HAVE_IBV_FLOW_DV_SUPPORT */ +} + +static int +mlx5_vdpa_mtu_set(struct mlx5_vdpa_priv *priv) +{ + struct ifreq request; + uint16_t vhost_mtu = 0; + uint16_t kern_mtu = 0; + int ret = rte_vhost_get_mtu(priv->vid, &vhost_mtu); + int sock; + int retries = MLX5_VDPA_MAX_RETRIES; + + if (ret) { + DRV_LOG(DEBUG, "Cannot get vhost MTU - %d.", ret); + return ret; + } + if (!vhost_mtu) { + DRV_LOG(DEBUG, "Vhost MTU is 0."); + return ret; + } + ret = mlx5_get_ifname_sysfs(priv->ctx->device->ibdev_path, + request.ifr_name); + if (ret) { + DRV_LOG(DEBUG, "Cannot get kernel IF name - %d.", ret); + return ret; + } + sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); + if (sock == -1) { + DRV_LOG(DEBUG, "Cannot open IF socket."); + return sock; + } + while (retries--) { + ret = ioctl(sock, SIOCGIFMTU, &request); + if (ret == -1) + break; + kern_mtu = request.ifr_mtu; + DRV_LOG(DEBUG, "MTU: current %d requested %d.", (int)kern_mtu, + (int)vhost_mtu); + if (kern_mtu == vhost_mtu) + break; + request.ifr_mtu = vhost_mtu; + ret = ioctl(sock, SIOCSIFMTU, &request); + if (ret == -1) + break; + request.ifr_mtu = 0; + usleep(MLX5_VDPA_USEC); + } + close(sock); + return kern_mtu == vhost_mtu ? 0 : -1; +} + static int mlx5_vdpa_dev_close(int vid) { - int did = rte_vhost_get_vdpa_device_id(vid); - struct mlx5_vdpa_priv *priv = mlx5_vdpa_find_priv_resource_by_did(did); + struct rte_vdpa_device *vdev = rte_vdpa_get_device( + rte_vhost_get_vdpa_device_id(vid)); + struct mlx5_vdpa_priv *priv = + mlx5_vdpa_find_priv_resource_by_vdev(vdev); int ret = 0; if (priv == NULL) { - DRV_LOG(ERR, "Invalid device id: %d.", did); + DRV_LOG(ERR, "Invalid vDPA device: %s.", vdev->device->name); return -1; } if (priv->configured) ret |= mlx5_vdpa_lm_log(priv); mlx5_vdpa_cqe_event_unset(priv); - ret |= mlx5_vdpa_steer_unset(priv); + mlx5_vdpa_steer_unset(priv); mlx5_vdpa_virtqs_release(priv); mlx5_vdpa_event_qp_global_release(priv); mlx5_vdpa_mem_dereg(priv); + if (priv->pd) { + claim_zero(mlx5_glue->dealloc_pd(priv->pd)); + priv->pd = NULL; + } priv->configured = 0; priv->vid = 0; + DRV_LOG(INFO, "vDPA device %d was closed.", vid); return ret; } static int mlx5_vdpa_dev_config(int vid) { - int did = rte_vhost_get_vdpa_device_id(vid); - struct mlx5_vdpa_priv *priv = mlx5_vdpa_find_priv_resource_by_did(did); + struct rte_vdpa_device *vdev = rte_vdpa_get_device( + rte_vhost_get_vdpa_device_id(vid)); + struct mlx5_vdpa_priv *priv = + mlx5_vdpa_find_priv_resource_by_vdev(vdev); if (priv == NULL) { - DRV_LOG(ERR, "Invalid device id: %d.", did); + DRV_LOG(ERR, "Invalid vDPA device: %s.", vdev->device->name); return -EINVAL; } if (priv->configured && mlx5_vdpa_dev_close(vid)) { @@ -209,15 +336,146 @@ mlx5_vdpa_dev_config(int vid) return -1; } priv->vid = vid; - if (mlx5_vdpa_mem_register(priv) || mlx5_vdpa_virtqs_prepare(priv) || - mlx5_vdpa_steer_setup(priv) || mlx5_vdpa_cqe_event_setup(priv)) { + if (mlx5_vdpa_mtu_set(priv)) + DRV_LOG(WARNING, "MTU cannot be set on device %s.", + vdev->device->name); + if (mlx5_vdpa_pd_create(priv) || mlx5_vdpa_mem_register(priv) || + mlx5_vdpa_direct_db_prepare(priv) || + mlx5_vdpa_virtqs_prepare(priv) || mlx5_vdpa_steer_setup(priv) || + mlx5_vdpa_cqe_event_setup(priv)) { mlx5_vdpa_dev_close(vid); return -1; } priv->configured = 1; + DRV_LOG(INFO, "vDPA device %d was configured.", vid); + return 0; +} + +static int +mlx5_vdpa_get_device_fd(int vid) +{ + struct rte_vdpa_device *vdev = rte_vdpa_get_device( + rte_vhost_get_vdpa_device_id(vid)); + struct mlx5_vdpa_priv *priv = + mlx5_vdpa_find_priv_resource_by_vdev(vdev); + + if (priv == NULL) { + DRV_LOG(ERR, "Invalid vDPA device: %s.", vdev->device->name); + return -EINVAL; + } + return priv->ctx->cmd_fd; +} + +static int +mlx5_vdpa_get_notify_area(int vid, int qid, uint64_t *offset, uint64_t *size) +{ + struct rte_vdpa_device *vdev = rte_vdpa_get_device( + rte_vhost_get_vdpa_device_id(vid)); + struct mlx5_vdpa_priv *priv = + mlx5_vdpa_find_priv_resource_by_vdev(vdev); + + RTE_SET_USED(qid); + if (priv == NULL) { + DRV_LOG(ERR, "Invalid vDPA device: %s.", vdev->device->name); + return -EINVAL; + } + if (!priv->var) { + DRV_LOG(ERR, "VAR was not created for device %s, is the device" + " configured?.", vdev->device->name); + return -EINVAL; + } + *offset = priv->var->mmap_off; + *size = priv->var->length; return 0; } +static int +mlx5_vdpa_get_stats_names(struct rte_vdpa_device *vdev, + struct rte_vdpa_stat_name *stats_names, + unsigned int size) +{ + static const char *mlx5_vdpa_stats_names[MLX5_VDPA_STATS_MAX] = { + "received_descriptors", + "completed_descriptors", + "bad descriptor errors", + "exceed max chain", + "invalid buffer", + "completion errors", + }; + struct mlx5_vdpa_priv *priv = + mlx5_vdpa_find_priv_resource_by_vdev(vdev); + unsigned int i; + + if (priv == NULL) { + DRV_LOG(ERR, "Invalid device: %s.", vdev->device->name); + return -ENODEV; + } + if (!stats_names) + return MLX5_VDPA_STATS_MAX; + size = RTE_MIN(size, (unsigned int)MLX5_VDPA_STATS_MAX); + for (i = 0; i < size; ++i) + strlcpy(stats_names[i].name, mlx5_vdpa_stats_names[i], + RTE_VDPA_STATS_NAME_SIZE); + return size; +} + +static int +mlx5_vdpa_get_stats(struct rte_vdpa_device *vdev, int qid, + struct rte_vdpa_stat *stats, unsigned int n) +{ + struct mlx5_vdpa_priv *priv = + mlx5_vdpa_find_priv_resource_by_vdev(vdev); + + if (priv == NULL) { + DRV_LOG(ERR, "Invalid device: %s.", vdev->device->name); + return -ENODEV; + } + if (!priv->configured) { + DRV_LOG(ERR, "Device %s was not configured.", + vdev->device->name); + return -ENODATA; + } + if (qid >= (int)priv->nr_virtqs) { + DRV_LOG(ERR, "Too big vring id: %d for device %s.", qid, + vdev->device->name); + return -E2BIG; + } + if (!priv->caps.queue_counters_valid) { + DRV_LOG(ERR, "Virtq statistics is not supported for device %s.", + vdev->device->name); + return -ENOTSUP; + } + return mlx5_vdpa_virtq_stats_get(priv, qid, stats, n); +} + +static int +mlx5_vdpa_reset_stats(struct rte_vdpa_device *vdev, int qid) +{ + struct mlx5_vdpa_priv *priv = + mlx5_vdpa_find_priv_resource_by_vdev(vdev); + + if (priv == NULL) { + DRV_LOG(ERR, "Invalid device: %s.", vdev->device->name); + return -ENODEV; + } + if (!priv->configured) { + DRV_LOG(ERR, "Device %s was not configured.", + vdev->device->name); + return -ENODATA; + } + if (qid >= (int)priv->nr_virtqs) { + DRV_LOG(ERR, "Too big vring id: %d for device %s.", qid, + vdev->device->name); + return -E2BIG; + } + if (!priv->caps.queue_counters_valid) { + DRV_LOG(ERR, "Virtq statistics is not supported for device %s.", + vdev->device->name); + return -ENOTSUP; + } + return mlx5_vdpa_virtq_stats_reset(priv, qid); +} + static struct rte_vdpa_dev_ops mlx5_vdpa_ops = { .get_queue_num = mlx5_vdpa_get_queue_num, .get_features = mlx5_vdpa_get_vdpa_features, @@ -228,8 +486,11 @@ static struct rte_vdpa_dev_ops mlx5_vdpa_ops = { .set_features = mlx5_vdpa_features_set, .migration_done = NULL, .get_vfio_group_fd = NULL, - .get_vfio_device_fd = NULL, - .get_notify_area = NULL, + .get_vfio_device_fd = mlx5_vdpa_get_device_fd, + .get_notify_area = mlx5_vdpa_get_notify_area, + .get_stats_names = mlx5_vdpa_get_stats_names, + .get_stats = mlx5_vdpa_get_stats, + .reset_stats = mlx5_vdpa_reset_stats, }; static struct ibv_device * @@ -249,7 +510,7 @@ mlx5_vdpa_get_ib_device_match(struct rte_pci_addr *addr) DRV_LOG(DEBUG, "Checking device \"%s\"..", ibv_list[n]->name); if (mlx5_dev_to_pci_addr(ibv_list[n]->ibdev_path, &pci_addr)) continue; - if (memcmp(addr, &pci_addr, sizeof(pci_addr))) + if (rte_pci_addr_cmp(addr, &pci_addr)) continue; ibv_match = ibv_list[n]; break; @@ -338,8 +599,6 @@ close: return ret; } -#define MLX5_VDPA_MAX_RETRIES 20 -#define MLX5_VDPA_USEC 1000 static int mlx5_vdpa_roce_disable(struct rte_pci_addr *addr, struct ibv_device **ibv) { @@ -422,47 +681,56 @@ mlx5_vdpa_pci_probe(struct rte_pci_driver *pci_drv __rte_unused, rte_errno = ENODEV; return -rte_errno; } - priv = rte_zmalloc("mlx5 vDPA device private", sizeof(*priv), + ret = mlx5_devx_cmd_query_hca_attr(ctx, &attr); + if (ret) { + DRV_LOG(ERR, "Unable to read HCA capabilities."); + rte_errno = ENOTSUP; + goto error; + } else if (!attr.vdpa.valid || !attr.vdpa.max_num_virtio_queues) { + DRV_LOG(ERR, "Not enough capabilities to support vdpa, maybe " + "old FW/OFED version?"); + rte_errno = ENOTSUP; + goto error; + } + if (!attr.vdpa.queue_counters_valid) + DRV_LOG(DEBUG, "No capability to support virtq statistics."); + priv = rte_zmalloc("mlx5 vDPA device private", sizeof(*priv) + + sizeof(struct mlx5_vdpa_virtq) * + attr.vdpa.max_num_virtio_queues * 2, RTE_CACHE_LINE_SIZE); if (!priv) { DRV_LOG(ERR, "Failed to allocate private memory."); rte_errno = ENOMEM; goto error; } - ret = mlx5_devx_cmd_query_hca_attr(ctx, &attr); - if (ret) { - DRV_LOG(ERR, "Unable to read HCA capabilities."); - rte_errno = ENOTSUP; + priv->caps = attr.vdpa; + priv->log_max_rqt_size = attr.log_max_rqt_size; + priv->ctx = ctx; + priv->pci_dev = pci_dev; + priv->var = mlx5_glue->dv_alloc_var(ctx, 0); + if (!priv->var) { + DRV_LOG(ERR, "Failed to allocate VAR %u.\n", errno); goto error; - } else { - if (!attr.vdpa.valid || !attr.vdpa.max_num_virtio_queues) { - DRV_LOG(ERR, "Not enough capabilities to support vdpa," - " maybe old FW/OFED version?"); - rte_errno = ENOTSUP; - goto error; - } - priv->caps = attr.vdpa; - priv->log_max_rqt_size = attr.log_max_rqt_size; } - priv->ctx = ctx; - priv->dev_addr.pci_addr = pci_dev->addr; - priv->dev_addr.type = PCI_ADDR; - priv->id = rte_vdpa_register_device(&priv->dev_addr, &mlx5_vdpa_ops); - if (priv->id < 0) { + priv->vdev = rte_vdpa_register_device(&pci_dev->device, + &mlx5_vdpa_ops); + if (priv->vdev == NULL) { DRV_LOG(ERR, "Failed to register vDPA device."); rte_errno = rte_errno ? rte_errno : EINVAL; goto error; } SLIST_INIT(&priv->mr_list); - SLIST_INIT(&priv->virtq_list); pthread_mutex_lock(&priv_list_lock); TAILQ_INSERT_TAIL(&priv_list, priv, next); pthread_mutex_unlock(&priv_list_lock); return 0; error: - if (priv) + if (priv) { + if (priv->var) + mlx5_glue->dv_free_var(priv->var); rte_free(priv); + } if (ctx) mlx5_glue->close_device(ctx); return -rte_errno; @@ -487,8 +755,7 @@ mlx5_vdpa_pci_remove(struct rte_pci_device *pci_dev) pthread_mutex_lock(&priv_list_lock); TAILQ_FOREACH(priv, &priv_list, next) { - if (memcmp(&priv->dev_addr.pci_addr, &pci_dev->addr, - sizeof(pci_dev->addr)) == 0) { + if (!rte_pci_addr_cmp(&priv->pci_dev->addr, &pci_dev->addr)) { found = 1; break; } @@ -499,6 +766,10 @@ mlx5_vdpa_pci_remove(struct rte_pci_device *pci_dev) if (found) { if (priv->configured) mlx5_vdpa_dev_close(priv->vid); + if (priv->var) { + mlx5_glue->dv_free_var(priv->var); + priv->var = NULL; + } mlx5_glue->close_device(priv->ctx); rte_free(priv); } @@ -506,14 +777,6 @@ mlx5_vdpa_pci_remove(struct rte_pci_device *pci_dev) } static const struct rte_pci_id mlx5_vdpa_pci_id_map[] = { - { - RTE_PCI_DEVICE(PCI_VENDOR_ID_MELLANOX, - PCI_DEVICE_ID_MELLANOX_CONNECTX5BF) - }, - { - RTE_PCI_DEVICE(PCI_VENDOR_ID_MELLANOX, - PCI_DEVICE_ID_MELLANOX_CONNECTX5BFVF) - }, { RTE_PCI_DEVICE(PCI_VENDOR_ID_MELLANOX, PCI_DEVICE_ID_MELLANOX_CONNECTX6)