aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/hugetlb_cgroup.h38
-rw-r--r--mm/hugetlb.c16
-rw-r--r--mm/hugetlb_cgroup.c80
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
56extern int hugetlb_cgroup_charge_cgroup(int idx, unsigned long nr_pages,
57 struct hugetlb_cgroup **ptr);
58extern void hugetlb_cgroup_commit_charge(int idx, unsigned long nr_pages,
59 struct hugetlb_cgroup *h_cg,
60 struct page *page);
61extern void hugetlb_cgroup_uncharge_page(int idx, unsigned long nr_pages,
62 struct page *page);
63extern void hugetlb_cgroup_uncharge_cgroup(int idx, unsigned long nr_pages,
64 struct hugetlb_cgroup *h_cg);
65
56#else 66#else
57static inline struct hugetlb_cgroup *hugetlb_cgroup_from_page(struct page *page) 67static 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
83static inline int
84hugetlb_cgroup_charge_cgroup(int idx, unsigned long nr_pages,
85 struct hugetlb_cgroup **ptr)
86{
87 return 0;
88}
89
90static inline void
91hugetlb_cgroup_commit_charge(int idx, unsigned long nr_pages,
92 struct hugetlb_cgroup *h_cg,
93 struct page *page)
94{
95 return;
96}
97
98static inline void
99hugetlb_cgroup_uncharge_page(int idx, unsigned long nr_pages, struct page *page)
100{
101 return;
102}
103
104static inline void
105hugetlb_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
114int 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;
130again:
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);
141done:
142 *ptr = h_cg;
143 return ret;
144}
145
146void 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 */
162void 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
179void 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
114struct cgroup_subsys hugetlb_subsys = { 194struct cgroup_subsys hugetlb_subsys = {
115 .name = "hugetlb", 195 .name = "hugetlb",
116 .create = hugetlb_cgroup_create, 196 .create = hugetlb_cgroup_create,