activeリストの回収
Rev.1を表示中。最新版はこちら。
shrink_active_list関数はactiveリストを回収します。処理の概要はactiveリストから所定のpageを抜き出します。そしてそのページをマップされているページかどうかで、activeリストかinactiveリストかに振り分けます。これらのリストはワークリスト下で行います。そしてこのactiveリストをzoneのactiveリストへ、inactiveリストをzoneのinactiveリストへ繋ぎ換えます。if (sc->may_swap)はマップpageも回収する場合、設定されています。sc->may_swap=1で回収処理チェックcalc_reclaim_mapped関数が呼び出されます。マップされているpageを回収するにはスワップが発生するためできるものなら避けたいものです。なお回収するならreclaim_mapped=1となります。
lru_add_drain関数でまずCPU変数下のpageをLRUリストに繋いだ後、isolate_pages関数でactiveリストから、所定のページ数をl_hold下に抜き出します。
while (!list_empty(&l_hold))で抜き出したpageのactive/inactiveリストへの振るい分けです。page = lru_to_page(&l_hold)でpageを抜き出して、list_del関数でそのpageをl_holdリストから外します。そしてpageがマップされているページなら、回収すべきかどうかの処理が行われます。回収すべきでないならactiveリスト候補に繋がれます。
回収すべきでない条件は、reclaim_mappedによるもの。(スワップ適性)/無名pageでスワップ領域が無い/pageのreferenceビットがセットされている。場合です。それ以外がinactiveリスト候補に繋がれます。
次にwhile (!list_empty(&l_inactive))で先に振り分けたinactive候補を、zoneのinactiveリストに繋ぎます。page = lru_to_page(&l_inactive)でpageを抜き出して、SetPageLRU(page)/ClearPageActive(page)でこのpageがLRUのinactiveリストに繋がっている旨の設定をした後、list_move(&page->lru, &zone->inactive_list)で実際にzoneのinactive_listに繋いでいます。
if (!pagevec_add(&pvec, page))についてはよく分かりません。なんかpageがもし参照カウンターが0なら、ページをCPU変数下のcoldリストに返しているようです。なお、while (!list_empty(&l_active))以降はzoneのiactiveリストに繋ぐ処理で、上と同じような処理が行われています。
回りくどい処理のようですが、たぶんスピンロック取得の処理のが必要のため、ワークリストを介してできるだけ効率よくリスト処理を行うためのテクニックかと・・・了解しています。
static void shrink_active_list(unsigned long nr_pages, struct zone *zone, struct scan_control *sc, int priority) { : : if (sc->may_swap) reclaim_mapped = calc_reclaim_mapped(sc, zone, priority); lru_add_drain(); spin_lock_irq(&zone->lru_lock); pgmoved = sc->isolate_pages(nr_pages, &l_hold, &pgscanned, sc->order, ISOLATE_ACTIVE, zone, sc->mem_cgroup, 1); if (scan_global_lru(sc)) zone->pages_scanned += pgscanned; __mod_zone_page_state(zone, NR_ACTIVE, -pgmoved); spin_unlock_irq(&zone->lru_lock); while (!list_empty(&l_hold)) { cond_resched(); page = lru_to_page(&l_hold); list_del(&page->lru); if (page_mapped(page)) { if (!reclaim_mapped || (total_swap_pages == 0 && PageAnon(page)) || page_referenced(page, 0, sc->mem_cgroup)) { list_add(&page->lru, &l_active); continue; } } list_add(&page->lru, &l_inactive); } pagevec_init(&pvec, 1); pgmoved = 0; spin_lock_irq(&zone->lru_lock); while (!list_empty(&l_inactive)) { page = lru_to_page(&l_inactive); prefetchw_prev_lru_page(page, &l_inactive, flags); VM_BUG_ON(PageLRU(page)); SetPageLRU(page); VM_BUG_ON(!PageActive(page)); ClearPageActive(page); list_move(&page->lru, &zone->inactive_list); mem_cgroup_move_lists(page, false); pgmoved++; if (!pagevec_add(&pvec, page)) { __mod_zone_page_state(zone, NR_INACTIVE, pgmoved); spin_unlock_irq(&zone->lru_lock); pgdeactivate += pgmoved; pgmoved = 0; if (buffer_heads_over_limit) pagevec_strip(&pvec); __pagevec_release(&pvec); spin_lock_irq(&zone->lru_lock); } } __mod_zone_page_state(zone, NR_INACTIVE, pgmoved); pgdeactivate += pgmoved; if (buffer_heads_over_limit) { spin_unlock_irq(&zone->lru_lock); pagevec_strip(&pvec); spin_lock_irq(&zone->lru_lock); } pgmoved = 0; while (!list_empty(&l_active)) { page = lru_to_page(&l_active); prefetchw_prev_lru_page(page, &l_active, flags); VM_BUG_ON(PageLRU(page)); SetPageLRU(page); VM_BUG_ON(!PageActive(page)); ClearPageActive(page); list_move(&page->lru, &zone->active_list); mem_cgroup_move_lists(page, true); pgmoved++; if (!pagevec_add(&pvec, page)) { __mod_zone_page_state(zone, NR_ACTIVE, pgmoved); pgmoved = 0; spin_unlock_irq(&zone->lru_lock); __pagevec_release(&pvec); spin_lock_irq(&zone->lru_lock); } } __mod_zone_page_state(zone, NR_ACTIVE, pgmoved); __count_zone_vm_events(PGREFILL, zone, pgscanned); __count_vm_events(PGDEACTIVATE, pgdeactivate); spin_unlock_irq(&zone->lru_lock); pagevec_release(&pvec); }calc_reclaim_mapped関数でマップページを回収とするか判断します。その指針としてswap_tendency = mapped_ratio / 2 + distress + sc->swappinessという物です。mapped_ratioはマップページ+無名ページの全体の割合です。この割合が多い程回収対象となります。次にistress = 100 >> min(prev_priority, priority)です。priorityはループで12から0として呼ばれるものでした。distressが大きいほどなかなか回収できないことを意味します。sc->swappiness=60で回収時呼ばれています。
そしてimbalanceとしてactiveリスト/inactiveリストの割合です。activeリストがinactiveリストより多いいとき回収しやすくなります(細かい計算式については無視)。なおinactiveリストの回収では、マップpageについて回収すべきかどうかは考慮していないからです。
static int calc_reclaim_mapped(struct scan_control *sc, struct zone *zone, int priority) { : : if (scan_global_lru(sc)) prev_priority = zone->prev_priority; else prev_priority = mem_cgroup_get_reclaim_priority(sc->mem_cgroup); distress = 100 >> min(prev_priority, priority); if (scan_global_lru(sc)) mapped_ratio = ((global_page_state(NR_FILE_MAPPED) + global_page_state(NR_ANON_PAGES)) * 100) / vm_total_pages; else mapped_ratio = mem_cgroup_calc_mapped_ratio(sc->mem_cgroup); swap_tendency = mapped_ratio / 2 + distress + sc->swappiness; if (scan_global_lru(sc)) { imbalance = zone_page_state(zone, NR_ACTIVE); imbalance /= zone_page_state(zone, NR_INACTIVE) + 1; } else imbalance = mem_cgroup_reclaim_imbalance(sc->mem_cgroup); imbalance *= (vm_swappiness + 1); imbalance /= 100; imbalance *= mapped_ratio; imbalance /= 100; swap_tendency += imbalance; if (swap_tendency >= 100) reclaim_mapped = 1; return reclaim_mapped; }