Armv8 Linux 内存管理(1)

来源:互联网 发布:肖凯农村淘宝 编辑:程序博客网 时间:2024/05/17 07:59

1. Buddy system之前的内存管理

Linux系统内存管理最著名的就是Buddy system(伙伴系统),linux系统是如何建立起这个系统的呢?在Buddy system之前内存是如何管理的呢?

在理解了一定的软件和硬件工作原理之后,代码是最好的指导老师。它能够告诉你如何把硬件的功能使用起来,如何实现你了解的软件设计。内存管理

也不例外。

start_kernel->setup_arch();


arch/arm64/kernel/setup.c


285 setup_arch(char **cmdline_p)
286 {
287         setup_processor();
288
289         setup_machine_fdt(__fdt_pointer);
290
291         init_mm.start_code = (unsigned long) _text;
292         init_mm.end_code   = (unsigned long) _etext;
293         init_mm.end_data   = (unsigned long) _edata;
294         init_mm.brk        = (unsigned long) _end;
295
296         *cmdline_p = boot_command_line;
297
298         parse_early_param();
299
300         arm64_memblock_init();
301
302         paging_init();
303         request_standard_resources();
304
305         unflatten_device_tree();
306
307         psci_init();
308
309         cpu_logical_map(0) = read_cpuid_mpidr() & MPIDR_HWID_BITMASK;
310 #ifdef CONFIG_SMP
311         smp_init_cpus();
312 #endif
313
314 #ifdef CONFIG_VT
315 #if defined(CONFIG_VGA_CONSOLE)
316         conswitchp = &vga_con;
317 #elif defined(CONFIG_DUMMY_CONSOLE)
318         conswitchp = &dummy_con;
319 #endif
320 #endif
321 }

289, 300,302是建立简单内存管理系统的核心函数。setup_machine_fdt建立memblock数据,从device tree内读到。


125 static void __init setup_machine_fdt(phys_addr_t dt_phys)
126 {
127         struct boot_param_header *devtree;
128         unsigned long dt_root;
129
130         /* Check we have a non-NULL DT pointer */
131         if (!dt_phys) {
132                 early_print("\n"
133                         "Error: NULL or invalid device tree blob\n"
134                         "The dtb must be 8-byte aligned and passed in the first 512MB of memory\n"
135                         "\nPlease check your bootloader.\n");
136
137                 while (true)
138                         cpu_relax();
139
140         }
141
142         devtree = phys_to_virt(dt_phys);
143
144         /* Check device tree validity */
145         if (be32_to_cpu(devtree->magic) != OF_DT_HEADER) {
146                 early_print("\n"
147                         "Error: invalid device tree blob at physical address 0x%p (virtual address 0x    %p)\n"
148                         "Expected 0x%x, found 0x%x\n"
149                         "\nPlease check your bootloader.\n",
150                         dt_phys, devtree, OF_DT_HEADER,
151                         be32_to_cpu(devtree->magic));
152
153                 while (true)
154                         cpu_relax();
155         }
156
157         initial_boot_params = devtree;
158         dt_root = of_get_flat_dt_root();
159
160         machine_name = of_get_flat_dt_prop(dt_root, "model", NULL);
161         if (!machine_name)
162                 machine_name = of_get_flat_dt_prop(dt_root, "compatible", NULL);
163         if (!machine_name)
164                 machine_name = "<unknown>";
165         pr_info("Machine: %s\n", machine_name);
166
167         /* Retrieve various information from the /chosen node */
168         of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);
169         /* Initialize {size,address}-cells info */
170         of_scan_flat_dt(early_init_dt_scan_root, NULL);
171         /* Setup memory, calling early_init_dt_add_memory_arch */
172         of_scan_flat_dt(early_init_dt_scan_memory, NULL);
173 }

172行遍历device tree,建立memblock数据


134 void __init arm64_memblock_init(void)
135 {
136         u64 *reserve_map, base, size;
137
138         /* Register the kernel text, kernel data and initrd with memblock */
139         memblock_reserve(__pa(_text), _end - _text);
140 #ifdef CONFIG_BLK_DEV_INITRD
141         if (phys_initrd_size) {
142                 memblock_reserve(phys_initrd_start, phys_initrd_size);
143
144                 /* Now convert initrd to virtual addresses */
145                 initrd_start = __phys_to_virt(phys_initrd_start);
146                 initrd_end = initrd_start + phys_initrd_size;
147         }
148 #endif
149
150         /*
151          * Reserve the page tables.  These are already in use,
152          * and can only be in node 0.
153          */
154         memblock_reserve(__pa(swapper_pg_dir), SWAPPER_DIR_SIZE);
155         memblock_reserve(__pa(idmap_pg_dir), IDMAP_DIR_SIZE);
156
157         /* Reserve the dtb region */
158         memblock_reserve(virt_to_phys(initial_boot_params),
159                          be32_to_cpu(initial_boot_params->totalsize));
160
161         /*
162          * Process the reserve map.  This will probably overlap the initrd
163          * and dtb locations which are already reserved, but overlapping
164          * doesn't hurt anything
165          */
166         reserve_map = ((void*)initial_boot_params) +
167                         be32_to_cpu(initial_boot_params->off_mem_rsvmap);
168         while (1) {
169                 base = be64_to_cpup(reserve_map++);
170                 size = be64_to_cpup(reserve_map++);
171                 if (!size)
172                         break;
173                 memblock_reserve(base, size);
174         }
175
176         memblock_allow_resize();
177         memblock_dump_all();
178 }

arm64_memblock_init 主要是设置预留块。


2. 建立预留快数据

drivers/of/fdt.c


618 int __init early_init_dt_scan_memory(unsigned long node, const char *uname,
619                                      int depth, void *data)
620 {
621         char *type = of_get_flat_dt_prop(node, "device_type", NULL);
622         __be32 *reg, *endp;
623         unsigned long l;
624
625         /* We are scanning "memory" nodes only */
626         if (type == NULL) {
627                 /*
628                  * The longtrail doesn't have a device_type on the
629                  * /memory node, so look for the node called /memory@0.
630                  */
631                 if (depth != 1 || strcmp(uname, "memory@0") != 0)
632                         return 0;
633         } else if (strcmp(type, "memory") != 0)
634                 return 0;
635
636         reg = of_get_flat_dt_prop(node, "linux,usable-memory", &l);
637         if (reg == NULL)
638                 reg = of_get_flat_dt_prop(node, "reg", &l);
639         if (reg == NULL)
640                 return 0;
641
642         endp = reg + (l / sizeof(__be32));
643
644         pr_debug("memory scan node %s, reg size %ld, data: %x %x %x %x,\n",
645             uname, l, reg[0], reg[1], reg[2], reg[3]);
646
647         while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) {
648                 u64 base, size;
649
650                 base = dt_mem_next_cell(dt_root_addr_cells, &reg);
651                 size = dt_mem_next_cell(dt_root_size_cells, &reg);
652
653                 if (size == 0)
654                         continue;
655                 pr_debug(" - %llx ,  %llx\n", (unsigned long long)base,
656                     (unsigned long long)size);
657
658                 early_init_dt_add_memory_arch(base, size);
659         }
660
661         return 0;
662 }

658行向内存块添加内存。

arch/arm64/kernel/setup.c

175 void __init early_init_dt_add_memory_arch(u64 base, u64 size)
176 {
177         base &= PAGE_MASK;
178         size &= PAGE_MASK;
179         if (base + size < PHYS_OFFSET) {
180                 pr_warning("Ignoring memory block 0x%llx - 0x%llx\n",
181                            base, base + size);
182                 return;
183         }
184         if (base < PHYS_OFFSET) {
185                 pr_warning("Ignoring memory range 0x%llx - 0x%llx\n",
186                            base, PHYS_OFFSET);
187                 size -= PHYS_OFFSET - base;
188                 base = PHYS_OFFSET;
189         }
190         memblock_add(base, size);
191 }
192

