dma_alloc_from_coherent(): fix fallback to generic memory
Andrew Morton [Tue, 6 Jan 2009 22:43:08 +0000 (14:43 -0800)]
If bitmap_find_free_region() fails and DMA_MEMORY_EXCLUSIVE is not set,
the function will fail to write anything to *ret and will return 1.             This will cause dma_alloc_coherent() to return an uninitialised value,
crashing the kernel, perhaps via DMA to a random address.

Fix that by changing it to return zero in this case, so the caller will
proceed to allocate the memory from the generic memory allocator.

Cc: Tetsuo Handa <penguin-kernel@i-love.sakura.ne.jp>
Cc: Dmitry Baryshkov <dbaryshkov@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

kernel/dma-coherent.c

index f013a0c..4bdcea8 100644 (file)
@@ -116,11 +116,25 @@ int dma_alloc_from_coherent(struct device *dev, ssize_t size,
                int page = bitmap_find_free_region(mem->bitmap, mem->size,
                                                     order);
                if (page >= 0) {
+                       /*
+                        * Memory was found in the per-device arena.
+                        */
                        *dma_handle = mem->device_base + (page << PAGE_SHIFT);
                        *ret = mem->virt_base + (page << PAGE_SHIFT);
                        memset(*ret, 0, size);
-               } else if (mem->flags & DMA_MEMORY_EXCLUSIVE)
+               } else if (mem->flags & DMA_MEMORY_EXCLUSIVE) {
+                       /*
+                        * The per-device arena is exhausted and we are not
+                        * permitted to fall back to generic memory.
+                        */
                        *ret = NULL;
+               } else {
+                       /*
+                        * The per-device arena is exhausted and we are
+                        * permitted to fall back to generic memory.
+                        */
+                        return 0;
+               }
        }
        return (mem != NULL);
 }