Commit 100b8a14 authored by Lu Baolu's avatar Lu Baolu Committed by Joerg Roedel
Browse files

iommu/vt-d: Add pasid private data helpers



We are about to use iommu_sva_alloc/free_pasid() helpers in iommu core.
That means the pasid life cycle will be managed by iommu core. Use a
local array to save the per pasid private data instead of attaching it
the real pasid.

Signed-off-by: default avatarLu Baolu <baolu.lu@linux.intel.com>
Link: https://lore.kernel.org/r/20210520031531.712333-1-baolu.lu@linux.intel.com
Link: https://lore.kernel.org/r/20210610020115.1637656-7-baolu.lu@linux.intel.com


Signed-off-by: default avatarJoerg Roedel <jroedel@suse.de>
parent 521f546b
Loading
Loading
Loading
Loading
+41 −21
Original line number Original line Diff line number Diff line
@@ -17,6 +17,7 @@
#include <linux/dmar.h>
#include <linux/dmar.h>
#include <linux/interrupt.h>
#include <linux/interrupt.h>
#include <linux/mm_types.h>
#include <linux/mm_types.h>
#include <linux/xarray.h>
#include <linux/ioasid.h>
#include <linux/ioasid.h>
#include <asm/page.h>
#include <asm/page.h>
#include <asm/fpu/api.h>
#include <asm/fpu/api.h>
@@ -28,6 +29,23 @@ static void intel_svm_drain_prq(struct device *dev, u32 pasid);


#define PRQ_ORDER 0
#define PRQ_ORDER 0


static DEFINE_XARRAY_ALLOC(pasid_private_array);
static int pasid_private_add(ioasid_t pasid, void *priv)
{
	return xa_alloc(&pasid_private_array, &pasid, priv,
			XA_LIMIT(pasid, pasid), GFP_ATOMIC);
}

static void pasid_private_remove(ioasid_t pasid)
{
	xa_erase(&pasid_private_array, pasid);
}

static void *pasid_private_find(ioasid_t pasid)
{
	return xa_load(&pasid_private_array, pasid);
}

int intel_svm_enable_prq(struct intel_iommu *iommu)
int intel_svm_enable_prq(struct intel_iommu *iommu)
{
{
	struct page *pages;
	struct page *pages;
@@ -224,7 +242,7 @@ static int pasid_to_svm_sdev(struct device *dev, unsigned int pasid,
	if (pasid == INVALID_IOASID || pasid >= PASID_MAX)
	if (pasid == INVALID_IOASID || pasid >= PASID_MAX)
		return -EINVAL;
		return -EINVAL;


	svm = ioasid_find(NULL, pasid, NULL);
	svm = pasid_private_find(pasid);
	if (IS_ERR(svm))
	if (IS_ERR(svm))
		return PTR_ERR(svm);
		return PTR_ERR(svm);


@@ -334,7 +352,7 @@ int intel_svm_bind_gpasid(struct iommu_domain *domain, struct device *dev,
			svm->gpasid = data->gpasid;
			svm->gpasid = data->gpasid;
			svm->flags |= SVM_FLAG_GUEST_PASID;
			svm->flags |= SVM_FLAG_GUEST_PASID;
		}
		}
		ioasid_set_data(data->hpasid, svm);
		pasid_private_add(data->hpasid, svm);
		INIT_LIST_HEAD_RCU(&svm->devs);
		INIT_LIST_HEAD_RCU(&svm->devs);
		mmput(svm->mm);
		mmput(svm->mm);
	}
	}
@@ -388,7 +406,7 @@ int intel_svm_bind_gpasid(struct iommu_domain *domain, struct device *dev,
	list_add_rcu(&sdev->list, &svm->devs);
	list_add_rcu(&sdev->list, &svm->devs);
 out:
 out:
	if (!IS_ERR_OR_NULL(svm) && list_empty(&svm->devs)) {
	if (!IS_ERR_OR_NULL(svm) && list_empty(&svm->devs)) {
		ioasid_set_data(data->hpasid, NULL);
		pasid_private_remove(data->hpasid);
		kfree(svm);
		kfree(svm);
	}
	}


@@ -431,7 +449,7 @@ int intel_svm_unbind_gpasid(struct device *dev, u32 pasid)
				 * the unbind, IOMMU driver will get notified
				 * the unbind, IOMMU driver will get notified
				 * and perform cleanup.
				 * and perform cleanup.
				 */
				 */
				ioasid_set_data(pasid, NULL);
				pasid_private_remove(pasid);
				kfree(svm);
				kfree(svm);
			}
			}
		}
		}
