Skip to content

Commit 9bdc730

Browse files
Oleksandr AndrushchenkoBoris Ostrovsky
Oleksandr Andrushchenko
authored and
Boris Ostrovsky
committed
xen/grant-table: Allow allocating buffers suitable for DMA
Extend grant table module API to allow allocating buffers that can be used for DMA operations and mapping foreign grant references on top of those. The resulting buffer is similar to the one allocated by the balloon driver in that proper memory reservation is made by ({increase|decrease}_reservation and VA mappings are updated if needed). This is useful for sharing foreign buffers with HW drivers which cannot work with scattered buffers provided by the balloon driver, but require DMAable memory instead. Signed-off-by: Oleksandr Andrushchenko <[email protected]> Reviewed-by: Boris Ostrovsky <[email protected]> Signed-off-by: Boris Ostrovsky <[email protected]>
1 parent ae4c51a commit 9bdc730

File tree

3 files changed

+129
-0
lines changed

3 files changed

+129
-0
lines changed

drivers/xen/Kconfig

+14
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,20 @@ config XEN_GRANT_DEV_ALLOC
161161
to other domains. This can be used to implement frontend drivers
162162
or as part of an inter-domain shared memory channel.
163163

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+
164178
config SWIOTLB_XEN
165179
def_bool y
166180
select SWIOTLB

drivers/xen/grant-table.c

+97
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@
4545
#include <linux/workqueue.h>
4646
#include <linux/ratelimit.h>
4747
#include <linux/moduleparam.h>
48+
#ifdef CONFIG_XEN_GRANT_DMA_ALLOC
49+
#include <linux/dma-mapping.h>
50+
#endif
4851

4952
#include <xen/xen.h>
5053
#include <xen/interface/xen.h>
@@ -57,6 +60,7 @@
5760
#ifdef CONFIG_X86
5861
#include <asm/xen/cpuid.h>
5962
#endif
63+
#include <xen/mem-reservation.h>
6064
#include <asm/xen/hypercall.h>
6165
#include <asm/xen/interface.h>
6266

@@ -838,6 +842,99 @@ void gnttab_free_pages(int nr_pages, struct page **pages)
838842
}
839843
EXPORT_SYMBOL_GPL(gnttab_free_pages);
840844

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+
841938
/* Handling of paged out grant targets (GNTST_eagain) */
842939
#define MAX_DELAY 256
843940
static inline void

include/xen/grant_table.h

+18
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,24 @@ void gnttab_free_auto_xlat_frames(void);
198198
int gnttab_alloc_pages(int nr_pages, struct page **pages);
199199
void gnttab_free_pages(int nr_pages, struct page **pages);
200200

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+
201219
int gnttab_pages_set_private(int nr_pages, struct page **pages);
202220
void gnttab_pages_clear_private(int nr_pages, struct page **pages);
203221

0 commit comments

Comments
 (0)