diff options
| -rw-r--r-- | drivers/vfio/vfio_iommu_type1.c | 637 | ||||
| -rw-r--r-- | include/uapi/linux/vfio.h | 1 |
2 files changed, 336 insertions, 302 deletions
diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c index 4fb7a8f83c8a..8c7bb9befdab 100644 --- a/drivers/vfio/vfio_iommu_type1.c +++ b/drivers/vfio/vfio_iommu_type1.c | |||
| @@ -30,7 +30,6 @@ | |||
| 30 | #include <linux/iommu.h> | 30 | #include <linux/iommu.h> |
| 31 | #include <linux/module.h> | 31 | #include <linux/module.h> |
| 32 | #include <linux/mm.h> | 32 | #include <linux/mm.h> |
| 33 | #include <linux/pci.h> /* pci_bus_type */ | ||
| 34 | #include <linux/rbtree.h> | 33 | #include <linux/rbtree.h> |
| 35 | #include <linux/sched.h> | 34 | #include <linux/sched.h> |
| 36 | #include <linux/slab.h> | 35 | #include <linux/slab.h> |
| @@ -55,11 +54,17 @@ MODULE_PARM_DESC(disable_hugepages, | |||
| 55 | "Disable VFIO IOMMU support for IOMMU hugepages."); | 54 | "Disable VFIO IOMMU support for IOMMU hugepages."); |
| 56 | 55 | ||
| 57 | struct vfio_iommu { | 56 | struct vfio_iommu { |
| 58 | struct iommu_domain *domain; | 57 | struct list_head domain_list; |
| 59 | struct mutex lock; | 58 | struct mutex lock; |
| 60 | struct rb_root dma_list; | 59 | struct rb_root dma_list; |
| 60 | bool v2; | ||
| 61 | }; | ||
| 62 | |||
| 63 | struct vfio_domain { | ||
| 64 | struct iommu_domain *domain; | ||
| 65 | struct list_head next; | ||
| 61 | struct list_head group_list; | 66 | struct list_head group_list; |
| 62 | bool cache; | 67 | int prot; /* IOMMU_CACHE */ |
| 63 | }; | 68 | }; |
| 64 | 69 | ||
| 65 | struct vfio_dma { | 70 | struct vfio_dma { |
| @@ -99,7 +104,7 @@ static struct vfio_dma *vfio_find_dma(struct vfio_iommu *iommu, | |||
| 99 | return NULL; | 104 | return NULL; |
| 100 | } | 105 | } |
| 101 | 106 | ||
| 102 | static void vfio_insert_dma(struct vfio_iommu *iommu, struct vfio_dma *new) | 107 | static void vfio_link_dma(struct vfio_iommu *iommu, struct vfio_dma *new) |
| 103 | { | 108 | { |
| 104 | struct rb_node **link = &iommu->dma_list.rb_node, *parent = NULL; | 109 | struct rb_node **link = &iommu->dma_list.rb_node, *parent = NULL; |
| 105 | struct vfio_dma *dma; | 110 | struct vfio_dma *dma; |
| @@ -118,7 +123,7 @@ static void vfio_insert_dma(struct vfio_iommu *iommu, struct vfio_dma *new) | |||
| 118 | rb_insert_color(&new->node, &iommu->dma_list); | 123 | rb_insert_color(&new->node, &iommu->dma_list); |
| 119 | } | 124 | } |
| 120 | 125 | ||
| 121 | static void vfio_remove_dma(struct vfio_iommu *iommu, struct vfio_dma *old) | 126 | static void vfio_unlink_dma(struct vfio_iommu *iommu, struct vfio_dma *old) |
| 122 | { | 127 | { |
| 123 | rb_erase(&old->node, &iommu->dma_list); | 128 | rb_erase(&old->node, &iommu->dma_list); |
| 124 | } | 129 | } |
| @@ -322,32 +327,39 @@ static long vfio_unpin_pages(unsigned long pfn, long npage, | |||
| 322 | return unlocked; | 327 | return unlocked; |
| 323 | } | 328 | } |
| 324 | 329 | ||
| 325 | static int vfio_unmap_unpin(struct vfio_iommu *iommu, struct vfio_dma *dma, | 330 | static void vfio_unmap_unpin(struct vfio_iommu *iommu, struct vfio_dma *dma) |
| 326 | dma_addr_t iova, size_t *size) | ||
| 327 | { | 331 | { |
| 328 | dma_addr_t start = iova, end = iova + *size; | 332 | dma_addr_t iova = dma->iova, end = dma->iova + dma->size; |
| 333 | struct vfio_domain *domain, *d; | ||
| 329 | long unlocked = 0; | 334 | long unlocked = 0; |
| 330 | 335 | ||
| 336 | if (!dma->size) | ||
| 337 | return; | ||
| 338 | /* | ||
| 339 | * We use the IOMMU to track the physical addresses, otherwise we'd | ||
| 340 | * need a much more complicated tracking system. Unfortunately that | ||
| 341 | * means we need to use one of the iommu domains to figure out the | ||
| 342 | * pfns to unpin. The rest need to be unmapped in advance so we have | ||
| 343 | * no iommu translations remaining when the pages are unpinned. | ||
| 344 | */ | ||
| 345 | domain = d = list_first_entry(&iommu->domain_list, | ||
| 346 | struct vfio_domain, next); | ||
| 347 | |||
| 348 | list_for_each_entry_continue(d, &iommu->domain_list, next) | ||
| 349 | iommu_unmap(d->domain, dma->iova, dma->size); | ||
| 350 | |||
| 331 | while (iova < end) { | 351 | while (iova < end) { |
| 332 | size_t unmapped; | 352 | size_t unmapped; |
| 333 | phys_addr_t phys; | 353 | phys_addr_t phys; |
| 334 | 354 | ||
| 335 | /* | 355 | phys = iommu_iova_to_phys(domain->domain, iova); |
| 336 | * We use the IOMMU to track the physical address. This | ||
| 337 | * saves us from having a lot more entries in our mapping | ||
| 338 | * tree. The downside is that we don't track the size | ||
| 339 | * used to do the mapping. We request unmap of a single | ||
| 340 | * page, but expect IOMMUs that support large pages to | ||
| 341 | * unmap a larger chunk. | ||
| 342 | */ | ||
| 343 | phys = iommu_iova_to_phys(iommu->domain, iova); | ||
| 344 | if (WARN_ON(!phys)) { | 356 | if (WARN_ON(!phys)) { |
| 345 | iova += PAGE_SIZE; | 357 | iova += PAGE_SIZE; |
| 346 | continue; | 358 | continue; |
| 347 | } | 359 | } |
| 348 | 360 | ||
| 349 | unmapped = iommu_unmap(iommu->domain, iova, PAGE_SIZE); | 361 | unmapped = iommu_unmap(domain->domain, iova, PAGE_SIZE); |
| 350 | if (!unmapped) | 362 | if (WARN_ON(!unmapped)) |
| 351 | break; | 363 | break; |
| 352 | 364 | ||
| 353 | unlocked += vfio_unpin_pages(phys >> PAGE_SHIFT, | 365 | unlocked += vfio_unpin_pages(phys >> PAGE_SHIFT, |
| @@ -357,119 +369,26 @@ static int vfio_unmap_unpin(struct vfio_iommu *iommu, struct vfio_dma *dma, | |||
| 357 | } | 369 | } |
| 358 | 370 | ||
| 359 | vfio_lock_acct(-unlocked); | 371 | vfio_lock_acct(-unlocked); |
| 360 | |||
| 361 | *size = iova - start; | ||
| 362 | |||
| 363 | return 0; | ||
| 364 | } | 372 | } |
| 365 | 373 | ||
| 366 | static int vfio_remove_dma_overlap(struct vfio_iommu *iommu, dma_addr_t start, | 374 | static void vfio_remove_dma(struct vfio_iommu *iommu, struct vfio_dma *dma) |
| 367 | size_t *size, struct vfio_dma *dma) | ||
| 368 | { | 375 | { |
| 369 | size_t offset, overlap, tmp; | 376 | vfio_unmap_unpin(iommu, dma); |
| 370 | struct vfio_dma *split; | 377 | vfio_unlink_dma(iommu, dma); |
| 371 | int ret; | 378 | kfree(dma); |
| 372 | 379 | } | |
| 373 | if (!*size) | ||
| 374 | return 0; | ||
| 375 | |||
| 376 | /* | ||
| 377 | * Existing dma region is completely covered, unmap all. This is | ||
| 378 | * the likely case since userspace tends to map and unmap buffers | ||
| 379 | * in one shot rather than multiple mappings within a buffer. | ||
| 380 | */ | ||
| 381 | if (likely(start <= dma->iova && | ||
| 382 | start + *size >= dma->iova + dma->size)) { | ||
| 383 | *size = dma->size; | ||
| 384 | ret = vfio_unmap_unpin(iommu, dma, dma->iova, size); | ||
| 385 | if (ret) | ||
| 386 | return ret; | ||
| 387 | |||
| 388 | /* | ||
| 389 | * Did we remove more than we have? Should never happen | ||
| 390 | * since a vfio_dma is contiguous in iova and vaddr. | ||
| 391 | */ | ||
| 392 | WARN_ON(*size != dma->size); | ||
| 393 | |||
| 394 | vfio_remove_dma(iommu, dma); | ||
| 395 | kfree(dma); | ||
| 396 | return 0; | ||
| 397 | } | ||
| 398 | |||
| 399 | /* Overlap low address of existing range */ | ||
| 400 | if (start <= dma->iova) { | ||
| 401 | overlap = start + *size - dma->iova; | ||
| 402 | ret = vfio_unmap_unpin(iommu, dma, dma->iova, &overlap); | ||
| 403 | if (ret) | ||
| 404 | return ret; | ||
| 405 | |||
| 406 | vfio_remove_dma(iommu, dma); | ||
| 407 | |||
| 408 | /* | ||
| 409 | * Check, we may have removed to whole vfio_dma. If not | ||
| 410 | * fixup and re-insert. | ||
| 411 | */ | ||
| 412 | if (overlap < dma->size) { | ||
| 413 | dma->iova += overlap; | ||
| 414 | dma->vaddr += overlap; | ||
| 415 | dma->size -= overlap; | ||
| 416 | vfio_insert_dma(iommu, dma); | ||
| 417 | } else | ||
| 418 | kfree(dma); | ||
| 419 | |||
| 420 | *size = overlap; | ||
| 421 | return 0; | ||
| 422 | } | ||
| 423 | |||
| 424 | |||
