diff options
author | Tejun Heo <tj@kernel.org> | 2012-09-18 17:24:59 -0400 |
---|---|---|
committer | Tejun Heo <tj@kernel.org> | 2012-09-19 13:15:01 -0400 |
commit | 6889125b8b4e09c5e53e6ecab3433bed1ce198c9 (patch) | |
tree | f7408140458c2d287014fb66d2b474af01ae9658 /drivers/cpufreq/powernow-k8.c | |
parent | ed48ece27cd3d5ee0354c32bbaec0f3e1d4715c3 (diff) |
cpufreq/powernow-k8: workqueue user shouldn't migrate the kworker to another CPU
powernowk8_target() runs off a per-cpu work item and if the
cpufreq_policy->cpu is different from the current one, it migrates the
kworker to the target CPU by manipulating current->cpus_allowed. The
function migrates the kworker back to the original CPU but this is
still broken. Workqueue concurrency management requires the kworkers
to stay on the same CPU and powernowk8_target() ends up triggerring
BUG_ON(rq != this_rq()) in try_to_wake_up_local() if it contends on
fidvid_mutex and sleeps.
It is unclear why this bug is being reported now. Duncan says it
appeared to be a regression of 3.6-rc1 and couldn't reproduce it on
3.5. Bisection seemed to point to 63d95a91 "workqueue: use @pool
instead of @gcwq or @cpu where applicable" which is an non-functional
change. Given that the reproduce case sometimes took upto days to
trigger, it's easy to be misled while bisecting. Maybe something made
contention on fidvid_mutex more likely? I don't know.
This patch fixes the bug by using work_on_cpu() instead if @pol->cpu
isn't the same as the current one. The code assumes that
cpufreq_policy->cpu is kept online by the caller, which Rafael tells
me is the case.
stable: ed48ece27c ("workqueue: reimplement work_on_cpu() using
system_wq") should be applied before this; otherwise, the
behavior could be horrible.
Signed-off-by: Tejun Heo <tj@kernel.org>
Reported-by: Duncan <1i5t5.duncan@cox.net>
Tested-by: Duncan <1i5t5.duncan@cox.net>
Cc: Rafael J. Wysocki <rjw@sisk.pl>
Cc: Andreas Herrmann <andreas.herrmann3@amd.com>
Cc: stable@vger.kernel.org
Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=47301
Diffstat (limited to 'drivers/cpufreq/powernow-k8.c')
-rw-r--r-- | drivers/cpufreq/powernow-k8.c | 63 |
1 files changed, 34 insertions, 29 deletions
diff --git a/drivers/cpufreq/powernow-k8.c b/drivers/cpufreq/powernow-k8.c index c0e816468e30..1a40935c85fd 100644 --- a/drivers/cpufreq/powernow-k8.c +++ b/drivers/cpufreq/powernow-k8.c | |||
@@ -35,7 +35,6 @@ | |||
35 | #include <linux/slab.h> | 35 | #include <linux/slab.h> |
36 | #include <linux/string.h> | 36 | #include <linux/string.h> |
37 | #include <linux/cpumask.h> | 37 | #include <linux/cpumask.h> |
38 | #include <linux/sched.h> /* for current / set_cpus_allowed() */ | ||
39 | #include <linux/io.h> | 38 | #include <linux/io.h> |
40 | #include <linux/delay.h> | 39 | #include <linux/delay.h> |
41 | 40 | ||
@@ -1139,16 +1138,23 @@ static int transition_frequency_pstate(struct powernow_k8_data *data, | |||
1139 | return res; | 1138 | return res; |
1140 | } | 1139 | } |
1141 | 1140 | ||
1142 | /* Driver entry point to switch to the target frequency */ | 1141 | struct powernowk8_target_arg { |
1143 | static int powernowk8_target(struct cpufreq_policy *pol, | 1142 | struct cpufreq_policy *pol; |
1144 | unsigned targfreq, unsigned relation) | 1143 | unsigned targfreq; |
1144 | unsigned relation; | ||
1145 | }; | ||
1146 | |||
1147 | static long powernowk8_target_fn(void *arg) | ||
1145 | { | 1148 | { |
1146 | cpumask_var_t oldmask; | 1149 | struct powernowk8_target_arg *pta = arg; |
1150 | struct cpufreq_policy *pol = pta->pol; | ||
1151 | unsigned targfreq = pta->targfreq; | ||
1152 | unsigned relation = pta->relation; | ||
1147 | struct powernow_k8_data *data = per_cpu(powernow_data, pol->cpu); | 1153 | struct powernow_k8_data *data = per_cpu(powernow_data, pol->cpu); |
1148 | u32 checkfid; | 1154 | u32 checkfid; |
1149 | u32 checkvid; | 1155 | u32 checkvid; |
1150 | unsigned int newstate; | 1156 | unsigned int newstate; |
1151 | int ret = -EIO; | 1157 | int ret; |
1152 | 1158 | ||
1153 | if (!data) | 1159 | if (!data) |
1154 | return -EINVAL; | 1160 | return -EINVAL; |
@@ -1156,29 +1162,16 @@ static int powernowk8_target(struct cpufreq_policy *pol, | |||
1156 | checkfid = data->currfid; | 1162 | checkfid = data->currfid; |
1157 | checkvid = data->currvid; | 1163 | checkvid = data->currvid; |
1158 | 1164 | ||
1159 | /* only run on specific CPU from here on. */ | ||
1160 | /* This is poor form: use a workqueue or smp_call_function_single */ | ||
1161 | if (!alloc_cpumask_var(&oldmask, GFP_KERNEL)) | ||
1162 | return -ENOMEM; | ||
1163 | |||
1164 | cpumask_copy(oldmask, tsk_cpus_allowed(current)); | ||
1165 | set_cpus_allowed_ptr(current, cpumask_of(pol->cpu)); | ||
1166 | |||
1167 | if (smp_processor_id() != pol->cpu) { | ||
1168 | printk(KERN_ERR PFX "limiting to cpu %u failed\n", pol->cpu); | ||
1169 | goto err_out; | ||
1170 | } | ||
1171 | |||
1172 | if (pending_bit_stuck()) { | 1165 | if (pending_bit_stuck()) { |
1173 | printk(KERN_ERR PFX "failing targ, change pending bit set\n"); | 1166 | printk(KERN_ERR PFX "failing targ, change pending bit set\n"); |
1174 | goto err_out; | 1167 | return -EIO; |
1175 | } | 1168 | } |
1176 | 1169 | ||
1177 | pr_debug("targ: cpu %d, %d kHz, min %d, max %d, relation %d\n", | 1170 | pr_debug("targ: cpu %d, %d kHz, min %d, max %d, relation %d\n", |
1178 | pol->cpu, targfreq, pol->min, pol->max, relation); | 1171 | pol->cpu, targfreq, pol->min, pol->max, relation); |
1179 | 1172 | ||
1180 | if (query_current_values_with_pending_wait(data)) | 1173 | if (query_current_values_with_pending_wait(data)) |
1181 | goto err_out; | 1174 | return -EIO; |
1182 | 1175 | ||
1183 | if (cpu_family != CPU_HW_PSTATE) { | 1176 | if (cpu_family != CPU_HW_PSTATE) { |
1184 | pr_debug("targ: curr fid 0x%x, vid 0x%x\n", | 1177 | pr_debug("targ: curr fid 0x%x, vid 0x%x\n", |
@@ -1196,7 +1189,7 @@ static int powernowk8_target(struct cpufreq_policy *pol, | |||
1196 | 1189 | ||
1197 | if (cpufreq_frequency_table_target(pol, data->powernow_table, | 1190 | if (cpufreq_frequency_table_target(pol, data->powernow_table, |
1198 | targfreq, relation, &newstate)) | 1191 | targfreq, relation, &newstate)) |
1199 | goto err_out; | 1192 | return -EIO; |
1200 | 1193 | ||
1201 | mutex_lock(&fidvid_mutex); | 1194 | mutex_lock(&fidvid_mutex); |
1202 | 1195 | ||
@@ -1209,9 +1202,8 @@ static int powernowk8_target(struct cpufreq_policy *pol, | |||
1209 | ret = transition_frequency_fidvid(data, newstate); | 1202 | ret = transition_frequency_fidvid(data, newstate); |
1210 | if (ret) { | 1203 | if (ret) { |
1211 | printk(KERN_ERR PFX "transition frequency failed\n"); | 1204 | printk(KERN_ERR PFX "transition frequency failed\n"); |
1212 | ret = 1; | ||
1213 | mutex_unlock(&fidvid_mutex); | 1205 | mutex_unlock(&fidvid_mutex); |
1214 | goto err_out; | 1206 | return 1; |
1215 | } | 1207 | } |
1216 | mutex_unlock(&fidvid_mutex); | 1208 | mutex_unlock(&fidvid_mutex); |
1217 | 1209 | ||
@@ -1220,12 +1212,25 @@ static int powernowk8_target(struct cpufreq_policy *pol, | |||
1220 | data->powernow_table[newstate].index); | 1212 | data->powernow_table[newstate].index); |
1221 | else | 1213 | else |
1222 | pol->cur = find_khz_freq_from_fid(data->currfid); | 1214 | pol->cur = find_khz_freq_from_fid(data->currfid); |
1223 | ret = 0; | ||
1224 | 1215 | ||
1225 | err_out: | 1216 | return 0; |
1226 | set_cpus_allowed_ptr(current, oldmask); | 1217 | } |
1227 | free_cpumask_var(oldmask); | 1218 | |
1228 | return ret; | 1219 | /* Driver entry point to switch to the target frequency */ |
1220 | static int powernowk8_target(struct cpufreq_policy *pol, | ||
1221 | unsigned targfreq, unsigned relation) | ||
1222 | { | ||
1223 | struct powernowk8_target_arg pta = { .pol = pol, .targfreq = targfreq, | ||
1224 | .relation = relation }; | ||
1225 | |||
1226 | /* | ||
1227 | * Must run on @pol->cpu. cpufreq core is responsible for ensuring | ||
1228 | * that we're bound to the current CPU and pol->cpu stays online. | ||
1229 | */ | ||
1230 | if (smp_processor_id() == pol->cpu) | ||
1231 | return powernowk8_target_fn(&pta); | ||
1232 | else | ||
1233 | return work_on_cpu(pol->cpu, powernowk8_target_fn, &pta); | ||
1229 | } | 1234 | } |
1230 | 1235 | ||
1231 | /* Driver entry point to verify the policy and range of frequencies */ | 1236 | /* Driver entry point to verify the policy and range of frequencies */ |