This adds create/remove window ioctls to create and remove DMA windows.

This changes VFIO_IOMMU_SPAPR_TCE_GET_INFO handler to return additional
information such as a number of supported windows and maximum number
levels of TCE tables.
Signed-off-by: Alexey Kardashevskiy <a...@ozlabs.ru>
---
 arch/powerpc/include/asm/iommu.h    |   2 +-
 drivers/vfio/vfio_iommu_spapr_tce.c | 137 +++++++++++++++++++++++++++++++++++-
 include/uapi/linux/vfio.h           |  24 ++++++-
 3 files changed, 160 insertions(+), 3 deletions(-)

diff --git a/arch/powerpc/include/asm/iommu.h b/arch/powerpc/include/asm/iommu.h
index 33009f9..7ca1c8c 100644
--- a/arch/powerpc/include/asm/iommu.h
+++ b/arch/powerpc/include/asm/iommu.h
@@ -133,7 +133,7 @@ extern void iommu_free_table(struct iommu_table *tbl, const 
char *node_name);
 extern struct iommu_table *iommu_init_table(struct iommu_table * tbl,
                                            int nid);
 
-#define POWERPC_IOMMU_MAX_TABLES       1
+#define POWERPC_IOMMU_MAX_TABLES       2
 
 #define POWERPC_IOMMU_DEFAULT_LEVELS   1
 
diff --git a/drivers/vfio/vfio_iommu_spapr_tce.c 
b/drivers/vfio/vfio_iommu_spapr_tce.c
index 8bcafb7..d3a1cc9 100644
--- a/drivers/vfio/vfio_iommu_spapr_tce.c
+++ b/drivers/vfio/vfio_iommu_spapr_tce.c
@@ -300,6 +300,20 @@ static struct iommu_table *spapr_tce_find_table(
        return ret;
 }
 
