1
2
3
4
5
6
7
8
9#include <linux/limits.h>
10#include <linux/cgroup.h>
11#include <linux/errno.h>
12#include <linux/atomic.h>
13#include <linux/slab.h>
14#include <linux/misc_cgroup.h>
15
16#define MAX_STR "max"
17#define MAX_NUM ULONG_MAX
18
19
20static const char *const misc_res_name[] = {
21#ifdef CONFIG_KVM_AMD_SEV
22
23 "sev",
24
25 "sev_es",
26#endif
27};
28
29
30static struct misc_cg root_cg;
31
32
33
34
35
36
37
38
39
40static unsigned long misc_res_capacity[MISC_CG_RES_TYPES];
41
42
43
44
45
46
47
48
49
50
51static struct misc_cg *parent_misc(struct misc_cg *cgroup)
52{
53 return cgroup ? css_misc(cgroup->css.parent) : NULL;
54}
55
56
57
58
59
60
61
62
63
64
65static inline bool valid_type(enum misc_res_type type)
66{
67 return type >= 0 && type < MISC_CG_RES_TYPES;
68}
69
70
71
72
73
74
75
76
77unsigned long misc_cg_res_total_usage(enum misc_res_type type)
78{
79 if (valid_type(type))
80 return atomic_long_read(&root_cg.res[type].usage);
81
82 return 0;
83}
84EXPORT_SYMBOL_GPL(misc_cg_res_total_usage);
85
86
87
88
89
90
91
92
93
94
95
96
97
98int misc_cg_set_capacity(enum misc_res_type type, unsigned long capacity)
99{
100 if (!valid_type(type))
101 return -EINVAL;
102
103 WRITE_ONCE(misc_res_capacity[type], capacity);
104 return 0;
105}
106EXPORT_SYMBOL_GPL(misc_cg_set_capacity);
107
108
109
110
111
112
113
114
115
116static void misc_cg_cancel_charge(enum misc_res_type type, struct misc_cg *cg,
117 unsigned long amount)
118{
119 WARN_ONCE(atomic_long_add_negative(-amount, &cg->res[type].usage),
120 "misc cgroup resource %s became less than 0",
121 misc_res_name[type]);
122}
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140int misc_cg_try_charge(enum misc_res_type type, struct misc_cg *cg,
141 unsigned long amount)
142{
143 struct misc_cg *i, *j;
144 int ret;
145 struct misc_res *res;
146 int new_usage;
147
148 if (!(valid_type(type) && cg && READ_ONCE(misc_res_capacity[type])))
149 return -EINVAL;
150
151 if (!amount)
152 return 0;
153
154 for (i = cg; i; i = parent_misc(i)) {
155 res = &i->res[type];
156
157 new_usage = atomic_long_add_return(amount, &res->usage);
158 if (new_usage > READ_ONCE(res->max) ||
159 new_usage > READ_ONCE(misc_res_capacity[type])) {
160 if (!res->failed) {
161 pr_info("cgroup: charge rejected by the misc controller for %s resource in ",
162 misc_res_name[type]);
163 pr_cont_cgroup_path(i->css.cgroup);
164 pr_cont("\n");
165 res->failed = true;
166 }
167 ret = -EBUSY;
168 goto err_charge;
169 }
170 }
171 return 0;
172
173err_charge:
174 for (j = cg; j != i; j = parent_misc(j))
175 misc_cg_cancel_charge(type, j, amount);
176 misc_cg_cancel_charge(type, i, amount);
177 return ret;
178}
179EXPORT_SYMBOL_GPL(misc_cg_try_charge);
180
181
182
183
184
185
186
187
188
189void misc_cg_uncharge(enum misc_res_type type, struct misc_cg *cg,
190 unsigned long amount)
191{
192 struct misc_cg *i;
193
194 if (!(amount && valid_type(type) && cg))
195 return;
196
197 for (i = cg; i; i = parent_misc(i))
198 misc_cg_cancel_charge(type, i, amount);
199}
200EXPORT_SYMBOL_GPL(misc_cg_uncharge);
201
202
203
204
205
206
207
208
209
210static int misc_cg_max_show(struct seq_file *sf, void *v)
211{
212 int i;
213 struct misc_cg *cg = css_misc(seq_css(sf));
214 unsigned long max;
215
216 for (i = 0; i < MISC_CG_RES_TYPES; i++) {
217 if (READ_ONCE(misc_res_capacity[i])) {
218 max = READ_ONCE(cg->res[i].max);
219 if (max == MAX_NUM)
220 seq_printf(sf, "%s max\n", misc_res_name[i]);
221 else
222 seq_printf(sf, "%s %lu\n", misc_res_name[i],
223 max);
224 }
225 }
226
227 return 0;
228}
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248static ssize_t misc_cg_max_write(struct kernfs_open_file *of, char *buf,
249 size_t nbytes, loff_t off)
250{
251 struct misc_cg *cg;
252 unsigned long max;
253 int ret = 0, i;
254 enum misc_res_type type = MISC_CG_RES_TYPES;
255 char *token;
256
257 buf = strstrip(buf);
258 token = strsep(&buf, " ");
259
260 if (!token || !buf)
261 return -EINVAL;
262
263 for (i = 0; i < MISC_CG_RES_TYPES; i++) {
264 if (!strcmp(misc_res_name[i], token)) {
265 type = i;
266 break;
267 }
268 }
269
270 if (type == MISC_CG_RES_TYPES)
271 return -EINVAL;
272
273 if (!strcmp(MAX_STR, buf)) {
274 max = MAX_NUM;
275 } else {
276 ret = kstrtoul(buf, 0, &max);
277 if (ret)
278 return ret;
279 }
280
281 cg = css_misc(of_css(of));
282
283 if (READ_ONCE(misc_res_capacity[type]))
284 WRITE_ONCE(cg->res[type].max, max);
285 else
286 ret = -EINVAL;
287
288 return ret ? ret : nbytes;
289}
290
291
292
293
294
295
296
297
298
299static int misc_cg_current_show(struct seq_file *sf, void *v)
300{
301 int i;
302 unsigned long usage;
303 struct misc_cg *cg = css_misc(seq_css(sf));
304
305 for (i = 0; i < MISC_CG_RES_TYPES; i++) {
306 usage = atomic_long_read(&cg->res[i].usage);
307 if (READ_ONCE(misc_res_capacity[i]) || usage)
308 seq_printf(sf, "%s %lu\n", misc_res_name[i], usage);
309 }
310
311 return 0;
312}
313
314
315
316
317
318
319
320
321
322
323
324static int misc_cg_capacity_show(struct seq_file *sf, void *v)
325{
326 int i;
327 unsigned long cap;
328
329 for (i = 0; i < MISC_CG_RES_TYPES; i++) {
330 cap = READ_ONCE(misc_res_capacity[i]);
331 if (cap)
332 seq_printf(sf, "%s %lu\n", misc_res_name[i], cap);
333 }
334
335 return 0;
336}
337
338
339static struct cftype misc_cg_files[] = {
340 {
341 .name = "max",
342 .write = misc_cg_max_write,
343 .seq_show = misc_cg_max_show,
344 .flags = CFTYPE_NOT_ON_ROOT,
345 },
346 {
347 .name = "current",
348 .seq_show = misc_cg_current_show,
349 .flags = CFTYPE_NOT_ON_ROOT,
350 },
351 {
352 .name = "capacity",
353 .seq_show = misc_cg_capacity_show,
354 .flags = CFTYPE_ONLY_ON_ROOT,
355 },
356 {}
357};
358
359
360
361
362
363
364
365
366
367
368static struct cgroup_subsys_state *
369misc_cg_alloc(struct cgroup_subsys_state *parent_css)
370{
371 enum misc_res_type i;
372 struct misc_cg *cg;
373
374 if (!parent_css) {
375 cg = &root_cg;
376 } else {
377 cg = kzalloc(sizeof(*cg), GFP_KERNEL);
378 if (!cg)
379 return ERR_PTR(-ENOMEM);
380 }
381
382 for (i = 0; i < MISC_CG_RES_TYPES; i++) {
383 WRITE_ONCE(cg->res[i].max, MAX_NUM);
384 atomic_long_set(&cg->res[i].usage, 0);
385 }
386
387 return &cg->css;
388}
389
390
391
392
393
394
395
396static void misc_cg_free(struct cgroup_subsys_state *css)
397{
398 kfree(css_misc(css));
399}
400
401
402struct cgroup_subsys misc_cgrp_subsys = {
403 .css_alloc = misc_cg_alloc,
404 .css_free = misc_cg_free,
405 .legacy_cftypes = misc_cg_files,
406 .dfl_cftypes = misc_cg_files,
407};
408