| From 89dcddf04150bd76903d0f02ffe263718cadd3a6 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Tue, 29 Mar 2022 12:21:07 +0800 |
| Subject: vdpa: mlx5: prevent cvq work from hogging CPU |
| |
| From: Jason Wang <jasowang@redhat.com> |
| |
| [ Upstream commit 55ebf0d60e3cc6c9e8593399e185842c00e12f36 ] |
| |
| A userspace triggerable infinite loop could happen in |
| mlx5_cvq_kick_handler() if userspace keeps sending a huge amount of |
| cvq requests. |
| |
| Fixing this by introducing a quota and re-queue the work if we're out |
| of the budget (currently the implicit budget is one) . While at it, |
| using a per device work struct to avoid on demand memory allocation |
| for cvq. |
| |
| Fixes: 5262912ef3cfc ("vdpa/mlx5: Add support for control VQ and MAC setting") |
| Signed-off-by: Jason Wang <jasowang@redhat.com> |
| Link: https://lore.kernel.org/r/20220329042109.4029-1-jasowang@redhat.com |
| Signed-off-by: Michael S. Tsirkin <mst@redhat.com> |
| Acked-by: Eli Cohen <elic@nvidia.com> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| drivers/vdpa/mlx5/net/mlx5_vnet.c | 21 +++++++++------------ |
| 1 file changed, 9 insertions(+), 12 deletions(-) |
| |
| diff --git a/drivers/vdpa/mlx5/net/mlx5_vnet.c b/drivers/vdpa/mlx5/net/mlx5_vnet.c |
| index 57bed3d74060..be50c15609b7 100644 |
| --- a/drivers/vdpa/mlx5/net/mlx5_vnet.c |
| +++ b/drivers/vdpa/mlx5/net/mlx5_vnet.c |
| @@ -164,6 +164,7 @@ struct mlx5_vdpa_net { |
| u32 cur_num_vqs; |
| struct notifier_block nb; |
| struct vdpa_callback config_cb; |
| + struct mlx5_vdpa_wq_ent cvq_ent; |
| }; |
| |
| static void free_resources(struct mlx5_vdpa_net *ndev); |
| @@ -1627,10 +1628,10 @@ static void mlx5_cvq_kick_handler(struct work_struct *work) |
| ndev = to_mlx5_vdpa_ndev(mvdev); |
| cvq = &mvdev->cvq; |
| if (!(ndev->mvdev.actual_features & BIT_ULL(VIRTIO_NET_F_CTRL_VQ))) |
| - goto out; |
| + return; |
| |
| if (!cvq->ready) |
| - goto out; |
| + return; |
| |
| while (true) { |
| err = vringh_getdesc_iotlb(&cvq->vring, &cvq->riov, &cvq->wiov, &cvq->head, |
| @@ -1664,9 +1665,10 @@ static void mlx5_cvq_kick_handler(struct work_struct *work) |
| |
| if (vringh_need_notify_iotlb(&cvq->vring)) |
| vringh_notify(&cvq->vring); |
| + |
| + queue_work(mvdev->wq, &wqent->work); |
| + break; |
| } |
| -out: |
| - kfree(wqent); |
| } |
| |
| static void mlx5_vdpa_kick_vq(struct vdpa_device *vdev, u16 idx) |
| @@ -1674,7 +1676,6 @@ static void mlx5_vdpa_kick_vq(struct vdpa_device *vdev, u16 idx) |
| struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev); |
| struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev); |
| struct mlx5_vdpa_virtqueue *mvq; |
| - struct mlx5_vdpa_wq_ent *wqent; |
| |
| if (!is_index_valid(mvdev, idx)) |
| return; |
| @@ -1683,13 +1684,7 @@ static void mlx5_vdpa_kick_vq(struct vdpa_device *vdev, u16 idx) |
| if (!mvdev->wq || !mvdev->cvq.ready) |
| return; |
| |
| - wqent = kzalloc(sizeof(*wqent), GFP_ATOMIC); |
| - if (!wqent) |
| - return; |
| - |
| - wqent->mvdev = mvdev; |
| - INIT_WORK(&wqent->work, mlx5_cvq_kick_handler); |
| - queue_work(mvdev->wq, &wqent->work); |
| + queue_work(mvdev->wq, &ndev->cvq_ent.work); |
| return; |
| } |
| |
| @@ -2634,6 +2629,8 @@ static int mlx5_vdpa_dev_add(struct vdpa_mgmt_dev *v_mdev, const char *name, |
| if (err) |
| goto err_mr; |
| |
| + ndev->cvq_ent.mvdev = mvdev; |
| + INIT_WORK(&ndev->cvq_ent.work, mlx5_cvq_kick_handler); |
| mvdev->wq = create_singlethread_workqueue("mlx5_vdpa_wq"); |
| if (!mvdev->wq) { |
| err = -ENOMEM; |
| -- |
| 2.35.1 |
| |