Re: [PATCH RFC v1 09/11] iommu/virtio: Implement sva bind/unbind calls

2021-09-21 Thread Jean-Philippe Brucker
On Fri, Apr 23, 2021 at 03:21:45PM +0530, Vivek Gautam wrote:
> SVA bind and unbind implementations will allow to prepare translation
> context with CPU page tables that can be programmed into host iommu
> hardware to realize shared address space utilization between the CPU
> and virtualized devices using virtio-iommu.
> 
> Signed-off-by: Vivek Gautam 
> ---
>  drivers/iommu/virtio-iommu.c  | 199 +-
>  include/uapi/linux/virtio_iommu.h |   2 +
>  2 files changed, 199 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/iommu/virtio-iommu.c b/drivers/iommu/virtio-iommu.c
> index 250c137a211b..08f1294baeab 100644
> --- a/drivers/iommu/virtio-iommu.c
> +++ b/drivers/iommu/virtio-iommu.c
> @@ -14,6 +14,9 @@
>  #include 
>  #include 
>  #include 
> +#include 
> +#include 
> +#include 
>  #include 
>  #include 
>  #include 
> @@ -28,6 +31,7 @@
>  #include 
>  #include "iommu-pasid-table.h"
>  #include "iommu-sva-lib.h"
> +#include "io-pgtable-arm.h"

Is this used here?

