diff options
-rw-r--r-- | include/linux/hugetlb_cgroup.h | 38 | ||||
-rw-r--r-- | mm/hugetlb.c | 16 | ||||
-rw-r--r-- | mm/hugetlb_cgroup.c | 80 |
3 files changed, 133 insertions, 1 deletions
diff --git a/include/linux/hugetlb_cgroup.h b/include/linux/hugetlb_cgroup.h index e5451a3b4ebc..7d3fde996be3 100644 --- a/include/linux/hugetlb_cgroup.h +++ b/include/linux/hugetlb_cgroup.h | |||
@@ -53,6 +53,16 @@ static inline bool hugetlb_cgroup_disabled(void) | |||
53 | return false; | 53 | return false; |
54 | } | 54 | } |
55 | 55 | ||
56 | extern int hugetlb_cgroup_charge_cgroup(int idx, unsigned long nr_pages, | ||
57 | struct hugetlb_cgroup **ptr); | ||
58 | extern void hugetlb_cgroup_commit_charge(int idx, unsigned long nr_pages, | ||
59 | struct hugetlb_cgroup *h_cg, | ||
60 | struct page *page); | ||
61 | extern void hugetlb_cgroup_uncharge_page(int idx, unsigned long nr_pages, | ||
62 | struct page *page); | ||
63 | extern void hugetlb_cgroup_uncharge_cgroup(int idx, unsigned long nr_pages, | ||
64 | struct hugetlb_cgroup *h_cg); | ||
65 | |||
56 | #else | 66 | #else |
57 | static inline struct hugetlb_cgroup *hugetlb_cgroup_from_page(struct page *page) | 67 | static inline struct hugetlb_cgroup *hugetlb_cgroup_from_page(struct page *page) |
58 | { | 68 | { |
@@ -70,5 +80,33 @@ static inline bool hugetlb_cgroup_disabled(void) | |||
70 | return true; | 80 | return true; |
71 | } | 81 | } |
72 | 82 | ||
83 | static inline int | ||
84 | hugetlb_cgroup_charge_cgroup(int idx, unsigned long nr_pages, | ||
85 | struct hugetlb_cgroup **ptr) | ||
86 | { | ||
87 | return 0; | ||
88 | } | ||
89 | |||
90 | static inline void | ||
91 | hugetlb_cgroup_commit_charge(int idx, unsigned long nr_pages, | ||
92 | struct hugetlb_cgroup *h_cg, | ||
93 | struct page *page) | ||
94 | { | ||
95 | return; | ||
96 | } | ||
97 | |||
98 | static inline void | ||
99 | hugetlb_cgroup_uncharge_page(int idx, unsigned long nr_pages, struct page *page) | ||
100 | { | ||
101 | return; | ||
102 | } | ||
103 | |||
104 | static inline void | ||
105 | hugetlb_cgroup_uncharge_cgroup(int idx, unsigned long nr_pages, | ||
106 | struct hugetlb_cgroup *h_cg) | ||
107 | { | ||
108 | return; | ||
109 | } | ||
110 | |||
73 | #endif /* CONFIG_MEM_RES_CTLR_HUGETLB */ | 111 | #endif /* CONFIG_MEM_RES_CTLR_HUGETLB */ |
74 | #endif | 112 | #endif |
diff --git a/mm/hugetlb.c b/mm/hugetlb.c index efe29b53daff..16a0f32c4820 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c | |||
@@ -627,6 +627,8 @@ static void free_huge_page(struct page *page) | |||
627 | BUG_ON(page_mapcount(page)); | 627 | BUG_ON(page_mapcount(page)); |
628 | 628 | ||
629 | spin_lock(&hugetlb_lock); | 629 | spin_lock(&hugetlb_lock); |
630 | hugetlb_cgroup_uncharge_page(hstate_index(h), | ||
631 | pages_per_huge_page(h), page); | ||
630 | if (h->surplus_huge_pages_node[nid] && huge_page_order(h) < MAX_ORDER) { | 632 | if (h->surplus_huge_pages_node[nid] && huge_page_order(h) < MAX_ORDER) { |
631 | /* remove the page from active list */ | 633 | /* remove the page from active list */ |
632 | list_del(&page->lru); | 634 | list_del(&page->lru); |
@@ -1115,7 +1117,10 @@ static struct page *alloc_huge_page(struct vm_area_struct *vma, | |||
1115 | struct hstate *h = hstate_vma(vma); | 1117 | struct hstate *h = hstate_vma(vma); |
1116 | struct page *page; | 1118 | struct page *page; |
1117 | long chg; | 1119 | long chg; |
1120 | int ret, idx; | ||
1121 | struct hugetlb_cgroup *h_cg; | ||
1118 | 1122 | ||
1123 | idx = hstate_index(h); | ||
1119 | /* | 1124 | /* |
1120 | * Processes that did not create the mapping will have no | 1125 | * Processes that did not create the mapping will have no |
1121 | * reserves and will not have accounted against subpool | 1126 | * reserves and will not have accounted against subpool |
@@ -1131,6 +1136,11 @@ static struct page *alloc_huge_page(struct vm_area_struct *vma, | |||
1131 | if (hugepage_subpool_get_pages(spool, chg)) | 1136 | if (hugepage_subpool_get_pages(spool, chg)) |
1132 | return ERR_PTR(-ENOSPC); | 1137 | return ERR_PTR(-ENOSPC); |
1133 | 1138 | ||
1139 | ret = hugetlb_cgroup_charge_cgroup(idx, pages_per_huge_page(h), &h_cg); | ||
1140 | if (ret) { | ||
1141 | hugepage_subpool_put_pages(spool, chg); | ||
1142 | return ERR_PTR(-ENOSPC); | ||
1143 | } | ||
1134 | spin_lock(&hugetlb_lock); | 1144 | spin_lock(&hugetlb_lock); |
1135 | page = dequeue_huge_page_vma(h, vma, addr, avoid_reserve); | 1145 | page = dequeue_huge_page_vma(h, vma, addr, avoid_reserve); |
1136 | spin_unlock(&hugetlb_lock); | 1146 | spin_unlock(&hugetlb_lock); |
@@ -1138,6 +1148,9 @@ static struct page *alloc_huge_page(struct vm_area_struct *vma, | |||
1138 | if (!page) { | 1148 | if (!page) { |
1139 | page = alloc_buddy_huge_page(h, NUMA_NO_NODE); | 1149 | page = alloc_buddy_huge_page(h, NUMA_NO_NODE); |
1140 | if (!page) { | 1150 | if (!page) { |
1151 | hugetlb_cgroup_uncharge_cgroup(idx, | ||
1152 | pages_per_huge_page(h), | ||
1153 | h_cg); | ||
1141 | hugepage_subpool_put_pages(spool, chg); | 1154 | hugepage_subpool_put_pages(spool, chg); |
1142 | return ERR_PTR(-ENOSPC); | 1155 | return ERR_PTR(-ENOSPC); |
1143 | } | 1156 | } |
@@ -1146,7 +1159,8 @@ static struct page *alloc_huge_page(struct vm_area_struct *vma, | |||
1146 | set_page_private(page, (unsigned long)spool); | 1159 | set_page_private(page, (unsigned long)spool); |
1147 | 1160 | ||
1148 | vma_commit_reservation(h, vma, addr); | 1161 | vma_commit_reservation(h, vma, addr); |
1149 | 1162 | /* update page cgroup details */ | |
1163 | hugetlb_cgroup_commit_charge(idx, pages_per_huge_page(h), h_cg, page); | ||
1150 | return page; | 1164 | return page; |
1151 | } | 1165 | } |
1152 | 1166 | ||
diff --git a/mm/hugetlb_cgroup.c b/mm/hugetlb_cgroup.c index 0d1a66e9039b..63e04cfa437d 100644 --- a/mm/hugetlb_cgroup.c +++ b/mm/hugetlb_cgroup.c | |||
@@ -111,6 +111,86 @@ static int hugetlb_cgroup_pre_destroy(struct cgroup *cgroup) | |||
111 | return -EBUSY; | 111 | return -EBUSY; |
112 | } | 112 | } |
113 | 113 | ||
114 | int hugetlb_cgroup_charge_cgroup(int idx, unsigned long nr_pages, | ||
115 | struct hugetlb_cgroup **ptr) | ||
116 | { | ||
117 | int ret = 0; | ||
118 | struct res_counter *fail_res; | ||
119 | struct hugetlb_cgroup *h_cg = NULL; | ||
120 | unsigned long csize = nr_pages * PAGE_SIZE; | ||
121 | |||
122 | if (hugetlb_cgroup_disabled()) | ||
123 | goto done; | ||
124 | /* | ||
125 | * We don't charge any cgroup if the compound page have less | ||
126 | * than 3 pages. | ||
127 | */ | ||
128 | if (huge_page_order(&hstates[idx]) < HUGETLB_CGROUP_MIN_ORDER) | ||
129 | goto done; | ||
130 | again: | ||
131 | rcu_read_lock(); | ||
132 | h_cg = hugetlb_cgroup_from_task(current); | ||
133 | if (!css_tryget(&h_cg->css)) { | ||
134 | rcu_read_unlock(); | ||
135 | goto again; | ||
136 | } | ||
137 | rcu_read_unlock(); | ||
138 | |||
139 | ret = res_counter_charge(&h_cg->hugepage[idx], csize, &fail_res); | ||
140 | css_put(&h_cg->css); | ||
141 | done: | ||
142 | *ptr = h_cg; | ||
143 | return ret; | ||
144 | } | ||
145 | |||
146 | void hugetlb_cgroup_commit_charge(int idx, unsigned long nr_pages, | ||
147 | struct hugetlb_cgroup *h_cg, | ||
148 | struct page *page) | ||
149 | { | ||
150 | if (hugetlb_cgroup_disabled() || !h_cg) | ||
151 | return; | ||
152 | |||
153 | spin_lock(&hugetlb_lock); | ||
154 | set_hugetlb_cgroup(page, h_cg); | ||
155 | spin_unlock(&hugetlb_lock); | ||
156 | return; | ||
157 | } | ||
158 | |||
159 | /* | ||
160 | * Should be called with hugetlb_lock held | ||
161 | */ | ||
162 | void hugetlb_cgroup_uncharge_page(int idx, unsigned long nr_pages, | ||
163 | struct page *page) | ||
164 | { | ||
165 | struct hugetlb_cgroup *h_cg; | ||
166 | unsigned long csize = nr_pages * PAGE_SIZE; | ||
167 | |||
168 | if (hugetlb_cgroup_disabled()) | ||
169 | return; | ||
170 | VM_BUG_ON(!spin_is_locked(&hugetlb_lock)); | ||
171 | h_cg = hugetlb_cgroup_from_page(page); | ||
172 | if (unlikely(!h_cg)) | ||
173 | return; | ||
174 | set_hugetlb_cgroup(page, NULL); | ||
175 | res_counter_uncharge(&h_cg->hugepage[idx], csize); | ||
176 | return; | ||
177 | } | ||
178 | |||
179 | void hugetlb_cgroup_uncharge_cgroup(int idx, unsigned long nr_pages, | ||
180 | struct hugetlb_cgroup *h_cg) | ||
181 | { | ||
182 | unsigned long csize = nr_pages * PAGE_SIZE; | ||
183 | |||
184 | if (hugetlb_cgroup_disabled() || !h_cg) | ||
185 | return; | ||
186 | |||
187 | if (huge_page_order(&hstates[idx]) < HUGETLB_CGROUP_MIN_ORDER) | ||
188 | return; | ||
189 | |||
190 | res_counter_uncharge(&h_cg->hugepage[idx], csize); | ||
191 | return; | ||
192 | } | ||
193 | |||
114 | struct cgroup_subsys hugetlb_subsys = { | 194 | struct cgroup_subsys hugetlb_subsys = { |
115 | .name = "hugetlb", | 195 | .name = "hugetlb", |
116 | .create = hugetlb_cgroup_create, | 196 | .create = hugetlb_cgroup_create, |