public inbox for linux-acpi@vger.kernel.org
 help / color / mirror / Atom feed
From: Shaohua Li <shaohua.li-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
To: ACPI-ML <linux-acpi-u79uwXL29TY76Z2rM5mHXA@public.gmane.org>
Cc: Len Brown <len.brown-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>,
	Pallipadi Venkatesh
	<venkatesh.pallipadi-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>,
	Thomas Renninger <trenn-l3A5Bk7waGM@public.gmane.org>,
	Dominik Brodowski <linux-JhLEnvuH02M@public.gmane.org>
Subject: [RFC 2/2] new C-state policy
Date: Tue, 10 Jan 2006 12:12:56 +0800	[thread overview]
Message-ID: <1136866376.5750.29.camel@sli10-desk.sh.intel.com> (raw)

This is a new C-state policy, based on Thomas's one.
http://lkml.org/lkml/2005/4/19/96

Enhancements:
1. it does all policy calculation before going into idle, so other tasks
can be quickly scheduled after idle.
2. it works w/wo dyntick feature.
3. more tunning parameters and hopefully better policy
4. using pm timer for time accounting, so jiffy doesn't impact the
policy.

Now we use below rules:
1. if cpu activity is high, use C1.
2. if average sleeping time is above or below threshold, do
promotion/demotion.
3. bm activity calculation is similar with current one, but slightly
changed to work with dyntick feature.

It's still not very good. Comments/suggestions are highly appreciated.

Thanks,
Shaohua
---

 linux-2.6.15-rc7-root/drivers/acpi/Kconfig                |    7 
 linux-2.6.15-rc7-root/drivers/acpi/Makefile               |    1 
 linux-2.6.15-rc7-root/drivers/acpi/processor_cstate_new.c |  373 ++++++++++++++
 3 files changed, 381 insertions(+)

diff -puN drivers/acpi/Kconfig~new-policy drivers/acpi/Kconfig
--- linux-2.6.15-rc7/drivers/acpi/Kconfig~new-policy	2006-01-09 13:28:20.000000000 +0800
+++ linux-2.6.15-rc7-root/drivers/acpi/Kconfig	2006-01-09 13:28:20.000000000 +0800
@@ -143,6 +143,13 @@ config ACPI_PROCESSOR
 	  support it.  It is required by several flavors of cpufreq
 	  Performance-state drivers.
 
+config ACPI_NEW_CSTATE_POLICY
+	tristate "New C-state policy"
+	depends on ACPI_PROCESSOR && EXPERIMENTAL
+	default m
+	help
+	 New policy for ACPI C-state. It's friendly with dyn-HZ.
+
 config ACPI_HOTPLUG_CPU
 	bool
 	depends on ACPI_PROCESSOR && HOTPLUG_CPU
diff -puN drivers/acpi/Makefile~new-policy drivers/acpi/Makefile
--- linux-2.6.15-rc7/drivers/acpi/Makefile~new-policy	2006-01-09 13:28:20.000000000 +0800
+++ linux-2.6.15-rc7-root/drivers/acpi/Makefile	2006-01-09 13:28:20.000000000 +0800
@@ -47,6 +47,7 @@ obj-$(CONFIG_ACPI_HOTKEY)	+= hotkey.o
 obj-y				+= pci_root.o pci_link.o pci_irq.o pci_bind.o
 obj-$(CONFIG_ACPI_POWER)	+= power.o
 obj-$(CONFIG_ACPI_PROCESSOR)	+= processor.o
+obj-$(CONFIG_ACPI_NEW_CSTATE_POLICY) += processor_cstate_new.o
 obj-$(CONFIG_ACPI_CONTAINER)	+= container.o
 obj-$(CONFIG_ACPI_THERMAL)	+= thermal.o
 obj-$(CONFIG_ACPI_SYSTEM)	+= system.o event.o
