Linux Headquarters
[ Register ]
[ About us ] [ Home Page ]

Advertisement
[ Kernel ] [ Documentation ] [ Links ] [ Books ]

Advertisement

Kernel v2.6.26-rc1 /kernel/cgroup.c

Filename:/kernel/cgroup.c
Lines Added:220
Lines Deleted:120
Also changed in: (Previous) 2.6.25-git20  2.6.25-git19  2.6.25.1  2.6.25-git18  2.6.25-git17  2.6.25-git16 
(Following) 2.6.26-rc2  2.6.26-rc3  2.6.26-rc3-git7  2.6.26-rc3-git8  2.6.26-rc4  2.6.26-rc5 

Location
[  2.6.26-rc1
  [  kernel
     o  cgroup.c

Patch

diff --git a/kernel/cgroup.c b/kernel/cgroup.c
index 2727f92..fbc6fc8 100644
--- a/kernel/cgroup.c
+++ b/kernel/cgroup.c
@@ -44,6 +44,7 @@
 #include <linux/kmod.h>
 #include <linux/delayacct.h>
 #include <linux/cgroupstats.h>
+#include <linux/hash.h>
 
 #include <asm/atomic.h>
 
@@ -118,17 +119,7 @@ static int root_count;
  * be called.
  */
 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,
-};
+static int need_mm_owner_callback __read_mostly;
 
 /* convenient tests for these bits */
 inline int cgroup_is_removed(const struct cgroup *cgrp)
@@ -204,6 +195,27 @@ static struct cg_cgroup_link init_css_set_link;
 static DEFINE_RWLOCK(css_set_lock);
 static int css_set_count;
 
+/* hash table for cgroup groups. This improves the performance to
+ * find an existing css_set */
+#define CSS_SET_HASH_BITS   7
+#define CSS_SET_TABLE_SIZE   (1 << CSS_SET_HASH_BITS)
+static struct hlist_head css_set_table[CSS_SET_TABLE_SIZE];
+
+static struct hlist_head *css_set_hash(struct cgroup_subsys_state *css[])
+{
+   int i;
+   int index;
+   unsigned long tmp = 0UL;
+
+   for (i = 0; i < CGROUP_SUBSYS_COUNT; i++)
+      tmp += (unsigned long)css[i];
+   tmp = (tmp >> 16) ^ tmp;
+
+   index = hash_long(tmp, CSS_SET_HASH_BITS);
+
+   return &css_set_table[index];
+}
+
 /* We don't maintain the lists running through each css_set to its
  * task until after the first call to cgroup_iter_start(). This
  * reduces the fork()/exit() overhead for people who have cgroups
@@ -230,7 +242,7 @@ static int use_task_css_set_links;
 static void unlink_css_set(struct css_set *cg)
 {
    write_lock(&css_set_lock);
-   list_del(&cg->list);
+   hlist_del(&cg->hlist);
    css_set_count--;
    while (!list_empty(&cg->cg_links)) {
       struct cg_cgroup_link *link;
@@ -295,9 +307,7 @@ static inline void put_css_set_taskexit(struct css_set *cg)
 /*
  * find_existing_css_set() is a helper for
  * find_css_set(), and checks to see whether an existing
- * css_set is suitable. This currently walks a linked-list for
- * simplicity; a later patch will use a hash table for better
- * performance
+ * css_set is suitable.
  *
  * oldcg: the cgroup group that we're using before the cgroup
  * transition
@@ -314,7 +324,9 @@ static struct css_set *find_existing_css_set(
 {
    int i;
    struct cgroupfs_root *root = cgrp->root;
-   struct list_head *l = &init_css_set.list;
+   struct hlist_head *hhead;
+   struct hlist_node *node;
+   struct css_set *cg;
 
    /* Built the set of subsystem state objects that we want to
     * see in the new css_set */
@@ -331,18 +343,13 @@ static struct css_set *find_existing_css_set(
       }
    }
 
-   /* Look through existing cgroup groups to find one to reuse */
-   do {
-      struct css_set *cg =
-         list_entry(l, struct css_set, list);
-
+   hhead = css_set_hash(template);
+   hlist_for_each_entry(cg, node, hhead, hlist) {
       if (!memcmp(template, cg->subsys, sizeof(cg->subsys))) {
          /* All subsystems matched */
          return cg;
       }
-      /* Try the next cgroup group */
-      l = l->next;
-   } while (l != &init_css_set.list);
+   }
 
    /* No existing cgroup group matched */
    return NULL;
