Zonned Allocator -10- (Kswapd)

노드마다 kswapd가 동작하며 평상 시에는 잠들어 있다가 페이지 할당자가 요청 order 페이지 할당을 시도하다 free 페이지가 low 워터마크 기준을 충족하지 못하는 순간 kswapd를 깨운다. kswapd는 자신의 노드에 포함된 zone에 대해 페이지 회수 및 compaction을 진행하는데 모든 zone에 대해 밸런스하게 high 워터마크 기준을 충족하게 되면 스스로 sleep 한다.

 

kswapd 초기화

kswapd_init()

mm/vmscan.c

static int __init kswapd_init(void)
{
        int nid;

        swap_setup();
        for_each_node_state(nid, N_MEMORY)
                kswapd_run(nid);
        hotcpu_notifier(cpu_callback, 0);
        return 0;
}

module_init(kswapd_init)
  • 코드 라인 05에서 kswapd 실행 전에 준비한다.
  • 코드 라인 06~07에서 모든 메모리 노드에 대해 kswapd를 실행시킨다.
  • 코드 라인 08에서 cpu 상태가 변화될 때 마다 호출될 수 있도록 cpu_callback() 함수를 우선 순위 0으로 cpu notifier block에 등록한다.

 

swap_setup()

mm/swap.c

/*
 * Perform any setup for the swap system
 */                                             
void __init swap_setup(void)
{
        unsigned long megs = totalram_pages >> (20 - PAGE_SHIFT); 
#ifdef CONFIG_SWAP
        int i;

        for (i = 0; i < MAX_SWAPFILES; i++)
                spin_lock_init(&swapper_spaces[i].tree_lock);
#endif

        /* Use a smaller cluster for small-memory machines */
        if (megs < 16)                          
                page_cluster = 2;
        else
                page_cluster = 3;
        /* 
         * Right now other parts of the system means that we
         * _really_ don't want to cluster much more
         */
}

kswapd 실행 전에 준비한다.

  • 코드 라인 10-11에서 최대 swap 파일 수 만큼 루프를 돌며 spin lock을 초기화한다.
  • 코드 라인 15~18에서 전역 total 램이 16M 이하이면 page_cluster에 2를 대입하고 그렇지 않으면 3을 대입한다.

 

kswapd_run()

mm/vmscan.c

int kswapd_run(int nid)
{
        pg_data_t *pgdat = NODE_DATA(nid);
        int ret = 0;

        if (pgdat->kswapd)
                return 0;

        pgdat->kswapd = kthread_run(kswapd, pgdat, "kswapd%d", nid);
        if (IS_ERR(pgdat->kswapd)) {
                /* failure at boot is fatal */
                BUG_ON(system_state == SYSTEM_BOOTING);
                pr_err("Failed to start kswapd on node %d\n", nid);
                ret = PTR_ERR(pgdat->kswapd);
                pgdat->kswapd = NULL;
        }
        return ret;
}

 

kswapd 동작

kswapd-1a

kswapd()

mm/vmscan.c

/*
 * The background pageout daemon, started as a kernel thread
 * from the init process.
 *
 * This basically trickles out pages so that we have _some_
 * free memory available even if there is no other activity
 * that frees anything up. This is needed for things like routing
 * etc, where we otherwise might have all activity going on in
 * asynchronous contexts that cannot page things out.
 *
 * If there are applications that are active memory-allocators
 * (most normal use), this basically shouldn't matter.
 */
