cgroup: Add misc cgroup controller
[linux-block.git] / kernel / cgroup / misc.c
CommitLineData
a72232ea
VS
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 ULONG_MAX
18
19/* Miscellaneous res name, keep it in sync with enum misc_res_type */
20static const char *const misc_res_name[] = {
21};
22
23/* Root misc cgroup */
24static 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 */
34static 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 */
45static 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 */
59static 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 */
71unsigned 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}
78EXPORT_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 */
92int 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}
100EXPORT_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 */
110static 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 */
134int 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
167err_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}
173EXPORT_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 */
183void 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}
194EXPORT_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 */
204static 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 */
242static 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 */
293static 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 */
318static 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 */
333static 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 */
362static struct cgroup_subsys_state *
363misc_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 */
390static void misc_cg_free(struct cgroup_subsys_state *css)
391{
392 kfree(css_misc(css));
393}
394
395/* Cgroup controller callbacks */
396struct 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};