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 为底的对数,类似于页分配器使用的对数,但它指的是字节而不是页。因此,如果 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,失败时返回 -ve 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,失败时返回 -ve 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() 添加时才有效。请注意,此函数与使用 unsigned long 值表示内核地址的通常 genpool 模式不同;它返回 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 结构中的 data 传递)。
gen_pool_first_fit_order_align 将分配与大小的顺序对齐。例如,一个 60 字节的分配将是 64 字节对齐的。
正如人们所期望的那样,gen_pool_best_fit 是一个简单的最佳适应分配器。
gen_pool_fixed_alloc 在内存池内的特定偏移量处进行分配(通过 data 参数中的 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 使用的附加数据。
描述
为通用内存池的每个块调用 func。调用 func 时会保持 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 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 的属性名称
int index
phandle 数组中的索引
描述
返回包含从 phandle 属性指向的设备树节点的物理地址开始的块的池,如果未找到,则返回 NULL。