主要谈三个问题。
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个,有点遗憾!!!
阅读(10143) | 评论(0) | 转发(2) |