啟動時記憶體管理

早期系統初始化無法使用“普通”記憶體管理,因為它尚未設定。但仍需要為各種資料結構分配記憶體,例如物理頁分配器。

一個名為 memblock 的專用分配器執行啟動時記憶體管理。特定架構的初始化必須在 setup_arch() 函式中設定它,並在 mem_init() 函式中將其拆除。

一旦早期記憶體管理可用,它會提供各種函式和宏用於記憶體分配。分配請求可以定向到第一個(也可能是唯一)節點,或者 NUMA 系統中的特定節點。存在在分配失敗時導致恐慌的 API 變體,也有不會恐慌的。

Memblock 還提供了多種控制其自身行為的 API。

Memblock 概述

Memblock 是一種在早期啟動期間管理記憶體區域的方法,此時常規的核心記憶體分配器尚未啟動和執行。

Memblock 將系統記憶體視為連續區域的集合。這些集合有幾種型別:

  • memory - 描述核心可用的物理記憶體;這可能與系統中實際安裝的物理記憶體不同,例如當記憶體受 mem= 命令列引數限制時

  • reserved - 描述已分配的區域

  • physmem - 描述啟動期間可用的實際物理記憶體,無論可能存在的限制和記憶體熱插拔/熱拔;physmem 型別僅在某些架構上可用。

每個區域由 struct memblock_region 表示,該結構定義了區域的範圍、其屬性以及 NUMA 系統上的 NUMA 節點 ID。每種記憶體型別由 struct memblock_type 描述,該結構包含一個記憶體區域陣列以及分配器元資料。“memory”和“reserved”型別被很好地封裝在 struct memblock 中。該結構在構建時靜態初始化。區域陣列的初始大小,“memory”型別為 INIT_MEMBLOCK_MEMORY_REGIONS,“reserved”型別為 INIT_MEMBLOCK_RESERVED_REGIONS。“physmem”型別的區域陣列初始大小為 INIT_PHYSMEM_REGIONS。memblock_allow_resize() 在新增新區域時啟用區域陣列的自動調整大小。應謹慎使用此功能,以確保為區域陣列分配的記憶體不會與應保留的區域(例如 initrd)重疊。

早期架構設定應使用 memblock_add()memblock_add_node() 函式告知 memblock 物理記憶體佈局。第一個函式不將區域分配給 NUMA 節點,適用於 UMA 系統。然而,它也可以在 NUMA 系統上使用,並在後續設定過程中使用 memblock_set_node() 將區域分配給 NUMA 節點。memblock_add_node() 直接執行此類分配。

一旦 memblock 設定完畢,就可以使用以下 API 變體之一分配記憶體:

  • memblock_phys_alloc*() - 這些函式返回已分配記憶體的物理地址

  • memblock_alloc*() - 這些函式返回已分配記憶體的虛擬地址。

請注意,這兩種 API 變體都隱含地假設了允許的記憶體範圍和回退方法。有關更詳細的描述,請查閱 memblock_alloc_internal()memblock_alloc_range_nid() 函式的文件。

隨著系統啟動的進行,特定架構的 mem_init() 函式會將所有記憶體釋放給夥伴頁分配器。

除非某個架構啟用 CONFIG_ARCH_KEEP_MEMBLOCK,否則 memblock 資料結構(“physmem”除外)將在系統初始化完成後被丟棄。

函式和結構體

以下是 memblock 資料結構、函式和宏的描述。其中一些實際上是內部的,但既然它們有文件,省略它們會很愚蠢。此外,閱讀內部函式的描述有助於理解其底層實際發生的情況。

enum memblock_flags

記憶體區域屬性定義

常量

MEMBLOCK_NONE

無特殊要求

MEMBLOCK_HOTPLUG

韌體在早期啟動期間提供的記憶體對映中指示的、可熱插拔(可熱拔插)的系統 RAM 記憶體區域(例如,以後可能被熱拔插的記憶體範圍)。如果在核心命令列中設定了“movable_node”,則嘗試保持此記憶體區域可熱拔插。不適用於早期啟動後新增(“熱插拔”)的 memblock。

MEMBLOCK_MIRROR

映象區域

MEMBLOCK_NOMAP

不新增到核心直接對映中,並在記憶體對映中視為保留;有關詳細資訊,請參閱 memblock_mark_nomap() 的描述

MEMBLOCK_DRIVER_MANAGED

始終透過驅動檢測並新增的記憶體區域,且從未在韌體提供的記憶體對映中指示為系統 RAM。這與核心資源樹中的 IORESOURCE_SYSRAM_DRIVER_MANAGED 相對應。

MEMBLOCK_RSRV_NOINIT

struct pages 未初始化的記憶體區域(僅適用於保留區域)。

MEMBLOCK_RSRV_KERN

為核心使用而保留的記憶體區域,可以透過 memblock_reserve_kern() 或透過 memblock 分配 API 顯式保留。所有 memblock 分配都會設定此標誌。

MEMBLOCK_KHO_SCRATCH

