bootmem_free_node

假如内存的物理起始地址为:0x30000000, 大小为:0x1000000


static void __init bootmem_free_node(int node, struct meminfo *mi)
{											//zone hole洞大小
	unsigned long zone_size[MAX_NR_ZONES], zhole_size[MAX_NR_ZONES];
	unsigned long min, max_low, max_high;
	int i;

	find_node_limits(node, mi, &min, &max_low, &max_high);

	/*
	 * initialise the zones within this node.
	 */
	memset(zone_size, 0, sizeof(zone_size));

	/*
	 * The size of this node has already been determined.  If we need
	 * to do anything fancy with the allocation of this memory to the
	 * zones, now is the time to do it.
	 */
	 
	zone_size[0] = max_low - min;
	
	memcpy(zhole_size, zone_size, sizeof(zhole_size));
	for_each_nodebank(i, mi, node) {
		int idx = 0;
		zhole_size[idx] -= bank_pfn_size(&mi->bank[i]);
	}

	/*
	 * Adjust the sizes according to any special requirements for
	 * this machine type.
	 */
	arch_adjust_zones(node, zone_size, zhole_size);//空函数

	free_area_init_node(node, zone_size, min, zhole_size);
}

在我的配置里,只定义了两个zone,分别为:ZONE_NORMAL和ZONE_MOVABLE,所以MAX_NR_ZONES = 2
1,find_node_limits函数前面已经分析过了,这里只列出它返回的值:
min=0x30000 
max_low=0x31000 
max_high=0x31000


2,zone_size[0] = max_low - min = 0x1000


3, for_each_nodebank(i, mi, node) {
int idx = 0;
zhole_size[idx] -= bank_pfn_size(&mi->bank[i]);
}
#define bank_pfn_size(bank) ((bank)->size >> PAGE_SHIFT)
在我的系统里只有1个node和1个bank,所以上面计算的结果为:
zhole_size[idx] -= bank_pfn_size(&mi->bank[i]);
zhole_size[0] = 0x1000 - 0x1000 = 0
该值表示ZONE_NORMAL区孔洞[hole]的大小为0


4,调用free_area_init_node函数


void __paginginit free_area_init_node(int nid, unsigned long *zones_size,
		unsigned long node_start_pfn, unsigned long *zholes_size)
{
/*
	nid=0
	zones_size[0]=0x1000   zones_size[1]=0
	node_start_pfn=0x30000
	zholes_size[0]=0       zholes_size[1]=0
*/
	
	pg_data_t *pgdat = NODE_DATA(nid);

	pgdat->node_id = nid;
	pgdat->node_start_pfn = node_start_pfn;
	/*
		该函数主要为pgdat结构的两个参数赋值
		pgdat->node_id = 0;
		pgdat->node_start_pfn = 0x30000;
		pgdat->node_spanned_pages = 0x1000(4096);
		pgdat->node_present_pages =  0x1000(4096);
	*/
	calculate_node_totalpages(pgdat, zones_size, zholes_size);

	alloc_node_mem_map(pgdat);
#ifdef CONFIG_FLAT_NODE_MEM_MAP
	printk(KERN_DEBUG "free_area_init_node: node %d, pgdat %08lx, node_mem_map %08lx\n",
		nid, (unsigned long)pgdat,
		(unsigned long)pgdat->node_mem_map);
#endif

	free_area_init_core(pgdat, zones_size, zholes_size);
}


该函数主要在初始化pgdat变量
1,pg_data_t *pgdat = NODE_DATA(nid) 定义局部变量pgdat并指向全局变量contig_page_data


#define NODE_DATA(nid) (&contig_page_data)
struct pglist_data __refdata contig_page_data = { .bdata = &bootmem_node_data[0] };
bootmem_data_t bootmem_node_data[MAX_NUMNODES];  //MAX_NUMNODES = 1


2,pgdat->node_id = nid = 0
   pgdat->node_start_pfn = node_start_pfn = 0x30000
  
