From eaecf574d4c7844296b8ae57d8eeaebda50cd3ab Mon Sep 17 00:00:00 2001 From: Tanguy Pruvot Date: Tue, 15 May 2012 14:02:50 +0200 Subject: [PATCH] overclock: boosted v2 governor and sio sched implementation (cpufreq governor based on smartass v2) Add sources and module compiled from sources Fix cpu sysfs owner to allow toggle from Android Settings Fixed sysfs write operations and renamed to cpufreq_boosted Change-Id: If5bc6ae696b945de08e0e21dd0c781653e8e771d --- bootmenu/config/overclock.conf | 15 + bootmenu/script/overclock.sh | 95 ++- modules/Makefile | 3 + modules/cpufreq_boosted.ko | Bin 0 -> 14832 bytes modules/cpufreq_conservative.ko | Bin 11062 -> 11174 bytes modules/cpufreq_interactive.ko | Bin 7039 -> 7127 bytes modules/cpufreq_powersave.ko | Bin 3419 -> 3491 bytes modules/cpufreq_smartass.ko | Bin 12355 -> 12299 bytes modules/cpufreq_stats.ko | Bin 6706 -> 6778 bytes modules/modules.dep | 3 + modules/overclock/Kconfig | 4 +- modules/overclock/Makefile | 9 +- modules/overclock/cpufreq_boosted.c | 872 ++++++++++++++++++++++++ modules/overclock/cpufreq_interactive.c | 5 +- modules/overclock/cpufreq_smartass.c | 50 +- modules/overclock/sio-iosched.c | 399 +++++++++++ modules/proximity.ko | Bin 10136 -> 10208 bytes modules/sio_iosched.ko | Bin 0 -> 7793 bytes 18 files changed, 1417 insertions(+), 38 deletions(-) create mode 100644 modules/cpufreq_boosted.ko create mode 100644 modules/overclock/cpufreq_boosted.c create mode 100644 modules/overclock/sio-iosched.c create mode 100644 modules/sio_iosched.ko diff --git a/bootmenu/config/overclock.conf b/bootmenu/config/overclock.conf index 23e3092b..84ad792e 100644 --- a/bootmenu/config/overclock.conf +++ b/bootmenu/config/overclock.conf @@ -4,9 +4,11 @@ scaling 2 clk1 300 clk2 600 clk3 1000 +clk4 1000 vsel1 30 vsel2 46 vsel3 58 +vsel4 58 con_up_threshold 80 con_down_threshold 20 con_freq_step 5 @@ -21,3 +23,16 @@ smt_sleep_max_freq 300000 smt_up_min_freq 1000000 smt_wakeup_freq 1000000 smt_ramp_up_step 250000 +bst_awake_ideal_freq 800000 +bst_debug_mask 0 +bst_down_rate_us 97000 +bst_max_cpu_load 70 +bst_min_cpu_load 40 +bst_ramp_down_step 160000 +bst_ramp_up_step 160000 +bst_sample_rate_jiffies 2 +bst_sleep_ideal_freq 300000 +bst_sleep_wakeup_freq 300000 +bst_up_rate_us 52000 +iosched_sio 0 + diff --git a/bootmenu/script/overclock.sh b/bootmenu/script/overclock.sh index 6c2c5244..f4fcc15b 100755 --- a/bootmenu/script/overclock.sh +++ b/bootmenu/script/overclock.sh @@ -20,9 +20,11 @@ ASKED_MODE=$1 # clk1 300 # clk2 600 # clk3 1000 +# clk4 1000 # vsel1 30 # vsel2 46 # vsel3 58 +# vsel4 58 # con_up_threshold 80 # con_down_threshold 20 # con_freq_step 5 @@ -37,6 +39,18 @@ ASKED_MODE=$1 # smt_up_min_freq 1000000 # smt_wakeup_freq 1000000 # smt_ramp_up_step 250000 +# bst_awake_ideal_freq 800000 +# bst_down_rate_us 97000 +# bst_max_cpu_load 70 +# bst_min_cpu_load 40 +# bst_ramp_down_step 160000 +# bst_ramp_up_step 160000 +# bst_sample_rate_jiffies 2 +# bst_sleep_ideal_freq 300000 +# bst_sleep_wakeup_freq 300000 +# bst_up_rate_us 52000 +# bst_debug_mask 0 +# iosched_sio 0 param_load() { @@ -47,6 +61,7 @@ param_load() param_safe() { + echo "cpufreq: ondemand safe" # for bootmenu operations # enable ondemand profile # which is in kernel @@ -72,7 +87,7 @@ param_safe() get_address() { cpufreq_table=`grep -e omap2_clk_init_cpufreq_table /proc/kallsyms | sed -e "s/\([0-9A-Fa-f]\{8\}\).*/\1/"` - stats_update=`grep -e cpufreq_stats_update /proc/kallsyms | sed -e "s/\([0-9A-Fa-f]\{8\}\).*/\1/"` + stats_update=`grep -e cpufreq_stats_update$ /proc/kallsyms | sed -e "s/\([0-9A-Fa-f]\{8\}\).*/\1/"` } ############################################################# @@ -93,7 +108,10 @@ install_module() insmod $MODULE_DIR/cpufreq_stats.ko insmod $MODULE_DIR/cpufreq_interactive.ko insmod $MODULE_DIR/cpufreq_smartass.ko + insmod $MODULE_DIR/cpufreq_boosted.ko fi + busybox chown -R system /sys/devices/system/cpu + busybox chown -R system /sys/class/block/mmc*/queue } ############################################################# @@ -138,6 +156,26 @@ set_scaling() echo "powersave" > $SCALING_GOVERNOR ;; "5" ) + if [ "$load_all" -eq "0" ]; then + insmod $MODULE_DIR/symsearch.ko + insmod $MODULE_DIR/clockfix.ko + insmod $MODULE_DIR/cpufreq_boosted.ko + fi + + echo boosted > $SCALING_GOVERNOR + echo $bst_debug_mask > /sys/devices/system/cpu/cpufreq/boosted/debug_mask + echo $bst_awake_ideal_freq > /sys/devices/system/cpu/cpufreq/boosted/awake_ideal_freq + echo $bst_down_rate_us > /sys/devices/system/cpu/cpufreq/boosted/down_rate_us + echo $bst_max_cpu_load > /sys/devices/system/cpu/cpufreq/boosted/max_cpu_load + echo $bst_min_cpu_load > /sys/devices/system/cpu/cpufreq/boosted/min_cpu_load + echo $bst_ramp_down_step > /sys/devices/system/cpu/cpufreq/boosted/ramp_down_step + echo $bst_ramp_up_step > /sys/devices/system/cpu/cpufreq/boosted/ramp_up_step + echo $bst_sample_rate_jiffies > /sys/devices/system/cpu/cpufreq/boosted/sample_rate_jiffies + echo $bst_sleep_ideal_freq > /sys/devices/system/cpu/cpufreq/boosted/sleep_ideal_freq + echo $bst_sleep_wakeup_freq > /sys/devices/system/cpu/cpufreq/boosted/sleep_wakeup_freq + echo $bst_up_rate_us > /sys/devices/system/cpu/cpufreq/boosted/up_rate_us + ;; + "6" ) if [ $load_all -eq 0 ]; then insmod $MODULE_DIR/symsearch.ko insmod $MODULE_DIR/clockfix.ko @@ -152,7 +190,7 @@ set_scaling() echo $smt_wakeup_freq > /sys/devices/system/cpu/cpu0/cpufreq/smartass/sleep_wakeup_freq echo $smt_ramp_up_step > /sys/devices/system/cpu/cpu0/cpufreq/smartass/ramp_up_step ;; - "6" ) + "7" ) if [ $load_all -eq 0 ]; then insmod $MODULE_DIR/cpufreq_userspace.ko fi @@ -163,17 +201,61 @@ set_scaling() esac } +############################################################# +# Alternative I/O Schedulers +# +# Default linux I/O Schedulers are optimized for Hard disks +# The alternative ones are optimized for flash disks +############################################################# + +set_ioscheduler() +{ + iosched="cfq" + if [ "$iosched_sio" -eq "1" ]; then + iosched="sio" + fi + + # options: 'noop cfq sio' + case "$iosched" in + "cfq" ) + ;; + "sio" ) + insmod $MODULE_DIR/sio_iosched.ko + for i in /sys/block/mmc*/queue; do + [ -f "$i/scheduler" ] && echo $iosched > $i/scheduler + + [ -f "$i/iosched/low_latency" ] && echo 1 > $i/iosched/low_latency + [ -f "$i/iosched/back_seek_penalty" ] && echo 1 > $i/iosched/back_seek_penalty + [ -f "$i/iosched/back_seek_max" ] && echo 1000000000 > $i/iosched/back_seek_max + [ -f "$i/iosched/slice_idle" ] && echo 0 > $i/iosched/slice_idle + [ -f "$i/iosched/fifo_batch" ] && echo 1 > $i/iosched/fifo_batch + [ -f "$i/iosched/quantum" ] && echo 16 > $i/iosched/quantum + [ -f "$i/nr_requests" ] && echo 512 > $i/nr_requests + + [ -f "$i/rotational" ] && [ "`cat $i/rotational`" -ne "0" ] && echo 0 > $i/rotational + [ -f "$i/iostats" ] && [ "`cat $i/iostats`" -ne "0" ] && echo 0 > $i/iostats + done + ;; + * ) + ;; + esac +} + ############################################################# # Set Clock Table ############################################################# set_overclock_table() { +# echo "$vsel4" > /proc/overclock/max_vsel echo "$vsel3" > /proc/overclock/max_vsel +# echo "${clk4}000" > /proc/overclock/max_rate echo "${clk3}000" > /proc/overclock/max_rate +# echo "4 ${clk4}000000 $vsel4" > /proc/overclock/mpu_opps echo "3 ${clk3}000000 $vsel3" > /proc/overclock/mpu_opps echo "2 ${clk2}000000 $vsel2" > /proc/overclock/mpu_opps echo "1 ${clk1}000000 $vsel1" > /proc/overclock/mpu_opps +# echo "0 ${clk4}000" > /proc/overclock/freq_table echo "0 ${clk3}000" > /proc/overclock/freq_table echo "1 ${clk2}000" > /proc/overclock/freq_table echo "2 ${clk1}000" > /proc/overclock/freq_table @@ -194,9 +276,18 @@ else fi if [ $enable -eq 1 ]; then + get_address install_module + + echo "set scaling..." set_scaling + echo "set overclock table..." set_overclock_table + echo "set ioscheduler..." + set_ioscheduler + + busybox chown -R system /sys/devices/system/cpu + fi diff --git a/modules/Makefile b/modules/Makefile index c2dcb8ef..b793977e 100644 --- a/modules/Makefile +++ b/modules/Makefile @@ -13,6 +13,9 @@ obj-m += clockfix/ obj-m += logcap/ obj-m += usbled/ +# mb525/mb526 specific +obj-m += proximity/ + PWD := $(shell pwd) diff --git a/modules/cpufreq_boosted.ko b/modules/cpufreq_boosted.ko new file mode 100644 index 0000000000000000000000000000000000000000..73f548c7f0b00380555b12e9bf8e16dbf2a97d7a GIT binary patch literal 14832 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 delta 1332 zcmZXUUrZEN6vn?XcaN+YDiPV z8jaON6Up_(ho&^?12Nicv)iN(wE94d8cq5@Q`6Q&q)m*`^u>p!G2MP=cD66xZ02{* z`OZ1_+&4;q+hU^iQjPPMkQh zwj%oZZN#^4?}*jMdk5FWS~j+9Y{YAx%yL;=QyuV>{iUDQp^zR+!7(ggTp z<^!sARBb*h4cm8?ZrM#(bs14@z{&f}Fgd$Kb^ianAE*wgM~3a)Tmqa^zCbNIek)-6 z?lY#-sW2RfYTfD-eMWgX-R*_&h!0jDpsxubVc+mFd z7`0wq4b}!Msll}@s&REU7@lre3GC*s3xmhxj8Q(VUBLIky}*6p2=KRXA7C)h!}BqP zqd^VO=>qW=IfoZ&?P diff --git a/modules/cpufreq_powersave.ko b/modules/cpufreq_powersave.ko index c495b7856a357b5e52ce9ef9a698b37ba4179063..97c1c36dad8bfa9f3a84195458f1f5e9c569c9c7 100644 GIT binary patch delta 435 zcmcaDwOD$Bf^Y~60|>C1fGG_ISq7<%#uJ&$RgKrFF<9y4v`jLe{nq2xah@X`lNsEd zovjo!-2Fl|6-@L@^o$uWL^l6svS8%P%P&dQ11i%kE=f$zo@~fc%z1}{fdPmZq$jUs zap!yjWD01nU%wuQG2o@hca&nQ1JmK1_l|R3f0Ml9MVi8Jd^u4r0ca97(gK+ zs|j%~6E~Evjo^a})dK1T12qVx1+x#vXGX|_Jjx`-z@P!5RT&uefM_7c1gM8e9jXpW YO^#<2m~6rMg&$d%nL%>1HkSc201qcW00000 delta 392 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 delta 704 zcmZXRJxCj29LAqJBYH9B1F0xpk}C#`B0=bZA{NxBr)lbE1QiPog)EBL!Kt8wxVQ-K zQcB$%9JT4tL8z0sv~($yw$Q2Ikb;{a4nF_8R~KJi`0+gd&-d=e{pjvHO{Fm8)Ve;h zaF9HCwo%`^RxO_|UGJ8UzJ>mtjc3~Tr*c(z`Fy{V^(N;#9XIFZdhp1p{H`w>YBv1F zD#d`GY?@ME!=BY02NrylZ1AidDi$-g*064UqU!V2KdLGfFJrGF>(=dq`VskJMH2V1 zgGSG?siytR|5U3FvFFh}brtP13aT^K-uRaOU@eel=Ac@PwQZd?1DiFJ`WTucq&*Sz zG^4@3_9XB}`A)&!d%y?bIB+bS z1uldyfEF%k&jFu>T%;vLakr~g*e~@< z*iC)v^JlpLLxnp2?m>O u{31+{1!calN677@qxlpV(OUkhnF3CQV|=JV4y%p2RONdjV^l&-n{{_q24R5! diff --git a/modules/modules.dep b/modules/modules.dep index 51f90a8c..90e6cb19 100644 --- a/modules/modules.dep +++ b/modules/modules.dep @@ -35,5 +35,8 @@ overclock_defy.ko: cpufreq_stats.ko: cpufreq_smartass.ko: symsearch.ko cpufreq_interactive.ko: symsearch.ko cpufreq_stats.ko +cpufreq_boosted.ko: symsearch.ko cpufreq_conservative.ko: +sio_iosched.ko: +xt_multiport.ko: diff --git a/modules/overclock/Kconfig b/modules/overclock/Kconfig index f753b6e6..6c5f2563 100644 --- a/modules/overclock/Kconfig +++ b/modules/overclock/Kconfig @@ -3,8 +3,8 @@ menu "Defy Overclock" config CPUFREQ_OVERCLOCK bool "overclock" default y -config CPUFREQ_GOV_SMARTASS - bool "smartass" +config CPUFREQ_GOV_BOOSTED + bool "smartass2 boosted variant" default y config CPUFREQ_GOV_INTERACTIVE bool "interactive" diff --git a/modules/overclock/Makefile b/modules/overclock/Makefile index 1b7ac2ef..0567e464 100644 --- a/modules/overclock/Makefile +++ b/modules/overclock/Makefile @@ -1,4 +1,9 @@ -#Module +# Modules -obj-m += cpufreq_conservative.o cpufreq_interactive.o cpufreq_smartass.o cpufreq_stats.o cpufreq_powersave.o +obj-m += cpufreq_conservative.o cpufreq_interactive.o cpufreq_powersave.o +obj-m += cpufreq_stats.o +obj-m += cpufreq_smartass.o cpufreq_boosted.o + +sio_iosched-objs = sio-iosched.o +obj-m += sio_iosched.o diff --git a/modules/overclock/cpufreq_boosted.c b/modules/overclock/cpufreq_boosted.c new file mode 100644 index 00000000..a580fa66 --- /dev/null +++ b/modules/overclock/cpufreq_boosted.c @@ -0,0 +1,872 @@ +/* + * drivers/cpufreq/cpufreq_boosted.c + * + * Copyright (C) 2012 AOSP, CyanogenDefy + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + * Author: Erasmux + * + * Based on the interactive governor By Mike Chan (mike@android.com) + * which was adaptated to 2.6.29 kernel by Nadlabak (pavel@doshaska.net) + * Ported to the Droid X by FirstEncounter + * Modified to PowerBoost Standard by BMc08GT + * + * Sysfs fix and renamed to "boosted" by Epsylon3 + * + * MOTO Devices: require symsearch.ko + * + * + * For a general overview see the cpufreq documentation in + * Documentation/cpu-freq/governors.txt + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../symsearch/symsearch.h" +#define TAG "boosted" + +SYMSEARCH_DECLARE_FUNCTION_STATIC(unsigned long, nr_running_k, void); + +/******************** Tunable parameters: ********************/ + +/* + * The "ideal" frequency to use when awake. The governor will ramp up faster + * towards the ideal frequency and slower after it has passed it. Similarly, + * lowering the frequency towards the ideal frequency is faster than below it. + */ +#define DEFAULT_AWAKE_IDEAL_FREQ 500000 +static unsigned int awake_ideal_freq; + +/* + * The "ideal" frequency to use when suspended. + * When set to 0, the governor will not track the suspended state (meaning + * that practically when sleep_ideal_freq==0 the awake_ideal_freq is used + * also when suspended). + */ +#define DEFAULT_SLEEP_IDEAL_FREQ 200000 +static unsigned int sleep_ideal_freq; + +/* + * Frequency delta when ramping up above the ideal frequency. + * Zero disables and causes to always jump straight to max frequency. + * When below the ideal frequency we always ramp up to the ideal freq. + */ +#define DEFAULT_RAMP_UP_STEP 160000 +static unsigned int ramp_up_step; + +/* + * Frequency delta when ramping down below the ideal frequency. + * Zero disables and will calculate ramp down according to load heuristic. + * When above the ideal frequency we always ramp down to the ideal freq. + */ +#define DEFAULT_RAMP_DOWN_STEP 160000 +static unsigned int ramp_down_step; + +/* + * CPU freq will be increased if measured load > max_cpu_load; + */ +#define DEFAULT_MAX_CPU_LOAD 70 +static unsigned long max_cpu_load; + +/* + * CPU freq will be decreased if measured load < min_cpu_load; + */ +#define DEFAULT_MIN_CPU_LOAD 40 +static unsigned long min_cpu_load; + +/* + * The minimum amount of time to spend at a frequency before we can ramp up. + * Notice we ignore this when we are below the ideal frequency. + */ +#define DEFAULT_UP_RATE_US 52000; +static unsigned long up_rate_us; + +/* + * The minimum amount of time to spend at a frequency before we can ramp down. + * Notice we ignore this when we are above the ideal frequency. + */ +#define DEFAULT_DOWN_RATE_US 97000; +static unsigned long down_rate_us; + +/* + * The frequency to set when waking up from sleep. + * When sleep_ideal_freq=0 this will have no effect. + */ +#define DEFAULT_SLEEP_WAKEUP_FREQ 300000 +static unsigned int sleep_wakeup_freq; + +/* + * Sampling rate, I highly recommend to leave it at 2. + */ +#define DEFAULT_SAMPLE_RATE_JIFFIES 2 +static unsigned int sample_rate_jiffies; + + +/*************** End of tunables ***************/ + + +static void (*pm_idle_old)(void); +static atomic_t active_count = ATOMIC_INIT(0); + +struct boosted_info_s { + struct cpufreq_policy *cur_policy; + struct cpufreq_frequency_table *freq_table; + struct timer_list timer; + u64 time_in_idle; + u64 idle_exit_time; + u64 freq_change_time; + u64 freq_change_time_in_idle; + int cur_cpu_load; + int old_freq; + int ramp_dir; + unsigned int enable; + int ideal_speed; +}; +static DEFINE_PER_CPU(struct boosted_info_s, boosted_info); + +/* Workqueues handle frequency scaling */ +static struct workqueue_struct *up_wq; +static struct workqueue_struct *down_wq; +static struct work_struct freq_scale_work; + +static cpumask_t work_cpumask; +static spinlock_t cpumask_lock; + +static unsigned int suspended; + +#define dprintk(flag,msg...) do { \ + if (debug_mask & flag) pr_debug(msg); \ + } while (0) + +enum { + DEBUG_JUMPS=1, + DEBUG_LOAD=2, + DEBUG_ALG=4 +}; + +/* + * Combination of the above debug flags. + */ +static unsigned long debug_mask; + +static int cpufreq_governor_boosted(struct cpufreq_policy *policy, + unsigned int event); + +#ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_BOOSTED +static +#endif +struct cpufreq_governor cpufreq_gov_boosted = { + .name = "boosted", + .governor = cpufreq_governor_boosted, + .max_transition_latency = 6000000, + .owner = THIS_MODULE, +}; + +inline static void boosted_update_min_max(struct boosted_info_s *this_boosted, struct cpufreq_policy *policy, int suspend) { + if (suspend) { + this_boosted->ideal_speed = // sleep_ideal_freq; but make sure it obeys the policy min/max + policy->max > sleep_ideal_freq ? + (sleep_ideal_freq > policy->min ? sleep_ideal_freq : policy->min) : policy->max; + } else { + this_boosted->ideal_speed = // awake_ideal_freq; but make sure it obeys the policy min/max + policy->min < awake_ideal_freq ? + (awake_ideal_freq < policy->max ? awake_ideal_freq : policy->max) : policy->min; + } +} + +inline static void boosted_update_min_max_allcpus(void) { + unsigned int i; + for_each_online_cpu(i) { + struct boosted_info_s *this_boosted = &per_cpu(boosted_info, i); + if (this_boosted->enable) + boosted_update_min_max(this_boosted,this_boosted->cur_policy,suspended); + } +} + +inline static unsigned int validate_freq(struct cpufreq_policy *policy, int freq) { + if (freq > (int)policy->max) + return policy->max; + if (freq < (int)policy->min) + return policy->min; + return freq; +} + +inline static void reset_timer(unsigned long cpu, struct boosted_info_s *this_boosted) { + this_boosted->time_in_idle = get_cpu_idle_time_us(cpu, &this_boosted->idle_exit_time); + mod_timer(&this_boosted->timer, jiffies + sample_rate_jiffies); +} + +inline static void work_cpumask_set(unsigned long cpu) { + unsigned long flags; + spin_lock_irqsave(&cpumask_lock, flags); + cpumask_set_cpu(cpu, &work_cpumask); + spin_unlock_irqrestore(&cpumask_lock, flags); +} + +inline static int work_cpumask_test_and_clear(unsigned long cpu) { + unsigned long flags; + int res = 0; + spin_lock_irqsave(&cpumask_lock, flags); + res = cpumask_test_and_clear_cpu(cpu, &work_cpumask); + spin_unlock_irqrestore(&cpumask_lock, flags); + return res; +} + +inline static int target_freq(struct cpufreq_policy *policy, struct boosted_info_s *this_boosted, + int new_freq, int old_freq, int prefered_relation) { + int index, target; + struct cpufreq_frequency_table *table = this_boosted->freq_table; + + if (new_freq == old_freq) + return 0; + new_freq = validate_freq(policy, new_freq); + if (new_freq == old_freq) + return 0; + + if (table && + !cpufreq_frequency_table_target(policy,table,new_freq,prefered_relation,&index)) + { + target = table[index].frequency; + if (target == old_freq) { + // if for example we are ramping up to *at most* current + ramp_up_step + // but there is no such frequency higher than the current, try also + // to ramp up to *at least* current + ramp_up_step. + if (new_freq > old_freq && prefered_relation==CPUFREQ_RELATION_H + && !cpufreq_frequency_table_target(policy,table,new_freq, + CPUFREQ_RELATION_L,&index)) + target = table[index].frequency; + // simlarly for ramping down: + else if (new_freq < old_freq && prefered_relation==CPUFREQ_RELATION_L + && !cpufreq_frequency_table_target(policy,table,new_freq, + CPUFREQ_RELATION_H,&index)) + target = table[index].frequency; + } + + if (target == old_freq) { + // We should not get here: + // If we got here we tried to change to a validated new_freq which is different + // from old_freq, so there is no reason for us to remain at same frequency. + pr_warning(TAG ": frequency change failed: %d to %d => %d\n", + old_freq,new_freq,target); + return 0; + } + } + else target = new_freq; + + __cpufreq_driver_target(policy, target, prefered_relation); + + dprintk(DEBUG_JUMPS,TAG ": jumping from %d to %d => %d (%d)\n", + old_freq,new_freq,target,policy->cur); + + return target; +} + +static void cpufreq_boosted_timer(unsigned long cpu) +{ + u64 delta_idle; + u64 delta_time; + int cpu_load; + int old_freq; + u64 update_time; + u64 now_idle; + int queued_work = 0; + struct boosted_info_s *this_boosted = &per_cpu(boosted_info, cpu); + struct cpufreq_policy *policy = this_boosted->cur_policy; + + now_idle = get_cpu_idle_time_us(cpu, &update_time); + old_freq = policy->cur; + + if (this_boosted->idle_exit_time == 0 || update_time == this_boosted->idle_exit_time) + return; + + delta_idle = cputime64_sub(now_idle, this_boosted->time_in_idle); + delta_time = cputime64_sub(update_time, this_boosted->idle_exit_time); + + // If timer ran less than 1ms after short-term sample started, retry. + if (delta_time < 1000) { + if (!timer_pending(&this_boosted->timer)) + reset_timer(cpu,this_boosted); + return; + } + + if (delta_idle > delta_time) + cpu_load = 0; + else + cpu_load = 100 * (unsigned int)(delta_time - delta_idle) / (unsigned int)delta_time; + + dprintk(DEBUG_LOAD,TAG "T @ %d: load %d (delta_time %llu)\n", + old_freq,cpu_load,delta_time); + + this_boosted->cur_cpu_load = cpu_load; + this_boosted->old_freq = old_freq; + + // Scale up if load is above max or if there where no idle cycles since coming out of idle, + // additionally, if we are at or above the ideal_speed, verify we have been at this frequency + // for at least up_rate_us: + if (cpu_load > max_cpu_load || delta_idle == 0) + { + if (old_freq < policy->max && + (old_freq < this_boosted->ideal_speed || delta_idle == 0 || + cputime64_sub(update_time, this_boosted->freq_change_time) >= up_rate_us)) + { + dprintk(DEBUG_ALG,TAG "T @ %d ramp up: load %d (delta_idle %llu)\n", + old_freq,cpu_load,delta_idle); + this_boosted->ramp_dir = 1; + work_cpumask_set(cpu); + queue_work(up_wq, &freq_scale_work); + queued_work = 1; + } + else this_boosted->ramp_dir = 0; + } + // Similarly for scale down: load should be below min and if we are at or below ideal + // frequency we require that we have been at this frequency for at least down_rate_us: + else if (cpu_load < min_cpu_load && old_freq > policy->min && + (old_freq > this_boosted->ideal_speed || + cputime64_sub(update_time, this_boosted->freq_change_time) >= down_rate_us)) + { + dprintk(DEBUG_ALG,TAG "T @ %d ramp down: load %d (delta_idle %llu)\n", + old_freq,cpu_load,delta_idle); + this_boosted->ramp_dir = -1; + work_cpumask_set(cpu); + queue_work(down_wq, &freq_scale_work); + queued_work = 1; + } + else this_boosted->ramp_dir = 0; + + // To avoid unnecessary load when the CPU is already at high load, we don't + // reset ourselves if we are at max speed. If and when there are idle cycles, + // the idle loop will activate the timer. + // Additionally, if we queued some work, the work task will reset the timer + // after it has done its adjustments. + if (!queued_work && old_freq < policy->max) + reset_timer(cpu,this_boosted); +} + +static void cpufreq_idle(void) +{ + struct boosted_info_s *this_boosted = &per_cpu(boosted_info, smp_processor_id()); + struct cpufreq_policy *policy = this_boosted->cur_policy; + + if (!this_boosted->enable) { + pm_idle_old(); + return; + } + + if (policy->cur == policy->min && timer_pending(&this_boosted->timer)) + del_timer(&this_boosted->timer); + + pm_idle_old(); + + if (!timer_pending(&this_boosted->timer)) + reset_timer(smp_processor_id(), this_boosted); +} + +/* We use the same work function to sale up and down */ +static void cpufreq_boosted_freq_change_time_work(struct work_struct *work) +{ + unsigned int cpu; + int new_freq; + int old_freq; + int ramp_dir; + struct boosted_info_s *this_boosted; + struct cpufreq_policy *policy; + unsigned int relation = CPUFREQ_RELATION_L; + for_each_possible_cpu(cpu) { + this_boosted = &per_cpu(boosted_info, cpu); + if (!work_cpumask_test_and_clear(cpu)) + continue; + + ramp_dir = this_boosted->ramp_dir; + this_boosted->ramp_dir = 0; + + old_freq = this_boosted->old_freq; + policy = this_boosted->cur_policy; + + if (old_freq != policy->cur) { + // frequency was changed by someone else? + pr_warning(TAG ": frequency changed by 3rd party: %d to %d\n", + old_freq,policy->cur); + new_freq = old_freq; + } + else if (ramp_dir > 0 && nr_running_k() > 1) { + // ramp up logic: + if (old_freq < this_boosted->ideal_speed) + new_freq = this_boosted->ideal_speed; + else if (ramp_up_step) { + new_freq = old_freq + ramp_up_step; + relation = CPUFREQ_RELATION_H; + } + else { + new_freq = policy->max; + relation = CPUFREQ_RELATION_H; + } + dprintk(DEBUG_ALG,TAG "Q @ %d ramp up: ramp_dir=%d ideal=%d\n", + old_freq,ramp_dir,this_boosted->ideal_speed); + } + else if (ramp_dir < 0) { + // ramp down logic: + if (old_freq > this_boosted->ideal_speed) { + new_freq = this_boosted->ideal_speed; + relation = CPUFREQ_RELATION_H; + } + else if (ramp_down_step) + new_freq = old_freq - ramp_down_step; + else { + // Load heuristics: Adjust new_freq such that, assuming a linear + // scaling of load vs. frequency, the load in the new frequency + // will be max_cpu_load: + new_freq = old_freq * this_boosted->cur_cpu_load / max_cpu_load; + if (new_freq > old_freq) // min_cpu_load > max_cpu_load ?! + new_freq = old_freq -1; + } + dprintk(DEBUG_ALG,TAG "Q @ %d ramp down: ramp_dir=%d ideal=%d\n", + old_freq,ramp_dir,this_boosted->ideal_speed); + } + else { + // ramp_dir==0 ?! Could the timer change its mind about a queued ramp up/down + // before the work task gets to run? + // This may also happen if we refused to ramp up because the nr_running()==1 + new_freq = old_freq; + dprintk(DEBUG_ALG,TAG "Q @ %d nothing: ramp_dir=%d nr_running=%lu\n", + old_freq,ramp_dir,nr_running_k()); + } + + // do actual ramp up (returns 0, if frequency change failed): + new_freq = target_freq(policy,this_boosted,new_freq,old_freq,relation); + if (new_freq) + this_boosted->freq_change_time_in_idle = + get_cpu_idle_time_us(cpu,&this_boosted->freq_change_time); + + // reset timer: + if (new_freq < policy->max) + reset_timer(cpu,this_boosted); + // if we are maxed out, it is pointless to use the timer + // (idle cycles wake up the timer when the timer comes) + else if (timer_pending(&this_boosted->timer)) + del_timer(&this_boosted->timer); + } +} + +static ssize_t show_debug_mask(struct kobject *kobj, struct attribute *attr, char *buf) +{ + return sprintf(buf, "%lu\n", debug_mask); +} + +static ssize_t store_debug_mask(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ + unsigned long input; + ssize_t res = strict_strtoul(buf, 10, &input); + if (res >= 0) + debug_mask = input; + return count; +} + +static ssize_t show_up_rate_us(struct kobject *kobj, struct attribute *attr, char *buf) +{ + return sprintf(buf, "%lu\n", up_rate_us); +} + +static ssize_t store_up_rate_us(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ + unsigned long input; + ssize_t res = strict_strtoul(buf, 10, &input); + if (res >= 0 && input >= 0 && input <= 100000000) + up_rate_us = input; + return count; +} + +static ssize_t show_down_rate_us(struct kobject *kobj, struct attribute *attr, char *buf) +{ + return sprintf(buf, "%lu\n", down_rate_us); +} + +static ssize_t store_down_rate_us(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ + unsigned long input; + ssize_t res = strict_strtoul(buf, 10, &input); + if (res >= 0 && input >= 0 && input <= 100000000) + down_rate_us = input; + return count; +} + +static ssize_t show_sleep_ideal_freq(struct kobject *kobj, struct attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", sleep_ideal_freq); +} + +static ssize_t store_sleep_ideal_freq(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ + unsigned long input; + ssize_t res = strict_strtoul(buf, 10, &input); + if (res >= 0 && input >= 0) { + sleep_ideal_freq = input; + if (suspended) + boosted_update_min_max_allcpus(); + } + return count; +} + +static ssize_t show_sleep_wakeup_freq(struct kobject *kobj, struct attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", sleep_wakeup_freq); +} + +static ssize_t store_sleep_wakeup_freq(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ + unsigned long input; + ssize_t res = strict_strtoul(buf, 10, &input); + if (res >= 0 && input >= 0) + sleep_wakeup_freq = input; + return count; +} + +static ssize_t show_awake_ideal_freq(struct kobject *kobj, struct attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", awake_ideal_freq); +} + +static ssize_t store_awake_ideal_freq(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ + unsigned long input; + ssize_t res = strict_strtoul(buf, 10, &input); + if (res >= 0 && input >= 0) { + awake_ideal_freq = input; + if (!suspended) + boosted_update_min_max_allcpus(); + } + return count; +} + +static ssize_t show_sample_rate_jiffies(struct kobject *kobj, struct attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", sample_rate_jiffies); +} + +static ssize_t store_sample_rate_jiffies(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ + unsigned long input; + ssize_t res = strict_strtoul(buf, 10, &input); + if (res >= 0 && input > 0 && input <= 1000) + sample_rate_jiffies = input; + return count; +} + +static ssize_t show_ramp_up_step(struct kobject *kobj, struct attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", ramp_up_step); +} + +static ssize_t store_ramp_up_step(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ + unsigned long input; + ssize_t res = strict_strtoul(buf, 10, &input); + if (res >= 0 && input >= 0) + ramp_up_step = input; + return count; +} + +static ssize_t show_ramp_down_step(struct kobject *kobj, struct attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", ramp_down_step); +} + +static ssize_t store_ramp_down_step(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ + unsigned long input; + ssize_t res = strict_strtoul(buf, 10, &input); + if (res >= 0 && input >= 0) + ramp_down_step = input; + return count; +} + +static ssize_t show_max_cpu_load(struct kobject *kobj, struct attribute *attr, char *buf) +{ + return sprintf(buf, "%lu\n", max_cpu_load); +} + +static ssize_t store_max_cpu_load(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ + unsigned long input; + ssize_t res = strict_strtoul(buf, 10, &input); + if (res >= 0 && input > 0 && input <= 100) + max_cpu_load = input; + return count; +} + +static ssize_t show_min_cpu_load(struct kobject *kobj, struct attribute *attr, char *buf) +{ + return sprintf(buf, "%lu\n", min_cpu_load); +} + +static ssize_t store_min_cpu_load(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ + unsigned long input; + ssize_t res = strict_strtoul(buf, 10, &input); + if (res >= 0 && input > 0 && input < 100) + min_cpu_load = input; + return count; +} + +#define define_global_rw_attr(_name) \ +static struct global_attr _name##_attr = \ + __ATTR(_name, 0644, show_##_name, store_##_name) + +define_global_rw_attr(debug_mask); +define_global_rw_attr(up_rate_us); +define_global_rw_attr(down_rate_us); +define_global_rw_attr(sleep_ideal_freq); +define_global_rw_attr(sleep_wakeup_freq); +define_global_rw_attr(awake_ideal_freq); +define_global_rw_attr(sample_rate_jiffies); +define_global_rw_attr(ramp_up_step); +define_global_rw_attr(ramp_down_step); +define_global_rw_attr(max_cpu_load); +define_global_rw_attr(min_cpu_load); + +static struct attribute * boosted_attributes[] = { + &debug_mask_attr.attr, + &up_rate_us_attr.attr, + &down_rate_us_attr.attr, + &sleep_ideal_freq_attr.attr, + &sleep_wakeup_freq_attr.attr, + &awake_ideal_freq_attr.attr, + &sample_rate_jiffies_attr.attr, + &ramp_up_step_attr.attr, + &ramp_down_step_attr.attr, + &max_cpu_load_attr.attr, + &min_cpu_load_attr.attr, + NULL, +}; + +static struct attribute_group boosted_attr_group = { + .attrs = boosted_attributes, + .name = "boosted", +}; + +static int cpufreq_governor_boosted(struct cpufreq_policy *new_policy, + unsigned int event) +{ + unsigned int cpu = new_policy->cpu; + int rc; + struct boosted_info_s *this_boosted = &per_cpu(boosted_info, cpu); + + switch (event) { + case CPUFREQ_GOV_START: + if ((!cpu_online(cpu)) || (!new_policy->cur)) + return -EINVAL; + + this_boosted->cur_policy = new_policy; + + this_boosted->enable = 1; + + boosted_update_min_max(this_boosted,new_policy,suspended); + + this_boosted->freq_table = cpufreq_frequency_get_table(cpu); + if (!this_boosted->freq_table) + pr_warning(TAG ": no frequency table for cpu %d?!\n",cpu); + + smp_wmb(); + + // Do not register the idle hook and create sysfs + // entries if we have already done so. + if (atomic_inc_return(&active_count) <= 1) { + rc = sysfs_create_group(cpufreq_global_kobject, + &boosted_attr_group); + if (rc) + return rc; + + pm_idle_old = pm_idle; + pm_idle = cpufreq_idle; + } + + if (this_boosted->cur_policy->cur < new_policy->max && !timer_pending(&this_boosted->timer)) + reset_timer(cpu,this_boosted); + + break; + + case CPUFREQ_GOV_LIMITS: + boosted_update_min_max(this_boosted,new_policy,suspended); + + if (this_boosted->cur_policy->cur > new_policy->max) { + dprintk(DEBUG_JUMPS,TAG "I: jumping to new max freq: %d\n",new_policy->max); + __cpufreq_driver_target(this_boosted->cur_policy, + new_policy->max, CPUFREQ_RELATION_H); + } + else if (this_boosted->cur_policy->cur < new_policy->min) { + dprintk(DEBUG_JUMPS,TAG "I: jumping to new min freq: %d\n",new_policy->min); + __cpufreq_driver_target(this_boosted->cur_policy, + new_policy->min, CPUFREQ_RELATION_L); + } + + if (this_boosted->cur_policy->cur < new_policy->max && !timer_pending(&this_boosted->timer)) + reset_timer(cpu,this_boosted); + + break; + + case CPUFREQ_GOV_STOP: + this_boosted->enable = 0; + smp_wmb(); + del_timer(&this_boosted->timer); + flush_work(&freq_scale_work); + this_boosted->idle_exit_time = 0; + + if (atomic_dec_return(&active_count) <= 1) { + sysfs_remove_group(cpufreq_global_kobject, + &boosted_attr_group); + pm_idle = pm_idle_old; + } + break; + } + + return 0; +} + +static void boosted_suspend(int cpu, int suspend) +{ + struct boosted_info_s *this_boosted = &per_cpu(boosted_info, smp_processor_id()); + struct cpufreq_policy *policy = this_boosted->cur_policy; + unsigned int new_freq; + + if (!this_boosted->enable) + return; + + boosted_update_min_max(this_boosted,policy,suspend); + if (!suspend) { // resume at max speed: + new_freq = validate_freq(policy,sleep_wakeup_freq); + + dprintk(DEBUG_JUMPS,TAG "S: awaking at %d\n",new_freq); + + __cpufreq_driver_target(policy, new_freq, + CPUFREQ_RELATION_L); + } else { + // to avoid wakeup issues with quick sleep/wakeup don't change actual frequency when entering sleep + // to allow some time to settle down. Instead we just reset our statistics (and reset the timer). + // Eventually, the timer will adjust the frequency if necessary. + + this_boosted->freq_change_time_in_idle = + get_cpu_idle_time_us(cpu,&this_boosted->freq_change_time); + + dprintk(DEBUG_JUMPS,TAG "S: suspending at %d\n",policy->cur); + } + + reset_timer(smp_processor_id(),this_boosted); +} + +static void boosted_early_suspend(struct early_suspend *handler) { + int i; + if (suspended || sleep_ideal_freq==0) // disable behavior for sleep_ideal_freq==0 + return; + suspended = 1; + for_each_online_cpu(i) + boosted_suspend(i,1); +} + +static void boosted_late_resume(struct early_suspend *handler) { + int i; + if (!suspended) // already not suspended so nothing to do + return; + suspended = 0; + for_each_online_cpu(i) + boosted_suspend(i,0); +} + +static struct early_suspend boosted_power_suspend = { + .suspend = boosted_early_suspend, + .resume = boosted_late_resume, +#ifdef CONFIG_MACH_HERO + .level = EARLY_SUSPEND_LEVEL_DISABLE_FB + 1, +#endif +}; + +static int __init cpufreq_boosted_init(void) +{ + unsigned int i; + struct boosted_info_s *this_boosted; + SYMSEARCH_BIND_FUNCTION_TO(cpufreq_boosted, nr_running, nr_running_k); + + debug_mask = 0; + up_rate_us = DEFAULT_UP_RATE_US; + down_rate_us = DEFAULT_DOWN_RATE_US; + sleep_ideal_freq = DEFAULT_SLEEP_IDEAL_FREQ; + sleep_wakeup_freq = DEFAULT_SLEEP_WAKEUP_FREQ; + awake_ideal_freq = DEFAULT_AWAKE_IDEAL_FREQ; + sample_rate_jiffies = DEFAULT_SAMPLE_RATE_JIFFIES; + ramp_up_step = DEFAULT_RAMP_UP_STEP; + ramp_down_step = DEFAULT_RAMP_DOWN_STEP; + max_cpu_load = DEFAULT_MAX_CPU_LOAD; + min_cpu_load = DEFAULT_MIN_CPU_LOAD; + + spin_lock_init(&cpumask_lock); + + suspended = 0; + + // Initalize per-cpu data: + for_each_possible_cpu(i) { + this_boosted = &per_cpu(boosted_info, i); + this_boosted->enable = 0; + this_boosted->cur_policy = 0; + this_boosted->ramp_dir = 0; + this_boosted->time_in_idle = 0; + this_boosted->idle_exit_time = 0; + this_boosted->freq_change_time = 0; + this_boosted->freq_change_time_in_idle = 0; + this_boosted->cur_cpu_load = 0; + // intialize timer: + init_timer_deferrable(&this_boosted->timer); + this_boosted->timer.function = cpufreq_boosted_timer; + this_boosted->timer.data = i; + work_cpumask_test_and_clear(i); + } + + // Scale up is high priority + up_wq = create_rt_workqueue("kboosted_up"); + down_wq = create_workqueue("kboosted_down"); + if (!up_wq || !down_wq) + return -ENOMEM; + + INIT_WORK(&freq_scale_work, cpufreq_boosted_freq_change_time_work); + + register_early_suspend(&boosted_power_suspend); + + return cpufreq_register_governor(&cpufreq_gov_boosted); +} + +#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_BOOSTED +fs_initcall(cpufreq_boosted_init); +#else +module_init(cpufreq_boosted_init); +#endif + +static void __exit cpufreq_boosted_exit(void) +{ + cpufreq_unregister_governor(&cpufreq_gov_boosted); + destroy_workqueue(up_wq); + destroy_workqueue(down_wq); +} + +module_exit(cpufreq_boosted_exit); + +MODULE_AUTHOR ("Erasmux"); +MODULE_AUTHOR ("modified by BMc08GT"); +MODULE_AUTHOR ("fixed by Epsylon3"); +MODULE_DESCRIPTION ("cpufreq_boosted - A smart cpufreq governor"); +MODULE_VERSION ("2.0.0"); +MODULE_LICENSE ("GPL"); diff --git a/modules/overclock/cpufreq_interactive.c b/modules/overclock/cpufreq_interactive.c index 2cc9857f..5f00aabe 100644 --- a/modules/overclock/cpufreq_interactive.c +++ b/modules/overclock/cpufreq_interactive.c @@ -218,7 +218,8 @@ 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); + strict_strtoul(buf, 10, &min_sample_time); + return count; } static struct global_attr min_sample_time_attr = __ATTR(min_sample_time, 0644, @@ -289,7 +290,7 @@ static int __init cpufreq_interactive_init(void) struct timer_list *t; min_sample_time = DEFAULT_MIN_SAMPLE_TIME; - SYMSEARCH_BIND_FUNCTION_TO(cpufreq_smartass,nr_running,_nr_running); + SYMSEARCH_BIND_FUNCTION_TO(cpufreq_interactive,nr_running,_nr_running); /* Initalize per-cpu timers */ for_each_possible_cpu(i) { diff --git a/modules/overclock/cpufreq_smartass.c b/modules/overclock/cpufreq_smartass.c index 8dc97886..71699718 100644 --- a/modules/overclock/cpufreq_smartass.c +++ b/modules/overclock/cpufreq_smartass.c @@ -331,12 +331,11 @@ static ssize_t show_down_rate_us(struct cpufreq_policy *policy, char *buf) static ssize_t store_down_rate_us(struct cpufreq_policy *policy, const char *buf, size_t count) { - ssize_t res; unsigned long input; - res = strict_strtoul(buf, 0, &input); + int res = strict_strtoul(buf, 10, &input); if (res >= 0 && input >= 1000 && input <= 100000000) down_rate_us = input; - return res; + return count; } static struct freq_attr down_rate_us_attr = __ATTR(down_rate_us, 0644, @@ -349,12 +348,11 @@ static ssize_t show_up_min_freq(struct cpufreq_policy *policy, char *buf) static ssize_t store_up_min_freq(struct cpufreq_policy *policy, const char *buf, size_t count) { - ssize_t res; unsigned long input; - res = strict_strtoul(buf, 0, &input); + int res = strict_strtoul(buf, 10, &input); if (res >= 0 && input >= 0) up_min_freq = input; - return res; + return count; } static struct freq_attr up_min_freq_attr = __ATTR(up_min_freq, 0644, @@ -367,12 +365,11 @@ static ssize_t show_sleep_max_freq(struct cpufreq_policy *policy, char *buf) static ssize_t store_sleep_max_freq(struct cpufreq_policy *policy, const char *buf, size_t count) { - ssize_t res; unsigned long input; - res = strict_strtoul(buf, 0, &input); + int res = strict_strtoul(buf, 10, &input); if (res >= 0 && input >= 0) sleep_max_freq = input; - return res; + return count; } static struct freq_attr sleep_max_freq_attr = __ATTR(sleep_max_freq, 0644, @@ -385,12 +382,11 @@ static ssize_t show_sleep_wakeup_freq(struct cpufreq_policy *policy, char *buf) static ssize_t store_sleep_wakeup_freq(struct cpufreq_policy *policy, const char *buf, size_t count) { - ssize_t res; unsigned long input; - res = strict_strtoul(buf, 0, &input); + int res = strict_strtoul(buf, 10, &input); if (res >= 0 && input >= 0) sleep_wakeup_freq = input; - return res; + return count; } static struct freq_attr sleep_wakeup_freq_attr = __ATTR(sleep_wakeup_freq, 0644, @@ -403,12 +399,11 @@ static ssize_t show_awake_min_freq(struct cpufreq_policy *policy, char *buf) static ssize_t store_awake_min_freq(struct cpufreq_policy *policy, const char *buf, size_t count) { - ssize_t res; unsigned long input; - res = strict_strtoul(buf, 0, &input); + int res = strict_strtoul(buf, 10, &input); if (res >= 0 && input >= 0) awake_min_freq = input; - return res; + return count; } static struct freq_attr awake_min_freq_attr = __ATTR(awake_min_freq, 0644, @@ -421,12 +416,11 @@ static ssize_t show_sample_rate_jiffies(struct cpufreq_policy *policy, char *buf static ssize_t store_sample_rate_jiffies(struct cpufreq_policy *policy, const char *buf, size_t count) { - ssize_t res; unsigned long input; - res = strict_strtoul(buf, 0, &input); + int res = strict_strtoul(buf, 10, &input); if (res >= 0 && input > 0 && input <= 1000) sample_rate_jiffies = input; - return res; + return count; } static struct freq_attr sample_rate_jiffies_attr = __ATTR(sample_rate_jiffies, 0644, @@ -439,12 +433,11 @@ static ssize_t show_ramp_up_step(struct cpufreq_policy *policy, char *buf) static ssize_t store_ramp_up_step(struct cpufreq_policy *policy, const char *buf, size_t count) { - ssize_t res; unsigned long input; - res = strict_strtoul(buf, 0, &input); + int res = strict_strtoul(buf, 10, &input); if (res >= 0) ramp_up_step = input; - return res; + return count; } static struct freq_attr ramp_up_step_attr = __ATTR(ramp_up_step, 0644, @@ -457,12 +450,11 @@ static ssize_t show_max_ramp_down(struct cpufreq_policy *policy, char *buf) static ssize_t store_max_ramp_down(struct cpufreq_policy *policy, const char *buf, size_t count) { - ssize_t res; unsigned long input; - res = strict_strtoul(buf, 0, &input); + int res = strict_strtoul(buf, 10, &input); if (res >= 0) max_ramp_down = input; - return res; + return count; } static struct freq_attr max_ramp_down_attr = __ATTR(max_ramp_down, 0644, @@ -475,12 +467,11 @@ static ssize_t show_max_cpu_load(struct cpufreq_policy *policy, char *buf) static ssize_t store_max_cpu_load(struct cpufreq_policy *policy, const char *buf, size_t count) { - ssize_t res; unsigned long input; - res = strict_strtoul(buf, 0, &input); + int res = strict_strtoul(buf, 10, &input); if (res >= 0 && input > 0 && input <= 100) max_cpu_load = input; - return res; + return count; } static struct freq_attr max_cpu_load_attr = __ATTR(max_cpu_load, 0644, @@ -493,12 +484,11 @@ static ssize_t show_min_cpu_load(struct cpufreq_policy *policy, char *buf) static ssize_t store_min_cpu_load(struct cpufreq_policy *policy, const char *buf, size_t count) { - ssize_t res; unsigned long input; - res = strict_strtoul(buf, 0, &input); + int res = strict_strtoul(buf, 10, &input); if (res >= 0 && input > 0 && input < 100) min_cpu_load = input; - return res; + return count; } static struct freq_attr min_cpu_load_attr = __ATTR(min_cpu_load, 0644, diff --git a/modules/overclock/sio-iosched.c b/modules/overclock/sio-iosched.c new file mode 100644 index 00000000..c52a67c5 --- /dev/null +++ b/modules/overclock/sio-iosched.c @@ -0,0 +1,399 @@ +/* + * Simple IO scheduler + * Based on Noop, Deadline and V(R) IO schedulers. + * + * Copyright (C) 2012 Miguel Boton + * + * + * This algorithm does not do any kind of sorting, as it is aimed for + * aleatory access devices, but it does some basic merging. We try to + * keep minimum overhead to achieve low latency. + * + * Asynchronous and synchronous requests are not treated separately, but + * we relay on deadlines to ensure fairness. + * + */ +#include +#include +#include +#include +#include +#include + +enum { ASYNC, SYNC }; + +/* Tunables */ +static const int sync_read_expire = HZ / 2; /* max time before a sync read is submitted. */ +static const int sync_write_expire = 2 * HZ; /* max time before a sync write is submitted. */ + +static const int async_read_expire = 4 * HZ; /* ditto for async, these limits are SOFT! */ +static const int async_write_expire = 16 * HZ; /* ditto for async, these limits are SOFT! */ + +static const int writes_starved = 2; /* max times reads can starve a write */ +static const int fifo_batch = 8; /* # of sequential requests treated as one + by the above parameters. For throughput. */ + +/* Elevator data */ +struct sio_data { + /* Request queues */ + struct list_head fifo_list[2][2]; + + /* Attributes */ + unsigned int batched; + unsigned int starved; + + /* Settings */ + int fifo_expire[2][2]; + int fifo_batch; + int writes_starved; +}; + +static void +sio_merged_requests(struct request_queue *q, struct request *rq, + struct request *next) +{ + /* + * If next expires before rq, assign its expire time to rq + * and move into next position (next will be deleted) in fifo. + */ + if (!list_empty(&rq->queuelist) && !list_empty(&next->queuelist)) { + if (time_before(rq_fifo_time(next), rq_fifo_time(rq))) { + list_move(&rq->queuelist, &next->queuelist); + rq_set_fifo_time(rq, rq_fifo_time(next)); + } + } + + /* Delete next request */ + rq_fifo_clear(next); +} + +static void +sio_add_request(struct request_queue *q, struct request *rq) +{ + struct sio_data *sd = q->elevator->elevator_data; + const int sync = rq_is_sync(rq); + const int data_dir = rq_data_dir(rq); + + /* + * Add request to the proper fifo list and set its + * expire time. + */ + rq_set_fifo_time(rq, jiffies + sd->fifo_expire[sync][data_dir]); + list_add_tail(&rq->queuelist, &sd->fifo_list[sync][data_dir]); +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,38) +static int +sio_queue_empty(struct request_queue *q) +{ + struct sio_data *sd = q->elevator->elevator_data; + + /* Check if fifo lists are empty */ + return list_empty(&sd->fifo_list[SYNC][READ]) && list_empty(&sd->fifo_list[SYNC][WRITE]) && + list_empty(&sd->fifo_list[ASYNC][READ]) && list_empty(&sd->fifo_list[ASYNC][WRITE]); +} +#endif + +static struct request * +sio_expired_request(struct sio_data *sd, int sync, int data_dir) +{ + struct list_head *list = &sd->fifo_list[sync][data_dir]; + struct request *rq; + + if (list_empty(list)) + return NULL; + + /* Retrieve request */ + rq = rq_entry_fifo(list->next); + + /* Request has expired */ + if (time_after(jiffies, rq_fifo_time(rq))) + return rq; + + return NULL; +} + +static struct request * +sio_choose_expired_request(struct sio_data *sd) +{ + struct request *rq; + + /* + * Check expired requests. + * Asynchronous requests have priority over synchronous. + * Write requests have priority over read. + */ + rq = sio_expired_request(sd, ASYNC, WRITE); + if (rq) + return rq; + rq = sio_expired_request(sd, ASYNC, READ); + if (rq) + return rq; + + rq = sio_expired_request(sd, SYNC, WRITE); + if (rq) + return rq; + rq = sio_expired_request(sd, SYNC, READ); + if (rq) + return rq; + + return NULL; +} + +static struct request * +sio_choose_request(struct sio_data *sd, int data_dir) +{ + struct list_head *sync = sd->fifo_list[SYNC]; + struct list_head *async = sd->fifo_list[ASYNC]; + + /* + * Retrieve request from available fifo list. + * Synchronous requests have priority over asynchronous. + * Read requests have priority over write. + */ + if (!list_empty(&sync[data_dir])) + return rq_entry_fifo(sync[data_dir].next); + if (!list_empty(&async[data_dir])) + return rq_entry_fifo(async[data_dir].next); + + if (!list_empty(&sync[!data_dir])) + return rq_entry_fifo(sync[!data_dir].next); + if (!list_empty(&async[!data_dir])) + return rq_entry_fifo(async[!data_dir].next); + + return NULL; +} + +static inline void +sio_dispatch_request(struct sio_data *sd, struct request *rq) +{ + /* + * Remove the request from the fifo list + * and dispatch it. + */ + rq_fifo_clear(rq); + elv_dispatch_add_tail(rq->q, rq); + + sd->batched++; + + if (rq_data_dir(rq)) + sd->starved = 0; + else + sd->starved++; +} + +static int +sio_dispatch_requests(struct request_queue *q, int force) +{ + struct sio_data *sd = q->elevator->elevator_data; + struct request *rq = NULL; + int data_dir = READ; + + /* + * Retrieve any expired request after a batch of + * sequential requests. + */ + if (sd->batched > sd->fifo_batch) { + sd->batched = 0; + rq = sio_choose_expired_request(sd); + } + + /* Retrieve request */ + if (!rq) { + if (sd->starved > sd->writes_starved) + data_dir = WRITE; + + rq = sio_choose_request(sd, data_dir); + if (!rq) + return 0; + } + + /* Dispatch request */ + sio_dispatch_request(sd, rq); + + return 1; +} + +static struct request * +sio_former_request(struct request_queue *q, struct request *rq) +{ + struct sio_data *sd = q->elevator->elevator_data; + const int sync = rq_is_sync(rq); + const int data_dir = rq_data_dir(rq); + + if (rq->queuelist.prev == &sd->fifo_list[sync][data_dir]) + return NULL; + + /* Return former request */ + return list_entry(rq->queuelist.prev, struct request, queuelist); +} + +static struct request * +sio_latter_request(struct request_queue *q, struct request *rq) +{ + struct sio_data *sd = q->elevator->elevator_data; + const int sync = rq_is_sync(rq); + const int data_dir = rq_data_dir(rq); + + if (rq->queuelist.next == &sd->fifo_list[sync][data_dir]) + return NULL; + + /* Return latter request */ + return list_entry(rq->queuelist.next, struct request, queuelist); +} + +static void * +sio_init_queue(struct request_queue *q) +{ + struct sio_data *sd; + + /* Allocate structure */ + sd = kmalloc_node(sizeof(*sd), GFP_KERNEL, q->node); + if (!sd) + return NULL; + + /* Initialize fifo lists */ + INIT_LIST_HEAD(&sd->fifo_list[SYNC][READ]); + INIT_LIST_HEAD(&sd->fifo_list[SYNC][WRITE]); + INIT_LIST_HEAD(&sd->fifo_list[ASYNC][READ]); + INIT_LIST_HEAD(&sd->fifo_list[ASYNC][WRITE]); + + /* Initialize data */ + sd->batched = 0; + sd->fifo_expire[SYNC][READ] = sync_read_expire; + sd->fifo_expire[SYNC][WRITE] = sync_write_expire; + sd->fifo_expire[ASYNC][READ] = async_read_expire; + sd->fifo_expire[ASYNC][WRITE] = async_write_expire; + sd->fifo_batch = fifo_batch; + + return sd; +} + +static void +sio_exit_queue(struct elevator_queue *e) +{ + struct sio_data *sd = e->elevator_data; + + BUG_ON(!list_empty(&sd->fifo_list[SYNC][READ])); + BUG_ON(!list_empty(&sd->fifo_list[SYNC][WRITE])); + BUG_ON(!list_empty(&sd->fifo_list[ASYNC][READ])); + BUG_ON(!list_empty(&sd->fifo_list[ASYNC][WRITE])); + + /* Free structure */ + kfree(sd); +} + +/* + * sysfs code + */ + +static ssize_t +sio_var_show(int var, char *page) +{ + return sprintf(page, "%d\n", var); +} + +static ssize_t +sio_var_store(int *var, const char *page, size_t count) +{ + char *p = (char *) page; + + *var = simple_strtol(p, &p, 10); + return count; +} + +#define SHOW_FUNCTION(__FUNC, __VAR, __CONV) \ +static ssize_t __FUNC(struct elevator_queue *e, char *page) \ +{ \ + struct sio_data *sd = e->elevator_data; \ + int __data = __VAR; \ + if (__CONV) \ + __data = jiffies_to_msecs(__data); \ + return sio_var_show(__data, (page)); \ +} +SHOW_FUNCTION(sio_sync_read_expire_show, sd->fifo_expire[SYNC][READ], 1); +SHOW_FUNCTION(sio_sync_write_expire_show, sd->fifo_expire[SYNC][WRITE], 1); +SHOW_FUNCTION(sio_async_read_expire_show, sd->fifo_expire[ASYNC][READ], 1); +SHOW_FUNCTION(sio_async_write_expire_show, sd->fifo_expire[ASYNC][WRITE], 1); +SHOW_FUNCTION(sio_fifo_batch_show, sd->fifo_batch, 0); +SHOW_FUNCTION(sio_writes_starved_show, sd->writes_starved, 0); +#undef SHOW_FUNCTION + +#define STORE_FUNCTION(__FUNC, __PTR, MIN, MAX, __CONV) \ +static ssize_t __FUNC(struct elevator_queue *e, const char *page, size_t count) \ +{ \ + struct sio_data *sd = e->elevator_data; \ + int __data; \ + int ret = sio_var_store(&__data, (page), count); \ + if (__data < (MIN)) \ + __data = (MIN); \ + else if (__data > (MAX)) \ + __data = (MAX); \ + if (__CONV) \ + *(__PTR) = msecs_to_jiffies(__data); \ + else \ + *(__PTR) = __data; \ + return ret; \ +} +STORE_FUNCTION(sio_sync_read_expire_store, &sd->fifo_expire[SYNC][READ], 0, INT_MAX, 1); +STORE_FUNCTION(sio_sync_write_expire_store, &sd->fifo_expire[SYNC][WRITE], 0, INT_MAX, 1); +STORE_FUNCTION(sio_async_read_expire_store, &sd->fifo_expire[ASYNC][READ], 0, INT_MAX, 1); +STORE_FUNCTION(sio_async_write_expire_store, &sd->fifo_expire[ASYNC][WRITE], 0, INT_MAX, 1); +STORE_FUNCTION(sio_fifo_batch_store, &sd->fifo_batch, 0, INT_MAX, 0); +STORE_FUNCTION(sio_writes_starved_store, &sd->writes_starved, 0, INT_MAX, 0); +#undef STORE_FUNCTION + +#define DD_ATTR(name) \ + __ATTR(name, S_IRUGO|S_IWUSR, sio_##name##_show, \ + sio_##name##_store) + +static struct elv_fs_entry sio_attrs[] = { + DD_ATTR(sync_read_expire), + DD_ATTR(sync_write_expire), + DD_ATTR(async_read_expire), + DD_ATTR(async_write_expire), + DD_ATTR(fifo_batch), + DD_ATTR(writes_starved), + __ATTR_NULL +}; + +static struct elevator_type iosched_sio = { + .ops = { + .elevator_merge_req_fn = sio_merged_requests, + .elevator_dispatch_fn = sio_dispatch_requests, + .elevator_add_req_fn = sio_add_request, +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,38) + .elevator_queue_empty_fn = sio_queue_empty, +#endif + .elevator_former_req_fn = sio_former_request, + .elevator_latter_req_fn = sio_latter_request, + .elevator_init_fn = sio_init_queue, + .elevator_exit_fn = sio_exit_queue, + }, + + .elevator_attrs = sio_attrs, + .elevator_name = "sio", + .elevator_owner = THIS_MODULE, +}; + +static int __init sio_init(void) +{ + /* Register elevator */ + elv_register(&iosched_sio); + + return 0; +} + +static void __exit sio_exit(void) +{ + /* Unregister elevator */ + elv_unregister(&iosched_sio); +} + +module_init(sio_init); +module_exit(sio_exit); + +MODULE_AUTHOR("Miguel Boton"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Simple IO scheduler"); +MODULE_VERSION("0.2"); diff --git a/modules/proximity.ko b/modules/proximity.ko index 0db23216f132f69fcb952db77c53dead899e9172..242c25ded3b0f95f3af0250787a98f232adfe0a6 100644 GIT binary patch delta 1121 zcmZXST}V@57{{M8xAZh+)2U~3n>*&m*3fa(Ge2S%;)iY)Tdqp%Psv zePFyn+C{93KnVI^5JI3`ML`!*H(iDHg;5t?M0u4x&)I7?Jq&)&|Nnn^_df4=7rNKG z=Vi-Q5l|Ua4IXfT<7s2HD2%hyqPyZ)5T&HP3!3m{nqU- zPcrw#YXUB=Gd>U$_Re@+YF7NM?W)oqR6CS|4MDZl-`UXGq-HMWztuMzU#hiEn_H+z)^M>R}woD>muq5WrM|>&e3}KyE&f-VfLGy~wu`@ohMW_&8+lR}ozR z9-<6T%YB7d0iY810km*GK&%94BjN>hAicV3fG#p>a0VO&OmOFUsWIy8N86}i$r|&E&e8d JrnCCE?l1gMw}Sux delta 1133 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 literal 0 HcmV?d00001