PMEM原理分析

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)。

  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. };  
static struct android_pmem_platform_data android_pmem_adsp_pdata = {
	.name = "pmem_adsp",	//给adsp使用
	.allocator_type = PMEM_ALLOCATORTYPE_BITMAP,	//都是使用bitmap算法,后面会讲到。
	.cached = 1,
	.memory_type = MEMTYPE_EBI1,	//内存类型都是EBI1
};

static struct platform_device android_pmem_adsp_device = {
	.name = "android_pmem",
	.id = 1,
	.dev = { .platform_data = &android_pmem_adsp_pdata },
};

static unsigned pmem_mdp_size = MSM_PMEM_MDP_SIZE;
static int __init pmem_mdp_size_setup(char *p)
{
	pmem_mdp_size = memparse(p, NULL);
	return 0;
}
/*可以通过传参来设置pmem mdp的size, 其他pmem模块也如此。*/
early_param("pmem_mdp_size", pmem_mdp_size_setup);

static unsigned pmem_adsp_size = MSM_PMEM_ADSP_SIZE;
static int __init pmem_adsp_size_setup(char *p)
{
	pmem_adsp_size = memparse(p, NULL);
	return 0;
}

early_param("pmem_adsp_size", pmem_adsp_size_setup);

static struct android_pmem_platform_data android_pmem_audio_pdata = {
	.name = "pmem_audio",		//给audio使用
	.allocator_type = PMEM_ALLOCATORTYPE_BITMAP,
	.cached = 0,
	.memory_type = MEMTYPE_EBI1,
};

static struct platform_device android_pmem_audio_device = {
	.name = "android_pmem",
	.id = 2,
	.dev = { .platform_data = &android_pmem_audio_pdata },
};

static struct android_pmem_platform_data android_pmem_pdata = {
	.name = "pmem",	//给mdp使用,Quaclomm为啥不写成pmem_mdp?
	.allocator_type = PMEM_ALLOCATORTYPE_BITMAP,
	.cached = 1,
	.memory_type = MEMTYPE_EBI1,
};
static struct platform_device android_pmem_device = {
	.name = "android_pmem",
	.id = 0,
	.dev = { .platform_data = &android_pmem_pdata },
};


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

  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. }  
static int __init pmem_init(void)
{
	/*创建sysfs,位于/sys/kernel/ pmem_regions ,以每个PMEM模块的名字
命名,如pmem_audio。目录下的信息主要供用户空间查看当前PMEM模块的使用状况。*/
	/* create /sys/kernel/<PMEM_SYSFS_DIR_NAME> directory */
	pmem_kset = kset_create_and_add(PMEM_SYSFS_DIR_NAME,
		NULL, kernel_kobj);
	if (!pmem_kset) {
		pr_err("pmem(%s):kset_create_and_add fail\n", __func__);
		return -ENOMEM;
	}
	/*寻找platform device,接着调用pmem_probe */
	return platform_driver_register(&pmem_driver);
}

static struct platform_driver pmem_driver = {
	.probe = pmem_probe,
	.remove = pmem_remove,
	.driver = { .name = "android_pmem",
		    .pm = &pmem_dev_pm_ops,
  }
};
static int pmem_probe(struct platform_device *pdev)
{
	struct android_pmem_platform_data *pdata;

	if (!pdev || !pdev->dev.platform_data) {
		pr_alert("Unable to probe pmem!\n");
		return -1;
	}
	pdata = pdev->dev.platform_data;
	/*power manager相关,这里不关注。*/
	pm_runtime_set_active(&pdev->dev);
	pm_runtime_enable(&pdev->dev);
	/*千呼万唤始出来,pmem初始化。*/
	return pmem_setup(pdata, NULL, NULL);
}

