linux cma 3种配置share pool的方法分析

初始化cma的share pool一共有dts、cmdline、config共3种。3种顺序是dts==>cmdline==>config。

3种方式的实现方式如下:
 1.dts定义成share pool的方法是添加linux,cma-default;属性。
 2.cmdline方式是在cmdline添加:cma=128M(意思是cma share pool大小为128M)。
 3.config方式是开启如下两个config:
     CONFIG_CMA_SIZE_MBYTES=16    //share pool空间大小单位是M
     CONFIG_CMA_SIZE_SEL_MBYTES=y    //确定使用config中CMA_SIZE_MBYTES大小来确定share pool空间大小

cma share pool的初始化是在start_kernel-->start_kernel.
在start_kernel中首先调用parse_early_param对cmdline中的cma进行解析,函数调用顺序如下parse_early_param-->parse_early_options-->do_early_param.
在do_early_param中函数会遍历__setup_start和 __setup_end之间的所有结构体,然后将 cmdline中的各个参数与字符串 ”cma”比较,如果匹配,那么调用early_cma函数。
函数early_cma代码如下,其中宏early_param("cma", early_cma);是将early_cma添加到__setup_start和 __setup_end之间。
 54 static int __init early_cma(char *p)
 55 {    
 56     pr_debug("%s(%s)\n", __func__, p);
 59     size_cmdline = memparse(p, &p);
 61     if (*p != '@')
 62         return 0;
 63     base_cmdline = memparse(p + 1, &p);
 64     if (*p != '-') {
 65         limit_cmdline = base_cmdline + size_cmdline;
 66         return 0;
 67     }
 68     limit_cmdline = memparse(p + 1, &p);
 69 
 70     return 0;
 71 }    
 72 early_param("cma", early_cma);

size_cmdline保存cmdline中cma=的数字,支持K、M、G、T等单位。
memparse代码如下:
128 unsigned long long memparse(const char *ptr, char **retptr)
129 {
130     char *endptr;   /* local pointer to end of parsed string */
131 
132     unsigned long long ret = simple_strtoull(ptr, &endptr, 0);  //保存具体数值
133 
134     switch (*endptr) {
135     case 'E':
136     case 'e':
137         ret <<= 10;
138     case 'P':
139     case 'p':
140         ret <<= 10;
141     case 'T':
142     case 't':
143         ret <<= 10;
144     case 'G':
145     case 'g':
146         ret <<= 10;
147     case 'M':
148     case 'm':
149         ret <<= 10;  //解析数值后面的单位,M左移10位,这里没有break,会继续执行后面K的操作,再左移10,达到M的量级。
150     case 'K':
151     case 'k':
152         ret <<= 10;  //解析数值后面的单位,K左移10位
153         endptr++;
154     default:
155         break;
156     }
157 
158     if (retptr)
159         *retptr = endptr;
160 
161     return ret;
162 }
163 EXPORT_SYMBOL(memparse);

至此cmdline中cma=128M的结果被保存到了size_cmdline中,如果认为先解析了cmdline就先使用cmdline中的数值对cma share pool进行初始化就错了,这里只是把cmdline中的cma值保存到size_cmdline中,并没有立刻使用这个值。

接下来在start_kernel中调用arm64_memblock_init-->early_init_fdt_scan_reserved_mem-->fdt_init_reserved_mem.
解析dts中如果cma没有定义reg则rmem->size为0,则调用__reserved_mem_alloc_siz动态申请。
261 void __init fdt_init_reserved_mem(void)
262 {
263     int i;
264 
265     /* check for overlapping reserved regions */
266     __rmem_check_for_overlap();
267 
268     for (i = 0; i < reserved_mem_count; i++) {
269         struct reserved_mem *rmem = &reserved_mem[i];
270         unsigned long node = rmem->fdt_node;
271         int len;
272         const __be32 *prop;
273         int err = 0;
274 
275         prop = of_get_flat_dt_prop(node, "phandle", &len);
276         if (!prop)
277             prop = of_get_flat_dt_prop(node, "linux,phandle", &len);
278         if (prop)
279             rmem->phandle = of_read_number(prop, len/4);
280 
282         if (rmem->size == 0)
283             err = __reserved_mem_alloc_size(node, rmem->name,
284                          &rmem->base, &rmem->size);  //size为0,动态申请。
285         if (err == 0)
286             __reserved_mem_init_node(rmem);
289     }
290 }  
在函数__reserved_mem_alloc_siz中可以通过dts增加alloc-ranges来限制alloc的地址方位。需注意alloc-ranges的范围要大于cma设定的大小,否组申请会失败,最终导致cma开启失败。
在dts中如果有no-map属性,则说明这段内存不需要映射,即buddy系统将不能见到这段内存。
在申请完内存调用early_init_dt_alloc_reserved_memory_arch中如果no-map为真,则会调用memblock_remove将这块从系统中移除掉。

