warn_alloc():page allocation failure问题分析

本文深入探讨了在Linux系统中遇到warn_alloc()提示时的内存分配问题,涉及GFP_ATOMIC、order、CMA等概念。通过分析触发warn_alloc()的情况、解析warn_alloc()的工作原理和实例解析,揭示了内存分配失败的原因,尤其是CMA区域的影响。最后,提出了内存碎片的解决方案和调整min_free_kbytes的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

关键词:warn_alloc()、__GFP_XXX、order、CMA等等。

 

在内存申请的时候经常会遇到类似“ xxx: page allocation failure: order:10...”类型的问题,这是warn_alloc()的输出。

warn_alloc()被如下函数调用:__alloc_pages_slowpath()、__vmalloc_area_node()、__vmalloc_node_range

下面分三部分了解这种问题的来龙去脉:

  • 什么情况会导致warn_alloc()?
  • warn_alloc()都做了哪些事情?
  • 结合实际问题分析问题原因。

 

1.触发warn_alloc()情况

要了什么情况下会导致warn_alloc(),就需要分析在何种情况下会被调用。

__alloc_pages_slowpath()表示页面申请进入了slowpath,那相对就有fastpath。

__alloc_pages_nodemask()中可知,这个fastpath就是get_page_from_freelist()。__alloc_pages_nodemask()是分配页面的后备选择。

static inline struct page *
__alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order,
                        struct alloc_context *ac)
{
    bool can_direct_reclaim = gfp_mask & __GFP_DIRECT_RECLAIM;
    struct page *page = NULL;
    unsigned int alloc_flags;
    unsigned long did_some_progress;
    enum compact_priority compact_priority;
    enum compact_result compact_result;
    int compaction_retries;
    int no_progress_loops;
    unsigned long alloc_start = jiffies;
    unsigned int stall_timeout = 10 * HZ;
    unsigned int cpuset_mems_cookie;

    if (order >= MAX_ORDER) {
        WARN_ON_ONCE(!(gfp_mask & __GFP_NOWARN));
        return NULL;
    }

    if (WARN_ON_ONCE((gfp_mask & (__GFP_ATOMIC|__GFP_DIRECT_RECLAIM)) ==
                (__GFP_ATOMIC|__GFP_DIRECT_RECLAIM)))
        gfp_mask &= ~__GFP_ATOMIC;

retry_cpuset:
    compaction_retries = 0;
    no_progress_loops = 0;
    compact_priority = DEF_COMPACT_PRIORITY;
    cpuset_mems_cookie = read_mems_allowed_begin();

    ac->preferred_zoneref = first_zones_zonelist(ac->zonelist,
                    ac->high_zoneidx, ac->nodemask);
    if (!ac->preferred_zoneref->zone)------------------------------------------------找不到合适的zone,进入nopage处理。
        goto nopage;

    alloc_flags = gfp_to_alloc_flags(gfp_mask);

    if (gfp_mask & __GFP_KSWAPD_RECLAIM)
        wake_all_kswapds(order, ac);

    page = get_page_from_freelist(gfp_mask, order, alloc_flags, ac);
    if (page)
        goto got_pg;

    if (can_direct_reclaim && order > PAGE_ALLOC_COSTLY_ORDER &&
        !gfp_pfmemalloc_allowed(gfp_mask)) {-----------------------------------------在定义__GFP_DIRECT_RECLAIM、__GFP_MEMALLOC并且order大于3,也即分配超过8页内存的时候。
        page = __alloc_pages_direct_compact(gfp_mask, order,
                        alloc_flags, ac,
                        INIT_COMPACT_PRIORITY,
                        &compact_result);---------------------------------------------页面较大情况下,走直接页面回收来获取内存。
        if (page)
            goto got_pg;

        if (gfp_mask & __GFP_NORETRY) {----------------------------------------------不做重试的情况。

            if (compact_result == COMPACT_DEFERRED)----------------------------------compaction不成功,进入nopage处理。
                goto nopage;

            compact_priority = INIT_COMPACT_PRIORITY;
        }
    }

retry:
    /* Ensure kswapd doesn't accidentally go to sleep as long as we loop */
    if (gfp_mask & __GFP_KSWAPD_RECLAIM)
        wake_all_kswapds(order, ac);-------------------------------------------------唤醒kswapd内核线程,让其处于工作状态。

    if (gfp_pfmemalloc_allowed(gfp_mask))
        alloc_flags = ALLOC_NO_WATERMARKS;

    if (!(alloc_flags & ALLOC_CPUSET) || (alloc_flags & ALLOC_NO_WATERMARKS)) {
        ac->zonelist = node_zonelist(numa_node_id(), gfp_mask);
        ac->preferred_zoneref = first_zones_zonelist(ac->zonelist,
                    ac->high_zoneidx, ac->nodemask);
    }

    /* Attempt with potentially adjusted zonelist and alloc_flags */
    page = get_page_from_freelist(gfp_mask, order, alloc_flags, ac);-----------------申请内存分配,成功则返回struct page地址。
    if (page)
        goto got_pg;

    /* Caller is not willing to reclaim, we can't balance anything */
    if (!can_direct_reclaim) {-------------------------------------------------------既不能内存规整direct compact,也无法从freelist获取内存的情况,进入nopage流程。

        WARN_ON_ONCE(gfp_mask & __GFP_NOFAIL);
        goto nopage;
    }

    /* Avoid recursion of direct reclaim */
    if (current->flags & PF_MEMALLOC) {

        if (WARN_ON_ONCE(gfp_mask & __GFP_NOFAIL)) {
            cond_resched();
            goto retry;
        }
        goto nopage;
    }

    /* Avoid allocations with no watermarks from looping endlessly */
    if (test_thread_flag(TIF_MEMDIE) && !(gfp_mask & __GFP_NOFAIL))
        goto nopage;


    /* Try direct reclaim and then allocating */
    page = __alloc_pages_direct_reclaim(gfp_mask, order, alloc_flags, ac,
                            &did_some_progress);
    if (page)
        goto got_pg;

    /* Try direct compaction and then allocating */
    page = __alloc_pages_direct_compact(gfp_mask, order, alloc_flags, ac,
                    compact_priority, &compact_result);
    if (page)
        goto got_pg;

    /* Do not loop if specifically requested */
    if (gfp_mask & __GFP_NORETRY)--------------------------------------------------------------强调不允许循环重试情况。
        goto nopage;

    /*
     * Do not retry costly high order allocations unless they are
     * __GFP_REPEAT
     */
    if (order > PAGE_ALLOC_COSTLY_ORDER && !(gfp_mask & __GFP_REPEAT))-------------------------针对高order情况,并且不允许__GFP_REPEAT的情况,进入nopage流程。
        goto nopage;

    /* Make sure we know about allocations which stall for too long */
    if (time_after(jiffies, alloc_start + stall_timeout)) {------------------------------------内存分配持续时间超过stall_timeout,初始为10秒,后面以10秒递增报警。
        warn_alloc(gfp_mask,
            "page allocation stalls for %ums, order:%u",
            jiffies_to_msecs(jiffies-alloc_start), order);
        stall_timeout += 10 * HZ;
    }

    if (should_reclaim_retry(gfp_mask, order, ac, alloc_flags,
                 did_some_progress > 0, &no_progress_loops))
        goto retry;

    if (did_some_progress > 0 &&
            should_compact_retry(ac, order, alloc_flags,
                compact_result, &compact_priority,
                &co
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值