diff options
author | Alexey Kardashevskiy <aik@ozlabs.ru> | 2017-03-22 00:21:50 -0400 |
---|---|---|
committer | Michael Ellerman <mpe@ellerman.id.au> | 2017-03-30 06:42:11 -0400 |
commit | e5afdf9dd515a9446c009f44f99f9bc2f91b89a7 (patch) | |
tree | 21683e5afe296b2a6cf32e22f5471b1da1a7a18e | |
parent | 11edf116e3a6352cfee6b1437d41603c9aff79c9 (diff) |
powerpc/vfio_spapr_tce: Add reference counting to iommu_table
So far iommu_table obejcts were only used in virtual mode and had
a single owner. We are going to change this by implementing in-kernel
acceleration of DMA mapping requests. The proposed acceleration
will handle requests in real mode and KVM will keep references to tables.
This adds a kref to iommu_table and defines new helpers to update it.
This replaces iommu_free_table() with iommu_tce_table_put() and makes
iommu_free_table() static. iommu_tce_table_get() is not used in this patch
but it will be in the following patch.
Since this touches prototypes, this also removes @node_name parameter as
it has never been really useful on powernv and carrying it for
the pseries platform code to iommu_free_table() seems to be quite
useless as well.
This should cause no behavioral change.
Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
Reviewed-by: David Gibson <david@gibson.dropbear.id.au>
Acked-by: Alex Williamson <alex.williamson@redhat.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
-rw-r--r-- | arch/powerpc/include/asm/iommu.h | 5 | ||||
-rw-r--r-- | arch/powerpc/kernel/iommu.c | 27 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/pci-ioda.c | 14 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/pci.c | 1 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/iommu.c | 3 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/vio.c | 2 | ||||
-rw-r--r-- | drivers/vfio/vfio_iommu_spapr_tce.c | 2 |
7 files changed, 37 insertions, 17 deletions
diff --git a/arch/powerpc/include/asm/iommu.h b/arch/powerpc/include/asm/iommu.h index 4554699aec02..d96142572e6d 100644 --- a/arch/powerpc/include/asm/iommu.h +++ b/arch/powerpc/include/asm/iommu.h | |||
@@ -119,6 +119,7 @@ struct iommu_table { | |||
119 | struct list_head it_group_list;/* List of iommu_table_group_link */ | 119 | struct list_head it_group_list;/* List of iommu_table_group_link */ |
120 | unsigned long *it_userspace; /* userspace view of the table */ | 120 | unsigned long *it_userspace; /* userspace view of the table */ |
121 | struct iommu_table_ops *it_ops; | 121 | struct iommu_table_ops *it_ops; |
122 | struct kref it_kref; | ||
122 | }; | 123 | }; |
123 | 124 | ||
124 | #define IOMMU_TABLE_USERSPACE_ENTRY(tbl, entry) \ | 125 | #define IOMMU_TABLE_USERSPACE_ENTRY(tbl, entry) \ |
@@ -151,8 +152,8 @@ static inline void *get_iommu_table_base(struct device *dev) | |||
151 | 152 | ||
152 | extern int dma_iommu_dma_supported(struct device *dev, u64 mask); | 153 | extern int dma_iommu_dma_supported(struct device *dev, u64 mask); |
153 | 154 | ||
154 | /* Frees table for an individual device node */ | 155 | extern struct iommu_table *iommu_tce_table_get(struct iommu_table *tbl); |
155 | extern void iommu_free_table(struct iommu_table *tbl, const char *node_name); | 156 | extern int iommu_tce_table_put(struct iommu_table *tbl); |
156 | 157 | ||
157 | /* Initializes an iommu_table based in values set in the passed-in | 158 | /* Initializes an iommu_table based in values set in the passed-in |
158 | * structure | 159 | * structure |
diff --git a/arch/powerpc/kernel/iommu.c b/arch/powerpc/kernel/iommu.c index a3689fdedd4a..5a3231fedf08 100644 --- a/arch/powerpc/kernel/iommu.c +++ b/arch/powerpc/kernel/iommu.c | |||
@@ -711,13 +711,13 @@ struct iommu_table *iommu_init_table(struct iommu_table *tbl, int nid) | |||
711 | return tbl; | 711 | return tbl; |
712 | } | 712 | } |
713 | 713 | ||
714 | void iommu_free_table(struct iommu_table *tbl, const char *node_name) | 714 | static void iommu_table_free(struct kref *kref) |
715 | { | 715 | { |
716 | unsigned long bitmap_sz; | 716 | unsigned long bitmap_sz; |
717 | unsigned int order; | 717 | unsigned int order; |
718 | struct iommu_table *tbl; | ||
718 | 719 | ||
719 | if (!tbl) | 720 | tbl = container_of(kref, struct iommu_table, it_kref); |
720 | return; | ||
721 | 721 | ||
722 | if (tbl->it_ops->free) | 722 | if (tbl->it_ops->free) |
723 | tbl->it_ops->free(tbl); | 723 | tbl->it_ops->free(tbl); |
@@ -736,7 +736,7 @@ void iommu_free_table(struct iommu_table *tbl, const char *node_name) | |||
736 | 736 | ||
737 | /* verify that table contains no entries */ | 737 | /* verify that table contains no entries */ |
738 | if (!bitmap_empty(tbl->it_map, tbl->it_size)) | 738 | if (!bitmap_empty(tbl->it_map, tbl->it_size)) |
739 | pr_warn("%s: Unexpected TCEs for %s\n", __func__, node_name); | 739 | pr_warn("%s: Unexpected TCEs\n", __func__); |
740 | 740 | ||
741 | /* calculate bitmap size in bytes */ | 741 | /* calculate bitmap size in bytes */ |
742 | bitmap_sz = BITS_TO_LONGS(tbl->it_size) * sizeof(unsigned long); | 742 | bitmap_sz = BITS_TO_LONGS(tbl->it_size) * sizeof(unsigned long); |
@@ -748,7 +748,24 @@ void iommu_free_table(struct iommu_table *tbl, const char *node_name) | |||
748 | /* free table */ | 748 | /* free table */ |
749 | kfree(tbl); | 749 | kfree(tbl); |
750 | } | 750 | } |
751 | EXPORT_SYMBOL_GPL(iommu_free_table); | 751 | |
752 | struct iommu_table *iommu_tce_table_get(struct iommu_table *tbl) | ||
753 | { | ||
754 | if (kref_get_unless_zero(&tbl->it_kref)) | ||
755 | return tbl; | ||
756 | |||
757 | return NULL; | ||
758 | } | ||
759 | EXPORT_SYMBOL_GPL(iommu_tce_table_get); | ||
760 | |||
761 | int iommu_tce_table_put(struct iommu_table *tbl) | ||
762 | { | ||
763 | if (WARN_ON(!tbl)) | ||
764 | return 0; | ||
765 | |||
766 | return kref_put(&tbl->it_kref, iommu_table_free); | ||
767 | } | ||
768 | EXPORT_SYMBOL_GPL(iommu_tce_table_put); | ||
752 | 769 | ||
753 | /* Creates TCEs for a user provided buffer. The user buffer must be | 770 | /* Creates TCEs for a user provided buffer. The user buffer must be |
754 | * contiguous real kernel storage (not vmalloc). The address passed here | 771 | * contiguous real kernel storage (not vmalloc). The address passed here |
diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c index 5dae54cb11e3..ee4cdb5b893f 100644 --- a/arch/powerpc/platforms/powernv/pci-ioda.c +++ b/arch/powerpc/platforms/powernv/pci-ioda.c | |||
@@ -1424,7 +1424,7 @@ static void pnv_pci_ioda2_release_dma_pe(struct pci_dev *dev, struct pnv_ioda_pe | |||
1424 | iommu_group_put(pe->table_group.group); | 1424 | iommu_group_put(pe->table_group.group); |
1425 | BUG_ON(pe->table_group.group); | 1425 | BUG_ON(pe->table_group.group); |
1426 | } | 1426 | } |
1427 | iommu_free_table(tbl, of_node_full_name(dev->dev.of_node)); | 1427 | iommu_tce_table_put(tbl); |
1428 | } | 1428 | } |
1429 | 1429 | ||
1430 | static void pnv_ioda_release_vf_PE(struct pci_dev *pdev) | 1430 | static void pnv_ioda_release_vf_PE(struct pci_dev *pdev) |
@@ -2225,7 +2225,7 @@ found: | |||
2225 | __free_pages(tce_mem, get_order(tce32_segsz * segs)); | 2225 | __free_pages(tce_mem, get_order(tce32_segsz * segs)); |
2226 | if (tbl) { | 2226 | if (tbl) { |
2227 | pnv_pci_unlink_table_and_group(tbl, &pe->table_group); | 2227 | pnv_pci_unlink_table_and_group(tbl, &pe->table_group); |
2228 | iommu_free_table(tbl, "pnv"); | 2228 | iommu_tce_table_put(tbl); |
2229 | } | 2229 | } |
2230 | } | 2230 | } |
2231 | 2231 | ||
@@ -2321,7 +2321,7 @@ static long pnv_pci_ioda2_create_table(struct iommu_table_group *table_group, | |||
2321 | bus_offset, page_shift, window_size, | 2321 | bus_offset, page_shift, window_size, |
2322 | levels, tbl); | 2322 | levels, tbl); |
2323 | if (ret) { | 2323 | if (ret) { |
2324 | iommu_free_table(tbl, "pnv"); | 2324 | iommu_tce_table_put(tbl); |
2325 | return ret; | 2325 | return ret; |
2326 | } | 2326 | } |
2327 | 2327 | ||
@@ -2365,7 +2365,7 @@ static long pnv_pci_ioda2_setup_default_config(struct pnv_ioda_pe *pe) | |||
2365 | if (rc) { | 2365 | if (rc) { |
2366 | pe_err(pe, "Failed to configure 32-bit TCE table, err %ld\n", | 2366 | pe_err(pe, "Failed to configure 32-bit TCE table, err %ld\n", |
2367 | rc); | 2367 | rc); |
2368 | iommu_free_table(tbl, ""); | 2368 | iommu_tce_table_put(tbl); |
2369 | return rc; | 2369 | return rc; |
2370 | } | 2370 | } |
2371 | 2371 | ||
@@ -2453,7 +2453,7 @@ static void pnv_ioda2_take_ownership(struct iommu_table_group *table_group) | |||
2453 | pnv_pci_ioda2_unset_window(&pe->table_group, 0); | 2453 | pnv_pci_ioda2_unset_window(&pe->table_group, 0); |
2454 | if (pe->pbus) | 2454 | if (pe->pbus) |
2455 | pnv_ioda_setup_bus_dma(pe, pe->pbus, false); | 2455 | pnv_ioda_setup_bus_dma(pe, pe->pbus, false); |
2456 | iommu_free_table(tbl, "pnv"); | 2456 | iommu_tce_table_put(tbl); |
2457 | } | 2457 | } |
2458 | 2458 | ||
2459 | static void pnv_ioda2_release_ownership(struct iommu_table_group *table_group) | 2459 | static void pnv_ioda2_release_ownership(struct iommu_table_group *table_group) |
@@ -3428,7 +3428,7 @@ static void pnv_pci_ioda1_release_pe_dma(struct pnv_ioda_pe *pe) | |||
3428 | } | 3428 | } |
3429 | 3429 | ||
3430 | free_pages(tbl->it_base, get_order(tbl->it_size << 3)); | 3430 | free_pages(tbl->it_base, get_order(tbl->it_size << 3)); |
3431 | iommu_free_table(tbl, "pnv"); | 3431 | iommu_tce_table_put(tbl); |
3432 | } | 3432 | } |
3433 | 3433 | ||
3434 | static void pnv_pci_ioda2_release_pe_dma(struct pnv_ioda_pe *pe) | 3434 | static void pnv_pci_ioda2_release_pe_dma(struct pnv_ioda_pe *pe) |
@@ -3455,7 +3455,7 @@ static void pnv_pci_ioda2_release_pe_dma(struct pnv_ioda_pe *pe) | |||
3455 | } | 3455 | } |
3456 | 3456 | ||
3457 | pnv_pci_ioda2_table_free_pages(tbl); | 3457 | pnv_pci_ioda2_table_free_pages(tbl); |
3458 | iommu_free_table(tbl, "pnv"); | 3458 | iommu_tce_table_put(tbl); |
3459 | } | 3459 | } |
3460 | 3460 | ||
3461 | static void pnv_ioda_free_pe_seg(struct pnv_ioda_pe *pe, | 3461 | static void pnv_ioda_free_pe_seg(struct pnv_ioda_pe *pe, |
diff --git a/arch/powerpc/platforms/powernv/pci.c b/arch/powerpc/platforms/powernv/pci.c index eb835e977e33..204a829ff506 100644 --- a/arch/powerpc/platforms/powernv/pci.c +++ b/arch/powerpc/platforms/powernv/pci.c | |||
@@ -767,6 +767,7 @@ struct iommu_table *pnv_pci_table_alloc(int nid) | |||
767 | 767 | ||
768 | tbl = kzalloc_node(sizeof(struct iommu_table), GFP_KERNEL, nid); | 768 | tbl = kzalloc_node(sizeof(struct iommu_table), GFP_KERNEL, nid); |
769 | INIT_LIST_HEAD_RCU(&tbl->it_group_list); | 769 | INIT_LIST_HEAD_RCU(&tbl->it_group_list); |
770 | kref_init(&tbl->it_kref); | ||
770 | 771 | ||
771 | return tbl; | 772 | return tbl; |
772 | } | 773 | } |
diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c index 4d757eaa46bf..7ce5db209abf 100644 --- a/arch/powerpc/platforms/pseries/iommu.c +++ b/arch/powerpc/platforms/pseries/iommu.c | |||
@@ -74,6 +74,7 @@ static struct iommu_table_group *iommu_pseries_alloc_group(int node) | |||
74 | goto fail_exit; | 74 | goto fail_exit; |
75 | 75 | ||
76 | INIT_LIST_HEAD_RCU(&tbl->it_group_list); | 76 | INIT_LIST_HEAD_RCU(&tbl->it_group_list); |
77 | kref_init(&tbl->it_kref); | ||
77 | tgl->table_group = table_group; | 78 | tgl->table_group = table_group; |
78 | list_add_rcu(&tgl->next, &tbl->it_group_list); | 79 | list_add_rcu(&tgl->next, &tbl->it_group_list); |
79 | 80 | ||
@@ -115,7 +116,7 @@ static void iommu_pseries_free_group(struct iommu_table_group *table_group, | |||
115 | BUG_ON(table_group->group); | 116 | BUG_ON(table_group->group); |
116 | } | 117 | } |
117 | #endif | 118 | #endif |
118 | iommu_free_table(tbl, node_name); | 119 | iommu_tce_table_put(tbl); |
119 | 120 | ||
120 | kfree(table_group); | 121 | kfree(table_group); |
121 | } | 122 | } |
diff --git a/arch/powerpc/platforms/pseries/vio.c b/arch/powerpc/platforms/pseries/vio.c index 720493932486..28b09fd797ec 100644 --- a/arch/powerpc/platforms/pseries/vio.c +++ b/arch/powerpc/platforms/pseries/vio.c | |||
@@ -1318,7 +1318,7 @@ static void vio_dev_release(struct device *dev) | |||
1318 | struct iommu_table *tbl = get_iommu_table_base(dev); | 1318 | struct iommu_table *tbl = get_iommu_table_base(dev); |
1319 | 1319 | ||
1320 | if (tbl) | 1320 | if (tbl) |
1321 | iommu_free_table(tbl, of_node_full_name(dev->of_node)); | 1321 | iommu_tce_table_put(tbl); |
1322 | of_node_put(dev->of_node); | 1322 | of_node_put(dev->of_node); |
1323 | kfree(to_vio_dev(dev)); | 1323 | kfree(to_vio_dev(dev)); |
1324 | } | 1324 | } |
diff --git a/drivers/vfio/vfio_iommu_spapr_tce.c b/drivers/vfio/vfio_iommu_spapr_tce.c index fbec7348a7e5..8031d3a55a17 100644 --- a/drivers/vfio/vfio_iommu_spapr_tce.c +++ b/drivers/vfio/vfio_iommu_spapr_tce.c | |||
@@ -680,7 +680,7 @@ static void tce_iommu_free_table(struct tce_container *container, | |||
680 | unsigned long pages = tbl->it_allocated_size >> PAGE_SHIFT; | 680 | unsigned long pages = tbl->it_allocated_size >> PAGE_SHIFT; |
681 | 681 | ||
682 | tce_iommu_userspace_view_free(tbl, container->mm); | 682 | tce_iommu_userspace_view_free(tbl, container->mm); |
683 | iommu_free_table(tbl, ""); | 683 | iommu_tce_table_put(tbl); |
684 | decrement_locked_vm(container->mm, pages); | 684 | decrement_locked_vm(container->mm, pages); |
685 | } | 685 | } |
686 | 686 | ||