Linux内存管理:CMA

http://blog.csdn.net/hongzg1982/article/details/50244495

某些驱动需要用到一大块连续的物理内存,但使用kmalloc等很分配很大的连续内存。
所以这里有一种三星实现叫CMA的方式,来连续的大内存分配。

Why is it needed?

  • Issue 1: Camera, Video Codec等Multimedia Device需要连续的数MB大小的Memory,但kmalloc/alloc_page不能保证分配到连续的数MB或者更大的内存

  • Issue 2: 在Booting Time预留内存的方式也可以(像早前的PMEM方式),但这么预留的Memory只能被特定的Device驱动所使用,System不能分配这部分内容。如果该Device不用或者所使用的内存没有预留的大的时候,就造成Memory浪费。

  • Issue 3: Multimedia Device之间需要共享Reserve的Memory
    E.g: Camera(FIMC)利用Video Codec(MFC)去Endoding的时候,需要从FIMC分配MFC Reserve的Memory。

CMA Solution

  • Issue 1: 可以分配连续的大的内存(Device需要的连续的大的内存,可以在Boot的时候进行Reserve之后再进行分配)

  • Issue 2: 防止Reserve方式的Memory浪费
    1) 支持Migration功能,所以即使是被某个驱动设备Reserve的区域,在驱动没有使用的时候System可以对该段内存进行分配使用
    2) 在System使用这段内存的时候,如果驱动要求分配这个预留的内存,System memory就会被Migration到其他内存区域,之后这段内存被分配给驱动设备

  • Issue 3: 驱动设备间的内存共享(通过CMA被Reserve的内存会通过CMA进行管理,所以可以驱动设备间共享该段,比如FIMC可以共享MFC预留的内存区域等)

CMA内存Reserve

1.查看整个物理内存大小

在系统启动的时候,整个物理内存保存在memblock变量里。通过在sanity_check_meminfo()函数里添加log来打印memblock的内容,这里可以看到整个物理内存大小为1.5GB。

<6>[0.000000]  [0:swapper:0] sanity_check_meminfo memblock.memory.cnt=2
<6>[0.000000]  [0:swapper:0] pys_addr vmalloc_limit = 0xa9c00000
<6>[0.000000]  [0:swapper:0] count = 1 , reg->base =0x80000000 , reg->size =0x2fd00000
<6>[0.000000]  [0:swapper:0] count = 2 , reg->base =0xb0000000 , reg->size =0x30000000
<6>[0.000000]  [0:swapper:0] arm_lowmem_limit =0xa9c00000

2.预留CMA相关的内存

之后在arm_memblock_init()->dma_contiguous_reserve()函数中,读取device tree相关的设置来预留内存给CMA

void __init arm_memblock_init(const struct machine_desc *mdesc) {
    /* Register the kernel text, kernel data and initrd with memblock. */
#ifdef CONFIG_XIP_KERNEL
    memblock_reserve(__pa(_sdata), _end - _sdata);
#else
    memblock_reserve(__pa(_stext), _end - _stext); //预留内核代码区域
#endif
#ifdef CONFIG_BLK_DEV_INITRD //预留initramfs区域
    if (phys_initrd_size &&
        !memblock_is_region_memory(phys_initrd_start, phys_initrd_size)) {
        pr_err("INITRD: 0x%08lx+0x%08lx is not a memory region - disabling initrd\n",
               phys_initrd_start, phys_initrd_size);
        phys_initrd_start = phys_initrd_size = 0;
    }
    if (phys_initrd_size &&
        memblock_is_region_reserved(phys_initrd_start, phys_initrd_size)) {
        pr_err("INITRD: 0x%08lx+0x%08lx overlaps in-use memory region - disabling initrd\n",
               phys_initrd_start, phys_initrd_size);
        phys_initrd_start = phys_initrd_size = 0;
    }
    if (phys_initrd_size) {
        memblock_reserve(phys_initrd_start, phys_initrd_size);

        /* Now convert initrd to virtual addresses */
        initrd_start = __phys_to_virt(phys_initrd_start);
        initrd_end = initrd_start + phys_initrd_size;
    }
#endif

    arm_mm_memblock_reserve();//预留page table区域

    /* reserve any platform specific memblock areas */
    if (mdesc->reserve)
        mdesc->reserve();

    early_init_fdt_scan_reserved_mem();//??reserved-memory什么的,但现在好像不用了

    /*
     * reserve memory for DMA contigouos allocations,
     * must come from DMA area inside low memory
     */
    //这个就是读取相关的device tree来预留相关的内存的
    dma_contiguous_reserve(min(arm_dma_limit, arm_lowmem_limit));

    arm_memblock_steal_permitted = false;
    memblock_allow_resize();
    memblock_dump_all();
}

