[RFC PATCH 05/12] iommu/vt-d: Retire struct intel_svm_dev

From: Tina Zhang
Date: Mon Oct 16 2023 - 23:21:43 EST


The struct dev_pasid_info is used by IOMMU domain to keep pasid info of
attached device. For sva domain, there is another structure which keeps
info of attached device, named intel_svm_dev. Instead of using two structs
to keep attached device info separately, sva domain should use struct
dev_pasid to centralize info of attach device. To achieve this, rcu/sid/
qdep fields are moved from struct intel_svm_dev into struct
dev_pasid_info and then sva domain switches to use dev_pasid_info to
keep attached device info. In this way, struct intel_svm_dev gets retired.

Signed-off-by: Tina Zhang <tina.zhang@xxxxxxxxx>
---
drivers/iommu/intel/iommu.h | 11 +----
drivers/iommu/intel/svm.c | 94 ++++++++++++++++++-------------------
2 files changed, 49 insertions(+), 56 deletions(-)

diff --git a/drivers/iommu/intel/iommu.h b/drivers/iommu/intel/iommu.h
index 1e972e7edeca..bd7210980fb2 100644
--- a/drivers/iommu/intel/iommu.h
+++ b/drivers/iommu/intel/iommu.h
@@ -722,6 +722,8 @@ struct dev_pasid_info {
struct list_head link_domain; /* link to domain siblings */
struct device *dev;
ioasid_t pasid;
+ struct rcu_head rcu;
+ u16 sid, qdep;
};