Pmem_setup()此函数比较长,不过流程还是比较简单的。
int pmem_setup(struct android_pmem_platform_data *pdata,
	       long (*ioctl)(struct file *, unsigned int, unsigned long),
	       int (*release)(struct inode *, struct file *))
{
	int i, index = 0, id;
	struct vm_struct *pmem_vma = NULL;
	struct page *page;
	/*系统对设备总的pmem模块数量有限制。*/
	if (id_count >= PMEM_MAX_DEVICES) {
		pr_alert("pmem: %s: unable to register driver(%s) - no more "
			"devices available!\n", __func__, pdata->name);
		goto err_no_mem;
	}
	/*size为0表示在系统初始化的时候并没有预留一部分内存空间给此PMEM模块。如果这样肯定会申请失败的。*/
	if (!pdata->size) {
		pr_alert("pmem: %s: unable to register pmem driver(%s) - zero "
			"size passed in!\n", __func__, pdata->name);
		goto err_no_mem;
	}

	id = id_count++;
	/*PMEM通过id来寻找对应的pmem模块*/
	pmem[id].id = id;
	/*表示已经分配过了。*/
	if (pmem[id].allocate) {
		pr_alert("pmem: %s: unable to register pmem driver - "
			"duplicate registration of %s!\n",
			__func__, pdata->name);
		goto err_no_mem;
	}
	/*PMEM支持多种不同的allocate算法,在下面的switch case语句中可看到,本平台都使用默认的bitmap算法,对应的allocate type为PMEM_ALLOCATORTYPE_BITMAP。 */
	pmem[id].allocator_type = pdata->allocator_type;
	/*quantum是bitmap的计算单位,最小为PAGE_SIZE,当然你也可以在
android_pmem_platform_data 结构中自己定义大小。*/
	/* 'quantum' is a "hidden" variable that defaults to 0 in the board
	 * files */
	pmem[id].quantum = pdata->quantum ?: PMEM_MIN_ALLOC;
	if (pmem[id].quantum < PMEM_MIN_ALLOC ||
		!is_power_of_2(pmem[id].quantum)) {
		pr_alert("pmem: %s: unable to register pmem driver %s - "
			"invalid quantum value (%#x)!\n",
			__func__, pdata->name, pmem[id].quantum);
		goto err_reset_pmem_info;
	}
	/*预留的PMEM模块size必须要以quantum对齐。*/
	if (pdata->size % pmem[id].quantum) {
		/* bad alignment for size! */
		pr_alert("pmem: %s: Unable to register driver %s - "
			"memory region size (%#lx) is not a multiple of "
			"quantum size(%#x)!\n", __func__, pdata->name,
			pdata->size, pmem[id].quantum);
		goto err_reset_pmem_info;
	}

	pmem[id].cached = pdata->cached;	//高速缓冲标志
	pmem[id].buffered = pdata->buffered;	//写缓存标志
	pmem[id].size = pdata->size;
	pmem[id].memory_type = pdata->memory_type;	/*系统使用的是EBI1接口的DDR,所以前面type定义都是*/
	strlcpy(pmem[id].name, pdata->name, PMEM_NAME_SIZE);
	/*PMEM模块可用的内存entries,以quantum为单位。*/
	pmem[id].num_entries = pmem[id].size / pmem[id].quantum;

	memset(&pmem[id].kobj, 0, sizeof(pmem[0].kobj));
	pmem[id].kobj.kset = pmem_kset;
	/*我们只用到bitmap算法,其他的有兴趣自己可研究。*/
	switch (pmem[id].allocator_type) {
	case PMEM_ALLOCATORTYPE_ALLORNOTHING:
~~snip
		break;
	case PMEM_ALLOCATORTYPE_BUDDYBESTFIT:
~~snip
		break;
	case PMEM_ALLOCATORTYPE_BITMAP: /* 0, default if not explicit */
		/*先分配64个bitm_alloc结构,用于后面管理PMEM模块的申请。
因为用户空间可能不会一下子申请调整个PMEM模块,如有两个进程都申请pmem_audio模块的一小部分内存。PMEM_INITIAL_NUM_BITMAP_ALLOCATIONS 为64.*/
		pmem[id].allocator.bitmap.bitm_alloc = kmalloc(
			PMEM_INITIAL_NUM_BITMAP_ALLOCATIONS *
				sizeof(*pmem[id].allocator.bitmap.bitm_alloc),
			GFP_KERNEL);
~~snip
		/*初始化 bitm_alloc结构体。*/
		for (i = 0; i < PMEM_INITIAL_NUM_BITMAP_ALLOCATIONS; i++) {
			pmem[id].allocator.bitmap.bitm_alloc[i].bit = -1;
			pmem[id].allocator.bitmap.bitm_alloc[i].quanta = 0;
		}
		/*记录当前已经申请的bitm_alloc数量。*/
		pmem[id].allocator.bitmap.bitmap_allocs =
			PMEM_INITIAL_NUM_BITMAP_ALLOCATIONS;
		/*以32为单位记录当前整个PMEM模块的内存数量。*/
		pmem[id].allocator.bitmap.bitmap =
			kcalloc((pmem[id].num_entries + 31) / 32,
				sizeof(unsigned int), GFP_KERNEL);
		if (!pmem[id].allocator.bitmap.bitmap) {
			pr_alert("pmem: %s: Unable to register pmem "
				"driver - can't allocate bitmap!\n",
				__func__);
			goto err_cant_register_device;
		}
		/*当前空闲的entries。*/
		pmem[id].allocator.bitmap.bitmap_free = pmem[id].num_entries;
		/*下面这几个函数会在用户空间通过open/ioctl/mmap用到。*/
		pmem[id].allocate = pmem_allocator_bitmap;
		pmem[id].free = pmem_free_bitmap;
		pmem[id].free_space = pmem_free_space_bitmap;
		pmem[id].len = pmem_len_bitmap;
		pmem[id].start_addr = pmem_start_addr_bitmap;

		DLOG("bitmap allocator id %d (%s), num_entries %u, raw size "
			"%lu, quanta size %u\n",
			id, pdata->name, pmem[id].allocator.bitmap.bitmap_free,
			pmem[id].size, pmem[id].quantum);
		break;

	case PMEM_ALLOCATORTYPE_SYSTEM:
~~snip
	}

	pmem[id].ioctl = ioctl;
	pmem[id].release = release;
	mutex_init(&pmem[id].arena_mutex);
	mutex_init(&pmem[id].data_list_mutex);
	INIT_LIST_HEAD(&pmem[id].data_list);

	pmem[id].dev.name = pdata->name;
	pmem[id].dev.minor = id;	//后面使用id来寻找对应的pmem模块。
	pmem[id].dev.fops = &pmem_fops;	//后面用户空间的操作都会调用的这个变量里的函数指针了。
	pmem[id].reusable = pdata->reusable;
	pr_info("pmem: Initializing %s as %s\n",
		pdata->name, pdata->cached ? "cached" : "non-cached");
	/*注册为字符设备,会看到/dev/pmem_**, 如/dev/pmem_audio,供用户空间
操作设备。*/
	if (misc_register(&pmem[id].dev)) {
		pr_alert("Unable to register pmem driver!\n");
		goto err_cant_register_device;
	}
~~snip
	page = alloc_page(GFP_KERNEL);
	if (!page) {
		pr_err("pmem: Failed to allocate page for %s\n", pdata->name);
		goto cleanup_vm;
	}
	pmem[id].garbage_pfn = page_to_pfn(page);
	atomic_set(&pmem[id].allocation_cnt, 0);

	if (pdata->setup_region)
		pmem[id].region_data = pdata->setup_region();

	if (pdata->request_region)
		pmem[id].mem_request = pdata->request_region;

	if (pdata->release_region)
		pmem[id].mem_release = pdata->release_region;

	pr_info("allocating %lu bytes at %lx physical for %s\n",
		pmem[id].size, pmem[id].base, pmem[id].name);

	return 0;

~~snip
	return -1;
}

 