190行掉用memblock_add。


mm/memblock.c


445 int __init_memblock memblock_add(phys_addr_t base, phys_addr_t size)
 446 {
 447         return memblock_add_region(&memblock.memory, base, size, MAX_NUMNODES);
 448 }

364 static int __init_memblock memblock_add_region(struct memblock_type *type,
 365                                 phys_addr_t base, phys_addr_t size, int nid)
 366 {
 367         bool insert = false;
 368         phys_addr_t obase = base;
 369         phys_addr_t end = base + memblock_cap_size(base, &size);
 370         int i, nr_new;
 371
 372         if (!size)
 373                 return 0;
 374
 375         /* special case for empty array */
 376         if (type->regions[0].size == 0) {
 377                 WARN_ON(type->cnt != 1 || type->total_size);
 378                 type->regions[0].base = base;
 379                 type->regions[0].size = size;
 380                 memblock_set_region_node(&type->regions[0], nid);
 381                 type->total_size = size;
 382                 return 0;
 383         }
 384 repeat:
 385         /*
 386          * The following is executed twice.  Once with %false @insert and
 387          * then with %true.  The first counts the number of regions needed
 388          * to accomodate the new area.  The second actually inserts them.
 389          */
 390         base = obase;
 391         nr_new = 0;
 392
 393         for (i = 0; i < type->cnt; i++) {
 394                 struct memblock_region *rgn = &type->regions[i];
 395                 phys_addr_t rbase = rgn->base;
 396                 phys_addr_t rend = rbase + rgn->size;
 397
 398                 if (rbase >= end)
 399                         break;
 400                 if (rend <= base)
 401                         continue;
 402                 /*
 403                  * @rgn overlaps.  If it separates the lower part of new
 404                  * area, insert that portion.
 405                  */
 406                 if (rbase > base) {
 407                         nr_new++;
 408                         if (insert)
 409                                 memblock_insert_region(type, i++, base,
 410                                                        rbase - base, nid);

411                 }
 412                 /* area below @rend is dealt with, forget about it */
 413                 base = min(rend, end);
 414         }
 415
 416         /* insert the remaining portion */
 417         if (base < end) {
 418                 nr_new++;
 419                 if (insert)
 420                         memblock_insert_region(type, i, base, end - base, nid);
 421         }
 422
 423         /*
 424          * If this was the first round, resize array and repeat for actual
 425          * insertions; otherwise, merge and return.
 426          */
 427         if (!insert) {
 428                 while (type->cnt + nr_new > type->max)
 429                         if (memblock_double_array(type, obase, size) < 0)
 430                                 return -ENOMEM;
 431                 insert = true;
 432                 goto repeat;
 433         } else {
 434                 memblock_merge_regions(type);
 435                 return 0;
 436         }
 437 }

如果详细了解具体细节,可以阅读mm/memblock.c的函数。


  23 static struct memblock_region memblock_memory_init_regions[INIT_MEMBLOCK_REGIONS] __initdata_membloc     k;
  24 static struct memblock_region memblock_reserved_init_regions[INIT_MEMBLOCK_REGIONS] __initdata_membl     ock;
  25
  26 struct memblock memblock __initdata_memblock = {
  27         .memory.regions         = memblock_memory_init_regions,
  28         .memory.cnt             = 1,    /* empty dummy entry */
  29         .memory.max             = INIT_MEMBLOCK_REGIONS,
  30
  31         .reserved.regions       = memblock_reserved_init_regions,
  32         .reserved.cnt           = 1,    /* empty dummy entry */
  33         .reserved.max           = INIT_MEMBLOCK_REGIONS,
  34
  35         .current_limit          = MEMBLOCK_ALLOC_ANYWHERE,
  36 };

setup_machine_fdt和arm64_memblock_init结束,memblock的数据结构已经初始化。可以利用这个结构来分配内存了。

0 0