假设有这样一个场景,人们在得到一个kmalloc出来的地址后,不清楚该地址是否位于合法的buddy system区间。
为了检查该区间的合法性,就可以在系统启动时,buddy system生成时、bootmem释放时,根据bootmem map
来确定哪些页框是被释放给buddy system。(除了bootmem的释放,后期还有释放init段空间,以及释放initrd空间,这里只考虑前者)
struct low_memory_buddy_region { unsigned long start; unsigned long end; }; #define MAX_BUDDY_LMBR 100 static struct low_memory_buddy_region lmbr[MAX_BUDDY_LMBR]={{0,0}}; static int index_lmbr = 0; void add_new_lmbr(unsigned long start,unsigned long end) { lmbr[index_lmbr].start = start; lmbr[index_lmbr].end = end; index_lmbr++; } void dump_all_lmbr(void) { int i = 0; for(i=0;i<index_lmbr;i++) printk("buddy region low pfn[%ld-%ld]\n", lmbr[i].start, lmbr[i].end); } void traverse_bootmem_map(unsigned long *map, unsigned long start_idx, unsigned long idx) { unsigned long i, m, v = 0; int new_region = 0 ; unsigned long start_pfn = 0; for (i = 0; i < idx; i += BITS_PER_LONG) { v = ~map[i / BITS_PER_LONG]; if (v) { /* * Fixme when v=~0UL */ for (m = 0; m<BITS_PER_LONG; m++) { if (v & (1UL<<m)){ if(!new_region){ start_pfn = i+m; new_region = 1; } }else{ if(new_region){ add_new_lmbr(start_pfn+start_idx,i+m+start_idx); new_region = 0; } } } }else{/*continuous zeros tell the end of region*/ if(new_region){ add_new_lmbr(start_pfn+start_idx,i+start_idx); new_region = 0; } } } /* * Last v's bit equals 1 tells the new region's ending */ if(v&1) add_new_lmbr(start_pfn+start_idx,idx+start_idx); }
static unsigned long __init free_all_bootmem_core(pg_data_t *pgdat) { /* first extant page of the node */ pfn = PFN_DOWN(bdata->node_boot_start); idx = bdata->node_low_pfn - pfn; map = bdata->node_bootmem_map; #if 1 traverse_bootmem_map(map,pfn,idx); #endif /* Check physaddr is O(LOG2(BITS_PER_LONG)) page aligned */ if (bdata->node_boot_start == 0 || ffs(bdata->node_boot_start) - PAGE_SHIFT > ffs(BITS_PER_LONG)) gofast = 1; }
#if 1 #define virt_to_pfn(kaddr) (__pa(kaddr) >> PAGE_SHIFT) add_new_lmbr(virt_to_pfn(bdata->node_bootmem_map), virt_to_pfn(bdata->node_bootmem_map)+idx); #endif total += count; bdata->node_bootmem_map = NULL;
上述代码有两处可以改进:
1)add_new_lmbr可以对提交的区间进行排序插入,考虑到区间不是太多,采用冒泡排序(可借鉴powerpc的lmb内存管理)
2)遍历bootmem map的时候,如果某时刻取到的unsigned long位图是全f,则可以直接跳过32个页。
3)上述代码没有考虑高端内存。
ps: 发现n久不写代码,上面那么一点点代码都耗了大量时间。特别是还漏掉了位图为连续的0的情况。真是惭愧!