您的位置:首页 > 其它

内核 kmap_atomic分析

2016-03-09 02:29 246 查看
1、为什么会有这个函数?

我想主要的原因就是kmap_atomic在效率上要比kmap提升不少。

在kmap函数中,会有如下几个比较耗时的部分:

1)、page_address函数

2)、Sleep for somebody else to unmap their entries

代码如下:

171 ____________DECLARE_WAITQUEUE(wait, current);

173 ______________set_current_state(TASK_UNINTERRUPTIBLE);

174____________add_wait_queue(&pkmap_map_wait,&wait);

175 ____________unlock_kmap();

176 ____________schedule();

177 ____________remove_wait_queue(&pkmap_map_wait, &wait);

3)、lock_kmap函数,需要加锁啊!!!

相反kmap_atomic函数,从名字就能看出,原子操作一气呵成。。没有sleep,没有锁。

2、怎么实现的?需要考虑什么问题?

首先得知道这个函数的主要目的是实现page 到 vaddr的转化。

其次我们得考虑多cpu和多任务,大家知道 kernel可以在多个cpu上同时运行不同的task,然而它们共同使用一个内存地址空间,因此如何能保证N个cpu调用kmap_atomic不会将page映射到一个虚拟地址(vaddr)呢?

我们来看看kmap_atomic是如何实现的?

1)、定义一个percpu变量__kmap_atomic_idx,同时在当前cpu上禁用抢占,直到unmap的时候才开启,这样就保证了同一cpu其它任务不会调用该函数。除非该进程在unmap之前睡眠,如果真的那样,别的进程就很可能在同一cpu重入kmap_atomic函数了,然后就可能映射到同一虚拟地址,因此在原子映射期间最好不要休眠。

2)、设计了一个完美的公式

type = kmap_atomic_idx_push(); //递增一个percpu变量,返回递增后的结果,增加一个计算type的函数表示kmap_atomic函数可以重入,就是上面说的该进程在unmap之前睡眠情况,但一般情况下不会发生冲入,所以该值应该是1,unmap时该值会--。当然重入的次数是有限制的,不会超过KM_TYPE_NR。

idx = type + KM_TYPE_NR*smp_processor_id();//不同的cpusmp_processor_id()的值是不同的,因此,不同的cpu得到的idx不同。

vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);//不同的idx得到的vaddr就不同了。

set_pte(kmap_pte-idx, mk_pte(page, prot)); //设置页表

return (void *)vaddr;

总结,通过percpu变量+禁抢占+加计算公式,就实现了,不同cpu的不同进程调用kmap_atomic函数得到的vaddr是不同的。同时这也给我们实现atomic提供了一种思路,实际上atomic一般都会有一个percpu变量。

3、得到vaddr之后,页表的建立。

我们就以arm为例,来说明一下,arm初始化的时候,会通过create_mapping来创建一些页表,比如map_lowmem就会调用create_mapping函数来建立整个低端内存的映射。

同样地在函数devicemaps_init中会有如下代码:

967 ____map.pfn = __phys_to_pfn(virt_to_phys(vectors_page));

968 ____map.virtual = 0xffff0000;

969 ____map.length = PAGE_SIZE;

970 ____map.type = MT_HIGH_VECTORS;

971 ____create_mapping(&map);

这里有一点需要说明一点,虽然只映射了一个PAGE_SIZE,但是如果内核采用的二级页表(即存在pgd,pte),而不是一级页表(只有pgd)的话,会在create_mapping函数中,分配512个pte项(即512个pte指针ptep),远远大于KM_TYPE_NR个数。但是如果采用一级页表的话,这就会有问题,这种情况应该不能用kmap_atomic函数,因为通过TOP_PTE(vaddr)宏是得不到ptep的,因此目前代码中有如下保护:

67 #ifdef CONFIG_DEBUG_HIGHMEM

68 ____/*

69 ____ * With debugging enabled, kunmap_atomic forces that entry to 0.

70 ____ * Make sure it was indeed properly unmapped.

71 ____ */

72____BUG_ON(!pte_none((TOP_PTE(vaddr))));

73 #endif

由此,我们可以看到kmap_atomic使用的是地址空间顶部的一小段地址空间(0xffff0000开始),内核逻辑将这一小段地址空间分成了若干个节(slot),每一节的大小是一个page的大小,可以用来映射一个page。虽然总共可用512个slot,但是只能用KM_TYPE_NR个,有点遗憾!!!

原文地址: http://blog.chinaunix.net/uid-26817832-id-3358944.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: