Ion是google为了解决不同Android设备的内存碎片问题,在Android4.0中提出的下一代更通用的内存管理器,支持各种不同的内存分配,这些内存种类包括有:carveout, 物理连续的,虚拟连续的,iommu,secure memory,ion也可以在不同进程,user和kernel间共享buffer。

Ion把不同的内存空间用heap来分割管理,一般在dts中配置,对应的struct是ion_heap。要使用ion就需要创建client,用户态进程通过/dev/ion创建一个唯一的client,对应的struct是ion_client,只对kernel开放,用户态需用fd和handle来处理。这里的handle是ioctl返回的,是a buffer的抽象,用ion_handle来表示,真实的实体是ion_buffer

Heap

heap type

/**
* enum ion_heap_types - list of all possible types of heaps
* @ION_HEAP_TYPE_SYSTEM: memory allocated via vmalloc
* @ION_HEAP_TYPE_SYSTEM_CONTIG: memory allocated via kmalloc
* @ION_HEAP_TYPE_CARVEOUT: memory allocated from a prereserved
* carveout heap, allocations are physically
* contiguous
* @ION_HEAP_TYPE_DMA: memory allocated via DMA API
* @ION_NUM_HEAPS: helper for iterating over heaps, a bit mask
* is used to identify the heaps, so only 32
* total heap types are supported
*/
enum ion_heap_type {
ION_HEAP_TYPE_SYSTEM,
ION_HEAP_TYPE_SYSTEM_CONTIG,
ION_HEAP_TYPE_CARVEOUT,
ION_HEAP_TYPE_CHUNK,
ION_HEAP_TYPE_DMA,
ION_HEAP_TYPE_CUSTOM, /* must be last so device specific heaps always
are at the end of this enum */
ION_NUM_HEAPS = 16,
};
  • ION_HEAP_TYPE_SYSTEM就是虚拟连续物理不连续的内存空间,come from vmalloc。
  • ION_HEAP_TYPE_SYSTEM_CONTIG就是物理连续内存空间,come from kmalloc。
  • ION_HEAP_TYPE_CARVEOUT就是预先分配好的物理上连续内存。

ps: 代码环境是kernel3.18,msm平台。

不同的heap区间需要不同的操作内存方法,比如allocate, free, map等,对应的struct是ion_heap_ops

大概看下:

/**
* struct ion_heap_ops - ops to operate on a given heap
* @allocate: allocate memory
* @free: free memory. Will be called with
* ION_PRIV_FLAG_SHRINKER_FREE set in buffer flags when
* called from a shrinker. In that case, the pages being
* free'd must be truly free'd back to the system, not put
* in a page pool or otherwise cached.
* @phys get physical address of a buffer (only define on
* physically contiguous heaps)
* @map_dma map the memory for dma to a scatterlist
* @unmap_dma unmap the memory for dma
* @map_kernel map memory to the kernel
* @unmap_kernel unmap memory to the kernel
* @map_user map memory to userspace
* @unmap_user unmap memory to userspace
*
* allocate, phys, and map_user return 0 on success, -errno on error.
* map_dma and map_kernel return pointer on success, ERR_PTR on
* error. @free will be called with ION_PRIV_FLAG_SHRINKER_FREE set in
* the buffer's private_flags when called from a shrinker. In that
* case, the pages being free'd must be truly free'd back to the
* system, not put in a page pool or otherwise cached.
*/
struct ion_heap_ops {
int (*allocate)(struct ion_heap *heap,
struct ion_buffer *buffer, unsigned long len,
unsigned long align, unsigned long flags);
void (*free)(struct ion_buffer *buffer);
int (*phys)(struct ion_heap *heap, struct ion_buffer *buffer,
ion_phys_addr_t *addr, size_t *len);
struct sg_table * (*map_dma)(struct ion_heap *heap,
struct ion_buffer *buffer);
void (*unmap_dma)(struct ion_heap *heap, struct ion_buffer *buffer);
void * (*map_kernel)(struct ion_heap *heap, struct ion_buffer *buffer);
void (*unmap_kernel)(struct ion_heap *heap, struct ion_buffer *buffer);
int (*map_user)(struct ion_heap *mapper, struct ion_buffer *buffer,
struct vm_area_struct *vma);
int (*shrink)(struct ion_heap *heap, gfp_t gfp_mask, int nr_to_scan);
void (*unmap_user) (struct ion_heap *mapper, struct ion_buffer *buffer);
int (*print_debug)(struct ion_heap *heap, struct seq_file *s,
const struct list_head *mem_map);
};

system heap在创建时是system_heap_ops:

static struct ion_heap_ops system_heap_ops = {
.allocate = ion_system_heap_allocate,
.free = ion_system_heap_free,
.map_dma = ion_system_heap_map_dma,
.unmap_dma = ion_system_heap_unmap_dma,
.map_kernel = ion_heap_map_kernel,
.unmap_kernel = ion_heap_unmap_kernel,
.map_user = ion_heap_map_user,
.shrink = ion_system_heap_shrink,
};

