cpufreq.c

来自「linux 内核源代码」· C语言 代码 · 共 1,883 行 · 第 1/4 页

C
1,883
字号
/* *  linux/drivers/cpufreq/cpufreq.c * *  Copyright (C) 2001 Russell King *            (C) 2002 - 2003 Dominik Brodowski <linux@brodo.de> * *  Oct 2005 - Ashok Raj <ashok.raj@intel.com> *	Added handling for CPU hotplug *  Feb 2006 - Jacob Shin <jacob.shin@amd.com> *	Fix handling for CPU hotplug -- affected CPUs * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * */#include <linux/kernel.h>#include <linux/module.h>#include <linux/init.h>#include <linux/notifier.h>#include <linux/cpufreq.h>#include <linux/delay.h>#include <linux/interrupt.h>#include <linux/spinlock.h>#include <linux/device.h>#include <linux/slab.h>#include <linux/cpu.h>#include <linux/completion.h>#include <linux/mutex.h>#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_CORE, \						"cpufreq-core", msg)/** * The "cpufreq driver" - the arch- or hardware-dependent low * level driver of CPUFreq support, and its spinlock. This lock * also protects the cpufreq_cpu_data array. */static struct cpufreq_driver *cpufreq_driver;static struct cpufreq_policy *cpufreq_cpu_data[NR_CPUS];#ifdef CONFIG_HOTPLUG_CPU/* This one keeps track of the previously set governor of a removed CPU */static struct cpufreq_governor *cpufreq_cpu_governor[NR_CPUS];#endifstatic DEFINE_SPINLOCK(cpufreq_driver_lock);/* * cpu_policy_rwsem is a per CPU reader-writer semaphore designed to cure * all cpufreq/hotplug/workqueue/etc related lock issues. * * The rules for this semaphore: * - Any routine that wants to read from the policy structure will *   do a down_read on this semaphore. * - Any routine that will write to the policy structure and/or may take away *   the policy altogether (eg. CPU hotplug), will hold this lock in write *   mode before doing so. * * Additional rules: * - All holders of the lock should check to make sure that the CPU they *   are concerned with are online after they get the lock. * - Governor routines that can be called in cpufreq hotplug path should not *   take this sem as top level hotplug notifier handler takes this. */static DEFINE_PER_CPU(int, policy_cpu);static DEFINE_PER_CPU(struct rw_semaphore, cpu_policy_rwsem);#define lock_policy_rwsem(mode, cpu)					\int lock_policy_rwsem_##mode						\(int cpu)								\{									\	int policy_cpu = per_cpu(policy_cpu, cpu);			\	BUG_ON(policy_cpu == -1);					\	down_##mode(&per_cpu(cpu_policy_rwsem, policy_cpu));		\	if (unlikely(!cpu_online(cpu))) {				\		up_##mode(&per_cpu(cpu_policy_rwsem, policy_cpu));	\		return -1;						\	}								\									\	return 0;							\}lock_policy_rwsem(read, cpu);EXPORT_SYMBOL_GPL(lock_policy_rwsem_read);lock_policy_rwsem(write, cpu);EXPORT_SYMBOL_GPL(lock_policy_rwsem_write);void unlock_policy_rwsem_read(int cpu){	int policy_cpu = per_cpu(policy_cpu, cpu);	BUG_ON(policy_cpu == -1);	up_read(&per_cpu(cpu_policy_rwsem, policy_cpu));}EXPORT_SYMBOL_GPL(unlock_policy_rwsem_read);void unlock_policy_rwsem_write(int cpu){	int policy_cpu = per_cpu(policy_cpu, cpu);	BUG_ON(policy_cpu == -1);	up_write(&per_cpu(cpu_policy_rwsem, policy_cpu));}EXPORT_SYMBOL_GPL(unlock_policy_rwsem_write);/* internal prototypes */static int __cpufreq_governor(struct cpufreq_policy *policy, unsigned int event);static unsigned int __cpufreq_get(unsigned int cpu);static void handle_update(struct work_struct *work);/** * Two notifier lists: the "policy" list is involved in the * validation process for a new CPU frequency policy; the * "transition" list for kernel code that needs to handle * changes to devices when the CPU clock speed changes. * The mutex locks both lists. */static BLOCKING_NOTIFIER_HEAD(cpufreq_policy_notifier_list);static struct srcu_notifier_head cpufreq_transition_notifier_list;static int __init init_cpufreq_transition_notifier_list(void){	srcu_init_notifier_head(&cpufreq_transition_notifier_list);	return 0;}pure_initcall(init_cpufreq_transition_notifier_list);static LIST_HEAD(cpufreq_governor_list);static DEFINE_MUTEX (cpufreq_governor_mutex);struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu){	struct cpufreq_policy *data;	unsigned long flags;	if (cpu >= NR_CPUS)		goto err_out;	/* get the cpufreq driver */	spin_lock_irqsave(&cpufreq_driver_lock, flags);	if (!cpufreq_driver)		goto err_out_unlock;	if (!try_module_get(cpufreq_driver->owner))		goto err_out_unlock;	/* get the CPU */	data = cpufreq_cpu_data[cpu];	if (!data)		goto err_out_put_module;	if (!kobject_get(&data->kobj))		goto err_out_put_module;	spin_unlock_irqrestore(&cpufreq_driver_lock, flags);	return data;err_out_put_module:	module_put(cpufreq_driver->owner);err_out_unlock:	spin_unlock_irqrestore(&cpufreq_driver_lock, flags);err_out:	return NULL;}EXPORT_SYMBOL_GPL(cpufreq_cpu_get);void cpufreq_cpu_put(struct cpufreq_policy *data){	kobject_put(&data->kobj);	module_put(cpufreq_driver->owner);}EXPORT_SYMBOL_GPL(cpufreq_cpu_put);/********************************************************************* *                     UNIFIED DEBUG HELPERS                         * *********************************************************************/#ifdef CONFIG_CPU_FREQ_DEBUG/* what part(s) of the CPUfreq subsystem are debugged? */static unsigned int debug;/* is the debug output ratelimit'ed using printk_ratelimit? User can * set or modify this value. */static unsigned int debug_ratelimit = 1;/* is the printk_ratelimit'ing enabled? It's enabled after a successful * loading of a cpufreq driver, temporarily disabled when a new policy * is set, and disabled upon cpufreq driver removal */static unsigned int disable_ratelimit = 1;static DEFINE_SPINLOCK(disable_ratelimit_lock);static void cpufreq_debug_enable_ratelimit(void){	unsigned long flags;	spin_lock_irqsave(&disable_ratelimit_lock, flags);	if (disable_ratelimit)		disable_ratelimit--;	spin_unlock_irqrestore(&disable_ratelimit_lock, flags);}static void cpufreq_debug_disable_ratelimit(void){	unsigned long flags;	spin_lock_irqsave(&disable_ratelimit_lock, flags);	disable_ratelimit++;	spin_unlock_irqrestore(&disable_ratelimit_lock, flags);}void cpufreq_debug_printk(unsigned int type, const char *prefix,							const char *fmt, ...){	char s[256];	va_list args;	unsigned int len;	unsigned long flags;	WARN_ON(!prefix);	if (type & debug) {		spin_lock_irqsave(&disable_ratelimit_lock, flags);		if (!disable_ratelimit && debug_ratelimit					&& !printk_ratelimit()) {			spin_unlock_irqrestore(&disable_ratelimit_lock, flags);			return;		}		spin_unlock_irqrestore(&disable_ratelimit_lock, flags);		len = snprintf(s, 256, KERN_DEBUG "%s: ", prefix);		va_start(args, fmt);		len += vsnprintf(&s[len], (256 - len), fmt, args);		va_end(args);		printk(s);		WARN_ON(len < 5);	}}EXPORT_SYMBOL(cpufreq_debug_printk);module_param(debug, uint, 0644);MODULE_PARM_DESC(debug, "CPUfreq debugging: add 1 to debug core,"			" 2 to debug drivers, and 4 to debug governors.");module_param(debug_ratelimit, uint, 0644);MODULE_PARM_DESC(debug_ratelimit, "CPUfreq debugging:"					" set to 0 to disable ratelimiting.");#else /* !CONFIG_CPU_FREQ_DEBUG */static inline void cpufreq_debug_enable_ratelimit(void) { return; }static inline void cpufreq_debug_disable_ratelimit(void) { return; }#endif /* CONFIG_CPU_FREQ_DEBUG *//********************************************************************* *            EXTERNALLY AFFECTING FREQUENCY CHANGES                 * *********************************************************************//** * adjust_jiffies - adjust the system "loops_per_jiffy" * * This function alters the system "loops_per_jiffy" for the clock * speed change. Note that loops_per_jiffy cannot be updated on SMP * systems as each CPU might be scaled differently. So, use the arch * per-CPU loops_per_jiffy value wherever possible. */#ifndef CONFIG_SMPstatic unsigned long l_p_j_ref;static unsigned int  l_p_j_ref_freq;static void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci){	if (ci->flags & CPUFREQ_CONST_LOOPS)		return;	if (!l_p_j_ref_freq) {		l_p_j_ref = loops_per_jiffy;		l_p_j_ref_freq = ci->old;		dprintk("saving %lu as reference value for loops_per_jiffy;"			"freq is %u kHz\n", l_p_j_ref, l_p_j_ref_freq);	}	if ((val == CPUFREQ_PRECHANGE  && ci->old < ci->new) ||	    (val == CPUFREQ_POSTCHANGE && ci->old > ci->new) ||	    (val == CPUFREQ_RESUMECHANGE || val == CPUFREQ_SUSPENDCHANGE)) {		loops_per_jiffy = cpufreq_scale(l_p_j_ref, l_p_j_ref_freq,								ci->new);		dprintk("scaling loops_per_jiffy to %lu"			"for frequency %u kHz\n", loops_per_jiffy, ci->new);	}}#elsestatic inline void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci){	return;}#endif/** * cpufreq_notify_transition - call notifier chain and adjust_jiffies * on frequency transition. * * This function calls the transition notifiers and the "adjust_jiffies" * function. It is called twice on all CPU frequency changes that have * external effects. */void cpufreq_notify_transition(struct cpufreq_freqs *freqs, unsigned int state){	struct cpufreq_policy *policy;	BUG_ON(irqs_disabled());	freqs->flags = cpufreq_driver->flags;	dprintk("notification %u of frequency transition to %u kHz\n",		state, freqs->new);	policy = cpufreq_cpu_data[freqs->cpu];	switch (state) {	case CPUFREQ_PRECHANGE:		/* detect if the driver reported a value as "old frequency"		 * which is not equal to what the cpufreq core thinks is		 * "old frequency".		 */		if (!(cpufreq_driver->flags & CPUFREQ_CONST_LOOPS)) {			if ((policy) && (policy->cpu == freqs->cpu) &&			    (policy->cur) && (policy->cur != freqs->old)) {				dprintk("Warning: CPU frequency is"					" %u, cpufreq assumed %u kHz.\n",					freqs->old, policy->cur);				freqs->old = policy->cur;			}		}		srcu_notifier_call_chain(&cpufreq_transition_notifier_list,				CPUFREQ_PRECHANGE, freqs);		adjust_jiffies(CPUFREQ_PRECHANGE, freqs);		break;	case CPUFREQ_POSTCHANGE:		adjust_jiffies(CPUFREQ_POSTCHANGE, freqs);		srcu_notifier_call_chain(&cpufreq_transition_notifier_list,				CPUFREQ_POSTCHANGE, freqs);		if (likely(policy) && likely(policy->cpu == freqs->cpu))			policy->cur = freqs->new;		break;	}}EXPORT_SYMBOL_GPL(cpufreq_notify_transition);/********************************************************************* *                          SYSFS INTERFACE                          * *********************************************************************/static struct cpufreq_governor *__find_governor(const char *str_governor){	struct cpufreq_governor *t;	list_for_each_entry(t, &cpufreq_governor_list, governor_list)		if (!strnicmp(str_governor,t->name,CPUFREQ_NAME_LEN))			return t;	return NULL;}/** * cpufreq_parse_governor - parse a governor string */static int cpufreq_parse_governor (char *str_governor, unsigned int *policy,				struct cpufreq_governor **governor){	int err = -EINVAL;	if (!cpufreq_driver)		goto out;	if (cpufreq_driver->setpolicy) {		if (!strnicmp(str_governor, "performance", CPUFREQ_NAME_LEN)) {			*policy = CPUFREQ_POLICY_PERFORMANCE;			err = 0;		} else if (!strnicmp(str_governor, "powersave",						CPUFREQ_NAME_LEN)) {			*policy = CPUFREQ_POLICY_POWERSAVE;			err = 0;		}	} else if (cpufreq_driver->target) {		struct cpufreq_governor *t;		mutex_lock(&cpufreq_governor_mutex);		t = __find_governor(str_governor);		if (t == NULL) {			char *name = kasprintf(GFP_KERNEL, "cpufreq_%s",								str_governor);			if (name) {				int ret;				mutex_unlock(&cpufreq_governor_mutex);				ret = request_module(name);				mutex_lock(&cpufreq_governor_mutex);				if (ret == 0)					t = __find_governor(str_governor);			}			kfree(name);		}		if (t != NULL) {			*governor = t;			err = 0;		}		mutex_unlock(&cpufreq_governor_mutex);	}  out:	return err;}/* drivers/base/cpu.c */extern struct sysdev_class cpu_sysdev_class;/** * cpufreq_per_cpu_attr_read() / show_##file_name() - * print out cpufreq information * * Write out information from cpufreq_driver->policy[cpu]; object must be * "unsigned int". */#define show_one(file_name, object)			\static ssize_t show_##file_name				\(struct cpufreq_policy * policy, char *buf)		\{							\	return sprintf (buf, "%u\n", policy->object);	\}show_one(cpuinfo_min_freq, cpuinfo.min_freq);show_one(cpuinfo_max_freq, cpuinfo.max_freq);show_one(scaling_min_freq, min);show_one(scaling_max_freq, max);show_one(scaling_cur_freq, cur);static int __cpufreq_set_policy(struct cpufreq_policy *data,				struct cpufreq_policy *policy);/** * cpufreq_per_cpu_attr_write() / store_##file_name() - sysfs write access */#define store_one(file_name, object)			\static ssize_t store_##file_name					\(struct cpufreq_policy * policy, const char *buf, size_t count)		\{									\	unsigned int ret = -EINVAL;					\	struct cpufreq_policy new_policy;				\

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?