以下看dma_contiguous_reserve()函数都怎么读device tree并预留内存给cma的

void __init dma_contiguous_reserve(phys_addr_t limit)
{
    phys_addr_t sel_size = 0;
    int i;

#ifdef CONFIG_OF
    of_scan_flat_dt(cma_fdt_scan, NULL); //这里就是读取device tree的内容的,具体看下面的函数说明
#endif
    pr_debug("%s(limit %pa)\n", __func__, &limit);

    if (size_cmdline != -1) {
        sel_size = size_cmdline;
    } else {
    //这里留8MB,
    //cma: CMA: reserved 8 MiB at 0xa9400000 for default region
#ifdef CONFIG_CMA_SIZE_SEL_MBYTES
        sel_size = size_bytes;
#elif defined(CONFIG_CMA_SIZE_SEL_PERCENTAGE)
        sel_size = cma_early_percent_memory();
#elif defined(CONFIG_CMA_SIZE_SEL_MIN)
        sel_size = min(size_bytes, cma_early_percent_memory());
#elif defined(CONFIG_CMA_SIZE_SEL_MAX)
        sel_size = max(size_bytes, cma_early_percent_memory());
#endif
    }

    //再调用sanity_check_meminfo()
    dma_contiguous_early_removal_fixup();
    allow_memblock_alloc = true;

    for (i = 0; i < cma_area_count; i++) {
        if (cma_areas[i].base == 0) {
            int ret;

            ret = __dma_contiguous_reserve_memory(
                        cma_areas[i].size,
                        cma_areas[i].alignment,
                        cma_areas[i].limit,
                        &cma_areas[i].base);
            if (ret) {
                pr_err("CMA: failed to reserve %ld MiB for %s\n",
                       (unsigned long)cma_areas[i].size / SZ_1M,
                       cma_areas[i].name);
                memmove(&cma_areas[i], &cma_areas[i+1],
                   (cma_area_count - i)*sizeof(cma_areas[i]));
                cma_area_count--;
                i--;
                continue;
            }
            //没有基地址的部分,都通过__dma_contiguous_reserve_memory分配
            //地址之后,保存到dma_mmu_remap区域中!!
            dma_contiguous_early_fixup(cma_areas[i].base,
                            cma_areas[i].size);
        }

        pr_info("CMA: reserved %ld MiB at %pa for %s\n",
            (unsigned long)cma_areas[i].size / SZ_1M,
            &cma_areas[i].base, cma_areas[i].name);
    }

    if (sel_size) {
        phys_addr_t base = 0;
        pr_debug("%s: reserving %ld MiB for global area\n", __func__,
             (unsigned long)sel_size / SZ_1M);
        //由于已经使能了CONFIG_CMA_SIZE_SEL_MBYTES,
        //而且CONFIG_CMA_SIZE_MBYTES = 8
        //所以会打印打印的 cma: CMA: reserved 8 MiB at 0xa9400000 for default region
        if (dma_contiguous_reserve_area(sel_size, &base, limit, NULL,
            CMA_RESERVE_AREA ? 0 : 1, false) == 0) {
            pr_info("CMA: reserved %ld MiB at %pa for default region\n",
                (unsigned long)sel_size / SZ_1M, &base);
            dma_contiguous_def_base = base;
        }
    }
}
//
int __init cma_fdt_scan(unsigned long node, const char *uname,
                int depth, void *data)
{
    phys_addr_t base, size;
    int len;
    const __be32 *prop;
    const char *name;
    bool in_system;
    bool remove;
    unsigned long size_cells = dt_root_size_cells;
    unsigned long addr_cells = dt_root_addr_cells;
    phys_addr_t limit = MEMBLOCK_ALLOC_ANYWHERE;
    const char *status;

    //每个需要预留的内存必须有"linux,reserve-contiguous-region"!!
    //看实际的device tree也是,每个部分都有"linux,reserve-contiguous-region"
    if (!of_get_flat_dt_prop(node, "linux,reserve-contiguous-region", NULL))
        return 0;

    //查看是否有status,没有就算了,但如果有的话,必须是"ok",不然这个区域就不会被读取
    status = of_get_flat_dt_prop(node, "status", NULL);
    /*
     * Yes, we actually want strncmp here to check for a prefix
     * ok vs. okay
     */
    if (status && (strncmp(status, "ok", 2) != 0))
        return 0;

    prop = of_get_flat_dt_prop(node, "#size-cells", NULL);
    if (prop)
        size_cells = be32_to_cpup(prop);

    prop = of_get_flat_dt_prop(node, "#address-cells", NULL);
    if (prop)
        addr_cells = be32_to_cpup(prop);

    prop = of_get_flat_dt_prop(node, "reg", &len);
    if (!prop || depth != 2)
        return 0;

    base = dt_mem_next_cell(addr_cells, &prop);
    size = dt_mem_next_cell(size_cells, &prop);


    name = of_get_flat_dt_prop(node, "label", NULL);

    //以下读取几个重要的设置,这些会在dma_contiguous_reserve_area()函数中被用到,
    in_system =
        of_get_flat_dt_prop(node, "linux,reserve-region", NULL) ? 0 : 1;

    prop = of_get_flat_dt_prop(node, "linux,memory-limit", NULL);
    if (prop)
        limit = be32_to_cpu(prop[0]);

    remove =
         of_get_flat_dt_prop(node, "linux,remove-completely", NULL) ? 1 : 0;

    pr_info("Found %s, memory base %pa, size %ld MiB, limit %pa\n", uname,
            &base, (unsigned long)size / SZ_1M, &limit);

    //
    dma_contiguous_reserve_area(size, &base, limit, name,
                    in_system, remove);

    return 0;
}

