mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-06 10:57:46 +00:00
iommu/vt-d: Add page response ops support
After page requests are handled, software must respond to the device which raised the page request with the result. This is done through the iommu ops.page_response if the request was reported to outside of vendor iommu driver through iommu_report_device_fault(). This adds the VT-d implementation of page_response ops. Co-developed-by: Jacob Pan <jacob.jun.pan@linux.intel.com> Co-developed-by: Liu Yi L <yi.l.liu@intel.com> Signed-off-by: Jacob Pan <jacob.jun.pan@linux.intel.com> Signed-off-by: Liu Yi L <yi.l.liu@intel.com> Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com> Reviewed-by: Kevin Tian <kevin.tian@intel.com> Link: https://lore.kernel.org/r/20200724014925.15523-12-baolu.lu@linux.intel.com Signed-off-by: Joerg Roedel <jroedel@suse.de>
This commit is contained in:
parent
eb8d93ea3c
commit
8b73712115
3 changed files with 103 additions and 0 deletions
|
@ -6067,6 +6067,7 @@ const struct iommu_ops intel_iommu_ops = {
|
||||||
.sva_bind = intel_svm_bind,
|
.sva_bind = intel_svm_bind,
|
||||||
.sva_unbind = intel_svm_unbind,
|
.sva_unbind = intel_svm_unbind,
|
||||||
.sva_get_pasid = intel_svm_get_pasid,
|
.sva_get_pasid = intel_svm_get_pasid,
|
||||||
|
.page_response = intel_svm_page_response,
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1078,3 +1078,102 @@ int intel_svm_get_pasid(struct iommu_sva *sva)
|
||||||
|
|
||||||
return pasid;
|
return pasid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int intel_svm_page_response(struct device *dev,
|
||||||
|
struct iommu_fault_event *evt,
|
||||||
|
struct iommu_page_response *msg)
|
||||||
|
{
|
||||||
|
struct iommu_fault_page_request *prm;
|
||||||
|
struct intel_svm_dev *sdev = NULL;
|
||||||
|
struct intel_svm *svm = NULL;
|
||||||
|
struct intel_iommu *iommu;
|
||||||
|
bool private_present;
|
||||||
|
bool pasid_present;
|
||||||
|
bool last_page;
|
||||||
|
u8 bus, devfn;
|
||||||
|
int ret = 0;
|
||||||
|
u16 sid;
|
||||||
|
|
||||||
|
if (!dev || !dev_is_pci(dev))
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
iommu = device_to_iommu(dev, &bus, &devfn);
|
||||||
|
if (!iommu)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
if (!msg || !evt)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
mutex_lock(&pasid_mutex);
|
||||||
|
|
||||||
|
prm = &evt->fault.prm;
|
||||||
|
sid = PCI_DEVID(bus, devfn);
|
||||||
|
pasid_present = prm->flags & IOMMU_FAULT_PAGE_REQUEST_PASID_VALID;
|
||||||
|
private_present = prm->flags & IOMMU_FAULT_PAGE_REQUEST_PRIV_DATA;
|
||||||
|
last_page = prm->flags & IOMMU_FAULT_PAGE_REQUEST_LAST_PAGE;
|
||||||
|
|
||||||
|
if (!pasid_present) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prm->pasid == 0 || prm->pasid >= PASID_MAX) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = pasid_to_svm_sdev(dev, prm->pasid, &svm, &sdev);
|
||||||
|
if (ret || !sdev) {
|
||||||
|
ret = -ENODEV;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For responses from userspace, need to make sure that the
|
||||||
|
* pasid has been bound to its mm.
|
||||||
|
*/
|
||||||
|
if (svm->flags & SVM_FLAG_GUEST_MODE) {
|
||||||
|
struct mm_struct *mm;
|
||||||
|
|
||||||
|
mm = get_task_mm(current);
|
||||||
|
if (!mm) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mm != svm->mm) {
|
||||||
|
ret = -ENODEV;
|
||||||
|
mmput(mm);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
mmput(mm);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Per VT-d spec. v3.0 ch7.7, system software must respond
|
||||||
|
* with page group response if private data is present (PDP)
|
||||||
|
* or last page in group (LPIG) bit is set. This is an
|
||||||
|
* additional VT-d requirement beyond PCI ATS spec.
|
||||||
|
*/
|
||||||
|
if (last_page || private_present) {
|
||||||
|
struct qi_desc desc;
|
||||||
|
|
||||||
|
desc.qw0 = QI_PGRP_PASID(prm->pasid) | QI_PGRP_DID(sid) |
|
||||||
|
QI_PGRP_PASID_P(pasid_present) |
|
||||||
|
QI_PGRP_PDP(private_present) |
|
||||||
|
QI_PGRP_RESP_CODE(msg->code) |
|
||||||
|
QI_PGRP_RESP_TYPE;
|
||||||
|
desc.qw1 = QI_PGRP_IDX(prm->grpid) | QI_PGRP_LPIG(last_page);
|
||||||
|
desc.qw2 = 0;
|
||||||
|
desc.qw3 = 0;
|
||||||
|
if (private_present)
|
||||||
|
memcpy(&desc.qw2, prm->private_data,
|
||||||
|
sizeof(prm->private_data));
|
||||||
|
|
||||||
|
qi_submit_sync(iommu, &desc, 1, 0);
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
mutex_unlock(&pasid_mutex);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
|
@ -740,6 +740,9 @@ struct iommu_sva *intel_svm_bind(struct device *dev, struct mm_struct *mm,
|
||||||
void *drvdata);
|
void *drvdata);
|
||||||
void intel_svm_unbind(struct iommu_sva *handle);
|
void intel_svm_unbind(struct iommu_sva *handle);
|
||||||
int intel_svm_get_pasid(struct iommu_sva *handle);
|
int intel_svm_get_pasid(struct iommu_sva *handle);
|
||||||
|
int intel_svm_page_response(struct device *dev, struct iommu_fault_event *evt,
|
||||||
|
struct iommu_page_response *msg);
|
||||||
|
|
||||||
struct svm_dev_ops;
|
struct svm_dev_ops;
|
||||||
|
|
||||||
struct intel_svm_dev {
|
struct intel_svm_dev {
|
||||||
|
|
Loading…
Reference in a new issue