diff options
Diffstat (limited to 'mm/swap.c')
-rw-r--r-- | mm/swap.c | 129 |
1 files changed, 78 insertions, 51 deletions
@@ -47,13 +47,15 @@ static DEFINE_PER_CPU(struct pagevec, lru_deactivate_pvecs); | |||
47 | static void __page_cache_release(struct page *page) | 47 | static void __page_cache_release(struct page *page) |
48 | { | 48 | { |
49 | if (PageLRU(page)) { | 49 | if (PageLRU(page)) { |
50 | unsigned long flags; | ||
51 | struct zone *zone = page_zone(page); | 50 | struct zone *zone = page_zone(page); |
51 | struct lruvec *lruvec; | ||
52 | unsigned long flags; | ||
52 | 53 | ||
53 | spin_lock_irqsave(&zone->lru_lock, flags); | 54 | spin_lock_irqsave(&zone->lru_lock, flags); |
55 | lruvec = mem_cgroup_page_lruvec(page, zone); | ||
54 | VM_BUG_ON(!PageLRU(page)); | 56 | VM_BUG_ON(!PageLRU(page)); |
55 | __ClearPageLRU(page); | 57 | __ClearPageLRU(page); |
56 | del_page_from_lru_list(zone, page, page_off_lru(page)); | 58 | del_page_from_lru_list(page, lruvec, page_off_lru(page)); |
57 | spin_unlock_irqrestore(&zone->lru_lock, flags); | 59 | spin_unlock_irqrestore(&zone->lru_lock, flags); |
58 | } | 60 | } |
59 | } | 61 | } |
@@ -82,6 +84,25 @@ static void put_compound_page(struct page *page) | |||
82 | if (likely(page != page_head && | 84 | if (likely(page != page_head && |
83 | get_page_unless_zero(page_head))) { | 85 | get_page_unless_zero(page_head))) { |
84 | unsigned long flags; | 86 | unsigned long flags; |
87 | |||
88 | /* | ||
89 | * THP can not break up slab pages so avoid taking | ||
90 | * compound_lock(). Slab performs non-atomic bit ops | ||
91 | * on page->flags for better performance. In particular | ||
92 | * slab_unlock() in slub used to be a hot path. It is | ||
93 | * still hot on arches that do not support | ||
94 | * this_cpu_cmpxchg_double(). | ||
95 | */ | ||
96 | if (PageSlab(page_head)) { | ||
97 | if (PageTail(page)) { | ||
98 | if (put_page_testzero(page_head)) | ||
99 | VM_BUG_ON(1); | ||
100 | |||
101 | atomic_dec(&page->_mapcount); | ||
102 | goto skip_lock_tail; | ||
103 | } else | ||
104 | goto skip_lock; | ||
105 | } | ||
85 | /* | 106 | /* |
86 | * page_head wasn't a dangling pointer but it | 107 | * page_head wasn't a dangling pointer but it |
87 | * may not be a head page anymore by the time | 108 | * may not be a head page anymore by the time |
@@ -92,10 +113,10 @@ static void put_compound_page(struct page *page) | |||
92 | if (unlikely(!PageTail(page))) { | 113 | if (unlikely(!PageTail(page))) { |
93 | /* __split_huge_page_refcount run before us */ | 114 | /* __split_huge_page_refcount run before us */ |
94 | compound_unlock_irqrestore(page_head, flags); | 115 | compound_unlock_irqrestore(page_head, flags); |
95 | VM_BUG_ON(PageHead(page_head)); | 116 | skip_lock: |
96 | if (put_page_testzero(page_head)) | 117 | if (put_page_testzero(page_head)) |
97 | __put_single_page(page_head); | 118 | __put_single_page(page_head); |
98 | out_put_single: | 119 | out_put_single: |
99 | if (put_page_testzero(page)) | 120 | if (put_page_testzero(page)) |
100 | __put_single_page(page); | 121 | __put_single_page(page); |
101 | return; | 122 | return; |
@@ -115,6 +136,8 @@ static void put_compound_page(struct page *page) | |||
115 | VM_BUG_ON(atomic_read(&page_head->_count) <= 0); | 136 | VM_BUG_ON(atomic_read(&page_head->_count) <= 0); |
116 | VM_BUG_ON(atomic_read(&page->_count) != 0); | 137 | VM_BUG_ON(atomic_read(&page->_count) != 0); |
117 | compound_unlock_irqrestore(page_head, flags); | 138 | compound_unlock_irqrestore(page_head, flags); |
139 | |||
140 | skip_lock_tail: | ||
118 | if (put_page_testzero(page_head)) { | 141 | if (put_page_testzero(page_head)) { |
119 | if (PageHead(page_head)) | 142 | if (PageHead(page_head)) |
120 | __put_compound_page(page_head); | 143 | __put_compound_page(page_head); |
@@ -162,6 +185,18 @@ bool __get_page_tail(struct page *page) | |||
162 | struct page *page_head = compound_trans_head(page); | 185 | struct page *page_head = compound_trans_head(page); |
163 | 186 | ||
164 | if (likely(page != page_head && get_page_unless_zero(page_head))) { | 187 | if (likely(page != page_head && get_page_unless_zero(page_head))) { |
188 | |||
189 | /* Ref to put_compound_page() comment. */ | ||
190 | if (PageSlab(page_head)) { | ||
191 | if (likely(PageTail(page))) { | ||
192 | __get_page_tail_foll(page, false); | ||
193 | return true; | ||
194 | } else { | ||
195 | put_page(page_head); | ||
196 | return false; | ||
197 | } | ||
198 | } | ||
199 | |||
165 | /* | 200 | /* |
166 | * page_head wasn't a dangling pointer but it | 201 | * page_head wasn't a dangling pointer but it |
167 | * may not be a head page anymore by the time | 202 | * may not be a head page anymore by the time |
@@ -202,11 +237,12 @@ void put_pages_list(struct list_head *pages) | |||
202 | EXPORT_SYMBOL(put_pages_list); | 237 | EXPORT_SYMBOL(put_pages_list); |
203 | 238 | ||
204 | static void pagevec_lru_move_fn(struct pagevec *pvec, | 239 | static void pagevec_lru_move_fn(struct pagevec *pvec, |
205 | void (*move_fn)(struct page *page, void *arg), | 240 | void (*move_fn)(struct page *page, struct lruvec *lruvec, void *arg), |
206 | void *arg) | 241 | void *arg) |
207 | { | 242 | { |
208 | int i; | 243 | int i; |
209 | struct zone *zone = NULL; | 244 | struct zone *zone = NULL; |
245 | struct lruvec *lruvec; | ||
210 | unsigned long flags = 0; | 246 | unsigned long flags = 0; |
211 | 247 | ||
212 | for (i = 0; i < pagevec_count(pvec); i++) { | 248 | for (i = 0; i < pagevec_count(pvec); i++) { |
@@ -220,7 +256,8 @@ static void pagevec_lru_move_fn(struct pagevec *pvec, | |||
220 | spin_lock_irqsave(&zone->lru_lock, flags); | 256 | spin_lock_irqsave(&zone->lru_lock, flags); |
221 | } | 257 | } |
222 | 258 | ||
223 | (*move_fn)(page, arg); | 259 | lruvec = mem_cgroup_page_lruvec(page, zone); |
260 | (*move_fn)(page, lruvec, arg); | ||
224 | } | 261 | } |
225 | if (zone) | 262 | if (zone) |
226 | spin_unlock_irqrestore(&zone->lru_lock, flags); | 263 | spin_unlock_irqrestore(&zone->lru_lock, flags); |
@@ -228,16 +265,13 @@ static void pagevec_lru_move_fn(struct pagevec *pvec, | |||
228 | pagevec_reinit(pvec); | 265 | pagevec_reinit(pvec); |
229 | } | 266 | } |
230 | 267 | ||
231 | static void pagevec_move_tail_fn(struct page *page, void *arg) | 268 | static void pagevec_move_tail_fn(struct page *page, struct lruvec *lruvec, |
269 | void *arg) | ||
232 | { | 270 | { |
233 | int *pgmoved = arg; | 271 | int *pgmoved = arg; |
234 | 272 | ||
235 | if (PageLRU(page) && !PageActive(page) && !PageUnevictable(page)) { | 273 | if (PageLRU(page) && !PageActive(page) && !PageUnevictable(page)) { |
236 | enum lru_list lru = page_lru_base_type(page); | 274 | enum lru_list lru = page_lru_base_type(page); |
237 | struct lruvec *lruvec; | ||
238 | |||
239 | lruvec = mem_cgroup_lru_move_lists(page_zone(page), | ||
240 | page, lru, lru); | ||
241 | list_move_tail(&page->lru, &lruvec->lists[lru]); | 275 | list_move_tail(&page->lru, &lruvec->lists[lru]); |
242 | (*pgmoved)++; | 276 | (*pgmoved)++; |
243 | } | 277 | } |
@@ -276,41 +310,30 @@ void rotate_reclaimable_page(struct page *page) | |||
276 | } | 310 | } |
277 | } | 311 | } |
278 | 312 | ||
279 | static void update_page_reclaim_stat(struct zone *zone, struct page *page, | 313 | static void update_page_reclaim_stat(struct lruvec *lruvec, |
280 | int file, int rotated) | 314 | int file, int rotated) |
281 | { | 315 | { |
282 | struct zone_reclaim_stat *reclaim_stat = &zone->reclaim_stat; | 316 | struct zone_reclaim_stat *reclaim_stat = &lruvec->reclaim_stat; |
283 | struct zone_reclaim_stat *memcg_reclaim_stat; | ||
284 | |||
285 | memcg_reclaim_stat = mem_cgroup_get_reclaim_stat_from_page(page); | ||
286 | 317 | ||
287 | reclaim_stat->recent_scanned[file]++; | 318 | reclaim_stat->recent_scanned[file]++; |
288 | if (rotated) | 319 | if (rotated) |
289 | reclaim_stat->recent_rotated[file]++; | 320 | reclaim_stat->recent_rotated[file]++; |
290 | |||
291 | if (!memcg_reclaim_stat) | ||
292 | return; | ||
293 | |||
294 | memcg_reclaim_stat->recent_scanned[file]++; | ||
295 | if (rotated) | ||
296 | memcg_reclaim_stat->recent_rotated[file]++; | ||
297 | } | 321 | } |
298 | 322 | ||
299 | static void __activate_page(struct page *page, void *arg) | 323 | static void __activate_page(struct page *page, struct lruvec *lruvec, |
324 | void *arg) | ||
300 | { | 325 | { |
301 | struct zone *zone = page_zone(page); | ||
302 | |||
303 | if (PageLRU(page) && !PageActive(page) && !PageUnevictable(page)) { | 326 | if (PageLRU(page) && !PageActive(page) && !PageUnevictable(page)) { |
304 | int file = page_is_file_cache(page); | 327 | int file = page_is_file_cache(page); |
305 | int lru = page_lru_base_type(page); | 328 | int lru = page_lru_base_type(page); |
306 | del_page_from_lru_list(zone, page, lru); | ||
307 | 329 | ||
330 | del_page_from_lru_list(page, lruvec, lru); | ||
308 | SetPageActive(page); | 331 | SetPageActive(page); |
309 | lru += LRU_ACTIVE; | 332 | lru += LRU_ACTIVE; |
310 | add_page_to_lru_list(zone, page, lru); | 333 | add_page_to_lru_list(page, lruvec, lru); |
311 | __count_vm_event(PGACTIVATE); | ||
312 | 334 | ||
313 | update_page_reclaim_stat(zone, page, file, 1); | 335 | __count_vm_event(PGACTIVATE); |
336 | update_page_reclaim_stat(lruvec, file, 1); | ||
314 | } | 337 | } |
315 | } | 338 | } |
316 | 339 | ||
@@ -347,7 +370,7 @@ void activate_page(struct page *page) | |||
347 | struct zone *zone = page_zone(page); | 370 | struct zone *zone = page_zone(page); |
348 | 371 | ||
349 | spin_lock_irq(&zone->lru_lock); | 372 | spin_lock_irq(&zone->lru_lock); |
350 | __activate_page(page, NULL); | 373 | __activate_page(page, mem_cgroup_page_lruvec(page, zone), NULL); |
351 | spin_unlock_irq(&zone->lru_lock); | 374 | spin_unlock_irq(&zone->lru_lock); |
352 | } | 375 | } |
353 | #endif | 376 | #endif |
@@ -414,11 +437,13 @@ void lru_cache_add_lru(struct page *page, enum lru_list lru) | |||
414 | void add_page_to_unevictable_list(struct page *page) | 437 | void add_page_to_unevictable_list(struct page *page) |
415 | { | 438 | { |
416 | struct zone *zone = page_zone(page); | 439 | struct zone *zone = page_zone(page); |
440 | struct lruvec *lruvec; | ||
417 | 441 | ||
418 | spin_lock_irq(&zone->lru_lock); | 442 | spin_lock_irq(&zone->lru_lock); |
443 | lruvec = mem_cgroup_page_lruvec(page, zone); | ||
419 | SetPageUnevictable(page); | 444 | SetPageUnevictable(page); |
420 | SetPageLRU(page); | 445 | SetPageLRU(page); |
421 | add_page_to_lru_list(zone, page, LRU_UNEVICTABLE); | 446 | add_page_to_lru_list(page, lruvec, LRU_UNEVICTABLE); |
422 | spin_unlock_irq(&zone->lru_lock); | 447 | spin_unlock_irq(&zone->lru_lock); |
423 | } | 448 | } |
424 | 449 | ||
@@ -443,11 +468,11 @@ void add_page_to_unevictable_list(struct page *page) | |||
443 | * be write it out by flusher threads as this is much more effective | 468 | * be write it out by flusher threads as this is much more effective |
444 | * than the single-page writeout from reclaim. | 469 | * than the single-page writeout from reclaim. |
445 | */ | 470 | */ |
446 | static void lru_deactivate_fn(struct page *page, void *arg) | 471 | static void lru_deactivate_fn(struct page *page, struct lruvec *lruvec, |
472 | void *arg) | ||
447 | { | 473 | { |
448 | int lru, file; | 474 | int lru, file; |
449 | bool active; | 475 | bool active; |
450 | struct zone *zone = page_zone(page); | ||
451 | 476 | ||
452 | if (!PageLRU(page)) | 477 | if (!PageLRU(page)) |
453 | return; | 478 | return; |
@@ -460,13 +485,13 @@ static void lru_deactivate_fn(struct page *page, void *arg) | |||
460 | return; | 485 | return; |
461 | 486 | ||
462 | active = PageActive(page); | 487 | active = PageActive(page); |
463 | |||
464 | file = page_is_file_cache(page); | 488 | file = page_is_file_cache(page); |
465 | lru = page_lru_base_type(page); | 489 | lru = page_lru_base_type(page); |
466 | del_page_from_lru_list(zone, page, lru + active); | 490 | |
491 | del_page_from_lru_list(page, lruvec, lru + active); | ||
467 | ClearPageActive(page); | 492 | ClearPageActive(page); |
468 | ClearPageReferenced(page); | 493 | ClearPageReferenced(page); |
469 | add_page_to_lru_list(zone, page, lru); | 494 | add_page_to_lru_list(page, lruvec, lru); |
470 | 495 | ||
471 | if (PageWriteback(page) || PageDirty(page)) { | 496 | if (PageWriteback(page) || PageDirty(page)) { |
472 | /* | 497 | /* |
@@ -476,19 +501,17 @@ static void lru_deactivate_fn(struct page *page, void *arg) | |||
476 | */ | 501 | */ |
477 | SetPageReclaim(page); | 502 | SetPageReclaim(page); |
478 | } else { | 503 | } else { |
479 | struct lruvec *lruvec; | ||
480 | /* | 504 | /* |
481 | * The page's writeback ends up during pagevec | 505 | * The page's writeback ends up during pagevec |
482 | * We moves tha page into tail of inactive. | 506 | * We moves tha page into tail of inactive. |
483 | */ | 507 | */ |
484 | lruvec = mem_cgroup_lru_move_lists(zone, page, lru, lru); | ||
485 | list_move_tail(&page->lru, &lruvec->lists[lru]); | 508 | list_move_tail(&page->lru, &lruvec->lists[lru]); |
486 | __count_vm_event(PGROTATED); | 509 | __count_vm_event(PGROTATED); |
487 | } | 510 | } |
488 | 511 | ||
489 | if (active) | 512 | if (active) |
490 | __count_vm_event(PGDEACTIVATE); | 513 | __count_vm_event(PGDEACTIVATE); |
491 | update_page_reclaim_stat(zone, page, file, 0); | 514 | update_page_reclaim_stat(lruvec, file, 0); |
492 | } | 515 | } |
493 | 516 | ||
494 | /* | 517 | /* |
@@ -588,6 +611,7 @@ void release_pages(struct page **pages, int nr, int cold) | |||
588 | int i; | 611 | int i; |
589 | LIST_HEAD(pages_to_free); | 612 | LIST_HEAD(pages_to_free); |
590 | struct zone *zone = NULL; | 613 | struct zone *zone = NULL; |
614 | struct lruvec *lruvec; | ||
591 | unsigned long uninitialized_var(flags); | 615 | unsigned long uninitialized_var(flags); |
592 | 616 | ||
593 | for (i = 0; i < nr; i++) { | 617 | for (i = 0; i < nr; i++) { |
@@ -615,9 +639,11 @@ void release_pages(struct page **pages, int nr, int cold) | |||
615 | zone = pagezone; | 639 | zone = pagezone; |
616 | spin_lock_irqsave(&zone->lru_lock, flags); | 640 | spin_lock_irqsave(&zone->lru_lock, flags); |
617 | } | 641 | } |
642 | |||
643 | lruvec = mem_cgroup_page_lruvec(page, zone); | ||
618 | VM_BUG_ON(!PageLRU(page)); | 644 | VM_BUG_ON(!PageLRU(page)); |
619 | __ClearPageLRU(page); | 645 | __ClearPageLRU(page); |
620 | del_page_from_lru_list(zone, page, page_off_lru(page)); | 646 | del_page_from_lru_list(page, lruvec, page_off_lru(page)); |
621 | } | 647 | } |
622 | 648 | ||
623 | list_add(&page->lru, &pages_to_free); | 649 | list_add(&page->lru, &pages_to_free); |
@@ -649,8 +675,8 @@ EXPORT_SYMBOL(__pagevec_release); | |||
649 | 675 | ||
650 | #ifdef CONFIG_TRANSPARENT_HUGEPAGE | 676 | #ifdef CONFIG_TRANSPARENT_HUGEPAGE |
651 | /* used by __split_huge_page_refcount() */ | 677 | /* used by __split_huge_page_refcount() */ |
652 | void lru_add_page_tail(struct zone* zone, | 678 | void lru_add_page_tail(struct page *page, struct page *page_tail, |
653 | struct page *page, struct page *page_tail) | 679 | struct lruvec *lruvec) |
654 | { | 680 | { |
655 | int uninitialized_var(active); | 681 | int uninitialized_var(active); |
656 | enum lru_list lru; | 682 | enum lru_list lru; |
@@ -659,7 +685,8 @@ void lru_add_page_tail(struct zone* zone, | |||
659 | VM_BUG_ON(!PageHead(page)); | 685 | VM_BUG_ON(!PageHead(page)); |
660 | VM_BUG_ON(PageCompound(page_tail)); | 686 | VM_BUG_ON(PageCompound(page_tail)); |
661 | VM_BUG_ON(PageLRU(page_tail)); | 687 | VM_BUG_ON(PageLRU(page_tail)); |
662 | VM_BUG_ON(NR_CPUS != 1 && !spin_is_locked(&zone->lru_lock)); | 688 | VM_BUG_ON(NR_CPUS != 1 && |
689 | !spin_is_locked(&lruvec_zone(lruvec)->lru_lock)); | ||
663 | 690 | ||
664 | SetPageLRU(page_tail); | 691 | SetPageLRU(page_tail); |
665 | 692 | ||
@@ -688,20 +715,20 @@ void lru_add_page_tail(struct zone* zone, | |||
688 | * Use the standard add function to put page_tail on the list, | 715 | * Use the standard add function to put page_tail on the list, |
689 | * but then correct its position so they all end up in order. | 716 | * but then correct its position so they all end up in order. |
690 | */ | 717 | */ |
691 | add_page_to_lru_list(zone, page_tail, lru); | 718 | add_page_to_lru_list(page_tail, lruvec, lru); |
692 | list_head = page_tail->lru.prev; | 719 | list_head = page_tail->lru.prev; |
693 | list_move_tail(&page_tail->lru, list_head); | 720 | list_move_tail(&page_tail->lru, list_head); |
694 | } | 721 | } |
695 | 722 | ||
696 | if (!PageUnevictable(page)) | 723 | if (!PageUnevictable(page)) |
697 | update_page_reclaim_stat(zone, page_tail, file, active); | 724 | update_page_reclaim_stat(lruvec, file, active); |
698 | } | 725 | } |
699 | #endif /* CONFIG_TRANSPARENT_HUGEPAGE */ | 726 | #endif /* CONFIG_TRANSPARENT_HUGEPAGE */ |
700 | 727 | ||
701 | static void __pagevec_lru_add_fn(struct page *page, void *arg) | 728 | static void __pagevec_lru_add_fn(struct page *page, struct lruvec *lruvec, |
729 | void *arg) | ||
702 | { | 730 | { |
703 | enum lru_list lru = (enum lru_list)arg; | 731 | enum lru_list lru = (enum lru_list)arg; |
704 | struct zone *zone = page_zone(page); | ||
705 | int file = is_file_lru(lru); | 732 | int file = is_file_lru(lru); |
706 | int active = is_active_lru(lru); | 733 | int active = is_active_lru(lru); |
707 | 734 | ||
@@ -712,8 +739,8 @@ static void __pagevec_lru_add_fn(struct page *page, void *arg) | |||
712 | SetPageLRU(page); | 739 | SetPageLRU(page); |
713 | if (active) | 740 | if (active) |
714 | SetPageActive(page); | 741 | SetPageActive(page); |
715 | add_page_to_lru_list(zone, page, lru); | 742 | add_page_to_lru_list(page, lruvec, lru); |
716 | update_page_reclaim_stat(zone, page, file, active); | 743 | update_page_reclaim_stat(lruvec, file, active); |
717 | } | 744 | } |
718 | 745 | ||
719 | /* | 746 | /* |