📄 vmscan.c
字号:
nr_taken++; scan++; break; case -EBUSY: /* else it is being freed elsewhere */ list_move(&cursor_page->lru, src); default: break; /* ! on LRU or wrong list */ } } } *scanned = scan; return nr_taken;}static unsigned long isolate_pages_global(unsigned long nr, struct list_head *dst, unsigned long *scanned, int order, int mode, struct zone *z, struct mem_cgroup *mem_cont, int active, int file){ int lru = LRU_BASE; if (active) lru += LRU_ACTIVE; if (file) lru += LRU_FILE; return isolate_lru_pages(nr, &z->lru[lru].list, dst, scanned, order, mode, !!file);}/* * clear_active_flags() is a helper for shrink_active_list(), clearing * any active bits from the pages in the list. */static unsigned long clear_active_flags(struct list_head *page_list, unsigned int *count){ int nr_active = 0; int lru; struct page *page; list_for_each_entry(page, page_list, lru) { lru = page_is_file_cache(page); if (PageActive(page)) { lru += LRU_ACTIVE; ClearPageActive(page); nr_active++; } count[lru]++; } return nr_active;}/** * isolate_lru_page - tries to isolate a page from its LRU list * @page: page to isolate from its LRU list * * Isolates a @page from an LRU list, clears PageLRU and adjusts the * vmstat statistic corresponding to whatever LRU list the page was on. * * Returns 0 if the page was removed from an LRU list. * Returns -EBUSY if the page was not on an LRU list. * * The returned page will have PageLRU() cleared. If it was found on * the active list, it will have PageActive set. If it was found on * the unevictable list, it will have the PageUnevictable bit set. That flag * may need to be cleared by the caller before letting the page go. * * The vmstat statistic corresponding to the list on which the page was * found will be decremented. * * Restrictions: * (1) Must be called with an elevated refcount on the page. This is a * fundamentnal difference from isolate_lru_pages (which is called * without a stable reference). * (2) the lru_lock must not be held. * (3) interrupts must be enabled. */int isolate_lru_page(struct page *page){ int ret = -EBUSY; if (PageLRU(page)) { struct zone *zone = page_zone(page); spin_lock_irq(&zone->lru_lock); if (PageLRU(page) && get_page_unless_zero(page)) { int lru = page_lru(page); ret = 0; ClearPageLRU(page); del_page_from_lru_list(zone, page, lru); } spin_unlock_irq(&zone->lru_lock); } return ret;}/* * shrink_inactive_list() is a helper for shrink_zone(). It returns the number * of reclaimed pages */static unsigned long shrink_inactive_list(unsigned long max_scan, struct zone *zone, struct scan_control *sc, int priority, int file){ LIST_HEAD(page_list); struct pagevec pvec; unsigned long nr_scanned = 0; unsigned long nr_reclaimed = 0; struct zone_reclaim_stat *reclaim_stat = get_reclaim_stat(zone, sc); pagevec_init(&pvec, 1); lru_add_drain(); spin_lock_irq(&zone->lru_lock); do { struct page *page; unsigned long nr_taken; unsigned long nr_scan; unsigned long nr_freed; unsigned long nr_active; unsigned int count[NR_LRU_LISTS] = { 0, }; int mode = ISOLATE_INACTIVE; /* * If we need a large contiguous chunk of memory, or have * trouble getting a small set of contiguous pages, we * will reclaim both active and inactive pages. * * We use the same threshold as pageout congestion_wait below. */ if (sc->order > PAGE_ALLOC_COSTLY_ORDER) mode = ISOLATE_BOTH; else if (sc->order && priority < DEF_PRIORITY - 2) mode = ISOLATE_BOTH; nr_taken = sc->isolate_pages(sc->swap_cluster_max, &page_list, &nr_scan, sc->order, mode, zone, sc->mem_cgroup, 0, file); nr_active = clear_active_flags(&page_list, count); __count_vm_events(PGDEACTIVATE, nr_active); __mod_zone_page_state(zone, NR_ACTIVE_FILE, -count[LRU_ACTIVE_FILE]); __mod_zone_page_state(zone, NR_INACTIVE_FILE, -count[LRU_INACTIVE_FILE]); __mod_zone_page_state(zone, NR_ACTIVE_ANON, -count[LRU_ACTIVE_ANON]); __mod_zone_page_state(zone, NR_INACTIVE_ANON, -count[LRU_INACTIVE_ANON]); if (scanning_global_lru(sc)) zone->pages_scanned += nr_scan; reclaim_stat->recent_scanned[0] += count[LRU_INACTIVE_ANON]; reclaim_stat->recent_scanned[0] += count[LRU_ACTIVE_ANON]; reclaim_stat->recent_scanned[1] += count[LRU_INACTIVE_FILE]; reclaim_stat->recent_scanned[1] += count[LRU_ACTIVE_FILE]; spin_unlock_irq(&zone->lru_lock); nr_scanned += nr_scan; nr_freed = shrink_page_list(&page_list, sc, PAGEOUT_IO_ASYNC); /* * If we are direct reclaiming for contiguous pages and we do * not reclaim everything in the list, try again and wait * for IO to complete. This will stall high-order allocations * but that should be acceptable to the caller */ if (nr_freed < nr_taken && !current_is_kswapd() && sc->order > PAGE_ALLOC_COSTLY_ORDER) { congestion_wait(WRITE, HZ/10); /* * The attempt at page out may have made some * of the pages active, mark them inactive again. */ nr_active = clear_active_flags(&page_list, count); count_vm_events(PGDEACTIVATE, nr_active); nr_freed += shrink_page_list(&page_list, sc, PAGEOUT_IO_SYNC); } nr_reclaimed += nr_freed; local_irq_disable(); if (current_is_kswapd()) { __count_zone_vm_events(PGSCAN_KSWAPD, zone, nr_scan); __count_vm_events(KSWAPD_STEAL, nr_freed); } else if (scanning_global_lru(sc)) __count_zone_vm_events(PGSCAN_DIRECT, zone, nr_scan); __count_zone_vm_events(PGSTEAL, zone, nr_freed); if (nr_taken == 0) goto done; spin_lock(&zone->lru_lock); /* * Put back any unfreeable pages. */ while (!list_empty(&page_list)) { int lru; page = lru_to_page(&page_list); VM_BUG_ON(PageLRU(page)); list_del(&page->lru); if (unlikely(!page_evictable(page, NULL))) { spin_unlock_irq(&zone->lru_lock); putback_lru_page(page); spin_lock_irq(&zone->lru_lock); continue; } SetPageLRU(page); lru = page_lru(page); add_page_to_lru_list(zone, page, lru); if (PageActive(page)) { int file = !!page_is_file_cache(page); reclaim_stat->recent_rotated[file]++; } if (!pagevec_add(&pvec, page)) { spin_unlock_irq(&zone->lru_lock); __pagevec_release(&pvec); spin_lock_irq(&zone->lru_lock); } } } while (nr_scanned < max_scan); spin_unlock(&zone->lru_lock);done: local_irq_enable(); pagevec_release(&pvec); return nr_reclaimed;}/* * We are about to scan this zone at a certain priority level. If that priority * level is smaller (ie: more urgent) than the previous priority, then note * that priority level within the zone. This is done so that when the next * process comes in to scan this zone, it will immediately start out at this * priority level rather than having to build up its own scanning priority. * Here, this priority affects only the reclaim-mapped threshold. */static inline void note_zone_scanning_priority(struct zone *zone, int priority){ if (priority < zone->prev_priority) zone->prev_priority = priority;}/* * This moves pages from the active list to the inactive list. * * We move them the other way if the page is referenced by one or more * processes, from rmap. * * If the pages are mostly unmapped, the processing is fast and it is * appropriate to hold zone->lru_lock across the whole operation. But if * the pages are mapped, the processing is slow (page_referenced()) so we * should drop zone->lru_lock around each page. It's impossible to balance * this, so instead we remove the pages from the LRU while processing them. * It is safe to rely on PG_active against the non-LRU pages in here because * nobody will play with that bit on a non-LRU page. * * The downside is that we have to touch page->_count against each page. * But we had to alter page->flags anyway. */static void shrink_active_list(unsigned long nr_pages, struct zone *zone, struct scan_control *sc, int priority, int file){ unsigned long pgmoved; int pgdeactivate = 0; unsigned long pgscanned; LIST_HEAD(l_hold); /* The pages which were snipped off */ LIST_HEAD(l_inactive); struct page *page; struct pagevec pvec; enum lru_list lru; struct zone_reclaim_stat *reclaim_stat = get_reclaim_stat(zone, sc); 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, file); /* * zone->pages_scanned is used for detect zone's oom * mem_cgroup remembers nr_scan by itself. */ if (scanning_global_lru(sc)) { zone->pages_scanned += pgscanned; } reclaim_stat->recent_scanned[!!file] += pgmoved; if (file) __mod_zone_page_state(zone, NR_ACTIVE_FILE, -pgmoved); else __mod_zone_page_state(zone, NR_ACTIVE_ANON, -pgmoved); spin_unlock_irq(&zone->lru_lock); pgmoved = 0; while (!list_empty(&l_hold)) { cond_resched(); page = lru_to_page(&l_hold); list_del(&page->lru); if (unlikely(!page_evictable(page, NULL))) { putback_lru_page(page); continue; } /* page_referenced clears PageReferenced */ if (page_mapping_inuse(page) && page_referenced(page, 0, sc->mem_cgroup)) pgmoved++; list_add(&page->lru, &l_inactive); } /* * Move the pages to the [file or anon] inactive list. */ pagevec_init(&pvec, 1); lru = LRU_BASE + file * LRU_FILE; spin_lock_irq(&zone->lru_lock); /* * Count referenced pages from currently used mappings as * rotated, even though they are moved to the inactive list. * This helps balance scan pressure between file and anonymous * pages in get_scan_ratio. */ reclaim_stat->recent_rotated[!!file] += pgmoved; pgmoved = 0; 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->lru[lru].list); mem_cgroup_add_lru_list(page, lru); pgmoved++; if (!pagevec_add(&pvec, page)) { __mod_zone_page_state(zone, NR_LRU_BASE + lru, 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_LRU_BASE + lru, pgmoved); pgdeactivate += pgmoved; if (buffer_heads_over_limit) { spin_unlock_irq(&zone->lru_lock); pagevec_strip(&pvec); spin_lock_irq(&zone->lru_lock); } __count_zone_vm_events(PGREFILL, zone, pgscanned); __count_vm_events(PGDEACTIVATE, pgdeactivate); spin_unlock_irq(&zone->lru_lock); if (vm_swap_full()) pagevec_swap_free(&pvec); pagevec_release(&pvec);}static int inactive_anon_is_low_global(struct zone *zone){ unsigned long active, inactive; active = zone_page_state(zone, NR_ACTIVE_ANON); inactive = zone_page_state(zone, NR_INACTIVE_ANON); if (inactive * zone->inactive_ratio < active) return 1; return 0;}/** * inactive_anon_is_low - check if anonymous pages need to be deactivated * @zone: zone to check * @sc: scan control of this context * * Returns true if the zone does not have enough inactive anon pages, * meaning some active anon pages need to be deactivated. */static int inactive_anon_is_low(struct zone *zone, struct scan_control *sc){ int low; if (scanning_global_lru(sc)) low = inactive_anon_is_low_global(zone); else low = mem_cgroup_inactive_anon_is_low(sc->mem_cgroup); return low;}static unsigned long shrink_list(enum lru_list lru, unsigned long nr_to_scan, struct zone *zone, struct scan_control *sc, int priority){ int file = is_file_lru(lru); if (lru == LRU_ACTIVE_FILE) { shrink_active_list(nr_to_scan, zone, sc, priority, file); return 0; } if (lru == LRU_ACTIVE_ANON && inactive_anon_is_low(zone, sc)) { shrink_active_list(nr_to_scan, zone, sc, priority, file); return 0; } return shrink_inactive_list(nr_to_scan, zone, sc, priority, file);}/* * Determine how aggressively the anon and file LRU lists should be * scanned. The relative value of each set of LRU lists is determined * by looking at the fraction of the pages scanned we did rotate back * onto the active list instead of evict. * * percent[0] specifies how much pressure to put on ram/swap backed * memory, while percent[1] determines pressure on the file LRUs. */static void get_scan_ratio(struct zone *zone, struct scan_control *sc, unsigned long *percent){ unsigned long anon, file, free; unsigned long anon_prio, file_prio; unsigned long ap, fp; struct zone_reclaim_stat *reclaim_stat = get_reclaim_stat(zone, sc); /* If we have no swap space, do not bother scanning anon pages. */ if (nr_swap_pages <= 0) { percent[0] = 0; percent[1] = 100; return; } anon = zone_nr_pages(zone, sc, LRU_ACTIVE_ANON) + zone_nr_pages(zone, sc, LRU_INACTIVE_ANON); file = zone_nr_pages(zone, sc, LRU_ACTIVE_FILE) + zone_nr_pages(zone, sc, LRU_INACTIVE_FILE); if (scanning_global_lru(sc)) { free = zone_page_state(zone, NR_FREE_PAGES); /* If we have very few page cache pages, force-scan anon pages. */ if (unlikely(file + free <= zone->pages_high)) { percent[0] = 100; percent[1] = 0; return; } } /*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -