u64 driver_addr;
        u16 avail_index;
        u16 used_index;
+       struct msi_map map;
        bool ready;
        bool restore;
 };
        u16 avail_idx;
        u16 used_idx;
        int fw_state;
+       struct msi_map map;
 
        /* keep last in the struct */
        struct mlx5_vq_restore_info ri;
               BIT_ULL(MLX5_OBJ_TYPE_VIRTIO_Q_COUNTERS);
 }
 
+static bool msix_mode_supported(struct mlx5_vdpa_dev *mvdev)
+{
+       return MLX5_CAP_DEV_VDPA_EMULATION(mvdev->mdev, event_mode) &
+               (1 << MLX5_VIRTIO_Q_EVENT_MODE_MSIX_MODE) &&
+               pci_msix_can_alloc_dyn(mvdev->mdev->pdev);
+}
+
 static int create_virtqueue(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq)
 {
        int inlen = MLX5_ST_SZ_BYTES(create_virtio_net_q_in);
        if (vq_is_tx(mvq->index))
                MLX5_SET(virtio_net_q_object, obj_context, tisn_or_qpn, ndev->res.tisn);
 
-       MLX5_SET(virtio_q, vq_ctx, event_mode, MLX5_VIRTIO_Q_EVENT_MODE_QP_MODE);
+       if (mvq->map.virq) {
+               MLX5_SET(virtio_q, vq_ctx, event_mode, MLX5_VIRTIO_Q_EVENT_MODE_MSIX_MODE);
+               MLX5_SET(virtio_q, vq_ctx, event_qpn_or_msix, mvq->map.index);
+       } else {
+               MLX5_SET(virtio_q, vq_ctx, event_mode, MLX5_VIRTIO_Q_EVENT_MODE_QP_MODE);
+               MLX5_SET(virtio_q, vq_ctx, event_qpn_or_msix, mvq->fwqp.mqp.qpn);
+       }
+
        MLX5_SET(virtio_q, vq_ctx, queue_index, mvq->index);
-       MLX5_SET(virtio_q, vq_ctx, event_qpn_or_msix, mvq->fwqp.mqp.qpn);
        MLX5_SET(virtio_q, vq_ctx, queue_size, mvq->num_ent);
        MLX5_SET(virtio_q, vq_ctx, virtio_version_1_0,
                 !!(ndev->mvdev.actual_features & BIT_ULL(VIRTIO_F_VERSION_1)));
                mlx5_vdpa_warn(&ndev->mvdev, "dealloc counter set 0x%x\n", mvq->counter_set_id);
 }
 
+static irqreturn_t mlx5_vdpa_int_handler(int irq, void *priv)
+{
+       struct vdpa_callback *cb = priv;
+
+       if (cb->callback)
+               return cb->callback(cb->private);
+
+       return IRQ_HANDLED;
+}
+
+static void alloc_vector(struct mlx5_vdpa_net *ndev,
+                        struct mlx5_vdpa_virtqueue *mvq)
+{
+       struct mlx5_vdpa_irq_pool *irqp = &ndev->irqp;
+       struct mlx5_vdpa_irq_pool_entry *ent;
+       int err;
+       int i;
+
+       for (i = 0; i < irqp->num_ent; i++) {
+               ent = &irqp->entries[i];
+               if (!ent->used) {
+                       snprintf(ent->name, MLX5_VDPA_IRQ_NAME_LEN, "%s-vq-%d",
+                                dev_name(&ndev->mvdev.vdev.dev), mvq->index);
+                       ent->dev_id = &ndev->event_cbs[mvq->index];
+                       err = request_irq(ent->map.virq, mlx5_vdpa_int_handler, 0,
+                                         ent->name, ent->dev_id);
+                       if (err)
+                               return;
+
+                       ent->used = true;
+                       mvq->map = ent->map;
+                       return;
+               }
+       }
+}
+
+static void dealloc_vector(struct mlx5_vdpa_net *ndev,
+                          struct mlx5_vdpa_virtqueue *mvq)
+{
+       struct mlx5_vdpa_irq_pool *irqp = &ndev->irqp;
+       int i;
+
+       for (i = 0; i < irqp->num_ent; i++)
+               if (mvq->map.virq == irqp->entries[i].map.virq) {
+                       free_irq(mvq->map.virq, irqp->entries[i].dev_id);
+                       irqp->entries[i].used = false;
+                       return;
+               }
+}
+
 static int setup_vq(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq)
 {
        u16 idx = mvq->index;
 
        err = counter_set_alloc(ndev, mvq);
        if (err)
-               goto err_counter;
+               goto err_connect;
 
+       alloc_vector(ndev, mvq);
        err = create_virtqueue(ndev, mvq);
        if (err)
-               goto err_connect;
+               goto err_vq;
 
        if (mvq->ready) {
                err = modify_virtqueue(ndev, mvq, MLX5_VIRTIO_NET_Q_OBJECT_STATE_RDY);
                if (err) {
                        mlx5_vdpa_warn(&ndev->mvdev, "failed to modify to ready vq idx %d(%d)\n",
                                       idx, err);
-                       goto err_connect;
+                       goto err_modify;
                }
        }
 
        mvq->initialized = true;
        return 0;
 
-err_connect:
+err_modify:
+       destroy_virtqueue(ndev, mvq);
+err_vq:
+       dealloc_vector(ndev, mvq);
        counter_set_dealloc(ndev, mvq);
-err_counter:
+err_connect:
        qp_destroy(ndev, &mvq->vqqp);
 err_vqqp:
        qp_destroy(ndev, &mvq->fwqp);
 
        suspend_vq(ndev, mvq);
        destroy_virtqueue(ndev, mvq);
+       dealloc_vector(ndev, mvq);
        counter_set_dealloc(ndev, mvq);
        qp_destroy(ndev, &mvq->vqqp);
        qp_destroy(ndev, &mvq->fwqp);
        ri->desc_addr = mvq->desc_addr;
        ri->device_addr = mvq->device_addr;
        ri->driver_addr = mvq->driver_addr;
+       ri->map = mvq->map;
        ri->restore = true;
        return 0;
 }
                mvq->desc_addr = ri->desc_addr;
                mvq->device_addr = ri->device_addr;
                mvq->driver_addr = ri->driver_addr;
