diff options
Diffstat (limited to 'mm/memcontrol.c')
| -rw-r--r-- | mm/memcontrol.c | 128 |
1 files changed, 65 insertions, 63 deletions
diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 90ccc1326356..da5912b84551 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c | |||
| @@ -524,7 +524,8 @@ unsigned long mem_cgroup_isolate_pages(unsigned long nr_to_scan, | |||
| 524 | * < 0 if the cgroup is over its limit | 524 | * < 0 if the cgroup is over its limit |
| 525 | */ | 525 | */ |
| 526 | static int mem_cgroup_charge_common(struct page *page, struct mm_struct *mm, | 526 | static int mem_cgroup_charge_common(struct page *page, struct mm_struct *mm, |
| 527 | gfp_t gfp_mask, enum charge_type ctype) | 527 | gfp_t gfp_mask, enum charge_type ctype, |
| 528 | struct mem_cgroup *memcg) | ||
| 528 | { | 529 | { |
| 529 | struct mem_cgroup *mem; | 530 | struct mem_cgroup *mem; |
| 530 | struct page_cgroup *pc; | 531 | struct page_cgroup *pc; |
| @@ -569,16 +570,21 @@ retry: | |||
| 569 | * thread group leader migrates. It's possible that mm is not | 570 | * thread group leader migrates. It's possible that mm is not |
| 570 | * set, if so charge the init_mm (happens for pagecache usage). | 571 | * set, if so charge the init_mm (happens for pagecache usage). |
| 571 | */ | 572 | */ |
| 572 | if (!mm) | 573 | if (!memcg) { |
| 573 | mm = &init_mm; | 574 | if (!mm) |
| 575 | mm = &init_mm; | ||
| 574 | 576 | ||
| 575 | rcu_read_lock(); | 577 | rcu_read_lock(); |
| 576 | mem = mem_cgroup_from_task(rcu_dereference(mm->owner)); | 578 | mem = mem_cgroup_from_task(rcu_dereference(mm->owner)); |
| 577 | /* | 579 | /* |
| 578 | * For every charge from the cgroup, increment reference count | 580 | * For every charge from the cgroup, increment reference count |
| 579 | */ | 581 | */ |
| 580 | css_get(&mem->css); | 582 | css_get(&mem->css); |
| 581 | rcu_read_unlock(); | 583 | rcu_read_unlock(); |
| 584 | } else { | ||
| 585 | mem = memcg; | ||
| 586 | css_get(&memcg->css); | ||
| 587 | } | ||
| 582 | 588 | ||
| 583 | while (res_counter_charge(&mem->res, PAGE_SIZE)) { | 589 | while (res_counter_charge(&mem->res, PAGE_SIZE)) { |
| 584 | if (!(gfp_mask & __GFP_WAIT)) | 590 | if (!(gfp_mask & __GFP_WAIT)) |
| @@ -648,7 +654,7 @@ err: | |||
| 648 | int mem_cgroup_charge(struct page *page, struct mm_struct *mm, gfp_t gfp_mask) | 654 | int mem_cgroup_charge(struct page *page, struct mm_struct *mm, gfp_t gfp_mask) |
| 649 | { | 655 | { |
| 650 | return mem_cgroup_charge_common(page, mm, gfp_mask, | 656 | return mem_cgroup_charge_common(page, mm, gfp_mask, |
| 651 | MEM_CGROUP_CHARGE_TYPE_MAPPED); | 657 | MEM_CGROUP_CHARGE_TYPE_MAPPED, NULL); |
| 652 | } | 658 | } |
| 653 | 659 | ||
| 654 | int mem_cgroup_cache_charge(struct page *page, struct mm_struct *mm, | 660 | int mem_cgroup_cache_charge(struct page *page, struct mm_struct *mm, |
| @@ -657,7 +663,22 @@ int mem_cgroup_cache_charge(struct page *page, struct mm_struct *mm, | |||
| 657 | if (!mm) | 663 | if (!mm) |
| 658 | mm = &init_mm; | 664 | mm = &init_mm; |
| 659 | return mem_cgroup_charge_common(page, mm, gfp_mask, | 665 | return mem_cgroup_charge_common(page, mm, gfp_mask, |
| 660 | MEM_CGROUP_CHARGE_TYPE_CACHE); | 666 | MEM_CGROUP_CHARGE_TYPE_CACHE, NULL); |
| 667 | } | ||
| 668 | |||
| 669 | int mem_cgroup_getref(struct page *page) | ||
| 670 | { | ||
| 671 | struct page_cgroup *pc; | ||
| 672 | |||
| 673 | if (mem_cgroup_subsys.disabled) | ||
| 674 | return 0; | ||
| 675 | |||
| 676 | lock_page_cgroup(page); | ||
| 677 | pc = page_get_page_cgroup(page); | ||
| 678 | VM_BUG_ON(!pc); | ||
| 679 | pc->ref_cnt++; | ||
| 680 | unlock_page_cgroup(page); | ||
| 681 | return 0; | ||
| 661 | } | 682 | } |
| 662 | 683 | ||
| 663 | /* | 684 | /* |
| @@ -707,65 +728,39 @@ unlock: | |||
| 707 | } | 728 | } |
| 708 | 729 | ||
| 709 | /* | 730 | /* |
| 710 | * Returns non-zero if a page (under migration) has valid page_cgroup member. | 731 | * Before starting migration, account against new page. |
| 711 | * Refcnt of page_cgroup is incremented. | ||
| 712 | */ | 732 | */ |
| 713 | int mem_cgroup_prepare_migration(struct page *page) | 733 | int mem_cgroup_prepare_migration(struct page *page, struct page *newpage) |
| 714 | { | 734 | { |
| 715 | struct page_cgroup *pc; | 735 | struct page_cgroup *pc; |
| 736 | struct mem_cgroup *mem = NULL; | ||
| 737 | enum charge_type ctype = MEM_CGROUP_CHARGE_TYPE_MAPPED; | ||
| 738 | int ret = 0; | ||
| 716 | 739 | ||
| 717 | if (mem_cgroup_subsys.disabled) | 740 | if (mem_cgroup_subsys.disabled) |
| 718 | return 0; | 741 | return 0; |
| 719 | 742 | ||
| 720 | lock_page_cgroup(page); | 743 | lock_page_cgroup(page); |
| 721 | pc = page_get_page_cgroup(page); | 744 | pc = page_get_page_cgroup(page); |
| 722 | if (pc) | 745 | if (pc) { |
| 723 | pc->ref_cnt++; | 746 | mem = pc->mem_cgroup; |
| 747 | css_get(&mem->css); | ||
| 748 | if (pc->flags & PAGE_CGROUP_FLAG_CACHE) | ||
| 749 | ctype = MEM_CGROUP_CHARGE_TYPE_CACHE; | ||
| 750 | } | ||
| 724 | unlock_page_cgroup(page); | 751 | unlock_page_cgroup(page); |
| 725 | return pc != NULL; | 752 | if (mem) { |
| 726 | } | 753 | ret = mem_cgroup_charge_common(newpage, NULL, GFP_KERNEL, |
| 727 | 754 | ctype, mem); | |
| 728 | void mem_cgroup_end_migration(struct page *page) | 755 | css_put(&mem->css); |
| 729 | { | 756 | } |
| 730 | mem_cgroup_uncharge_page(page); | 757 | return ret; |
| 731 | } | 758 | } |
| 732 | 759 | ||
| 733 | /* | 760 | /* remove redundant charge */ |
| 734 | * We know both *page* and *newpage* are now not-on-LRU and PG_locked. | 761 | void mem_cgroup_end_migration(struct page *newpage) |
| 735 | * And no race with uncharge() routines because page_cgroup for *page* | ||
| 736 | * has extra one reference by mem_cgroup_prepare_migration. | ||
| 737 | */ | ||
| 738 | void mem_cgroup_page_migration(struct page *page, struct page *newpage) | ||
| 739 | { | 762 | { |
| 740 | struct page_cgroup *pc; | 763 | mem_cgroup_uncharge_page(newpage); |
| 741 | struct mem_cgroup_per_zone *mz; | ||
| 742 | unsigned long flags; | ||
| 743 | |||
| 744 | lock_page_cgroup(page); | ||
| 745 | pc = page_get_page_cgroup(page); | ||
| 746 | if (!pc) { | ||
| 747 | unlock_page_cgroup(page); | ||
| 748 | return; | ||
| 749 | } | ||
| 750 | |||
| 751 | mz = page_cgroup_zoneinfo(pc); | ||
| 752 | spin_lock_irqsave(&mz->lru_lock, flags); | ||
| 753 | __mem_cgroup_remove_list(mz, pc); | ||
| 754 | spin_unlock_irqrestore(&mz->lru_lock, flags); | ||
| 755 | |||
| 756 | page_assign_page_cgroup(page, NULL); | ||
| 757 | unlock_page_cgroup(page); | ||
| 758 | |||
| 759 | pc->page = newpage; | ||
| 760 | lock_page_cgroup(newpage); | ||
| 761 | page_assign_page_cgroup(newpage, pc); | ||
| 762 | |||
| 763 | mz = page_cgroup_zoneinfo(pc); | ||
| 764 | spin_lock_irqsave(&mz->lru_lock, flags); | ||
| 765 | __mem_cgroup_add_list(mz, pc); | ||
| 766 | spin_unlock_irqrestore(&mz->lru_lock, flags); | ||
| 767 | |||
| 768 | unlock_page_cgroup(newpage); | ||
| 769 | } | 764 | } |
| 770 | 765 | ||
| 771 | /* | 766 | /* |
| @@ -795,12 +790,19 @@ static void mem_cgroup_force_empty_list(struct mem_cgroup *mem, | |||
| 795 | page = pc->page; | 790 | page = pc->page; |
| 796 | get_page(page); | 791 | get_page(page); |
| 797 | spin_unlock_irqrestore(&mz->lru_lock, flags); | 792 | spin_unlock_irqrestore(&mz->lru_lock, flags); |
| 798 | mem_cgroup_uncharge_page(page); | 793 | /* |
| 799 | put_page(page); | 794 | * Check if this page is on LRU. !LRU page can be found |
| 800 | if (--count <= 0) { | 795 | * if it's under page migration. |
| 801 | count = FORCE_UNCHARGE_BATCH; | 796 | */ |
| 797 | if (PageLRU(page)) { | ||
| 798 | mem_cgroup_uncharge_page(page); | ||
| 799 | put_page(page); | ||
| 800 | if (--count <= 0) { | ||
| 801 | count = FORCE_UNCHARGE_BATCH; | ||
| 802 | cond_resched(); | ||
| 803 | } | ||
| 804 | } else | ||
| 802 | cond_resched(); | 805 | cond_resched(); |
| 803 | } | ||
| 804 | spin_lock_irqsave(&mz->lru_lock, flags); | 806 | spin_lock_irqsave(&mz->lru_lock, flags); |
| 805 | } | 807 | } |
| 806 | spin_unlock_irqrestore(&mz->lru_lock, flags); | 808 | spin_unlock_irqrestore(&mz->lru_lock, flags); |
