PMEM原理分析

来源:互联网 发布:电脑看书软件app 编辑:程序博客网 时间:2024/05/15 12:11

http://blog.csdn.net/zirconsdu/article/details/8968601

转自http://blog.csdn.net/kris_fei/article/details/8634908

考察平台:

Chipset:MSM8x25Q

Codebase:Android 4.1


PMEM使用:

PMEM使用比较简单,分单进程使用和共享进程使用同一块PMEM。

单进程使用:

1.      int master_fd = open(“/dev/pmem_xxx”,O_RDWR, 0);

2.      然后再mmap就可以使用了。

进程间共享PMEM:

进程A:

         和单进程使用方法一样。

进程B:

1.      int fd = open(“/dev/pmem_xxx”,O_RDWR, 0);

2.      ioctl(fd, PMEM_CONNECT,master_fd)    //PMEM_CONNECT表示准备要连接了,连接的PMEM对应的fd为master_fd,即进程A打开的PMEM对应的fd。

3.      然后作mmap就可以使用了。

因此关键是第二步,master_fd是从进程A传过来的,可以使用binder等通信机制。(增加:跨进程binder时,应该是BINDER_TYPE_FD转换过的,关键是要找到对应的struct file

PMEM对应的驱动代码流程也是比较简单的,利用了字符驱动的open/ioctl/mmap操作,下面直接分析代码。

PMEM初始化:

PMEM类似于ION里的carved-outmemory,先预留一块内存,然后需要使用的时候从上面分配一块。因此PMEM总有一天会被ION替代,至少我这么认为.

平台上定义了如下三个PMEM模块:  pmem_adsp, pmem_audio, pmem(mdp_pmem)。

[html] view plaincopyprint?
  1. static struct android_pmem_platform_data android_pmem_adsp_pdata = {  
  2.     .name = "pmem_adsp",    //给adsp使用  
  3.     .allocator_type = PMEM_ALLOCATORTYPE_BITMAP,    //都是使用bitmap算法,后面会讲到。  
  4.     .cached = 1,  
  5.     .memory_type = MEMTYPE_EBI1,    //内存类型都是EBI1  
  6. };  
  7.   
  8. static struct platform_device android_pmem_adsp_device = {  
  9.     .name = "android_pmem",  
  10.     .id = 1,  
  11.     .dev = { .platform_data = &android_pmem_adsp_pdata },  
  12. };  
  13.   
  14. static unsigned pmem_mdp_size = MSM_PMEM_MDP_SIZE;  
  15. static int __init pmem_mdp_size_setup(char *p)  
  16. {  
  17.     pmem_mdp_size = memparse(p, NULL);  
  18.     return 0;  
  19. }  
  20. /*可以通过传参来设置pmem mdp的size, 其他pmem模块也如此。*/  
  21. early_param("pmem_mdp_size", pmem_mdp_size_setup);  
  22.   
  23. static unsigned pmem_adsp_size = MSM_PMEM_ADSP_SIZE;  
  24. static int __init pmem_adsp_size_setup(char *p)  
  25. {  
  26.     pmem_adsp_size = memparse(p, NULL);  
  27.     return 0;  
  28. }  
  29.   
  30. early_param("pmem_adsp_size", pmem_adsp_size_setup);  
  31.   
  32. static struct android_pmem_platform_data android_pmem_audio_pdata = {  
  33.     .name = "pmem_audio",       //给audio使用  
  34.     .allocator_type = PMEM_ALLOCATORTYPE_BITMAP,  
  35.     .cached = 0,  
  36.     .memory_type = MEMTYPE_EBI1,  
  37. };  
  38.   
  39. static struct platform_device android_pmem_audio_device = {  
  40.     .name = "android_pmem",  
  41.     .id = 2,  
  42.     .dev = { .platform_data = &android_pmem_audio_pdata },  
  43. };  
  44.   
  45. static struct android_pmem_platform_data android_pmem_pdata = {  
  46.     .name = "pmem", //给mdp使用,Quaclomm为啥不写成pmem_mdp?  
  47.     .allocator_type = PMEM_ALLOCATORTYPE_BITMAP,  
  48.     .cached = 1,  
  49.     .memory_type = MEMTYPE_EBI1,  
  50. };  
  51. static struct platform_device android_pmem_device = {  
  52.     .name = "android_pmem",  
  53.     .id = 0,  
  54.     .dev = { .platform_data = &android_pmem_pdata },  
  55. };  


有了相应的platform_device之后,肯定要找到其platform_driver作设备匹配去。对应文件是pmem.c

[html] view plaincopyprint?
  1. static int __init pmem_init(void)  
  2. {  
  3.     /*创建sysfs,位于/sys/kernel/ pmem_regions ,以每个PMEM模块的名字  
  4. 命名,如pmem_audio。目录下的信息主要供用户空间查看当前PMEM模块的使用状况。*/  
  5.     /* create /sys/kernel/<PMEM_SYSFS_DIR_NAME> directory */  
  6.     pmem_kset = kset_create_and_add(PMEM_SYSFS_DIR_NAME,  
  7.         NULL, kernel_kobj);  
  8.     if (!pmem_kset) {  
  9.         pr_err("pmem(%s):kset_create_and_add fail\n", __func__);  
  10.         return -ENOMEM;  
  11.     }  
  12.     /*寻找platform device,接着调用pmem_probe */  
  13.     return platform_driver_register(&pmem_driver);  
  14. }  
  15.   
  16. static struct platform_driver pmem_driver = {  
  17.     .probe = pmem_probe,  
  18.     .remove = pmem_remove,  
  19.     .driver = { .name = "android_pmem",  
  20.             .pm = &pmem_dev_pm_ops,  
  21.   }  
  22. };  
  23. static int pmem_probe(struct platform_device *pdev)  
  24. {  
  25.     struct android_pmem_platform_data *pdata;  
  26.   
  27.     if (!pdev || !pdev->dev.platform_data) {  
  28.         pr_alert("Unable to probe pmem!\n");  
  29.         return -1;  
  30.     }  
  31.     pdata = pdev->dev.platform_data;  
  32.     /*power manager相关,这里不关注。*/  
  33.     pm_runtime_set_active(&pdev->dev);  
  34.     pm_runtime_enable(&pdev->dev);  
  35.     /*千呼万唤始出来,pmem初始化。*/  
  36.     return pmem_setup(pdata, NULL, NULL);  
  37. }  
  38.   
  39. Pmem_setup()此函数比较长,不过流程还是比较简单的。  
  40. int pmem_setup(struct android_pmem_platform_data *pdata,  
  41.            long (*ioctl)(struct file *, unsigned int, unsigned long),  
  42.            int (*release)(struct inode *, struct file *))  
  43. {  
  44.     int i, index = 0, id;  
  45.     struct vm_struct *pmem_vma = NULL;  
  46.     struct page *page;  
  47.     /*系统对设备总的pmem模块数量有限制。*/  
  48.     if (id_count >= PMEM_MAX_DEVICES) {  
  49.         pr_alert("pmem: %s: unable to register driver(%s) - no more "  
  50.             "devices available!\n", __func__, pdata->name);  
  51.         goto err_no_mem;  
  52.     }  
  53.     /*size为0表示在系统初始化的时候并没有预留一部分内存空间给此PMEM模块。如果这样肯定会申请失败的。*/  
  54.     if (!pdata->size) {  
  55.         pr_alert("pmem: %s: unable to register pmem driver(%s) - zero "  
  56.             "size passed in!\n", __func__, pdata->name);  
  57.         goto err_no_mem;  
  58.     }  
  59.   
  60.     id = id_count++;  
  61.     /*PMEM通过id来寻找对应的pmem模块*/  
  62.     pmem[id].id = id;  
  63.     /*表示已经分配过了。*/  
  64.     if (pmem[id].allocate) {  
  65.         pr_alert("pmem: %s: unable to register pmem driver - "  
  66.             "duplicate registration of %s!\n",  
  67.             __func__, pdata->name);  
  68.         goto err_no_mem;  
  69.     }  
  70.     /*PMEM支持多种不同的allocate算法,在下面的switch case语句中可看到,本平台都使用默认的bitmap算法,对应的allocate type为PMEM_ALLOCATORTYPE_BITMAP。 */  
  71.     pmem[id].allocator_type = pdata->allocator_type;  
  72.     /*quantum是bitmap的计算单位,最小为PAGE_SIZE,当然你也可以在  
  73. android_pmem_platform_data 结构中自己定义大小。*/  
  74.     /* 'quantum' is a "hidden" variable that defaults to 0 in the board  
  75.      * files */  
  76.     pmem[id].quantum = pdata->quantum ?: PMEM_MIN_ALLOC;  
  77.     if (pmem[id].quantum < PMEM_MIN_ALLOC ||  
  78.         !is_power_of_2(pmem[id].quantum)) {  
  79.         pr_alert("pmem: %s: unable to register pmem driver %s - "  
  80.             "invalid quantum value (%#x)!\n",  
  81.             __func__, pdata->name, pmem[id].quantum);  
  82.         goto err_reset_pmem_info;  
  83.     }  
  84.     /*预留的PMEM模块size必须要以quantum对齐。*/  
  85.     if (pdata->size % pmem[id].quantum) {  
  86.         /* bad alignment for size! */  
  87.         pr_alert("pmem: %s: Unable to register driver %s - "  
  88.             "memory region size (%#lx) is not a multiple of "  
  89.             "quantum size(%#x)!\n", __func__, pdata->name,  
  90.             pdata->size, pmem[id].quantum);  
  91.         goto err_reset_pmem_info;  
  92.     }  
  93.   
  94.     pmem[id].cached = pdata->cached; //高速缓冲标志  
  95.     pmem[id].buffered = pdata->buffered; //写缓存标志  
  96.     pmem[id].size = pdata->size;  
  97.     pmem[id].memory_type = pdata->memory_type;   /*系统使用的是EBI1接口的DDR,所以前面type定义都是*/  
  98.     strlcpy(pmem[id].name, pdata->name, PMEM_NAME_SIZE);  
  99.     /*PMEM模块可用的内存entries,以quantum为单位。*/  
  100.     pmem[id].num_entries = pmem[id].size / pmem[id].quantum;  
  101.   
  102.     memset(&pmem[id].kobj, 0, sizeof(pmem[0].kobj));  
  103.     pmem[id].kobj.kset = pmem_kset;  
  104.     /*我们只用到bitmap算法,其他的有兴趣自己可研究。*/  
  105.     switch (pmem[id].allocator_type) {  
  106.     case PMEM_ALLOCATORTYPE_ALLORNOTHING:  
  107. ~~snip  
  108.         break;  
  109.     case PMEM_ALLOCATORTYPE_BUDDYBESTFIT:  
  110. ~~snip  
  111.         break;  
  112.     case PMEM_ALLOCATORTYPE_BITMAP: /* 0, default if not explicit */  
  113.         /*先分配64个bitm_alloc结构,用于后面管理PMEM模块的申请。  
  114. 因为用户空间可能不会一下子申请调整个PMEM模块,如有两个进程都申请pmem_audio模块的一小部分内存。PMEM_INITIAL_NUM_BITMAP_ALLOCATIONS 为64.*/  
  115.         pmem[id].allocator.bitmap.bitm_alloc = kmalloc(  
  116.             PMEM_INITIAL_NUM_BITMAP_ALLOCATIONS *  
  117.                 sizeof(*pmem[id].allocator.bitmap.bitm_alloc),  
  118.             GFP_KERNEL);  
  119. ~~snip  
  120.         /*初始化 bitm_alloc结构体。*/  
  121.         for (i = 0; i < PMEM_INITIAL_NUM_BITMAP_ALLOCATIONS; i++) {  
  122.             pmem[id].allocator.bitmap.bitm_alloc[i].bit = -1;  
  123.             pmem[id].allocator.bitmap.bitm_alloc[i].quanta = 0;  
  124.         }  
  125.         /*记录当前已经申请的bitm_alloc数量。*/  
  126.         pmem[id].allocator.bitmap.bitmap_allocs =  
  127.             PMEM_INITIAL_NUM_BITMAP_ALLOCATIONS;  
  128.         /*以32为单位记录当前整个PMEM模块的内存数量。*/  
  129.         pmem[id].allocator.bitmap.bitmap =  
  130.             kcalloc((pmem[id].num_entries + 31) / 32,  
  131.                 sizeof(unsigned int), GFP_KERNEL);  
  132.         if (!pmem[id].allocator.bitmap.bitmap) {  
  133.             pr_alert("pmem: %s: Unable to register pmem "  
  134.                 "driver - can't allocate bitmap!\n",  
  135.                 __func__);  
  136.             goto err_cant_register_device;  
  137.         }  
  138.         /*当前空闲的entries。*/  
  139.         pmem[id].allocator.bitmap.bitmap_free = pmem[id].num_entries;  
  140.         /*下面这几个函数会在用户空间通过open/ioctl/mmap用到。*/  
  141.         pmem[id].allocate = pmem_allocator_bitmap;  
  142.         pmem[id].free = pmem_free_bitmap;  
  143.         pmem[id].free_space = pmem_free_space_bitmap;  
  144.         pmem[id].len = pmem_len_bitmap;  
  145.         pmem[id].start_addr = pmem_start_addr_bitmap;  
  146.   
  147.         DLOG("bitmap allocator id %d (%s), num_entries %u, raw size "  
  148.             "%lu, quanta size %u\n",  
  149.             id, pdata->name, pmem[id].allocator.bitmap.bitmap_free,  
  150.             pmem[id].size, pmem[id].quantum);  
  151.         break;  
  152.   
  153.     case PMEM_ALLOCATORTYPE_SYSTEM:  
  154. ~~snip  
  155.     }  
  156.   
  157.     pmem[id].ioctl = ioctl;  
  158.     pmem[id].release = release;  
  159.     mutex_init(&pmem[id].arena_mutex);  
  160.     mutex_init(&pmem[id].data_list_mutex);  
  161.     INIT_LIST_HEAD(&pmem[id].data_list);  
  162.   
  163.     pmem[id].dev.name = pdata->name;  
  164.     pmem[id].dev.minor = id;    //后面使用id来寻找对应的pmem模块。  
  165.     pmem[id].dev.fops = &pmem_fops; //后面用户空间的操作都会调用的这个变量里的函数指针了。  
  166.     pmem[id].reusable = pdata->reusable;  
  167.     pr_info("pmem: Initializing %s as %s\n",  
  168.         pdata->name, pdata->cached ? "cached" : "non-cached");  
  169.     /*注册为字符设备,会看到/dev/pmem_**, 如/dev/pmem_audio,供用户空间  
  170. 操作设备。*/  
  171.     if (misc_register(&pmem[id].dev)) {  
  172.         pr_alert("Unable to register pmem driver!\n");  
  173.         goto err_cant_register_device;  
  174.     }  
  175. ~~snip  
  176.     page = alloc_page(GFP_KERNEL);  
  177.     if (!page) {  
  178.         pr_err("pmem: Failed to allocate page for %s\n", pdata->name);  
  179.         goto cleanup_vm;  
  180.     }  
  181.     pmem[id].garbage_pfn = page_to_pfn(page);  
  182.     atomic_set(&pmem[id].allocation_cnt, 0);  
  183.   
  184.     if (pdata->setup_region)  
  185.         pmem[id].region_data = pdata->setup_region();  
  186.   
  187.     if (pdata->request_region)  
  188.         pmem[id].mem_request = pdata->request_region;  
  189.   
  190.     if (pdata->release_region)  
  191.         pmem[id].mem_release = pdata->release_region;  
  192.   
  193.     pr_info("allocating %lu bytes at %lx physical for %s\n",  
  194.         pmem[id].size, pmem[id].base, pmem[id].name);  
  195.   
  196.     return 0;  
  197.   
  198. ~~snip  
  199.     return -1;  
  200. }  

 

PMEM使用驱动分析:

open

当用户进程open pmem设备的时候,会调用到pmem_open:

[html] view plaincopyprint?
  1. static int pmem_open(struct inode *inode, struct file *file)  
  2. {  
  3.     struct pmem_data *data;  
  4.     int id = get_id(file);  
  5.     int ret = 0;  
  6. #if PMEM_DEBUG_MSGS  
  7.     char currtask_name[FIELD_SIZEOF(struct task_struct, comm) + 1];  
  8. #endif  
  9.   
  10.     DLOG("pid %u(%s) file %p(%ld) dev %s(id: %d)\n",  
  11.         current->pid, get_task_comm(currtask_name, current),  
  12.         file, file_count(file), get_name(file), id);  
  13.     /*分配struct pmem_data。*/  
  14.     data = kmalloc(sizeof(struct pmem_data), GFP_KERNEL);  
  15.     if (!data) {  
  16.         printk(KERN_ALERT "pmem: %s: unable to allocate memory for "  
  17.                 "pmem metadata.", __func__);  
  18.         return -1;  
  19.     }  
  20.     data->flags = 0;  
  21.     data->index = -1;  
  22.     data->task = NULL;  
  23.     data->vma = NULL;  
  24.     data->pid = 0;  
  25.     data->master_file = NULL;  
  26. #if PMEM_DEBUG  
  27.     data->ref = 0;  
  28. #endif  
  29.     INIT_LIST_HEAD(&data->region_list);  
  30.     init_rwsem(&data->sem);  
  31.   
  32.     file->private_data = data;  
  33.     INIT_LIST_HEAD(&data->list);  
  34.   
  35.     mutex_lock(&pmem[id].data_list_mutex);  
  36.     list_add(&data->list, &pmem[id].data_list);  
  37.     mutex_unlock(&pmem[id].data_list_mutex);  
  38.     return ret;  
  39. }  


 

Open似乎没做什么特殊事情,只是分配一个struct pmem_data,然后初始化之后保存到file的私有数据之中。

mmap

open好了之后,用户进程想要使用PMEM,通过mmap实现,对应的是Kernel中的pmem_mmap。

[html] view plaincopyprint?
  1. static int pmem_mmap(struct file *file, struct vm_area_struct *vma)  
  2. {  
  3.     /*取出open时创建的struct pem_data.*/  
  4.     struct pmem_data *data = file->private_data;  
  5.     int index = -1;  
  6.     /*要映射的size大小。*/  
  7.     unsigned long vma_size =  vma->vm_end - vma->vm_start;  
  8.     int ret = 0id = get_id(file);  
  9.   
  10. ~~snip  
  11.     /* check this file isn't already mmaped, for submaps check this file  
  12.      * has never been mmaped */  
  13.     /*如果类型为submap,也不用再mmap。这部分和进程间共享PMEM有关,  
  14. 也就是说当主进程做了mmap之后,另外一个要共享的进程就无需再mmap了。*/  
  15.     if ((data->flags & PMEM_FLAGS_SUBMAP) ||  
  16.         (data->flags & PMEM_FLAGS_UNSUBMAP)) {  
  17. #if PMEM_DEBUG  
  18.         pr_err("pmem: you can only mmap a pmem file once, "  
  19.                "this file is already mmaped. %x\n", data->flags);  
  20. #endif  
  21.         ret = -EINVAL;  
  22.         goto error;  
  23.     }  
  24.     /* if file->private_data == unalloced, alloc*/  
  25.     /*index表示当前分配的位于bitmap中的索引,如果为-1就表示未分配。*/  
  26.     if (data->index == -1) {  
  27.         mutex_lock(&pmem[id].arena_mutex);  
  28.         /*根据id号来从PMEM模块上分配一部分内存,返回在bitmap的索引。*/  
  29.         index = pmem_allocate_from_id(id,  
  30.                 vma->vm_end - vma->vm_start,  
  31.                 SZ_4K);  
  32.         mutex_unlock(&pmem[id].arena_mutex);  
  33.         /* either no space was available or an error occured */  
  34.         if (index == -1) {  
  35.             pr_err("pmem: mmap unable to allocate memory"  
  36.                 "on %s\n", get_name(file));  
  37.             ret = -ENOMEM;  
  38.             goto error;  
  39.         }  
  40.         /* store the index of a successful allocation */  
  41.         data->index = index;  
  42.     }  
  43.     /*分配的size不能超过整个PMEM模块长度。*/  
  44.     if (pmem[id].len(id, data) < vma_size) {  
  45. #if PMEM_DEBUG  
  46.         pr_err("pmem: mmap size [%lu] does not match"  
  47.                " size of backing region [%lu].\n", vma_size,  
  48.                pmem[id].len(id, data));  
  49. #endif  
  50.         ret = -EINVAL;  
  51.         goto error;  
  52.     }  
  53.     /*调用的是pmem_start_addr_bitmap 函数,返回当前在整个PMEM模块  
  54. 中的偏移。*/  
  55.     vma->vm_pgoff = pmem[id].start_addr(id, data) >> PAGE_SHIFT;  
  56.     /*cache的禁止操作。*/  
  57.     vma->vm_page_prot = pmem_phys_mem_access_prot(file, vma->vm_page_prot);  
  58.     /* PMEM_FLAGS_CONNECTED 在ioctl接口中会被定义,表示要共享PMEM内存。可以先看如果要共享内存,mmap做了什么。*/  
  59.     if (data->flags & PMEM_FLAGS_CONNECTED) {  
  60.         struct pmem_region_node *region_node;  
  61.         struct list_head *elt;  
  62.     /*插入一个pfn页框到用vma中*/  
  63.         if (pmem_map_garbage(id, vma, data, 0, vma_size)) {  
  64.             pr_alert("pmem: mmap failed in kernel!\n");  
  65.             ret = -EAGAIN;  
  66.             goto error;  
  67.         }  
  68.     /*根据当前有多少region_list作一一映射。*/  
  69.         list_for_each(elt, &data->region_list) {  
  70.             region_node = list_entry(elt, struct pmem_region_node,  
  71.                          list);  
  72.             DLOG("remapping file: %p %lx %lx\n", file,  
  73.                 region_node->region.offset,  
  74.                 region_node->region.len);  
  75.             if (pmem_remap_pfn_range(id, vma, data,  
  76.                          region_node->region.offset,  
  77.                          region_node->region.len)) {  
  78.                 ret = -EAGAIN;  
  79.                 goto error;  
  80.             }  
  81.         }  
  82.         /*标记当前是submap。*/  
  83.         data->flags |= PMEM_FLAGS_SUBMAP;  
  84.         get_task_struct(current->group_leader);  
  85.         data->task = current->group_leader;  
  86.         data->vma = vma;  
  87. #if PMEM_DEBUG  
  88.         data->pid = current->pid;  
  89. #endif  
  90.         DLOG("submmapped file %p vma %p pid %u\n", file, vma,  
  91.              current->pid);  
  92.     } else {  
  93.         /mastermap走如下流程。映射vma_size大小到用户空间。*/  
  94.         if (pmem_map_pfn_range(id, vma, data, 0, vma_size)) {  
  95.             pr_err("pmem: mmap failed in kernel!\n");  
  96.             ret = -EAGAIN;  
  97.             goto error;  
  98.         }  
  99.         data->flags |= PMEM_FLAGS_MASTERMAP;  
  100.         data->pid = current->pid;  
  101.     }  
  102.     vma->vm_ops = &vm_ops;  
  103. error:  
  104.     up_write(&data->sem);  
  105.     return ret;  
  106. }  


在这个阶段,我们主要关心的为非共享PMEM的操作,当执行了mmap之后,用户空间就直接可以操作pmem了。

上面还有个重要的函数没作分析,pmem_allocate_from_id():

[html] view plaincopyprint?
  1. static int pmem_allocate_from_id(const int id, const unsigned long size,  
  2.                         const unsigned int align)  
  3. {  
  4.     int ret;  
  5.     ret = pmem_get_region(id);  
  6.     if (ret)  
  7.         return -1;  
  8.     /*调用的是pmem_allocator_bitmap().*/  
  9.     ret = pmem[id].allocate(id, size, align);  
  10.     if (ret < 0)  
  11.         pmem_put_region(id);  
  12.     return ret;  
  13. }  
  14. static int pmem_get_region(int id)  
  15. {  
  16.     /* Must be called with arena mutex locked */  
  17.     atomic_inc(&pmem[id].allocation_cnt);  
  18.     if (!pmem[id].vbase) {  
  19. ~~snip  
  20.         /*根据id作ioremap*/  
  21.         ioremap_pmem(id);  
  22.     }  
  23. ~~snip  
  24. }  
  25. static void ioremap_pmem(int id)  
  26. {  
  27.     unsigned long addr;  
  28.     const struct mem_type *type;  
  29.   
  30.     DLOG("PMEMDEBUG: ioremaping for %s\n", pmem[id].name);  
  31.     if (pmem[id].map_on_demand) {  
  32. ~~snip  
  33.     } else {  
  34.     /*如果需要cache则调用ioremap_cached,否则调用ioremap。*/  
  35.         if (pmem[id].cached)  
  36.             pmem[id].vbase = ioremap_cached(pmem[id].base,  
  37.                         pmem[id].size);  
  38. ~~snip  
  39.         else  
  40.             pmem[id].vbase = ioremap(pmem[id].base, pmem[id].size);  
  41.     }  
  42. }  
  43. }  