struct ion_heap *ion_system_heap_create(struct ion_platform_heap *unused)
{
struct ion_system_heap *heap;
int i;
int pools_size = sizeof(struct ion_page_pool *) * num_orders;

heap = kzalloc(sizeof(struct ion_system_heap), GFP_KERNEL);
if (!heap)
return ERR_PTR(-ENOMEM);
heap->heap.ops = &system_heap_ops;
heap->heap.type = ION_HEAP_TYPE_SYSTEM;
heap->heap.flags = ION_HEAP_FLAG_DEFER_FREE;
...

system contig heap创建时是kmalloc_ops:

static struct ion_heap_ops kmalloc_ops = {
.allocate = ion_system_contig_heap_allocate,
.free = ion_system_contig_heap_free,
.phys = ion_system_contig_heap_phys,
.map_dma = ion_system_contig_heap_map_dma,
.unmap_dma = ion_system_contig_heap_unmap_dma,
.map_kernel = ion_heap_map_kernel,
.unmap_kernel = ion_heap_unmap_kernel,
.map_user = ion_heap_map_user,
};

struct ion_heap *ion_system_contig_heap_create(struct ion_platform_heap *unused)
{
struct ion_heap *heap;

heap = kzalloc(sizeof(struct ion_heap), GFP_KERNEL);
if (!heap)
return ERR_PTR(-ENOMEM);
heap->ops = &kmalloc_ops;
heap->type = ION_HEAP_TYPE_SYSTEM_CONTIG;
return heap;
}

创建流程是从probe开始:

msm_ion_probe -> msm_ion_heap_create -> ion_system_heap_create (if ION_HEAP_TYPE_SYSTEM)

heap ID

对于heap的描述,除了heap type,msm还定义了heap id,如下

struct ion_heap_desc {
unsigned int id;
enum ion_heap_type type;
const char *name;
unsigned int permission_type;
};

那是怎么用的了:

#ifdef CONFIG_OF
static struct ion_heap_desc ion_heap_meta[] = {
{
.id = ION_SYSTEM_HEAP_ID,
.name = ION_SYSTEM_HEAP_NAME,
},
{
.id = ION_SYSTEM_CONTIG_HEAP_ID,
.name = ION_KMALLOC_HEAP_NAME,
},
{
.id = ION_SECURE_HEAP_ID,
.name = ION_SECURE_HEAP_NAME,
},
{
.id = ION_CP_MM_HEAP_ID,
.name = ION_MM_HEAP_NAME,
.permission_type = IPT_TYPE_MM_CARVEOUT,
},
...

/**
* These are the only ids that should be used for Ion heap ids.
* The ids listed are the order in which allocation will be attempted
* if specified. Don't swap the order of heap ids unless you know what
* you are doing!
* Id's are spaced by purpose to allow new Id's to be inserted in-between (for
* possible fallbacks)
*/

enum ion_heap_ids {
INVALID_HEAP_ID = -1,
ION_CP_MM_HEAP_ID = 8,
ION_SECURE_HEAP_ID = 9,
ION_SECURE_DISPLAY_HEAP_ID = 10,
ION_CP_MFC_HEAP_ID = 12,
ION_CP_WB_HEAP_ID = 16, /* 8660 only */
ION_CAMERA_HEAP_ID = 20, /* 8660 only */
ION_SYSTEM_CONTIG_HEAP_ID = 21,
ION_ADSP_HEAP_ID = 22,
ION_PIL1_HEAP_ID = 23, /* Currently used for other PIL images */
ION_SF_HEAP_ID = 24,
ION_SYSTEM_HEAP_ID = 25,
ION_PIL2_HEAP_ID = 26, /* Currently used for modem firmware images */
ION_QSECOM_HEAP_ID = 27,
ION_AUDIO_HEAP_ID = 28,

ION_MM_FIRMWARE_HEAP_ID = 29,

ION_HEAP_ID_RESERVED = 31 /** Bit reserved for ION_FLAG_SECURE flag */
};

显然和dts有关。

ion heap configuration in dts

看下某个平台的msmxxxx-ion.dtsi定义

&soc {
qcom,ion {
compatible = "qcom,msm-ion";
#address-cells = <1>;
#size-cells = <0>;

qcom,ion-heap@25 {
reg = <25>;
qcom,ion-heap-type = "SYSTEM";
};

qcom,ion-heap@8 { /* CP_MM HEAP */
reg = <8>;
memory-region = <&secure_mem>;
qcom,ion-heap-type = "SECURE_DMA";
};

qcom,ion-heap@27 { /* QSEECOM HEAP */
reg = <27>;
memory-region = <&qseecom_mem>;
qcom,ion-heap-type = "DMA";
};
};
};

reg就是ion heap ID了, msm_ion_probe时会解析这个配置,根据这个配置创建不同的heap。

clients

对kernel,ion_client_create创建client后就可以用ion_alloc从heap中分配内存,用ion_handle对应分配的memory。分配好以后,client就拥有了ion handle,client要访问ion_hanlde关联的ion_buffer,就必须要有kernel virtual address,ion_map_kernel可以完成。ion_phys返回ion_buffer的物理地址,前提当然这块空间要是物理连续,比如ion_system_contig_heap_phys。

对user app,是通过open /dev/ion来创建的,对应的文件操作是:

static const struct file_operations ion_fops = {
.owner = THIS_MODULE,
.open = ion_open,
.release = ion_release,
.unlocked_ioctl = ion_ioctl,
.compat_ioctl = compat_ion_ioctl,
};

ion_open里会调用kernel的ion_client_create,和kernel本地创建client一个样。

alloc/free/map等操作都在ioctl中实现,分配cmd就是ION_IOC_ALLOC了,直接看代码很清楚,ION_IOC_IMPORT是结合ION_IOC_SHARE用来共享buffer在user app间。

看下heap的mapping。

ion heap map

ion heap可以被map到user space供app读写, 用的cmd是ION_IOC_MAP,然后用mmap就可以了。

对map into kernel,使用ion_map_kernel,不同heap不同map方法,比如对于system/contig heap就是ion_heap_map_kernel。

调试接口fs在/sys/kernel/debug/ion里,就是clients heaps两个目录(kernel3.18)。

完。