diff options
Diffstat (limited to 'kernel/cgroup_freezer.c')
| -rw-r--r-- | kernel/cgroup_freezer.c | 161 | 
1 files changed, 79 insertions, 82 deletions
diff --git a/kernel/cgroup_freezer.c b/kernel/cgroup_freezer.c index f0ff64d0eba..a79e40f9d70 100644 --- a/kernel/cgroup_freezer.c +++ b/kernel/cgroup_freezer.c @@ -21,6 +21,7 @@  #include <linux/uaccess.h>  #include <linux/freezer.h>  #include <linux/seq_file.h> +#include <linux/mutex.h>  /*   * A cgroup is freezing if any FREEZING flags are set.  FREEZING_SELF is @@ -42,9 +43,10 @@ enum freezer_state_flags {  struct freezer {  	struct cgroup_subsys_state	css;  	unsigned int			state; -	spinlock_t			lock;  }; +static DEFINE_MUTEX(freezer_mutex); +  static inline struct freezer *css_freezer(struct cgroup_subsys_state *css)  {  	return css ? container_of(css, struct freezer, css) : NULL; @@ -52,12 +54,12 @@ static inline struct freezer *css_freezer(struct cgroup_subsys_state *css)  static inline struct freezer *task_freezer(struct task_struct *task)  { -	return css_freezer(task_css(task, freezer_subsys_id)); +	return css_freezer(task_css(task, freezer_cgrp_id));  }  static struct freezer *parent_freezer(struct freezer *freezer)  { -	return css_freezer(css_parent(&freezer->css)); +	return css_freezer(freezer->css.parent);  }  bool cgroup_freezing(struct task_struct *task) @@ -71,10 +73,6 @@ bool cgroup_freezing(struct task_struct *task)  	return ret;  } -/* - * cgroups_write_string() limits the size of freezer state strings to - * CGROUP_LOCAL_BUFFER_SIZE - */  static const char *freezer_state_strs(unsigned int state)  {  	if (state & CGROUP_FROZEN) @@ -84,8 +82,6 @@ static const char *freezer_state_strs(unsigned int state)  	return "THAWED";  }; -struct cgroup_subsys freezer_subsys; -  static struct cgroup_subsys_state *  freezer_css_alloc(struct cgroup_subsys_state *parent_css)  { @@ -95,7 +91,6 @@ freezer_css_alloc(struct cgroup_subsys_state *parent_css)  	if (!freezer)  		return ERR_PTR(-ENOMEM); -	spin_lock_init(&freezer->lock);  	return &freezer->css;  } @@ -112,14 +107,7 @@ static int freezer_css_online(struct cgroup_subsys_state *css)  	struct freezer *freezer = css_freezer(css);  	struct freezer *parent = parent_freezer(freezer); -	/* -	 * The following double locking and freezing state inheritance -	 * guarantee that @cgroup can never escape ancestors' freezing -	 * states.  See css_for_each_descendant_pre() for details. -	 */ -	if (parent) -		spin_lock_irq(&parent->lock); -	spin_lock_nested(&freezer->lock, SINGLE_DEPTH_NESTING); +	mutex_lock(&freezer_mutex);  	freezer->state |= CGROUP_FREEZER_ONLINE; @@ -128,10 +116,7 @@ static int freezer_css_online(struct cgroup_subsys_state *css)  		atomic_inc(&system_freezing_cnt);  	} -	spin_unlock(&freezer->lock); -	if (parent) -		spin_unlock_irq(&parent->lock); - +	mutex_unlock(&freezer_mutex);  	return 0;  } @@ -146,14 +131,14 @@ static void freezer_css_offline(struct cgroup_subsys_state *css)  {  	struct freezer *freezer = css_freezer(css); -	spin_lock_irq(&freezer->lock); +	mutex_lock(&freezer_mutex);  	if (freezer->state & CGROUP_FREEZING)  		atomic_dec(&system_freezing_cnt);  	freezer->state = 0; -	spin_unlock_irq(&freezer->lock); +	mutex_unlock(&freezer_mutex);  }  static void freezer_css_free(struct cgroup_subsys_state *css) @@ -177,7 +162,7 @@ static void freezer_attach(struct cgroup_subsys_state *new_css,  	struct task_struct *task;  	bool clear_frozen = false; -	spin_lock_irq(&freezer->lock); +	mutex_lock(&freezer_mutex);  	/*  	 * Make the new tasks conform to the current state of @new_css. @@ -189,7 +174,7 @@ static void freezer_attach(struct cgroup_subsys_state *new_css,  	 * current state before executing the following - !frozen tasks may  	 * be visible in a FROZEN cgroup and frozen tasks in a THAWED one.  	 */ -	cgroup_taskset_for_each(task, new_css, tset) { +	cgroup_taskset_for_each(task, tset) {  		if (!(freezer->state & CGROUP_FREEZING)) {  			__thaw_task(task);  		} else { @@ -199,43 +184,48 @@ static void freezer_attach(struct cgroup_subsys_state *new_css,  		}  	} -	spin_unlock_irq(&freezer->lock); - -	/* -	 * Propagate FROZEN clearing upwards.  We may race with -	 * update_if_frozen(), but as long as both work bottom-up, either -	 * update_if_frozen() sees child's FROZEN cleared or we clear the -	 * parent's FROZEN later.  No parent w/ !FROZEN children can be -	 * left FROZEN. -	 */ +	/* propagate FROZEN clearing upwards */  	while (clear_frozen && (freezer = parent_freezer(freezer))) { -		spin_lock_irq(&freezer->lock);  		freezer->state &= ~CGROUP_FROZEN;  		clear_frozen = freezer->state & CGROUP_FREEZING; -		spin_unlock_irq(&freezer->lock);  	} + +	mutex_unlock(&freezer_mutex);  } +/** + * freezer_fork - cgroup post fork callback + * @task: a task which has just been forked + * + * @task has just been created and should conform to the current state of + * the cgroup_freezer it belongs to.  This function may race against + * freezer_attach().  Losing to freezer_attach() means that we don't have + * to do anything as freezer_attach() will put @task into the appropriate + * state. + */  static void freezer_fork(struct task_struct *task)  {  	struct freezer *freezer; -	rcu_read_lock(); -	freezer = task_freezer(task); -  	/* -	 * The root cgroup is non-freezable, so we can skip the -	 * following check. +	 * The root cgroup is non-freezable, so we can skip locking the +	 * freezer.  This is safe regardless of race with task migration. +	 * If we didn't race or won, skipping is obviously the right thing +	 * to do.  If we lost and root is the new cgroup, noop is still the +	 * right thing to do.  	 */ -	if (!parent_freezer(freezer)) -		goto out; +	if (task_css_is_root(task, freezer_cgrp_id)) +		return; -	spin_lock_irq(&freezer->lock); +	mutex_lock(&freezer_mutex); +	rcu_read_lock(); + +	freezer = task_freezer(task);  	if (freezer->state & CGROUP_FREEZING)  		freeze_task(task); -	spin_unlock_irq(&freezer->lock); -out: +  	rcu_read_unlock(); +	mutex_unlock(&freezer_mutex);  }  /** @@ -261,22 +251,24 @@ static void update_if_frozen(struct cgroup_subsys_state *css)  	struct css_task_iter it;  	struct task_struct *task; -	WARN_ON_ONCE(!rcu_read_lock_held()); - -	spin_lock_irq(&freezer->lock); +	lockdep_assert_held(&freezer_mutex);  	if (!(freezer->state & CGROUP_FREEZING) ||  	    (freezer->state & CGROUP_FROZEN)) -		goto out_unlock; +		return;  	/* are all (live) children frozen? */ +	rcu_read_lock();  	css_for_each_child(pos, css) {  		struct freezer *child = css_freezer(pos);  		if ((child->state & CGROUP_FREEZER_ONLINE) && -		    !(child->state & CGROUP_FROZEN)) -			goto out_unlock; +		    !(child->state & CGROUP_FROZEN)) { +			rcu_read_unlock(); +			return; +		}  	} +	rcu_read_unlock();  	/* are all tasks frozen? */  	css_task_iter_start(css, &it); @@ -297,22 +289,29 @@ static void update_if_frozen(struct cgroup_subsys_state *css)  	freezer->state |= CGROUP_FROZEN;  out_iter_end:  	css_task_iter_end(&it); -out_unlock: -	spin_unlock_irq(&freezer->lock);  } -static int freezer_read(struct cgroup_subsys_state *css, struct cftype *cft, -			struct seq_file *m) +static int freezer_read(struct seq_file *m, void *v)  { -	struct cgroup_subsys_state *pos; +	struct cgroup_subsys_state *css = seq_css(m), *pos; +	mutex_lock(&freezer_mutex);  	rcu_read_lock();  	/* update states bottom-up */ -	css_for_each_descendant_post(pos, css) +	css_for_each_descendant_post(pos, css) { +		if (!css_tryget_online(pos)) +			continue; +		rcu_read_unlock(); +  		update_if_frozen(pos); +		rcu_read_lock(); +		css_put(pos); +	} +  	rcu_read_unlock(); +	mutex_unlock(&freezer_mutex);  	seq_puts(m, freezer_state_strs(css_freezer(css)->state));  	seq_putc(m, '\n'); @@ -354,7 +353,7 @@ static void freezer_apply_state(struct freezer *freezer, bool freeze,  				unsigned int state)  {  	/* also synchronizes against task migration, see freezer_attach() */ -	lockdep_assert_held(&freezer->lock); +	lockdep_assert_held(&freezer_mutex);  	if (!(freezer->state & CGROUP_FREEZER_ONLINE))  		return; @@ -395,47 +394,47 @@ static void freezer_change_state(struct freezer *freezer, bool freeze)  	 * descendant will try to inherit its parent's FREEZING state as  	 * CGROUP_FREEZING_PARENT.  	 */ +	mutex_lock(&freezer_mutex);  	rcu_read_lock();  	css_for_each_descendant_pre(pos, &freezer->css) {  		struct freezer *pos_f = css_freezer(pos);  		struct freezer *parent = parent_freezer(pos_f); -		spin_lock_irq(&pos_f->lock); +		if (!css_tryget_online(pos)) +			continue; +		rcu_read_unlock(); -		if (pos_f == freezer) { +		if (pos_f == freezer)  			freezer_apply_state(pos_f, freeze,  					    CGROUP_FREEZING_SELF); -		} else { -			/* -			 * Our update to @parent->state is already visible -			 * which is all we need.  No need to lock @parent. -			 * For more info on synchronization, see -			 * freezer_post_create(). -			 */ +		else  			freezer_apply_state(pos_f,  					    parent->state & CGROUP_FREEZING,  					    CGROUP_FREEZING_PARENT); -		} -		spin_unlock_irq(&pos_f->lock); +		rcu_read_lock(); +		css_put(pos);  	}  	rcu_read_unlock(); +	mutex_unlock(&freezer_mutex);  } -static int freezer_write(struct cgroup_subsys_state *css, struct cftype *cft, -			 const char *buffer) +static ssize_t freezer_write(struct kernfs_open_file *of, +			     char *buf, size_t nbytes, loff_t off)  {  	bool freeze; -	if (strcmp(buffer, freezer_state_strs(0)) == 0) +	buf = strstrip(buf); + +	if (strcmp(buf, freezer_state_strs(0)) == 0)  		freeze = false; -	else if (strcmp(buffer, freezer_state_strs(CGROUP_FROZEN)) == 0) +	else if (strcmp(buf, freezer_state_strs(CGROUP_FROZEN)) == 0)  		freeze = true;  	else  		return -EINVAL; -	freezer_change_state(css_freezer(css), freeze); -	return 0; +	freezer_change_state(css_freezer(of_css(of)), freeze); +	return nbytes;  }  static u64 freezer_self_freezing_read(struct cgroup_subsys_state *css, @@ -458,8 +457,8 @@ static struct cftype files[] = {  	{  		.name = "state",  		.flags = CFTYPE_NOT_ON_ROOT, -		.read_seq_string = freezer_read, -		.write_string = freezer_write, +		.seq_show = freezer_read, +		.write = freezer_write,  	},  	{  		.name = "self_freezing", @@ -474,13 +473,11 @@ static struct cftype files[] = {  	{ }	/* terminate */  }; -struct cgroup_subsys freezer_subsys = { -	.name		= "freezer", +struct cgroup_subsys freezer_cgrp_subsys = {  	.css_alloc	= freezer_css_alloc,  	.css_online	= freezer_css_online,  	.css_offline	= freezer_css_offline,  	.css_free	= freezer_css_free, -	.subsys_id	= freezer_subsys_id,  	.attach		= freezer_attach,  	.fork		= freezer_fork,  	.base_cftypes	= files,  | 
