diff options
-rw-r--r-- | include/linux/memcontrol.h | 49 | ||||
-rw-r--r-- | mm/memcontrol.c | 70 | ||||
-rw-r--r-- | mm/vmscan.c | 16 |
3 files changed, 32 insertions, 103 deletions
diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index 60e95872da29..ef2b9bd7fafa 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h | |||
@@ -53,23 +53,6 @@ struct mem_cgroup_reclaim_cookie { | |||
53 | unsigned int generation; | 53 | unsigned int generation; |
54 | }; | 54 | }; |
55 | 55 | ||
56 | enum mem_cgroup_filter_t { | ||
57 | VISIT, /* visit current node */ | ||
58 | SKIP, /* skip the current node and continue traversal */ | ||
59 | SKIP_TREE, /* skip the whole subtree and continue traversal */ | ||
60 | }; | ||
61 | |||
62 | /* | ||
63 | * mem_cgroup_filter_t predicate might instruct mem_cgroup_iter_cond how to | ||
64 | * iterate through the hierarchy tree. Each tree element is checked by the | ||
65 | * predicate before it is returned by the iterator. If a filter returns | ||
66 | * SKIP or SKIP_TREE then the iterator code continues traversal (with the | ||
67 | * next node down the hierarchy or the next node that doesn't belong under the | ||
68 | * memcg's subtree). | ||
69 | */ | ||
70 | typedef enum mem_cgroup_filter_t | ||
71 | (*mem_cgroup_iter_filter)(struct mem_cgroup *memcg, struct mem_cgroup *root); | ||
72 | |||
73 | #ifdef CONFIG_MEMCG | 56 | #ifdef CONFIG_MEMCG |
74 | /* | 57 | /* |
75 | * All "charge" functions with gfp_mask should use GFP_KERNEL or | 58 | * All "charge" functions with gfp_mask should use GFP_KERNEL or |
@@ -137,18 +120,9 @@ mem_cgroup_prepare_migration(struct page *page, struct page *newpage, | |||
137 | extern void mem_cgroup_end_migration(struct mem_cgroup *memcg, | 120 | extern void mem_cgroup_end_migration(struct mem_cgroup *memcg, |
138 | struct page *oldpage, struct page *newpage, bool migration_ok); | 121 | struct page *oldpage, struct page *newpage, bool migration_ok); |
139 | 122 | ||
140 | struct mem_cgroup *mem_cgroup_iter_cond(struct mem_cgroup *root, | 123 | struct mem_cgroup *mem_cgroup_iter(struct mem_cgroup *, |
141 | struct mem_cgroup *prev, | 124 | struct mem_cgroup *, |
142 | struct mem_cgroup_reclaim_cookie *reclaim, | 125 | struct mem_cgroup_reclaim_cookie *); |
143 | mem_cgroup_iter_filter cond); | ||
144 | |||
145 | static inline struct mem_cgroup *mem_cgroup_iter(struct mem_cgroup *root, | ||
146 | struct mem_cgroup *prev, | ||
147 | struct mem_cgroup_reclaim_cookie *reclaim) | ||
148 | { | ||
149 | return mem_cgroup_iter_cond(root, prev, reclaim, NULL); | ||
150 | } | ||
151 | |||
152 | void mem_cgroup_iter_break(struct mem_cgroup *, struct mem_cgroup *); | 126 | void mem_cgroup_iter_break(struct mem_cgroup *, struct mem_cgroup *); |
153 | 127 | ||
154 | /* | 128 | /* |
@@ -260,8 +234,7 @@ static inline void mem_cgroup_dec_page_stat(struct page *page, | |||
260 | mem_cgroup_update_page_stat(page, idx, -1); | 234 | mem_cgroup_update_page_stat(page, idx, -1); |
261 | } | 235 | } |
262 | 236 | ||
263 | enum mem_cgroup_filter_t | 237 | bool mem_cgroup_soft_reclaim_eligible(struct mem_cgroup *memcg, |
264 | mem_cgroup_soft_reclaim_eligible(struct mem_cgroup *memcg, | ||
265 | struct mem_cgroup *root); | 238 | struct mem_cgroup *root); |
266 | 239 | ||
267 | void __mem_cgroup_count_vm_event(struct mm_struct *mm, enum vm_event_item idx); | 240 | void __mem_cgroup_count_vm_event(struct mm_struct *mm, enum vm_event_item idx); |
@@ -376,15 +349,6 @@ static inline void mem_cgroup_end_migration(struct mem_cgroup *memcg, | |||
376 | struct page *oldpage, struct page *newpage, bool migration_ok) | 349 | struct page *oldpage, struct page *newpage, bool migration_ok) |
377 | { | 350 | { |
378 | } | 351 | } |
379 | static inline struct mem_cgroup * | ||
380 | mem_cgroup_iter_cond(struct mem_cgroup *root, | ||
381 | struct mem_cgroup *prev, | ||
382 | struct mem_cgroup_reclaim_cookie *reclaim, | ||
383 | mem_cgroup_iter_filter cond) | ||
384 | { | ||
385 | /* first call must return non-NULL, second return NULL */ | ||
386 | return (struct mem_cgroup *)(unsigned long)!prev; | ||
387 | } | ||
388 | 352 | ||
389 | static inline struct mem_cgroup * | 353 | static inline struct mem_cgroup * |
390 | mem_cgroup_iter(struct mem_cgroup *root, | 354 | mem_cgroup_iter(struct mem_cgroup *root, |
@@ -471,11 +435,10 @@ static inline void mem_cgroup_dec_page_stat(struct page *page, | |||
471 | } | 435 | } |
472 | 436 | ||
473 | static inline | 437 | static inline |
474 | enum mem_cgroup_filter_t | 438 | bool mem_cgroup_soft_reclaim_eligible(struct mem_cgroup *memcg, |
475 | mem_cgroup_soft_reclaim_eligible(struct mem_cgroup *memcg, | ||
476 | struct mem_cgroup *root) | 439 | struct mem_cgroup *root) |
477 | { | 440 | { |
478 | return VISIT; | 441 | return false; |
479 | } | 442 | } |
480 | 443 | ||
481 | static inline void mem_cgroup_split_huge_fixup(struct page *head) | 444 | static inline void mem_cgroup_split_huge_fixup(struct page *head) |
diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 916892c2b8e0..65e7bec4b0f0 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c | |||
@@ -862,15 +862,6 @@ struct mem_cgroup *try_get_mem_cgroup_from_mm(struct mm_struct *mm) | |||
862 | return memcg; | 862 | return memcg; |
863 | } | 863 | } |
864 | 864 | ||
865 | static enum mem_cgroup_filter_t | ||
866 | mem_cgroup_filter(struct mem_cgroup *memcg, struct mem_cgroup *root, | ||
867 | mem_cgroup_iter_filter cond) | ||
868 | { | ||
869 | if (!cond) | ||
870 | return VISIT; | ||
871 | return cond(memcg, root); | ||
872 | } | ||
873 | |||
874 | /* | 865 | /* |
875 | * Returns a next (in a pre-order walk) alive memcg (with elevated css | 866 | * Returns a next (in a pre-order walk) alive memcg (with elevated css |
876 | * ref. count) or NULL if the whole root's subtree has been visited. | 867 | * ref. count) or NULL if the whole root's subtree has been visited. |
@@ -878,7 +869,7 @@ mem_cgroup_filter(struct mem_cgroup *memcg, struct mem_cgroup *root, | |||
878 | * helper function to be used by mem_cgroup_iter | 869 | * helper function to be used by mem_cgroup_iter |
879 | */ | 870 | */ |
880 | static struct mem_cgroup *__mem_cgroup_iter_next(struct mem_cgroup *root, | 871 | static struct mem_cgroup *__mem_cgroup_iter_next(struct mem_cgroup *root, |
881 | struct mem_cgroup *last_visited, mem_cgroup_iter_filter cond) | 872 | struct mem_cgroup *last_visited) |
882 | { | 873 | { |
883 | struct cgroup_subsys_state *prev_css, *next_css; | 874 | struct cgroup_subsys_state *prev_css, *next_css; |
884 | 875 | ||
@@ -896,31 +887,11 @@ skip_node: | |||
896 | if (next_css) { | 887 | if (next_css) { |
897 | struct mem_cgroup *mem = mem_cgroup_from_css(next_css); | 888 | struct mem_cgroup *mem = mem_cgroup_from_css(next_css); |
898 | 889 | ||
899 | switch (mem_cgroup_filter(mem, root, cond)) { | 890 | if (css_tryget(&mem->css)) |
900 | case SKIP: | 891 | return mem; |
892 | else { | ||
901 | prev_css = next_css; | 893 | prev_css = next_css; |
902 | goto skip_node; | 894 | goto skip_node; |
903 | case SKIP_TREE: | ||
904 | if (mem == root) | ||
905 | return NULL; | ||
906 | /* | ||
907 | * css_rightmost_descendant is not an optimal way to | ||
908 | * skip through a subtree (especially for imbalanced | ||
909 | * trees leaning to right) but that's what we have right | ||
910 | * now. More effective solution would be traversing | ||
911 | * right-up for first non-NULL without calling | ||
912 | * css_next_descendant_pre afterwards. | ||
913 | */ | ||
914 | prev_css = css_rightmost_descendant(next_css); | ||
915 | goto skip_node; | ||
916 | case VISIT: | ||
917 | if (css_tryget(&mem->css)) | ||
918 | return mem; | ||
919 | else { | ||
920 | prev_css = next_css; | ||
921 | goto skip_node; | ||
922 | } | ||
923 | break; | ||
924 | } | 895 | } |
925 | } | 896 | } |
926 | 897 | ||
@@ -984,7 +955,6 @@ static void mem_cgroup_iter_update(struct mem_cgroup_reclaim_iter *iter, | |||
984 | * @root: hierarchy root | 955 | * @root: hierarchy root |
985 | * @prev: previously returned memcg, NULL on first invocation | 956 | * @prev: previously returned memcg, NULL on first invocation |
986 | * @reclaim: cookie for shared reclaim walks, NULL for full walks | 957 | * @reclaim: cookie for shared reclaim walks, NULL for full walks |
987 | * @cond: filter for visited nodes, NULL for no filter | ||
988 | * | 958 | * |
989 | * Returns references to children of the hierarchy below @root, or | 959 | * Returns references to children of the hierarchy below @root, or |
990 | * @root itself, or %NULL after a full round-trip. | 960 | * @root itself, or %NULL after a full round-trip. |
@@ -997,18 +967,15 @@ static void mem_cgroup_iter_update(struct mem_cgroup_reclaim_iter *iter, | |||
997 | * divide up the memcgs in the hierarchy among all concurrent | 967 | * divide up the memcgs in the hierarchy among all concurrent |
998 | * reclaimers operating on the same zone and priority. | 968 | * reclaimers operating on the same zone and priority. |
999 | */ | 969 | */ |
1000 | struct mem_cgroup *mem_cgroup_iter_cond(struct mem_cgroup *root, | 970 | struct mem_cgroup *mem_cgroup_iter(struct mem_cgroup *root, |
1001 | struct mem_cgroup *prev, | 971 | struct mem_cgroup *prev, |
1002 | struct mem_cgroup_reclaim_cookie *reclaim, | 972 | struct mem_cgroup_reclaim_cookie *reclaim) |
1003 | mem_cgroup_iter_filter cond) | ||
1004 | { | 973 | { |
1005 | struct mem_cgroup *memcg = NULL; | 974 | struct mem_cgroup *memcg = NULL; |
1006 | struct mem_cgroup *last_visited = NULL; | 975 | struct mem_cgroup *last_visited = NULL; |
1007 | 976 | ||
1008 | if (mem_cgroup_disabled()) { | 977 | if (mem_cgroup_disabled()) |
1009 | /* first call must return non-NULL, second return NULL */ | 978 | return NULL; |
1010 | return (struct mem_cgroup *)(unsigned long)!prev; | ||
1011 | } | ||
1012 | 979 | ||
1013 | if (!root) | 980 | if (!root) |
1014 | root = root_mem_cgroup; | 981 | root = root_mem_cgroup; |
@@ -1019,9 +986,7 @@ struct mem_cgroup *mem_cgroup_iter_cond(struct mem_cgroup *root, | |||
1019 | if (!root->use_hierarchy && root != root_mem_cgroup) { | 986 | if (!root->use_hierarchy && root != root_mem_cgroup) { |
1020 | if (prev) | 987 | if (prev) |
1021 | goto out_css_put; | 988 | goto out_css_put; |
1022 | if (mem_cgroup_filter(root, root, cond) == VISIT) | 989 | return root; |
1023 | return root; | ||
1024 | return NULL; | ||
1025 | } | 990 | } |
1026 | 991 | ||
1027 | rcu_read_lock(); | 992 | rcu_read_lock(); |
@@ -1044,7 +1009,7 @@ struct mem_cgroup *mem_cgroup_iter_cond(struct mem_cgroup *root, | |||
1044 | last_visited = mem_cgroup_iter_load(iter, root, &seq); | 1009 | last_visited = mem_cgroup_iter_load(iter, root, &seq); |
1045 | } | 1010 | } |
1046 | 1011 | ||
1047 | memcg = __mem_cgroup_iter_next(root, last_visited, cond); | 1012 | memcg = __mem_cgroup_iter_next(root, last_visited); |
1048 | 1013 | ||
1049 | if (reclaim) { | 1014 | if (reclaim) { |
1050 | mem_cgroup_iter_update(iter, last_visited, memcg, seq); | 1015 | mem_cgroup_iter_update(iter, last_visited, memcg, seq); |
@@ -1055,11 +1020,7 @@ struct mem_cgroup *mem_cgroup_iter_cond(struct mem_cgroup *root, | |||
1055 | reclaim->generation = iter->generation; | 1020 | reclaim->generation = iter->generation; |
1056 | } | 1021 | } |
1057 | 1022 | ||
1058 | /* | 1023 | if (prev && !memcg) |
1059 | * We have finished the whole tree walk or no group has been | ||
1060 | * visited because filter told us to skip the root node. | ||
1061 | */ | ||
1062 | if (!memcg && (prev || (cond && !last_visited))) | ||
1063 | goto out_unlock; | 1024 | goto out_unlock; |
1064 | } | 1025 | } |
1065 | out_unlock: | 1026 | out_unlock: |
@@ -1804,14 +1765,13 @@ int mem_cgroup_select_victim_node(struct mem_cgroup *memcg) | |||
1804 | * a) it is over its soft limit | 1765 | * a) it is over its soft limit |
1805 | * b) any parent up the hierarchy is over its soft limit | 1766 | * b) any parent up the hierarchy is over its soft limit |
1806 | */ | 1767 | */ |
1807 | enum mem_cgroup_filter_t | 1768 | bool mem_cgroup_soft_reclaim_eligible(struct mem_cgroup *memcg, |
1808 | mem_cgroup_soft_reclaim_eligible(struct mem_cgroup *memcg, | ||
1809 | struct mem_cgroup *root) | 1769 | struct mem_cgroup *root) |
1810 | { | 1770 | { |
1811 | struct mem_cgroup *parent = memcg; | 1771 | struct mem_cgroup *parent = memcg; |
1812 | 1772 | ||
1813 | if (res_counter_soft_limit_excess(&memcg->res)) | 1773 | if (res_counter_soft_limit_excess(&memcg->res)) |
1814 | return VISIT; | 1774 | return true; |
1815 | 1775 | ||
1816 | /* | 1776 | /* |
1817 | * If any parent up to the root in the hierarchy is over its soft limit | 1777 | * If any parent up to the root in the hierarchy is over its soft limit |
@@ -1819,12 +1779,12 @@ mem_cgroup_soft_reclaim_eligible(struct mem_cgroup *memcg, | |||
1819 | */ | 1779 | */ |
1820 | while ((parent = parent_mem_cgroup(parent))) { | 1780 | while ((parent = parent_mem_cgroup(parent))) { |
1821 | if (res_counter_soft_limit_excess(&parent->res)) | 1781 | if (res_counter_soft_limit_excess(&parent->res)) |
1822 | return VISIT; | 1782 | return true; |
1823 | if (parent == root) | 1783 | if (parent == root) |
1824 | break; | 1784 | break; |
1825 | } | 1785 | } |
1826 | 1786 | ||
1827 | return SKIP; | 1787 | return false; |
1828 | } | 1788 | } |
1829 | 1789 | ||
1830 | static DEFINE_SPINLOCK(memcg_oom_lock); | 1790 | static DEFINE_SPINLOCK(memcg_oom_lock); |
diff --git a/mm/vmscan.c b/mm/vmscan.c index cdd300b81485..17a7134de4d7 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c | |||
@@ -2185,16 +2185,21 @@ __shrink_zone(struct zone *zone, struct scan_control *sc, bool soft_reclaim) | |||
2185 | .zone = zone, | 2185 | .zone = zone, |
2186 | .priority = sc->priority, | 2186 | .priority = sc->priority, |
2187 | }; | 2187 | }; |
2188 | struct mem_cgroup *memcg = NULL; | 2188 | struct mem_cgroup *memcg; |
2189 | mem_cgroup_iter_filter filter = (soft_reclaim) ? | ||
2190 | mem_cgroup_soft_reclaim_eligible : NULL; | ||
2191 | 2189 | ||
2192 | nr_reclaimed = sc->nr_reclaimed; | 2190 | nr_reclaimed = sc->nr_reclaimed; |
2193 | nr_scanned = sc->nr_scanned; | 2191 | nr_scanned = sc->nr_scanned; |
2194 | 2192 | ||
2195 | while ((memcg = mem_cgroup_iter_cond(root, memcg, &reclaim, filter))) { | 2193 | memcg = mem_cgroup_iter(root, NULL, &reclaim); |
2194 | do { | ||
2196 | struct lruvec *lruvec; | 2195 | struct lruvec *lruvec; |
2197 | 2196 | ||
2197 | if (soft_reclaim && | ||
2198 | !mem_cgroup_soft_reclaim_eligible(memcg, root)) { | ||
2199 | memcg = mem_cgroup_iter(root, memcg, &reclaim); | ||
2200 | continue; | ||
2201 | } | ||
2202 | |||
2198 | lruvec = mem_cgroup_zone_lruvec(zone, memcg); | 2203 | lruvec = mem_cgroup_zone_lruvec(zone, memcg); |
2199 | 2204 | ||
2200 | shrink_lruvec(lruvec, sc); | 2205 | shrink_lruvec(lruvec, sc); |
@@ -2214,7 +2219,8 @@ __shrink_zone(struct zone *zone, struct scan_control *sc, bool soft_reclaim) | |||
2214 | mem_cgroup_iter_break(root, memcg); | 2219 | mem_cgroup_iter_break(root, memcg); |
2215 | break; | 2220 | break; |
2216 | } | 2221 | } |
2217 | } | 2222 | memcg = mem_cgroup_iter(root, memcg, &reclaim); |
2223 | } while (memcg); | ||
2218 | 2224 | ||
2219 | vmpressure(sc->gfp_mask, sc->target_mem_cgroup, | 2225 | vmpressure(sc->gfp_mask, sc->target_mem_cgroup, |
2220 | sc->nr_scanned - nr_scanned, | 2226 | sc->nr_scanned - nr_scanned, |