Android pmem分析

最近在优化mx51项目中内存的使用,512MB RAM的划分为:

64MB pmem_adsp

64MB pmem gpu

96MB DMA zone

96MB GPU memory

这样只剩下192M给kernel 的Normal zone,由于普通的alloc_pages无法使用DMA zone的空间,内存相当紧张,想从pmem中释放一些内存出来,因此花了点时间了解pmem.


pmem是android为DSP vpu gpu等设备提供的一种内存分配机制,我们都知道vpu gpu这一类设备需要大块的连续物理内存以便进行硬件解码,硬件显示加速。PMEM就像一个小型的buddy内存管理系统,独立于linux kernel内存管理模块管理,不会受到内存管理中的外碎片的影响,同时还可以灵活的提供额外功能。

当然在系统运行一段时间后,PMEM也同样面临着外碎片问题,因此PMEM内存区的使用者尽量分配大块的内存,而不是零星的小内存。


pmem内存区使用情况:

PMEM_DEBUG控制pmem驱动是否提供debugfs接口

进入开发板后,可以通过如下命令安装debugfs文件系统

# mount -t debugfs none /sys/kernel/debug

# ls /sys/kernel/debug/
asoc
binder
hid
pmem_gpu
pmem_adsp
bluetooth
mmc3
mmc2
mmc1
mmc0
usb
gpio
bdi

其中pmem_gpu是为GPU显示加速提供的内存分配区,pmem_adsp是为vpu解码提供的内存非配区

可以使用cat命令查看pmem的使用情况:

# cat /sys/kernel/debug/pmem_gpu

# cat /sys/kernel/debug/pmem_adsp


应用层使用方法

1. 分配的内存只在一个进程中使用

pmem_fd = open("/dev/pmem_gpu", O_RDWR, 0);

pmem_base = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, pmem_fd, 0);


2. 不同进程间共享。这利用了PMEM 驱动的Connect功能

进程1:

pmem_fd0 = open("/dev/pmem_gpu", O_RDWR, 0);

pmem_base  = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, pmem_fd0, 0);

进程2:

pmem_fd1 = open("/dev/pmem_gpu", O_RDWR, 0);

ret = ioctl(pmem_fd1, PMEM_CONNECT, pmem_fd0);

ret = ioctl(pmem_fd1, PMEM_MAP, &region1);

一个进程打开PMEM设备,通过mmap操作映射内存到进程空间,该进程称为PMEM的master进程。其他进程可以再次打开该设备,然后通过PMEM_CONNECT操作,pmem_fd1就和pmem_fd0获得了相同的Pmem空间,这样该进程就称为PMEM的client进程。


Kernel空间PMEM driver分析


数据结构

Android pmem分析_第1张图片


图是网上偷来的

pmem[0]和pmem[1]

是pmem_info结构,每一个PMEM设备都有一个pmem_info结构

struct pmem_info {
    struct miscdevice dev;
    unsigned long base;
    unsigned char __iomem *vbase;
    unsigned long size;
    unsigned long num_entries;
    unsigned long garbage_pfn;
    int garbage_index;
    struct pmem_bits *bitmap;
    unsigned no_allocator;
    unsigned cached;
    unsigned buffered;
    unsigned allocated;
    struct semaphore data_list_sem;
    struct list_head data_list;
    struct rw_semaphore bitmap_sem;

    long (*ioctl)(struct file *, unsigned int, unsigned long);
    int (*release)(struct inode *, struct file *);
};

@dev: PMEM设备被注册在misc设备下

@base: 是这个PMEM设备对应的物理内存地址

@vbase: 是物理内存remap后得到的内核虚拟地址

@size: pmem设备的总容量,从驱动本身来说并不要求2次幂大小,但是imx51强制为2次幂,否则android无法启动

@num_entries: pmem最小分配单位数目,最小分配单位大小至少要为page的2次幂倍

@bitmap: 每个pmem设备把该设备的mem空间划分为PMEM_MIN_ALLOC大小的分配单元,每个分配单元都对应着一个pmem_bits结构,这个pmem_bits用来描述分配单元的大小和使用情况。

@no_allocator: 这个pmem设备,是否有内存分配器,有的pmem设备只被一个进程使用一次,这种情况下,是不需要分配器的,整个pmem设备的内存一次性的全部分配给第一个进行映射的进程。

@cached:所谓cached,就是pmem内存是否使能cpu 缓冲。如果需要混合使用cached和uncached内存,那么可以设置这个标志,并且使用O_SYNC标志打开设备文件来获取uncached区域。

@allocaed:仅当no_allocator设置时有效,用来表示整个pmem空间是否被分配。

@data_list:系统的所有pmem  设备都通过data_list连接到一起。


PMEM设备接口

内核为每一个PMEM创建一个misc 设备节点,应用层通过这个设备节点的file_operations操作PMEM设备。


内存分配:

PMEM内存是供应用空间程序使用的,kernel和驱动并不会使用PMEM。应用程序首先打开相应PMEM设备节点,使用mmap系统调用或者PMEM_MAP PMEM_ALLOCATE ioctl申请pmem内存


内存释放:

应用程序可以显示的调用PMEM_UNMAP来释放分配的pmem内存,文件release操作会释放这个PMEM设备所有分配的内存。因此只要close了文件,那么就保证所有分配的内存都被回收,防止由于用户疏忽或者程序异常造成的内存泄漏。


获取物理内存地址

sometimes,应用空间需要获取物理地址

ioctl PMEM_GET_PHYS 获取pmem内存物理地址,我们知道pmem驱动维护着多个pmem设备,并且每个pmem设备还可能进行多次分配。那么这里是如何确定请求哪个pmem设备,哪一次分配的物理地址呢?

通过设备节点的从设备号,就可以确定这次请求的pmem设备;此外在file结构的private_data是一个pmem_data结构,pmem_data->index指向了最后一次分配的索引位置。因此PMEM_GET_PHYS获取的是这个pmem设备最后一次分配内存的起始物理地址。


PMEM_GET_SIZE, PMEM_GET_TOTAL_SIZE

PMEM_GET_SIZE用来获取给定PMEM设备最后一次分配的size;而PMEM_GET_TOTAL_SIZE则获取给定PMEM的整个空间size


PMEM_CACHE_FLUSH

刷新给定内存区的cache,dmac_flush_range包括writeback和invalidate操作,使得写cache写回,读cache无效。

你可能感兴趣的:(Android pmem分析)