[PATCH] cpuset: minor spacing initializer fixes
[powerpc.git] / kernel / cpuset.c
index 5a737ed..f513dd9 100644 (file)
 #include <asm/atomic.h>
 #include <asm/semaphore.h>
 
-#define CPUSET_SUPER_MAGIC             0x27e0eb
+#define CPUSET_SUPER_MAGIC             0x27e0eb
+
+/* See "Frequency meter" comments, below. */
+
+struct fmeter {
+       int cnt;                /* unprocessed events count */
+       int val;                /* most recent output value */
+       time_t time;            /* clock (secs) when val computed */
+       spinlock_t lock;        /* guards read or write of above */
+};
 
 struct cpuset {
        unsigned long flags;            /* "unsigned long" so bitops work */
@@ -80,13 +89,16 @@ struct cpuset {
         * Copy of global cpuset_mems_generation as of the most
         * recent time this cpuset changed its mems_allowed.
         */
-        int mems_generation;
+       int mems_generation;
+
+       struct fmeter fmeter;           /* memory_pressure filter */
 };
 
 /* bits in struct cpuset flags field */
 typedef enum {
        CS_CPU_EXCLUSIVE,
        CS_MEM_EXCLUSIVE,
+       CS_MEMORY_MIGRATE,
        CS_REMOVED,
        CS_NOTIFY_ON_RELEASE
 } cpuset_flagbits_t;
@@ -112,6 +124,11 @@ static inline int notify_on_release(const struct cpuset *cs)
        return !!test_bit(CS_NOTIFY_ON_RELEASE, &cs->flags);
 }
 