3,调用calculate_node_totalpages函数计算总页数totalpages


4,调用alloc_node_mem_map函数为pgdat中的成员分配内存


5,调用free_area_init_core函数继续对pgdat中的成员初始化,初始化zone以及page结构

typedef struct pglist_data {
	struct zone node_zones[MAX_NR_ZONES];
	/*
		#ifdef CONFIG_NUMA 
		        MAX_ZONELISTS  2
		#else
			MAX_ZONELISTS  1
		#endif
	*/
	struct zonelist node_zonelists[MAX_ZONELISTS];//MAX_ZONELISTS  1
	int nr_zones;
#ifdef CONFIG_FLAT_NODE_MEM_MAP	/* means !SPARSEMEM   这个配置了*/
	/*
		bootmem会在内存中为每一个页分配一个struct page的结构,用于描述该页
		那么node_mem_map变量指向这个内存的起始地址
	*/
	struct page *node_mem_map;			
#ifdef CONFIG_CGROUP_MEM_RES_CTLR        //这个没有配置
	//struct page_cgroup *node_page_cgroup;
#endif
#endif
	struct bootmem_data *bdata;
#ifdef CONFIG_MEMORY_HOTPLUG //这个没有配置
	/*
	 * Must be held any time you expect node_start_pfn, node_present_pages
	 * or node_spanned_pages stay constant.  Holding this will also
	 * guarantee that any pfn_valid() stays that way.
	 *
	 * Nests above zone->lock and zone->size_seqlock.
	 */
	//spinlock_t node_size_lock;
#endif
	unsigned long node_start_pfn;   /*启始页帧号*/
	unsigned long node_present_pages; /* total number of physical pages    不包含孔洞的物理页的总个数*/
	unsigned long node_spanned_pages; /* total size of physical page
					     range, including holes */  /*包含孔洞的物理页的总个数*/
	int node_id;
	wait_queue_head_t kswapd_wait;
	struct task_struct *kswapd;
	int kswapd_max_order;
} pg_data_t;




static void __meminit calculate_node_totalpages(struct pglist_data *pgdat,
		unsigned long *zones_size, unsigned long *zholes_size)
{
	unsigned long realtotalpages, totalpages = 0;
	enum zone_type i;

	/*zones_size[0]=0x1000   zones_size[1]=0*/
	for (i = 0; i < MAX_NR_ZONES; i++)
		totalpages += zone_spanned_pages_in_node(pgdat->node_id, i,
								zones_size);
	pgdat->node_spanned_pages = totalpages;

	realtotalpages = totalpages;
	
	/*zholes_size[0]=0       zholes_size[1]=0*/
	for (i = 0; i < MAX_NR_ZONES; i++)
		realtotalpages -=
			zone_absent_pages_in_node(pgdat->node_id, i,
								zholes_size);
	pgdat->node_present_pages = realtotalpages;
	printk(KERN_DEBUG "On node %d totalpages: %lu\n", pgdat->node_id,
							realtotalpages);
}
该函数主要作用:
1,计算值 pgdat->node_spanned_pages = 0x1000(4096);
2,计算值 pgdat->node_present_pages =  0x1000(4096);

static inline unsigned long __meminit zone_spanned_pages_in_node(int nid,
					unsigned long zone_type,
					unsigned long *zones_size)
{
	return zones_size[zone_type];
}

static inline unsigned long __meminit zone_absent_pages_in_node(int nid,
						unsigned long zone_type,
						unsigned long *zholes_size)
{
	if (!zholes_size)
		return 0;

	return zholes_size[zone_type];
}