kexec 在移交模式下可以傳遞給下一個核心的記憶體區域。在早期啟動期間,我們尚不瞭解所有記憶體保留,因此我們從前一個核心中獲取我們知道可以使用的臨時記憶體。這是在此階段唯一可以進行分配的記憶體。

struct memblock_region

表示一個記憶體區域

定義:

struct memblock_region {
    phys_addr_t base;
    phys_addr_t size;
    enum memblock_flags flags;
#ifdef CONFIG_NUMA;
    int nid;
#endif;
};

成員

base

區域的基地址

size

區域的大小

flags

記憶體區域屬性

nid

NUMA 節點 ID

struct memblock_type

某種型別的記憶體區域集合

定義:

struct memblock_type {
    unsigned long cnt;
    unsigned long max;
    phys_addr_t total_size;
    struct memblock_region *regions;
    char *name;
};

成員

cnt

區域數量

max

已分配陣列的大小

total_size

所有區域的總大小

regions

區域陣列

name

記憶體型別的符號名稱

struct memblock

memblock 分配器元資料

定義:

struct memblock {
    bool bottom_up;
    phys_addr_t current_limit;
    struct memblock_type memory;
    struct memblock_type reserved;
};

成員

bottom_up

是自下而上方向嗎?

current_limit

當前分配限制的物理地址

memory

可用記憶體區域

reserved

保留記憶體區域

for_each_physmem_range

for_each_physmem_range (i, type, p_start, p_end)

遍歷不包含在 type 中的 physmem 區域。

引數

i

用作迴圈變數的 u64

type

指向 memblock_type 的指標,該型別從迭代中排除,可以是 NULL

p_start

指向 phys_addr_t 的指標,用於範圍的起始地址,可以是 NULL

p_end

指向 phys_addr_t 的指標,用於範圍的結束地址,可以是 NULL

__for_each_mem_range

__for_each_mem_range (i, type_a, type_b, nid, flags, p_start, p_end, p_nid)

遍歷 type_a 中的 memblock 區域,且不包含在 type_b 中。如果 type_b 為 NULL,則僅遍歷 type_a。

引數

i

用作迴圈變數的 u64

type_a

指向要迭代的 memblock_type 的指標

type_b

指向 memblock_type 的指標,該型別從迭代中排除

nid

節點選擇器,NUMA_NO_NODE 表示所有節點

flags

根據記憶體屬性從塊中選擇

p_start

指向 phys_addr_t 的指標,用於範圍的起始地址,可以是 NULL

p_end

指向 phys_addr_t 的指標,用於範圍的結束地址,可以是 NULL

p_nid

指向 int 的指標,用於範圍的 nid,可以是 NULL

__for_each_mem_range_rev

__for_each_mem_range_rev (i, type_a, type_b, nid, flags, p_start, p_end, p_nid)

反向遍歷 type_a 中的 memblock 區域,且不包含在 type_b 中。如果 type_b 為 NULL,則僅反向遍歷 type_a。

引數

i

用作迴圈變數的 u64

type_a

指向要迭代的 memblock_type 的指標

type_b

指向 memblock_type 的指標,該型別從迭代中排除

nid

節點選擇器,NUMA_NO_NODE 表示所有節點

flags

根據記憶體屬性從塊中選擇

p_start

指向 phys_addr_t 的指標,用於範圍的起始地址,可以是 NULL

p_end

指向 phys_addr_t 的指標,用於範圍的結束地址,可以是 NULL

p_nid

指向 int 的指標,用於範圍的 nid,可以是 NULL

for_each_mem_range

for_each_mem_range (i, p_start, p_end)

遍歷記憶體區域。

引數

i

用作迴圈變數的 u64

p_start

指向 phys_addr_t 的指標,用於範圍的起始地址,可以是 NULL

p_end

指向 phys_addr_t 的指標,用於範圍的結束地址,可以是 NULL

for_each_mem_range_rev

for_each_mem_range_rev (i, p_start, p_end)

反向遍歷 type_a 中的 memblock 區域,且不包含在 type_b 中。如果 type_b 為 NULL,則僅反向遍歷 type_a。

引數

i

用作迴圈變數的 u64

p_start

指向 phys_addr_t 的指標,用於範圍的起始地址,可以是 NULL

p_end

指向 phys_addr_t 的指標,用於範圍的結束地址,可以是 NULL

for_each_reserved_mem_range

for_each_reserved_mem_range (i, p_start, p_end)

遍歷所有保留的 memblock 區域

引數

i

用作迴圈變數的 u64

p_start

指向 phys_addr_t 的指標,用於範圍的起始地址,可以是 NULL

p_end

指向 phys_addr_t 的指標,用於範圍的結束地址,可以是 NULL

描述

遍歷 memblock 的保留區域。Memblock 初始化後即可使用。

for_each_mem_pfn_range

for_each_mem_pfn_range (i, nid, p_start, p_end, p_nid)

早期記憶體 pfn 範圍迭代器

引數

i

用作迴圈變數的整數

nid

節點選擇器,MAX_NUMNODES 表示所有節點

p_start

