diff options
author | Arjan van de Ven <arjan@linux.intel.com> | 2006-10-01 02:27:17 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-10-01 03:39:19 -0400 |
commit | 5c87579e65ee4f419b2369407f82326d38b5d2d8 (patch) | |
tree | 3e015ba93eb6eefb7ed4318daf95be0771d596a8 /drivers | |
parent | 130c6b98984a058068ea595c465fba2beb48b9ef (diff) |
[PATCH] maximum latency tracking infrastructure
Add infrastructure to track "maximum allowable latency" for power saving
policies.
The reason for adding this infrastructure is that power management in the
idle loop needs to make a tradeoff between latency and power savings
(deeper power save modes have a longer latency to running code again). The
code that today makes this tradeoff just does a rather simple algorithm;
however this is not good enough: There are devices and use cases where a
lower latency is required than that the higher power saving states provide.
An example would be audio playback, but another example is the ipw2100
wireless driver that right now has a very direct and ugly acpi hook to
disable some higher power states randomly when it gets certain types of
error.
The proposed solution is to have an interface where drivers can
* announce the maximum latency (in microseconds) that they can deal with
* modify this latency
* give up their constraint
and a function where the code that decides on power saving strategy can
query the current global desired maximum.
This patch has a user of each side: on the consumer side, ACPI is patched
to use this, on the producer side the ipw2100 driver is patched.
A generic maximum latency is also registered of 2 timer ticks (more and you
lose accurate time tracking after all).
While the existing users of the patch are x86 specific, the infrastructure
is not. I'd like to ask the arch maintainers of other architectures if the
infrastructure is generic enough for their use (assuming the architecture
has such a tradeoff as concept at all), and the sound/multimedia driver
owners to look at the driver facing API to see if this is something they
can use.
[akpm@osdl.org: cleanups]
Signed-off-by: Arjan van de Ven <arjan@linux.intel.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Acked-by: Jesse Barnes <jesse.barnes@intel.com>
Cc: "Brown, Len" <len.brown@intel.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/acpi/processor_idle.c | 38 | ||||
-rw-r--r-- | drivers/net/wireless/ipw2100.c | 10 |
2 files changed, 44 insertions, 4 deletions
diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 71066066d626..0a395fca843b 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c | |||
@@ -38,6 +38,7 @@ | |||
38 | #include <linux/dmi.h> | 38 | #include <linux/dmi.h> |
39 | #include <linux/moduleparam.h> | 39 | #include <linux/moduleparam.h> |
40 | #include <linux/sched.h> /* need_resched() */ | 40 | #include <linux/sched.h> /* need_resched() */ |
41 | #include <linux/latency.h> | ||
41 | 42 | ||
42 | #include <asm/io.h> | 43 | #include <asm/io.h> |
43 | #include <asm/uaccess.h> | 44 | #include <asm/uaccess.h> |
@@ -453,7 +454,8 @@ static void acpi_processor_idle(void) | |||
453 | */ | 454 | */ |
454 | if (cx->promotion.state && | 455 | if (cx->promotion.state && |
455 | ((cx->promotion.state - pr->power.states) <= max_cstate)) { | 456 | ((cx->promotion.state - pr->power.states) <= max_cstate)) { |
456 | if (sleep_ticks > cx->promotion.threshold.ticks) { | 457 | if (sleep_ticks > cx->promotion.threshold.ticks && |
458 | cx->promotion.state->latency <= system_latency_constraint()) { | ||
457 | cx->promotion.count++; | 459 | cx->promotion.count++; |
458 | cx->demotion.count = 0; | 460 | cx->demotion.count = 0; |
459 | if (cx->promotion.count >= | 461 | if (cx->promotion.count >= |
@@ -494,8 +496,10 @@ static void acpi_processor_idle(void) | |||
494 | end: | 496 | end: |
495 | /* | 497 | /* |
496 | * Demote if current state exceeds max_cstate | 498 | * Demote if current state exceeds max_cstate |
499 | * or if the latency of the current state is unacceptable | ||
497 | */ | 500 | */ |
498 | if ((pr->power.state - pr->power.states) > max_cstate) { | 501 | if ((pr->power.state - pr->power.states) > max_cstate || |
502 | pr->power.state->latency > system_latency_constraint()) { | ||
499 | if (cx->demotion.state) | 503 | if (cx->demotion.state) |
500 | next_state = cx->demotion.state; | 504 | next_state = cx->demotion.state; |
501 | } | 505 | } |
@@ -1009,9 +1013,11 @@ static int acpi_processor_power_seq_show(struct seq_file *seq, void *offset) | |||
1009 | 1013 | ||
1010 | seq_printf(seq, "active state: C%zd\n" | 1014 | seq_printf(seq, "active state: C%zd\n" |
1011 | "max_cstate: C%d\n" | 1015 | "max_cstate: C%d\n" |
1012 | "bus master activity: %08x\n", | 1016 | "bus master activity: %08x\n" |
1017 | "maximum allowed latency: %d usec\n", | ||
1013 | pr->power.state ? pr->power.state - pr->power.states : 0, | 1018 | pr->power.state ? pr->power.state - pr->power.states : 0, |
1014 | max_cstate, (unsigned)pr->power.bm_activity); | 1019 | max_cstate, (unsigned)pr->power.bm_activity, |
1020 | system_latency_constraint()); | ||
1015 | 1021 | ||
1016 | seq_puts(seq, "states:\n"); | 1022 | seq_puts(seq, "states:\n"); |
1017 | 1023 | ||
@@ -1077,6 +1083,28 @@ static const struct file_operations acpi_processor_power_fops = { | |||
1077 | .release = single_release, | 1083 | .release = single_release, |
1078 | }; | 1084 | }; |
1079 | 1085 | ||
1086 | static void smp_callback(void *v) | ||
1087 | { | ||
1088 | /* we already woke the CPU up, nothing more to do */ | ||
1089 | } | ||
1090 | |||
1091 | /* | ||
1092 | * This function gets called when a part of the kernel has a new latency | ||
1093 | * requirement. This means we need to get all processors out of their C-state, | ||
1094 | * and then recalculate a new suitable C-state. Just do a cross-cpu IPI; that | ||
1095 | * wakes them all right up. | ||
1096 | */ | ||
1097 | static int acpi_processor_latency_notify(struct notifier_block *b, | ||
1098 | unsigned long l, void *v) | ||
1099 | { | ||
1100 | smp_call_function(smp_callback, NULL, 0, 1); | ||
1101 | return NOTIFY_OK; | ||
1102 | } | ||
1103 | |||
1104 | static struct notifier_block acpi_processor_latency_notifier = { | ||
1105 | .notifier_call = acpi_processor_latency_notify, | ||
1106 | }; | ||
1107 | |||
1080 | int acpi_processor_power_init(struct acpi_processor *pr, | 1108 | int acpi_processor_power_init(struct acpi_processor *pr, |
1081 | struct acpi_device *device) | 1109 | struct acpi_device *device) |
1082 | { | 1110 | { |
@@ -1093,6 +1121,7 @@ int acpi_processor_power_init(struct acpi_processor *pr, | |||
1093 | "ACPI: processor limited to max C-state %d\n", | 1121 | "ACPI: processor limited to max C-state %d\n", |
1094 | max_cstate); | 1122 | max_cstate); |
1095 | first_run++; | 1123 | first_run++; |
1124 | register_latency_notifier(&acpi_processor_latency_notifier); | ||
1096 | } | 1125 | } |
1097 | 1126 | ||
1098 | if (!pr) | 1127 | if (!pr) |
@@ -1164,6 +1193,7 @@ int acpi_processor_power_exit(struct acpi_processor *pr, | |||
1164 | * copies of pm_idle before proceeding. | 1193 | * copies of pm_idle before proceeding. |
1165 | */ | 1194 | */ |
1166 | cpu_idle_wait(); | 1195 | cpu_idle_wait(); |
1196 | unregister_latency_notifier(&acpi_processor_latency_notifier); | ||
1167 | } | 1197 | } |
1168 | 1198 | ||
1169 | return 0; | 1199 | return 0; |
diff --git a/drivers/net/wireless/ipw2100.c b/drivers/net/wireless/ipw2100.c index 6c5add701a6f..97937809de09 100644 --- a/drivers/net/wireless/ipw2100.c +++ b/drivers/net/wireless/ipw2100.c | |||
@@ -163,6 +163,7 @@ that only one external action is invoked at a time. | |||
163 | #include <linux/firmware.h> | 163 | #include <linux/firmware.h> |
164 | #include <linux/acpi.h> | 164 | #include <linux/acpi.h> |
165 | #include <linux/ctype.h> | 165 | #include <linux/ctype.h> |
166 | #include <linux/latency.h> | ||
166 | 167 | ||
167 | #include "ipw2100.h" | 168 | #include "ipw2100.h" |
168 | 169 | ||
@@ -1697,6 +1698,11 @@ static int ipw2100_up(struct ipw2100_priv *priv, int deferred) | |||
1697 | return 0; | 1698 | return 0; |
1698 | } | 1699 | } |
1699 | 1700 | ||
1701 | /* the ipw2100 hardware really doesn't want power management delays | ||
1702 | * longer than 175usec | ||
1703 | */ | ||
1704 | modify_acceptable_latency("ipw2100", 175); | ||
1705 | |||
1700 | /* If the interrupt is enabled, turn it off... */ | 1706 | /* If the interrupt is enabled, turn it off... */ |
1701 | spin_lock_irqsave(&priv->low_lock, flags); | 1707 | spin_lock_irqsave(&priv->low_lock, flags); |
1702 | ipw2100_disable_interrupts(priv); | 1708 | ipw2100_disable_interrupts(priv); |
@@ -1849,6 +1855,8 @@ static void ipw2100_down(struct ipw2100_priv *priv) | |||
1849 | ipw2100_disable_interrupts(priv); | 1855 | ipw2100_disable_interrupts(priv); |
1850 | spin_unlock_irqrestore(&priv->low_lock, flags); | 1856 | spin_unlock_irqrestore(&priv->low_lock, flags); |
1851 | 1857 | ||
1858 | modify_acceptable_latency("ipw2100", INFINITE_LATENCY); | ||
1859 | |||
1852 | #ifdef ACPI_CSTATE_LIMIT_DEFINED | 1860 | #ifdef ACPI_CSTATE_LIMIT_DEFINED |
1853 | if (priv->config & CFG_C3_DISABLED) { | 1861 | if (priv->config & CFG_C3_DISABLED) { |
1854 | IPW_DEBUG_INFO(": Resetting C3 transitions.\n"); | 1862 | IPW_DEBUG_INFO(": Resetting C3 transitions.\n"); |
@@ -6534,6 +6542,7 @@ static int __init ipw2100_init(void) | |||
6534 | 6542 | ||
6535 | ret = pci_register_driver(&ipw2100_pci_driver); | 6543 | ret = pci_register_driver(&ipw2100_pci_driver); |
6536 | 6544 | ||
6545 | set_acceptable_latency("ipw2100", INFINITE_LATENCY); | ||
6537 | #ifdef CONFIG_IPW2100_DEBUG | 6546 | #ifdef CONFIG_IPW2100_DEBUG |
6538 | ipw2100_debug_level = debug; | 6547 | ipw2100_debug_level = debug; |
6539 | driver_create_file(&ipw2100_pci_driver.driver, | 6548 | driver_create_file(&ipw2100_pci_driver.driver, |
@@ -6554,6 +6563,7 @@ static void __exit ipw2100_exit(void) | |||
6554 | &driver_attr_debug_level); | 6563 | &driver_attr_debug_level); |
6555 | #endif | 6564 | #endif |
6556 | pci_unregister_driver(&ipw2100_pci_driver); | 6565 | pci_unregister_driver(&ipw2100_pci_driver); |
6566 | remove_acceptable_latency("ipw2100"); | ||
6557 | } | 6567 | } |
6558 | 6568 | ||
6559 | module_init(ipw2100_init); | 6569 | module_init(ipw2100_init); |