//cma_fdt_scan()函数读取的device tree内容
    memory {
        #address-cells = <2>;
        #size-cells = <2>;

/* Additionally Reserved 6MB for TIMA and Increased the TZ app size
 * by 2MB [total 8 MB ]
 */
        //高通平台trustzone存放的地址,如果这个地址改了,必须要修改
        //TZBSP_EBI1_TZ_APP_BASE和TZBSP_EBI1_TZ_APP_END等的地址
        //不然kernel启动不了!!
        external_image_mem: external_image__region@0 {
            linux,reserve-contiguous-region;
            linux,reserve-region;
            linux,remove-completely;
            reg = <0x0 0x85500000 0x0 0x01300000>;
            label = "external_image_mem";
        };

        //高通平台(msm8916)modem的存放地址开始地址和大小取决于
        //modem binary
        modem_adsp_mem: modem_adsp_region@0 {
            linux,reserve-contiguous-region;
            linux,reserve-region;
            linux,remove-completely;
            reg = <0x0 0x86800000 0x0 0x05800000>;
            label = "modem_adsp_mem";
        };
        //这个部分是wcnss相关的存放地址,这部分内存需要紧挨着modem内存。
        //modem大小修改的时候,这个内存区域的开始地址也需要相应调整
        peripheral_mem: pheripheral_region@0 {
            linux,reserve-contiguous-region;
            linux,reserve-region;
            linux,remove-completely;
            reg = <0x0 0x8C000000 0x0 0x0600000>;
            label = "peripheral_mem";
        };

        venus_mem: venus_region@0 {
            linux,reserve-contiguous-region;
            linux,reserve-region;
            linux,remove-completely;
            reg = <0x0 0x8C600000 0x0 0x0500000>;
            label = "venus_mem";
        };

        secure_mem: secure_region@0 {
            linux,reserve-contiguous-region;
            reg = <0 0 0 0x6D00000>;
            label = "secure_mem";
        };

        qseecom_mem: qseecom_region@0 {
            linux,reserve-contiguous-region;
            reg = <0 0 0 0xD00000>;
            label = "qseecom_mem";
        };

        audio_mem: audio_region@0 {
            linux,reserve-contiguous-region;
            reg = <0 0 0 0x314000>;
            label = "audio_mem";
        };

        cont_splash_mem: splash_region@8E000000 {
            linux,reserve-contiguous-region;
            linux,reserve-region;
            reg = <0x0 0x8E000000 0x0 0x1400000>;
            label = "cont_splash_mem";
        };
    };

