0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017 #include <linux/export.h>
0018 #include <linux/slab.h>
0019 #include <linux/cgroup.h>
0020 #include <linux/fs.h>
0021 #include <linux/uaccess.h>
0022 #include <linux/freezer.h>
0023 #include <linux/seq_file.h>
0024 #include <linux/mutex.h>
0025
0026
0027
0028
0029
0030
0031
0032
0033 enum freezer_state_flags {
0034 CGROUP_FREEZER_ONLINE = (1 << 0),
0035 CGROUP_FREEZING_SELF = (1 << 1),
0036 CGROUP_FREEZING_PARENT = (1 << 2),
0037 CGROUP_FROZEN = (1 << 3),
0038
0039
0040 CGROUP_FREEZING = CGROUP_FREEZING_SELF | CGROUP_FREEZING_PARENT,
0041 };
0042
0043 struct freezer {
0044 struct cgroup_subsys_state css;
0045 unsigned int state;
0046 };
0047
0048 static DEFINE_MUTEX(freezer_mutex);
0049
0050 static inline struct freezer *css_freezer(struct cgroup_subsys_state *css)
0051 {
0052 return css ? container_of(css, struct freezer, css) : NULL;
0053 }
0054
0055 static inline struct freezer *task_freezer(struct task_struct *task)
0056 {
0057 return css_freezer(task_css(task, freezer_cgrp_id));
0058 }
0059
0060 static struct freezer *parent_freezer(struct freezer *freezer)
0061 {
0062 return css_freezer(freezer->css.parent);
0063 }
0064
0065 bool cgroup_freezing(struct task_struct *task)
0066 {
0067 bool ret;
0068
0069 rcu_read_lock();
0070 ret = task_freezer(task)->state & CGROUP_FREEZING;
0071 rcu_read_unlock();
0072
0073 return ret;
0074 }
0075
0076 static const char *freezer_state_strs(unsigned int state)
0077 {
0078 if (state & CGROUP_FROZEN)
0079 return "FROZEN";
0080 if (state & CGROUP_FREEZING)
0081 return "FREEZING";
0082 return "THAWED";
0083 };
0084
0085 static struct cgroup_subsys_state *
0086 freezer_css_alloc(struct cgroup_subsys_state *parent_css)
0087 {
0088 struct freezer *freezer;
0089
0090 freezer = kzalloc(sizeof(struct freezer), GFP_KERNEL);
0091 if (!freezer)
0092 return ERR_PTR(-ENOMEM);
0093
0094 return &freezer->css;
0095 }
0096
0097
0098
0099
0100
0101
0102
0103
0104
0105 static int freezer_css_online(struct cgroup_subsys_state *css)
0106 {
0107 struct freezer *freezer = css_freezer(css);
0108 struct freezer *parent = parent_freezer(freezer);
0109
0110 mutex_lock(&freezer_mutex);
0111
0112 freezer->state |= CGROUP_FREEZER_ONLINE;
0113
0114 if (parent && (parent->state & CGROUP_FREEZING)) {
0115 freezer->state |= CGROUP_FREEZING_PARENT | CGROUP_FROZEN;
0116 atomic_inc(&system_freezing_cnt);
0117 }
0118
0119 mutex_unlock(&freezer_mutex);
0120 return 0;
0121 }
0122
0123
0124
0125
0126
0127
0128
0129
0130 static void freezer_css_offline(struct cgroup_subsys_state *css)
0131 {
0132 struct freezer *freezer = css_freezer(css);
0133
0134 mutex_lock(&freezer_mutex);
0135
0136 if (freezer->state & CGROUP_FREEZING)
0137 atomic_dec(&system_freezing_cnt);
0138
0139 freezer->state = 0;
0140
0141 mutex_unlock(&freezer_mutex);
0142 }
0143
0144 static void freezer_css_free(struct cgroup_subsys_state *css)
0145 {
0146 kfree(css_freezer(css));
0147 }
0148
0149
0150
0151
0152
0153
0154
0155
0156
0157
0158 static void freezer_attach(struct cgroup_taskset *tset)
0159 {
0160 struct task_struct *task;
0161 struct cgroup_subsys_state *new_css;
0162
0163 mutex_lock(&freezer_mutex);
0164
0165
0166
0167
0168
0169
0170
0171
0172
0173
0174
0175 cgroup_taskset_for_each(task, new_css, tset) {
0176 struct freezer *freezer = css_freezer(new_css);
0177
0178 if (!(freezer->state & CGROUP_FREEZING)) {
0179 __thaw_task(task);
0180 } else {
0181 freeze_task(task);
0182
0183 while (freezer && (freezer->state & CGROUP_FROZEN)) {
0184 freezer->state &= ~CGROUP_FROZEN;
0185 freezer = parent_freezer(freezer);
0186 }
0187 }
0188 }
0189
0190 mutex_unlock(&freezer_mutex);
0191 }
0192
0193
0194
0195
0196
0197
0198
0199
0200
0201
0202
0203 static void freezer_fork(struct task_struct *task)
0204 {
0205 struct freezer *freezer;
0206
0207
0208
0209
0210
0211
0212
0213
0214 if (task_css_is_root(task, freezer_cgrp_id))
0215 return;
0216
0217 mutex_lock(&freezer_mutex);
0218 rcu_read_lock();
0219
0220 freezer = task_freezer(task);
0221 if (freezer->state & CGROUP_FREEZING)
0222 freeze_task(task);
0223
0224 rcu_read_unlock();
0225 mutex_unlock(&freezer_mutex);
0226 }
0227
0228
0229
0230
0231
0232
0233
0234
0235
0236
0237
0238
0239
0240
0241
0242
0243
0244 static void update_if_frozen(struct cgroup_subsys_state *css)
0245 {
0246 struct freezer *freezer = css_freezer(css);
0247 struct cgroup_subsys_state *pos;
0248 struct css_task_iter it;
0249 struct task_struct *task;
0250
0251 lockdep_assert_held(&freezer_mutex);
0252
0253 if (!(freezer->state & CGROUP_FREEZING) ||
0254 (freezer->state & CGROUP_FROZEN))
0255 return;
0256
0257
0258 rcu_read_lock();
0259 css_for_each_child(pos, css) {
0260 struct freezer *child = css_freezer(pos);
0261
0262 if ((child->state & CGROUP_FREEZER_ONLINE) &&
0263 !(child->state & CGROUP_FROZEN)) {
0264 rcu_read_unlock();
0265 return;
0266 }
0267 }
0268 rcu_read_unlock();
0269
0270
0271 css_task_iter_start(css, 0, &it);
0272
0273 while ((task = css_task_iter_next(&it))) {
0274 if (freezing(task)) {
0275
0276
0277
0278
0279
0280
0281 if (!frozen(task) && !freezer_should_skip(task))
0282 goto out_iter_end;
0283 }
0284 }
0285
0286 freezer->state |= CGROUP_FROZEN;
0287 out_iter_end:
0288 css_task_iter_end(&it);
0289 }
0290
0291 static int freezer_read(struct seq_file *m, void *v)
0292 {
0293 struct cgroup_subsys_state *css = seq_css(m), *pos;
0294
0295 mutex_lock(&freezer_mutex);
0296 rcu_read_lock();
0297
0298
0299 css_for_each_descendant_post(pos, css) {
0300 if (!css_tryget_online(pos))
0301 continue;
0302 rcu_read_unlock();
0303
0304 update_if_frozen(pos);
0305
0306 rcu_read_lock();
0307 css_put(pos);
0308 }
0309
0310 rcu_read_unlock();
0311 mutex_unlock(&freezer_mutex);
0312
0313 seq_puts(m, freezer_state_strs(css_freezer(css)->state));
0314 seq_putc(m, '\n');
0315 return 0;
0316 }
0317
0318 static void freeze_cgroup(struct freezer *freezer)
0319 {
0320 struct css_task_iter it;
0321 struct task_struct *task;
0322
0323 css_task_iter_start(&freezer->css, 0, &it);
0324 while ((task = css_task_iter_next(&it)))
0325 freeze_task(task);
0326 css_task_iter_end(&it);
0327 }
0328
0329 static void unfreeze_cgroup(struct freezer *freezer)
0330 {
0331 struct css_task_iter it;
0332 struct task_struct *task;
0333
0334 css_task_iter_start(&freezer->css, 0, &it);
0335 while ((task = css_task_iter_next(&it)))
0336 __thaw_task(task);
0337 css_task_iter_end(&it);
0338 }
0339
0340
0341
0342
0343
0344
0345
0346
0347
0348
0349 static void freezer_apply_state(struct freezer *freezer, bool freeze,
0350 unsigned int state)
0351 {
0352
0353 lockdep_assert_held(&freezer_mutex);
0354
0355 if (!(freezer->state & CGROUP_FREEZER_ONLINE))
0356 return;
0357
0358 if (freeze) {
0359 if (!(freezer->state & CGROUP_FREEZING))
0360 atomic_inc(&system_freezing_cnt);
0361 freezer->state |= state;
0362 freeze_cgroup(freezer);
0363 } else {
0364 bool was_freezing = freezer->state & CGROUP_FREEZING;
0365
0366 freezer->state &= ~state;
0367
0368 if (!(freezer->state & CGROUP_FREEZING)) {
0369 if (was_freezing)
0370 atomic_dec(&system_freezing_cnt);
0371 freezer->state &= ~CGROUP_FROZEN;
0372 unfreeze_cgroup(freezer);
0373 }
0374 }
0375 }
0376
0377
0378
0379
0380
0381
0382
0383
0384
0385 static void freezer_change_state(struct freezer *freezer, bool freeze)
0386 {
0387 struct cgroup_subsys_state *pos;
0388
0389
0390
0391
0392
0393
0394 mutex_lock(&freezer_mutex);
0395 rcu_read_lock();
0396 css_for_each_descendant_pre(pos, &freezer->css) {
0397 struct freezer *pos_f = css_freezer(pos);
0398 struct freezer *parent = parent_freezer(pos_f);
0399
0400 if (!css_tryget_online(pos))
0401 continue;
0402 rcu_read_unlock();
0403
0404 if (pos_f == freezer)
0405 freezer_apply_state(pos_f, freeze,
0406 CGROUP_FREEZING_SELF);
0407 else
0408 freezer_apply_state(pos_f,
0409 parent->state & CGROUP_FREEZING,
0410 CGROUP_FREEZING_PARENT);
0411
0412 rcu_read_lock();
0413 css_put(pos);
0414 }
0415 rcu_read_unlock();
0416 mutex_unlock(&freezer_mutex);
0417 }
0418
0419 static ssize_t freezer_write(struct kernfs_open_file *of,
0420 char *buf, size_t nbytes, loff_t off)
0421 {
0422 bool freeze;
0423
0424 buf = strstrip(buf);
0425
0426 if (strcmp(buf, freezer_state_strs(0)) == 0)
0427 freeze = false;
0428 else if (strcmp(buf, freezer_state_strs(CGROUP_FROZEN)) == 0)
0429 freeze = true;
0430 else
0431 return -EINVAL;
0432
0433 freezer_change_state(css_freezer(of_css(of)), freeze);
0434 return nbytes;
0435 }
0436
0437 static u64 freezer_self_freezing_read(struct cgroup_subsys_state *css,
0438 struct cftype *cft)
0439 {
0440 struct freezer *freezer = css_freezer(css);
0441
0442 return (bool)(freezer->state & CGROUP_FREEZING_SELF);
0443 }
0444
0445 static u64 freezer_parent_freezing_read(struct cgroup_subsys_state *css,
0446 struct cftype *cft)
0447 {
0448 struct freezer *freezer = css_freezer(css);
0449
0450 return (bool)(freezer->state & CGROUP_FREEZING_PARENT);
0451 }
0452
0453 static struct cftype files[] = {
0454 {
0455 .name = "state",
0456 .flags = CFTYPE_NOT_ON_ROOT,
0457 .seq_show = freezer_read,
0458 .write = freezer_write,
0459 },
0460 {
0461 .name = "self_freezing",
0462 .flags = CFTYPE_NOT_ON_ROOT,
0463 .read_u64 = freezer_self_freezing_read,
0464 },
0465 {
0466 .name = "parent_freezing",
0467 .flags = CFTYPE_NOT_ON_ROOT,
0468 .read_u64 = freezer_parent_freezing_read,
0469 },
0470 { }
0471 };
0472
0473 struct cgroup_subsys freezer_cgrp_subsys = {
0474 .css_alloc = freezer_css_alloc,
0475 .css_online = freezer_css_online,
0476 .css_offline = freezer_css_offline,
0477 .css_free = freezer_css_free,
0478 .attach = freezer_attach,
0479 .fork = freezer_fork,
0480 .legacy_cftypes = files,
0481 };