質問の更新に投稿したように、根本的な問題は、remap_pfn_range()
を使用してマップされたメモリに対してゼロコピー ネットワークが機能しないことです。 (どの dma_mmap_coherent()
ボンネットの下でも使用されます)。その理由は、このタイプのメモリ (VM_PFNMAP
フラグが設定されている) には struct page*
の形式のメタデータがありません
解決策は、struct page*
の方法でメモリを割り当てることです。 は
現在、メモリを割り当てるワークフローは次のとおりです。
<オール>struct page* page = alloc_pages(GFP_USER, page_order);
を使用 連続した物理メモリのブロックを割り当てます。割り当てられる連続したページの数は 2**page_order
で指定されます .split_page(page, page_order);
を呼び出して、上位/複合ページを 0 次ページに分割します。 .これは struct page* page
を意味します 2**page_order
の配列になっています このようなリージョンを DMA に送信するには (データ受信用):
<オール>dma_addr = dma_map_page(dev, page, 0, length, DMA_FROM_DEVICE);
dma_desc = dmaengine_prep_slave_single(dma_chan, dma_addr, length, DMA_DEV_TO_MEM, 0);
dmaengine_submit(dma_desc);
転送が完了したことを示すコールバックを DMA から受け取ったら、領域のマップを解除して、このメモリ ブロックの所有権を CPU に戻す必要があります。CPU はキャッシュを処理して、古いデータを読み取っていないことを確認します。 /P> <オール>
dma_unmap_page(dev, dma_addr, length, DMA_FROM_DEVICE);
さて、 mmap()
を実装したいとき vm_insert_page()
を呼び出すだけです。 事前に割り当てたすべての 0-order ページに対して繰り返し:
static int my_mmap(struct file *file, struct vm_area_struct *vma) {
int res;
...
for (i = 0; i < 2**page_order; ++i) {
if ((res = vm_insert_page(vma, vma->vm_start + i*PAGE_SIZE, &page[i])) < 0) {
break;
}
}
vma->vm_flags |= VM_LOCKED | VM_DONTCOPY | VM_DONTEXPAND | VM_DENYWRITE;
...
return res;
}
ファイルを閉じたら、忘れずにページを解放してください:
for (i = 0; i < 2**page_order; ++i) {
__free_page(&dev->shm[i].pages[i]);
}
mmap()
の実装 この方法で、ソケットがこのバッファを sendmsg()
使用できるようになりました MSG_ZEROCOPY
で
これは機能しますが、このアプローチには 2 つの点が気に入りません。
alloc_pages
を呼び出すロジックを実装することはできますが、このメソッドでは 2 のべき乗サイズのバッファしか割り当てることができません。 さまざまなサイズのサブバッファーで構成される任意のサイズのバッファーを取得するために、降順で必要な回数だけ。これには、mmap()
でこれらのバッファを結び付けるロジックが必要になります。 スキャッターギャザー (sg
)single
ではなく呼び出し .split_page()
そのドキュメントには次のように書かれています:
* Note: this is probably too low level an operation for use in drivers.
* Please consult with lkml before using this in your driver.
これらの問題は、カーネルに任意の量の連続した物理ページを割り当てるためのインターフェイスがあれば簡単に解決できます。ない理由はわかりませんが、上記の問題は、これが利用できない理由/実装方法を掘り下げるほど重要ではありません:-)
おそらくこれは、alloc_pages が 2 の累乗のページ番号を必要とする理由を理解するのに役立つでしょう。
頻繁に使用されるページ割り当てプロセスを最適化する (および外部断片化を減らす) ために、Linux カーネルは、CPU ごとのページ キャッシュと、メモリを割り当てるバディ アロケータを開発しました (別のアロケータであるスラブがあり、メモリ割り当てよりも小さいメモリ割り当てを提供します)。ページ)
CPU ごとのページ キャッシュは 1 ページの割り当て要求を処理しますが、バディ アロケーターは 11 個のリストを保持し、それぞれに 2^{0-10} 物理ページが含まれます。これらのリストは、ページを割り当てて解放するときに適切に機能します。もちろん、2 のべき乗サイズのバッファーを要求していることが前提です。