Я отображаю когерентную память 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
)
}