This post discusses the internal details of some memblock functions—specifically, functions that may trigger memblock_double_array(). The goal is to explain it in such a way that a beginner C or C++ programmer unfamiliar with the Linux kernel can comprehend it. I discussed the basics of memblock previously.

Looking at memblock.c, we see that memblock_double_array() is called by memblock_add_range() and memblock_isolate_range(). These are both internal functions with no prototype in memblock.h. By internal function, I mean a function that is called by a function in memblock.h or memblock.c.

memblock_add_range() makes two passes through a portion of its code.[1] In the first pass, it determines nr_new, the number of new regions that need to be added. It then calls memblock_double_array() if the current number of memblock_type *type regions plus nr_new exceeds the current size of that type’s regions array.

memblock_isolate_range() splits memblock regions that overlap with a given range at the boundaries of the range.[2] It calls memblock_double_array() if the current number of memblock_type *type regions plus 2 exceeds the current size of that type’s regions array, since at most two additional regions will be created by the splits.

We can trace memblock_add_range() and memblock_isolate_range() upwards until we find the external functions (those with prototypes in memblock.h) that can trigger memblock_double_array().

memblock_add_range()

memblock_add_range() is called by:

  • memblock_add_node(): external function[3]
  • memblock_add(): external function
  • memblock_reserve(): external and internal function
  • memblock_physmem_add(): external function

By external and internal function, I mean a function that has a prototype in memblock.h and is called in memblock.c or memblock.h.

memblock_reserve()

memblock_reserve() is called by:

  • memblock_double_array()
  • memblock_alloc_range_nid(): external and internal function

memblock_alloc_range_nid()

memblock_alloc_range_nid() is called by:

  • memblock_phys_alloc_range(): external and internal function called by:
    • memblock_phys_alloc(): external function
  • memblock_phys_alloc_try_nid(): external function
  • memblock_alloc_internal(): internal function called by:
    • memblock_alloc_exact_nid_raw(): external function
    • memblock_alloc_try_nid_raw(): external and internal function called by:
      • memblock_alloc_raw(): external function
    • memblock_alloc_try_nid(): external and internal function called by:
      • memblock_alloc(): external function
      • memblock_alloc_from(): external function
      • memblock_alloc_low(): external function
      • memblock_alloc_node(): external function

memblock_isolate_range()

This one is a bit more complicated. memblock_isolate_range() is called by:

  • memblock_remove_range(): internal function
  • memblock_setclr_flag(): internal function
  • memblock_set_node(): external function
  • memblock_cap_memory_range(): external and internal function

memblock_remove_range()

memblock_remove_range() is called by:

  • memblock_remove(): external function
  • memblock_phys_free(): external function and internal function
  • memblock_enforce_memory_limit(): external function
  • memblock_cap_memory_range()

memblock_phys_free()

memblock_phys_free() is called by:

  • memblock_free(): external function and internal function called by:
    • memblock_double_array()
  • free_memmap(): internal function called by:
    • free_unused_memmap(): internal function called by:
      • memblock_free_all(): external function

memblock_setclr_flag()

Each memblock_region has a set of memblock_flags. memblock_setclr_flag() uses memblock_isolate_range() to isolate a specific region so that it can set or clear flags for that region. The regions are re-merged at the end of the function, so there is no net increase in the number of regions.

memblock_setclr_flag() is called by:

  • memblock_mark_hotplug(): external function
  • memblock_clear_hotplug(): external function and internal function called by:
    • free_low_memory_core_early(): internal function called by memblock_free_all()
  • memblock_mark_mirror(): external function
  • memblock_mark_nomap(): external function
  • memblock_clear_nomap(): external function

memblock_cap_memory_range()

memblock_cap_memory_range() is called by:

  • memblock_mem_limit_remove_map(): external function

External functions

Thus, most of the external functions in memblock can trigger memblock_double_array(). This includes:

  • memblock_add_node()
  • memblock_add()
  • memblock_reserve()
  • memblock_physmem_add()
  • memblock_alloc_range_nid()
  • memblock_phys_alloc_range()
  • memblock_phys_alloc()
  • memblock_phys_alloc_try_nid()
  • memblock_alloc_exact_nid_raw()
  • memblock_alloc_try_nid_raw()
  • memblock_alloc_raw()
  • memblock_alloc_try_nid()
  • memblock_alloc()
  • memblock_alloc_from()
  • memblock_alloc_low()
  • memblock_alloc_node()
  • memblock_set_node()
  • memblock_cap_memory_range()
  • memblock_remove()
  • memblock_phys_free()
  • memblock_enforce_memory_limit()
  • memblock_free()
  • memblock_free_all()
  • memblock_mark_hotplug()
  • memblock_clear_hotplug()
  • memblock_mark_mirror()
  • memblock_mark_nomap()
  • memblock_clear_nomap()
  • memblock_mem_limit_remove_map()

References

  1. memblock.c: memblock_add_range(): repeat
  2. memblock.c: memblock_isolate_range()
  3. memblock.h