Skip to content

Commit

Permalink
cpufreq: interactive: fix deadlock
Browse files Browse the repository at this point in the history
up_read() may wakeup some tasks, so do not call up_read() in scheduler,
or it will cause deadlock as below:

Thread #4 5 (Name: cpu3, state: debug-request) (Suspended : Container)
        queued_spin_lock_slowpath() at qspinlock.c:369 0xffffff8008119120
        queued_spin_lock() at qspinlock.h:88 0xffffff8008f0a470
        do_raw_spin_lock() at spinlock.h:180 0xffffff8008f0a470
        __raw_spin_lock() at spinlock_api_smp.h:143 0xffffff8008f0a470
        _raw_spin_lock() at spinlock.c:144 0xffffff8008f0a470
        rq_lock() at sched.h:1,244 0xffffff80080f2f4c
        ttwu_queue() at core.c:2,442 0xffffff80080f2f4c
        try_to_wake_up() at core.c:2,658 0xffffff80080eb998
        wake_up_q() at core.c:450 0xffffff80080eb6a8
        rwsem_wake() at rwsem-xadd.c:703 0xffffff800811a44c
        __up_read() at rwsem.h:107 0xffffff8008118930
        up_read() at rwsem.c:122 0xffffff8008118930
        cpufreq_task_boost() at cpufreq_interactive.c:1,449 0xffffff8008a4bdb4
        enqueue_task_fair() at fair.c:5,285 0xffffff80080f7814
        enqueue_task() at core.c:1,324 0xffffff80080ec15c
        activate_task() at core.c:1,346 0xffffff80080ec15c
        ttwu_activate() at core.c:2,240 0xffffff80080f2fc0
        ttwu_do_activate() at core.c:2,299 0xffffff80080f2fc0
        ttwu_queue() at core.c:2,444 0xffffff80080f2fc0
        try_to_wake_up() at core.c:2,658 0xffffff80080eb998
        wake_up_q() at core.c:450 0xffffff80080eb6a8
        futex_wake() at futex.c:1,636 0xffffff8008159e78
        do_futex() at futex.c:3,714 0xffffff8008158fb0
        __do_sys_futex() at futex.c:3,770 0xffffff800815bd98
        __se_sys_futex() at futex.c:3,738 0xffffff800815bd98
        __arm64_sys_futex() at futex.c:3,738 0xffffff800815bd98
        __invoke_syscall() at syscall.c:36 0xffffff8008098d6c
        invoke_syscall() at syscall.c:48 0xffffff8008098d6c
        el0_svc_common() at syscall.c:117 0xffffff8008098d6c
        el0_svc_handler() at syscall.c:163 0xffffff8008098ccc
        el0_svc() at entry.S:940 0xffffff8008083d08

Fixes: 205ed4e (cpufreq: interactive: introduce boost cpufreq interface for task)

Change-Id: I9607faa5ede3a662e7f2f55da29b08fc328f4d43
Signed-off-by: Liang Chen <[email protected]>
  • Loading branch information
Liang Chen committed Dec 29, 2020
1 parent da3b23f commit d74a177
Showing 1 changed file with 41 additions and 47 deletions.
88 changes: 41 additions & 47 deletions drivers/cpufreq/cpufreq_interactive.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ struct cpufreq_interactive_cpuinfo {
int governor_enabled;
int cpu;
unsigned int task_boost_freq;
unsigned long task_boost_util;
u64 task_boos_endtime;
struct irq_work irq_work;
};
Expand Down Expand Up @@ -1341,32 +1342,6 @@ static void rockchip_cpufreq_policy_init(struct cpufreq_policy *policy)
*tunables = backup_tunables[index];
}

