hugetlb/cgroup: add charge/uncharge routines for hugetlb cgroup
[firefly-linux-kernel-4.4.55.git] / mm / hugetlb_cgroup.c
1 /*
2  *
3  * Copyright IBM Corporation, 2012
4  * Author Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of version 2.1 of the GNU Lesser General Public License
8  * as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it would be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13  *
14  */
15
16 #include <linux/cgroup.h>
17 #include <linux/slab.h>
18 #include <linux/hugetlb.h>
19 #include <linux/hugetlb_cgroup.h>
20
21 struct hugetlb_cgroup {
22         struct cgroup_subsys_state css;
23         /*
24          * the counter to account for hugepages from hugetlb.
25          */
26         struct res_counter hugepage[HUGE_MAX_HSTATE];
27 };
28
29 struct cgroup_subsys hugetlb_subsys __read_mostly;
30 static struct hugetlb_cgroup *root_h_cgroup __read_mostly;
31
32 static inline
33 struct hugetlb_cgroup *hugetlb_cgroup_from_css(struct cgroup_subsys_state *s)
34 {
35         return container_of(s, struct hugetlb_cgroup, css);
36 }
37
38 static inline
39 struct hugetlb_cgroup *hugetlb_cgroup_from_cgroup(struct cgroup *cgroup)
40 {
41         return hugetlb_cgroup_from_css(cgroup_subsys_state(cgroup,
42                                                            hugetlb_subsys_id));
43 }
44
45 static inline
46 struct hugetlb_cgroup *hugetlb_cgroup_from_task(struct task_struct *task)
47 {
48         return hugetlb_cgroup_from_css(task_subsys_state(task,
49                                                          hugetlb_subsys_id));
50 }
51
52 static inline bool hugetlb_cgroup_is_root(struct hugetlb_cgroup *h_cg)
53 {
54         return (h_cg == root_h_cgroup);
55 }
56
57 static inline struct hugetlb_cgroup *parent_hugetlb_cgroup(struct cgroup *cg)
58 {
59         if (!cg->parent)
60                 return NULL;
61         return hugetlb_cgroup_from_cgroup(cg->parent);
62 }
63
64 static inline bool hugetlb_cgroup_have_usage(struct cgroup *cg)
65 {
66         int idx;
67         struct hugetlb_cgroup *h_cg = hugetlb_cgroup_from_cgroup(cg);
68
69         for (idx = 0; idx < hugetlb_max_hstate; idx++) {
70                 if ((res_counter_read_u64(&h_cg->hugepage[idx], RES_USAGE)) > 0)
71                         return true;
72         }
73         return false;
74 }
75
76 static struct cgroup_subsys_state *hugetlb_cgroup_create(struct cgroup *cgroup)
77 {
78         int idx;
79         struct cgroup *parent_cgroup;
80         struct hugetlb_cgroup *h_cgroup, *parent_h_cgroup;
81
82         h_cgroup = kzalloc(sizeof(*h_cgroup), GFP_KERNEL);
83         if (!h_cgroup)
84                 return ERR_PTR(-ENOMEM);
85
86         parent_cgroup = cgroup->parent;
87         if (parent_cgroup) {
88                 parent_h_cgroup = hugetlb_cgroup_from_cgroup(parent_cgroup);
89                 for (idx = 0; idx < HUGE_MAX_HSTATE; idx++)
90                         res_counter_init(&h_cgroup->hugepage[idx],
91                                          &parent_h_cgroup->hugepage[idx]);
92         } else {
93                 root_h_cgroup = h_cgroup;
94                 for (idx = 0; idx < HUGE_MAX_HSTATE; idx++)
95                         res_counter_init(&h_cgroup->hugepage[idx], NULL);
96         }
97         return &h_cgroup->css;
98 }
99
100 static void hugetlb_cgroup_destroy(struct cgroup *cgroup)
101 {
102         struct hugetlb_cgroup *h_cgroup;
103
104         h_cgroup = hugetlb_cgroup_from_cgroup(cgroup);
105         kfree(h_cgroup);
106 }
107
108 static int hugetlb_cgroup_pre_destroy(struct cgroup *cgroup)
109 {
110         /* We will add the cgroup removal support in later patches */
111            return -EBUSY;
112 }
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
194 struct cgroup_subsys hugetlb_subsys = {
195         .name = "hugetlb",
196         .create     = hugetlb_cgroup_create,
197         .pre_destroy = hugetlb_cgroup_pre_destroy,
198         .destroy    = hugetlb_cgroup_destroy,
199         .subsys_id  = hugetlb_subsys_id,
200 };