diff options
author | Johannes Weiner <jweiner@redhat.com> | 2012-01-12 20:18:15 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-01-12 23:13:05 -0500 |
commit | 925b7673cce39116ce61e7a06683a4a0dad1e72a (patch) | |
tree | 66c134db836e531e196ee3dfc23c124ff74ac827 | |
parent | 6290df545814990ca2663baf6e894669132d5f73 (diff) |
mm: make per-memcg LRU lists exclusive
Now that all code that operated on global per-zone LRU lists is
converted to operate on per-memory cgroup LRU lists instead, there is no
reason to keep the double-LRU scheme around any longer.
The pc->lru member is removed and page->lru is linked directly to the
per-memory cgroup LRU lists, which removes two pointers from a
descriptor that exists for every page frame in the system.
Signed-off-by: Johannes Weiner <jweiner@redhat.com>
Signed-off-by: Hugh Dickins <hughd@google.com>
Signed-off-by: Ying Han <yinghan@google.com>
Reviewed-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Reviewed-by: Michal Hocko <mhocko@suse.cz>
Reviewed-by: Kirill A. Shutemov <kirill@shutemov.name>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Greg Thelen <gthelen@google.com>
Cc: Michel Lespinasse <walken@google.com>
Cc: Rik van Riel <riel@redhat.com>
Cc: Minchan Kim <minchan.kim@gmail.com>
Cc: Christoph Hellwig <hch@infradead.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | include/linux/memcontrol.h | 51 | ||||
-rw-r--r-- | include/linux/mm_inline.h | 21 | ||||
-rw-r--r-- | include/linux/page_cgroup.h | 1 | ||||
-rw-r--r-- | mm/memcontrol.c | 311 | ||||
-rw-r--r-- | mm/page_cgroup.c | 1 | ||||
-rw-r--r-- | mm/swap.c | 23 | ||||
-rw-r--r-- | mm/vmscan.c | 64 |
7 files changed, 225 insertions, 247 deletions
diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index 3b99dce85293..e2f8e7caf04b 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h | |||
@@ -32,14 +32,6 @@ enum mem_cgroup_page_stat_item { | |||
32 | MEMCG_NR_FILE_MAPPED, /* # of pages charged as file rss */ | 32 | MEMCG_NR_FILE_MAPPED, /* # of pages charged as file rss */ |
33 | }; | 33 | }; |
34 | 34 | ||
35 | extern unsigned long mem_cgroup_isolate_pages(unsigned long nr_to_scan, | ||
36 | struct list_head *dst, | ||
37 | unsigned long *scanned, int order, | ||
38 | isolate_mode_t mode, | ||
39 | struct zone *z, | ||
40 | struct mem_cgroup *mem_cont, | ||
41 | int active, int file); | ||
42 | |||
43 | struct mem_cgroup_reclaim_cookie { | 35 | struct mem_cgroup_reclaim_cookie { |
44 | struct zone *zone; | 36 | struct zone *zone; |
45 | int priority; | 37 | int priority; |
@@ -69,13 +61,14 @@ extern void mem_cgroup_cancel_charge_swapin(struct mem_cgroup *ptr); | |||
69 | 61 | ||
70 | extern int mem_cgroup_cache_charge(struct page *page, struct mm_struct *mm, | 62 | extern int mem_cgroup_cache_charge(struct page *page, struct mm_struct *mm, |
71 | gfp_t gfp_mask); | 63 | gfp_t gfp_mask); |
72 | extern void mem_cgroup_add_lru_list(struct page *page, enum lru_list lru); | 64 | |
73 | extern void mem_cgroup_del_lru_list(struct page *page, enum lru_list lru); | 65 | struct lruvec *mem_cgroup_zone_lruvec(struct zone *, struct mem_cgroup *); |
74 | extern void mem_cgroup_rotate_reclaimable_page(struct page *page); | 66 | struct lruvec *mem_cgroup_lru_add_list(struct zone *, struct page *, |
75 | extern void mem_cgroup_rotate_lru_list(struct page *page, enum lru_list lru); | 67 | enum lru_list); |
76 | extern void mem_cgroup_del_lru(struct page *page); | 68 | void mem_cgroup_lru_del_list(struct page *, enum lru_list); |
77 | extern void mem_cgroup_move_lists(struct page *page, | 69 | void mem_cgroup_lru_del(struct page *); |
78 | enum lru_list from, enum lru_list to); | 70 | struct lruvec *mem_cgroup_lru_move_lists(struct zone *, struct page *, |
71 | enum lru_list, enum lru_list); | ||
79 | 72 | ||
80 | /* For coalescing uncharge for reducing memcg' overhead*/ | 73 | /* For coalescing uncharge for reducing memcg' overhead*/ |
81 | extern void mem_cgroup_uncharge_start(void); | 74 | extern void mem_cgroup_uncharge_start(void); |
@@ -223,33 +216,33 @@ static inline void mem_cgroup_uncharge_cache_page(struct page *page) | |||
223 | { | 216 | { |
224 | } | 217 | } |
225 | 218 | ||
226 | static inline void mem_cgroup_add_lru_list(struct page *page, int lru) | 219 | static inline struct lruvec *mem_cgroup_zone_lruvec(struct zone *zone, |
227 | { | 220 | struct mem_cgroup *memcg) |
228 | } | ||
229 | |||
230 | static inline void mem_cgroup_del_lru_list(struct page *page, int lru) | ||
231 | { | 221 | { |
232 | return ; | 222 | return &zone->lruvec; |
233 | } | 223 | } |
234 | 224 | ||
235 | static inline void mem_cgroup_rotate_reclaimable_page(struct page *page) | 225 | static inline struct lruvec *mem_cgroup_lru_add_list(struct zone *zone, |
226 | struct page *page, | ||
227 | enum lru_list lru) | ||
236 | { | 228 | { |
237 | return ; | 229 | return &zone->lruvec; |
238 | } | 230 | } |
239 | 231 | ||
240 | static inline void mem_cgroup_rotate_lru_list(struct page *page, int lru) | 232 | static inline void mem_cgroup_lru_del_list(struct page *page, enum lru_list lru) |
241 | { | 233 | { |
242 | return ; | ||
243 | } | 234 | } |
244 | 235 | ||
245 | static inline void mem_cgroup_del_lru(struct page *page) | 236 | static inline void mem_cgroup_lru_del(struct page *page) |
246 | { | 237 | { |
247 | return ; | ||
248 | } | 238 | } |
249 | 239 | ||
250 | static inline void | 240 | static inline struct lruvec *mem_cgroup_lru_move_lists(struct zone *zone, |
251 | mem_cgroup_move_lists(struct page *page, enum lru_list from, enum lru_list to) | 241 | struct page *page, |
242 | enum lru_list from, | ||
243 | enum lru_list to) | ||
252 | { | 244 | { |
245 | return &zone->lruvec; | ||
253 | } | 246 | } |
254 | 247 | ||
255 | static inline struct mem_cgroup *try_get_mem_cgroup_from_page(struct page *page) | 248 | static inline struct mem_cgroup *try_get_mem_cgroup_from_page(struct page *page) |
diff --git a/include/linux/mm_inline.h b/include/linux/mm_inline.h index e6a7ffe16d31..4e3478e71926 100644 --- a/include/linux/mm_inline.h +++ b/include/linux/mm_inline.h | |||
@@ -22,26 +22,21 @@ static inline int page_is_file_cache(struct page *page) | |||
22 | } | 22 | } |
23 | 23 | ||
24 | static inline void | 24 | static inline void |
25 | __add_page_to_lru_list(struct zone *zone, struct page *page, enum lru_list l, | ||
26 | struct list_head *head) | ||
27 | { | ||
28 | list_add(&page->lru, head); | ||
29 | __mod_zone_page_state(zone, NR_LRU_BASE + l, hpage_nr_pages(page)); | ||
30 | mem_cgroup_add_lru_list(page, l); | ||
31 | } | ||
32 | |||
33 | static inline void | ||
34 | add_page_to_lru_list(struct zone *zone, struct page *page, enum lru_list l) | 25 | add_page_to_lru_list(struct zone *zone, struct page *page, enum lru_list l) |
35 | { | 26 | { |
36 | __add_page_to_lru_list(zone, page, l, &zone->lruvec.lists[l]); | 27 | struct lruvec *lruvec; |
28 | |||
29 | lruvec = mem_cgroup_lru_add_list(zone, page, l); | ||
30 | list_add(&page->lru, &lruvec->lists[l]); | ||
31 | __mod_zone_page_state(zone, NR_LRU_BASE + l, hpage_nr_pages(page)); | ||
37 | } | 32 | } |
38 | 33 | ||
39 | static inline void | 34 | static inline void |
40 | del_page_from_lru_list(struct zone *zone, struct page *page, enum lru_list l) | 35 | del_page_from_lru_list(struct zone *zone, struct page *page, enum lru_list l) |
41 | { | 36 | { |
37 | mem_cgroup_lru_del_list(page, l); | ||
42 | list_del(&page->lru); | 38 | list_del(&page->lru); |
43 | __mod_zone_page_state(zone, NR_LRU_BASE + l, -hpage_nr_pages(page)); | 39 | __mod_zone_page_state(zone, NR_LRU_BASE + l, -hpage_nr_pages(page)); |
44 | mem_cgroup_del_lru_list(page, l); | ||
45 | } | 40 | } |
46 | 41 | ||
47 | /** | 42 | /** |
@@ -64,7 +59,6 @@ del_page_from_lru(struct zone *zone, struct page *page) | |||
64 | { | 59 | { |
65 | enum lru_list l; | 60 | enum lru_list l; |
66 | 61 | ||
67 | list_del(&page->lru); | ||
68 | if (PageUnevictable(page)) { | 62 | if (PageUnevictable(page)) { |
69 | __ClearPageUnevictable(page); | 63 | __ClearPageUnevictable(page); |
70 | l = LRU_UNEVICTABLE; | 64 | l = LRU_UNEVICTABLE; |
@@ -75,8 +69,9 @@ del_page_from_lru(struct zone *zone, struct page *page) | |||
75 | l += LRU_ACTIVE; | 69 | l += LRU_ACTIVE; |
76 | } | 70 | } |
77 | } | 71 | } |
72 | mem_cgroup_lru_del_list(page, l); | ||
73 | list_del(&page->lru); | ||
78 | __mod_zone_page_state(zone, NR_LRU_BASE + l, -hpage_nr_pages(page)); | 74 | __mod_zone_page_state(zone, NR_LRU_BASE + l, -hpage_nr_pages(page)); |
79 | mem_cgroup_del_lru_list(page, l); | ||
80 | } | 75 | } |
81 | 76 | ||
82 | /** | 77 | /** |
diff --git a/include/linux/page_cgroup.h b/include/linux/page_cgroup.h index 961ecc7d30bc..5bae7535c202 100644 --- a/include/linux/page_cgroup.h +++ b/include/linux/page_cgroup.h | |||
@@ -31,7 +31,6 @@ enum { | |||
31 | struct page_cgroup { | 31 | struct page_cgroup { |
32 | unsigned long flags; | 32 | unsigned long flags; |
33 | struct mem_cgroup *mem_cgroup; | 33 | struct mem_cgroup *mem_cgroup; |
34 | struct list_head lru; /* per cgroup LRU list */ | ||
35 | }; | 34 | }; |
36 | 35 | ||
37 | void __meminit pgdat_page_cgroup_init(struct pglist_data *pgdat); | 36 | void __meminit pgdat_page_cgroup_init(struct pglist_data *pgdat); |
diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 6e7f849a1a9e..972878b648c2 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c | |||
@@ -995,6 +995,27 @@ out: | |||
995 | } | 995 | } |
996 | EXPORT_SYMBOL(mem_cgroup_count_vm_event); | 996 | EXPORT_SYMBOL(mem_cgroup_count_vm_event); |
997 | 997 | ||
998 | /** | ||
999 | * mem_cgroup_zone_lruvec - get the lru list vector for a zone and memcg | ||
1000 | * @zone: zone of the wanted lruvec | ||
1001 | * @mem: memcg of the wanted lruvec | ||
1002 | * | ||
1003 | * Returns the lru list vector holding pages for the given @zone and | ||
1004 | * @mem. This can be the global zone lruvec, if the memory controller | ||
1005 | * is disabled. | ||
1006 | */ | ||
1007 | struct lruvec *mem_cgroup_zone_lruvec(struct zone *zone, | ||
1008 | struct mem_cgroup *memcg) | ||
1009 | { | ||
1010 | struct mem_cgroup_per_zone *mz; | ||
1011 | |||
1012 | if (mem_cgroup_disabled()) | ||
1013 | return &zone->lruvec; | ||
1014 | |||
1015 | mz = mem_cgroup_zoneinfo(memcg, zone_to_nid(zone), zone_idx(zone)); | ||
1016 | return &mz->lruvec; | ||
1017 | } | ||
1018 | |||
998 | /* | 1019 | /* |
999 | * Following LRU functions are allowed to be used without PCG_LOCK. | 1020 | * Following LRU functions are allowed to be used without PCG_LOCK. |
1000 | * Operations are called by routine of global LRU independently from memcg. | 1021 | * Operations are called by routine of global LRU independently from memcg. |
@@ -1009,104 +1030,123 @@ EXPORT_SYMBOL(mem_cgroup_count_vm_event); | |||
1009 | * When moving account, the page is not on LRU. It's isolated. | 1030 | * When moving account, the page is not on LRU. It's isolated. |
1010 | */ | 1031 | */ |
1011 | 1032 | ||
1012 | void mem_cgroup_del_lru_list(struct page *page, enum lru_list lru) | 1033 | /** |
1034 | * mem_cgroup_lru_add_list - account for adding an lru page and return lruvec | ||
1035 | * @zone: zone of the page | ||
1036 | * @page: the page | ||
1037 | * @lru: current lru | ||
1038 | * | ||
1039 | * This function accounts for @page being added to @lru, and returns | ||
1040 | * the lruvec for the given @zone and the memcg @page is charged to. | ||
1041 | * | ||
1042 | * The callsite is then responsible for physically linking the page to | ||
1043 | * the returned lruvec->lists[@lru]. | ||
1044 | */ | ||
1045 | struct lruvec *mem_cgroup_lru_add_list(struct zone *zone, struct page *page, | ||
1046 | enum lru_list lru) | ||
1013 | { | 1047 | { |
1014 | struct page_cgroup *pc; | ||
1015 | struct mem_cgroup_per_zone *mz; | 1048 | struct mem_cgroup_per_zone *mz; |
1049 | struct mem_cgroup *memcg; | ||
1050 | struct page_cgroup *pc; | ||
1016 | 1051 | ||
1017 | if (mem_cgroup_disabled()) | 1052 | if (mem_cgroup_disabled()) |
1018 | return; | 1053 | return &zone->lruvec; |
1054 | |||
1019 | pc = lookup_page_cgroup(page); | 1055 | pc = lookup_page_cgroup(page); |
1020 | /* can happen while we handle swapcache. */ | 1056 | VM_BUG_ON(PageCgroupAcctLRU(pc)); |
1021 | if (!TestClearPageCgroupAcctLRU(pc)) | ||
1022 | return; | ||
1023 | VM_BUG_ON(!pc->mem_cgroup); | ||
1024 | /* | 1057 | /* |
1025 | * We don't check PCG_USED bit. It's cleared when the "page" is finally | 1058 | * putback: charge: |
1026 | * removed from global LRU. | 1059 | * SetPageLRU SetPageCgroupUsed |
1060 | * smp_mb smp_mb | ||
1061 | * PageCgroupUsed && add to memcg LRU PageLRU && add to memcg LRU | ||
1062 | * | ||
1063 | * Ensure that one of the two sides adds the page to the memcg | ||
1064 | * LRU during a race. | ||
1027 | */ | 1065 | */ |
1028 | mz = page_cgroup_zoneinfo(pc->mem_cgroup, page); | 1066 | smp_mb(); |
1029 | /* huge page split is done under lru_lock. so, we have no races. */ | 1067 | /* |
1030 | MEM_CGROUP_ZSTAT(mz, lru) -= 1 << compound_order(page); | 1068 | * If the page is uncharged, it may be freed soon, but it |
1031 | VM_BUG_ON(list_empty(&pc->lru)); | 1069 | * could also be swap cache (readahead, swapoff) that needs to |
1032 | list_del_init(&pc->lru); | 1070 | * be reclaimable in the future. root_mem_cgroup will babysit |
1033 | } | 1071 | * it for the time being. |
1034 | 1072 | */ | |
1035 | void mem_cgroup_del_lru(struct page *page) | 1073 | if (PageCgroupUsed(pc)) { |
1036 | { | 1074 | /* Ensure pc->mem_cgroup is visible after reading PCG_USED. */ |
1037 | mem_cgroup_del_lru_list(page, page_lru(page)); | 1075 | smp_rmb(); |
1076 | memcg = pc->mem_cgroup; | ||
1077 | SetPageCgroupAcctLRU(pc); | ||
1078 | } else | ||
1079 | memcg = root_mem_cgroup; | ||
1080 | mz = page_cgroup_zoneinfo(memcg, page); | ||
1081 | /* compound_order() is stabilized through lru_lock */ | ||
1082 | MEM_CGROUP_ZSTAT(mz, lru) += 1 << compound_order(page); | ||
1083 | return &mz->lruvec; | ||
1038 | } | 1084 | } |
1039 | 1085 | ||
1040 | /* | 1086 | /** |
1041 | * Writeback is about to end against a page which has been marked for immediate | 1087 | * mem_cgroup_lru_del_list - account for removing an lru page |
1042 | * reclaim. If it still appears to be reclaimable, move it to the tail of the | 1088 | * @page: the page |
1043 | * inactive list. | 1089 | * @lru: target lru |
1090 | * | ||
1091 | * This function accounts for @page being removed from @lru. | ||
1092 | * | ||
1093 | * The callsite is then responsible for physically unlinking | ||
1094 | * @page->lru. | ||
1044 | */ | 1095 | */ |
1045 | void mem_cgroup_rotate_reclaimable_page(struct page *page) | 1096 | void mem_cgroup_lru_del_list(struct page *page, enum lru_list lru) |
1046 | { | 1097 | { |
1047 | struct mem_cgroup_per_zone *mz; | 1098 | struct mem_cgroup_per_zone *mz; |
1099 | struct mem_cgroup *memcg; | ||
1048 | struct page_cgroup *pc; | 1100 | struct page_cgroup *pc; |
1049 | enum lru_list lru = page_lru(page); | ||
1050 | 1101 | ||
1051 | if (mem_cgroup_disabled()) | 1102 | if (mem_cgroup_disabled()) |
1052 | return; | 1103 | return; |
1053 | 1104 | ||
1054 | pc = lookup_page_cgroup(page); | 1105 | pc = lookup_page_cgroup(page); |
1055 | /* unused page is not rotated. */ | 1106 | /* |
1056 | if (!PageCgroupUsed(pc)) | 1107 | * root_mem_cgroup babysits uncharged LRU pages, but |
1057 | return; | 1108 | * PageCgroupUsed is cleared when the page is about to get |
1058 | /* Ensure pc->mem_cgroup is visible after reading PCG_USED. */ | 1109 | * freed. PageCgroupAcctLRU remembers whether the |
1059 | smp_rmb(); | 1110 | * LRU-accounting happened against pc->mem_cgroup or |
1060 | mz = page_cgroup_zoneinfo(pc->mem_cgroup, page); | 1111 | * root_mem_cgroup. |
1061 | list_move_tail(&pc->lru, &mz->lruvec.lists[lru]); | 1112 | */ |
1113 | if (TestClearPageCgroupAcctLRU(pc)) { | ||
1114 | VM_BUG_ON(!pc->mem_cgroup); | ||
1115 | memcg = pc->mem_cgroup; | ||
1116 | } else | ||
1117 | memcg = root_mem_cgroup; | ||
1118 | mz = page_cgroup_zoneinfo(memcg, page); | ||
1119 | /* huge page split is done under lru_lock. so, we have no races. */ | ||
1120 | MEM_CGROUP_ZSTAT(mz, lru) -= 1 << compound_order(page); | ||
1062 | } | 1121 | } |
1063 | 1122 | ||
1064 | void mem_cgroup_rotate_lru_list(struct page *page, enum lru_list lru) | 1123 | void mem_cgroup_lru_del(struct page *page) |
1065 | { | 1124 | { |
1066 | struct mem_cgroup_per_zone *mz; | 1125 | mem_cgroup_lru_del_list(page, page_lru(page)); |
1067 | struct page_cgroup *pc; | ||
1068 | |||
1069 | if (mem_cgroup_disabled()) | ||
1070 | return; | ||
1071 | |||
1072 | pc = lookup_page_cgroup(page); | ||
1073 | /* unused page is not rotated. */ | ||
1074 | if (!PageCgroupUsed(pc)) | ||
1075 | return; | ||
1076 | /* Ensure pc->mem_cgroup is visible after reading PCG_USED. */ | ||
1077 | smp_rmb(); | ||
1078 | mz = page_cgroup_zoneinfo(pc->mem_cgroup, page); | ||
1079 | list_move(&pc->lru, &mz->lruvec.lists[lru]); | ||
1080 | } | 1126 | } |
1081 | 1127 | ||
1082 | void mem_cgroup_add_lru_list(struct page *page, enum lru_list lru) | 1128 | /** |
1129 | * mem_cgroup_lru_move_lists - account for moving a page between lrus | ||
1130 | * @zone: zone of the page | ||
1131 | * @page: the page | ||
1132 | * @from: current lru | ||
1133 | * @to: target lru | ||
1134 | * | ||
1135 | * This function accounts for @page being moved between the lrus @from | ||
1136 | * and @to, and returns the lruvec for the given @zone and the memcg | ||
1137 | * @page is charged to. | ||
1138 | * | ||
1139 | * The callsite is then responsible for physically relinking | ||
1140 | * @page->lru to the returned lruvec->lists[@to]. | ||
1141 | */ | ||
1142 | struct lruvec *mem_cgroup_lru_move_lists(struct zone *zone, | ||
1143 | struct page *page, | ||
1144 | enum lru_list from, | ||
1145 | enum lru_list to) | ||
1083 | { | 1146 | { |
1084 | struct page_cgroup *pc; | 1147 | /* XXX: Optimize this, especially for @from == @to */ |
1085 | struct mem_cgroup_per_zone *mz; | 1148 | mem_cgroup_lru_del_list(page, from); |
1086 | 1149 | return mem_cgroup_lru_add_list(zone, page, to); | |
1087 | if (mem_cgroup_disabled()) | ||
1088 | return; | ||
1089 | pc = lookup_page_cgroup(page); | ||
1090 | VM_BUG_ON(PageCgroupAcctLRU(pc)); | ||
1091 | /* | ||
1092 | * putback: charge: | ||
1093 | * SetPageLRU SetPageCgroupUsed | ||
1094 | * smp_mb smp_mb | ||
1095 | * PageCgroupUsed && add to memcg LRU PageLRU && add to memcg LRU | ||
1096 | * | ||
1097 | * Ensure that one of the two sides adds the page to the memcg | ||
1098 | * LRU during a race. | ||
1099 | */ | ||
1100 | smp_mb(); | ||
1101 | if (!PageCgroupUsed(pc)) | ||
1102 | return; | ||
1103 | /* Ensure pc->mem_cgroup is visible after reading PCG_USED. */ | ||
1104 | smp_rmb(); | ||
1105 | mz = page_cgroup_zoneinfo(pc->mem_cgroup, page); | ||
1106 | /* huge page split is done under lru_lock. so, we have no races. */ | ||
1107 | MEM_CGROUP_ZSTAT(mz, lru) += 1 << compound_order(page); | ||
1108 | SetPageCgroupAcctLRU(pc); | ||
1109 | list_add(&pc->lru, &mz->lruvec.lists[lru]); | ||
1110 | } | 1150 | } |
1111 | 1151 | ||
1112 | /* | 1152 | /* |
@@ -1117,6 +1157,7 @@ void mem_cgroup_add_lru_list(struct page *page, enum lru_list lru) | |||
1117 | */ | 1157 | */ |
1118 | static void mem_cgroup_lru_del_before_commit(struct page *page) | 1158 | static void mem_cgroup_lru_del_before_commit(struct page *page) |
1119 | { | 1159 | { |
1160 | enum lru_list lru; | ||
1120 | unsigned long flags; | 1161 | unsigned long flags; |
1121 | struct zone *zone = page_zone(page); | 1162 | struct zone *zone = page_zone(page); |
1122 | struct page_cgroup *pc = lookup_page_cgroup(page); | 1163 | struct page_cgroup *pc = lookup_page_cgroup(page); |
@@ -1133,17 +1174,28 @@ static void mem_cgroup_lru_del_before_commit(struct page *page) | |||
1133 | return; | 1174 | return; |
1134 | 1175 | ||
1135 | spin_lock_irqsave(&zone->lru_lock, flags); | 1176 | spin_lock_irqsave(&zone->lru_lock, flags); |
1177 | lru = page_lru(page); | ||
1136 | /* | 1178 | /* |
1137 | * Forget old LRU when this page_cgroup is *not* used. This Used bit | 1179 | * The uncharged page could still be registered to the LRU of |
1138 | * is guarded by lock_page() because the page is SwapCache. | 1180 | * the stale pc->mem_cgroup. |
1181 | * | ||
1182 | * As pc->mem_cgroup is about to get overwritten, the old LRU | ||
1183 | * accounting needs to be taken care of. Let root_mem_cgroup | ||
1184 | * babysit the page until the new memcg is responsible for it. | ||
1185 | * | ||
1186 | * The PCG_USED bit is guarded by lock_page() as the page is | ||
1187 | * swapcache/pagecache. | ||
1139 | */ | 1188 | */ |
1140 | if (!PageCgroupUsed(pc)) | 1189 | if (PageLRU(page) && PageCgroupAcctLRU(pc) && !PageCgroupUsed(pc)) { |
1141 | mem_cgroup_del_lru_list(page, page_lru(page)); | 1190 | del_page_from_lru_list(zone, page, lru); |
1191 | add_page_to_lru_list(zone, page, lru); | ||
1192 | } | ||
1142 | spin_unlock_irqrestore(&zone->lru_lock, flags); | 1193 | spin_unlock_irqrestore(&zone->lru_lock, flags); |
1143 | } | 1194 | } |
1144 | 1195 | ||
1145 | static void mem_cgroup_lru_add_after_commit(struct page *page) | 1196 | static void mem_cgroup_lru_add_after_commit(struct page *page) |
1146 | { | 1197 | { |
1198 | enum lru_list lru; | ||
1147 | unsigned long flags; | 1199 | unsigned long flags; |
1148 | struct zone *zone = page_zone(page); | 1200 | struct zone *zone = page_zone(page); |
1149 | struct page_cgroup *pc = lookup_page_cgroup(page); | 1201 | struct page_cgroup *pc = lookup_page_cgroup(page); |
@@ -1161,22 +1213,22 @@ static void mem_cgroup_lru_add_after_commit(struct page *page) | |||
1161 | if (likely(!PageLRU(page))) | 1213 | if (likely(!PageLRU(page))) |
1162 | return; | 1214 | return; |
1163 | spin_lock_irqsave(&zone->lru_lock, flags); | 1215 | spin_lock_irqsave(&zone->lru_lock, flags); |
1164 | /* link when the page is linked to LRU but page_cgroup isn't */ | 1216 | lru = page_lru(page); |
1165 | if (PageLRU(page) && !PageCgroupAcctLRU(pc)) | 1217 | /* |
1166 | mem_cgroup_add_lru_list(page, page_lru(page)); | 1218 | * If the page is not on the LRU, someone will soon put it |
1219 | * there. If it is, and also already accounted for on the | ||
1220 | * memcg-side, it must be on the right lruvec as setting | ||
1221 | * pc->mem_cgroup and PageCgroupUsed is properly ordered. | ||
1222 | * Otherwise, root_mem_cgroup has been babysitting the page | ||
1223 | * during the charge. Move it to the new memcg now. | ||
1224 | */ | ||
1225 | if (PageLRU(page) && !PageCgroupAcctLRU(pc)) { | ||
1226 | del_page_from_lru_list(zone, page, lru); | ||
1227 | add_page_to_lru_list(zone, page, lru); | ||
1228 | } | ||
1167 | spin_unlock_irqrestore(&zone->lru_lock, flags); | 1229 | spin_unlock_irqrestore(&zone->lru_lock, flags); |
1168 | } | 1230 | } |
1169 | 1231 | ||
1170 | |||
1171 | void mem_cgroup_move_lists(struct page *page, | ||
1172 | enum lru_list from, enum lru_list to) | ||
1173 | { | ||
1174 | if (mem_cgroup_disabled()) | ||
1175 | return; | ||
1176 | mem_cgroup_del_lru_list(page, from); | ||
1177 | mem_cgroup_add_lru_list(page, to); | ||
1178 | } | ||
1179 | |||
1180 | /* | 1232 | /* |
1181 | * Checks whether given mem is same or in the root_mem_cgroup's | 1233 | * Checks whether given mem is same or in the root_mem_cgroup's |
1182 | * hierarchy subtree | 1234 | * hierarchy subtree |
@@ -1282,68 +1334,6 @@ mem_cgroup_get_reclaim_stat_from_page(struct page *page) | |||
1282 | return &mz->reclaim_stat; | 1334 | return &mz->reclaim_stat; |
1283 | } | 1335 | } |
1284 | 1336 | ||
1285 | unsigned long mem_cgroup_isolate_pages(unsigned long nr_to_scan, | ||
1286 | struct list_head *dst, | ||
1287 | unsigned long *scanned, int order, | ||
1288 | isolate_mode_t mode, | ||
1289 | struct zone *z, | ||
1290 | struct mem_cgroup *mem_cont, | ||
1291 | int active, int file) | ||
1292 | { | ||
1293 | unsigned long nr_taken = 0; | ||
1294 | struct page *page; | ||
1295 | unsigned long scan; | ||
1296 | LIST_HEAD(pc_list); | ||
1297 | struct list_head *src; | ||
1298 | struct page_cgroup *pc, *tmp; | ||
1299 | int nid = zone_to_nid(z); | ||
1300 | int zid = zone_idx(z); | ||
1301 | struct mem_cgroup_per_zone *mz; | ||
1302 | int lru = LRU_FILE * file + active; | ||
1303 | int ret; | ||
1304 | |||
1305 | BUG_ON(!mem_cont); | ||
1306 | mz = mem_cgroup_zoneinfo(mem_cont, nid, zid); | ||
1307 | src = &mz->lruvec.lists[lru]; | ||
1308 | |||
1309 | scan = 0; | ||
1310 | list_for_each_entry_safe_reverse(pc, tmp, src, lru) { | ||
1311 | if (scan >= nr_to_scan) | ||
1312 | break; | ||
1313 | |||
1314 | if (unlikely(!PageCgroupUsed(pc))) | ||
1315 | continue; | ||
1316 | |||
1317 | page = lookup_cgroup_page(pc); | ||
1318 | |||
1319 | if (unlikely(!PageLRU(page))) | ||
1320 | continue; | ||
1321 | |||
1322 | scan++; | ||
1323 | ret = __isolate_lru_page(page, mode, file); | ||
1324 | switch (ret) { | ||
1325 | case 0: | ||
1326 | list_move(&page->lru, dst); | ||
1327 | mem_cgroup_del_lru(page); | ||
1328 | nr_taken += hpage_nr_pages(page); | ||
1329 | break; | ||
1330 | case -EBUSY: | ||
1331 | /* we don't affect global LRU but rotate in our LRU */ | ||
1332 | mem_cgroup_rotate_lru_list(page, page_lru(page)); | ||
1333 | break; | ||
1334 | default: | ||
1335 | break; | ||
1336 | } | ||
1337 | } | ||
1338 | |||
1339 | *scanned = scan; | ||
1340 | |||
1341 | trace_mm_vmscan_memcg_isolate(0, nr_to_scan, scan, nr_taken, | ||
1342 | 0, 0, 0, mode); | ||
1343 | |||
1344 | return nr_taken; | ||
1345 | } | ||
1346 | |||
1347 | #define mem_cgroup_from_res_counter(counter, member) \ | 1337 | #define mem_cgroup_from_res_counter(counter, member) \ |
1348 | container_of(counter, struct mem_cgroup, member) | 1338 | container_of(counter, struct mem_cgroup, member) |
1349 | 1339 | ||
@@ -3726,11 +3716,11 @@ unsigned long mem_cgroup_soft_limit_reclaim(struct zone *zone, int order, | |||
3726 | static int mem_cgroup_force_empty_list(struct mem_cgroup *memcg, | 3716 | static int mem_cgroup_force_empty_list(struct mem_cgroup *memcg, |
3727 | int node, int zid, enum lru_list lru) | 3717 | int node, int zid, enum lru_list lru) |
3728 | { | 3718 | { |
3729 | struct zone *zone; | ||
3730 | struct mem_cgroup_per_zone *mz; | 3719 | struct mem_cgroup_per_zone *mz; |
3731 | struct page_cgroup *pc, *busy; | ||
3732 | unsigned long flags, loop; | 3720 | unsigned long flags, loop; |
3733 | struct list_head *list; | 3721 | struct list_head *list; |
3722 | struct page *busy; | ||
3723 | struct zone *zone; | ||
3734 | int ret = 0; | 3724 | int ret = 0; |
3735 | 3725 | ||
3736 | zone = &NODE_DATA(node)->node_zones[zid]; | 3726 | zone = &NODE_DATA(node)->node_zones[zid]; |
@@ -3742,6 +3732,7 @@ static int mem_cgroup_force_empty_list(struct mem_cgroup *memcg, | |||
3742 | loop += 256; | 3732 | loop += 256; |
3743 | busy = NULL; | 3733 | busy = NULL; |
3744 | while (loop--) { | 3734 | while (loop--) { |
3735 | struct page_cgroup *pc; | ||
3745 | struct page *page; | 3736 | struct page *page; |
3746 | 3737 | ||
3747 | ret = 0; | 3738 | ret = 0; |
@@ -3750,16 +3741,16 @@ static int mem_cgroup_force_empty_list(struct mem_cgroup *memcg, | |||
3750 | spin_unlock_irqrestore(&zone->lru_lock, flags); | 3741 | spin_unlock_irqrestore(&zone->lru_lock, flags); |
3751 | break; | 3742 | break; |
3752 | } | 3743 | } |
3753 | pc = list_entry(list->prev, struct page_cgroup, lru); | 3744 | page = list_entry(list->prev, struct page, lru); |
3754 | if (busy == pc) { | 3745 | if (busy == page) { |
3755 | list_move(&pc->lru, list); | 3746 | list_move(&page->lru, list); |
3756 | busy = NULL; | 3747 | busy = NULL; |
3757 | spin_unlock_irqrestore(&zone->lru_lock, flags); | 3748 | spin_unlock_irqrestore(&zone->lru_lock, flags); |
3758 | continue; | 3749 | continue; |
3759 | } | 3750 | } |
3760 | spin_unlock_irqrestore(&zone->lru_lock, flags); | 3751 | spin_unlock_irqrestore(&zone->lru_lock, flags); |
3761 | 3752 | ||
3762 | page = lookup_cgroup_page(pc); | 3753 | pc = lookup_page_cgroup(page); |
3763 | 3754 | ||
3764 | ret = mem_cgroup_move_parent(page, pc, memcg, GFP_KERNEL); | 3755 | ret = mem_cgroup_move_parent(page, pc, memcg, GFP_KERNEL); |
3765 | if (ret == -ENOMEM) | 3756 | if (ret == -ENOMEM) |
@@ -3767,7 +3758,7 @@ static int mem_cgroup_force_empty_list(struct mem_cgroup *memcg, | |||
3767 | 3758 | ||
3768 | if (ret == -EBUSY || ret == -EINVAL) { | 3759 | if (ret == -EBUSY || ret == -EINVAL) { |
3769 | /* found lock contention or "pc" is obsolete. */ | 3760 | /* found lock contention or "pc" is obsolete. */ |
3770 | busy = pc; | 3761 | busy = page; |
3771 | cond_resched(); | 3762 | cond_resched(); |
3772 | } else | 3763 | } else |
3773 | busy = NULL; | 3764 | busy = NULL; |
diff --git a/mm/page_cgroup.c b/mm/page_cgroup.c index 2d123f94a8df..f59405a8d752 100644 --- a/mm/page_cgroup.c +++ b/mm/page_cgroup.c | |||
@@ -16,7 +16,6 @@ static void __meminit init_page_cgroup(struct page_cgroup *pc, unsigned long id) | |||
16 | pc->flags = 0; | 16 | pc->flags = 0; |
17 | set_page_cgroup_array_id(pc, id); | 17 | set_page_cgroup_array_id(pc, id); |
18 | pc->mem_cgroup = NULL; | 18 | pc->mem_cgroup = NULL; |
19 | INIT_LIST_HEAD(&pc->lru); | ||
20 | } | 19 | } |
21 | static unsigned long total_usage; | 20 | static unsigned long total_usage; |
22 | 21 | ||
@@ -232,12 +232,14 @@ static void pagevec_lru_move_fn(struct pagevec *pvec, | |||
232 | static void pagevec_move_tail_fn(struct page *page, void *arg) | 232 | static void pagevec_move_tail_fn(struct page *page, void *arg) |
233 | { | 233 | { |
234 | int *pgmoved = arg; | 234 | int *pgmoved = arg; |
235 | struct zone *zone = page_zone(page); | ||
236 | 235 | ||
237 | if (PageLRU(page) && !PageActive(page) && !PageUnevictable(page)) { | 236 | if (PageLRU(page) && !PageActive(page) && !PageUnevictable(page)) { |
238 | enum lru_list lru = page_lru_base_type(page); | 237 | enum lru_list lru = page_lru_base_type(page); |
239 | list_move_tail(&page->lru, &zone->lruvec.lists[lru]); | 238 | struct lruvec *lruvec; |
240 | mem_cgroup_rotate_reclaimable_page(page); | 239 | |
240 | lruvec = mem_cgroup_lru_move_lists(page_zone(page), | ||
241 | page, lru, lru); | ||
242 | list_move_tail(&page->lru, &lruvec->lists[lru]); | ||
241 | (*pgmoved)++; | 243 | (*pgmoved)++; |
242 | } | 244 | } |
243 | } | 245 | } |
@@ -476,12 +478,13 @@ static void lru_deactivate_fn(struct page *page, void *arg) | |||
476 | */ | 478 | */ |
477 | SetPageReclaim(page); | 479 | SetPageReclaim(page); |
478 | } else { | 480 | } else { |
481 | struct lruvec *lruvec; | ||
479 | /* | 482 | /* |
480 | * The page's writeback ends up during pagevec | 483 | * The page's writeback ends up during pagevec |
481 | * We moves tha page into tail of inactive. | 484 | * We moves tha page into tail of inactive. |
482 | */ | 485 | */ |
483 | list_move_tail(&page->lru, &zone->lruvec.lists[lru]); | 486 | lruvec = mem_cgroup_lru_move_lists(zone, page, lru, lru); |
484 | mem_cgroup_rotate_reclaimable_page(page); | 487 | list_move_tail(&page->lru, &lruvec->lists[lru]); |
485 | __count_vm_event(PGROTATED); | 488 | __count_vm_event(PGROTATED); |
486 | } | 489 | } |
487 | 490 | ||
@@ -663,6 +666,8 @@ void lru_add_page_tail(struct zone* zone, | |||
663 | SetPageLRU(page_tail); | 666 | SetPageLRU(page_tail); |
664 | 667 | ||
665 | if (page_evictable(page_tail, NULL)) { | 668 | if (page_evictable(page_tail, NULL)) { |
669 | struct lruvec *lruvec; | ||
670 | |||
666 | if (PageActive(page)) { | 671 | if (PageActive(page)) { |
667 | SetPageActive(page_tail); | 672 | SetPageActive(page_tail); |
668 | active = 1; | 673 | active = 1; |
@@ -672,11 +677,13 @@ void lru_add_page_tail(struct zone* zone, | |||
672 | lru = LRU_INACTIVE_ANON; | 677 | lru = LRU_INACTIVE_ANON; |
673 | } | 678 | } |
674 | update_page_reclaim_stat(zone, page_tail, file, active); | 679 | update_page_reclaim_stat(zone, page_tail, file, active); |
680 | lruvec = mem_cgroup_lru_add_list(zone, page_tail, lru); | ||
675 | if (likely(PageLRU(page))) | 681 | if (likely(PageLRU(page))) |
676 | __add_page_to_lru_list(zone, page_tail, lru, | 682 | list_add(&page_tail->lru, page->lru.prev); |
677 | page->lru.prev); | ||
678 | else | 683 | else |
679 | add_page_to_lru_list(zone, page_tail, lru); | 684 | list_add(&page_tail->lru, &lruvec->lists[lru]); |
685 | __mod_zone_page_state(zone, NR_LRU_BASE + lru, | ||
686 | hpage_nr_pages(page_tail)); | ||
680 | } else { | 687 | } else { |
681 | SetPageUnevictable(page_tail); | 688 | SetPageUnevictable(page_tail); |
682 | add_page_to_lru_list(zone, page_tail, LRU_UNEVICTABLE); | 689 | add_page_to_lru_list(zone, page_tail, LRU_UNEVICTABLE); |
diff --git a/mm/vmscan.c b/mm/vmscan.c index 93cdc44a1693..813aae820a27 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c | |||
@@ -1139,15 +1139,14 @@ static unsigned long isolate_lru_pages(unsigned long nr_to_scan, | |||
1139 | 1139 | ||
1140 | switch (__isolate_lru_page(page, mode, file)) { | 1140 | switch (__isolate_lru_page(page, mode, file)) { |
1141 | case 0: | 1141 | case 0: |
1142 | mem_cgroup_lru_del(page); | ||
1142 | list_move(&page->lru, dst); | 1143 | list_move(&page->lru, dst); |
1143 | mem_cgroup_del_lru(page); | ||
1144 | nr_taken += hpage_nr_pages(page); | 1144 | nr_taken += hpage_nr_pages(page); |
1145 | break; | 1145 | break; |
1146 | 1146 | ||
1147 | case -EBUSY: | 1147 | case -EBUSY: |
1148 | /* else it is being freed elsewhere */ | 1148 | /* else it is being freed elsewhere */ |
1149 | list_move(&page->lru, src); | 1149 | list_move(&page->lru, src); |
1150 | mem_cgroup_rotate_lru_list(page, page_lru(page)); | ||
1151 | continue; | 1150 | continue; |
1152 | 1151 | ||
1153 | default: | 1152 | default: |
@@ -1197,8 +1196,8 @@ static unsigned long isolate_lru_pages(unsigned long nr_to_scan, | |||
1197 | break; | 1196 | break; |
1198 | 1197 | ||
1199 | if (__isolate_lru_page(cursor_page, mode, file) == 0) { | 1198 | if (__isolate_lru_page(cursor_page, mode, file) == 0) { |
1199 | mem_cgroup_lru_del(cursor_page); | ||
1200 | list_move(&cursor_page->lru, dst); | 1200 | list_move(&cursor_page->lru, dst); |
1201 | mem_cgroup_del_lru(cursor_page); | ||
1202 | nr_taken += hpage_nr_pages(cursor_page); | 1201 | nr_taken += hpage_nr_pages(cursor_page); |
1203 | nr_lumpy_taken++; | 1202 | nr_lumpy_taken++; |
1204 | if (PageDirty(cursor_page)) | 1203 | if (PageDirty(cursor_page)) |
@@ -1239,18 +1238,20 @@ static unsigned long isolate_lru_pages(unsigned long nr_to_scan, | |||
1239 | return nr_taken; | 1238 | return nr_taken; |
1240 | } | 1239 | } |
1241 | 1240 | ||
1242 | static unsigned long isolate_pages_global(unsigned long nr, | 1241 | static unsigned long isolate_pages(unsigned long nr, struct mem_cgroup_zone *mz, |
1243 | struct list_head *dst, | 1242 | struct list_head *dst, |
1244 | unsigned long *scanned, int order, | 1243 | unsigned long *scanned, int order, |
1245 | isolate_mode_t mode, | 1244 | isolate_mode_t mode, int active, int file) |
1246 | struct zone *z, int active, int file) | ||
1247 | { | 1245 | { |
1246 | struct lruvec *lruvec; | ||
1248 | int lru = LRU_BASE; | 1247 | int lru = LRU_BASE; |
1248 | |||
1249 | lruvec = mem_cgroup_zone_lruvec(mz->zone, mz->mem_cgroup); | ||
1249 | if (active) | 1250 | if (active) |
1250 | lru += LRU_ACTIVE; | 1251 | lru += LRU_ACTIVE; |
1251 | if (file) | 1252 | if (file) |
1252 | lru += LRU_FILE; | 1253 | lru += LRU_FILE; |
1253 | return isolate_lru_pages(nr, &z->lruvec.lists[lru], dst, | 1254 | return isolate_lru_pages(nr, &lruvec->lists[lru], dst, |
1254 | scanned, order, mode, file); | 1255 | scanned, order, mode, file); |
1255 | } | 1256 | } |
1256 | 1257 | ||
@@ -1518,14 +1519,9 @@ shrink_inactive_list(unsigned long nr_to_scan, struct mem_cgroup_zone *mz, | |||
1518 | 1519 | ||
1519 | spin_lock_irq(&zone->lru_lock); | 1520 | spin_lock_irq(&zone->lru_lock); |
1520 | 1521 | ||
1521 | if (scanning_global_lru(mz)) { | 1522 | nr_taken = isolate_pages(nr_to_scan, mz, &page_list, |
1522 | nr_taken = isolate_pages_global(nr_to_scan, &page_list, | 1523 | &nr_scanned, sc->order, |
1523 | &nr_scanned, sc->order, reclaim_mode, zone, 0, file); | 1524 | reclaim_mode, 0, file); |
1524 | } else { | ||
1525 | nr_taken = mem_cgroup_isolate_pages(nr_to_scan, &page_list, | ||
1526 | &nr_scanned, sc->order, reclaim_mode, zone, | ||
1527 | mz->mem_cgroup, 0, file); | ||
1528 | } | ||
1529 | if (global_reclaim(sc)) { | 1525 | if (global_reclaim(sc)) { |
1530 | zone->pages_scanned += nr_scanned; | 1526 | zone->pages_scanned += nr_scanned; |
1531 | if (current_is_kswapd()) | 1527 | if (current_is_kswapd()) |
@@ -1625,13 +1621,15 @@ static void move_active_pages_to_lru(struct zone *zone, | |||
1625 | pagevec_init(&pvec, 1); | 1621 | pagevec_init(&pvec, 1); |
1626 | 1622 | ||
1627 | while (!list_empty(list)) { | 1623 | while (!list_empty(list)) { |
1624 | struct lruvec *lruvec; | ||
1625 | |||
1628 | page = lru_to_page(list); | 1626 | page = lru_to_page(list); |
1629 | 1627 | ||
1630 | VM_BUG_ON(PageLRU(page)); | 1628 | VM_BUG_ON(PageLRU(page)); |
1631 | SetPageLRU(page); | 1629 | SetPageLRU(page); |
1632 | 1630 | ||
1633 | list_move(&page->lru, &zone->lruvec.lists[lru]); | 1631 | lruvec = mem_cgroup_lru_add_list(zone, page, lru); |
1634 | mem_cgroup_add_lru_list(page, lru); | 1632 | list_move(&page->lru, &lruvec->lists[lru]); |
1635 | pgmoved += hpage_nr_pages(page); | 1633 | pgmoved += hpage_nr_pages(page); |
1636 | 1634 | ||
1637 | if (!pagevec_add(&pvec, page) || list_empty(list)) { | 1635 | if (!pagevec_add(&pvec, page) || list_empty(list)) { |
@@ -1672,17 +1670,10 @@ static void shrink_active_list(unsigned long nr_pages, | |||
1672 | reclaim_mode |= ISOLATE_CLEAN; | 1670 | reclaim_mode |= ISOLATE_CLEAN; |
1673 | 1671 | ||
1674 | spin_lock_irq(&zone->lru_lock); | 1672 | spin_lock_irq(&zone->lru_lock); |
1675 | if (scanning_global_lru(mz)) { | 1673 | |
1676 | nr_taken = isolate_pages_global(nr_pages, &l_hold, | 1674 | nr_taken = isolate_pages(nr_pages, mz, &l_hold, |
1677 | &pgscanned, sc->order, | 1675 | &pgscanned, sc->order, |
1678 | reclaim_mode, zone, | 1676 | reclaim_mode, 1, file); |
1679 | 1, file); | ||
1680 | } else { | ||
1681 | nr_taken = mem_cgroup_isolate_pages(nr_pages, &l_hold, | ||
1682 | &pgscanned, sc->order, | ||
1683 | reclaim_mode, zone, | ||
1684 | mz->mem_cgroup, 1, file); | ||
1685 | } | ||
1686 | 1677 | ||
1687 | if (global_reclaim(sc)) | 1678 | if (global_reclaim(sc)) |
1688 | zone->pages_scanned += pgscanned; | 1679 | zone->pages_scanned += pgscanned; |
@@ -3440,16 +3431,18 @@ int page_evictable(struct page *page, struct vm_area_struct *vma) | |||
3440 | */ | 3431 | */ |
3441 | static void check_move_unevictable_page(struct page *page, struct zone *zone) | 3432 | static void check_move_unevictable_page(struct page *page, struct zone *zone) |
3442 | { | 3433 | { |
3443 | VM_BUG_ON(PageActive(page)); | 3434 | struct lruvec *lruvec; |
3444 | 3435 | ||
3436 | VM_BUG_ON(PageActive(page)); | ||
3445 | retry: | 3437 | retry: |
3446 | ClearPageUnevictable(page); | 3438 | ClearPageUnevictable(page); |
3447 | if (page_evictable(page, NULL)) { | 3439 | if (page_evictable(page, NULL)) { |
3448 | enum lru_list l = page_lru_base_type(page); | 3440 | enum lru_list l = page_lru_base_type(page); |
3449 | 3441 | ||
3450 | __dec_zone_state(zone, NR_UNEVICTABLE); | 3442 | __dec_zone_state(zone, NR_UNEVICTABLE); |
3451 | list_move(&page->lru, &zone->lruvec.lists[l]); | 3443 | lruvec = mem_cgroup_lru_move_lists(zone, page, |
3452 | mem_cgroup_move_lists(page, LRU_UNEVICTABLE, l); | 3444 | LRU_UNEVICTABLE, l); |
3445 | list_move(&page->lru, &lruvec->lists[l]); | ||
3453 | __inc_zone_state(zone, NR_INACTIVE_ANON + l); | 3446 | __inc_zone_state(zone, NR_INACTIVE_ANON + l); |
3454 | __count_vm_event(UNEVICTABLE_PGRESCUED); | 3447 | __count_vm_event(UNEVICTABLE_PGRESCUED); |
3455 | } else { | 3448 | } else { |
@@ -3457,8 +3450,9 @@ retry: | |||
3457 | * rotate unevictable list | 3450 | * rotate unevictable list |
3458 | */ | 3451 | */ |
3459 | SetPageUnevictable(page); | 3452 | SetPageUnevictable(page); |
3460 | list_move(&page->lru, &zone->lruvec.lists[LRU_UNEVICTABLE]); | 3453 | lruvec = mem_cgroup_lru_move_lists(zone, page, LRU_UNEVICTABLE, |
3461 | mem_cgroup_rotate_lru_list(page, LRU_UNEVICTABLE); | 3454 | LRU_UNEVICTABLE); |
3455 | list_move(&page->lru, &lruvec->lists[LRU_UNEVICTABLE]); | ||
3462 | if (page_evictable(page, NULL)) | 3456 | if (page_evictable(page, NULL)) |
3463 | goto retry; | 3457 | goto retry; |
3464 | } | 3458 | } |