static inline void __iommu_flush_cache(
@@ -859,15 +861,6 @@ struct iommu_domain *intel_svm_domain_alloc(void);
void intel_svm_remove_dev_pasid(struct device *dev, ioasid_t pasid);
void intel_drain_pasid_prq(struct device *dev, u32 pasid);

-struct intel_svm_dev {
- struct list_head list;
- struct rcu_head rcu;
- struct device *dev;
- struct intel_iommu *iommu;
- u16 did;
- u16 sid, qdep;
-};
-
struct intel_svm {
struct mmu_notifier notifier;
struct mm_struct *mm;
diff --git a/drivers/iommu/intel/svm.c b/drivers/iommu/intel/svm.c
index 03406395ac5b..e0373586811e 100644
--- a/drivers/iommu/intel/svm.c
+++ b/drivers/iommu/intel/svm.c
@@ -44,21 +44,21 @@ static void *pasid_private_find(ioasid_t pasid)
return xa_load(&pasid_private_array, pasid);
}

-static struct intel_svm_dev *
-svm_lookup_device_by_dev(struct intel_svm *svm, struct device *dev)
+static struct dev_pasid_info *
+svm_lookup_dev_pasid_info_by_dev(struct intel_svm *svm, struct device *dev)
{
- struct intel_svm_dev *sdev = NULL, *t;
+ struct dev_pasid_info *dev_pasid = NULL, *t;

rcu_read_lock();
- list_for_each_entry_rcu(t, &svm->devs, list) {
+ list_for_each_entry_rcu(t, &svm->devs, link_domain) {
if (t->dev == dev) {
- sdev = t;
+ dev_pasid = t;
break;
}
}
rcu_read_unlock();

- return sdev;
+ return dev_pasid;
}

int intel_svm_enable_prq(struct intel_iommu *iommu)
@@ -170,27 +170,28 @@ void intel_svm_check(struct intel_iommu *iommu)
}

static void __flush_svm_range_dev(struct intel_svm *svm,
- struct intel_svm_dev *sdev,
+ struct dev_pasid_info *dev_pasid,
unsigned long address,
unsigned long pages, int ih)
{
- struct device_domain_info *info = dev_iommu_priv_get(sdev->dev);
+ struct device_domain_info *info = dev_iommu_priv_get(dev_pasid->dev);
+ struct intel_iommu *iommu = dev_to_intel_iommu(dev_pasid->dev);

if (WARN_ON(!pages))
return;

- qi_flush_piotlb(sdev->iommu, sdev->did, svm->pasid, address, pages, ih);
+ qi_flush_piotlb(iommu, FLPT_DEFAULT_DID, svm->pasid, address, pages, ih);
if (info->ats_enabled) {
- qi_flush_dev_iotlb_pasid(sdev->iommu, sdev->sid, info->pfsid,
- svm->pasid, sdev->qdep, address,
+ qi_flush_dev_iotlb_pasid(iommu, dev_pasid->sid, info->pfsid,
+ svm->pasid, dev_pasid->qdep, address,
order_base_2(pages));
quirk_extra_dev_tlb_flush(info, address, order_base_2(pages),
- svm->pasid, sdev->qdep);
+ svm->pasid, dev_pasid->qdep);
}
}

static void intel_flush_svm_range_dev(struct intel_svm *svm,
- struct intel_svm_dev *sdev,
+ struct dev_pasid_info *dev_pasid,
unsigned long address,
unsigned long pages, int ih)
{
@@ -200,7 +201,7 @@ static void intel_flush_svm_range_dev(struct intel_svm *svm,
unsigned long end = ALIGN(address + (pages << VTD_PAGE_SHIFT), align);

while (start < end) {
- __flush_svm_range_dev(svm, sdev, start, align >> VTD_PAGE_SHIFT, ih);
+ __flush_svm_range_dev(svm, dev_pasid, start, align >> VTD_PAGE_SHIFT, ih);
start += align;
}
}
@@ -208,11 +209,11 @@ static void intel_flush_svm_range_dev(struct intel_svm *svm,
static void intel_flush_svm_range(struct intel_svm *svm, unsigned long address,
unsigned long pages, int ih)
{
- struct intel_svm_dev *sdev;
+ struct dev_pasid_info *dev_pasid;

rcu_read_lock();
- list_for_each_entry_rcu(sdev, &svm->devs, list)
- intel_flush_svm_range_dev(svm, sdev, address, pages, ih);
+ list_for_each_entry_rcu(dev_pasid, &svm->devs, link_domain)
+ intel_flush_svm_range_dev(svm, dev_pasid, address, pages, ih);
rcu_read_unlock();
}

@@ -230,7 +231,8 @@ static void intel_arch_invalidate_secondary_tlbs(struct mmu_notifier *mn,
static void intel_mm_release(struct mmu_notifier *mn, struct mm_struct *mm)
{
struct intel_svm *svm = container_of(mn, struct intel_svm, notifier);
- struct intel_svm_dev *sdev;
+ struct dev_pasid_info *dev_pasid;
+ struct intel_iommu *iommu;

/* This might end up being called from exit_mmap(), *before* the page
* tables are cleared. And __mmu_notifier_release() will delete us from
@@ -245,9 +247,11 @@ static void intel_mm_release(struct mmu_notifier *mn, struct mm_struct *mm)
* *has* to handle gracefully without affecting other processes.
*/
rcu_read_lock();
- list_for_each_entry_rcu(sdev, &svm->devs, list)
- intel_pasid_tear_down_entry(sdev->iommu, sdev->dev,
+ list_for_each_entry_rcu(dev_pasid, &svm->devs, link_domain) {
+ iommu = dev_to_intel_iommu(dev_pasid->dev);
+ intel_pasid_tear_down_entry(iommu, dev_pasid->dev,
svm->pasid, true);
+ }
rcu_read_unlock();

}
@@ -257,11 +261,11 @@ static const struct mmu_notifier_ops intel_mmuops = {
.arch_invalidate_secondary_tlbs = intel_arch_invalidate_secondary_tlbs,
};

-static int pasid_to_svm_sdev(struct device *dev, unsigned int pasid,
+static int pasid_to_dev_pasid_info(struct device *dev, unsigned int pasid,
struct intel_svm **rsvm,
- struct intel_svm_dev **rsdev)
+ struct dev_pasid_info **rsdev_pasid_info)
{
- struct intel_svm_dev *sdev = NULL;
+ struct dev_pasid_info *dev_pasid = NULL;
struct intel_svm *svm;

if (pasid == IOMMU_PASID_INVALID || pasid >= PASID_MAX)
@@ -280,11 +284,11 @@ static int pasid_to_svm_sdev(struct device *dev, unsigned int pasid,
*/
if (WARN_ON(list_empty(&svm->devs)))
return -EINVAL;
- sdev = svm_lookup_device_by_dev(svm, dev);
+ dev_pasid = svm_lookup_dev_pasid_info_by_dev(svm, dev);

out:
*rsvm = svm;
- *rsdev = sdev;
+ *rsdev_pasid_info = dev_pasid;

return 0;
}
@@ -295,7 +299,7 @@ static int intel_svm_set_dev_pasid(struct iommu_domain *domain,
struct device_domain_info *info = dev_iommu_priv_get(dev);
struct intel_iommu *iommu = info->iommu;
struct mm_struct *mm = domain->mm;
- struct intel_svm_dev *sdev;
+ struct dev_pasid_info *dev_pasid;
struct intel_svm *svm;
unsigned long sflags;
int ret = 0;
@@ -325,20 +329,18 @@ static int intel_svm_set_dev_pasid(struct iommu_domain *domain,
}
}

- sdev = kzalloc(sizeof(*sdev), GFP_KERNEL);
- if (!sdev) {
+ dev_pasid = kzalloc(sizeof(*dev_pasid), GFP_KERNEL);
+ if (!dev_pasid) {
ret = -ENOMEM;
goto free_svm;
}

- sdev->dev = dev;
- sdev->iommu = iommu;
- sdev->did = FLPT_DEFAULT_DID;
- sdev->sid = PCI_DEVID(info->bus, info->devfn);
+ dev_pasid->dev = dev;
+ dev_pasid->sid = PCI_DEVID(info->bus, info->devfn);
if (info->ats_enabled) {
- sdev->qdep = info->ats_qdep;
- if (sdev->qdep >= QI_DEV_EIOTLB_MAX_INVS)
- sdev->qdep = 0;
+ dev_pasid->qdep = info->ats_qdep;
+ if (dev_pasid->qdep >= QI_DEV_EIOTLB_MAX_INVS)
+ dev_pasid->qdep = 0;
}

/* Setup the pasid table: */
@@ -346,14 +348,14 @@ static int intel_svm_set_dev_pasid(struct iommu_domain *domain,
ret = intel_pasid_setup_first_level(iommu, dev, mm->pgd, pasid,
FLPT_DEFAULT_DID, sflags);
if (ret)
- goto free_sdev;
+ goto free_dev_pasid;

- list_add_rcu(&sdev->list, &svm->devs);
+ list_add_rcu(&dev_pasid->link_domain, &svm->devs);

return 0;

-free_sdev:
- kfree(sdev);
+free_dev_pasid:
+ kfree(dev_pasid);
free_svm:
if (list_empty(&svm->devs)) {
mmu_notifier_unregister(&svm->notifier, mm);
@@ -366,26 +368,24 @@ static int intel_svm_set_dev_pasid(struct iommu_domain *domain,

void intel_svm_remove_dev_pasid(struct device *dev, u32 pasid)
{
- struct intel_svm_dev *sdev;
+ struct dev_pasid_info *dev_pasid;
struct intel_iommu *iommu;
struct intel_svm *svm;
- struct mm_struct *mm;

iommu = device_to_iommu(dev, NULL, NULL);
if (!iommu)
return;

- if (pasid_to_svm_sdev(dev, pasid, &svm, &sdev))
+ if (pasid_to_dev_pasid_info(dev, pasid, &svm, &dev_pasid))
return;
- mm = svm->mm;

- if (sdev) {
- list_del_rcu(&sdev->list);
- kfree_rcu(sdev, rcu);
+ if (dev_pasid) {
+ list_del_rcu(&dev_pasid->link_domain);
+ kfree_rcu(dev_pasid, rcu);

if (list_empty(&svm->devs)) {
if (svm->notifier.ops)
- mmu_notifier_unregister(&svm->notifier, mm);
+ mmu_notifier_unregister(&svm->notifier, svm->mm);
pasid_private_remove(svm->pasid);
kfree(svm);
}
--
2.39.3