preface

Linux kernel source code analysis of setup_arch (three) basically the main functions of setup_arch are analyzed, because of the distance from the last time for a long time, so here re paste a general flow chart, this article is mainly analyzed is bootmem_init function.

The code analysis

The structure of the bootmem_init function is as follows:

Find_limits obtains the page box numbers of low and high memory from the memory information stored in meminfo, and places them in min, max_low, and max_high, respectively.

static void __init find_limits(unsigned long *min, unsigned long *max_low,
          unsigned long *max_high)
{... *min = bank_pfn_start(&mi->bank[0]);
 for_each_bank (i, mi)
  if (mi->bank[i].highmem)
    break;
 *max_low = bank_pfn_end(&mi->bank[i - 1]);
 *max_high = bank_pfn_end(&mi->bank[mi->nr_banks - 1]);
}
Copy the code

Arm_bootmem_init manages the low-end memory region. The flow chart is as follows:

Bootmem_bootmap_pages is used to calculate the size of the bitmap to be allocated. After assigning the bitmap, call init_bootmem_node to write the start and end box numbers and bitmap information to the PGDAT.

/* arch/arm/mm/init.c */
static void __init arm_bootmem_init(unsigned long start_pfn,
 unsigned long end_pfn)
{... boot_pages = bootmem_bootmap_pages(end_pfn - start_pfn); bitmap = memblock_alloc_base(boot_pages << PAGE_SHIFT, L1_CACHE_BYTES, __pfn_to_phys(end_pfn)); node_set_online(0);
 pgdat = NODE_DATA(0); init_bootmem_node(pgdat, __phys_to_pfn(bitmap), start_pfn, end_pfn); . }Copy the code

Finally, the memblock-managed memory is transferred to bootmem for management. For the free area in memblock, the corresponding bit in bitmap is set to zero by free_bootmem, and the used memory, That is, use reserve_bootmem to set the reserved bit of the memblock to 1.

/* arch/arm/mm/init.c */
static void __init arm_bootmem_init(...).
{.../* Free the lowmem regions from memblock into bootmem. */
 for_each_memblock(memory, reg) {
  ...
  free_bootmem(__pfn_to_phys(start), (end - start) << PAGE_SHIFT);
 }
 /* Reserve the lowmem memblock reserved regions in bootmem. */for_each_memblock(reserved, reg) { ... reserve_bootmem(__pfn_to_phys(start), (end - start) << PAGE_SHIFT, BOOTMEM_DEFAULT); }}Copy the code

After the bitmap is set, the corresponding page structure is allocated. Before the page structure is allocated, the memory holes in each zone are counted and stored in zhole_size.

/* arch/arm/mm/init.c */
static void __init arm_bootmem_free(unsigned long min, unsigned long max_low,
 unsigned long max_high)
{... for_each_memblock(memory, reg) { ...if (start < max_low) {
   unsigned long low_end = min(end, max_low);
   zhole_size[0] -= low_end - start;
  }
#ifdef CONFIG_HIGHMEM
  if (end > max_low) {
   unsigned long high_start = max(start, max_low);
   zhole_size[ZONE_HIGHMEM] -= end - high_start;
  }
#endif}... free_area_init_node(0, zone_size, min, zhole_size);
}
Copy the code

The page structure allocates the required memory space, and the general process is as follows:

Calculate_node_totalpages calculates the total number of memory pages and the actual number of available memory pages from zones_size and zholes_size, They are recorded in pGDAT -> node_SPANneD_pages and PGDat -> node_PRESENT_pages respectively.

/* mm/page_alloc.c */
void __paginginit free_area_init_node(int nid, unsigned long *zones_size,
  unsigned long node_start_pfn, unsigned long *zholes_size)
{
 pg_data_t *pgdat = NODE_DATA(nid);

 pgdat->node_id = nid;
 pgdat->node_start_pfn = node_start_pfn;
 calculate_node_totalpages(pgdat, zones_size, zholes_size);

 alloc_node_mem_map(pgdat);
 free_area_init_core(pgdat, zones_size, zholes_size);
}
Copy the code

Alloc_node_mem_map Determines the number of page structures to be allocated according to the size of pGDat -> node_SPANned_pages. This includes the area of the memory void. After the allocation, the starting address is recorded in the pGDat ->node_mem_map. It is also recorded in the global variable mem_map.

/* mm/page_alloc.c */
static void __init_refok alloc_node_mem_map(struct pglist_data *pgdat)
{...if(! pgdat->node_mem_map) { ... start = pgdat->node_start_pfn & ~(MAX_ORDER_NR_PAGES -1);
  end = pgdat->node_start_pfn + pgdat->node_spanned_pages;
  end = ALIGN(end, MAX_ORDER_NR_PAGES);
  size =  (end - start) * sizeof(struct page);
  map = alloc_remap(pgdat->node_id, size);
  if (!map)
   map = alloc_bootmem_node_nopanic(pgdat, size);
  pgdat->node_mem_map = map + (pgdat->node_start_pfn - start);
 }

 if (pgdat == NODE_DATA(0))
  mem_map = NODE_DATA(0)->node_mem_map; . }Copy the code

Initialize the page structure with free_areA_init_core. The process is as follows:

The main function is to set the information of each zone structure, such as the available space of the zone, initialize the page structure of each zone, record its zone and node_id, and set the status of the page structure to PG_reserved. Free memory pages will be released in mm_init::mem_init::free_all_bootmem. In addition, SetPageReserved is declared via macros, so it cannot be found, as defined in page-flags.h.

conclusion

The bootmem_init function allocates the space required by the bitmap and the page structure, marks the used and free memory areas to the bitmap, and then updates the memory information of each zone and initializes the page structure corresponding to the memory space of each zone. All are set to PG_reserved.