+               mvq->map = ri->map;
        }
 }
 
        return mvdev->vdev.dma_dev;
 }
 
+static void free_irqs(struct mlx5_vdpa_net *ndev)
+{
+       struct mlx5_vdpa_irq_pool_entry *ent;
+       int i;
+
+       if (!msix_mode_supported(&ndev->mvdev))
+               return;
+
+       if (!ndev->irqp.entries)
+               return;
+
+       for (i = ndev->irqp.num_ent - 1; i >= 0; i--) {
+               ent = ndev->irqp.entries + i;
+               if (ent->map.virq)
+                       pci_msix_free_irq(ndev->mvdev.mdev->pdev, ent->map);
+       }
+       kfree(ndev->irqp.entries);
+}
+
 static void mlx5_vdpa_free(struct vdpa_device *vdev)
 {
        struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
                mlx5_mpfs_del_mac(pfmdev, ndev->config.mac);
        }
        mlx5_vdpa_free_resources(&ndev->mvdev);
+       free_irqs(ndev);
        kfree(ndev->event_cbs);
        kfree(ndev->vqs);
 }
        return ret;
 }
 
-static int mlx5_get_vq_irq(struct vdpa_device *vdv, u16 idx)
+static int mlx5_get_vq_irq(struct vdpa_device *vdev, u16 idx)
 {
-       return -EOPNOTSUPP;
+       struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
+       struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
+       struct mlx5_vdpa_virtqueue *mvq;
+
+       if (!is_index_valid(mvdev, idx))
+               return -EINVAL;
+
+       if (is_ctrl_vq_idx(mvdev, idx))
+               return -EOPNOTSUPP;
+
+       mvq = &ndev->vqs[idx];
+       if (!mvq->map.virq)
+               return -EOPNOTSUPP;
+
+       return mvq->map.virq;
 }
 
 static u64 mlx5_vdpa_get_driver_features(struct vdpa_device *vdev)
        return err;
 }
 
+static void allocate_irqs(struct mlx5_vdpa_net *ndev)
+{
+       struct mlx5_vdpa_irq_pool_entry *ent;
+       int i;
+
+       if (!msix_mode_supported(&ndev->mvdev))
+               return;
+
+       if (!ndev->mvdev.mdev->pdev)
+               return;
+
+       ndev->irqp.entries = kcalloc(ndev->mvdev.max_vqs, sizeof(*ndev->irqp.entries), GFP_KERNEL);
+       if (!ndev->irqp.entries)
+               return;
+
+
+       for (i = 0; i < ndev->mvdev.max_vqs; i++) {
+               ent = ndev->irqp.entries + i;
+               snprintf(ent->name, MLX5_VDPA_IRQ_NAME_LEN, "%s-vq-%d",
+                        dev_name(&ndev->mvdev.vdev.dev), i);
+               ent->map = pci_msix_alloc_irq_at(ndev->mvdev.mdev->pdev, MSI_ANY_INDEX, NULL);
+               if (!ent->map.virq)
+                       return;
+
+               ndev->irqp.num_ent++;
+       }
+}
+
 static int mlx5_vdpa_dev_add(struct vdpa_mgmt_dev *v_mdev, const char *name,
                             const struct vdpa_dev_set_config *add_config)
 {
        }
 
        init_mvqs(ndev);
+       allocate_irqs(ndev);
        init_rwsem(&ndev->reslock);
        config = &ndev->config;
 
        kfree(mgtdev);
 }
 
+static void mlx5v_shutdown(struct auxiliary_device *auxdev)
+{
+       struct mlx5_vdpa_mgmtdev *mgtdev;
+       struct mlx5_vdpa_net *ndev;
+
+       mgtdev = auxiliary_get_drvdata(auxdev);
+       ndev = mgtdev->ndev;
+
+       free_irqs(ndev);
+}
+
 static const struct auxiliary_device_id mlx5v_id_table[] = {
        { .name = MLX5_ADEV_NAME ".vnet", },
        {},
        .name = "vnet",
        .probe = mlx5v_probe,
        .remove = mlx5v_remove,
+       .shutdown = mlx5v_shutdown,
        .id_table = mlx5v_id_table,
 };