快车道-分配页

内核提供如下函数用于分配页:

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初始化页

总结如下就是快车道的页分配

原创文章 204 获赞 130 访问量 38万+

猜你喜欢

转载自blog.csdn.net/longwang155069/article/details/105640899