static void __init_refok alloc_node_mem_map(struct pglist_data *pgdat)
{
	/* Skip empty nodes */
	if (!pgdat->node_spanned_pages)
		return;

#ifdef CONFIG_FLAT_NODE_MEM_MAP
	/* ia64 gets its own node_mem_map, before this, without bootmem */
	if (!pgdat->node_mem_map) {
		unsigned long size, start, end;
		struct page *map;
		 
		/*
		pgdat->node_id = 0;
		pgdat->node_start_pfn = 0x30000;
		pgdat->node_spanned_pages = 0x1000(4096);
		pgdat->node_present_pages =  0x1000(4096);
		*/
		//MAX_ORDER_NR_PAGES = 1 << 10 = 1024
		//~(MAX_ORDER_NR_PAGES - 1) = 0xffff fc00
		//start=0x30000 end=0x31000 size=0x20000 map=0xc0434000 pgdat->node_mem_map=0xc0434000
		start = pgdat->node_start_pfn & ~(MAX_ORDER_NR_PAGES - 1);
		end = pgdat->node_start_pfn + pgdat->node_spanned_pages;
		end = ALIGN(end, MAX_ORDER_NR_PAGES);
		size =  (end - start) * sizeof(struct page);    
		
		map = alloc_remap(pgdat->node_id, size);//空函数
		if (!map)
			map = alloc_bootmem_node(pgdat, size);
		pgdat->node_mem_map = map + (pgdat->node_start_pfn - start);
	}
#ifndef CONFIG_NEED_MULTIPLE_NODES
	/*
	 * With no DISCONTIG, the global mem_map is just set as node 0's
	 */
	if (pgdat == NODE_DATA(0)) {
		mem_map = NODE_DATA(0)->node_mem_map;
	}
#endif
#endif /* CONFIG_FLAT_NODE_MEM_MAP */
}
分析:
该函数的重点在于计算size值并用该值调用alloc_bootmem_node函数
先看一下主调函数传进来的值:
pgdat->node_id = 0;
pgdat->node_start_pfn = 0x30000;
pgdat->node_spanned_pages = 0x1000(4096);
pgdat->node_present_pages =  0x1000(4096);
然后开始计算:
MAX_ORDER_NR_PAGES = 1 << 10 = 1024    ~(MAX_ORDER_NR_PAGES - 1) = 0xfffffc00
start = pgdat->node_start_pfn & ~(MAX_ORDER_NR_PAGES - 1) = 0x30000 & 0xfffffc00 = 0x30000
end = pgdat->node_start_pfn + pgdat->node_spanned_pages = 0x30000 + 0x1000 = 0x31000
end = ALIGN(end, MAX_ORDER_NR_PAGES) = ALIGN(0x31000, 1024) = 0x31000
size =  (end - start) * sizeof(struct page) = 0x1000 * sizeof(struct page) = 0x20000
可以看出,size大小等于页数 * (struct page)结构的大小,也就是说,要为每个页在内存中分配一个(struct page)结构
用于描述这个页。
接下来就是调用alloc_bootmem_node函数来分配内存。

//SMP_CACHE_BYTES = 1 << 5 = 32  #define MAX_DMA_ADDRESS	0xffffffff
#define alloc_bootmem_node(pgdat, x) \
	__alloc_bootmem_node(pgdat, x, SMP_CACHE_BYTES, __pa(MAX_DMA_ADDRESS))

void * __init __alloc_bootmem_node(pg_data_t *pgdat, unsigned long size,
				   unsigned long align, unsigned long goal)
{
	if (WARN_ON_ONCE(slab_is_available()))
		return kzalloc_node(size, GFP_NOWAIT, pgdat->node_id);

	return ___alloc_bootmem_node(pgdat->bdata, size, align, goal, 0);
}

static void * __init ___alloc_bootmem_node(bootmem_data_t *bdata,
				unsigned long size, unsigned long align,
				unsigned long goal, unsigned long limit)
{
	void *ptr;

	ptr = alloc_arch_preferred_bootmem(bdata, size, align, goal, limit);
	if (ptr)
		return ptr;

	ptr = alloc_bootmem_core(bdata, size, align, goal, limit);
	if (ptr)
		return ptr;

	return ___alloc_bootmem(size, align, goal, limit);
}

