假如内存的物理起始地址为: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
}
}