PMEM使用驱动分析:

open

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

  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. }  
static int pmem_open(struct inode *inode, struct file *file)
{
	struct pmem_data *data;
	int id = get_id(file);
	int ret = 0;
#if PMEM_DEBUG_MSGS
	char currtask_name[FIELD_SIZEOF(struct task_struct, comm) + 1];
#endif

	DLOG("pid %u(%s) file %p(%ld) dev %s(id: %d)\n",
		current->pid, get_task_comm(currtask_name, current),
		file, file_count(file), get_name(file), id);
	/*分配struct pmem_data。*/
	data = kmalloc(sizeof(struct pmem_data), GFP_KERNEL);
	if (!data) {
		printk(KERN_ALERT "pmem: %s: unable to allocate memory for "
				"pmem metadata.", __func__);
		return -1;
	}
	data->flags = 0;
	data->index = -1;
	data->task = NULL;
	data->vma = NULL;
	data->pid = 0;
	data->master_file = NULL;
#if PMEM_DEBUG
	data->ref = 0;
#endif
	INIT_LIST_HEAD(&data->region_list);
	init_rwsem(&data->sem);

	file->private_data = data;
	INIT_LIST_HEAD(&data->list);

	mutex_lock(&pmem[id].data_list_mutex);
	list_add(&data->list, &pmem[id].data_list);
	mutex_unlock(&pmem[id].data_list_mutex);
	return ret;
}


 

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