static int kswapd(void *p)
{
        unsigned long order, new_order;
        unsigned balanced_order;
        int classzone_idx, new_classzone_idx;
        int balanced_classzone_idx;
        pg_data_t *pgdat = (pg_data_t*)p;
        struct task_struct *tsk = current;

        struct reclaim_state reclaim_state = {
                .reclaimed_slab = 0,
        };
        const struct cpumask *cpumask = cpumask_of_node(pgdat->node_id);

        lockdep_set_current_reclaim_state(GFP_KERNEL);

        if (!cpumask_empty(cpumask))
                set_cpus_allowed_ptr(tsk, cpumask);
        current->reclaim_state = &reclaim_state;

        /*
         * Tell the memory management that we're a "memory allocator",
         * and that if we need more memory we should get access to it
         * regardless (see "__alloc_pages()"). "kswapd" should
         * never get caught in the normal page freeing logic.
         *
         * (Kswapd normally doesn't need memory anyway, but sometimes
         * you need a small amount of memory in order to be able to
         * page out something else, and this flag essentially protects
         * us from recursively trying to free more memory as we're
         * trying to free the first piece of memory in the first place).
         */
        tsk->flags |= PF_MEMALLOC | PF_SWAPWRITE | PF_KSWAPD;
        set_freezable();

        order = new_order = 0;
        balanced_order = 0;
        classzone_idx = new_classzone_idx = pgdat->nr_zones - 1;
        balanced_classzone_idx = classzone_idx;

 

  • 코드 라인 26~31에서 요청 노드에서 동작하는 온라인 cpumask를 읽어와서 현재 태스크에 설정한다.
    • 요청 노드의 cpumask를 현재 task에 cpus_allowed 등을 설정한다.
  • 코드 라인 32에서 현재 태스크의 reclaim_state가 초기화된 전역 reclaim_state 구조체를 가리키게 한다.
  • 코드 라인 46에서 현재 태스크의 플래그에 PF_MEMALLOC, PF_SWAPWRITE 및 PF_KSWAPD를 설정한다.
    •  PF_MEMALLOC: 비상용 메모리까지 사용
    • PF_SWAPWRITE: anon 메모리에 대해 swap 기록 요청
    • PF_KSWAPD: kswapd task를 의미
  • 코드 라인 47에서 현재 태스크를 freeze할 수 있도록 PF_NOFREEZE 플래그를 제거한다.

 

        for ( ; ; ) {
                bool ret;

                /*
                 * If the last balance_pgdat was unsuccessful it's unlikely a
                 * new request of a similar or harder type will succeed soon
                 * so consider going to sleep on the basis we reclaimed at
                 */
                if (balanced_classzone_idx >= new_classzone_idx &&
                                        balanced_order == new_order) {
                        new_order = pgdat->kswapd_max_order;
                        new_classzone_idx = pgdat->classzone_idx;
                        pgdat->kswapd_max_order =  0;
                        pgdat->classzone_idx = pgdat->nr_zones - 1;
                }

                if (order < new_order || classzone_idx > new_classzone_idx) {
                        /*
                         * Don't sleep if someone wants a larger 'order'
                         * allocation or has tigher zone constraints
                         */
                        order = new_order;
                        classzone_idx = new_classzone_idx;
                } else {
                        kswapd_try_to_sleep(pgdat, balanced_order,
                                                balanced_classzone_idx);
                        order = pgdat->kswapd_max_order;
                        classzone_idx = pgdat->classzone_idx;
                        new_order = order;
                        new_classzone_idx = classzone_idx;
                        pgdat->kswapd_max_order = 0;
                        pgdat->classzone_idx = pgdat->nr_zones - 1;
                }

                ret = try_to_freeze();
                if (kthread_should_stop())
                        break;

                /*
                 * We can speed up thawing tasks if we don't call balance_pgdat
                 * after returning from the refrigerator
                 */
                if (!ret) {
                        trace_mm_vmscan_kswapd_wake(pgdat->node_id, order);
                        balanced_classzone_idx = classzone_idx;
                        balanced_order = balance_pgdat(pgdat, order,
                                                &balanced_classzone_idx);
                }
        }

        tsk->flags &= ~(PF_MEMALLOC | PF_SWAPWRITE | PF_KSWAPD);
        current->reclaim_state = NULL;
        lockdep_clear_current_reclaim_state();

        return 0;
}

