检测合法的伙伴系统区间

假设有这样一个场景,人们在得到一个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的情况。真是惭愧!

你可能感兴趣的:(检测合法的伙伴系统区间)