2016-12-12 4 views
0

私は、arm64 CPUを搭載したUltraScale + FPGAをベースにしたデータ収集システムを構築しています。 データはDMA経由でRAMに転送されます。ドライバーでのDMAバッファは以下のように予約されていますドライバのmmap関数でLinux on arm64:sendtoは、mmappedコヒーレントDMAバッファからデータを送信するときに「処理できないフォールト:アライメントフォールト(0x96000021)」を引き起こします。

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

、ユーザ空間へのマッピングは次のように行われます。

#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 

私のウルトラスケール+ CPU remap_pfn_rangeであります中古。 ユーザスペースアプリケーションでは、データはバッファから読み込まれ、現在はMAX_DGRAM(当初は572)に制限された長さのUDPパケットをすぐに送信します。

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 FPGAで完全に機能しました。しかし、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バッファ内のデータがより複雑な処理に送られると問題が再現することがあります。 memcpy関数に関連するarm64アーキテクチャでも同様の問題が報告されています([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()のおかげで、強く注文されたバッファ(AArch64用語ではデバイスnGnRnE)のユーザースペースマッピングを作成することになります。これは一般的には悪い考えです。ユーザ空間コードは、(通常そうでないと明示的に作られていない限り)通常のメモリ上で動作していると想定し、デバイスタイプのメモリで間違ってうまくいかない。カーネルバッファーのマッピングからデータをコピーするカーネルでどんな狂気が終了するのか尋ねるつもりはありません*カーネルは - copy_{to,from,in}_user()経由でも十分ですノーマルメモリとしてマップされ、アンアラインドアクセスに対して安全です。率直に言えば、これは32ビットARMでも同様に爆発しないので、ちょっと驚いています。あなたのデータは常に少なくとも4バイト整列していると思います。これは32ビットアクセスで読み込みが行われる理由を説明します。 64ビットのダブルワードアクセスだけがミスアライメントされる可能性がある場合は問題ありません。

簡潔に言えば、dma_mmap_coherent()を使用して、オープンコード化された不良コードを取り除くだけです。これにより、ユーザー空間に通常のキャッシュ不可能なマッピング(またはハードウェア一貫性のあるデバイス用のキャッシュ可能なマッピング)が提供され、期待どおりに動作します。あなたのドライバコードがそうしているように、dma_addr_tが物理的なアドレスであると仮定すると、それは壊れていません。これは遅かれ早かれあなたを襲う可能性がある別のものです(ZynqMPにはシステムMMUがあります。恐らく4.9カーネルに更新し、いくつかのストリームIDを結びつけてDTに追加し、その前提が新しくエキサイティングな方法で盛り上がるのを見てください)。

*時々、次のページがあることを起こった場合に知らず知らずのうちにこれを引き起こす可能性が、次のページへオーバー読むことができるページの一番最後からのコピーの下に、いくつかの事情があったように私には発生しないが、 Device/Strongly-ordered mappingで、this patch in 4.5になりました。そのようなメモリレイアウトに対するLinusの応答は"...and nobody sane actually does that..."

+0

ありがとう!確かにそれは問題でした。 私はARCH_HAS_DMA_MMAP_COHERENTの削除を見逃しているに違いありません。 私はFPGAと関連するカーネルドライバの両方でSoCを扱っているので、自分の知識を最新のものにすることがますます難しくなっています。 実際には、それぞれ問題が発生していないバッファの非整列部分へのアクセス! 私はdma_mmap_coherentに切り替えました(vma-> vm_pgoffをバッファ番号の定義に使用した後、0に設定することを忘れないでください)、すべてが完全に機能します。 – wzab

関連する問題