diff options
Diffstat (limited to 'drivers/iommu/intel/svm.c')
| -rw-r--r-- | drivers/iommu/intel/svm.c | 82 | 
1 files changed, 39 insertions, 43 deletions
| diff --git a/drivers/iommu/intel/svm.c b/drivers/iommu/intel/svm.c index 574a7e657a9a..5165cea90421 100644 --- a/drivers/iommu/intel/svm.c +++ b/drivers/iommu/intel/svm.c @@ -462,13 +462,12 @@ static void load_pasid(struct mm_struct *mm, u32 pasid)  /* Caller must hold pasid_mutex, mm reference */  static int  intel_svm_bind_mm(struct device *dev, unsigned int flags, -		  struct svm_dev_ops *ops,  		  struct mm_struct *mm, struct intel_svm_dev **sd)  {  	struct intel_iommu *iommu = device_to_iommu(dev, NULL, NULL); +	struct intel_svm *svm = NULL, *t;  	struct device_domain_info *info;  	struct intel_svm_dev *sdev; -	struct intel_svm *svm = NULL;  	unsigned long iflags;  	int pasid_max;  	int ret; @@ -494,34 +493,26 @@ intel_svm_bind_mm(struct device *dev, unsigned int flags,  		}  	} -	if (!(flags & SVM_FLAG_PRIVATE_PASID)) { -		struct intel_svm *t; - -		list_for_each_entry(t, &global_svm_list, list) { -			if (t->mm != mm || (t->flags & SVM_FLAG_PRIVATE_PASID)) -				continue; - -			svm = t; -			if (svm->pasid >= pasid_max) { -				dev_warn(dev, -					 "Limited PASID width. Cannot use existing PASID %d\n", -					 svm->pasid); -				ret = -ENOSPC; -				goto out; -			} +	list_for_each_entry(t, &global_svm_list, list) { +		if (t->mm != mm) +			continue; -			/* Find the matching device in svm list */ -			for_each_svm_dev(sdev, svm, dev) { -				if (sdev->ops != ops) { -					ret = -EBUSY; -					goto out; -				} -				sdev->users++; -				goto success; -			} +		svm = t; +		if (svm->pasid >= pasid_max) { +			dev_warn(dev, +				 "Limited PASID width. Cannot use existing PASID %d\n", +				 svm->pasid); +			ret = -ENOSPC; +			goto out; +		} -			break; +		/* Find the matching device in svm list */ +		for_each_svm_dev(sdev, svm, dev) { +			sdev->users++; +			goto success;  		} + +		break;  	}  	sdev = kzalloc(sizeof(*sdev), GFP_KERNEL); @@ -550,7 +541,6 @@ intel_svm_bind_mm(struct device *dev, unsigned int flags,  	/* Finish the setup now we know we're keeping it */  	sdev->users = 1; -	sdev->ops = ops;  	init_rcu_head(&sdev->rcu);  	if (!svm) { @@ -862,7 +852,7 @@ intel_svm_prq_report(struct device *dev, struct page_req_dsc *desc)  	/* Fill in event data for device specific processing */  	memset(&event, 0, sizeof(struct iommu_fault_event));  	event.fault.type = IOMMU_FAULT_PAGE_REQ; -	event.fault.prm.addr = desc->addr; +	event.fault.prm.addr = (u64)desc->addr << VTD_PAGE_SHIFT;  	event.fault.prm.pasid = desc->pasid;  	event.fault.prm.grpid = desc->prg_index;  	event.fault.prm.perm = prq_to_iommu_prot(desc); @@ -895,6 +885,7 @@ static irqreturn_t prq_event_thread(int irq, void *d)  	struct intel_iommu *iommu = d;  	struct intel_svm *svm = NULL;  	int head, tail, handled = 0; +	unsigned int flags = 0;  	/* Clear PPR bit before reading head/tail registers, to  	 * ensure that we get a new interrupt if needed. */ @@ -920,7 +911,17 @@ static irqreturn_t prq_event_thread(int irq, void *d)  			       ((unsigned long long *)req)[1]);  			goto no_pasid;  		} - +		/* We shall not receive page request for supervisor SVM */ +		if (req->pm_req && (req->rd_req | req->wr_req)) { +			pr_err("Unexpected page request in Privilege Mode"); +			/* No need to find the matching sdev as for bad_req */ +			goto no_pasid; +		} +		/* DMA read with exec requeset is not supported. */ +		if (req->exe_req && req->rd_req) { +			pr_err("Execution request not supported\n"); +			goto no_pasid; +		}  		if (!svm || svm->pasid != req->pasid) {  			rcu_read_lock();  			svm = ioasid_find(NULL, req->pasid, NULL); @@ -982,9 +983,11 @@ static irqreturn_t prq_event_thread(int irq, void *d)  		if (access_error(vma, req))  			goto invalid; -		ret = handle_mm_fault(vma, address, -				      req->wr_req ? FAULT_FLAG_WRITE : 0, -				      NULL); +		flags = FAULT_FLAG_USER | FAULT_FLAG_REMOTE; +		if (req->wr_req) +			flags |= FAULT_FLAG_WRITE; + +		ret = handle_mm_fault(vma, address, flags, NULL);  		if (ret & VM_FAULT_ERROR)  			goto invalid; @@ -993,13 +996,6 @@ invalid:  		mmap_read_unlock(svm->mm);  		mmput(svm->mm);  bad_req: -		WARN_ON(!sdev); -		if (sdev && sdev->ops && sdev->ops->fault_cb) { -			int rwxp = (req->rd_req << 3) | (req->wr_req << 2) | -				(req->exe_req << 1) | (req->pm_req); -			sdev->ops->fault_cb(sdev->dev, req->pasid, req->addr, -					    req->priv_data, rwxp, result); -		}  		/* We get here in the error case where the PASID lookup failed,  		   and these can be NULL. Do not use them below this point! */  		sdev = NULL; @@ -1021,12 +1017,12 @@ no_pasid:  				QI_PGRP_RESP_TYPE;  			resp.qw1 = QI_PGRP_IDX(req->prg_index) |  				QI_PGRP_LPIG(req->lpig); +			resp.qw2 = 0; +			resp.qw3 = 0;  			if (req->priv_data_present)  				memcpy(&resp.qw2, req->priv_data,  				       sizeof(req->priv_data)); -			resp.qw2 = 0; -			resp.qw3 = 0;  			qi_submit_sync(iommu, &resp, 1, 0);  		}  prq_advance: @@ -1074,7 +1070,7 @@ intel_svm_bind(struct device *dev, struct mm_struct *mm, void *drvdata)  	if (drvdata)  		flags = *(unsigned int *)drvdata;  	mutex_lock(&pasid_mutex); -	ret = intel_svm_bind_mm(dev, flags, NULL, mm, &sdev); +	ret = intel_svm_bind_mm(dev, flags, mm, &sdev);  	if (ret)  		sva = ERR_PTR(ret);  	else if (sdev) | 