각 노드에서 동작되는 kswapd 스레드는 각 노드에서 동작하는 zone에 대해 free 페이지가 low 워터마크 이하로 내려가는 경우 백그라운드에서 페이지 회수를 진행하고 high 워터마크 이상이 되는 경우 페이지 회수를 멈춘다.

  • 코드 라인 09~15에서 마지막 balance_pgdat() 함수의 처리가 실패하는 경우 유사한 유형도 성공하지 않을 것 같으므로 sleep에 들어가도록 유도한다.
    • pgdat->kswapd_max_order와 pgdat->classzone_idx 값을 임시 변수에 받은 후 새로운 요청을 받기 위해 가장 기본 값으로 초기화 한다.
    • wakeup_kswapd() 함수 내에서 kswapd를 깨우기 전에 pgdat->kswapd_max_order와 pgdat->classzone_idx 값을 update 받는다.
  • 코드 라인 17~23에서 더 큰 새로운 order 또는 zone이 요청된 경우 이 값으로 시도하려고 대입한다.
  • 코드 라인 24에서 balanced order 및 zone 값으로 free 페이지를 high 워터마크 기준을 충족하면 sleep한다.
  • 코드 라인 25~31에서 새로이 요청받은 pgdat->kswapd_max_order 와 pgdat->classzone_idx 값을 받은 후 기본 값으로 초기화하다.
  • 코드 라인 33에서 현재 태스크 kswapd에 대해 freeze 요청이 있는 경우 freeze 시도한다.
  • 코드 라인 34~35에서 현재 태스크의 KTHREAD_SHOULD_STOP 플래그 비트가 설정된 경우 루프를 탈출하고 스레드 종료 처리한다.
  • 코드 라인 41~46에서 freeze 한 적이 없었던 경우 order 값으로 페이지 회수를 진행하고 그 후 balanced 값들을 업데이트한다.
  • 코드 라인 48~52에서 kswapd 스레드의 종료 처리를 진행한다.

 

kswapd_try_to_sleep()

mm/vmscan.c

static void kswapd_try_to_sleep(pg_data_t *pgdat, int order, int classzone_idx)
{
        long remaining = 0;
        DEFINE_WAIT(wait);

        if (freezing(current) || kthread_should_stop())
                return;

        prepare_to_wait(&pgdat->kswapd_wait, &wait, TASK_INTERRUPTIBLE);

        /* Try to sleep for a short interval */
        if (prepare_kswapd_sleep(pgdat, order, remaining, classzone_idx)) {
                remaining = schedule_timeout(HZ/10);
                finish_wait(&pgdat->kswapd_wait, &wait);
                prepare_to_wait(&pgdat->kswapd_wait, &wait, TASK_INTERRUPTIBLE);
        }

        /*
         * After a short sleep, check if it was a premature sleep. If not, then
         * go fully to sleep until explicitly woken up.
         */
        if (prepare_kswapd_sleep(pgdat, order, remaining, classzone_idx)) {
                trace_mm_vmscan_kswapd_sleep(pgdat->node_id);

                /*
                 * vmstat counters are not perfectly accurate and the estimated
                 * value for counters such as NR_FREE_PAGES can deviate from the
                 * true value by nr_online_cpus * threshold. To avoid the zone
                 * watermarks being breached while under pressure, we reduce the
                 * per-cpu vmstat threshold while kswapd is awake and restore
                 * them before going back to sleep.
                 */
                set_pgdat_percpu_threshold(pgdat, calculate_normal_threshold);

                /*
                 * Compaction records what page blocks it recently failed to
                 * isolate pages from and skips them in the future scanning.
                 * When kswapd is going to sleep, it is reasonable to assume
                 * that pages and compaction may succeed so reset the cache.
                 */
                reset_isolation_suitable(pgdat);

                if (!kthread_should_stop())
                        schedule();

                set_pgdat_percpu_threshold(pgdat, calculate_pressure_threshold);
        } else {
                if (remaining)
                        count_vm_event(KSWAPD_LOW_WMARK_HIT_QUICKLY);
                else
                        count_vm_event(KSWAPD_HIGH_WMARK_HIT_QUICKLY);
        }
        finish_wait(&pgdat->kswapd_wait, &wait);
}

