【内存管理】ION内存管理器(carveout heap预留内存)
什么是carveout heap
- carveout heap从代码中给的解释来看,是reserved预留的物理内存来实现的,这些内存buddy系统是没办法看到和管理到的
- carveout heap中的内存通过自建通用内存分配器gen_pool,使用bitmap来管理申请和释放
- 比如多数平台是在dts中配置保留的物理内存,将该内存专门用来作为carveout heap,ion_platform_heap结构体就是用来指示平台配置的
carvout heap创建
- ion_carveout_heap_create()函数:
- ion_carveout_heap_create接口是ION提供的创建carveout heap的接口,不同平台需要主动调用该接口来创建heap
- 其实现主要是将平台预留的物理内存放入gen_pool中
struct ion_heap *ion_carveout_heap_create(struct ion_platform_heap *heap_data)
{
struct ion_carveout_heap *carveout_heap;
int ret;
struct page *page;
size_t size;
//根据传递过来的base物理基地址转换成page(所有的物理内存都对应有一个page在表示)
page = pfn_to_page(PFN_DOWN(heap_data->base));
size = heap_data->size;
//将该内存块做清零处理:通过将page映射到vmalloc虚拟地址端,调用memset执行清零。
ret = ion_heap_pages_zero(page, size, pgprot_writecombine(PAGE_KERNEL));
if (ret)
return ERR_PTR(ret);
carveout_heap = kzalloc(sizeof(*carveout_heap), GFP_KERNEL);
if (!carveout_heap)
return ERR_PTR(-ENOMEM);
//创建内存池对象,PAGE_SHIFT表示pool中最小分配为4K大小
carveout_heap->pool = gen_pool_create(PAGE_SHIFT, -1);
if (!carveout_heap->pool) {
kfree(carveout_heap);
return ERR_PTR(-ENOMEM);
}
carveout_heap->base = heap_data->base;
//将该内存块(base到base+size之间)加入到pool中,通过bitmap来表示使用和释放
gen_pool_add(carveout_heap->pool, carveout_heap->base, heap_data->size,
-1);
carveout_heap->heap.ops = &carveout_heap_ops;
carveout_heap->heap.type = ION_HEAP_TYPE_CARVEOUT;
carveout_heap->heap.flags = ION_HEAP_FLAG_DEFER_FREE;
//返回ion_heap对象,该对象最后由平台自己add到ion dev的heaps链表中
return &carveout_heap->heap;
}
- gen_pool创建:gen_pool_create()函数
gen_pool看起来还挺简单,它通过chunk来管理不同内存块,min_alloc_order表示bitmap中一个bit代表多大内存,比如我们这里是PAGE_SHIFT=12,那么就是4K
algo是分配算法,其实就是bitmap的查找算法,其实就是调用bitmap_find_next_zero_area()函数
data目前没什么用,可能用来存放私有数据
struct gen_pool {
spinlock_t lock;
struct list_head chunks; /* list of chunks in this pool */
int min_alloc_order; /* minimum allocation order */
genpool_algo_t algo; /* allocation function */
void *data;
const char *name;
};
- chunk直译就是大块,其实就表示内存块
avail当前内存块的可用内存大小,按字节算
phys_addr按理说应该是跟start_addr一样,但从代码中被设置为-1
start_addr表示起始物理地址
end_addr结束物理地址
bit[0] 表示bitmap
/*
* General purpose special memory pool chunk descriptor.
*/
struct gen_pool_chunk {
struct list_head next_chunk; /* next chunk in pool */
atomic_t avail;
phys_addr_t phys_addr; /* physical starting address of memory chunk */
unsigned long start_addr; /* start address of memory chunk */
unsigned long end_addr; /* end address of memory chunk (inclusive) */
unsigned long bits[0]; /* bitmap for allocating memory chunk */
};
- gen_pool创建
struct gen_pool *gen_pool_create(int min_alloc_order, int nid)
{
struct gen_pool *pool;
pool = kmalloc_node(sizeof(struct gen_pool), GFP_KERNEL, nid);
if (pool != NULL) {
spin_lock_init(&pool->lock);
INIT_LIST_HEAD(&pool->chunks);
pool->min_alloc_order = min_alloc_order;
pool->algo = gen_pool_first_fit; //初始化查找算法,起始就是bitmap的查找
pool->data = NULL;
pool->name = NULL;
}
return pool;
}
- 将内存加入到gen_pool中,中间会创建chunk来存放和管理该内存
- gen_pool_add()->gen_pool_add_virt()函数:
int gen_pool_add_virt(struct gen_pool *pool, unsigned long virt, phys_addr_t phys,
size_t size, int nid)
{
struct gen_pool_chunk *chunk;
//根据一个bit代表多大内存,计算出当前内存需要多少个bit
int nbits = size >> pool->min_alloc_order;
//计算chunk结构体大小+需要表示内存大小的bit位数转换成long型变量,总共需要多少内存
int nbytes = sizeof(struct gen_pool_chunk) +
BITS_TO_LONGS(nbits) * sizeof(long);
//申请chunk对象,使用nbytes
chunk = kzalloc_node(nbytes, GFP_KERNEL, nid);
if (unlikely(chunk == NULL))
return -ENOMEM;
//初始化chunk中的内存信息,包括内存起始地址,结束地址,avail可用内存大小(size是字节)
chunk->phys_addr = phys;
chunk->start_addr = virt;
chunk->end_addr = virt + size - 1;
atomic_set(&chunk->avail, size);
spin_lock(&pool->lock);
//最后将当前chunk加入到gen_pool链表中
list_add_rcu(&chunk->next_chunk, &pool->chunks);
spin_unlock(&pool->lock);
return 0;
}
- 创建完成
carvout heap分配
- ion_carveout_heap_allocate()函数实现分配:
static int ion_carveout_heap_allocate(struct ion_heap *heap,
struct ion_buffer *buffer,
unsigned long size,
unsigned long flags)
{
struct sg_table *table;
phys_addr_t paddr;
int ret;
table = kmalloc(sizeof(*table), GFP_KERNEL);
if (!table)
return -ENOMEM;
//我们看到通过ion方式申请到的内存都是通过sg_table来组织
ret = sg_alloc_table(table, 1, GFP_KERNEL);
if (ret)
goto err_free;
//关键函数,申请内存,这里申请过来的是物理地址,因为gen_pool中存放的就是物理地址,通过bitmap来管理
paddr = ion_carveout_allocate(heap, size);
if (paddr == ION_CARVEOUT_ALLOCATE_FAIL) {
ret = -ENOMEM;
goto err_free_table;
}
//将申请到的内存加入到sg_table中
//生成的buffer最终都在ion_alloc总入口处转换成dma_buf,并最终转换成fd句柄返回给用户层
sg_set_page(table->sgl, pfn_to_page(PFN_DOWN(paddr)), size, 0);
buffer->sg_table = table;
return 0;
err_free_table:
sg_free_table(table);
err_free:
kfree(table);
return ret;
}
- 调用ion_carveout_allocate()->gen_pool_alloc()->gen_pool_alloc_algo()真正实现分配:
unsigned long gen_pool_alloc_algo(struct gen_pool *pool, size_t size,
genpool_algo_t algo, void *data)
{
struct gen_pool_chunk *chunk;
unsigned long addr = 0;
int order = pool->min_alloc_order;
int nbits, start_bit, end_bit, remain;
#ifndef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG
BUG_ON(in_nmi());
#endif
if (size == 0)
return 0;
//假如一个bit代表4K的内存(当前是4K),总共需要多少个bit才能表示用户申请的内存size
nbits = (size + (1UL << order) - 1) >> order;
rcu_read_lock();
//遍历当前gen_pool中所有的chunk内存块(一般ION都只创建一块),找到bitmap中连续nbits个bit为0的位
list_for_each_entry_rcu(chunk, &pool->chunks, next_chunk) {
if (size > atomic_read(&chunk->avail))
continue;
start_bit = 0;
//计算当前chunk中末位bit的下标
end_bit = chunk_size(chunk) >> order;
retry:
//开始查找,algo算法实际在创建gen_pool时已经指定,其实就是在bits中查找满足要求的位
start_bit = algo(chunk->bits, end_bit, start_bit,
nbits, data, pool);
if (start_bit >= end_bit)
continue;
//对找到的满足要求的bits位置1,表示分配出去
remain = bitmap_set_ll(chunk->bits, start_bit, nbits);
if (remain) {
remain = bitmap_clear_ll(chunk->bits, start_bit,
nbits - remain);
BUG_ON(remain);
goto retry;
}
//然后计算出分配到的内存实际的起始物理地址,start_addr+偏移地址
addr = chunk->start_addr + ((unsigned long)start_bit << order);
size = nbits << order;
//更新当前chunk中可用的物理内存大小
atomic_sub(size, &chunk->avail);
break;
}
rcu_read_unlock();
//返回申请到的内存起始物理地址
return addr;
}
- 分配完成
carvout heap释放
- 释放主要调用ion_carveout_free()->gen_pool_free()函数:
void gen_pool_free(struct gen_pool *pool, unsigned long addr, size_t size)
{
struct gen_pool_chunk *chunk;
int order = pool->min_alloc_order;
int start_bit, nbits, remain;
#ifndef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG
BUG_ON(in_nmi());
#endif
//计算出需要释放的内存大小转换成bit代表的内存,总共需要多少个bit来表示
//比如释放5K内存,由于order是4K,那么转换出来就是2个bit,也就是8K
//不过,由于在分配时已经按order对齐,所以一般传递进来的内存应该也是order对齐的
nbits = (size + (1UL << order) - 1) >> order;
rcu_read_lock();
list_for_each_entry_rcu(chunk, &pool->chunks, next_chunk) {
//遍历所有的chunk,找到当前要释放的内存物理地址所属的chunk
//通过物理地址范围确定所属chunk
if (addr >= chunk->start_addr && addr <= chunk->end_addr) {
//开始释放
BUG_ON(addr + size - 1 > chunk->end_addr);
//计算释放内存的地址对应的bitmap的起始bit
start_bit = (addr - chunk->start_addr) >> order;
//从起始bit的连续nbits个位置0,表示释放
remain = bitmap_clear_ll(chunk->bits, start_bit, nbits);
BUG_ON(remain);
//计算释放的内存,并更新chunk中的avail可用内存大小
size = nbits << order;
atomic_add(size, &chunk->avail);
rcu_read_unlock();
return;
}
}
rcu_read_unlock();
BUG();
}
- 释放完成
【内存管理】ION内存管理器(carveout heap预留内存)的更多相关文章
- java虚拟机学习-JVM内存管理:深入垃圾收集器与内存分配策略(4)
Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来. 概述: 说起垃圾收集(Garbage Collection,下文简称GC),大部分人都把这项 ...
- Java内存管理-掌握虚拟机类加载器(五)
勿在流沙筑高台,出来混迟早要还的. 做一个积极的人 编码.改bug.提升自己 我有一个乐园,面向编程,春暖花开! 上一篇介绍虚拟机类加载机制,讲解了类加载机制中的三个阶段,分别是:加载.连接(验证.准 ...
- JVM内存管理之垃圾搜集器参数精解
本文是GC相关的最后一篇,这次LZ只是罗列一下hotspot JVM中垃圾搜集器相关的重点参数,以及各个参数的解释.废话不多说,这就开始. 垃圾搜集器文章传送门 JVM内存管理------JAVA语言 ...
- 【深入理解JAVA虚拟机】第二部分.内存自动管理机制.3.垃圾收集器与内存分配策略
1.学习目的 当需要排查各种内存溢出. 内存泄漏问题时,当垃圾收集成为系统达到更高并发量的瓶颈时,我们就需要对这些“自动化”的技术实施必要的监控和调节. Java内存运行时区域的各个部分,其中程序计数 ...
- 垃圾回收GC:.Net自己主动内存管理 上(三)终结器
垃圾回收GC:.Net自己主动内存管理 上(三)终结器 垃圾回收GC:.Net自己主动内存管理 上(一)内存分配 垃圾回收GC:.Net自己主动内存管理 上(二)内存算法 垃圾回收GC:.Net自己主 ...
- <转载>内存管理内幕-动态分配的选择、折衷和实现 对malloc内存分配有个简单的描述,对内存管理有个大致的说明
这篇文章看后感觉不错,和我在glibc下的hurdmalloc.c文件里关于malloc的实现基本意思相同,同时,这篇文章还介绍了一些内存管理方面的知识,值得推荐. 原文链接地址为:http://ww ...
- C语言(记录)——内存相关_2:内存的编址与管理
本文是基于嵌入式的C语言 --------------------------------------------------------------------------------------- ...
- Java自动内存管理机制学习(一):Java内存区域与内存溢出异常
备注:本文引用自<深入理解Java虚拟机第二版> 2.1 运行时数据区域 Java虚拟机在执行Java程序的过程中把它所管理的内存划分为若干个不同的数据区域.这些区域都有各自的用途,以及创 ...
- java内存的分配和管理
常用的三个内存空间 栈内存 ,堆内存 ,方法区 栈内存存储的内容: 局部变量. 函数(栈中的局部变量,需要手动赋值.当变量,或者函数执行完毕,就自动被释放) 堆内存,存储的内容 :全局变量.数据容器. ...
- Tomcat配置与优化(内存、并发、管理)与性能监控
原文链接:http://blog.csdn.net/xyang81/article/details/51530979 一.JVM内存配置优化 在开发当中,当一个项目比较大时,依赖的jar包通常比较多, ...
随机推荐
- 面试官:MySQL一千万数据,怎么快速查询?
前言 面试官:来说说,一千万的数据,你是怎么查询的? me:直接分页查询,使用limit分页. 面试官:有实操过吗? me:肯定有呀 此刻献上一首<凉凉> 也许有些人没遇过上千万数据量的表 ...
- [NOIP1999 提高组] 旅行家的预算
洛咕 题意:一个旅行家想驾驶汽车以最少的费用从一个城市到另一个城市(假设出发时油箱是空的).给定两个城市之间的距离 \(D_1\).汽车油箱的容量 \(C\)(以升为单位).每升汽油能行驶的距离 \( ...
- linux 服务器 重命名
vim /etc/hosts 追加 10.10.134.68 RmcbTestDB3 RmcbTestDB3 # ip 名称 名称 127.0.0.1 localhost ...
- Python3 学习基础知识
python是动态语言(对象属性可以动态改变,删除添加..),不是强类型语言,所以和java,c/c++等强类型静态语言有不一样地方需要注意. 一:基本数据类型 变量 counter = 1 # ...
- 2020icpc济南 - A
组合数学 + 高斯消元 [A-Matrix Equation_第 45 届国际大学生程序设计竞赛(ICPC)亚洲区域赛(济南) (nowcoder.com)](https://codeforces.c ...
- PostProcess
后处理器: AutowiredAnnotationBeanPostProcess.class 可以处理@Autowired.@Value 如何注册:context.registerBean(xxx.c ...
- JS学习-异步JS
异步JS setTimeout() 我们希望传递给setTimeout()中运行的函数的任何参数,都必须作为列表末尾的附加参数传递给它. function sayHi(who) { alert('He ...
- 小米手机MIUI12获取hci.log的方法记录
按照之前的方式,开发者选项打开获取蓝牙HCI的log开关,但是在本地一直找不到log. 在网上查了很久资料,终于找到有用的方法了.记录一下. 感谢大佬 https://www.jianshu.com/ ...
- windows 设置修改本地 hosts 访问 github 快速访问 提高访问 github 速度
获取IP地址 查询 以下域名IP地址 github.com github.global.ssl.fastly.net assets-cdn.github.com 通过在线网址查询:https://we ...
- vue解决点击事件冒泡 .stop
vue解决点击事件冒泡 .stop <div @click="toCourse()" > <van-button type="primary" ...