genalloc/genpool 子系統

核心中存在許多記憶體分配子系統,每個都旨在滿足特定的需求。然而,有時核心開發者需要為特定範圍的專用記憶體實現一個新的分配器;這些記憶體通常位於某個裝置上。該裝置驅動的作者當然可以編寫一個小的分配器來完成這項工作,但這會導致核心中充斥著數十個測試不充分的分配器。早在2005年,Jes Sorensen 從 sym53c8xx_2 驅動程式中提取了其中一個分配器,並將其作為建立專用記憶體分配器的通用模組釋出。這段程式碼在 2.6.13 版本中被合併;此後它被大大修改了。

使用此分配器的程式碼應包含 <linux/genalloc.h>。操作從使用以下函式之一建立記憶體池開始:

struct gen_pool *gen_pool_create(int min_alloc_order, int nid)

建立一個新的專用記憶體池

引數

int min_alloc_order

每個點陣圖位代表的位元組數的 2 的對數

int nid

記憶體池結構應在其上分配的節點 ID,或 -1

描述

建立一個新的專用記憶體池,可用於管理非由常規 kmalloc/kfree 介面管理的特殊用途記憶體。

struct gen_pool *devm_gen_pool_create(struct device *dev, int min_alloc_order, int nid, const char *name)

受管理的 gen_pool_create

引數

struct device *dev

提供 gen_pool 的裝置

int min_alloc_order

每個點陣圖位代表的位元組數的 2 的對數

int nid

用於已分配 gen_pool 的節點選擇器,對於所有節點使用 NUMA_NO_NODE

const char *name

gen_pool 的名稱或 NULL,用於標識裝置上的特定 gen_pool

描述

建立一個新的專用記憶體池,可用於管理非由常規 kmalloc/kfree 介面管理的特殊用途記憶體。該記憶體池將由裝置管理程式碼自動銷燬。

呼叫 gen_pool_create() 將建立一個記憶體池。分配的粒度由 min_alloc_order 設定;它是一個類似於頁分配器使用的 2 的對數基數(log-base-2)值,但它指的是位元組而非頁。因此,如果 min_alloc_order 傳入 3,則所有分配都將是 8 位元組的倍數。增加 min_alloc_order 會減少跟蹤記憶體池中記憶體所需的記憶體量。nid 引數指定應使用哪個 NUMA 節點來分配管理結構;如果呼叫者不關心,則可以為 -1。

“託管”介面 devm_gen_pool_create() 將記憶體池繫結到特定裝置。此外,當給定裝置被銷燬時,它會自動清理記憶體池。

記憶體池透過以下函式關閉:

void gen_pool_destroy(struct gen_pool *pool)

銷燬專用記憶體池

引數

struct gen_pool *pool

要銷燬的記憶體池

描述

銷燬指定的專用記憶體池。驗證沒有未完成的分配。

值得注意的是,如果給定記憶體池中仍有未完成的分配,此函式將採取相當極端的措施,即呼叫 BUG(),導致整個系統崩潰。特此警告。

一個新建立的記憶體池沒有可供分配的記憶體。在這種狀態下它幾乎無用,因此首要任務之一通常是向記憶體池新增記憶體。這可以透過以下函式之一完成:

int gen_pool_add(struct gen_pool *pool, unsigned long addr, size_t size, int nid)

向記憶體池新增一個新的專用記憶體塊

引數

struct gen_pool *pool

要新增新記憶體塊的記憶體池

unsigned long addr

要新增到記憶體池的記憶體塊的起始地址

size_t size

要新增到記憶體池的記憶體塊的位元組大小

int nid

記憶體塊結構和點陣圖應在其上分配的節點 ID,或 -1

描述

向指定的記憶體池新增一個新的專用記憶體塊。

成功返回 0,失敗返回負數 errno。

int gen_pool_add_owner(struct gen_pool *pool, unsigned long virt, phys_addr_t phys, size_t size, int nid, void *owner)

向記憶體池新增一個新的專用記憶體塊

引數

struct gen_pool *pool

要新增新記憶體塊的記憶體池

unsigned long virt

要新增到記憶體池的記憶體塊的虛擬起始地址

