summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Woodhouse <David.Woodhouse@intel.com>2015-10-15 10:52:15 -0400
committerDavid Woodhouse <David.Woodhouse@intel.com>2015-10-15 10:52:21 -0400
commit5cec753709adf1a20c8b15edf8e5245cf4fd4e82 (patch)
tree9a600f9f1addd96d4ae4a536a53b33c0d58ba2e8
parent569e4f7782fb92d0e1b395b5fb01de642dd74dcf (diff)
iommu/vt-d: Implement SVM_FLAG_SUPERVISOR_MODE for kernel access
This is only usable for the static 1:1 mapping of physical memory. Any access to vmalloc or module regions will require some way of doing an IOTLB flush. It's theoretically possible to hook into the tlb_flush_kernel_range() function, but that seems like overkill — most of the addresses accessed through a kernel PASID *will* be in the 1:1 mapping. If we really need to allow access to more interesting kernel regions, then the answer will probably be an explicit IOTLB flush call after use, akin to the DMA API's unmap function. In fact, it might be worth introducing that sooner rather than later, and making it just BUG() if the address isn't in the static 1:1 mapping. Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
-rw-r--r--drivers/iommu/intel-svm.c43
-rw-r--r--include/linux/intel-svm.h15
2 files changed, 45 insertions, 13 deletions
diff --git a/drivers/iommu/intel-svm.c b/drivers/iommu/intel-svm.c
index 89d4d47d0ab3..817be769e94f 100644
--- a/drivers/iommu/intel-svm.c
+++ b/drivers/iommu/intel-svm.c
@@ -269,11 +269,10 @@ int intel_svm_bind_mm(struct device *dev, int *pasid, int flags, struct svm_dev_
269 struct intel_iommu *iommu = intel_svm_device_to_iommu(dev); 269 struct intel_iommu *iommu = intel_svm_device_to_iommu(dev);
270 struct intel_svm_dev *sdev; 270 struct intel_svm_dev *sdev;
271 struct intel_svm *svm = NULL; 271 struct intel_svm *svm = NULL;
272 struct mm_struct *mm = NULL;
272 int pasid_max; 273 int pasid_max;
273 int ret; 274 int ret;
274 275
275 BUG_ON(pasid && !current->mm);
276
277 if (WARN_ON(!iommu)) 276 if (WARN_ON(!iommu))
278 return -EINVAL; 277 return -EINVAL;
279 278
@@ -284,12 +283,20 @@ int intel_svm_bind_mm(struct device *dev, int *pasid, int flags, struct svm_dev_
284 } else 283 } else
285 pasid_max = 1 << 20; 284 pasid_max = 1 << 20;
286 285
286 if ((flags & SVM_FLAG_SUPERVISOR_MODE)) {
287 if (!ecap_srs(iommu->ecap))
288 return -EINVAL;
289 } else if (pasid) {
290 mm = get_task_mm(current);
291 BUG_ON(!mm);
292 }
293
287 mutex_lock(&pasid_mutex); 294 mutex_lock(&pasid_mutex);
288 if (pasid && !(flags & SVM_FLAG_PRIVATE_PASID)) { 295 if (pasid && !(flags & SVM_FLAG_PRIVATE_PASID)) {
289 int i; 296 int i;
290 297
291 idr_for_each_entry(&iommu->pasid_idr, svm, i) { 298 idr_for_each_entry(&iommu->pasid_idr, svm, i) {
292 if (svm->mm != current->mm || 299 if (svm->mm != mm ||
293 (svm->flags & SVM_FLAG_PRIVATE_PASID)) 300 (svm->flags & SVM_FLAG_PRIVATE_PASID))
294 continue; 301 continue;
295 302
@@ -355,17 +362,22 @@ int intel_svm_bind_mm(struct device *dev, int *pasid, int flags, struct svm_dev_
355 } 362 }
356 svm->pasid = ret; 363 svm->pasid = ret;
357 svm->notifier.ops = &intel_mmuops; 364 svm->notifier.ops = &intel_mmuops;
358 svm->mm = get_task_mm(current); 365 svm->mm = mm;
359 svm->flags = flags; 366 svm->flags = flags;
360 INIT_LIST_HEAD_RCU(&svm->devs); 367 INIT_LIST_HEAD_RCU(&svm->devs);
361 ret = -ENOMEM; 368 ret = -ENOMEM;
362 if (!svm->mm || (ret = mmu_notifier_register(&svm->notifier, svm->mm))) { 369 if (mm) {
363 idr_remove(&svm->iommu->pasid_idr, svm->pasid); 370 ret = mmu_notifier_register(&svm->notifier, mm);
364 kfree(svm); 371 if (ret) {
365 kfree(sdev); 372 idr_remove(&svm->iommu->pasid_idr, svm->pasid);
366 goto out; 373 kfree(svm);
367 } 374 kfree(sdev);
368 iommu->pasid_table[svm->pasid].val = (u64)__pa(svm->mm->pgd) | 1; 375 goto out;
376 }
377 iommu->pasid_table[svm->pasid].val = (u64)__pa(mm->pgd) | 1;
378 mm = NULL;
379 } else
380 iommu->pasid_table[svm->pasid].val = (u64)__pa(init_mm.pgd) | 1 | (1ULL << 11);
369 wmb(); 381 wmb();
370 } 382 }
371 list_add_rcu(&sdev->list, &svm->devs); 383 list_add_rcu(&sdev->list, &svm->devs);
@@ -375,6 +387,8 @@ int intel_svm_bind_mm(struct device *dev, int *pasid, int flags, struct svm_dev_
375 ret = 0; 387 ret = 0;
376 out: 388 out:
377 mutex_unlock(&pasid_mutex); 389 mutex_unlock(&pasid_mutex);
390 if (mm)
391 mmput(mm);
378 return ret; 392 return ret;
379} 393}
380EXPORT_SYMBOL_GPL(intel_svm_bind_mm); 394EXPORT_SYMBOL_GPL(intel_svm_bind_mm);
@@ -416,7 +430,8 @@ int intel_svm_unbind_mm(struct device *dev, int pasid)
416 mmu_notifier_unregister(&svm->notifier, svm->mm); 430 mmu_notifier_unregister(&svm->notifier, svm->mm);
417 431
418 idr_remove(&svm->iommu->pasid_idr, svm->pasid); 432 idr_remove(&svm->iommu->pasid_idr, svm->pasid);
419 mmput(svm->mm); 433 if (svm->mm)
434 mmput(svm->mm);
420 /* We mandate that no page faults may be outstanding 435 /* We mandate that no page faults may be outstanding
421 * for the PASID when intel_svm_unbind_mm() is called. 436 * for the PASID when intel_svm_unbind_mm() is called.
422 * If that is not obeyed, subtle errors will happen. 437 * If that is not obeyed, subtle errors will happen.
@@ -500,6 +515,10 @@ static irqreturn_t prq_event_thread(int irq, void *d)
500 } 515 }
501 516
502 result = QI_RESP_INVALID; 517 result = QI_RESP_INVALID;
518 /* Since we're using init_mm.pgd directly, we should never take
519 * any faults on kernel addresses. */
520 if (!svm->mm)
521 goto bad_req;
503 down_read(&svm->mm->mmap_sem); 522 down_read(&svm->mm->mmap_sem);
504 vma = find_extend_vma(svm->mm, address); 523 vma = find_extend_vma(svm->mm, address);
505 if (!vma || address < vma->vm_start) 524 if (!vma || address < vma->vm_start)
diff --git a/include/linux/intel-svm.h b/include/linux/intel-svm.h
index dd94ab45a4db..0a48ccff24ae 100644
--- a/include/linux/intel-svm.h
+++ b/include/linux/intel-svm.h
@@ -40,7 +40,20 @@ struct svm_dev_ops {
40 * disambiguate between multiple device contexts which access the same MM, 40 * disambiguate between multiple device contexts which access the same MM,
41 * if there is no other way to do so. It should be used sparingly, if at all. 41 * if there is no other way to do so. It should be used sparingly, if at all.
42 */ 42 */
43#define SVM_FLAG_PRIVATE_PASID (1<<0) 43#define SVM_FLAG_PRIVATE_PASID (1<<0)
44
45/*
46 * The SVM_FLAG_SUPERVISOR_MODE flag requests a PASID which can be used only
47 * for access to kernel addresses. No IOTLB flushes are automatically done
48 * for kernel mappings; it is valid only for access to the kernel's static
49 * 1:1 mapping of physical memory — not to vmalloc or even module mappings.
50 * A future API addition may permit the use of such ranges, by means of an
51 * explicit IOTLB flush call (akin to the DMA API's unmap method).
52 *
53 * It is unlikely that we will ever hook into flush_tlb_kernel_range() to
54 * do such IOTLB flushes automatically.
55 */
56#define SVM_FLAG_SUPERVISOR_MODE (1<<1)
44 57
45/** 58/**
46 * intel_svm_bind_mm() - Bind the current process to a PASID 59 * intel_svm_bind_mm() - Bind the current process to a PASID