diff options
author | David Woodhouse <David.Woodhouse@intel.com> | 2015-10-13 12:18:10 -0400 |
---|---|---|
committer | David Woodhouse <David.Woodhouse@intel.com> | 2015-10-15 10:35:28 -0400 |
commit | 0204a49609824163092c32a8aeb073f7e9acc76d (patch) | |
tree | acf951173aa656c2781694deb985e3a7f14b21ce | |
parent | a222a7f0bb6c94c31cc9c755110593656f19de89 (diff) |
iommu/vt-d: Add callback to device driver on page faults
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
-rw-r--r-- | drivers/iommu/intel-svm.c | 26 | ||||
-rw-r--r-- | include/linux/intel-iommu.h | 3 | ||||
-rw-r--r-- | include/linux/intel-svm.h | 21 |
3 files changed, 46 insertions, 4 deletions
diff --git a/drivers/iommu/intel-svm.c b/drivers/iommu/intel-svm.c index 0e8654282484..006e95dd64ae 100644 --- a/drivers/iommu/intel-svm.c +++ b/drivers/iommu/intel-svm.c | |||
@@ -264,7 +264,7 @@ static const struct mmu_notifier_ops intel_mmuops = { | |||
264 | 264 | ||
265 | static DEFINE_MUTEX(pasid_mutex); | 265 | static DEFINE_MUTEX(pasid_mutex); |
266 | 266 | ||
267 | int intel_svm_bind_mm(struct device *dev, int *pasid) | 267 | int intel_svm_bind_mm(struct device *dev, int *pasid, int flags, struct svm_dev_ops *ops) |
268 | { | 268 | { |
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; |
@@ -302,6 +302,10 @@ int intel_svm_bind_mm(struct device *dev, int *pasid) | |||
302 | 302 | ||
303 | list_for_each_entry(sdev, &svm->devs, list) { | 303 | list_for_each_entry(sdev, &svm->devs, list) { |
304 | if (dev == sdev->dev) { | 304 | if (dev == sdev->dev) { |
305 | if (sdev->ops != ops) { | ||
306 | ret = -EBUSY; | ||
307 | goto out; | ||
308 | } | ||
305 | sdev->users++; | 309 | sdev->users++; |
306 | goto success; | 310 | goto success; |
307 | } | 311 | } |
@@ -327,6 +331,7 @@ int intel_svm_bind_mm(struct device *dev, int *pasid) | |||
327 | } | 331 | } |
328 | /* Finish the setup now we know we're keeping it */ | 332 | /* Finish the setup now we know we're keeping it */ |
329 | sdev->users = 1; | 333 | sdev->users = 1; |
334 | sdev->ops = ops; | ||
330 | init_rcu_head(&sdev->rcu); | 335 | init_rcu_head(&sdev->rcu); |
331 | 336 | ||
332 | if (!svm) { | 337 | if (!svm) { |
@@ -456,6 +461,7 @@ static irqreturn_t prq_event_thread(int irq, void *d) | |||
456 | tail = dmar_readq(iommu->reg + DMAR_PQT_REG) & PRQ_RING_MASK; | 461 | tail = dmar_readq(iommu->reg + DMAR_PQT_REG) & PRQ_RING_MASK; |
457 | head = dmar_readq(iommu->reg + DMAR_PQH_REG) & PRQ_RING_MASK; | 462 | head = dmar_readq(iommu->reg + DMAR_PQH_REG) & PRQ_RING_MASK; |
458 | while (head != tail) { | 463 | while (head != tail) { |
464 | struct intel_svm_dev *sdev; | ||
459 | struct vm_area_struct *vma; | 465 | struct vm_area_struct *vma; |
460 | struct page_req_dsc *req; | 466 | struct page_req_dsc *req; |
461 | struct qi_desc resp; | 467 | struct qi_desc resp; |
@@ -507,6 +513,24 @@ static irqreturn_t prq_event_thread(int irq, void *d) | |||
507 | up_read(&svm->mm->mmap_sem); | 513 | up_read(&svm->mm->mmap_sem); |
508 | bad_req: | 514 | bad_req: |
509 | /* Accounting for major/minor faults? */ | 515 | /* Accounting for major/minor faults? */ |
516 | rcu_read_lock(); | ||
517 | list_for_each_entry_rcu(sdev, &svm->devs, list) { | ||
518 | if (sdev->sid == PCI_DEVID(req->bus, req->devfn)); | ||
519 | break; | ||
520 | } | ||
521 | /* Other devices can go away, but the drivers are not permitted | ||
522 | * to unbind while any page faults might be in flight. So it's | ||
523 | * OK to drop the 'lock' here now we have it. */ | ||
524 | rcu_read_unlock(); | ||
525 | |||
526 | if (WARN_ON(&sdev->list == &svm->devs)) | ||
527 | sdev = NULL; | ||
528 | |||
529 | if (sdev && sdev->ops && sdev->ops->fault_cb) { | ||
530 | int rwxp = (req->rd_req << 3) | (req->wr_req << 2) | | ||
531 | (req->wr_req << 1) | (req->exe_req); | ||
532 | sdev->ops->fault_cb(sdev->dev, req->pasid, req->addr, req->private, rwxp, result); | ||
533 | } | ||
510 | 534 | ||
511 | if (req->lpig) { | 535 | if (req->lpig) { |
512 | /* Page Group Response */ | 536 | /* Page Group Response */ |
diff --git a/include/linux/intel-iommu.h b/include/linux/intel-iommu.h index e5b80d31eb1b..57be14fce640 100644 --- a/include/linux/intel-iommu.h +++ b/include/linux/intel-iommu.h | |||
@@ -472,10 +472,13 @@ extern int intel_svm_free_pasid_tables(struct intel_iommu *iommu); | |||
472 | extern int intel_svm_enable_prq(struct intel_iommu *iommu); | 472 | extern int intel_svm_enable_prq(struct intel_iommu *iommu); |
473 | extern int intel_svm_finish_prq(struct intel_iommu *iommu); | 473 | extern int intel_svm_finish_prq(struct intel_iommu *iommu); |
474 | 474 | ||
475 | struct svm_dev_ops; | ||
476 | |||
475 | struct intel_svm_dev { | 477 | struct intel_svm_dev { |
476 | struct list_head list; | 478 | struct list_head list; |
477 | struct rcu_head rcu; | 479 | struct rcu_head rcu; |
478 | struct device *dev; | 480 | struct device *dev; |
481 | struct svm_dev_ops *ops; | ||
479 | int users; | 482 | int users; |
480 | u16 did; | 483 | u16 did; |
481 | u16 dev_iotlb:1; | 484 | u16 dev_iotlb:1; |
diff --git a/include/linux/intel-svm.h b/include/linux/intel-svm.h index 42f80ad7a7a0..239f8a13a332 100644 --- a/include/linux/intel-svm.h +++ b/include/linux/intel-svm.h | |||
@@ -20,10 +20,23 @@ | |||
20 | 20 | ||
21 | struct device; | 21 | struct device; |
22 | 22 | ||
23 | struct svm_dev_ops { | ||
24 | void (*fault_cb)(struct device *dev, int pasid, u64 address, | ||
25 | u32 private, int rwxp, int response); | ||
26 | }; | ||
27 | |||
28 | /* Values for rxwp in fault_cb callback */ | ||
29 | #define SVM_REQ_READ (1<<3) | ||
30 | #define SVM_REQ_WRITE (1<<2) | ||
31 | #define SVM_REQ_EXEC (1<<1) | ||
32 | #define SVM_REQ_PRIV (1<<0) | ||
33 | |||
23 | /** | 34 | /** |
24 | * intel_svm_bind_mm() - Bind the current process to a PASID | 35 | * intel_svm_bind_mm() - Bind the current process to a PASID |
25 | * @dev: Device to be granted acccess | 36 | * @dev: Device to be granted acccess |
26 | * @pasid: Address for allocated PASID | 37 | * @pasid: Address for allocated PASID |
38 | * @flags: Flags. Later for requesting supervisor mode, etc. | ||
39 | * @ops: Callbacks to device driver | ||
27 | * | 40 | * |
28 | * This function attempts to enable PASID support for the given device. | 41 | * This function attempts to enable PASID support for the given device. |
29 | * If the @pasid argument is non-%NULL, a PASID is allocated for access | 42 | * If the @pasid argument is non-%NULL, a PASID is allocated for access |
@@ -45,7 +58,8 @@ struct device; | |||
45 | * Multiple calls from the same process may result in the same PASID | 58 | * Multiple calls from the same process may result in the same PASID |
46 | * being re-used. A reference count is kept. | 59 | * being re-used. A reference count is kept. |
47 | */ | 60 | */ |
48 | extern int intel_svm_bind_mm(struct device *dev, int *pasid); | 61 | extern int intel_svm_bind_mm(struct device *dev, int *pasid, int flags, |
62 | struct svm_dev_ops *ops); | ||
49 | 63 | ||
50 | /** | 64 | /** |
51 | * intel_svm_unbind_mm() - Unbind a specified PASID | 65 | * intel_svm_unbind_mm() - Unbind a specified PASID |
@@ -66,7 +80,8 @@ extern int intel_svm_unbind_mm(struct device *dev, int pasid); | |||
66 | 80 | ||
67 | #else /* CONFIG_INTEL_IOMMU_SVM */ | 81 | #else /* CONFIG_INTEL_IOMMU_SVM */ |
68 | 82 | ||
69 | static inline int intel_svm_bind_mm(struct device *dev, int *pasid) | 83 | static inline int intel_svm_bind_mm(struct device *dev, int *pasid, |
84 | int flags, struct svm_dev_ops *ops) | ||
70 | { | 85 | { |
71 | return -ENOSYS; | 86 | return -ENOSYS; |
72 | } | 87 | } |
@@ -77,6 +92,6 @@ static inline int intel_svm_unbind_mm(struct device *dev, int pasid) | |||
77 | } | 92 | } |
78 | #endif /* CONFIG_INTEL_IOMMU_SVM */ | 93 | #endif /* CONFIG_INTEL_IOMMU_SVM */ |
79 | 94 | ||
80 | #define intel_svm_available(dev) (!intel_svm_bind_mm((dev), NULL)) | 95 | #define intel_svm_available(dev) (!intel_svm_bind_mm((dev), NULL, 0, NULL)) |
81 | 96 | ||
82 | #endif /* __INTEL_SVM_H__ */ | 97 | #endif /* __INTEL_SVM_H__ */ |