Commit fcd95807fb61e67d602610e7ff7129ed769e9fee
1 parent
b146a1c9f7
Exists in
master
and in
4 other branches
kvm: Change kvm_iommu_map_pages to map large pages
This patch changes the implementation of of kvm_iommu_map_pages to map the pages with the host page size into the io virtual address space. Signed-off-by: Joerg Roedel <joerg.roedel@amd.com> Acked-By: Avi Kivity <avi@redhat.com>
Showing 1 changed file with 91 additions and 22 deletions Inline Diff
virt/kvm/iommu.c
1 | /* | 1 | /* |
2 | * Copyright (c) 2006, Intel Corporation. | 2 | * Copyright (c) 2006, Intel Corporation. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify it | 4 | * This program is free software; you can redistribute it and/or modify it |
5 | * under the terms and conditions of the GNU General Public License, | 5 | * under the terms and conditions of the GNU General Public License, |
6 | * version 2, as published by the Free Software Foundation. | 6 | * version 2, as published by the Free Software Foundation. |
7 | * | 7 | * |
8 | * This program is distributed in the hope it will be useful, but WITHOUT | 8 | * This program is distributed in the hope it will be useful, but WITHOUT |
9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
11 | * more details. | 11 | * more details. |
12 | * | 12 | * |
13 | * You should have received a copy of the GNU General Public License along with | 13 | * You should have received a copy of the GNU General Public License along with |
14 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple | 14 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple |
15 | * Place - Suite 330, Boston, MA 02111-1307 USA. | 15 | * Place - Suite 330, Boston, MA 02111-1307 USA. |
16 | * | 16 | * |
17 | * Copyright (C) 2006-2008 Intel Corporation | 17 | * Copyright (C) 2006-2008 Intel Corporation |
18 | * Copyright IBM Corporation, 2008 | 18 | * Copyright IBM Corporation, 2008 |
19 | * Author: Allen M. Kay <allen.m.kay@intel.com> | 19 | * Author: Allen M. Kay <allen.m.kay@intel.com> |
20 | * Author: Weidong Han <weidong.han@intel.com> | 20 | * Author: Weidong Han <weidong.han@intel.com> |
21 | * Author: Ben-Ami Yassour <benami@il.ibm.com> | 21 | * Author: Ben-Ami Yassour <benami@il.ibm.com> |
22 | */ | 22 | */ |
23 | 23 | ||
24 | #include <linux/list.h> | 24 | #include <linux/list.h> |
25 | #include <linux/kvm_host.h> | 25 | #include <linux/kvm_host.h> |
26 | #include <linux/pci.h> | 26 | #include <linux/pci.h> |
27 | #include <linux/dmar.h> | 27 | #include <linux/dmar.h> |
28 | #include <linux/iommu.h> | 28 | #include <linux/iommu.h> |
29 | #include <linux/intel-iommu.h> | 29 | #include <linux/intel-iommu.h> |
30 | 30 | ||
31 | static int kvm_iommu_unmap_memslots(struct kvm *kvm); | 31 | static int kvm_iommu_unmap_memslots(struct kvm *kvm); |
32 | static void kvm_iommu_put_pages(struct kvm *kvm, | 32 | static void kvm_iommu_put_pages(struct kvm *kvm, |
33 | gfn_t base_gfn, unsigned long npages); | 33 | gfn_t base_gfn, unsigned long npages); |
34 | 34 | ||
35 | static pfn_t kvm_pin_pages(struct kvm *kvm, struct kvm_memory_slot *slot, | ||
36 | gfn_t gfn, unsigned long size) | ||
37 | { | ||
38 | gfn_t end_gfn; | ||
39 | pfn_t pfn; | ||
40 | |||
41 | pfn = gfn_to_pfn_memslot(kvm, slot, gfn); | ||
42 | end_gfn = gfn + (size >> PAGE_SHIFT); | ||
43 | gfn += 1; | ||
44 | |||
45 | if (is_error_pfn(pfn)) | ||
46 | return pfn; | ||
47 | |||
48 | while (gfn < end_gfn) | ||
49 | gfn_to_pfn_memslot(kvm, slot, gfn++); | ||
50 | |||
51 | return pfn; | ||
52 | } | ||
53 | |||
35 | int kvm_iommu_map_pages(struct kvm *kvm, struct kvm_memory_slot *slot) | 54 | int kvm_iommu_map_pages(struct kvm *kvm, struct kvm_memory_slot *slot) |
36 | { | 55 | { |
37 | gfn_t gfn = slot->base_gfn; | 56 | gfn_t gfn, end_gfn; |
38 | unsigned long npages = slot->npages; | ||
39 | pfn_t pfn; | 57 | pfn_t pfn; |
40 | int i, r = 0; | 58 | int r = 0; |
41 | struct iommu_domain *domain = kvm->arch.iommu_domain; | 59 | struct iommu_domain *domain = kvm->arch.iommu_domain; |
42 | int flags; | 60 | int flags; |
43 | 61 | ||
44 | /* check if iommu exists and in use */ | 62 | /* check if iommu exists and in use */ |
45 | if (!domain) | 63 | if (!domain) |
46 | return 0; | 64 | return 0; |
47 | 65 | ||
66 | gfn = slot->base_gfn; | ||
67 | end_gfn = gfn + slot->npages; | ||
68 | |||
48 | flags = IOMMU_READ | IOMMU_WRITE; | 69 | flags = IOMMU_READ | IOMMU_WRITE; |
49 | if (kvm->arch.iommu_flags & KVM_IOMMU_CACHE_COHERENCY) | 70 | if (kvm->arch.iommu_flags & KVM_IOMMU_CACHE_COHERENCY) |
50 | flags |= IOMMU_CACHE; | 71 | flags |= IOMMU_CACHE; |
51 | 72 | ||
52 | for (i = 0; i < npages; i++) { | 73 | |
53 | /* check if already mapped */ | 74 | while (gfn < end_gfn) { |
54 | if (iommu_iova_to_phys(domain, gfn_to_gpa(gfn))) | 75 | unsigned long page_size; |
76 | |||
77 | /* Check if already mapped */ | ||
78 | if (iommu_iova_to_phys(domain, gfn_to_gpa(gfn))) { | ||
79 | gfn += 1; | ||
55 | continue; | 80 | continue; |
81 | } | ||
56 | 82 | ||
57 | pfn = gfn_to_pfn_memslot(kvm, slot, gfn); | 83 | /* Get the page size we could use to map */ |
58 | r = iommu_map_range(domain, | 84 | page_size = kvm_host_page_size(kvm, gfn); |
59 | gfn_to_gpa(gfn), | 85 | |
60 | pfn_to_hpa(pfn), | 86 | /* Make sure the page_size does not exceed the memslot */ |
61 | PAGE_SIZE, flags); | 87 | while ((gfn + (page_size >> PAGE_SHIFT)) > end_gfn) |
88 | page_size >>= 1; | ||
89 | |||
90 | /* Make sure gfn is aligned to the page size we want to map */ | ||
91 | while ((gfn << PAGE_SHIFT) & (page_size - 1)) | ||
92 | page_size >>= 1; | ||
93 | |||
94 | /* | ||
95 | * Pin all pages we are about to map in memory. This is | ||
96 | * important because we unmap and unpin in 4kb steps later. | ||
97 | */ | ||
98 | pfn = kvm_pin_pages(kvm, slot, gfn, page_size); | ||
99 | if (is_error_pfn(pfn)) { | ||
100 | gfn += 1; | ||
101 | continue; | ||
102 | } | ||
103 | |||
104 | /* Map into IO address space */ | ||
105 | r = iommu_map(domain, gfn_to_gpa(gfn), pfn_to_hpa(pfn), | ||
106 | get_order(page_size), flags); | ||
62 | if (r) { | 107 | if (r) { |
63 | printk(KERN_ERR "kvm_iommu_map_address:" | 108 | printk(KERN_ERR "kvm_iommu_map_address:" |
64 | "iommu failed to map pfn=%lx\n", pfn); | 109 | "iommu failed to map pfn=%lx\n", pfn); |
65 | goto unmap_pages; | 110 | goto unmap_pages; |
66 | } | 111 | } |
67 | gfn++; | 112 | |
113 | gfn += page_size >> PAGE_SHIFT; | ||
114 | |||
115 | |||
68 | } | 116 | } |
117 | |||
69 | return 0; | 118 | return 0; |
70 | 119 | ||
71 | unmap_pages: | 120 | unmap_pages: |
72 | kvm_iommu_put_pages(kvm, slot->base_gfn, i); | 121 | kvm_iommu_put_pages(kvm, slot->base_gfn, gfn); |
73 | return r; | 122 | return r; |
74 | } | 123 | } |
75 | 124 | ||
76 | static int kvm_iommu_map_memslots(struct kvm *kvm) | 125 | static int kvm_iommu_map_memslots(struct kvm *kvm) |
77 | { | 126 | { |
78 | int i, r = 0; | 127 | int i, r = 0; |
79 | struct kvm_memslots *slots; | 128 | struct kvm_memslots *slots; |
80 | 129 | ||
81 | slots = rcu_dereference(kvm->memslots); | 130 | slots = rcu_dereference(kvm->memslots); |
82 | 131 | ||
83 | for (i = 0; i < slots->nmemslots; i++) { | 132 | for (i = 0; i < slots->nmemslots; i++) { |
84 | r = kvm_iommu_map_pages(kvm, &slots->memslots[i]); | 133 | r = kvm_iommu_map_pages(kvm, &slots->memslots[i]); |
85 | if (r) | 134 | if (r) |
86 | break; | 135 | break; |
87 | } | 136 | } |
88 | 137 | ||
89 | return r; | 138 | return r; |
90 | } | 139 | } |
91 | 140 | ||
92 | int kvm_assign_device(struct kvm *kvm, | 141 | int kvm_assign_device(struct kvm *kvm, |
93 | struct kvm_assigned_dev_kernel *assigned_dev) | 142 | struct kvm_assigned_dev_kernel *assigned_dev) |
94 | { | 143 | { |
95 | struct pci_dev *pdev = NULL; | 144 | struct pci_dev *pdev = NULL; |
96 | struct iommu_domain *domain = kvm->arch.iommu_domain; | 145 | struct iommu_domain *domain = kvm->arch.iommu_domain; |
97 | int r, last_flags; | 146 | int r, last_flags; |
98 | 147 | ||
99 | /* check if iommu exists and in use */ | 148 | /* check if iommu exists and in use */ |
100 | if (!domain) | 149 | if (!domain) |
101 | return 0; | 150 | return 0; |
102 | 151 | ||
103 | pdev = assigned_dev->dev; | 152 | pdev = assigned_dev->dev; |
104 | if (pdev == NULL) | 153 | if (pdev == NULL) |
105 | return -ENODEV; | 154 | return -ENODEV; |
106 | 155 | ||
107 | r = iommu_attach_device(domain, &pdev->dev); | 156 | r = iommu_attach_device(domain, &pdev->dev); |
108 | if (r) { | 157 | if (r) { |
109 | printk(KERN_ERR "assign device %x:%x:%x.%x failed", | 158 | printk(KERN_ERR "assign device %x:%x:%x.%x failed", |
110 | pci_domain_nr(pdev->bus), | 159 | pci_domain_nr(pdev->bus), |
111 | pdev->bus->number, | 160 | pdev->bus->number, |
112 | PCI_SLOT(pdev->devfn), | 161 | PCI_SLOT(pdev->devfn), |
113 | PCI_FUNC(pdev->devfn)); | 162 | PCI_FUNC(pdev->devfn)); |
114 | return r; | 163 | return r; |
115 | } | 164 | } |
116 | 165 | ||
117 | last_flags = kvm->arch.iommu_flags; | 166 | last_flags = kvm->arch.iommu_flags; |
118 | if (iommu_domain_has_cap(kvm->arch.iommu_domain, | 167 | if (iommu_domain_has_cap(kvm->arch.iommu_domain, |
119 | IOMMU_CAP_CACHE_COHERENCY)) | 168 | IOMMU_CAP_CACHE_COHERENCY)) |
120 | kvm->arch.iommu_flags |= KVM_IOMMU_CACHE_COHERENCY; | 169 | kvm->arch.iommu_flags |= KVM_IOMMU_CACHE_COHERENCY; |
121 | 170 | ||
122 | /* Check if need to update IOMMU page table for guest memory */ | 171 | /* Check if need to update IOMMU page table for guest memory */ |
123 | if ((last_flags ^ kvm->arch.iommu_flags) == | 172 | if ((last_flags ^ kvm->arch.iommu_flags) == |
124 | KVM_IOMMU_CACHE_COHERENCY) { | 173 | KVM_IOMMU_CACHE_COHERENCY) { |
125 | kvm_iommu_unmap_memslots(kvm); | 174 | kvm_iommu_unmap_memslots(kvm); |
126 | r = kvm_iommu_map_memslots(kvm); | 175 | r = kvm_iommu_map_memslots(kvm); |
127 | if (r) | 176 | if (r) |
128 | goto out_unmap; | 177 | goto out_unmap; |
129 | } | 178 | } |
130 | 179 | ||
131 | printk(KERN_DEBUG "assign device %x:%x:%x.%x\n", | 180 | printk(KERN_DEBUG "assign device %x:%x:%x.%x\n", |
132 | assigned_dev->host_segnr, | 181 | assigned_dev->host_segnr, |
133 | assigned_dev->host_busnr, | 182 | assigned_dev->host_busnr, |
134 | PCI_SLOT(assigned_dev->host_devfn), | 183 | PCI_SLOT(assigned_dev->host_devfn), |
135 | PCI_FUNC(assigned_dev->host_devfn)); | 184 | PCI_FUNC(assigned_dev->host_devfn)); |
136 | 185 | ||
137 | return 0; | 186 | return 0; |
138 | out_unmap: | 187 | out_unmap: |
139 | kvm_iommu_unmap_memslots(kvm); | 188 | kvm_iommu_unmap_memslots(kvm); |
140 | return r; | 189 | return r; |
141 | } | 190 | } |
142 | 191 | ||
143 | int kvm_deassign_device(struct kvm *kvm, | 192 | int kvm_deassign_device(struct kvm *kvm, |
144 | struct kvm_assigned_dev_kernel *assigned_dev) | 193 | struct kvm_assigned_dev_kernel *assigned_dev) |
145 | { | 194 | { |
146 | struct iommu_domain *domain = kvm->arch.iommu_domain; | 195 | struct iommu_domain *domain = kvm->arch.iommu_domain; |
147 | struct pci_dev *pdev = NULL; | 196 | struct pci_dev *pdev = NULL; |
148 | 197 | ||
149 | /* check if iommu exists and in use */ | 198 | /* check if iommu exists and in use */ |
150 | if (!domain) | 199 | if (!domain) |
151 | return 0; | 200 | return 0; |
152 | 201 | ||
153 | pdev = assigned_dev->dev; | 202 | pdev = assigned_dev->dev; |
154 | if (pdev == NULL) | 203 | if (pdev == NULL) |
155 | return -ENODEV; | 204 | return -ENODEV; |
156 | 205 | ||
157 | iommu_detach_device(domain, &pdev->dev); | 206 | iommu_detach_device(domain, &pdev->dev); |
158 | 207 | ||
159 | printk(KERN_DEBUG "deassign device %x:%x:%x.%x\n", | 208 | printk(KERN_DEBUG "deassign device %x:%x:%x.%x\n", |
160 | assigned_dev->host_segnr, | 209 | assigned_dev->host_segnr, |
161 | assigned_dev->host_busnr, | 210 | assigned_dev->host_busnr, |
162 | PCI_SLOT(assigned_dev->host_devfn), | 211 | PCI_SLOT(assigned_dev->host_devfn), |
163 | PCI_FUNC(assigned_dev->host_devfn)); | 212 | PCI_FUNC(assigned_dev->host_devfn)); |
164 | 213 | ||
165 | return 0; | 214 | return 0; |
166 | } | 215 | } |
167 | 216 | ||
168 | int kvm_iommu_map_guest(struct kvm *kvm) | 217 | int kvm_iommu_map_guest(struct kvm *kvm) |
169 | { | 218 | { |
170 | int r; | 219 | int r; |
171 | 220 | ||
172 | if (!iommu_found()) { | 221 | if (!iommu_found()) { |
173 | printk(KERN_ERR "%s: iommu not found\n", __func__); | 222 | printk(KERN_ERR "%s: iommu not found\n", __func__); |
174 | return -ENODEV; | 223 | return -ENODEV; |
175 | } | 224 | } |
176 | 225 | ||
177 | kvm->arch.iommu_domain = iommu_domain_alloc(); | 226 | kvm->arch.iommu_domain = iommu_domain_alloc(); |
178 | if (!kvm->arch.iommu_domain) | 227 | if (!kvm->arch.iommu_domain) |
179 | return -ENOMEM; | 228 | return -ENOMEM; |
180 | 229 | ||
181 | r = kvm_iommu_map_memslots(kvm); | 230 | r = kvm_iommu_map_memslots(kvm); |
182 | if (r) | 231 | if (r) |
183 | goto out_unmap; | 232 | goto out_unmap; |
184 | 233 | ||
185 | return 0; | 234 | return 0; |
186 | 235 | ||
187 | out_unmap: | 236 | out_unmap: |
188 | kvm_iommu_unmap_memslots(kvm); | 237 | kvm_iommu_unmap_memslots(kvm); |
189 | return r; | 238 | return r; |
190 | } | 239 | } |
191 | 240 | ||
241 | static void kvm_unpin_pages(struct kvm *kvm, pfn_t pfn, unsigned long npages) | ||
242 | { | ||
243 | unsigned long i; | ||
244 | |||
245 | for (i = 0; i < npages; ++i) | ||
246 | kvm_release_pfn_clean(pfn + i); | ||
247 | } | ||
248 | |||
192 | static void kvm_iommu_put_pages(struct kvm *kvm, | 249 | static void kvm_iommu_put_pages(struct kvm *kvm, |
193 | gfn_t base_gfn, unsigned long npages) | 250 | gfn_t base_gfn, unsigned long npages) |
194 | { | 251 | { |
195 | gfn_t gfn = base_gfn; | 252 | struct iommu_domain *domain; |
253 | gfn_t end_gfn, gfn; | ||
196 | pfn_t pfn; | 254 | pfn_t pfn; |
197 | struct iommu_domain *domain = kvm->arch.iommu_domain; | ||
198 | unsigned long i; | ||
199 | u64 phys; | 255 | u64 phys; |
200 | 256 | ||
257 | domain = kvm->arch.iommu_domain; | ||
258 | end_gfn = base_gfn + npages; | ||
259 | gfn = base_gfn; | ||
260 | |||
201 | /* check if iommu exists and in use */ | 261 | /* check if iommu exists and in use */ |
202 | if (!domain) | 262 | if (!domain) |
203 | return; | 263 | return; |
204 | 264 | ||
205 | for (i = 0; i < npages; i++) { | 265 | while (gfn < end_gfn) { |
266 | unsigned long unmap_pages; | ||
267 | int order; | ||
268 | |||
269 | /* Get physical address */ | ||
206 | phys = iommu_iova_to_phys(domain, gfn_to_gpa(gfn)); | 270 | phys = iommu_iova_to_phys(domain, gfn_to_gpa(gfn)); |
207 | pfn = phys >> PAGE_SHIFT; | 271 | pfn = phys >> PAGE_SHIFT; |
208 | kvm_release_pfn_clean(pfn); | ||
209 | gfn++; | ||
210 | } | ||
211 | 272 | ||
212 | iommu_unmap_range(domain, gfn_to_gpa(base_gfn), PAGE_SIZE * npages); | 273 | /* Unmap address from IO address space */ |
274 | order = iommu_unmap(domain, gfn_to_gpa(gfn), PAGE_SIZE); | ||
275 | unmap_pages = 1ULL << order; | ||
276 | |||
277 | /* Unpin all pages we just unmapped to not leak any memory */ | ||
278 | kvm_unpin_pages(kvm, pfn, unmap_pages); | ||
279 | |||
280 | gfn += unmap_pages; | ||
281 | } | ||
213 | } | 282 | } |
214 | 283 | ||
215 | static int kvm_iommu_unmap_memslots(struct kvm *kvm) | 284 | static int kvm_iommu_unmap_memslots(struct kvm *kvm) |
216 | { | 285 | { |
217 | int i; | 286 | int i; |
218 | struct kvm_memslots *slots; | 287 | struct kvm_memslots *slots; |
219 | 288 | ||
220 | slots = rcu_dereference(kvm->memslots); | 289 | slots = rcu_dereference(kvm->memslots); |
221 | 290 | ||
222 | for (i = 0; i < slots->nmemslots; i++) { | 291 | for (i = 0; i < slots->nmemslots; i++) { |
223 | kvm_iommu_put_pages(kvm, slots->memslots[i].base_gfn, | 292 | kvm_iommu_put_pages(kvm, slots->memslots[i].base_gfn, |
224 | slots->memslots[i].npages); | 293 | slots->memslots[i].npages); |
225 | } | 294 | } |
226 | 295 | ||
227 | return 0; | 296 | return 0; |
228 | } | 297 | } |
229 | 298 | ||
230 | int kvm_iommu_unmap_guest(struct kvm *kvm) | 299 | int kvm_iommu_unmap_guest(struct kvm *kvm) |
231 | { | 300 | { |
232 | struct iommu_domain *domain = kvm->arch.iommu_domain; | 301 | struct iommu_domain *domain = kvm->arch.iommu_domain; |
233 | 302 | ||
234 | /* check if iommu exists and in use */ | 303 | /* check if iommu exists and in use */ |
235 | if (!domain) | 304 | if (!domain) |
236 | return 0; | 305 | return 0; |