@@ -547,8 +565,7 @@ intel_svm_bind_mm(struct device *dev, unsigned int flags,
		svm = kzalloc(sizeof(*svm), GFP_KERNEL);
		svm = kzalloc(sizeof(*svm), GFP_KERNEL);
		if (!svm) {
		if (!svm) {
			ret = -ENOMEM;
			ret = -ENOMEM;
			kfree(sdev);
			goto sdev_err;
			goto out;
		}
		}


		if (pasid_max > intel_pasid_max_id)
		if (pasid_max > intel_pasid_max_id)
@@ -556,13 +573,16 @@ intel_svm_bind_mm(struct device *dev, unsigned int flags,


		/* Do not use PASID 0, reserved for RID to PASID */
		/* Do not use PASID 0, reserved for RID to PASID */
		svm->pasid = ioasid_alloc(NULL, PASID_MIN,
		svm->pasid = ioasid_alloc(NULL, PASID_MIN,
					  pasid_max - 1, svm);
					  pasid_max - 1, NULL);
		if (svm->pasid == INVALID_IOASID) {
		if (svm->pasid == INVALID_IOASID) {
			kfree(svm);
			kfree(sdev);
			ret = -ENOSPC;
			ret = -ENOSPC;
			goto out;
			goto svm_err;
		}
		}

		ret = pasid_private_add(svm->pasid, svm);
		if (ret)
			goto pasid_err;

		svm->notifier.ops = &intel_mmuops;
		svm->notifier.ops = &intel_mmuops;
		svm->mm = mm;
		svm->mm = mm;
		svm->flags = flags;
		svm->flags = flags;
@@ -571,12 +591,8 @@ intel_svm_bind_mm(struct device *dev, unsigned int flags,
		ret = -ENOMEM;
		ret = -ENOMEM;
		if (mm) {
		if (mm) {
			ret = mmu_notifier_register(&svm->notifier, mm);
			ret = mmu_notifier_register(&svm->notifier, mm);
			if (ret) {
			if (ret)
				ioasid_put(svm->pasid);
				goto priv_err;
				kfree(svm);
				kfree(sdev);
				goto out;
			}
		}
		}


		spin_lock_irqsave(&iommu->lock, iflags);
		spin_lock_irqsave(&iommu->lock, iflags);
@@ -590,8 +606,13 @@ intel_svm_bind_mm(struct device *dev, unsigned int flags,
		if (ret) {
		if (ret) {
			if (mm)
			if (mm)
				mmu_notifier_unregister(&svm->notifier, mm);
				mmu_notifier_unregister(&svm->notifier, mm);
priv_err:
			pasid_private_remove(svm->pasid);
pasid_err:
			ioasid_put(svm->pasid);
			ioasid_put(svm->pasid);
svm_err:
			kfree(svm);
			kfree(svm);
sdev_err:
			kfree(sdev);
			kfree(sdev);
			goto out;
			goto out;
		}
		}
@@ -614,10 +635,8 @@ intel_svm_bind_mm(struct device *dev, unsigned int flags,
						(cpu_feature_enabled(X86_FEATURE_LA57) ?
						(cpu_feature_enabled(X86_FEATURE_LA57) ?
						PASID_FLAG_FL5LP : 0));
						PASID_FLAG_FL5LP : 0));
		spin_unlock_irqrestore(&iommu->lock, iflags);
		spin_unlock_irqrestore(&iommu->lock, iflags);
		if (ret) {
		if (ret)
			kfree(sdev);
			goto sdev_err;
			goto out;
		}
	}
	}
	list_add_rcu(&sdev->list, &svm->devs);
	list_add_rcu(&sdev->list, &svm->devs);
success:
success:
@@ -670,6 +689,7 @@ static int intel_svm_unbind_mm(struct device *dev, u32 pasid)
					load_pasid(svm->mm, PASID_DISABLED);
					load_pasid(svm->mm, PASID_DISABLED);
				}
				}
				list_del(&svm->list);
				list_del(&svm->list);
				pasid_private_remove(svm->pasid);
				/* We mandate that no page faults may be outstanding
				/* We mandate that no page faults may be outstanding
				 * for the PASID when intel_svm_unbind_mm() is called.
				 * for the PASID when intel_svm_unbind_mm() is called.
				 * If that is not obeyed, subtle errors will happen.
				 * If that is not obeyed, subtle errors will happen.
@@ -924,7 +944,7 @@ static irqreturn_t prq_event_thread(int irq, void *d)
		}
		}
		if (!svm || svm->pasid != req->pasid) {
		if (!svm || svm->pasid != req->pasid) {
			rcu_read_lock();
			rcu_read_lock();
			svm = ioasid_find(NULL, req->pasid, NULL);
			svm = pasid_private_find(req->pasid);
			/* It *can't* go away, because the driver is not permitted
			/* It *can't* go away, because the driver is not permitted
			 * to unbind the mm while any page faults are outstanding.
			 * to unbind the mm while any page faults are outstanding.
			 * So we only need RCU to protect the internal idr code. */
			 * So we only need RCU to protect the internal idr code. */