diff options
author | Dmitriy Zavin <dmitriyz@google.com> | 2006-09-26 04:52:42 -0400 |
---|---|---|
committer | Andi Kleen <andi@basil.nowhere.org> | 2006-09-26 04:52:42 -0400 |
commit | 15d5f8398311f565682959daaca30e3ca7aea600 (patch) | |
tree | ba38e77782832191dd38e182e99525870f83c085 /arch | |
parent | 3b171672831b9633c2ed8fa94805255cd4d5af19 (diff) |
[PATCH] x86: Refactor thermal throttle processing
Refactor the event processing (syslog messaging and rate limiting)
into separate file therm_throt.c. This allows consistent reporting
of CPU thermal throttle events.
After ACK'ing the interrupt, if the event is current, the user
(p4.c/mce_intel.c) calls therm_throt_process to log (and rate limit)
the event. If that function returns 1, the user has the option to log
things further (such as to mce_log in x86_64).
AK: minor cleanup
Signed-off-by: Dmitriy Zavin <dmitriyz@google.com>
Signed-off-by: Andi Kleen <ak@suse.de>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/i386/kernel/cpu/mcheck/Makefile | 2 | ||||
-rw-r--r-- | arch/i386/kernel/cpu/mcheck/p4.c | 23 | ||||
-rw-r--r-- | arch/i386/kernel/cpu/mcheck/therm_throt.c | 58 | ||||
-rw-r--r-- | arch/x86_64/kernel/Makefile | 4 | ||||
-rw-r--r-- | arch/x86_64/kernel/mce.c | 27 | ||||
-rw-r--r-- | arch/x86_64/kernel/mce_intel.c | 27 |
6 files changed, 100 insertions, 41 deletions
diff --git a/arch/i386/kernel/cpu/mcheck/Makefile b/arch/i386/kernel/cpu/mcheck/Makefile index 30808f3d6715..f1ebe1c1c17a 100644 --- a/arch/i386/kernel/cpu/mcheck/Makefile +++ b/arch/i386/kernel/cpu/mcheck/Makefile | |||
@@ -1,2 +1,2 @@ | |||
1 | obj-y = mce.o k7.o p4.o p5.o p6.o winchip.o | 1 | obj-y = mce.o k7.o p4.o p5.o p6.o winchip.o therm_throt.o |
2 | obj-$(CONFIG_X86_MCE_NONFATAL) += non-fatal.o | 2 | obj-$(CONFIG_X86_MCE_NONFATAL) += non-fatal.o |
diff --git a/arch/i386/kernel/cpu/mcheck/p4.c b/arch/i386/kernel/cpu/mcheck/p4.c index b95f1b3d53aa..d83a669d376f 100644 --- a/arch/i386/kernel/cpu/mcheck/p4.c +++ b/arch/i386/kernel/cpu/mcheck/p4.c | |||
@@ -13,6 +13,8 @@ | |||
13 | #include <asm/msr.h> | 13 | #include <asm/msr.h> |
14 | #include <asm/apic.h> | 14 | #include <asm/apic.h> |
15 | 15 | ||
16 | #include <asm/therm_throt.h> | ||
17 | |||
16 | #include "mce.h" | 18 | #include "mce.h" |
17 | 19 | ||
18 | /* as supported by the P4/Xeon family */ | 20 | /* as supported by the P4/Xeon family */ |
@@ -44,25 +46,12 @@ static void unexpected_thermal_interrupt(struct pt_regs *regs) | |||
44 | /* P4/Xeon Thermal transition interrupt handler */ | 46 | /* P4/Xeon Thermal transition interrupt handler */ |
45 | static void intel_thermal_interrupt(struct pt_regs *regs) | 47 | static void intel_thermal_interrupt(struct pt_regs *regs) |
46 | { | 48 | { |
47 | u32 l, h; | 49 | __u64 msr_val; |
48 | unsigned int cpu = smp_processor_id(); | ||
49 | static unsigned long next[NR_CPUS]; | ||
50 | 50 | ||
51 | ack_APIC_irq(); | 51 | ack_APIC_irq(); |
52 | 52 | ||
53 | if (time_after(next[cpu], jiffies)) | 53 | rdmsrl(MSR_IA32_THERM_STATUS, msr_val); |
54 | return; | 54 | therm_throt_process(msr_val & 0x1); |
55 | |||
56 | next[cpu] = jiffies + HZ*5; | ||
57 | rdmsr(MSR_IA32_THERM_STATUS, l, h); | ||
58 | if (l & 0x1) { | ||
59 | printk(KERN_EMERG "CPU%d: Temperature above threshold\n", cpu); | ||
60 | printk(KERN_EMERG "CPU%d: Running in modulated clock mode\n", | ||
61 | cpu); | ||
62 | add_taint(TAINT_MACHINE_CHECK); | ||
63 | } else { | ||
64 | printk(KERN_INFO "CPU%d: Temperature/speed normal\n", cpu); | ||
65 | } | ||
66 | } | 55 | } |
67 | 56 | ||
68 | /* Thermal interrupt handler for this CPU setup */ | 57 | /* Thermal interrupt handler for this CPU setup */ |
@@ -122,7 +111,7 @@ static void intel_init_thermal(struct cpuinfo_x86 *c) | |||
122 | 111 | ||
123 | rdmsr (MSR_IA32_MISC_ENABLE, l, h); | 112 | rdmsr (MSR_IA32_MISC_ENABLE, l, h); |
124 | wrmsr (MSR_IA32_MISC_ENABLE, l | (1<<3), h); | 113 | wrmsr (MSR_IA32_MISC_ENABLE, l | (1<<3), h); |
125 | 114 | ||
126 | l = apic_read (APIC_LVTTHMR); | 115 | l = apic_read (APIC_LVTTHMR); |
127 | apic_write_around (APIC_LVTTHMR, l & ~APIC_LVT_MASKED); | 116 | apic_write_around (APIC_LVTTHMR, l & ~APIC_LVT_MASKED); |
128 | printk (KERN_INFO "CPU%d: Thermal monitoring enabled\n", cpu); | 117 | printk (KERN_INFO "CPU%d: Thermal monitoring enabled\n", cpu); |
diff --git a/arch/i386/kernel/cpu/mcheck/therm_throt.c b/arch/i386/kernel/cpu/mcheck/therm_throt.c new file mode 100644 index 000000000000..85eba00d680e --- /dev/null +++ b/arch/i386/kernel/cpu/mcheck/therm_throt.c | |||
@@ -0,0 +1,58 @@ | |||
1 | /* | ||
2 | * linux/arch/i386/kerne/cpu/mcheck/therm_throt.c | ||
3 | * | ||
4 | * Thermal throttle event support code. | ||
5 | * | ||
6 | * Author: Dmitriy Zavin (dmitriyz@google.com) | ||
7 | * | ||
8 | * Credits: Adapted from Zwane Mwaikambo's original code in mce_intel.c. | ||
9 | * | ||
10 | */ | ||
11 | |||
12 | #include <linux/percpu.h> | ||
13 | #include <linux/cpu.h> | ||
14 | #include <asm/cpu.h> | ||
15 | #include <linux/notifier.h> | ||
16 | #include <asm/therm_throt.h> | ||
17 | |||
18 | /* How long to wait between reporting thermal events */ | ||
19 | #define CHECK_INTERVAL (300 * HZ) | ||
20 | |||
21 | static DEFINE_PER_CPU(unsigned long, next_check); | ||
22 | |||
23 | /*** | ||
24 | * therm_throt_process - Process thermal throttling event | ||
25 | * @curr: Whether the condition is current or not (boolean), since the | ||
26 | * thermal interrupt normally gets called both when the thermal | ||
27 | * event begins and once the event has ended. | ||
28 | * | ||
29 | * This function is normally called by the thermal interrupt after the | ||
30 | * IRQ has been acknowledged. | ||
31 | * | ||
32 | * It will take care of rate limiting and printing messages to the syslog. | ||
33 | * | ||
34 | * Returns: 0 : Event should NOT be further logged, i.e. still in | ||
35 | * "timeout" from previous log message. | ||
36 | * 1 : Event should be logged further, and a message has been | ||
37 | * printed to the syslog. | ||
38 | */ | ||
39 | int therm_throt_process(int curr) | ||
40 | { | ||
41 | unsigned int cpu = smp_processor_id(); | ||
42 | |||
43 | if (time_before(jiffies, __get_cpu_var(next_check))) | ||
44 | return 0; | ||
45 | |||
46 | __get_cpu_var(next_check) = jiffies + CHECK_INTERVAL; | ||
47 | |||
48 | /* if we just entered the thermal event */ | ||
49 | if (curr) { | ||
50 | printk(KERN_CRIT "CPU%d: Temperature above threshold, " | ||
51 | "cpu clock throttled\n", cpu); | ||
52 | add_taint(TAINT_MACHINE_CHECK); | ||
53 | } else { | ||
54 | printk(KERN_CRIT "CPU%d: Temperature/speed normal\n", cpu); | ||
55 | } | ||
56 | |||
57 | return 1; | ||
58 | } | ||
diff --git a/arch/x86_64/kernel/Makefile b/arch/x86_64/kernel/Makefile index 2466fbd035ee..3c7cbff04d3d 100644 --- a/arch/x86_64/kernel/Makefile +++ b/arch/x86_64/kernel/Makefile | |||
@@ -11,7 +11,7 @@ obj-y := process.o signal.o entry.o traps.o irq.o \ | |||
11 | pci-dma.o pci-nommu.o alternative.o | 11 | pci-dma.o pci-nommu.o alternative.o |
12 | 12 | ||
13 | obj-$(CONFIG_STACKTRACE) += stacktrace.o | 13 | obj-$(CONFIG_STACKTRACE) += stacktrace.o |
14 | obj-$(CONFIG_X86_MCE) += mce.o | 14 | obj-$(CONFIG_X86_MCE) += mce.o therm_throt.o |
15 | obj-$(CONFIG_X86_MCE_INTEL) += mce_intel.o | 15 | obj-$(CONFIG_X86_MCE_INTEL) += mce_intel.o |
16 | obj-$(CONFIG_X86_MCE_AMD) += mce_amd.o | 16 | obj-$(CONFIG_X86_MCE_AMD) += mce_amd.o |
17 | obj-$(CONFIG_MTRR) += ../../i386/kernel/cpu/mtrr/ | 17 | obj-$(CONFIG_MTRR) += ../../i386/kernel/cpu/mtrr/ |
@@ -46,6 +46,7 @@ obj-y += intel_cacheinfo.o | |||
46 | 46 | ||
47 | CFLAGS_vsyscall.o := $(PROFILING) -g0 | 47 | CFLAGS_vsyscall.o := $(PROFILING) -g0 |
48 | 48 | ||
49 | therm_throt-y += ../../i386/kernel/cpu/mcheck/therm_throt.o | ||
49 | bootflag-y += ../../i386/kernel/bootflag.o | 50 | bootflag-y += ../../i386/kernel/bootflag.o |
50 | cpuid-$(subst m,y,$(CONFIG_X86_CPUID)) += ../../i386/kernel/cpuid.o | 51 | cpuid-$(subst m,y,$(CONFIG_X86_CPUID)) += ../../i386/kernel/cpuid.o |
51 | topology-y += ../../i386/kernel/topology.o | 52 | topology-y += ../../i386/kernel/topology.o |
@@ -55,4 +56,3 @@ quirks-y += ../../i386/kernel/quirks.o | |||
55 | i8237-y += ../../i386/kernel/i8237.o | 56 | i8237-y += ../../i386/kernel/i8237.o |
56 | msr-$(subst m,y,$(CONFIG_X86_MSR)) += ../../i386/kernel/msr.o | 57 | msr-$(subst m,y,$(CONFIG_X86_MSR)) += ../../i386/kernel/msr.o |
57 | alternative-y += ../../i386/kernel/alternative.o | 58 | alternative-y += ../../i386/kernel/alternative.o |
58 | |||
diff --git a/arch/x86_64/kernel/mce.c b/arch/x86_64/kernel/mce.c index 1a93c3738404..bbea88801d88 100644 --- a/arch/x86_64/kernel/mce.c +++ b/arch/x86_64/kernel/mce.c | |||
@@ -274,6 +274,33 @@ void do_machine_check(struct pt_regs * regs, long error_code) | |||
274 | atomic_dec(&mce_entry); | 274 | atomic_dec(&mce_entry); |
275 | } | 275 | } |
276 | 276 | ||
277 | #ifdef CONFIG_X86_MCE_INTEL | ||
278 | /*** | ||
279 | * mce_log_therm_throt_event - Logs the thermal throttling event to mcelog | ||
280 | * @cpu: The CPU on which the event occured. | ||
281 | * @status: Event status information | ||
282 | * | ||
283 | * This function should be called by the thermal interrupt after the | ||
284 | * event has been processed and the decision was made to log the event | ||
285 | * further. | ||
286 | * | ||
287 | * The status parameter will be saved to the 'status' field of 'struct mce' | ||
288 | * and historically has been the register value of the | ||
289 | * MSR_IA32_THERMAL_STATUS (Intel) msr. | ||
290 | */ | ||
291 | void mce_log_therm_throt_event(unsigned int cpu, __u64 status) | ||
292 | { | ||
293 | struct mce m; | ||
294 | |||
295 | memset(&m, 0, sizeof(m)); | ||
296 | m.cpu = cpu; | ||
297 | m.bank = MCE_THERMAL_BANK; | ||
298 | m.status = status; | ||
299 | rdtscll(m.tsc); | ||
300 | mce_log(&m); | ||
301 | } | ||
302 | #endif /* CONFIG_X86_MCE_INTEL */ | ||
303 | |||
277 | /* | 304 | /* |
278 | * Periodic polling timer for "silent" machine check errors. | 305 | * Periodic polling timer for "silent" machine check errors. |
279 | */ | 306 | */ |
diff --git a/arch/x86_64/kernel/mce_intel.c b/arch/x86_64/kernel/mce_intel.c index 8f533d2c40cb..dec11219e278 100644 --- a/arch/x86_64/kernel/mce_intel.c +++ b/arch/x86_64/kernel/mce_intel.c | |||
@@ -11,36 +11,21 @@ | |||
11 | #include <asm/mce.h> | 11 | #include <asm/mce.h> |
12 | #include <asm/hw_irq.h> | 12 | #include <asm/hw_irq.h> |
13 | #include <asm/idle.h> | 13 | #include <asm/idle.h> |
14 | 14 | #include <asm/therm_throt.h> | |
15 | static DEFINE_PER_CPU(unsigned long, next_check); | ||
16 | 15 | ||
17 | asmlinkage void smp_thermal_interrupt(void) | 16 | asmlinkage void smp_thermal_interrupt(void) |
18 | { | 17 | { |
19 | struct mce m; | 18 | __u64 msr_val; |
20 | 19 | ||
21 | ack_APIC_irq(); | 20 | ack_APIC_irq(); |
22 | 21 | ||
23 | exit_idle(); | 22 | exit_idle(); |
24 | irq_enter(); | 23 | irq_enter(); |
25 | if (time_before(jiffies, __get_cpu_var(next_check))) | ||
26 | goto done; | ||
27 | |||
28 | __get_cpu_var(next_check) = jiffies + HZ*300; | ||
29 | memset(&m, 0, sizeof(m)); | ||
30 | m.cpu = smp_processor_id(); | ||
31 | m.bank = MCE_THERMAL_BANK; | ||
32 | rdtscll(m.tsc); | ||
33 | rdmsrl(MSR_IA32_THERM_STATUS, m.status); | ||
34 | if (m.status & 0x1) { | ||
35 | printk(KERN_EMERG | ||
36 | "CPU%d: Temperature above threshold, cpu clock throttled\n", m.cpu); | ||
37 | add_taint(TAINT_MACHINE_CHECK); | ||
38 | } else { | ||
39 | printk(KERN_EMERG "CPU%d: Temperature/speed normal\n", m.cpu); | ||
40 | } | ||
41 | 24 | ||
42 | mce_log(&m); | 25 | rdmsrl(MSR_IA32_THERM_STATUS, msr_val); |
43 | done: | 26 | if (therm_throt_process(msr_val & 1)) |
27 | mce_log_therm_throt_event(smp_processor_id(), msr_val); | ||
28 | |||
44 | irq_exit(); | 29 | irq_exit(); |
45 | } | 30 | } |
46 | 31 | ||