>  
>  #define MSI_IOVA_BASE0x800
>  #define MSI_IOVA_LENGTH  0x10
> @@ -41,6 +45,7 @@ DEFINE_XARRAY_ALLOC1(viommu_asid_xa);
>  
>  static DEFINE_MUTEX(sva_lock);
>  static DEFINE_MUTEX(iopf_lock);
> +static DEFINE_MUTEX(viommu_asid_lock);
>  
>  struct viommu_dev_pri_work {
>   struct work_struct  work;
> @@ -88,10 +93,22 @@ struct viommu_mapping {
>  struct viommu_mm {
>   int pasid;
>   u64 archid;
> + struct viommu_sva_bond  *bond;
>   struct io_pgtable_ops   *ops;
>   struct viommu_domain*domain;
>  };
>  
> +struct viommu_sva_bond {
> + struct iommu_svasva;
> + struct mm_struct*mm;
> + struct iommu_psdtable_mmu_notifier  *viommu_mn;
> + struct list_headlist;
> + refcount_t  refs;
> +};
> +
> +#define sva_to_viommu_bond(handle) \
> + container_of(handle, struct viommu_sva_bond, sva)
> +
>  struct viommu_domain {
>   struct iommu_domain domain;
>   struct viommu_dev   *viommu;
> @@ -136,6 +153,7 @@ struct viommu_endpoint {
>   boolpri_supported;
>   boolsva_enabled;
>   booliopf_enabled;
> + struct list_headbonds;
>  };
>  
>  struct viommu_ep_entry {
> @@ -1423,14 +1441,15 @@ static int viommu_attach_pasid_table(struct 
> viommu_endpoint *vdev,
>  
>   pst_cfg->iommu_dev = viommu->dev->parent;
>  
> + mutex_lock(_asid_lock);
>   /* Prepare PASID tables info to allocate a new table */
>   ret = viommu_prepare_pst(vdev, pst_cfg, fmt);
>   if (ret)
> - return ret;
> + goto err_out_unlock;
>  
>   ret = iommu_psdtable_alloc(tbl, pst_cfg);
>   if (ret)
> - return ret;
> + goto err_out_unlock;
>  
>   pst_cfg->iommu_dev = viommu->dev->parent;
>   pst_cfg->fmt = PASID_TABLE_ARM_SMMU_V3;
> @@ -1452,6 +1471,7 @@ static int viommu_attach_pasid_table(struct 
> viommu_endpoint *vdev,
>   if (ret)
>   goto err_free_ops;
>   }
> + mutex_unlock(_asid_lock);
>   } else {
>   /* TODO: otherwise, check for compatibility with vdev. */
>   return -ENOSYS;
> @@ -1467,6 +1487,8 @@ static int viommu_attach_pasid_table(struct 
> viommu_endpoint *vdev,
>  err_free_psdtable:
>   iommu_psdtable_free(tbl, >cfg);
>  
> +err_out_unlock:
> + mutex_unlock(_asid_lock);
>   return ret;
>  }
>  
> @@ -1706,6 +1728,7 @@ static struct iommu_device *viommu_probe_device(struct 
> device *dev)
>   vdev->dev = dev;
>   vdev->viommu = viommu;
>   INIT_LIST_HEAD(>resv_regions);
> + INIT_LIST_HEAD(>bonds);
>   dev_iommu_priv_set(dev, vdev);
>  
>   if (viommu->probe_size) {
> @@ -1755,6 +1778,175 @@ static int viommu_of_xlate(struct device *dev, struct 
> of_phandle_args *args)
>   return iommu_fwspec_add_ids(dev, args->args, 1);
>  }
>  
> +static u32 viommu_sva_get_pasid(struct iommu_sva *handle)
> +{
> + struct viommu_sva_bond *bond = sva_to_viommu_bond(handle);
> +
> + return bond->mm->pasid;
> +}
> +
> +static void viommu_mmu_notifier_free(struct mmu_notifier *mn)
> +{
> + kfree(mn_to_pstiommu(mn));
> +}
> +
> +static struct mmu_notifier_ops viommu_mmu_notifier_ops = {
> + .free_notifier  = viommu_mmu_notifier_free,

.invalidate_range and .release will be needed as well, to keep up to date
with changes to the address space

> +};
> +
> +/* Allocate or get existing MMU notifier for this {domain, mm} pair */
> +static struct iommu_psdtable_mmu_notifier *
> +viommu_mmu_notifier_get(struct 

[PATCH RFC v1 09/11] iommu/virtio: Implement sva bind/unbind calls

2021-04-23 Thread Vivek Gautam
SVA bind and unbind implementations will allow to prepare translation
context with CPU page tables that can be programmed into host iommu
hardware to realize shared address space utilization between the CPU
and virtualized devices using virtio-iommu.

Signed-off-by: Vivek Gautam 
---
 drivers/iommu/virtio-iommu.c  | 199 +-
 include/uapi/linux/virtio_iommu.h |   2 +
 2 files changed, 199 insertions(+), 2 deletions(-)

diff --git a/drivers/iommu/virtio-iommu.c b/drivers/iommu/virtio-iommu.c
index 250c137a211b..08f1294baeab 100644
--- a/drivers/iommu/virtio-iommu.c
+++ b/drivers/iommu/virtio-iommu.c
@@ -14,6 +14,9 @@
 #include 
 #include 
 #include 
+#include 
+#include 
+#include 
 #include 
 #include 
 #include 
@@ -28,6 +31,7 @@
 #include 
 #include "iommu-pasid-table.h"
 #include "iommu-sva-lib.h"
+#include "io-pgtable-arm.h"
 
 #define MSI_IOVA_BASE  0x800
 #define MSI_IOVA_LENGTH0x10
@@ -41,6 +45,7 @@ DEFINE_XARRAY_ALLOC1(viommu_asid_xa);
 
 static DEFINE_MUTEX(sva_lock);
 static DEFINE_MUTEX(iopf_lock);
+static DEFINE_MUTEX(viommu_asid_lock);
 
 struct viommu_dev_pri_work {
struct work_struct  work;
@@ -88,10 +93,22 @@ struct viommu_mapping {
 struct viommu_mm {
int pasid;
u64 archid;
+   struct viommu_sva_bond  *bond;
struct io_pgtable_ops   *ops;
struct viommu_domain*domain;
 };
 
+struct viommu_sva_bond {
+   struct iommu_svasva;
+   struct mm_struct*mm;
+   struct iommu_psdtable_mmu_notifier  *viommu_mn;
+   struct list_headlist;
+   refcount_t  refs;
+};
+
+#define sva_to_viommu_bond(handle) \
+   container_of(handle, struct viommu_sva_bond, sva)
+
 struct viommu_domain {
struct iommu_domain domain;
struct viommu_dev   *viommu;
@@ -136,6 +153,7 @@ struct viommu_endpoint {
boolpri_supported;
boolsva_enabled;
booliopf_enabled;
+   struct list_headbonds;
 };
 
 struct viommu_ep_entry {
@@ -1423,14 +1441,15 @@ static int viommu_attach_pasid_table(struct 
viommu_endpoint *vdev,
 
pst_cfg->iommu_dev = viommu->dev->parent;
 
+   mutex_lock(_asid_lock);
/* Prepare PASID tables info to allocate a new table */
ret = viommu_prepare_pst(vdev, pst_cfg, fmt);
if (ret)
-   return ret;
+   goto err_out_unlock;
 
ret = iommu_psdtable_alloc(tbl, pst_cfg);
if (ret)
-   return ret;
+   goto err_out_unlock;
 
pst_cfg->iommu_dev = viommu->dev->parent;
pst_cfg->fmt = PASID_TABLE_ARM_SMMU_V3;
@@ -1452,6 +1471,7 @@ static int viommu_attach_pasid_table(struct 
viommu_endpoint *vdev,
if (ret)
goto err_free_ops;
}
+   mutex_unlock(_asid_lock);
} else {
/* TODO: otherwise, check for compatibility with vdev. */
return -ENOSYS;
@@ -1467,6 +1487,8 @@ static int viommu_attach_pasid_table(struct 
viommu_endpoint *vdev,
 err_free_psdtable:
iommu_psdtable_free(tbl, >cfg);
 
+err_out_unlock:
+   mutex_unlock(_asid_lock);
return ret;
 }
 
@@ -1706,6 +1728,7 @@ static struct iommu_device *viommu_probe_device(struct 
device *dev)
vdev->dev = dev;
vdev->viommu = viommu;
INIT_LIST_HEAD(>resv_regions);
+   INIT_LIST_HEAD(>bonds);
dev_iommu_priv_set(dev, vdev);
 
if (viommu->probe_size) {
@@ -1755,6 +1778,175 @@ static int viommu_of_xlate(struct device *dev, struct 
of_phandle_args *args)
return iommu_fwspec_add_ids(dev, args->args, 1);
 }
 
+static u32 viommu_sva_get_pasid(struct iommu_sva *handle)
+{
+   struct viommu_sva_bond *bond = sva_to_viommu_bond(handle);
+
+   return bond->mm->pasid;
+}
+
+static void viommu_mmu_notifier_free(struct mmu_notifier *mn)
+{
+   kfree(mn_to_pstiommu(mn));
+}
+
+static struct mmu_notifier_ops viommu_mmu_notifier_ops = {
+   .free_notifier  = viommu_mmu_notifier_free,
+};
+
+/* Allocate or get existing MMU notifier for this {domain, mm} pair */
+static struct iommu_psdtable_mmu_notifier *
+viommu_mmu_notifier_get(struct viommu_domain *vdomain, struct mm_struct *mm,
+   u32 asid_bits)
+{
+   int ret;
+   struct iommu_psdtable_mmu_notifier *viommu_mn;
+   struct iommu_pasid_table *tbl = vdomain->pasid_tbl;
+
+   list_for_each_entry(viommu_mn, >mmu_notifiers, list) {
+   if (viommu_mn->mn.mm == mm) {
+   refcount_inc(_mn->refs);
+