노드에 대해 요청 order 및 zone 까지 free 페이지가 high 워터마크 기준으로 밸런스하게 할당할 수 있는 상태라면 sleep 한다.

  • 코드 섹션 06~07에서 freeze 요청이 있는 경우 함수를 빠져나간다.
  • 코드 섹션 09에서 현재 태스크를 kswapd_wait에 추가한다.
    • 이후 sleep한 상태가 된 후 페이지 할당 함수에서 free 페이지가 low 워터마크 기준 이하로 부족해지면 wakeup을 하여 sleep함수에서 깨어나게 된다.
  • 코드 섹션 12~16에서 요청 zone 까지 그리고 요청 order에 대해 free 페이지가 밸런스된 high 워터마크 기준을 충족하면 0.1초를 sleep한다. 그 후 다시 sleep할 준비를 한다.
  • 코드 섹션 22에서 여전히 요청 zone 까지 그리고 요청 order에 대해 free 페이지가 밸런스된 high 워터마크 기준을 충족하면
  • 코드 섹션 33에서 NR_FREE_PAGES 등의 vmstat을 정밀하게 계산해야 할 때가 있으므로 이러한 경우에 사용하기 위해 각 zone에 대해 vmstat에 대한 zone별 normal한 스레졸드 기준을 cpu별로 zone->pageset->stat_threshold 에 저장한다.
  • 코드 섹션 41에서 각 zone의 full compaction이 최근에 끝난 경우에 한해 해당 zone에 대해 나중에 compaction을 처음부터 할 수 있도록 리셋한다.
    • migrate 및 free 스캐너들의 시작 위치를 초기화하고 각 페이지 블럭의 skip 비트를 0으로 클리어한다.
  • 코드 섹션 43~44에서 스레드 종료 요청이 아닌 경우 sleep한다.
  • 코드 섹션 46에서 vmstat을 위해 이번에는 pressure한 스레졸드 값을  대입한다.
  • 코드 섹션 47~52에서 밸런스드 high 워터마크 기준을 충족하지 못한 상황이다. 이 때 remain이 0이 아닌 경우 0.1초간 잠시 sleep 하는 와중에 메모리 부족을 이유로 현재 스레드인 kswapd가 깨어난 경우이므로 KSWAPD_LOW_WMARK_HIT_QUICKLY 카운터를 증가시키고 그렇지 않은 경우는 KSWAPD_HIGH_WMARK_HIT_QUICKLY 카운터를 증가시킨다. 이 두 가지 stat 카운터는 둘 다 메모리가 확보되어 잠들자마자 깨어난 경우이다.
  • 코드 섹션 53에서 kswapd_wait 에서 현재 태스크를 제거한다.

 

kswapd_try_to_sleep-1

 

밸런스 체크

pgdat_balanced()

mm/vmscan.c

/*
 * pgdat_balanced() is used when checking if a node is balanced.
 *
 * For order-0, all zones must be balanced!
 *
 * For high-order allocations only zones that meet watermarks and are in a
 * zone allowed by the callers classzone_idx are added to balanced_pages. The
 * total of balanced pages must be at least 25% of the zones allowed by
 * classzone_idx for the node to be considered balanced. Forcing all zones to
 * be balanced for high orders can cause excessive reclaim when there are
 * imbalanced zones.
 * The choice of 25% is due to
 *   o a 16M DMA zone that is balanced will not balance a zone on any
 *     reasonable sized machine
 *   o On all other machines, the top zone must be at least a reasonable
 *     percentage of the middle zones. For example, on 32-bit x86, highmem
 *     would need to be at least 256M for it to be balance a whole node.
 *     Similarly, on x86-64 the Normal zone would need to be at least 1G
 *     to balance a node on its own. These seemed like reasonable ratios.
 */
static bool pgdat_balanced(pg_data_t *pgdat, int order, int classzone_idx)
{
        unsigned long managed_pages = 0;
        unsigned long balanced_pages = 0;
        int i;

        /* Check the watermark levels */
        for (i = 0; i <= classzone_idx; i++) {
                struct zone *zone = pgdat->node_zones + i;

                if (!populated_zone(zone))
                        continue;

                managed_pages += zone->managed_pages;

                /*
                 * A special case here:
                 *
                 * balance_pgdat() skips over all_unreclaimable after
                 * DEF_PRIORITY. Effectively, it considers them balanced so
                 * they must be considered balanced here as well!
                 */
                if (!zone_reclaimable(zone)) {
                        balanced_pages += zone->managed_pages;
                        continue;
                }

                if (zone_balanced(zone, order, 0, i))
                        balanced_pages += zone->managed_pages;
                else if (!order)
                        return false;
        }

        if (order)
                return balanced_pages >= (managed_pages >> 2);
        else
                return true;
}

