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