diff options
author | Alexey Kardashevskiy <aik@ozlabs.ru> | 2015-06-05 02:35:26 -0400 |
---|---|---|
committer | Michael Ellerman <mpe@ellerman.id.au> | 2015-06-11 01:16:55 -0400 |
commit | e633bc86a922468a82300eef5b9802e17be5e23d (patch) | |
tree | ccbe3f4a7dc46dc8caed2aff7733548ba9d60c30 /drivers/vfio | |
parent | 2157e7b82f3b81f57bd80cd67cef09ef26e5f74c (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.c | 196 |
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 | ||
214 | static 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 | |||
214 | static int tce_iommu_enable(struct tce_container *container) | 226 | static 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 | ||
608 | static 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 | |||
663 | unset_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 | |||
673 | static 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 | |||
596 | static long tce_iommu_ioctl(void *iommu_data, | 712 | static 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; |