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等通信机制。
PMEM对应的驱动代码流程也是比较简单的,利用了字符驱动的open/ioctl/mmap操作,下面直接分析代码。
PMEM初始化:
PMEM类似于ION里的carved-outmemory,先预留一块内存,然后需要使用的时候从上面分配一块。因此PMEM总有一天会被ION替代,至少我这么认为.
平台上定义了如下三个PMEM模块: pmem_adsp, pmem_audio, pmem(mdp_pmem)。
[html] view plain copy print ?
- 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 },
- };
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
[html] view plain copy print ?
- 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;
- }
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:
[html] view plain copy print ?
- 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;
- }
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。
[html] view plain copy print ?
- 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;
- }
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():
[html] view plain copy print ?
- 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);
- }
- }
- }
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来分配及管理内存。
[html] view plain copy print ?
- 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;
- }
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,其他几个很简单,可自行分析。
[html] view plain copy print ?
- 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;
- }
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 .
[html] view plain copy print ?
- 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;
- }
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而开设的,不过我想不到什么时候要重新映射呢?
[html] view plain copy print ?
- 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;
- }
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;
}
2013/03/04