I have a driver that does a DMA transfer to a memory thats malloc'ed in a userspace application and then handed down to the kernel (get_user_pages...).
Problem: I get the "invalid pointer" message when calling free(). I print both pointer values, they are equal, not changed. The error does NOT happen when I use posix_memalign() instead malloc(). In case of posix_memalign() I get always page aligned pointers ( 0x....000). With malloc() its often unaligned addresses.
I tried already skipping the Driver DMA transfer call completely, basically just doing malloc() and free() in the application - it then always works!
That tells me that somehow the kernel seem to think the pages are still "locked" or so in kernel space.
I digged already through some drivers, especially the drivers/media/pci/ivtv/ivtv-udma.c and ivtv-yuv.c look interesting since they seem to do exactly the same.
There I found the function "put_page" which I tried already in my driver, but it didnt help, more, the driver then stuck.
Can someone point me in the right direction or an information source how the correct handling of such user pages in kernel space is ?
Here the relevant code. The order is basically: get_user_pages -> dma_map_page -> kick off HW DMA (in our FPGA) -> dma_unmap_page -> SetPageDirty -> put_page
But still same error as below occurs for non-aligned addresses.
perform_user_dma_func(..)
{
[...]
rv = get_user_pages( current, current->mm, uaddr, nr_pages, (direction == DMA_FROM_DEVICE), 0, pages, NULL);
if( rv < nr_pages )
goto CLEANUP;
/*--- build scatter/gather list ---*/
offset = uaddr & ~PAGE_MASK;
#ifdef VME4L_DBG_DMA_DATA
initOffs = offset;
#endif
for ( i = 0; i < nr_pages; ++i, sgList++ ) {
struct page *page = pages[i];
sgList->dmaLength = PAGE_SIZE - offset;
dmaAddr = dma_map_page( pDev, page, 0x0, PAGE_SIZE, direction );
if ( dma_mapping_error( pDev, dmaAddr ) ) {
printk( KERN_ERR "error mapping DMA space with dma_map_page\n" );
goto CLEANUP;
} else {
sgList->dmaDataAddress = dmaAddr + offset; /* Add offset between page begin and payload data, often > 0 */
sgList->dmaPageAddress = dmaAddr; /* store page address for later dma_unmap_page */
}
if( totlen + sgList->dmaLength > count )
sgList->dmaLength = count - totlen;
VME4LDBG(" sglist %d: pageAddr=%p off=0x%lx dmaAddr=%p length=0x%x\n", i, page_address(page), offset, dmaAddr, sgList->dmaLength);
totlen += sgList->dmaLength;
offset = 0;
}
/*--- now do DMA in HW (device touches memory) ---*/
rv = vme4l_perform_zc_dma( spc, sgListStart, nr_pages, blk->direction, blk->vmeAddr, swapMode );
CLEANUP:
/*--- free pages ---*/
sgList = sgListStart;
for (i = 0; i < nr_pages; i++, sgList++) {
dma_unmap_page( pDev, sgList->dmaPageAddress, PAGE_SIZE, direction );
}
/* mark pages as dirty */
if( blk->direction == READ ) {
for (i = 0; i < nr_pages; i++ ) {
if ( !PageReserved( pages[i] ))
SetPageDirty( pages[i] );
}
}
sgList = sgListStart;
for (i = 0; i < nr_pages; i++, sgList++) {
/* __free_page( pages[i] ); */
put_page( pages[i] );
}
[...]
}
The error message when usimg malloc buffer (unaligned memory):
*** Error in `vme4l_rwex': free(): invalid pointer: 0x00000000008e4010 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x777e5)[0x7fe0a34367e5]
/lib/x86_64-linux-gnu/libc.so.6(+0x8037a)[0x7fe0a343f37a]
/lib/x86_64-linux-gnu/libc.so.6(cfree+0x4c)[0x7fe0a344353c]
vme4l_rwex[0x4010f8]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7fe0a33df830]
vme4l_rwex[0x401419]
======= Memory map: ========
00400000-00402000 r-xp 00000000 08:15 931122 /usr/local/bin/vme4l_rwex
00601000-00602000 r--p 00001000 08:15 931122 /usr/local/bin/vme4l_rwex
00602000-00603000 rw-p 00002000 08:15 931122 /usr/local/bin/vme4l_rwex
008e4000-00906000 rw-p 00000000 00:00 0 [heap]
7fe09c000000-7fe09c021000 rw-p 00000000 00:00 0
7fe09c021000-7fe0a0000000 ---p 00000000 00:00 0
7fe0a31a9000-7fe0a31bf000 r-xp 00000000 08:15 1035123 /lib/x86_64-linux-gnu/libgcc_s.so.1
7fe0a31bf000-7fe0a33be000 ---p 00016000 08:15 1035123 /lib/x86_64-linux-gnu/libgcc_s.so.1
7fe0a33be000-7fe0a33bf000 rw-p 00015000 08:15 1035123 /lib/x86_64-linux-gnu/libgcc_s.so.1
7fe0a33bf000-7fe0a357f000 r-xp 00000000 08:15 1045483 /lib/x86_64-linux-gnu/libc-2.23.so
7fe0a357f000-7fe0a377f000 ---p 001c0000 08:15 1045483 /lib/x86_64-linux-gnu/libc-2.23.so
7fe0a377f000-7fe0a3783000 r--p 001c0000 08:15 1045483 /lib/x86_64-linux-gnu/libc-2.23.so
7fe0a3783000-7fe0a3785000 rw-p 001c4000 08:15 1045483 /lib/x86_64-linux-gnu/libc-2.23.so
7fe0a3785000-7fe0a3789000 rw-p 00000000 00:00 0
7fe0a3789000-7fe0a378b000 r-xp 00000000 08:15 905612 /usr/local/lib/libusr_utl.so
7fe0a378b000-7fe0a398a000 ---p 00002000 08:15 905612 /usr/local/lib/libusr_utl.so
7fe0a398a000-7fe0a398b000 r--p 00001000 08:15 905612 /usr/local/lib/libusr_utl.so
7fe0a398b000-7fe0a398c000 rw-p 00002000 08:15 905612 /usr/local/lib/libusr_utl.so
7fe0a398c000-7fe0a398e000 r-xp 00000000 08:15 905739 /usr/local/lib/libvme4l_api.so
7fe0a398e000-7fe0a3b8e000 ---p 00002000 08:15 905739 /usr/local/lib/libvme4l_api.so
7fe0a3b8e000-7fe0a3b8f000 r--p 00002000 08:15 905739 /usr/local/lib/libvme4l_api.so
7fe0a3b8f000-7fe0a3b90000 rw-p 00003000 08:15 905739 /usr/local/lib/libvme4l_api.so
7fe0a3b90000-7fe0a3bb6000 r-xp 00000000 08:15 1045455 /lib/x86_64-linux-gnu/ld-2.23.so
7fe0a3da6000-7fe0a3da9000 rw-p 00000000 00:00 0
7fe0a3db2000-7fe0a3db5000 rw-p 00000000 00:00 0
7fe0a3db5000-7fe0a3db6000 r--p 00025000 08:15 1045455 /lib/x86_64-linux-gnu/ld-2.23.so
7fe0a3db6000-7fe0a3db7000 rw-p 00026000 08:15 1045455 /lib/x86_64-linux-gnu/ld-2.23.so
7fe0a3db7000-7fe0a3db8000 rw-p 00000000 00:00 0
7ffd66125000-7ffd66146000 rw-p 00000000 00:00 0 [stack]
7ffd661b4000-7ffd661b6000 r--p 00000000 00:00 0 [vvar]
7ffd661b6000-7ffd661b8000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
Aborted