summaryrefslogtreecommitdiffstats
path: root/kernel/cgroup.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/cgroup.c')
-rw-r--r--kernel/cgroup.c82
1 files changed, 62 insertions, 20 deletions
diff --git a/kernel/cgroup.c b/kernel/cgroup.c
index 0398f2a6673b..452a90e455fa 100644
--- a/kernel/cgroup.c
+++ b/kernel/cgroup.c
@@ -3032,6 +3032,62 @@ static bool cgroup_drain_offline(struct cgroup *cgrp)
}
/**
+ * cgroup_save_control - save control masks of a subtree
+ * @cgrp: root of the target subtree
+ *
+ * Save ->subtree_control and ->subtree_ss_mask to the respective old_
+ * prefixed fields for @cgrp's subtree including @cgrp itself.
+ */
+static void cgroup_save_control(struct cgroup *cgrp)
+{
+ struct cgroup *dsct;
+ struct cgroup_subsys_state *d_css;
+
+ cgroup_for_each_live_descendant_pre(dsct, d_css, cgrp) {
+ dsct->old_subtree_control = dsct->subtree_control;
+ dsct->old_subtree_ss_mask = dsct->subtree_ss_mask;
+ }
+}
+
+/**
+ * cgroup_propagate_control - refresh control masks of a subtree
+ * @cgrp: root of the target subtree
+ *
+ * For @cgrp and its subtree, ensure ->subtree_ss_mask matches
+ * ->subtree_control and propagate controller availability through the
+ * subtree so that descendants don't have unavailable controllers enabled.
+ */
+static void cgroup_propagate_control(struct cgroup *cgrp)
+{
+ struct cgroup *dsct;
+ struct cgroup_subsys_state *d_css;
+
+ cgroup_for_each_live_descendant_pre(dsct, d_css, cgrp) {
+ dsct->subtree_control &= cgroup_control(dsct);
+ dsct->subtree_ss_mask = cgroup_calc_subtree_ss_mask(dsct,
+ dsct->subtree_control);
+ }
+}
+
+/**
+ * cgroup_restore_control - restore control masks of a subtree
+ * @cgrp: root of the target subtree
+ *
+ * Restore ->subtree_control and ->subtree_ss_mask from the respective old_
+ * prefixed fields for @cgrp's subtree including @cgrp itself.
+ */
+static void cgroup_restore_control(struct cgroup *cgrp)
+{
+ struct cgroup *dsct;
+ struct cgroup_subsys_state *d_css;
+
+ cgroup_for_each_live_descendant_post(dsct, d_css, cgrp) {
+ dsct->subtree_control = dsct->old_subtree_control;
+ dsct->subtree_ss_mask = dsct->old_subtree_ss_mask;
+ }
+}
+
+/**
* cgroup_apply_control_enable - enable or show csses according to control
* @cgrp: root of the target subtree
*
@@ -3119,7 +3175,6 @@ static ssize_t cgroup_subtree_control_write(struct kernfs_open_file *of,
loff_t off)
{
u16 enable = 0, disable = 0;
- u16 css_enable, css_disable, old_sc, new_sc, old_ss, new_ss;
struct cgroup *cgrp, *child;
struct cgroup_subsys *ss;
char *tok;
@@ -3203,25 +3258,14 @@ static ssize_t cgroup_subtree_control_write(struct kernfs_open_file *of,
return restart_syscall();
}
- /*
- * Update subsys masks and calculate what needs to be done. More
- * subsystems than specified may need to be enabled or disabled
- * depending on subsystem dependencies.
- */
- old_sc = cgrp->subtree_control;
- old_ss = cgrp->subtree_ss_mask;
- new_sc = (old_sc | enable) & ~disable;
- new_ss = cgroup_calc_subtree_ss_mask(cgrp, new_sc);
+ /* save and update control masks and prepare csses */
+ cgroup_save_control(cgrp);
- css_enable = ~old_ss & new_ss;
- css_disable = old_ss & ~new_ss;
- enable |= css_enable;
- disable |= css_disable;
+ cgrp->subtree_control |= enable;
+ cgrp->subtree_control &= ~disable;
- cgrp->subtree_control = new_sc;
- cgrp->subtree_ss_mask = new_ss;
+ cgroup_propagate_control(cgrp);
- /* prepare csses */
ret = cgroup_apply_control_enable(cgrp);
if (ret)
goto err_undo_css;
@@ -3246,9 +3290,7 @@ out_unlock:
err_undo_css:
/* restore masks and shoot down new csses */
- cgrp->subtree_control = old_sc;
- cgrp->subtree_ss_mask = old_ss;
-
+ cgroup_restore_control(cgrp);
cgroup_apply_control_disable(cgrp);
goto out_unlock;