static void * __init alloc_bootmem_core(struct bootmem_data *bdata,
					unsigned long size, unsigned long align,
					unsigned long goal, unsigned long limit)
{
	unsigned long fallback = 0;
	unsigned long min, max, start, sidx, midx, step;

	bdebug("nid=%td size=%lx [%lu pages] align=%lx goal=%lx limit=%lx\n",
		bdata - bootmem_node_data, size, PAGE_ALIGN(size) >> PAGE_SHIFT,
		align, goal, limit);

/* 分配的内存大小不能为0 */
	BUG_ON(!size);
/* 对齐大小必须是2的幂数 */
	BUG_ON(align & (align - 1));
 /* 起始地址+申请的内存大小>截止地址,参数错误 */
	BUG_ON(limit && goal + size > limit);

/* 未初始化此bootmem的位图,返回 */
	if (!bdata->node_bootmem_map)
		return NULL;
	
/* 
min表示bootmem的起始pfn 
max表示bootmem的截止pfn
*/
//min=0x30000 max=0x31000 
	min = bdata->node_min_pfn;
	max = bdata->node_low_pfn;

/*
限定范围的起止地址转换为pfn
*/
//goal=0x6ffff limit=0x0
	goal >>= PAGE_SHIFT;
	limit >>= PAGE_SHIFT;

/* 参数指定的截止地址更小,以参数指定的截止地址为准 */
	if (limit && max > limit)
		max = limit;
/* 起止地址错误,返回 */
	if (max <= min)
		return NULL;

/* step为扫描位图时,每次递增的页数/pfn偏移数[align >> PAGE_SHIFT]。
由对齐大小计算而来,不足一页时,step为1,表示每次递增一页 */
//step=0x1(1)
	step = max(align >> PAGE_SHIFT, 1UL);

/* 参数指定的起始pfn更大,根据它计算起始pfn,并按申请的页数对齐*/
	if (goal && min < goal && goal < max)
		start = ALIGN(goal, step);
	else
		/* 取bootmem的起始pfn */
		//start=0x30000(196608)
		start = ALIGN(min, step);

/* 计算起始pfn与bootmem起始pfn的偏移,后面扫描位图时从这个sidx开始 */
//sidx=0x0 midx=0x1000
	sidx = start - bdata->node_min_pfn;
/* 计算截止pfn与bootmem起始pfn的偏移。扫描的就是sidx至midx之间的这部分bootmem内存区 */
	midx = max - bdata->node_min_pfn;

/* hint_idx表示优先扫描的偏移,如果大于sidx,则从hint_idx处开始扫描。
何为优先扫描呢?由于分配是从低地址开始,显然下一次扫描时,
从上一次分配的截止地址开始扫描成功率会更高。
hint_idx之前的空间可能由于对齐的原因仍是空闲的。
释放bootmem时会更新hint_idx的值,指向释放的位置。
仅当优先扫描失败,才需要回过头来扫描以前分配过的区域。
*/
	if (bdata->hint_idx > sidx) {
		/*
		 * Handle the valid case of sidx being zero and still
		 * catch the fallback below.
		 */

/* fallback表示首次扫描是否优先扫描 */
		fallback = sidx + 1;
		 /* 从优先扫描偏移处开始扫描 */
		sidx = align_idx(bdata, bdata->hint_idx, step);
	}

	while (1) {
		int merge;
		void *region;
		unsigned long eidx, i, start_off, end_off;

/* 从位图中找到满足对齐要求的、为0的、连续的比特位 */
find_block:

/* 从位图中找到下一个为0的比特位 */
		sidx = find_next_zero_bit(bdata->node_bootmem_map, midx, sidx);
/* 将sidx按照申请的页数对齐,对齐后的比特位不一定是0 */
		sidx = align_idx(bdata, sidx, step);
/* 计算结尾pfn */
		eidx = sidx + PFN_UP(size);

/* 如果超出了截止pfn,整个位图扫描完毕,未找到符合条件的空闲内存区,跳出循环 */
		if (sidx >= midx || eidx > midx)
			break;

/* 检查sidx和eidx之间的所有比特位是否全部为0 */
		for (i = sidx; i < eidx; i++)
			if (test_bit(i, bdata->node_bootmem_map)) {
				/* 某比特位为1,这段区间不符合条件,从下一个对齐偏移处继续扫描 */
				sidx = align_idx(bdata, i, step);

				/* 前面对齐采用的是入式对齐,如果是第一个比特位为1,
				即对齐余数为0,没有入上去,需要加上step */
				if (sidx == i)
					sidx += step;

				/* 继续扫描 */
				goto find_block;
			}

/* last_end_off表示上一次分配截止地址的偏移。
如果其不是页面大小对齐的,说明该页中还有空闲的空间。
并且如果其所在的页面就是扫描到的内存区的上一页,则可以利用该页的空闲空间。*/
		if (bdata->last_end_off & (PAGE_SIZE - 1) &&
				PFN_DOWN(bdata->last_end_off) + 1 == sidx)
			/* start_off表示本次分配的内存区起始处的地址偏移,需要按照指定方式对齐 */
			start_off = align_off(bdata, bdata->last_end_off, align);
		else
			/* 否则的话,就是从新的页面开始分配了,直接通过pfn偏移计算地址偏移 */
			start_off = PFN_PHYS(sidx);

/* 如果使用了上一页的空闲空间,该页对应的比特位无需置1,已经置过了 */
		merge = PFN_DOWN(start_off) < sidx;
/* 计算本次分配的截止地址偏移 */
		end_off = start_off + size;
/* 用本次分配的截止地址偏移更新last_end_off */
		bdata->last_end_off = end_off;
/* 更新hint_idx为last_end_off之后第一个可用的pfn,即下一次优先扫描的位置 */
		bdata->hint_idx = PFN_UP(end_off);

		/*
		 * Reserve the area now:
		 */
		 /* 将要分配出去的内存区对应的位图比特位置1,BOOTMEM_EXCLUSIVE表示此次分配是排它的 */
		if (__reserve(bdata, PFN_DOWN(start_off) + merge,
				PFN_UP(end_off), BOOTMEM_EXCLUSIVE))
			BUG();

/* 将要分配出去的内存区清0 */
		region = phys_to_virt(PFN_PHYS(bdata->node_min_pfn) +
				start_off);
		memset(region, 0, size);
		/*
		 * The min_count is set to 0 so that bootmem allocated blocks
		 * are never reported as leaks.
		 */
		  /* 内存泄露检测相关,不去管它 */
		 //空函数
		kmemleak_alloc(region, size, 0, 0);
		return region;
	}

/* 走到这说明优先扫描没有找到符合条件的空闲内存区,
扫描初始sidx(fallback记录)之前的区域 */
	if (fallback) {
		sidx = align_idx(bdata, fallback - 1, step);
		 /* 扫描一次即可 */
		fallback = 0;
		goto find_block;
	}

/* 还没有找到,分配失败 */
	return NULL;
}
先看一下函数各个形参的意义:
1)bdata:某内存节点的bootmem
2)size:分配内存的大小
3)align:对齐大小
4)goal:限定范围的起始地址
5)limit:限定范围的截止地址
再看一下传递进来的形参的值:
bdata->node_min_pfn=0x30000
bdata->node_low_pfn=0x31000
bdata->node_bootmem_map=0xc0431000
size=0x20000 
align=0x20[32] 
goal=0x6fffffff 
limit=0x0