phys_addr_t phys

要新增到記憶體池的記憶體塊的物理起始地址

size_t size

要新增到記憶體池的記憶體塊的位元組大小

int nid

記憶體塊結構和點陣圖應在其上分配的節點 ID,或 -1

void *owner

釋出者希望在分配時回溯的私有資料

描述

向指定的記憶體池新增一個新的專用記憶體塊。

成功返回 0,失敗返回負數 errno。

呼叫 gen_pool_add() 會將從 addr 開始(在核心的虛擬地址空間中)的 size 位元組記憶體放入給定記憶體池中,再次使用 nid 作為輔助記憶體分配的節點 ID。gen_pool_add_virt() 變體將一個顯式物理地址與記憶體關聯起來;只有當記憶體池用於 DMA 分配時才需要這樣做。

從記憶體池分配記憶體(並將其放回)的函式是:

unsigned long gen_pool_alloc(struct gen_pool *pool, size_t size)

從記憶體池分配專用記憶體

引數

struct gen_pool *pool

要從中分配的記憶體池

size_t size

要從記憶體池分配的位元組數

描述

從指定的記憶體池中分配請求的位元組數。使用記憶體池分配函式(預設為首次適應演算法)。在沒有 NMI 安全的 cmpxchg 實現的架構上,不能在 NMI 處理程式中使用此函式。

void *gen_pool_dma_alloc(struct gen_pool *pool, size_t size, dma_addr_t *dma)

從記憶體池為 DMA 用途分配專用記憶體

引數

struct gen_pool *pool

要從中分配的記憶體池

size_t size

要從記憶體池分配的位元組數

dma_addr_t *dma

DMA 檢視的物理地址返回值。如果不需要,請使用 NULL

描述

從指定的記憶體池中分配請求的位元組數。使用記憶體池分配函式(預設為首次適應演算法)。在沒有 NMI 安全的 cmpxchg 實現的架構上,不能在 NMI 處理程式中使用此函式。

返回值

分配記憶體的虛擬地址,失敗時為 NULL

void gen_pool_free_owner(struct gen_pool *pool, unsigned long addr, size_t size, void **owner)

將已分配的專用記憶體釋放回記憶體池

引數

struct gen_pool *pool

要釋放到的記憶體池

unsigned long addr

要釋放回記憶體池的記憶體起始地址

size_t size

要釋放記憶體的位元組大小

void **owner

gen_pool_add() 時儲存的私有資料

描述

將先前分配的專用記憶體釋放回指定的記憶體池。在沒有 NMI 安全的 cmpxchg 實現的架構上,不能在 NMI 處理程式中使用此函式。

正如預期,gen_pool_alloc() 將從給定記憶體池分配 `size` 位元組的記憶體。gen_pool_dma_alloc() 變體為 DMA 操作分配記憶體,並在 dma 指向的空間中返回關聯的物理地址。這僅在記憶體透過 gen_pool_add_virt() 新增時才有效。請注意,此函式與 genpool 通常使用 unsigned long 值表示核心地址的模式不同;它返回一個 void *。

這一切看起來相對簡單;事實上,一些開發者顯然發現它過於簡單。畢竟,上述介面無法控制分配函式如何選擇返回哪個特定的記憶體塊。如果需要這種控制,以下函式將很有用:

unsigned long gen_pool_alloc_algo_owner(struct gen_pool *pool, size_t size, genpool_algo_t algo, void *data, void **owner)

從記憶體池分配專用記憶體

引數

struct gen_pool *pool

要從中分配的記憶體池

size_t size

要從記憶體池分配的位元組數

genpool_algo_t algo

呼叫者傳入的演算法

void *data

傳遞給演算法的資料

void **owner

可選地檢索塊所有者

描述

從指定的記憶體池中分配請求的位元組數。使用記憶體池分配函式(預設為首次適應演算法)。在沒有 NMI 安全的 cmpxchg 實現的架構上,不能在 NMI 處理程式中使用此函式。

void gen_pool_set_algo(struct gen_pool *pool, genpool_algo_t algo, void *data)

設定分配演算法

引數

struct gen_pool *pool

要更改分配演算法的記憶體池

