---
 drivers/cpuoffline/Makefile            |    2 +-
 drivers/cpuoffline/governors/Kconfig   |    9 +
 drivers/cpuoffline/governors/Makefile  |    2 +
 drivers/cpuoffline/governors/avgload.c |  255 ++++++++++++++++++++++++++++++++
 4 files changed, 267 insertions(+), 1 deletions(-)
 create mode 100644 drivers/cpuoffline/governors/Kconfig
 create mode 100644 drivers/cpuoffline/governors/Makefile
 create mode 100644 drivers/cpuoffline/governors/avgload.c

diff --git a/drivers/cpuoffline/Makefile b/drivers/cpuoffline/Makefile
index 0b5aa59..ca3277a 100644
--- a/drivers/cpuoffline/Makefile
+++ b/drivers/cpuoffline/Makefile
@@ -1,2 +1,2 @@
 # CPUoffline core
-obj-$(CONFIG_CPU_OFFLINE)              += cpuoffline.o
+obj-$(CONFIG_CPU_OFFLINE)              += cpuoffline.o governors/
diff --git a/drivers/cpuoffline/governors/Kconfig 
b/drivers/cpuoffline/governors/Kconfig
new file mode 100644
index 0000000..5ec9d64
--- /dev/null
+++ b/drivers/cpuoffline/governors/Kconfig
@@ -0,0 +1,9 @@
+config CPU_OFFLINE_GOVERNOR_AVGLOAD
+       bool "CPUoffline Avgload governor"
+       depends on CPU_OFFLINE
+       help
+         A simple governor that puts CPUs online or offline based on
+         CPU load statistics.  It will always leave one CPU online in a
+         partition.
+
+         If in doubt, say N.
diff --git a/drivers/cpuoffline/governors/Makefile 
b/drivers/cpuoffline/governors/Makefile
new file mode 100644
index 0000000..5d990a0
--- /dev/null
+++ b/drivers/cpuoffline/governors/Makefile
@@ -0,0 +1,2 @@
+# CPUoffline governors
+obj-$(CONFIG_CPU_OFFLINE)              += avgload.o
diff --git a/drivers/cpuoffline/governors/avgload.c 
b/drivers/cpuoffline/governors/avgload.c
new file mode 100644
index 0000000..0185d45
--- /dev/null
+++ b/drivers/cpuoffline/governors/avgload.c
@@ -0,0 +1,255 @@
+/*
+ * CPU Offline Average Load governor
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ * Mike Turquette <mturque...@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/types.h>
+#include <linux/cpuoffline.h>
+#include <linux/slab.h>
+#include <linux/hrtimer.h>
+#include <linux/tick.h>
+#include <linux/cpumask.h>
+
+#include <asm/cputime.h>
+
+#define AVGLOAD_DEFAULT_SAMPLING_RATE          1000000
+#define AVGLOAD_DEFAULT_ONLINE_THRESHOLD       80
+#define AVGLOAD_DEFAULT_OFFLINE_THRESHOLD      20
+
+DEFINE_MUTEX(avgload_mutex);
+
+struct avgload_instance {
+       struct cpuoffline_partition *partition;
+       cputime64_t prev_time_wall;
+       struct delayed_work work;
+       struct mutex timer_mutex;
+       int sampling_rate;
+       int online_threshold;
+       int offline_threshold;
+};
+
+struct avgload_cpu_data {
+       cputime64_t prev_time_idle;
+       bool offline;
+};
+
+/* XXX this seems pretty inefficient... */
+static DEFINE_PER_CPU(struct avgload_cpu_data, avgload_data);
+
+static void avgload_do_work(struct avgload_instance *instance)
+{
+       unsigned int cpu;
+       cputime64_t cur_time_wall, cur_time_idle;
+       cputime64_t delta_wall, delta_idle;
+       u64 load = 0;
+       struct cpuoffline_partition *partition = instance->partition;
+       struct cpumask mask;
+
+       if (!instance || !partition) {
+               pr_warning("%s: data does not exist\n", __func__);
+               return;
+       }
+
+       /* find CPUs in this partition that are online */
+       cpumask_and(&mask, cpu_online_mask, partition->cpus);
+
+       /* this should only happen if CPUs are offlined from userspace */
+       if (!cpumask_weight(&mask)) {
+               pr_err("%s: no cpus are online in this partition.  aborting\n",
+                               __func__);
+               return;
+       }
+
+       /* determine load for all online CPUs in the partition */
+       for_each_cpu(cpu, &mask) {
+               cur_time_idle = get_cpu_idle_time_us(cpu, &cur_time_wall);
+
+               delta_wall = cputime64_sub(cur_time_wall,
+                               instance->prev_time_wall);
+               delta_idle = cputime64_sub(cur_time_idle,
+                               per_cpu(avgload_data, cpu).prev_time_idle);
+
+               per_cpu(avgload_data, cpu).prev_time_idle = cur_time_idle;
+
+               /* rollover happens often when bringing a CPU back online */
+               if (!delta_wall || delta_wall < delta_idle)
+                       continue;
+
+               /* aggregate load */
+               delta_idle = 100 * (delta_wall - delta_idle);
+               do_div(delta_idle, delta_wall);
+               load += delta_idle;
+       }
+
+       /* save last timestamp for next iteration */
+       instance->prev_time_wall = cur_time_wall;
+
+       /* average the load */
+       do_div(load, cpumask_weight(&mask));
+
+       /* bring a cpu back online */
+       if (load > instance->online_threshold) {
+               /* which CPUs are offline? */
+               cpumask_complement(&mask, cpu_online_mask);
+
+               /* which offline CPUs are in this partition? */
+               cpumask_and(&mask, &mask, partition->cpus);
+
+               /* which offline CPUs in this partition can hotplug? */
+               cpumask_and(&mask, &mask, cpu_hotpluggable_mask);
+
+               /* bail out if all CPUs are online */
+               if (!cpumask_weight(&mask))
+                       return;
+
+               /* pick a "random" CPU to bring online */
+               cpu = cpumask_any(&mask);
+
+               cpu_up(cpu);
+
+               return;
+       }
+
+       /* take a cpu offline */
+       if (load < instance->offline_threshold) {
+               /* can any of those CPUs hotplug? */
+               cpumask_and(&mask, &mask, cpu_hotpluggable_mask);
+
+               if (!cpumask_weight(&mask))
+                       return;
+
+               /* pick a "random" CPU to go offline */
+               cpu = cpumask_any(&mask);
+
+               cpu_down(cpu);
+
+               return;
+       }
+}
+
+static void do_avgload_timer(struct work_struct *work)
+{
+       int delay;
+       struct avgload_instance *instance =
+               container_of(work, struct avgload_instance, work.work);
+
+       mutex_lock(&instance->timer_mutex);
+
+       /* do the work */
+       avgload_do_work(instance);
+
+       delay = usecs_to_jiffies(instance->sampling_rate);
+       schedule_delayed_work(&instance->work, delay);
+
+       mutex_unlock(&instance->timer_mutex);
+}
+
+static void avgload_timer_init(struct avgload_instance *instance)
+{
+       int delay = usecs_to_jiffies(instance->sampling_rate);
+
+       INIT_DELAYED_WORK_DEFERRABLE(&instance->work, do_avgload_timer);
+       schedule_delayed_work(&instance->work, delay);
+}
+
+static void avgload_timer_exit(struct avgload_instance *instance)
+{
+       cancel_delayed_work_sync(&instance->work);
+}
+
+static int cpuoffline_avgload_start(struct cpuoffline_partition *partition)
+{
+       struct cpuoffline_governor *gov;
+       struct avgload_instance *instance;
+       struct avgload_cpu_data *cpu_data;
+       int cpu;
+
+       instance = kmalloc(sizeof(struct avgload_instance), GFP_KERNEL);
+       if (!instance)
+               return -ENOMEM;
+
+       gov = partition->governor;
+       if (!gov) {
+               pr_err("%s: no governor\n", __func__);
+               return -EINVAL;
+       }
+
+       mutex_lock(&avgload_mutex);
+
+       /* initialize defaults */
+       instance->sampling_rate = AVGLOAD_DEFAULT_SAMPLING_RATE;
+       instance->online_threshold = AVGLOAD_DEFAULT_ONLINE_THRESHOLD;
+       instance->offline_threshold = AVGLOAD_DEFAULT_OFFLINE_THRESHOLD;
+
+       /* remember who we are */
+       instance->partition = partition;
+       partition->private_data = instance;
+
+       /* populate idle times before kicking off the workqueue */
+       for_each_cpu(cpu, partition->cpus) {
+               cpu_data = &per_cpu(avgload_data, cpu);
+
+               cpu_data->prev_time_idle = (cputime_t) get_cpu_idle_time_us(cpu,
+                               &instance->prev_time_wall);
+       }
+
+       /* XXX initialize sysfs stuff here */
+
+       mutex_unlock(&avgload_mutex);
+
+       mutex_init(&instance->timer_mutex);
+       avgload_timer_init(instance);
+
+       return 0;
+}
+
+static int cpuoffline_avgload_stop(struct cpuoffline_partition *partition)
+{
+       struct avgload_instance *instance = partition->private_data;
+
+       if (!instance)
+               return -EINVAL;
+
+       mutex_lock(&instance->timer_mutex);
+       avgload_timer_exit(instance);
+       mutex_unlock(&instance->timer_mutex);
+
+       mutex_lock(&partition->mutex);
+       partition->private_data = NULL;
+       mutex_unlock(&partition->mutex);
+
+       kfree(instance);
+
+       return 0;
+}
+
+struct cpuoffline_governor cpuoffline_governor_avgload = {
+       .name   = "avgload",
+       .owner  = THIS_MODULE,
+       .start  = cpuoffline_avgload_start,
+       .stop   = cpuoffline_avgload_stop,
+};
+
+static int __init cpuoffline_avgload_init(void)
+{
+       pr_notice("%s: registering avgload\n", __func__);
+       return cpuoffline_register_governor(&cpuoffline_governor_avgload);
+}
+
+static void __exit cpuoffline_avgload_exit(void)
+{
+       pr_notice("%s: unregistering avgload\n", __func__);
+       cpuoffline_unregister_governor(&cpuoffline_governor_avgload);
+}
+
+MODULE_AUTHOR("Mike Turquette <mturque...@ti.com>");
+MODULE_DESCRIPTION("cpuoffline_avgload - offline CPUs based on their load");
+MODULE_LICENSE("GPL");
+
+module_init(cpuoffline_avgload_init);
-- 
1.7.4.1


_______________________________________________
linaro-dev mailing list
linaro-dev@lists.linaro.org
http://lists.linaro.org/mailman/listinfo/linaro-dev

Reply via email to