static void vfio_msi_enable(VFIOPCIDevice *vdev)

{

    int ret, i;



    vfio_disable_interrupts(vdev);



    vdev->nr_vectors = msi_nr_vectors_allocated(&vdev->pdev);

retry:

    vdev->msi_vectors = g_malloc0(vdev->nr_vectors * sizeof(VFIOMSIVector));



    for (i = 0; i < vdev->nr_vectors; i++) {

        VFIOMSIVector *vector = &vdev->msi_vectors[i];

        MSIMessage msg = msi_get_message(&vdev->pdev, i);



        vector->vdev = vdev;

        vector->virq = -1;

        vector->use = true;



        if (event_notifier_init(&vector->interrupt, 0)) {

            error_report("vfio: Error: event_notifier_init failed");

        }



        qemu_set_fd_handler(event_notifier_get_fd(&vector->interrupt),

                            vfio_msi_interrupt, NULL, vector);



        /*

         * Attempt to enable route through KVM irqchip,

         * default to userspace handling if unavailable.

         */

        vfio_add_kvm_msi_virq(vector, &msg, false);

    }



    /* Set interrupt type prior to possible interrupts */

    vdev->interrupt = VFIO_INT_MSI;



    ret = vfio_enable_vectors(vdev, false);

    if (ret) {

        if (ret < 0) {

            error_report("vfio: Error: Failed to setup MSI fds: %m");

        } else if (ret != vdev->nr_vectors) {

            error_report("vfio: Error: Failed to enable %d "

                         "MSI vectors, retry with %d", vdev->nr_vectors, ret);

        }



        for (i = 0; i < vdev->nr_vectors; i++) {

            VFIOMSIVector *vector = &vdev->msi_vectors[i];

            if (vector->virq >= 0) {

                vfio_remove_kvm_msi_virq(vector);

            }

            qemu_set_fd_handler(event_notifier_get_fd(&vector->interrupt),

                                NULL, NULL, NULL);

            event_notifier_cleanup(&vector->interrupt);

        }



        g_free(vdev->msi_vectors);



        if (ret > 0 && ret != vdev->nr_vectors) {

            vdev->nr_vectors = ret;

            goto retry;

        }

        vdev->nr_vectors = 0;



        /*

         * Failing to setup MSI doesn't really fall within any specification.

         * Let's try leaving interrupts disabled and hope the guest figures

         * out to fall back to INTx for this device.

         */

        error_report("vfio: Error: Failed to enable MSI");

        vdev->interrupt = VFIO_INT_NONE;



        return;

    }



    trace_vfio_msi_enable(vdev->vbasedev.name, vdev->nr_vectors);

}
