diff options
Diffstat (limited to 'mm/swap.c')
-rw-r--r-- | mm/swap.c | 189 |
1 files changed, 143 insertions, 46 deletions
@@ -39,6 +39,7 @@ int page_cluster; | |||
39 | 39 | ||
40 | static DEFINE_PER_CPU(struct pagevec[NR_LRU_LISTS], lru_add_pvecs); | 40 | static DEFINE_PER_CPU(struct pagevec[NR_LRU_LISTS], lru_add_pvecs); |
41 | static DEFINE_PER_CPU(struct pagevec, lru_rotate_pvecs); | 41 | static DEFINE_PER_CPU(struct pagevec, lru_rotate_pvecs); |
42 | static DEFINE_PER_CPU(struct pagevec, lru_deactivate_pvecs); | ||
42 | 43 | ||
43 | /* | 44 | /* |
44 | * This path almost never happens for VM activity - pages are normally | 45 | * This path almost never happens for VM activity - pages are normally |
@@ -178,15 +179,13 @@ void put_pages_list(struct list_head *pages) | |||
178 | } | 179 | } |
179 | EXPORT_SYMBOL(put_pages_list); | 180 | EXPORT_SYMBOL(put_pages_list); |
180 | 181 | ||
181 | /* | 182 | static void pagevec_lru_move_fn(struct pagevec *pvec, |
182 | * pagevec_move_tail() must be called with IRQ disabled. | 183 | void (*move_fn)(struct page *page, void *arg), |
183 | * Otherwise this may cause nasty races. | 184 | void *arg) |
184 | */ | ||
185 | static void pagevec_move_tail(struct pagevec *pvec) | ||
186 | { | 185 | { |
187 | int i; | 186 | int i; |
188 | int pgmoved = 0; | ||
189 | struct zone *zone = NULL; | 187 | struct zone *zone = NULL; |
188 | unsigned long flags = 0; | ||
190 | 189 | ||
191 | for (i = 0; i < pagevec_count(pvec); i++) { | 190 | for (i = 0; i < pagevec_count(pvec); i++) { |
192 | struct page *page = pvec->pages[i]; | 191 | struct page *page = pvec->pages[i]; |
@@ -194,29 +193,50 @@ static void pagevec_move_tail(struct pagevec *pvec) | |||
194 | 193 | ||
195 | if (pagezone != zone) { | 194 | if (pagezone != zone) { |
196 | if (zone) | 195 | if (zone) |
197 | spin_unlock(&zone->lru_lock); | 196 | spin_unlock_irqrestore(&zone->lru_lock, flags); |
198 | zone = pagezone; | 197 | zone = pagezone; |
199 | spin_lock(&zone->lru_lock); | 198 | spin_lock_irqsave(&zone->lru_lock, flags); |
200 | } | ||
201 | if (PageLRU(page) && !PageActive(page) && !PageUnevictable(page)) { | ||
202 | int lru = page_lru_base_type(page); | ||
203 | list_move_tail(&page->lru, &zone->lru[lru].list); | ||
204 | pgmoved++; | ||
205 | } | 199 | } |
200 | |||
201 | (*move_fn)(page, arg); | ||
206 | } | 202 | } |
207 | if (zone) | 203 | if (zone) |
208 | spin_unlock(&zone->lru_lock); | 204 | spin_unlock_irqrestore(&zone->lru_lock, flags); |
209 | __count_vm_events(PGROTATED, pgmoved); | ||
210 | release_pages(pvec->pages, pvec->nr, pvec->cold); | 205 | release_pages(pvec->pages, pvec->nr, pvec->cold); |
211 | pagevec_reinit(pvec); | 206 | pagevec_reinit(pvec); |
212 | } | 207 | } |
213 | 208 | ||
209 | static void pagevec_move_tail_fn(struct page *page, void *arg) | ||
210 | { | ||
211 | int *pgmoved = arg; | ||
212 | struct zone *zone = page_zone(page); | ||
213 | |||
214 | if (PageLRU(page) && !PageActive(page) && !PageUnevictable(page)) { | ||
215 | enum lru_list lru = page_lru_base_type(page); | ||
216 | list_move_tail(&page->lru, &zone->lru[lru].list); | ||
217 | mem_cgroup_rotate_reclaimable_page(page); | ||
218 | (*pgmoved)++; | ||
219 | } | ||
220 | } | ||
221 | |||
222 | /* | ||
223 | * pagevec_move_tail() must be called with IRQ disabled. | ||
224 | * Otherwise this may cause nasty races. | ||
225 | */ | ||
226 | static void pagevec_move_tail(struct pagevec *pvec) | ||
227 | { | ||
228 | int pgmoved = 0; | ||
229 | |||
230 | pagevec_lru_move_fn(pvec, pagevec_move_tail_fn, &pgmoved); | ||
231 | __count_vm_events(PGROTATED, pgmoved); | ||
232 | } | ||
233 | |||
214 | /* | 234 | /* |
215 | * Writeback is about to end against a page which has been marked for immediate | 235 | * Writeback is about to end against a page which has been marked for immediate |
216 | * reclaim. If it still appears to be reclaimable, move it to the tail of the | 236 | * reclaim. If it still appears to be reclaimable, move it to the tail of the |
217 | * inactive list. | 237 | * inactive list. |
218 | */ | 238 | */ |
219 | void rotate_reclaimable_page(struct page *page) | 239 | void rotate_reclaimable_page(struct page *page) |
220 | { | 240 | { |
221 | if (!PageLocked(page) && !PageDirty(page) && !PageActive(page) && | 241 | if (!PageLocked(page) && !PageDirty(page) && !PageActive(page) && |
222 | !PageUnevictable(page) && PageLRU(page)) { | 242 | !PageUnevictable(page) && PageLRU(page)) { |
@@ -347,6 +367,71 @@ void add_page_to_unevictable_list(struct page *page) | |||
347 | } | 367 | } |
348 | 368 | ||
349 | /* | 369 | /* |
370 | * If the page can not be invalidated, it is moved to the | ||
371 | * inactive list to speed up its reclaim. It is moved to the | ||
372 | * head of the list, rather than the tail, to give the flusher | ||
373 | * threads some time to write it out, as this is much more | ||
374 | * effective than the single-page writeout from reclaim. | ||
375 | * | ||
376 | * If the page isn't page_mapped and dirty/writeback, the page | ||
377 | * could reclaim asap using PG_reclaim. | ||
378 | * | ||
379 | * 1. active, mapped page -> none | ||
380 | * 2. active, dirty/writeback page -> inactive, head, PG_reclaim | ||
381 | * 3. inactive, mapped page -> none | ||
382 | * 4. inactive, dirty/writeback page -> inactive, head, PG_reclaim | ||
383 | * 5. inactive, clean -> inactive, tail | ||
384 | * 6. Others -> none | ||
385 | * | ||
386 | * In 4, why it moves inactive's head, the VM expects the page would | ||
387 | * be write it out by flusher threads as this is much more effective | ||
388 | * than the single-page writeout from reclaim. | ||
389 | */ | ||
390 | static void lru_deactivate_fn(struct page *page, void *arg) | ||
391 | { | ||
392 | int lru, file; | ||
393 | bool active; | ||
394 | struct zone *zone = page_zone(page); | ||
395 | |||
396 | if (!PageLRU(page)) | ||
397 | return; | ||
398 | |||
399 | /* Some processes are using the page */ | ||
400 | if (page_mapped(page)) | ||
401 | return; | ||
402 | |||
403 | active = PageActive(page); | ||
404 | |||
405 | file = page_is_file_cache(page); | ||
406 | lru = page_lru_base_type(page); | ||
407 | del_page_from_lru_list(zone, page, lru + active); | ||
408 | ClearPageActive(page); | ||
409 | ClearPageReferenced(page); | ||
410 | add_page_to_lru_list(zone, page, lru); | ||
411 | |||
412 | if (PageWriteback(page) || PageDirty(page)) { | ||
413 | /* | ||
414 | * PG_reclaim could be raced with end_page_writeback | ||
415 | * It can make readahead confusing. But race window | ||
416 | * is _really_ small and it's non-critical problem. | ||
417 | */ | ||
418 | SetPageReclaim(page); | ||
419 | } else { | ||
420 | /* | ||
421 | * The page's writeback ends up during pagevec | ||
422 | * We moves tha page into tail of inactive. | ||
423 | */ | ||
424 | list_move_tail(&page->lru, &zone->lru[lru].list); | ||
425 | mem_cgroup_rotate_reclaimable_page(page); | ||
426 | __count_vm_event(PGROTATED); | ||
427 | } | ||
428 | |||
429 | if (active) | ||
430 | __count_vm_event(PGDEACTIVATE); | ||
431 | update_page_reclaim_stat(zone, page, file, 0); | ||
432 | } | ||
433 | |||
434 | /* | ||
350 | * Drain pages out of the cpu's pagevecs. | 435 | * Drain pages out of the cpu's pagevecs. |
351 | * Either "cpu" is the current CPU, and preemption has already been | 436 | * Either "cpu" is the current CPU, and preemption has already been |
352 | * disabled; or "cpu" is being hot-unplugged, and is already dead. | 437 | * disabled; or "cpu" is being hot-unplugged, and is already dead. |
@@ -372,6 +457,29 @@ static void drain_cpu_pagevecs(int cpu) | |||
372 | pagevec_move_tail(pvec); | 457 | pagevec_move_tail(pvec); |
373 | local_irq_restore(flags); | 458 | local_irq_restore(flags); |
374 | } | 459 | } |
460 | |||
461 | pvec = &per_cpu(lru_deactivate_pvecs, cpu); | ||
462 | if (pagevec_count(pvec)) | ||
463 | pagevec_lru_move_fn(pvec, lru_deactivate_fn, NULL); | ||
464 | } | ||
465 | |||
466 | /** | ||
467 | * deactivate_page - forcefully deactivate a page | ||
468 | * @page: page to deactivate | ||
469 | * | ||
470 | * This function hints the VM that @page is a good reclaim candidate, | ||
471 | * for example if its invalidation fails due to the page being dirty | ||
472 | * or under writeback. | ||
473 | */ | ||
474 | void deactivate_page(struct page *page) | ||
475 | { | ||
476 | if (likely(get_page_unless_zero(page))) { | ||
477 | struct pagevec *pvec = &get_cpu_var(lru_deactivate_pvecs); | ||
478 | |||
479 | if (!pagevec_add(pvec, page)) | ||
480 | pagevec_lru_move_fn(pvec, lru_deactivate_fn, NULL); | ||
481 | put_cpu_var(lru_deactivate_pvecs); | ||
482 | } | ||
375 | } | 483 | } |
376 | 484 | ||
377 | void lru_add_drain(void) | 485 | void lru_add_drain(void) |
@@ -516,44 +624,33 @@ void lru_add_page_tail(struct zone* zone, | |||
516 | } | 624 | } |
517 | } | 625 | } |
518 | 626 | ||
627 | static void ____pagevec_lru_add_fn(struct page *page, void *arg) | ||
628 | { | ||
629 | enum lru_list lru = (enum lru_list)arg; | ||
630 | struct zone *zone = page_zone(page); | ||
631 | int file = is_file_lru(lru); | ||
632 | int active = is_active_lru(lru); | ||
633 | |||
634 | VM_BUG_ON(PageActive(page)); | ||
635 | VM_BUG_ON(PageUnevictable(page)); | ||
636 | VM_BUG_ON(PageLRU(page)); | ||
637 | |||
638 | SetPageLRU(page); | ||
639 | if (active) | ||
640 | SetPageActive(page); | ||
641 | update_page_reclaim_stat(zone, page, file, active); | ||
642 | add_page_to_lru_list(zone, page, lru); | ||
643 | } | ||
644 | |||
519 | /* | 645 | /* |
520 | * Add the passed pages to the LRU, then drop the caller's refcount | 646 | * Add the passed pages to the LRU, then drop the caller's refcount |
521 | * on them. Reinitialises the caller's pagevec. | 647 | * on them. Reinitialises the caller's pagevec. |
522 | */ | 648 | */ |
523 | void ____pagevec_lru_add(struct pagevec *pvec, enum lru_list lru) | 649 | void ____pagevec_lru_add(struct pagevec *pvec, enum lru_list lru) |
524 | { | 650 | { |
525 | int i; | ||
526 | struct zone *zone = NULL; | ||
527 | |||
528 | VM_BUG_ON(is_unevictable_lru(lru)); | 651 | VM_BUG_ON(is_unevictable_lru(lru)); |
529 | 652 | ||
530 | for (i = 0; i < pagevec_count(pvec); i++) { | 653 | pagevec_lru_move_fn(pvec, ____pagevec_lru_add_fn, (void *)lru); |
531 | struct page *page = pvec->pages[i]; | ||
532 | struct zone *pagezone = page_zone(page); | ||
533 | int file; | ||
534 | int active; | ||
535 | |||
536 | if (pagezone != zone) { | ||
537 | if (zone) | ||
538 | spin_unlock_irq(&zone->lru_lock); | ||
539 | zone = pagezone; | ||
540 | spin_lock_irq(&zone->lru_lock); | ||
541 | } | ||
542 | VM_BUG_ON(PageActive(page)); | ||
543 | VM_BUG_ON(PageUnevictable(page)); | ||
544 | VM_BUG_ON(PageLRU(page)); | ||
545 | SetPageLRU(page); | ||
546 | active = is_active_lru(lru); | ||
547 | file = is_file_lru(lru); | ||
548 | if (active) | ||
549 | SetPageActive(page); | ||
550 | update_page_reclaim_stat(zone, page, file, active); | ||
551 | add_page_to_lru_list(zone, page, lru); | ||
552 | } | ||
553 | if (zone) | ||
554 | spin_unlock_irq(&zone->lru_lock); | ||
555 | release_pages(pvec->pages, pvec->nr, pvec->cold); | ||
556 | pagevec_reinit(pvec); | ||
557 | } | 654 | } |
558 | 655 | ||
559 | EXPORT_SYMBOL(____pagevec_lru_add); | 656 | EXPORT_SYMBOL(____pagevec_lru_add); |