mmap

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

  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. }  
static int pmem_mmap(struct file *file, struct vm_area_struct *vma)
{
	/*取出open时创建的struct pem_data.*/
	struct pmem_data *data = file->private_data;
	int index = -1;
	/*要映射的size大小。*/
	unsigned long vma_size =  vma->vm_end - vma->vm_start;
	int ret = 0, id = get_id(file);

~~snip
	/* check this file isn't already mmaped, for submaps check this file
	 * has never been mmaped */
	/*如果类型为submap,也不用再mmap。这部分和进程间共享PMEM有关,
也就是说当主进程做了mmap之后,另外一个要共享的进程就无需再mmap了。*/
	if ((data->flags & PMEM_FLAGS_SUBMAP) ||
	    (data->flags & PMEM_FLAGS_UNSUBMAP)) {
#if PMEM_DEBUG
		pr_err("pmem: you can only mmap a pmem file once, "
		       "this file is already mmaped. %x\n", data->flags);
#endif
		ret = -EINVAL;
		goto error;
	}
	/* if file->private_data == unalloced, alloc*/
	/*index表示当前分配的位于bitmap中的索引,如果为-1就表示未分配。*/
	if (data->index == -1) {
		mutex_lock(&pmem[id].arena_mutex);
		/*根据id号来从PMEM模块上分配一部分内存,返回在bitmap的索引。*/
		index = pmem_allocate_from_id(id,
				vma->vm_end - vma->vm_start,
				SZ_4K);
		mutex_unlock(&pmem[id].arena_mutex);
		/* either no space was available or an error occured */
		if (index == -1) {
			pr_err("pmem: mmap unable to allocate memory"
				"on %s\n", get_name(file));
			ret = -ENOMEM;
			goto error;
		}
		/* store the index of a successful allocation */
		data->index = index;
	}
	/*分配的size不能超过整个PMEM模块长度。*/
	if (pmem[id].len(id, data) < vma_size) {
#if PMEM_DEBUG
		pr_err("pmem: mmap size [%lu] does not match"
		       " size of backing region [%lu].\n", vma_size,
		       pmem[id].len(id, data));
#endif
		ret = -EINVAL;
		goto error;
	}
	/*调用的是pmem_start_addr_bitmap 函数,返回当前在整个PMEM模块
中的偏移。*/
	vma->vm_pgoff = pmem[id].start_addr(id, data) >> PAGE_SHIFT;
	/*cache的禁止操作。*/
	vma->vm_page_prot = pmem_phys_mem_access_prot(file, vma->vm_page_prot);
	/* PMEM_FLAGS_CONNECTED 在ioctl接口中会被定义,表示要共享PMEM内存。可以先看如果要共享内存,mmap做了什么。*/
	if (data->flags & PMEM_FLAGS_CONNECTED) {
		struct pmem_region_node *region_node;
		struct list_head *elt;
	/*插入一个pfn页框到用vma中*/
		if (pmem_map_garbage(id, vma, data, 0, vma_size)) {
			pr_alert("pmem: mmap failed in kernel!\n");
			ret = -EAGAIN;
			goto error;
		}
	/*根据当前有多少region_list作一一映射。*/
		list_for_each(elt, &data->region_list) {
			region_node = list_entry(elt, struct pmem_region_node,
						 list);
			DLOG("remapping file: %p %lx %lx\n", file,
				region_node->region.offset,
				region_node->region.len);
			if (pmem_remap_pfn_range(id, vma, data,
						 region_node->region.offset,
						 region_node->region.len)) {
				ret = -EAGAIN;
				goto error;
			}
		}
		/*标记当前是submap。*/
		data->flags |= PMEM_FLAGS_SUBMAP;
		get_task_struct(current->group_leader);
		data->task = current->group_leader;
		data->vma = vma;
#if PMEM_DEBUG
		data->pid = current->pid;
#endif
		DLOG("submmapped file %p vma %p pid %u\n", file, vma,
		     current->pid);
	} else {
		/mastermap走如下流程。映射vma_size大小到用户空间。*/
		if (pmem_map_pfn_range(id, vma, data, 0, vma_size)) {
			pr_err("pmem: mmap failed in kernel!\n");
			ret = -EAGAIN;
			goto error;
		}
		data->flags |= PMEM_FLAGS_MASTERMAP;
		data->pid = current->pid;
	}
	vma->vm_ops = &vm_ops;
error:
	up_write(&data->sem);
	return ret;
}


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

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

  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. }  
static int pmem_allocate_from_id(const int id, const unsigned long size,
						const unsigned int align)
{
	int ret;
	ret = pmem_get_region(id);
	if (ret)
		return -1;
	/*调用的是pmem_allocator_bitmap().*/
	ret = pmem[id].allocate(id, size, align);
	if (ret < 0)
		pmem_put_region(id);
	return ret;
}
static int pmem_get_region(int id)
{
	/* Must be called with arena mutex locked */
	atomic_inc(&pmem[id].allocation_cnt);
	if (!pmem[id].vbase) {
~~snip
		/*根据id作ioremap*/
		ioremap_pmem(id);
	}
~~snip
}
static void ioremap_pmem(int id)
{
	unsigned long addr;
	const struct mem_type *type;

	DLOG("PMEMDEBUG: ioremaping for %s\n", pmem[id].name);
	if (pmem[id].map_on_demand) {
~~snip
	} else {
	/*如果需要cache则调用ioremap_cached,否则调用ioremap。*/
		if (pmem[id].cached)
			pmem[id].vbase = ioremap_cached(pmem[id].base,
						pmem[id].size);
~~snip
		else
			pmem[id].vbase = ioremap(pmem[id].base, pmem[id].size);
	}
}
}


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

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

  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. }  
static int pmem_allocator_bitmap(const int id,
		const unsigned long len,
		const unsigned int align)
{
	/* caller should hold the lock on arena_mutex! */
	int bitnum, i;
	unsigned int quanta_needed;

	DLOG("bitmap id %d, len %ld, align %u\n", id, len, align);
~~snip
	/*以quantum为单位计算要分配的内存大小。*/
	quanta_needed = (len + pmem[id].quantum - 1) / pmem[id].quantum;
	/*超过整个pmem模块的数量则失败。*/
	if (pmem[id].allocator.bitmap.bitmap_free < quanta_needed) {
		return -1;
	}
	/*将要申请的quanta数量再次作一个转换,因为要考虑对齐等因素。*/
	bitnum = reserve_quanta(quanta_needed, id, align);
	if (bitnum == -1)
		goto leave;
	/*找到第一个未被使用过的bitmap的位置。*/
	for (i = 0;
		i < pmem[id].allocator.bitmap.bitmap_allocs &&
			pmem[id].allocator.bitmap.bitm_alloc[i].bit != -1;
		i++)
		;
	/*如果找到的位置已经超出当前的bitmap_allocs数量,则要重新
分配更大的一块bitm_alloc.*/
	if (i >= pmem[id].allocator.bitmap.bitmap_allocs) {
		void *temp;
		/*申请的数量比上次大一倍。*/
		int32_t new_bitmap_allocs =
			pmem[id].allocator.bitmap.bitmap_allocs << 1;
		int j;

		if (!new_bitmap_allocs) { /* failed sanity check!! */
			return -1;
		}
		/*申请数量不能大于当前PMEM模块实际的数量。*/
		if (new_bitmap_allocs > pmem[id].num_entries) {
			/* failed sanity check!! */
			return -1;
		}
		/*重新分配和指定。*/
		temp = krealloc(pmem[id].allocator.bitmap.bitm_alloc,
				new_bitmap_allocs *
				sizeof(*pmem[id].allocator.bitmap.bitm_alloc),
				GFP_KERNEL);
		if (!temp) {
			return -1;
		}
		pmem[id].allocator.bitmap.bitmap_allocs = new_bitmap_allocs;
		pmem[id].allocator.bitmap.bitm_alloc = temp;
		/*只对重新分配的部分作初始化。*/
		for (j = i; j < new_bitmap_allocs; j++) {
			pmem[id].allocator.bitmap.bitm_alloc[j].bit = -1;
			pmem[id].allocator.bitmap.bitm_alloc[i].quanta = 0;
		}

		DLOG("increased # of allocated regions to %d for id %d\n",
			pmem[id].allocator.bitmap.bitmap_allocs, id);
	}

	DLOG("bitnum %d, bitm_alloc index %d\n", bitnum, i);

	pmem[id].allocator.bitmap.bitmap_free -= quanta_needed;
	pmem[id].allocator.bitmap.bitm_alloc[i].bit = bitnum;
	pmem[id].allocator.bitmap.bitm_alloc[i].quanta = quanta_needed;
leave:
	return bitnum;
}

