diff options
-rw-r--r-- | include/linux/memcontrol.h | 19 | ||||
-rw-r--r-- | mm/memcontrol.c | 43 | ||||
-rw-r--r-- | mm/migrate.c | 13 |
3 files changed, 72 insertions, 3 deletions
diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index 42536c737d9c..4ec712967f7c 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h | |||
@@ -60,6 +60,10 @@ static inline void mem_cgroup_uncharge_page(struct page *page) | |||
60 | mem_cgroup_uncharge(page_get_page_cgroup(page)); | 60 | mem_cgroup_uncharge(page_get_page_cgroup(page)); |
61 | } | 61 | } |
62 | 62 | ||
63 | extern int mem_cgroup_prepare_migration(struct page *page); | ||
64 | extern void mem_cgroup_end_migration(struct page *page); | ||
65 | extern void mem_cgroup_page_migration(struct page *page, struct page *newpage); | ||
66 | |||
63 | #else /* CONFIG_CGROUP_MEM_CONT */ | 67 | #else /* CONFIG_CGROUP_MEM_CONT */ |
64 | static inline void mm_init_cgroup(struct mm_struct *mm, | 68 | static inline void mm_init_cgroup(struct mm_struct *mm, |
65 | struct task_struct *p) | 69 | struct task_struct *p) |
@@ -117,6 +121,21 @@ static inline int task_in_mem_cgroup(struct task_struct *task, | |||
117 | return 1; | 121 | return 1; |
118 | } | 122 | } |
119 | 123 | ||
124 | static inline int mem_cgroup_prepare_migration(struct page *page) | ||
125 | { | ||
126 | return 0; | ||
127 | } | ||
128 | |||
129 | static inline void mem_cgroup_end_migration(struct page *page) | ||
130 | { | ||
131 | } | ||
132 | |||
133 | static inline void | ||
134 | mem_cgroup_page_migration(struct page *page, struct page *newpage) | ||
135 | { | ||
136 | } | ||
137 | |||
138 | |||
120 | #endif /* CONFIG_CGROUP_MEM_CONT */ | 139 | #endif /* CONFIG_CGROUP_MEM_CONT */ |
121 | 140 | ||
122 | #endif /* _LINUX_MEMCONTROL_H */ | 141 | #endif /* _LINUX_MEMCONTROL_H */ |
diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 3270ce7375db..128f45c16fa6 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c | |||
@@ -492,6 +492,49 @@ void mem_cgroup_uncharge(struct page_cgroup *pc) | |||
492 | } | 492 | } |
493 | } | 493 | } |
494 | } | 494 | } |
495 | /* | ||
496 | * Returns non-zero if a page (under migration) has valid page_cgroup member. | ||
497 | * Refcnt of page_cgroup is incremented. | ||
498 | */ | ||
499 | |||
500 | int mem_cgroup_prepare_migration(struct page *page) | ||
501 | { | ||
502 | struct page_cgroup *pc; | ||
503 | int ret = 0; | ||
504 | lock_page_cgroup(page); | ||
505 | pc = page_get_page_cgroup(page); | ||
506 | if (pc && atomic_inc_not_zero(&pc->ref_cnt)) | ||
507 | ret = 1; | ||
508 | unlock_page_cgroup(page); | ||
509 | return ret; | ||
510 | } | ||
511 | |||
512 | void mem_cgroup_end_migration(struct page *page) | ||
513 | { | ||
514 | struct page_cgroup *pc = page_get_page_cgroup(page); | ||
515 | mem_cgroup_uncharge(pc); | ||
516 | } | ||
517 | /* | ||
518 | * We know both *page* and *newpage* are now not-on-LRU and Pg_locked. | ||
519 | * And no race with uncharge() routines because page_cgroup for *page* | ||
520 | * has extra one reference by mem_cgroup_prepare_migration. | ||
521 | */ | ||
522 | |||
523 | void mem_cgroup_page_migration(struct page *page, struct page *newpage) | ||
524 | { | ||
525 | struct page_cgroup *pc; | ||
526 | retry: | ||
527 | pc = page_get_page_cgroup(page); | ||
528 | if (!pc) | ||
529 | return; | ||
530 | if (clear_page_cgroup(page, pc) != pc) | ||
531 | goto retry; | ||
532 | pc->page = newpage; | ||
533 | lock_page_cgroup(newpage); | ||
534 | page_assign_page_cgroup(newpage, pc); | ||
535 | unlock_page_cgroup(newpage); | ||
536 | return; | ||
537 | } | ||
495 | 538 | ||
496 | int mem_cgroup_write_strategy(char *buf, unsigned long long *tmp) | 539 | int mem_cgroup_write_strategy(char *buf, unsigned long long *tmp) |
497 | { | 540 | { |
diff --git a/mm/migrate.c b/mm/migrate.c index 763794144697..a73504ff5ab9 100644 --- a/mm/migrate.c +++ b/mm/migrate.c | |||
@@ -593,9 +593,10 @@ static int move_to_new_page(struct page *newpage, struct page *page) | |||
593 | else | 593 | else |
594 | rc = fallback_migrate_page(mapping, newpage, page); | 594 | rc = fallback_migrate_page(mapping, newpage, page); |
595 | 595 | ||
596 | if (!rc) | 596 | if (!rc) { |
597 | mem_cgroup_page_migration(page, newpage); | ||
597 | remove_migration_ptes(page, newpage); | 598 | remove_migration_ptes(page, newpage); |
598 | else | 599 | } else |
599 | newpage->mapping = NULL; | 600 | newpage->mapping = NULL; |
600 | 601 | ||
601 | unlock_page(newpage); | 602 | unlock_page(newpage); |
@@ -614,6 +615,7 @@ static int unmap_and_move(new_page_t get_new_page, unsigned long private, | |||
614 | int *result = NULL; | 615 | int *result = NULL; |
615 | struct page *newpage = get_new_page(page, private, &result); | 616 | struct page *newpage = get_new_page(page, private, &result); |
616 | int rcu_locked = 0; | 617 | int rcu_locked = 0; |
618 | int charge = 0; | ||
617 | 619 | ||
618 | if (!newpage) | 620 | if (!newpage) |
619 | return -ENOMEM; | 621 | return -ENOMEM; |
@@ -673,14 +675,19 @@ static int unmap_and_move(new_page_t get_new_page, unsigned long private, | |||
673 | goto rcu_unlock; | 675 | goto rcu_unlock; |
674 | } | 676 | } |
675 | 677 | ||
678 | charge = mem_cgroup_prepare_migration(page); | ||
676 | /* Establish migration ptes or remove ptes */ | 679 | /* Establish migration ptes or remove ptes */ |
677 | try_to_unmap(page, 1); | 680 | try_to_unmap(page, 1); |
678 | 681 | ||
679 | if (!page_mapped(page)) | 682 | if (!page_mapped(page)) |
680 | rc = move_to_new_page(newpage, page); | 683 | rc = move_to_new_page(newpage, page); |
681 | 684 | ||
682 | if (rc) | 685 | if (rc) { |
683 | remove_migration_ptes(page, page); | 686 | remove_migration_ptes(page, page); |
687 | if (charge) | ||
688 | mem_cgroup_end_migration(page); | ||
689 | } else if (charge) | ||
690 | mem_cgroup_end_migration(newpage); | ||
684 | rcu_unlock: | 691 | rcu_unlock: |
685 | if (rcu_locked) | 692 | if (rcu_locked) |
686 | rcu_read_unlock(); | 693 | rcu_read_unlock(); |