Skip to content
cgroup.c 38.8 KiB
Newer Older
	.read = cgroup_file_read,
	.write = cgroup_file_write,
	.llseek = generic_file_llseek,
	.open = cgroup_file_open,
	.release = cgroup_file_release,
};

static struct inode_operations cgroup_dir_inode_operations = {
	.lookup = simple_lookup,
	.mkdir = cgroup_mkdir,
	.rmdir = cgroup_rmdir,
	.rename = cgroup_rename,
};

static int cgroup_create_file(struct dentry *dentry, int mode,
				struct super_block *sb)
{
	static struct dentry_operations cgroup_dops = {
		.d_iput = cgroup_diput,
	};

	struct inode *inode;

	if (!dentry)
		return -ENOENT;
	if (dentry->d_inode)
		return -EEXIST;

	inode = cgroup_new_inode(mode, sb);
	if (!inode)
		return -ENOMEM;

	if (S_ISDIR(mode)) {
		inode->i_op = &cgroup_dir_inode_operations;
		inode->i_fop = &simple_dir_operations;

		/* start off with i_nlink == 2 (for "." entry) */
		inc_nlink(inode);

		/* start with the directory inode held, so that we can
		 * populate it without racing with another mkdir */
		mutex_lock(&inode->i_mutex);
	} else if (S_ISREG(mode)) {
		inode->i_size = 0;
		inode->i_fop = &cgroup_file_operations;
	}
	dentry->d_op = &cgroup_dops;
	d_instantiate(dentry, inode);
	dget(dentry);	/* Extra count - pin the dentry in core */
	return 0;
}

/*
 *	cgroup_create_dir - create a directory for an object.
 *	cont:	the cgroup we create the directory for.
 *		It must have a valid ->parent field
 *		And we are going to fill its ->dentry field.
 *	dentry: dentry of the new container
 *	mode:	mode to set on new directory.
 */
static int cgroup_create_dir(struct cgroup *cont, struct dentry *dentry,
				int mode)
{
	struct dentry *parent;
	int error = 0;

	parent = cont->parent->dentry;
	error = cgroup_create_file(dentry, S_IFDIR | mode, cont->root->sb);
	if (!error) {
		dentry->d_fsdata = cont;
		inc_nlink(parent->d_inode);
		cont->dentry = dentry;
		dget(dentry);
	}
	dput(dentry);

	return error;
}

int cgroup_add_file(struct cgroup *cont,
		       struct cgroup_subsys *subsys,
		       const struct cftype *cft)
{
	struct dentry *dir = cont->dentry;
	struct dentry *dentry;
	int error;

	char name[MAX_CGROUP_TYPE_NAMELEN + MAX_CFTYPE_NAME + 2] = { 0 };
	if (subsys && !test_bit(ROOT_NOPREFIX, &cont->root->flags)) {
		strcpy(name, subsys->name);
		strcat(name, ".");
	}
	strcat(name, cft->name);
	BUG_ON(!mutex_is_locked(&dir->d_inode->i_mutex));
	dentry = lookup_one_len(name, dir, strlen(name));
	if (!IS_ERR(dentry)) {
		error = cgroup_create_file(dentry, 0644 | S_IFREG,
						cont->root->sb);
		if (!error)
			dentry->d_fsdata = (void *)cft;
		dput(dentry);
	} else
		error = PTR_ERR(dentry);
	return error;
}

int cgroup_add_files(struct cgroup *cont,
			struct cgroup_subsys *subsys,
			const struct cftype cft[],
			int count)
{
	int i, err;
	for (i = 0; i < count; i++) {
		err = cgroup_add_file(cont, subsys, &cft[i]);
		if (err)
			return err;
	}
	return 0;
}

/* Count the number of tasks in a cgroup. Could be made more
 * time-efficient but less space-efficient with more linked lists
 * running through each cgroup and the css_set structures that
 * referenced it. Must be called with tasklist_lock held for read or
 * write or in an rcu critical section.
 */
int __cgroup_task_count(const struct cgroup *cont)
{
	int count = 0;
	struct task_struct *g, *p;
	struct cgroup_subsys_state *css;
	int subsys_id;

	get_first_subsys(cont, &css, &subsys_id);
	do_each_thread(g, p) {
		if (task_subsys_state(p, subsys_id) == css)
			count ++;
	} while_each_thread(g, p);
	return count;
}

/*
 * Stuff for reading the 'tasks' file.
 *
 * Reading this file can return large amounts of data if a cgroup has
 * *lots* of attached tasks. So it may need several calls to read(),
 * but we cannot guarantee that the information we produce is correct
 * unless we produce it entirely atomically.
 *
 * Upon tasks file open(), a struct ctr_struct is allocated, that
 * will have a pointer to an array (also allocated here).  The struct
 * ctr_struct * is stored in file->private_data.  Its resources will
 * be freed by release() when the file is closed.  The array is used
 * to sprintf the PIDs and then used by read().
 */
struct ctr_struct {
	char *buf;
	int bufsz;
};

/*
 * Load into 'pidarray' up to 'npids' of the tasks using cgroup
 * 'cont'.  Return actual number of pids loaded.  No need to
 * task_lock(p) when reading out p->cgroup, since we're in an RCU
 * read section, so the css_set can't go away, and is
 * immutable after creation.
 */
static int pid_array_load(pid_t *pidarray, int npids, struct cgroup *cont)
{
	int n = 0;
	struct task_struct *g, *p;
	struct cgroup_subsys_state *css;
	int subsys_id;

	get_first_subsys(cont, &css, &subsys_id);
	rcu_read_lock();
	do_each_thread(g, p) {
		if (task_subsys_state(p, subsys_id) == css) {
			pidarray[n++] = pid_nr(task_pid(p));
			if (unlikely(n == npids))
				goto array_full;
		}
	} while_each_thread(g, p);

array_full:
	rcu_read_unlock();
	return n;
}

static int cmppid(const void *a, const void *b)
{
	return *(pid_t *)a - *(pid_t *)b;
}

/*
 * Convert array 'a' of 'npids' pid_t's to a string of newline separated
 * decimal pids in 'buf'.  Don't write more than 'sz' chars, but return
 * count 'cnt' of how many chars would be written if buf were large enough.
 */
static int pid_array_to_buf(char *buf, int sz, pid_t *a, int npids)
{
	int cnt = 0;
	int i;

	for (i = 0; i < npids; i++)
		cnt += snprintf(buf + cnt, max(sz - cnt, 0), "%d\n", a[i]);
	return cnt;
}

/*
 * Handle an open on 'tasks' file.  Prepare a buffer listing the
 * process id's of tasks currently attached to the cgroup being opened.
 *
 * Does not require any specific cgroup mutexes, and does not take any.
 */
static int cgroup_tasks_open(struct inode *unused, struct file *file)
{
	struct cgroup *cont = __d_cont(file->f_dentry->d_parent);
	struct ctr_struct *ctr;
	pid_t *pidarray;
	int npids;
	char c;

	if (!(file->f_mode & FMODE_READ))
		return 0;

	ctr = kmalloc(sizeof(*ctr), GFP_KERNEL);
	if (!ctr)
		goto err0;

	/*
	 * If cgroup gets more users after we read count, we won't have
	 * enough space - tough.  This race is indistinguishable to the
	 * caller from the case that the additional cgroup users didn't
	 * show up until sometime later on.
	 */
	npids = cgroup_task_count(cont);
	if (npids) {
		pidarray = kmalloc(npids * sizeof(pid_t), GFP_KERNEL);
		if (!pidarray)
			goto err1;

		npids = pid_array_load(pidarray, npids, cont);
		sort(pidarray, npids, sizeof(pid_t), cmppid, NULL);

		/* Call pid_array_to_buf() twice, first just to get bufsz */
		ctr->bufsz = pid_array_to_buf(&c, sizeof(c), pidarray, npids) + 1;
		ctr->buf = kmalloc(ctr->bufsz, GFP_KERNEL);
		if (!ctr->buf)
			goto err2;
		ctr->bufsz = pid_array_to_buf(ctr->buf, ctr->bufsz, pidarray, npids);

		kfree(pidarray);
	} else {
		ctr->buf = 0;
		ctr->bufsz = 0;
	}
	file->private_data = ctr;
	return 0;

err2:
	kfree(pidarray);
err1:
	kfree(ctr);
err0:
	return -ENOMEM;
}

static ssize_t cgroup_tasks_read(struct cgroup *cont,
				    struct cftype *cft,
				    struct file *file, char __user *buf,
				    size_t nbytes, loff_t *ppos)
{
	struct ctr_struct *ctr = file->private_data;

	return simple_read_from_buffer(buf, nbytes, ppos, ctr->buf, ctr->bufsz);
}

static int cgroup_tasks_release(struct inode *unused_inode,
					struct file *file)
{
	struct ctr_struct *ctr;

	if (file->f_mode & FMODE_READ) {
		ctr = file->private_data;
		kfree(ctr->buf);
		kfree(ctr);
	}
	return 0;
}

/*
 * for the common functions, 'private' gives the type of file
 */
static struct cftype cft_tasks = {
	.name = "tasks",
	.open = cgroup_tasks_open,
	.read = cgroup_tasks_read,
	.write = cgroup_common_file_write,
	.release = cgroup_tasks_release,
	.private = FILE_TASKLIST,
};

static int cgroup_populate_dir(struct cgroup *cont)
{
	int err;
	struct cgroup_subsys *ss;

	/* First clear out any existing files */
	cgroup_clear_directory(cont->dentry);

	err = cgroup_add_file(cont, NULL, &cft_tasks);
	if (err < 0)
		return err;

	for_each_subsys(cont->root, ss) {
		if (ss->populate && (err = ss->populate(ss, cont)) < 0)
			return err;
	}

	return 0;
}

static void init_cgroup_css(struct cgroup_subsys_state *css,
			       struct cgroup_subsys *ss,
			       struct cgroup *cont)
{
	css->cgroup = cont;
	atomic_set(&css->refcnt, 0);
	css->flags = 0;
	if (cont == dummytop)
		set_bit(CSS_ROOT, &css->flags);
	BUG_ON(cont->subsys[ss->subsys_id]);
	cont->subsys[ss->subsys_id] = css;
}

/*
 *	cgroup_create - create a cgroup
 *	parent:	cgroup that will be parent of the new cgroup.
 *	name:		name of the new cgroup. Will be strcpy'ed.
 *	mode:		mode to set on new inode
 *
 *	Must be called with the mutex on the parent inode held
 */

static long cgroup_create(struct cgroup *parent, struct dentry *dentry,
			     int mode)
{
	struct cgroup *cont;
	struct cgroupfs_root *root = parent->root;
	int err = 0;
	struct cgroup_subsys *ss;
	struct super_block *sb = root->sb;

	cont = kzalloc(sizeof(*cont), GFP_KERNEL);
	if (!cont)
		return -ENOMEM;

	/* Grab a reference on the superblock so the hierarchy doesn't
	 * get deleted on unmount if there are child cgroups.  This
	 * can be done outside cgroup_mutex, since the sb can't
	 * disappear while someone has an open control file on the
	 * fs */
	atomic_inc(&sb->s_active);

	mutex_lock(&cgroup_mutex);

	cont->flags = 0;
	INIT_LIST_HEAD(&cont->sibling);
	INIT_LIST_HEAD(&cont->children);

	cont->parent = parent;
	cont->root = parent->root;
	cont->top_cgroup = parent->top_cgroup;

	for_each_subsys(root, ss) {
		struct cgroup_subsys_state *css = ss->create(ss, cont);
		if (IS_ERR(css)) {
			err = PTR_ERR(css);
			goto err_destroy;
		}
		init_cgroup_css(css, ss, cont);
	}

	list_add(&cont->sibling, &cont->parent->children);
	root->number_of_cgroups++;

	err = cgroup_create_dir(cont, dentry, mode);
	if (err < 0)
		goto err_remove;

	/* The cgroup directory was pre-locked for us */
	BUG_ON(!mutex_is_locked(&cont->dentry->d_inode->i_mutex));

	err = cgroup_populate_dir(cont);
	/* If err < 0, we have a half-filled directory - oh well ;) */

	mutex_unlock(&cgroup_mutex);
	mutex_unlock(&cont->dentry->d_inode->i_mutex);

	return 0;

 err_remove:

	list_del(&cont->sibling);
	root->number_of_cgroups--;

 err_destroy:

	for_each_subsys(root, ss) {
		if (cont->subsys[ss->subsys_id])
			ss->destroy(ss, cont);
	}

	mutex_unlock(&cgroup_mutex);

	/* Release the reference count that we took on the superblock */
	deactivate_super(sb);

	kfree(cont);
	return err;
}

static int cgroup_mkdir(struct inode *dir, struct dentry *dentry, int mode)
{
	struct cgroup *c_parent = dentry->d_parent->d_fsdata;

	/* the vfs holds inode->i_mutex already */
	return cgroup_create(c_parent, dentry, mode | S_IFDIR);
}