指向 ulong 的指標,用於範圍的起始 pfn,可以是 NULL

p_end

指向 ulong 的指標,用於範圍的結束 pfn,可以是 NULL

p_nid

指向 int 的指標,用於範圍的 nid,可以是 NULL

描述

遍歷已配置的記憶體範圍。

for_each_free_mem_pfn_range_in_zone_from

for_each_free_mem_pfn_range_in_zone_from (i, zone, p_start, p_end)

從給定點開始遍歷特定區域的空閒 memblock 區域

引數

i

用作迴圈變數的 u64

zone

所有記憶體塊所在的區域

p_start

指向 phys_addr_t 的指標,用於範圍的起始地址,可以是 NULL

p_end

指向 phys_addr_t 的指標,用於範圍的結束地址,可以是 NULL

描述

遍歷特定區域中空閒的(memory && !reserved)memblock 區域,從當前位置繼續。Memblock 初始化後即可使用。

for_each_free_mem_range

for_each_free_mem_range (i, nid, flags, p_start, p_end, p_nid)

遍歷空閒的 memblock 區域

引數

i

用作迴圈變數的 u64

nid

節點選擇器,NUMA_NO_NODE 表示所有節點

flags

根據記憶體屬性從塊中選擇

p_start

指向 phys_addr_t 的指標,用於範圍的起始地址,可以是 NULL

p_end

指向 phys_addr_t 的指標,用於範圍的結束地址,可以是 NULL

p_nid

指向 int 的指標,用於範圍的 nid,可以是 NULL

描述

遍歷 memblock 中空閒的(memory && !reserved)區域。Memblock 初始化後即可使用。

for_each_free_mem_range_reverse

for_each_free_mem_range_reverse (i, nid, flags, p_start, p_end, p_nid)

反向遍歷空閒的 memblock 區域

引數

i

用作迴圈變數的 u64

nid

節點選擇器,NUMA_NO_NODE 表示所有節點

flags

根據記憶體屬性從塊中選擇

p_start

指向 phys_addr_t 的指標,用於範圍的起始地址,可以是 NULL

p_end

指向 phys_addr_t 的指標,用於範圍的結束地址,可以是 NULL

p_nid

指向 int 的指標,用於範圍的 nid,可以是 NULL

描述

以相反順序遍歷 memblock 中空閒的(memory && !reserved)區域。Memblock 初始化後即可使用。

void memblock_set_current_limit(phys_addr_t limit)

設定當前分配限制,以允許將分配限制在啟動期間當前可訪問的範圍。

引數

phys_addr_t limit

新的限制值(物理地址)

unsigned long memblock_region_memory_base_pfn(const struct memblock_region *reg)

獲取記憶體區域的最低 pfn

引數

const struct memblock_region *reg

memblock_region 結構體

返回值

與記憶體區域相交的最低 pfn

unsigned long memblock_region_memory_end_pfn(const struct memblock_region *reg)

獲取記憶體區域的結束 pfn

引數

const struct memblock_region *reg

memblock_region 結構體

返回值

保留區域的 end_pfn

unsigned long memblock_region_reserved_base_pfn(const struct memblock_region *reg)

獲取保留區域的最低 pfn

引數

const struct memblock_region *reg

memblock_region 結構體

返回值

與保留區域相交的最低 pfn

unsigned long memblock_region_reserved_end_pfn(const struct memblock_region *reg)

獲取保留區域的結束 pfn

引數

const struct memblock_region *reg

memblock_region 結構體

返回值

保留區域的 end_pfn

for_each_mem_region

for_each_mem_region (region)

遍歷記憶體區域

引數

region

迴圈變數

for_each_reserved_mem_region

for_each_reserved_mem_region (region)

遍歷保留記憶體區域

引數

region

迴圈變數

phys_addr_t __init_memblock __memblock_find_range_bottom_up(phys_addr_t start, phys_addr_t end, phys_addr_t size, phys_addr_t align, int nid, enum memblock_flags flags)

自下而上查詢空閒區域的工具函式

引數

phys_addr_t start

候選範圍的起始

phys_addr_t end

候選範圍的結束,可以是 MEMBLOCK_ALLOC_ANYWHEREMEMBLOCK_ALLOC_ACCESSIBLE

phys_addr_t size

要查詢的空閒區域的大小

phys_addr_t align

要查詢的空閒區域的對齊方式

int nid

要查詢的空閒區域的 nid,NUMA_NO_NODE 表示任意節點

enum memblock_flags flags

根據記憶體屬性從塊中選擇

描述

memblock_find_in_range_node() 呼叫的工具函式,自下而上查詢空閒區域。

返回值

成功時返回找到的地址,失敗時返回 0。

phys_addr_t __init_memblock __memblock_find_range_top_down(phys_addr_t start, phys_addr_t end, phys_addr_t size, phys_addr_t align, int nid, enum memblock_flags flags)

自上而下查詢空閒區域的工具函式

引數

phys_addr_t start

候選範圍的起始

phys_addr_t end

候選範圍的結束,可以是 MEMBLOCK_ALLOC_ANYWHEREMEMBLOCK_ALLOC_ACCESSIBLE

