diff options
Diffstat (limited to 'drivers/vfio')
-rw-r--r-- | drivers/vfio/vfio_iommu_spapr_tce.c | 88 |
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 | ||
336 | static 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 | |||
367 | static 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 | |||
336 | static long tce_iommu_ioctl(void *iommu_data, | 375 | static 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, | |||
546 | static void tce_iommu_release_ownership_ddw(struct tce_container *container, | 585 | static 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 | ||
552 | static long tce_iommu_take_ownership_ddw(struct tce_container *container, | 611 | static 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 | ||
560 | static int tce_iommu_attach_group(void *iommu_data, | 646 | static int tce_iommu_attach_group(void *iommu_data, |