+static int spapr_tce_find_free_table(struct tce_container *container)
+{
+       int i;
+
+       for (i = 0; i < POWERPC_IOMMU_MAX_TABLES; ++i) {
+               struct iommu_table *tbl = &container->tables[i];
+
+               if (!tbl->it_size)
+                       return i;
+       }
+
+       return -1;
+}
+
 static unsigned long tce_default_winsize(struct tce_container *container)
 {
        struct tce_iommu_group *tcegrp;
@@ -594,7 +608,7 @@ static long tce_iommu_ioctl(void *iommu_data,
                                 unsigned int cmd, unsigned long arg)
 {
        struct tce_container *container = iommu_data;
-       unsigned long minsz;
+       unsigned long minsz, ddwsz;
        long ret;
 
        switch (cmd) {
@@ -636,6 +650,15 @@ static long tce_iommu_ioctl(void *iommu_data,
 
                info.dma32_window_start = iommu->tce32_start;
                info.dma32_window_size = iommu->tce32_size;
+               info.windows_supported = iommu->windows_supported;
+               info.levels = iommu->levels;
+               info.flags = iommu->flags;
+
+               ddwsz = offsetofend(struct vfio_iommu_spapr_tce_info,
+                               levels);
+
+               if (info.argsz == ddwsz)
+                       minsz = ddwsz;
 
                if (copy_to_user((void __user *)arg, &info, minsz))
                        return -EFAULT;
@@ -800,6 +823,118 @@ static long tce_iommu_ioctl(void *iommu_data,
                return ret;
        }
 
+       case VFIO_IOMMU_SPAPR_TCE_CREATE: {
+               struct vfio_iommu_spapr_tce_create create;
+               struct powerpc_iommu *iommu;
+               struct tce_iommu_group *tcegrp;
+               int num;
+
+               if (!tce_preregistered(container))
+                       return -ENXIO;
+
+               minsz = offsetofend(struct vfio_iommu_spapr_tce_create,
+                               start_addr);
+
+               if (copy_from_user(&create, (void __user *)arg, minsz))
+                       return -EFAULT;
+
+               if (create.argsz < minsz)
+                       return -EINVAL;
+
+               if (create.flags)
+                       return -EINVAL;
+
+               num = spapr_tce_find_free_table(container);
+               if (num < 0)
+                       return -ENOSYS;
+
+               tcegrp = list_first_entry(&container->group_list,
+                               struct tce_iommu_group, next);
+               iommu = iommu_group_get_iommudata(tcegrp->grp);
+
+               ret = iommu->ops->create_table(iommu, num,
+                               create.page_shift, create.window_shift,
+                               create.levels,
+                               &container->tables[num]);
+               if (ret)
+                       return ret;
+
+               list_for_each_entry(tcegrp, &container->group_list, next) {
+                       struct powerpc_iommu *iommutmp =
+                                       iommu_group_get_iommudata(tcegrp->grp);
+
+                       if (WARN_ON_ONCE(iommutmp->ops != iommu->ops))
+                               return -EFAULT;
+
+                       ret = iommu->ops->set_window(iommutmp, num,
+                                       &container->tables[num]);
+                       if (ret)
+                               return ret;
+               }
+
+               create.start_addr =
+                               container->tables[num].it_offset <<
+                               container->tables[num].it_page_shift;
+
+               if (copy_to_user((void __user *)arg, &create, minsz))
+                       return -EFAULT;
+
+               mutex_lock(&container->lock);
+               mutex_unlock(&container->lock);
+
+               return ret;
+       }
+       case VFIO_IOMMU_SPAPR_TCE_REMOVE: {
+               struct vfio_iommu_spapr_tce_remove remove;
+               struct powerpc_iommu *iommu = NULL;
+               struct iommu_table *tbl;
+               struct tce_iommu_group *tcegrp;
+               int num;
+
+               if (!tce_preregistered(container))
+                       return -ENXIO;
+
+               minsz = offsetofend(struct vfio_iommu_spapr_tce_remove,
+                               start_addr);
+
+               if (copy_from_user(&remove, (void __user *)arg, minsz))
+                       return -EFAULT;
+
+               if (remove.argsz < minsz)
+                       return -EINVAL;
+
+               if (remove.flags)
+                       return -EINVAL;
+
+
+               tbl = spapr_tce_find_table(container, remove.start_addr);
+               if (!tbl)
+                       return -EINVAL;
+
+               /* Detach windows from IOMMUs */
+               mutex_lock(&container->lock);
+
+               /* Detach groups from IOMMUs */
+               num = tbl - container->tables;
+               list_for_each_entry(tcegrp, &container->group_list, next) {
+                       iommu = iommu_group_get_iommudata(tcegrp->grp);
+                       if (container->tables[num].it_size)
+                               iommu->ops->unset_window(iommu, num);
+               }
+
+               /* Free table */
+               tcegrp = list_first_entry(&container->group_list,
+                               struct tce_iommu_group, next);
+               iommu = iommu_group_get_iommudata(tcegrp->grp);
+
+               tce_iommu_clear(container, tbl,
+                               tbl->it_offset, tbl->it_size);
+               iommu->ops->free_table(tbl);
+
+               mutex_unlock(&container->lock);
+
+               return 0;
+       }
        }
 
        return -ENOTTY;
diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h
index 2bb0c9b..7ed7000 100644
--- a/include/uapi/linux/vfio.h
+++ b/include/uapi/linux/vfio.h
@@ -483,9 +483,11 @@ struct vfio_iommu_type1_unregister_memory {
  */
 struct vfio_iommu_spapr_tce_info {
        __u32 argsz;
-       __u32 flags;                    /* reserved for future use */
+       __u32 flags;
        __u32 dma32_window_start;       /* 32 bit window start (bytes) */
        __u32 dma32_window_size;        /* 32 bit window size (bytes) */
+       __u32 windows_supported;
+       __u32 levels;
 };
 
 #define VFIO_IOMMU_SPAPR_TCE_GET_INFO  _IO(VFIO_TYPE, VFIO_BASE + 12)
@@ -521,6 +523,26 @@ struct vfio_eeh_pe_op {
 
 #define VFIO_EEH_PE_OP                 _IO(VFIO_TYPE, VFIO_BASE + 21)
 
+struct vfio_iommu_spapr_tce_create {
+       __u32 argsz;
+       __u32 flags;
+       /* in */
+       __u32 page_shift;
+       __u32 window_shift;
+       __u32 levels;
+       /* out */
+       __u64 start_addr;
+};
+#define VFIO_IOMMU_SPAPR_TCE_CREATE    _IO(VFIO_TYPE, VFIO_BASE + 19)
+
+struct vfio_iommu_spapr_tce_remove {
+       __u32 argsz;
+       __u32 flags;
+       /* in */
+       __u64 start_addr;
+};
+#define VFIO_IOMMU_SPAPR_TCE_REMOVE    _IO(VFIO_TYPE, VFIO_BASE + 20)
+
 /* ***************************************************************** */
 
 #endif /* _UAPIVFIO_H */
-- 
2.0.0

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to