驱动代码分析:
static struct of_device_id tcc_ion_of_match[] = {
{ .compatible = "telechips,tcc-ion" },
{},
};
static struct platform_driver tcc_ion_driver = {
.probe = tcc_ion_probe,
.driver = {
.of_match_table = of_match_ptr(tcc_ion_of_match),
},
};
int tcc_ion_probe(struct platform_device *pdev)
{
struct ion_platform_data *pdata;
int i;
if (pdev->dev.of_node) {
pdata = tcc_ion_parse_dt(pdev);
}
num_heaps = pdata->nr;
heaps = kzalloc(sizeof(struct ion_heap *) * pdata->nr, GFP_KERNEL);
/* create the heaps as specified in the board file */
for (i = 0; i < num_heaps; i++) {
struct ion_platform_heap *heap_data = &pdata->heaps[i];
heaps[i] = ion_heap_create(heap_data);
ion_device_add_heap(heaps[i]);
}
return 0;
}
结构体定义:
struct ion_platform_data {
int nr;
struct ion_platform_heap *heaps;
};
/**
* struct ion_platform_heap - defines a heap in the given platform
* @type: type of the heap from ion_heap_type enum
* @id: unique identifier for heap.
* @name: used for debug purposes
* @base: base address of heap in physical memory if applicable
* @size: size of the heap in bytes if applicable
* @priv: private info passed from the board file
*
* Provided by the board file.
*/
struct ion_platform_heap {
enum ion_heap_type type;
unsigned int id;
const char *name;
phys_addr_t base;
size_t size;
phys_addr_t align;
void *priv;
};
设备树节点设置:
telechips,ion {
compatible = "telechips,tcc-ion";
ion-heap@2 {
reg = <2>;
telechips,ion-heap-name = "ion_carveout_heap";
};
ion-heap@3 {
reg = <3>;
telechips,ion-heap-name = "ion_carveout_cam_heap";
};
};
pmap_ump_reserved: ump_reserved {
compatible = "telechips,pmap";
telechips,pmap-name = "ump_reserved";
size = <PMAP_SIZE_UMP_RESERVED>;
};
tcc_ion_parse_dt函数,解析设备树:
static struct ion_platform_data *tcc_ion_parse_dt(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
struct device_node *child;
struct ion_platform_data *pdata;
int index = 0;
u32 val;
num_heaps = 0;
for_each_child_of_node(node, child)
++num_heaps;
pdata = devm_kzalloc(&pdev->dev, sizeof(struct ion_platform_data),
GFP_KERNEL);
pdata->heaps = devm_kzalloc(&pdev->dev,num_heaps * sizeof(struct ion_platform_heap),
GFP_KERNEL);
for_each_child_of_node(node, child) {
struct ion_platform_heap *heap = &pdata->heaps[index];
of_property_read_u32(child, "reg", &val);
heap->id = index;
heap->type = val;
of_property_read_string(child,"telechips,ion-heap-name", &heap->name);
if(heap->type == ION_HEAP_TYPE_CARVEOUT)
{
if(0 > pmap_get_info("ump_reserved", &pmap_ump_reserved)){
return ERR_PTR(-ENOMEM);
}
heap->base = pmap_ump_reserved.base;
heap->size = pmap_ump_reserved.size;
}
.....
++index;
}
pdata->nr = num_heaps;
return pdata;
}
ion_heap_create函数:
struct ion_heap *ion_heap_create(struct ion_platform_heap *heap_data)
{
struct ion_heap *heap = NULL;
switch (heap_data->type) {
#ifdef CONFIG_ION_CARVEOUT_HEAP
case ION_HEAP_TYPE_CARVEOUT:
heap = ion_carveout_heap_create(heap_data);
break;
#endif
......
default:
return ERR_PTR(-EINVAL);
}
heap->name = heap_data->name;
heap->id = heap_data->id;
return heap;
}
ion_carveout_heap_create函数:
struct ion_heap *ion_carveout_heap_create(struct ion_platform_heap *heap_data)
{
struct ion_carveout_heap *carveout_heap;
struct page *page;
size_t size;
int ret;
page = pfn_to_page(PFN_DOWN(heap_data->base));
size = heap_data->size;
ion_heap_pages_zero(page, size, pgprot_writecombine(PAGE_KERNEL));
carveout_heap = kzalloc(sizeof(*carveout_heap), GFP_KERNEL);
carveout_heap->pool = gen_pool_create(PAGE_SHIFT, -1);
carveout_heap->base = heap_data->base;
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;
......
return &carveout_heap->heap;
}
结构体定义:
struct ion_carveout_heap {
struct ion_heap heap;
struct gen_pool *pool;
phys_addr_t base;
......
};
//函数指针
typedef unsigned long (*genpool_algo_t)(unsigned long *map,unsigned long size,
unsigned long start, unsigned int nr,void *data, struct gen_pool *pool);
/*General purpose special memory pool descriptor.*/
struct gen_pool {
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;
......
};
struct ion_heap {
struct plist_node node;
struct ion_device *dev;
enum ion_heap_type type;
struct ion_heap_ops *ops;
unsigned long flags;
unsigned int id;
const char *name;
struct shrinker shrinker;
struct list_head free_list;
size_t free_list_size;
wait_queue_head_t waitqueue;
struct task_struct *task;
int (*debug_show)(struct ion_heap *heap, struct seq_file *, void *);
};
/*General purpose special memory pool chunk descriptor.*/
struct gen_pool_chunk {
struct list_head next_chunk; /* next chunk in pool */
atomic_long_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_create函数:分配一个内存池
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;
pool->data = NULL;
pool->name = NULL;
}
return pool;
}
gen_pool_first_fit函数:查找第一块满足要求的可用内存区域。
/*
* @map: The address to base the search on
* @size: The bitmap size in bits
* @start: The bitnumber to start searching at
* @nr: The number of zeroed bits we're looking for
* @data: additional data - unused
* @pool: pool to find the fit region memory from
*/
unsigned long gen_pool_first_fit(unsigned long *map, unsigned long size,
unsigned long start, unsigned int nr, void *data,struct gen_pool *pool)
{
return bitmap_find_next_zero_area(map, size, start, nr, 0);
}
static inline unsigned long bitmap_find_next_zero_area(unsigned long *map,
unsigned long size,
unsigned long start,
unsigned int nr,
unsigned long align_mask)
{
return bitmap_find_next_zero_area_off(map, size, start, nr, align_mask, 0);
}
/**
* bitmap_find_next_zero_area_off - find a contiguous aligned zero area
* @map: The address to base the search on
* @size: The bitmap size in bits
* @start: The bitnumber to start searching at
* @nr: The number of zeroed bits we're looking for
* @align_mask: Alignment mask for zero area
* @align_offset: Alignment offset for zero area.
*/
unsigned long bitmap_find_next_zero_area_off(unsigned long *map,
unsigned long size,
unsigned long start,
unsigned int nr,
unsigned long align_mask,
unsigned long align_offset)
{
unsigned long index, end, i;
again:
//以位图的map为基地址、start为起点、大小为size,开始查找第一个是0的比特值的索引下标
index = find_next_zero_bit(map, size, start);
//从该下标开始连续nr个bit都是0
end = index + nr;
//以位图的map为基地址、index为起点、大小为end,开始查找第一个是1的比特值的索引下标
i = find_next_bit(map, end, index);
if (i < end) { //如果i小于end,表示以index为起点、大小为nr个bit的范围内有比特值为1的值
start = i + 1;
goto again;
}
return index; //表示以index为起点、大小为nr个bit的范围内都是0
}
first fit:
First Fit分配器是最基本的内存分配器,它使用bitmap而不是空闲块列表来表示内存。在bitmap中,如果page对应位为1,则表示此page已经被分配,为0则表示此page没有被分配。为了分配小于一个page的内存块,First Fit分配器记录了最后被分配的PFN (Page Frame Number)和分配的结束地址在页内的偏移量。随后小的内存分配被Merge到一起并存储到同一页中。
First Fit分配器不会造成严重的内存碎片,但其效率较低,由于内存经常通过线性地址进行search,而First Fit中的小块内存经常在物理内存的开始处,为了分配大块内存而不得不扫描前面大量的内存。
参考:安全验证 - 知乎
gen_pool_add函数:
/**
* gen_pool_add - add a new chunk of special memory to the pool
* @pool: pool to add new memory chunk to
* @addr: starting address of memory chunk to add to pool
* @size: size in bytes of the memory chunk to add to pool
* @nid: node id of the node the chunk structure and bitmap should be
* allocated on, or -1
*/
static inline int gen_pool_add(struct gen_pool *pool, unsigned long addr,
size_t size, int nid)
{
return gen_pool_add_virt(pool, addr, -1, size, nid);
}
/**
* gen_pool_add_virt - add a new chunk of special memory to the pool
* @pool: pool to add new memory chunk to
* @virt: virtual starting address of memory chunk to add to pool
* @phys: physical starting address of memory chunk to add to pool
* @size: size in bytes of the memory chunk to add to pool
* @nid: node id of the node the chunk structure and bitmap should be
* allocated on, or -1
*/
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;
// pool->min_alloc_order等于PAGE_SHIFT,用位图表示:一个page对应1个bit,
//大小为size的内存有多少page,即有多少bit位
int nbits = size >> pool->min_alloc_order;
//BITS_TO_LONGS(nbits) * sizeof(long):
//nbits个bit数转换成long的数量*每个long占的字节数,计算出bitmap占的字节数
int nbytes = sizeof(struct gen_pool_chunk) +
BITS_TO_LONGS(nbits) * sizeof(long);
chunk = kzalloc_node(nbytes, GFP_KERNEL, nid);
chunk->phys_addr = phys;
chunk->start_addr = virt;
chunk->end_addr = virt + size - 1;
atomic_long_set(&chunk->avail, size);
//将内存块添加到内存池的链表中
list_add_rcu(&chunk->next_chunk, &pool->chunks);
return 0;
}
struct gen_pool_chunk {
struct list_head next_chunk; /* next chunk in pool */
atomic_long_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 */
};
carveout_heap_ops的实现:
static struct ion_heap_ops carveout_heap_ops = {
.allocate = ion_carveout_heap_allocate,
.free = ion_carveout_heap_free,
.map_user = ion_heap_map_user,
.map_kernel = ion_heap_map_kernel,
.unmap_kernel = ion_heap_unmap_kernel,
};
重点关注ion_carveout_heap_allocate函数,该函数的实现在下面会连续起来一起讲。
ion_device_add_heap函数:将堆添加到ion设备的链表上
void ion_device_add_heap(struct ion_heap *heap)
{
struct ion_device *dev = internal_dev;
heap->dev = dev;
heap->id = heap_id++;
//将heap添加到ion设备的heaps链表上
plist_node_init(&heap->node, -heap->id);
plist_add(&heap->node, &dev->heaps);
dev->heap_cnt++;
......
}
ion的入口驱动:
static int ion_device_create(void)
{
struct ion_device *idev;
idev = kzalloc(sizeof(*idev), GFP_KERNEL);
idev->dev.minor = MISC_DYNAMIC_MINOR;
idev->dev.name = "ion";
idev->dev.fops = &ion_fops;
misc_register(&idev->dev);
......
debugfs_done:
plist_head_init(&idev->heaps);
//internal_dev作为全局变量,在这里初始化,后面将会被到处使用
internal_dev = idev;
return 0;
}
subsys_initcall(ion_device_create);
static const struct file_operations ion_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = ion_ioctl,
};
ioctl函数:
long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
unsigned int dir;
union ion_ioctl_arg data;
dir = ion_ioctl_dir(cmd);
if (copy_from_user(&data, (void __user *)arg, _IOC_SIZE(cmd)))
return -EFAULT;
switch (cmd) {
case ION_IOC_ALLOC:
{
int fd;
fd = ion_alloc(data.allocation.len,data.allocation.heap_id_mask,
data.allocation.flags);
data.allocation.fd = fd;
break;
}
}
}
ion_alloc函数:
int ion_alloc(size_t len, unsigned int heap_id_mask, unsigned int flags)
{
struct ion_device *dev = internal_dev;
struct ion_buffer *buffer = NULL;
struct ion_heap *heap;
len = PAGE_ALIGN(len);
plist_for_each_entry(heap, &dev->heaps, node) {
/* if the caller didn't specify this heap id */
if (!((1 << heap->id) & heap_id_mask))
continue;
buffer = ion_buffer_create(heap, dev, len, flags);
if (!IS_ERR(buffer))
break;
}
......
}
结构体定义:
struct ion_buffer {
union {
struct rb_node node;
struct list_head list;
};
struct ion_device *dev;
struct ion_heap *heap;
unsigned long flags;
unsigned long private_flags;
size_t size;
void *priv_virt;
struct mutex lock;
int kmap_cnt;
void *vaddr;
struct sg_table *sg_table;
......
};
ion_buffer_create函数:
static struct ion_buffer *ion_buffer_create(struct ion_heap *heap,
struct ion_device *dev,
unsigned long len,
unsigned long flags)
{
struct ion_buffer *buffer;
struct sg_table *table;
buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
buffer->heap = heap;
buffer->flags = flags;
//对应ion_carveout_heap_allocate函数
heap->ops->allocate(heap, buffer, len, flags);
table = buffer->sg_table;
buffer->dev = dev;
buffer->size = len;
ion_buffer_add(dev, buffer);
return buffer;
}
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;
unsigned long paddr;
table = kmalloc(sizeof(*table), GFP_KERNEL);
sg_alloc_table(table, 1, GFP_KERNEL);
paddr = ion_carveout_allocate(heap, size);
sg_set_page(table->sgl, pfn_to_page(PFN_DOWN(paddr)), size, 0);
buffer->sg_table = table;
return 0;
}
ion_carveout_allocate函数:
static unsigned long ion_carveout_allocate(struct ion_heap *heap,
unsigned long size)
{
struct ion_carveout_heap *carveout_heap = container_of(heap, struct ion_carveout_heap,
heap);
unsigned long offset = gen_pool_alloc(carveout_heap->pool, size);
return offset;
}
gen_pool_alloc函数:
/**
* gen_pool_alloc - allocate special memory from the pool
* @pool: pool to allocate from
* @size: number of bytes to allocate from the pool
*/
unsigned long gen_pool_alloc(struct gen_pool *pool, size_t size)
{
return gen_pool_alloc_algo(pool, size, pool->algo, pool->data);
}
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;
//1 page =4KB,即2^12B,order即是指数12,用log2X即是对数
int order = pool->min_alloc_order;
int nbits, start_bit, end_bit, remain;
//(1UL << order) - 1低12位都是1
//只要size低12位不为0,就会向高位进1,即如果size剩余内存有不满1page时当作1page
nbits = (size + (1UL << order) - 1) >> order;
//遍历内存池的块链表
list_for_each_entry_rcu(chunk, &pool->chunks, next_chunk) {
if (size > atomic_long_read(&chunk->avail))
continue;
start_bit = 0;
end_bit = chunk_size(chunk) >> order;
retry:
//查找chunk位图中连续nbits都是0的开始下标,algo指向gen_pool_first_fit函数
start_bit = algo(chunk->bits, end_bit, start_bit,
nbits, data, pool);
if (start_bit >= end_bit)
continue;
//将块中位图以start_bit为开始、大小为nbits的值都设置为1,表示已用过
remain = bitmap_set_ll(chunk->bits, start_bit, nbits);
//计算开始地址、大小
addr = chunk->start_addr + ((unsigned long)start_bit << order);
size = nbits << order;
atomic_long_sub(size, &chunk->avail);
break;
}
return addr;
}