Skip to content

Commit

Permalink
vfio: Generalize region support
Browse files Browse the repository at this point in the history
Both platform and PCI vfio drivers create a "slow", I/O memory region
with one or more mmap memory regions overlayed when supported by the
device. Generalize this to a set of common helpers in the core that
pulls the region info from vfio, fills the region data, configures
slow mapping, and adds helpers for comleting the mmap, enable/disable,
and teardown.  This can be immediately used by the PCI MSI-X code,
which needs to mmap around the MSI-X vector table.

This also changes VFIORegion.mem to be dynamically allocated because
otherwise we don't know how the caller has allocated VFIORegion and
therefore don't know whether to unreference it to destroy the
MemoryRegion or not.

Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
  • Loading branch information
awilliam committed Mar 11, 2016
1 parent 4690022 commit db0da02
Show file tree
Hide file tree
Showing 7 changed files with 283 additions and 190 deletions.
4 changes: 2 additions & 2 deletions hw/arm/sysbus-fdt.c
Expand Up @@ -240,7 +240,7 @@ static int add_calxeda_midway_xgmac_fdt_node(SysBusDevice *sbdev, void *opaque)
mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, i);
reg_attr[2 * i] = cpu_to_be32(mmio_base);
reg_attr[2 * i + 1] = cpu_to_be32(
memory_region_size(&vdev->regions[i]->mem));
memory_region_size(vdev->regions[i]->mem));
}
qemu_fdt_setprop(fdt, nodename, "reg", reg_attr,
vbasedev->num_regions * 2 * sizeof(uint32_t));
Expand Down Expand Up @@ -374,7 +374,7 @@ static int add_amd_xgbe_fdt_node(SysBusDevice *sbdev, void *opaque)
mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, i);
reg_attr[2 * i] = cpu_to_be32(mmio_base);
reg_attr[2 * i + 1] = cpu_to_be32(
memory_region_size(&vdev->regions[i]->mem));
memory_region_size(vdev->regions[i]->mem));
}
qemu_fdt_setprop(guest_fdt, nodename, "reg", reg_attr,
vbasedev->num_regions * 2 * sizeof(uint32_t));
Expand Down
172 changes: 144 additions & 28 deletions hw/vfio/common.c
Expand Up @@ -493,46 +493,162 @@ static void vfio_listener_release(VFIOContainer *container)
memory_listener_unregister(&container->listener);
}