diff -puN /dev/null drivers/acpi/processor_cstate_new.c
--- /dev/null	2006-01-05 19:58:28.436640750 +0800
+++ linux-2.6.15-rc7-root/drivers/acpi/processor_cstate_new.c	2006-01-10 11:36:40.000000000 +0800
@@ -0,0 +1,373 @@
+/*
+ *  Copyright (C) 2005  Shaohua Li <shaohua.li-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
+ *			Venkatesh Pallipadi <venkatesh.pallipadi-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or (at
+ *  your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/acpi.h>
+#include <linux/moduleparam.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+#include <acpi/acpi_bus.h>
+#include <acpi/processor.h>
+#include <acpi/processor_cstate_policy.h>
+
+#define ACPI_PROCESSOR_COMPONENT        0x01000000
+#define _COMPONENT              ACPI_PROCESSOR_COMPONENT
+ACPI_MODULE_NAME                ("acpi_processor")
+
+#define MAX_TICK_COUNT 10
+#define PM_TICKS_PER_MS 3579 /* 3.579 ticks per us*/
+
+/* cpu activity threshold */
+static unsigned int cpu_activity = PM_TICKS_PER_MS * 20;
+module_param(cpu_activity, uint, 0644);
+
+/*
+ * bm_history -- bit-mask with a bit per ms of bus-master activity
+ * reduce history for more aggressive entry into C3
+ */
+static unsigned int bm_history = 0xFF;
+module_param(bm_history, uint, 0644);
+static uint bm_activity_measure_time = PM_TICKS_PER_MS * 4; /* 4ms*/
+module_param(bm_activity_measure_time, uint, 0644);
+
+static int param_set_count(const char *val, struct kernel_param *kp)
+{
+	char *endp;
+	uint l;
+
+	if (!val)
+		return -EINVAL;
+
+	l = simple_strtoul(val, &endp, 0);
+	if (endp == val)
+		return -EINVAL;
+	if (l > MAX_TICK_COUNT ) {
+		printk("%s should be <= %d\n",
+			kp->name, MAX_TICK_COUNT);
+		return -EINVAL;
+	}
+	*((uint *)kp->arg) = l;
+	return 0;
+}
+/* promotion to C2 history count */
+static unsigned int promote_c2_count = 4;
+module_param_call(promote_c2_count, param_set_count, param_get_uint,
+	&promote_c2_count, 0644);
+/* promotion to C3 history count */
+static unsigned int promote_c3_count = 10;
+module_param_call(promote_c3_count, param_set_count, param_get_uint,
+	&promote_c3_count, 0644);
+/* demotion history count */
+static unsigned int demote_count = 1;
+module_param_call(demote_count, param_set_count, param_get_uint,
+	&demote_count, 0644);
+
+struct acpi_processor_threshold {
+	u32 time;
+	u32 ticks; /* threshold ticks */
+	uint *count; /* threshold count */
+	uint bm; /* if need bm check? */
+};
+
+struct acpi_processor_sleep {
+	u32 sleep_ticks[MAX_TICK_COUNT];
+	int current_index;
+	struct acpi_processor_threshold prom;
+	struct acpi_processor_threshold demo;
+};
+
+struct acpi_processor_policy_data {
+	u32 active_timestamp;
+	u32 bm_check_timestamp;
+	struct acpi_processor_sleep sleep[ACPI_PROCESSOR_MAX_POWER];
+};
+
+static int calculate_sleep_average(struct acpi_processor_sleep *p, int c)
+{
+	int i = p->current_index - 1, j;
+	u32 avg = 0;
+	for (j = c; j > 0; j --) {
+		if (i < 0)
+			i = MAX_TICK_COUNT - 1;
+		avg += p->sleep_ticks[i];
+		i --;
+	}
+	return avg/c;
+}
+
+static int calculate_bm(struct acpi_processor *pr)
+{
+	u32 bm_status = 0;
+	u32 diff;
+	struct acpi_processor_policy_data *p = pr->power.policy_data;
+
+	diff = ticks_elapsed(p->bm_check_timestamp, read_acpi_pmtimer());
+	diff /= bm_activity_measure_time;
+	if (diff > 32)
+		diff = 32;
+	acpi_get_register(ACPI_BITREG_BUS_MASTER_STATUS,
+			&bm_status, ACPI_MTX_DO_NOT_LOCK);
+	/* no busmaster in last interval */
+	if (!bm_status) {
+		pr->power.bm_activity <<= diff;
+		diff = 0;
+	}
+	while (diff) {
+		/* if we didn't get called, assume there was busmaster activity */
+		diff --;
+		if (diff)
+			pr->power.bm_activity |= 0x1;
+		pr->power.bm_activity <<= 1;
+	}
+
+	if (bm_status) {
+		pr->power.bm_activity++;
+		acpi_set_register(ACPI_BITREG_BUS_MASTER_STATUS,
+				1, ACPI_MTX_DO_NOT_LOCK);
+	}
+
+	p->bm_check_timestamp = read_acpi_pmtimer();
+	return pr->power.bm_activity & bm_history;
+}
+
+static int init_this_policy(struct acpi_processor *pr)
+{
+	struct acpi_processor_policy_data *p;
+	unsigned int i;
+	struct acpi_processor_cx *cx;
+	int lower = 0, higher = 0;
+	int state_is_set = 0;
+
+	ACPI_FUNCTION_TRACE("init_this_policy");
+
+	if (pr->power.policy_data) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+			"Other policies are busy\n"));
+		return_VALUE(-EBUSY);
+	}
+	p = kzalloc(sizeof(*p), GFP_KERNEL);
+	if (!p) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+			"Out of memory\n"));
+		return_VALUE(-ENOMEM);
+	}
+
+	/* startup state */
+	for (i=1; i < ACPI_PROCESSOR_MAX_POWER; i++) {
+		cx = &pr->power.states[i];
+		if (!cx->valid)
+			continue;
+
+		if (!state_is_set)
+			pr->power.state = cx;
+		state_is_set++;
+		break;
+ 	}
+
+	if (!state_is_set) {
+		kfree(p);
+		return_VALUE(-ENODEV);
+	}
+
+	for (i = 1; i < ACPI_PROCESSOR_MAX_POWER; i++) {
+		cx = &pr->power.states[i];
+		if (!cx->valid)
+			continue;
+
+		if (lower) {
+			pr->power.states[i].demo_state = &pr->power.states[lower];
+			p->sleep[i].demo.ticks = cx->latency_ticks;
+			p->sleep[i].demo.count = &demote_count;
+			if (cx->type == ACPI_STATE_C3)
+				p->sleep[i].demo.bm = 1;
+		}
+
+		lower = i;
+	}
+
+	/* promotion */
+	for (i = (ACPI_PROCESSOR_MAX_POWER - 1); i > 0; i--) {
+		cx = &pr->power.states[i];
+		if (!cx->valid)
+			continue;
+
+		if (higher) {
+			pr->power.states[i].prom_state  = &pr->power.states[higher];
+			p->sleep[i].prom.ticks = cx->latency_ticks;
+			if (cx->type < ACPI_STATE_C2)
+				p->sleep[i].prom.count = &promote_c2_count;
+			else
+				p->sleep[i].prom.count = &promote_c3_count;
+			if (pr->power.states[higher].type == ACPI_STATE_C3)
+				p->sleep[i].prom.bm = 1;
+		}
+
+		higher = i;
+	}
+	pr->power.bm_activity = 0;
+	pr->power.policy_data = p;
+	return_VALUE(0);
+}
+
+static int exit_this_policy(struct acpi_processor *pr)
+{
+	struct acpi_processor_policy_data *p;
+
+	ACPI_FUNCTION_TRACE("exit_this_policy");
+
+	if (!pr->power.policy_data) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+			"Policy data is invalid\n"));
+		return_VALUE(-EINVAL);
+	}
+	p = pr->power.policy_data;
+	pr->power.policy_data = NULL;
+	kfree(p);
+
+	pr->power.bm_activity = 0;
+	return_VALUE(0);
+}
+
+static struct acpi_processor_cx* pre_cx_this_policy(struct acpi_processor *pr)
+{
+	u32 active_diff;
+	struct acpi_processor_cx *next_state;
+	struct acpi_processor_policy_data *p;
+	int index;
+	struct acpi_processor_cx *pro_cx, *dem_cx;
+	struct acpi_processor_sleep *sleep_data;
+	int sleep_ticks;
+	int bm_activity = 0;
+
+	p = pr->power.policy_data;
+	index = pr->power.state - pr->power.states;
+	next_state = pr->power.state;
+
+	/* too many cpu activity, uses C1 */
+	active_diff = ticks_elapsed(p->active_timestamp, read_acpi_pmtimer());
+	if (active_diff > cpu_activity) {
+		next_state = &pr->power.states[ACPI_STATE_C1];
+		goto end;
+	}
+
+	/* bm activity? Demote to bm non-sensitive state */
+	if (pr->flags.bm_check) {
+		bm_activity = calculate_bm(pr);
+		/* this works for both bm activity algorithms */
+		while (p->sleep[index].demo.bm && bm_activity) {
+			next_state = pr->power.states[index].demo_state;
+			index = next_state - pr->power.states;
+		}
+		if (next_state != pr->power.state)
+			goto end;
+	}
+
+	local_irq_enable();
+
+	pro_cx = pr->power.states[index].prom_state;
+	dem_cx = pr->power.states[index].demo_state;
+	sleep_data = &p->sleep[index];
+	/* last sleep time */
+	sleep_ticks = sleep_data->sleep_ticks[sleep_data->current_index - 1 < 0 ?
+		MAX_TICK_COUNT -1 : sleep_data->current_index - 1];
+
+	if (pro_cx && ((pro_cx - pr->power.states) <= max_cstate) &&
+		(sleep_ticks > sleep_data->prom.ticks) &&
+		(calculate_sleep_average(sleep_data, *sleep_data->prom.count) >
+			sleep_data->prom.ticks)) {
+			if (!pr->flags.bm_check || !((sleep_data->prom.bm) && bm_activity))
+				next_state = pro_cx;
+			goto end;
+	}
+
+	if (dem_cx && (sleep_ticks < sleep_data->demo.ticks)) {
+		if (calculate_sleep_average(sleep_data, *sleep_data->demo.count)
+			< sleep_data->demo.ticks) {
+			next_state = dem_cx;
+		}
+	}
+
+end:
+	/* Clean previous state's statistics */
+	if (next_state != pr->power.state) {
+		index = pr->power.state - pr->power.states;
+		memset(p->sleep[index].sleep_ticks, 0,
+			sizeof(p->sleep[index].sleep_ticks));
+	}
+
+	local_irq_disable();
+	return next_state;
+}
+
+static struct acpi_processor_cx* post_cx_this_policy(struct acpi_processor *pr,
+	int sleep_ticks)
+{
+	struct acpi_processor_cx *next_state;
+	struct acpi_processor_policy_data *p;
+	int index;
+	struct acpi_processor_sleep *sleep_data;
+
+	next_state = pr->power.state;
+	p = pr->power.policy_data;
+	index = pr->power.state - pr->power.states;
+	sleep_data = &p->sleep[index];
+
+	sleep_data->sleep_ticks[sleep_data->current_index ++] = sleep_ticks;
+	sleep_data->current_index %= MAX_TICK_COUNT;
+
+	p->active_timestamp = read_acpi_pmtimer();
+	return next_state;
+}
+
+static int update_this_policy(struct acpi_processor *pr)
+{
+	exit_this_policy(pr);
+	init_this_policy(pr);
+	return 0;
+}
+
+static struct acpi_processor_cstate_policy this_policy = {
+	.init = init_this_policy,
+	.exit = exit_this_policy,
+	.update = update_this_policy,
+	.pre_cx = pre_cx_this_policy,
+	.post_cx = post_cx_this_policy,
+};
+
+static int __init policy_init(void)
+{
+	return register_acpi_cstate_policy(&this_policy);
+}
+
+static void __exit policy_exit(void)
+{
+	unregister_acpi_cstate_policy(&this_policy);
+}
+module_init(policy_init);
+module_exit(policy_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Shaohua Li <shaohua.li-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>, Venkatesh Pallipadi <venkatesh.pallipadi-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>");
_


-
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

             reply	other threads:[~2006-01-10  4:12 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2006-01-10  4:12 Shaohua Li [this message]
     [not found] ` <1136866376.5750.29.camel-U5EdaLXB8smDugQYiPIPGdh3ngVCH38I@public.gmane.org>
2006-01-10 23:17   ` [RFC 2/2] new C-state policy Dominik Brodowski
     [not found]     ` <20060110231711.GB30356-JwFqNg2GrOVrgjWwlLH9qw@public.gmane.org>
2006-01-11  2:00       ` Shaohua Li
     [not found]         ` <1136944855.5750.61.camel-U5EdaLXB8smDugQYiPIPGdh3ngVCH38I@public.gmane.org>
2006-01-11  8:04           ` Dominik Brodowski
     [not found]             ` <20060111080402.GB599-JwFqNg2GrOVrgjWwlLH9qw@public.gmane.org>
2006-01-11 11:22               ` Thomas Renninger
     [not found]                 ` <43C4EA5D.9040907-l3A5Bk7waGM@public.gmane.org>
2006-01-11 13:51                   ` Dominik Brodowski
     [not found]                     ` <20060111135113.GA11960-JwFqNg2GrOVrgjWwlLH9qw@public.gmane.org>
2006-01-11 14:19                       ` Thomas Renninger
2006-01-12  2:54               ` Shaohua Li

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1136866376.5750.29.camel@sli10-desk.sh.intel.com \
    --to=shaohua.li-ral2jqcrhueavxtiumwx3w@public.gmane.org \
    --cc=len.brown-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org \
    --cc=linux-JhLEnvuH02M@public.gmane.org \
    --cc=linux-acpi-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
    --cc=trenn-l3A5Bk7waGM@public.gmane.org \
    --cc=venkatesh.pallipadi-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox