diff options
Diffstat (limited to 'kernel/cgroup.c')
| -rw-r--r-- | kernel/cgroup.c | 131 | 
1 files changed, 106 insertions, 25 deletions
diff --git a/kernel/cgroup.c b/kernel/cgroup.c index f89d9292eee6..2cf0f79f1fc9 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -107,8 +107,8 @@ static DEFINE_SPINLOCK(release_agent_path_lock);  struct percpu_rw_semaphore cgroup_threadgroup_rwsem;  #define cgroup_assert_mutex_or_rcu_locked()				\ -	rcu_lockdep_assert(rcu_read_lock_held() ||			\ -			   lockdep_is_held(&cgroup_mutex),		\ +	RCU_LOCKDEP_WARN(!rcu_read_lock_held() &&			\ +			   !lockdep_is_held(&cgroup_mutex),		\  			   "cgroup_mutex or RCU read lock required");  /* @@ -145,6 +145,7 @@ static const char *cgroup_subsys_name[] = {   * part of that cgroup.   */  struct cgroup_root cgrp_dfl_root; +EXPORT_SYMBOL_GPL(cgrp_dfl_root);  /*   * The default hierarchy always exists but is hidden until mounted for the @@ -186,6 +187,9 @@ static u64 css_serial_nr_next = 1;  static unsigned long have_fork_callback __read_mostly;  static unsigned long have_exit_callback __read_mostly; +/* Ditto for the can_fork callback. */ +static unsigned long have_canfork_callback __read_mostly; +  static struct cftype cgroup_dfl_base_files[];  static struct cftype cgroup_legacy_base_files[]; @@ -207,7 +211,7 @@ static int cgroup_idr_alloc(struct idr *idr, void *ptr, int start, int end,  	idr_preload(gfp_mask);  	spin_lock_bh(&cgroup_idr_lock); -	ret = idr_alloc(idr, ptr, start, end, gfp_mask); +	ret = idr_alloc(idr, ptr, start, end, gfp_mask & ~__GFP_WAIT);  	spin_unlock_bh(&cgroup_idr_lock);  	idr_preload_end();  	return ret; @@ -1027,10 +1031,13 @@ static const struct file_operations proc_cgroupstats_operations;  static char *cgroup_file_name(struct cgroup *cgrp, const struct cftype *cft,  			      char *buf)  { +	struct cgroup_subsys *ss = cft->ss; +  	if (cft->ss && !(cft->flags & CFTYPE_NO_PREFIX) &&  	    !(cgrp->root->flags & CGRP_ROOT_NOPREFIX))  		snprintf(buf, CGROUP_FILE_NAME_MAX, "%s.%s", -			 cft->ss->name, cft->name); +			 cgroup_on_dfl(cgrp) ? ss->name : ss->legacy_name, +			 cft->name);  	else  		strncpy(buf, cft->name, CGROUP_FILE_NAME_MAX);  	return buf; @@ -1332,9 +1339,10 @@ static int cgroup_show_options(struct seq_file *seq,  	struct cgroup_subsys *ss;  	int ssid; -	for_each_subsys(ss, ssid) -		if (root->subsys_mask & (1 << ssid)) -			seq_printf(seq, ",%s", ss->name); +	if (root != &cgrp_dfl_root) +		for_each_subsys(ss, ssid) +			if (root->subsys_mask & (1 << ssid)) +				seq_show_option(seq, ss->legacy_name, NULL);  	if (root->flags & CGRP_ROOT_NOPREFIX)  		seq_puts(seq, ",noprefix");  	if (root->flags & CGRP_ROOT_XATTR) @@ -1342,13 +1350,14 @@ static int cgroup_show_options(struct seq_file *seq,  	spin_lock(&release_agent_path_lock);  	if (strlen(root->release_agent_path)) -		seq_printf(seq, ",release_agent=%s", root->release_agent_path); +		seq_show_option(seq, "release_agent", +				root->release_agent_path);  	spin_unlock(&release_agent_path_lock);  	if (test_bit(CGRP_CPUSET_CLONE_CHILDREN, &root->cgrp.flags))  		seq_puts(seq, ",clone_children");  	if (strlen(root->name)) -		seq_printf(seq, ",name=%s", root->name); +		seq_show_option(seq, "name", root->name);  	return 0;  } @@ -1447,7 +1456,7 @@ static int parse_cgroupfs_options(char *data, struct cgroup_sb_opts *opts)  		}  		for_each_subsys(ss, i) { -			if (strcmp(token, ss->name)) +			if (strcmp(token, ss->legacy_name))  				continue;  			if (ss->disabled)  				continue; @@ -1666,7 +1675,7 @@ static int cgroup_setup_root(struct cgroup_root *root, unsigned long ss_mask)  	lockdep_assert_held(&cgroup_mutex); -	ret = cgroup_idr_alloc(&root->cgroup_idr, root_cgrp, 1, 2, GFP_NOWAIT); +	ret = cgroup_idr_alloc(&root->cgroup_idr, root_cgrp, 1, 2, GFP_KERNEL);  	if (ret < 0)  		goto out;  	root_cgrp->id = ret; @@ -4579,7 +4588,7 @@ static int create_css(struct cgroup *cgrp, struct cgroup_subsys *ss,  	if (err)  		goto err_free_css; -	err = cgroup_idr_alloc(&ss->css_idr, NULL, 2, 0, GFP_NOWAIT); +	err = cgroup_idr_alloc(&ss->css_idr, NULL, 2, 0, GFP_KERNEL);  	if (err < 0)  		goto err_free_percpu_ref;  	css->id = err; @@ -4656,7 +4665,7 @@ static int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name,  	 * Temporarily set the pointer to NULL, so idr_find() won't return  	 * a half-baked cgroup.  	 */ -	cgrp->id = cgroup_idr_alloc(&root->cgroup_idr, NULL, 2, 0, GFP_NOWAIT); +	cgrp->id = cgroup_idr_alloc(&root->cgroup_idr, NULL, 2, 0, GFP_KERNEL);  	if (cgrp->id < 0) {  		ret = -ENOMEM;  		goto out_cancel_ref; @@ -4955,6 +4964,7 @@ static void __init cgroup_init_subsys(struct cgroup_subsys *ss, bool early)  	have_fork_callback |= (bool)ss->fork << ss->id;  	have_exit_callback |= (bool)ss->exit << ss->id; +	have_canfork_callback |= (bool)ss->can_fork << ss->id;  	/* At system boot, before all subsystems have been  	 * registered, no tasks have been forked, so we don't @@ -4993,6 +5003,8 @@ int __init cgroup_init_early(void)  		ss->id = i;  		ss->name = cgroup_subsys_name[i]; +		if (!ss->legacy_name) +			ss->legacy_name = cgroup_subsys_name[i];  		if (ss->early_init)  			cgroup_init_subsys(ss, true); @@ -5136,9 +5148,11 @@ int proc_cgroup_show(struct seq_file *m, struct pid_namespace *ns,  			continue;  		seq_printf(m, "%d:", root->hierarchy_id); -		for_each_subsys(ss, ssid) -			if (root->subsys_mask & (1 << ssid)) -				seq_printf(m, "%s%s", count++ ? "," : "", ss->name); +		if (root != &cgrp_dfl_root) +			for_each_subsys(ss, ssid) +				if (root->subsys_mask & (1 << ssid)) +					seq_printf(m, "%s%s", count++ ? "," : "", +						   ss->legacy_name);  		if (strlen(root->name))  			seq_printf(m, "%sname=%s", count ? "," : "",  				   root->name); @@ -5178,7 +5192,7 @@ static int proc_cgroupstats_show(struct seq_file *m, void *v)  	for_each_subsys(ss, i)  		seq_printf(m, "%s\t%d\t%d\t%d\n", -			   ss->name, ss->root->hierarchy_id, +			   ss->legacy_name, ss->root->hierarchy_id,  			   atomic_read(&ss->root->nr_cgrps), !ss->disabled);  	mutex_unlock(&cgroup_mutex); @@ -5197,6 +5211,19 @@ static const struct file_operations proc_cgroupstats_operations = {  	.release = single_release,  }; +static void **subsys_canfork_priv_p(void *ss_priv[CGROUP_CANFORK_COUNT], int i) +{ +	if (CGROUP_CANFORK_START <= i && i < CGROUP_CANFORK_END) +		return &ss_priv[i - CGROUP_CANFORK_START]; +	return NULL; +} + +static void *subsys_canfork_priv(void *ss_priv[CGROUP_CANFORK_COUNT], int i) +{ +	void **private = subsys_canfork_priv_p(ss_priv, i); +	return private ? *private : NULL; +} +  /**   * cgroup_fork - initialize cgroup related fields during copy_process()   * @child: pointer to task_struct of forking parent process. @@ -5212,6 +5239,57 @@ void cgroup_fork(struct task_struct *child)  }  /** + * cgroup_can_fork - called on a new task before the process is exposed + * @child: the task in question. + * + * This calls the subsystem can_fork() callbacks. If the can_fork() callback + * returns an error, the fork aborts with that error code. This allows for + * a cgroup subsystem to conditionally allow or deny new forks. + */ +int cgroup_can_fork(struct task_struct *child, +		    void *ss_priv[CGROUP_CANFORK_COUNT]) +{ +	struct cgroup_subsys *ss; +	int i, j, ret; + +	for_each_subsys_which(ss, i, &have_canfork_callback) { +		ret = ss->can_fork(child, subsys_canfork_priv_p(ss_priv, i)); +		if (ret) +			goto out_revert; +	} + +	return 0; + +out_revert: +	for_each_subsys(ss, j) { +		if (j >= i) +			break; +		if (ss->cancel_fork) +			ss->cancel_fork(child, subsys_canfork_priv(ss_priv, j)); +	} + +	return ret; +} + +/** + * cgroup_cancel_fork - called if a fork failed after cgroup_can_fork() + * @child: the task in question + * + * This calls the cancel_fork() callbacks if a fork failed *after* + * cgroup_can_fork() succeded. + */ +void cgroup_cancel_fork(struct task_struct *child, +			void *ss_priv[CGROUP_CANFORK_COUNT]) +{ +	struct cgroup_subsys *ss; +	int i; + +	for_each_subsys(ss, i) +		if (ss->cancel_fork) +			ss->cancel_fork(child, subsys_canfork_priv(ss_priv, i)); +} + +/**   * cgroup_post_fork - called on a new task after adding it to the task list   * @child: the task in question   * @@ -5221,7 +5299,8 @@ void cgroup_fork(struct task_struct *child)   * cgroup_task_iter_start() - to guarantee that the new task ends up on its   * list.   */ -void cgroup_post_fork(struct task_struct *child) +void cgroup_post_fork(struct task_struct *child, +		      void *old_ss_priv[CGROUP_CANFORK_COUNT])  {  	struct cgroup_subsys *ss;  	int i; @@ -5266,7 +5345,7 @@ void cgroup_post_fork(struct task_struct *child)  	 * and addition to css_set.  	 */  	for_each_subsys_which(ss, i, &have_fork_callback) -		ss->fork(child); +		ss->fork(child, subsys_canfork_priv(old_ss_priv, i));  }  /** @@ -5400,12 +5479,14 @@ static int __init cgroup_disable(char *str)  			continue;  		for_each_subsys(ss, i) { -			if (!strcmp(token, ss->name)) { -				ss->disabled = 1; -				printk(KERN_INFO "Disabling %s control group" -					" subsystem\n", ss->name); -				break; -			} +			if (strcmp(token, ss->name) && +			    strcmp(token, ss->legacy_name)) +				continue; + +			ss->disabled = 1; +			printk(KERN_INFO "Disabling %s control group subsystem\n", +			       ss->name); +			break;  		}  	}  	return 1;  |