On 25/07/18 07:19, Tian, Kevin wrote:
>> From: Tian, Kevin
>> Sent: Wednesday, July 25, 2018 10:16 AM
>>
> [...]
>>>
>>> There is another way: as we're planning to add a generic pasid_alloc()
>>> function to the IOMMU API, the mdev module itself could allocate a
>>> default PASID for each mdev by calling pasid_alloc() on the mdev's
>>> parent, and then do map()/unmap() with that PASID. This way we don't
>>> have to add IOMMU ops to the mdev bus, everything can still be done
>>> using the ops of the parent. And IOMMU drivers "only" have to
>> implement
>>> PASID ops, which will be reused by drivers other than mdev.
>>
>> yes, PASID is more general abstraction than mdev. However there is
>> one problem. Please note with new scalable mode now PASID is not
>> used just for SVA. You can do 1st-level, 2nd-level and nested in PASID
>> granularity, meaning each PASID can be bound to an unique iommu
>> domain. However today IOMMU driver allows only one iommu_domain
>> per device. If we simply install allocated PASID to parent device, we
>> should also remove that iommu_domain limitation. Is it the way that
>> we want to pursue?
>>
>> mdev avoids such problem as it introduces a separate device...
> 
> another thing to think about is to simplify the caller logic. It would
> be good to have consistent IOMMU APIs cross PCI device and 
> mdev, for common VFIO operations e.g. map/unmap, iommu_
> attach_group, etc. If we need special version ops with PASID, it 
> brings burden to VFIO and other IOMMU clients...

Yes, providing special ops is the simplest to implement (or least
invasive, I guess) but isn't particularly elegant. See the patch from
Jordan below, that I was planning to add to the SVA series (I'm
laboriously working towards next version) and my email from last week:
https://patchwork.kernel.org/patch/10412251/

Whenever I come back to hierarchical IOMMU domains I reject it as too
complicated, but maybe that is what we need. I find it difficult to
reason about because domains currently represent both a collection of
devices and a one or more address spaces. I proposed the io_mm thing to
represent a single address space, and to avoid adding special cases to
every function in the IOMMU subsystem that manipulates domains.

> For IOMMU-capable mdev, there are two use cases which have 
> been talked so far:
> 
> 1) mdev with PASID-granular DMA isolation
> 2) mdev inheriting RID from parent device (no sharing)

Would that be a single mdev per parent device? Otherwise I don't really
understand how it would work without all map/unmap operations going to
the parent device's driver.

> 1) is what this RFC is currently for. 2) is what Alex concerned 
> which is not covered in RFC yet.
> 
> In my mind, an ideal design is like below:
> 
> a) VFIO provides an interface for parent driver to specify 
> whether a mdev is IOMMU capable, with flag to indicate 
> whether it's PASID-granular or RID-inherit;
> 
> b) Once an IOMMU-capable mdev is created, VFIO treats it no
> different from normal physical devices, using exactly same 
> IOMMU APIs to operate (including future SVA). VFIO doesn't 
> care whether PASID or RID will be used in hardware;
> 
> c) IOMMU driver then handle RID/PASID difference internally, 
> based on its own configuration and mdev type.
> 
> To support above goal, I feel a new device handle is a nice fit 
> to represent mdev within IOMMU driver, with same set of 
> capabilities as observed on its parent device.

It's a good abstraction, but I'm still concerned about other users of
PASID-granular DMA isolation, for example GPU drivers wanting to improve
isolation of DMA bufs, will want the same functionality without going
through the vfio-mdev module.

The IOMMU operations we care about don't take a device handle, I think,
just a domain. And VFIO itself only deals with domains when doing
map/unmap. Maybe we could add this operation to the IOMMU subsystem:

child_domain = domain_create_child(parent_dev, parent_domain)

A child domain would be a smaller isolation granule, getting a PASID if
that's what the IOMMU implements or another mechanism for 2). It is
automatically attached to its parent's devices, so attach/detach
operations wouldn't be necessary on the child.

Depending on the underlying architecture the child domain can support
map/unmap on a single stage, or map/unmap for 2nd level and bind_pgtable
for 1st level.

I'm not sure how this works for host SVA though. I think the
sva_bind_dev() API could stay the same, but the internals will need
to change.

Thanks,
Jean