해당 노드에서 classzone_idx까지 요청 order로 free 페이지가 high 워터마크 기준에 맞게 밸런스하게 할당 가능한지 여부를 체크한다.

  • 0-order 요청의 경우 모든 zone에 대해 균형을 맞춰야 한다.
  • high order의 경우 할당 가능한 zone의 managed pages가 요청한 zone 까지의 managed pages의 25% 이상이어야 한다.

 

  • 코드 라인 28~32에서 하위 zone부터 요청 zone까지 루프를 돌며 요청 노드의 zone이 활성화된 zone이 아니면 skip 한다.
  • 코드 라인 34에서 각 zone의 managed_pages를 더한다.
  • 코드 라인 43~46에서 스페셜 케이스를 처리하기 위해 해당 zone이 페이지 회수가 불가능한 상태이면 balanced_pages에 zone->managed_pages를 더하고  skip 한다.
  • 코드 라인 48~51에서 요청 order로 zone이 밸런스한 경우 balanced_pages에 zone->managed_pages를 더한다. 그렇지 않으면서 요청 order가 0인 경우 false를 반환한다.
  • 코드 라인 54~57에서 order가 0보다 큰 경우 balanced_pages가 managed_pages의 25% 미만인 경우 false를 반환하고 그 이외의 경우 true를 반환한다.

 

pgdat_balanced-1

 

zone_reclaimable()

mm/vmscan.c

bool zone_reclaimable(struct zone *zone)
{
        return zone_page_state(zone, NR_PAGES_SCANNED) <
                zone_reclaimable_pages(zone) * 6;
}

요청 zone에서 회수 가능 여부를 반환한다. (true=회수 가능)

  • 코드 03~04에서 스캔한 페이지 수가 회수 가능한 페이지의 6배보다 작은 경우 더 회수할 수 있다고 판단하여 true를 반환한다.
    • free되는 페이지가 없고 메모리 부족 시 페이지 회수를 NR_PAGES_SCANNED 카운터는 계속 증가한다.

 

zone_reclaimable_pages()

mm/vmscan.c

static unsigned long zone_reclaimable_pages(struct zone *zone)
{
        int nr;

        nr = zone_page_state(zone, NR_ACTIVE_FILE) +
             zone_page_state(zone, NR_INACTIVE_FILE);

        if (get_nr_swap_pages() > 0)
                nr += zone_page_state(zone, NR_ACTIVE_ANON) +
                      zone_page_state(zone, NR_INACTIVE_ANON);

        return nr;
}

회수 가능한 페이지 수를 산출한다.

  • 코드 라인 05~06에서 lru active file 페이지 수와 lru inactive file 페이지 수를 더한다.
  • 코드 라인 08~10에서 swap 페이지가 있는 경우 lru active anon 페이지 수와 lru inactive anon 페이지 수도 더한다.

 

zone_balanced()

mm/vmscan.c

static bool zone_balanced(struct zone *zone, int order,
                          unsigned long balance_gap, int classzone_idx)
{
        if (!zone_watermark_ok_safe(zone, order, high_wmark_pages(zone) +
                                    balance_gap, classzone_idx, 0))
                return false;

        if (IS_ENABLED(CONFIG_COMPACTION) && order && compaction_suitable(zone,
                                order, 0, classzone_idx) == COMPACT_SKIPPED)
                return false;

        return true;
}

