summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Stultz <john.stultz@linaro.org>2011-07-21 14:39:06 -0700
committerJohn Stultz <john.stultz@linaro.org>2011-07-21 14:39:06 -0700
commitd6348a53b9f9d776358b7261dfcdab06ffeafc78 (patch)
treec457cfa36d17817d8e4b6f7e43452cc2e929246e
parent3371370efef73fef66926509b6e84248c6731c97 (diff)
parentd74348cebf7b883a872c5f0cfdf815855e08db5a (diff)
Merge branch 'upstream/android-3.0' into linaro-android-3.0
-rw-r--r--Documentation/cpu-freq/governors.txt8
-rw-r--r--arch/arm/kernel/leds.c27
-rw-r--r--arch/arm/kernel/process.c5
-rw-r--r--arch/x86/include/asm/idle.h7
-rw-r--r--arch/x86/kernel/process_64.c18
-rw-r--r--drivers/cpufreq/Kconfig11
-rw-r--r--drivers/cpufreq/cpufreq_interactive.c263
-rw-r--r--drivers/net/tun.c6
-rw-r--r--drivers/usb/gadget/f_mtp.c148
-rw-r--r--include/linux/cpu.h7
-rw-r--r--kernel/cpu.c20
-rw-r--r--kernel/power/Kconfig7
-rw-r--r--kernel/power/Makefile1
-rw-r--r--kernel/power/suspend_time.c111
-rw-r--r--net/bluetooth/hci_conn.c3
-rw-r--r--net/bluetooth/hidp/core.c18
-rw-r--r--net/bluetooth/hidp/hidp.h1
-rw-r--r--net/bluetooth/l2cap_core.c7
18 files changed, 363 insertions, 305 deletions
diff --git a/Documentation/cpu-freq/governors.txt b/Documentation/cpu-freq/governors.txt
index 16799cede68..51b1cd360c3 100644
--- a/Documentation/cpu-freq/governors.txt
+++ b/Documentation/cpu-freq/governors.txt
@@ -212,13 +212,13 @@ idle. When the cpu comes out of idle, a timer is configured to fire
within 1-2 ticks. If the cpu is very busy between exiting idle and
when the timer fires then we assume the cpu is underpowered and ramp
to MAX speed.
-
+
If the cpu was not sufficiently busy to immediately ramp to MAX speed,
then governor evaluates the cpu load since the last speed adjustment,
-choosing th highest value between that longer-term load or the
+choosing the highest value between that longer-term load or the
short-term load since idle exit to determine the cpu speed to ramp to.
-The tuneable value for this governor are:
+The tuneable values for this governor are:
min_sample_time: The minimum amount of time to spend at the current
frequency before ramping down. This is to ensure that the governor has
@@ -228,6 +228,8 @@ workload. Default is 80000 uS.
go_maxspeed_load: The CPU load at which to ramp to max speed. Default
is 85.
+timer_rate: Sample rate for reevaluating cpu load when the system is
+not idle. Default is 30000 uS.
3. The Governor Interface in the CPUfreq Core
=============================================
diff --git a/arch/arm/kernel/leds.c b/arch/arm/kernel/leds.c
index 0f107dcb034..136e8376a3e 100644
--- a/arch/arm/kernel/leds.c
+++ b/arch/arm/kernel/leds.c
@@ -9,6 +9,8 @@
*/
#include <linux/module.h>
#include <linux/init.h>
+#include <linux/notifier.h>
+#include <linux/cpu.h>
#include <linux/sysdev.h>
#include <linux/syscore_ops.h>
@@ -101,6 +103,25 @@ static struct syscore_ops leds_syscore_ops = {
.resume = leds_resume,
};
+static int leds_idle_notifier(struct notifier_block *nb, unsigned long val,
+ void *data)
+{
+ switch (val) {
+ case IDLE_START:
+ leds_event(led_idle_start);
+ break;
+ case IDLE_END:
+ leds_event(led_idle_end);
+ break;
+ }
+
+ return 0;
+}
+
+static struct notifier_block leds_idle_nb = {
+ .notifier_call = leds_idle_notifier,
+};
+
static int __init leds_init(void)
{
int ret;
@@ -109,8 +130,12 @@ static int __init leds_init(void)
ret = sysdev_register(&leds_device);
if (ret == 0)
ret = sysdev_create_file(&leds_device, &attr_event);
- if (ret == 0)
+
+ if (ret == 0) {
register_syscore_ops(&leds_syscore_ops);
+ idle_notifier_register(&leds_idle_nb);
+ }
+
return ret;
}
diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c
index 34ea86448ea..919de7c84f7 100644
--- a/arch/arm/kernel/process.c
+++ b/arch/arm/kernel/process.c
@@ -32,7 +32,6 @@
#include <linux/hw_breakpoint.h>
#include <asm/cacheflush.h>
-#include <asm/leds.h>
#include <asm/processor.h>
#include <asm/system.h>
#include <asm/thread_notify.h>
@@ -183,7 +182,7 @@ void cpu_idle(void)
/* endless idle loop with no priority at all */
while (1) {
tick_nohz_stop_sched_tick(1);
- leds_event(led_idle_start);
+ idle_notifier_call_chain(IDLE_START);
while (!need_resched()) {
#ifdef CONFIG_HOTPLUG_CPU
if (cpu_is_offline(smp_processor_id()))
@@ -207,7 +206,7 @@ void cpu_idle(void)
local_irq_enable();
}
}
- leds_event(led_idle_end);
+ idle_notifier_call_chain(IDLE_END);
tick_nohz_restart_sched_tick();
preempt_enable_no_resched();
schedule();
diff --git a/arch/x86/include/asm/idle.h b/arch/x86/include/asm/idle.h
index f49253d7571..f1e4268ef3c 100644
--- a/arch/x86/include/asm/idle.h
+++ b/arch/x86/include/asm/idle.h
@@ -1,13 +1,6 @@
#ifndef _ASM_X86_IDLE_H
#define _ASM_X86_IDLE_H
-#define IDLE_START 1
-#define IDLE_END 2
-
-struct notifier_block;
-void idle_notifier_register(struct notifier_block *n);
-void idle_notifier_unregister(struct notifier_block *n);
-
#ifdef CONFIG_X86_64
void enter_idle(void);
void exit_idle(void);
diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c
index ca6f7ab8df3..63c8aedbe5b 100644
--- a/arch/x86/kernel/process_64.c
+++ b/arch/x86/kernel/process_64.c
@@ -56,31 +56,17 @@ asmlinkage extern void ret_from_fork(void);
DEFINE_PER_CPU(unsigned long, old_rsp);
static DEFINE_PER_CPU(unsigned char, is_idle);
-static ATOMIC_NOTIFIER_HEAD(idle_notifier);
-
-void idle_notifier_register(struct notifier_block *n)
-{
- atomic_notifier_chain_register(&idle_notifier, n);
-}
-EXPORT_SYMBOL_GPL(idle_notifier_register);
-
-void idle_notifier_unregister(struct notifier_block *n)
-{
- atomic_notifier_chain_unregister(&idle_notifier, n);
-}
-EXPORT_SYMBOL_GPL(idle_notifier_unregister);
-
void enter_idle(void)
{
percpu_write(is_idle, 1);
- atomic_notifier_call_chain(&idle_notifier, IDLE_START, NULL);
+ idle_notifier_call_chain(IDLE_START);
}
static void __exit_idle(void)
{
if (x86_test_and_clear_bit_percpu(0, is_idle) == 0)
return;
- atomic_notifier_call_chain(&idle_notifier, IDLE_END, NULL);
+ idle_notifier_call_chain(IDLE_END);
}
/* Called from interrupts to signify idle end */
diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig
index 20facb80554..194708850ed 100644
--- a/drivers/cpufreq/Kconfig
+++ b/drivers/cpufreq/Kconfig
@@ -172,6 +172,17 @@ config CPU_FREQ_GOV_INTERACTIVE
'interactive' - This driver adds a dynamic cpufreq policy governor
designed for latency-sensitive workloads.
+ This governor attempts to reduce the latency of clock
+ increases so that the system is more responsive to
+ interactive workloads.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cpufreq_interactive.
+
+ For details, take a look at linux/Documentation/cpu-freq.
+
+ If in doubt, say N.
+
config CPU_FREQ_GOV_CONSERVATIVE
tristate "'conservative' cpufreq governor"
depends on CPU_FREQ
diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c
index bcbb7ac8306..f90d3a5d52e 100644
--- a/drivers/cpufreq/cpufreq_interactive.c
+++ b/drivers/cpufreq/cpufreq_interactive.c
@@ -28,7 +28,6 @@
#include <asm/cputime.h>
-static void (*pm_idle_old)(void);
static atomic_t active_count = ATOMIC_INIT(0);
struct cpufreq_interactive_cpuinfo {
@@ -67,92 +66,11 @@ static unsigned long go_maxspeed_load;
#define DEFAULT_MIN_SAMPLE_TIME 80000;
static unsigned long min_sample_time;
-#define DEBUG 0
-#define BUFSZ 128
-
-#if DEBUG
-#include <linux/proc_fs.h>
-
-struct dbgln {
- int cpu;
- unsigned long jiffy;
- unsigned long run;
- char buf[BUFSZ];
-};
-
-#define NDBGLNS 256
-
-static struct dbgln dbgbuf[NDBGLNS];
-static int dbgbufs;
-static int dbgbufe;
-static struct proc_dir_entry *dbg_proc;
-static spinlock_t dbgpr_lock;
-
-static u64 up_request_time;
-static unsigned int up_max_latency;
-
-static void dbgpr(char *fmt, ...)
-{
- va_list args;
- int n;
- unsigned long flags;
-
- spin_lock_irqsave(&dbgpr_lock, flags);
- n = dbgbufe;
- va_start(args, fmt);
- vsnprintf(dbgbuf[n].buf, BUFSZ, fmt, args);
- va_end(args);
- dbgbuf[n].cpu = smp_processor_id();
- dbgbuf[n].run = nr_running();
- dbgbuf[n].jiffy = jiffies;
-
- if (++dbgbufe >= NDBGLNS)
- dbgbufe = 0;
-
- if (dbgbufe == dbgbufs)
- if (++dbgbufs >= NDBGLNS)
- dbgbufs = 0;
-
- spin_unlock_irqrestore(&dbgpr_lock, flags);
-}
-
-static void dbgdump(void)
-{
- int i, j;
- unsigned long flags;
- static struct dbgln prbuf[NDBGLNS];
-
- spin_lock_irqsave(&dbgpr_lock, flags);
- i = dbgbufs;
- j = dbgbufe;
- memcpy(prbuf, dbgbuf, sizeof(dbgbuf));
- dbgbufs = 0;
- dbgbufe = 0;
- spin_unlock_irqrestore(&dbgpr_lock, flags);
-
- while (i != j)
- {
- printk("%lu %d %lu %s",
- prbuf[i].jiffy, prbuf[i].cpu, prbuf[i].run,
- prbuf[i].buf);
- if (++i == NDBGLNS)
- i = 0;
- }
-}
-
-static int dbg_proc_read(char *buffer, char **start, off_t offset,
- int count, int *peof, void *dat)
-{
- printk("max up_task latency=%uus\n", up_max_latency);
- dbgdump();
- *peof = 1;
- return 0;
-}
-
-
-#else
-#define dbgpr(...) do {} while (0)
-#endif
+/*
+ * The sample rate of the timer used to increase frequency
+ */
+#define DEFAULT_TIMER_RATE 30000;
+static unsigned long timer_rate;
static int cpufreq_governor_interactive(struct cpufreq_policy *policy,
unsigned int event);
@@ -202,16 +120,8 @@ static void cpufreq_interactive_timer(unsigned long data)
smp_wmb();
/* If we raced with cancelling a timer, skip. */
- if (!idle_exit_time) {
- dbgpr("timer %d: no valid idle exit sample\n", (int) data);
+ if (!idle_exit_time)
goto exit;
- }
-
-#if DEBUG
- if ((int) jiffies - (int) pcpu->cpu_timer.expires >= 10)
- dbgpr("timer %d: late by %d ticks\n",
- (int) data, jiffies - pcpu->cpu_timer.expires);
-#endif
delta_idle = (unsigned int) cputime64_sub(now_idle, time_in_idle);
delta_time = (unsigned int) cputime64_sub(pcpu->timer_run_time,
@@ -220,11 +130,8 @@ static void cpufreq_interactive_timer(unsigned long data)
/*
* If timer ran less than 1ms after short-term sample started, retry.
*/
- if (delta_time < 1000) {
- dbgpr("timer %d: time delta %u too short exit=%llu now=%llu\n", (int) data,
- delta_time, idle_exit_time, pcpu->timer_run_time);
+ if (delta_time < 1000)
goto rearm;
- }
if (delta_idle > delta_time)
cpu_load = 0;
@@ -232,7 +139,7 @@ static void cpufreq_interactive_timer(unsigned long data)
cpu_load = 100 * (delta_time - delta_idle) / delta_time;
delta_idle = (unsigned int) cputime64_sub(now_idle,
- pcpu->freq_change_time_in_idle);
+ pcpu->freq_change_time_in_idle);
delta_time = (unsigned int) cputime64_sub(pcpu->timer_run_time,
pcpu->freq_change_time);
@@ -258,32 +165,26 @@ static void cpufreq_interactive_timer(unsigned long data)
if (cpufreq_frequency_table_target(pcpu->policy, pcpu->freq_table,
new_freq, CPUFREQ_RELATION_H,
&index)) {
- dbgpr("timer %d: cpufreq_frequency_table_target error\n", (int) data);
+ pr_warn_once("timer %d: cpufreq_frequency_table_target error\n",
+ (int) data);
goto rearm;
}
new_freq = pcpu->freq_table[index].frequency;
if (pcpu->target_freq == new_freq)
- {
- dbgpr("timer %d: load=%d, already at %d\n", (int) data, cpu_load, new_freq);
goto rearm_if_notmax;
- }
/*
* Do not scale down unless we have been at this frequency for the
* minimum sample time.
*/
if (new_freq < pcpu->target_freq) {
- if (cputime64_sub(pcpu->timer_run_time, pcpu->freq_change_time) <
- min_sample_time) {
- dbgpr("timer %d: load=%d cur=%d tgt=%d not yet\n", (int) data, cpu_load, pcpu->target_freq, new_freq);
+ if (cputime64_sub(pcpu->timer_run_time, pcpu->freq_change_time)
+ < min_sample_time)
goto rearm;
- }
}
- dbgpr("timer %d: load=%d cur=%d tgt=%d queue\n", (int) data, cpu_load, pcpu->target_freq, new_freq);
-
if (new_freq < pcpu->target_freq) {
pcpu->target_freq = new_freq;
spin_lock_irqsave(&down_cpumask_lock, flags);
@@ -292,9 +193,6 @@ static void cpufreq_interactive_timer(unsigned long data)
queue_work(down_wq, &freq_scale_down_work);
} else {
pcpu->target_freq = new_freq;
-#if DEBUG
- up_request_time = ktime_to_us(ktime_get());
-#endif
spin_lock_irqsave(&up_cpumask_lock, flags);
cpumask_set_cpu(data, &up_cpumask);
spin_unlock_irqrestore(&up_cpumask_lock, flags);
@@ -319,34 +217,30 @@ rearm:
if (pcpu->target_freq == pcpu->policy->min) {
smp_rmb();
- if (pcpu->idling) {
- dbgpr("timer %d: cpu idle, don't re-arm\n", (int) data);
+ if (pcpu->idling)
goto exit;
- }
pcpu->timer_idlecancel = 1;
}
pcpu->time_in_idle = get_cpu_idle_time_us(
data, &pcpu->idle_exit_time);
- mod_timer(&pcpu->cpu_timer, jiffies + 2);
- dbgpr("timer %d: set timer for %lu exit=%llu\n", (int) data, pcpu->cpu_timer.expires, pcpu->idle_exit_time);
+ mod_timer(&pcpu->cpu_timer,
+ jiffies + usecs_to_jiffies(timer_rate));
}
exit:
return;
}
-static void cpufreq_interactive_idle(void)
+static void cpufreq_interactive_idle_start(void)
{
struct cpufreq_interactive_cpuinfo *pcpu =
&per_cpu(cpuinfo, smp_processor_id());
int pending;
- if (!pcpu->governor_enabled) {
- pm_idle_old();
+ if (!pcpu->governor_enabled)
return;
- }
pcpu->idling = 1;
smp_wmb();
@@ -366,10 +260,8 @@ static void cpufreq_interactive_idle(void)
pcpu->time_in_idle = get_cpu_idle_time_us(
smp_processor_id(), &pcpu->idle_exit_time);
pcpu->timer_idlecancel = 0;
- mod_timer(&pcpu->cpu_timer, jiffies + 2);
- dbgpr("idle: enter at %d, set timer for %lu exit=%llu\n",
- pcpu->target_freq, pcpu->cpu_timer.expires,
- pcpu->idle_exit_time);
+ mod_timer(&pcpu->cpu_timer,
+ jiffies + usecs_to_jiffies(timer_rate));
}
#endif
} else {
@@ -380,7 +272,6 @@ static void cpufreq_interactive_idle(void)
* CPU didn't go busy; we'll recheck things upon idle exit.
*/
if (pending && pcpu->timer_idlecancel) {
- dbgpr("idle: cancel timer for %lu\n", pcpu->cpu_timer.expires);
del_timer(&pcpu->cpu_timer);
/*
* Ensure last timer run time is after current idle
@@ -392,7 +283,13 @@ static void cpufreq_interactive_idle(void)
}
}
- pm_idle_old();
+}
+
+static void cpufreq_interactive_idle_end(void)
+{
+ struct cpufreq_interactive_cpuinfo *pcpu =
+ &per_cpu(cpuinfo, smp_processor_id());
+
pcpu->idling = 0;
smp_wmb();
@@ -414,14 +311,8 @@ static void cpufreq_interactive_idle(void)
get_cpu_idle_time_us(smp_processor_id(),
&pcpu->idle_exit_time);
pcpu->timer_idlecancel = 0;
- mod_timer(&pcpu->cpu_timer, jiffies + 2);
- dbgpr("idle: exit, set timer for %lu exit=%llu\n", pcpu->cpu_timer.expires, pcpu->idle_exit_time);
-#if DEBUG
- } else if (timer_pending(&pcpu->cpu_timer) == 0 &&
- pcpu->timer_run_time < pcpu->idle_exit_time) {
- dbgpr("idle: timer not run yet: exit=%llu tmrrun=%llu\n",
- pcpu->idle_exit_time, pcpu->timer_run_time);
-#endif
+ mod_timer(&pcpu->cpu_timer,
+ jiffies + usecs_to_jiffies(timer_rate));
}
}
@@ -433,12 +324,6 @@ static int cpufreq_interactive_up_task(void *data)
unsigned long flags;
struct cpufreq_interactive_cpuinfo *pcpu;
-#if DEBUG
- u64 now;
- u64 then;
- unsigned int lat;
-#endif
-
while (1) {
set_current_state(TASK_INTERRUPTIBLE);
spin_lock_irqsave(&up_cpumask_lock, flags);
@@ -455,18 +340,6 @@ static int cpufreq_interactive_up_task(void *data)
set_current_state(TASK_RUNNING);
-#if DEBUG
- then = up_request_time;
- now = ktime_to_us(ktime_get());
-
- if (now > then) {
- lat = ktime_to_us(ktime_get()) - then;
-
- if (lat > up_max_latency)
- up_max_latency = lat;
- }
-#endif
-
tmp_mask = up_cpumask;
cpumask_clear(&up_cpumask);
spin_unlock_irqrestore(&up_cpumask_lock, flags);
@@ -474,11 +347,6 @@ static int cpufreq_interactive_up_task(void *data)
for_each_cpu(cpu, &tmp_mask) {
pcpu = &per_cpu(cpuinfo, cpu);
- if (nr_running() == 1) {
- dbgpr("up %d: tgt=%d nothing else running\n", cpu,
- pcpu->target_freq);
- }
-
smp_rmb();
if (!pcpu->governor_enabled)
@@ -490,7 +358,6 @@ static int cpufreq_interactive_up_task(void *data)
pcpu->freq_change_time_in_idle =
get_cpu_idle_time_us(cpu,
&pcpu->freq_change_time);
- dbgpr("up %d: set tgt=%d (actual=%d)\n", cpu, pcpu->target_freq, pcpu->policy->cur);
}
}
@@ -523,7 +390,6 @@ static void cpufreq_interactive_freq_down(struct work_struct *work)
pcpu->freq_change_time_in_idle =
get_cpu_idle_time_us(cpu,
&pcpu->freq_change_time);
- dbgpr("down %d: set tgt=%d (actual=%d)\n", cpu, pcpu->target_freq, pcpu->policy->cur);
}
}
@@ -536,7 +402,14 @@ static ssize_t show_go_maxspeed_load(struct kobject *kobj,
static ssize_t store_go_maxspeed_load(struct kobject *kobj,
struct attribute *attr, const char *buf, size_t count)
{
- return strict_strtoul(buf, 0, &go_maxspeed_load);
+ int ret;
+ unsigned long val;
+
+ ret = strict_strtoul(buf, 0, &val);
+ if (ret < 0)
+ return ret;
+ go_maxspeed_load = val;
+ return count;
}
static struct global_attr go_maxspeed_load_attr = __ATTR(go_maxspeed_load, 0644,
@@ -551,15 +424,45 @@ static ssize_t show_min_sample_time(struct kobject *kobj,
static ssize_t store_min_sample_time(struct kobject *kobj,
struct attribute *attr, const char *buf, size_t count)
{
- return strict_strtoul(buf, 0, &min_sample_time);
+ int ret;
+ unsigned long val;
+
+ ret = strict_strtoul(buf, 0, &val);
+ if (ret < 0)
+ return ret;
+ min_sample_time = val;
+ return count;
}
static struct global_attr min_sample_time_attr = __ATTR(min_sample_time, 0644,
show_min_sample_time, store_min_sample_time);
+static ssize_t show_timer_rate(struct kobject *kobj,
+ struct attribute *attr, char *buf)
+{
+ return sprintf(buf, "%lu\n", timer_rate);
+}
+
+static ssize_t store_timer_rate(struct kobject *kobj,
+ struct attribute *attr, const char *buf, size_t count)
+{
+ int ret;
+ unsigned long val;
+
+ ret = strict_strtoul(buf, 0, &val);
+ if (ret < 0)
+ return ret;
+ timer_rate = val;
+ return count;
+}
+
+static struct global_attr timer_rate_attr = __ATTR(timer_rate, 0644,
+ show_timer_rate, store_timer_rate);
+
static struct attribute *interactive_attributes[] = {
&go_maxspeed_load_attr.attr,
&min_sample_time_attr.attr,
+ &timer_rate_attr.attr,
NULL,
};
@@ -608,8 +511,6 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy,
if (rc)
return rc;
- pm_idle_old = pm_idle;
- pm_idle = cpufreq_interactive_idle;
break;
case CPUFREQ_GOV_STOP:
@@ -635,7 +536,6 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy,
sysfs_remove_group(cpufreq_global_kobject,
&interactive_attr_group);
- pm_idle = pm_idle_old;
break;
case CPUFREQ_GOV_LIMITS:
@@ -650,6 +550,26 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy,
return 0;
}
+static int cpufreq_interactive_idle_notifier(struct notifier_block *nb,
+ unsigned long val,
+ void *data)
+{
+ switch (val) {
+ case IDLE_START:
+ cpufreq_interactive_idle_start();
+ break;
+ case IDLE_END:
+ cpufreq_interactive_idle_end();
+ break;
+ }
+
+ return 0;
+}
+
+static struct notifier_block cpufreq_interactive_idle_nb = {
+ .notifier_call = cpufreq_interactive_idle_notifier,
+};
+
static int __init cpufreq_interactive_init(void)
{
unsigned int i;
@@ -658,6 +578,7 @@ static int __init cpufreq_interactive_init(void)
go_maxspeed_load = DEFAULT_GO_MAXSPEED_LOAD;
min_sample_time = DEFAULT_MIN_SAMPLE_TIME;
+ timer_rate = DEFAULT_TIMER_RATE;
/* Initalize per-cpu timers */
for_each_possible_cpu(i) {
@@ -679,7 +600,7 @@ static int __init cpufreq_interactive_init(void)
warm cache (probably doesn't matter much). */
down_wq = alloc_workqueue("knteractive_down", 0, 1);
- if (! down_wq)
+ if (!down_wq)
goto err_freeuptask;
INIT_WORK(&freq_scale_down_work,
@@ -688,11 +609,7 @@ static int __init cpufreq_interactive_init(void)
spin_lock_init(&up_cpumask_lock);
spin_lock_init(&down_cpumask_lock);
-#if DEBUG
- spin_lock_init(&dbgpr_lock);
- dbg_proc = create_proc_entry("igov", S_IWUSR | S_IRUGO, NULL);
- dbg_proc->read_proc = dbg_proc_read;
-#endif
+ idle_notifier_register(&cpufreq_interactive_idle_nb);
return cpufreq_register_governor(&cpufreq_gov_interactive);
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 5235f48be1b..67e474ff1a9 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -1238,6 +1238,12 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
int vnet_hdr_sz;
int ret;
+#ifdef CONFIG_ANDROID_PARANOID_NETWORK
+ if (cmd != TUNGETIFF && !capable(CAP_NET_ADMIN)) {
+ return -EPERM;
+ }
+#endif
+
if (cmd == TUNSETIFF || _IOC_TYPE(cmd) == 0x89)
if (copy_from_user(&ifr, argp, ifreq_len))
return -EFAULT;
diff --git a/drivers/usb/gadget/f_mtp.c b/drivers/usb/gadget/f_mtp.c
index a383bc570c6..2829231327d 100644
--- a/drivers/usb/gadget/f_mtp.c
+++ b/drivers/usb/gadget/f_mtp.c
@@ -983,10 +983,12 @@ static struct miscdevice mtp_device = {
static int mtp_ctrlrequest(struct usb_composite_dev *cdev,
const struct usb_ctrlrequest *ctrl)
{
+ struct mtp_dev *dev = _mtp_dev;
int value = -EOPNOTSUPP;
u16 w_index = le16_to_cpu(ctrl->wIndex);
u16 w_value = le16_to_cpu(ctrl->wValue);
u16 w_length = le16_to_cpu(ctrl->wLength);
+ unsigned long flags;
VDBG(cdev, "mtp_ctrlrequest "
"%02x.%02x v%04x i%04x l%u\n",
@@ -1002,7 +1004,61 @@ static int mtp_ctrlrequest(struct usb_composite_dev *cdev,
value = (w_length < sizeof(mtp_os_string)
? w_length : sizeof(mtp_os_string));
memcpy(cdev->req->buf, mtp_os_string, value);
+ } else if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_VENDOR) {
+ /* Handle MTP OS descriptor */
+ DBG(cdev, "vendor request: %d index: %d value: %d length: %d\n",
+ ctrl->bRequest, w_index, w_value, w_length);
+
+ if (ctrl->bRequest == 1
+ && (ctrl->bRequestType & USB_DIR_IN)
+ && (w_index == 4 || w_index == 5)) {
+ value = (w_length < sizeof(mtp_ext_config_desc) ?
+ w_length : sizeof(mtp_ext_config_desc));
+ memcpy(cdev->req->buf, &mtp_ext_config_desc, value);
+ }
+ } else if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS) {
+ DBG(cdev, "class request: %d index: %d value: %d length: %d\n",
+ ctrl->bRequest, w_index, w_value, w_length);
+
+ if (ctrl->bRequest == MTP_REQ_CANCEL && w_index == 0
+ && w_value == 0) {
+ DBG(cdev, "MTP_REQ_CANCEL\n");
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state == STATE_BUSY) {
+ dev->state = STATE_CANCELED;
+ wake_up(&dev->read_wq);
+ wake_up(&dev->write_wq);
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ /* We need to queue a request to read the remaining
+ * bytes, but we don't actually need to look at
+ * the contents.
+ */
+ value = w_length;
+ } else if (ctrl->bRequest == MTP_REQ_GET_DEVICE_STATUS
+ && w_index == 0 && w_value == 0) {
+ struct mtp_device_status *status = cdev->req->buf;
+ status->wLength =
+ __constant_cpu_to_le16(sizeof(*status));
+
+ DBG(cdev, "MTP_REQ_GET_DEVICE_STATUS\n");
+ spin_lock_irqsave(&dev->lock, flags);
+ /* device status is "busy" until we report
+ * the cancelation to userspace
+ */
+ if (dev->state == STATE_CANCELED)
+ status->wCode =
+ __cpu_to_le16(MTP_RESPONSE_DEVICE_BUSY);
+ else
+ status->wCode =
+ __cpu_to_le16(MTP_RESPONSE_OK);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ value = sizeof(*status);
+ }
}
+
/* respond with data transfer or status phase? */
if (value >= 0) {
int rc;
@@ -1068,97 +1124,6 @@ mtp_function_unbind(struct usb_configuration *c, struct usb_function *f)
dev->state = STATE_OFFLINE;
}
-static int mtp_function_setup(struct usb_function *f,
- const struct usb_ctrlrequest *ctrl)
-{
- struct mtp_dev *dev = func_to_mtp(f);
- struct usb_composite_dev *cdev = dev->cdev;
- int value = -EOPNOTSUPP;
- u16 w_index = le16_to_cpu(ctrl->wIndex);
- u16 w_value = le16_to_cpu(ctrl->wValue);
- u16 w_length = le16_to_cpu(ctrl->wLength);
- unsigned long flags;
-
- VDBG(cdev, "mtp_function_setup "
- "%02x.%02x v%04x i%04x l%u\n",
- ctrl->bRequestType, ctrl->bRequest,
- w_value, w_index, w_length);
-
- if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_VENDOR) {
- /* Handle MTP OS descriptor */
- DBG(cdev, "vendor request: %d index: %d value: %d length: %d\n",
- ctrl->bRequest, w_index, w_value, w_length);
-
- if (ctrl->bRequest == 1
- && (ctrl->bRequestType & USB_DIR_IN)
- && (w_index == 4 || w_index == 5)) {
- value = (w_length < sizeof(mtp_ext_config_desc) ?
- w_length : sizeof(mtp_ext_config_desc));
- memcpy(cdev->req->buf, &mtp_ext_config_desc, value);
- }
- }
- if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS) {
- DBG(cdev, "class request: %d index: %d value: %d length: %d\n",
- ctrl->bRequest, w_index, w_value, w_length);
-
- if (ctrl->bRequest == MTP_REQ_CANCEL && w_index == 0
- && w_value == 0) {
- DBG(cdev, "MTP_REQ_CANCEL\n");
-
- spin_lock_irqsave(&dev->lock, flags);
- if (dev->state == STATE_BUSY) {
- dev->state = STATE_CANCELED;
- wake_up(&dev->read_wq);
- wake_up(&dev->write_wq);
- }
- spin_unlock_irqrestore(&dev->lock, flags);
-
- /* We need to queue a request to read the remaining
- * bytes, but we don't actually need to look at
- * the contents.
- */
- value = w_length;
- } else if (ctrl->bRequest == MTP_REQ_GET_DEVICE_STATUS
- && w_index == 0 && w_value == 0) {
- struct mtp_device_status *status = cdev->req->buf;
- status->wLength =
- __constant_cpu_to_le16(sizeof(*status));
-
- DBG(cdev, "MTP_REQ_GET_DEVICE_STATUS\n");
- spin_lock_irqsave(&dev->lock, flags);
- /* device status is "busy" until we report
- * the cancelation to userspace
- */
- if (dev->state == STATE_CANCELED)
- status->wCode =
- __cpu_to_le16(MTP_RESPONSE_DEVICE_BUSY);
- else
- status->wCode =
- __cpu_to_le16(MTP_RESPONSE_OK);
- spin_unlock_irqrestore(&dev->lock, flags);
- value = sizeof(*status);
- }
- }
-
- /* respond with data transfer or status phase? */
- if (value >= 0) {
- int rc;
- cdev->req->zero = value < w_length;
- cdev->req->length = value;
- rc = usb_ep_queue(cdev->gadget->ep0, cdev->req, GFP_ATOMIC);
- if (rc < 0)
- ERROR(cdev, "%s setup response queue error\n", __func__);
- }
-
- if (value == -EOPNOTSUPP)
- VDBG(cdev,
- "unknown class-specific control req "
- "%02x.%02x v%04x i%04x l%u\n",
- ctrl->bRequestType, ctrl->bRequest,
- w_value, w_index, w_length);
- return value;
-}
-
static int mtp_function_set_alt(struct usb_function *f,
unsigned intf, unsigned alt)
{
@@ -1239,7 +1204,6 @@ static int mtp_bind_config(struct usb_configuration *c, bool ptp_config)
}
dev->function.bind = mtp_function_bind;
dev->function.unbind = mtp_function_unbind;
- dev->function.setup = mtp_function_setup;
dev->function.set_alt = mtp_function_set_alt;
dev->function.disable = mtp_function_disable;
diff --git a/include/linux/cpu.h b/include/linux/cpu.h
index 5f09323ee88..97f1ca76b4a 100644
--- a/include/linux/cpu.h
+++ b/include/linux/cpu.h
@@ -174,4 +174,11 @@ static inline int disable_nonboot_cpus(void) { return 0; }
static inline void enable_nonboot_cpus(void) {}
#endif /* !CONFIG_PM_SLEEP_SMP */
+#define IDLE_START 1
+#define IDLE_END 2
+
+void idle_notifier_register(struct notifier_block *n);
+void idle_notifier_unregister(struct notifier_block *n);
+void idle_notifier_call_chain(unsigned long val);
+
#endif /* _LINUX_CPU_H_ */
diff --git a/kernel/cpu.c b/kernel/cpu.c
index 12b7458f23b..404770761a4 100644
--- a/kernel/cpu.c
+++ b/kernel/cpu.c
@@ -594,3 +594,23 @@ void init_cpu_online(const struct cpumask *src)
{
cpumask_copy(to_cpumask(cpu_online_bits), src);
}
+
+static ATOMIC_NOTIFIER_HEAD(idle_notifier);
+
+void idle_notifier_register(struct notifier_block *n)
+{
+ atomic_notifier_chain_register(&idle_notifier, n);
+}
+EXPORT_SYMBOL_GPL(idle_notifier_register);
+
+void idle_notifier_unregister(struct notifier_block *n)
+{
+ atomic_notifier_chain_unregister(&idle_notifier, n);
+}
+EXPORT_SYMBOL_GPL(idle_notifier_unregister);
+
+void idle_notifier_call_chain(unsigned long val)
+{
+ atomic_notifier_call_chain(&idle_notifier, val, NULL);
+}
+EXPORT_SYMBOL_GPL(idle_notifier_call_chain);
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index a70f6c82063..b90fb99fe45 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -294,3 +294,10 @@ config PM_OPP
config PM_RUNTIME_CLK
def_bool y
depends on PM_RUNTIME && HAVE_CLK
+
+config SUSPEND_TIME
+ bool "Log time spent in suspend"
+ ---help---
+ Prints the time spent in suspend in the kernel log, and
+ keeps statistics on the time spent in suspend in
+ /sys/kernel/debug/suspend_time
diff --git a/kernel/power/Makefile b/kernel/power/Makefile
index 493f19d2a29..9b224e16b19 100644
--- a/kernel/power/Makefile
+++ b/kernel/power/Makefile
@@ -13,5 +13,6 @@ obj-$(CONFIG_USER_WAKELOCK) += userwakelock.o
obj-$(CONFIG_EARLYSUSPEND) += earlysuspend.o
obj-$(CONFIG_CONSOLE_EARLYSUSPEND) += consoleearlysuspend.o
obj-$(CONFIG_FB_EARLYSUSPEND) += fbearlysuspend.o
+obj-$(CONFIG_SUSPEND_TIME) += suspend_time.o
obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o
diff --git a/kernel/power/suspend_time.c b/kernel/power/suspend_time.c
new file mode 100644
index 00000000000..d2a65da9f22
--- /dev/null
+++ b/kernel/power/suspend_time.c
@@ -0,0 +1,111 @@
+/*
+ * debugfs file to track time spent in suspend
+ *
+ * Copyright (c) 2011, Google, Inc.
+ *
+ * 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.
+ */
+
+#include <linux/debugfs.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/seq_file.h>
+#include <linux/syscore_ops.h>
+#include <linux/time.h>
+
+static struct timespec suspend_time_before;
+static unsigned int time_in_suspend_bins[32];
+
+#ifdef CONFIG_DEBUG_FS
+static int suspend_time_debug_show(struct seq_file *s, void *data)
+{
+ int bin;
+ seq_printf(s, "time (secs) count\n");
+ seq_printf(s, "------------------\n");
+ for (bin = 0; bin < 32; bin++) {
+ if (time_in_suspend_bins[bin] == 0)
+ continue;
+ seq_printf(s, "%4d - %4d %4u\n",
+ bin ? 1 << (bin - 1) : 0, 1 << bin,
+ time_in_suspend_bins[bin]);
+ }
+ return 0;
+}
+
+static int suspend_time_debug_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, suspend_time_debug_show, NULL);
+}
+
+static const struct file_operations suspend_time_debug_fops = {
+ .open = suspend_time_debug_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int __init suspend_time_debug_init(void)
+{
+ struct dentry *d;
+
+ d = debugfs_create_file("suspend_time", 0755, NULL, NULL,
+ &suspend_time_debug_fops);
+ if (!d) {
+ pr_err("Failed to create suspend_time debug file\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+late_initcall(suspend_time_debug_init);
+#endif
+
+static int suspend_time_syscore_suspend(void)
+{
+ read_persistent_clock(&suspend_time_before);
+
+ return 0;
+}
+
+static void suspend_time_syscore_resume(void)
+{
+ struct timespec after;
+
+ read_persistent_clock(&after);
+
+ after = timespec_sub(after, suspend_time_before);
+
+ time_in_suspend_bins[fls(after.tv_sec)]++;
+
+ pr_info("Suspended for %lu.%03lu seconds\n", after.tv_sec,
+ after.tv_nsec / NSEC_PER_MSEC);
+}
+
+static struct syscore_ops suspend_time_syscore_ops = {
+ .suspend = suspend_time_syscore_suspend,
+ .resume = suspend_time_syscore_resume,
+};
+
+static int suspend_time_syscore_init(void)
+{
+ register_syscore_ops(&suspend_time_syscore_ops);
+
+ return 0;
+}
+
+static void suspend_time_syscore_exit(void)
+{
+ unregister_syscore_ops(&suspend_time_syscore_ops);
+}
+module_init(suspend_time_syscore_init);
+module_exit(suspend_time_syscore_exit);
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index b7ff6e3ee25..5ec0db42316 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -453,6 +453,9 @@ int hci_conn_del(struct hci_conn *conn)
hci_dev_put(hdev);
+ if (conn->handle == 0)
+ kfree(conn);
+
return 0;
}
diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c
index c405a954a60..43b4c2deb7c 100644
--- a/net/bluetooth/hidp/core.c
+++ b/net/bluetooth/hidp/core.c
@@ -464,7 +464,8 @@ static void hidp_idle_timeout(unsigned long arg)
{
struct hidp_session *session = (struct hidp_session *) arg;
- kthread_stop(session->task);
+ atomic_inc(&session->terminate);
+ wake_up_process(session->task);
}
static void hidp_set_timer(struct hidp_session *session)
@@ -535,7 +536,8 @@ static void hidp_process_hid_control(struct hidp_session *session,
skb_queue_purge(&session->ctrl_transmit);
skb_queue_purge(&session->intr_transmit);
- kthread_stop(session->task);
+ atomic_inc(&session->terminate);
+ wake_up_process(current);
}
}
@@ -706,9 +708,8 @@ static int hidp_session(void *arg)
add_wait_queue(sk_sleep(intr_sk), &intr_wait);
session->waiting_for_startup = 0;
wake_up_interruptible(&session->startup_queue);
- while (!kthread_should_stop()) {
- set_current_state(TASK_INTERRUPTIBLE);
-
+ set_current_state(TASK_INTERRUPTIBLE);
+ while (!atomic_read(&session->terminate)) {
if (ctrl_sk->sk_state != BT_CONNECTED ||
intr_sk->sk_state != BT_CONNECTED)
break;
@@ -726,6 +727,7 @@ static int hidp_session(void *arg)
hidp_process_transmit(session);
schedule();
+ set_current_state(TASK_INTERRUPTIBLE);
}
set_current_state(TASK_RUNNING);
remove_wait_queue(sk_sleep(intr_sk), &intr_wait);
@@ -1060,7 +1062,8 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock,
err_add_device:
hid_destroy_device(session->hid);
session->hid = NULL;
- kthread_stop(session->task);
+ atomic_inc(&session->terminate);
+ wake_up_process(session->task);
unlink:
hidp_del_timer(session);
@@ -1111,7 +1114,8 @@ int hidp_del_connection(struct hidp_conndel_req *req)
skb_queue_purge(&session->ctrl_transmit);
skb_queue_purge(&session->intr_transmit);
- kthread_stop(session->task);
+ atomic_inc(&session->terminate);
+ wake_up_process(session->task);
}
} else
err = -ENOENT;
diff --git a/net/bluetooth/hidp/hidp.h b/net/bluetooth/hidp/hidp.h
index 19e95004b28..af1bcc823f2 100644
--- a/net/bluetooth/hidp/hidp.h
+++ b/net/bluetooth/hidp/hidp.h
@@ -142,6 +142,7 @@ struct hidp_session {
uint ctrl_mtu;
uint intr_mtu;
+ atomic_t terminate;
struct task_struct *task;
unsigned char keys[8];
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 9c7bccfcc72..ed602042a95 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -2524,7 +2524,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
sk = chan->sk;
- if (chan->state != BT_CONFIG) {
+ if (sk->sk_state != BT_CONFIG && sk->sk_state != BT_CONNECT2) {
struct l2cap_cmd_rej rej;
rej.reason = cpu_to_le16(0x0002);
@@ -2535,7 +2535,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
/* Reject if config buffer is too small. */
len = cmd_len - sizeof(*req);
- if (chan->conf_len + len > sizeof(chan->conf_req)) {
+ if (len < 0 || chan->conf_len + len > sizeof(chan->conf_req)) {
l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP,
l2cap_build_conf_rsp(chan, rsp,
L2CAP_CONF_REJECT, flags), rsp);
@@ -4147,7 +4147,8 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
struct sock *parent = bt_sk(sk)->parent;
res = L2CAP_CR_PEND;
stat = L2CAP_CS_AUTHOR_PEND;
- parent->sk_data_ready(parent, 0);
+ if (parent)
+ parent->sk_data_ready(parent, 0);
} else {
l2cap_state_change(chan, BT_CONFIG);
res = L2CAP_CR_SUCCESS;