Skip to content

Commit

Permalink
iommu/vt-d: Add page response ops support
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Lu Baolu authored and Joerg Roedel committed Jul 24, 2020
1 parent eb8d93e commit 8b73712
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 0 deletions.
1 change: 1 addition & 0 deletions drivers/iommu/intel/iommu.c
Original file line number Diff line number Diff line change
Expand Up @@ -6067,6 +6067,7 @@ const struct iommu_ops intel_iommu_ops = {
.sva_bind = intel_svm_bind,
.sva_unbind = intel_svm_unbind,
.sva_get_pasid = intel_svm_get_pasid,
.page_response = intel_svm_page_response,
#endif
};

Expand Down
99 changes: 99 additions & 0 deletions drivers/iommu/intel/svm.c
Original file line number Diff line number Diff line change
Expand Up @@ -1078,3 +1078,102 @@ int intel_svm_get_pasid(struct iommu_sva *sva)

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;
}
3 changes: 3 additions & 0 deletions include/linux/intel-iommu.h
Original file line number Diff line number Diff line change
Expand Up @@ -740,6 +740,9 @@ struct iommu_sva *intel_svm_bind(struct device *dev, struct mm_struct *mm,
void *drvdata);
void intel_svm_unbind(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 intel_svm_dev {
Expand Down

0 comments on commit 8b73712

Please sign in to comment.