linux CMA使用机制分析--基于SigmaStar SSD202
前一篇文檔提到CMA相關(guān)的內(nèi)容Linux虛擬內(nèi)存映射分析以及CMA測試 - 以SSD202為例,其主要使用標(biāo)準(zhǔn)內(nèi)核CMA API進(jìn)行,并直接經(jīng)由CMA進(jìn)行管理,通過進(jìn)一步查探,SSD202內(nèi)核有獨(dú)立的模塊msys位于CMA之上,另外多做了一層管理
模塊位置:drivers/sstar/msys/ms_msys.c
此模塊屬于綜合性模塊,包含多個(gè)類型,主要是與cpu特性相關(guān)的,也就是獨(dú)立屬于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_ALLdevice_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_APIdevice_create_file(sys_dev.this_device, &dev_attr_PIU_T); #endif#ifdef CONFIG_MS_CPU_FREQdevice_create_file(sys_dev.this_device, &dev_attr_TEMP_R); #endifdevice_create_file(sys_dev.this_device, &dev_attr_CHIP_VERSION);#ifdef CONFIG_MS_US_TICK_APIdevice_create_file(sys_dev.this_device, &dev_attr_us_ticks); #endif#ifdef CONFIG_SS_PROFILING_TIMEdevice_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); #endifdevice_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); #endifreturn 0; }其主要功能有:
1. 創(chuàng)建msys設(shè)備文件,支持ioctl相關(guān)設(shè)置
2. cma memory的屬性控制
3. Frequency以及Temperature相關(guān)
分析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);
管理核心結(jié)構(gòu)為
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區(qū)域通過name和length來描述和管理
核心的API有2個(gè)msys_request_dmem 和 msys_release_dmem
有了屬性文件,方便在userspace進(jìn)行使用
查閱已創(chuàng)建的cam區(qū)
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區(qū)
指定名稱即可,比如釋放test3,echo test3 > /sys/class/mstar/msys/release_dmem
[ 4385.943383] cma: cma_release(page c63be800)
[ 4385.947603] [MSYS] DMEM [test3]@0x264C0000 successfully released
再次查看已分配區(qū)域確認(rèn)一下:
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
分配新的區(qū)域
指定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
確認(rèn)結(jié)果
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實(shí)現(xiàn)
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);
----先釋放舊的空間,直接調(diào)用cma相關(guān)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;
{
----創(chuàng)建list管理節(jié)點(diǎn)
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
???? ----從管理上,支持固定區(qū)域,不需要釋放,用單獨(dú)的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;
??????????? }
??????? }
??? }
?? ---調(diào)用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管理節(jié)點(diǎn)
??? 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或者物理地址即可
總結(jié)
以上是生活随笔為你收集整理的linux CMA使用机制分析--基于SigmaStar SSD202的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux虚拟内存映射分析以及CMA测试
- 下一篇: Linux 信号可靠性,同步,异步,多线