这样,得到PMEM模块对应的内核虚拟地址。

接着就是pmem_allocator_bitmap,看它如何利用bitmap来分配及管理内存。

[html] view plaincopyprint?
  1. static int pmem_allocator_bitmap(const int id,  
  2.         const unsigned long len,  
  3.         const unsigned int align)  
  4. {  
  5.     /* caller should hold the lock on arena_mutex! */  
  6.     int bitnum, i;  
  7.     unsigned int quanta_needed;  
  8.   
  9.     DLOG("bitmap id %d, len %ld, align %u\n", id, len, align);  
  10. ~~snip  
  11.     /*以quantum为单位计算要分配的内存大小。*/  
  12.     quanta_needed = (len + pmem[id].quantum - 1) / pmem[id].quantum;  
  13.     /*超过整个pmem模块的数量则失败。*/  
  14.     if (pmem[id].allocator.bitmap.bitmap_free < quanta_needed) {  
  15.         return -1;  
  16.     }  
  17.     /*将要申请的quanta数量再次作一个转换,因为要考虑对齐等因素。*/  
  18.     bitnum = reserve_quanta(quanta_needed, id, align);  
  19.     if (bitnum == -1)  
  20.         goto leave;  
  21.     /*找到第一个未被使用过的bitmap的位置。*/  
  22.     for (i = 0;  
  23.         i < pmem[id].allocator.bitmap.bitmap_allocs &&  
  24.             pmem[id].allocator.bitmap.bitm_alloc[i].bit != -1;  
  25.         i++)  
  26.         ;  
  27.     /*如果找到的位置已经超出当前的bitmap_allocs数量,则要重新  
  28. 分配更大的一块bitm_alloc.*/  
  29.     if (i >= pmem[id].allocator.bitmap.bitmap_allocs) {  
  30.         void *temp;  
  31.         /*申请的数量比上次大一倍。*/  
  32.         int32_t new_bitmap_allocs =  
  33.             pmem[id].allocator.bitmap.bitmap_allocs << 1;  
  34.         int j;  
  35.   
  36.         if (!new_bitmap_allocs) { /* failed sanity check!! */  
  37.             return -1;  
  38.         }  
  39.         /*申请数量不能大于当前PMEM模块实际的数量。*/  
  40.         if (new_bitmap_allocs > pmem[id].num_entries) {  
  41.             /* failed sanity check!! */  
  42.             return -1;  
  43.         }  
  44.         /*重新分配和指定。*/  
  45.         temp = krealloc(pmem[id].allocator.bitmap.bitm_alloc,  
  46.                 new_bitmap_allocs *  
  47.                 sizeof(*pmem[id].allocator.bitmap.bitm_alloc),  
  48.                 GFP_KERNEL);  
  49.         if (!temp) {  
  50.             return -1;  
  51.         }  
  52.         pmem[id].allocator.bitmap.bitmap_allocs = new_bitmap_allocs;  
  53.         pmem[id].allocator.bitmap.bitm_alloc = temp;  
  54.         /*只对重新分配的部分作初始化。*/  
  55.         for (j = i; j < new_bitmap_allocs; j++) {  
  56.             pmem[id].allocator.bitmap.bitm_alloc[j].bit = -1;  
  57.             pmem[id].allocator.bitmap.bitm_alloc[i].quanta = 0;  
  58.         }  
  59.   
  60.         DLOG("increased # of allocated regions to %d for id %d\n",  
  61.             pmem[id].allocator.bitmap.bitmap_allocs, id);  
  62.     }  
  63.   
  64.     DLOG("bitnum %d, bitm_alloc index %d\n", bitnum, i);  
  65.   
  66.     pmem[id].allocator.bitmap.bitmap_free -quanta_needed;  
  67.     pmem[id].allocator.bitmap.bitm_alloc[i].bit = bitnum;  
  68.     pmem[id].allocator.bitmap.bitm_alloc[i].quanta = quanta_needed;  
  69. leave:  
  70.     return bitnum;  
  71. }  
  72.   
  73. static int reserve_quanta(const unsigned int quanta_needed,  
  74.         const int id,  
  75.         unsigned int align)  
  76. {  
  77.     /* alignment should be a valid power of 2 */  
  78.     int ret = -1, start_bit = 0spacing = 1;  
  79. ~~snip  
  80.     start_bit = bit_from_paddr(id,  
  81.         (pmem[id].base + align - 1) & ~(align - 1));  
  82.     if (start_bit <= -1) {  
  83.         return -1;  
  84.     }  
  85.     spacing = align / pmem[id].quantum;  
  86.     spacing = spacing > 1 ? spacing : 1;  
  87.     /*从memory pool上也就是当前拥有的PMEM内存块上分配出一片  
  88. 区域来给当前申请用户进程。*/  
  89.     ret = bitmap_allocate_contiguous(pmem[id].allocator.bitmap.bitmap,  
  90.         quanta_needed,  
  91.         (pmem[id].size + pmem[id].quantum - 1) / pmem[id].quantum,  
  92.         spacing,  
  93.         start_bit);  
  94.   
  95.     return ret;  
  96. }  


