mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/herbert/cryptodev-2.6.git
synced 2026-05-02 18:17:50 -04:00
vfio: selftests: Add driver framework
Add a driver framework to VFIO selftests, so that devices can generate DMA and interrupts in a common way that can be then utilized by tests. This will enable VFIO selftests to exercise real hardware DMA and interrupt paths, without needing any device-specific code in the test itself. Subsequent commits will introduce drivers for specific devices. Acked-by: Shuah Khan <skhan@linuxfoundation.org> Signed-off-by: David Matlack <dmatlack@google.com> Link: https://lore.kernel.org/r/20250822212518.4156428-13-dmatlack@google.com Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
This commit is contained in:
committed by
Alex Williamson
parent
50d8fe805f
commit
1b197032ac
@@ -63,6 +63,85 @@ struct vfio_dma_region {
|
||||
u64 size;
|
||||
};
|
||||
|
||||
struct vfio_pci_device;
|
||||
|
||||
struct vfio_pci_driver_ops {
|
||||
const char *name;
|
||||
|
||||
/**
|
||||
* @probe() - Check if the driver supports the given device.
|
||||
*
|
||||
* Return: 0 on success, non-0 on failure.
|
||||
*/
|
||||
int (*probe)(struct vfio_pci_device *device);
|
||||
|
||||
/**
|
||||
* @init() - Initialize the driver for @device.
|
||||
*
|
||||
* Must be called after device->driver.region has been initialized.
|
||||
*/
|
||||
void (*init)(struct vfio_pci_device *device);
|
||||
|
||||
/**
|
||||
* remove() - Deinitialize the driver for @device.
|
||||
*/
|
||||
void (*remove)(struct vfio_pci_device *device);
|
||||
|
||||
/**
|
||||
* memcpy_start() - Kick off @count repeated memcpy operations from
|
||||
* [@src, @src + @size) to [@dst, @dst + @size).
|
||||
*
|
||||
* Guarantees:
|
||||
* - The device will attempt DMA reads on [src, src + size).
|
||||
* - The device will attempt DMA writes on [dst, dst + size).
|
||||
* - The device will not generate any interrupts.
|
||||
*
|
||||
* memcpy_start() returns immediately, it does not wait for the
|
||||
* copies to complete.
|
||||
*/
|
||||
void (*memcpy_start)(struct vfio_pci_device *device,
|
||||
iova_t src, iova_t dst, u64 size, u64 count);
|
||||
|
||||
/**
|
||||
* memcpy_wait() - Wait until the memcpy operations started by
|
||||
* memcpy_start() have finished.
|
||||
*
|
||||
* Guarantees:
|
||||
* - All in-flight DMAs initiated by memcpy_start() are fully complete
|
||||
* before memcpy_wait() returns.
|
||||
*
|
||||
* Returns non-0 if the driver detects that an error occurred during the
|
||||
* memcpy, 0 otherwise.
|
||||
*/
|
||||
int (*memcpy_wait)(struct vfio_pci_device *device);
|
||||
|
||||
/**
|
||||
* send_msi() - Make the device send the MSI device->driver.msi.
|
||||
*
|
||||
* Guarantees:
|
||||
* - The device will send the MSI once.
|
||||
*/
|
||||
void (*send_msi)(struct vfio_pci_device *device);
|
||||
};
|
||||
|
||||
struct vfio_pci_driver {
|
||||
const struct vfio_pci_driver_ops *ops;
|
||||
bool initialized;
|
||||
bool memcpy_in_progress;
|
||||
|
||||
/* Region to be used by the driver (e.g. for in-memory descriptors) */
|
||||
struct vfio_dma_region region;
|
||||
|
||||
/* The maximum size that can be passed to memcpy_start(). */
|
||||
u64 max_memcpy_size;
|
||||
|
||||
/* The maximum count that can be passed to memcpy_start(). */
|
||||
u64 max_memcpy_count;
|
||||
|
||||
/* The MSI vector the device will signal in ops->send_msi(). */
|
||||
int msi;
|
||||
};
|
||||
|
||||
struct vfio_pci_device {
|
||||
int fd;
|
||||
int group_fd;
|
||||
@@ -79,6 +158,8 @@ struct vfio_pci_device {
|
||||
|
||||
/* eventfds for MSI and MSI-x interrupts */
|
||||
int msi_eventfds[PCI_MSIX_FLAGS_QSIZE + 1];
|
||||
|
||||
struct vfio_pci_driver driver;
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -174,4 +255,15 @@ static inline bool vfio_pci_device_match(struct vfio_pci_device *device,
|
||||
(device_id == vfio_pci_config_readw(device, PCI_DEVICE_ID));
|
||||
}
|
||||
|
||||
void vfio_pci_driver_probe(struct vfio_pci_device *device);
|
||||
void vfio_pci_driver_init(struct vfio_pci_device *device);
|
||||
void vfio_pci_driver_remove(struct vfio_pci_device *device);
|
||||
int vfio_pci_driver_memcpy(struct vfio_pci_device *device,
|
||||
iova_t src, iova_t dst, u64 size);
|
||||
void vfio_pci_driver_memcpy_start(struct vfio_pci_device *device,
|
||||
iova_t src, iova_t dst, u64 size,
|
||||
u64 count);
|
||||
int vfio_pci_driver_memcpy_wait(struct vfio_pci_device *device);
|
||||
void vfio_pci_driver_send_msi(struct vfio_pci_device *device);
|
||||
|
||||
#endif /* SELFTESTS_VFIO_LIB_INCLUDE_VFIO_UTIL_H */
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
VFIO_DIR := $(selfdir)/vfio
|
||||
|
||||
LIBVFIO_C := lib/vfio_pci_device.c
|
||||
LIBVFIO_C += lib/vfio_pci_driver.c
|
||||
|
||||
LIBVFIO_O := $(patsubst %.c, $(OUTPUT)/%.o, $(LIBVFIO_C))
|
||||
|
||||
|
||||
@@ -344,6 +344,8 @@ struct vfio_pci_device *vfio_pci_device_init(const char *bdf, int iommu_type)
|
||||
vfio_pci_iommu_setup(device, iommu_type);
|
||||
vfio_pci_device_setup(device, bdf);
|
||||
|
||||
vfio_pci_driver_probe(device);
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
@@ -351,6 +353,9 @@ void vfio_pci_device_cleanup(struct vfio_pci_device *device)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (device->driver.initialized)
|
||||
vfio_pci_driver_remove(device);
|
||||
|
||||
vfio_pci_bar_unmap_all(device);
|
||||
|
||||
VFIO_ASSERT_EQ(close(device->fd), 0);
|
||||
|
||||
116
tools/testing/selftests/vfio/lib/vfio_pci_driver.c
Normal file
116
tools/testing/selftests/vfio/lib/vfio_pci_driver.c
Normal file
@@ -0,0 +1,116 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
#include <stdio.h>
|
||||
|
||||
#include "../../../kselftest.h"
|
||||
#include <vfio_util.h>
|
||||
|
||||
static struct vfio_pci_driver_ops *driver_ops[] = {};
|
||||
|
||||
void vfio_pci_driver_probe(struct vfio_pci_device *device)
|
||||
{
|
||||
struct vfio_pci_driver_ops *ops;
|
||||
int i;
|
||||
|
||||
VFIO_ASSERT_NULL(device->driver.ops);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(driver_ops); i++) {
|
||||
ops = driver_ops[i];
|
||||
|
||||
if (ops->probe(device))
|
||||
continue;
|
||||
|
||||
printf("Driver found: %s\n", ops->name);
|
||||
device->driver.ops = ops;
|
||||
}
|
||||
}
|
||||
|
||||
static void vfio_check_driver_op(struct vfio_pci_driver *driver, void *op,
|
||||
const char *op_name)
|
||||
{
|
||||
VFIO_ASSERT_NOT_NULL(driver->ops);
|
||||
VFIO_ASSERT_NOT_NULL(op, "Driver has no %s()\n", op_name);
|
||||
VFIO_ASSERT_EQ(driver->initialized, op != driver->ops->init);
|
||||
VFIO_ASSERT_EQ(driver->memcpy_in_progress, op == driver->ops->memcpy_wait);
|
||||
}
|
||||
|
||||
#define VFIO_CHECK_DRIVER_OP(_driver, _op) do { \
|
||||
struct vfio_pci_driver *__driver = (_driver); \
|
||||
vfio_check_driver_op(__driver, __driver->ops->_op, #_op); \
|
||||
} while (0)
|
||||
|
||||
void vfio_pci_driver_init(struct vfio_pci_device *device)
|
||||
{
|
||||
struct vfio_pci_driver *driver = &device->driver;
|
||||
|
||||
VFIO_ASSERT_NOT_NULL(driver->region.vaddr);
|
||||
VFIO_CHECK_DRIVER_OP(driver, init);
|
||||
|
||||
driver->ops->init(device);
|
||||
|
||||
driver->initialized = true;
|
||||
|
||||
printf("%s: region: vaddr %p, iova 0x%lx, size 0x%lx\n",
|
||||
driver->ops->name,
|
||||
driver->region.vaddr,
|
||||
driver->region.iova,
|
||||
driver->region.size);
|
||||
|
||||
printf("%s: max_memcpy_size 0x%lx, max_memcpy_count 0x%lx\n",
|
||||
driver->ops->name,
|
||||
driver->max_memcpy_size,
|
||||
driver->max_memcpy_count);
|
||||
}
|
||||
|
||||
void vfio_pci_driver_remove(struct vfio_pci_device *device)
|
||||
{
|
||||
struct vfio_pci_driver *driver = &device->driver;
|
||||
|
||||
VFIO_CHECK_DRIVER_OP(driver, remove);
|
||||
|
||||
driver->ops->remove(device);
|
||||
driver->initialized = false;
|
||||
}
|
||||
|
||||
void vfio_pci_driver_send_msi(struct vfio_pci_device *device)
|
||||
{
|
||||
struct vfio_pci_driver *driver = &device->driver;
|
||||
|
||||
VFIO_CHECK_DRIVER_OP(driver, send_msi);
|
||||
|
||||
driver->ops->send_msi(device);
|
||||
}
|
||||
|
||||
void vfio_pci_driver_memcpy_start(struct vfio_pci_device *device,
|
||||
iova_t src, iova_t dst, u64 size,
|
||||
u64 count)
|
||||
{
|
||||
struct vfio_pci_driver *driver = &device->driver;
|
||||
|
||||
VFIO_ASSERT_LE(size, driver->max_memcpy_size);
|
||||
VFIO_ASSERT_LE(count, driver->max_memcpy_count);
|
||||
VFIO_CHECK_DRIVER_OP(driver, memcpy_start);
|
||||
|
||||
driver->ops->memcpy_start(device, src, dst, size, count);
|
||||
driver->memcpy_in_progress = true;
|
||||
}
|
||||
|
||||
int vfio_pci_driver_memcpy_wait(struct vfio_pci_device *device)
|
||||
{
|
||||
struct vfio_pci_driver *driver = &device->driver;
|
||||
int r;
|
||||
|
||||
VFIO_CHECK_DRIVER_OP(driver, memcpy_wait);
|
||||
|
||||
r = driver->ops->memcpy_wait(device);
|
||||
driver->memcpy_in_progress = false;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int vfio_pci_driver_memcpy(struct vfio_pci_device *device,
|
||||
iova_t src, iova_t dst, u64 size)
|
||||
{
|
||||
vfio_pci_driver_memcpy_start(device, src, dst, size, 1);
|
||||
|
||||
return vfio_pci_driver_memcpy_wait(device);
|
||||
}
|
||||
Reference in New Issue
Block a user