Commit 82d26c4f authored by Pawel Wodkowski's avatar Pawel Wodkowski Committed by Piotr Pelplinski
Browse files

vhost: track VFIO mappings



If more than one controller is added to guest the same memory table is
set multiple times making VFIO registration/unregistration failures

Change-Id: Ib55c38e292495e549c070beb0234e73f269e63d5
Signed-off-by: default avatarPawel Wodkowski <pawelx.wodkowski@intel.com>
parent f2c8d0dc
Loading
Loading
Loading
Loading
+72 −2
Original line number Diff line number Diff line
@@ -42,9 +42,21 @@

#include "vhost_iommu.h"

struct vfio_map {
	uint64_t iova;
	uint64_t size;
	size_t ref;
};

static struct {
	int need_init;
	int container_fd;


	pthread_mutex_t map_lock;
	struct vfio_map *maps;
	size_t maps_count;
	size_t maps_max_count;
} vfio_cfg = { 1, -1 };

/* Internal DPDK function forward declaration */
@@ -96,6 +108,7 @@ vfio_cfg_init(void)
		return -1;
	}

	pthread_mutex_init(&vfio_cfg.map_lock, NULL);
	return 0;
}

@@ -146,18 +159,72 @@ vfio_pci_memory_region_unmap(int vfio_container_fd, uint64_t phys_addr, uint64_t
static int
vfio_pci_memory_region_op(uint64_t vaddr, uint64_t phys_addr, uint64_t size, int op)
{
	int ret = 0;
	size_t idx;
	struct vfio_map *map = vfio_cfg.maps;
	bool found = false;

	if (vfio_cfg.container_fd == -1) {
		return 0;
	}

	for (idx = 0; idx < vfio_cfg.maps_count; idx++, map++) {
		assert(map->ref);
		if (map->iova == phys_addr && map->size == size) {
			found = true;
			break;
		}
	}

	if (op == VFIO_IOMMU_MAP_DMA) {
		return vfio_pci_memory_region_map(vfio_cfg.container_fd, vaddr, phys_addr, size);
		if (found) {
			map->ref++;
			return 0;
		}

		ret = vfio_pci_memory_region_map(vfio_cfg.container_fd, vaddr, phys_addr, size);
		if (ret) {
			return ret;
		}

		if (vfio_cfg.maps_count == vfio_cfg.maps_max_count) {
			vfio_cfg.maps_max_count += 128;
			vfio_cfg.maps = realloc(vfio_cfg.maps, vfio_cfg.maps_max_count * sizeof(vfio_cfg.maps[0]));
			map = &vfio_cfg.maps[idx];
		}

		vfio_cfg.maps_count++;
		map->iova = phys_addr;
		map->size = size;
		map->ref = 1;
	} else {
		return vfio_pci_memory_region_unmap(vfio_cfg.container_fd, phys_addr, size);
		if (!found) {
			SPDK_ERRLOG("Region vaddr=%p phys_addr=%p len=%#"PRIx64" not VFIO DMA mapped\n",
				    (void *)vaddr, (void *)phys_addr, size);
			return -1;
		}

		map->ref--;
		if (!map->ref) {
			vfio_cfg.maps_count--;
			if (vfio_cfg.maps_count != idx) {
				memmove(map, map + 1, (vfio_cfg.maps_count - idx) * sizeof(map[0]));
			}

			if (vfio_cfg.maps_count == 0) {
				free(vfio_cfg.maps);
				vfio_cfg.maps = NULL;
				vfio_cfg.maps_count = 0;
				vfio_cfg.maps_max_count = 0;
			}

			ret = vfio_pci_memory_region_unmap(vfio_cfg.container_fd, phys_addr, size);
		}
	}

	return ret;
}


#define SHIFT_2MB	21 /* (1 << 21) == 2MB */
#define MASK_2MB	((1ULL << SHIFT_2MB) - 1)
@@ -177,6 +244,8 @@ spdk_vfio_mem_op(uint64_t addr, uint64_t len, int dma_op)
		return 0;
	}

	pthread_mutex_lock(&vfio_cfg.map_lock);

	vaddr = addr;
	while (len > 0) {
		vlen = spdk_min(len_2mb - (vaddr & MASK_2MB), len);
@@ -213,6 +282,7 @@ spdk_vfio_mem_op(uint64_t addr, uint64_t len, int dma_op)
		spdk_vfio_mem_op(addr, vaddr - addr, VFIO_IOMMU_UNMAP_DMA);
	}

	pthread_mutex_unlock(&vfio_cfg.map_lock);
	return ret;
}