static int cgroup_rmdir(struct inode *unused_dir, struct dentry *dentry)
{
	struct cgroup *cont = dentry->d_fsdata;
	struct dentry *d;
	struct cgroup *parent;
	struct cgroup_subsys *ss;
	struct super_block *sb;
	struct cgroupfs_root *root;
	int css_busy = 0;

	/* the vfs holds both inode->i_mutex already */

	mutex_lock(&cgroup_mutex);
	if (atomic_read(&cont->count) != 0) {
		mutex_unlock(&cgroup_mutex);
		return -EBUSY;
	}
	if (!list_empty(&cont->children)) {
		mutex_unlock(&cgroup_mutex);
		return -EBUSY;
	}

	parent = cont->parent;
	root = cont->root;
	sb = root->sb;

	/* Check the reference count on each subsystem. Since we
	 * already established that there are no tasks in the
	 * cgroup, if the css refcount is also 0, then there should
	 * be no outstanding references, so the subsystem is safe to
	 * destroy */
	for_each_subsys(root, ss) {
		struct cgroup_subsys_state *css;
		css = cont->subsys[ss->subsys_id];
		if (atomic_read(&css->refcnt)) {
			css_busy = 1;
			break;
		}
	}
	if (css_busy) {
		mutex_unlock(&cgroup_mutex);
		return -EBUSY;
	}

	for_each_subsys(root, ss) {
		if (cont->subsys[ss->subsys_id])
			ss->destroy(ss, cont);
	}

	set_bit(CONT_REMOVED, &cont->flags);
	/* delete my sibling from parent->children */
	list_del(&cont->sibling);
	spin_lock(&cont->dentry->d_lock);
	d = dget(cont->dentry);
	cont->dentry = NULL;
	spin_unlock(&d->d_lock);

	cgroup_d_remove_dir(d);
	dput(d);
	root->number_of_cgroups--;

	mutex_unlock(&cgroup_mutex);
	/* Drop the active superblock reference that we took when we
	 * created the cgroup */
	deactivate_super(sb);
	return 0;
}

static void cgroup_init_subsys(struct cgroup_subsys *ss)
{
	struct task_struct *g, *p;
	struct cgroup_subsys_state *css;
	printk(KERN_ERR "Initializing cgroup subsys %s\n", ss->name);

	/* Create the top cgroup state for this subsystem */
	ss->root = &rootnode;
	css = ss->create(ss, dummytop);
	/* We don't handle early failures gracefully */
	BUG_ON(IS_ERR(css));
	init_cgroup_css(css, ss, dummytop);

	/* Update all tasks to contain a subsys pointer to this state
	 * - since the subsystem is newly registered, all tasks are in
	 * the subsystem's top cgroup. */

 	/* If this subsystem requested that it be notified with fork
 	 * events, we should send it one now for every process in the
 	 * system */

	read_lock(&tasklist_lock);
	init_task.cgroups.subsys[ss->subsys_id] = css;
	if (ss->fork)
		ss->fork(ss, &init_task);

	do_each_thread(g, p) {
		printk(KERN_INFO "Setting task %p css to %p (%d)\n", css, p, p->pid);
		p->cgroups.subsys[ss->subsys_id] = css;
		if (ss->fork)
			ss->fork(ss, p);
	} while_each_thread(g, p);
	read_unlock(&tasklist_lock);

	need_forkexit_callback |= ss->fork || ss->exit;

	ss->active = 1;
}

/**
 * cgroup_init_early - initialize cgroups at system boot, and
 * initialize any subsystems that request early init.
 */
int __init cgroup_init_early(void)
{
	int i;
	init_cgroup_root(&rootnode);
	list_add(&rootnode.root_list, &roots);

	for (i = 0; i < CGROUP_SUBSYS_COUNT; i++) {
		struct cgroup_subsys *ss = subsys[i];

		BUG_ON(!ss->name);
		BUG_ON(strlen(ss->name) > MAX_CGROUP_TYPE_NAMELEN);
		BUG_ON(!ss->create);
		BUG_ON(!ss->destroy);
		if (ss->subsys_id != i) {
			printk(KERN_ERR "Subsys %s id == %d\n",
			       ss->name, ss->subsys_id);
			BUG();
		}

		if (ss->early_init)
			cgroup_init_subsys(ss);
	}
	return 0;
}

/**
 * cgroup_init - register cgroup filesystem and /proc file, and
 * initialize any subsystems that didn't request early init.
 */
int __init cgroup_init(void)
{
	int err;
	int i;

	for (i = 0; i < CGROUP_SUBSYS_COUNT; i++) {
		struct cgroup_subsys *ss = subsys[i];
		if (!ss->early_init)
			cgroup_init_subsys(ss);
	}

	err = register_filesystem(&cgroup_fs_type);
	if (err < 0)
		goto out;

out:
	return err;
}