Память Mmap DMA без кэширования: "map pfn ram range req uncached-minus получил обратную запись" - PullRequest
0 голосов
/ 07 ноября 2018

Я отображаю когерентную память DMA из ядра в пространство пользователя. На уровне пользователя я использую mmap(), а в драйвере ядра я использую dma_alloc_coherent(), а затем remap_pfn_range() для переназначения страниц. Это в основном работает, так как я могу записать данные в сопоставленную область в моем приложении и проверить это в своем драйвере ядра.

Однако, несмотря на использование dma_alloc_coherent (который должен выделять не кэшированную память) и pgprot_noncached(), ядро ​​сообщает мне этот вывод dmesg:

map pfn ram range req uncached-minus для [mem 0xABC-0xCBA], получена обратная запись

В моем понимании обратная запись - это кэшированная память. Но мне нужна некэшированная память для операции DMA.

Код (показаны только важные части):

Приложение пользователя

fd = open(dev_fn, O_RDWR | O_SYNC);
if (fd > 0)
{
    mem = mmap ( NULL 
               , mmap_len
               , PROT_READ | PROT_WRITE
               , MAP_SHARED
               , fd
               , 0
               );
}

Для тестирования я использовал mmap_len = getpagesize(); Что составляет 4096 .

Драйвер ядра

typedef struct
{
    size_t     mem_size;
    dma_addr_t dma_addr;
    void       *cpu_addr;
} Dma_Priv;

fops_mmap()
{
    dma_priv->mem_size = vma->vm_end - vma->vm_start;

    dma_priv->cpu_addr = dma_alloc_coherent ( &gen_dev
                                            , dma_priv->mem_size
                                            , &dma_priv->dma_addr
                                            , GFP_KERNEL
                                            );
    if (dma_priv->cpu_addr != NULL)
    {
        vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
        remap_pfn_range ( vma
                        , vma->vm_start
                        , virt_to_phys(dma_priv->cpu_addr)>>PAGE_SHIFT
                        , dma_priv->mem_size
                        , vma->vm_page_prot
                        )
    }
}

Полезная информация, которую я нашел

1) Паттинг Linux : https://www.kernel.org/doc/ols/2008/ols2008v2-pages-135-144.pdf

Страница 7 -> mmap с O_SYNC ( без кэширования ):

Приложения могут открывать / dev / mem с флагом O_SYNC и затем делать mmap в теме. При этом приложения будут получать доступ к этому адресу с некэшированный тип памяти. Mmap будет успешным, только если нет другого конфликтующие сопоставления с одним и тем же регионом.

Я использовал флаг, не помогает.

Страница 7 -> mmap без O_SYNC ( uncached-minus ):

mmap без O_SYNC, без существующего отображения и без области обратной записи: Для mmap, относящегося к этой категории, мы используем тип uncached-minus отображение. В отсутствие какого-либо ССО для этого региона, эффективный тип будет некэширован. Но в тех случаях, когда есть MTRR, что делает этот регион пишут-объединяют, тогда эффективный тип будет написать-комбайн.

2) pgprot_noncached ()

В / arch / x86 / include / asm / pgtable.h Я нашел это:

#define pgprot_noncached(prot)                              \
    ((boot_cpu_data.x86 > 3)                                \
     ? (__pgprot(pgprot_val(prot) |                         \
             cachemode2protval(_PAGE_CACHE_MODE_UC_MINUS))) \
     : (prot))

Возможно ли, что x86 всегда устанавливает не кэшированный запрос в UC_MINUS, что приводит к комбинации с MTRR в кэшированной обратной записи?

Я использую Ubuntu 16.04.1 , Ядро: 4.10.0-40-generic .

РЕДАКТИРОВАТЬ: РЕШЕНО

https://www.kernel.org/doc/Documentation/x86/pat.txt

Драйверы, желающие экспортировать некоторые страницы в пространство пользователя, делают это с помощью mmap интерфейс и комбинация 1) pgprot_noncached () 2) io_remap_pfn_range () или remap_pfn_range () или vmf_insert_pfn ()

При поддержке PAT добавляется новый API pgprot_writecombine. Так, водители могут продолжать использовать вышеуказанную последовательность, либо с pgprot_noncached () или pgprot_writecombine () на шаге 1, а затем шаг 2.

Кроме того, шаг 2 внутренне отслеживает регион как UC или WC в список memtype, чтобы избежать конфликтных отображений.

Обратите внимание, что этот набор API работает только с регионами ввода-вывода (не RAM). Если Драйвер хочет экспортировать область RAM, он должен сделать set_memory_uc () или set_memory_wc () в качестве шага 0 выше, а также отслеживать использование этих страниц и используйте set_memory_wb () до того, как страница будет освобождена из пула.

Я добавил set_memory_uc() перед pgprot_noncached (), и он сделал это.

if (dma_priv->cpu_addr != NULL)
{
    set_memory_uc(dma_priv->cpu_addr, (dma_priv->mem_size/PAGE_SIZE));
    vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
    remap_pfn_range ( vma
                    , vma->vm_start
                    , virt_to_phys(dma_priv->cpu_addr)>>PAGE_SHIFT
                    , dma_priv->mem_size
                    , vma->vm_page_prot
                    )
}
...