+static inline int is_memory_migrate(const struct cpuset *cs)
+{
+       return !!test_bit(CS_MEMORY_MIGRATE, &cs->flags);
+}
+
 /*
  * Increment this atomic integer everytime any cpuset changes its
  * mems_allowed value.  Users of cpusets can track this generation
@@ -137,13 +154,10 @@ static struct cpuset top_cpuset = {
        .count = ATOMIC_INIT(0),
        .sibling = LIST_HEAD_INIT(top_cpuset.sibling),
        .children = LIST_HEAD_INIT(top_cpuset.children),
-       .parent = NULL,
-       .dentry = NULL,
-       .mems_generation = 0,
 };
 
 static struct vfsmount *cpuset_mount;
-static struct super_block *cpuset_sb = NULL;
+static struct super_block *cpuset_sb;
 
 /*
  * We have two global cpuset semaphores below.  They can nest.
@@ -602,16 +616,24 @@ static void refresh_mems(void)
        if (current->cpuset_mems_generation != my_cpusets_mem_gen) {
                struct cpuset *cs;
                nodemask_t oldmem = current->mems_allowed;
+               int migrate;
 
                down(&callback_sem);
                task_lock(current);
                cs = current->cpuset;
+               migrate = is_memory_migrate(cs);
                guarantee_online_mems(cs, &current->mems_allowed);
                current->cpuset_mems_generation = cs->mems_generation;
                task_unlock(current);
                up(&callback_sem);
-               if (!nodes_equal(oldmem, current->mems_allowed))
+               if (!nodes_equal(oldmem, current->mems_allowed)) {
                        numa_policy_rebind(&oldmem, &current->mems_allowed);
+                       if (migrate) {
+                               do_migrate_pages(current->mm, &oldmem,
+                                       &current->mems_allowed,
+                                       MPOL_MF_MOVE_ALL);
+                       }
+               }
        }
 }
 
@@ -792,10 +814,23 @@ static int update_nodemask(struct cpuset *cs, char *buf)
        return retval;
 }
 
+/*
+ * Call with manage_sem held.
+ */
+
+static int update_memory_pressure_enabled(struct cpuset *cs, char *buf)
+{
+       if (simple_strtoul(buf, NULL, 10) != 0)
+               cpuset_memory_pressure_enabled = 1;
+       else
+               cpuset_memory_pressure_enabled = 0;
+       return 0;
+}
+
 /*
  * update_flag - read a 0 or a 1 in a file and update associated flag
  * bit:        the bit to update (CS_CPU_EXCLUSIVE, CS_MEM_EXCLUSIVE,
- *                                             CS_NOTIFY_ON_RELEASE)
+ *                             CS_NOTIFY_ON_RELEASE, CS_MEMORY_MIGRATE)
  * cs: the cpuset to update
  * buf:        the buffer where we read the 0 or 1
  *
@@ -833,6 +868,104 @@ static int update_flag(cpuset_flagbits_t bit, struct cpuset *cs, char *buf)
        return 0;
 }
 
+/*
+ * Frequency meter - How fast is some event occuring?
+ *
+ * These routines manage a digitally filtered, constant time based,
+ * event frequency meter.  There are four routines:
+ *   fmeter_init() - initialize a frequency meter.
+ *   fmeter_markevent() - called each time the event happens.
+ *   fmeter_getrate() - returns the recent rate of such events.
+ *   fmeter_update() - internal routine used to update fmeter.
+ *
+ * A common data structure is passed to each of these routines,
+ * which is used to keep track of the state required to manage the
+ * frequency meter and its digital filter.
+ *
+ * The filter works on the number of events marked per unit time.
+ * The filter is single-pole low-pass recursive (IIR).  The time unit
+ * is 1 second.  Arithmetic is done using 32-bit integers scaled to
+ * simulate 3 decimal digits of precision (multiplied by 1000).
+ *
+ * With an FM_COEF of 933, and a time base of 1 second, the filter
+ * has a half-life of 10 seconds, meaning that if the events quit
+ * happening, then the rate returned from the fmeter_getrate()
+ * will be cut in half each 10 seconds, until it converges to zero.
+ *
+ * It is not worth doing a real infinitely recursive filter.  If more
+ * than FM_MAXTICKS ticks have elapsed since the last filter event,
+ * just compute FM_MAXTICKS ticks worth, by which point the level
+ * will be stable.
+ *
+ * Limit the count of unprocessed events to FM_MAXCNT, so as to avoid
+ * arithmetic overflow in the fmeter_update() routine.
+ *
+ * Given the simple 32 bit integer arithmetic used, this meter works
+ * best for reporting rates between one per millisecond (msec) and
+ * one per 32 (approx) seconds.  At constant rates faster than one
+ * per msec it maxes out at values just under 1,000,000.  At constant
+ * rates between one per msec, and one per second it will stabilize
+ * to a value N*1000, where N is the rate of events per second.
+ * At constant rates between one per second and one per 32 seconds,
+ * it will be choppy, moving up on the seconds that have an event,
+ * and then decaying until the next event.  At rates slower than
+ * about one in 32 seconds, it decays all the way back to zero between
+ * each event.
+ */
+
+#define FM_COEF 933            /* coefficient for half-life of 10 secs */
+#define FM_MAXTICKS ((time_t)99) /* useless computing more ticks than this */
+#define FM_MAXCNT 1000000      /* limit cnt to avoid overflow */
+#define FM_SCALE 1000          /* faux fixed point scale */
+
+/* Initialize a frequency meter */
+static void fmeter_init(struct fmeter *fmp)
+{
+       fmp->cnt = 0;
+       fmp->val = 0;
+       fmp->time = 0;
+       spin_lock_init(&fmp->lock);
+}
+
+/* Internal meter update - process cnt events and update value */
+static void fmeter_update(struct fmeter *fmp)
+{
+       time_t now = get_seconds();
+       time_t ticks = now - fmp->time;
+
+       if (ticks == 0)
+               return;
+
+       ticks = min(FM_MAXTICKS, ticks);
+       while (ticks-- > 0)
+               fmp->val = (FM_COEF * fmp->val) / FM_SCALE;
+       fmp->time = now;
+
+       fmp->val += ((FM_SCALE - FM_COEF) * fmp->cnt) / FM_SCALE;
+       fmp->cnt = 0;
+}
+
+/* Process any previous ticks, then bump cnt by one (times scale). */
+static void fmeter_markevent(struct fmeter *fmp)
+{
+       spin_lock(&fmp->lock);
+       fmeter_update(fmp);
+       fmp->cnt = min(FM_MAXCNT, fmp->cnt + FM_SCALE);
+       spin_unlock(&fmp->lock);
+}
+
+/* Process any previous ticks, then return current value. */
+static int fmeter_getrate(struct fmeter *fmp)
+{
+       int val;
+
+       spin_lock(&fmp->lock);
+       fmeter_update(fmp);
+       val = fmp->val;
+       spin_unlock(&fmp->lock);
+       return val;
+}
+
 /*
  * Attack task specified by pid in 'pidbuf' to cpuset 'cs', possibly
  * writing the path of the old cpuset in 'ppathbuf' if it needs to be
@@ -848,6 +981,7 @@ static int attach_task(struct cpuset *cs, char *pidbuf, char **ppathbuf)
        struct task_struct *tsk;
        struct cpuset *oldcs;
        cpumask_t cpus;
+       nodemask_t from, to;
 
        if (sscanf(pidbuf, "%d", &pid) != 1)
                return -EIO;
@@ -893,7 +1027,12 @@ static int attach_task(struct cpuset *cs, char *pidbuf, char **ppathbuf)
        guarantee_online_cpus(cs, &cpus);
        set_cpus_allowed(tsk, cpus);
 
+       from = oldcs->mems_allowed;
+       to = cs->mems_allowed;
+
        up(&callback_sem);
+       if (is_memory_migrate(cs))
+               do_migrate_pages(tsk->mm, &from, &to, MPOL_MF_MOVE_ALL);
        put_task_struct(tsk);
        if (atomic_dec_and_test(&oldcs->count))
                check_for_release(oldcs, ppathbuf);
@@ -905,11 +1044,14 @@ static int attach_task(struct cpuset *cs, char *pidbuf, char **ppathbuf)
 typedef enum {
        FILE_ROOT,
        FILE_DIR,
+       FILE_MEMORY_MIGRATE,
        FILE_CPULIST,
        FILE_MEMLIST,
        FILE_CPU_EXCLUSIVE,
        FILE_MEM_EXCLUSIVE,
        FILE_NOTIFY_ON_RELEASE,
+       FILE_MEMORY_PRESSURE_ENABLED,
+       FILE_MEMORY_PRESSURE,
        FILE_TASKLIST,
 } cpuset_filetype_t;
 
@@ -960,6 +1102,15 @@ static ssize_t cpuset_common_file_write(struct file *file, const char __user *us
        case FILE_NOTIFY_ON_RELEASE:
                retval = update_flag(CS_NOTIFY_ON_RELEASE, cs, buffer);
                break;
+       case FILE_MEMORY_MIGRATE:
+               retval = update_flag(CS_MEMORY_MIGRATE, cs, buffer);
+               break;
+       case FILE_MEMORY_PRESSURE_ENABLED:
+               retval = update_memory_pressure_enabled(cs, buffer);
+               break;
+       case FILE_MEMORY_PRESSURE:
+               retval = -EACCES;
+               break;
        case FILE_TASKLIST:
                retval = attach_task(cs, buffer, &pathbuf);
                break;
@@ -1060,6 +1211,15 @@ static ssize_t cpuset_common_file_read(struct file *file, char __user *buf,
        case FILE_NOTIFY_ON_RELEASE:
                *s++ = notify_on_release(cs) ? '1' : '0';
                break;
+       case FILE_MEMORY_MIGRATE:
+               *s++ = is_memory_migrate(cs) ? '1' : '0';
+               break;
+       case FILE_MEMORY_PRESSURE_ENABLED:
+               *s++ = cpuset_memory_pressure_enabled ? '1' : '0';
+               break;
+       case FILE_MEMORY_PRESSURE:
+               s += sprintf(s, "%d", fmeter_getrate(&cs->fmeter));
+               break;
        default:
                retval = -EINVAL;
                goto out;
@@ -1178,7 +1338,7 @@ static int cpuset_create_file(struct dentry *dentry, int mode)
 
 /*
  *     cpuset_create_dir - create a directory for an object.
- *     cs:     the cpuset we create the directory for.
+ *     cs:     the cpuset we create the directory for.
  *             It must have a valid ->parent field
  *             And we are going to fill its ->dentry field.
  *     name:   The name to give to the cpuset directory. Will be copied.
@@ -1408,6 +1568,21 @@ static struct cftype cft_notify_on_release = {
        .private = FILE_NOTIFY_ON_RELEASE,
 };
 
+static struct cftype cft_memory_migrate = {
+       .name = "memory_migrate",
+       .private = FILE_MEMORY_MIGRATE,
+};
+
+static struct cftype cft_memory_pressure_enabled = {
+       .name = "memory_pressure_enabled",
+       .private = FILE_MEMORY_PRESSURE_ENABLED,
+};
+
+static struct cftype cft_memory_pressure = {
+       .name = "memory_pressure",
+       .private = FILE_MEMORY_PRESSURE,
+};
+
 static int cpuset_populate_dir(struct dentry *cs_dentry)
 {
        int err;
@@ -1422,6 +1597,10 @@ static int cpuset_populate_dir(struct dentry *cs_dentry)
                return err;
        if ((err = cpuset_add_file(cs_dentry, &cft_notify_on_release)) < 0)
                return err;
+       if ((err = cpuset_add_file(cs_dentry, &cft_memory_migrate)) < 0)
+               return err;
+       if ((err = cpuset_add_file(cs_dentry, &cft_memory_pressure)) < 0)
+               return err;
        if ((err = cpuset_add_file(cs_dentry, &cft_tasks)) < 0)
                return err;
        return 0;
@@ -1457,6 +1636,7 @@ static long cpuset_create(struct cpuset *parent, const char *name, int mode)
        INIT_LIST_HEAD(&cs->children);
        atomic_inc(&cpuset_mems_generation);
        cs->mems_generation = atomic_read(&cpuset_mems_generation);
+       fmeter_init(&cs->fmeter);
 
        cs->parent = parent;
 
@@ -1546,6 +1726,7 @@ int __init cpuset_init(void)
        top_cpuset.cpus_allowed = CPU_MASK_ALL;
        top_cpuset.mems_allowed = NODE_MASK_ALL;
 
+       fmeter_init(&top_cpuset.fmeter);
        atomic_inc(&cpuset_mems_generation);
        top_cpuset.mems_generation = atomic_read(&cpuset_mems_generation);
 
@@ -1567,6 +1748,9 @@ int __init cpuset_init(void)
        top_cpuset.dentry = root;
        root->d_inode->i_op = &cpuset_dir_inode_operations;
        err = cpuset_populate_dir(root);
+       /* memory_pressure_enabled is in root cpuset only */
+       if (err == 0)
+               err = cpuset_add_file(root, &cft_memory_pressure_enabled);
 out:
        return err;
 }
