⬢ DragonFlyBSD Kernel Audit
DF-5227 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/drm/linux_dma.c
+++ b/sys/dev/drm/linux_dma.c
@@ -26,51 +26,35 @@
 
 #include <linux/dma-mapping.h>
 
+#include <sys/malloc.h>
+#include <vm/vm_kern.h>
 #include <vm/vm_extern.h>
 
 void *
 dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle,
     gfp_t flag)
 {
-#if 1
-	vm_paddr_t high;
-	size_t align;
 	void *mem;
 
-#if 0 /* XXX swildner */
-	if (dev->dma_mask)
-		high = *dev->dma_mask;
-	else
-#endif
-		high = BUS_SPACE_MAXADDR_32BIT;
-	align = PAGE_SIZE << get_order(size);
-	mem = (void *)kmem_alloc_contig(size, 0, high, align);
+	/*
+	 * The Linux DMA API requires dma_alloc_coherent() to return
+	 * zeroed memory (all in-tree callers currently carry manual
+	 * memset() workarounds for this).  kmem_alloc_contig() cannot
+	 * zero, so use contigmalloc_map() with M_ZERO.
+	 *
+	 * dma_free_coherent() must release the wired pages and remove
+	 * the pmap_kenter() mappings: bare kmem_free() orphans the
+	 * kernel PTEs and leaks every physical page (they stay wired
+	 * forever).  The matching free is contigfree().
+	 */
+	mem = contigmalloc_map(size, M_DEVBUF, M_NOWAIT | M_ZERO, 0,
+	    BUS_SPACE_MAXADDR_32BIT, PAGE_SIZE << get_order(size), 0ul,
+	    kernel_map);
 	if (mem)
 		*dma_handle = vtophys(mem);
 	else
 		*dma_handle = 0;
 	return (mem);
-#endif
-
-#if 0
-	size_t align = PAGE_SIZE << get_order(size);
-	int error;
-	drm_dma_handle_t *dmah;
-
-	dmah = kmalloc(sizeof(drm_dma_handle_t), M_DRM, M_ZERO | M_NOWAIT);
-	if (dmah == NULL)
-		return NULL;
-
-	error = bus_dmamem_coherent_any(
-		bus_get_dma_tag(dev->dev->bsddev),
-		align, size, BUS_DMA_WAITOK | BUS_DMA_ZERO | BUS_DMA_NOCACHE,
-		&dmah->tag, &dmah->map, &dmah->vaddr);
-	if (error != 0) {
-		kfree(dmah);
-		return NULL;
-	}	
-	return dmah;
-#endif
 }
 
 void
@@ -78,5 +62,5 @@
     dma_addr_t dma_handle)
 {
 
-	kmem_free(kernel_map, (vm_offset_t)cpu_addr, size);
+	contigfree(cpu_addr, size, M_DEVBUF);
 }