内存分配完成。

ioctl

PMEM提供了若干个ioctl的cmd供用户空间操作,有获取当前申请的len,获取PMEM模块的总size,申请pmem等,这里我们重点关注alloc, map, connect,其他几个很简单,可自行分析。

[cpp] view plaincopyprint?
  1. static long pmem_ioctl(struct file *file, unsigned int cmd, unsigned long arg)  
  2. {  
  3.     /* called from user space as file op, so file guaranteed to be not 
  4.      * NULL 
  5.      */  
  6.     struct pmem_data *data = file->private_data;  
  7.     int id = get_id(file);  
  8. #if PMEM_DEBUG_MSGS  
  9.     char currtask_name[  
  10.         FIELD_SIZEOF(struct task_struct, comm) + 1];  
  11. #endif  
  12.   
  13.     DLOG("pid %u(%s) file %p(%ld) cmd %#x, dev %s(id: %d)\n",  
  14.         current->pid, get_task_comm(currtask_name, current),  
  15.         file, file_count(file), cmd, get_name(file), id);  
  16.   
  17.     switch (cmd) {  
  18.     case PMEM_GET_PHYS:  
  19.         ~~snip  
  20.     case PMEM_MAP:  
  21.         {  
  22.             struct pmem_region region;  
  23.             DLOG("map\n");  
  24.             if (copy_from_user(®ion, (void __user *)arg,  
  25.                         sizeof(struct pmem_region)))  
  26.                 return -EFAULT;  
  27.             return pmem_remap(®ion, file, PMEM_MAP);  
  28.         }  
  29.         break;  
  30.     case PMEM_UNMAP:  
  31.         ~~snip  
  32.     case PMEM_GET_SIZE:  
  33.         ~~snip  
  34.     case PMEM_GET_TOTAL_SIZE:  
  35.         ~~snip  
  36.     case PMEM_GET_FREE_SPACE:  
  37. ~~snip  
  38.     case PMEM_ALLOCATE:  
  39.         {  
  40.             int ret = 0;  
  41.             DLOG("allocate, id %d\n", id);  
  42.             down_write(&data->sem);  
  43.             if (has_allocation(file)) {  
  44.                 pr_err("pmem: Existing allocation found on "  
  45.                     "this file descrpitor\n");  
  46.                 up_write(&data->sem);  
  47.                 return -EINVAL;  
  48.             }  
  49.   
  50.             mutex_lock(&pmem[id].arena_mutex);  
  51.             data->index = pmem_allocate_from_id(id,  
  52.                     arg,  
  53.                     SZ_4K);  
  54.             mutex_unlock(&pmem[id].arena_mutex);  
  55.             ret = data->index == -1 ? -ENOMEM :  
  56.                 data->index;  
  57.             up_write(&data->sem);  
  58.             return ret;  
  59.         }  
  60.     case PMEM_ALLOCATE_ALIGNED:  
  61.         ~~snip  
  62.     case PMEM_CONNECT:  
  63.         DLOG("connect\n");  
  64.         return pmem_connect(arg, file);  
  65.     case PMEM_CLEAN_INV_CACHES:  
  66.     case PMEM_CLEAN_CACHES:  
  67.     case PMEM_INV_CACHES:  
  68.         ~~snip  
  69.     default:  
  70.         if (pmem[id].ioctl)  
  71.             return pmem[id].ioctl(file, cmd, arg);  
  72.   
  73.         DLOG("ioctl invalid (%#x)\n", cmd);  
  74.         return -EINVAL;  
  75.     }  
  76.     return 0;  
  77. }  


 

