summaryrefslogtreecommitdiffstats
path: root/drivers/vfio
diff options
context:
space:
mode:
authorAlexey Kardashevskiy <aik@ozlabs.ru>2015-06-05 02:35:26 -0400
committerMichael Ellerman <mpe@ellerman.id.au>2015-06-11 01:16:55 -0400
commite633bc86a922468a82300eef5b9802e17be5e23d (patch)
treeccbe3f4a7dc46dc8caed2aff7733548ba9d60c30 /drivers/vfio
parent2157e7b82f3b81f57bd80cd67cef09ef26e5f74c (diff)
vfio: powerpc/spapr: Support Dynamic DMA windows
This adds create/remove window ioctls to create and remove DMA windows. sPAPR defines a Dynamic DMA windows capability which allows para-virtualized guests to create additional DMA windows on a PCI bus. The existing linux kernels use this new window to map the entire guest memory and switch to the direct DMA operations saving time on map/unmap requests which would normally happen in a big amounts. This adds 2 ioctl handlers - VFIO_IOMMU_SPAPR_TCE_CREATE and VFIO_IOMMU_SPAPR_TCE_REMOVE - to create and remove windows. Up to 2 windows are supported now by the hardware and by this driver. 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. DDW is added as a capability, not as a SPAPR TCE IOMMU v2 unique feature as we still want to support v2 on platforms which cannot do DDW for the sake of TCE acceleration in KVM (coming soon). Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru> [aw: for the vfio related changes] Acked-by: Alex Williamson <alex.williamson@redhat.com> Reviewed-by: David Gibson <david@gibson.dropbear.id.au> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Diffstat (limited to 'drivers/vfio')
-rw-r--r--drivers/vfio/vfio_iommu_spapr_tce.c196
1 files changed, 195 insertions, 1 deletions
diff --git a/drivers/vfio/vfio_iommu_spapr_tce.c b/drivers/vfio/vfio_iommu_spapr_tce.c
index 91a32239bd0a..0582b72ef377 100644
--- a/drivers/vfio/vfio_iommu_spapr_tce.c
+++ b/drivers/vfio/vfio_iommu_spapr_tce.c
@@ -211,6 +211,18 @@ static long tce_iommu_find_table(struct tce_container *container,
211 return -1; 211 return -1;
212} 212}
213 213
214static int tce_iommu_find_free_table(struct tce_container *container)
215{
216 int i;
217
218 for (i = 0; i < IOMMU_TABLE_GROUP_MAX_TABLES; ++i) {
219 if (!container->tables[i])
220 return i;
221 }
222
223 return -ENOSPC;
224}
225
214static int tce_iommu_enable(struct tce_container *container) 226static int tce_iommu_enable(struct tce_container *container)
215{ 227{
216 int ret = 0; 228 int ret = 0;
@@ -593,11 +605,115 @@ static void tce_iommu_free_table(struct iommu_table *tbl)
593 decrement_locked_vm(pages); 605 decrement_locked_vm(pages);
594} 606}
595 607
608static long tce_iommu_create_window(struct tce_container *container,
609 __u32 page_shift, __u64 window_size, __u32 levels,
610 __u64 *start_addr)
611{
612 struct tce_iommu_group *tcegrp;
613 struct iommu_table_group *table_group;
614 struct iommu_table *tbl = NULL;
615 long ret, num;
616
617 num = tce_iommu_find_free_table(container);
618 if (num < 0)
619 return num;
620
621 /* Get the first group for ops::create_table */
622 tcegrp = list_first_entry(&container->group_list,
623 struct tce_iommu_group, next);
624 table_group = iommu_group_get_iommudata(tcegrp->grp);
625 if (!table_group)
626 return -EFAULT;
627
628 if (!(table_group->pgsizes & (1ULL << page_shift)))
629 return -EINVAL;
630
631 if (!table_group->ops->set_window || !table_group->ops->unset_window ||
632 !table_group->ops->get_table_size ||
633 !table_group->ops->create_table)
634 return -EPERM;
635
636 /* Create TCE table */
637 ret = tce_iommu_create_table(container, table_group, num,
638 page_shift, window_size, levels, &tbl);
639 if (ret)
640 return ret;
641
642 BUG_ON(!tbl->it_ops->free);
643
644 /*
645 * Program the table to every group.
646 * Groups have been tested for compatibility at the attach time.
647 */
648 list_for_each_entry(tcegrp, &container->group_list, next) {
649 table_group = iommu_group_get_iommudata(tcegrp->grp);
650
651 ret = table_group->ops->set_window(table_group, num, tbl);
652 if (ret)
653 goto unset_exit;
654 }
655
656 container->tables[num] = tbl;
657
658 /* Return start address assigned by platform in create_table() */
659 *start_addr = tbl->it_offset << tbl->it_page_shift;
660
661 return 0;
662
663unset_exit:
664 list_for_each_entry(tcegrp, &container->group_list, next) {
665 table_group = iommu_group_get_iommudata(tcegrp->grp);
666 table_group->ops->unset_window(table_group, num);
667 }
668 tce_iommu_free_table(tbl);
669
670 return ret;
671}
672
673static long tce_iommu_remove_window(struct tce_container *container,
674 __u64 start_addr)
675{
676 struct iommu_table_group *table_group = NULL;
677 struct iommu_table *tbl;
678 struct tce_iommu_group *tcegrp;
679 int num;
680
681 num = tce_iommu_find_table(container, start_addr, &tbl);
682 if (num < 0)
683 return -EINVAL;
684
685 BUG_ON(!tbl->it_size);
686
687 /* Detach groups from IOMMUs */
688 list_for_each_entry(tcegrp, &container->group_list, next) {
689 table_group = iommu_group_get_iommudata(tcegrp->grp);
690
691 /*
692 * SPAPR TCE IOMMU exposes the default DMA window to
693 * the guest via dma32_window_start/size of
694 * VFIO_IOMMU_SPAPR_TCE_GET_INFO. Some platforms allow
695 * the userspace to remove this window, some do not so
696 * here we check for the platform capability.
697 */
698 if (!table_group->ops || !table_group->ops->unset_window)
699 return -EPERM;
700
701 table_group->ops->unset_window(table_group, num);
702 }
703
704 /* Free table */
705 tce_iommu_clear(container, tbl, tbl->it_offset, tbl->it_size);
706 tce_iommu_free_table(tbl);
707 container->tables[num] = NULL;
708
709 return 0;
710}
711
596static long tce_iommu_ioctl(void *iommu_data, 712static long tce_iommu_ioctl(void *iommu_data,
597 unsigned int cmd, unsigned long arg) 713 unsigned int cmd, unsigned long arg)
598{ 714{
599 struct tce_container *container = iommu_data; 715 struct tce_container *container = iommu_data;
600 unsigned long minsz; 716 unsigned long minsz, ddwsz;
601 long ret; 717 long ret;
602 718
603 switch (cmd) { 719 switch (cmd) {
@@ -641,6 +757,21 @@ static long tce_iommu_ioctl(void *iommu_data,
641 info.dma32_window_start = table_group->tce32_start; 757 info.dma32_window_start = table_group->tce32_start;
642 info.dma32_window_size = table_group->tce32_size; 758 info.dma32_window_size = table_group->tce32_size;
643 info.flags = 0; 759 info.flags = 0;
760 memset(&info.ddw, 0, sizeof(info.ddw));
761
762 if (table_group->max_dynamic_windows_supported &&
763 container->v2) {
764 info.flags |= VFIO_IOMMU_SPAPR_INFO_DDW;
765 info.ddw.pgsizes = table_group->pgsizes;
766 info.ddw.max_dynamic_windows_supported =
767 table_group->max_dynamic_windows_supported;
768 info.ddw.levels = table_group->max_levels;
769 }
770
771 ddwsz = offsetofend(struct vfio_iommu_spapr_tce_info, ddw);
772
773 if (info.argsz >= ddwsz)
774 minsz = ddwsz;
644 775
645 if (copy_to_user((void __user *)arg, &info, minsz)) 776 if (copy_to_user((void __user *)arg, &info, minsz))
646 return -EFAULT; 777 return -EFAULT;
@@ -834,6 +965,69 @@ static long tce_iommu_ioctl(void *iommu_data,
834 return ret; 965 return ret;
835 } 966 }
836 967
968 case VFIO_IOMMU_SPAPR_TCE_CREATE: {
969 struct vfio_iommu_spapr_tce_create create;
970
971 if (!container->v2)
972 break;
973
974 if (!tce_groups_attached(container))
975 return -ENXIO;
976
977 minsz = offsetofend(struct vfio_iommu_spapr_tce_create,
978 start_addr);
979
980 if (copy_from_user(&create, (void __user *)arg, minsz))
981 return -EFAULT;
982
983 if (create.argsz < minsz)
984 return -EINVAL;
985
986 if (create.flags)
987 return -EINVAL;
988
989 mutex_lock(&container->lock);
990
991 ret = tce_iommu_create_window(container, create.page_shift,
992 create.window_size, create.levels,
993 &create.start_addr);
994
995 mutex_unlock(&container->lock);
996
997 if (!ret && copy_to_user((void __user *)arg, &create, minsz))
998 ret = -EFAULT;
999
1000 return ret;
1001 }
1002 case VFIO_IOMMU_SPAPR_TCE_REMOVE: {
1003 struct vfio_iommu_spapr_tce_remove remove;
1004
1005 if (!container->v2)
1006 break;
1007
1008 if (!tce_groups_attached(container))
1009 return -ENXIO;
1010
1011 minsz = offsetofend(struct vfio_iommu_spapr_tce_remove,
1012 start_addr);
1013
1014 if (copy_from_user(&remove, (void __user *)arg, minsz))
1015 return -EFAULT;
1016
1017 if (remove.argsz < minsz)
1018 return -EINVAL;
1019
1020 if (remove.flags)
1021 return -EINVAL;
1022
1023 mutex_lock(&container->lock);
1024
1025 ret = tce_iommu_remove_window(container, remove.start_addr);
1026
1027 mutex_unlock(&container->lock);
1028
1029 return ret;
1030 }
837 } 1031 }
838 1032
839 return -ENOTTY; 1033 return -ENOTTY;