]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
vfio: Introduce interface to flush virqfd inject workqueue
authorAlex Williamson <alex.williamson@redhat.com>
Mon, 1 Apr 2024 16:52:57 +0000 (10:52 -0600)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 13 Apr 2024 10:59:20 +0000 (12:59 +0200)
[ Upstream commit b620ecbd17a03cacd06f014a5d3f3a11285ce053 ]

In order to synchronize changes that can affect the thread callback,
introduce an interface to force a flush of the inject workqueue.  The
irqfd pointer is only valid under spinlock, but the workqueue cannot
be flushed under spinlock.  Therefore the flush work for the irqfd is
queued under spinlock.  The vfio_irqfd_cleanup_wq workqueue is re-used
for queuing this work such that flushing the workqueue is also ordered
relative to shutdown.

Reviewed-by: Kevin Tian <kevin.tian@intel.com>
Reviewed-by: Reinette Chatre <reinette.chatre@intel.com>
Reviewed-by: Eric Auger <eric.auger@redhat.com>
Link: https://lore.kernel.org/r/20240308230557.805580-4-alex.williamson@redhat.com
Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/vfio/virqfd.c
include/linux/vfio.h

index 997cb5d0a657cb1cb795ca609704f886c7ffc276..1cff533017abf162a011aece7d20638c28073cf1 100644 (file)
@@ -101,6 +101,13 @@ static void virqfd_inject(struct work_struct *work)
                virqfd->thread(virqfd->opaque, virqfd->data);
 }
 
+static void virqfd_flush_inject(struct work_struct *work)
+{
+       struct virqfd *virqfd = container_of(work, struct virqfd, flush_inject);
+
+       flush_work(&virqfd->inject);
+}
+
 int vfio_virqfd_enable(void *opaque,
                       int (*handler)(void *, void *),
                       void (*thread)(void *, void *),
@@ -124,6 +131,7 @@ int vfio_virqfd_enable(void *opaque,
 
        INIT_WORK(&virqfd->shutdown, virqfd_shutdown);
        INIT_WORK(&virqfd->inject, virqfd_inject);
+       INIT_WORK(&virqfd->flush_inject, virqfd_flush_inject);
 
        irqfd = fdget(fd);
        if (!irqfd.file) {
@@ -214,6 +222,19 @@ void vfio_virqfd_disable(struct virqfd **pvirqfd)
 }
 EXPORT_SYMBOL_GPL(vfio_virqfd_disable);
 
+void vfio_virqfd_flush_thread(struct virqfd **pvirqfd)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&virqfd_lock, flags);
+       if (*pvirqfd && (*pvirqfd)->thread)
+               queue_work(vfio_irqfd_cleanup_wq, &(*pvirqfd)->flush_inject);
+       spin_unlock_irqrestore(&virqfd_lock, flags);
+
+       flush_workqueue(vfio_irqfd_cleanup_wq);
+}
+EXPORT_SYMBOL_GPL(vfio_virqfd_flush_thread);
+
 module_init(vfio_virqfd_init);
 module_exit(vfio_virqfd_exit);
 
index f479c5d7f2c37c2304efee5c6a1e145c9de83483..56d18912c41afa7e772e6e4753063ef7b8b8e619 100644 (file)
@@ -221,6 +221,7 @@ struct virqfd {
        wait_queue_entry_t              wait;
        poll_table              pt;
        struct work_struct      shutdown;
+       struct work_struct      flush_inject;
        struct virqfd           **pvirqfd;
 };
 
@@ -229,5 +230,6 @@ extern int vfio_virqfd_enable(void *opaque,
                              void (*thread)(void *, void *),
                              void *data, struct virqfd **pvirqfd, int fd);
 extern void vfio_virqfd_disable(struct virqfd **pvirqfd);
+void vfio_virqfd_flush_thread(struct virqfd **pvirqfd);
 
 #endif /* VFIO_H */