Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

vfio: Introduce interface to flush virqfd inject workqueue

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>

+23
+21
drivers/vfio/virqfd.c
··· 101 101 virqfd->thread(virqfd->opaque, virqfd->data); 102 102 } 103 103 104 + static void virqfd_flush_inject(struct work_struct *work) 105 + { 106 + struct virqfd *virqfd = container_of(work, struct virqfd, flush_inject); 107 + 108 + flush_work(&virqfd->inject); 109 + } 110 + 104 111 int vfio_virqfd_enable(void *opaque, 105 112 int (*handler)(void *, void *), 106 113 void (*thread)(void *, void *), ··· 131 124 132 125 INIT_WORK(&virqfd->shutdown, virqfd_shutdown); 133 126 INIT_WORK(&virqfd->inject, virqfd_inject); 127 + INIT_WORK(&virqfd->flush_inject, virqfd_flush_inject); 134 128 135 129 irqfd = fdget(fd); 136 130 if (!irqfd.file) { ··· 221 213 flush_workqueue(vfio_irqfd_cleanup_wq); 222 214 } 223 215 EXPORT_SYMBOL_GPL(vfio_virqfd_disable); 216 + 217 + void vfio_virqfd_flush_thread(struct virqfd **pvirqfd) 218 + { 219 + unsigned long flags; 220 + 221 + spin_lock_irqsave(&virqfd_lock, flags); 222 + if (*pvirqfd && (*pvirqfd)->thread) 223 + queue_work(vfio_irqfd_cleanup_wq, &(*pvirqfd)->flush_inject); 224 + spin_unlock_irqrestore(&virqfd_lock, flags); 225 + 226 + flush_workqueue(vfio_irqfd_cleanup_wq); 227 + } 228 + EXPORT_SYMBOL_GPL(vfio_virqfd_flush_thread);
+2
include/linux/vfio.h
··· 356 356 wait_queue_entry_t wait; 357 357 poll_table pt; 358 358 struct work_struct shutdown; 359 + struct work_struct flush_inject; 359 360 struct virqfd **pvirqfd; 360 361 }; 361 362 ··· 364 363 void (*thread)(void *, void *), void *data, 365 364 struct virqfd **pvirqfd, int fd); 366 365 void vfio_virqfd_disable(struct virqfd **pvirqfd); 366 + void vfio_virqfd_flush_thread(struct virqfd **pvirqfd); 367 367 368 368 #endif /* VFIO_H */