-----
I was going to send the following patch for dealing with private PASIDs.
I used it for a vfio-mdev prototype internally: VFIO would call
iommu_sva_alloc_pasid on the parent device and then sva_map/sva_unmap on
the parent domain + allocated io_mm (which redirects to
iommu_map/iommu_unmap when there is no io_mm). Since it relies on io_mm
instead of child domains, it duplicates the iommu ops, and as discussed
might not be adapted to Vt-d scalable mode.

Subject: [PATCH] iommu: sva: Add support for private PASIDs

Provide an API for allocating PASIDs and populating them manually. To ease
cleanup and factor allocation code, reuse the io_mm structure for private
PASID. Private io_mm has a NULL mm_struct pointer, and cannot be bound to
multiple devices. The mm_alloc() IOMMU op must now check if the mm
argument is NULL, in which case it should allocate io_pgtables instead of
binding to an mm.

Signed-off-by: Jordan Crouse <[email protected]>
Signed-off-by: Jean-Philippe Brucker <[email protected]>
---
 drivers/iommu/iommu-sva.c | 211 +++++++++++++++++++++++++++++++++-----
 drivers/iommu/iommu.c     |  49 ++++++---
 include/linux/iommu.h     | 118 +++++++++++++++++++--
 3 files changed, 331 insertions(+), 47 deletions(-)