int vfio_mmap_region(Object *obj, VFIORegion *region,
MemoryRegion *mem, MemoryRegion *submem,
void **map, size_t size, off_t offset,
const char *name)
int vfio_region_setup(Object *obj, VFIODevice *vbasedev, VFIORegion *region,
int index, const char *name)
{
int ret = 0;
VFIODevice *vbasedev = region->vbasedev;
struct vfio_region_info *info;
int ret;

ret = vfio_get_region_info(vbasedev, index, &info);
if (ret) {
return ret;
}

region->vbasedev = vbasedev;
region->flags = info->flags;
region->size = info->size;
region->fd_offset = info->offset;
region->nr = index;

if (!vbasedev->no_mmap && size && region->flags &
VFIO_REGION_INFO_FLAG_MMAP) {
int prot = 0;
if (region->size) {
region->mem = g_new0(MemoryRegion, 1);
memory_region_init_io(region->mem, obj, &vfio_region_ops,
region, name, region->size);

if (region->flags & VFIO_REGION_INFO_FLAG_READ) {
prot |= PROT_READ;
if (!vbasedev->no_mmap &&
region->flags & VFIO_REGION_INFO_FLAG_MMAP &&
!(region->size & ~qemu_real_host_page_mask)) {

region->nr_mmaps = 1;
region->mmaps = g_new0(VFIOMmap, region->nr_mmaps);

region->mmaps[0].offset = 0;
region->mmaps[0].size = region->size;
}
}

g_free(info);

trace_vfio_region_setup(vbasedev->name, index, name,
region->flags, region->fd_offset, region->size);
return 0;
}

if (region->flags & VFIO_REGION_INFO_FLAG_WRITE) {
prot |= PROT_WRITE;
int vfio_region_mmap(VFIORegion *region)
{
int i, prot = 0;
char *name;

if (!region->mem) {
return 0;
}

prot |= region->flags & VFIO_REGION_INFO_FLAG_READ ? PROT_READ : 0;
prot |= region->flags & VFIO_REGION_INFO_FLAG_WRITE ? PROT_WRITE : 0;

for (i = 0; i < region->nr_mmaps; i++) {
region->mmaps[i].mmap = mmap(NULL, region->mmaps[i].size, prot,
MAP_SHARED, region->vbasedev->fd,
region->fd_offset +
region->mmaps[i].offset);
if (region->mmaps[i].mmap == MAP_FAILED) {
int ret = -errno;

trace_vfio_region_mmap_fault(memory_region_name(region->mem), i,
region->fd_offset +
region->mmaps[i].offset,
region->fd_offset +
region->mmaps[i].offset +
region->mmaps[i].size - 1, ret);

region->mmaps[i].mmap = NULL;

for (i--; i >= 0; i--) {
memory_region_del_subregion(region->mem, &region->mmaps[i].mem);
munmap(region->mmaps[i].mmap, region->mmaps[i].size);
object_unparent(OBJECT(&region->mmaps[i].mem));
region->mmaps[i].mmap = NULL;
}

return ret;
}

*map = mmap(NULL, size, prot, MAP_SHARED,
vbasedev->fd,
region->fd_offset + offset);
if (*map == MAP_FAILED) {
*map = NULL;
ret = -errno;
goto empty_region;
name = g_strdup_printf("%s mmaps[%d]",
memory_region_name(region->mem), i);
memory_region_init_ram_ptr(&region->mmaps[i].mem,
memory_region_owner(region->mem),
name, region->mmaps[i].size,
region->mmaps[i].mmap);
g_free(name);
memory_region_set_skip_dump(&region->mmaps[i].mem);
memory_region_add_subregion(region->mem, region->mmaps[i].offset,
&region->mmaps[i].mem);

trace_vfio_region_mmap(memory_region_name(&region->mmaps[i].mem),
region->mmaps[i].offset,
region->mmaps[i].offset +
region->mmaps[i].size - 1);
}

return 0;
}

void vfio_region_exit(VFIORegion *region)
{
int i;

if (!region->mem) {
return;
}

for (i = 0; i < region->nr_mmaps; i++) {
if (region->mmaps[i].mmap) {
memory_region_del_subregion(region->mem, &region->mmaps[i].mem);
}
}

memory_region_init_ram_ptr(submem, obj, name, size, *map);
memory_region_set_skip_dump(submem);
} else {
empty_region:
/* Create a zero sized sub-region to make cleanup easy. */
memory_region_init(submem, obj, name, 0);
trace_vfio_region_exit(region->vbasedev->name, region->nr);
}

void vfio_region_finalize(VFIORegion *region)
{
int i;

if (!region->mem) {
return;
}

memory_region_add_subregion(mem, offset, submem);
for (i = 0; i < region->nr_mmaps; i++) {
if (region->mmaps[i].mmap) {
munmap(region->mmaps[i].mmap, region->mmaps[i].size);
object_unparent(OBJECT(&region->mmaps[i].mem));
}
}

return ret;
object_unparent(OBJECT(region->mem));

g_free(region->mem);
g_free(region->mmaps);

trace_vfio_region_finalize(region->vbasedev->name, region->nr);
}

void vfio_region_mmaps_set_enabled(VFIORegion *region, bool enabled)
{
int i;

if (!region->mem) {
return;
}

for (i = 0; i < region->nr_mmaps; i++) {
if (region->mmaps[i].mmap) {
memory_region_set_enabled(&region->mmaps[i].mem, enabled);
}
}

trace_vfio_region_mmaps_set_enabled(memory_region_name(region->mem),
enabled);
}

void vfio_reset_handler(void *opaque)
Expand Down
24 changes: 12 additions & 12 deletions hw/vfio/pci-quirks.c
Expand Up @@ -337,14 +337,14 @@ static void vfio_probe_ati_bar4_quirk(VFIOPCIDevice *vdev, int nr)
memory_region_init_io(window->addr_mem, OBJECT(vdev),
&vfio_generic_window_address_quirk, window,
"vfio-ati-bar4-window-address-quirk", 4);
memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
memory_region_add_subregion_overlap(vdev->bars[nr].region.mem,
window->address_offset,
window->addr_mem, 1);

memory_region_init_io(window->data_mem, OBJECT(vdev),
&vfio_generic_window_data_quirk, window,
"vfio-ati-bar4-window-data-quirk", 4);
memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
memory_region_add_subregion_overlap(vdev->bars[nr].region.mem,
window->data_offset,
window->data_mem, 1);

Expand Down Expand Up @@ -378,7 +378,7 @@ static void vfio_probe_ati_bar2_quirk(VFIOPCIDevice *vdev, int nr)
memory_region_init_io(mirror->mem, OBJECT(vdev),
&vfio_generic_mirror_quirk, mirror,
"vfio-ati-bar2-4000-quirk", PCI_CONFIG_SPACE_SIZE);
memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
memory_region_add_subregion_overlap(vdev->bars[nr].region.mem,
mirror->offset, mirror->mem, 1);

QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
Expand Down Expand Up @@ -683,29 +683,29 @@ static void vfio_probe_nvidia_bar5_quirk(VFIOPCIDevice *vdev, int nr)
memory_region_init_io(window->addr_mem, OBJECT(vdev),
&vfio_generic_window_address_quirk, window,
"vfio-nvidia-bar5-window-address-quirk", 4);
memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
memory_region_add_subregion_overlap(vdev->bars[nr].region.mem,
window->address_offset,
window->addr_mem, 1);
memory_region_set_enabled(window->addr_mem, false);

memory_region_init_io(window->data_mem, OBJECT(vdev),
&vfio_generic_window_data_quirk, window,
"vfio-nvidia-bar5-window-data-quirk", 4);
memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
memory_region_add_subregion_overlap(vdev->bars[nr].region.mem,
window->data_offset,
window->data_mem, 1);
memory_region_set_enabled(window->data_mem, false);

memory_region_init_io(&quirk->mem[2], OBJECT(vdev),
&vfio_nvidia_bar5_quirk_master, bar5,
"vfio-nvidia-bar5-master-quirk", 4);
memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
memory_region_add_subregion_overlap(vdev->bars[nr].region.mem,
0, &quirk->mem[2], 1);

memory_region_init_io(&quirk->mem[3], OBJECT(vdev),
&vfio_nvidia_bar5_quirk_enable, bar5,
"vfio-nvidia-bar5-enable-quirk", 4);
memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
memory_region_add_subregion_overlap(vdev->bars[nr].region.mem,
4, &quirk->mem[3], 1);

QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
Expand Down Expand Up @@ -767,7 +767,7 @@ static void vfio_probe_nvidia_bar0_quirk(VFIOPCIDevice *vdev, int nr)
&vfio_nvidia_mirror_quirk, mirror,
"vfio-nvidia-bar0-88000-mirror-quirk",
vdev->config_size);
memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
memory_region_add_subregion_overlap(vdev->bars[nr].region.mem,
mirror->offset, mirror->mem, 1);

QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
Expand All @@ -786,7 +786,7 @@ static void vfio_probe_nvidia_bar0_quirk(VFIOPCIDevice *vdev, int nr)
&vfio_nvidia_mirror_quirk, mirror,
"vfio-nvidia-bar0-1800-mirror-quirk",
PCI_CONFIG_SPACE_SIZE);
memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
memory_region_add_subregion_overlap(vdev->bars[nr].region.mem,
mirror->offset, mirror->mem, 1);

QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
Expand Down Expand Up @@ -947,13 +947,13 @@ static void vfio_probe_rtl8168_bar2_quirk(VFIOPCIDevice *vdev, int nr)
memory_region_init_io(&quirk->mem[0], OBJECT(vdev),
&vfio_rtl_address_quirk, rtl,
"vfio-rtl8168-window-address-quirk", 4);
memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
memory_region_add_subregion_overlap(vdev->bars[nr].region.mem,
0x74, &quirk->mem[0], 1);

memory_region_init_io(&quirk->mem[1], OBJECT(vdev),
&vfio_rtl_data_quirk, rtl,
"vfio-rtl8168-window-data-quirk", 4);
memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
memory_region_add_subregion_overlap(vdev->bars[nr].region.mem,
0x70, &quirk->mem[1], 1);

QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
Expand Down Expand Up @@ -1020,7 +1020,7 @@ void vfio_bar_quirk_teardown(VFIOPCIDevice *vdev, int nr)

QLIST_FOREACH(quirk, &bar->quirks, next) {
for (i = 0; i < quirk->nr_mem; i++) {
memory_region_del_subregion(&bar->region.mem, &quirk->mem[i]);
memory_region_del_subregion(bar->region.mem, &quirk->mem[i]);
}
}
}
Expand Down

0 comments on commit db0da02

Please sign in to comment.