2016-12-12 6 views
0

Я строю систему сбора данных на базе UltraScale + FPGA, оснащенной процессором arm64. Данные передаются в ОЗУ через DMA. Буферы DMA в драйвере зарезервированы, как показано ниже:Linux на arm64: sendto вызывает «Неисправленная ошибка: ошибка выравнивания (0x96000021)» при отправке данных из когерентного DMA-буфера mmapped

virt_buf[i] = dma_zalloc_coherent(&pdev->dev, BUF_SIZE, &phys_buf[i],GFP_KERNEL); 

В функции MMAP водителя, отображение в пространстве пользователя выполняется следующим образом:

#ifdef ARCH_HAS_DMA_MMAP_COHERENT 
    printk(KERN_INFO "Mapping with dma_map_coherent DMA buffer at phys: %p virt %p\n",phys_buf[off],virt_buf[off]); 
    res = dma_mmap_coherent(&my_pdev->dev, vma, virt_buf[off], phys_buf[off], vsize); 
#else 
    physical = phys_buf[off]; 
    res=remap_pfn_range(vma,vma->vm_start, physical >> PAGE_SHIFT , vsize, pgprot_noncached(vma->vm_page_prot)); 
    printk(KERN_INFO "Mapping with remap_pfn_range DMA buffer at phys: %p virt %p\n",physical,virt_buf[off]); 
#endif 

На мой UltraScale + remap_pfn_range процессор используемый. В приложении пользовательского пространства данные считываются из буфера и в настоящее время немедленно отправляют в UDP-пакеты с длиной, ограниченной MAX_DGRAM (первоначально равной 572).

int i = 0; 
int bleft = nbytes; 
while(i<nbytes) { 
    int bts = bleft < MAX_DGRAM ? bleft : MAX_DGRAM; 
    if (sendto(fd,&buf[nbuf][i],bts,0, res2->ai_addr,res2->ai_addrlen)==-1) { 
     printf("%s",strerror(errno)); 
     exit(1); 
    } 
    bleft -= bts; 
    i+= bts; 
} 

Все работает отлично на 32-битной ПЛИС Zynq. Однако после того, как я переместил его на 64-битную UltraScale + FPGA, я начал получать случайные ошибки после нескольких сотен переводов.

