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