diff options
Diffstat (limited to 'mm/memcontrol.c')
-rw-r--r-- | mm/memcontrol.c | 68 |
1 files changed, 56 insertions, 12 deletions
diff --git a/mm/memcontrol.c b/mm/memcontrol.c index e74d7080ec9e..9a6a51a7c416 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c | |||
@@ -4077,14 +4077,14 @@ static struct cftype mem_cgroup_legacy_files[] = { | |||
4077 | 4077 | ||
4078 | static DEFINE_IDR(mem_cgroup_idr); | 4078 | static DEFINE_IDR(mem_cgroup_idr); |
4079 | 4079 | ||
4080 | static void mem_cgroup_id_get(struct mem_cgroup *memcg) | 4080 | static void mem_cgroup_id_get_many(struct mem_cgroup *memcg, unsigned int n) |
4081 | { | 4081 | { |
4082 | atomic_inc(&memcg->id.ref); | 4082 | atomic_add(n, &memcg->id.ref); |
4083 | } | 4083 | } |
4084 | 4084 | ||
4085 | static void mem_cgroup_id_put(struct mem_cgroup *memcg) | 4085 | static void mem_cgroup_id_put_many(struct mem_cgroup *memcg, unsigned int n) |
4086 | { | 4086 | { |
4087 | if (atomic_dec_and_test(&memcg->id.ref)) { | 4087 | if (atomic_sub_and_test(n, &memcg->id.ref)) { |
4088 | idr_remove(&mem_cgroup_idr, memcg->id.id); | 4088 | idr_remove(&mem_cgroup_idr, memcg->id.id); |
4089 | memcg->id.id = 0; | 4089 | memcg->id.id = 0; |
4090 | 4090 | ||
@@ -4093,6 +4093,16 @@ static void mem_cgroup_id_put(struct mem_cgroup *memcg) | |||
4093 | } | 4093 | } |
4094 | } | 4094 | } |
4095 | 4095 | ||
4096 | static inline void mem_cgroup_id_get(struct mem_cgroup *memcg) | ||
4097 | { | ||
4098 | mem_cgroup_id_get_many(memcg, 1); | ||
4099 | } | ||
4100 | |||
4101 | static inline void mem_cgroup_id_put(struct mem_cgroup *memcg) | ||
4102 | { | ||
4103 | mem_cgroup_id_put_many(memcg, 1); | ||
4104 | } | ||
4105 | |||
4096 | /** | 4106 | /** |
4097 | * mem_cgroup_from_id - look up a memcg from a memcg id | 4107 | * mem_cgroup_from_id - look up a memcg from a memcg id |
4098 | * @id: the memcg id to look up | 4108 | * @id: the memcg id to look up |
@@ -4727,6 +4737,8 @@ static void __mem_cgroup_clear_mc(void) | |||
4727 | if (!mem_cgroup_is_root(mc.from)) | 4737 | if (!mem_cgroup_is_root(mc.from)) |
4728 | page_counter_uncharge(&mc.from->memsw, mc.moved_swap); | 4738 | page_counter_uncharge(&mc.from->memsw, mc.moved_swap); |
4729 | 4739 | ||
4740 | mem_cgroup_id_put_many(mc.from, mc.moved_swap); | ||
4741 | |||
4730 | /* | 4742 | /* |
4731 | * we charged both to->memory and to->memsw, so we | 4743 | * we charged both to->memory and to->memsw, so we |
4732 | * should uncharge to->memory. | 4744 | * should uncharge to->memory. |
@@ -4734,9 +4746,9 @@ static void __mem_cgroup_clear_mc(void) | |||
4734 | if (!mem_cgroup_is_root(mc.to)) | 4746 | if (!mem_cgroup_is_root(mc.to)) |
4735 | page_counter_uncharge(&mc.to->memory, mc.moved_swap); | 4747 | page_counter_uncharge(&mc.to->memory, mc.moved_swap); |
4736 | 4748 | ||
4737 | css_put_many(&mc.from->css, mc.moved_swap); | 4749 | mem_cgroup_id_get_many(mc.to, mc.moved_swap); |
4750 | css_put_many(&mc.to->css, mc.moved_swap); | ||
4738 | 4751 | ||
4739 | /* we've already done css_get(mc.to) */ | ||
4740 | mc.moved_swap = 0; | 4752 | mc.moved_swap = 0; |
4741 | } | 4753 | } |
4742 | memcg_oom_recover(from); | 4754 | memcg_oom_recover(from); |
@@ -5791,6 +5803,24 @@ static int __init mem_cgroup_init(void) | |||
5791 | subsys_initcall(mem_cgroup_init); | 5803 | subsys_initcall(mem_cgroup_init); |
5792 | 5804 | ||
5793 | #ifdef CONFIG_MEMCG_SWAP | 5805 | #ifdef CONFIG_MEMCG_SWAP |
5806 | static struct mem_cgroup *mem_cgroup_id_get_online(struct mem_cgroup *memcg) | ||
5807 | { | ||
5808 | while (!atomic_inc_not_zero(&memcg->id.ref)) { | ||
5809 | /* | ||
5810 | * The root cgroup cannot be destroyed, so it's refcount must | ||
5811 | * always be >= 1. | ||
5812 | */ | ||
5813 | if (WARN_ON_ONCE(memcg == root_mem_cgroup)) { | ||
5814 | VM_BUG_ON(1); | ||
5815 | break; | ||
5816 | } | ||
5817 | memcg = parent_mem_cgroup(memcg); | ||
5818 | if (!memcg) | ||
5819 | memcg = root_mem_cgroup; | ||
5820 | } | ||
5821 | return memcg; | ||
5822 | } | ||
5823 | |||
5794 | /** | 5824 | /** |
5795 | * mem_cgroup_swapout - transfer a memsw charge to swap | 5825 | * mem_cgroup_swapout - transfer a memsw charge to swap |
5796 | * @page: page whose memsw charge to transfer | 5826 | * @page: page whose memsw charge to transfer |
@@ -5800,7 +5830,7 @@ subsys_initcall(mem_cgroup_init); | |||
5800 | */ | 5830 | */ |
5801 | void mem_cgroup_swapout(struct page *page, swp_entry_t entry) | 5831 | void mem_cgroup_swapout(struct page *page, swp_entry_t entry) |
5802 | { | 5832 | { |
5803 | struct mem_cgroup *memcg; | 5833 | struct mem_cgroup *memcg, *swap_memcg; |
5804 | unsigned short oldid; | 5834 | unsigned short oldid; |
5805 | 5835 | ||
5806 | VM_BUG_ON_PAGE(PageLRU(page), page); | 5836 | VM_BUG_ON_PAGE(PageLRU(page), page); |
@@ -5815,16 +5845,27 @@ void mem_cgroup_swapout(struct page *page, swp_entry_t entry) | |||
5815 | if (!memcg) | 5845 | if (!memcg) |
5816 | return; | 5846 | return; |
5817 | 5847 | ||
5818 | mem_cgroup_id_get(memcg); | 5848 | /* |
5819 | oldid = swap_cgroup_record(entry, mem_cgroup_id(memcg)); | 5849 | * In case the memcg owning these pages has been offlined and doesn't |
5850 | * have an ID allocated to it anymore, charge the closest online | ||
5851 | * ancestor for the swap instead and transfer the memory+swap charge. | ||
5852 | */ | ||
5853 | swap_memcg = mem_cgroup_id_get_online(memcg); | ||
5854 | oldid = swap_cgroup_record(entry, mem_cgroup_id(swap_memcg)); | ||
5820 | VM_BUG_ON_PAGE(oldid, page); | 5855 | VM_BUG_ON_PAGE(oldid, page); |
5821 | mem_cgroup_swap_statistics(memcg, true); | 5856 | mem_cgroup_swap_statistics(swap_memcg, true); |
5822 | 5857 | ||
5823 | page->mem_cgroup = NULL; | 5858 | page->mem_cgroup = NULL; |
5824 | 5859 | ||
5825 | if (!mem_cgroup_is_root(memcg)) | 5860 | if (!mem_cgroup_is_root(memcg)) |
5826 | page_counter_uncharge(&memcg->memory, 1); | 5861 | page_counter_uncharge(&memcg->memory, 1); |
5827 | 5862 | ||
5863 | if (memcg != swap_memcg) { | ||
5864 | if (!mem_cgroup_is_root(swap_memcg)) | ||
5865 | page_counter_charge(&swap_memcg->memsw, 1); | ||
5866 | page_counter_uncharge(&memcg->memsw, 1); | ||
5867 | } | ||
5868 | |||
5828 | /* | 5869 | /* |
5829 | * Interrupts should be disabled here because the caller holds the | 5870 | * Interrupts should be disabled here because the caller holds the |
5830 | * mapping->tree_lock lock which is taken with interrupts-off. It is | 5871 | * mapping->tree_lock lock which is taken with interrupts-off. It is |
@@ -5863,11 +5904,14 @@ int mem_cgroup_try_charge_swap(struct page *page, swp_entry_t entry) | |||
5863 | if (!memcg) | 5904 | if (!memcg) |
5864 | return 0; | 5905 | return 0; |
5865 | 5906 | ||
5907 | memcg = mem_cgroup_id_get_online(memcg); | ||
5908 | |||
5866 | if (!mem_cgroup_is_root(memcg) && | 5909 | if (!mem_cgroup_is_root(memcg) && |
5867 | !page_counter_try_charge(&memcg->swap, 1, &counter)) | 5910 | !page_counter_try_charge(&memcg->swap, 1, &counter)) { |
5911 | mem_cgroup_id_put(memcg); | ||
5868 | return -ENOMEM; | 5912 | return -ENOMEM; |
5913 | } | ||
5869 | 5914 | ||
5870 | mem_cgroup_id_get(memcg); | ||
5871 | oldid = swap_cgroup_record(entry, mem_cgroup_id(memcg)); | 5915 | oldid = swap_cgroup_record(entry, mem_cgroup_id(memcg)); |
5872 | VM_BUG_ON_PAGE(oldid, page); | 5916 | VM_BUG_ON_PAGE(oldid, page); |
5873 | mem_cgroup_swap_statistics(memcg, true); | 5917 | mem_cgroup_swap_statistics(memcg, true); |