[ 852.703491] Unhandled fault: alignment fault (0x96000021) at 0x0000007f82635584 
[ 852.710739] Internal error: : 96000021 [#4] SMP 
[ 852.715235] Modules linked in: axi4s2dmov(O) ksgpio(O) 
[ 852.720358] CPU: 0 PID: 1870 Comm: a4s2dmov_send Tainted: G  D O 4.4.0 #3 
[ 852.728001] Hardware name: ZynqMP ZCU102 RevB (DT) 
[ 852.732769] task: ffffffc0718ac180 ti: ffffffc0718b8000 task.ti: ffffffc0718b8000 
[ 852.740248] PC is at __copy_from_user+0x8c/0x180 
[ 852.744836] LR is at copy_from_iter+0x70/0x24c 
[ 852.749261] pc : [<ffffffc00039210c>] lr : [<ffffffc0003a36a8>] pstate: 80000145 
[ 852.756644] sp : ffffffc0718bba40 
[ 852.759935] x29: ffffffc0718bba40 x28: ffffffc06a4bae00 
[ 852.765228] x27: ffffffc0718ac820 x26: 000000000000000c 
[ 852.770523] x25: 0000000000000014 x24: 0000000000000000 
[ 852.775818] x23: ffffffc0718bbe08 x22: ffffffc0710eba38 
[ 852.781112] x21: ffffffc0718bbde8 x20: 000000000000000c 
[ 852.786407] x19: 000000000000000c x18: ffffffc000823020 
[ 852.791702] x17: 0000000000000000 x16: 0000000000000000 
[ 852.796997] x15: 0000000000000000 x14: 00000000c0a85f32 
[ 852.802292] x13: 0000000000000000 x12: 0000000000000032 
[ 852.807586] x11: 0000000000000014 x10: 0000000000000014 
[ 852.812881] x9 : ffffffc0718bbcf8 x8 : 000000000000000c 
[ 852.818176] x7 : ffffffc0718bbdf8 x6 : ffffffc0710eba2c 
[ 852.823471] x5 : ffffffc0710eba38 x4 : 0000000000000000 
[ 852.828766] x3 : 000000000000000c x2 : 000000000000000c 
[ 852.834061] x1 : 0000007f82635584 x0 : ffffffc0710eba2c 
[ 852.839355] 
[ 852.840833] Process a4s2dmov_send (pid: 1870, stack limit = 0xffffffc0718b8020) 
[ 852.848134] Stack: (0xffffffc0718bba40 to 0xffffffc0718bc000) 
[ 852.853858] ba40: ffffffc0718bba90 ffffffc0006a1b2c 000000000000000c ffffffc06a9bdb00 
[ 852.861676] ba60: 00000000000005dc ffffffc071a0d200 0000000000000000 ffffffc0718bbdf8 
[ 852.869488] ba80: 0000000000000014 ffffffc06a959000 ffffffc0718bbad0 ffffffc0006a2358 
[...] 
[ 853.213212] Call trace: 
[ 853.215639] [<ffffffc00039210c>] __copy_from_user+0x8c/0x180 
[ 853.221284] [<ffffffc0006a1b2c>] ip_generic_getfrag+0xa4/0xc4 
[ 853.227011] [<ffffffc0006a2358>] __ip_append_data.isra.43+0x80c/0xa70 
[ 853.233434] [<ffffffc0006a3d50>] ip_make_skb+0xc4/0x148 
[ 853.238642] [<ffffffc0006c9d04>] udp_sendmsg+0x280/0x740 
[ 853.243937] [<ffffffc0006d38e4>] inet_sendmsg+0x7c/0xbc 
[ 853.249145] [<ffffffc000651f5c>] sock_sendmsg+0x18/0x2c 
[ 853.254352] [<ffffffc000654b14>] SyS_sendto+0xb0/0xf0 
[ 853.259388] [<ffffffc000084470>] el0_svc_naked+0x24/0x28 
[ 853.264682] Code: a88120c7 a8c12027 a88120c7 36180062 (f8408423) 
[ 853.270791] ---[ end trace 30e1cd8e2ccd56c5 ]--- 
Segmentation fault 
[email protected]_2:~# 

Странная вещь, что, когда я просто читать слова из буфера, он не вызывает каких-либо ошибок выравнивания.

Кажется, что отправить функцию неправильно использует __copy_from_user функции, вызывая невыровненный доступ к памяти. Вопрос в том, является ли это ошибкой ядра, или я сделал что-то неправильно?

Однако, как правило, отправка блока данных, не начинающаяся с 8-байтовой границы, не вызывает ошибку выравнивания. Проблема возникает с относительно низкой вероятностью. Я не смог изолировать условия, приводящие к ошибке.

Я справился с проблемой, настроив MAX_DGRAM так, чтобы он был кратным 8. Однако я боюсь, что проблема может возникнуть, если данные в mmapped-буфере будут отправлены в более сложную обработку. Некоторые люди сообщали о подобных проблемах в архитектуре arm64, связанной с функцией memcpy (например, [https://bugs.launchpad.net/linux-linaro/+bug/1271649]).

Каков правильный метод для сопоставления когерентных буферов DMA в пространстве пользователя, чтобы избежать ошибок выравнивания памяти?

ответ

1

Этот драйвер нуждается в обновлении. ARCH_HAS_DMA_MMAP_COHERENT не был определен ничем, кроме PowerPC, долгое время, и даже это выглядит как забытый остаток.

Внедрение dma_mmap_coherent()since 3.6, так что оно может и должно использоваться безоговорочно. Результатом текущего кода является то, что благодаря #ifdef вы всегда берете другой путь, а затем благодаря pgprot_noncached() вы в конечном итоге создаете сопоставление пользовательского пространства в буфере. Строго-упорядоченное (Device nGnRnE в терминах AArch64). Это, как правило, плохая идея, поскольку код пользовательского пространства предполагает, что он всегда работает в нормальной памяти (если явно не создан), и может безопасно делать такие вещи, как нестандартный или эксклюзивный доступ, оба из которых могут ошибочно ошибиться в памяти устройства , Я даже не собираюсь спрашивать, какое сумасшествие заканчивается тем, что ядро ​​копирует данные обратно из сопоставления пользовательского пространства из буфера ядра*, но достаточно сказать, что ядро ​​- через copy_{to,from,in}_user() - также предполагает, что адреса пользовательского пространства отображается как обычная память и, таким образом, безопасна для неравномерных доступов.Честно говоря, я немного удивлен, что это не взрывается аналогично на 32-битной ARM, поэтому я думаю, что ваши данные всегда будут по крайней мере с 4 байтами, что также объясняет, почему чтение слов (с 32-битными обращениями) прекрасно, если только 64-битные двойные обращения могут быть неправильно смещены.

Короче говоря, просто используйте dma_mmap_coherent() и избавьтесь от слабого эквивалента с низким кодом. Это даст пользователю пространство нормальное не кэшируемое сопоставление (или кэшируемое для аппаратно-когерентного устройства), которое будет работать должным образом. Это также не нарушено с точки зрения предположения, что dma_addr_t - это физический адрес, поскольку ваш код драйвера, похоже, делает это - это еще одна вещь, которая может возникнуть и укусить вас в заднице раньше или позже (у ZynqMP есть системный MMU, так что вы можете предположительно, обновить до ядра 4.9, подключить некоторые идентификаторы потока, добавить их в DT и посмотреть, что это предположение идет по-новому и захватывающим образом).

* Несмотря на то, что мне пришло в голову, что было какое-то обстоятельство, при котором копирование с самого конца страницы иногда может быть перечитано на следующую страницу, что может вызвать это невольно, если следующая страница оказалась Устройство/Сильно упорядоченное отображение, которое привело к this patch in 4.5. Ответ Linus на такие макеты памяти был "...and nobody sane actually does that..."

+0

Спасибо большое! Действительно, это была проблема. Я, должно быть, не обратил внимания на удаление ARCH_HAS_DMA_MMAP_COHERENT. Поскольку я имею дело с SoCs - как с FPGA, так и с родственными драйверами ядра, я считаю, что держать свои знания в курсе все более и более трудными ... На самом деле я довольно удивлен, что проблема возникла НЕ во время каждого доступ к не выровненной части буфера! Я переключился на dma_mmap_coherent (помня, чтобы установить vma-> vm_pgoff в 0, после того как он используется для определения количества буфера), и все работает отлично. – wzab