快车道-分配页
Posted Loopers
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了快车道-分配页相关的知识,希望对你有一定的参考价值。
内核提供如下函数用于分配页:
alloc_pages(gfp_mask, order) //用于分配一个order阶数的页
alloc_page(gfp_mask) //用于分配一页,最终调用的是alloc_pages(gfp_mask, 0)
那我们就分析alloc_pages的具体实现
static inline struct page *alloc_pages_node(int nid, gfp_t gfp_mask,
unsigned int order)
if (nid == NUMA_NO_NODE)
nid = numa_mem_id();
return __alloc_pages_node(nid, gfp_mask, order);
- 确定当前是否是NUMA还是UMA,然后根据node去调用__alloc_pages_node
static inline struct page *__alloc_pages_node(int nid, gfp_t gfp_mask, unsigned int order)
VM_BUG_ON(nid < 0 || nid >= MAX_NUMNODES);
VM_WARN_ON((gfp_mask & __GFP_THISNODE) && !node_online(nid));
return __alloc_pages(gfp_mask, order, nid);
static inline struct page *
__alloc_pages(gfp_t gfp_mask, unsigned int order, int preferred_nid)
return __alloc_pages_nodemask(gfp_mask, order, preferred_nid, NULL);
- 确保node是在范围之类,而且此node是online的,因为node是可以热插拔的
- 确保分配的gfp不是__GFP_THISNODE
- 最终调用到__alloc_pages_nodemask
struct page * __alloc_pages_nodemask(gfp_t gfp_mask, unsigned int order, int preferred_nid, nodemask_t *nodemask)
struct page *page;
unsigned int alloc_flags = ALLOC_WMARK_LOW; //先尝试从LOW水位分配
gfp_t alloc_mask; /* The gfp_t that was actually used for allocation */
struct alloc_context ac = ;
gfp_mask &= gfp_allowed_mask;
alloc_mask = gfp_mask;
if (!prepare_alloc_pages(gfp_mask, order, preferred_nid, nodemask, &ac, &alloc_mask, &alloc_flags))
return NULL;
/* First allocation attempt */
page = get_page_from_freelist(alloc_mask, order, alloc_flags, &ac);
page = __alloc_pages_slowpath(alloc_mask, order, &ac);
return page;
- 此函数是所谓分配页的heart函数,精简下次函数,主要分为三大步骤
-
通过prepare_alloc_pages函数初始化alloc_context,确认下分配的zone,zonelist,mask,以及迁移类型
ac->high_zoneidx = gfp_zone(gfp_mask);
ac->zonelist = node_zonelist(preferred_nid, gfp_mask);
ac->nodemask = nodemask;
ac->migratetype = gfpflags_to_migratetype(gfp_mask);
- 调用get_page_from_freelist函数从LOW水位进行第一次尝试分配,如果分配成功则返回。
- 如果分配失败则进入慢车道尝试分配
- 我们将第一次尝试分配,称为快速快车道分配,本节重点看下快车道的分配
static struct page *
get_page_from_freelist(gfp_t gfp_mask, unsigned int order, int alloc_flags, const struct alloc_context *ac)
struct zoneref *z;
struct zone *zone;
struct pglist_data *last_pgdat_dirty_limit = NULL;
bool no_fallback;
retry:
/*
* Scan zonelist, looking for a zone with enough free.
* See also __cpuset_node_allowed() comment in kernel/cpuset.c.
*/
no_fallback = alloc_flags & ALLOC_NOFRAGMENT;
z = ac->preferred_zoneref;
for_next_zone_zonelist_nodemask(zone, z, ac->zonelist, ac->high_zoneidx,
ac->nodemask)
struct page *page;
unsigned long mark;
if (cpusets_enabled() &&
(alloc_flags & ALLOC_CPUSET) &&
!__cpuset_zone_allowed(zone, gfp_mask)) //cpuset判断
continue;
if (ac->spread_dirty_pages) //脏页判断
if (last_pgdat_dirty_limit == zone->zone_pgdat)
continue;
if (!node_dirty_ok(zone->zone_pgdat))
last_pgdat_dirty_limit = zone->zone_pgdat;
continue;
mark = wmark_pages(zone, alloc_flags & ALLOC_WMARK_MASK); //水位判断
if (!zone_watermark_fast(zone, order, mark,
ac_classzone_idx(ac), alloc_flags))
int ret;
/* Checked here to keep the fast path fast */
BUILD_BUG_ON(ALLOC_NO_WATERMARKS < NR_WMARK);
if (alloc_flags & ALLOC_NO_WATERMARKS) //不检查水位
goto try_this_zone;
if (node_reclaim_mode == 0 ||
!zone_allows_reclaim(ac->preferred_zoneref->zone, zone)) //不支持回收功能
continue;
ret = node_reclaim(zone->zone_pgdat, gfp_mask, order); //zone回收
switch (ret)
case NODE_RECLAIM_NOSCAN:
/* did not scan */
continue;
case NODE_RECLAIM_FULL:
/* scanned but unreclaimable */
continue;
default:
/* did we reclaim enough */
if (zone_watermark_ok(zone, order, mark,
ac_classzone_idx(ac), alloc_flags))
goto try_this_zone;
continue;
try_this_zone:
page = rmqueue(ac->preferred_zoneref->zone, zone, order, //分配一页
gfp_mask, alloc_flags, ac->migratetype);
if (page)
prep_new_page(page, order, gfp_mask, alloc_flags); //初始化一页
/*
* If this is a high-order atomic allocation then check
* if the pageblock should be reserved for the future
*/
if (unlikely(order && (alloc_flags & ALLOC_HARDER)))
reserve_highatomic_pageblock(page, zone, order);
return page;
else
return NULL;
- 遍历所有的zonelist,在其中的一个zone中找到空闲的free page
- 如果使能了cpuset,而且分配标志位页设置了cpuset,但是cpuset不允许从此zone分配,则跳过此zone
- 如果脏页的数量超过此zone的限制,则跳过此zone
- 获取LOW水位的门限值mark,如果当前zone的空闲页数小于水位的值,则会做如下的操作,具体见zone_watermark_fast函数
- 如果设置了不检查水位(ALLOC_NO_WATERMARKS)则尝试从此zone分配页
- 如果此node没开启回收页功能或者此zone不允许回收页,则跳过
- 在此node进行回收页,然后判断检查空闲页是否大于水位,大于的话则尝试从此zone分配
- 当走到try_this_zone进行page分配的话就会调用到我们之前说的buddy分配一页的流程了
- 如果分配到一页的话,则调用prep_new_page初始化页
总结如下就是快车道的页分配
以上是关于快车道-分配页的主要内容,如果未能解决你的问题,请参考以下文章