phys_addr_t size

要查詢的空閒區域的大小

phys_addr_t align

要查詢的空閒區域的對齊方式

int nid

要查詢的空閒區域的 nid,NUMA_NO_NODE 表示任意節點

enum memblock_flags flags

根據記憶體屬性從塊中選擇

描述

memblock_find_in_range_node() 呼叫的工具函式,自上而下查詢空閒區域。

返回值

成功時返回找到的地址,失敗時返回 0。

phys_addr_t __init_memblock memblock_find_in_range_node(phys_addr_t size, phys_addr_t align, phys_addr_t start, phys_addr_t end, int nid, enum memblock_flags flags)

在給定範圍和節點中查詢空閒區域

引數

phys_addr_t size

要查詢的空閒區域的大小

phys_addr_t align

要查詢的空閒區域的對齊方式

phys_addr_t start

候選範圍的起始

phys_addr_t end

候選範圍的結束,可以是 MEMBLOCK_ALLOC_ANYWHEREMEMBLOCK_ALLOC_ACCESSIBLE

int nid

要查詢的空閒區域的 nid,NUMA_NO_NODE 表示任意節點

enum memblock_flags flags

根據記憶體屬性從塊中選擇

描述

在指定的範圍和節點中,查詢大小為 size 並按 align 對齊的空閒區域。

返回值

成功時返回找到的地址,失敗時返回 0。

phys_addr_t __init_memblock memblock_find_in_range(phys_addr_t start, phys_addr_t end, phys_addr_t size, phys_addr_t align)

在給定範圍中查詢空閒區域

引數

phys_addr_t start

候選範圍的起始

phys_addr_t end

候選範圍的結束,可以是 MEMBLOCK_ALLOC_ANYWHEREMEMBLOCK_ALLOC_ACCESSIBLE

phys_addr_t size

要查詢的空閒區域的大小

phys_addr_t align

要查詢的空閒區域的對齊方式

描述

在指定的範圍中,查詢大小為 size 並按 align 對齊的空閒區域。

返回值

成功時返回找到的地址,失敗時返回 0。

void memblock_discard(void)

如果記憶體和保留陣列已分配,則將其丟棄

引數

void

無引數

int __init_memblock memblock_double_array(struct memblock_type *type, phys_addr_t new_area_start, phys_addr_t new_area_size)

將 memblock 區域陣列的大小加倍

引數

struct memblock_type *type

正在加倍的區域陣列的 memblock 型別

phys_addr_t new_area_start

要避免重疊的記憶體範圍的起始地址

phys_addr_t new_area_size

要避免重疊的記憶體範圍的大小

描述

type 區域陣列的大小加倍。如果 memblock 用於為一個新的保留區域陣列分配記憶體,並且存在一個先前已分配的記憶體範圍 [new_area_start, new_area_start + new_area_size] 等待保留,請確保新陣列使用的記憶體不與此範圍重疊。

返回值

成功時返回 0,失敗時返回 -1。

void __init_memblock memblock_merge_regions(struct memblock_type *type, unsigned long start_rgn, unsigned long end_rgn)

合併相鄰的相容區域

引數

struct memblock_type *type

要掃描的 memblock 型別

unsigned long start_rgn

從 (start_rgn - 1) 開始掃描

unsigned long end_rgn

掃描結束於 (end_rgn - 1)。掃描 type 並在 [start_rgn - 1, end_rgn) 範圍內合併相鄰的相容區域。

void __init_memblock memblock_insert_region(struct memblock_type *type, int idx, phys_addr_t base, phys_addr_t size, int nid, enum memblock_flags flags)

插入新的 memblock 區域

引數

struct memblock_type *type

要插入的 memblock 型別

int idx

插入點的索引

phys_addr_t base

新區域的基地址

phys_addr_t size

新區域的大小

int nid

新區域的節點 ID

enum memblock_flags flags

新區域的標誌

描述

將新的 memblock 區域 [base, base + size) 插入到 typeidx 位置。type 必須已經有足夠的額外空間來容納新區域。

int __init_memblock memblock_add_range(struct memblock_type *type, phys_addr_t base, phys_addr_t size, int nid, enum memblock_flags flags)

新增新的 memblock 區域

引數

struct memblock_type *type

要新增新區域的 memblock 型別

phys_addr_t base

新區域的基地址

phys_addr_t size

新區域的大小

int nid

新區域的 nid

enum memblock_flags flags

新區域的標誌

描述

將新的 memblock 區域 [base, base + size) 新增到 type 中。新區域允許與現有區域重疊 - 重疊不會影響已存在的區域。新增後,type 保證是最小的(所有相鄰的相容區域都已合併)。

返回值

成功時返回 0,失敗時返回 -errno。

int __init_memblock memblock_add_node(phys_addr_t base, phys_addr_t size, int nid, enum memblock_flags flags)

在 NUMA 節點內新增新的 memblock 區域

引數

phys_addr_t base

新區域的基地址

phys_addr_t size

新區域的大小

int nid

