]> err.no Git - linux-2.6/blobdiff - kernel/cgroup.c
cgroups: add the trigger callback to struct cftype
[linux-2.6] / kernel / cgroup.c
index 2727f92383596e4a6f7bec5072b1e804a7801916..7c8cc5141877af3ab384fe58c416667e4afd0d35 100644 (file)
@@ -119,17 +119,6 @@ static int root_count;
  */
 static int need_forkexit_callback;
 
-/* bits in struct cgroup flags field */
-enum {
-       /* Control Group is dead */
-       CGRP_REMOVED,
-       /* Control Group has previously had a child cgroup or a task,
-        * but no longer (only if CGRP_NOTIFY_ON_RELEASE is set) */
-       CGRP_RELEASABLE,
-       /* Control Group requires release notifications to userspace */
-       CGRP_NOTIFY_ON_RELEASE,
-};
-
 /* convenient tests for these bits */
 inline int cgroup_is_removed(const struct cgroup *cgrp)
 {
@@ -1307,18 +1296,16 @@ enum cgroup_filetype {
        FILE_DIR,
        FILE_TASKLIST,
        FILE_NOTIFY_ON_RELEASE,
-       FILE_RELEASABLE,
        FILE_RELEASE_AGENT,
 };
 
-static ssize_t cgroup_write_uint(struct cgroup *cgrp, struct cftype *cft,
-                                struct file *file,
-                                const char __user *userbuf,
-                                size_t nbytes, loff_t *unused_ppos)
+static ssize_t cgroup_write_X64(struct cgroup *cgrp, struct cftype *cft,
+                               struct file *file,
+                               const char __user *userbuf,
+                               size_t nbytes, loff_t *unused_ppos)
 {
        char buffer[64];
        int retval = 0;
-       u64 val;
        char *end;
 
        if (!nbytes)
@@ -1329,16 +1316,18 @@ static ssize_t cgroup_write_uint(struct cgroup *cgrp, struct cftype *cft,
                return -EFAULT;
 
        buffer[nbytes] = 0;     /* nul-terminate */
-
-       /* strip newline if necessary */
-       if (nbytes && (buffer[nbytes-1] == '\n'))
-               buffer[nbytes-1] = 0;
-       val = simple_strtoull(buffer, &end, 0);
-       if (*end)
-               return -EINVAL;
-
-       /* Pass to subsystem */
-       retval = cft->write_uint(cgrp, cft, val);
+       strstrip(buffer);
+       if (cft->write_u64) {
+               u64 val = simple_strtoull(buffer, &end, 0);
+               if (*end)
+                       return -EINVAL;
+               retval = cft->write_u64(cgrp, cft, val);
+       } else {
+               s64 val = simple_strtoll(buffer, &end, 0);
+               if (*end)
+                       return -EINVAL;
+               retval = cft->write_s64(cgrp, cft, val);
+       }
        if (!retval)
                retval = nbytes;
        return retval;
@@ -1419,23 +1408,39 @@ static ssize_t cgroup_file_write(struct file *file, const char __user *buf,
                return -ENODEV;
        if (cft->write)
                return cft->write(cgrp, cft, file, buf, nbytes, ppos);
-       if (cft->write_uint)
-               return cgroup_write_uint(cgrp, cft, file, buf, nbytes, ppos);
+       if (cft->write_u64 || cft->write_s64)
+               return cgroup_write_X64(cgrp, cft, file, buf, nbytes, ppos);
+       if (cft->trigger) {
+               int ret = cft->trigger(cgrp, (unsigned int)cft->private);
+               return ret ? ret : nbytes;
+       }
        return -EINVAL;
 }
 
-static ssize_t cgroup_read_uint(struct cgroup *cgrp, struct cftype *cft,
-                                  struct file *file,
-                                  char __user *buf, size_t nbytes,
-                                  loff_t *ppos)
+static ssize_t cgroup_read_u64(struct cgroup *cgrp, struct cftype *cft,
+                              struct file *file,
+                              char __user *buf, size_t nbytes,
+                              loff_t *ppos)
 {
        char tmp[64];
-       u64 val = cft->read_uint(cgrp, cft);
+       u64 val = cft->read_u64(cgrp, cft);
        int len = sprintf(tmp, "%llu\n", (unsigned long long) val);
 
        return simple_read_from_buffer(buf, nbytes, ppos, tmp, len);
 }
 
+static ssize_t cgroup_read_s64(struct cgroup *cgrp, struct cftype *cft,
+                              struct file *file,
+                              char __user *buf, size_t nbytes,
+                              loff_t *ppos)
+{
+       char tmp[64];
+       s64 val = cft->read_s64(cgrp, cft);
+       int len = sprintf(tmp, "%lld\n", (long long) val);
+
+       return simple_read_from_buffer(buf, nbytes, ppos, tmp, len);
+}
+
 static ssize_t cgroup_common_file_read(struct cgroup *cgrp,
                                          struct cftype *cft,
                                          struct file *file,
@@ -1490,11 +1495,53 @@ static ssize_t cgroup_file_read(struct file *file, char __user *buf,
 
        if (cft->read)
                return cft->read(cgrp, cft, file, buf, nbytes, ppos);
-       if (cft->read_uint)
-               return cgroup_read_uint(cgrp, cft, file, buf, nbytes, ppos);
+       if (cft->read_u64)
+               return cgroup_read_u64(cgrp, cft, file, buf, nbytes, ppos);
+       if (cft->read_s64)
+               return cgroup_read_s64(cgrp, cft, file, buf, nbytes, ppos);
        return -EINVAL;
 }
 
+/*
+ * seqfile ops/methods for returning structured data. Currently just
+ * supports string->u64 maps, but can be extended in future.
+ */
+
+struct cgroup_seqfile_state {
+       struct cftype *cft;
+       struct cgroup *cgroup;
+};
+
+static int cgroup_map_add(struct cgroup_map_cb *cb, const char *key, u64 value)
+{
+       struct seq_file *sf = cb->state;
+       return seq_printf(sf, "%s %llu\n", key, (unsigned long long)value);
+}
+
+static int cgroup_seqfile_show(struct seq_file *m, void *arg)
+{
+       struct cgroup_seqfile_state *state = m->private;
+       struct cftype *cft = state->cft;
+       struct cgroup_map_cb cb = {
+               .fill = cgroup_map_add,
+               .state = m,
+       };
+       return cft->read_map(state->cgroup, cft, &cb);
+}
+
+int cgroup_seqfile_release(struct inode *inode, struct file *file)
+{
+       struct seq_file *seq = file->private_data;
+       kfree(seq->private);
+       return single_release(inode, file);
+}
+
+static struct file_operations cgroup_seqfile_operations = {
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = cgroup_seqfile_release,
+};
+
 static int cgroup_file_open(struct inode *inode, struct file *file)
 {
        int err;
@@ -1507,7 +1554,18 @@ static int cgroup_file_open(struct inode *inode, struct file *file)
        cft = __d_cft(file->f_dentry);
        if (!cft)
                return -ENODEV;
-       if (cft->open)
+       if (cft->read_map) {
+               struct cgroup_seqfile_state *state =
+                       kzalloc(sizeof(*state), GFP_USER);
+               if (!state)
+                       return -ENOMEM;
+               state->cft = cft;
+               state->cgroup = __d_cgrp(file->f_dentry->d_parent);
+               file->f_op = &cgroup_seqfile_operations;
+               err = single_open(file, cgroup_seqfile_show, state);
+               if (err < 0)
+                       kfree(state);
+       } else if (cft->open)
                err = cft->open(inode, file);
        else
                err = 0;
@@ -1715,14 +1773,19 @@ static void cgroup_advance_iter(struct cgroup *cgrp,
  * The tasklist_lock is not held here, as do_each_thread() and
  * while_each_thread() are protected by RCU.
  */
-void cgroup_enable_task_cg_lists(void)
+static void cgroup_enable_task_cg_lists(void)
 {
        struct task_struct *p, *g;
        write_lock(&css_set_lock);
        use_task_css_set_links = 1;
        do_each_thread(g, p) {
                task_lock(p);
-               if (list_empty(&p->cg_list))
+               /*
+                * We should check if the process is exiting, otherwise
+                * it will race with cgroup_exit() in that the list
+                * entry won't be deleted though the process has exited.
+                */
+               if (!(p->flags & PF_EXITING) && list_empty(&p->cg_list))
                        list_add(&p->cg_list, &p->cgroups->tasks);
                task_unlock(p);
        } while_each_thread(g, p);
@@ -1908,14 +1971,14 @@ int cgroup_scan_tasks(struct cgroup_scanner *scan)
 
        if (heap->size) {
                for (i = 0; i < heap->size; i++) {
-                       struct task_struct *p = heap->ptrs[i];
+                       struct task_struct *q = heap->ptrs[i];
                        if (i == 0) {
-                               latest_time = p->start_time;
-                               latest_task = p;
+                               latest_time = q->start_time;
+                               latest_task = q;
                        }
                        /* Process the task per the caller's callback */
-                       scan->process_task(p, scan);
-                       put_task_struct(p);
+                       scan->process_task(q, scan);
+                       put_task_struct(q);
                }
                /*
                 * If we had to process any tasks at all, scan again
@@ -2133,11 +2196,6 @@ static u64 cgroup_read_notify_on_release(struct cgroup *cgrp,
        return notify_on_release(cgrp);
 }
 
-static u64 cgroup_read_releasable(struct cgroup *cgrp, struct cftype *cft)
-{
-       return test_bit(CGRP_RELEASABLE, &cgrp->flags);
-}
-
 /*
  * for the common functions, 'private' gives the type of file
  */
@@ -2153,16 +2211,10 @@ static struct cftype files[] = {
 
        {
                .name = "notify_on_release",
-               .read_uint = cgroup_read_notify_on_release,
+               .read_u64 = cgroup_read_notify_on_release,
                .write = cgroup_common_file_write,
                .private = FILE_NOTIFY_ON_RELEASE,
        },
-
-       {
-               .name = "releasable",
-               .read_uint = cgroup_read_releasable,
-               .private = FILE_RELEASABLE,
-       }
 };
 
 static struct cftype cft_release_agent = {
@@ -2396,7 +2448,7 @@ static int cgroup_rmdir(struct inode *unused_dir, struct dentry *dentry)
        return 0;
 }
 
-static void cgroup_init_subsys(struct cgroup_subsys *ss)
+static void __init cgroup_init_subsys(struct cgroup_subsys *ss)
 {
        struct cgroup_subsys_state *css;
        struct list_head *l;
@@ -2497,7 +2549,6 @@ int __init cgroup_init(void)
 {
        int err;
        int i;
-       struct proc_dir_entry *entry;
 
        err = bdi_init(&cgroup_backing_dev_info);
        if (err)
@@ -2513,9 +2564,7 @@ int __init cgroup_init(void)
        if (err < 0)
                goto out;
 
-       entry = create_proc_entry("cgroups", 0, NULL);
-       if (entry)
-               entry->proc_fops = &proc_cgroupstats_operations;
+       proc_create("cgroups", 0, NULL, &proc_cgroupstats_operations);
 
 out:
        if (err)