static unsigned long align_idx(struct bootmem_data *bdata, unsigned long idx,
			unsigned long step)
{
	unsigned long base = bdata->node_min_pfn;

	/*
	 * Align the index with respect to the node start so that the
	 * combination of both satisfies the requested alignment.
	 */

	return ALIGN(base + idx, step) - base;
}

static unsigned long align_off(struct bootmem_data *bdata, unsigned long off,
			unsigned long align)
{
	unsigned long base = PFN_PHYS(bdata->node_min_pfn);

	/* Same as align_idx for byte offsets */

	return ALIGN(base + off, align) - base;
}


static void __paginginit free_area_init_core(struct pglist_data *pgdat,
		unsigned long *zones_size, unsigned long *zholes_size)
{
/*
	zones_size[0]=0x1000(4096) zones_size[1]=0
	zholes_size[0]=0                   zholes_size[1]=0

	pgdat->node_id = 0;
	pgdat->node_start_pfn = 0x30000;
	pgdat->node_spanned_pages = 0x1000(4096);
	pgdat->node_present_pages =  0x1000(4096);
*/
	enum zone_type j;
	int nid = pgdat->node_id;
	unsigned long zone_start_pfn = pgdat->node_start_pfn;
	int ret;

	//空函数
	pgdat_resize_init(pgdat);/* 初始化pgdat->node_size_lock自旋锁 */  
	pgdat->nr_zones = 0;
	init_waitqueue_head(&pgdat->kswapd_wait);/* 初始化pgdat->kswapd_wait等待队列 */
	pgdat->kswapd_max_order = 0;/* 初始化页换出守护进程创建空闲块的大小,为2^kswapd_max_order */ 
	pgdat_page_cgroup_init(pgdat);/* 空函数 */

	/*遍历每个管理区*/
	for (j = 0; j < MAX_NR_ZONES; j++) {
		struct zone *zone = pgdat->node_zones + j;
		unsigned long size, realsize, memmap_pages;
		enum lru_list l;

		/* size为该管理区中的页框数,包括洞 */
		size = zone_spanned_pages_in_node(nid, j, zones_size);
		/* realsize为管理区中的页框数,不包括洞 */
		realsize = size - zone_absent_pages_in_node(nid, j,
								zholes_size);

		/*
		 * Adjust realsize so that it accounts for how much memory
		 * is used by this zone for memmap. This affects the watermark
		 * and per-cpu initialisations
		 */
		 /* 调整realsize的大小,即减去page结构体占用的内存大小 */
		memmap_pages =    /* memmap_pags为包括洞的所有页框的page结构体所占的大小 */
			PAGE_ALIGN(size * sizeof(struct page)) >> PAGE_SHIFT;
		if (realsize >= memmap_pages) {
			realsize -= memmap_pages;
			if (memmap_pages)
				printk(KERN_DEBUG
				       "  %s zone: %lu pages used for memmap\n",
				       zone_names[j], memmap_pages);
		} else  /* 内存不够存放page结构体 */
			printk(KERN_WARNING
				"  %s zone: %lu pages exceeds realsize %lu\n",
				zone_names[j], memmap_pages, realsize);

		/* Account for reserved pages */
		/* 调整realsize的大小,即减去DMA保留页的大小 */
		if (j == 0 && realsize > dma_reserve) {
			realsize -= dma_reserve;
			printk(KERN_DEBUG "  %s zone: %lu pages reserved\n",
					zone_names[0], dma_reserve);
		}

		if (!is_highmem_idx(j))
			nr_kernel_pages += realsize;
		nr_all_pages += realsize;

		zone->spanned_pages = size;/* 设置zone->spanned_pages为包括洞的页框数 */
		zone->present_pages = realsize;/* 设置zone->present+pages为不包括洞的页框数 */
#ifdef CONFIG_NUMA
		zone->node = nid;
		zone->min_unmapped_pages = (realsize*sysctl_min_unmapped_ratio)
						/ 100;
		zone->min_slab_pages = (realsize * sysctl_min_slab_ratio) / 100;
#endif
		zone->name = zone_names[j];/* 设置zone的名称 */ 

		/* 初始化各种锁 */
		spin_lock_init(&zone->lock);
		spin_lock_init(&zone->lru_lock);
		zone_seqlock_init(zone);
		zone->zone_pgdat = pgdat;/* 设置管理区属于的节点对应的pg_data_t结构 */

		zone->prev_priority = DEF_PRIORITY;

		zone_pcp_init(zone);/* 初始化cpu的页面缓存 */
		/* 初始化lru相关成员 */
		for_each_lru(l) {
			INIT_LIST_HEAD(&zone->lru[l].list);
			zone->reclaim_stat.nr_saved_scan[l] = 0;
		}
		zone->reclaim_stat.recent_rotated[0] = 0;
		zone->reclaim_stat.recent_rotated[1] = 0;
		zone->reclaim_stat.recent_scanned[0] = 0;
		zone->reclaim_stat.recent_scanned[1] = 0;
		zap_zone_vm_stats(zone);/* 初始化zone->vm_stat为0 */
		zone->flags = 0;
		if (!size)
			continue;

		//空函数
		set_pageblock_order(pageblock_default_order());/* pageblock_default_order()返回9*/
		setup_usemap(pgdat, zone, size);


		/* 设置pgdat->nr_zones和zone->zone_start_pfn成员  
                 * 初始化zone->free_area成员 
                 * 初始化zone->wait_table相关成员  
                 */  
		ret = init_currently_empty_zone(zone, zone_start_pfn,
						size, MEMMAP_EARLY);
		BUG_ON(ret);
		memmap_init(size, nid, j, zone_start_pfn);/* 初始化该zone对应的page结构 */
		zone_start_pfn += size;					/* 调整zone_start_pfn为下一个zone的起始页面 */
	}
}

