Loading drivers/cpufreq/Makefile +2 −0 Original line number Diff line number Diff line Loading @@ -10,7 +10,9 @@ obj-$(CONFIG_CPU_FREQ_GOV_USERSPACE) += cpufreq_userspace.o obj-$(CONFIG_CPU_FREQ_GOV_ONDEMAND) += cpufreq_ondemand.o obj-$(CONFIG_CPU_FREQ_GOV_CONSERVATIVE) += cpufreq_conservative.o obj-$(CONFIG_CPU_FREQ_GOV_INTERACTIVE) += cpufreq_interactive.o ifdef CONFIG_SCHED_FREQ_INPUT obj-$(CONFIG_CPU_FREQ_GOV_INTERACTIVE) += cpufreq_interactive_pro.o endif obj-$(CONFIG_CPU_FREQ_GOV_COMMON) += cpufreq_governor.o obj-$(CONFIG_CPU_BOOST) += cpu-boost.o Loading drivers/cpufreq/cpufreq_interactive_pro.c +113 −15 Original line number Diff line number Diff line Loading @@ -63,6 +63,7 @@ static struct task_struct *speedchange_task; static cpumask_t speedchange_cpumask; static spinlock_t speedchange_cpumask_lock; static struct mutex gov_lock; static int active_count; /* Target load. Lower values result in higher CPU speeds. */ #define DEFAULT_TARGET_LOAD 90 Loading Loading @@ -122,6 +123,8 @@ static DEFINE_PER_CPU(struct cpufreq_interactive_tunables *, cached_tunables); static struct attribute_group *get_sysfs_attr(void); static bool use_sched_hint; /* Round to starting jiffy of next evaluation window */ static u64 round_to_nw_start(u64 jif, struct cpufreq_interactive_tunables *tunables) Loading @@ -132,9 +135,34 @@ static u64 round_to_nw_start(u64 jif, return (jif + 1) * step; } static void cpufreq_interactive_timer_resched( struct cpufreq_interactive_cpuinfo *pcpu) static inline int set_window_helper( struct cpufreq_interactive_tunables *tunables) { return sched_set_window(round_to_nw_start(get_jiffies_64(), tunables), usecs_to_jiffies(tunables->timer_rate)); } /* * Schedule the timer to fire immediately. This is a helper function for * atomic notification from scheduler so that CPU load can be re-evaluated * immediately. Since we are just re-evaluating previous window's load, we * should not push back slack timer. */ static void cpufreq_interactive_timer_resched_now(unsigned long cpu) { unsigned long flags; struct cpufreq_interactive_cpuinfo *pcpu = &per_cpu(cpuinfo, cpu); spin_lock_irqsave(&pcpu->load_lock, flags); del_timer(&pcpu->cpu_timer); pcpu->cpu_timer.expires = jiffies; add_timer_on(&pcpu->cpu_timer, cpu); spin_unlock_irqrestore(&pcpu->load_lock, flags); } static void cpufreq_interactive_timer_resched(unsigned long cpu) { struct cpufreq_interactive_cpuinfo *pcpu = &per_cpu(cpuinfo, cpu); struct cpufreq_interactive_tunables *tunables = pcpu->policy->governor_data; u64 expires; Loading @@ -148,12 +176,16 @@ static void cpufreq_interactive_timer_resched( pcpu->cputime_speedadj = 0; pcpu->cputime_speedadj_timestamp = pcpu->time_in_idle_timestamp; expires = round_to_nw_start(pcpu->last_evaluated_jiffy, tunables); mod_timer_pinned(&pcpu->cpu_timer, expires); del_timer(&pcpu->cpu_timer); pcpu->cpu_timer.expires = expires; add_timer_on(&pcpu->cpu_timer, cpu); if (tunables->timer_slack_val >= 0 && pcpu->target_freq > pcpu->policy->min) { expires += usecs_to_jiffies(tunables->timer_slack_val); mod_timer_pinned(&pcpu->cpu_slack_timer, expires); del_timer(&pcpu->cpu_slack_timer); pcpu->cpu_slack_timer.expires = expires; add_timer_on(&pcpu->cpu_slack_timer, cpu); } spin_unlock_irqrestore(&pcpu->load_lock, flags); Loading Loading @@ -364,17 +396,39 @@ static void cpufreq_interactive_timer(unsigned long data) goto exit; spin_lock_irqsave(&pcpu->load_lock, flags); pcpu->last_evaluated_jiffy = get_jiffies_64(); now = update_load(data); delta_time = (unsigned int)(now - pcpu->cputime_speedadj_timestamp); if (use_sched_hint) { /* * Unlock early to avoid deadlock. * * cpufreq_interactive_timer_resched_now() is called * in thread migration notification which already holds * rq lock. Then it locks load_lock to avoid racing with * cpufreq_interactive_timer_resched/start(). * sched_get_busy() will also acquire rq lock. Thus we * can't hold load_lock when calling sched_get_busy(). * * load_lock used in this function protects time * and load information. These stats are not used when * scheduler hint is available. Thus unlocking load_lock * early is perfectly OK. */ spin_unlock_irqrestore(&pcpu->load_lock, flags); cputime_speedadj = (u64)sched_get_busy(data) * pcpu->policy->cpuinfo.max_freq; do_div(cputime_speedadj, tunables->timer_rate); } else { delta_time = (unsigned int) (now - pcpu->cputime_speedadj_timestamp); cputime_speedadj = pcpu->cputime_speedadj; pcpu->last_evaluated_jiffy = get_jiffies_64(); spin_unlock_irqrestore(&pcpu->load_lock, flags); if (WARN_ON_ONCE(!delta_time)) goto rearm; do_div(cputime_speedadj, delta_time); } spin_lock_irqsave(&pcpu->target_freq_lock, flags); do_div(cputime_speedadj, delta_time); loadadjfreq = (unsigned int)cputime_speedadj * 100; cpu_load = loadadjfreq / pcpu->target_freq; boosted = tunables->boost_val || now < tunables->boostpulse_endtime; Loading Loading @@ -470,7 +524,7 @@ rearm_if_notmax: rearm: if (!timer_pending(&pcpu->cpu_timer)) cpufreq_interactive_timer_resched(pcpu); cpufreq_interactive_timer_resched(data); exit: up_read(&pcpu->enable_sem); Loading Loading @@ -502,7 +556,7 @@ static void cpufreq_interactive_idle_start(void) * the CPUFreq driver. */ if (!pending) cpufreq_interactive_timer_resched(pcpu); cpufreq_interactive_timer_resched(smp_processor_id()); } up_read(&pcpu->enable_sem); Loading @@ -522,7 +576,7 @@ static void cpufreq_interactive_idle_end(void) /* Arm the timer for 1-2 ticks later if not already. */ if (!timer_pending(&pcpu->cpu_timer)) { cpufreq_interactive_timer_resched(pcpu); cpufreq_interactive_timer_resched(smp_processor_id()); } else if (time_after_eq(jiffies, pcpu->cpu_timer.expires)) { del_timer(&pcpu->cpu_timer); del_timer(&pcpu->cpu_slack_timer); Loading Loading @@ -633,6 +687,28 @@ static void cpufreq_interactive_boost(void) wake_up_process(speedchange_task); } static int load_change_callback(struct notifier_block *nb, unsigned long val, void *data) { unsigned long cpu = (unsigned long) data; struct cpufreq_interactive_cpuinfo *pcpu = &per_cpu(cpuinfo, cpu); /* * We can't acquire enable_sem here. However, this doesn't matter * because timer function will grab it and check if governor is * enabled before doing anything. Therefore the execution is * still correct. */ if (pcpu->governor_enabled) cpufreq_interactive_timer_resched_now(cpu); return 0; } static struct notifier_block load_notifier_block = { .notifier_call = load_change_callback, }; static int cpufreq_interactive_notifier( struct notifier_block *nb, unsigned long val, void *data) { Loading Loading @@ -876,6 +952,7 @@ static ssize_t store_timer_rate(struct cpufreq_interactive_tunables *tunables, if (ret < 0) return ret; tunables->timer_rate = val; set_window_helper(tunables); return count; } Loading Loading @@ -980,6 +1057,7 @@ static ssize_t store_io_is_busy(struct cpufreq_interactive_tunables *tunables, if (ret < 0) return ret; tunables->io_is_busy = val; sched_set_io_is_busy(val); return count; } Loading Loading @@ -1245,6 +1323,18 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, CPUFREQ_TRANSITION_NOTIFIER); } if (!active_count) { rc = set_window_helper(tunables); if (!rc) { use_sched_hint = true; sched_set_io_is_busy(tunables->io_is_busy); atomic_notifier_chain_register( &load_alert_notifier_head, &load_notifier_block); } } active_count++; break; case CPUFREQ_GOV_POLICY_EXIT: Loading @@ -1264,6 +1354,14 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, } policy->governor_data = NULL; active_count--; if (!active_count && use_sched_hint) { atomic_notifier_chain_unregister( &load_alert_notifier_head, &load_notifier_block); use_sched_hint = false; } break; case CPUFREQ_GOV_START: Loading Loading
drivers/cpufreq/Makefile +2 −0 Original line number Diff line number Diff line Loading @@ -10,7 +10,9 @@ obj-$(CONFIG_CPU_FREQ_GOV_USERSPACE) += cpufreq_userspace.o obj-$(CONFIG_CPU_FREQ_GOV_ONDEMAND) += cpufreq_ondemand.o obj-$(CONFIG_CPU_FREQ_GOV_CONSERVATIVE) += cpufreq_conservative.o obj-$(CONFIG_CPU_FREQ_GOV_INTERACTIVE) += cpufreq_interactive.o ifdef CONFIG_SCHED_FREQ_INPUT obj-$(CONFIG_CPU_FREQ_GOV_INTERACTIVE) += cpufreq_interactive_pro.o endif obj-$(CONFIG_CPU_FREQ_GOV_COMMON) += cpufreq_governor.o obj-$(CONFIG_CPU_BOOST) += cpu-boost.o Loading
drivers/cpufreq/cpufreq_interactive_pro.c +113 −15 Original line number Diff line number Diff line Loading @@ -63,6 +63,7 @@ static struct task_struct *speedchange_task; static cpumask_t speedchange_cpumask; static spinlock_t speedchange_cpumask_lock; static struct mutex gov_lock; static int active_count; /* Target load. Lower values result in higher CPU speeds. */ #define DEFAULT_TARGET_LOAD 90 Loading Loading @@ -122,6 +123,8 @@ static DEFINE_PER_CPU(struct cpufreq_interactive_tunables *, cached_tunables); static struct attribute_group *get_sysfs_attr(void); static bool use_sched_hint; /* Round to starting jiffy of next evaluation window */ static u64 round_to_nw_start(u64 jif, struct cpufreq_interactive_tunables *tunables) Loading @@ -132,9 +135,34 @@ static u64 round_to_nw_start(u64 jif, return (jif + 1) * step; } static void cpufreq_interactive_timer_resched( struct cpufreq_interactive_cpuinfo *pcpu) static inline int set_window_helper( struct cpufreq_interactive_tunables *tunables) { return sched_set_window(round_to_nw_start(get_jiffies_64(), tunables), usecs_to_jiffies(tunables->timer_rate)); } /* * Schedule the timer to fire immediately. This is a helper function for * atomic notification from scheduler so that CPU load can be re-evaluated * immediately. Since we are just re-evaluating previous window's load, we * should not push back slack timer. */ static void cpufreq_interactive_timer_resched_now(unsigned long cpu) { unsigned long flags; struct cpufreq_interactive_cpuinfo *pcpu = &per_cpu(cpuinfo, cpu); spin_lock_irqsave(&pcpu->load_lock, flags); del_timer(&pcpu->cpu_timer); pcpu->cpu_timer.expires = jiffies; add_timer_on(&pcpu->cpu_timer, cpu); spin_unlock_irqrestore(&pcpu->load_lock, flags); } static void cpufreq_interactive_timer_resched(unsigned long cpu) { struct cpufreq_interactive_cpuinfo *pcpu = &per_cpu(cpuinfo, cpu); struct cpufreq_interactive_tunables *tunables = pcpu->policy->governor_data; u64 expires; Loading @@ -148,12 +176,16 @@ static void cpufreq_interactive_timer_resched( pcpu->cputime_speedadj = 0; pcpu->cputime_speedadj_timestamp = pcpu->time_in_idle_timestamp; expires = round_to_nw_start(pcpu->last_evaluated_jiffy, tunables); mod_timer_pinned(&pcpu->cpu_timer, expires); del_timer(&pcpu->cpu_timer); pcpu->cpu_timer.expires = expires; add_timer_on(&pcpu->cpu_timer, cpu); if (tunables->timer_slack_val >= 0 && pcpu->target_freq > pcpu->policy->min) { expires += usecs_to_jiffies(tunables->timer_slack_val); mod_timer_pinned(&pcpu->cpu_slack_timer, expires); del_timer(&pcpu->cpu_slack_timer); pcpu->cpu_slack_timer.expires = expires; add_timer_on(&pcpu->cpu_slack_timer, cpu); } spin_unlock_irqrestore(&pcpu->load_lock, flags); Loading Loading @@ -364,17 +396,39 @@ static void cpufreq_interactive_timer(unsigned long data) goto exit; spin_lock_irqsave(&pcpu->load_lock, flags); pcpu->last_evaluated_jiffy = get_jiffies_64(); now = update_load(data); delta_time = (unsigned int)(now - pcpu->cputime_speedadj_timestamp); if (use_sched_hint) { /* * Unlock early to avoid deadlock. * * cpufreq_interactive_timer_resched_now() is called * in thread migration notification which already holds * rq lock. Then it locks load_lock to avoid racing with * cpufreq_interactive_timer_resched/start(). * sched_get_busy() will also acquire rq lock. Thus we * can't hold load_lock when calling sched_get_busy(). * * load_lock used in this function protects time * and load information. These stats are not used when * scheduler hint is available. Thus unlocking load_lock * early is perfectly OK. */ spin_unlock_irqrestore(&pcpu->load_lock, flags); cputime_speedadj = (u64)sched_get_busy(data) * pcpu->policy->cpuinfo.max_freq; do_div(cputime_speedadj, tunables->timer_rate); } else { delta_time = (unsigned int) (now - pcpu->cputime_speedadj_timestamp); cputime_speedadj = pcpu->cputime_speedadj; pcpu->last_evaluated_jiffy = get_jiffies_64(); spin_unlock_irqrestore(&pcpu->load_lock, flags); if (WARN_ON_ONCE(!delta_time)) goto rearm; do_div(cputime_speedadj, delta_time); } spin_lock_irqsave(&pcpu->target_freq_lock, flags); do_div(cputime_speedadj, delta_time); loadadjfreq = (unsigned int)cputime_speedadj * 100; cpu_load = loadadjfreq / pcpu->target_freq; boosted = tunables->boost_val || now < tunables->boostpulse_endtime; Loading Loading @@ -470,7 +524,7 @@ rearm_if_notmax: rearm: if (!timer_pending(&pcpu->cpu_timer)) cpufreq_interactive_timer_resched(pcpu); cpufreq_interactive_timer_resched(data); exit: up_read(&pcpu->enable_sem); Loading Loading @@ -502,7 +556,7 @@ static void cpufreq_interactive_idle_start(void) * the CPUFreq driver. */ if (!pending) cpufreq_interactive_timer_resched(pcpu); cpufreq_interactive_timer_resched(smp_processor_id()); } up_read(&pcpu->enable_sem); Loading @@ -522,7 +576,7 @@ static void cpufreq_interactive_idle_end(void) /* Arm the timer for 1-2 ticks later if not already. */ if (!timer_pending(&pcpu->cpu_timer)) { cpufreq_interactive_timer_resched(pcpu); cpufreq_interactive_timer_resched(smp_processor_id()); } else if (time_after_eq(jiffies, pcpu->cpu_timer.expires)) { del_timer(&pcpu->cpu_timer); del_timer(&pcpu->cpu_slack_timer); Loading Loading @@ -633,6 +687,28 @@ static void cpufreq_interactive_boost(void) wake_up_process(speedchange_task); } static int load_change_callback(struct notifier_block *nb, unsigned long val, void *data) { unsigned long cpu = (unsigned long) data; struct cpufreq_interactive_cpuinfo *pcpu = &per_cpu(cpuinfo, cpu); /* * We can't acquire enable_sem here. However, this doesn't matter * because timer function will grab it and check if governor is * enabled before doing anything. Therefore the execution is * still correct. */ if (pcpu->governor_enabled) cpufreq_interactive_timer_resched_now(cpu); return 0; } static struct notifier_block load_notifier_block = { .notifier_call = load_change_callback, }; static int cpufreq_interactive_notifier( struct notifier_block *nb, unsigned long val, void *data) { Loading Loading @@ -876,6 +952,7 @@ static ssize_t store_timer_rate(struct cpufreq_interactive_tunables *tunables, if (ret < 0) return ret; tunables->timer_rate = val; set_window_helper(tunables); return count; } Loading Loading @@ -980,6 +1057,7 @@ static ssize_t store_io_is_busy(struct cpufreq_interactive_tunables *tunables, if (ret < 0) return ret; tunables->io_is_busy = val; sched_set_io_is_busy(val); return count; } Loading Loading @@ -1245,6 +1323,18 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, CPUFREQ_TRANSITION_NOTIFIER); } if (!active_count) { rc = set_window_helper(tunables); if (!rc) { use_sched_hint = true; sched_set_io_is_busy(tunables->io_is_busy); atomic_notifier_chain_register( &load_alert_notifier_head, &load_notifier_block); } } active_count++; break; case CPUFREQ_GOV_POLICY_EXIT: Loading @@ -1264,6 +1354,14 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, } policy->governor_data = NULL; active_count--; if (!active_count && use_sched_hint) { atomic_notifier_chain_unregister( &load_alert_notifier_head, &load_notifier_block); use_sched_hint = false; } break; case CPUFREQ_GOV_START: Loading