以下简单说一下高通msm8916平台,modem大小检查以及修改方法。
1) modem binary的大小可以从以下编译的log里边看出来!!(modem_proc/build/ms目录下的pplk-XXX.log或者build_xxxx.log)。
根据大小对齐1MB大小,就是modem binary需要流出来的大小。看如下例子里边的log,总的大小是77.04,
所以需要在上面的dtsi文件中留出来78MB就可以。

  Image loaded at virtual address 0xc0000000 
  Image:                                   55.44 MiB 
  AMSS Heap:                                7.50 MiB (dynamic) 
  MPSS Heap:                                4.00 MiB (dynamic) 
  DSM Pools:                                5.06 MiB  
  Q6Zip RO, Swap Pool:                      2.00 MiB (dynamic) 
  Q6Zip RW, Swap Pool:                      1.00 MiB (dynamic) 
  Q6Zip RW, dlpager Heap:                   1.00 MiB 
  Extra:                                    0.54 MiB 
  Pad ding:                                  0.37 MiB 
  End Address Alignment:                    0.13 MiB 
  Total:                                   77.04 MiB 
  Available:                                7.96 MiB

2) 然后去修改modem_proc/config/xxx/ 目录下的cust_config.xml文件中修改modem大小

      
        "DEFAULT_PHYSPOOL"> 
             "0x88000000"  size="0x5500000" /> 
             "0x88000000" size="0x4E00000" /> 
          

3) 重新编译,然后在第一步中编译出来的文件中重新确认大小

以下就是实际操作cma_areas变量的函数

