ION Buffer sharing mechanism

by groleo

Android ION is a memory manager introduced by Google to facilitate buffer-sharing.
When a buffer is shared by two components, the memory copies is eliminated, thus achieving zero-memory-copy.
Through this framework, one can allocate,free,share and import a buffer; the latter can either live in main memory or
on other device-specific memory.
The layer doing the actual memory allocation is called a ”heap” and it resides in the kernel.
Since ION can be used from kernelspace and userspace, a set of IOCTLs are used to communicate between the two.

ION allow you to have multiple memory allocators: vmalloc, kmalloc, carveout (genalloc), and CMA, with one common API.

From an embedded point of view, it can also help to save power.
For example you can have 2 carveout heaps:
one with id=0 on DDR bank 0
one with id=1 on DDR bank 1.
An ION client can ask for memory coming from one of the carveout heap and, when it is possible, the allocations are done in heap id=0 so you can shutdown DDR bank 1 because heap id=1 isn’t used.

Userspace Usage

Typically, the HALs will use ION to allocate large contiguous media buffers(See [[Contiguous_Memory_Allocator|CMA]])
and share those between hardware components. You can see the OMAP use-case below.

The userspace-kernel communication is done using ”’/dev/ion”’ as an entry point for the following IOCTLs:

 ION_IOC_ALLOC : allocate memory. Not yet CPU accessible
 ION_IOC_FREE  : free memory
 ION_IOC_MAP   : get a file descriptor to mmap
 ION_IOC_SHARE : creates a file descriptor to use to share an allocation
 ION_IOC_IMPORT : imports a shared file descriptor
 ION_IOC_CUSTOM : architecture specific ioctl. this is used to implement platform specific functionality.
            +----+       +----+           +----+
            |HAL1|       |HAL2|           |HAL3|
            +---++       +-+--+           +--+-+
                |          |                 |
                |          |                 |  ioctl(/dev/ion)
                |          |                 |  buffer sharing
                |          |                 |  buffer allocation
                |          |                 |
                |          |                 |
                +--------+ |  +--------------+
                         | |  |
                         | |  |
                       +-+-+--+-+                         +----------------+
                       |ION Core|<------------------------+omap framebuffer|
                       +-+-+-+-++    client registration  +----------------+
                         | | | |     buffer allocation
                         | | | |     buffer sharing
                         | | | |
                         | | | +-----------------------------------------------------+
          +--------------+ | |                                                       |
          |                | +-------------------------------+                       |
          |                |                                 |                       |
      +---+-------+    +---+-------------------+   +---------+-----------+   +-------+--------+
      |system heap|    |system contiguous heap |   |carveout heap        |   |omap tiler heap |
      |uses vmalloc    |uses kmalloc           |   |uses preserved memory|   |                |
      +-----------+    +-----------------------+   +---------------------+   +----------------+

Userspace API

A library is provided in ''system/core/libion'' that maps the IOCTLs to C functions.

//Returns an ION control file descriptor.

//Close an ION control file descriptor.
ion_close(int fd)

//Allocate a shared-buffer handler. The shared-buffer is not yet accessible from CPU.
ion_alloc(int fd, size_t len, size_t align, unsigned int flags, struct ion_handle **handle)

//Free a memory buffer.
ion_free(int fd, struct ion_handle *handle)

//Get a file descriptor to mmap.
ion_map(int fd, struct ion_handle *handle, size_t length, int prot,int flags, off_t offset, unsigned char **ptr, int *map_fd)

//Create a file descriptor to be used for sharing a buffer.
ion_share(int fd, struct ion_handle *handle, int *share_fd)

//Import a shared file descriptor and creates a ion_handle for it.
ion_import(int fd, int share_fd, struct ion_handle **handle)

//It's based on an ION_IOC_CUSTOM ioctl and it calls into the OMAP Heap.
ion_alloc_tiler(int fd, struct ion_handle *handle, size_t length, int prot,int flags, off_t offset, unsigned char **ptr, int *map_fd)

<h3> Kernel API </h3>
Kernel drivers can register themself as "clients" of the ION allocator and specify through a bit-mask what type of heap(s) they want to use. The Kernel API consists of the folowing functions:

//allocate an ion_client and returns it, dev beeing the global ION device.
ion_client_create(struct ion_device *dev,unsigned int heap_mask, const char *name)

//free's a ion_client and all it's handles
ion_client_destroy(struct ion_client *client)

//allocate ion memory, it return an opaque handle it
ion_alloc(struct ion_client *client, size_t len,size_t align, unsigned int flags)

//free a handle
ion_free(struct ion_client *client, struct ion_handle *handle)

//returns the physical address and len of a handle
ion_phys(struct ion_client *client, struct ion_handle *handle,ion_phys_addr_t *addr, size_t *len)

//return an sg_table describing a handle
ion_sg_table(struct ion_client *client,struct ion_handle *handle)

//create mapping for the given handle
ion_map_kernel(struct ion_client *client, struct ion_handle *handle)

//destroy a kernel mapping for a handle
ion_unmap_kernel(struct ion_client *client, struct ion_handle *handle)

//given an ion client, create a dma-buf fd
ion_share_dma_buf(struct ion_client *client, struct ion_handle *handle)

//given an dma-buf fd from the ion exporter get handle
ion_import_dma_buf(struct ion_client *client, int fd)

Adding a new type of heap

