aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/vfio
diff options
context:
space:
mode:
authorAlex Williamson <alex.williamson@redhat.com>2014-02-26 13:38:36 -0500
committerAlex Williamson <alex.williamson@redhat.com>2014-02-26 13:38:36 -0500
commit1ef3e2bc04223ff956dc62abaf2dff1f3322a431 (patch)
treeff3d2b15264d6a8fec4b7780d80fc8ca79a997f4 /drivers/vfio
parentcfbf8d4857c26a8a307fb7cd258074c9dcd8c691 (diff)
vfio/iommu_type1: Multi-IOMMU domain support
We currently have a problem that we cannot support advanced features of an IOMMU domain (ex. IOMMU_CACHE), because we have no guarantee that those features will be supported by all of the hardware units involved with the domain over its lifetime. For instance, the Intel VT-d architecture does not require that all DRHDs support snoop control. If we create a domain based on a device behind a DRHD that does support snoop control and enable SNP support via the IOMMU_CACHE mapping option, we cannot then add a device behind a DRHD which does not support snoop control or we'll get reserved bit faults from the SNP bit in the pagetables. To add to the complexity, we can't know the properties of a domain until a device is attached. We could pass this problem off to userspace and require that a separate vfio container be used, but we don't know how to handle page accounting in that case. How do we know that a page pinned in one container is the same page as a different container and avoid double billing the user for the page. The solution is therefore to support multiple IOMMU domains per container. In the majority of cases, only one domain will be required since hardware is typically consistent within a system. However, this provides us the ability to validate compatibility of domains and support mixed environments where page table flags can be different between domains. To do this, our DMA tracking needs to change. We currently try to coalesce user mappings into as few tracking entries as possible. The problem then becomes that we lose granularity of user mappings. We've never guaranteed that a user is able to unmap at a finer granularity than the original mapping, but we must honor the granularity of the original mapping. This coalescing code is therefore removed, allowing only unmaps covering complete maps. The change in accounting is fairly small here, a typical QEMU VM will start out with roughly a dozen entries, so it's arguable if this coalescing was ever needed. We also move IOMMU domain creation to the point where a group is attached to the container. An interesting side-effect of this is that we now have access to the device at the time of domain creation and can probe the devices within the group to determine the bus_type. This finally makes vfio_iommu_type1 completely device/bus agnostic. In fact, each IOMMU domain can host devices on different buses managed by different physical IOMMUs, and present a single DMA mapping interface to the user. When a new domain is created, mappings are replayed to bring the IOMMU pagetables up to the state of the current container. And of course, DMA mapping and unmapping automatically traverse all of the configured IOMMU domains. Signed-off-by: Alex Williamson <alex.williamson@redhat.com> Cc: Varun Sethi <Varun.Sethi@freescale.com>
Diffstat (limited to 'drivers/vfio')
-rw-r--r--drivers/vfio/vfio_iommu_type1.c637
1 files changed, 335 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
57struct vfio_iommu { 56struct 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
63struct 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
65struct vfio_dma { 70struct 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
102static void vfio_insert_dma(struct vfio_iommu *iommu, struct vfio_dma *new) 107static 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
121static void vfio_remove_dma(struct vfio_iommu *iommu, struct vfio_dma *old) 126static 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
325static int vfio_unmap_unpin(struct vfio_iommu *iommu, struct vfio_dma *dma, 330static 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
366static int vfio_remove_dma_overlap(struct vfio_iommu *iommu, dma_addr_t start, 374static 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 /*