GNU/Linux >> Linux の 問題 >  >> Linux

dma_mmap_coherent() マップされたメモリのゼロコピー ユーザー空間 TCP 送信

質問の更新に投稿したように、根本的な問題は、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 のべき乗サイズのバッファーを要求していることが前提です。


    Linux
    1. Linux メモリ管理 – スワッピング、キャッシュ、および共有 VM

    2. ioremap() とは

    3. デバイスからユーザー空間メモリーへの DMA への Linux カーネル デバイス ドライバー

    1. SO_SNDBUF の効果

    2. ページサイズがアラインされたメモリをどのように割り当てますか?

    3. Linux 非アクティブ メモリ

    1. Linuxのメモリ使用量

    2. Sip経由でFAXを送信しますか?

    3. PHPのメモリ制限