ION内存管理.docx
- 文档编号:6686201
- 上传时间:2023-01-09
- 格式:DOCX
- 页数:36
- 大小:256.52KB
ION内存管理.docx
《ION内存管理.docx》由会员分享,可在线阅读,更多相关《ION内存管理.docx(36页珍藏版)》请在冰豆网上搜索。
ION内存管理
1.android之ION内存管理器
(1)--简介
为什么需要ION
回顾2011年末[2],LWN审查了androidkernelpatch[3],以期望将这些patch合并到kernel主线中。
但是PMEM(android实现的一个内存分配器)使这个愿望破灭了。
为什么PMEM不被linux社区接受的原因在[3]中有讲到。
从那开始,PMEM很明确会被完全抛弃,取而代之的是ION内存管理器。
ION是google在Android4.0ICS为了解决内存碎片管理而引入的通用内存管理器,它会更加融合kernel。
目前QCOMMSM,NVDIATegra,TIOMAP,MRVLPXA都用ION替换PMEM。
如何获取sourcecode
IONcodesresideindrivers/gpu/ion
Specificusageexamplesonomap4:
ION框架[1]
ION定义了四种不同的heap,实现不同的内存分配策略。
∙ION_HEAP_TYPE_SYSTEM:
通过vmalloc分配内存
∙ION_HEAP_TYPE_SYSTEM_CONTIG:
通过kmalloc分配内存
∙ION_HEAP_TYPE_CARVEOUT:
在保留内存块中(reservememory)分配内存
∙ION_HEAP_TYPE_CUSTOM:
由客户自己定义
下图是两个client共享内存的示意图。
图中有2个heap(每种heap都有自己的内存分配策略),每个heap中分配了若干个buffer。
client的handle管理到对应的buffer。
两个client是通过文件描述符fd来实现内存共享的。
IONAPIs
用户空间API
定义了6种ioctl接口,可以与用户应用程序交互。
∙ION_IOC_ALLOC:
分配内存
∙ION_IOC_FREE:
释放内存
∙ION_IOC_MAP:
获取文件描述符进行mmap (?
在code中未使用这个定义)
∙ION_IOC_SHARE:
创建文件描述符来实现共享内存
∙ION_IOC_IMPORT:
获取文件描述符
∙ION_IOC_CUSTOM:
调用用户自定义的ioctl
ION_IOC_SHARE及ION_IOC_IMPORT是基于DMABUF实现的,所以当共享进程获取文件描述符后,可以直接调用mmap来操作共享内存。
mmap实现由DMABUF子系统调用ION子系统中mmap回调函数完成。
内核空间API
内核驱动也可以注册为一个ION的客户端(client),可以选择使用哪种类型的heap来申请内存。
∙ion_client_create:
分配一个客户端。
∙ion_client_destroy:
释放一个客户端及绑定在它上面的所有ionhandle.
ionhandle:
这里每个ionhandle映射到一个buffer中,每个buffer关联一个heap。
也就是说一个客户端可以操作多块buffer。
Buffer申请及释放函数:
∙ion_alloc:
申请ion内存,返回ionhandle
∙ion_free:
释放ionhandle
ION通过handle来管理buffer,驱动需要可以访问到buffer的地址。
ION通过下面的函数来达到这个目的
∙ion_phys:
返回buffer的物理地址(address)及大小(size)
∙ion_map_kernel:
给指定的buffer创建内核内存映射
∙ion_unmap_kernel:
销毁指定buffer的内核内存映射
∙ion_map_dma:
为指定buffer创建dma映射,返回sglist(scatter/gatherlist)
∙ion_unmap_dma:
销毁指定buffer的dma映射
ION是通过handle而非buffer地址来实现驱动间共享内存,用户空间共享内存也是利用同样原理。
∙ion_share:
givenahandle,obtainabuffertopasstootherclients
∙ion_import:
givenanbufferinanotherclient,importit
∙ion_import_fd:
givenanfdobtainedviaION_IOC_SHAREioctl,importit
HeapAPI
Heap接口定义[drivers/gpu/ion/ion_priv.h]
这些接口不是暴露给驱动或者用户应用程序的。
/**
*struction_heap_ops-opstooperateonagivenheap
*@allocate:
allocatememory
*@free:
freememory
*@physgetphysicaladdressofabuffer(onlydefineonphysicallycontiguousheaps)
*@map_dmamapthememoryfordmatoascatterlist
*@unmap_dmaunmapthememoryfordma
*@map_kernelmapmemorytothekernel
*@unmap_kernelunmapmemorytothekernel
*@map_usermapmemorytouserspace
*/
struction_heap_ops{
int(*allocate)(struction_heap*heap,struction_buffer*buffer,unsignedlonglen,unsignedlongalign,unsignedlongflags);
void(*free)(struction_buffer*buffer);
int(*phys)(struction_heap*heap,struction_buffer*buffer,ion_phys_addr_t*addr,size_t*len);
structscatterlist*(*map_dma)(struction_heap*heap,struction_buffer*buffer);
void(*unmap_dma)(struction_heap*heap,struction_buffer*buffer);
void*(*map_kernel)(struction_heap*heap,struction_buffer*buffer);
void(*unmap_kernel)(struction_heap*heap,struction_buffer*buffer);
int(*map_user)(struction_heap*mapper,struction_buffer*buffer,structvm_area_struct*vma);
};
IONdebug
ION在/sys/kernel/debug/ion/提供一个debugfs接口。
每个heap都有自己的debugfs目录,client内存使用状况显示在/sys/kernel/debug/ion/<
$cat/sys/kernel/debug/ion/ion-heap-1
clientpidsize
test_ion289016384
每个由pid标识的client也有一个debugfs目录/sys/kernel/debug/ion/<
$cat/sys/kernel/debug/ion/2890
heap_name:
size_in_bytes
ion-heap-1:
4096011
参考文献
1. https:
//wiki.linaro.org/BenjaminGaignard/ion
2.
3.
2.android之ION内存管理器
(2)--cache
ION如何实现buffer共享的思路倒是很清晰的,但是深入代码研究,发现ION是依赖于DMAMapping的,而DMAmapping模块对我而言还是挺复杂的,看这个模块遇到的第一个问题就是cache。
既然是DMAmapping,直接将申请的buffer设置为non-cacheable不就行了?
这样就可以保证CPU通过DMA读写缓冲区的一致性了。
为什么还有ConsistentDMAmappings和StreamingDMAmappings一说呢?
下面先介绍一下Cache的概念。
Cache
Cache写机制:
Write-through与Write-back[5]
Cache写机制分为writethrough和writeback两种。
∙Write-through-Writeisdonesynchronouslybothtothecacheandtothebackingstore.
∙Write-back (or Write-behind)–Writingisdoneonlytothecache.Amodifiedcacheblockiswrittenbacktothestore,justbeforeitisreplaced.
Write-through(直写模式)在数据更新时,同时写入缓存Cache和后端存储。
此模式的优点是操作简单;缺点是因为数据修改需要同时写入存储,数据写入速度较慢。
Write-back(回写模式)在数据更新时只写入缓存Cache。
只在数据被替换出缓存时,被修改的缓存数据才会被写到后端存储。
此模式的优点是数据写入速度快,因为不需要写存储;缺点是一旦更新后的数据未被写入存储时出现系统掉电的情况,数据将无法找回。
Write-misses写缺失的处理方式
对于写操作,存在写入缓存缺失数据的情况,这时有两种处理方式:
∙Writeallocate (aka Fetchonwrite)–Datumatthemissed-writelocationisloadedtocache,followedbyawrite-hitoperation.Inthisapproach,writemissesaresimilartoread-misses.
∙No-writeallocate (aka Write-no-allocate, Writearound)–Datumatthemissed-writelocationisnotloadedtocache,andiswrittendirectlytothebackingstore.Inthisapproach,actuallyonlysystemreadsarebeingcached.
Writeallocate方式将写入位置读入缓存,然后采用write-hit(缓存命中写入)操作。
写缺失操作与读缺失操作类似。
No-writeallocate方式并不将写入位置读入缓存,而是直接将数据写入存储。
这种方式下,只有读操作会被缓存。
无论是Write-through还是Write-back都可以使用写缺失的两种方式之一。
只是通常Write-back采用Writeallocate方式,而Write-through采用No-writeallocate方式;因为多次写入同一缓存时,Writeallocate配合Write-back可以提升性能;而对于Write-through则没有帮助。
Cache的两个函数
--Flush
把Cache内容写回Memory,当Cache为Writethrough,不需要Flush
--Invalidate
把Cache内容直接丢掉不要。
Cache的使用场合
当有DMA在使用memory的时候,一般要用到cache的处理。
因为DMA在访问memory时是不经过cache的。
比较典型的比如在Ethernet,wireless,USB等driver里,DMA会操作descriptors和packetbuffers,Driver要做这些处理
--如果driver使用descripter和packetbuffer的地址都是cache的地址,那么
a).Driver在读descripter里一些状态,有没有收到包时,要对descripter当前结构里的内容做cacheinvalidate,收到packet后,也要对packetbuffer做cacheinvalidate
b).Driver在写descripter里一些状态,要发送包时,要对descripter当前结构里的内容做cacheflush,发送packet时,也要对packetbuffer做cacheflush
--有些driver会对descripter使用uncache地址,那么上面两种情况里invalidate/flush就不用做了。
一般很少会对packetbuffer也用uncache地址的,因为对packet内容的处理将会很频繁,使用uncache会很慢。
而descripter一般由于结构比较小,如果也使用cache地址的话,做invalidate/flush的时间消耗可能会比uncache的还要多。
下面文字采自于[6]
CB位的具体含义
00无cache,无写缓冲(strongly-ordered);任何对memory的读写都反映到总线上。
对memory的操作过程中CPU需要等待。
01无cache,有写缓冲(shareabledevice);读操作直接反映到总线上;写操作,CPU将数据写入到写缓冲后继续运行,由写缓冲进行写回操作。
10有cache,写通模式(write-through);读操作首先考虑cachehit;写操作时直接将数据写入写缓冲,如果同时出现cachehit,那么也更新cache。
11有cache,写回模式(write-back);读操作首先考虑cachehit;写操作也首先考虑cachehit。
处理流程图
Write-through模式处理流程:
AWrite-ThroughcachewithNo-WriteAllocation
Write-back模式处理流程:
参考文献
1. https:
//wiki.linaro.org/BenjaminGaignard/ion
2.
3.
4.《linux设备驱动程序》第三版JONATHANCORBET
5.
6.ARMArchitectureReferenceManual,ARMv7-AandARMv7-Redition DDI0406C
3.linux之DMAAPI--通用设备的动态DMA映射
文描述DMAAPI。
更详细的介绍请参看Documentation/DMA-API-HOWTO.txt。
API分为两部分,第一部分描述API,第二部分描述可以支持非一致性内存机器的扩展API。
你应该使用第一部分所描述的API,除非你知道你的驱动必须要支持非一致性平台。
第一部分DMAAPI
为了可以引用DMAAPI,你必须#include
1-1使用大块DMA一致性缓冲区(dma-coherentbuffers)
void*
dma_alloc_coherent(structdevice*dev,size_tsize,
dma_addr_t*dma_handle,gfp_tflag)
一致性内存:
设备对一块内存进行写操作,处理器可以立即进行读操作,而无需担心处理器高速缓存(cache)的影响。
同样的,处理器对一块内存进行些操作,设备可以立即进行读操作。
(在告诉设备读内存时,你可能需要确定刷新处理器的写缓存。
)
此函数申请一段大小为size字节的一致性内存,返回两个参数。
一个是dma_handle,它可以用作这段内存的物理地址。
另一个是指向被分配内存的指针(处理器的虚拟地址)。
注意:
由于在某些平台上,使用一致性内存代价很高,比如最小的分配长度为一个页。
因此你应该尽可能合并申请一致性内存的请求。
最简单的办法是使用dma_pool函数调用(详见下文)。
参数flag(仅存在于dma_alloc_coherent中)运行调用者定义申请内存时的GFP_flags(详见kmalloc)。
void*
dma_zalloc_coherent(structdevice*dev,size_tsize,
dma_addr_t*dma_handle,gfp_tflag)
对dma_alloc_coherent()的封装,如果内存分配成功,则返回清零的内存。
void
dma_free_coherent(structdevice*dev,size_tsize,void*cpu_addr,
dma_addr_tdma_handle)
释放之前申请的一致性内存。
dev,size及dma_handle必须和申请一致性内存的函数参数相同。
cpu_addr必须为申请一致性内存函数的返回虚拟地址。
注意:
和其他内存分配函数不同,这些函数必须要在中断使能的情况下使用。
1-2使用小块DMA一致性缓冲区
如果要使用这部分DMAAPI,必须#include
许多驱动程序需要为DMA描述符或者I/O内存申请大量小块DMA一致性内存。
你可以使用DMA内存池,而不是申请以页为单位的内存块或者调用dma_alloc_coherent()。
这种机制有点像structkmem_cache,只是它利用了DMA一致性内存分配器,而不是调用__get_free_pages()。
同样地,DMA内存池知道通用硬件的对齐限制,比如队列头需要N字节对齐。
structdma_pool*
dma_pool_create(constchar*name,structdevice*dev,
size_tsize,size_talign,size_talloc);
create()函数为设备初始化DMA一致性内存的内存池。
它必须要在可睡眠上下文调用。
name为内存池的名字(就像structkmem_cachename一样)。
dev及size就如dma_alloc_coherent()参数一样。
align为设备硬件需要的对齐大小(单位为字节,必须为2的幂次方)。
如果设备没有边界限制,可以设置该参数为0。
如果设置为4096,则表示从内存池分配的内存不能超过4K字节的边界。
void*
dma_pool_alloc(structdma_pool*pool,gfp_tgfp_flags,
dma_addr_t*dma_handle);
从内存池中分配内存。
返回的内存同时满足申请的大小及对齐要求。
设置GFP_ATOMIC可以确保内存分配被block,设置GFP_KERNEL(不能再中断上下文,不会保持SMP锁)允许内存分配被block。
和dma_alloc_coherent()一样,这个函数会返回两个值:
一个值是cpu可以使用的虚拟地址,另一个值是内存池设备可以使用的dma物理地址。
void
dma_pool_free(structdma_pool*pool,void*vaddr,
dma_addr_taddr);
返回内存给内存池。
参数pool为传递给dma_pool_alloc()的pool,参数vaddr及addr为dma_pool_alloc()的返回值。
void
dma_pool_destroy(structdma_pool*pool);
内存池析构函数用于释放内存池的资源。
这个函数在可睡眠上下文调用。
请确认在调用此函数时,所有从该内存池申请的内存必须都要归还给内存池。
1-3DMA寻址限制
int
dma_supported(structdevice*dev,u64mask)
用来检测该设备是否支持掩码所表示的DMA寻址能力。
比如mask为0x0FFFFFF,则检测该设备是否支持24位寻址。
返回1表示支持,0表示不支持。
注意:
该函数很少用于检测是否掩码为可用的,它不会改变当前掩码设置。
它是一个内部API而非供驱动者使用的外部API。
int
dma_set_mask(structdevice*dev,u64mask)
检测该掩码是否合法,如果合法,则更新设备参数。
即更新设备的寻址能力。
返回0表示成功,返回负值表示失败。
int
dma_set_coherent_mask(structdevice*dev,u64mask)
检测该掩码是否合法,如果合法,则更新设备参数。
即更新设备的寻址能力。
返回0表示成功,返回负值表示失败。
u64
dma_get_required_mask(structdevice*dev)
该函数返回平台可以高效工作的掩码。
通常这意味着返回掩码是可以寻址到所有内存的最小值。
检查该值可以让DMA描述符的大小尽量的小。
请求平台需要的掩码并不会改变当前掩码。
如果你想利用这点,可以利用改返回值通过dma_set_mask()设置当前掩码。
1-4流式DMA映射
dma_addr_t
dma_map_single(structdevice*dev,void*cpu_addr,size_tsize,
enumdma_data_directiondirection)
映射一块处理器的虚拟地址,这样可以让外设访问。
该函数返回内存的物理地址。
在dma_API中强烈建议使用表示D
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- ION 内存 管理