From 3abbd337c60591305cbfeb984ff2922c175be37f Mon Sep 17 00:00:00 2001 From: Zhao Yakui Date: Mon, 28 Jan 2008 13:53:21 +0800 Subject: [PATCH 1/6] ACPI: Set _PSD ACPI_PDC_SMP_T_SWCOORD The ACPI_PDC_SMP_T_SWCOORD bit is set by and OS that is capable of native ACPI throttling software coordination for mutli-processors using the _TSD information. Signed-off-by: Zhao Yakui Signed-off-by: Len Brown --- arch/ia64/kernel/acpi-processor.c | 6 ++++++ arch/x86/kernel/acpi/processor.c | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/arch/ia64/kernel/acpi-processor.c b/arch/ia64/kernel/acpi-processor.c index 5a216c019924..cbe6cee5a550 100644 --- a/arch/ia64/kernel/acpi-processor.c +++ b/arch/ia64/kernel/acpi-processor.c @@ -45,6 +45,12 @@ static void init_intel_pdc(struct acpi_processor *pr) buf[0] = ACPI_PDC_REVISION_ID; buf[1] = 1; buf[2] = ACPI_PDC_EST_CAPABILITY_SMP; + /* + * The default of PDC_SMP_T_SWCOORD bit is set for IA64 cpu so + * that OSPM is capable of native ACPI throttling software + * coordination using BIOS supplied _TSD info. + */ + buf[2] |= ACPI_PDC_SMP_T_SWCOORD; obj->type = ACPI_TYPE_BUFFER; obj->buffer.length = 12; diff --git a/arch/x86/kernel/acpi/processor.c b/arch/x86/kernel/acpi/processor.c index a25db514c719..324eb0cab19c 100644 --- a/arch/x86/kernel/acpi/processor.c +++ b/arch/x86/kernel/acpi/processor.c @@ -46,6 +46,12 @@ static void init_intel_pdc(struct acpi_processor *pr, struct cpuinfo_x86 *c) buf[1] = 1; buf[2] = ACPI_PDC_C_CAPABILITY_SMP; + /* + * The default of PDC_SMP_T_SWCOORD bit is set for intel x86 cpu so + * that OSPM is capable of native ACPI throttling software + * coordination using BIOS supplied _TSD info. + */ + buf[2] |= ACPI_PDC_SMP_T_SWCOORD; if (cpu_has(c, X86_FEATURE_EST)) buf[2] |= ACPI_PDC_EST_CAPABILITY_SWSMP; From 87654273ef63213f90c4243913987436495824f0 Mon Sep 17 00:00:00 2001 From: Zhao Yakui Date: Mon, 28 Jan 2008 13:53:30 +0800 Subject: [PATCH 2/6] ACPI : Check parameter when calling acpi_processor_get/set_throttling It is necessary to check the parameter when calling the function of acpi_processor_get/set_throttling function so as to avoid the NULL pointer reference in pr or throttling. http://bugzilla.kernel.org/show_bug.cgi?id=9747 Signed-off-by: Zhao Yakui Signed-off-by: Len Brown --- drivers/acpi/processor_throttling.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/acpi/processor_throttling.c b/drivers/acpi/processor_throttling.c index 1685b40abda7..5d2eae207886 100644 --- a/drivers/acpi/processor_throttling.c +++ b/drivers/acpi/processor_throttling.c @@ -589,6 +589,11 @@ static int acpi_processor_get_throttling(struct acpi_processor *pr) cpumask_t saved_mask; int ret; + if (!pr) + return -EINVAL; + + if (!pr->flags.throttling) + return -ENODEV; /* * Migrate task to the cpu pointed by pr. */ @@ -743,6 +748,16 @@ int acpi_processor_set_throttling(struct acpi_processor *pr, int state) { cpumask_t saved_mask; int ret; + + if (!pr) + return -EINVAL; + + if (!pr->flags.throttling) + return -ENODEV; + + if ((state < 0) || (state > (pr->throttling.state_count - 1))) + return -EINVAL; + /* * Migrate task to the cpu pointed by pr. */ From 1180509f6b3ec3ac2505375a78ccd72d270f2169 Mon Sep 17 00:00:00 2001 From: Zhao Yakui Date: Mon, 28 Jan 2008 13:53:42 +0800 Subject: [PATCH 3/6] ACPI : Update T-state coordination after getting _TSD info Accordint to ACPI spec, the _TSD object provides T-state control cross logical processor dependency information to OSPM. After the _TSD data for all cpus are obtained, OSPM will set up the T-state coordination between CPUs. Of course if the _TSD doesn't exist or _TSD data is incorrect , it is assumed that there is no T-state coordination and T-state is changed independently. Now there is no proper solution to update T-state coordination after one cpu is hotplugged. So this patch won't support hotplugged cpu very well. Signed-off-by: Zhao Yakui Signed-off-by: Len Brown --- drivers/acpi/processor_core.c | 2 + drivers/acpi/processor_throttling.c | 180 +++++++++++++++++++++++++++- include/acpi/processor.h | 4 +- 3 files changed, 184 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index e48ee4f8749f..b3b537342715 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -1061,6 +1061,8 @@ static int __init acpi_processor_init(void) acpi_processor_ppc_init(); + acpi_processor_throttling_init(); + return 0; out_cpuidle: diff --git a/drivers/acpi/processor_throttling.c b/drivers/acpi/processor_throttling.c index 5d2eae207886..d6780f41d28c 100644 --- a/drivers/acpi/processor_throttling.c +++ b/drivers/acpi/processor_throttling.c @@ -48,6 +48,154 @@ ACPI_MODULE_NAME("processor_throttling"); static int acpi_processor_get_throttling(struct acpi_processor *pr); int acpi_processor_set_throttling(struct acpi_processor *pr, int state); +static int acpi_processor_update_tsd_coord(void) +{ + int count, count_target; + int retval = 0; + unsigned int i, j; + cpumask_t covered_cpus; + struct acpi_processor *pr, *match_pr; + struct acpi_tsd_package *pdomain, *match_pdomain; + struct acpi_processor_throttling *pthrottling, *match_pthrottling; + + /* + * Now that we have _TSD data from all CPUs, lets setup T-state + * coordination among all CPUs. + */ + for_each_possible_cpu(i) { + pr = processors[i]; + if (!pr) + continue; + + /* Basic validity check for domain info */ + pthrottling = &(pr->throttling); + + /* + * If tsd package for one cpu is invalid, the coordination + * among all CPUs is thought as invalid. + * Maybe it is ugly. + */ + if (!pthrottling->tsd_valid_flag) { + retval = -EINVAL; + break; + } + } + if (retval) + goto err_ret; + + cpus_clear(covered_cpus); + for_each_possible_cpu(i) { + pr = processors[i]; + if (!pr) + continue; + + if (cpu_isset(i, covered_cpus)) + continue; + pthrottling = &pr->throttling; + + pdomain = &(pthrottling->domain_info); + cpu_set(i, pthrottling->shared_cpu_map); + cpu_set(i, covered_cpus); + /* + * If the number of processor in the TSD domain is 1, it is + * unnecessary to parse the coordination for this CPU. + */ + if (pdomain->num_processors <= 1) + continue; + + /* Validate the Domain info */ + count_target = pdomain->num_processors; + count = 1; + + for_each_possible_cpu(j) { + if (i == j) + continue; + + match_pr = processors[j]; + if (!match_pr) + continue; + + match_pthrottling = &(match_pr->throttling); + match_pdomain = &(match_pthrottling->domain_info); + if (match_pdomain->domain != pdomain->domain) + continue; + + /* Here i and j are in the same domain. + * If two TSD packages have the same domain, they + * should have the same num_porcessors and + * coordination type. Otherwise it will be regarded + * as illegal. + */ + if (match_pdomain->num_processors != count_target) { + retval = -EINVAL; + goto err_ret; + } + + if (pdomain->coord_type != match_pdomain->coord_type) { + retval = -EINVAL; + goto err_ret; + } + + cpu_set(j, covered_cpus); + cpu_set(j, pthrottling->shared_cpu_map); + count++; + } + for_each_possible_cpu(j) { + if (i == j) + continue; + + match_pr = processors[j]; + if (!match_pr) + continue; + + match_pthrottling = &(match_pr->throttling); + match_pdomain = &(match_pthrottling->domain_info); + if (match_pdomain->domain != pdomain->domain) + continue; + + /* + * If some CPUS have the same domain, they + * will have the same shared_cpu_map. + */ + match_pthrottling->shared_cpu_map = + pthrottling->shared_cpu_map; + } + } + +err_ret: + for_each_possible_cpu(i) { + pr = processors[i]; + if (!pr) + continue; + + /* + * Assume no coordination on any error parsing domain info. + * The coordination type will be forced as SW_ALL. + */ + if (retval) { + pthrottling = &(pr->throttling); + cpus_clear(pthrottling->shared_cpu_map); + cpu_set(i, pthrottling->shared_cpu_map); + pthrottling->shared_type = DOMAIN_COORD_TYPE_SW_ALL; + } + } + + return retval; +} + +/* + * Update the T-state coordination after the _TSD + * data for all cpus is obtained. + */ +void acpi_processor_throttling_init(void) +{ + if (acpi_processor_update_tsd_coord()) + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Assume no T-state coordination\n")); + + return; +} + /* * _TPC - Throttling Present Capabilities */ @@ -293,6 +441,10 @@ static int acpi_processor_get_tsd(struct acpi_processor *pr) struct acpi_buffer state = { 0, NULL }; union acpi_object *tsd = NULL; struct acpi_tsd_package *pdomain; + struct acpi_processor_throttling *pthrottling; + + pthrottling = &pr->throttling; + pthrottling->tsd_valid_flag = 0; status = acpi_evaluate_object(pr->handle, "_TSD", NULL, &buffer); if (ACPI_FAILURE(status)) { @@ -340,6 +492,22 @@ static int acpi_processor_get_tsd(struct acpi_processor *pr) goto end; } + pthrottling = &pr->throttling; + pthrottling->tsd_valid_flag = 1; + pthrottling->shared_type = pdomain->coord_type; + cpu_set(pr->id, pthrottling->shared_cpu_map); + /* + * If the coordination type is not defined in ACPI spec, + * the tsd_valid_flag will be clear and coordination type + * will be forecd as DOMAIN_COORD_TYPE_SW_ALL. + */ + if (pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ALL && + pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ANY && + pdomain->coord_type != DOMAIN_COORD_TYPE_HW_ALL) { + pthrottling->tsd_valid_flag = 0; + pthrottling->shared_type = DOMAIN_COORD_TYPE_SW_ALL; + } + end: kfree(buffer.pointer); return result; @@ -772,6 +940,7 @@ int acpi_processor_set_throttling(struct acpi_processor *pr, int state) int acpi_processor_get_throttling_info(struct acpi_processor *pr) { int result = 0; + struct acpi_processor_throttling *pthrottling; ACPI_DEBUG_PRINT((ACPI_DB_INFO, "pblk_address[0x%08x] duty_offset[%d] duty_width[%d]\n", @@ -803,7 +972,16 @@ int acpi_processor_get_throttling_info(struct acpi_processor *pr) &acpi_processor_set_throttling_ptc; } - acpi_processor_get_tsd(pr); + /* + * If TSD package for one CPU can't be parsed successfully, it means + * that this CPU will have no coordination with other CPUs. + */ + if (acpi_processor_get_tsd(pr)) { + pthrottling = &pr->throttling; + pthrottling->tsd_valid_flag = 0; + cpu_set(pr->id, pthrottling->shared_cpu_map); + pthrottling->shared_type = DOMAIN_COORD_TYPE_SW_ALL; + } /* * PIIX4 Errata: We don't support throttling on the original PIIX4. diff --git a/include/acpi/processor.h b/include/acpi/processor.h index 76411b1fc4fd..d90ad0d63c24 100644 --- a/include/acpi/processor.h +++ b/include/acpi/processor.h @@ -176,6 +176,8 @@ struct acpi_processor_throttling { u32 address; u8 duty_offset; u8 duty_width; + u8 tsd_valid_flag; + unsigned int shared_type; struct acpi_processor_tx states[ACPI_PROCESSOR_MAX_THROTTLING]; }; @@ -316,7 +318,7 @@ static inline int acpi_processor_ppc_has_changed(struct acpi_processor *pr) int acpi_processor_get_throttling_info(struct acpi_processor *pr); extern int acpi_processor_set_throttling(struct acpi_processor *pr, int state); extern struct file_operations acpi_processor_throttling_fops; - +extern void acpi_processor_throttling_init(void); /* in processor_idle.c */ int acpi_processor_power_init(struct acpi_processor *pr, struct acpi_device *device); From e4aa5cb2138e7423a6edef6a76a3837b94c2f107 Mon Sep 17 00:00:00 2001 From: Zhao Yakui Date: Mon, 28 Jan 2008 13:54:46 +0800 Subject: [PATCH 4/6] ACPI : Add T-state event notifier function The t-state coordination should be considered when T-state for one cpu is changed.It means that OSPM should select one proper target T-state for the all affected cpus before updating T-state. So the function of acpi_processor_throttling_notifier is added. Before updating T-state it can be called for all the affected cpus to get the proper target T-state, which can meet the requirement of thermal, user and _TPC. After updating T-state, it can be called to update T-state flag. Signed-off-by: Zhao Yakui Signed-off-by: Len Brown --- drivers/acpi/processor_throttling.c | 72 +++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/drivers/acpi/processor_throttling.c b/drivers/acpi/processor_throttling.c index d6780f41d28c..18a873a55256 100644 --- a/drivers/acpi/processor_throttling.c +++ b/drivers/acpi/processor_throttling.c @@ -45,6 +45,14 @@ #define _COMPONENT ACPI_PROCESSOR_COMPONENT ACPI_MODULE_NAME("processor_throttling"); +struct throttling_tstate { + unsigned int cpu; /* cpu nr */ + int target_state; /* target T-state */ +}; + +#define THROTTLING_PRECHANGE (1) +#define THROTTLING_POSTCHANGE (2) + static int acpi_processor_get_throttling(struct acpi_processor *pr); int acpi_processor_set_throttling(struct acpi_processor *pr, int state); @@ -196,6 +204,70 @@ void acpi_processor_throttling_init(void) return; } +static int acpi_processor_throttling_notifier(unsigned long event, void *data) +{ + struct throttling_tstate *p_tstate = data; + struct acpi_processor *pr; + unsigned int cpu ; + int target_state; + struct acpi_processor_limit *p_limit; + struct acpi_processor_throttling *p_throttling; + + cpu = p_tstate->cpu; + pr = processors[cpu]; + if (!pr) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Invalid pr pointer\n")); + return 0; + } + if (!pr->flags.throttling) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Throttling control is " + "unsupported on CPU %d\n", cpu)); + return 0; + } + target_state = p_tstate->target_state; + p_throttling = &(pr->throttling); + switch (event) { + case THROTTLING_PRECHANGE: + /* + * Prechange event is used to choose one proper t-state, + * which meets the limits of thermal, user and _TPC. + */ + p_limit = &pr->limit; + if (p_limit->thermal.tx > target_state) + target_state = p_limit->thermal.tx; + if (p_limit->user.tx > target_state) + target_state = p_limit->user.tx; + if (pr->throttling_platform_limit > target_state) + target_state = pr->throttling_platform_limit; + if (target_state >= p_throttling->state_count) { + printk(KERN_WARNING + "Exceed the limit of T-state \n"); + target_state = p_throttling->state_count - 1; + } + p_tstate->target_state = target_state; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "PreChange Event:" + "target T-state of CPU %d is T%d\n", + cpu, target_state)); + break; + case THROTTLING_POSTCHANGE: + /* + * Postchange event is only used to update the + * T-state flag of acpi_processor_throttling. + */ + p_throttling->state = target_state; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "PostChange Event:" + "CPU %d is switched to T%d\n", + cpu, target_state)); + break; + default: + printk(KERN_WARNING + "Unsupported Throttling notifier event\n"); + break; + } + + return 0; +} + /* * _TPC - Throttling Present Capabilities */ From 33a2a529f7a2fb481812b99737176628fda457b0 Mon Sep 17 00:00:00 2001 From: Zhao Yakui Date: Mon, 28 Jan 2008 13:55:56 +0800 Subject: [PATCH 5/6] ACPI: Update the t-state for every affected cpu when t-state is changed According to ACPI spec, the _TSD object provides T-state control cross logical processor dependency information to OSPM. So the t-state coordination should be considered when T-state for one cpu is changed. According to ACPI spec, three types of coordination are defined. SW_ALL, SW_ANY and HW_ALL. SW_ALL: it means that OSPM needs to initiate T-state transition on all processors in the domain. It is necessary to call throttling set function for all affected cpus. SW_ANY: it means that OSPM may initiate T-state transition on any processor in the domain. HW_ALL: Spec only says that hardware will perform the coordination and doesn't recommend how OSPM coordinate T-state among the affected cpus. So it is treated as the type of SW_ALL. It means that OSPM needs to initiate t-state transition on all the processors in the domain. Signed-off-by: Zhao Yakui Signed-off-by: Len Brown --- drivers/acpi/processor_throttling.c | 81 ++++++++++++++++++++++++++--- 1 file changed, 75 insertions(+), 6 deletions(-) diff --git a/drivers/acpi/processor_throttling.c b/drivers/acpi/processor_throttling.c index 18a873a55256..86c790e9f4fc 100644 --- a/drivers/acpi/processor_throttling.c +++ b/drivers/acpi/processor_throttling.c @@ -68,7 +68,7 @@ static int acpi_processor_update_tsd_coord(void) /* * Now that we have _TSD data from all CPUs, lets setup T-state - * coordination among all CPUs. + * coordination between all CPUs. */ for_each_possible_cpu(i) { pr = processors[i]; @@ -988,6 +988,11 @@ int acpi_processor_set_throttling(struct acpi_processor *pr, int state) { cpumask_t saved_mask; int ret; + unsigned int i; + struct acpi_processor *match_pr; + struct acpi_processor_throttling *p_throttling; + struct throttling_tstate t_state; + cpumask_t online_throttling_cpus; if (!pr) return -EINVAL; @@ -998,12 +1003,76 @@ int acpi_processor_set_throttling(struct acpi_processor *pr, int state) if ((state < 0) || (state > (pr->throttling.state_count - 1))) return -EINVAL; - /* - * Migrate task to the cpu pointed by pr. - */ saved_mask = current->cpus_allowed; - set_cpus_allowed(current, cpumask_of_cpu(pr->id)); - ret = pr->throttling.acpi_processor_set_throttling(pr, state); + t_state.target_state = state; + p_throttling = &(pr->throttling); + cpus_and(online_throttling_cpus, cpu_online_map, + p_throttling->shared_cpu_map); + /* + * The throttling notifier will be called for every + * affected cpu in order to get one proper T-state. + * The notifier event is THROTTLING_PRECHANGE. + */ + for_each_cpu_mask(i, online_throttling_cpus) { + t_state.cpu = i; + acpi_processor_throttling_notifier(THROTTLING_PRECHANGE, + &t_state); + } + /* + * The function of acpi_processor_set_throttling will be called + * to switch T-state. If the coordination type is SW_ALL or HW_ALL, + * it is necessary to call it for every affected cpu. Otherwise + * it can be called only for the cpu pointed by pr. + */ + if (p_throttling->shared_type == DOMAIN_COORD_TYPE_SW_ANY) { + set_cpus_allowed(current, cpumask_of_cpu(pr->id)); + ret = p_throttling->acpi_processor_set_throttling(pr, + t_state.target_state); + } else { + /* + * When the T-state coordination is SW_ALL or HW_ALL, + * it is necessary to set T-state for every affected + * cpus. + */ + for_each_cpu_mask(i, online_throttling_cpus) { + match_pr = processors[i]; + /* + * If the pointer is invalid, we will report the + * error message and continue. + */ + if (!match_pr) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Invalid Pointer for CPU %d\n", i)); + continue; + } + /* + * If the throttling control is unsupported on CPU i, + * we will report the error message and continue. + */ + if (!match_pr->flags.throttling) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Throttling Controll is unsupported " + "on CPU %d\n", i)); + continue; + } + t_state.cpu = i; + set_cpus_allowed(current, cpumask_of_cpu(i)); + ret = match_pr->throttling. + acpi_processor_set_throttling( + match_pr, t_state.target_state); + } + } + /* + * After the set_throttling is called, the + * throttling notifier is called for every + * affected cpu to update the T-states. + * The notifier event is THROTTLING_POSTCHANGE + */ + for_each_cpu_mask(i, online_throttling_cpus) { + t_state.cpu = i; + acpi_processor_throttling_notifier(THROTTLING_POSTCHANGE, + &t_state); + } /* restore the previous state */ set_cpus_allowed(current, saved_mask); return ret; From 3391a76f2bbb74e42b9ba44c05a7366ffd388753 Mon Sep 17 00:00:00 2001 From: Len Brown Date: Sat, 2 Feb 2008 03:56:18 -0500 Subject: [PATCH 6/6] ACPI: throttling: fix build warning Signed-off-by: Len Brown --- drivers/acpi/processor_throttling.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/processor_throttling.c b/drivers/acpi/processor_throttling.c index 86c790e9f4fc..1b8e592a8241 100644 --- a/drivers/acpi/processor_throttling.c +++ b/drivers/acpi/processor_throttling.c @@ -987,7 +987,7 @@ static int acpi_processor_set_throttling_ptc(struct acpi_processor *pr, int acpi_processor_set_throttling(struct acpi_processor *pr, int state) { cpumask_t saved_mask; - int ret; + int ret = 0; unsigned int i; struct acpi_processor *match_pr; struct acpi_processor_throttling *p_throttling;