@@ -404,6 +411,8 @@ static struct css_set *find_css_set(
    struct list_head tmp_cg_links;
    struct cg_cgroup_link *link;
 
+   struct hlist_head *hhead;
+
    /* First see if we already have a cgroup group that matches
     * the desired set */
    write_lock(&css_set_lock);
@@ -428,6 +437,7 @@ static struct css_set *find_css_set(
    kref_init(&res->ref);
    INIT_LIST_HEAD(&res->cg_links);
    INIT_LIST_HEAD(&res->tasks);
+   INIT_HLIST_NODE(&res->hlist);
 
    /* Copy the set of subsystem state objects generated in
     * find_existing_css_set() */
@@ -467,9 +477,12 @@ static struct css_set *find_css_set(
 
    BUG_ON(!list_empty(&tmp_cg_links));
 
-   /* Link this cgroup group into the list */
-   list_add(&res->list, &init_css_set.list);
    css_set_count++;
+
+   /* Add this cgroup group to the hash table */
+   hhead = css_set_hash(res->subsys);
+   hlist_add_head(&res->hlist, hhead);
+
    write_unlock(&css_set_lock);
 
    return res;
@@ -562,7 +575,7 @@ static struct inode_operations cgroup_dir_inode_operations;
 static struct file_operations proc_cgroupstats_operations;
 
 static struct backing_dev_info cgroup_backing_dev_info = {
-   .capabilities   = BDI_CAP_NO_ACCT_DIRTY | BDI_CAP_NO_WRITEBACK,
+   .capabilities   = BDI_CAP_NO_ACCT_AND_WRITEBACK,
 };
 
 static struct inode *cgroup_new_inode(mode_t mode, struct super_block *sb)
@@ -948,7 +961,7 @@ static int cgroup_get_sb(struct file_system_type *fs_type,
    int ret = 0;
    struct super_block *sb;
    struct cgroupfs_root *root;
-   struct list_head tmp_cg_links, *l;
+   struct list_head tmp_cg_links;
    INIT_LIST_HEAD(&tmp_cg_links);
 
    /* First find the desired set of subsystems */
@@ -990,6 +1003,7 @@ static int cgroup_get_sb(struct file_system_type *fs_type,
       /* New superblock */
       struct cgroup *cgrp = &root->top_cgroup;
       struct inode *inode;
+      int i;
 
       BUG_ON(sb->s_root != NULL);
 
@@ -1034,22 +1048,25 @@ static int cgroup_get_sb(struct file_system_type *fs_type,
       /* Link the top cgroup in this hierarchy into all
        * the css_set objects */
       write_lock(&css_set_lock);
-      l = &init_css_set.list;
-      do {
+      for (i = 0; i < CSS_SET_TABLE_SIZE; i++) {
+         struct hlist_head *hhead = &css_set_table[i];
+         struct hlist_node *node;
          struct css_set *cg;
-         struct cg_cgroup_link *link;
-         cg = list_entry(l, struct css_set, list);
-         BUG_ON(list_empty(&tmp_cg_links));
-         link = list_entry(tmp_cg_links.next,
-                 struct cg_cgroup_link,
-                 cgrp_link_list);
-         list_del(&link->cgrp_link_list);
-         link->cg = cg;
-         list_add(&link->cgrp_link_list,
-             &root->top_cgroup.css_sets);
-         list_add(&link->cg_link_list, &cg->cg_links);
-         l = l->next;
-      } while (l != &init_css_set.list);
+
+         hlist_for_each_entry(cg, node, hhead, hlist) {
+            struct cg_cgroup_link *link;
+
+            BUG_ON(list_empty(&tmp_cg_links));
+            link = list_entry(tmp_cg_links.next,
+                    struct cg_cgroup_link,
+                    cgrp_link_list);
+            list_del(&link->cgrp_link_list);
+            link->cg = cg;
+            list_add(&link->cgrp_link_list,
+                &root->top_cgroup.css_sets);
+            list_add(&link->cg_link_list, &cg->cg_links);
+         }
+      }
       write_unlock(&css_set_lock);
 
       free_cg_links(&tmp_cg_links);
@@ -1307,18 +1324,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 +1344,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 +1436,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 +1523,56 @@ 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;
+   if (cft->read_map) {
+      struct cgroup_map_cb cb = {
+         .fill = cgroup_map_add,
+         .state = m,
+      };
+      return cft->read_map(state->cgroup, cft, &cb);
+   }
+   return cft->read_seq_string(state->cgroup, cft, m);
+}
+
+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 +1585,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 || cft->read_seq_string) {
+      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 +1804,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 +2002,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 +2227,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 +2242,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,10 +2479,9 @@ 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;
 
    printk(KERN_INFO "Initializing cgroup subsys %s\n", ss->name);
 
@@ -2410,34 +2492,19 @@ static void cgroup_init_subsys(struct cgroup_subsys *ss)
    BUG_ON(IS_ERR(css));
    init_cgroup_css(css, ss, dummytop);
 
-   /* Update all cgroup groups to contain a subsys
+   /* Update the init_css_set to contain a subsys
     * pointer to this state - since the subsystem is
-    * newly registered, all tasks and hence all cgroup
-    * groups are in the subsystem's top cgroup. */
-   write_lock(&css_set_lock);
-   l = &init_css_set.list;
-   do {
-      struct css_set *cg =
-         list_entry(l, struct css_set, list);
-      cg->subsys[ss->subsys_id] = dummytop->subsys[ss->subsys_id];
-      l = l->next;
-   } while (l != &init_css_set.list);
-   write_unlock(&css_set_lock);
-
-    /* If this subsystem requested that it be notified with fork
-     * events, we should send it one now for every process in the
-     * system */
-   if (ss->fork) {
-      struct task_struct *g, *p;
-
-      read_lock(&tasklist_lock);
-      do_each_thread(g, p) {
-         ss->fork(ss, p);
-      } while_each_thread(g, p);
-      read_unlock(&tasklist_lock);
-   }
+    * newly registered, all tasks and hence the
+    * init_css_set is in the subsystem's top cgroup. */
+   init_css_set.subsys[ss->subsys_id] = dummytop->subsys[ss->subsys_id];
 
    need_forkexit_callback |= ss->fork || ss->exit;
+   need_mm_owner_callback |= !!ss->mm_owner_changed;
+
+   /* At system boot, before all subsystems have been
+    * registered, no tasks have been forked, so we don't
+    * need to invoke fork callbacks here. */
+   BUG_ON(!list_empty(&init_task.tasks));
 
    ss->active = 1;
 }
@@ -2453,9 +2520,9 @@ int __init cgroup_init_early(void)
    int i;
    kref_init(&init_css_set.ref);
    kref_get(&init_css_set.ref);
-   INIT_LIST_HEAD(&init_css_set.list);
    INIT_LIST_HEAD(&init_css_set.cg_links);
    INIT_LIST_HEAD(&init_css_set.tasks);
+   INIT_HLIST_NODE(&init_css_set.hlist);
    css_set_count = 1;
    init_cgroup_root(&rootnode);
    list_add(&rootnode.root_list, &roots);
@@ -2468,6 +2535,9 @@ int __init cgroup_init_early(void)
    list_add(&init_css_set_link.cg_link_list,
        &init_css_set.cg_links);
 
+   for (i = 0; i < CSS_SET_TABLE_SIZE; i++)
+      INIT_HLIST_HEAD(&css_set_table[i]);
+
    for (i = 0; i < CGROUP_SUBSYS_COUNT; i++) {
       struct cgroup_subsys *ss = subsys[i];
 
@@ -2497,7 +2567,7 @@ int __init cgroup_init(void)
 {
    int err;
    int i;
-   struct proc_dir_entry *entry;
+   struct hlist_head *hhead;
 
    err = bdi_init(&cgroup_backing_dev_info);
    if (err)
@@ -2509,13 +2579,15 @@ int __init cgroup_init(void)
          cgroup_init_subsys(ss);
    }
 
+   /* Add init_css_set to the hash table */
+   hhead = css_set_hash(init_css_set.subsys);
+   hlist_add_head(&init_css_set.hlist, hhead);
+
    err = register_filesystem(&cgroup_fs_type);
    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)
@@ -2678,6 +2750,34 @@ void cgroup_fork_callbacks(struct task_struct *child)
    }
 }
 
+#ifdef CONFIG_MM_OWNER
+/**
+ * cgroup_mm_owner_callbacks - run callbacks when the mm->owner changes
+ * @p: the new owner
+ *
+ * Called on every change to mm->owner. mm_init_owner() does not
+ * invoke this routine, since it assigns the mm->owner the first time
+ * and does not change it.
+ */
+void cgroup_mm_owner_callbacks(struct task_struct *old, struct task_struct *new)
+{
+   struct cgroup *oldcgrp, *newcgrp;
+
+   if (need_mm_owner_callback) {
+      int i;
+      for (i = 0; i < CGROUP_SUBSYS_COUNT; i++) {
+         struct cgroup_subsys *ss = subsys[i];
+         oldcgrp = task_cgroup(old, ss->subsys_id);
+         newcgrp = task_cgroup(new, ss->subsys_id);
+         if (oldcgrp == newcgrp)
+            continue;
+         if (ss->mm_owner_changed)
+            ss->mm_owner_changed(ss, oldcgrp, newcgrp);
+      }
+   }
+}
+#endif /* CONFIG_MM_OWNER */
+
 /**
  * cgroup_post_fork - called on a new task after adding it to the task list
  * @child: the task in question


Comments: webmaster (at) linuxhq.com.
Advertising: banners (at) linuxhq.com.
Compilation ©1998-2008 Linux Headquarters, Inc.