diff options
Diffstat (limited to 'mm')
-rw-r--r-- | mm/memcontrol.c | 122 |
1 files changed, 82 insertions, 40 deletions
diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 10846b9656aa..031682e7ef0c 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c | |||
@@ -157,12 +157,46 @@ struct page_cgroup { | |||
157 | struct list_head lru; /* per cgroup LRU list */ | 157 | struct list_head lru; /* per cgroup LRU list */ |
158 | struct page *page; | 158 | struct page *page; |
159 | struct mem_cgroup *mem_cgroup; | 159 | struct mem_cgroup *mem_cgroup; |
160 | int flags; | 160 | unsigned long flags; |
161 | }; | ||
162 | |||
163 | enum { | ||
164 | /* flags for mem_cgroup */ | ||
165 | PCG_CACHE, /* charged as cache */ | ||
166 | /* flags for LRU placement */ | ||
167 | PCG_ACTIVE, /* page is active in this cgroup */ | ||
168 | PCG_FILE, /* page is file system backed */ | ||
169 | PCG_UNEVICTABLE, /* page is unevictableable */ | ||
161 | }; | 170 | }; |
162 | #define PAGE_CGROUP_FLAG_CACHE (0x1) /* charged as cache */ | 171 | |
163 | #define PAGE_CGROUP_FLAG_ACTIVE (0x2) /* page is active in this cgroup */ | 172 | #define TESTPCGFLAG(uname, lname) \ |
164 | #define PAGE_CGROUP_FLAG_FILE (0x4) /* page is file system backed */ | 173 | static inline int PageCgroup##uname(struct page_cgroup *pc) \ |
165 | #define PAGE_CGROUP_FLAG_UNEVICTABLE (0x8) /* page is unevictableable */ | 174 | { return test_bit(PCG_##lname, &pc->flags); } |
175 | |||
176 | #define SETPCGFLAG(uname, lname) \ | ||
177 | static inline void SetPageCgroup##uname(struct page_cgroup *pc)\ | ||
178 | { set_bit(PCG_##lname, &pc->flags); } | ||
179 | |||
180 | #define CLEARPCGFLAG(uname, lname) \ | ||
181 | static inline void ClearPageCgroup##uname(struct page_cgroup *pc) \ | ||
182 | { clear_bit(PCG_##lname, &pc->flags); } | ||
183 | |||
184 | |||
185 | /* Cache flag is set only once (at allocation) */ | ||
186 | TESTPCGFLAG(Cache, CACHE) | ||
187 | |||
188 | /* LRU management flags (from global-lru definition) */ | ||
189 | TESTPCGFLAG(File, FILE) | ||
190 | SETPCGFLAG(File, FILE) | ||
191 | CLEARPCGFLAG(File, FILE) | ||
192 | |||
193 | TESTPCGFLAG(Active, ACTIVE) | ||
194 | SETPCGFLAG(Active, ACTIVE) | ||
195 | CLEARPCGFLAG(Active, ACTIVE) | ||
196 | |||
197 | TESTPCGFLAG(Unevictable, UNEVICTABLE) | ||
198 | SETPCGFLAG(Unevictable, UNEVICTABLE) | ||
199 | CLEARPCGFLAG(Unevictable, UNEVICTABLE) | ||
166 | 200 | ||
167 | static int page_cgroup_nid(struct page_cgroup *pc) | 201 | static int page_cgroup_nid(struct page_cgroup *pc) |
168 | { | 202 | { |
@@ -177,15 +211,25 @@ static enum zone_type page_cgroup_zid(struct page_cgroup *pc) | |||
177 | enum charge_type { | 211 | enum charge_type { |
178 | MEM_CGROUP_CHARGE_TYPE_CACHE = 0, | 212 | MEM_CGROUP_CHARGE_TYPE_CACHE = 0, |
179 | MEM_CGROUP_CHARGE_TYPE_MAPPED, | 213 | MEM_CGROUP_CHARGE_TYPE_MAPPED, |
180 | MEM_CGROUP_CHARGE_TYPE_FORCE, /* used by force_empty */ | ||
181 | MEM_CGROUP_CHARGE_TYPE_SHMEM, /* used by page migration of shmem */ | 214 | MEM_CGROUP_CHARGE_TYPE_SHMEM, /* used by page migration of shmem */ |
215 | MEM_CGROUP_CHARGE_TYPE_FORCE, /* used by force_empty */ | ||
216 | NR_CHARGE_TYPE, | ||
217 | }; | ||
218 | |||
219 | static const unsigned long | ||
220 | pcg_default_flags[NR_CHARGE_TYPE] = { | ||
221 | ((1 << PCG_CACHE) | (1 << PCG_FILE)), | ||
222 | ((1 << PCG_ACTIVE)), | ||
223 | ((1 << PCG_ACTIVE) | (1 << PCG_CACHE)), | ||
224 | 0, | ||
182 | }; | 225 | }; |
183 | 226 | ||
184 | /* | 227 | /* |
185 | * Always modified under lru lock. Then, not necessary to preempt_disable() | 228 | * Always modified under lru lock. Then, not necessary to preempt_disable() |
186 | */ | 229 | */ |
187 | static void mem_cgroup_charge_statistics(struct mem_cgroup *mem, int flags, | 230 | static void mem_cgroup_charge_statistics(struct mem_cgroup *mem, |
188 | bool charge) | 231 | struct page_cgroup *pc, |
232 | bool charge) | ||
189 | { | 233 | { |
190 | int val = (charge)? 1 : -1; | 234 | int val = (charge)? 1 : -1; |
191 | struct mem_cgroup_stat *stat = &mem->stat; | 235 | struct mem_cgroup_stat *stat = &mem->stat; |
@@ -194,7 +238,7 @@ static void mem_cgroup_charge_statistics(struct mem_cgroup *mem, int flags, | |||
194 | VM_BUG_ON(!irqs_disabled()); | 238 | VM_BUG_ON(!irqs_disabled()); |
195 | 239 | ||
196 | cpustat = &stat->cpustat[smp_processor_id()]; | 240 | cpustat = &stat->cpustat[smp_processor_id()]; |
197 | if (flags & PAGE_CGROUP_FLAG_CACHE) | 241 | if (PageCgroupCache(pc)) |
198 | __mem_cgroup_stat_add_safe(cpustat, MEM_CGROUP_STAT_CACHE, val); | 242 | __mem_cgroup_stat_add_safe(cpustat, MEM_CGROUP_STAT_CACHE, val); |
199 | else | 243 | else |
200 | __mem_cgroup_stat_add_safe(cpustat, MEM_CGROUP_STAT_RSS, val); | 244 | __mem_cgroup_stat_add_safe(cpustat, MEM_CGROUP_STAT_RSS, val); |
@@ -295,18 +339,18 @@ static void __mem_cgroup_remove_list(struct mem_cgroup_per_zone *mz, | |||
295 | { | 339 | { |
296 | int lru = LRU_BASE; | 340 | int lru = LRU_BASE; |
297 | 341 | ||
298 | if (pc->flags & PAGE_CGROUP_FLAG_UNEVICTABLE) | 342 | if (PageCgroupUnevictable(pc)) |
299 | lru = LRU_UNEVICTABLE; | 343 | lru = LRU_UNEVICTABLE; |
300 | else { | 344 | else { |
301 | if (pc->flags & PAGE_CGROUP_FLAG_ACTIVE) | 345 | if (PageCgroupActive(pc)) |
302 | lru += LRU_ACTIVE; | 346 | lru += LRU_ACTIVE; |
303 | if (pc->flags & PAGE_CGROUP_FLAG_FILE) | 347 | if (PageCgroupFile(pc)) |
304 | lru += LRU_FILE; | 348 | lru += LRU_FILE; |
305 | } | 349 | } |
306 | 350 | ||
307 | MEM_CGROUP_ZSTAT(mz, lru) -= 1; | 351 | MEM_CGROUP_ZSTAT(mz, lru) -= 1; |
308 | 352 | ||
309 | mem_cgroup_charge_statistics(pc->mem_cgroup, pc->flags, false); | 353 | mem_cgroup_charge_statistics(pc->mem_cgroup, pc, false); |
310 | list_del(&pc->lru); | 354 | list_del(&pc->lru); |
311 | } | 355 | } |
312 | 356 | ||
@@ -315,27 +359,27 @@ static void __mem_cgroup_add_list(struct mem_cgroup_per_zone *mz, | |||
315 | { | 359 | { |
316 | int lru = LRU_BASE; | 360 | int lru = LRU_BASE; |
317 | 361 | ||
318 | if (pc->flags & PAGE_CGROUP_FLAG_UNEVICTABLE) | 362 | if (PageCgroupUnevictable(pc)) |
319 | lru = LRU_UNEVICTABLE; | 363 | lru = LRU_UNEVICTABLE; |
320 | else { | 364 | else { |
321 | if (pc->flags & PAGE_CGROUP_FLAG_ACTIVE) | 365 | if (PageCgroupActive(pc)) |
322 | lru += LRU_ACTIVE; | 366 | lru += LRU_ACTIVE; |
323 | if (pc->flags & PAGE_CGROUP_FLAG_FILE) | 367 | if (PageCgroupFile(pc)) |
324 | lru += LRU_FILE; | 368 | lru += LRU_FILE; |
325 | } | 369 | } |
326 | 370 | ||
327 | MEM_CGROUP_ZSTAT(mz, lru) += 1; | 371 | MEM_CGROUP_ZSTAT(mz, lru) += 1; |
328 | list_add(&pc->lru, &mz->lists[lru]); | 372 | list_add(&pc->lru, &mz->lists[lru]); |
329 | 373 | ||
330 | mem_cgroup_charge_statistics(pc->mem_cgroup, pc->flags, true); | 374 | mem_cgroup_charge_statistics(pc->mem_cgroup, pc, true); |
331 | } | 375 | } |
332 | 376 | ||
333 | static void __mem_cgroup_move_lists(struct page_cgroup *pc, enum lru_list lru) | 377 | static void __mem_cgroup_move_lists(struct page_cgroup *pc, enum lru_list lru) |
334 | { | 378 | { |
335 | struct mem_cgroup_per_zone *mz = page_cgroup_zoneinfo(pc); | 379 | struct mem_cgroup_per_zone *mz = page_cgroup_zoneinfo(pc); |
336 | int active = pc->flags & PAGE_CGROUP_FLAG_ACTIVE; | 380 | int active = PageCgroupActive(pc); |
337 | int file = pc->flags & PAGE_CGROUP_FLAG_FILE; | 381 | int file = PageCgroupFile(pc); |
338 | int unevictable = pc->flags & PAGE_CGROUP_FLAG_UNEVICTABLE; | 382 | int unevictable = PageCgroupUnevictable(pc); |
339 | enum lru_list from = unevictable ? LRU_UNEVICTABLE : | 383 | enum lru_list from = unevictable ? LRU_UNEVICTABLE : |
340 | (LRU_FILE * !!file + !!active); | 384 | (LRU_FILE * !!file + !!active); |
341 | 385 | ||
@@ -343,16 +387,20 @@ static void __mem_cgroup_move_lists(struct page_cgroup *pc, enum lru_list lru) | |||
343 | return; | 387 | return; |
344 | 388 | ||
345 | MEM_CGROUP_ZSTAT(mz, from) -= 1; | 389 | MEM_CGROUP_ZSTAT(mz, from) -= 1; |
346 | 390 | /* | |
391 | * However this is done under mz->lru_lock, another flags, which | ||
392 | * are not related to LRU, will be modified from out-of-lock. | ||
393 | * We have to use atomic set/clear flags. | ||
394 | */ | ||
347 | if (is_unevictable_lru(lru)) { | 395 | if (is_unevictable_lru(lru)) { |
348 | pc->flags &= ~PAGE_CGROUP_FLAG_ACTIVE; | 396 | ClearPageCgroupActive(pc); |
349 | pc->flags |= PAGE_CGROUP_FLAG_UNEVICTABLE; | 397 | SetPageCgroupUnevictable(pc); |
350 | } else { | 398 | } else { |
351 | if (is_active_lru(lru)) | 399 | if (is_active_lru(lru)) |
352 | pc->flags |= PAGE_CGROUP_FLAG_ACTIVE; | 400 | SetPageCgroupActive(pc); |
353 | else | 401 | else |
354 | pc->flags &= ~PAGE_CGROUP_FLAG_ACTIVE; | 402 | ClearPageCgroupActive(pc); |
355 | pc->flags &= ~PAGE_CGROUP_FLAG_UNEVICTABLE; | 403 | ClearPageCgroupUnevictable(pc); |
356 | } | 404 | } |
357 | 405 | ||
358 | MEM_CGROUP_ZSTAT(mz, lru) += 1; | 406 | MEM_CGROUP_ZSTAT(mz, lru) += 1; |
@@ -589,16 +637,7 @@ static int mem_cgroup_charge_common(struct page *page, struct mm_struct *mm, | |||
589 | * If a page is accounted as a page cache, insert to inactive list. | 637 | * If a page is accounted as a page cache, insert to inactive list. |
590 | * If anon, insert to active list. | 638 | * If anon, insert to active list. |
591 | */ | 639 | */ |
592 | if (ctype == MEM_CGROUP_CHARGE_TYPE_CACHE) { | 640 | pc->flags = pcg_default_flags[ctype]; |
593 | pc->flags = PAGE_CGROUP_FLAG_CACHE; | ||
594 | if (page_is_file_cache(page)) | ||
595 | pc->flags |= PAGE_CGROUP_FLAG_FILE; | ||
596 | else | ||
597 | pc->flags |= PAGE_CGROUP_FLAG_ACTIVE; | ||
598 | } else if (ctype == MEM_CGROUP_CHARGE_TYPE_MAPPED) | ||
599 | pc->flags = PAGE_CGROUP_FLAG_ACTIVE; | ||
600 | else /* MEM_CGROUP_CHARGE_TYPE_SHMEM */ | ||
601 | pc->flags = PAGE_CGROUP_FLAG_CACHE | PAGE_CGROUP_FLAG_ACTIVE; | ||
602 | 641 | ||
603 | lock_page_cgroup(page); | 642 | lock_page_cgroup(page); |
604 | if (unlikely(page_get_page_cgroup(page))) { | 643 | if (unlikely(page_get_page_cgroup(page))) { |
@@ -677,8 +716,12 @@ int mem_cgroup_cache_charge(struct page *page, struct mm_struct *mm, | |||
677 | if (unlikely(!mm)) | 716 | if (unlikely(!mm)) |
678 | mm = &init_mm; | 717 | mm = &init_mm; |
679 | 718 | ||
680 | return mem_cgroup_charge_common(page, mm, gfp_mask, | 719 | if (page_is_file_cache(page)) |
720 | return mem_cgroup_charge_common(page, mm, gfp_mask, | ||
681 | MEM_CGROUP_CHARGE_TYPE_CACHE, NULL); | 721 | MEM_CGROUP_CHARGE_TYPE_CACHE, NULL); |
722 | else | ||
723 | return mem_cgroup_charge_common(page, mm, gfp_mask, | ||
724 | MEM_CGROUP_CHARGE_TYPE_SHMEM, NULL); | ||
682 | } | 725 | } |
683 | 726 | ||
684 | /* | 727 | /* |
@@ -706,8 +749,7 @@ __mem_cgroup_uncharge_common(struct page *page, enum charge_type ctype) | |||
706 | VM_BUG_ON(pc->page != page); | 749 | VM_BUG_ON(pc->page != page); |
707 | 750 | ||
708 | if ((ctype == MEM_CGROUP_CHARGE_TYPE_MAPPED) | 751 | if ((ctype == MEM_CGROUP_CHARGE_TYPE_MAPPED) |
709 | && ((pc->flags & PAGE_CGROUP_FLAG_CACHE) | 752 | && ((PageCgroupCache(pc) || page_mapped(page)))) |
710 | || page_mapped(page))) | ||
711 | goto unlock; | 753 | goto unlock; |
712 | 754 | ||
713 | mz = page_cgroup_zoneinfo(pc); | 755 | mz = page_cgroup_zoneinfo(pc); |
@@ -758,7 +800,7 @@ int mem_cgroup_prepare_migration(struct page *page, struct page *newpage) | |||
758 | if (pc) { | 800 | if (pc) { |
759 | mem = pc->mem_cgroup; | 801 | mem = pc->mem_cgroup; |
760 | css_get(&mem->css); | 802 | css_get(&mem->css); |
761 | if (pc->flags & PAGE_CGROUP_FLAG_CACHE) { | 803 | if (PageCgroupCache(pc)) { |
762 | if (page_is_file_cache(page)) | 804 | if (page_is_file_cache(page)) |
763 | ctype = MEM_CGROUP_CHARGE_TYPE_CACHE; | 805 | ctype = MEM_CGROUP_CHARGE_TYPE_CACHE; |
764 | else | 806 | else |