新區域的 nid

enum memblock_flags flags

新區域的標誌

描述

將新的 memblock 區域 [base, base + size) 新增到“memory”型別中。有關模式詳細資訊,請參閱 memblock_add_range() 的描述。

返回值

成功時返回 0,失敗時返回 -errno。

int __init_memblock memblock_add(phys_addr_t base, phys_addr_t size)

新增新的 memblock 區域

引數

phys_addr_t base

新區域的基地址

phys_addr_t size

新區域的大小

描述

將新的 memblock 區域 [base, base + size) 新增到“memory”型別中。有關模式詳細資訊,請參閱 memblock_add_range() 的描述。

返回值

成功時返回 0,失敗時返回 -errno。

bool __init_memblock memblock_validate_numa_coverage(unsigned long threshold_bytes)

檢查未分配節點 ID 的記憶體量是否小於閾值

引數

unsigned long threshold_bytes

可以具有未分配節點 ID 的最大記憶體大小(以位元組為單位)。

描述

有缺陷的韌體可能會報告不屬於任何節點的記憶體。檢查此類記憶體量是否低於 threshold_bytes

返回值

成功時返回 true,失敗時返回 false。

int __init_memblock memblock_isolate_range(struct memblock_type *type, phys_addr_t base, phys_addr_t size, int *start_rgn, int *end_rgn)

將給定範圍隔離成不相交的 memblock

引數

struct memblock_type *type

要隔離範圍的 memblock 型別

phys_addr_t base

要隔離的範圍基址

phys_addr_t size

要隔離的範圍大小

int *start_rgn

隔離區域起始的輸出引數

int *end_rgn

隔離區域結束的輸出引數

描述

遍歷 type 並確保區域不越過 [base, base + size) 定義的邊界。跨越邊界的區域將在邊界處被分割,這最多會建立兩個額外的區域。範圍內的第一個區域的索引在 *start_rgn 中返回,範圍後的第一個區域的索引在 *end_rgn 中返回。

返回值

成功時返回 0,失敗時返回 -errno。

void __init_memblock memblock_free(void *ptr, size_t size)

釋放啟動記憶體分配

引數

void *ptr

啟動記憶體分配的起始地址

size_t size

啟動記憶體塊的大小(以位元組為單位)

描述

釋放先前由 memblock_alloc_xx() API 分配的啟動記憶體塊。釋放的記憶體將不會被釋放到夥伴分配器。

int __init_memblock memblock_phys_free(phys_addr_t base, phys_addr_t size)

釋放啟動記憶體塊

引數

phys_addr_t base

啟動記憶體塊的物理起始地址

phys_addr_t size

啟動記憶體塊的大小(以位元組為單位)

描述

釋放先前由 memblock_phys_alloc_xx() API 分配的啟動記憶體塊。釋放的記憶體將不會被釋放到夥伴分配器。

int __init_memblock memblock_setclr_flag(struct memblock_type *type, phys_addr_t base, phys_addr_t size, int set, int flag)

設定或清除記憶體區域的標誌

引數

struct memblock_type *type

要設定/清除標誌的 memblock 型別

phys_addr_t base

區域的基地址

phys_addr_t size

區域的大小

int set

設定或清除標誌

int flag

要更新的標誌

描述

此函式隔離區域 [base, base + size),並設定/清除標誌。

返回值

成功時返回 0,失敗時返回 -errno。

int __init_memblock memblock_mark_hotplug(phys_addr_t base, phys_addr_t size)

用 MEMBLOCK_HOTPLUG 標誌標記可熱插拔記憶體。

引數

phys_addr_t base

區域的基物理地址

phys_addr_t size

區域的大小

返回值

成功時返回 0,失敗時返回 -errno。

int __init_memblock memblock_clear_hotplug(phys_addr_t base, phys_addr_t size)

清除指定區域的 MEMBLOCK_HOTPLUG 標誌。

引數

phys_addr_t base

區域的基物理地址

phys_addr_t size

區域的大小

返回值

成功時返回 0,失敗時返回 -errno。

int __init_memblock memblock_mark_mirror(phys_addr_t base, phys_addr_t size)

用 MEMBLOCK_MIRROR 標誌標記映象記憶體。

引數

phys_addr_t base

區域的基物理地址

phys_addr_t size

區域的大小

返回值

成功時返回 0,失敗時返回 -errno。

int __init_memblock memblock_mark_nomap(phys_addr_t base, phys_addr_t size)

用 MEMBLOCK_NOMAP 標誌標記記憶體區域。

引數

phys_addr_t base

區域的基物理地址

phys_addr_t size

區域的大小

描述

標記有 MEMBLOCK_NOMAP 的記憶體區域將不會被新增到物理記憶體的直接對映中。這些區域仍將由記憶體對映覆蓋。記憶體對映中代表 NOMAP 記憶體幀的 struct page 將是 PageReserved()。

注意

如果正在標記為 MEMBLOCK_NOMAP 的記憶體是從 memblock 分配的,則呼叫方必須通知 kmemleak 忽略該記憶體。

返回值

