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