PMEM_ALLOCATE:

咦,也是调用pmem_allocate_from_id(),在前面的mmap中,我们已经分析过了。

PMEM_CONNECT:

调用的是pmem_connect(),这个cmd主要是供另外一个进程和主进程共享PMEM用的,传进去的参数为主进程打开PMEM的fd,这里称主进程master

[cpp] view plaincopyprint?
  1. static int pmem_connect(unsigned long connect, struct file *file)  
  2. {  
  3.     int ret = 0, put_needed;  
  4.     struct file *src_file;  
  5. ~~snip  
  6.     /*根据主进程的fd获得相对应的file.*/  
  7.     src_file = fget_light(connect, &put_needed);  
  8. ~~snip  
  9.     if (unlikely(!is_pmem_file(src_file))) {  
  10.         pr_err("pmem: %s: src file is not a pmem file!\n",  
  11.             __func__);  
  12.         ret = -EINVAL;  
  13.         goto put_src_file;  
  14.     } else {  
  15.         /*得到master的pmem data.*/  
  16.         struct pmem_data *src_data = src_file->private_data;  
  17.   
  18.         if (!src_data) {  
  19.             pr_err("pmem: %s: src file pointer has no"  
  20.                 "private data, bailing out!\n", __func__);  
  21.             ret = -EINVAL;  
  22.             goto put_src_file;  
  23.         }  
  24.   
  25.         down_read(&src_data->sem);  
  26.   
  27.         if (unlikely(!has_allocation(src_file))) {  
  28.             up_read(&src_data->sem);  
  29.             pr_err("pmem: %s: src file has no allocation!\n",  
  30.                 __func__);  
  31.             ret = -EINVAL;  
  32.         } else {  
  33.             struct pmem_data *data;  
  34.             /*获得master分配到的内存在bitmap中的index.*/  
  35.             int src_index = src_data->index;  
  36.   
  37.             up_read(&src_data->sem);  
  38.   
  39.             data = file->private_data;  
  40.             if (!data) {  
  41.                 pr_err("pmem: %s: passed in file "  
  42.                     "pointer has no private data, bailing"  
  43.                     " out!\n", __func__);  
  44.                 ret = -EINVAL;  
  45.                 goto put_src_file;  
  46.             }  
  47.   
  48.             down_write(&data->sem);  
  49.             if (has_allocation(file) &&  
  50.                     (data->index != src_index)) {  
  51.                 up_write(&data->sem);  
  52.   
  53.                 pr_err("pmem: %s: file is already "  
  54.                     "mapped but doesn't match this "  
  55.                     "src_file!\n", __func__);  
  56.                 ret = -EINVAL;  
  57.             } else {  
  58.                 /*将master的pmem data数据保存到当前进程中。*/  
  59.                 data->index = src_index;  
  60.                 data->flags |= PMEM_FLAGS_CONNECTED;     //设置标志,在mmap中用到。  
  61.                 data->master_fd = connect;  
  62.                 data->master_file = src_file;  
  63.   
  64.                 up_write(&data->sem);  
  65.   
  66.                 DLOG("connect %p to %p\n", file, src_file);  
  67.             }  
  68.         }  
  69.     }  
  70. put_src_file:  
  71.     fput_light(src_file, put_needed);  
  72. leave:  
  73.     return ret;  
  74. }  