static void task_boost_irq_work(struct irq_work *irq_work)
{
struct cpufreq_interactive_cpuinfo *pcpu;
unsigned long flags[2];

pcpu = container_of(irq_work, struct cpufreq_interactive_cpuinfo, irq_work);
if (!down_read_trylock(&pcpu->enable_sem))
return;

if (!pcpu->governor_enabled || !pcpu->policy)
goto out;

spin_lock_irqsave(&speedchange_cpumask_lock, flags[0]);
spin_lock_irqsave(&pcpu->target_freq_lock, flags[1]);
if (pcpu->target_freq < pcpu->task_boost_freq) {
pcpu->target_freq = pcpu->task_boost_freq;
cpumask_set_cpu(pcpu->cpu, &speedchange_cpumask);
wake_up_process(speedchange_task);
}
spin_unlock_irqrestore(&pcpu->target_freq_lock, flags[1]);
spin_unlock_irqrestore(&speedchange_cpumask_lock, flags[0]);

out:
up_read(&pcpu->enable_sem);
}

static unsigned int get_freq_for_util(struct cpufreq_policy *policy, unsigned long util)
{
struct cpufreq_frequency_table *pos;
Expand All @@ -1385,51 +1360,70 @@ static unsigned int get_freq_for_util(struct cpufreq_policy *policy, unsigned lo
return freq;
}

void cpufreq_task_boost(int cpu, unsigned long util)
static void task_boost_irq_work(struct irq_work *irq_work)
{
struct cpufreq_interactive_tunables *tunables;
struct cpufreq_interactive_cpuinfo *pcpu = &per_cpu(cpuinfo, cpu);
struct cpufreq_policy *policy = pcpu->policy;
unsigned long cap, min_util;

if (!speedchange_task)
return;
struct cpufreq_interactive_cpuinfo *pcpu;
struct cpufreq_policy *policy;
unsigned long flags[2];
u64 now, prev_boos_endtime;
unsigned int boost_freq;

pcpu = container_of(irq_work, struct cpufreq_interactive_cpuinfo, irq_work);
if (!down_read_trylock(&pcpu->enable_sem))
return;

policy = pcpu->policy;
if (!pcpu->governor_enabled || !policy)
goto out;

if (policy->cur == policy->max)
goto out;

if (have_governor_per_policy())
tunables = pcpu->policy->governor_data;
tunables = policy->governor_data;
else
tunables = common_tunables;
if (!tunables)
goto out;

min_util = util + (util >> 2);
cap = capacity_curr_of(cpu);
if (min_util > cap) {
u64 now = ktime_to_us(ktime_get());
u64 prev_boos_endtime = pcpu->task_boos_endtime;
unsigned int boost_freq;

pcpu->task_boos_endtime = now + tunables->timer_rate;
boost_freq = get_freq_for_util(policy, min_util);
if ((now < prev_boos_endtime) && (boost_freq <= pcpu->task_boost_freq))
goto out;
pcpu->task_boost_freq = boost_freq;
now = ktime_to_us(ktime_get());
prev_boos_endtime = pcpu->task_boos_endtime;
pcpu->task_boos_endtime = now + tunables->timer_rate;
boost_freq = get_freq_for_util(policy, pcpu->task_boost_util);
if ((now < prev_boos_endtime) && (boost_freq <= pcpu->task_boost_freq))
goto out;
pcpu->task_boost_freq = boost_freq;

irq_work_queue(&pcpu->irq_work);
spin_lock_irqsave(&speedchange_cpumask_lock, flags[0]);
spin_lock_irqsave(&pcpu->target_freq_lock, flags[1]);
if (pcpu->target_freq < pcpu->task_boost_freq) {
pcpu->target_freq = pcpu->task_boost_freq;
cpumask_set_cpu(pcpu->cpu, &speedchange_cpumask);
wake_up_process(speedchange_task);
}
spin_unlock_irqrestore(&pcpu->target_freq_lock, flags[1]);
spin_unlock_irqrestore(&speedchange_cpumask_lock, flags[0]);

out:
up_read(&pcpu->enable_sem);
}

void cpufreq_task_boost(int cpu, unsigned long util)
{
struct cpufreq_interactive_cpuinfo *pcpu = &per_cpu(cpuinfo, cpu);
unsigned long cap, min_util;

if (!speedchange_task)
return;

min_util = util + (util >> 2);
cap = capacity_curr_of(cpu);
if (min_util > cap) {
pcpu->task_boost_util = min_util;
irq_work_queue(&pcpu->irq_work);
}
}
#endif

static int cpufreq_governor_interactive(struct cpufreq_policy *policy,
Expand Down

0 comments on commit d74a177

Please sign in to comment.