在函数__reserved_mem_init_node中完成对cma结构体的初始化。
194 static int __init __reserved_mem_init_node(struct reserved_mem *rmem)
195 {
196     extern const struct of_device_id __reservedmem_of_table[];
197     const struct of_device_id *i;
198 
199     for (i = __reservedmem_of_table; i < &__rmem_of_table_sentinel; i++) {
200         reservedmem_of_init_fn initfn = i->data;
201         const char *compat = i->compatible;
202 
203         if (!of_flat_dt_is_compatible(rmem->fdt_node, compat))
204             continue;
206         if (initfn(rmem) == 0) {  //调用函数rmem_cma_setup完成对cma结构体的初始化
207             pr_info("initialized node %s, compatible id %s\n",
208                 rmem->name, compat);
209             return 0;
210         }
212     }
213     return -ENOENT;
214 } 
RESERVEDMEM_OF_DECLARE(cma, "shared-dma-pool", rmem_cma_setup);  //定义了cma的init函数为rmem_cma_setup。

rmem_cma_setup中通过判断cma的dts中是否linux,cma-default,来决定是否设置dma_contiguous_default_are,这个全局变量后面会作为是否继续使用cmdline和config中的size进行初始化的关键判断。
258 static int __init rmem_cma_setup(struct reserved_mem *rmem)
259 {
260     phys_addr_t align = PAGE_SIZE << max(MAX_ORDER - 1, pageblock_order);
261     phys_addr_t mask = align - 1;
262     unsigned long node = rmem->fdt_node;
263     struct cma *cma;
264     int err;
265 
266     if (!of_get_flat_dt_prop(node, "reusable", NULL) ||
267         of_get_flat_dt_prop(node, "no-map", NULL))
268         return -EINVAL;
269 
270     if ((rmem->base & mask) || (rmem->size & mask)) {
271         pr_err("Reserved memory: incorrect alignment of CMA region\n");
272         return -EINVAL;
273     }
274        
275     err = cma_init_reserved_mem(rmem->base, rmem->size, 0, rmem->name, &cma);
276     if (err) {
277         pr_err("Reserved memory: unable to setup CMA region\n");
278         return err;      
279     }                    
280     /* Architecture specific contiguous memory fixup. */
281     dma_contiguous_early_fixup(rmem->base, rmem->size);
282                          
283     if (of_get_flat_dt_prop(node, "linux,cma-default", NULL)) {
285         dma_contiguous_set_default(cma);
286     }
287 
288     rmem->ops = &rmem_cma_ops;
289     rmem->priv = cma;
290 
291     pr_info("Reserved memory: created CMA memory pool at %pa, size %ld MiB\n",
292         &rmem->base, (unsigned long)rmem->size / SZ_1M);
293 
294     return 0;
295 }  

至此通过dts初始化cma share pool的操作完成。

在函数arm64_memblock_init中调用dma_contiguous_reserve来对cmdline或config的cma进行初始化。
dma_contiguous_reserve的开启需要CONFIG_DMA_CMA否则这个就是空函数。
函数dma_contiguous_reserve首先判断size_cmdline是否为默认值-1,如果不同则cmdline中有对cma进行设值,则用cmdline中cma的值设置share pool的size值,由此可知cmdline配置由于config。
当size_cmdline的值为默认值才使用config中配置作为cma share pool的值进行初始化。
110 void __init dma_contiguous_reserve(phys_addr_t limit)
111 {
112     phys_addr_t selected_size = 0;
113     phys_addr_t selected_base = 0;
114     phys_addr_t selected_limit = limit;
115     bool fixed = false;
116 
117     pr_debug("%s(limit %08lx)\n", __func__, (unsigned long)limit);
118     pr_err("====liangcai %s(limit %08lx), dma_contiguous_default_area=0x%llx\n", __func__, (unsigned long)limit,(unsigned long)dma_contiguous_default_area);
119 
120     if (size_cmdline != -1) {  //判断是否为默认值,为默认值则说明cmdline中没有对cma进行设置
121         selected_size = size_cmdline;
122         selected_base = base_cmdline;
123         selected_limit = min_not_zero(limit_cmdline, limit);
124         if (base_cmdline + size_cmdline == limit_cmdline)
125             fixed = true;
126     } else {
127 #ifdef CONFIG_CMA_SIZE_SEL_MBYTES
128         selected_size = size_bytes;  //用config中的数值进行设置 cma share pool
129 #elif defined(CONFIG_CMA_SIZE_SEL_PERCENTAGE)
130         selected_size = cma_early_percent_memory();
131 #elif defined(CONFIG_CMA_SIZE_SEL_MIN)
132         selected_size = min(size_bytes, cma_early_percent_memory());
133 #elif defined(CONFIG_CMA_SIZE_SEL_MAX)
134         selected_size = max(size_bytes, cma_early_percent_memory());
135 #endif  
136     }
137     
138     if (selected_size && !dma_contiguous_default_area) {  //通过判断dma_contiguous_default_area值是否为null来判断cma share pool是否已经初始化。如果通过dts中数值进行了初始化,则忽略后面的初始化。
139         pr_debug("%s: reserving %ld MiB for global area\n", __func__,
140              (unsigned long)selected_size / SZ_1M);
143              
144         dma_contiguous_reserve_area(selected_size, selected_base,
145                         selected_limit,
146                         &dma_contiguous_default_area,
147                         fixed);
148     }                   
149 }

分析最后dma_contiguous_reserve,可知首先判断是否dts中有配置,如果配置了就忽略后面对cma share pool的初始化。如果dts中没有配置,则看cmdline中是否有cma的设置,如果有就用cmdline中的数值初始化cma,如果没有就用config中的值进行初始化。cmdline和config的初始化操作由dma_contiguous_reserve_area完成。

由以上分析可知cma 3种配置方法的顺序是dts==>cmdline==>config。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值