【问题标题】:Why do we need DMA pool?为什么我们需要 DMA 池?
【发布时间】:2020-03-07 03:12:21
【问题描述】:

我正在阅读https://www.kernel.org/doc/Documentation/DMA-API.txt,但我不明白为什么需要 DMA 池。

为什么不使用 PAGE_SIZE DMA 分配内存 dma_alloc_coherent 并使用偏移量?

另外,为什么动态 DMA 对网络设备驱动程序有用,而不是重复使用相同的 DMA 内存?

【问题讨论】:

  • 1.我没有得到第一个问题。 DMA 池是一个管理页面大小块的基础设施,用于跟踪偏移量等。 DMA 池正是您所说的。
  • 2.我不知道动态DMA的概念,那是什么?
  • 3.很大程度上取决于硬件、软件、操作系统模型(RT 或非 RT)等...
  • 哦,好吧,我以为它分配了几个较小的区域。
  • 动态 DMA 正在为单个事务 (rd/wr) 分配缓冲区并在此之后释放。好像是在网络驱动中使用的。

标签: linux-kernel driver linux-device-driver


【解决方案1】:

警告:我不是 linux 内核方面的专家。

LDD book(这可能是更好的开始阅读)表示 DMA 池更适合较小的 dma 区域(比页面短)-https://static.lwn.net/images/pdf/LDD3/ch15.pdf 第 447 页或https://www.oreilly.com/library/view/linux-device-drivers/0596005903/ch15.html,“DMA 池”部分:

DMA 池是一种用于小型、一致的 DMA 映射的分配机制。从dma_alloc_coherent 获得的映射可能最小为一页。如果您的设备需要比这更小的 DMA 区域,您可能应该使用 DMA 池。 DMA 池在您可能想要对嵌入在较大结构中的小区域执行 DMA 的情况下也很有用。一些非常模糊的驱动程序错误已被追溯到与小 DMA 区域相邻的结构字段的缓存一致性问题。为避免此问题,您应该始终明确地为 DMA 操作分配区域,远离其他非 DMA 数据结构。 ...分配由dma_pool_alloc处理

https://www.kernel.org/doc/Documentation/DMA-API-HOWTO.txt 中也有同样的说明

如果您的驱动程序需要大量较小的内存区域,您可以编写 自定义代码来细分 dma_alloc_coherent() 返回的页面, 或者您可以使用 dma_pool API 来执行此操作。 dma_pool 就像 一个 kmem_cache,但它使用 dma_alloc_coherent(),而不是 __get_free_pages()。 此外,它了解对齐的常见硬件约束, 比如需要在 N 字节边界上对齐的队列头。

因此,DMA 池针对较小的分配进行了优化。您可以为每个小的 dma 内存单独使用dma_alloc_coherent(具有更大的开销),或者您可以尝试构建自己的池(更多自定义代码来管理偏移和分配)。但是 DMA 池已经实现并且可以使用。

应针对您的案例分析方法的性能。

网络驱动中的动态dma注册示例(用于skb片段): https://elixir.bootlin.com/linux/v4.6/source/drivers/net/ethernet/realtek/r8169.c

static struct sk_buff *rtl8169_alloc_rx_data
    mapping = dma_map_single(d, rtl8169_align(data), rx_buf_sz,
                 DMA_FROM_DEVICE);
static int rtl8169_xmit_frags
        mapping = dma_map_single(d, addr, len, DMA_TO_DEVICE);
static netdev_tx_t rtl8169_start_xmit
    mapping = dma_map_single(d, skb->data, len, DMA_TO_DEVICE);
static void rtl8169_unmap_tx_skb
    dma_unmap_single(d, le64_to_cpu(desc->addr), len, DMA_TO_DEVICE);

就地为 dma 注册 skb 片段(如果 NIC 芯片支持 sg dma)比将每个片段从 skb 复制到某些 DMA 内存中更好。查看“了解 Linux 网络内部结构”一书的“dev_queue_xmit 函数”部分和第 21 章;和skb_linearize

DMA 池使用示例 - nvme 驱动程序(prp 为 part of Submission Queue Element,物理区域页面,64 位指针,以及“PRP 列表包含通常没有偏移的 PRP 列表。”):

https://elixir.bootlin.com/linux/v4.6/source/drivers/nvme/host/pci.c#L1807

static int nvme_setup_prp_pools(struct nvme_dev *dev)
{
    dev->prp_page_pool = dma_pool_create("prp list page", dev->dev,
                        PAGE_SIZE, PAGE_SIZE, 0);

static bool nvme_setup_prps
    prp_list = dma_pool_alloc(pool, GFP_ATOMIC, &prp_dma);

static void nvme_free_iod
        dma_pool_free(dev->prp_small_pool, list[0], prp_dma);

【讨论】:

  • 我不确定 OP 是否询问了 same 内存的映射和取消映射(OP 指的是不同的分配)。
  • @0andriy,问题作者在哪里提到了不同的分配?
  • ...为什么...而不是重复使用相同的 DMA 内存?这意味着 OP 正在谈论不同的分配。跨度>
  • 通过 PCIe 移动数据有很多选择。像往常一样,小/大是相对的。不能更令人困惑。但是感谢您的总结!
猜你喜欢
  • 2019-06-09
  • 2013-04-12
  • 2014-06-18
  • 2017-02-26
  • 2011-04-03
  • 2017-07-27
  • 2020-09-21
  • 2020-03-09
  • 2018-12-24
相关资源
最近更新 更多