成功時返回 0,失敗時返回 -errno。

int __init_memblock memblock_clear_nomap(phys_addr_t base, phys_addr_t size)

清除指定區域的 MEMBLOCK_NOMAP 標誌。

引數

phys_addr_t base

區域的基物理地址

phys_addr_t size

區域的大小

返回值

成功時返回 0,失敗時返回 -errno。

int __init_memblock memblock_reserved_mark_noinit(phys_addr_t base, phys_addr_t size)

用 MEMBLOCK_RSRV_NOINIT 標誌標記一個保留記憶體區域,這將導致該區域的 struct pages 不會被初始化。

引數

phys_addr_t base

區域的基物理地址

phys_addr_t size

區域的大小

描述

對於標記有 MEMBLOCK_RSRV_NOINIT 的保留記憶體區域,struct pages 將不會被初始化。

返回值

成功時返回 0,失敗時返回 -errno。

int memblock_mark_kho_scratch(phys_addr_t base, phys_addr_t size)

將記憶體區域標記為 MEMBLOCK_KHO_SCRATCH。

引數

phys_addr_t base

區域的基物理地址

phys_addr_t size

區域的大小

描述

只有標記有 MEMBLOCK_KHO_SCRATCH 的記憶體區域才會在 kexec 移交的早期啟動期間被考慮用於分配。

返回值

成功時返回 0,失敗時返回 -errno。

int memblock_clear_kho_scratch(phys_addr_t base, phys_addr_t size)

清除指定區域的 MEMBLOCK_KHO_SCRATCH 標誌。

引數

phys_addr_t base

區域的基物理地址

phys_addr_t size

區域的大小

返回值

成功時返回 0,失敗時返回 -errno。

void __next_mem_range(u64 *idx, int nid, enum memblock_flags flags, struct memblock_type *type_a, struct memblock_type *type_b, phys_addr_t *out_start, phys_addr_t *out_end, int *out_nid)

用於for_each_free_mem_range()等的下一個函式。

引數

u64 *idx

指向u64迴圈變數的指標

int nid

節點選擇器,NUMA_NO_NODE 表示所有節點

enum memblock_flags flags

根據記憶體屬性從塊中選擇

struct memblock_type *type_a

指向獲取記憶體範圍的memblock_type的指標

struct memblock_type *type_b

指向排除記憶體的memblock_type的指標

phys_addr_t *out_start

指向 phys_addr_t 的指標,用於範圍的起始地址,可以是 NULL

phys_addr_t *out_end

指向 phys_addr_t 的指標,用於範圍的結束地址,可以是 NULL

int *out_nid

指向 int 的指標,用於範圍的 nid,可以是 NULL

描述

*idx 中找到第一個匹配 nid 的區域,填充輸出引數,並更新 *idx 以供下一次迭代。 *idx 的低32位包含type_a的索引,高32位索引type_b中每個區域之前的區域。例如,如果type_b區域如下所示:

0:[0-16), 1:[32-48), 2:[128-130)

高32位索引以下區域。

0:[0-0), 1:[16-32), 2:[48-128), 3:[130-MAX)

由於兩個區域陣列都已排序,函式會同步推進兩個索引並返回每個交集。

void __init_memblock __next_mem_range_rev(u64 *idx, int nid, enum memblock_flags flags, struct memblock_type *type_a, struct memblock_type *type_b, phys_addr_t *out_start, phys_addr_t *out_end, int *out_nid)

for_each_*_range_rev() 的通用下一個函式

引數

u64 *idx

指向u64迴圈變數的指標

int nid

節點選擇器,NUMA_NO_NODE 表示所有節點

enum memblock_flags flags

根據記憶體屬性從塊中選擇

struct memblock_type *type_a

指向獲取記憶體範圍的memblock_type的指標

struct memblock_type *type_b

指向排除記憶體的memblock_type的指標

phys_addr_t *out_start

指向 phys_addr_t 的指標,用於範圍的起始地址,可以是 NULL

phys_addr_t *out_end

指向 phys_addr_t 的指標,用於範圍的結束地址,可以是 NULL

int *out_nid

指向 int 的指標,用於範圍的 nid,可以是 NULL

描述

從type_a中找到未在type_b中標記為不適合的下一個範圍。

__next_mem_range()相反。

int __init_memblock memblock_set_node(phys_addr_t base, phys_addr_t size, struct memblock_type *type, int nid)

設定memblock區域的節點ID

引數

phys_addr_t base

要設定節點ID的區域的基地址

phys_addr_t size

要設定節點ID的區域的大小

struct memblock_type *type

要設定節點ID的memblock型別

int nid

要設定的節點ID

描述

將memblock type 區域在 [base, base + size) 範圍內的節點ID設定為 nid。必要時,跨區域邊界的區域會被分割。

返回值

成功時返回 0,失敗時返回 -errno。

void __init_memblock __next_mem_pfn_range_in_zone(u64 *idx, struct zone *zone, unsigned long *out_spfn, unsigned long *out_epfn)

for_each_*_range_in_zone()的迭代器