void __meminit memmap_init_zone(unsigned long size, int nid, unsigned long zone,
		unsigned long start_pfn, enum memmap_context context)
{
	struct page *page;
	unsigned long end_pfn = start_pfn + size;
	unsigned long pfn;
	struct zone *z;

	if (highest_memmap_pfn < end_pfn - 1)/* 调整最高mem_map的页面数 */
		highest_memmap_pfn = end_pfn - 1;

	z = &NODE_DATA(nid)->node_zones[zone];/* 取得zone的指针 */
	for (pfn = start_pfn; pfn < end_pfn; pfn++) {
		/*
		 * There can be holes in boot-time mem_map[]s
		 * handed to this function.  They do not
		 * exist on hotplugged memory.
		 */
		if (context == MEMMAP_EARLY) {
			if (!early_pfn_valid(pfn))
				continue;
			if (!early_pfn_in_nid(pfn, nid))
				continue;
		}
		page = pfn_to_page(pfn);/* 获得pfn对应的page结构, 此时page还未初始化 */ 
		set_page_links(page, zone, nid, pfn);/* 设置page->flags中关于zone、node、section的标志位 */
		mminit_verify_page_links(page, zone, nid, pfn);/* DEBUG用,无视之 */
		init_page_count(page);/* 设置page->_count引用计数为1 */
		reset_page_mapcount(page);/* 设置page->_mapcount为-1 */
		SetPageReserved(page);/* 无此函数 */
		/*
		 * Mark the block movable so that blocks are reserved for
		 * movable at startup. This will force kernel allocations
		 * to reserve their blocks rather than leaking throughout
		 * the address space during boot when many long-lived
		 * kernel allocations are made. Later some blocks near
		 * the start are marked MIGRATE_RESERVE by
		 * setup_zone_migrate_reserve()
		 *
		 * bitmap is created for zone's valid pfn range. but memmap
		 * can be created for invalid pages (for alignment)
		 * check here not to call set_pageblock_migratetype() against
		 * pfn out of zone.
		 */
		if ((z->zone_start_pfn <= pfn)
		    && (pfn < z->zone_start_pfn + z->spanned_pages)
		    && !(pfn & (pageblock_nr_pages - 1)))
			set_pageblock_migratetype(page, MIGRATE_MOVABLE);

		INIT_LIST_HEAD(&page->lru);/* 初始化lru链表 */
#ifdef WANT_PAGE_VIRTUAL
		/* The shift won't overflow because ZONE_NORMAL is below 4G. */
		if (!is_highmem_idx(zone))
			set_page_address(page, __va(pfn << PAGE_SHIFT));
#endif
	}
}


























评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值