summaryrefslogtreecommitdiffstats
path: root/drivers/vfio
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/vfio')
-rw-r--r--drivers/vfio/vfio_iommu_spapr_tce.c88
1 files changed, 87 insertions, 1 deletions
diff --git a/drivers/vfio/vfio_iommu_spapr_tce.c b/drivers/vfio/vfio_iommu_spapr_tce.c
index 6d919eb4251f..203caacf2242 100644
--- a/drivers/vfio/vfio_iommu_spapr_tce.c
+++ b/drivers/vfio/vfio_iommu_spapr_tce.c
@@ -333,6 +333,45 @@ static long tce_iommu_build(struct tce_container *container,
333 return ret; 333 return ret;
334} 334}
335 335
336static long tce_iommu_create_table(struct tce_container *container,
337 struct iommu_table_group *table_group,
338 int num,
339 __u32 page_shift,
340 __u64 window_size,
341 __u32 levels,
342 struct iommu_table **ptbl)
343{
344 long ret, table_size;
345
346 table_size = table_group->ops->get_table_size(page_shift, window_size,
347 levels);
348 if (!table_size)
349 return -EINVAL;
350
351 ret = try_increment_locked_vm(table_size >> PAGE_SHIFT);
352 if (ret)
353 return ret;
354
355 ret = table_group->ops->create_table(table_group, num,
356 page_shift, window_size, levels, ptbl);
357
358 WARN_ON(!ret && !(*ptbl)->it_ops->free);
359 WARN_ON(!ret && ((*ptbl)->it_allocated_size != table_size));
360
361 if (ret)
362 decrement_locked_vm(table_size >> PAGE_SHIFT);
363
364 return ret;
365}
366
367static void tce_iommu_free_table(struct iommu_table *tbl)
368{
369 unsigned long pages = tbl->it_allocated_size >> PAGE_SHIFT;
370
371 tbl->it_ops->free(tbl);
372 decrement_locked_vm(pages);
373}
374
336static long tce_iommu_ioctl(void *iommu_data, 375static long tce_iommu_ioctl(void *iommu_data,
337 unsigned int cmd, unsigned long arg) 376 unsigned int cmd, unsigned long arg)
338{ 377{
@@ -546,15 +585,62 @@ static int tce_iommu_take_ownership(struct tce_container *container,
546static void tce_iommu_release_ownership_ddw(struct tce_container *container, 585static void tce_iommu_release_ownership_ddw(struct tce_container *container,
547 struct iommu_table_group *table_group) 586 struct iommu_table_group *table_group)
548{ 587{
588 long i;
589
590 if (!table_group->ops->unset_window) {
591 WARN_ON_ONCE(1);
592 return;
593 }
594
595 for (i = 0; i < IOMMU_TABLE_GROUP_MAX_TABLES; ++i) {
596 /* Store table pointer as unset_window resets it */
597 struct iommu_table *tbl = table_group->tables[i];
598
599 if (!tbl)
600 continue;
601
602 table_group->ops->unset_window(table_group, i);
603 tce_iommu_clear(container, tbl,
604 tbl->it_offset, tbl->it_size);
605 tce_iommu_free_table(tbl);
606 }
607
549 table_group->ops->release_ownership(table_group); 608 table_group->ops->release_ownership(table_group);
550} 609}
551 610
552static long tce_iommu_take_ownership_ddw(struct tce_container *container, 611static long tce_iommu_take_ownership_ddw(struct tce_container *container,
553 struct iommu_table_group *table_group) 612 struct iommu_table_group *table_group)
554{ 613{
614 long ret;
615 struct iommu_table *tbl = NULL;
616
617 if (!table_group->ops->create_table || !table_group->ops->set_window ||
618 !table_group->ops->release_ownership) {
619 WARN_ON_ONCE(1);
620 return -EFAULT;
621 }
622
555 table_group->ops->take_ownership(table_group); 623 table_group->ops->take_ownership(table_group);
556 624
557 return 0; 625 ret = tce_iommu_create_table(container,
626 table_group,
627 0, /* window number */
628 IOMMU_PAGE_SHIFT_4K,
629 table_group->tce32_size,
630 1, /* default levels */
631 &tbl);
632 if (!ret) {
633 ret = table_group->ops->set_window(table_group, 0, tbl);
634 if (ret)
635 tce_iommu_free_table(tbl);
636 else
637 table_group->tables[0] = tbl;
638 }
639
640 if (ret)
641 table_group->ops->release_ownership(table_group);
642
643 return ret;
558} 644}
559 645
560static int tce_iommu_attach_group(void *iommu_data, 646static int tce_iommu_attach_group(void *iommu_data,