diff options
-rw-r--r-- | drivers/xen/Kconfig | 14 | ||||
-rw-r--r-- | drivers/xen/grant-table.c | 97 | ||||
-rw-r--r-- | include/xen/grant_table.h | 18 |
3 files changed, 129 insertions, 0 deletions
diff --git a/drivers/xen/Kconfig b/drivers/xen/Kconfig index e5d0c28372ea..75e5c40f80a5 100644 --- a/drivers/xen/Kconfig +++ b/drivers/xen/Kconfig | |||
@@ -161,6 +161,20 @@ config XEN_GRANT_DEV_ALLOC | |||
161 | to other domains. This can be used to implement frontend drivers | 161 | to other domains. This can be used to implement frontend drivers |
162 | or as part of an inter-domain shared memory channel. | 162 | or as part of an inter-domain shared memory channel. |
163 | 163 | ||
164 | config XEN_GRANT_DMA_ALLOC | ||
165 | bool "Allow allocating DMA capable buffers with grant reference module" | ||
166 | depends on XEN && HAS_DMA | ||
167 | help | ||
168 | Extends grant table module API to allow allocating DMA capable | ||
169 | buffers and mapping foreign grant references on top of it. | ||
170 | The resulting buffer is similar to one allocated by the balloon | ||
171 | driver in that proper memory reservation is made by | ||
172 | ({increase|decrease}_reservation and VA mappings are updated if | ||
173 | needed). | ||
174 | This is useful for sharing foreign buffers with HW drivers which | ||
175 | cannot work with scattered buffers provided by the balloon driver, | ||
176 | but require DMAable memory instead. | ||
177 | |||
164 | config SWIOTLB_XEN | 178 | config SWIOTLB_XEN |
165 | def_bool y | 179 | def_bool y |
166 | select SWIOTLB | 180 | select SWIOTLB |
diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c index bb4840653bf2..7bafa703a992 100644 --- a/drivers/xen/grant-table.c +++ b/drivers/xen/grant-table.c | |||
@@ -45,6 +45,9 @@ | |||
45 | #include <linux/workqueue.h> | 45 | #include <linux/workqueue.h> |
46 | #include <linux/ratelimit.h> | 46 | #include <linux/ratelimit.h> |
47 | #include <linux/moduleparam.h> | 47 | #include <linux/moduleparam.h> |
48 | #ifdef CONFIG_XEN_GRANT_DMA_ALLOC | ||
49 | #include <linux/dma-mapping.h> | ||
50 | #endif | ||
48 | 51 | ||
49 | #include <xen/xen.h> | 52 | #include <xen/xen.h> |
50 | #include <xen/interface/xen.h> | 53 | #include <xen/interface/xen.h> |
@@ -57,6 +60,7 @@ | |||
57 | #ifdef CONFIG_X86 | 60 | #ifdef CONFIG_X86 |
58 | #include <asm/xen/cpuid.h> | 61 | #include <asm/xen/cpuid.h> |
59 | #endif | 62 | #endif |
63 | #include <xen/mem-reservation.h> | ||
60 | #include <asm/xen/hypercall.h> | 64 | #include <asm/xen/hypercall.h> |
61 | #include <asm/xen/interface.h> | 65 | #include <asm/xen/interface.h> |
62 | 66 | ||
@@ -838,6 +842,99 @@ void gnttab_free_pages(int nr_pages, struct page **pages) | |||
838 | } | 842 | } |
839 | EXPORT_SYMBOL_GPL(gnttab_free_pages); | 843 | EXPORT_SYMBOL_GPL(gnttab_free_pages); |
840 | 844 | ||
845 | #ifdef CONFIG_XEN_GRANT_DMA_ALLOC | ||
846 | /** | ||
847 | * gnttab_dma_alloc_pages - alloc DMAable pages suitable for grant mapping into | ||
848 | * @args: arguments to the function | ||
849 | */ | ||
850 | int gnttab_dma_alloc_pages(struct gnttab_dma_alloc_args *args) | ||
851 | { | ||
852 | unsigned long pfn, start_pfn; | ||
853 | size_t size; | ||
854 | int i, ret; | ||
855 | |||
856 | size = args->nr_pages << PAGE_SHIFT; | ||
857 | if (args->coherent) | ||
858 | args->vaddr = dma_alloc_coherent(args->dev, size, | ||
859 | &args->dev_bus_addr, | ||
860 | GFP_KERNEL | __GFP_NOWARN); | ||
861 | else | ||
862 | args->vaddr = dma_alloc_wc(args->dev, size, | ||
863 | &args->dev_bus_addr, | ||
864 | GFP_KERNEL | __GFP_NOWARN); | ||
865 | if (!args->vaddr) { | ||
866 | pr_debug("Failed to allocate DMA buffer of size %zu\n", size); | ||
867 | return -ENOMEM; | ||
868 | } | ||
869 | |||
870 | start_pfn = __phys_to_pfn(args->dev_bus_addr); | ||
871 | for (pfn = start_pfn, i = 0; pfn < start_pfn + args->nr_pages; | ||
872 | pfn++, i++) { | ||
873 | struct page *page = pfn_to_page(pfn); | ||
874 | |||
875 | args->pages[i] = page; | ||
876 | args->frames[i] = xen_page_to_gfn(page); | ||
877 | xenmem_reservation_scrub_page(page); | ||
878 | } | ||
879 | |||
880 | xenmem_reservation_va_mapping_reset(args->nr_pages, args->pages); | ||
881 | |||
882 | ret = xenmem_reservation_decrease(args->nr_pages, args->frames); | ||
883 | if (ret != args->nr_pages) { | ||
884 | pr_debug("Failed to decrease reservation for DMA buffer\n"); | ||
885 | ret = -EFAULT; | ||
886 | goto fail; | ||
887 | } | ||
888 | |||
889 | ret = gnttab_pages_set_private(args->nr_pages, args->pages); | ||
890 | if (ret < 0) | ||
891 | goto fail; | ||
892 | |||
893 | return 0; | ||
894 | |||
895 | fail: | ||
896 | gnttab_dma_free_pages(args); | ||
897 | return ret; | ||
898 | } | ||
899 | EXPORT_SYMBOL_GPL(gnttab_dma_alloc_pages); | ||
900 | |||
901 | /** | ||
902 | * gnttab_dma_free_pages - free DMAable pages | ||
903 | * @args: arguments to the function | ||
904 | */ | ||
905 | int gnttab_dma_free_pages(struct gnttab_dma_alloc_args *args) | ||
906 | { | ||
907 | size_t size; | ||
908 | int i, ret; | ||
909 | |||
910 | gnttab_pages_clear_private(args->nr_pages, args->pages); | ||
911 | |||
912 | for (i = 0; i < args->nr_pages; i++) | ||
913 | args->frames[i] = page_to_xen_pfn(args->pages[i]); | ||
914 | |||
915 | ret = xenmem_reservation_increase(args->nr_pages, args->frames); | ||
916 | if (ret != args->nr_pages) { | ||
917 | pr_debug("Failed to decrease reservation for DMA buffer\n"); | ||
918 | ret = -EFAULT; | ||
919 | } else { | ||
920 | ret = 0; | ||
921 | } | ||
922 | |||
923 | xenmem_reservation_va_mapping_update(args->nr_pages, args->pages, | ||
924 | args->frames); | ||
925 | |||
926 | size = args->nr_pages << PAGE_SHIFT; | ||
927 | if (args->coherent) | ||
928 | dma_free_coherent(args->dev, size, | ||
929 | args->vaddr, args->dev_bus_addr); | ||
930 | else | ||
931 | dma_free_wc(args->dev, size, | ||
932 | args->vaddr, args->dev_bus_addr); | ||
933 | return ret; | ||
934 | } | ||
935 | EXPORT_SYMBOL_GPL(gnttab_dma_free_pages); | ||
936 | #endif | ||
937 | |||
841 | /* Handling of paged out grant targets (GNTST_eagain) */ | 938 | /* Handling of paged out grant targets (GNTST_eagain) */ |
842 | #define MAX_DELAY 256 | 939 | #define MAX_DELAY 256 |
843 | static inline void | 940 | static inline void |
diff --git a/include/xen/grant_table.h b/include/xen/grant_table.h index de03f2542bb7..9bc5bc07d4d3 100644 --- a/include/xen/grant_table.h +++ b/include/xen/grant_table.h | |||
@@ -198,6 +198,24 @@ void gnttab_free_auto_xlat_frames(void); | |||
198 | int gnttab_alloc_pages(int nr_pages, struct page **pages); | 198 | int gnttab_alloc_pages(int nr_pages, struct page **pages); |
199 | void gnttab_free_pages(int nr_pages, struct page **pages); | 199 | void gnttab_free_pages(int nr_pages, struct page **pages); |
200 | 200 | ||
201 | #ifdef CONFIG_XEN_GRANT_DMA_ALLOC | ||
202 | struct gnttab_dma_alloc_args { | ||
203 | /* Device for which DMA memory will be/was allocated. */ | ||
204 | struct device *dev; | ||
205 | /* If set then DMA buffer is coherent and write-combine otherwise. */ | ||
206 | bool coherent; | ||
207 | |||
208 | int nr_pages; | ||
209 | struct page **pages; | ||
210 | xen_pfn_t *frames; | ||
211 | void *vaddr; | ||
212 | dma_addr_t dev_bus_addr; | ||
213 | }; | ||
214 | |||
215 | int gnttab_dma_alloc_pages(struct gnttab_dma_alloc_args *args); | ||
216 | int gnttab_dma_free_pages(struct gnttab_dma_alloc_args *args); | ||
217 | #endif | ||
218 | |||
201 | int gnttab_pages_set_private(int nr_pages, struct page **pages); | 219 | int gnttab_pages_set_private(int nr_pages, struct page **pages); |
202 | void gnttab_pages_clear_private(int nr_pages, struct page **pages); | 220 | void gnttab_pages_clear_private(int nr_pages, struct page **pages); |
203 | 221 | ||