2019-06-09 13:43:25

by Jacob Pan

[permalink] [raw]
Subject: [PATCH v4 14/22] iommu/vt-d: Add custom allocator for IOASID

When VT-d driver runs in the guest, PASID allocation must be
performed via virtual command interface. This patch registers a
custom IOASID allocator which takes precedence over the default
XArray based allocator. The resulting IOASID allocation will always
come from the host. This ensures that PASID namespace is system-
wide.

Signed-off-by: Lu Baolu <[email protected]>
Signed-off-by: Liu, Yi L <[email protected]>
Signed-off-by: Jacob Pan <[email protected]>
---
drivers/iommu/Kconfig | 1 +
drivers/iommu/intel-iommu.c | 60 +++++++++++++++++++++++++++++++++++++++++++++
include/linux/intel-iommu.h | 2 ++
3 files changed, 63 insertions(+)

diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index c40c4b5..5d1bc2a 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -213,6 +213,7 @@ config INTEL_IOMMU_SVM
bool "Support for Shared Virtual Memory with Intel IOMMU"
depends on INTEL_IOMMU && X86
select PCI_PASID
+ select IOASID
select MMU_NOTIFIER
select IOASID
help
diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index 09b8ff0..5b84994 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -1711,6 +1711,8 @@ static void free_dmar_iommu(struct intel_iommu *iommu)
if (ecap_prs(iommu->ecap))
intel_svm_finish_prq(iommu);
}
+ ioasid_unregister_allocator(&iommu->pasid_allocator);
+
#endif
}

@@ -4820,6 +4822,46 @@ static int __init platform_optin_force_iommu(void)
return 1;
}

+#ifdef CONFIG_INTEL_IOMMU_SVM
+static ioasid_t intel_ioasid_alloc(ioasid_t min, ioasid_t max, void *data)
+{
+ struct intel_iommu *iommu = data;
+ ioasid_t ioasid;
+
+ /*
+ * VT-d virtual command interface always uses the full 20 bit
+ * PASID range. Host can partition guest PASID range based on
+ * policies but it is out of guest's control.
+ */
+ if (min < PASID_MIN || max > PASID_MAX)
+ return -EINVAL;
+
+ if (vcmd_alloc_pasid(iommu, &ioasid))
+ return INVALID_IOASID;
+
+ return ioasid;
+}
+
+static void intel_ioasid_free(ioasid_t ioasid, void *data)
+{
+ struct iommu_pasid_alloc_info *svm;
+ struct intel_iommu *iommu = data;
+
+ if (!iommu)
+ return;
+ /*
+ * Sanity check the ioasid owner is done at upper layer, e.g. VFIO
+ * We can only free the PASID when all the devices are unbond.
+ */
+ svm = ioasid_find(NULL, ioasid, NULL);
+ if (!svm) {
+ pr_warn("Freeing unbond IOASID %d\n", ioasid);
+ return;
+ }
+ vcmd_free_pasid(iommu, ioasid);
+}
+#endif
+
int __init intel_iommu_init(void)
{
int ret = -ENODEV;
@@ -4924,6 +4966,24 @@ int __init intel_iommu_init(void)
"%s", iommu->name);
iommu_device_set_ops(&iommu->iommu, &intel_iommu_ops);
iommu_device_register(&iommu->iommu);
+#ifdef CONFIG_INTEL_IOMMU_SVM
+ if (cap_caching_mode(iommu->cap) && sm_supported(iommu)) {
+ /*
+ * Register a custom ASID allocator if we are running
+ * in a guest, the purpose is to have a system wide PASID
+ * namespace among all PASID users.
+ * There can be multiple vIOMMUs in each guest but only
+ * one allocator is active. All vIOMMU allocators will
+ * eventually be calling the same host allocator.
+ */
+ iommu->pasid_allocator.alloc = intel_ioasid_alloc;
+ iommu->pasid_allocator.free = intel_ioasid_free;
+ iommu->pasid_allocator.pdata = (void *)iommu;
+ ret = ioasid_register_allocator(&iommu->pasid_allocator);
+ if (ret)
+ pr_warn("Custom PASID allocator registeration failed\n");
+ }
+#endif
}