引數

u64 *idx

指向u64迴圈變數的指標

struct zone *zone

所有記憶體塊所在的區域

unsigned long *out_spfn

指向 ulong 的指標,用於範圍的起始 pfn,可以是 NULL

unsigned long *out_epfn

指向 ulong 的指標,用於範圍的結束 pfn,可以是 NULL

描述

此函式旨在作為for_each_mem_range型別迭代器的 zone/pfn 特定包裝器。它們專門用於延遲記憶體初始化例程中,因此我們在整個程式碼中重複了許多相同的邏輯。因此,與其在多個位置擁有它,不如將其集中到一個新的迭代器中,該迭代器可以完成它們所需的一切,這似乎更有意義。

phys_addr_t memblock_alloc_range_nid(phys_addr_t size, phys_addr_t align, phys_addr_t start, phys_addr_t end, int nid, bool exact_nid)

分配引導記憶體塊

引數

phys_addr_t size

要分配的記憶體塊的大小(位元組)

phys_addr_t align

區域和塊大小的對齊

phys_addr_t start

要分配的記憶體區域的下界(物理地址)

phys_addr_t end

要分配的記憶體區域的上界(物理地址)

int nid

要查詢的空閒區域的 nid,NUMA_NO_NODE 表示任意節點

bool exact_nid

控制分配是否回退到其他節點

描述

如果 end == MEMBLOCK_ALLOC_ACCESSIBLE,則從 memblock.current_limit 限制的記憶體區域進行分配。

如果指定的節點無法容納請求的記憶體且 exact_nid 為 false,則分配將回退到系統中的任何節點。

對於具有記憶體映象的系統,首先嚐試從啟用映象的區域分配,然後從任何記憶體區域重試。

此外,函式使用 kmemleak_alloc_phys 分配引導記憶體塊,它永不報告為洩漏。

返回值

成功時為分配的記憶體塊的物理地址,失敗時為0

phys_addr_t memblock_phys_alloc_range(phys_addr_t size, phys_addr_t align, phys_addr_t start, phys_addr_t end)

在指定範圍內分配記憶體塊

引數

phys_addr_t size

要分配的記憶體塊的大小(位元組)

phys_addr_t align

區域和塊大小的對齊

phys_addr_t start

要分配的記憶體區域的下界(物理地址)

phys_addr_t end

要分配的記憶體區域的上界(物理地址)

描述

startend 之間分配 size 位元組。

返回值

成功時返回分配的記憶體塊的物理地址,失敗時返回0

phys_addr_t memblock_phys_alloc_try_nid(phys_addr_t size, phys_addr_t align, int nid)

從指定的NUMA節點分配記憶體塊

引數

phys_addr_t size

要分配的記憶體塊的大小(位元組)

phys_addr_t align

區域和塊大小的對齊

int nid

要查詢的空閒區域的 nid,NUMA_NO_NODE 表示任意節點

描述

從指定的NUMA節點分配記憶體塊。如果該節點沒有可用記憶體,則嘗試從系統中的任何節點分配。

返回值

成功時返回分配的記憶體塊的物理地址,失敗時返回0

void *memblock_alloc_internal(phys_addr_t size, phys_addr_t align, phys_addr_t min_addr, phys_addr_t max_addr, int nid, bool exact_nid)

分配引導記憶體塊

引數

phys_addr_t size

要分配的記憶體塊的大小(位元組)

phys_addr_t align

區域和塊大小的對齊

phys_addr_t min_addr

要分配的記憶體區域的下界(物理地址)

phys_addr_t max_addr

要分配的記憶體區域的上界(物理地址)

int nid

要查詢的空閒區域的 nid,NUMA_NO_NODE 表示任意節點

bool exact_nid

控制分配是否回退到其他節點

描述

使用memblock_alloc_range_nid()分配記憶體塊,並將返回的物理地址轉換為虛擬地址。

如果無法滿足 min_addr 限制,則會放棄該限制,並回退到 min_addr 以下的記憶體進行分配。其他約束,例如節點和映象記憶體,將再次在memblock_alloc_range_nid()中處理。

返回值

成功時返回分配的記憶體塊的虛擬地址,失敗時返回NULL。

void *memblock_alloc_exact_nid_raw(phys_addr_t size, phys_addr_t align, phys_addr_t min_addr, phys_addr_t max_addr, int nid)

在精確節點上分配引導記憶體塊,不清零記憶體

引數

phys_addr_t size

要分配的記憶體塊的大小(位元組)

phys_addr_t align

區域和塊大小的對齊

phys_addr_t min_addr

優先從中分配記憶體區域的下界(物理地址)

phys_addr_t max_addr

優先從中分配記憶體區域的上界(物理地址),或者MEMBLOCK_ALLOC_ACCESSIBLE表示僅從memblock.current_limit值限制的記憶體中分配

int nid

要查詢的空閒區域的 nid,NUMA_NO_NODE 表示任意節點

描述

公共函式,如果啟用,提供額外的除錯資訊(包括呼叫者資訊)。不清零已分配的記憶體。

返回值

