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 | |
| 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
| -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 */ |