bus_set_iommu(&pci_bus_type, &intel_iommu_ops);
diff --git a/include/linux/intel-iommu.h b/include/linux/intel-iommu.h
index bff907b..8605c74 100644
--- a/include/linux/intel-iommu.h
+++ b/include/linux/intel-iommu.h
@@ -31,6 +31,7 @@
#include <linux/iommu.h>
#include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/dmar.h>
+#include <linux/ioasid.h>

#include <asm/cacheflush.h>
#include <asm/iommu.h>
@@ -549,6 +550,7 @@ struct intel_iommu {
#ifdef CONFIG_INTEL_IOMMU_SVM
struct page_req_dsc *prq;
unsigned char prq_name[16]; /* Name for PRQ interrupt */
+ struct ioasid_allocator_ops pasid_allocator; /* Custom allocator for PASIDs */
#endif
struct q_inval *qi; /* Queued invalidation info */
u32 *iommu_state; /* Store iommu states between suspend and resume.*/
--
2.7.4


2019-07-16 09:31:28

by Eric Auger

[permalink] [raw]
Subject: Re: [PATCH v4 14/22] iommu/vt-d: Add custom allocator for IOASID

Hi Jacob,

On 6/9/19 3:44 PM, Jacob Pan wrote:
> When VT-d driver runs in the guest, PASID allocation must be
> performed via virtual command interface. This patch registers a
> custom IOASID allocator which takes precedence over the default
> XArray based allocator. The resulting IOASID allocation will always
> come from the host. This ensures that PASID namespace is system-
> wide.
>
> Signed-off-by: Lu Baolu <[email protected]>
> Signed-off-by: Liu, Yi L <[email protected]>
> Signed-off-by: Jacob Pan <[email protected]>
> ---
> drivers/iommu/Kconfig | 1 +
> drivers/iommu/intel-iommu.c | 60 +++++++++++++++++++++++++++++++++++++++++++++
> include/linux/intel-iommu.h | 2 ++
> 3 files changed, 63 insertions(+)
>
> diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
> index c40c4b5..5d1bc2a 100644
> --- a/drivers/iommu/Kconfig
> +++ b/drivers/iommu/Kconfig
> @@ -213,6 +213,7 @@ config INTEL_IOMMU_SVM
> bool "Support for Shared Virtual Memory with Intel IOMMU"
> depends on INTEL_IOMMU && X86
> select PCI_PASID
> + select IOASID
> select MMU_NOTIFIER
> select IOASID
> help
> diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
> index 09b8ff0..5b84994 100644
> --- a/drivers/iommu/intel-iommu.c
> +++ b/drivers/iommu/intel-iommu.c
> @@ -1711,6 +1711,8 @@ static void free_dmar_iommu(struct intel_iommu *iommu)
> if (ecap_prs(iommu->ecap))
> intel_svm_finish_prq(iommu);
> }
> + ioasid_unregister_allocator(&iommu->pasid_allocator);
> +
> #endif
> }
>
> @@ -4820,6 +4822,46 @@ static int __init platform_optin_force_iommu(void)
> return 1;
> }
>
> +#ifdef CONFIG_INTEL_IOMMU_SVM
> +static ioasid_t intel_ioasid_alloc(ioasid_t min, ioasid_t max, void *data)
> +{
> + struct intel_iommu *iommu = data;
> + ioasid_t ioasid;
> +
> + /*
> + * VT-d virtual command interface always uses the full 20 bit
> + * PASID range. Host can partition guest PASID range based on
> + * policies but it is out of guest's control.
> + */
> + if (min < PASID_MIN || max > PASID_MAX)
> + return -EINVAL;
ioasid_alloc() does not handle that error value, use INVALID_IOASID?
> +
> + if (vcmd_alloc_pasid(iommu, &ioasid))
> + return INVALID_IOASID;
> +
> + return ioasid;
> +}
> +
> +static void intel_ioasid_free(ioasid_t ioasid, void *data)
> +{
> + struct iommu_pasid_alloc_info *svm;
> + struct intel_iommu *iommu = data;
> +
> + if (!iommu)
> + return;
> + /*
> + * Sanity check the ioasid owner is done at upper layer, e.g. VFIO
> + * We can only free the PASID when all the devices are unbond.
> + */
> + svm = ioasid_find(NULL, ioasid, NULL);
> + if (!svm) {
> + pr_warn("Freeing unbond IOASID %d\n", ioasid);
> + return;
> + }
> + vcmd_free_pasid(iommu, ioasid);
> +}
> +#endif
> +
> int __init intel_iommu_init(void)
> {
> int ret = -ENODEV;
> @@ -4924,6 +4966,24 @@ int __init intel_iommu_init(void)
> "%s", iommu->name);
> iommu_device_set_ops(&iommu->iommu, &intel_iommu_ops);
> iommu_device_register(&iommu->iommu);
> +#ifdef CONFIG_INTEL_IOMMU_SVM
> + if (cap_caching_mode(iommu->cap) && sm_supported(iommu)) {
> + /*
> + * Register a custom ASID allocator if we are running
> + * in a guest, the purpose is to have a system wide PASID
> + * namespace among all PASID users.
> + * There can be multiple vIOMMUs in each guest but only
> + * one allocator is active. All vIOMMU allocators will
> + * eventually be calling the same host allocator.
> + */
> + iommu->pasid_allocator.alloc = intel_ioasid_alloc;
> + iommu->pasid_allocator.free = intel_ioasid_free;
> + iommu->pasid_allocator.pdata = (void *)iommu;
> + ret = ioasid_register_allocator(&iommu->pasid_allocator);
> + if (ret)
> + pr_warn("Custom PASID allocator registeration failed\n");
what if it fails, don't you want a tear down path?

Thanks

Eric
> + }
> +#endif
> }
>
> bus_set_iommu(&pci_bus_type, &intel_iommu_ops);
> diff --git a/include/linux/intel-iommu.h b/include/linux/intel-iommu.h
> index bff907b..8605c74 100644
> --- a/include/linux/intel-iommu.h
> +++ b/include/linux/intel-iommu.h
> @@ -31,6 +31,7 @@
> #include <linux/iommu.h>
> #include <linux/io-64-nonatomic-lo-hi.h>
> #include <linux/dmar.h>
> +#include <linux/ioasid.h>
>
> #include <asm/cacheflush.h>
> #include <asm/iommu.h>
> @@ -549,6 +550,7 @@ struct intel_iommu {
> #ifdef CONFIG_INTEL_IOMMU_SVM
> struct page_req_dsc *prq;
> unsigned char prq_name[16]; /* Name for PRQ interrupt */
> + struct ioasid_allocator_ops pasid_allocator; /* Custom allocator for PASIDs */
> #endif
> struct q_inval *qi; /* Queued invalidation info */
> u32 *iommu_state; /* Store iommu states between suspend and resume.*/
>

2019-08-05 19:59:45

by Jacob Pan

[permalink] [raw]
Subject: Re: [PATCH v4 14/22] iommu/vt-d: Add custom allocator for IOASID

On Tue, 16 Jul 2019 11:30:14 +0200
Auger Eric <[email protected]> wrote:

> Hi Jacob,
>
> On 6/9/19 3:44 PM, Jacob Pan wrote:
> > When VT-d driver runs in the guest, PASID allocation must be
> > performed via virtual command interface. This patch registers a
> > custom IOASID allocator which takes precedence over the default
> > XArray based allocator. The resulting IOASID allocation will always
> > come from the host. This ensures that PASID namespace is system-
> > wide.
> >
> > Signed-off-by: Lu Baolu <[email protected]>
> > Signed-off-by: Liu, Yi L <[email protected]>
> > Signed-off-by: Jacob Pan <[email protected]>
> > ---
> > drivers/iommu/Kconfig | 1 +
> > drivers/iommu/intel-iommu.c | 60
> > +++++++++++++++++++++++++++++++++++++++++++++
> > include/linux/intel-iommu.h | 2 ++ 3 files changed, 63
> > insertions(+)
> >
> > diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
> > index c40c4b5..5d1bc2a 100644
> > --- a/drivers/iommu/Kconfig
> > +++ b/drivers/iommu/Kconfig
> > @@ -213,6 +213,7 @@ config INTEL_IOMMU_SVM
> > bool "Support for Shared Virtual Memory with Intel IOMMU"
> > depends on INTEL_IOMMU && X86
> > select PCI_PASID
> > + select IOASID
> > select MMU_NOTIFIER
> > select IOASID
> > help
> > diff --git a/drivers/iommu/intel-iommu.c
> > b/drivers/iommu/intel-iommu.c index 09b8ff0..5b84994 100644
> > --- a/drivers/iommu/intel-iommu.c
> > +++ b/drivers/iommu/intel-iommu.c
> > @@ -1711,6 +1711,8 @@ static void free_dmar_iommu(struct
> > intel_iommu *iommu) if (ecap_prs(iommu->ecap))
> > intel_svm_finish_prq(iommu);
> > }
> > + ioasid_unregister_allocator(&iommu->pasid_allocator);
> > +
> > #endif
> > }
> >
> > @@ -4820,6 +4822,46 @@ static int __init
> > platform_optin_force_iommu(void) return 1;
> > }
> >
> > +#ifdef CONFIG_INTEL_IOMMU_SVM
> > +static ioasid_t intel_ioasid_alloc(ioasid_t min, ioasid_t max,
> > void *data) +{
> > + struct intel_iommu *iommu = data;
> > + ioasid_t ioasid;
> > +
> > + /*
> > + * VT-d virtual command interface always uses the full 20
> > bit
> > + * PASID range. Host can partition guest PASID range based
> > on
> > + * policies but it is out of guest's control.
> > + */
> > + if (min < PASID_MIN || max > PASID_MAX)
> > + return -EINVAL;
> ioasid_alloc() does not handle that error value, use INVALID_IOASID?
> > +
> > + if (vcmd_alloc_pasid(iommu, &ioasid))
> > + return INVALID_IOASID;
> > +
> > + return ioasid;
> > +}
> > +
> > +static void intel_ioasid_free(ioasid_t ioasid, void *data)
> > +{
> > + struct iommu_pasid_alloc_info *svm;
> > + struct intel_iommu *iommu = data;
> > +
> > + if (!iommu)
> > + return;
> > + /*
> > + * Sanity check the ioasid owner is done at upper layer,
> > e.g. VFIO
> > + * We can only free the PASID when all the devices are
> > unbond.
> > + */
> > + svm = ioasid_find(NULL, ioasid, NULL);
> > + if (!svm) {
> > + pr_warn("Freeing unbond IOASID %d\n", ioasid);
> > + return;
> > + }
> > + vcmd_free_pasid(iommu, ioasid);
> > +}
> > +#endif
> > +
> > int __init intel_iommu_init(void)
> > {
> > int ret = -ENODEV;
> > @@ -4924,6 +4966,24 @@ int __init intel_iommu_init(void)
> > "%s", iommu->name);
> > iommu_device_set_ops(&iommu->iommu,
> > &intel_iommu_ops); iommu_device_register(&iommu->iommu);
> > +#ifdef CONFIG_INTEL_IOMMU_SVM
> > + if (cap_caching_mode(iommu->cap) &&
> > sm_supported(iommu)) {
> > + /*
> > + * Register a custom ASID allocator if we
> > are running
> > + * in a guest, the purpose is to have a
> > system wide PASID
> > + * namespace among all PASID users.
> > + * There can be multiple vIOMMUs in each
> > guest but only
> > + * one allocator is active. All vIOMMU
> > allocators will
> > + * eventually be calling the same host
> > allocator.
> > + */
> > + iommu->pasid_allocator.alloc =
> > intel_ioasid_alloc;
> > + iommu->pasid_allocator.free =
> > intel_ioasid_free;
> > + iommu->pasid_allocator.pdata = (void
> > *)iommu;
> > + ret =
> > ioasid_register_allocator(&iommu->pasid_allocator);
> > + if (ret)
> > + pr_warn("Custom PASID allocator
> > registeration failed\n");
> what if it fails, don't you want a tear down path?
>

Good point, we need to disable PASID usage, i.e. disable scalable mode
if there is no virtual command based PASID allocator in the guest.

Sorry for the late reply.

> Thanks
>
> Eric
> [...]

[Jacob Pan]