aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlex Williamson <alex.williamson@redhat.com>2012-07-31 10:16:23 -0400
committerAlex Williamson <alex.williamson@redhat.com>2012-07-31 10:16:23 -0400
commit73fa0d10d077d9521ee2dace2307ae2c9a965336 (patch)
tree2c820b194dd8ea00f23d85c382e86ea6c3beb498
parent4a5b2a20ec87384eeb19e70991e7e15a00cad87b (diff)
vfio: Type1 IOMMU implementation
This VFIO IOMMU backend is designed primarily for AMD-Vi and Intel VT-d hardware, but is potentially usable by anything supporting similar mapping functionality. We arbitrarily call this a Type1 backend for lack of a better name. This backend has no IOVA or host memory mapping restrictions for the user and is optimized for relatively static mappings. Mapped areas are pinned into system memory. Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
-rw-r--r--drivers/vfio/Kconfig6
-rw-r--r--drivers/vfio/Makefile2
-rw-r--r--drivers/vfio/vfio.c7
-rw-r--r--drivers/vfio/vfio_iommu_type1.c753
-rw-r--r--include/linux/vfio.h54
5 files changed, 821 insertions, 1 deletions
diff --git a/drivers/vfio/Kconfig b/drivers/vfio/Kconfig
index 9acb1e729bd6..128b97910b8e 100644
--- a/drivers/vfio/Kconfig
+++ b/drivers/vfio/Kconfig
@@ -1,6 +1,12 @@
1config VFIO_IOMMU_TYPE1
2 tristate
3 depends on VFIO
4 default n
5
1menuconfig VFIO 6menuconfig VFIO
2 tristate "VFIO Non-Privileged userspace driver framework" 7 tristate "VFIO Non-Privileged userspace driver framework"
3 depends on IOMMU_API 8 depends on IOMMU_API
9 select VFIO_IOMMU_TYPE1 if X86
4 help 10 help
5 VFIO provides a framework for secure userspace device drivers. 11 VFIO provides a framework for secure userspace device drivers.
6 See Documentation/vfio.txt for more details. 12 See Documentation/vfio.txt for more details.
diff --git a/drivers/vfio/Makefile b/drivers/vfio/Makefile
index 7500a67a42a0..2398d4a0e38b 100644
--- a/drivers/vfio/Makefile
+++ b/drivers/vfio/Makefile
@@ -1 +1,3 @@
1obj-$(CONFIG_VFIO) += vfio.o 1obj-$(CONFIG_VFIO) += vfio.o
2obj-$(CONFIG_VFIO_IOMMU_TYPE1) += vfio_iommu_type1.o
3obj-$(CONFIG_VFIO_PCI) += pci/
diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c
index 052e310aed72..9591e2b509d7 100644
--- a/drivers/vfio/vfio.c
+++ b/drivers/vfio/vfio.c
@@ -1376,6 +1376,13 @@ static int __init vfio_init(void)
1376 1376
1377 pr_info(DRIVER_DESC " version: " DRIVER_VERSION "\n"); 1377 pr_info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
1378 1378
1379 /*
1380 * Attempt to load known iommu-drivers. This gives us a working
1381 * environment without the user needing to explicitly load iommu
1382 * drivers.
1383 */
1384 request_module_nowait("vfio_iommu_type1");
1385
1379 return 0; 1386 return 0;
1380 1387
1381err_groups_cdev: 1388err_groups_cdev:
diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c
new file mode 100644
index 000000000000..6f3fbc48a6c7
--- /dev/null
+++ b/drivers/vfio/vfio_iommu_type1.c
@@ -0,0 +1,753 @@
1/*
2 * VFIO: IOMMU DMA mapping support for Type1 IOMMU
3 *
4 * Copyright (C) 2012 Red Hat, Inc. All rights reserved.
5 * Author: Alex Williamson <alex.williamson@redhat.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 *
11 * Derived from original vfio:
12 * Copyright 2010 Cisco Systems, Inc. All rights reserved.
13 * Author: Tom Lyon, pugs@cisco.com
14 *
15 * We arbitrarily define a Type1 IOMMU as one matching the below code.
16 * It could be called the x86 IOMMU as it's designed for AMD-Vi & Intel
17 * VT-d, but that makes it harder to re-use as theoretically anyone
18 * implementing a similar IOMMU could make use of this. We expect the
19 * IOMMU to support the IOMMU API and have few to no restrictions around
20 * the IOVA range that can be mapped. The Type1 IOMMU is currently
21 * optimized for relatively static mappings of a userspace process with
22 * userpsace pages pinned into memory. We also assume devices and IOMMU
23 * domains are PCI based as the IOMMU API is still centered around a
24 * device/bus interface rather than a group interface.
25 */
26
27#include <linux/compat.h>
28#include <linux/device.h>
29#include <linux/fs.h>
30#include <linux/iommu.h>
31#include <linux/module.h>
32#include <linux/mm.h>
33#include <linux/pci.h> /* pci_bus_type */
34#include <linux/sched.h>
35#include <linux/slab.h>
36#include <linux/uaccess.h>
37#include <linux/vfio.h>
38#include <linux/workqueue.h>
39
40#define DRIVER_VERSION "0.2"
41#define DRIVER_AUTHOR "Alex Williamson <alex.williamson@redhat.com>"
42#define DRIVER_DESC "Type1 IOMMU driver for VFIO"
43
44static bool allow_unsafe_interrupts;
45module_param_named(allow_unsafe_interrupts,
46 allow_unsafe_interrupts, bool, S_IRUGO | S_IWUSR);
47MODULE_PARM_DESC(allow_unsafe_interrupts,
48 "Enable VFIO IOMMU support for on platforms without interrupt remapping support.");
49
50struct vfio_iommu {
51 struct iommu_domain *domain;
52 struct mutex lock;
53 struct list_head dma_list;
54 struct list_head group_list;
55 bool cache;
56};
57
58struct vfio_dma {
59 struct list_head next;
60 dma_addr_t iova; /* Device address */
61 unsigned long vaddr; /* Process virtual addr */
62 long npage; /* Number of pages */
63 int prot; /* IOMMU_READ/WRITE */
64};
65
66struct vfio_group {
67 struct iommu_group *iommu_group;
68 struct list_head next;
69};
70
71/*
72 * This code handles mapping and unmapping of user data buffers
73 * into DMA'ble space using the IOMMU
74 */
75
76#define NPAGE_TO_SIZE(npage) ((size_t)(npage) << PAGE_SHIFT)
77
78struct vwork {
79 struct mm_struct *mm;
80 long npage;
81 struct work_struct work;
82};
83
84/* delayed decrement/increment for locked_vm */
85static void vfio_lock_acct_bg(struct work_struct *work)
86{
87 struct vwork *vwork = container_of(work, struct vwork, work);
88 struct mm_struct *mm;
89
90 mm = vwork->mm;
91 down_write(&mm->mmap_sem);
92 mm->locked_vm += vwork->npage;
93 up_write(&mm->mmap_sem);
94 mmput(mm);
95 kfree(vwork);
96}
97
98static void vfio_lock_acct(long npage)
99{
100 struct vwork *vwork;
101 struct mm_struct *mm;
102
103 if (!current->mm)
104 return; /* process exited */
105
106 if (down_write_trylock(&current->mm->mmap_sem)) {
107 current->mm->locked_vm += npage;
108 up_write(&current->mm->mmap_sem);
109 return;
110 }
111
112 /*
113 * Couldn't get mmap_sem lock, so must setup to update
114 * mm->locked_vm later. If locked_vm were atomic, we
115 * wouldn't need this silliness
116 */
117 vwork = kmalloc(sizeof(struct vwork), GFP_KERNEL);
118 if (!vwork)
119 return;
120 mm = get_task_mm(current);
121 if (!mm) {
122 kfree(vwork);
123 return;
124 }
125 INIT_WORK(&vwork->work, vfio_lock_acct_bg);
126 vwork->mm = mm;
127 vwork->npage = npage;
128 schedule_work(&vwork->work);
129}
130
131/*
132 * Some mappings aren't backed by a struct page, for example an mmap'd
133 * MMIO range for our own or another device. These use a different
134 * pfn conversion and shouldn't be tracked as locked pages.
135 */
136static bool is_invalid_reserved_pfn(unsigned long pfn)
137{
138 if (pfn_valid(pfn)) {
139 bool reserved;
140 struct page *tail = pfn_to_page(pfn);
141 struct page *head = compound_trans_head(tail);
142 reserved = !!(PageReserved(head));
143 if (head != tail) {
144 /*
145 * "head" is not a dangling pointer
146 * (compound_trans_head takes care of that)
147 * but the hugepage may have been split