//有"linux,reserve-region"的区域 to_system就是false
//有"linux,remove-completely"的区域,remove就是true
int __init dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t *res_base,
                       phys_addr_t limit, const char *name,
                       bool to_system, bool remove)
{
    phys_addr_t base = *res_base;
    phys_addr_t alignment = PAGE_SIZE;
    int ret = 0;

    pr_debug("%s(size %lx, base %pa, limit %pa)\n", __func__,
         (unsigned long)size, &base,
         &limit);

    /* Sanity checks */
    if (cma_area_count == ARRAY_SIZE(cma_areas)) {
        pr_err("Not enough slots for CMA reserved regions!\n");
        return -ENOSPC;
    }

    if (!size)
        return -EINVAL;

    /* Sanitise input arguments */
    if (!remove)
        alignment = PAGE_SIZE << max(MAX_ORDER - 1, pageblock_order);
    base = ALIGN(base, alignment);
    size = ALIGN(size, alignment);
    limit &= ~(alignment - 1);

    //有base地址的设置,如果不与其他预留的地址冲突的话,就调用memblock_reset从memblock.memory中去掉!!
    //所以上面device tree设置中external_image_mem,modem_adsp_mem,peripheral_mem,venus_mem被
    //直接从memblock给去掉了,,这个可以看一下前后对比的memblock打印的内容
    /* Reserve memory */
    if (base) {
        if (memblock_is_region_reserved(base, size) ||
            memblock_reserve(base, size) < 0) {
            ret = -EBUSY;
            goto err;
        }
    } else {
        //如果是没有指定base地址的,就要通过__dma_contiguous_reserve_memory()函数
        //从memblock相关的接口分配到满足大小的地址,并返回base地址了
        ret = __dma_contiguous_reserve_memory(size, alignment, limit,
                            &base);
        if (ret)
            goto err;
    }

    //有base地址,有"linux,remove-completely",但没有"linux,reserve-region"就会报错!
    //所以查看上面的device tree内容,external_image_mem,modem_adsp_mem,
    //peripheral_mem,venus_mem等有base地址的都是既有"linux,remove-completely"
    //又有"linux,reserve-region"的就会跑到下面的if(!to_system)内
    if (base && remove) {
        if (!to_system) {
            //这里边就是把上面的区域从memblock.memory里边去掉,加到membloc.reserve里边等操作的
            memblock_free(base, size);
            memblock_remove(base, size);
        } else {
            WARN(1, "Removing is incompatible with staying in the system\n");
        }
    }

    /*
     * Each reserved area must be initialised later, when more kernel
     * subsystems (like slab allocator) are available.
     */
    cma_areas[cma_area_count].base = base;
    cma_areas[cma_area_count].size = size;
    cma_areas[cma_area_count].name = name;
    cma_areas[cma_area_count].alignment = alignment;
    cma_areas[cma_area_count].limit = limit;
    cma_areas[cma_area_count].to_system = to_system;
    cma_area_count++;
    *res_base = base;


    //以下是有base地址,且没有定义"linux,remove-completely"的
    //比如fb相关的,上面的定义是"cont_splash_mem"
    //这些内存都会调用dma_contiguous_early_fixup()函数
    //这个函数很简单,就是把相关的内存都保存在dma_mmu_remap中。
    //有多少区域,用dma_mmu_remap_num来表示。
    /* Architecture specific contiguous memory fixup. */
    if (!remove && base)
        dma_contiguous_early_fixup(base, size);
    return 0;
err:
    pr_err("CMA: failed to reserve %ld MiB\n", (unsigned long)size / SZ_1M);
    return ret;
}
//这个函数可以总结一些规律:
//举例来说,像msm8916这种,如果不是AP这边控制的内存,像modem相关的内存,有base地址
//而且都是定义了remove,从内存中整个去掉,因为不是AP这边需要控制的部分,只要预留出来
//就好。像external_image_mem,modem_adsp_mem,peripheral_mem,venus_mem就是这样的。
//这些部分看上面的函数,就是直接从memblock.memory中挖掉,放到memblock.reserve里。
//不会在paging_init的时候被map到lowmemory里边。

//但frambuffer对应的内存(cont_splash_mem),既要预留,又要AP这边控制的,就只定义了base地址,但没有定义
//"linux,remove-completely"来去掉这部分内存!!这种会通过dma_contiguous_early_fixup()把这部分内存
//放到dma_mmu_remap中!!这部分会在paging_init()->dma_contiguous_remap()中map起来(这部分在dma部分细说)

//当然cma_areas和cma_area_count是会保存所有上面所说的内容的!!

这里写图片描述

