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 ret = -EBUSY;
161 goto err_charge;
162 }
163 }
164 return 0;
165
166err_charge:
167 for (j = i; j; j = parent_misc(j)) {
168 atomic_long_inc(&j->res[type].events);
169 cgroup_file_notify(&j->events_file);
170 }
171
172 for (j = cg; j != i; j = parent_misc(j))
173 misc_cg_cancel_charge(type, j, amount);
174 misc_cg_cancel_charge(type, i, amount);
175 return ret;
176}
177EXPORT_SYMBOL_GPL(misc_cg_try_charge);
178
179
180
181
182
183
184
185
186
187void misc_cg_uncharge(enum misc_res_type type, struct misc_cg *cg,
188 unsigned long amount)
189{
190 struct misc_cg *i;
191
192 if (!(amount && valid_type(type) && cg))
193 return;
194
195 for (i = cg; i; i = parent_misc(i))
196 misc_cg_cancel_charge(type, i, amount);
197}
198EXPORT_SYMBOL_GPL(misc_cg_uncharge);
199
200
201
202
203
204
205
206
207
208static int misc_cg_max_show(struct seq_file *sf, void *v)
209{
210 int i;
211 struct misc_cg *cg = css_misc(seq_css(sf));
212 unsigned long max;
213
214 for (i = 0; i < MISC_CG_RES_TYPES; i++) {
215 if (READ_ONCE(misc_res_capacity[i])) {
216 max = READ_ONCE(cg->res[i].max);
217 if (max == MAX_NUM)
218 seq_printf(sf, "%s max\n", misc_res_name[i]);
219 else
220 seq_printf(sf, "%s %lu\n", misc_res_name[i],
221 max);
222 }
223 }
224
225 return 0;
226}
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246static ssize_t misc_cg_max_write(struct kernfs_open_file *of, char *buf,
247 size_t nbytes, loff_t off)
248{
249 struct misc_cg *cg;
250 unsigned long max;
251 int ret = 0, i;
252 enum misc_res_type type = MISC_CG_RES_TYPES;
253 char *token;
254
255 buf = strstrip(buf);
256 token = strsep(&buf, " ");
257
258 if (!token || !buf)
259 return -EINVAL;
260
261 for (i = 0; i < MISC_CG_RES_TYPES; i++) {
262 if (!strcmp(misc_res_name[i], token)) {
263 type = i;
264 break;
265 }
266 }
267
268 if (type == MISC_CG_RES_TYPES)
269 return -EINVAL;
270
271 if (!strcmp(MAX_STR, buf)) {
272 max = MAX_NUM;
273 } else {
274 ret = kstrtoul(buf, 0, &max);
275 if (ret)
276 return ret;
277 }
278
279 cg = css_misc(of_css(of));
280
281 if (READ_ONCE(misc_res_capacity[type]))
282 WRITE_ONCE(cg->res[type].max, max);
283 else
284 ret = -EINVAL;
285
286 return ret ? ret : nbytes;
287}
288
289
290
291
292
293
294
295
296
297static int misc_cg_current_show(struct seq_file *sf, void *v)
298{
299 int i;
300 unsigned long usage;
301 struct misc_cg *cg = css_misc(seq_css(sf));
302
303 for (i = 0; i < MISC_CG_RES_TYPES; i++) {
304 usage = atomic_long_read(&cg->res[i].usage);
305 if (READ_ONCE(misc_res_capacity[i]) || usage)
306 seq_printf(sf, "%s %lu\n", misc_res_name[i], usage);
307 }
308
309 return 0;
310}
311
312
313
314
315
316
317
318
319
320
321
322static int misc_cg_capacity_show(struct seq_file *sf, void *v)
323{
324 int i;
325 unsigned long cap;
326
327 for (i = 0; i < MISC_CG_RES_TYPES; i++) {
328 cap = READ_ONCE(misc_res_capacity[i]);
329 if (cap)
330 seq_printf(sf, "%s %lu\n", misc_res_name[i], cap);
331 }
332
333 return 0;
334}
335
336static int misc_events_show(struct seq_file *sf, void *v)
337{
338 struct misc_cg *cg = css_misc(seq_css(sf));
339 unsigned long events, i;
340
341 for (i = 0; i < MISC_CG_RES_TYPES; i++) {
342 events = atomic_long_read(&cg->res[i].events);
343 if (READ_ONCE(misc_res_capacity[i]) || events)
344 seq_printf(sf, "%s.max %lu\n", misc_res_name[i], events);
345 }
346 return 0;
347}
348
349
350static struct cftype misc_cg_files[] = {
351 {
352 .name = "max",
353 .write = misc_cg_max_write,
354 .seq_show = misc_cg_max_show,
355 .flags = CFTYPE_NOT_ON_ROOT,
356 },
357 {
358 .name = "current",
359 .seq_show = misc_cg_current_show,
360 .flags = CFTYPE_NOT_ON_ROOT,
361 },
362 {
363 .name = "capacity",
364 .seq_show = misc_cg_capacity_show,
365 .flags = CFTYPE_ONLY_ON_ROOT,
366 },
367 {
368 .name = "events",
369 .flags = CFTYPE_NOT_ON_ROOT,
370 .file_offset = offsetof(struct misc_cg, events_file),
371 .seq_show = misc_events_show,
372 },
373 {}
374};
375
376
377
378
379
380
381
382
383
384
385static struct cgroup_subsys_state *
386misc_cg_alloc(struct cgroup_subsys_state *parent_css)
387{
388 enum misc_res_type i;
389 struct misc_cg *cg;
390
391 if (!parent_css) {
392 cg = &root_cg;
393 } else {
394 cg = kzalloc(sizeof(*cg), GFP_KERNEL);
395 if (!cg)
396 return ERR_PTR(-ENOMEM);
397 }
398
399 for (i = 0; i < MISC_CG_RES_TYPES; i++) {
400 WRITE_ONCE(cg->res[i].max, MAX_NUM);
401 atomic_long_set(&cg->res[i].usage, 0);
402 }
403
404 return &cg->css;
405}
406
407
408
409
410
411
412
413static void misc_cg_free(struct cgroup_subsys_state *css)
414{
415 kfree(css_misc(css));
416}
417
418
419struct cgroup_subsys misc_cgrp_subsys = {
420 .css_alloc = misc_cg_alloc,
421 .css_free = misc_cg_free,
422 .legacy_cftypes = misc_cg_files,
423 .dfl_cftypes = misc_cg_files,
424};
425