高端内存管理--持久映射-固定映射 -临时映射
FROM:http://blog.163.com/yangfan876@126/blog/static/80612456201321531341694
高端内存管理--持久映射

234/*
235 * Describes one page->virtual association
236 */
237struct page_address_map {
238struct page *page;
239void*virtual;
240struct list_head list;
241};

262void*page_address(struct page *page)
263{
264unsignedlong flags;
265void*ret;
266struct page_address_slot *pas;
267
268if(!PageHighMem(page))//如果该页描述符不是高端内存的页框则直接调用lowmem_page_address来返回
269return lowmem_page_address(page);//其对应的虚拟地址空间的线性地址
270
271 pas = page_slot(page);//否则通过oage_slot函数查找该页描述符存放在散列表中的表项地址
272 ret = NULL;
273 spin_lock_irqsave(&pas->lock, flags);//获取对应表项的自旋锁
274if(!list_empty(&pas->lh)){//如果该表项之后的链表为空则说明没有建立任何映射关系
275struct page_address_map *pam;
276
277 list_for_each_entry(pam,&pas->lh, list){//如果不为空,则遍历该链表的元素
278if(pam->page == page){//找到所要的页描述符
279 ret = pam->virtual;
280gotodone;
281}
282}
283}
284done:
285 spin_unlock_irqrestore(&pas->lock, flags);
286return ret;
287}
4void*kmap(struct page *page)
5{
6 might_sleep();
7if(!PageHighMem(page))
8return page_address(page);
9return kmap_high(page);
10}
166void fastcall *kmap_high(struct page *page)
167{
168unsignedlong vaddr;
169
170/*
171 * For highmem pages, we can't trust "virtual" until
172 * after we have the lock.
173 *
174 * We cannot call this from interrupts, as it may block
175 */
176 spin_lock(&kmap_lock);//获得自旋锁
177 vaddr =(unsignedlong)page_address(page);
178if(!vaddr)//如果该页描述符未被映射则调用map_new_virtual来进行映射
179 vaddr = map_new_virtual(page);
180 pkmap_count[PKMAP_NR(vaddr)]++;//状态记录数组对应的位置值自增,注意即使这里没有执行map_new_virtual也会
//进行自增,因为这表明此页描述符被多个内核成分使用
181 BUG_ON(pkmap_count[PKMAP_NR(vaddr)] < 2);
182 spin_unlock(&kmap_lock);
183return(void*) vaddr;
184}
116staticinlineunsignedlong map_new_virtual(struct page *page)
117{
118unsignedlong vaddr;
119int count;
120
121 start:
122 count = LAST_PKMAP;
123/* Find an empty entry */
124for(;;){
125 last_pkmap_nr =(last_pkmap_nr +1)& LAST_PKMAP_MASK;//从上次使用的位置开始
126if(!last_pkmap_nr){//如果last_pkmap_nr又从头开始查询则调用flush_all_zero_pkmaps来输出CPU高速缓存,
127 flush_all_zero_pkmaps();//因为这个时候pkmap_count数组中可能会存在一些1值项,即未映射但不可使用页表项
128 count = LAST_PKMAP;
129}
130if(!pkmap_count[last_pkmap_nr])//找到了0值项,即可用页表项
131break;/* Found a usable entry */
132if(--count)
133continue;
134
135/*
136 * Sleep for somebody else to unmap their entries //因为该函数可以睡面,如果没有找到所需页表项则睡眠,直
137 *///到内核的其它线程释放一些页表项,该函数则从start处开始
138 { //重新运行
139 DECLARE_WAITQUEUE(wait, current);
140
141 __set_current_state(TASK_UNINTERRUPTIBLE);
142 add_wait_queue(&pkmap_map_wait,&wait);
144 schedule();
145 remove_wait_queue(&pkmap_map_wait,&wait);
146 spin_lock(&kmap_lock);
147
148/* Somebody else might have mapped it while we slept */
149if(page_address(page))
150return(unsignedlong)page_address(page);
151
152/* Re-start */
153goto start;
154}
155}
156 vaddr = PKMAP_ADDR(last_pkmap_nr);//所需页表项之后保存其线性地址
157 set_pte_at(&init_mm, vaddr,//设置页表项,建立映射
158&(pkmap_page_table[last_pkmap_nr]), mk_pte(page, kmap_prot));
159
160 pkmap_count[last_pkmap_nr]=1;//因为这里只建立了映射关系,但是TLB中为更新,所以这里置1
161 set_page_address(page,(void*)vaddr);//将该页加入持久映射数据结构中
162
163return vaddr;
164}
高端内存管理--固定映射

