dma: use per-device dma_arenas for dedicated devices This commit adds the plumbing for picking a dma_arena for a device, instead of assuming the default physical pages arena. Future commits will add the ability to create and set a process's user_pages arena. Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
diff --git a/kern/include/dma.h b/kern/include/dma.h index 8343dec..6ed7db6 100644 --- a/kern/include/dma.h +++ b/kern/include/dma.h
@@ -9,6 +9,7 @@ #pragma once #include <arena.h> +#include <arch/pci.h> typedef physaddr_t dma_addr_t; @@ -20,6 +21,7 @@ /* Default arena: basically just physical pages */ extern struct dma_arena dma_phys_pages; +struct dma_arena *dev_to_dma_arena(struct device *d); void dma_arena_init(void); @@ -31,7 +33,7 @@ size_t size); /* Compatible with Linux's DMA pool */ -struct dma_pool *dma_pool_create(const char *name, void *dev, +struct dma_pool *dma_pool_create(const char *name, struct device *dev, size_t size, size_t align, size_t allocation); void dma_pool_destroy(struct dma_pool *pool); void *dma_pool_alloc(struct dma_pool *pool, int mem_flags, dma_addr_t *handle);
diff --git a/kern/include/env.h b/kern/include/env.h index dba5378..3689106 100644 --- a/kern/include/env.h +++ b/kern/include/env.h
@@ -22,6 +22,7 @@ #include <ns.h> #include <arch/vmm/vmm.h> #include <arch/pci.h> +#include <dma.h> TAILQ_HEAD(vcore_tailq, vcore); /* 'struct proc_list' declared in sched.h (not ideal...) */ @@ -121,6 +122,7 @@ struct strace *strace; struct pcidev_tq pci_devices; /* for device passthru */ TAILQ_ENTRY(proc) iommu_link; + struct dma_arena *user_pages; }; /* Til we remove all Env references */
diff --git a/kern/include/linux_compat.h b/kern/include/linux_compat.h index bc92122..b331334 100644 --- a/kern/include/linux_compat.h +++ b/kern/include/linux_compat.h
@@ -126,19 +126,19 @@ static inline void *dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, int gfp) { - return dma_arena_zalloc(&dma_phys_pages, size, dma_handle, gfp); + return dma_arena_zalloc(dev_to_dma_arena(dev), size, dma_handle, gfp); } static inline void *dma_zalloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, int gfp) { - return dma_arena_zalloc(&dma_phys_pages, size, dma_handle, gfp); + return dma_arena_zalloc(dev_to_dma_arena(dev), size, dma_handle, gfp); } static inline void dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, dma_addr_t dma_handle) { - dma_arena_free(&dma_phys_pages, cpu_addr, dma_handle, size); + dma_arena_free(dev_to_dma_arena(dev), cpu_addr, dma_handle, size); } #define dma_map_single(dev, addr, size, direction) \
diff --git a/kern/src/dma.c b/kern/src/dma.c index fcad42d..f4293e4 100644 --- a/kern/src/dma.c +++ b/kern/src/dma.c
@@ -54,6 +54,23 @@ * xalloc, and maybe more flexibility. */ struct dma_arena dma_phys_pages; +struct dma_arena *dev_to_dma_arena(struct device *d) +{ + struct pci_device *pdev; + + if (!d) + return &dma_phys_pages; + pdev = container_of(d, struct pci_device, linux_dev); + if (!pdev->proc_owner) + return &dma_phys_pages; + if (!pdev->proc_owner->user_pages) { + warn("Proc %d owns a device, but has no user_pages!", + pdev->proc_owner->pid); + return &dma_phys_pages; + } + return pdev->proc_owner->user_pages; +} + static void *dma_phys_a(struct arena *a, size_t amt, int flags) { return (void*)PADDR(arena_alloc(a, amt, flags)); @@ -147,7 +164,7 @@ struct dma_arena *source; }; -struct dma_pool *dma_pool_create(const char *name, void *dev, +struct dma_pool *dma_pool_create(const char *name, struct device *dev, size_t size, size_t align, size_t boundary) { struct dma_pool *dp; @@ -164,8 +181,7 @@ align = size; } dp = kzmalloc(sizeof(struct dma_pool), MEM_WAIT); - /* TODO: this will be device specific. Assuming the default. */ - dp->source = &dma_phys_pages; + dp->source = dev_to_dma_arena(dev); /* We're sourcing directly from the dma_arena's arena. */ __kmem_cache_create(&dp->kc, name, size, align, KMC_NOTOUCH, &dp->source->arena, NULL, NULL, NULL);