therm_throt.c 6.0 KB
Newer Older
1
/*
2 3
 * Thermal throttle event support code (such as syslog messaging and rate
 * limiting) that was factored out from x86_64 (mce_intel.c) and i386 (p4.c).
I
Ingo Molnar 已提交
4
 *
5 6 7 8 9
 * This allows consistent reporting of CPU thermal throttle events.
 *
 * Maintains a counter in /sys that keeps track of the number of thermal
 * events, such that the user knows how bad the thermal problem might be
 * (since the logging to syslog and mcelog is rate limited).
10 11 12 13
 *
 * Author: Dmitriy Zavin (dmitriyz@google.com)
 *
 * Credits: Adapted from Zwane Mwaikambo's original code in mce_intel.c.
14
 *          Inspired by Ross Biro's and Al Borchers' counter code.
15
 */
16
#include <linux/interrupt.h>
I
Ingo Molnar 已提交
17 18
#include <linux/notifier.h>
#include <linux/jiffies.h>
19
#include <linux/percpu.h>
20
#include <linux/sysdev.h>
21
#include <linux/cpu.h>
I
Ingo Molnar 已提交
22

23
#include <asm/therm_throt.h>
24 25
#include <asm/idle.h>
#include <asm/mce.h>
26 27

/* How long to wait between reporting thermal events */
I
Ingo Molnar 已提交
28
#define CHECK_INTERVAL		(300 * HZ)
29

30 31
static DEFINE_PER_CPU(__u64, next_check) = INITIAL_JIFFIES;
static DEFINE_PER_CPU(unsigned long, thermal_throttle_count);
I
Ingo Molnar 已提交
32 33

atomic_t therm_throt_en		= ATOMIC_INIT(0);
34 35

