free(): неверный указатель только после доступа к драйверу DMA с нулевым копированием

У меня есть драйвер, который выполняет передачу DMA в память, которая распределена в приложении пользовательского пространства, а затем передается ядру (get_user_pages...).

Проблема: я получаю сообщение "неверный указатель" при вызове free(). Я печатаю оба значения указателя, они равны, не изменены. Ошибка НЕ ​​возникает, когда я использую posix_memalign() вместо malloc(). В случае posix_memalign() я всегда получаю указатели, выровненные по странице (0x....000). С malloc() это часто невыровненные адреса.

Я уже пытался полностью пропустить вызов передачи Driver DMA, в основном просто выполняя malloc() и free() в приложении - тогда это всегда работает!

Это говорит мне о том, что каким-то образом ядро ​​​​кажется, что страницы все еще «заблокированы» или около того в пространстве ядра.

Я уже перерыл некоторые драйверы, особенно драйверы/media/pci/ivtv/ivtv-udma.c и ivtv-yuv.c выглядят интересно, так как они, похоже, делают то же самое.

Там я нашел функцию "put_page", которую я пробовал уже в своем драйвере, но она не помогла, более того, драйвер потом застрял.

Может ли кто-нибудь указать мне правильное направление или источник информации о том, как правильно обрабатывать такие пользовательские страницы в пространстве ядра?

Вот соответствующий код. Порядок в основном такой: get_user_pages -> dma_map_page -> запуск HW DMA (в нашей FPGA) -> dma_unmap_page -> SetPageDirty -> put_page

Но по-прежнему возникает та же ошибка, что и ниже, для невыровненных адресов.

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] );
    }
[...]
}

Сообщение об ошибке при использовании буфера malloc (невыровненная память):

*** 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

person Thomas S.    schedule 05.10.2017    source источник
comment
Не могли бы вы показать хотя бы часть вашего кода здесь? Возможно, наиболее важные части вашей установки и демонтажа прямого доступа к памяти в лернеле. Вы знаете, отлаживать код, который мы не видим, непросто.   -  person    schedule 05.10.2017
comment
Вы помечали страницы как грязные, прежде чем положить их?   -  person Ian Abbott    schedule 06.10.2017
comment
Привет, Бронислав, Ян, найдите выше прикрепленный код, отрезанный. Я делаю там SetPageDirty, но результат тот же. Спасибо!   -  person Thomas S.    schedule 09.10.2017
comment
Оказалось, что информация о хранилище чанков из malloc() была перезаписана. Если я вообще не выполняю dma_map_page(), а просто получаю физ. адрес и передать его в DMA, передача работает без ошибки недопустимого указателя. Но, похоже, проблема с очисткой кеша остается, я вижу поступающие данные DMA в физической памяти, но не отражаются в буфере приложения malloc().   -  person Thomas S.    schedule 09.11.2017


Ответы (1)


Сам решил.

оказалось, что отображение с помощью dma_map_page() делает все переданные данные правильно видимыми в ОЗУ, но все остальные данные на этой странице перезаписываются (включая данные управления указателем malloc прямо перед и после переданных данных, что приводит к ошибке libc), кэширование как-то не работает, как ожидалось. При dma_unmap_page() данные были видны только через каждые несколько вызовов функции.

С dma_map_single() все выглядит лучше. Я делаю get_user_pages_fast(), затем dma_map_single() виртуальный адрес страницы плюс смещение - работает нормально, и функции отладки DMA в ядре не жалуются. См. код ниже.

    for (i = 0; i < nr_pages; ++i, sgList++) {
            struct page *page = pages[i];
            sgList->dmaLength  = PAGE_SIZE - offset;
            pVirtAddr = ((unsigned char*)(page_address( page ))) + offset;

            if( totlen + sgList->dmaLength > count )
                sgList->dmaLength = count - totlen;

            dmaAddr = dma_map_single( pDev, pVirtAddr, sgList->dmaLength, direction );
            if ( dma_mapping_error( pDev, dmaAddr ) ) {
                   printk( KERN_ERR "*** error mapping DMA space!\n" );
                   goto CLEANUP;
            } else {
                   sgList->dmaAddress = dmaAddr;
            }

            VME4LDBG(" sglist %d: pageAddr=%p off=0x%04lx dmaAddr=%p length=0x%04x\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 ---*/
       if( locked ) {
             sgList = sgListStart;
             for (i = 0; i < nr_pages; i++, sgList++) {
                 dma_unmap_single( pDev, sgList->dmaAddress, sgList->dmaLength , direction );
                 put_page( pages[i] ); /* release pages locked with get_user_pages */
             }
   } 
person Thomas S.    schedule 15.11.2017