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