File Index
Files and directories
Sign in
FILE

misc.c

File preview with syntax highlighting.

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