요청 zone 및 order로 high 워터마크 + 밸런스 gap 기준에 적합한지 정밀하게 체크한다. 단 high order 요청인 경우 low 워터마크 기준에 적합하지 않을 것 같으면 실패를 반환한다.

  • 코드 라인 04~06에서 요청 zone 및 order로 high 워터마크 + 밸런스 gap 기준에 적합하지 않는 경우 false를 반환한다.
  • 코드 라인 08~10에서 요청 zone에 대해 compaction하더라도 high order가 low 워터마크 기준에 적합하지 않을 것 같으면 false를 반환한다.

 

밸런스될 때까지 페이지 회수

balance_pgdat()

mm/vmscan.c

/*
 * For kswapd, balance_pgdat() will work across all this node's zones until
 * they are all at high_wmark_pages(zone).
 *
 * Returns the final order kswapd was reclaiming at
 *
 * There is special handling here for zones which are full of pinned pages.
 * This can happen if the pages are all mlocked, or if they are all used by
 * device drivers (say, ZONE_DMA).  Or if they are all in use by hugetlb.
 * What we do is to detect the case where all pages in the zone have been
 * scanned twice and there has been zero successful reclaim.  Mark the zone as
 * dead and from now on, only perform a short scan.  Basically we're polling
 * the zone for when the problem goes away.
 *
 * kswapd scans the zones in the highmem->normal->dma direction.  It skips
 * zones which have free_pages > high_wmark_pages(zone), but once a zone is
 * found to have free_pages <= high_wmark_pages(zone), we scan that zone and the
 * lower zones regardless of the number of free pages in the lower zones. This
 * interoperates with the page allocator fallback scheme to ensure that aging
 * of pages is balanced across the zones.
 */