#ifdef CONFIG_SYSFS
I
Ingo Molnar 已提交
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
#define define_therm_throt_sysdev_one_ro(_name)				\
	static SYSDEV_ATTR(_name, 0444, therm_throt_sysdev_show_##_name, NULL)

#define define_therm_throt_sysdev_show_func(name)			\
static ssize_t therm_throt_sysdev_show_##name(struct sys_device *dev,	\
					struct sysdev_attribute *attr,	\
					      char *buf)		\
{									\
	unsigned int cpu = dev->id;					\
	ssize_t ret;							\
									\
	preempt_disable();	/* CPU hotplug */			\
	if (cpu_online(cpu))						\
		ret = sprintf(buf, "%lu\n",				\
			      per_cpu(thermal_throttle_##name, cpu));	\
	else								\
		ret = 0;						\
	preempt_enable();						\
									\
	return ret;							\
56 57 58 59 60 61 62 63 64 65 66
}

define_therm_throt_sysdev_show_func(count);
define_therm_throt_sysdev_one_ro(count);

static struct attribute *thermal_throttle_attrs[] = {
	&attr_count.attr,
	NULL
};

static struct attribute_group thermal_throttle_attr_group = {
I
Ingo Molnar 已提交
67 68
	.attrs	= thermal_throttle_attrs,
	.name	= "thermal_throttle"
69 70
};
#endif /* CONFIG_SYSFS */
71 72

/***
73
 * therm_throt_process - Process thermal throttling event from interrupt
74 75 76 77
 * @curr: Whether the condition is current or not (boolean), since the
 *        thermal interrupt normally gets called both when the thermal
 *        event begins and once the event has ended.
 *
78
 * This function is called by the thermal interrupt after the
79 80 81 82 83 84 85 86 87 88 89 90
 * IRQ has been acknowledged.
 *
 * It will take care of rate limiting and printing messages to the syslog.
 *
 * Returns: 0 : Event should NOT be further logged, i.e. still in
 *              "timeout" from previous log message.
 *          1 : Event should be logged further, and a message has been
 *              printed to the syslog.
 */
int therm_throt_process(int curr)
{
	unsigned int cpu = smp_processor_id();
91
	__u64 tmp_jiffs = get_jiffies_64();
92

93 94 95
	if (curr)
		__get_cpu_var(thermal_throttle_count)++;

96
	if (time_before64(tmp_jiffs, __get_cpu_var(next_check)))
97 98
		return 0;

99
	__get_cpu_var(next_check) = tmp_jiffs + CHECK_INTERVAL;
100 101 102 103

	/* if we just entered the thermal event */
	if (curr) {
		printk(KERN_CRIT "CPU%d: Temperature above threshold, "
104 105 106
		       "cpu clock throttled (total events = %lu)\n", cpu,
		       __get_cpu_var(thermal_throttle_count));

107 108 109 110 111 112 113
		add_taint(TAINT_MACHINE_CHECK);
	} else {
		printk(KERN_CRIT "CPU%d: Temperature/speed normal\n", cpu);
	}

	return 1;
}
114 115

#ifdef CONFIG_SYSFS
I
Ingo Molnar 已提交
116
/* Add/Remove thermal_throttle interface for CPU device: */
117
static __cpuinit int thermal_throttle_add_dev(struct sys_device *sys_dev)
118
{
I
Ingo Molnar 已提交
119 120
	return sysfs_create_group(&sys_dev->kobj,
				  &thermal_throttle_attr_group);
121 122
}

123
static __cpuinit void thermal_throttle_remove_dev(struct sys_device *sys_dev)
124
{
125
	sysfs_remove_group(&sys_dev->kobj, &thermal_throttle_attr_group);
126 127
}

I
Ingo Molnar 已提交
128
/* Mutex protecting device creation against CPU hotplug: */
129 130 131
static DEFINE_MUTEX(therm_cpu_lock);

/* Get notified when a cpu comes on/off. Be hotplug friendly. */
I
Ingo Molnar 已提交
132 133 134 135
static __cpuinit int
thermal_throttle_cpu_callback(struct notifier_block *nfb,
			      unsigned long action,
			      void *hcpu)
136 137 138
{
	unsigned int cpu = (unsigned long)hcpu;
	struct sys_device *sys_dev;
139
	int err = 0;
140 141

	sys_dev = get_cpu_sysdev(cpu);
I
Ingo Molnar 已提交
142

143
	switch (action) {
144 145
	case CPU_UP_PREPARE:
	case CPU_UP_PREPARE_FROZEN:
146
		mutex_lock(&therm_cpu_lock);
147
		err = thermal_throttle_add_dev(sys_dev);
148
		mutex_unlock(&therm_cpu_lock);
149
		WARN_ON(err);
150
		break;
151 152
	case CPU_UP_CANCELED:
	case CPU_UP_CANCELED_FROZEN:
153
	case CPU_DEAD:
154
	case CPU_DEAD_FROZEN:
155
		mutex_lock(&therm_cpu_lock);
156
		thermal_throttle_remove_dev(sys_dev);
157
		mutex_unlock(&therm_cpu_lock);
158 159
		break;
	}
160
	return err ? NOTIFY_BAD : NOTIFY_OK;
161 162
}

S
Satyam Sharma 已提交
163
static struct notifier_block thermal_throttle_cpu_notifier __cpuinitdata =
164 165 166 167 168 169 170
{
	.notifier_call = thermal_throttle_cpu_callback,
};

static __init int thermal_throttle_init_device(void)
{
	unsigned int cpu = 0;
171
	int err;
172 173 174 175 176 177 178 179 180 181

	if (!atomic_read(&therm_throt_en))
		return 0;

	register_hotcpu_notifier(&thermal_throttle_cpu_notifier);

#ifdef CONFIG_HOTPLUG_CPU
	mutex_lock(&therm_cpu_lock);
#endif
	/* connect live CPUs to sysfs */
182 183 184 185
	for_each_online_cpu(cpu) {
		err = thermal_throttle_add_dev(get_cpu_sysdev(cpu));
		WARN_ON(err);
	}
186 187 188 189 190 191 192
#ifdef CONFIG_HOTPLUG_CPU
	mutex_unlock(&therm_cpu_lock);
#endif

	return 0;
}
device_initcall(thermal_throttle_init_device);
193

194
#endif /* CONFIG_SYSFS */
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229

/* Thermal transition interrupt handler */
void intel_thermal_interrupt(void)
{
	__u64 msr_val;

	rdmsrl(MSR_IA32_THERM_STATUS, msr_val);
	if (therm_throt_process(msr_val & THERM_STATUS_PROCHOT))
		mce_log_therm_throt_event(msr_val);
}

static void unexpected_thermal_interrupt(void)
{
	printk(KERN_ERR "CPU%d: Unexpected LVT TMR interrupt!\n",
			smp_processor_id());
	add_taint(TAINT_MACHINE_CHECK);
}

static void (*smp_thermal_vector)(void) = unexpected_thermal_interrupt;

asmlinkage void smp_thermal_interrupt(struct pt_regs *regs)
{
	exit_idle();
	irq_enter();
	inc_irq_stat(irq_thermal_count);
	smp_thermal_vector();
	irq_exit();
	/* Ack only at the end to avoid potential reentry */
	ack_APIC_irq();
}

void intel_set_thermal_handler(void)
{
	smp_thermal_vector = intel_thermal_interrupt;
}