1233 lines · 30.0 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* * processor_throttling.c - Throttling submodule of the ACPI processor driver * *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> *  Copyright (C) 2004       Dominik Brodowski <linux@brodo.de> *  Copyright (C) 2004  Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com> *                      - Added processor hotplug support */ #define pr_fmt(fmt) "ACPI: " fmt #include <linux/kernel.h>#include <linux/module.h>#include <linux/slab.h>#include <linux/init.h>#include <linux/sched.h>#include <linux/cpufreq.h>#include <linux/acpi.h>#include <linux/uaccess.h>#include <acpi/processor.h>#include <asm/io.h>#ifdef CONFIG_X86#include <asm/msr.h>#endif /* ignore_tpc: *  0 -> acpi processor driver doesn't ignore _TPC values *  1 -> acpi processor driver ignores _TPC values */static int ignore_tpc;module_param(ignore_tpc, int, 0644);MODULE_PARM_DESC(ignore_tpc, "Disable broken BIOS _TPC throttling support"); struct throttling_tstate {	unsigned int cpu;		/* cpu nr */	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_POSTCHANGE      (2) static int acpi_processor_get_throttling(struct acpi_processor *pr);static int __acpi_processor_set_throttling(struct acpi_processor *pr,					   int state, bool force, bool direct); static int acpi_processor_update_tsd_coord(void){	int count_target;	int retval = 0;	unsigned int i, j;	cpumask_var_t covered_cpus;	struct acpi_processor *pr, *match_pr;	struct acpi_tsd_package *pdomain, *match_pdomain;	struct acpi_processor_throttling *pthrottling, *match_pthrottling; 	if (!zalloc_cpumask_var(&covered_cpus, GFP_KERNEL))		return -ENOMEM; 	/*	 * Now that we have _TSD data from all CPUs, lets setup T-state	 * coordination between all CPUs.	 */	for_each_possible_cpu(i) {		pr = per_cpu(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; 	for_each_possible_cpu(i) {		pr = per_cpu(processors, i);		if (!pr)			continue; 		if (cpumask_test_cpu(i, covered_cpus))			continue;		pthrottling = &pr->throttling; 		pdomain = &(pthrottling->domain_info);		cpumask_set_cpu(i, pthrottling->shared_cpu_map);		cpumask_set_cpu(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; 		for_each_possible_cpu(j) {			if (i == j)				continue; 			match_pr = per_cpu(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;			} 			cpumask_set_cpu(j, covered_cpus);			cpumask_set_cpu(j, pthrottling->shared_cpu_map);		}		for_each_possible_cpu(j) {			if (i == j)				continue; 			match_pr = per_cpu(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.			 */			cpumask_copy(match_pthrottling->shared_cpu_map,				     pthrottling->shared_cpu_map);		}	} err_ret:	free_cpumask_var(covered_cpus); 	for_each_possible_cpu(i) {		pr = per_cpu(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);			cpumask_clear(pthrottling->shared_cpu_map);			cpumask_set_cpu(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())		pr_debug("Assume no T-state coordination\n");} 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 = per_cpu(processors, cpu);	if (!pr) {		pr_debug("Invalid pr pointer\n");		return 0;	}	if (!pr->flags.throttling) {		acpi_handle_debug(pr->handle,				  "Throttling control 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) {			pr_warn("Exceed the limit of T-state\n");			target_state = p_throttling->state_count - 1;		}		p_tstate->target_state = target_state;		acpi_handle_debug(pr->handle,				  "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_handle_debug(pr->handle,				  "PostChange Event: CPU %d is switched to T%d\n",				  cpu, target_state);		break;	default:		pr_warn("Unsupported Throttling notifier event\n");		break;	} 	return 0;} /* * _TPC - Throttling Present Capabilities */static int acpi_processor_get_platform_limit(struct acpi_processor *pr){	acpi_status status = 0;	unsigned long long tpc = 0; 	if (!pr)		return -EINVAL; 	if (ignore_tpc)		goto end; 	status = acpi_evaluate_integer(pr->handle, "_TPC", NULL, &tpc);	if (ACPI_FAILURE(status)) {		if (status != AE_NOT_FOUND)			acpi_evaluation_failure_warn(pr->handle, "_TPC", status); 		return -ENODEV;	} end:	pr->throttling_platform_limit = (int)tpc;	return 0;} int acpi_processor_tstate_has_changed(struct acpi_processor *pr){	int result = 0;	int throttling_limit;	int current_state;	struct acpi_processor_limit *limit;	int target_state; 	if (ignore_tpc)		return 0; 	result = acpi_processor_get_platform_limit(pr);	if (result) {		/* Throttling Limit is unsupported */		return result;	} 	throttling_limit = pr->throttling_platform_limit;	if (throttling_limit >= pr->throttling.state_count) {		/* Uncorrect Throttling Limit */		return -EINVAL;	} 	current_state = pr->throttling.state;	if (current_state > throttling_limit) {		/*		 * The current state can meet the requirement of		 * _TPC limit. But it is reasonable that OSPM changes		 * t-states from high to low for better performance.		 * Of course the limit condition of thermal		 * and user should be considered.		 */		limit = &pr->limit;		target_state = throttling_limit;		if (limit->thermal.tx > target_state)			target_state = limit->thermal.tx;		if (limit->user.tx > target_state)			target_state = limit->user.tx;	} else if (current_state == throttling_limit) {		/*		 * Unnecessary to change the throttling state		 */		return 0;	} else {		/*		 * If the current state is lower than the limit of _TPC, it		 * will be forced to switch to the throttling state defined		 * by throttling_platfor_limit.		 * Because the previous state meets with the limit condition		 * of thermal and user, it is unnecessary to check it again.		 */		target_state = throttling_limit;	}	return acpi_processor_set_throttling(pr, target_state, false);} /* * This function is used to reevaluate whether the T-state is valid * after one CPU is onlined/offlined. * It is noted that it won't reevaluate the following properties for * the T-state. *	1. Control method. *	2. the number of supported T-state *	3. TSD domain */void acpi_processor_reevaluate_tstate(struct acpi_processor *pr,					bool is_dead){	int result = 0; 	if (is_dead) {		/* When one CPU is offline, the T-state throttling		 * will be invalidated.		 */		pr->flags.throttling = 0;		return;	}	/* the following is to recheck whether the T-state is valid for	 * the online CPU	 */	if (!pr->throttling.state_count) {		/* If the number of T-state is invalid, it is		 * invalidated.		 */		pr->flags.throttling = 0;		return;	}	pr->flags.throttling = 1; 	/* Disable throttling (if enabled).  We'll let subsequent	 * policy (e.g.thermal) decide to lower performance if it	 * so chooses, but for now we'll crank up the speed.	 */ 	result = acpi_processor_get_throttling(pr);	if (result)		goto end; 	if (pr->throttling.state) {		result = acpi_processor_set_throttling(pr, 0, false);		if (result)			goto end;	} end:	if (result)		pr->flags.throttling = 0;}/* * _PTC - Processor Throttling Control (and status) register location */static int acpi_processor_get_throttling_control(struct acpi_processor *pr){	int result = 0;	acpi_status status = 0;	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };	union acpi_object *ptc = NULL;	union acpi_object obj;	struct acpi_processor_throttling *throttling; 	status = acpi_evaluate_object(pr->handle, "_PTC", NULL, &buffer);	if (ACPI_FAILURE(status)) {		if (status != AE_NOT_FOUND)			acpi_evaluation_failure_warn(pr->handle, "_PTC", status); 		return -ENODEV;	} 	ptc = (union acpi_object *)buffer.pointer;	if (!ptc || (ptc->type != ACPI_TYPE_PACKAGE)	    || (ptc->package.count != 2)) {		pr_err("Invalid _PTC data\n");		result = -EFAULT;		goto end;	} 	/*	 * control_register	 */ 	obj = ptc->package.elements[0]; 	if ((obj.type != ACPI_TYPE_BUFFER)	    || (obj.buffer.length < sizeof(struct acpi_ptc_register))	    || (obj.buffer.pointer == NULL)) {		pr_err("Invalid _PTC data (control_register)\n");		result = -EFAULT;		goto end;	}	memcpy(&pr->throttling.control_register, obj.buffer.pointer,	       sizeof(struct acpi_ptc_register)); 	/*	 * status_register	 */ 	obj = ptc->package.elements[1]; 	if ((obj.type != ACPI_TYPE_BUFFER)	    || (obj.buffer.length < sizeof(struct acpi_ptc_register))	    || (obj.buffer.pointer == NULL)) {		pr_err("Invalid _PTC data (status_register)\n");		result = -EFAULT;		goto end;	} 	memcpy(&pr->throttling.status_register, obj.buffer.pointer,	       sizeof(struct acpi_ptc_register)); 	throttling = &pr->throttling; 	if ((throttling->control_register.bit_width +		throttling->control_register.bit_offset) > 32) {		pr_err("Invalid _PTC control register\n");		result = -EFAULT;		goto end;	} 	if ((throttling->status_register.bit_width +		throttling->status_register.bit_offset) > 32) {		pr_err("Invalid _PTC status register\n");		result = -EFAULT;		goto end;	} end:	kfree(buffer.pointer); 	return result;} /* * _TSS - Throttling Supported States */static int acpi_processor_get_throttling_states(struct acpi_processor *pr){	int result = 0;	acpi_status status = AE_OK;	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };	struct acpi_buffer format = { sizeof("NNNNN"), "NNNNN" };	struct acpi_buffer state = { 0, NULL };	union acpi_object *tss = NULL;	int i; 	status = acpi_evaluate_object(pr->handle, "_TSS", NULL, &buffer);	if (ACPI_FAILURE(status)) {		if (status != AE_NOT_FOUND)			acpi_evaluation_failure_warn(pr->handle, "_TSS", status); 		return -ENODEV;

Lines 1–500 of 1233.