static unsigned long balance_pgdat(pg_data_t *pgdat, int order,
                                                        int *classzone_idx)
{
        int i;
        int end_zone = 0;       /* Inclusive.  0 = ZONE_DMA */
        unsigned long nr_soft_reclaimed;
        unsigned long nr_soft_scanned;
        struct scan_control sc = {
                .gfp_mask = GFP_KERNEL,
                .order = order,
                .priority = DEF_PRIORITY,
                .may_writepage = !laptop_mode,
                .may_unmap = 1,
                .may_swap = 1,
        };
        count_vm_event(PAGEOUTRUN);

        do {
                unsigned long nr_attempted = 0;
                bool raise_priority = true;
                bool pgdat_needs_compaction = (order > 0);

                sc.nr_reclaimed = 0;

                /*
                 * Scan in the highmem->dma direction for the highest
                 * zone which needs scanning
                 */
                for (i = pgdat->nr_zones - 1; i >= 0; i--) {
                        struct zone *zone = pgdat->node_zones + i;

                        if (!populated_zone(zone))
                                continue;

                        if (sc.priority != DEF_PRIORITY &&
                            !zone_reclaimable(zone))
                                continue;

                        /*
                         * Do some background aging of the anon list, to give
                         * pages a chance to be referenced before reclaiming.
                         */
                        age_active_anon(zone, &sc);

                        /*
                         * If the number of buffer_heads in the machine
                         * exceeds the maximum allowed level and this node
                         * has a highmem zone, force kswapd to reclaim from
                         * it to relieve lowmem pressure.
                         */
                        if (buffer_heads_over_limit && is_highmem_idx(i)) {
                                end_zone = i;
                                break;
                        }

                        if (!zone_balanced(zone, order, 0, 0)) {
                                end_zone = i;
                                break;
                        } else {
                                /*
                                 * If balanced, clear the dirty and congested
                                 * flags
                                 */
                                clear_bit(ZONE_CONGESTED, &zone->flags);
                                clear_bit(ZONE_DIRTY, &zone->flags);
                        }
                }

                if (i < 0)
                        goto out;

우선 순위를 12부터 1까지 높여가며 페이지 회수 및 compaction을 진행하여 free 페이지가 요청 order 및 zone까지 밸런스하게 high 워터마크 기준을  충족할 때까지 진행한다.

 

  • 코드 라인 29~36에서 준비한 scan_control 구조체에 unmap 및 swap 할 수 있도록 enable한다. 또한 laptop 모드가 아닌 경우 writepage 기능도 enable한다.
  • 코드 라인 38~44에서 이 루틴에서 반복하는데 시도 횟수를 0, 우선 순위 상승을 가능상태, high order 인 경우 compaction 가능 및 회수 카운터를 0으로 초기화한다.
  • 코드 라인 50~53에서 가장 높은 zone 부터 낮은 zone까지 루프를 돌되 이 노드에서 활성화 되지 않은 zone은 skip 한다.
  • 코드 라인 56~58에서 우선 순위를 높여 다시 시도하는데 zone 이 회수 불가능 상태인 경우 skip 한다.
  • 코드 라인 64에서 swap이 활성화된 경우 inactive anon이 active anon보다 작을 경우 active 리스트에 대해 shrink를 수행하여 active와 inactive간의 밸런스를 다시 잡아준다.
  • 코드 라인 72~75에서 현재 처리하는 zone이 highmem이면서 시스템의 buffer_heads 수가 최대 허용 레벨을 초과하는 경우 kswapd가 강제로 lowmem 압력을 해제하도록 강제한다.
  • 코드라인 77~79에서 현재 처리하는 zone까지 요청 order로 high 워터마크 기준에 밸런스하지 않은 상태이면 이 zone을 end_zone으로 대입하고 루프를 빠져나간다.
  • 코드 라인 80~88에서 현재 zone의 플래그에 ZONE_CONGESTED와 ZONE_DIRTY를 clear하고 다음 zone 루프를 돈다.
  • 코드 라인 90~91에서 페이지를 회수할 zone을 찾지못한 경우 함수를 빠져나간다.

 

.               for (i = 0; i <= end_zone; i++) {
                        struct zone *zone = pgdat->node_zones + i;

                        if (!populated_zone(zone))
                                continue;

                        /*
                         * If any zone is currently balanced then kswapd will
                         * not call compaction as it is expected that the
                         * necessary pages are already available.
                         */
                        if (pgdat_needs_compaction &&
                                        zone_watermark_ok(zone, order,
                                                low_wmark_pages(zone),
                                                *classzone_idx, 0))
                                pgdat_needs_compaction = false;
                }

                /*
                 * If we're getting trouble reclaiming, start doing writepage
                 * even in laptop mode.
                 */
                if (sc.priority < DEF_PRIORITY - 2)
                        sc.may_writepage = 1;

                /*
                 * Now scan the zone in the dma->highmem direction, stopping
                 * at the last zone which needs scanning.
                 *
                 * We do this because the page allocator works in the opposite
                 * direction.  This prevents the page allocator from allocating
                 * pages behind kswapd's direction of progress, which would
                 * cause too much scanning of the lower zones.
                 */
                for (i = 0; i <= end_zone; i++) {
                        struct zone *zone = pgdat->node_zones + i;

                        if (!populated_zone(zone))
                                continue;

                        if (sc.priority != DEF_PRIORITY &&
                            !zone_reclaimable(zone))
                                continue;

                        sc.nr_scanned = 0;

                        nr_soft_scanned = 0;
                        /*
                         * Call soft limit reclaim before calling shrink_zone.
                         */
                        nr_soft_reclaimed = mem_cgroup_soft_limit_reclaim(zone,
                                                        order, sc.gfp_mask,
                                                        &nr_soft_scanned);
                        sc.nr_reclaimed += nr_soft_reclaimed;

                        /*
                         * There should be no need to raise the scanning
                         * priority if enough pages are already being scanned
                         * that that high watermark would be met at 100%
                         * efficiency.
                         */
                        if (kswapd_shrink_zone(zone, end_zone,
                                               &sc, &nr_attempted))
                                raise_priority = false;
                }
  • 코드 라인 01~05에서 가장 낮은 zone 부터 위 루틴에서 결정한 end_zone 까지 루프를 돌되 노드에 활성화되지 않은 zone은 skip 한다.
  • 코드 라인 12~16에서 하나의 zone이라도 free 페이지를 현재 zone에서 요청 order로 low 워터마크 기준을 만족하는 경우 compaction 하지 못하게 한다.
  • 코드 라인 23~24에서 페이지 회수 문제가 지속되는 경우 writepage 기능을 enable한다.