You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过DMA API正确分配多个预留内存区域

如何为单个驱动正确分配多个预留内存区域

我需要为单个设备驱动预留3至4个内存区域,参考Xilinx相关指南实现了单个内存区域的预留,但当前配置的设备树包含两个预留内存区域,驱动的probe函数仅能成功分配第一个区域。以下是我的设备树配置及驱动probe函数代码,请问如何正确为每个区域分配内存?

设备树配置

reserved-memory {
    #address-cells = <2>;
    #size-cells = <2>;
    ranges;
    reserved_m1: buffer1@60000000 {
        compatible = "shared-dma-pool";
        no-map;      
        reg = <0x0 0x60000000 0x0 0x00400000>;   
    };
    reserved_m2: buffer2@80000000 {
        compatible = "shared-dma-pool";
        no-map;      
        reg = <0x0 0x80000000 0x0 0x00100000>;   
    };
};
my_driver@0 {
    compatible = "dummy,my_driver";
    status = "okay";
    memory-region = <&reserved_m1>, <&reserved_m2>;
};

原驱动probe函数代码

static int my_driver_probe(struct platform_device *pdev)
{
    ...
    rc = of_reserved_mem_device_init(&pdev->dev);
    debug = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
    v_addr_m1 = dma_alloc_coherent(&pdev->dev, MY_MEM1_SIZE, &paddr, GFP_KERNEL); 
    printk("Allocated coherent memory Size: %d MB, vaddr: 0x%0llX paddr: 0x%0llX\n", MY_MEM_SIZE / 1024 / 1024, (u64)v_addr, (u32)paddr);

    rc = of_reserved_mem_device_init_by_idx(&pdev->dev, np, 1);
    v_addr_m2 = dma_alloc_coherent(&pdev->dev, MY_MEM2_SIZE, &paddr_m2, GFP_KERNEL);
    
    printk("Allocated coherent memory Size: %d MB, vaddr: 0x%0llX paddr: 0x%0llX\n", MY_MEM_SIZE / 1024 / 1024, (u64)v_addr, (u32)paddr);
    ...
    ...
}

问题分析

原代码存在几个关键错误:

  • of_reserved_mem_device_init会将第一个内存区域绑定到设备,后续调用of_reserved_mem_device_init_by_idx不会新增绑定,反而可能导致逻辑冲突。
  • dma_alloc_coherent默认只会从第一个绑定的预留内存池分配,无法直接获取第二个区域。
  • 代码中np变量未定义,printk存在变量不匹配问题(如用全局MY_MEM_SIZE代替对应区域的MY_MEM1_SIZE/MY_MEM2_SIZE,v_addr未对应v_addr_m1/v_addr_m2)。

解决方案

方法一:直接获取物理地址并手动映射(推荐)

由于设备树配置了no-map,内核不会自动映射该内存区域,可直接读取每个区域的物理地址和大小,用devm_ioremap完成虚拟地址映射:

#include <linux/of_reserved_mem.h>
#include <linux/io.h>

static int my_driver_probe(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;
    struct device_node *np = dev->of_node;
    struct reserved_mem *rmem;
    void __iomem *v_addr_m1, *v_addr_m2;
    phys_addr_t paddr_m1, paddr_m2;
    size_t size_m1, size_m2;

    // 处理第一个预留内存区域
    rmem = devm_of_reserved_mem_lookup_by_idx(dev, np, 0);
    if (!rmem) {
        dev_err(dev, "Failed to lookup reserved memory index 0\n");
        return -ENODEV;
    }
    paddr_m1 = rmem->base;
    size_m1 = rmem->size;
    v_addr_m1 = devm_ioremap(dev, paddr_m1, size_m1);
    if (!v_addr_m1) {
        dev_err(dev, "Failed to remap reserved memory 0\n");
        return -ENOMEM;
    }
    dev_info(dev, "Reserved memory 0: size %zu MB, vaddr 0x%pK, paddr 0x%pa\n",
             size_m1 / 1024 / 1024, v_addr_m1, &paddr_m1);

    // 处理第二个预留内存区域
    rmem = devm_of_reserved_mem_lookup_by_idx(dev, np, 1);
    if (!rmem) {
        dev_err(dev, "Failed to lookup reserved memory index 1\n");
        return -ENODEV;
    }
    paddr_m2 = rmem->base;
    size_m2 = rmem->size;
    v_addr_m2 = devm_ioremap(dev, paddr_m2, size_m2);
    if (!v_addr_m2) {
        dev_err(dev, "Failed to remap reserved memory 1\n");
        return -ENOMEM;
    }
    dev_info(dev, "Reserved memory 1: size %zu MB, vaddr 0x%pK, paddr 0x%pa\n",
             size_m2 / 1024 / 1024, v_addr_m2, &paddr_m2);

    // 后续业务逻辑

    return 0;
}

方法二:逐个绑定预留区域并分配内存

如果需要使用dma_alloc_coherent分配,可逐个绑定预留区域,分配完成后释放绑定,再处理下一个区域:

static int my_driver_probe(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;
    struct device_node *np = dev->of_node;
    void *v_addr_m1, *v_addr_m2;
    dma_addr_t paddr_m1, paddr_m2;
    int rc;

    // 绑定第一个区域并分配内存
    rc = of_reserved_mem_device_init_by_idx(dev, np, 0);
    if (rc) {
        dev_err(dev, "Failed to init reserved memory index 0\n");
        return rc;
    }
    v_addr_m1 = dma_alloc_coherent(dev, MY_MEM1_SIZE, &paddr_m1, GFP_KERNEL);
    if (!v_addr_m1) {
        dev_err(dev, "Failed to allocate from region 0\n");
        of_reserved_mem_device_release(dev);
        return -ENOMEM;
    }
    dev_info(dev, "Allocated from region 0: size %d MB, vaddr 0x%pK, paddr 0x%pad\n",
             MY_MEM1_SIZE / 1024 / 1024, v_addr_m1, &paddr_m1);
    of_reserved_mem_device_release(dev);

    // 绑定第二个区域并分配内存
    rc = of_reserved_mem_device_init_by_idx(dev, np, 1);
    if (rc) {
        dev_err(dev, "Failed to init reserved memory index 1\n");
        dma_free_coherent(dev, MY_MEM1_SIZE, v_addr_m1, paddr_m1);
        return rc;
    }
    v_addr_m2 = dma_alloc_coherent(dev, MY_MEM2_SIZE, &paddr_m2, GFP_KERNEL);
    if (!v_addr_m2) {
        dev_err(dev, "Failed to allocate from region 1\n");
        of_reserved_mem_device_release(dev);
        dma_free_coherent(dev, MY_MEM1_SIZE, v_addr_m1, paddr_m1);
        return -ENOMEM;
    }
    dev_info(dev, "Allocated from region 1: size %d MB, vaddr 0x%pK, paddr 0x%pad\n",
             MY_MEM2_SIZE / 1024 / 1024, v_addr_m2, &paddr_m2);

    // 后续业务逻辑

    return 0;
}

注意事项

  1. 方法一更适合no-map配置的预留内存,直接映射物理地址,避免内存池分配的限制。
  2. 方法二中每次分配后必须释放当前绑定的预留内存,否则无法绑定下一个区域。
  3. 确保MY_MEM1_SIZE/MY_MEM2_SIZE与设备树中reg字段的大小完全匹配(如第一个区域为4MB,第二个为1MB)。

内容的提问来源于stack exchange,提问作者Pea

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 21:05:00