static int reserve_quanta(const unsigned int quanta_needed,
		const int id,
		unsigned int align)
{
	/* alignment should be a valid power of 2 */
	int ret = -1, start_bit = 0, spacing = 1;
~~snip
	start_bit = bit_from_paddr(id,
		(pmem[id].base + align - 1) & ~(align - 1));
	if (start_bit <= -1) {
		return -1;
	}
	spacing = align / pmem[id].quantum;
	spacing = spacing > 1 ? spacing : 1;
	/*从memory pool上也就是当前拥有的PMEM内存块上分配出一片
区域来给当前申请用户进程。*/
	ret = bitmap_allocate_contiguous(pmem[id].allocator.bitmap.bitmap,
		quanta_needed,
		(pmem[id].size + pmem[id].quantum - 1) / pmem[id].quantum,
		spacing,
		start_bit);

	return ret;
}


内存分配完成。

ioctl

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

  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. }  
static long pmem_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	/* called from user space as file op, so file guaranteed to be not
	 * NULL
	 */
	struct pmem_data *data = file->private_data;
	int id = get_id(file);
#if PMEM_DEBUG_MSGS
	char currtask_name[
		FIELD_SIZEOF(struct task_struct, comm) + 1];
#endif

	DLOG("pid %u(%s) file %p(%ld) cmd %#x, dev %s(id: %d)\n",
		current->pid, get_task_comm(currtask_name, current),
		file, file_count(file), cmd, get_name(file), id);

	switch (cmd) {
	case PMEM_GET_PHYS:
		~~snip
	case PMEM_MAP:
		{
			struct pmem_region region;
			DLOG("map\n");
			if (copy_from_user(®ion, (void __user *)arg,
						sizeof(struct pmem_region)))
				return -EFAULT;
			return pmem_remap(®ion, file, PMEM_MAP);
		}
		break;
	case PMEM_UNMAP:
		~~snip
	case PMEM_GET_SIZE:
		~~snip
	case PMEM_GET_TOTAL_SIZE:
		~~snip
	case PMEM_GET_FREE_SPACE:
~~snip
	case PMEM_ALLOCATE:
		{
			int ret = 0;
			DLOG("allocate, id %d\n", id);
			down_write(&data->sem);
			if (has_allocation(file)) {
				pr_err("pmem: Existing allocation found on "
					"this file descrpitor\n");
				up_write(&data->sem);
				return -EINVAL;
			}

			mutex_lock(&pmem[id].arena_mutex);
			data->index = pmem_allocate_from_id(id,
					arg,
					SZ_4K);
			mutex_unlock(&pmem[id].arena_mutex);
			ret = data->index == -1 ? -ENOMEM :
				data->index;
			up_write(&data->sem);
			return ret;
		}
	case PMEM_ALLOCATE_ALIGNED:
		~~snip
	case PMEM_CONNECT:
		DLOG("connect\n");
		return pmem_connect(arg, file);
	case PMEM_CLEAN_INV_CACHES:
	case PMEM_CLEAN_CACHES:
	case PMEM_INV_CACHES:
		~~snip
	default:
		if (pmem[id].ioctl)
			return pmem[id].ioctl(file, cmd, arg);

		DLOG("ioctl invalid (%#x)\n", cmd);
		return -EINVAL;
	}
	return 0;
}


 

PMEM_ALLOCATE:

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

PMEM_CONNECT:

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

  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. }  
