Commit ed9e8417 authored by Alex Williamson's avatar Alex Williamson Committed by Jinjie Ruan
Browse files

vfio: Introduce interface to flush virqfd inject workqueue

stable inclusion
from stable-v5.15.154
commit 26a6a1e0b4ecea56862f40fd2939f327395afc49
category: bugfix
bugzilla: https://gitee.com/src-openeuler/kernel/issues/I9E6TE
CVE: CVE-2024-26812

Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=26a6a1e0b4ecea56862f40fd2939f327395afc49



--------------------------------

[ 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: default avatarKevin Tian <kevin.tian@intel.com>
Reviewed-by: default avatarReinette Chatre <reinette.chatre@intel.com>
Reviewed-by: default avatarEric Auger <eric.auger@redhat.com>
Link: https://lore.kernel.org/r/20240308230557.805580-4-alex.williamson@redhat.com


Signed-off-by: default avatarAlex Williamson <alex.williamson@redhat.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>

Conflicts:
	include/linux/vfio.h

Signed-off-by: default avatarJinjie Ruan <ruanjinjie@huawei.com>
parent a474d353
Loading
Loading
Loading
Loading
+21 −0
Original line number Diff line number Diff line
@@ -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);

+2 −0
Original line number Diff line number Diff line
@@ -225,6 +225,7 @@ struct virqfd {
	wait_queue_entry_t		wait;
	poll_table		pt;
	struct work_struct	shutdown;
	struct work_struct	flush_inject;
	struct virqfd		**pvirqfd;
};

@@ -233,6 +234,7 @@ 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);

extern int vfio_pci_num_regions(void *device_data);
extern struct pci_dev *vfio_pci_pdev(void *device_data);