ION is a memory manager introduced by Google in Android ICS v4.0 to facilitate buffer-sharing.
When a buffer is shared by two components, the memory copies are eliminated, thus achieving zero-memory-copy.
Through this framework, one can allocate, free, share or import a memory buffer;
the latter can either live in main memory (CPU addressable) 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 or userspace, a set of IOCTLs are used to communicate.
ION allows you to have multiple memory allocators (vmalloc, kmalloc, carveout (genalloc) or CMA) but using a single API.
This abstraction can be used for power-savings by turning off unused memory banks; for example lets suppose 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 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
Android provides a library, in ”system/core/libion’,’ that maps the IOCTLs to C functions.
//Returns an ION control file descriptor.
ion_open()
//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)
</pre>
<h3> Kernel API</h3>
<pre>
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
/hardware/ti/omap4xxx/camera/MemoryManager.cpp
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
/hardware/ti/omap4xxx/''domx''/
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 [http://omappedia.org/wiki/Ducati_For_Dummies Distributed OpenMAX].
domx/omx_proxy_common/src/omx_proxy_common.c
OMX_ERRORTYPE OMX_ProxyCommonInit(OMX_HANDLETYPE hComponent)
: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;
PROXY_COMPONENT_PRIVATE *pCompPrv = (PROXY_COMPONENT_PRIVATE *) hComp->pComponentPrivate;
:called by: OMX-Core OMX_ComponentInit.
:::OMX_ComponentInit is implemented in: omx_proxy_h264enc.c, omx_proxy_mpeg4enc.c
OMX_ERRORTYPE PROXY_ComponentDeInit(OMX_HANDLETYPE hComponent)
: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
OMX_ERRORTYPE PROXY_AllocateBuffer(OMX_IN OMX_HANDLETYPE hComponent, OMX_INOUT OMX_BUFFERHEADERTYPE ** ppBufferHdr, OMX_IN OMX_U32 nPortIndex, OMX_IN OMX_PTR pAppPrivate, OMX_IN OMX_U32 nSizeBytes)
@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.
@return OMX_ERRORTYPE
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_ERRORTYPE PROXY_UseBuffer(OMX_IN OMX_HANDLETYPE hComponent,
OMX_INOUT OMX_BUFFERHEADERTYPE ** ppBufferHdr,
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 ???
OMX_ERRORTYPE PROXY_FreeBuffer(OMX_IN OMX_HANDLETYPE hComponent,OMX_IN OMX_U32 nPortIndex, OMX_IN OMX_BUFFERHEADERTYPE * pBufferHdr)
:called by ??? when ???
GPU Driver
/omap_kernel/drivers/gpu/pvr/
module.c, ion.h, ion.c, mm.c
omaplfb/omaplfb_displayclass.c
/omap_kernel/drivers/rpmsg/rpmsg_omx.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.
Debugging
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