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 + -
显示快捷键?