When the DT node has "wakeup-source", vm_find_vqs() calls
enable_irq_wake() on the shared IRQ, but vm_del_vqs() freed that IRQ
without a matching disable_irq_wake(). That leaves a wake reference
behind and can warn on later free_irq()/request_irq() cycles.

Record whether enable_irq_wake() succeeded, and disable it in
vm_del_vqs() before free_irq().

Fixes: 02213273f72a ("virtio_mmio: add support to set IRQ of a virtio device as 
wakeup source")
Cc: [email protected]
Signed-off-by: Xiong Weimin <[email protected]>
---
 drivers/virtio/virtio_mmio.c | 16 +++++++++++++---
 1 file changed, 13 insertions(+), 3 deletions(-)

diff --git a/drivers/virtio/virtio_mmio.c b/drivers/virtio/virtio_mmio.c
index 510b7c4ef..d5b148bde 100644
--- a/drivers/virtio/virtio_mmio.c
+++ b/drivers/virtio/virtio_mmio.c
@@ -87,6 +87,9 @@ struct virtio_mmio_device {
 
        void __iomem *base;
        unsigned long version;
+
+       /* True if enable_irq_wake() succeeded for the shared IRQ. */
+       bool wake_irq_enabled;
 };
 
 /* Configuration interface */
@@ -329,11 +332,17 @@ static void vm_del_vqs(struct virtio_device *vdev)
 {
        struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
        struct virtqueue *vq, *n;
+       int irq = platform_get_irq(vm_dev->pdev, 0);
 
        list_for_each_entry_safe(vq, n, &vdev->vqs, list)
                vm_del_vq(vq);
 
-       free_irq(platform_get_irq(vm_dev->pdev, 0), vm_dev);
+       if (vm_dev->wake_irq_enabled) {
+               disable_irq_wake(irq);
+               vm_dev->wake_irq_enabled = false;
+       }
+
+       free_irq(irq, vm_dev);
 }
 
 static void vm_synchronize_cbs(struct virtio_device *vdev)
@@ -460,8 +469,9 @@ static int vm_find_vqs(struct virtio_device *vdev, unsigned 
int nvqs,
        if (err)
                return err;
 
-       if (of_property_read_bool(vm_dev->pdev->dev.of_node, "wakeup-source"))
-               enable_irq_wake(irq);
+       if (of_property_read_bool(vm_dev->pdev->dev.of_node, "wakeup-source") &&
+           !enable_irq_wake(irq))
+               vm_dev->wake_irq_enabled = true;
 
        for (i = 0; i < nvqs; ++i) {
                struct virtqueue_info *vqi = &vqs_info[i];
-- 
2.43.0


Reply via email to