diff --git a/drivers/iommu/iommu-sva.c b/drivers/iommu/iommu-sva.c
index 35d0f387fbef..12c9b861014d 100644
--- a/drivers/iommu/iommu-sva.c
+++ b/drivers/iommu/iommu-sva.c
@@ -15,11 +15,11 @@
 /**
  * DOC: io_mm model
  *
- * The io_mm keeps track of process address spaces shared between CPU and
IOMMU.
- * The following example illustrates the relation between structures
- * iommu_domain, io_mm and iommu_bond. An iommu_bond is a link between
io_mm and
- * device. A device can have multiple io_mm and an io_mm may be bound to
- * multiple devices.
+ * When used with the bind()/unbind() functions, the io_mm keeps track of
+ * process address spaces shared between CPU and IOMMU. The following example
+ * illustrates the relation between structures iommu_domain, io_mm and
+ * iommu_bond. An iommu_bond is a link between io_mm and device. A device can
+ * have multiple io_mm and an io_mm may be bound to multiple devices.
  *              ___________________________
  *             |  IOMMU domain A           |
  *             |  ________________         |
@@ -97,6 +97,12 @@
  * non-PASID translations. In this case PASID 0 is reserved and entry 0
points
  * to the io_pgtable base. On Intel IOMMU, the io_pgtable base would be
held in
  * the device table and PASID 0 would be available to the allocator.
+ *
+ * The io_mm can also represent a private IOMMU address space, which isn't
+ * shared with a process. The device driver calls iommu_sva_alloc_pasid which
+ * returns an io_mm that can be populated with the iommu_sva_map/unmap
+ * functions. The principle is the same as shared io_mm, except that a
private
+ * io_mm cannot be bound to multiple devices.
  */

 struct iommu_bond {
@@ -130,6 +136,9 @@ static DEFINE_SPINLOCK(iommu_sva_lock);

 static struct mmu_notifier_ops iommu_mmu_notifier;

+#define io_mm_is_private(io_mm) ((io_mm) != NULL && (io_mm)->mm == NULL)
+#define io_mm_is_shared(io_mm) ((io_mm) != NULL && (io_mm)->mm != NULL)
+
 static struct io_mm *
 io_mm_alloc(struct iommu_domain *domain, struct device *dev,
            struct mm_struct *mm, unsigned long flags)
@@ -148,19 +157,10 @@ io_mm_alloc(struct iommu_domain *domain, struct
device *dev,
        if (!io_mm)
                return ERR_PTR(-ENOMEM);

-       /*
-        * The mm must not be freed until after the driver frees the io_mm
-        * (which may involve unpinning the CPU ASID for instance, requiring a
-        * valid mm struct.)
-        */
-       mmgrab(mm);
-
        io_mm->flags            = flags;
        io_mm->mm               = mm;
-       io_mm->notifier.ops     = &iommu_mmu_notifier;
        io_mm->release          = domain->ops->mm_free;
        INIT_LIST_HEAD(&io_mm->devices);
-       /* Leave kref to zero until the io_mm is fully initialized */

        idr_preload(GFP_KERNEL);
        spin_lock(&iommu_sva_lock);
@@ -175,6 +175,32 @@ io_mm_alloc(struct iommu_domain *domain, struct
device *dev,
                goto err_free_mm;
        }

+       return io_mm;
+
+err_free_mm:
+       io_mm->release(io_mm);
+       return ERR_PTR(ret);
+}
+
+static struct io_mm *
+io_mm_alloc_shared(struct iommu_domain *domain, struct device *dev,
+                  struct mm_struct *mm, unsigned long flags)
+{
+       int ret;
+       struct io_mm *io_mm;
+
+       io_mm = io_mm_alloc(domain, dev, mm, flags);
+       if (IS_ERR(io_mm))
+               return io_mm;
+
+       /*
+        * The mm must not be freed until after the driver frees the io_mm
+        * (which may involve unpinning the CPU ASID for instance, requiring a
+        * valid mm struct.)
+        */
+       mmgrab(mm);
+
+       io_mm->notifier.ops = &iommu_mmu_notifier;
        ret = mmu_notifier_register(&io_mm->notifier, mm);
        if (ret)
                goto err_free_pasid;
@@ -202,7 +228,6 @@ io_mm_alloc(struct iommu_domain *domain, struct device
*dev,
        idr_remove(&iommu_pasid_idr, io_mm->pasid);
        spin_unlock(&iommu_sva_lock);

-err_free_mm:
        io_mm->release(io_mm);
        mmdrop(mm);

@@ -231,6 +256,11 @@ static void io_mm_release(struct kref *kref)
        /* The PASID can now be reallocated for another mm. */
        idr_remove(&iommu_pasid_idr, io_mm->pasid);

+       if (io_mm_is_private(io_mm)) {
+               io_mm->release(io_mm);
+               return;
+       }
+
        /*
         * If we're being released from mm exit, the notifier callback ->release
         * has already been called. Otherwise we don't need ->release, the io_mm
@@ -258,7 +288,7 @@ static int io_mm_get_locked(struct io_mm *io_mm)
        if (io_mm && kref_get_unless_zero(&io_mm->kref)) {
                /*
                 * kref_get_unless_zero doesn't provide ordering for reads. This
-                * barrier pairs with the one in io_mm_alloc.
+                * barrier pairs with the one in io_mm_alloc_shared.
                 */
                smp_rmb();
                return 1;
@@ -289,7 +319,7 @@ static int io_mm_attach(struct iommu_domain *domain,
struct device *dev,
        struct iommu_sva_param *param = dev->iommu_param->sva_param;

        if (!domain->ops->mm_attach || !domain->ops->mm_detach ||
-           !domain->ops->mm_invalidate)
+           (io_mm_is_shared(io_mm) && !domain->ops->mm_invalidate))
                return -ENODEV;

        if (pasid > param->max_pasid || pasid < param->min_pasid)
@@ -550,7 +580,7 @@ int iommu_sva_device_init(struct device *dev, unsigned
long features,
        struct iommu_sva_param *param;
        struct iommu_domain *domain = iommu_get_domain_for_dev(dev);

-       if (!domain || !domain->ops->sva_device_init)
+       if (!domain)
                return -ENODEV;

        if (features & ~IOMMU_SVA_FEAT_IOPF)
@@ -584,9 +614,11 @@ int iommu_sva_device_init(struct device *dev,
unsigned long features,
         * IOMMU driver updates the limits depending on the IOMMU and device
         * capabilities.
         */
-       ret = domain->ops->sva_device_init(dev, param);
-       if (ret)
-               goto err_free_param;
+       if (domain->ops->sva_device_init) {
+               ret = domain->ops->sva_device_init(dev, param);
+               if (ret)
+                       goto err_free_param;
+       }

        dev->iommu_param->sva_param = param;
        mutex_unlock(&dev->iommu_param->lock);
@@ -688,7 +720,7 @@ int __iommu_sva_bind_device(struct device *dev, struct
mm_struct *mm,
        }

        if (!io_mm) {
-               io_mm = io_mm_alloc(domain, dev, mm, flags);
+               io_mm = io_mm_alloc_shared(domain, dev, mm, flags);
                if (IS_ERR(io_mm))
                        return PTR_ERR(io_mm);
        }
@@ -723,6 +755,9 @@ int __iommu_sva_unbind_device(struct device *dev, int
pasid)
        /* spin_lock_irq matches the one in wait_event_lock_irq */
        spin_lock_irq(&iommu_sva_lock);
        list_for_each_entry(bond, &param->mm_list, dev_head) {
+               if (io_mm_is_private(bond->io_mm))
+                       continue;
+
                if (bond->io_mm->pasid == pasid) {
                        io_mm_detach_locked(bond, true);
                        ret = 0;
@@ -765,6 +800,136 @@ void __iommu_sva_unbind_dev_all(struct device *dev)
 }
 EXPORT_SYMBOL_GPL(__iommu_sva_unbind_dev_all);

+/*
+ * iommu_sva_alloc_pasid - Allocate a private PASID
+ *
+ * Allocate a PASID for private map/unmap operations. Create a new I/O
address
+ * space for this device, that isn't bound to any process.
+ *
+ * iommu_sva_device_init must have been called first.
+ */
+int iommu_sva_alloc_pasid(struct device *dev, struct io_mm **out)
+{
+       int ret;
+       struct io_mm *io_mm;
+       struct iommu_domain *domain;
+       struct iommu_sva_param *param = dev->iommu_param->sva_param;
+
+       if (!out || !param)
+               return -EINVAL;
+
+       domain = iommu_get_domain_for_dev(dev);
+       if (!domain)
+               return -EINVAL;
+
+       io_mm = io_mm_alloc(domain, dev, NULL, 0);
+       if (IS_ERR(io_mm))
+               return PTR_ERR(io_mm);
+
+       kref_init(&io_mm->kref);
+
+       ret = io_mm_attach(domain, dev, io_mm, NULL);
+       if (ret) {
+               io_mm_put(io_mm);
+               return ret;
+       }
+
+       *out = io_mm;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(iommu_sva_alloc_pasid);
+
+void iommu_sva_free_pasid(struct device *dev, struct io_mm *io_mm)
+{
+       struct iommu_bond *bond;
+
+       if (WARN_ON(io_mm_is_shared(io_mm)))
+               return;
+
+       spin_lock(&iommu_sva_lock);
+       list_for_each_entry(bond, &io_mm->devices, mm_head) {
+               if (bond->dev == dev) {
+                       io_mm_detach_locked(bond, false);
+                       break;
+               }
+       }
+       spin_unlock(&iommu_sva_lock);
+}
+EXPORT_SYMBOL_GPL(iommu_sva_free_pasid);
+
+int iommu_sva_map(struct iommu_domain *domain, struct io_mm *io_mm,
+                 unsigned long iova, phys_addr_t paddr, size_t size, int prot)
+{
+       if (WARN_ON(io_mm_is_shared(io_mm)))
+               return -ENODEV;
+
+       return __iommu_map(domain, io_mm, iova, paddr, size, prot);
+}
+EXPORT_SYMBOL_GPL(iommu_sva_map);
+
+size_t iommu_sva_map_sg(struct iommu_domain *domain, struct io_mm *io_mm,
+                       unsigned long iova, struct scatterlist *sg,
+                       unsigned int nents, int prot)
+{
+       if (WARN_ON(io_mm_is_shared(io_mm)))
+               return -ENODEV;
+
+       return domain->ops->map_sg(domain, io_mm, iova, sg, nents, prot);
+}
+EXPORT_SYMBOL_GPL(iommu_sva_map_sg);
+
+size_t iommu_sva_unmap(struct iommu_domain *domain, struct io_mm *io_mm,
+                      unsigned long iova, size_t size)
+{
+       if (WARN_ON(io_mm_is_shared(io_mm)))
+               return 0;
+
+       return __iommu_unmap(domain, io_mm, iova, size, true);
+}
+EXPORT_SYMBOL_GPL(iommu_sva_unmap);
+
+size_t iommu_sva_unmap_fast(struct iommu_domain *domain, struct io_mm *io_mm,
+                           unsigned long iova, size_t size)
+{
+       if (WARN_ON(io_mm_is_shared(io_mm)))
+               return 0;
+
+       return __iommu_unmap(domain, io_mm, iova, size, false);
+}
+EXPORT_SYMBOL_GPL(iommu_sva_unmap_fast);
+
+phys_addr_t iommu_sva_iova_to_phys(struct iommu_domain *domain,
+                                  struct io_mm *io_mm, dma_addr_t iova)
+{
+       if (!io_mm)
+               return iommu_iova_to_phys(domain, iova);
+
+       if (WARN_ON(io_mm_is_shared(io_mm)))
+               return 0;
+
+       if (unlikely(domain->ops->sva_iova_to_phys == NULL))
+               return 0;
+
+       return domain->ops->sva_iova_to_phys(domain, io_mm, iova);
+}
+EXPORT_SYMBOL_GPL(iommu_sva_iova_to_phys);
+
+void iommu_sva_tlb_range_add(struct iommu_domain *domain, struct io_mm
*io_mm,
+                            unsigned long iova, size_t size)
+{
+       if (!io_mm) {
+               iommu_tlb_range_add(domain, iova, size);
+               return;
+       }
+
+       if (WARN_ON(io_mm_is_shared(io_mm)))
+               return;
+
+       if (domain->ops->sva_iotlb_range_add != NULL)
+               domain->ops->sva_iotlb_range_add(domain, io_mm, iova, size);
+}
+EXPORT_SYMBOL_GPL(iommu_sva_tlb_range_add);
+
 /**
  * iommu_sva_find() - Find mm associated to the given PASID
  * @pasid: Process Address Space ID assigned to the mm
@@ -779,7 +944,7 @@ struct mm_struct *iommu_sva_find(int pasid)

        spin_lock(&iommu_sva_lock);
        io_mm = idr_find(&iommu_pasid_idr, pasid);
-       if (io_mm && io_mm_get_locked(io_mm)) {
+       if (io_mm_is_shared(io_mm) && io_mm_get_locked(io_mm)) {
                if (mmget_not_zero(io_mm->mm))
                        mm = io_mm->mm;

diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index b02e1d32c733..26ac9b2a813e 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -1816,8 +1816,8 @@ static size_t iommu_pgsize(struct iommu_domain *domain,
        return pgsize;
 }

-int iommu_map(struct iommu_domain *domain, unsigned long iova,
-             phys_addr_t paddr, size_t size, int prot)
+int __iommu_map(struct iommu_domain *domain, struct io_mm *io_mm,
+               unsigned long iova, phys_addr_t paddr, size_t size, int prot)
 {
        unsigned long orig_iova = iova;
        unsigned int min_pagesz;
@@ -1825,7 +1825,8 @@ int iommu_map(struct iommu_domain *domain, unsigned
long iova,
        phys_addr_t orig_paddr = paddr;
        int ret = 0;

-       if (unlikely(domain->ops->map == NULL ||
+       if (unlikely((!io_mm && domain->ops->map == NULL) ||
+                    (io_mm && domain->ops->sva_map == NULL) ||
                     domain->pgsize_bitmap == 0UL))
                return -ENODEV;

@@ -1854,7 +1855,12 @@ int iommu_map(struct iommu_domain *domain, unsigned
long iova,
                pr_debug("mapping: iova 0x%lx pa %pa pgsize 0x%zx\n",
                         iova, &paddr, pgsize);

-               ret = domain->ops->map(domain, iova, paddr, pgsize, prot);
+               if (io_mm)
+                       ret = domain->ops->sva_map(domain, io_mm, iova, paddr,
+                                                  pgsize, prot);
+               else
+                       ret = domain->ops->map(domain, iova, paddr, pgsize,
+                                              prot);
                if (ret)
                        break;

@@ -1865,24 +1871,30 @@ int iommu_map(struct iommu_domain *domain,
unsigned long iova,

        /* unroll mapping in case something went wrong */
        if (ret)
-               iommu_unmap(domain, orig_iova, orig_size - size);
+               __iommu_unmap(domain, io_mm, orig_iova, orig_size - size, true);
        else
                trace_map(orig_iova, orig_paddr, orig_size);

        return ret;
 }
+
+int iommu_map(struct iommu_domain *domain, unsigned long iov,
+             phys_addr_t paddr, size_t size, int prot)
+{
+       return __iommu_map(domain, NULL, iov, paddr, size, prot);
+}
 EXPORT_SYMBOL_GPL(iommu_map);

-static size_t __iommu_unmap(struct iommu_domain *domain,
-                           unsigned long iova, size_t size,
-                           bool sync)
+size_t __iommu_unmap(struct iommu_domain *domain, struct io_mm *io_mm,
+                    unsigned long iova, size_t size, bool sync)
 {
        const struct iommu_ops *ops = domain->ops;
        size_t unmapped_page, unmapped = 0;
        unsigned long orig_iova = iova;
        unsigned int min_pagesz;

-       if (unlikely(ops->unmap == NULL ||
+       if (unlikely((!io_mm && ops->unmap == NULL) ||
+                    (io_mm && ops->sva_unmap == NULL) ||
                     domain->pgsize_bitmap == 0UL))
                return 0;

@@ -1912,7 +1924,11 @@ static size_t __iommu_unmap(struct iommu_domain
*domain,
        while (unmapped < size) {
                size_t pgsize = iommu_pgsize(domain, iova, size - unmapped);

-               unmapped_page = ops->unmap(domain, iova, pgsize);
+               if (io_mm)
+                       unmapped_page = ops->sva_unmap(domain, io_mm, iova,
+                                                      pgsize);
+               else
+                       unmapped_page = ops->unmap(domain, iova, pgsize);
                if (!unmapped_page)
                        break;

@@ -1936,19 +1952,20 @@ static size_t __iommu_unmap(struct iommu_domain
*domain,
 size_t iommu_unmap(struct iommu_domain *domain,
                   unsigned long iova, size_t size)
 {
-       return __iommu_unmap(domain, iova, size, true);
+       return __iommu_unmap(domain, NULL, iova, size, true);
 }
 EXPORT_SYMBOL_GPL(iommu_unmap);

 size_t iommu_unmap_fast(struct iommu_domain *domain,
                        unsigned long iova, size_t size)
 {
-       return __iommu_unmap(domain, iova, size, false);
+       return __iommu_unmap(domain, NULL, iova, size, false);
 }
 EXPORT_SYMBOL_GPL(iommu_unmap_fast);

-size_t default_iommu_map_sg(struct iommu_domain *domain, unsigned long iova,
-                        struct scatterlist *sg, unsigned int nents, int prot)
+size_t default_iommu_map_sg(struct iommu_domain *domain, struct io_mm *io_mm,
+                           unsigned long iova, struct scatterlist *sg, unsigned
+                           int nents, int prot)
 {
        struct scatterlist *s;
        size_t mapped = 0;
@@ -1972,7 +1989,7 @@ size_t default_iommu_map_sg(struct iommu_domain
*domain, unsigned long iova,
                if (!IS_ALIGNED(s->offset, min_pagesz))
                        goto out_err;

-               ret = iommu_map(domain, iova + mapped, phys, s->length, prot);
+               ret = __iommu_map(domain, io_mm, iova + mapped, phys, 
s->length, prot);
                if (ret)
                        goto out_err;

@@ -1983,7 +2000,7 @@ size_t default_iommu_map_sg(struct iommu_domain
*domain, unsigned long iova,

 out_err:
        /* undo mappings already done */
-       iommu_unmap(domain, iova, mapped);
+       __iommu_unmap(domain, io_mm, iova, mapped, true);

        return 0;

diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index b525f5636df8..1f63a8691173 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -248,13 +248,17 @@ struct iommu_sva_param {
  * @mm_invalidate: Invalidate a range of mappings for an mm
  * @map: map a physically contiguous memory region to an iommu domain
  * @unmap: unmap a physically contiguous memory region from an iommu domain
+ * @sva_map: map a physically contiguous memory region to an address space
+ * @sva_unmap: unmap a physically contiguous memory region from an
address space
  * @map_sg: map a scatter-gather list of physically contiguous memory chunks
  *          to an iommu domain
  * @flush_tlb_all: Synchronously flush all hardware TLBs for this domain
  * @tlb_range_add: Add a given iova range to the flush queue for this domain
+ * @sva_iotlb_range_add: Add a given iova range to the flush queue for
this mm
  * @tlb_sync: Flush all queued ranges from the hardware TLBs and empty flush
  *            queue
  * @iova_to_phys: translate iova to physical address
+ * @sva_iova_to_phys: translate iova to physical address
  * @add_device: add device to iommu grouping
  * @remove_device: remove device from iommu grouping
  * @device_group: find iommu group for a particular device
@@ -302,13 +306,24 @@ struct iommu_ops {
                   phys_addr_t paddr, size_t size, int prot);
        size_t (*unmap)(struct iommu_domain *domain, unsigned long iova,
                     size_t size);
-       size_t (*map_sg)(struct iommu_domain *domain, unsigned long iova,
-                        struct scatterlist *sg, unsigned int nents, int prot);
+       int (*sva_map)(struct iommu_domain *domain, struct io_mm *io_mm,
+                      unsigned long iova, phys_addr_t paddr, size_t size,
+                      int prot);
+       size_t (*sva_unmap)(struct iommu_domain *domain, struct io_mm *io_mm,
+                           unsigned long iova, size_t size);
+       size_t (*map_sg)(struct iommu_domain *domain, struct io_mm *io_mm,
+                        unsigned long iova, struct scatterlist *sg,
+                        unsigned int nents, int prot);
        void (*flush_iotlb_all)(struct iommu_domain *domain);
        void (*iotlb_range_add)(struct iommu_domain *domain,
                                unsigned long iova, size_t size);
+       void (*sva_iotlb_range_add)(struct iommu_domain *domain,
+                                   struct io_mm *io_mm, unsigned long iova,
+                                   size_t size);
        void (*iotlb_sync)(struct iommu_domain *domain);
        phys_addr_t (*iova_to_phys)(struct iommu_domain *domain, dma_addr_t 
iova);
+       phys_addr_t (*sva_iova_to_phys)(struct iommu_domain *domain,
+                                       struct io_mm *io_mm, dma_addr_t iova);
        int (*add_device)(struct device *dev);
        void (*remove_device)(struct device *dev);
        struct iommu_group *(*device_group)(struct device *dev);
@@ -528,15 +543,20 @@ extern int iommu_sva_invalidate(struct iommu_domain
*domain,
                struct device *dev, struct tlb_invalidate_info *inv_info);

 extern struct iommu_domain *iommu_get_domain_for_dev(struct device *dev);
+extern int __iommu_map(struct iommu_domain *domain, struct io_mm *io_mm,
+                      unsigned long iova, phys_addr_t paddr, size_t size,
+                      int prot);
 extern int iommu_map(struct iommu_domain *domain, unsigned long iova,
                     phys_addr_t paddr, size_t size, int prot);
+extern size_t __iommu_unmap(struct iommu_domain *domain, struct io_mm *io_mm,
+                           unsigned long iova, size_t size, bool sync);
 extern size_t iommu_unmap(struct iommu_domain *domain, unsigned long iova,
                          size_t size);
 extern size_t iommu_unmap_fast(struct iommu_domain *domain,
                               unsigned long iova, size_t size);
-extern size_t default_iommu_map_sg(struct iommu_domain *domain, unsigned
long iova,
-                               struct scatterlist *sg,unsigned int nents,
-                               int prot);
+extern size_t default_iommu_map_sg(struct iommu_domain *domain, struct
io_mm *io_mm,
+                                  unsigned long iova, struct scatterlist *sg,
+                                  unsigned int nents, int prot);
 extern phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain,
dma_addr_t iova);
 extern void iommu_set_fault_handler(struct iommu_domain *domain,
                        iommu_fault_handler_t handler, void *token);
@@ -622,7 +642,7 @@ static inline size_t iommu_map_sg(struct iommu_domain
*domain,
                                  unsigned long iova, struct scatterlist *sg,
                                  unsigned int nents, int prot)
 {
-       return domain->ops->map_sg(domain, iova, sg, nents, prot);
+       return domain->ops->map_sg(domain, NULL, iova, sg, nents, prot);
 }

 /* PCI device grouping function */
@@ -710,12 +730,25 @@ static inline struct iommu_domain
*iommu_get_domain_for_dev(struct device *dev)
        return NULL;
 }

+static inline int __iommu_map(struct iommu_domain *domain, struct io_mm
*io_mm,
+                             unsigned long iova, phys_addr_t paddr,
+                             size_t size, int prot)
+{
+       return -ENODEV;
+}
+
 static inline int iommu_map(struct iommu_domain *domain, unsigned long iova,
                            phys_addr_t paddr, size_t size, int prot)
 {
        return -ENODEV;
 }

+static inline size_t __iommu_unmap(struct iommu_domain *domain, struct
io_mm *io_mm,
+                    unsigned long iova, size_t size, bool sync)
+{
+       return 0;
+}
+
 static inline size_t iommu_unmap(struct iommu_domain *domain,
                                 unsigned long iova, size_t size)
 {
@@ -729,8 +762,9 @@ static inline size_t iommu_unmap_fast(struct
iommu_domain *domain,
 }

 static inline size_t iommu_map_sg(struct iommu_domain *domain,
-                                 unsigned long iova, struct scatterlist *sg,
-                                 unsigned int nents, int prot)
+                                 struct io_mm *io_mm, unsigned long iova,
+                                 struct scatterlist *sg, unsigned int nents,
+                                 int prot)
 {
        return 0;
 }
@@ -1017,6 +1051,22 @@ extern int __iommu_sva_bind_device(struct device
*dev, struct mm_struct *mm,
 extern int __iommu_sva_unbind_device(struct device *dev, int pasid);
 extern void __iommu_sva_unbind_dev_all(struct device *dev);

+int iommu_sva_alloc_pasid(struct device *dev, struct io_mm **io_mm);
+void iommu_sva_free_pasid(struct device *dev, struct io_mm *io_mm);
+
+int iommu_sva_map(struct iommu_domain *domain, struct io_mm *io_mm,
+                 unsigned long iova, phys_addr_t paddr, size_t size, int prot);
+size_t iommu_sva_map_sg(struct iommu_domain *domain, struct io_mm *io_mm,
+                       unsigned long iova, struct scatterlist *sg,
+                       unsigned int nents, int prot);
+size_t iommu_sva_unmap(struct iommu_domain *domain,
+                      struct io_mm *io_mm, unsigned long iova, size_t size);
+size_t iommu_sva_unmap_fast(struct iommu_domain *domain, struct io_mm *io_mm,
+                           unsigned long iova, size_t size);
+phys_addr_t iommu_sva_iova_to_phys(struct iommu_domain *domain,
+                                  struct io_mm *io_mm, dma_addr_t iova);
+void iommu_sva_tlb_range_add(struct iommu_domain *domain, struct io_mm
*io_mm,
+                            unsigned long iova, size_t size);
 extern struct mm_struct *iommu_sva_find(int pasid);
 #else /* CONFIG_IOMMU_SVA */
 static inline int iommu_sva_device_init(struct device *dev,
@@ -1048,6 +1098,58 @@ static inline void
__iommu_sva_unbind_dev_all(struct device *dev)
 {
 }

+static inline struct io_mm *
+iommu_sva_alloc_pasid(struct iommu_domain *domain, struct device *dev)
+{
+       return ERR_PTR(-ENODEV);
+}
+
+static inline void iommu_sva_free_pasid(struct io_mm *io_mm, struct
device *dev)
+{
+}
+
+static inline int iommu_sva_map(struct iommu_domain *domain,
+                               struct io_mm *io_mm, unsigned long iova,
+                               phys_addr_t paddr, size_t size, int prot)
+{
+       return -EINVAL;
+}
+
+static inline size_t iommu_sva_map_sg(struct iommu_domain *domain,
+                                     struct io_mm *io_mm, unsigned long iova,
+                                     struct scatterlist *sg,
+                                     unsigned int nents, int prot)
+{
+       return 0;
+}
+
+static inline size_t iommu_sva_unmap(struct iommu_domain *domain,
+                                    struct io_mm *io_mm, unsigned long iova,
+                                    size_t size)
+{
+       return 0;
+}
+
+static inline size_t iommu_sva_unmap_fast(struct iommu_domain *domain,
+                                         struct io_mm *io_mm,
+                                         unsigned long iova, size_t size)
+{
+       return 0;
+}
+
+static inline phys_addr_t iommu_sva_iova_to_phys(struct iommu_domain *domain,
+                                                struct io_mm *io_mm,
+                                                dma_addr_t iova)
+{
+       return 0;
+}
+
+static inline void iommu_sva_tlb_range_add(struct iommu_domain *domain,
+                                          struct io_mm *io_mm,
+                                          unsigned long iova, size_t size)
+{
+}
+
 static inline struct mm_struct *iommu_sva_find(int pasid)
 {
        return NULL;
-- 
2.18.0


_______________________________________________
iommu mailing list
[email protected]
https://lists.linuxfoundation.org/mailman/listinfo/iommu

Reply via email to