diff options
author | KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com> | 2009-01-07 21:07:50 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-01-08 11:31:04 -0500 |
commit | 01b1ae63c2270cbacfd43fea94578c17950eb548 (patch) | |
tree | ab0275f32e8548c4413014d43cab1f52f03c9c5c /mm/memcontrol.c | |
parent | bced0520fe462bb94021dcabd32e99630c171be2 (diff) |
memcg: simple migration handling
Now, management of "charge" under page migration is done under following
manner. (Assume migrate page contents from oldpage to newpage)
before
- "newpage" is charged before migration.
at success.
- "oldpage" is uncharged at somewhere(unmap, radix-tree-replace)
at failure
- "newpage" is uncharged.
- "oldpage" is charged if necessary (*1)
But (*1) is not reliable....because of GFP_ATOMIC.
This patch tries to change behavior as following by charge/commit/cancel ops.
before
- charge PAGE_SIZE (no target page)
success
- commit charge against "newpage".
failure
- commit charge against "oldpage".
(PCG_USED bit works effectively to avoid double-counting)
- if "oldpage" is obsolete, cancel charge of PAGE_SIZE.
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <balbir@in.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm/memcontrol.c')
-rw-r--r-- | mm/memcontrol.c | 108 |
1 files changed, 52 insertions, 56 deletions
diff --git a/mm/memcontrol.c b/mm/memcontrol.c index c34eb52bdc3f..b71195e8198b 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c | |||
@@ -627,34 +627,6 @@ int mem_cgroup_newpage_charge(struct page *page, | |||
627 | MEM_CGROUP_CHARGE_TYPE_MAPPED, NULL); | 627 | MEM_CGROUP_CHARGE_TYPE_MAPPED, NULL); |
628 | } | 628 | } |
629 | 629 | ||
630 | /* | ||
631 | * same as mem_cgroup_newpage_charge(), now. | ||
632 | * But what we assume is different from newpage, and this is special case. | ||
633 | * treat this in special function. easy for maintenance. | ||
634 | */ | ||
635 | |||
636 | int mem_cgroup_charge_migrate_fixup(struct page *page, | ||
637 | struct mm_struct *mm, gfp_t gfp_mask) | ||
638 | { | ||
639 | if (mem_cgroup_subsys.disabled) | ||
640 | return 0; | ||
641 | |||
642 | if (PageCompound(page)) | ||
643 | return 0; | ||
644 | |||
645 | if (page_mapped(page) || (page->mapping && !PageAnon(page))) | ||
646 | return 0; | ||
647 | |||
648 | if (unlikely(!mm)) | ||
649 | mm = &init_mm; | ||
650 | |||
651 | return mem_cgroup_charge_common(page, mm, gfp_mask, | ||
652 | MEM_CGROUP_CHARGE_TYPE_MAPPED, NULL); | ||
653 | } | ||
654 | |||
655 | |||
656 | |||
657 | |||
658 | int mem_cgroup_cache_charge(struct page *page, struct mm_struct *mm, | 630 | int mem_cgroup_cache_charge(struct page *page, struct mm_struct *mm, |
659 | gfp_t gfp_mask) | 631 | gfp_t gfp_mask) |
660 | { | 632 | { |
@@ -697,7 +669,6 @@ int mem_cgroup_cache_charge(struct page *page, struct mm_struct *mm, | |||
697 | MEM_CGROUP_CHARGE_TYPE_SHMEM, NULL); | 669 | MEM_CGROUP_CHARGE_TYPE_SHMEM, NULL); |
698 | } | 670 | } |
699 | 671 | ||
700 | |||
701 | void mem_cgroup_commit_charge_swapin(struct page *page, struct mem_cgroup *ptr) | 672 | void mem_cgroup_commit_charge_swapin(struct page *page, struct mem_cgroup *ptr) |
702 | { | 673 | { |
703 | struct page_cgroup *pc; | 674 | struct page_cgroup *pc; |
@@ -782,13 +753,13 @@ void mem_cgroup_uncharge_cache_page(struct page *page) | |||
782 | } | 753 | } |
783 | 754 | ||
784 | /* | 755 | /* |
785 | * Before starting migration, account against new page. | 756 | * Before starting migration, account PAGE_SIZE to mem_cgroup that the old |
757 | * page belongs to. | ||
786 | */ | 758 | */ |
787 | int mem_cgroup_prepare_migration(struct page *page, struct page *newpage) | 759 | int mem_cgroup_prepare_migration(struct page *page, struct mem_cgroup **ptr) |
788 | { | 760 | { |
789 | struct page_cgroup *pc; | 761 | struct page_cgroup *pc; |
790 | struct mem_cgroup *mem = NULL; | 762 | struct mem_cgroup *mem = NULL; |
791 | enum charge_type ctype = MEM_CGROUP_CHARGE_TYPE_MAPPED; | ||
792 | int ret = 0; | 763 | int ret = 0; |
793 | 764 | ||
794 | if (mem_cgroup_subsys.disabled) | 765 | if (mem_cgroup_subsys.disabled) |
@@ -799,42 +770,67 @@ int mem_cgroup_prepare_migration(struct page *page, struct page *newpage) | |||
799 | if (PageCgroupUsed(pc)) { | 770 | if (PageCgroupUsed(pc)) { |
800 | mem = pc->mem_cgroup; | 771 | mem = pc->mem_cgroup; |
801 | css_get(&mem->css); | 772 | css_get(&mem->css); |
802 | if (PageCgroupCache(pc)) { | ||
803 | if (page_is_file_cache(page)) | ||
804 | ctype = MEM_CGROUP_CHARGE_TYPE_CACHE; | ||
805 | else | ||
806 | ctype = MEM_CGROUP_CHARGE_TYPE_SHMEM; | ||
807 | } | ||
808 | } | 773 | } |
809 | unlock_page_cgroup(pc); | 774 | unlock_page_cgroup(pc); |
775 | |||
810 | if (mem) { | 776 | if (mem) { |
811 | ret = mem_cgroup_charge_common(newpage, NULL, | 777 | ret = mem_cgroup_try_charge(NULL, GFP_HIGHUSER_MOVABLE, &mem); |
812 | GFP_HIGHUSER_MOVABLE, | ||
813 | ctype, mem); | ||
814 | css_put(&mem->css); | 778 | css_put(&mem->css); |
815 | } | 779 | } |
780 | *ptr = mem; | ||
816 | return ret; | 781 | return ret; |
817 | } | 782 | } |
818 | 783 | ||
819 | /* remove redundant charge if migration failed*/ | 784 | /* remove redundant charge if migration failed*/ |
820 | void mem_cgroup_end_migration(struct page *newpage) | 785 | void mem_cgroup_end_migration(struct mem_cgroup *mem, |
786 | struct page *oldpage, struct page *newpage) | ||
821 | { | 787 | { |
788 | struct page *target, *unused; | ||
789 | struct page_cgroup *pc; | ||
790 | enum charge_type ctype; | ||
791 | |||
792 | if (!mem) | ||
793 | return; | ||
794 | |||
795 | /* at migration success, oldpage->mapping is NULL. */ | ||
796 | if (oldpage->mapping) { | ||
797 | target = oldpage; | ||
798 | unused = NULL; | ||
799 | } else { | ||
800 | target = newpage; | ||
801 | unused = oldpage; | ||
802 | } | ||
803 | |||
804 | if (PageAnon(target)) | ||
805 | ctype = MEM_CGROUP_CHARGE_TYPE_MAPPED; | ||
806 | else if (page_is_file_cache(target)) | ||
807 | ctype = MEM_CGROUP_CHARGE_TYPE_CACHE; | ||
808 | else | ||
809 | ctype = MEM_CGROUP_CHARGE_TYPE_SHMEM; | ||
810 | |||
811 | /* unused page is not on radix-tree now. */ | ||
812 | if (unused && ctype != MEM_CGROUP_CHARGE_TYPE_MAPPED) | ||
813 | __mem_cgroup_uncharge_common(unused, ctype); | ||
814 | |||
815 | pc = lookup_page_cgroup(target); | ||
822 | /* | 816 | /* |
823 | * At success, page->mapping is not NULL. | 817 | * __mem_cgroup_commit_charge() check PCG_USED bit of page_cgroup. |
824 | * special rollback care is necessary when | 818 | * So, double-counting is effectively avoided. |
825 | * 1. at migration failure. (newpage->mapping is cleared in this case) | 819 | */ |
826 | * 2. the newpage was moved but not remapped again because the task | 820 | __mem_cgroup_commit_charge(mem, pc, ctype); |
827 | * exits and the newpage is obsolete. In this case, the new page | 821 | |
828 | * may be a swapcache. So, we just call mem_cgroup_uncharge_page() | 822 | /* |
829 | * always for avoiding mess. The page_cgroup will be removed if | 823 | * Both of oldpage and newpage are still under lock_page(). |
830 | * unnecessary. File cache pages is still on radix-tree. Don't | 824 | * Then, we don't have to care about race in radix-tree. |
831 | * care it. | 825 | * But we have to be careful that this page is unmapped or not. |
826 | * | ||
827 | * There is a case for !page_mapped(). At the start of | ||
828 | * migration, oldpage was mapped. But now, it's zapped. | ||
829 | * But we know *target* page is not freed/reused under us. | ||
830 | * mem_cgroup_uncharge_page() does all necessary checks. | ||
832 | */ | 831 | */ |
833 | if (!newpage->mapping) | 832 | if (ctype == MEM_CGROUP_CHARGE_TYPE_MAPPED) |
834 | __mem_cgroup_uncharge_common(newpage, | 833 | mem_cgroup_uncharge_page(target); |
835 | MEM_CGROUP_CHARGE_TYPE_FORCE); | ||
836 | else if (PageAnon(newpage)) | ||
837 | mem_cgroup_uncharge_page(newpage); | ||
838 | } | 834 | } |
839 | 835 | ||
840 | /* | 836 | /* |