util/vfio-helpers: Collect IOVA reserved regions
The IOVA allocator currently ignores host reserved regions. As a result some chosen IOVAs may collide with some of them, resulting in VFIO MAP_DMA errors later on. This happens on ARM where the MSI reserved window quickly is encountered: [0x8000000, 0x8100000]. since 5.4 kernel, VFIO returns the usable IOVA regions. So let's enumerate them in the prospect to avoid them, later on. Signed-off-by: Eric Auger <eric.auger@redhat.com> Message-id: 20200929085550.30926-2-eric.auger@redhat.com Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
parent
ad1e691db2
commit
4487d420d0
@ -40,6 +40,11 @@ typedef struct {
|
|||||||
uint64_t iova;
|
uint64_t iova;
|
||||||
} IOVAMapping;
|
} IOVAMapping;
|
||||||
|
|
||||||
|
struct IOVARange {
|
||||||
|
uint64_t start;
|
||||||
|
uint64_t end;
|
||||||
|
};
|
||||||
|
|
||||||
struct QEMUVFIOState {
|
struct QEMUVFIOState {
|
||||||
QemuMutex lock;
|
QemuMutex lock;
|
||||||
|
|
||||||
@ -49,6 +54,8 @@ struct QEMUVFIOState {
|
|||||||
int device;
|
int device;
|
||||||
RAMBlockNotifier ram_notifier;
|
RAMBlockNotifier ram_notifier;
|
||||||
struct vfio_region_info config_region_info, bar_region_info[6];
|
struct vfio_region_info config_region_info, bar_region_info[6];
|
||||||
|
struct IOVARange *usable_iova_ranges;
|
||||||
|
uint8_t nb_iova_ranges;
|
||||||
|
|
||||||
/* These fields are protected by @lock */
|
/* These fields are protected by @lock */
|
||||||
/* VFIO's IO virtual address space is managed by splitting into a few
|
/* VFIO's IO virtual address space is managed by splitting into a few
|
||||||
@ -236,6 +243,35 @@ static int qemu_vfio_pci_write_config(QEMUVFIOState *s, void *buf, int size, int
|
|||||||
return ret == size ? 0 : -errno;
|
return ret == size ? 0 : -errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void collect_usable_iova_ranges(QEMUVFIOState *s, void *buf)
|
||||||
|
{
|
||||||
|
struct vfio_iommu_type1_info *info = (struct vfio_iommu_type1_info *)buf;
|
||||||
|
struct vfio_info_cap_header *cap = (void *)buf + info->cap_offset;
|
||||||
|
struct vfio_iommu_type1_info_cap_iova_range *cap_iova_range;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
while (cap->id != VFIO_IOMMU_TYPE1_INFO_CAP_IOVA_RANGE) {
|
||||||
|
if (!cap->next) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cap = (struct vfio_info_cap_header *)(buf + cap->next);
|
||||||
|
}
|
||||||
|
|
||||||
|
cap_iova_range = (struct vfio_iommu_type1_info_cap_iova_range *)cap;
|
||||||
|
|
||||||
|
s->nb_iova_ranges = cap_iova_range->nr_iovas;
|
||||||
|
if (s->nb_iova_ranges > 1) {
|
||||||
|
s->usable_iova_ranges =
|
||||||
|
g_realloc(s->usable_iova_ranges,
|
||||||
|
s->nb_iova_ranges * sizeof(struct IOVARange));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < s->nb_iova_ranges; i++) {
|
||||||
|
s->usable_iova_ranges[i].start = cap_iova_range->iova_ranges[i].start;
|
||||||
|
s->usable_iova_ranges[i].end = cap_iova_range->iova_ranges[i].end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int qemu_vfio_init_pci(QEMUVFIOState *s, const char *device,
|
static int qemu_vfio_init_pci(QEMUVFIOState *s, const char *device,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
@ -243,10 +279,13 @@ static int qemu_vfio_init_pci(QEMUVFIOState *s, const char *device,
|
|||||||
int i;
|
int i;
|
||||||
uint16_t pci_cmd;
|
uint16_t pci_cmd;
|
||||||
struct vfio_group_status group_status = { .argsz = sizeof(group_status) };
|
struct vfio_group_status group_status = { .argsz = sizeof(group_status) };
|
||||||
struct vfio_iommu_type1_info iommu_info = { .argsz = sizeof(iommu_info) };
|
struct vfio_iommu_type1_info *iommu_info = NULL;
|
||||||
|
size_t iommu_info_size = sizeof(*iommu_info);
|
||||||
struct vfio_device_info device_info = { .argsz = sizeof(device_info) };
|
struct vfio_device_info device_info = { .argsz = sizeof(device_info) };
|
||||||
char *group_file = NULL;
|
char *group_file = NULL;
|
||||||
|
|
||||||
|
s->usable_iova_ranges = NULL;
|
||||||
|
|
||||||
/* Create a new container */
|
/* Create a new container */
|
||||||
s->container = open("/dev/vfio/vfio", O_RDWR);
|
s->container = open("/dev/vfio/vfio", O_RDWR);
|
||||||
|
|
||||||
@ -310,13 +349,35 @@ static int qemu_vfio_init_pci(QEMUVFIOState *s, const char *device,
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
iommu_info = g_malloc0(iommu_info_size);
|
||||||
|
iommu_info->argsz = iommu_info_size;
|
||||||
|
|
||||||
/* Get additional IOMMU info */
|
/* Get additional IOMMU info */
|
||||||
if (ioctl(s->container, VFIO_IOMMU_GET_INFO, &iommu_info)) {
|
if (ioctl(s->container, VFIO_IOMMU_GET_INFO, iommu_info)) {
|
||||||
error_setg_errno(errp, errno, "Failed to get IOMMU info");
|
error_setg_errno(errp, errno, "Failed to get IOMMU info");
|
||||||
ret = -errno;
|
ret = -errno;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* if the kernel does not report usable IOVA regions, choose
|
||||||
|
* the legacy [QEMU_VFIO_IOVA_MIN, QEMU_VFIO_IOVA_MAX -1] region
|
||||||
|
*/
|
||||||
|
s->nb_iova_ranges = 1;
|
||||||
|
s->usable_iova_ranges = g_new0(struct IOVARange, 1);
|
||||||
|
s->usable_iova_ranges[0].start = QEMU_VFIO_IOVA_MIN;
|
||||||
|
s->usable_iova_ranges[0].end = QEMU_VFIO_IOVA_MAX - 1;
|
||||||
|
|
||||||
|
if (iommu_info->argsz > iommu_info_size) {
|
||||||
|
iommu_info_size = iommu_info->argsz;
|
||||||
|
iommu_info = g_realloc(iommu_info, iommu_info_size);
|
||||||
|
if (ioctl(s->container, VFIO_IOMMU_GET_INFO, iommu_info)) {
|
||||||
|
ret = -errno;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
collect_usable_iova_ranges(s, iommu_info);
|
||||||
|
}
|
||||||
|
|
||||||
s->device = ioctl(s->group, VFIO_GROUP_GET_DEVICE_FD, device);
|
s->device = ioctl(s->group, VFIO_GROUP_GET_DEVICE_FD, device);
|
||||||
|
|
||||||
if (s->device < 0) {
|
if (s->device < 0) {
|
||||||
@ -365,8 +426,13 @@ static int qemu_vfio_init_pci(QEMUVFIOState *s, const char *device,
|
|||||||
if (ret) {
|
if (ret) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
g_free(iommu_info);
|
||||||
return 0;
|
return 0;
|
||||||
fail:
|
fail:
|
||||||
|
g_free(s->usable_iova_ranges);
|
||||||
|
s->usable_iova_ranges = NULL;
|
||||||
|
s->nb_iova_ranges = 0;
|
||||||
|
g_free(iommu_info);
|
||||||
close(s->group);
|
close(s->group);
|
||||||
fail_container:
|
fail_container:
|
||||||
close(s->container);
|
close(s->container);
|
||||||
@ -716,6 +782,8 @@ void qemu_vfio_close(QEMUVFIOState *s)
|
|||||||
qemu_vfio_undo_mapping(s, &s->mappings[i], NULL);
|
qemu_vfio_undo_mapping(s, &s->mappings[i], NULL);
|
||||||
}
|
}
|
||||||
ram_block_notifier_remove(&s->ram_notifier);
|
ram_block_notifier_remove(&s->ram_notifier);
|
||||||
|
g_free(s->usable_iova_ranges);
|
||||||
|
s->nb_iova_ranges = 0;
|
||||||
qemu_vfio_reset(s);
|
qemu_vfio_reset(s);
|
||||||
close(s->device);
|
close(s->device);
|
||||||
close(s->group);
|
close(s->group);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user