diff options
Diffstat (limited to 'mm/memcontrol.c')
-rw-r--r-- | mm/memcontrol.c | 71 |
1 files changed, 71 insertions, 0 deletions
diff --git a/mm/memcontrol.c b/mm/memcontrol.c index a4bb857d902c..a18e228f140b 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c | |||
@@ -138,6 +138,7 @@ static const char * const mem_cgroup_lru_names[] = { | |||
138 | */ | 138 | */ |
139 | enum mem_cgroup_events_target { | 139 | enum mem_cgroup_events_target { |
140 | MEM_CGROUP_TARGET_THRESH, | 140 | MEM_CGROUP_TARGET_THRESH, |
141 | MEM_CGROUP_TARGET_SOFTLIMIT, | ||
141 | MEM_CGROUP_TARGET_NUMAINFO, | 142 | MEM_CGROUP_TARGET_NUMAINFO, |
142 | MEM_CGROUP_NTARGETS, | 143 | MEM_CGROUP_NTARGETS, |
143 | }; | 144 | }; |
@@ -315,6 +316,22 @@ struct mem_cgroup { | |||
315 | atomic_t numainfo_events; | 316 | atomic_t numainfo_events; |
316 | atomic_t numainfo_updating; | 317 | atomic_t numainfo_updating; |
317 | #endif | 318 | #endif |
319 | /* | ||
320 | * Protects soft_contributed transitions. | ||
321 | * See mem_cgroup_update_soft_limit | ||
322 | */ | ||
323 | spinlock_t soft_lock; | ||
324 | |||
325 | /* | ||
326 | * If true then this group has increased parents' children_in_excess | ||
327 | * when it got over the soft limit. | ||
328 | * When a group falls bellow the soft limit, parents' children_in_excess | ||
329 | * is decreased and soft_contributed changed to false. | ||
330 | */ | ||
331 | bool soft_contributed; | ||
332 | |||
333 | /* Number of children that are in soft limit excess */ | ||
334 | atomic_t children_in_excess; | ||
318 | 335 | ||
319 | struct mem_cgroup_per_node *nodeinfo[0]; | 336 | struct mem_cgroup_per_node *nodeinfo[0]; |
320 | /* WARNING: nodeinfo must be the last member here */ | 337 | /* WARNING: nodeinfo must be the last member here */ |
@@ -802,6 +819,9 @@ static bool mem_cgroup_event_ratelimit(struct mem_cgroup *memcg, | |||
802 | case MEM_CGROUP_TARGET_THRESH: | 819 | case MEM_CGROUP_TARGET_THRESH: |
803 | next = val + THRESHOLDS_EVENTS_TARGET; | 820 | next = val + THRESHOLDS_EVENTS_TARGET; |
804 | break; | 821 | break; |
822 | case MEM_CGROUP_TARGET_SOFTLIMIT: | ||
823 | next = val + SOFTLIMIT_EVENTS_TARGET; | ||
824 | break; | ||
805 | case MEM_CGROUP_TARGET_NUMAINFO: | 825 | case MEM_CGROUP_TARGET_NUMAINFO: |
806 | next = val + NUMAINFO_EVENTS_TARGET; | 826 | next = val + NUMAINFO_EVENTS_TARGET; |
807 | break; | 827 | break; |
@@ -815,6 +835,42 @@ static bool mem_cgroup_event_ratelimit(struct mem_cgroup *memcg, | |||
815 | } | 835 | } |
816 | 836 | ||
817 | /* | 837 | /* |
838 | * Called from rate-limitted memcg_check_events when enough | ||
839 | * MEM_CGROUP_TARGET_SOFTLIMIT events are accumulated and it makes sure | ||
840 | * that all the parents up the hierarchy will be noticed that this group | ||
841 | * is in excess or that it is not in excess anymore. mmecg->soft_contributed | ||
842 | * makes the transition a single action whenever the state flips from one to | ||
843 | * other. | ||
844 | */ | ||
845 | static void mem_cgroup_update_soft_limit(struct mem_cgroup *memcg) | ||
846 | { | ||
847 | unsigned long long excess = res_counter_soft_limit_excess(&memcg->res); | ||
848 | struct mem_cgroup *parent = memcg; | ||
849 | int delta = 0; | ||
850 | |||
851 | spin_lock(&memcg->soft_lock); | ||
852 | if (excess) { | ||
853 | if (!memcg->soft_contributed) { | ||
854 | delta = 1; | ||
855 | memcg->soft_contributed = true; | ||
856 | } | ||
857 | } else { | ||
858 | if (memcg->soft_contributed) { | ||
859 | delta = -1; | ||
860 | memcg->soft_contributed = false; | ||
861 | } | ||
862 | } | ||
863 | |||
864 | /* | ||
865 | * Necessary to update all ancestors when hierarchy is used | ||
866 | * because their event counter is not touched. | ||
867 | */ | ||
868 | while (delta && (parent = parent_mem_cgroup(parent))) | ||
869 | atomic_add(delta, &parent->children_in_excess); | ||
870 | spin_unlock(&memcg->soft_lock); | ||
871 | } | ||
872 | |||
873 | /* | ||
818 | * Check events in order. | 874 | * Check events in order. |
819 | * | 875 | * |
820 | */ | 876 | */ |
@@ -824,8 +880,11 @@ static void memcg_check_events(struct mem_cgroup *memcg, struct page *page) | |||
824 | /* threshold event is triggered in finer grain than soft limit */ | 880 | /* threshold event is triggered in finer grain than soft limit */ |
825 | if (unlikely(mem_cgroup_event_ratelimit(memcg, | 881 | if (unlikely(mem_cgroup_event_ratelimit(memcg, |
826 | MEM_CGROUP_TARGET_THRESH))) { | 882 | MEM_CGROUP_TARGET_THRESH))) { |
883 | bool do_softlimit; | ||
827 | bool do_numainfo __maybe_unused; | 884 | bool do_numainfo __maybe_unused; |
828 | 885 | ||
886 | do_softlimit = mem_cgroup_event_ratelimit(memcg, | ||
887 | MEM_CGROUP_TARGET_SOFTLIMIT); | ||
829 | #if MAX_NUMNODES > 1 | 888 | #if MAX_NUMNODES > 1 |
830 | do_numainfo = mem_cgroup_event_ratelimit(memcg, | 889 | do_numainfo = mem_cgroup_event_ratelimit(memcg, |
831 | MEM_CGROUP_TARGET_NUMAINFO); | 890 | MEM_CGROUP_TARGET_NUMAINFO); |
@@ -833,6 +892,8 @@ static void memcg_check_events(struct mem_cgroup *memcg, struct page *page) | |||
833 | preempt_enable(); | 892 | preempt_enable(); |
834 | 893 | ||
835 | mem_cgroup_threshold(memcg); | 894 | mem_cgroup_threshold(memcg); |
895 | if (unlikely(do_softlimit)) | ||
896 | mem_cgroup_update_soft_limit(memcg); | ||
836 | #if MAX_NUMNODES > 1 | 897 | #if MAX_NUMNODES > 1 |
837 | if (unlikely(do_numainfo)) | 898 | if (unlikely(do_numainfo)) |
838 | atomic_inc(&memcg->numainfo_events); | 899 | atomic_inc(&memcg->numainfo_events); |
@@ -1816,6 +1877,9 @@ int mem_cgroup_select_victim_node(struct mem_cgroup *memcg) | |||
1816 | * hierarchy if | 1877 | * hierarchy if |
1817 | * a) it is over its soft limit | 1878 | * a) it is over its soft limit |
1818 | * b) any parent up the hierarchy is over its soft limit | 1879 | * b) any parent up the hierarchy is over its soft limit |
1880 | * | ||
1881 | * If the given group doesn't have any children over the limit then it | ||
1882 | * doesn't make any sense to iterate its subtree. | ||
1819 | */ | 1883 | */ |
1820 | enum mem_cgroup_filter_t | 1884 | enum mem_cgroup_filter_t |
1821 | mem_cgroup_soft_reclaim_eligible(struct mem_cgroup *memcg, | 1885 | mem_cgroup_soft_reclaim_eligible(struct mem_cgroup *memcg, |
@@ -1837,6 +1901,8 @@ mem_cgroup_soft_reclaim_eligible(struct mem_cgroup *memcg, | |||
1837 | break; | 1901 | break; |
1838 | } | 1902 | } |
1839 | 1903 | ||
1904 | if (!atomic_read(&memcg->children_in_excess)) | ||
1905 | return SKIP_TREE; | ||
1840 | return SKIP; | 1906 | return SKIP; |
1841 | } | 1907 | } |
1842 | 1908 | ||
@@ -5892,6 +5958,7 @@ mem_cgroup_css_alloc(struct cgroup_subsys_state *parent_css) | |||
5892 | mutex_init(&memcg->thresholds_lock); | 5958 | mutex_init(&memcg->thresholds_lock); |
5893 | spin_lock_init(&memcg->move_lock); | 5959 | spin_lock_init(&memcg->move_lock); |
5894 | vmpressure_init(&memcg->vmpressure); | 5960 | vmpressure_init(&memcg->vmpressure); |
5961 | spin_lock_init(&memcg->soft_lock); | ||
5895 | 5962 | ||
5896 | return &memcg->css; | 5963 | return &memcg->css; |
5897 | 5964 | ||
@@ -5969,6 +6036,10 @@ static void mem_cgroup_css_offline(struct cgroup_subsys_state *css) | |||
5969 | 6036 | ||
5970 | mem_cgroup_invalidate_reclaim_iterators(memcg); | 6037 | mem_cgroup_invalidate_reclaim_iterators(memcg); |
5971 | mem_cgroup_reparent_charges(memcg); | 6038 | mem_cgroup_reparent_charges(memcg); |
6039 | if (memcg->soft_contributed) { | ||
6040 | while ((memcg = parent_mem_cgroup(memcg))) | ||
6041 | atomic_dec(&memcg->children_in_excess); | ||
6042 | } | ||
5972 | mem_cgroup_destroy_all_caches(memcg); | 6043 | mem_cgroup_destroy_all_caches(memcg); |
5973 | vmpressure_cleanup(&memcg->vmpressure); | 6044 | vmpressure_cleanup(&memcg->vmpressure); |
5974 | } | 6045 | } |