5

大規模なコード ベースを Linux カーネル デバイス ドライバーに移植しています。ASIC は膨大な数の DMA チャネルを使用します。

でメモリを kmalloc しますGFP_KERNEL|GFP_DMA。DMA を開始する前に、dma_map_single を使用してハードウェア (物理) メモリ アドレスを取得し、ハードウェアに供給します。(また、dcache からメモリをフラッシュ/無効化しますか?) DMA が完了すると、データへの CPU アクセスが必要になることがありますが、頻繁ではありません。コードを介してデータにアクセスする前に、dma_unmap_single を実行してキャッシュの一貫性の問題を回避します。

CPU アクセスがまったく必要ない場合でも、呼び出す必要がありますdma_unmap_singleか? dma_unmap_singleすべてのポインター I を使用する必要がありますdma_map_singleか? 解放さdma_map_singleれるリソース (テーブル エントリなど) を消費しますか?dma_unmap_single

DMA-API.txt は、適切な DMA メモリの衛生状態について明確ではありません。

ありがとう!

4

3 に答える 3

5

dma_map_singleDMA 転送用のメモリをマップします。メモリへの物理ポインタを取得するため、デバイスはそのアドレスに DMA できます。

dma_unmap_single上記でマップされたメモリをアンマップします。転送が終了したら、これを行う必要があります。

メモリ領域をマップして複数の DMA 転送に使用し、ジョブが完了したらマップを解除できます。DMA メモリにアクセスするたびに、同期する必要があります。デバイスがメモリにアクセスする場合は、次のことを行う必要がありますdma_sync_single_for_device。ホストがメモリにアクセスしようとしている場合は、そうする必要がありますdma_sync_single_for_cpu

于 2013-04-23T21:57:58.720 に答える
0

簡潔な答え:

はい。dma_unmap_singleマップされているすべてのバッファを使用する必要がありますdma_map_single

長い答え:

はい。すべての DMA トランザクションの最後に
呼び出す必要があります。dma_unmap_single

しかし、dma_map_single / dma_unmap_singleはコストのかかる操作であるため、(パケットが大きすぎない場合) DMA マップ バッファを再利用することを好む場合があるためdma_unmap_single、DMA トランザクションの最後にを呼び出す代わりに、 を呼び出しdma_sync_single_for_cpuて、 DMA マップ バッファを別のバッファに挿入してから を呼び出すことdma_sync_single_for_deviceで、次の DMA トランザクションの前に、すでに DMA マップされているバッファをアンマップして再マップしなくても再利用できるようになりました。

パケットの大きさのしきい値はアーキテクチャ間で異なり、測定する必要があると思います。

measure_time(dma_sync_single_for_cpu + memcpy(packet_size) + dma_sync_single_for_device) 
>?<
measure_time(dma_unmap_single + dma_map_single)

短い例:

    if (frame_len < FRAME_LEN_THRESHOLD) {            
        skb = netdev_alloc_skb_ip_align(priv->dev, frame_len);
        if (unlikely(!skb)) {
            printk("packet dropped\n");
            continue;
        }
        
        dma_sync_single_for_cpu(priv->device, rx_skbuff_dma[entry],                                   
                                frame_len, DMA_FROM_DEVICE);

        // same as memcpy
        skb_copy_to_linear_data(skb, rx_skbuff[entry]->data, frame_len);

        dma_sync_single_for_device(priv->device, rx_skbuff_dma[entry],
                                   frame_len, DMA_FROM_DEVICE);

        /* now we can reuse rx_skbuff_dma[entry]. 
           no need to call dma_unmap_single */                           
    } else {
        skb = rx_skbuff[entry];
        if (unlikely(!skb)) {
            printk("packet dropped\n");
            continue;
        }

        rx_skbuff[entry] = NULL;

        dma_unmap_single(priv->device, rx_skbuff_dma[entry],
                         priv->dma_buffer_size, DMA_FROM_DEVICE);

        /* if we want to use rx_skbuff_dma[entry] for another DMA transaction, 
           we will need to realocate a buffer and call dma_map_single */                 
    }
于 2020-10-16T13:33:46.007 に答える