X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=kernel%2Fcgroup_freezer.c;h=e5c0244962b020ab3cd4e4d00a0be04e5cc18af5;hb=278554bd6579206921f5d8a523649a7a57f8850d;hp=b08722de610c38aa41ef0fcbb125e8c63eb48749;hpb=dc52ddc0e6f45b04780b26fc0813509f8e798c42;p=safe%2Fjmp%2Flinux-2.6 diff --git a/kernel/cgroup_freezer.c b/kernel/cgroup_freezer.c index b08722d..e5c0244 100644 --- a/kernel/cgroup_freezer.c +++ b/kernel/cgroup_freezer.c @@ -15,6 +15,7 @@ */ #include +#include #include #include #include @@ -22,9 +23,9 @@ #include enum freezer_state { - STATE_RUNNING = 0, - STATE_FREEZING, - STATE_FROZEN, + CGROUP_THAWED = 0, + CGROUP_FREEZING, + CGROUP_FROZEN, }; struct freezer { @@ -47,17 +48,20 @@ static inline struct freezer *task_freezer(struct task_struct *task) struct freezer, css); } -int cgroup_frozen(struct task_struct *task) +int cgroup_freezing_or_frozen(struct task_struct *task) { struct freezer *freezer; enum freezer_state state; task_lock(task); freezer = task_freezer(task); - state = freezer->state; + if (!freezer->css.cgroup->parent) + state = CGROUP_THAWED; /* root cgroup can't be frozen */ + else + state = freezer->state; task_unlock(task); - return state == STATE_FROZEN; + return (state == CGROUP_FREEZING) || (state == CGROUP_FROZEN); } /* @@ -65,7 +69,7 @@ int cgroup_frozen(struct task_struct *task) * CGROUP_LOCAL_BUFFER_SIZE */ static const char *freezer_state_strs[] = { - "RUNNING", + "THAWED", "FREEZING", "FROZEN", }; @@ -75,10 +79,10 @@ static const char *freezer_state_strs[] = { * Transitions are caused by userspace writes to the freezer.state file. * The values in parenthesis are state labels. The rest are edge labels. * - * (RUNNING) --FROZEN--> (FREEZING) --FROZEN--> (FROZEN) - * ^ ^ | | - * | \_______RUNNING_______/ | - * \_____________________________RUNNING___________/ + * (THAWED) --FROZEN--> (FREEZING) --FROZEN--> (FROZEN) + * ^ ^ | | + * | \_______THAWED_______/ | + * \__________________________THAWED____________/ */ struct cgroup_subsys freezer_subsys; @@ -135,7 +139,7 @@ static struct cgroup_subsys_state *freezer_create(struct cgroup_subsys *ss, return ERR_PTR(-ENOMEM); spin_lock_init(&freezer->lock); - freezer->state = STATE_RUNNING; + freezer->state = CGROUP_THAWED; return &freezer->css; } @@ -145,37 +149,81 @@ static void freezer_destroy(struct cgroup_subsys *ss, kfree(cgroup_freezer(cgroup)); } +/* Task is frozen or will freeze immediately when next it gets woken */ +static bool is_task_frozen_enough(struct task_struct *task) +{ + return frozen(task) || + (task_is_stopped_or_traced(task) && freezing(task)); +} +/* + * The call to cgroup_lock() in the freezer.state write method prevents + * a write to that file racing against an attach, and hence the + * can_attach() result will remain valid until the attach completes. + */ static int freezer_can_attach(struct cgroup_subsys *ss, struct cgroup *new_cgroup, - struct task_struct *task) + struct task_struct *task, bool threadgroup) { struct freezer *freezer; - int retval = 0; /* - * The call to cgroup_lock() in the freezer.state write method prevents - * a write to that file racing against an attach, and hence the - * can_attach() result will remain valid until the attach completes. + * Anything frozen can't move or be moved to/from. + * + * Since orig_freezer->state == FROZEN means that @task has been + * frozen, so it's sufficient to check the latter condition. */ + + if (is_task_frozen_enough(task)) + return -EBUSY; + freezer = cgroup_freezer(new_cgroup); - if (freezer->state == STATE_FROZEN) - retval = -EBUSY; - return retval; + if (freezer->state == CGROUP_FROZEN) + return -EBUSY; + + if (threadgroup) { + struct task_struct *c; + + rcu_read_lock(); + list_for_each_entry_rcu(c, &task->thread_group, thread_group) { + if (is_task_frozen_enough(c)) { + rcu_read_unlock(); + return -EBUSY; + } + } + rcu_read_unlock(); + } + + return 0; } static void freezer_fork(struct cgroup_subsys *ss, struct task_struct *task) { struct freezer *freezer; - task_lock(task); + /* + * No lock is needed, since the task isn't on tasklist yet, + * so it can't be moved to another cgroup, which means the + * freezer won't be removed and will be valid during this + * function call. Nevertheless, apply RCU read-side critical + * section to suppress RCU lockdep false positives. + */ + rcu_read_lock(); freezer = task_freezer(task); - task_unlock(task); + rcu_read_unlock(); + + /* + * The root cgroup is non-freezable, so we can skip the + * following check. + */ + if (!freezer->css.cgroup->parent) + return; - BUG_ON(freezer->state == STATE_FROZEN); spin_lock_irq(&freezer->lock); - /* Locking avoids race with FREEZING -> RUNNING transitions. */ - if (freezer->state == STATE_FREEZING) + BUG_ON(freezer->state == CGROUP_FROZEN); + + /* Locking avoids race with FREEZING -> THAWED transitions. */ + if (freezer->state == CGROUP_FREEZING) freeze_task(task, true); spin_unlock_irq(&freezer->lock); } @@ -183,8 +231,8 @@ static void freezer_fork(struct cgroup_subsys *ss, struct task_struct *task) /* * caller must hold freezer->lock */ -static void check_if_frozen(struct cgroup *cgroup, - struct freezer *freezer) +static void update_freezer_state(struct cgroup *cgroup, + struct freezer *freezer) { struct cgroup_iter it; struct task_struct *task; @@ -193,12 +241,7 @@ static void check_if_frozen(struct cgroup *cgroup, cgroup_iter_start(cgroup, &it); while ((task = cgroup_iter_next(cgroup, &it))) { ntotal++; - /* - * Task is frozen or will freeze immediately when next it gets - * woken - */ - if (frozen(task) || - (task_is_stopped_or_traced(task) && freezing(task))) + if (is_task_frozen_enough(task)) nfrozen++; } @@ -208,7 +251,11 @@ static void check_if_frozen(struct cgroup *cgroup, * tasks. */ if (nfrozen == ntotal) - freezer->state = STATE_FROZEN; + freezer->state = CGROUP_FROZEN; + else if (nfrozen > 0) + freezer->state = CGROUP_FREEZING; + else + freezer->state = CGROUP_THAWED; cgroup_iter_end(cgroup, &it); } @@ -224,10 +271,10 @@ static int freezer_read(struct cgroup *cgroup, struct cftype *cft, freezer = cgroup_freezer(cgroup); spin_lock_irq(&freezer->lock); state = freezer->state; - if (state == STATE_FREEZING) { + if (state == CGROUP_FREEZING) { /* We change from FREEZING to FROZEN lazily if the cgroup was * only partially frozen when we exitted write. */ - check_if_frozen(cgroup, freezer); + update_freezer_state(cgroup, freezer); state = freezer->state; } spin_unlock_irq(&freezer->lock); @@ -244,16 +291,12 @@ static int try_to_freeze_cgroup(struct cgroup *cgroup, struct freezer *freezer) struct task_struct *task; unsigned int num_cant_freeze_now = 0; - freezer->state = STATE_FREEZING; + freezer->state = CGROUP_FREEZING; cgroup_iter_start(cgroup, &it); while ((task = cgroup_iter_next(cgroup, &it))) { if (!freeze_task(task, true)) continue; - if (task_is_stopped_or_traced(task) && freezing(task)) - /* - * The freeze flag is set so these tasks will - * immediately go into the fridge upon waking. - */ + if (is_task_frozen_enough(task)) continue; if (!freezing(task) && !freezer_should_skip(task)) num_cant_freeze_now++; @@ -263,25 +306,18 @@ static int try_to_freeze_cgroup(struct cgroup *cgroup, struct freezer *freezer) return num_cant_freeze_now ? -EBUSY : 0; } -static int unfreeze_cgroup(struct cgroup *cgroup, struct freezer *freezer) +static void unfreeze_cgroup(struct cgroup *cgroup, struct freezer *freezer) { struct cgroup_iter it; struct task_struct *task; cgroup_iter_start(cgroup, &it); while ((task = cgroup_iter_next(cgroup, &it))) { - int do_wake; - - task_lock(task); - do_wake = __thaw_process(task); - task_unlock(task); - if (do_wake) - wake_up_process(task); + thaw_process(task); } cgroup_iter_end(cgroup, &it); - freezer->state = STATE_RUNNING; - return 0; + freezer->state = CGROUP_THAWED; } static int freezer_change_state(struct cgroup *cgroup, @@ -291,27 +327,22 @@ static int freezer_change_state(struct cgroup *cgroup, int retval = 0; freezer = cgroup_freezer(cgroup); + spin_lock_irq(&freezer->lock); - check_if_frozen(cgroup, freezer); /* may update freezer->state */ + + update_freezer_state(cgroup, freezer); if (goal_state == freezer->state) goto out; - switch (freezer->state) { - case STATE_RUNNING: - retval = try_to_freeze_cgroup(cgroup, freezer); + + switch (goal_state) { + case CGROUP_THAWED: + unfreeze_cgroup(cgroup, freezer); break; - case STATE_FREEZING: - if (goal_state == STATE_FROZEN) { - /* Userspace is retrying after - * "/bin/echo FROZEN > freezer.state" returned -EBUSY */ - retval = try_to_freeze_cgroup(cgroup, freezer); - break; - } - /* state == FREEZING and goal_state == RUNNING, so unfreeze */ - case STATE_FROZEN: - retval = unfreeze_cgroup(cgroup, freezer); + case CGROUP_FROZEN: + retval = try_to_freeze_cgroup(cgroup, freezer); break; default: - break; + BUG(); } out: spin_unlock_irq(&freezer->lock); @@ -326,12 +357,12 @@ static int freezer_write(struct cgroup *cgroup, int retval; enum freezer_state goal_state; - if (strcmp(buffer, freezer_state_strs[STATE_RUNNING]) == 0) - goal_state = STATE_RUNNING; - else if (strcmp(buffer, freezer_state_strs[STATE_FROZEN]) == 0) - goal_state = STATE_FROZEN; + if (strcmp(buffer, freezer_state_strs[CGROUP_THAWED]) == 0) + goal_state = CGROUP_THAWED; + else if (strcmp(buffer, freezer_state_strs[CGROUP_FROZEN]) == 0) + goal_state = CGROUP_FROZEN; else - return -EIO; + return -EINVAL; if (!cgroup_lock_live_group(cgroup)) return -ENODEV; @@ -350,6 +381,8 @@ static struct cftype files[] = { static int freezer_populate(struct cgroup_subsys *ss, struct cgroup *cgroup) { + if (!cgroup->parent) + return 0; return cgroup_add_files(cgroup, ss, files, ARRAY_SIZE(files)); }