xref: /linux/kernel/cgroup/misc.c (revision 0d20742b8e6bd94a3a335c061557ec9592a3444b)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Miscellaneous cgroup controller
4  *
5  * Copyright 2020 Google LLC
6  * Author: Vipin Sharma <vipinsh@google.com>
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 U64_MAX
18 
19 /* Miscellaneous res name, keep it in sync with enum misc_res_type */
20 static const char *const misc_res_name[] = {
21 #ifdef CONFIG_KVM_AMD_SEV
22 	/* AMD SEV ASIDs resource */
23 	"sev",
24 	/* AMD SEV-ES ASIDs resource */
25 	"sev_es",
26 #endif
27 #ifdef CONFIG_INTEL_TDX_HOST
28 	/* Intel TDX HKIDs resource */
29 	"tdx",
30 #endif
31 };
32 
33 /* Root misc cgroup */
34 static struct misc_cg root_cg;
35 
36 /*
37  * Miscellaneous resources capacity for the entire machine. 0 capacity means
38  * resource is not initialized or not present in the host.
39  *
40  * root_cg.max and capacity are independent of each other. root_cg.max can be
41  * more than the actual capacity. We are using Limits resource distribution
42  * model of cgroup for miscellaneous controller.
43  */
44 static u64 misc_res_capacity[MISC_CG_RES_TYPES];
45 
46 /**
47  * parent_misc() - Get the parent of the passed misc cgroup.
48  * @cgroup: cgroup whose parent needs to be fetched.
49  *
50  * Context: Any context.
51  * Return:
52  * * struct misc_cg* - Parent of the @cgroup.
53  * * %NULL - If @cgroup is null or the passed cgroup does not have a parent.
54  */
55 static struct misc_cg *parent_misc(struct misc_cg *cgroup)
56 {
57 	return cgroup ? css_misc(cgroup->css.parent) : NULL;
58 }
59 
60 /**
61  * valid_type() - Check if @type is valid or not.
62  * @type: misc res type.
63  *
64  * Context: Any context.
65  * Return:
66  * * true - If valid type.
67  * * false - If not valid type.
68  */
69 static inline bool valid_type(enum misc_res_type type)
70 {
71 	return type >= 0 && type < MISC_CG_RES_TYPES;
72 }
73 
74 /**
75  * misc_cg_res_total_usage() - Get the current total usage of the resource.
76  * @type: misc res type.
77  *
78  * Context: Any context.
79  * Return: Current total usage of the resource.
80  */
81 u64 misc_cg_res_total_usage(enum misc_res_type type)
82 {
83 	if (valid_type(type))
84 		return atomic64_read(&root_cg.res[type].usage);
85 
86 	return 0;
87 }
88 EXPORT_SYMBOL_GPL(misc_cg_res_total_usage);
89 
90 /**
91  * misc_cg_set_capacity() - Set the capacity of the misc cgroup res.
92  * @type: Type of the misc res.
93  * @capacity: Supported capacity of the misc res on the host.
94  *
95  * If capacity is 0 then the charging a misc cgroup fails for that type.
96  *
97  * Context: Any context.
98  * Return:
99  * * %0 - Successfully registered the capacity.
100  * * %-EINVAL - If @type is invalid.
101  */
102 int misc_cg_set_capacity(enum misc_res_type type, u64 capacity)
103 {
104 	if (!valid_type(type))
105 		return -EINVAL;
106 
107 	WRITE_ONCE(misc_res_capacity[type], capacity);
108 	return 0;
109 }
110 EXPORT_SYMBOL_GPL(misc_cg_set_capacity);
111 
112 /**
113  * misc_cg_cancel_charge() - Cancel the charge from the misc cgroup.
114  * @type: Misc res type in misc cg to cancel the charge from.
115  * @cg: Misc cgroup to cancel charge from.
116  * @amount: Amount to cancel.
117  *
118  * Context: Any context.
119  */
120 static void misc_cg_cancel_charge(enum misc_res_type type, struct misc_cg *cg,
121 				  u64 amount)
122 {
123 	WARN_ONCE(atomic64_add_negative(-amount, &cg->res[type].usage),
124 		  "misc cgroup resource %s became less than 0",
125 		  misc_res_name[type]);
126 }
127 
128 static void misc_cg_update_watermark(struct misc_res *res, u64 new_usage)
129 {
130 	u64 old;
131 
132 	while (true) {
133 		old = atomic64_read(&res->watermark);
134 		if (new_usage <= old)
135 			break;
136 		if (atomic64_cmpxchg(&res->watermark, old, new_usage) == old)
137 			break;
138 	}
139 }
140 
141 static void misc_cg_event(enum misc_res_type type, struct misc_cg *cg)
142 {
143 	atomic64_inc(&cg->res[type].events_local);
144 	cgroup_file_notify(&cg->events_local_file);
145 
146 	for (; parent_misc(cg); cg = parent_misc(cg)) {
147 		atomic64_inc(&cg->res[type].events);
148 		cgroup_file_notify(&cg->events_file);
149 	}
150 }
151 
152 /**
153  * misc_cg_try_charge() - Try charging the misc cgroup.
154  * @type: Misc res type to charge.
155  * @cg: Misc cgroup which will be charged.
156  * @amount: Amount to charge.
157  *
158  * Charge @amount to the misc cgroup. Caller must use the same cgroup during
159  * the uncharge call.
160  *
161  * Context: Any context.
162  * Return:
163  * * %0 - If successfully charged.
164  * * -EINVAL - If @type is invalid or misc res has 0 capacity.
165  * * -EBUSY - If max limit will be crossed or total usage will be more than the
166  *	      capacity.
167  */
168 int misc_cg_try_charge(enum misc_res_type type, struct misc_cg *cg, u64 amount)
169 {
170 	struct misc_cg *i, *j;
171 	int ret;
172 	struct misc_res *res;
173 	u64 new_usage;
174 
175 	if (!(valid_type(type) && cg && READ_ONCE(misc_res_capacity[type])))
176 		return -EINVAL;
177 
178 	if (!amount)
179 		return 0;
180 
181 	for (i = cg; i; i = parent_misc(i)) {
182 		res = &i->res[type];
183 
184 		new_usage = atomic64_add_return(amount, &res->usage);
185 		if (new_usage > READ_ONCE(res->max) ||
186 		    new_usage > READ_ONCE(misc_res_capacity[type])) {
187 			ret = -EBUSY;
188 			goto err_charge;
189 		}
190 		misc_cg_update_watermark(res, new_usage);
191 	}
192 	return 0;
193 
194 err_charge:
195 	misc_cg_event(type, i);
196 
197 	for (j = cg; j != i; j = parent_misc(j))
198 		misc_cg_cancel_charge(type, j, amount);
199 	misc_cg_cancel_charge(type, i, amount);
200 	return ret;
201 }
202 EXPORT_SYMBOL_GPL(misc_cg_try_charge);
203 
204 /**
205  * misc_cg_uncharge() - Uncharge the misc cgroup.
206  * @type: Misc res type which was charged.
207  * @cg: Misc cgroup which will be uncharged.
208  * @amount: Charged amount.
209  *
210  * Context: Any context.
211  */
212 void misc_cg_uncharge(enum misc_res_type type, struct misc_cg *cg, u64 amount)
213 {
214 	struct misc_cg *i;
215 
216 	if (!(amount && valid_type(type) && cg))
217 		return;
218 
219 	for (i = cg; i; i = parent_misc(i))
220 		misc_cg_cancel_charge(type, i, amount);
221 }
222 EXPORT_SYMBOL_GPL(misc_cg_uncharge);
223 
224 /**
225  * misc_cg_max_show() - Show the misc cgroup max limit.
226  * @sf: Interface file
227  * @v: Arguments passed
228  *
229  * Context: Any context.
230  * Return: 0 to denote successful print.
231  */
232 static int misc_cg_max_show(struct seq_file *sf, void *v)
233 {
234 	int i;
235 	struct misc_cg *cg = css_misc(seq_css(sf));
236 	u64 max;
237 
238 	for (i = 0; i < MISC_CG_RES_TYPES; i++) {
239 		if (READ_ONCE(misc_res_capacity[i])) {
240 			max = READ_ONCE(cg->res[i].max);
241 			if (max == MAX_NUM)
242 				seq_printf(sf, "%s max\n", misc_res_name[i]);
243 			else
244 				seq_printf(sf, "%s %llu\n", misc_res_name[i],
245 					   max);
246 		}
247 	}
248 
249 	return 0;
250 }
251 
252 /**
253  * misc_cg_max_write() - Update the maximum limit of the cgroup.
254  * @of: Handler for the file.
255  * @buf: Data from the user. It should be either "max", 0, or a positive
256  *	 integer.
257  * @nbytes: Number of bytes of the data.
258  * @off: Offset in the file.
259  *
260  * User can pass data like:
261  * echo sev 23 > misc.max, OR
262  * echo sev max > misc.max
263  *
264  * Context: Any context.
265  * Return:
266  * * >= 0 - Number of bytes processed in the input.
267  * * -EINVAL - If buf is not valid.
268  * * -ERANGE - If number is bigger than the u64 capacity.
269  */
270 static ssize_t misc_cg_max_write(struct kernfs_open_file *of, char *buf,
271 				 size_t nbytes, loff_t off)
272 {
273 	struct misc_cg *cg;
274 	u64 max;
275 	int ret = 0, i;
276 	enum misc_res_type type = MISC_CG_RES_TYPES;
277 	char *token;
278 
279 	buf = strstrip(buf);
280 	token = strsep(&buf, " ");
281 
282 	if (!token || !buf)
283 		return -EINVAL;
284 
285 	for (i = 0; i < MISC_CG_RES_TYPES; i++) {
286 		if (!strcmp(misc_res_name[i], token)) {
287 			type = i;
288 			break;
289 		}
290 	}
291 
292 	if (type == MISC_CG_RES_TYPES)
293 		return -EINVAL;
294 
295 	if (!strcmp(MAX_STR, buf)) {
296 		max = MAX_NUM;
297 	} else {
298 		ret = kstrtou64(buf, 0, &max);
299 		if (ret)
300 			return ret;
301 	}
302 
303 	cg = css_misc(of_css(of));
304 
305 	if (READ_ONCE(misc_res_capacity[type]))
306 		WRITE_ONCE(cg->res[type].max, max);
307 	else
308 		ret = -EINVAL;
309 
310 	return ret ? ret : nbytes;
311 }
312 
313 /**
314  * misc_cg_current_show() - Show the current usage of the misc cgroup.
315  * @sf: Interface file
316  * @v: Arguments passed
317  *
318  * Context: Any context.
319  * Return: 0 to denote successful print.
320  */
321 static int misc_cg_current_show(struct seq_file *sf, void *v)
322 {
323 	int i;
324 	u64 usage;
325 	struct misc_cg *cg = css_misc(seq_css(sf));
326 
327 	for (i = 0; i < MISC_CG_RES_TYPES; i++) {
328 		usage = atomic64_read(&cg->res[i].usage);
329 		if (READ_ONCE(misc_res_capacity[i]) || usage)
330 			seq_printf(sf, "%s %llu\n", misc_res_name[i], usage);
331 	}
332 
333 	return 0;
334 }
335 
336 /**
337  * misc_cg_peak_show() - Show the peak usage of the misc cgroup.
338  * @sf: Interface file
339  * @v: Arguments passed
340  *
341  * Context: Any context.
342  * Return: 0 to denote successful print.
343  */
344 static int misc_cg_peak_show(struct seq_file *sf, void *v)
345 {
346 	int i;
347 	u64 watermark;
348 	struct misc_cg *cg = css_misc(seq_css(sf));
349 
350 	for (i = 0; i < MISC_CG_RES_TYPES; i++) {
351 		watermark = atomic64_read(&cg->res[i].watermark);
352 		if (READ_ONCE(misc_res_capacity[i]) || watermark)
353 			seq_printf(sf, "%s %llu\n", misc_res_name[i], watermark);
354 	}
355 
356 	return 0;
357 }
358 
359 /**
360  * misc_cg_capacity_show() - Show the total capacity of misc res on the host.
361  * @sf: Interface file
362  * @v: Arguments passed
363  *
364  * Only present in the root cgroup directory.
365  *
366  * Context: Any context.
367  * Return: 0 to denote successful print.
368  */
369 static int misc_cg_capacity_show(struct seq_file *sf, void *v)
370 {
371 	int i;
372 	u64 cap;
373 
374 	for (i = 0; i < MISC_CG_RES_TYPES; i++) {
375 		cap = READ_ONCE(misc_res_capacity[i]);
376 		if (cap)
377 			seq_printf(sf, "%s %llu\n", misc_res_name[i], cap);
378 	}
379 
380 	return 0;
381 }
382 
383 static int __misc_events_show(struct seq_file *sf, bool local)
384 {
385 	struct misc_cg *cg = css_misc(seq_css(sf));
386 	u64 events;
387 	int i;
388 
389 	for (i = 0; i < MISC_CG_RES_TYPES; i++) {
390 		if (local)
391 			events = atomic64_read(&cg->res[i].events_local);
392 		else
393 			events = atomic64_read(&cg->res[i].events);
394 		if (READ_ONCE(misc_res_capacity[i]) || events)
395 			seq_printf(sf, "%s.max %llu\n", misc_res_name[i], events);
396 	}
397 	return 0;
398 }
399 
400 static int misc_events_show(struct seq_file *sf, void *v)
401 {
402 	return __misc_events_show(sf, false);
403 }
404 
405 static int misc_events_local_show(struct seq_file *sf, void *v)
406 {
407 	return __misc_events_show(sf, true);
408 }
409 
410 /* Misc cgroup interface files */
411 static struct cftype misc_cg_files[] = {
412 	{
413 		.name = "max",
414 		.write = misc_cg_max_write,
415 		.seq_show = misc_cg_max_show,
416 		.flags = CFTYPE_NOT_ON_ROOT,
417 	},
418 	{
419 		.name = "current",
420 		.seq_show = misc_cg_current_show,
421 	},
422 	{
423 		.name = "peak",
424 		.seq_show = misc_cg_peak_show,
425 	},
426 	{
427 		.name = "capacity",
428 		.seq_show = misc_cg_capacity_show,
429 		.flags = CFTYPE_ONLY_ON_ROOT,
430 	},
431 	{
432 		.name = "events",
433 		.flags = CFTYPE_NOT_ON_ROOT,
434 		.file_offset = offsetof(struct misc_cg, events_file),
435 		.seq_show = misc_events_show,
436 	},
437 	{
438 		.name = "events.local",
439 		.flags = CFTYPE_NOT_ON_ROOT,
440 		.file_offset = offsetof(struct misc_cg, events_local_file),
441 		.seq_show = misc_events_local_show,
442 	},
443 	{}
444 };
445 
446 /**
447  * misc_cg_alloc() - Allocate misc cgroup.
448  * @parent_css: Parent cgroup.
449  *
450  * Context: Process context.
451  * Return:
452  * * struct cgroup_subsys_state* - css of the allocated cgroup.
453  * * ERR_PTR(-ENOMEM) - No memory available to allocate.
454  */
455 static struct cgroup_subsys_state *
456 misc_cg_alloc(struct cgroup_subsys_state *parent_css)
457 {
458 	enum misc_res_type i;
459 	struct misc_cg *cg;
460 
461 	if (!parent_css) {
462 		cg = &root_cg;
463 	} else {
464 		cg = kzalloc(sizeof(*cg), GFP_KERNEL);
465 		if (!cg)
466 			return ERR_PTR(-ENOMEM);
467 	}
468 
469 	for (i = 0; i < MISC_CG_RES_TYPES; i++) {
470 		WRITE_ONCE(cg->res[i].max, MAX_NUM);
471 		atomic64_set(&cg->res[i].usage, 0);
472 	}
473 
474 	return &cg->css;
475 }
476 
477 /**
478  * misc_cg_free() - Free the misc cgroup.
479  * @css: cgroup subsys object.
480  *
481  * Context: Any context.
482  */
483 static void misc_cg_free(struct cgroup_subsys_state *css)
484 {
485 	kfree(css_misc(css));
486 }
487 
488 /* Cgroup controller callbacks */
489 struct cgroup_subsys misc_cgrp_subsys = {
490 	.css_alloc = misc_cg_alloc,
491 	.css_free = misc_cg_free,
492 	.legacy_cftypes = misc_cg_files,
493 	.dfl_cftypes = misc_cg_files,
494 };
495