@@ -1715,16 +1899,6 @@ done:
                refresh_mems();
 }
 
-/**
- * cpuset_restrict_to_mems_allowed - limit nodes to current mems_allowed
- * @nodes: pointer to a node bitmap that is and-ed with mems_allowed
- */
-void cpuset_restrict_to_mems_allowed(unsigned long *nodes)
-{
-       bitmap_and(nodes, nodes, nodes_addr(current->mems_allowed),
-                                                       MAX_NUMNODES);
-}
-
 /**
  * cpuset_zonelist_valid_mems_allowed - check zonelist vs. curremt mems_allowed
  * @zl: the zonelist to be checked
@@ -1809,11 +1983,12 @@ int cpuset_zone_allowed(struct zone *z, gfp_t gfp_mask)
        if (gfp_mask & __GFP_HARDWALL)  /* If hardwall request, stop here */
                return 0;
 
+       if (current->flags & PF_EXITING) /* Let dying task have memory */
+               return 1;
+
        /* Not hardwall and node outside mems_allowed: scan up cpusets */
        down(&callback_sem);
 
-       if (current->flags & PF_EXITING) /* Let dying task have memory */
-               return 1;
        task_lock(current);
        cs = nearest_exclusive_ancestor(current->cpuset);
        task_unlock(current);
