]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blame - arch/x86/kernel/cpu/mcheck/therm_throt.c
x86, mce: unify smp_thermal_interrupt
[mirror_ubuntu-artful-kernel.git] / arch / x86 / kernel / cpu / mcheck / therm_throt.c
CommitLineData
15d5f839 1/*
3222b36f
DZ
2 * Thermal throttle event support code (such as syslog messaging and rate
3 * limiting) that was factored out from x86_64 (mce_intel.c) and i386 (p4.c).
cb6f3c15 4 *
3222b36f
DZ
5 * This allows consistent reporting of CPU thermal throttle events.
6 *
7 * Maintains a counter in /sys that keeps track of the number of thermal
8 * events, such that the user knows how bad the thermal problem might be
9 * (since the logging to syslog and mcelog is rate limited).
15d5f839
DZ
10 *
11 * Author: Dmitriy Zavin (dmitriyz@google.com)
12 *
13 * Credits: Adapted from Zwane Mwaikambo's original code in mce_intel.c.
3222b36f 14 * Inspired by Ross Biro's and Al Borchers' counter code.
15d5f839 15 */
a65c88dd 16#include <linux/interrupt.h>
cb6f3c15
IM
17#include <linux/notifier.h>
18#include <linux/jiffies.h>
15d5f839 19#include <linux/percpu.h>
3222b36f 20#include <linux/sysdev.h>
15d5f839 21#include <linux/cpu.h>
cb6f3c15 22
15d5f839 23#include <asm/therm_throt.h>
a65c88dd
HS
24#include <asm/idle.h>
25#include <asm/mce.h>
15d5f839
DZ
26
27/* How long to wait between reporting thermal events */
cb6f3c15 28#define CHECK_INTERVAL (300 * HZ)
15d5f839 29
3222b36f
DZ
30static DEFINE_PER_CPU(__u64, next_check) = INITIAL_JIFFIES;
31static DEFINE_PER_CPU(unsigned long, thermal_throttle_count);
cb6f3c15
IM
32
33atomic_t therm_throt_en = ATOMIC_INIT(0);
3222b36f
DZ
34
35#ifdef CONFIG_SYSFS
cb6f3c15
IM
36#define define_therm_throt_sysdev_one_ro(_name) \
37 static SYSDEV_ATTR(_name, 0444, therm_throt_sysdev_show_##_name, NULL)
38
39#define define_therm_throt_sysdev_show_func(name) \
40static ssize_t therm_throt_sysdev_show_##name(struct sys_device *dev, \
41 struct sysdev_attribute *attr, \
42 char *buf) \
43{ \
44 unsigned int cpu = dev->id; \
45 ssize_t ret; \
46 \
47 preempt_disable(); /* CPU hotplug */ \
48 if (cpu_online(cpu)) \
49 ret = sprintf(buf, "%lu\n", \
50 per_cpu(thermal_throttle_##name, cpu)); \
51 else \
52 ret = 0; \
53 preempt_enable(); \
54 \
55 return ret; \
3222b36f
DZ
56}
57
58define_therm_throt_sysdev_show_func(count);
59define_therm_throt_sysdev_one_ro(count);
60
61static struct attribute *thermal_throttle_attrs[] = {
62 &attr_count.attr,
63 NULL
64};
65
66static struct attribute_group thermal_throttle_attr_group = {
cb6f3c15
IM
67 .attrs = thermal_throttle_attrs,
68 .name = "thermal_throttle"
3222b36f
DZ
69};
70#endif /* CONFIG_SYSFS */
15d5f839
DZ
71
72/***
3222b36f 73 * therm_throt_process - Process thermal throttling event from interrupt
15d5f839
DZ
74 * @curr: Whether the condition is current or not (boolean), since the
75 * thermal interrupt normally gets called both when the thermal
76 * event begins and once the event has ended.
77 *
3222b36f 78 * This function is called by the thermal interrupt after the
15d5f839
DZ
79 * IRQ has been acknowledged.
80 *
81 * It will take care of rate limiting and printing messages to the syslog.
82 *
83 * Returns: 0 : Event should NOT be further logged, i.e. still in
84 * "timeout" from previous log message.
85 * 1 : Event should be logged further, and a message has been
86 * printed to the syslog.
87 */
88int therm_throt_process(int curr)
89{
90 unsigned int cpu = smp_processor_id();
66aea991 91 __u64 tmp_jiffs = get_jiffies_64();
15d5f839 92
3222b36f
DZ
93 if (curr)
94 __get_cpu_var(thermal_throttle_count)++;
95
66aea991 96 if (time_before64(tmp_jiffs, __get_cpu_var(next_check)))
15d5f839
DZ
97 return 0;
98
66aea991 99 __get_cpu_var(next_check) = tmp_jiffs + CHECK_INTERVAL;
15d5f839
DZ
100
101 /* if we just entered the thermal event */
102 if (curr) {
103 printk(KERN_CRIT "CPU%d: Temperature above threshold, "
3222b36f
DZ
104 "cpu clock throttled (total events = %lu)\n", cpu,
105 __get_cpu_var(thermal_throttle_count));
106
15d5f839
DZ
107 add_taint(TAINT_MACHINE_CHECK);
108 } else {
109 printk(KERN_CRIT "CPU%d: Temperature/speed normal\n", cpu);
110 }
111
112 return 1;
113}
3222b36f
DZ
114
115#ifdef CONFIG_SYSFS
cb6f3c15 116/* Add/Remove thermal_throttle interface for CPU device: */
6569345a 117static __cpuinit int thermal_throttle_add_dev(struct sys_device *sys_dev)
3222b36f 118{
cb6f3c15
IM
119 return sysfs_create_group(&sys_dev->kobj,
120 &thermal_throttle_attr_group);
3222b36f
DZ
121}
122
6569345a 123static __cpuinit void thermal_throttle_remove_dev(struct sys_device *sys_dev)
3222b36f 124{
7c36752a 125 sysfs_remove_group(&sys_dev->kobj, &thermal_throttle_attr_group);
3222b36f
DZ
126}
127
cb6f3c15 128/* Mutex protecting device creation against CPU hotplug: */
3222b36f
DZ
129static DEFINE_MUTEX(therm_cpu_lock);
130
131/* Get notified when a cpu comes on/off. Be hotplug friendly. */
cb6f3c15
IM
132static __cpuinit int
133thermal_throttle_cpu_callback(struct notifier_block *nfb,
134 unsigned long action,
135 void *hcpu)
3222b36f
DZ
136{
137 unsigned int cpu = (unsigned long)hcpu;
138 struct sys_device *sys_dev;
c7e38a9c 139 int err = 0;
3222b36f
DZ
140
141 sys_dev = get_cpu_sysdev(cpu);
cb6f3c15 142
3222b36f 143 switch (action) {
c7e38a9c
AM
144 case CPU_UP_PREPARE:
145 case CPU_UP_PREPARE_FROZEN:
38ef6d19 146 mutex_lock(&therm_cpu_lock);
6569345a 147 err = thermal_throttle_add_dev(sys_dev);
38ef6d19 148 mutex_unlock(&therm_cpu_lock);
6569345a 149 WARN_ON(err);
3222b36f 150 break;
c7e38a9c
AM
151 case CPU_UP_CANCELED:
152 case CPU_UP_CANCELED_FROZEN:
3222b36f 153 case CPU_DEAD:
8bb78442 154 case CPU_DEAD_FROZEN:
38ef6d19 155 mutex_lock(&therm_cpu_lock);
3222b36f 156 thermal_throttle_remove_dev(sys_dev);
38ef6d19 157 mutex_unlock(&therm_cpu_lock);
3222b36f
DZ
158 break;
159 }
c7e38a9c 160 return err ? NOTIFY_BAD : NOTIFY_OK;
3222b36f
DZ
161}
162
25d1b516 163static struct notifier_block thermal_throttle_cpu_notifier __cpuinitdata =
3222b36f
DZ
164{
165 .notifier_call = thermal_throttle_cpu_callback,
166};
3222b36f
DZ
167
168static __init int thermal_throttle_init_device(void)
169{
170 unsigned int cpu = 0;
6569345a 171 int err;
3222b36f
DZ
172
173 if (!atomic_read(&therm_throt_en))
174 return 0;
175
176 register_hotcpu_notifier(&thermal_throttle_cpu_notifier);
177
178#ifdef CONFIG_HOTPLUG_CPU
179 mutex_lock(&therm_cpu_lock);
180#endif
181 /* connect live CPUs to sysfs */
6569345a
SH
182 for_each_online_cpu(cpu) {
183 err = thermal_throttle_add_dev(get_cpu_sysdev(cpu));
184 WARN_ON(err);
185 }
3222b36f
DZ
186#ifdef CONFIG_HOTPLUG_CPU
187 mutex_unlock(&therm_cpu_lock);
188#endif
189
190 return 0;
191}
3222b36f 192device_initcall(thermal_throttle_init_device);
a65c88dd 193
3222b36f 194#endif /* CONFIG_SYSFS */
a65c88dd
HS
195
196/* Thermal transition interrupt handler */
197void intel_thermal_interrupt(void)
198{
199 __u64 msr_val;
200
201 rdmsrl(MSR_IA32_THERM_STATUS, msr_val);
202 if (therm_throt_process(msr_val & THERM_STATUS_PROCHOT))
203 mce_log_therm_throt_event(msr_val);
204}
205
206static void unexpected_thermal_interrupt(void)
207{
208 printk(KERN_ERR "CPU%d: Unexpected LVT TMR interrupt!\n",
209 smp_processor_id());
210 add_taint(TAINT_MACHINE_CHECK);
211}
212
213static void (*smp_thermal_vector)(void) = unexpected_thermal_interrupt;
214
215asmlinkage void smp_thermal_interrupt(struct pt_regs *regs)
216{
217 exit_idle();
218 irq_enter();
219 inc_irq_stat(irq_thermal_count);
220 smp_thermal_vector();
221 irq_exit();
222 /* Ack only at the end to avoid potential reentry */
223 ack_APIC_irq();
224}
225
226void intel_set_thermal_handler(void)
227{
228 smp_thermal_vector = intel_thermal_interrupt;
229}