ION内存管理.docx

上传人:b****6 文档编号:6686201 上传时间:2023-01-09 格式:DOCX 页数:36 大小:256.52KB
下载 相关 举报
ION内存管理.docx_第1页
第1页 / 共36页
ION内存管理.docx_第2页
第2页 / 共36页
ION内存管理.docx_第3页
第3页 / 共36页
ION内存管理.docx_第4页
第4页 / 共36页
ION内存管理.docx_第5页
第5页 / 共36页
点击查看更多>>
下载资源
资源描述

ION内存管理.docx

《ION内存管理.docx》由会员分享,可在线阅读,更多相关《ION内存管理.docx(36页珍藏版)》请在冰豆网上搜索。

ION内存管理.docx

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

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 总结汇报

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1