Commit f3ca4164 authored by Lan Tianyu's avatar Lan Tianyu Committed by Rafael J. Wysocki

ACPI / processor: Rework processor throttling with work_on_cpu()

acpi_processor_set_throttling() uses set_cpus_allowed_ptr() to make
sure that the (struct acpi_processor)->acpi_processor_set_throttling()
callback will run on the right CPU.  However, the function may be
called from a worker thread already bound to a different CPU in which
case that won't work.

Make acpi_processor_set_throttling() use work_on_cpu() as appropriate
instead of abusing set_cpus_allowed_ptr().
Reported-and-tested-by: default avatarJiri Olsa <jolsa@redhat.com>
Signed-off-by: default avatarLan Tianyu <tianyu.lan@intel.com>
Cc: All applicable <stable@vger.kernel.org>
[rjw: Changelog]
Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
parent cfbf8d48
...@@ -56,6 +56,12 @@ struct throttling_tstate { ...@@ -56,6 +56,12 @@ struct throttling_tstate {
int target_state; /* target T-state */ int target_state; /* target T-state */
}; };
struct acpi_processor_throttling_arg {
struct acpi_processor *pr;
int target_state;
bool force;
};
#define THROTTLING_PRECHANGE (1) #define THROTTLING_PRECHANGE (1)
#define THROTTLING_POSTCHANGE (2) #define THROTTLING_POSTCHANGE (2)
...@@ -1060,16 +1066,24 @@ static int acpi_processor_set_throttling_ptc(struct acpi_processor *pr, ...@@ -1060,16 +1066,24 @@ static int acpi_processor_set_throttling_ptc(struct acpi_processor *pr,
return 0; return 0;
} }
static long acpi_processor_throttling_fn(void *data)
{
struct acpi_processor_throttling_arg *arg = data;
struct acpi_processor *pr = arg->pr;
return pr->throttling.acpi_processor_set_throttling(pr,
arg->target_state, arg->force);
}
int acpi_processor_set_throttling(struct acpi_processor *pr, int acpi_processor_set_throttling(struct acpi_processor *pr,
int state, bool force) int state, bool force)
{ {
cpumask_var_t saved_mask;
int ret = 0; int ret = 0;
unsigned int i; unsigned int i;
struct acpi_processor *match_pr; struct acpi_processor *match_pr;
struct acpi_processor_throttling *p_throttling; struct acpi_processor_throttling *p_throttling;
struct acpi_processor_throttling_arg arg;
struct throttling_tstate t_state; struct throttling_tstate t_state;
cpumask_var_t online_throttling_cpus;
if (!pr) if (!pr)
return -EINVAL; return -EINVAL;
...@@ -1080,14 +1094,6 @@ int acpi_processor_set_throttling(struct acpi_processor *pr, ...@@ -1080,14 +1094,6 @@ int acpi_processor_set_throttling(struct acpi_processor *pr,
if ((state < 0) || (state > (pr->throttling.state_count - 1))) if ((state < 0) || (state > (pr->throttling.state_count - 1)))
return -EINVAL; return -EINVAL;
if (!alloc_cpumask_var(&saved_mask, GFP_KERNEL))
return -ENOMEM;
if (!alloc_cpumask_var(&online_throttling_cpus, GFP_KERNEL)) {
free_cpumask_var(saved_mask);
return -ENOMEM;
}
if (cpu_is_offline(pr->id)) { if (cpu_is_offline(pr->id)) {
/* /*
* the cpu pointed by pr->id is offline. Unnecessary to change * the cpu pointed by pr->id is offline. Unnecessary to change
...@@ -1096,17 +1102,15 @@ int acpi_processor_set_throttling(struct acpi_processor *pr, ...@@ -1096,17 +1102,15 @@ int acpi_processor_set_throttling(struct acpi_processor *pr,
return -ENODEV; return -ENODEV;
} }
cpumask_copy(saved_mask, &current->cpus_allowed);
t_state.target_state = state; t_state.target_state = state;
p_throttling = &(pr->throttling); p_throttling = &(pr->throttling);
cpumask_and(online_throttling_cpus, cpu_online_mask,
p_throttling->shared_cpu_map);
/* /*
* The throttling notifier will be called for every * The throttling notifier will be called for every
* affected cpu in order to get one proper T-state. * affected cpu in order to get one proper T-state.
* The notifier event is THROTTLING_PRECHANGE. * The notifier event is THROTTLING_PRECHANGE.
*/ */
for_each_cpu(i, online_throttling_cpus) { for_each_cpu_and(i, cpu_online_mask, p_throttling->shared_cpu_map) {
t_state.cpu = i; t_state.cpu = i;
acpi_processor_throttling_notifier(THROTTLING_PRECHANGE, acpi_processor_throttling_notifier(THROTTLING_PRECHANGE,
&t_state); &t_state);
...@@ -1118,21 +1122,18 @@ int acpi_processor_set_throttling(struct acpi_processor *pr, ...@@ -1118,21 +1122,18 @@ int acpi_processor_set_throttling(struct acpi_processor *pr,
* it can be called only for the cpu pointed by pr. * it can be called only for the cpu pointed by pr.
*/ */
if (p_throttling->shared_type == DOMAIN_COORD_TYPE_SW_ANY) { if (p_throttling->shared_type == DOMAIN_COORD_TYPE_SW_ANY) {
/* FIXME: use work_on_cpu() */ arg.pr = pr;
if (set_cpus_allowed_ptr(current, cpumask_of(pr->id))) { arg.target_state = state;
/* Can't migrate to the pr->id CPU. Exit */ arg.force = force;
ret = -ENODEV; ret = work_on_cpu(pr->id, acpi_processor_throttling_fn, &arg);
goto exit;
}
ret = p_throttling->acpi_processor_set_throttling(pr,
t_state.target_state, force);
} else { } else {
/* /*
* When the T-state coordination is SW_ALL or HW_ALL, * When the T-state coordination is SW_ALL or HW_ALL,
* it is necessary to set T-state for every affected * it is necessary to set T-state for every affected
* cpus. * cpus.
*/ */
for_each_cpu(i, online_throttling_cpus) { for_each_cpu_and(i, cpu_online_mask,
p_throttling->shared_cpu_map) {
match_pr = per_cpu(processors, i); match_pr = per_cpu(processors, i);
/* /*
* If the pointer is invalid, we will report the * If the pointer is invalid, we will report the
...@@ -1153,13 +1154,12 @@ int acpi_processor_set_throttling(struct acpi_processor *pr, ...@@ -1153,13 +1154,12 @@ int acpi_processor_set_throttling(struct acpi_processor *pr,
"on CPU %d\n", i)); "on CPU %d\n", i));
continue; continue;
} }
t_state.cpu = i;
/* FIXME: use work_on_cpu() */ arg.pr = match_pr;
if (set_cpus_allowed_ptr(current, cpumask_of(i))) arg.target_state = state;
continue; arg.force = force;
ret = match_pr->throttling. ret = work_on_cpu(pr->id, acpi_processor_throttling_fn,
acpi_processor_set_throttling( &arg);
match_pr, t_state.target_state, force);
} }
} }
/* /*
...@@ -1168,17 +1168,12 @@ int acpi_processor_set_throttling(struct acpi_processor *pr, ...@@ -1168,17 +1168,12 @@ int acpi_processor_set_throttling(struct acpi_processor *pr,
* affected cpu to update the T-states. * affected cpu to update the T-states.
* The notifier event is THROTTLING_POSTCHANGE * The notifier event is THROTTLING_POSTCHANGE
*/ */
for_each_cpu(i, online_throttling_cpus) { for_each_cpu_and(i, cpu_online_mask, p_throttling->shared_cpu_map) {
t_state.cpu = i; t_state.cpu = i;
acpi_processor_throttling_notifier(THROTTLING_POSTCHANGE, acpi_processor_throttling_notifier(THROTTLING_POSTCHANGE,
&t_state); &t_state);
} }
/* restore the previous state */
/* FIXME: use work_on_cpu() */
set_cpus_allowed_ptr(current, saved_mask);
exit:
free_cpumask_var(online_throttling_cpus);
free_cpumask_var(saved_mask);
return ret; return ret;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment