linux CMA使用机制分析--基于SigmaStar SSD202

前一篇文档提到CMA相关的内容Linux虚拟内存映射分析以及CMA测试 - 以SSD202为例,其主要使用标准内核CMA API进行,并直接经由CMA进行管理,通过进一步查探,SSD202内核有独立的模块msys位于CMA之上,另外多做了一层管理

模块位置:drivers/sstar/msys/ms_msys.c

此模块属于综合性模块,包含多个类型,主要是与cpu特性相关的,也就是独立属于SSD202平台的。

首先看一下初始化,大概就能看出其功能

static int __init msys_init(void)
{
    int ret;

    //ret = misc_register(&sys_dev);
    ret = register_chrdev(MAJOR_SYS_NUM, "msys", &msys_fops);
    if (ret != 0) {
        MSYS_ERROR("cannot register msys  (err=%d)\n", ret);
    }

    sys_dev.this_device = device_create(msys_get_sysfs_class(), NULL,
	    MKDEV(MAJOR_SYS_NUM, MINOR_SYS_NUM), NULL, "msys");

    sys_dev.this_device->dma_mask=&sys_dma_mask;
    sys_dev.this_device->coherent_dma_mask=sys_dma_mask;

    mutex_init(&dmem_mutex);
    INIT_LIST_HEAD(&kept_mem_head);
    INIT_LIST_HEAD(&fixed_mem_head);

    device_create_file(sys_dev.this_device, &dev_attr_dmem);
    device_create_file(sys_dev.this_device, &dev_attr_dmem_alloc);
    device_create_file(sys_dev.this_device, &dev_attr_release_dmem);
#ifdef CONFIG_MSYS_DMEM_SYSFS_ALL
    device_create_file(sys_dev.this_device, &dev_attr_fixed_dmem);
    device_create_file(sys_dev.this_device, &dev_attr_unfix_dmem);
    device_create_file(sys_dev.this_device, &dev_attr_dmem_retry_count);
    device_create_file(sys_dev.this_device, &dev_attr_dmem_realloc);
#endif

#ifdef CONFIG_MS_PIU_TICK_API
    device_create_file(sys_dev.this_device, &dev_attr_PIU_T);
#endif

#ifdef CONFIG_MS_CPU_FREQ
    device_create_file(sys_dev.this_device, &dev_attr_TEMP_R);
#endif

    device_create_file(sys_dev.this_device, &dev_attr_CHIP_VERSION);

#ifdef CONFIG_MS_US_TICK_API
    device_create_file(sys_dev.this_device, &dev_attr_us_ticks);
#endif

#ifdef CONFIG_SS_PROFILING_TIME
    device_create_file(sys_dev.this_device, &dev_attr_booting_time);
#endif
#if defined(CONFIG_ARCH_INFINITY2)
#if defined(CONFIG_MS_MOVE_DMA)
    device_create_file(sys_dev.this_device, &dev_attr_movedma);
#endif
    device_create_file(sys_dev.this_device, &dev_attr_bytedma);
    device_create_file(sys_dev.this_device, &dev_attr_bytedmacp);
#endif
#if defined(CONFIG_PROC_FS) && defined(CONFIG_MSYS_REQUEST_PROC)
    mutex_init(&proc_info_mutex);
    INIT_LIST_HEAD(&proc_info_head);
    proc_class=proc_mkdir("mstar",NULL);
    proc_zen_kernel=proc_mkdir("kernel",proc_class);
#endif

    return 0;
}

其主要功能有:

1. 创建msys设备文件,支持ioctl相关设置

2. cma memory的属性控制

3. Frequency以及Temperature相关

分析CMA使用,可以从cma memory的属性控制入手,也就是

device_create_file(sys_dev.this_device, &dev_attr_dmem);

device_create_file(sys_dev.this_device, &dev_attr_dmem_alloc);

device_create_file(sys_dev.this_device, &dev_attr_release_dmem);

管理核心结构为

typedef struct

{

unsigned int VerChk_Version;

char name[16];

unsigned int length; //32 bit

unsigned long long phys; //64 bit

unsigned long long kvirt; //Kernel Virtual Address 64 bit

unsigned int option; //reserved

unsigned int VerChk_Size;

} __attribute__ ((__packed__)) MSYS_DMEM_INFO;

基本上可以理解为将cma区域通过name和length来描述和管理

核心的API有2个msys_request_dmem 和 msys_release_dmem

有了属性文件,方便在userspace进行使用

查阅已创建的cam区

cat /sys/class/mstar/msys/dmem

root@wireless-tag:~# cat /sys/class/mstar/msys/dmem

序号:大小                 物理地址   名称
0000 : 0x00001000@2644C000 [AESDMA_ENG1]
0001 : 0x00001000@2644B000 [AESDMA_ENG]
0002 : 0x000FA000@264C0000 [test3]
0003 : 0x00060812@26450000 [emac1_buff]
0004 : 0x00000812@26449000 [emac0_buff]
0005 : 0x00000840@26448000 [BDMA]

TOTAL: 0x0015D864    总大小

释放cam区

指定名称即可,比如释放test3,echo test3 > /sys/class/mstar/msys/release_dmem

[ 4385.943383] cma: cma_release(page c63be800)
[ 4385.947603] [MSYS] DMEM [test3]@0x264C0000 successfully released

再次查看已分配区域确认一下:

root@wireless-tag:~# cat /sys/class/mstar/msys/dmem
0000 : 0x00001000@2644C000 [AESDMA_ENG1]
0001 : 0x00001000@2644B000 [AESDMA_ENG]
0002 : 0x00060812@26450000 [emac1_buff]
0003 : 0x00000812@26449000 [emac0_buff]
0004 : 0x00000840@26448000 [BDMA]

TOTAL: 0x00063864

分配新的区域

指定name和length即可,echo new1k 1024 > /sys/class/mstar/msys/dmem_alloc

[ 4509.950954] MSYS: DMEM request: [new1k]:0x00000400 Bytes
[ 4509.956153] cma: cma_alloc(cma c0435ef0, count 1, align 0)
[ 4509.961723] cma: cma_alloc(): returned c63bd9a0
[ 4509.966504] MSYS: DMEM request: [new1k]:0x00000400 success, CPU phy:@0x2644D000, virt:@0xC644D000

确认结果

root@wireless-tag:~# cat /sys/class/mstar/msys/dmem
0000 : 0x00000400@2644D000 [new1k]
0001 : 0x00001000@2644C000 [AESDMA_ENG1]
0002 : 0x00001000@2644B000 [AESDMA_ENG]
0003 : 0x00060812@26450000 [emac1_buff]
0004 : 0x00000812@26449000 [emac0_buff]
0005 : 0x00000840@26448000 [BDMA]

TOTAL: 0x00063C64

分配API  msys_request_dmem实现

int msys_request_dmem(MSYS_DMEM_INFO *mem_info)

