@@ -610,14 +610,44 @@ void msix_unuse_all_vectors(PCIDevice *dev)
int msix_set_mask_notifier(PCIDevice *dev, unsigned vector, void *opaque)
{
+ int r;
+ if (vector >= dev->msix_entries_nr || !dev->msix_entry_used[vector])
+ return 0;
+
+ assert(dev->msix_mask_notifier);
+ assert(opaque);
+ assert(!dev->msix_mask_notifier_opaque[vector]);
+
+ if (msix_is_masked(dev, vector)) {
+ return 0;
+ }
+ r = dev->msix_mask_notifier(dev, vector, opaque,
+ msix_is_masked(dev, vector));
+ if (r < 0) {
+ return r;
+ }
+ dev->msix_mask_notifier_opaque[vector] = opaque;
+ return r;
+}
+
+int msix_unset_mask_notifier(PCIDevice *dev, unsigned vector)
+{
int r = 0;
if (vector >= dev->msix_entries_nr || !dev->msix_entry_used[vector])
return 0;
- if (dev->msix_mask_notifier)
- r = dev->msix_mask_notifier(dev, vector, opaque,
- msix_is_masked(dev, vector));
- if (r >= 0)
- dev->msix_mask_notifier_opaque[vector] = opaque;
+ assert(dev->msix_mask_notifier);
+ assert(dev->msix_mask_notifier_opaque[vector]);
+
+ if (msix_is_masked(dev, vector)) {
+ return 0;
+ }
+ r = dev->msix_mask_notifier(dev, vector,
+ dev->msix_mask_notifier_opaque[vector],
+ msix_is_masked(dev, vector));
+ if (r < 0) {
+ return r;
+ }
+ dev->msix_mask_notifier_opaque[vector] = NULL;
return r;
}
@@ -34,4 +34,5 @@ void msix_reset(PCIDevice *dev);
extern int msix_supported;
int msix_set_mask_notifier(PCIDevice *dev, unsigned vector, void *opaque);
+int msix_unset_mask_notifier(PCIDevice *dev, unsigned vector);
#endif
@@ -437,10 +437,13 @@ static int virtio_pci_guest_notifier(void *opaque, int n, bool assign)
msix_set_mask_notifier(&proxy->pci_dev,
virtio_queue_vector(proxy->vdev, n), vq);
} else {
- msix_set_mask_notifier(&proxy->pci_dev,
- virtio_queue_vector(proxy->vdev, n), NULL);
+ msix_unset_mask_notifier(&proxy->pci_dev,
+ virtio_queue_vector(proxy->vdev, n));
qemu_set_fd_handler(event_notifier_get_fd(notifier),
NULL, NULL, NULL);
+ /* Test and clear notifier before closing it,
+ * in case poll callback didn't have time to run. */
+ virtio_pci_guest_notifier_read(vq);
event_notifier_cleanup(notifier);
}