成功時返回分配的記憶體塊的虛擬地址,失敗時返回NULL。

void *memblock_alloc_try_nid_raw(phys_addr_t size, phys_addr_t align, phys_addr_t min_addr, phys_addr_t max_addr, int nid)

分配引導記憶體塊,不清零記憶體且不發生panic

引數

phys_addr_t size

要分配的記憶體塊的大小(位元組)

phys_addr_t align

區域和塊大小的對齊

phys_addr_t min_addr

優先從中分配記憶體區域的下界(物理地址)

phys_addr_t max_addr

優先從中分配記憶體區域的上界(物理地址),或者MEMBLOCK_ALLOC_ACCESSIBLE表示僅從memblock.current_limit值限制的記憶體中分配

int nid

要查詢的空閒區域的 nid,NUMA_NO_NODE 表示任意節點

描述

公共函式,如果啟用,提供額外的除錯資訊(包括呼叫者資訊)。不清零已分配的記憶體,如果請求無法滿足則不發生panic。

返回值

成功時返回分配的記憶體塊的虛擬地址,失敗時返回NULL。

void *memblock_alloc_try_nid(phys_addr_t size, phys_addr_t align, phys_addr_t min_addr, phys_addr_t max_addr, int nid)

分配引導記憶體塊

引數

phys_addr_t size

要分配的記憶體塊的大小(位元組)

phys_addr_t align

區域和塊大小的對齊

phys_addr_t min_addr

優先從中分配記憶體區域的下界(物理地址)

phys_addr_t max_addr

優先從中分配記憶體區域的上界(物理地址),或者MEMBLOCK_ALLOC_ACCESSIBLE表示僅從memblock.current_limit值限制的記憶體中分配

int nid

要查詢的空閒區域的 nid,NUMA_NO_NODE 表示任意節點

描述

公共函式,如果啟用,提供額外的除錯資訊(包括呼叫者資訊)。此函式會將已分配的記憶體清零。

返回值

成功時返回分配的記憶體塊的虛擬地址,失敗時返回NULL。

void *__memblock_alloc_or_panic(phys_addr_t size, phys_addr_t align, const char *func)

嘗試分配記憶體並在失敗時panic

引數

phys_addr_t size

要分配的記憶體塊的大小(位元組)

phys_addr_t align

區域和塊大小的對齊

const char *func

呼叫函式名

描述

此函式嘗試使用memblock_alloc分配記憶體,如果失敗,則會使用格式化的訊息呼叫panic。此函式不應直接使用,請使用宏memblock_alloc_or_panic。

void memblock_free_late(phys_addr_t base, phys_addr_t size)

直接將頁面釋放到buddy分配器

引數

phys_addr_t base

啟動記憶體塊的物理起始地址

phys_addr_t size

啟動記憶體塊的大小(以位元組為單位)

描述

這僅在memblock分配器已被拆除但系統仍在初始化時有用。頁面直接釋放到buddy分配器。

unsigned long memblock_estimated_nr_free_pages(void)

從memblock角度返回估計的空閒頁面數

引數

void

無引數

描述

在啟動期間,子系統可能需要在buddy提供精確數字之前,獲得整個系統中空閒頁面的粗略估計。尤其是在啟用CONFIG_DEFERRED_STRUCT_PAGE_INIT的情況下,從buddy獲得的數字在啟動期間可能非常不精確。

返回值

從memblock角度估計的空閒頁面數。

bool __init_memblock memblock_is_region_memory(phys_addr_t base, phys_addr_t size)

檢查區域是否為記憶體的子集

引數

phys_addr_t base

要檢查區域的基地址

phys_addr_t size

要檢查區域的大小

描述

檢查區域 [base, base + size) 是否為記憶體塊的子集。

返回值

如果為false則返回0,如果為true則返回非零值

bool __init_memblock memblock_is_region_reserved(phys_addr_t base, phys_addr_t size)

檢查區域是否與保留記憶體相交

引數

phys_addr_t base

要檢查區域的基地址

phys_addr_t size

要檢查區域的大小

描述

檢查區域 [base, base + size) 是否與保留記憶體塊相交。

返回值

如果相交則為True,否則為False。

void memblock_free_all(void)

將空閒頁面釋放到buddy分配器

引數

void

無引數

int reserve_mem_find_by_name(const char *name, phys_addr_t *start, phys_addr_t *size)

透過名稱查詢保留記憶體區域

引數

const char *name

附加到保留記憶體區域的名稱

phys_addr_t *start

如果找到,儲存起始地址

phys_addr_t *size

如果找到,儲存地址的大小。

描述

只有當找到 name 時,startsize 才會更新。

返回值

如果找到則為1,如果未找到則為0。

int reserve_mem_release_by_name(const char *name)

釋放具有給定名稱的保留記憶體區域

引數

const char *name

附加到保留記憶體區域的名稱

描述

強制釋放保留記憶體區域中的頁面,以便這些記憶體可以用作空閒記憶體。釋放後,保留區域的大小變為0。

返回值

如果釋放則為1,如果未找到則為0。