genpool_algo_t algo

自定義演算法函式

void *data

algo 使用的額外資料

描述

對記憶體池中的每個記憶體分配呼叫 algo。如果 algo 為 NULL,則使用 gen_pool_first_fit 作為預設記憶體分配函式。

使用 gen_pool_alloc_algo() 的分配指定用於選擇要分配記憶體的演算法;預設演算法可以透過 gen_pool_set_algo() 設定。資料值會傳遞給演算法;大多數演算法會忽略它,但有時會需要。當然,可以編寫一個專用演算法,但目前已經有一套相當不錯的可用演算法:

  • gen_pool_first_fit 是一個簡單的首次適應分配器;如果沒有指定其他演算法,這就是預設演算法。

  • gen_pool_first_fit_align 強制分配具有特定對齊方式(透過 genpool_data_align 結構中的資料傳遞)。

  • gen_pool_first_fit_order_align 將分配對齊到大小的階數。例如,一個 60 位元組的分配將對齊到 64 位元組。

  • gen_pool_best_fit,正如人們所料,是一個簡單的最佳適應分配器。

  • gen_pool_fixed_alloc 在記憶體池內的一個特定偏移量處(透過資料引數在 genpool_data_fixed 結構中傳遞)分配記憶體。如果指示的記憶體不可用,則分配失敗。

還有一些其他函式,主要用於查詢記憶體池中的可用空間或遍歷記憶體塊等目的。然而,大多數使用者應該不需要超出上述描述的功能。希望對這個模組的更廣泛認知將有助於在未來避免編寫專用記憶體分配器。

phys_addr_t gen_pool_virt_to_phys(struct gen_pool *pool, unsigned long addr)

返回記憶體的物理地址

引數

struct gen_pool *pool

要從中分配的記憶體池

unsigned long addr

記憶體起始地址

描述

成功返回物理地址,失敗返回 -1。

void gen_pool_for_each_chunk(struct gen_pool *pool, void (*func)(struct gen_pool *pool, struct gen_pool_chunk *chunk, void *data), void *data)

對通用記憶體池的每個記憶體塊呼叫 func

引數

struct gen_pool *pool

通用記憶體池

void (*func)(struct gen_pool *pool, struct gen_pool_chunk *chunk, void *data)

要呼叫的函式 func

void *data

func 使用的額外資料

描述

對通用記憶體池的每個記憶體塊呼叫 funcfunc 在持有 rcu_read_lock 的情況下被呼叫。

bool gen_pool_has_addr(struct gen_pool *pool, unsigned long start, size_t size)

檢查地址是否在記憶體池範圍內

引數

struct gen_pool *pool

通用記憶體池

unsigned long start

起始地址

size_t size

區域大小

描述

檢查地址範圍是否落在指定的記憶體池內。如果整個範圍都包含在記憶體池中則返回 true,否則返回 false。

size_t gen_pool_avail(struct gen_pool *pool)

獲取記憶體池的可用空閒空間

引數

struct gen_pool *pool

要獲取可用空閒空間的記憶體池

描述

返回指定記憶體池的可用空閒空間。

size_t gen_pool_size(struct gen_pool *pool)

獲取記憶體池管理的記憶體位元組大小

引數

struct gen_pool *pool

要獲取大小的記憶體池

描述

返回記憶體池管理的記憶體位元組大小。

struct gen_pool *gen_pool_get(struct device *dev, const char *name)

獲取裝置的 gen_pool(如果有)

引數

struct device *dev

要從中檢索 gen_pool 的裝置

const char *name

gen_pool 的名稱或 NULL,用於標識裝置上的特定 gen_pool

描述

如果裝置存在 gen_pool 則返回它,否則返回 NULL。

struct gen_pool *of_gen_pool_get(struct device_node *np, const char *propname, int index)

透過 phandle 屬性查詢記憶體池

引數

struct device_node *np

裝置節點

const char *propname

包含 phandle(s) 的屬性名稱

int index

phandle 陣列中的索引

描述

返回包含從 phandle 屬性指向的裝置樹節點的物理地址開始的記憶體塊的記憶體池,如果未找到則返回 NULL。