-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Description
I am implementing DMA transfers from a ring buffer in a kernel module to SPI0 on Raspberry Pi 3 Model B BCM2835 for ILI9341 display (https://github.com/juj/fbcp-ili9341), and I've got DMA writes out to SPI working great (translating kernel physical addresses to bus addresses with an OR with 0xC0000000), but connecting the DMA write source memory to the algorithm I'm running is proving difficult.
What I do is I allocate a piece of memory in a kernel module using dma_alloc_writecombine(), and then have one thread produce data into that buffer in a lockfree manner, and have another thread that kicks off DMA tasks from that buffer. These two threads synchronize by using __atomic_fetch_add and __atomic_fetch_sub to head and tail locations in the allocated memory. However doing atomic operations to dma allocated memory results in a crash of the kernel module. Something like the following reproduces:
#include <linux/kernel.h>
#include <linux/module.h>
#define SHARED_MEMORY_SIZE 385024
int init_module(void)
{
dma_attr_t spiTaskMemoryPhysical = 0;
uint32_t *spiTaskMemory = (uint32_t*)dma_alloc_writecombine(0, SHARED_MEMORY_SIZE, &spiTaskMemoryPhysical, GFP_KERNEL);
__atomic_fetch_add(&spiTaskMemory, 1, __ATOMIC_RELAXED); // crash
return 0;
}The crash reads as follows:
[ 266.919451] Unhandled fault: unknown 21 (0x405) at 0xafa00008
[ 266.919625] pgd = 80004000
[ 266.919703] [afa00008] *pgd=2f7fd801, *pte=2fa00647, *ppte=2fa00417
[ 266.919891] Internal error: : 405 [#1] SMP ARM
Tried doing __SEQ_CST operations and __sync_val_compare_and_swap CAS loop to update, but those give the same issue as well. I'm not sure if this is something that's unsupported, though "unknown fault" and "internal error" message suggests that this might be a bug? Is there a way to do atomic operations on dma_alloc_writecombined memory? Should I be using some other memory allocation function or flags when performing the allocation?
If I replace dma_alloc_writecombine() with kmalloc(SHARED_MEMORY_SIZE, GFP_KERNEL | GFP_DMA) then I am able to call __atomic_fetch_add() on the allocated memory fine, but it looks like I can't use kmalloced memory as source for DMA, even if GFP_DMA is passed. I am not very familiar with the DMA allocation functions in linux, so perhaps there is some option I'm missing, or is this some kind of bug due to the internal error?