From patchwork Fri Aug 19 19:38:18 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mike Turquette X-Patchwork-Id: 3582 Return-Path: X-Original-To: patchwork@peony.canonical.com Delivered-To: patchwork@peony.canonical.com Received: from fiordland.canonical.com (fiordland.canonical.com [91.189.94.145]) by peony.canonical.com (Postfix) with ESMTP id F205423F26 for ; Fri, 19 Aug 2011 19:40:01 +0000 (UTC) Received: from mail-gy0-f180.google.com (mail-gy0-f180.google.com [209.85.160.180]) by fiordland.canonical.com (Postfix) with ESMTP id AA194A1800B for ; Fri, 19 Aug 2011 19:40:01 +0000 (UTC) Received: by mail-gy0-f180.google.com with SMTP id 15so3520029gyc.11 for ; Fri, 19 Aug 2011 12:40:01 -0700 (PDT) Received: by 10.151.21.21 with SMTP id y21mr98522ybi.447.1313782801422; Fri, 19 Aug 2011 12:40:01 -0700 (PDT) X-Forwarded-To: linaro-patchwork@canonical.com X-Forwarded-For: patch@linaro.org linaro-patchwork@canonical.com Delivered-To: patches@linaro.org Received: by 10.150.157.17 with SMTP id f17cs113592ybe; Fri, 19 Aug 2011 12:40:01 -0700 (PDT) Received: by 10.142.50.19 with SMTP id x19mr46844wfx.340.1313782800541; Fri, 19 Aug 2011 12:40:00 -0700 (PDT) Received: from na3sys009aog111.obsmtp.com ([74.125.149.205]) by mx.google.com with SMTP id e8si3091066pbh.49.2011.08.19.12.39.59 (version=TLSv1/SSLv3 cipher=OTHER); Fri, 19 Aug 2011 12:40:00 -0700 (PDT) Received-SPF: pass (google.com: domain of mturquette@ti.com designates 74.125.149.205 as permitted sender) client-ip=74.125.149.205; Authentication-Results: mx.google.com; spf=pass (google.com: domain of mturquette@ti.com designates 74.125.149.205 as permitted sender) smtp.mail=mturquette@ti.com Received: from mail-gy0-f172.google.com ([209.85.160.172]) (using TLSv1) by na3sys009aob111.postini.com ([74.125.148.12]) with SMTP ID DSNKTk68D2ySr5CkBUVyMR5RZvTdMlSSkpw2@postini.com; Fri, 19 Aug 2011 12:40:00 PDT Received: by mail-gy0-f172.google.com with SMTP id 3so2831559gyf.31 for ; Fri, 19 Aug 2011 12:39:59 -0700 (PDT) Received: by 10.236.182.135 with SMTP id o7mr899368yhm.48.1313782799427; Fri, 19 Aug 2011 12:39:59 -0700 (PDT) Received: from localhost.localdomain (dragon.ti.com [192.94.94.33]) by mx.google.com with ESMTPS id e21sm1179802yhn.35.2011.08.19.12.39.57 (version=TLSv1/SSLv3 cipher=OTHER); Fri, 19 Aug 2011 12:39:58 -0700 (PDT) From: Mike Turquette To: linaro-dev@lists.linaro.org, patches@linaro.org Cc: Mike Turquette Subject: [PATCH 5/6] governors Date: Fri, 19 Aug 2011 12:38:18 -0700 Message-Id: <1313782699-29411-6-git-send-email-mturquette@ti.com> X-Mailer: git-send-email 1.7.4.1 In-Reply-To: <1313782699-29411-1-git-send-email-mturquette@ti.com> References: <1313782699-29411-1-git-send-email-mturquette@ti.com> --- 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 + * + * 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 +#include +#include +#include +#include +#include + +#include + +#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 "); +MODULE_DESCRIPTION("cpuoffline_avgload - offline CPUs based on their load"); +MODULE_LICENSE("GPL"); + +module_init(cpuoffline_avgload_init);