By default, the ION driver offers three types of heap. The intention is for the sets of heaps and mappers to grow to cover the use cases as they appear.

# ION_HEAP_TYPE_SYSTEM: memory allocated via vmalloc_user
# ION_HEAP_TYPE_SYSTEM_CONTIG: memory allocated via kzalloc
# ION_HEAP_TYPE_CARVEOUT: carveout memory is physically contiguous and set aside at boot

Custom heaps can also be implemented, as seen in the OMAP tiler(drivers/gpu/ion/omap/omap_tiler_heap.c)

ION heap provider 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
                    ,signed 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);

OMAP4 Usage

ION is used on OMAP to share buffers between the camera and the codecs. These devices are separate entities, and don’t have a memory of their own
A chunk of system memory is allocated by the Camera MemoryManager which is populated with the frames it captures.
DOMX is then used communicate with the codec unit. The unit will read the shared memory zone and encode/decode it.
The content of the shared buffers can be image data, or gralloc surface IDs, when the scenario involves the framebuffer as well.

Camera HAL


void* MemoryManager::allocateBuffer(int width, int height, const char* format, int &bytes, int numBufs)
In this case, ION is used to allocate '''numBufs''' of buffers located in the ION_HEAP_TYPE_CARVEOUT heap.
:called by: CameraHal::allocImageBufs
:called by: CameraHal::allocPreviewDataBufs

Codec HAL

         domx/omx_proxy_common/src/omx_proxy_common.c                      # The Common Proxy layer of DOMX
         omx_proxy_component/omx_camera/src/omx_proxy_camera.c             # The DOMX Proxy for Camera device
         omx_proxy_component/omx_video_dec/src/omx_proxy_videodec_secure.c # The DOMX Proxy for video decoder

”domx” refers to [ Distributed OpenMAX].


:ion_open is called to get the ION control handle.
:the ion control handle is saved into a pCompPrv->ion_fd
 OMX_COMPONENTTYPE *hComp          = (OMX_COMPONENTTYPE *) hComponent;
:called by: OMX-Core OMX_ComponentInit.
:::OMX_ComponentInit is implemented in: omx_proxy_h264enc.c, omx_proxy_mpeg4enc.c

:calls ion_close.
:called by:LOCAL_PROXY_H264E_ComponentDeInit, LOCAL_PROXY_MPEG4E_ComponentDeInit, PROXY_VIDDEC_Secure_ComponentDeInit

RPC_OMX_ERRORTYPE RPC_RegisterBuffer(OMX_HANDLETYPE hRPCCtx, int fd, struct ion_handle **handle)

:from ”’fd”’ using the ”’ION_IOC_IMPORT”’ ioctl, RPC_RegisterBuffer obtains an ion_handle, ”’handle”’
:!never called

 @param [in] hComponent
   Handle of the component to be accessed.  This is the component
   handle returned by the call to the OMX_GetHandle function.
 @param [out] ppBuffer
   pointer to an OMX_BUFFERHEADERTYPE structure used to receive.
   the pointer to the buffer header
 @param [in] nPortIndex
   nPortIndex is used to select the port on the component the buffer will
   be used with.  The port can be found by using the nPortIndex
   value as an index into the Port Definition array of the component.
 @param [in] pAppPrivate
   pAppPrivate is used to initialize the pAppPrivate member of the.
   buffer header structure.
 @param [in] nSizeBytes
   size of the buffer to allocate.  Used when bAllocateNew is true.
   If the command successfully executes, the return code will be
   OMX_ErrorNone.  Otherwise the appropriate OMX error will be returned.

:uses ion_map to make a userspace mapping of the ion handle. The mapping is done onto '''&((*ppBufferHdr)->pBuffer'''
:what does pBuffer buffer contains ?
:called by: LOCAL_PROXY_MPEG4E_AllocateBuffer  when ???
:In OMX-IL, the OMX_AllocateBuffer macro will request that the component allocate a new buffer and buffer header.
:::pBufferHdr->pBuffer = (OMX_U8 *)(pGrallocHandle->fd[0]);

OMX_IN OMX_U32 nPortIndex, OMX_IN OMX_PTR pAppPrivate,
OMX_IN OMX_U32 nSizeBytes, OMX_IN OMX_U8 * pBuffer)
:uses ion_map to make a userspace mapping of the ion handle.
:what does the mapped buffer contains ?
:called by '''PROXY_AllocateBuffer''' when ???

OMX_ERRORTYPE PROXY_AllocateBufferIonCarveout(PROXY_COMPONENT_PRIVATE *pCompPrv, size_t len, struct ion_handle **handle)
:calls ion_alloc to create an ion_handle:'''handle'''
:called by '''PROXY_AllocateBuffer''' when ???
:called by '''PROXY_UseBuffer''' when ???


:called by ??? when ???

GPU Driver

         module.c, ion.h, ion.c, mm.c

OMAP Use Case

The OMAP kernel uses the ION allocator to share a buffer between the framebuffer device driver and the TILERTiling and Isometric Lightweight Engine for Rotation. The tiler unit enables video rotation on OMAP4 and Netra chips.


The ION Allocator exports heap information through debugfs. Heap information is keyed after the PID and can be dumped like:

 cat /sys/kernel/debug/ion/ion-heap-1
 cat /sys/kernel/debug/ion/PID

External links

CMA Integration on ION
ION architecture documentation
basic test for ION heaps
test application