嗯,这个cmd就是将master的struct pmem_data给了当前要共享的进程。

PMEM_MAP:

这个接口是为了用空进程想要执行remap而开设的,不过我想不到什么时候要重新映射呢?

[cpp] view plaincopyprint?
  1. int pmem_remap(struct pmem_region *region, struct file *file,  
  2.               unsigned operation)  
  3. {  
  4.     int ret;  
  5.     struct pmem_region_node *region_node;  
  6.     struct mm_struct *mm = NULL;  
  7.     struct list_head *elt, *elt2;  
  8.     int id = get_id(file);  
  9.     struct pmem_data *data;  
  10. ~~snip  
  11.   
  12.     /* is_pmem_file fails if !file */  
  13.     data = file->private_data;  
  14. ~~snip  
  15.     /* lock the mm and data */  
  16.     ret = pmem_lock_data_and_mm(file, data, &mm);  
  17.     if (ret)  
  18.         return 0;  
  19. /*明确指定只有master file才能作remap动作。*/  
  20.     /* only the owner of the master file can remap the client fds 
  21.      * that back in it */  
  22.     if (!is_master_owner(file)) {  
  23.         ret = -EINVAL;  
  24.         goto err;  
  25.     }  
  26. ~~snip  
  27.     if (operation == PMEM_MAP) {  
  28.     /*生成一个struct pem_region_node,用来保存上层传下来的region信息。*/  
  29.         region_node = kmalloc(sizeof(struct pmem_region_node),  
  30.                   GFP_KERNEL);  
  31.         if (!region_node) {  
  32.             ret = -ENOMEM;  
  33.             goto err;  
  34.         }  
  35.         region_node->region = *region;  
  36.         /*添加到data的region_list中。*/  
  37.         list_add(®ion_node->list, &data->region_list);  
  38.     } else if (operation == PMEM_UNMAP) {  
  39.         int found = 0;  
  40.         list_for_each_safe(elt, elt2, &data->region_list) {  
  41.             region_node = list_entry(elt, struct pmem_region_node,  
  42.                       list);  
  43.             if (region->len == 0 ||  
  44.                 (region_node->region.offset == region->offset &&  
  45.                 region_node->region.len == region->len)) {  
  46.                 list_del(elt);  
  47.                 kfree(region_node);  
  48.                 found = 1;  
  49.             }  
  50.         }  
  51. ~~snip  
  52.     /*比较好奇PMEM_FLAGS_SUBMAP是想共享的进程设置的,但是前面的判断又是 
  53. 只能master file才能作remap,这样岂不是永远执行不了?*/  
  54.     if (data->vma && PMEM_IS_SUBMAP(data)) {  
  55.         if (operation == PMEM_MAP)  
  56.             ret = pmem_remap_pfn_range(id, data->vma, data,  
  57.                    region->offset, region->len);  
  58.         else if (operation == PMEM_UNMAP)  
  59.             ret = pmem_unmap_pfn_range(id, data->vma, data,  
  60.                    region->offset, region->len);  
  61.     }  
  62.   
  63. err:  
  64.     pmem_unlock_data_and_mm(data, mm);  
  65.     return ret;  
  66. }  


[END]



0 0
原创粉丝点击