static int pmem_connect(unsigned long connect, struct file *file)
{
	int ret = 0, put_needed;
	struct file *src_file;
~~snip
	/*根据主进程的fd获得相对应的file.*/
	src_file = fget_light(connect, &put_needed);
~~snip
	if (unlikely(!is_pmem_file(src_file))) {
		pr_err("pmem: %s: src file is not a pmem file!\n",
			__func__);
		ret = -EINVAL;
		goto put_src_file;
	} else {
		/*得到master的pmem data.*/
		struct pmem_data *src_data = src_file->private_data;

		if (!src_data) {
			pr_err("pmem: %s: src file pointer has no"
				"private data, bailing out!\n", __func__);
			ret = -EINVAL;
			goto put_src_file;
		}

		down_read(&src_data->sem);

		if (unlikely(!has_allocation(src_file))) {
			up_read(&src_data->sem);
			pr_err("pmem: %s: src file has no allocation!\n",
				__func__);
			ret = -EINVAL;
		} else {
			struct pmem_data *data;
			/*获得master分配到的内存在bitmap中的index.*/
			int src_index = src_data->index;

			up_read(&src_data->sem);

			data = file->private_data;
			if (!data) {
				pr_err("pmem: %s: passed in file "
					"pointer has no private data, bailing"
					" out!\n", __func__);
				ret = -EINVAL;
				goto put_src_file;
			}

			down_write(&data->sem);
			if (has_allocation(file) &&
					(data->index != src_index)) {
				up_write(&data->sem);

				pr_err("pmem: %s: file is already "
					"mapped but doesn't match this "
					"src_file!\n", __func__);
				ret = -EINVAL;
			} else {
				/*将master的pmem data数据保存到当前进程中。*/
				data->index = src_index;
				data->flags |= PMEM_FLAGS_CONNECTED;		//设置标志,在mmap中用到。
				data->master_fd = connect;
				data->master_file = src_file;

				up_write(&data->sem);

				DLOG("connect %p to %p\n", file, src_file);
			}
		}
	}
put_src_file:
	fput_light(src_file, put_needed);
leave:
	return ret;
}


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

PMEM_MAP:

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

  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. }  
int pmem_remap(struct pmem_region *region, struct file *file,
		      unsigned operation)
{
	int ret;
	struct pmem_region_node *region_node;
	struct mm_struct *mm = NULL;
	struct list_head *elt, *elt2;
	int id = get_id(file);
	struct pmem_data *data;
~~snip

	/* is_pmem_file fails if !file */
	data = file->private_data;
~~snip
	/* lock the mm and data */
	ret = pmem_lock_data_and_mm(file, data, &mm);
	if (ret)
		return 0;
/*明确指定只有master file才能作remap动作。*/
	/* only the owner of the master file can remap the client fds
	 * that back in it */
	if (!is_master_owner(file)) {
		ret = -EINVAL;
		goto err;
	}
~~snip
	if (operation == PMEM_MAP) {
	/*生成一个struct pem_region_node,用来保存上层传下来的region信息。*/
		region_node = kmalloc(sizeof(struct pmem_region_node),
			      GFP_KERNEL);
		if (!region_node) {
			ret = -ENOMEM;
			goto err;
		}
		region_node->region = *region;
		/*添加到data的region_list中。*/
		list_add(®ion_node->list, &data->region_list);
	} else if (operation == PMEM_UNMAP) {
		int found = 0;
		list_for_each_safe(elt, elt2, &data->region_list) {
			region_node = list_entry(elt, struct pmem_region_node,
				      list);
			if (region->len == 0 ||
			    (region_node->region.offset == region->offset &&
			    region_node->region.len == region->len)) {
				list_del(elt);
				kfree(region_node);
				found = 1;
			}
		}
~~snip
	/*比较好奇PMEM_FLAGS_SUBMAP是想共享的进程设置的,但是前面的判断又是
只能master file才能作remap,这样岂不是永远执行不了?*/
	if (data->vma && PMEM_IS_SUBMAP(data)) {
		if (operation == PMEM_MAP)
			ret = pmem_remap_pfn_range(id, data->vma, data,
				   region->offset, region->len);
		else if (operation == PMEM_UNMAP)
			ret = pmem_unmap_pfn_range(id, data->vma, data,
				   region->offset, region->len);
	}

err:
	pmem_unlock_data_and_mm(data, mm);
	return ret;
}


[END]



  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值