{

dma_addr_t phys_addr;

int err=0;

int retry=0;


if(mem_info->name[0]==0||strlen(mem_info->name)>15)

{

MSYS_ERROR( "Invalid DMEM name!! Either garbage or empty name!!\n");

return -EINVAL;

}


/*if(mem_info->length<=0)

{

MSYS_ERROR( "Invalid DMEM length!! [%s]:0x%08X\n",mem_info->name,(unsigned int)mem_info->length);

return -EFAULT;

}*/


MSYS_ERROR("DMEM request: [%s]:0x%08X Bytes\n",mem_info->name,(unsigned int)mem_info->length);


mutex_lock(&dmem_mutex);

// if(mem_info->name[0]!=0)

{

struct list_head *ptr;

struct DMEM_INFO_LIST *entry;

---所有cam块通过全局list管理kept_mem_head

list_for_each(ptr, &kept_mem_head)

{

entry = list_entry(ptr, struct DMEM_INFO_LIST, list);
---已存在重新分配

if (0==strncmp(entry->dmem_info.name, mem_info->name,strnlen(mem_info->name,15)))

{

if(dmem_realloc_enabled && (entry->dmem_info.length != mem_info->length))

{

MSYS_ERROR("dmem realloc %s", entry->dmem_info.name);
----先释放旧的空间,直接调用cma相关API

dma_free_coherent(sys_dev.this_device, PAGE_ALIGN(entry->dmem_info.length),(void *)(uintptr_t)entry->dmem_info.kvirt,entry->dmem_info.phys);

MSYS_ERROR("DMEM [%s]@0x%08X successfully released\n",entry->dmem_info.name,(unsigned int)entry->dmem_info.phys);

list_del_init(&entry->list);

break;

}

else

{

memcpy(mem_info,&entry->dmem_info,sizeof(MSYS_DMEM_INFO));

MSYS_ERROR("DMEM kept entry found: name=%s, phys=0x%08X, length=0x%08X\n",mem_info->name,(unsigned int)mem_info->phys,(unsigned int)mem_info->length);

goto BEACH_ENTRY_FOUND;

}

}

}


//MSYS_PRINT(KERN_WARNING"can not found kept direct requested memory entry name=%s\n",mem_info.name);


}

// else

// {

// MSYS_PRINT(" !!ERROR!! Anonymous DMEM request is forbidden !!\n");

// return -EFAULT;

// }

-----分配新空间
while( !(mem_info->kvirt = (u64)(uintptr_t)dma_alloc_coherent(sys_dev.this_device, PAGE_ALIGN(mem_info->length), &phys_addr, GFP_KERNEL)) )

{

if(retry >= dmem_retry_count)

{

MSYS_ERROR( "unable to allocate direct memory\n");

err = -ENOMEM;

goto BEACH_ALLOCATE_FAILED;

}

MSYS_ERROR( "retry ALLOC_DMEM %d [%s]:0x%08X\n", retry, mem_info->name, (unsigned int)mem_info->length);

sysctl_compaction_handler(NULL, 1, NULL, NULL, NULL);

msleep(1000);

retry++;

}


mem_info->phys=(u64)phys_addr;

{
----创建list管理节点
struct DMEM_INFO_LIST *new=(struct DMEM_INFO_LIST *)kmalloc(sizeof(struct DMEM_INFO_LIST),GFP_KERNEL);

if(new==NULL)

{

MSYS_ERROR("allocate memory for mem_list entry error\n" ) ;

err = -ENOMEM;

goto BEACH;


}


memset(new->dmem_info.name,0,16);

/*

new->dmem_info.kvirt=mem_info->kvirt;

new->dmem_info.phys=mem_info->phys;

new->dmem_info.length=mem_info->length;

if(mem_info->name!=NULL){

memcpy(new->dmem_info.name,mem_info->name,strnlen(mem_info->name,15));

}

*/

memcpy(&new->dmem_info,mem_info,sizeof(MSYS_DMEM_INFO));


list_add(&new->list, &kept_mem_head);


}


if(retry)

MSYS_ERROR("DMEM request: [%s]:0x%08X success, @0x%08X (retry=%d)\n",mem_info->name,(unsigned int)mem_info->length, (unsigned int)mem_info->phys, retry);

else

MSYS_ERROR("DMEM request: [%s]:0x%08X success, CPU phy:@0x%08X, virt:@0x%08X\n",mem_info->name,(unsigned int)mem_info->length, (unsigned int)mem_info->phys, (unsigned int)mem_info->kvirt);

BEACH:

if(err==-ENOMEM)

{

msys_release_dmem(mem_info);

}


BEACH_ALLOCATE_FAILED:

BEACH_ENTRY_FOUND:

if(err)

{

MSYS_ERROR("DMEM request: [%s]:0x%08X FAILED!! (retry=%d)\n",mem_info->name,(unsigned int)mem_info->length, retry);

}


#if 0

if(0==err){

memset((void *)((unsigned int)mem_info->kvirt),0,mem_info->length);

Chip_Flush_CacheAll();

MSYS_PRINT("DMEM CLEAR!!\n");

}


#endif


mutex_unlock(&dmem_mutex);

return err;


}

EXPORT_SYMBOL(msys_request_dmem);

释放空间 msys_release_dmem

int msys_release_dmem(MSYS_DMEM_INFO *mem_info)
{

    //MSYS_DMEM_INFO mem_info;
    struct list_head *ptr;
    struct DMEM_INFO_LIST *entry,*match_entry;

    int dmem_fixed=0;

    mutex_lock(&dmem_mutex);
    match_entry=NULL;

//  MSYS_PRINT("\nFREEING DMEM [%s]\n\n",mem_info->name);
    if(mem_info->name[0]!=0)
    {
        ----遍历全局list查找name块
        list_for_each(ptr, &kept_mem_head)
        {
            int res=0;
            entry = list_entry(ptr, struct DMEM_INFO_LIST, list);
            res=strncmp(entry->dmem_info.name, mem_info->name,strnlen(mem_info->name,15));
//          MSYS_PRINT("DMEM0 [%s],%s %d\n",entry->dmem_info.name,match_entry->dmem_info.name,res);
            if (0==res)
            {
                match_entry=entry;
                break;
            }
        }
    }

  ---通过name找不到,再检索物理地址
    if(match_entry==NULL && (0!=mem_info->phys))
    {
        MSYS_ERROR("WARNING!! DMEM [%s]@0x%08X can not be found by name, try to find by phys address\n",mem_info->name, (unsigned int)mem_info->phys);
        list_for_each(ptr, &kept_mem_head)
        {
            entry = list_entry(ptr, struct DMEM_INFO_LIST, list);
            if (entry->dmem_info.phys==mem_info->phys)
            {
                match_entry=entry;
                break;
            }
        }

    }

  ---name和phys都找不到即退出
    if(match_entry==NULL)
    {
        MSYS_ERROR("DMEM [%s]@0x%08X not found, skipping release...\n",mem_info->name, (unsigned int)mem_info->phys);
        goto BEACH;
    }

    if(fixed_dmem_enabled)
    {
        //check if entry is fixed

     ----从管理上,支持固定区域,不需要释放,用单独的list管理 fixed_mem_head
        list_for_each(ptr, &fixed_mem_head)
        {
            int res=0;
            entry = list_entry(ptr, struct DMEM_INFO_LIST, list);
            res=strcmp(entry->dmem_info.name, match_entry->dmem_info.name);
            if (0==res)
            {
                dmem_fixed=1;
                MSYS_PRINT("DMEM [%s]@0x%08X is fixed, skipping release...\n",match_entry->dmem_info.name,(unsigned int)match_entry->dmem_info.phys);
                goto BEACH;
            }
        }
    }

   ---调用CMA释放接口
    dma_free_coherent(sys_dev.this_device, PAGE_ALIGN(match_entry->dmem_info.length),(void *)(uintptr_t)match_entry->dmem_info.kvirt,match_entry->dmem_info.phys);

    MSYS_PRINT("DMEM [%s]@0x%08X successfully released\n",match_entry->dmem_info.name,(unsigned int)match_entry->dmem_info.phys);

    ---从全局列表删除
    list_del_init(&match_entry->list);
    ---释放list管理节点
    kfree(match_entry);


BEACH:
    mutex_unlock(&dmem_mutex);
    return 0;

}
EXPORT_SYMBOL(msys_release_dmem);

其他模块使用,以AES模块中的封装为例

static void* alloc_dmem(const char* name, unsigned int size, dma_addr_t *addr)
{
    MSYS_DMEM_INFO dmem;
    memcpy(dmem.name,name,strlen(name)+1);
    dmem.length=size;
    if(0!=msys_request_dmem(&dmem)){
        return NULL;
    }
    *addr=dmem.phys;
    return (void *)((uintptr_t)dmem.kvirt);
}
void free_dmem(const char* name, unsigned int size, void *virt, dma_addr_t addr )
{
	MSYS_DMEM_INFO dmem;
	memcpy(dmem.name,name,strlen(name)+1);
	dmem.length=size;
	dmem.kvirt=(unsigned long long)((uintptr_t)virt);
	dmem.phys=(unsigned long long)((uintptr_t)addr);
	msys_release_dmem(&dmem);
}

分配指定name和大小即可,返回虚拟地址直接使用

释放指定name或者物理地址即可

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值