0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/limits.h>
0010 #include <linux/cgroup.h>
0011 #include <linux/errno.h>
0012 #include <linux/atomic.h>
0013 #include <linux/slab.h>
0014 #include <linux/misc_cgroup.h>
0015
0016 #define MAX_STR "max"
0017 #define MAX_NUM ULONG_MAX
0018
0019
0020 static const char *const misc_res_name[] = {
0021 #ifdef CONFIG_KVM_AMD_SEV
0022
0023 "sev",
0024
0025 "sev_es",
0026 #endif
0027 };
0028
0029
0030 static struct misc_cg root_cg;
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040 static unsigned long misc_res_capacity[MISC_CG_RES_TYPES];
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051 static struct misc_cg *parent_misc(struct misc_cg *cgroup)
0052 {
0053 return cgroup ? css_misc(cgroup->css.parent) : NULL;
0054 }
0055
0056
0057
0058
0059
0060
0061
0062
0063
0064
0065 static inline bool valid_type(enum misc_res_type type)
0066 {
0067 return type >= 0 && type < MISC_CG_RES_TYPES;
0068 }
0069
0070
0071
0072
0073
0074
0075
0076
0077 unsigned long misc_cg_res_total_usage(enum misc_res_type type)
0078 {
0079 if (valid_type(type))
0080 return atomic_long_read(&root_cg.res[type].usage);
0081
0082 return 0;
0083 }
0084 EXPORT_SYMBOL_GPL(misc_cg_res_total_usage);
0085
0086
0087
0088
0089
0090
0091
0092
0093
0094
0095
0096
0097
0098 int misc_cg_set_capacity(enum misc_res_type type, unsigned long capacity)
0099 {
0100 if (!valid_type(type))
0101 return -EINVAL;
0102
0103 WRITE_ONCE(misc_res_capacity[type], capacity);
0104 return 0;
0105 }
0106 EXPORT_SYMBOL_GPL(misc_cg_set_capacity);
0107
0108
0109
0110
0111
0112
0113
0114
0115
0116 static void misc_cg_cancel_charge(enum misc_res_type type, struct misc_cg *cg,
0117 unsigned long amount)
0118 {
0119 WARN_ONCE(atomic_long_add_negative(-amount, &cg->res[type].usage),
0120 "misc cgroup resource %s became less than 0",
0121 misc_res_name[type]);
0122 }
0123
0124
0125
0126
0127
0128
0129
0130
0131
0132
0133
0134
0135
0136
0137
0138
0139
0140 int misc_cg_try_charge(enum misc_res_type type, struct misc_cg *cg,
0141 unsigned long amount)
0142 {
0143 struct misc_cg *i, *j;
0144 int ret;
0145 struct misc_res *res;
0146 int new_usage;
0147
0148 if (!(valid_type(type) && cg && READ_ONCE(misc_res_capacity[type])))
0149 return -EINVAL;
0150
0151 if (!amount)
0152 return 0;
0153
0154 for (i = cg; i; i = parent_misc(i)) {
0155 res = &i->res[type];
0156
0157 new_usage = atomic_long_add_return(amount, &res->usage);
0158 if (new_usage > READ_ONCE(res->max) ||
0159 new_usage > READ_ONCE(misc_res_capacity[type])) {
0160 ret = -EBUSY;
0161 goto err_charge;
0162 }
0163 }
0164 return 0;
0165
0166 err_charge:
0167 for (j = i; j; j = parent_misc(j)) {
0168 atomic_long_inc(&j->res[type].events);
0169 cgroup_file_notify(&j->events_file);
0170 }
0171
0172 for (j = cg; j != i; j = parent_misc(j))
0173 misc_cg_cancel_charge(type, j, amount);
0174 misc_cg_cancel_charge(type, i, amount);
0175 return ret;
0176 }
0177 EXPORT_SYMBOL_GPL(misc_cg_try_charge);
0178
0179
0180
0181
0182
0183
0184
0185
0186
0187 void misc_cg_uncharge(enum misc_res_type type, struct misc_cg *cg,
0188 unsigned long amount)
0189 {
0190 struct misc_cg *i;
0191
0192 if (!(amount && valid_type(type) && cg))
0193 return;
0194
0195 for (i = cg; i; i = parent_misc(i))
0196 misc_cg_cancel_charge(type, i, amount);
0197 }
0198 EXPORT_SYMBOL_GPL(misc_cg_uncharge);
0199
0200
0201
0202
0203
0204
0205
0206
0207
0208 static int misc_cg_max_show(struct seq_file *sf, void *v)
0209 {
0210 int i;
0211 struct misc_cg *cg = css_misc(seq_css(sf));
0212 unsigned long max;
0213
0214 for (i = 0; i < MISC_CG_RES_TYPES; i++) {
0215 if (READ_ONCE(misc_res_capacity[i])) {
0216 max = READ_ONCE(cg->res[i].max);
0217 if (max == MAX_NUM)
0218 seq_printf(sf, "%s max\n", misc_res_name[i]);
0219 else
0220 seq_printf(sf, "%s %lu\n", misc_res_name[i],
0221 max);
0222 }
0223 }
0224
0225 return 0;
0226 }
0227
0228
0229
0230
0231
0232
0233
0234
0235
0236
0237
0238
0239
0240
0241
0242
0243
0244
0245
0246 static ssize_t misc_cg_max_write(struct kernfs_open_file *of, char *buf,
0247 size_t nbytes, loff_t off)
0248 {
0249 struct misc_cg *cg;
0250 unsigned long max;
0251 int ret = 0, i;
0252 enum misc_res_type type = MISC_CG_RES_TYPES;
0253 char *token;
0254
0255 buf = strstrip(buf);
0256 token = strsep(&buf, " ");
0257
0258 if (!token || !buf)
0259 return -EINVAL;
0260
0261 for (i = 0; i < MISC_CG_RES_TYPES; i++) {
0262 if (!strcmp(misc_res_name[i], token)) {
0263 type = i;
0264 break;
0265 }
0266 }
0267
0268 if (type == MISC_CG_RES_TYPES)
0269 return -EINVAL;
0270
0271 if (!strcmp(MAX_STR, buf)) {
0272 max = MAX_NUM;
0273 } else {
0274 ret = kstrtoul(buf, 0, &max);
0275 if (ret)
0276 return ret;
0277 }
0278
0279 cg = css_misc(of_css(of));
0280
0281 if (READ_ONCE(misc_res_capacity[type]))
0282 WRITE_ONCE(cg->res[type].max, max);
0283 else
0284 ret = -EINVAL;
0285
0286 return ret ? ret : nbytes;
0287 }
0288
0289
0290
0291
0292
0293
0294
0295
0296
0297 static int misc_cg_current_show(struct seq_file *sf, void *v)
0298 {
0299 int i;
0300 unsigned long usage;
0301 struct misc_cg *cg = css_misc(seq_css(sf));
0302
0303 for (i = 0; i < MISC_CG_RES_TYPES; i++) {
0304 usage = atomic_long_read(&cg->res[i].usage);
0305 if (READ_ONCE(misc_res_capacity[i]) || usage)
0306 seq_printf(sf, "%s %lu\n", misc_res_name[i], usage);
0307 }
0308
0309 return 0;
0310 }
0311
0312
0313
0314
0315
0316
0317
0318
0319
0320
0321
0322 static int misc_cg_capacity_show(struct seq_file *sf, void *v)
0323 {
0324 int i;
0325 unsigned long cap;
0326
0327 for (i = 0; i < MISC_CG_RES_TYPES; i++) {
0328 cap = READ_ONCE(misc_res_capacity[i]);
0329 if (cap)
0330 seq_printf(sf, "%s %lu\n", misc_res_name[i], cap);
0331 }
0332
0333 return 0;
0334 }
0335
0336 static int misc_events_show(struct seq_file *sf, void *v)
0337 {
0338 struct misc_cg *cg = css_misc(seq_css(sf));
0339 unsigned long events, i;
0340
0341 for (i = 0; i < MISC_CG_RES_TYPES; i++) {
0342 events = atomic_long_read(&cg->res[i].events);
0343 if (READ_ONCE(misc_res_capacity[i]) || events)
0344 seq_printf(sf, "%s.max %lu\n", misc_res_name[i], events);
0345 }
0346 return 0;
0347 }
0348
0349
0350 static struct cftype misc_cg_files[] = {
0351 {
0352 .name = "max",
0353 .write = misc_cg_max_write,
0354 .seq_show = misc_cg_max_show,
0355 .flags = CFTYPE_NOT_ON_ROOT,
0356 },
0357 {
0358 .name = "current",
0359 .seq_show = misc_cg_current_show,
0360 .flags = CFTYPE_NOT_ON_ROOT,
0361 },
0362 {
0363 .name = "capacity",
0364 .seq_show = misc_cg_capacity_show,
0365 .flags = CFTYPE_ONLY_ON_ROOT,
0366 },
0367 {
0368 .name = "events",
0369 .flags = CFTYPE_NOT_ON_ROOT,
0370 .file_offset = offsetof(struct misc_cg, events_file),
0371 .seq_show = misc_events_show,
0372 },
0373 {}
0374 };
0375
0376
0377
0378
0379
0380
0381
0382
0383
0384
0385 static struct cgroup_subsys_state *
0386 misc_cg_alloc(struct cgroup_subsys_state *parent_css)
0387 {
0388 enum misc_res_type i;
0389 struct misc_cg *cg;
0390
0391 if (!parent_css) {
0392 cg = &root_cg;
0393 } else {
0394 cg = kzalloc(sizeof(*cg), GFP_KERNEL);
0395 if (!cg)
0396 return ERR_PTR(-ENOMEM);
0397 }
0398
0399 for (i = 0; i < MISC_CG_RES_TYPES; i++) {
0400 WRITE_ONCE(cg->res[i].max, MAX_NUM);
0401 atomic_long_set(&cg->res[i].usage, 0);
0402 }
0403
0404 return &cg->css;
0405 }
0406
0407
0408
0409
0410
0411
0412
0413 static void misc_cg_free(struct cgroup_subsys_state *css)
0414 {
0415 kfree(css_misc(css));
0416 }
0417
0418
0419 struct cgroup_subsys misc_cgrp_subsys = {
0420 .css_alloc = misc_cg_alloc,
0421 .css_free = misc_cg_free,
0422 .legacy_cftypes = misc_cg_files,
0423 .dfl_cftypes = misc_cg_files,
0424 };