The Android ION memory allocator
Back in December 2011, LWN reviewed the list of Android
kernel patches in the linux-next staging directory. The merging of these drivers,
one of which is a memory allocator called PMEM, holds the promise that the
mainline kernel release can one day boot an Android user space.
Since then, it has become clear
that PMEM is considered obsolete and
will be replaced by the ION memory manager.
ION is a generalized memory manager that Google introduced in the Android 4.0
ICS (Ice Cream Sandwich) release to address the issue of
fragmented memory management interfaces across different Android devices. There are at least three, probably more, PMEM-like interfaces.
On Android devices using NVIDIA Tegra, there is "NVMAP";
on Android devices using TI OMAP, there is "CMEM";
and on Android devices using Qualcomm MSM, there is "PMEM" .
All three SoC vendors are in the process of switching to ION.
This article takes a look at ION, summarizing its interfaces to user space
and to kernel-space drivers. Besides being a memory pool manager, ION also enables its clients to share buffers,
hence it treads the same ground as the
DMA buffer sharing framework from Linaro (DMABUF). This article will end with a comparison of the two buffer sharing schemes.
ION heaps
Like its PMEM-like predecessors, ION manages one or more memory pools, some of which are set
aside at boot time to combat fragmentation or to serve special hardware needs.
GPUs, display controllers, and cameras are some of the hardware blocks that
may have special memory requirements.
ION presents its memory pools as ION heaps. Each type of Android device can be
provisioned with a different set of ION heaps according to the memory
requirements of the device.
The provider of an ION heap must implement the following set of callbacks:
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 scatterlist *(*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 *heap, struct ion_buffer *buffer,
struct vm_area_struct *vma);
};
Briefly, allocate() and free() obtain or release an ion_buffer object from the heap.
A call to phys() will return the physical address and length of the buffer, but only for physically-contiguous buffers.
If the heap does not provide physically contiguous buffers, it does not have to provide this callback. Here ion_phys_addr_t
is a typedef of unsigned long, and will, someday, be replaced by
phys_addr_t in include/linux/types.h.
The map_dma() and unmap_dma() callbacks cause the buffer
to be prepared (or unprepared) for DMA. The map_kernel() and
unmap_kernel() callbacks map (or unmap) the physical memory into the
kernel virtual address space. A call to map_user() will map the
memory to user space. There is no unmap_user() because the
mapping is represented as a file descriptor in user space. The
closing of that file descriptor will cause the memory to be unmapped from
the calling process.