初始化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。