@@ -1865,6 +2040,42 @@ done:
        return overlap;
 }
 
+/*
+ * Collection of memory_pressure is suppressed unless
+ * this flag is enabled by writing "1" to the special
+ * cpuset file 'memory_pressure_enabled' in the root cpuset.
+ */
+
+int cpuset_memory_pressure_enabled __read_mostly;
+
+/**
+ * cpuset_memory_pressure_bump - keep stats of per-cpuset reclaims.
+ *
+ * Keep a running average of the rate of synchronous (direct)
+ * page reclaim efforts initiated by tasks in each cpuset.
+ *
+ * This represents the rate at which some task in the cpuset
+ * ran low on memory on all nodes it was allowed to use, and
+ * had to enter the kernels page reclaim code in an effort to
+ * create more free memory by tossing clean pages or swapping
+ * or writing dirty pages.
+ *
+ * Display to user space in the per-cpuset read-only file
+ * "memory_pressure".  Value displayed is an integer
+ * representing the recent rate of entry into the synchronous
+ * (direct) page reclaim by any task attached to the cpuset.
+ **/
+
+void __cpuset_memory_pressure_bump(void)
+{
+       struct cpuset *cs;
+
+       task_lock(current);
+       cs = current->cpuset;
+       fmeter_markevent(&cs->fmeter);
+       task_unlock(current);
+}
+
 /*
  * proc_cpuset_show()
  *  - Print tasks cpuset path into seq_file.