<6>[0.000000]  [0:swapper:0] sanity_check_meminfo memblock.memory.cnt=2
<6>[0.000000]  [0:swapper:0] pys_addr vmalloc_limit = 0xa9c00000
<6>[0.000000]  [0:swapper:0] count = 1 , reg->base =0x80000000 , reg->size =0x2fd00000
<6>[0.000000]  [0:swapper:0] count = 2 , reg->base =0xb0000000 , reg->size =0x30000000
<6>[0.000000]  [0:swapper:0] arm_lowmem_limit =0xa9c00000
<6>[0.000000]  [0:swapper:0] cma: Found external_image__region@0, memory base 0x85500000, size 19 MiB, limit 0xffffffff
<6>[0.000000]  [0:swapper:0] cma: Found modem_adsp_region@0, memory base 0x86800000, size 88 MiB, limit 0xffffffff
<6>[0.000000]  [0:swapper:0] cma: Found pheripheral_region@0, memory base 0x8c000000, size 6 MiB, limit 0xffffffff
<6>[0.000000]  [0:swapper:0] cma: Found venus_region@0, memory base 0x8c600000, size 5 MiB, limit 0xffffffff
<6>[0.000000]  [0:swapper:0] cma: Found secure_region@0, memory base 0x00000000, size 109 MiB, limit 0xffffffff
<6>[0.000000]  [0:swapper:0] cma: Found qseecom_region@0, memory base 0x00000000, size 13 MiB, limit 0xffffffff
<6>[0.000000]  [0:swapper:0] cma: Found audio_region@0, memory base 0x00000000, size 3 MiB, limit 0xffffffff
<6>[0.000000]  [0:swapper:0] cma: Found splash_region@8E000000, memory base 0x8e000000, size 20 MiB, limit 0xffffffff
<6>[0.000000]  [0:swapper:0] sanity_check_meminfo memblock.memory.cnt=3
<6>[0.000000]  [0:swapper:0] pys_addr vmalloc_limit = 0xa9c00000
<6>[0.000000]  [0:swapper:0] count = 1 , reg->base =0x80000000 , reg->size =0x5500000
<6>[0.000000]  [0:swapper:0] count = 2 , reg->base =0x8cb00000 , reg->size =0x23200000
<6>[0.000000]  [0:swapper:0] count = 3 , reg->base =0xb0000000 , reg->size =0x30000000
<6>[0.000000]  [0:swapper:0] arm_lowmem_limit =0xb1200000
<6>[0.000000]  [0:swapper:0] cma: CMA: reserved 19 MiB at 0x85500000 for external_image_mem
<6>[0.000000]  [0:swapper:0] cma: CMA: reserved 88 MiB at 0x86800000 for modem_adsp_mem
<6>[0.000000]  [0:swapper:0] cma: CMA: reserved 6 MiB at 0x8c000000 for peripheral_mem
<6>[0.000000]  [0:swapper:0] cma: CMA: reserved 5 MiB at 0x8c600000 for venus_mem
<6>[0.000000]  [0:swapper:0] cma: CMA: reserved 112 MiB at 0xd9000000 for secure_mem
<6>[0.000000]  [0:swapper:0] cma: CMA: reserved 16 MiB at 0xd8000000 for qseecom_mem
<6>[0.000000]  [0:swapper:0] cma: CMA: reserved 4 MiB at 0xd7c00000 for audio_mem
<6>[0.000000]  [0:swapper:0] cma: CMA: reserved 20 MiB at 0x8e000000 for cont_splash_mem
<6>[0.000000]  [0:swapper:0] cma: CMA: reserved 8 MiB at 0xa9400000 for default region
<4>[0.000000]  [0:swapper:0] Memory policy: ECC disabled, Data cache writealloc

CMA内存的分配和使用

相关的接口都在dma-contiguous.c文件里边

//以下函数为每个cma_areas初始化cma并保存到cma_areas[i].cma里边
//cma的定义如下:
struct cma {
    unsigned long   base_pfn;
    unsigned long   count;
    unsigned long   *bitmap;
    bool in_system;
    struct mutex lock;
};