97unsignedlong __FIXADDR_TOP =0xfffff000;
98 EXPORT_SYMBOL(__FIXADDR_TOP);
156#define FIXADDR_BOOT_SIZE (__end_of_fixed_addresses << PAGE_SHIFT)
157#define FIXADDR_START (FIXADDR_TOP - FIXADDR_SIZE)
73enum fixed_addresses {
74#ifdef CONFIG_X86_32
75 FIX_HOLE,
76 FIX_VDSO,
77#else
78 VSYSCALL_LAST_PAGE,
79 VSYSCALL_FIRST_PAGE = VSYSCALL_LAST_PAGE
80+((VSYSCALL_END-VSYSCALL_START)>> PAGE_SHIFT)-1,
81 VVAR_PAGE,
82 VSYSCALL_HPET,
83#endif
...........
200static __always_inline unsignedlong fix_to_virt(constunsignedint idx)
201{
202/*
203 * this branch gets completely eliminated after inlining,
204 * except when someone tries to use fixaddr indices in an
205 * illegal way. (such as mixing up address types or using
206 * out-of-range indices).
207 *
208 * If it doesn't get removed, the linker will complain
209 * loudly with a reasonably clear error message..
210 */
211if(idx >= __end_of_fixed_addresses)
212 __this_fixmap_does_not_exist();
213
214return __fix_to_virt(idx);
215}
190#define __fix_to_virt(x)(FIXADDR_TOP -((x)<< PAGE_SHIFT))
这个宏表明寻找虚拟地址是从尾部开始向前偏移X页。对于固定映射来说,建立虚拟地址和物理内存页之间的关联是通过set_fixmap和set_fixmao_nocache函数来执行:178#define set_fixmap(idx, phys) \
179 __set_fixmap(idx, phys, PAGE_KERNEL)
431void __native_set_fixmap(enum fixed_addresses idx,pte_t pte)
432{
433unsignedlong address = __fix_to_virt(idx);//寻找idx对应的虚拟空间线性地址
434
435if(idx >= __end_of_fixed_addresses){
436 BUG();
437return;
438}
439 set_pte_vaddr(address, pte);//执行映射操作
440 fixmaps_set++;
441}
高端内存管理--临时映射

108#ifdef CONFIG_X86_32
109 FIX_KMAP_BEGIN,/* reserved pte's for temporary kernel mappings */
110 FIX_KMAP_END = FIX_KMAP_BEGIN+(KM_TYPE_NR*NR_CPUS)-1,

4#ifdef __WITH_KM_FENCE
5# define KMAP_D(n) __KM_FENCE_##n ,
6#else
7# define KMAP_D(n)
8#endif
9
10enum km_type {
11 KMAP_D(0) KM_BOUNCE_READ,
12 KMAP_D(1) KM_SKB_SUNRPC_DATA,
13 KMAP_D(2) KM_SKB_DATA_SOFTIRQ,
14 KMAP_D(3) KM_USER0,
15 KMAP_D(4) KM_USER1,
16 KMAP_D(5) KM_BIO_SRC_IRQ,
17 KMAP_D(6) KM_BIO_DST_IRQ,
18 KMAP_D(7) KM_PTE0,
19 KMAP_D(8) KM_PTE1,
20 KMAP_D(9) KM_IRQ0,
.............
49void*kmap_atomic(struct page *page,enum km_type type)
50{
51return kmap_atomic_prot(page, type, kmap_prot);
52}
29void*kmap_atomic_prot(struct page *page,enum km_type type,pgprot_t prot)
30{
31enum fixed_addresses idx;
32unsignedlong vaddr;
33
34/* even !CONFIG_PREEMPT needs this, for in_atomic in do_page_fault */
35 pagefault_disable();
36
37if(!PageHighMem(page))//判断指定页是否在高端内存
38return page_address(page);
39
40 idx = type + KM_TYPE_NR*smp_processor_id();//这两句是为了获取指定的虚拟
41 vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);//空间的线性地址
42 BUG_ON(!pte_none(*(kmap_pte-idx)));
43 set_pte(kmap_pte-idx, mk_pte(page, prot));//建立映射关系
44 arch_flush_lazy_mmu_mode();
45
46return(void*)vaddr;
47}