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