static int __init cma_init_reserved_areas(void)
{
    struct cma *cma;
    int i;

    for (i = 0; i < cma_area_count; i++) {
        phys_addr_t base = PFN_DOWN(cma_areas[i].base);
        unsigned int count = cma_areas[i].size >> PAGE_SHIFT;
        bool system = cma_areas[i].to_system;

        cma = cma_create_area(base, count, system);
        if (!IS_ERR(cma))
            cma_areas[i].cma = cma;
    }
    //默认的8MB的cma区域保存在dma_contiguous_def_area里边,
    //这个cma区域在dev_get_cma_area()函数中,如果没有找到dev对应的cma区域的话,就会使用
    //dma_contiguous_def_area这个默认的cma区域
    dma_contiguous_def_area = cma_get_area(dma_contiguous_def_base);

    for (i = 0; i < cma_map_count; i++) {
        cma = cma_get_area(cma_maps[i].base);
        dev_set_cma_area(cma_maps[i].dev, cma);
    }
//注册platform_bus_type的notifier函数,在每个platform设备驱动注册的时候,检查是否有
//"linux,contiguous-region",有的话会根据相应的名字分配对应的cma并保存到dev->cma里边
//这个在后面有具体说明
#ifdef CONFIG_OF 
    bus_register_notifier(&platform_bus_type, &cma_dev_init_nb);
#endif
    return 0;
}
core_initcall(cma_init_reserved_areas);

bus_register_notifier(&platform_bus_type, &cma_dev_init_nb) 这样注册platform_bus的notifier函数之后,
有以下platform设备在注册的时候,会通过cma_assign_device_from_dt()函数找到相应的cma区域并
赋值给dev->cma

static void cma_assign_device_from_dt(struct device *dev)
{
    struct device_node *node;
    struct cma *cma;
    const char *name;
    u32 value;

    //找到相应的device tree设置里边有没有"linux,contiguous-region",
    node = of_parse_phandle(dev->of_node, "linux,contiguous-region", 0);
    if (!node)
        return;
    if (of_property_read_u32(node, "reg", &value) && !value)
        return;

    //找到label的名字
    if (of_property_read_string(node, "label", &name))
        return;

    //根据名字找到cma area
    cma = cma_get_area_by_name(name);
    if (!cma)
        return;
    //cma赋值给dev->cma_area
    dev_set_cma_area(dev, cma);

    //如果有"linux,remove-completely",就把removed_dma_ops赋值给
    //dev->archdata.dma_ops = removed_dma_ops
    if (of_property_read_bool(node, "linux,remove-completely"))
        set_dma_ops(dev, &removed_dma_ops);

    pr_info("Assigned CMA region at %lx to %s device\n", (unsigned long)value, dev_name(dev));
}

以下是所有有cma区域的platform设备在初始化的时候找到的cma区域

<6>[0.487642]  [0:swapper/0:1] cma: Assigned CMA region at 0 to 1de0000.qcom,venus device
<6>[0.489469]  [0:swapper/0:1] cma: Assigned CMA region at 0 to 4080000.qcom,mss device
<6>[0.490756]  [0:swapper/0:1] cma: Assigned CMA region at 0 to a21b000.qcom,pronto device
<6>[1.125342]  [0:swapper/0:1] cma: Assigned CMA region at 0 to 8.qcom,ion-heap device
<6>[1.125793]  [0:swapper/0:1] cma: Assigned CMA region at 0 to 1b.qcom,ion-heap device
<6>[1.126233]  [0:swapper/0:1] cma: Assigned CMA region at 0 to 1c.qcom,ion-heap device
<6>[1.126671]  [0:swapper/0:1] cma: Assigned CMA region at 0 to 17.qcom,ion-heap device
<6>[1.127298]  [0:swapper/0:1] cma: Assigned CMA region at 0 to 1a.qcom,ion-heap device

这些CMA划分出去的区域,除了external_image_mem是存放trustzone的区域不用管之外,其他的由msm_ion.c统一管理。这个在ION Memory相关的说明中再说。。。

你可能感兴趣的:(Linux知识)