* [PATCH v3 1/3] iommu/vt-d: Move intel_iommu info from struct intel_svm to struct intel_svm_dev
2020-12-29 3:25 [PATCH v3 0/3] iommu/vt-d: Misc fixes on scalable mode Liu Yi L
@ 2020-12-29 3:25 ` Liu Yi L
2020-12-29 8:34 ` Lu Baolu
2020-12-29 3:25 ` [PATCH v3 2/3] iommu/vt-d: Track device aux-attach with subdevice_domain_info Liu Yi L
2020-12-29 3:25 ` [PATCH v3 3/3] iommu/vt-d: Fix ineffective devTLB invalidation for subdevices Liu Yi L
2 siblings, 1 reply; 11+ messages in thread
From: Liu Yi L @ 2020-12-29 3:25 UTC (permalink / raw)
To: baolu.lu, joro, will
Cc: kevin.tian, jacob.jun.pan, ashok.raj, yi.l.liu, jun.j.tian,
yi.y.sun, iommu, linux-kernel, David Woodhouse, Guo Kaijie,
Xin Zeng
Current struct intel_svm has a field to record the struct intel_iommu
pointer for a PASID bind. And struct intel_svm will be shared by all
the devices bind to the same process. The devices may be behind different
DMAR units. As the iommu driver code uses the intel_iommu pointer stored
in intel_svm struct to do cache invalidations, it may only flush the cache
on a single DMAR unit, for others, the cache invalidation is missed.
As intel_svm struct already has a device list, this patch just moves the
intel_iommu pointer to be a field of intel_svm_dev struct.
Fixes: 1c4f88b7f1f92 ("iommu/vt-d: Shared virtual address in scalable mode")
Cc: Lu Baolu <baolu.lu@linux.intel.com>
Cc: Jacob Pan <jacob.jun.pan@linux.intel.com>
Cc: Raj Ashok <ashok.raj@intel.com>
Cc: David Woodhouse <dwmw2@infradead.org>
Reported-by: Guo Kaijie <Kaijie.Guo@intel.com>
Reported-by: Xin Zeng <xin.zeng@intel.com>
Signed-off-by: Guo Kaijie <Kaijie.Guo@intel.com>
Signed-off-by: Xin Zeng <xin.zeng@intel.com>
Signed-off-by: Liu Yi L <yi.l.liu@intel.com>
---
drivers/iommu/intel/svm.c | 9 +++++----
include/linux/intel-iommu.h | 2 +-
2 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/drivers/iommu/intel/svm.c b/drivers/iommu/intel/svm.c
index 4fa248b98031..69566695d032 100644
--- a/drivers/iommu/intel/svm.c
+++ b/drivers/iommu/intel/svm.c
@@ -142,7 +142,7 @@ static void intel_flush_svm_range_dev (struct intel_svm *svm, struct intel_svm_d
}
desc.qw2 = 0;
desc.qw3 = 0;
- qi_submit_sync(svm->iommu, &desc, 1, 0);
+ qi_submit_sync(sdev->iommu, &desc, 1, 0);
if (sdev->dev_iotlb) {
desc.qw0 = QI_DEV_EIOTLB_PASID(svm->pasid) |
@@ -166,7 +166,7 @@ static void intel_flush_svm_range_dev (struct intel_svm *svm, struct intel_svm_d
}
desc.qw2 = 0;
desc.qw3 = 0;
- qi_submit_sync(svm->iommu, &desc, 1, 0);
+ qi_submit_sync(sdev->iommu, &desc, 1, 0);
}
}
@@ -211,7 +211,7 @@ static void intel_mm_release(struct mmu_notifier *mn, struct mm_struct *mm)
*/
rcu_read_lock();
list_for_each_entry_rcu(sdev, &svm->devs, list)
- intel_pasid_tear_down_entry(svm->iommu, sdev->dev,
+ intel_pasid_tear_down_entry(sdev->iommu, sdev->dev,
svm->pasid, true);
rcu_read_unlock();
@@ -363,6 +363,7 @@ int intel_svm_bind_gpasid(struct iommu_domain *domain, struct device *dev,
}
sdev->dev = dev;
sdev->sid = PCI_DEVID(info->bus, info->devfn);
+ sdev->iommu = iommu;
/* Only count users if device has aux domains */
if (iommu_dev_feature_enabled(dev, IOMMU_DEV_FEAT_AUX))
@@ -546,6 +547,7 @@ intel_svm_bind_mm(struct device *dev, unsigned int flags,
goto out;
}
sdev->dev = dev;
+ sdev->iommu = iommu;
ret = intel_iommu_enable_pasid(iommu, dev);
if (ret) {
@@ -575,7 +577,6 @@ intel_svm_bind_mm(struct device *dev, unsigned int flags,
kfree(sdev);
goto out;
}
- svm->iommu = iommu;
if (pasid_max > intel_pasid_max_id)
pasid_max = intel_pasid_max_id;
diff --git a/include/linux/intel-iommu.h b/include/linux/intel-iommu.h
index d956987ed032..94522685a0d9 100644
--- a/include/linux/intel-iommu.h
+++ b/include/linux/intel-iommu.h
@@ -758,6 +758,7 @@ struct intel_svm_dev {
struct list_head list;
struct rcu_head rcu;
struct device *dev;
+ struct intel_iommu *iommu;
struct svm_dev_ops *ops;
struct iommu_sva sva;
u32 pasid;
@@ -771,7 +772,6 @@ struct intel_svm {
struct mmu_notifier notifier;
struct mm_struct *mm;
- struct intel_iommu *iommu;
unsigned int flags;
u32 pasid;
int gpasid; /* In case that guest PASID is different from host PASID */
--
2.25.1
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH v3 1/3] iommu/vt-d: Move intel_iommu info from struct intel_svm to struct intel_svm_dev
2020-12-29 3:25 ` [PATCH v3 1/3] iommu/vt-d: Move intel_iommu info from struct intel_svm to struct intel_svm_dev Liu Yi L
@ 2020-12-29 8:34 ` Lu Baolu
0 siblings, 0 replies; 11+ messages in thread
From: Lu Baolu @ 2020-12-29 8:34 UTC (permalink / raw)
To: Liu Yi L, joro, will
Cc: baolu.lu, kevin.tian, jacob.jun.pan, ashok.raj, jun.j.tian,
yi.y.sun, iommu, linux-kernel, David Woodhouse, Guo Kaijie,
Xin Zeng
Hi Yi,
On 2020/12/29 11:25, Liu Yi L wrote:
> Current struct intel_svm has a field to record the struct intel_iommu
> pointer for a PASID bind. And struct intel_svm will be shared by all
> the devices bind to the same process. The devices may be behind different
> DMAR units. As the iommu driver code uses the intel_iommu pointer stored
> in intel_svm struct to do cache invalidations, it may only flush the cache
> on a single DMAR unit, for others, the cache invalidation is missed.
>
> As intel_svm struct already has a device list, this patch just moves the
> intel_iommu pointer to be a field of intel_svm_dev struct.
>
> Fixes: 1c4f88b7f1f92 ("iommu/vt-d: Shared virtual address in scalable mode")
> Cc: Lu Baolu <baolu.lu@linux.intel.com>
> Cc: Jacob Pan <jacob.jun.pan@linux.intel.com>
> Cc: Raj Ashok <ashok.raj@intel.com>
> Cc: David Woodhouse <dwmw2@infradead.org>
> Reported-by: Guo Kaijie <Kaijie.Guo@intel.com>
> Reported-by: Xin Zeng <xin.zeng@intel.com>
> Signed-off-by: Guo Kaijie <Kaijie.Guo@intel.com>
> Signed-off-by: Xin Zeng <xin.zeng@intel.com>
> Signed-off-by: Liu Yi L <yi.l.liu@intel.com>
This fix looks good to me. Thanks!
Cc: stable@vger.kernel.org # v5.0+
Acked-by: Lu Baolu <baolu.lu@linux.intel.com>
Best regards,
baolu
> ---
> drivers/iommu/intel/svm.c | 9 +++++----
> include/linux/intel-iommu.h | 2 +-
> 2 files changed, 6 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/iommu/intel/svm.c b/drivers/iommu/intel/svm.c
> index 4fa248b98031..69566695d032 100644
> --- a/drivers/iommu/intel/svm.c
> +++ b/drivers/iommu/intel/svm.c
> @@ -142,7 +142,7 @@ static void intel_flush_svm_range_dev (struct intel_svm *svm, struct intel_svm_d
> }
> desc.qw2 = 0;
> desc.qw3 = 0;
> - qi_submit_sync(svm->iommu, &desc, 1, 0);
> + qi_submit_sync(sdev->iommu, &desc, 1, 0);
>
> if (sdev->dev_iotlb) {
> desc.qw0 = QI_DEV_EIOTLB_PASID(svm->pasid) |
> @@ -166,7 +166,7 @@ static void intel_flush_svm_range_dev (struct intel_svm *svm, struct intel_svm_d
> }
> desc.qw2 = 0;
> desc.qw3 = 0;
> - qi_submit_sync(svm->iommu, &desc, 1, 0);
> + qi_submit_sync(sdev->iommu, &desc, 1, 0);
> }
> }
>
> @@ -211,7 +211,7 @@ static void intel_mm_release(struct mmu_notifier *mn, struct mm_struct *mm)
> */
> rcu_read_lock();
> list_for_each_entry_rcu(sdev, &svm->devs, list)
> - intel_pasid_tear_down_entry(svm->iommu, sdev->dev,
> + intel_pasid_tear_down_entry(sdev->iommu, sdev->dev,
> svm->pasid, true);
> rcu_read_unlock();
>
> @@ -363,6 +363,7 @@ int intel_svm_bind_gpasid(struct iommu_domain *domain, struct device *dev,
> }
> sdev->dev = dev;
> sdev->sid = PCI_DEVID(info->bus, info->devfn);
> + sdev->iommu = iommu;
>
> /* Only count users if device has aux domains */
> if (iommu_dev_feature_enabled(dev, IOMMU_DEV_FEAT_AUX))
> @@ -546,6 +547,7 @@ intel_svm_bind_mm(struct device *dev, unsigned int flags,
> goto out;
> }
> sdev->dev = dev;
> + sdev->iommu = iommu;
>
> ret = intel_iommu_enable_pasid(iommu, dev);
> if (ret) {
> @@ -575,7 +577,6 @@ intel_svm_bind_mm(struct device *dev, unsigned int flags,
> kfree(sdev);
> goto out;
> }
> - svm->iommu = iommu;
>
> if (pasid_max > intel_pasid_max_id)
> pasid_max = intel_pasid_max_id;
> diff --git a/include/linux/intel-iommu.h b/include/linux/intel-iommu.h
> index d956987ed032..94522685a0d9 100644
> --- a/include/linux/intel-iommu.h
> +++ b/include/linux/intel-iommu.h
> @@ -758,6 +758,7 @@ struct intel_svm_dev {
> struct list_head list;
> struct rcu_head rcu;
> struct device *dev;
> + struct intel_iommu *iommu;
> struct svm_dev_ops *ops;
> struct iommu_sva sva;
> u32 pasid;
> @@ -771,7 +772,6 @@ struct intel_svm {
> struct mmu_notifier notifier;
> struct mm_struct *mm;
>
> - struct intel_iommu *iommu;
> unsigned int flags;
> u32 pasid;
> int gpasid; /* In case that guest PASID is different from host PASID */
>
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v3 2/3] iommu/vt-d: Track device aux-attach with subdevice_domain_info
2020-12-29 3:25 [PATCH v3 0/3] iommu/vt-d: Misc fixes on scalable mode Liu Yi L
2020-12-29 3:25 ` [PATCH v3 1/3] iommu/vt-d: Move intel_iommu info from struct intel_svm to struct intel_svm_dev Liu Yi L
@ 2020-12-29 3:25 ` Liu Yi L
2020-12-29 8:38 ` Lu Baolu
2020-12-29 3:25 ` [PATCH v3 3/3] iommu/vt-d: Fix ineffective devTLB invalidation for subdevices Liu Yi L
2 siblings, 1 reply; 11+ messages in thread
From: Liu Yi L @ 2020-12-29 3:25 UTC (permalink / raw)
To: baolu.lu, joro, will
Cc: kevin.tian, jacob.jun.pan, ashok.raj, yi.l.liu, jun.j.tian,
yi.y.sun, iommu, linux-kernel, Xin Zeng
In the existing code, loop all devices attached to a domain does not
include sub-devices attached via iommu_aux_attach_device().
This was found by when I'm working on the belwo patch, There is no
device in the domain->devices list, thus unable to get the cap and
ecap of iommu unit. But this domain actually has subdevice which is
attached via aux-manner. But it is tracked by domain. This patch is
going to fix it.
https://lore.kernel.org/kvm/1599734733-6431-17-git-send-email-yi.l.liu@intel.com/
And this fix goes beyond the patch above, such sub-device tracking is
necessary for other cases. For example, flushing device_iotlb for a
domain which has sub-devices attached by auxiliary manner.
Co-developed-by: Xin Zeng <xin.zeng@intel.com>
Signed-off-by: Xin Zeng <xin.zeng@intel.com>
Signed-off-by: Liu Yi L <yi.l.liu@intel.com>
---
drivers/iommu/intel/iommu.c | 95 +++++++++++++++++++++++++++----------
include/linux/intel-iommu.h | 16 +++++--
2 files changed, 82 insertions(+), 29 deletions(-)
diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
index 788119c5b021..d7720a836268 100644
--- a/drivers/iommu/intel/iommu.c
+++ b/drivers/iommu/intel/iommu.c
@@ -1877,6 +1877,7 @@ static struct dmar_domain *alloc_domain(int flags)
domain->flags |= DOMAIN_FLAG_USE_FIRST_LEVEL;
domain->has_iotlb_device = false;
INIT_LIST_HEAD(&domain->devices);
+ INIT_LIST_HEAD(&domain->subdevices);
return domain;
}
@@ -2547,7 +2548,7 @@ static struct dmar_domain *dmar_insert_one_dev_info(struct intel_iommu *iommu,
info->iommu = iommu;
info->pasid_table = NULL;
info->auxd_enabled = 0;
- INIT_LIST_HEAD(&info->auxiliary_domains);
+ INIT_LIST_HEAD(&info->subdevices);
if (dev && dev_is_pci(dev)) {
struct pci_dev *pdev = to_pci_dev(info->dev);
@@ -4475,33 +4476,61 @@ is_aux_domain(struct device *dev, struct iommu_domain *domain)
domain->type == IOMMU_DOMAIN_UNMANAGED;
}
-static void auxiliary_link_device(struct dmar_domain *domain,
- struct device *dev)
+static inline struct subdev_domain_info *
+lookup_subdev_info(struct dmar_domain *domain, struct device *dev)
+{
+ struct subdev_domain_info *sinfo;
+
+ if (!list_empty(&domain->subdevices)) {
+ list_for_each_entry(sinfo, &domain->subdevices, link_domain) {
+ if (sinfo->pdev == dev)
+ return sinfo;
+ }
+ }
+
+ return NULL;
+}
+
+static int auxiliary_link_device(struct dmar_domain *domain,
+ struct device *dev)
{
struct device_domain_info *info = get_domain_info(dev);
+ struct subdev_domain_info *sinfo = lookup_subdev_info(domain, dev);
assert_spin_locked(&device_domain_lock);
if (WARN_ON(!info))
- return;
+ return -EINVAL;
+
+ if (!sinfo) {
+ sinfo = kzalloc(sizeof(*sinfo), GFP_ATOMIC);
+ sinfo->domain = domain;
+ sinfo->pdev = dev;
+ list_add(&sinfo->link_phys, &info->subdevices);
+ list_add(&sinfo->link_domain, &domain->subdevices);
+ }
- domain->auxd_refcnt++;
- list_add(&domain->auxd, &info->auxiliary_domains);
+ return ++sinfo->users;
}
-static void auxiliary_unlink_device(struct dmar_domain *domain,
- struct device *dev)
+static int auxiliary_unlink_device(struct dmar_domain *domain,
+ struct device *dev)
{
struct device_domain_info *info = get_domain_info(dev);
+ struct subdev_domain_info *sinfo = lookup_subdev_info(domain, dev);
+ int ret;
assert_spin_locked(&device_domain_lock);
- if (WARN_ON(!info))
- return;
+ if (WARN_ON(!info || !sinfo || sinfo->users <= 0))
+ return -EINVAL;
- list_del(&domain->auxd);
- domain->auxd_refcnt--;
+ ret = --sinfo->users;
+ if (!ret) {
+ list_del(&sinfo->link_phys);
+ list_del(&sinfo->link_domain);
+ kfree(sinfo);
+ }
- if (!domain->auxd_refcnt && domain->default_pasid > 0)
- ioasid_put(domain->default_pasid);
+ return ret;
}
static int aux_domain_add_dev(struct dmar_domain *domain,
@@ -4530,6 +4559,19 @@ static int aux_domain_add_dev(struct dmar_domain *domain,
}
spin_lock_irqsave(&device_domain_lock, flags);
+ ret = auxiliary_link_device(domain, dev);
+ if (ret <= 0)
+ goto link_failed;
+
+ /*
+ * Subdevices from the same physical device can be attached to the
+ * same domain. For such cases, only the first subdevice attachment
+ * needs to go through the full steps in this function. So if ret >
+ * 1, just goto out.
+ */
+ if (ret > 1)
+ goto out;
+
/*
* iommu->lock must be held to attach domain to iommu and setup the
* pasid entry for second level translation.
@@ -4548,10 +4590,9 @@ static int aux_domain_add_dev(struct dmar_domain *domain,
domain->default_pasid);
if (ret)
goto table_failed;
- spin_unlock(&iommu->lock);
-
- auxiliary_link_device(domain, dev);
+ spin_unlock(&iommu->lock);
+out:
spin_unlock_irqrestore(&device_domain_lock, flags);
return 0;
@@ -4560,8 +4601,10 @@ static int aux_domain_add_dev(struct dmar_domain *domain,
domain_detach_iommu(domain, iommu);
attach_failed:
spin_unlock(&iommu->lock);
+ auxiliary_unlink_device(domain, dev);
+link_failed:
spin_unlock_irqrestore(&device_domain_lock, flags);
- if (!domain->auxd_refcnt && domain->default_pasid > 0)
+ if (list_empty(&domain->subdevices) && domain->default_pasid > 0)
ioasid_put(domain->default_pasid);
return ret;
@@ -4581,14 +4624,18 @@ static void aux_domain_remove_dev(struct dmar_domain *domain,
info = get_domain_info(dev);
iommu = info->iommu;
- auxiliary_unlink_device(domain, dev);
-
- spin_lock(&iommu->lock);
- intel_pasid_tear_down_entry(iommu, dev, domain->default_pasid, false);
- domain_detach_iommu(domain, iommu);
- spin_unlock(&iommu->lock);
+ if (!auxiliary_unlink_device(domain, dev)) {
+ spin_lock(&iommu->lock);
+ intel_pasid_tear_down_entry(iommu, dev,
+ domain->default_pasid, false);
+ domain_detach_iommu(domain, iommu);
+ spin_unlock(&iommu->lock);
+ }
spin_unlock_irqrestore(&device_domain_lock, flags);
+
+ if (list_empty(&domain->subdevices) && domain->default_pasid > 0)
+ ioasid_put(domain->default_pasid);
}
static int prepare_domain_attach_device(struct iommu_domain *domain,
diff --git a/include/linux/intel-iommu.h b/include/linux/intel-iommu.h
index 94522685a0d9..09c6a0bf3892 100644
--- a/include/linux/intel-iommu.h
+++ b/include/linux/intel-iommu.h
@@ -533,11 +533,10 @@ struct dmar_domain {
/* Domain ids per IOMMU. Use u16 since
* domain ids are 16 bit wide according
* to VT-d spec, section 9.3 */
- unsigned int auxd_refcnt; /* Refcount of auxiliary attaching */
bool has_iotlb_device;
struct list_head devices; /* all devices' list */
- struct list_head auxd; /* link to device's auxiliary list */
+ struct list_head subdevices; /* all subdevices' list */
struct iova_domain iovad; /* iova's that belong to this domain */
struct dma_pte *pgd; /* virtual address */
@@ -610,14 +609,21 @@ struct intel_iommu {
struct dmar_drhd_unit *drhd;
};
+/* Per subdevice private data */
+struct subdev_domain_info {
+ struct list_head link_phys; /* link to phys device siblings */
+ struct list_head link_domain; /* link to domain siblings */
+ struct device *pdev; /* physical device derived from */
+ struct dmar_domain *domain; /* aux-domain */
+ int users; /* user count */
+};
+
/* PCI domain-device relationship */
struct device_domain_info {
struct list_head link; /* link to domain siblings */
struct list_head global; /* link to global list */
struct list_head table; /* link to pasid table */
- struct list_head auxiliary_domains; /* auxiliary domains
- * attached to this device
- */
+ struct list_head subdevices; /* subdevices sibling */
u32 segment; /* PCI segment number */
u8 bus; /* PCI bus number */
u8 devfn; /* PCI devfn number */
--
2.25.1
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH v3 2/3] iommu/vt-d: Track device aux-attach with subdevice_domain_info
2020-12-29 3:25 ` [PATCH v3 2/3] iommu/vt-d: Track device aux-attach with subdevice_domain_info Liu Yi L
@ 2020-12-29 8:38 ` Lu Baolu
2021-01-05 5:58 ` Liu, Yi L
0 siblings, 1 reply; 11+ messages in thread
From: Lu Baolu @ 2020-12-29 8:38 UTC (permalink / raw)
To: Liu Yi L, joro, will
Cc: baolu.lu, kevin.tian, jacob.jun.pan, ashok.raj, jun.j.tian,
yi.y.sun, iommu, linux-kernel, Xin Zeng
Hi Yi,
On 2020/12/29 11:25, Liu Yi L wrote:
> In the existing code, loop all devices attached to a domain does not
> include sub-devices attached via iommu_aux_attach_device().
>
> This was found by when I'm working on the belwo patch, There is no
^^^^^
below
> device in the domain->devices list, thus unable to get the cap and
> ecap of iommu unit. But this domain actually has subdevice which is
> attached via aux-manner. But it is tracked by domain. This patch is
> going to fix it.
>
> https://lore.kernel.org/kvm/1599734733-6431-17-git-send-email-yi.l.liu@intel.com/
>
> And this fix goes beyond the patch above, such sub-device tracking is
> necessary for other cases. For example, flushing device_iotlb for a
> domain which has sub-devices attached by auxiliary manner.
>
> Co-developed-by: Xin Zeng <xin.zeng@intel.com>
> Signed-off-by: Xin Zeng <xin.zeng@intel.com>
> Signed-off-by: Liu Yi L <yi.l.liu@intel.com>
Others look good to me.
Fixes: 67b8e02b5e761 ("iommu/vt-d: Aux-domain specific domain
attach/detach")
Acked-by: Lu Baolu <baolu.lu@linux.intel.com>
Best regards,
baolu
> ---
> drivers/iommu/intel/iommu.c | 95 +++++++++++++++++++++++++++----------
> include/linux/intel-iommu.h | 16 +++++--
> 2 files changed, 82 insertions(+), 29 deletions(-)
>
> diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
> index 788119c5b021..d7720a836268 100644
> --- a/drivers/iommu/intel/iommu.c
> +++ b/drivers/iommu/intel/iommu.c
> @@ -1877,6 +1877,7 @@ static struct dmar_domain *alloc_domain(int flags)
> domain->flags |= DOMAIN_FLAG_USE_FIRST_LEVEL;
> domain->has_iotlb_device = false;
> INIT_LIST_HEAD(&domain->devices);
> + INIT_LIST_HEAD(&domain->subdevices);
>
> return domain;
> }
> @@ -2547,7 +2548,7 @@ static struct dmar_domain *dmar_insert_one_dev_info(struct intel_iommu *iommu,
> info->iommu = iommu;
> info->pasid_table = NULL;
> info->auxd_enabled = 0;
> - INIT_LIST_HEAD(&info->auxiliary_domains);
> + INIT_LIST_HEAD(&info->subdevices);
>
> if (dev && dev_is_pci(dev)) {
> struct pci_dev *pdev = to_pci_dev(info->dev);
> @@ -4475,33 +4476,61 @@ is_aux_domain(struct device *dev, struct iommu_domain *domain)
> domain->type == IOMMU_DOMAIN_UNMANAGED;
> }
>
> -static void auxiliary_link_device(struct dmar_domain *domain,
> - struct device *dev)
> +static inline struct subdev_domain_info *
> +lookup_subdev_info(struct dmar_domain *domain, struct device *dev)
> +{
> + struct subdev_domain_info *sinfo;
> +
> + if (!list_empty(&domain->subdevices)) {
> + list_for_each_entry(sinfo, &domain->subdevices, link_domain) {
> + if (sinfo->pdev == dev)
> + return sinfo;
> + }
> + }
> +
> + return NULL;
> +}
> +
> +static int auxiliary_link_device(struct dmar_domain *domain,
> + struct device *dev)
> {
> struct device_domain_info *info = get_domain_info(dev);
> + struct subdev_domain_info *sinfo = lookup_subdev_info(domain, dev);
>
> assert_spin_locked(&device_domain_lock);
> if (WARN_ON(!info))
> - return;
> + return -EINVAL;
> +
> + if (!sinfo) {
> + sinfo = kzalloc(sizeof(*sinfo), GFP_ATOMIC);
> + sinfo->domain = domain;
> + sinfo->pdev = dev;
> + list_add(&sinfo->link_phys, &info->subdevices);
> + list_add(&sinfo->link_domain, &domain->subdevices);
> + }
>
> - domain->auxd_refcnt++;
> - list_add(&domain->auxd, &info->auxiliary_domains);
> + return ++sinfo->users;
> }
>
> -static void auxiliary_unlink_device(struct dmar_domain *domain,
> - struct device *dev)
> +static int auxiliary_unlink_device(struct dmar_domain *domain,
> + struct device *dev)
> {
> struct device_domain_info *info = get_domain_info(dev);
> + struct subdev_domain_info *sinfo = lookup_subdev_info(domain, dev);
> + int ret;
>
> assert_spin_locked(&device_domain_lock);
> - if (WARN_ON(!info))
> - return;
> + if (WARN_ON(!info || !sinfo || sinfo->users <= 0))
> + return -EINVAL;
>
> - list_del(&domain->auxd);
> - domain->auxd_refcnt--;
> + ret = --sinfo->users;
> + if (!ret) {
> + list_del(&sinfo->link_phys);
> + list_del(&sinfo->link_domain);
> + kfree(sinfo);
> + }
>
> - if (!domain->auxd_refcnt && domain->default_pasid > 0)
> - ioasid_put(domain->default_pasid);
> + return ret;
> }
>
> static int aux_domain_add_dev(struct dmar_domain *domain,
> @@ -4530,6 +4559,19 @@ static int aux_domain_add_dev(struct dmar_domain *domain,
> }
>
> spin_lock_irqsave(&device_domain_lock, flags);
> + ret = auxiliary_link_device(domain, dev);
> + if (ret <= 0)
> + goto link_failed;
> +
> + /*
> + * Subdevices from the same physical device can be attached to the
> + * same domain. For such cases, only the first subdevice attachment
> + * needs to go through the full steps in this function. So if ret >
> + * 1, just goto out.
> + */
> + if (ret > 1)
> + goto out;
> +
> /*
> * iommu->lock must be held to attach domain to iommu and setup the
> * pasid entry for second level translation.
> @@ -4548,10 +4590,9 @@ static int aux_domain_add_dev(struct dmar_domain *domain,
> domain->default_pasid);
> if (ret)
> goto table_failed;
> - spin_unlock(&iommu->lock);
> -
> - auxiliary_link_device(domain, dev);
>
> + spin_unlock(&iommu->lock);
> +out:
> spin_unlock_irqrestore(&device_domain_lock, flags);
>
> return 0;
> @@ -4560,8 +4601,10 @@ static int aux_domain_add_dev(struct dmar_domain *domain,
> domain_detach_iommu(domain, iommu);
> attach_failed:
> spin_unlock(&iommu->lock);
> + auxiliary_unlink_device(domain, dev);
> +link_failed:
> spin_unlock_irqrestore(&device_domain_lock, flags);
> - if (!domain->auxd_refcnt && domain->default_pasid > 0)
> + if (list_empty(&domain->subdevices) && domain->default_pasid > 0)
> ioasid_put(domain->default_pasid);
>
> return ret;
> @@ -4581,14 +4624,18 @@ static void aux_domain_remove_dev(struct dmar_domain *domain,
> info = get_domain_info(dev);
> iommu = info->iommu;
>
> - auxiliary_unlink_device(domain, dev);
> -
> - spin_lock(&iommu->lock);
> - intel_pasid_tear_down_entry(iommu, dev, domain->default_pasid, false);
> - domain_detach_iommu(domain, iommu);
> - spin_unlock(&iommu->lock);
> + if (!auxiliary_unlink_device(domain, dev)) {
> + spin_lock(&iommu->lock);
> + intel_pasid_tear_down_entry(iommu, dev,
> + domain->default_pasid, false);
> + domain_detach_iommu(domain, iommu);
> + spin_unlock(&iommu->lock);
> + }
>
> spin_unlock_irqrestore(&device_domain_lock, flags);
> +
> + if (list_empty(&domain->subdevices) && domain->default_pasid > 0)
> + ioasid_put(domain->default_pasid);
> }
>
> static int prepare_domain_attach_device(struct iommu_domain *domain,
> diff --git a/include/linux/intel-iommu.h b/include/linux/intel-iommu.h
> index 94522685a0d9..09c6a0bf3892 100644
> --- a/include/linux/intel-iommu.h
> +++ b/include/linux/intel-iommu.h
> @@ -533,11 +533,10 @@ struct dmar_domain {
> /* Domain ids per IOMMU. Use u16 since
> * domain ids are 16 bit wide according
> * to VT-d spec, section 9.3 */
> - unsigned int auxd_refcnt; /* Refcount of auxiliary attaching */
>
> bool has_iotlb_device;
> struct list_head devices; /* all devices' list */
> - struct list_head auxd; /* link to device's auxiliary list */
> + struct list_head subdevices; /* all subdevices' list */
> struct iova_domain iovad; /* iova's that belong to this domain */
>
> struct dma_pte *pgd; /* virtual address */
> @@ -610,14 +609,21 @@ struct intel_iommu {
> struct dmar_drhd_unit *drhd;
> };
>
> +/* Per subdevice private data */
> +struct subdev_domain_info {
> + struct list_head link_phys; /* link to phys device siblings */
> + struct list_head link_domain; /* link to domain siblings */
> + struct device *pdev; /* physical device derived from */
> + struct dmar_domain *domain; /* aux-domain */
> + int users; /* user count */
> +};
> +
> /* PCI domain-device relationship */
> struct device_domain_info {
> struct list_head link; /* link to domain siblings */
> struct list_head global; /* link to global list */
> struct list_head table; /* link to pasid table */
> - struct list_head auxiliary_domains; /* auxiliary domains
> - * attached to this device
> - */
> + struct list_head subdevices; /* subdevices sibling */
> u32 segment; /* PCI segment number */
> u8 bus; /* PCI bus number */
> u8 devfn; /* PCI devfn number */
>
^ permalink raw reply [flat|nested] 11+ messages in thread
* RE: [PATCH v3 2/3] iommu/vt-d: Track device aux-attach with subdevice_domain_info
2020-12-29 8:38 ` Lu Baolu
@ 2021-01-05 5:58 ` Liu, Yi L
0 siblings, 0 replies; 11+ messages in thread
From: Liu, Yi L @ 2021-01-05 5:58 UTC (permalink / raw)
To: Lu Baolu, joro, will
Cc: Tian, Kevin, jacob.jun.pan, Raj, Ashok, Tian, Jun J, Sun, Yi Y,
iommu, linux-kernel, Zeng, Xin
Hi Baolu,
> From: Lu Baolu <baolu.lu@linux.intel.com>
> Sent: Tuesday, December 29, 2020 4:38 PM
>
> Hi Yi,
>
> On 2020/12/29 11:25, Liu Yi L wrote:
> > In the existing code, loop all devices attached to a domain does not
> > include sub-devices attached via iommu_aux_attach_device().
> >
> > This was found by when I'm working on the belwo patch, There is no
> ^^^^^
> below
nice catch. 😉
> > device in the domain->devices list, thus unable to get the cap and
> > ecap of iommu unit. But this domain actually has subdevice which is
> > attached via aux-manner. But it is tracked by domain. This patch is
> > going to fix it.
> >
> > https://lore.kernel.org/kvm/1599734733-6431-17-git-send-email-
> yi.l.liu@intel.com/
> >
> > And this fix goes beyond the patch above, such sub-device tracking is
> > necessary for other cases. For example, flushing device_iotlb for a
> > domain which has sub-devices attached by auxiliary manner.
> >
> > Co-developed-by: Xin Zeng <xin.zeng@intel.com>
> > Signed-off-by: Xin Zeng <xin.zeng@intel.com>
> > Signed-off-by: Liu Yi L <yi.l.liu@intel.com>
>
> Others look good to me.
>
> Fixes: 67b8e02b5e761 ("iommu/vt-d: Aux-domain specific domain
> attach/detach")
> Acked-by: Lu Baolu <baolu.lu@linux.intel.com>
thanks,
Regards,
Yi Liu
> Best regards,
> baolu
>
> > ---
> > drivers/iommu/intel/iommu.c | 95 +++++++++++++++++++++++++++--------
> --
> > include/linux/intel-iommu.h | 16 +++++--
> > 2 files changed, 82 insertions(+), 29 deletions(-)
> >
> > diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
> > index 788119c5b021..d7720a836268 100644
> > --- a/drivers/iommu/intel/iommu.c
> > +++ b/drivers/iommu/intel/iommu.c
> > @@ -1877,6 +1877,7 @@ static struct dmar_domain *alloc_domain(int
> flags)
> > domain->flags |= DOMAIN_FLAG_USE_FIRST_LEVEL;
> > domain->has_iotlb_device = false;
> > INIT_LIST_HEAD(&domain->devices);
> > + INIT_LIST_HEAD(&domain->subdevices);
> >
> > return domain;
> > }
> > @@ -2547,7 +2548,7 @@ static struct dmar_domain
> *dmar_insert_one_dev_info(struct intel_iommu *iommu,
> > info->iommu = iommu;
> > info->pasid_table = NULL;
> > info->auxd_enabled = 0;
> > - INIT_LIST_HEAD(&info->auxiliary_domains);
> > + INIT_LIST_HEAD(&info->subdevices);
> >
> > if (dev && dev_is_pci(dev)) {
> > struct pci_dev *pdev = to_pci_dev(info->dev);
> > @@ -4475,33 +4476,61 @@ is_aux_domain(struct device *dev, struct
> iommu_domain *domain)
> > domain->type == IOMMU_DOMAIN_UNMANAGED;
> > }
> >
> > -static void auxiliary_link_device(struct dmar_domain *domain,
> > - struct device *dev)
> > +static inline struct subdev_domain_info *
> > +lookup_subdev_info(struct dmar_domain *domain, struct device *dev)
> > +{
> > + struct subdev_domain_info *sinfo;
> > +
> > + if (!list_empty(&domain->subdevices)) {
> > + list_for_each_entry(sinfo, &domain->subdevices,
> link_domain) {
> > + if (sinfo->pdev == dev)
> > + return sinfo;
> > + }
> > + }
> > +
> > + return NULL;
> > +}
> > +
> > +static int auxiliary_link_device(struct dmar_domain *domain,
> > + struct device *dev)
> > {
> > struct device_domain_info *info = get_domain_info(dev);
> > + struct subdev_domain_info *sinfo = lookup_subdev_info(domain,
> dev);
> >
> > assert_spin_locked(&device_domain_lock);
> > if (WARN_ON(!info))
> > - return;
> > + return -EINVAL;
> > +
> > + if (!sinfo) {
> > + sinfo = kzalloc(sizeof(*sinfo), GFP_ATOMIC);
> > + sinfo->domain = domain;
> > + sinfo->pdev = dev;
> > + list_add(&sinfo->link_phys, &info->subdevices);
> > + list_add(&sinfo->link_domain, &domain->subdevices);
> > + }
> >
> > - domain->auxd_refcnt++;
> > - list_add(&domain->auxd, &info->auxiliary_domains);
> > + return ++sinfo->users;
> > }
> >
> > -static void auxiliary_unlink_device(struct dmar_domain *domain,
> > - struct device *dev)
> > +static int auxiliary_unlink_device(struct dmar_domain *domain,
> > + struct device *dev)
> > {
> > struct device_domain_info *info = get_domain_info(dev);
> > + struct subdev_domain_info *sinfo = lookup_subdev_info(domain,
> dev);
> > + int ret;
> >
> > assert_spin_locked(&device_domain_lock);
> > - if (WARN_ON(!info))
> > - return;
> > + if (WARN_ON(!info || !sinfo || sinfo->users <= 0))
> > + return -EINVAL;
> >
> > - list_del(&domain->auxd);
> > - domain->auxd_refcnt--;
> > + ret = --sinfo->users;
> > + if (!ret) {
> > + list_del(&sinfo->link_phys);
> > + list_del(&sinfo->link_domain);
> > + kfree(sinfo);
> > + }
> >
> > - if (!domain->auxd_refcnt && domain->default_pasid > 0)
> > - ioasid_put(domain->default_pasid);
> > + return ret;
> > }
> >
> > static int aux_domain_add_dev(struct dmar_domain *domain,
> > @@ -4530,6 +4559,19 @@ static int aux_domain_add_dev(struct
> dmar_domain *domain,
> > }
> >
> > spin_lock_irqsave(&device_domain_lock, flags);
> > + ret = auxiliary_link_device(domain, dev);
> > + if (ret <= 0)
> > + goto link_failed;
> > +
> > + /*
> > + * Subdevices from the same physical device can be attached to the
> > + * same domain. For such cases, only the first subdevice attachment
> > + * needs to go through the full steps in this function. So if ret >
> > + * 1, just goto out.
> > + */
> > + if (ret > 1)
> > + goto out;
> > +
> > /*
> > * iommu->lock must be held to attach domain to iommu and setup
> the
> > * pasid entry for second level translation.
> > @@ -4548,10 +4590,9 @@ static int aux_domain_add_dev(struct
> dmar_domain *domain,
> > domain->default_pasid);
> > if (ret)
> > goto table_failed;
> > - spin_unlock(&iommu->lock);
> > -
> > - auxiliary_link_device(domain, dev);
> >
> > + spin_unlock(&iommu->lock);
> > +out:
> > spin_unlock_irqrestore(&device_domain_lock, flags);
> >
> > return 0;
> > @@ -4560,8 +4601,10 @@ static int aux_domain_add_dev(struct
> dmar_domain *domain,
> > domain_detach_iommu(domain, iommu);
> > attach_failed:
> > spin_unlock(&iommu->lock);
> > + auxiliary_unlink_device(domain, dev);
> > +link_failed:
> > spin_unlock_irqrestore(&device_domain_lock, flags);
> > - if (!domain->auxd_refcnt && domain->default_pasid > 0)
> > + if (list_empty(&domain->subdevices) && domain->default_pasid > 0)
> > ioasid_put(domain->default_pasid);
> >
> > return ret;
> > @@ -4581,14 +4624,18 @@ static void aux_domain_remove_dev(struct
> dmar_domain *domain,
> > info = get_domain_info(dev);
> > iommu = info->iommu;
> >
> > - auxiliary_unlink_device(domain, dev);
> > -
> > - spin_lock(&iommu->lock);
> > - intel_pasid_tear_down_entry(iommu, dev, domain->default_pasid,
> false);
> > - domain_detach_iommu(domain, iommu);
> > - spin_unlock(&iommu->lock);
> > + if (!auxiliary_unlink_device(domain, dev)) {
> > + spin_lock(&iommu->lock);
> > + intel_pasid_tear_down_entry(iommu, dev,
> > + domain->default_pasid, false);
> > + domain_detach_iommu(domain, iommu);
> > + spin_unlock(&iommu->lock);
> > + }
> >
> > spin_unlock_irqrestore(&device_domain_lock, flags);
> > +
> > + if (list_empty(&domain->subdevices) && domain->default_pasid > 0)
> > + ioasid_put(domain->default_pasid);
> > }
> >
> > static int prepare_domain_attach_device(struct iommu_domain
> *domain,
> > diff --git a/include/linux/intel-iommu.h b/include/linux/intel-iommu.h
> > index 94522685a0d9..09c6a0bf3892 100644
> > --- a/include/linux/intel-iommu.h
> > +++ b/include/linux/intel-iommu.h
> > @@ -533,11 +533,10 @@ struct dmar_domain {
> > /* Domain ids per IOMMU. Use u16
> since
> > * domain ids are 16 bit wide
> according
> > * to VT-d spec, section 9.3 */
> > - unsigned int auxd_refcnt; /* Refcount of auxiliary attaching */
> >
> > bool has_iotlb_device;
> > struct list_head devices; /* all devices' list */
> > - struct list_head auxd; /* link to device's auxiliary list */
> > + struct list_head subdevices; /* all subdevices' list */
> > struct iova_domain iovad; /* iova's that belong to this domain
> */
> >
> > struct dma_pte *pgd; /* virtual address */
> > @@ -610,14 +609,21 @@ struct intel_iommu {
> > struct dmar_drhd_unit *drhd;
> > };
> >
> > +/* Per subdevice private data */
> > +struct subdev_domain_info {
> > + struct list_head link_phys; /* link to phys device siblings */
> > + struct list_head link_domain; /* link to domain siblings */
> > + struct device *pdev; /* physical device derived from */
> > + struct dmar_domain *domain; /* aux-domain */
> > + int users; /* user count */
> > +};
> > +
> > /* PCI domain-device relationship */
> > struct device_domain_info {
> > struct list_head link; /* link to domain siblings */
> > struct list_head global; /* link to global list */
> > struct list_head table; /* link to pasid table */
> > - struct list_head auxiliary_domains; /* auxiliary domains
> > - * attached to this device
> > - */
> > + struct list_head subdevices; /* subdevices sibling */
> > u32 segment; /* PCI segment number */
> > u8 bus; /* PCI bus number */
> > u8 devfn; /* PCI devfn number */
> >
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v3 3/3] iommu/vt-d: Fix ineffective devTLB invalidation for subdevices
2020-12-29 3:25 [PATCH v3 0/3] iommu/vt-d: Misc fixes on scalable mode Liu Yi L
2020-12-29 3:25 ` [PATCH v3 1/3] iommu/vt-d: Move intel_iommu info from struct intel_svm to struct intel_svm_dev Liu Yi L
2020-12-29 3:25 ` [PATCH v3 2/3] iommu/vt-d: Track device aux-attach with subdevice_domain_info Liu Yi L
@ 2020-12-29 3:25 ` Liu Yi L
2020-12-29 8:41 ` Lu Baolu
2 siblings, 1 reply; 11+ messages in thread
From: Liu Yi L @ 2020-12-29 3:25 UTC (permalink / raw)
To: baolu.lu, joro, will
Cc: kevin.tian, jacob.jun.pan, ashok.raj, yi.l.liu, jun.j.tian,
yi.y.sun, iommu, linux-kernel
iommu_flush_dev_iotlb() is called to invalidate caches on device. It only
loops the devices which are full-attached to the domain. For sub-devices,
this is ineffective. This results in invalid caching entries left on the
device. Fix it by adding loop for subdevices as well. Also, the domain->
has_iotlb_device needs to be updated when attaching to subdevices.
Fixes: 67b8e02b5e761 ("iommu/vt-d: Aux-domain specific domain attach/detach")
Signed-off-by: Liu Yi L <yi.l.liu@intel.com>
---
drivers/iommu/intel/iommu.c | 53 ++++++++++++++++++++++++++-----------
1 file changed, 37 insertions(+), 16 deletions(-)
diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
index d7720a836268..d48a60b61ba6 100644
--- a/drivers/iommu/intel/iommu.c
+++ b/drivers/iommu/intel/iommu.c
@@ -719,6 +719,8 @@ static int domain_update_device_node(struct dmar_domain *domain)
return nid;
}
+static void domain_update_iotlb(struct dmar_domain *domain);
+
/* Some capabilities may be different across iommus */
static void domain_update_iommu_cap(struct dmar_domain *domain)
{
@@ -744,6 +746,8 @@ static void domain_update_iommu_cap(struct dmar_domain *domain)
domain->domain.geometry.aperture_end = __DOMAIN_MAX_ADDR(domain->gaw - 1);
else
domain->domain.geometry.aperture_end = __DOMAIN_MAX_ADDR(domain->gaw);
+
+ domain_update_iotlb(domain);
}
struct context_entry *iommu_context_addr(struct intel_iommu *iommu, u8 bus,
@@ -1464,17 +1468,22 @@ static void domain_update_iotlb(struct dmar_domain *domain)
assert_spin_locked(&device_domain_lock);
- list_for_each_entry(info, &domain->devices, link) {
- struct pci_dev *pdev;
-
- if (!info->dev || !dev_is_pci(info->dev))
- continue;
-
- pdev = to_pci_dev(info->dev);
- if (pdev->ats_enabled) {
+ list_for_each_entry(info, &domain->devices, link)
+ if (info && info->ats_enabled) {
has_iotlb_device = true;
break;
}
+
+ if (!has_iotlb_device) {
+ struct subdev_domain_info *sinfo;
+
+ list_for_each_entry(sinfo, &domain->subdevices, link_domain) {
+ info = get_domain_info(sinfo->pdev);
+ if (info && info->ats_enabled) {
+ has_iotlb_device = true;
+ break;
+ }
+ }
}
domain->has_iotlb_device = has_iotlb_device;
@@ -1555,25 +1564,37 @@ static void iommu_disable_dev_iotlb(struct device_domain_info *info)
#endif
}
+static void __iommu_flush_dev_iotlb(struct device_domain_info *info,
+ u64 addr, unsigned int mask)
+{
+ u16 sid, qdep;
+
+ if (!info || !info->ats_enabled)
+ return;
+
+ sid = info->bus << 8 | info->devfn;
+ qdep = info->ats_qdep;
+ qi_flush_dev_iotlb(info->iommu, sid, info->pfsid,
+ qdep, addr, mask);
+}
+
static void iommu_flush_dev_iotlb(struct dmar_domain *domain,
u64 addr, unsigned mask)
{
- u16 sid, qdep;
unsigned long flags;
struct device_domain_info *info;
+ struct subdev_domain_info *sinfo;
if (!domain->has_iotlb_device)
return;
spin_lock_irqsave(&device_domain_lock, flags);
- list_for_each_entry(info, &domain->devices, link) {
- if (!info->ats_enabled)
- continue;
+ list_for_each_entry(info, &domain->devices, link)
+ __iommu_flush_dev_iotlb(info, addr, mask);
- sid = info->bus << 8 | info->devfn;
- qdep = info->ats_qdep;
- qi_flush_dev_iotlb(info->iommu, sid, info->pfsid,
- qdep, addr, mask);
+ list_for_each_entry(sinfo, &domain->subdevices, link_domain) {
+ __iommu_flush_dev_iotlb(get_domain_info(sinfo->pdev),
+ addr, mask);
}
spin_unlock_irqrestore(&device_domain_lock, flags);
}
--
2.25.1
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH v3 3/3] iommu/vt-d: Fix ineffective devTLB invalidation for subdevices
2020-12-29 3:25 ` [PATCH v3 3/3] iommu/vt-d: Fix ineffective devTLB invalidation for subdevices Liu Yi L
@ 2020-12-29 8:41 ` Lu Baolu
2021-01-05 5:50 ` Liu, Yi L
0 siblings, 1 reply; 11+ messages in thread
From: Lu Baolu @ 2020-12-29 8:41 UTC (permalink / raw)
To: Liu Yi L, joro, will
Cc: baolu.lu, kevin.tian, jacob.jun.pan, ashok.raj, jun.j.tian,
yi.y.sun, iommu, linux-kernel
Hi Yi,
On 2020/12/29 11:25, Liu Yi L wrote:
> iommu_flush_dev_iotlb() is called to invalidate caches on device. It only
> loops the devices which are full-attached to the domain. For sub-devices,
> this is ineffective. This results in invalid caching entries left on the
> device. Fix it by adding loop for subdevices as well. Also, the domain->
> has_iotlb_device needs to be updated when attaching to subdevices.
>
> Fixes: 67b8e02b5e761 ("iommu/vt-d: Aux-domain specific domain attach/detach")
> Signed-off-by: Liu Yi L <yi.l.liu@intel.com>
> ---
> drivers/iommu/intel/iommu.c | 53 ++++++++++++++++++++++++++-----------
> 1 file changed, 37 insertions(+), 16 deletions(-)
>
> diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
> index d7720a836268..d48a60b61ba6 100644
> --- a/drivers/iommu/intel/iommu.c
> +++ b/drivers/iommu/intel/iommu.c
> @@ -719,6 +719,8 @@ static int domain_update_device_node(struct dmar_domain *domain)
> return nid;
> }
>
> +static void domain_update_iotlb(struct dmar_domain *domain);
> +
> /* Some capabilities may be different across iommus */
> static void domain_update_iommu_cap(struct dmar_domain *domain)
> {
> @@ -744,6 +746,8 @@ static void domain_update_iommu_cap(struct dmar_domain *domain)
> domain->domain.geometry.aperture_end = __DOMAIN_MAX_ADDR(domain->gaw - 1);
> else
> domain->domain.geometry.aperture_end = __DOMAIN_MAX_ADDR(domain->gaw);
> +
> + domain_update_iotlb(domain);
> }
>
> struct context_entry *iommu_context_addr(struct intel_iommu *iommu, u8 bus,
> @@ -1464,17 +1468,22 @@ static void domain_update_iotlb(struct dmar_domain *domain)
>
> assert_spin_locked(&device_domain_lock);
>
> - list_for_each_entry(info, &domain->devices, link) {
> - struct pci_dev *pdev;
> -
> - if (!info->dev || !dev_is_pci(info->dev))
> - continue;
> -
> - pdev = to_pci_dev(info->dev);
> - if (pdev->ats_enabled) {
> + list_for_each_entry(info, &domain->devices, link)
> + if (info && info->ats_enabled) {
> has_iotlb_device = true;
> break;
> }
> +
> + if (!has_iotlb_device) {
> + struct subdev_domain_info *sinfo;
> +
> + list_for_each_entry(sinfo, &domain->subdevices, link_domain) {
> + info = get_domain_info(sinfo->pdev);
> + if (info && info->ats_enabled) {
> + has_iotlb_device = true;
> + break;
> + }
> + }
> }
>
> domain->has_iotlb_device = has_iotlb_device;
> @@ -1555,25 +1564,37 @@ static void iommu_disable_dev_iotlb(struct device_domain_info *info)
> #endif
> }
>
> +static void __iommu_flush_dev_iotlb(struct device_domain_info *info,
> + u64 addr, unsigned int mask)
> +{
> + u16 sid, qdep;
> +
> + if (!info || !info->ats_enabled)
> + return;
> +
> + sid = info->bus << 8 | info->devfn;
> + qdep = info->ats_qdep;
> + qi_flush_dev_iotlb(info->iommu, sid, info->pfsid,
> + qdep, addr, mask);
> +}
> +
> static void iommu_flush_dev_iotlb(struct dmar_domain *domain,
> u64 addr, unsigned mask)
> {
> - u16 sid, qdep;
> unsigned long flags;
> struct device_domain_info *info;
> + struct subdev_domain_info *sinfo;
>
> if (!domain->has_iotlb_device)
> return;
>
> spin_lock_irqsave(&device_domain_lock, flags);
> - list_for_each_entry(info, &domain->devices, link) {
> - if (!info->ats_enabled)
> - continue;
> + list_for_each_entry(info, &domain->devices, link)
> + __iommu_flush_dev_iotlb(info, addr, mask);
>
> - sid = info->bus << 8 | info->devfn;
> - qdep = info->ats_qdep;
> - qi_flush_dev_iotlb(info->iommu, sid, info->pfsid,
> - qdep, addr, mask);
> + list_for_each_entry(sinfo, &domain->subdevices, link_domain) {
> + __iommu_flush_dev_iotlb(get_domain_info(sinfo->pdev),
> + addr, mask);
> }
Nit:
list_for_each_entry(sinfo, &domain->subdevices, link_domain) {
info = get_domain_info(sinfo->pdev);
__iommu_flush_dev_iotlb(info, addr, mask);
}
Others look good to me.
Acked-by: Lu Baolu <baolu.lu@linux.intel.com>
Best regards,
baolu
> spin_unlock_irqrestore(&device_domain_lock, flags);
> }
>
^ permalink raw reply [flat|nested] 11+ messages in thread
* RE: [PATCH v3 3/3] iommu/vt-d: Fix ineffective devTLB invalidation for subdevices
2020-12-29 8:41 ` Lu Baolu
@ 2021-01-05 5:50 ` Liu, Yi L
2021-01-05 17:23 ` Will Deacon
0 siblings, 1 reply; 11+ messages in thread
From: Liu, Yi L @ 2021-01-05 5:50 UTC (permalink / raw)
To: Lu Baolu, joro, will
Cc: Tian, Kevin, jacob.jun.pan, Raj, Ashok, Tian, Jun J, Sun, Yi Y,
iommu, linux-kernel
Hi Baolu,
> From: Lu Baolu <baolu.lu@linux.intel.com>
> Sent: Tuesday, December 29, 2020 4:42 PM
>
> Hi Yi,
>
> On 2020/12/29 11:25, Liu Yi L wrote:
> > iommu_flush_dev_iotlb() is called to invalidate caches on device. It only
> > loops the devices which are full-attached to the domain. For sub-devices,
> > this is ineffective. This results in invalid caching entries left on the
> > device. Fix it by adding loop for subdevices as well. Also, the domain->
> > has_iotlb_device needs to be updated when attaching to subdevices.
> >
> > Fixes: 67b8e02b5e761 ("iommu/vt-d: Aux-domain specific domain
> attach/detach")
> > Signed-off-by: Liu Yi L <yi.l.liu@intel.com>
> > ---
> > drivers/iommu/intel/iommu.c | 53 ++++++++++++++++++++++++++---------
> --
> > 1 file changed, 37 insertions(+), 16 deletions(-)
> >
> > diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
> > index d7720a836268..d48a60b61ba6 100644
> > --- a/drivers/iommu/intel/iommu.c
> > +++ b/drivers/iommu/intel/iommu.c
> > @@ -719,6 +719,8 @@ static int domain_update_device_node(struct
> dmar_domain *domain)
> > return nid;
> > }
> >
> > +static void domain_update_iotlb(struct dmar_domain *domain);
> > +
> > /* Some capabilities may be different across iommus */
> > static void domain_update_iommu_cap(struct dmar_domain *domain)
> > {
> > @@ -744,6 +746,8 @@ static void domain_update_iommu_cap(struct
> dmar_domain *domain)
> > domain->domain.geometry.aperture_end =
> __DOMAIN_MAX_ADDR(domain->gaw - 1);
> > else
> > domain->domain.geometry.aperture_end =
> __DOMAIN_MAX_ADDR(domain->gaw);
> > +
> > + domain_update_iotlb(domain);
> > }
> >
> > struct context_entry *iommu_context_addr(struct intel_iommu *iommu,
> u8 bus,
> > @@ -1464,17 +1468,22 @@ static void domain_update_iotlb(struct
> dmar_domain *domain)
> >
> > assert_spin_locked(&device_domain_lock);
> >
> > - list_for_each_entry(info, &domain->devices, link) {
> > - struct pci_dev *pdev;
> > -
> > - if (!info->dev || !dev_is_pci(info->dev))
> > - continue;
> > -
> > - pdev = to_pci_dev(info->dev);
> > - if (pdev->ats_enabled) {
> > + list_for_each_entry(info, &domain->devices, link)
> > + if (info && info->ats_enabled) {
> > has_iotlb_device = true;
> > break;
> > }
> > +
> > + if (!has_iotlb_device) {
> > + struct subdev_domain_info *sinfo;
> > +
> > + list_for_each_entry(sinfo, &domain->subdevices,
> link_domain) {
> > + info = get_domain_info(sinfo->pdev);
> > + if (info && info->ats_enabled) {
> > + has_iotlb_device = true;
> > + break;
> > + }
> > + }
> > }
> >
> > domain->has_iotlb_device = has_iotlb_device;
> > @@ -1555,25 +1564,37 @@ static void iommu_disable_dev_iotlb(struct
> device_domain_info *info)
> > #endif
> > }
> >
> > +static void __iommu_flush_dev_iotlb(struct device_domain_info *info,
> > + u64 addr, unsigned int mask)
> > +{
> > + u16 sid, qdep;
> > +
> > + if (!info || !info->ats_enabled)
> > + return;
> > +
> > + sid = info->bus << 8 | info->devfn;
> > + qdep = info->ats_qdep;
> > + qi_flush_dev_iotlb(info->iommu, sid, info->pfsid,
> > + qdep, addr, mask);
> > +}
> > +
> > static void iommu_flush_dev_iotlb(struct dmar_domain *domain,
> > u64 addr, unsigned mask)
> > {
> > - u16 sid, qdep;
> > unsigned long flags;
> > struct device_domain_info *info;
> > + struct subdev_domain_info *sinfo;
> >
> > if (!domain->has_iotlb_device)
> > return;
> >
> > spin_lock_irqsave(&device_domain_lock, flags);
> > - list_for_each_entry(info, &domain->devices, link) {
> > - if (!info->ats_enabled)
> > - continue;
> > + list_for_each_entry(info, &domain->devices, link)
> > + __iommu_flush_dev_iotlb(info, addr, mask);
> >
> > - sid = info->bus << 8 | info->devfn;
> > - qdep = info->ats_qdep;
> > - qi_flush_dev_iotlb(info->iommu, sid, info->pfsid,
> > - qdep, addr, mask);
> > + list_for_each_entry(sinfo, &domain->subdevices, link_domain) {
> > + __iommu_flush_dev_iotlb(get_domain_info(sinfo->pdev),
> > + addr, mask);
> > }
>
> Nit:
> list_for_each_entry(sinfo, &domain->subdevices, link_domain) {
> info = get_domain_info(sinfo->pdev);
> __iommu_flush_dev_iotlb(info, addr, mask);
> }
you are right. this should be better.
> Others look good to me.
>
> Acked-by: Lu Baolu <baolu.lu@linux.intel.com>
>
> Best regards,
> baolu
Regards,
Yi Liu
> > spin_unlock_irqrestore(&device_domain_lock, flags);
> > }
> >
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v3 3/3] iommu/vt-d: Fix ineffective devTLB invalidation for subdevices
2021-01-05 5:50 ` Liu, Yi L
@ 2021-01-05 17:23 ` Will Deacon
2021-01-07 5:22 ` Liu, Yi L
0 siblings, 1 reply; 11+ messages in thread
From: Will Deacon @ 2021-01-05 17:23 UTC (permalink / raw)
To: Liu, Yi L
Cc: Lu Baolu, joro, Tian, Kevin, jacob.jun.pan, Raj, Ashok, Tian,
Jun J, Sun, Yi Y, iommu, linux-kernel
On Tue, Jan 05, 2021 at 05:50:22AM +0000, Liu, Yi L wrote:
> > > +static void __iommu_flush_dev_iotlb(struct device_domain_info *info,
> > > + u64 addr, unsigned int mask)
> > > +{
> > > + u16 sid, qdep;
> > > +
> > > + if (!info || !info->ats_enabled)
> > > + return;
> > > +
> > > + sid = info->bus << 8 | info->devfn;
> > > + qdep = info->ats_qdep;
> > > + qi_flush_dev_iotlb(info->iommu, sid, info->pfsid,
> > > + qdep, addr, mask);
> > > +}
> > > +
> > > static void iommu_flush_dev_iotlb(struct dmar_domain *domain,
> > > u64 addr, unsigned mask)
> > > {
> > > - u16 sid, qdep;
> > > unsigned long flags;
> > > struct device_domain_info *info;
> > > + struct subdev_domain_info *sinfo;
> > >
> > > if (!domain->has_iotlb_device)
> > > return;
> > >
> > > spin_lock_irqsave(&device_domain_lock, flags);
> > > - list_for_each_entry(info, &domain->devices, link) {
> > > - if (!info->ats_enabled)
> > > - continue;
> > > + list_for_each_entry(info, &domain->devices, link)
> > > + __iommu_flush_dev_iotlb(info, addr, mask);
> > >
> > > - sid = info->bus << 8 | info->devfn;
> > > - qdep = info->ats_qdep;
> > > - qi_flush_dev_iotlb(info->iommu, sid, info->pfsid,
> > > - qdep, addr, mask);
> > > + list_for_each_entry(sinfo, &domain->subdevices, link_domain) {
> > > + __iommu_flush_dev_iotlb(get_domain_info(sinfo->pdev),
> > > + addr, mask);
> > > }
> >
> > Nit:
> > list_for_each_entry(sinfo, &domain->subdevices, link_domain) {
> > info = get_domain_info(sinfo->pdev);
> > __iommu_flush_dev_iotlb(info, addr, mask);
> > }
>
> you are right. this should be better.
Please can you post a v4, with Lu's acks and the issue reported by Dan fixed
too?
Thanks,
Will
^ permalink raw reply [flat|nested] 11+ messages in thread
* RE: [PATCH v3 3/3] iommu/vt-d: Fix ineffective devTLB invalidation for subdevices
2021-01-05 17:23 ` Will Deacon
@ 2021-01-07 5:22 ` Liu, Yi L
0 siblings, 0 replies; 11+ messages in thread
From: Liu, Yi L @ 2021-01-07 5:22 UTC (permalink / raw)
To: Will Deacon
Cc: Lu Baolu, joro, Tian, Kevin, jacob.jun.pan, Raj, Ashok, Tian,
Jun J, Sun, Yi Y, iommu, linux-kernel
Hi Will,
> From: Will Deacon <will@kernel.org>
> Sent: Wednesday, January 6, 2021 1:24 AM
>
> On Tue, Jan 05, 2021 at 05:50:22AM +0000, Liu, Yi L wrote:
> > > > +static void __iommu_flush_dev_iotlb(struct device_domain_info
> *info,
> > > > + u64 addr, unsigned int mask)
> > > > +{
> > > > + u16 sid, qdep;
> > > > +
> > > > + if (!info || !info->ats_enabled)
> > > > + return;
> > > > +
> > > > + sid = info->bus << 8 | info->devfn;
> > > > + qdep = info->ats_qdep;
> > > > + qi_flush_dev_iotlb(info->iommu, sid, info->pfsid,
> > > > + qdep, addr, mask);
> > > > +}
> > > > +
> > > > static void iommu_flush_dev_iotlb(struct dmar_domain *domain,
> > > > u64 addr, unsigned mask)
> > > > {
> > > > - u16 sid, qdep;
> > > > unsigned long flags;
> > > > struct device_domain_info *info;
> > > > + struct subdev_domain_info *sinfo;
> > > >
> > > > if (!domain->has_iotlb_device)
> > > > return;
> > > >
> > > > spin_lock_irqsave(&device_domain_lock, flags);
> > > > - list_for_each_entry(info, &domain->devices, link) {
> > > > - if (!info->ats_enabled)
> > > > - continue;
> > > > + list_for_each_entry(info, &domain->devices, link)
> > > > + __iommu_flush_dev_iotlb(info, addr, mask);
> > > >
> > > > - sid = info->bus << 8 | info->devfn;
> > > > - qdep = info->ats_qdep;
> > > > - qi_flush_dev_iotlb(info->iommu, sid, info->pfsid,
> > > > - qdep, addr, mask);
> > > > + list_for_each_entry(sinfo, &domain->subdevices, link_domain) {
> > > > + __iommu_flush_dev_iotlb(get_domain_info(sinfo->pdev),
> > > > + addr, mask);
> > > > }
> > >
> > > Nit:
> > > list_for_each_entry(sinfo, &domain->subdevices, link_domain) {
> > > info = get_domain_info(sinfo->pdev);
> > > __iommu_flush_dev_iotlb(info, addr, mask);
> > > }
> >
> > you are right. this should be better.
>
> Please can you post a v4, with Lu's acks and the issue reported by Dan fixed
> too?
sure, will send out later.
Regards,
Yi Liu
> Thanks,
>
> Will
^ permalink raw reply [flat|nested] 11+ messages in thread