diff options
author | Linus Torvalds <torvalds@g5.osdl.org> | 2006-09-26 16:07:55 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-09-26 16:07:55 -0400 |
commit | b278240839e20fa9384ea430df463b367b90e04e (patch) | |
tree | f99f0c8cdd4cc7f177cd75440e6bd181cded7fb3 /arch/x86_64/kernel/time.c | |
parent | dd77a4ee0f3981693d4229aa1d57cea9e526ff47 (diff) | |
parent | 3f75f42d7733e73aca5c78326489efd4189e0111 (diff) |
Merge branch 'for-linus' of git://one.firstfloor.org/home/andi/git/linux-2.6
* 'for-linus' of git://one.firstfloor.org/home/andi/git/linux-2.6: (225 commits)
[PATCH] Don't set calgary iommu as default y
[PATCH] i386/x86-64: New Intel feature flags
[PATCH] x86: Add a cumulative thermal throttle event counter.
[PATCH] i386: Make the jiffies compares use the 64bit safe macros.
[PATCH] x86: Refactor thermal throttle processing
[PATCH] Add 64bit jiffies compares (for use with get_jiffies_64)
[PATCH] Fix unwinder warning in traps.c
[PATCH] x86: Allow disabling early pci scans with pci=noearly or disallowing conf1
[PATCH] x86: Move direct PCI scanning functions out of line
[PATCH] i386/x86-64: Make all early PCI scans dependent on CONFIG_PCI
[PATCH] Don't leak NT bit into next task
[PATCH] i386/x86-64: Work around gcc bug with noreturn functions in unwinder
[PATCH] Fix some broken white space in ia32_signal.c
[PATCH] Initialize argument registers for 32bit signal handlers.
[PATCH] Remove all traces of signal number conversion
[PATCH] Don't synchronize time reading on single core AMD systems
[PATCH] Remove outdated comment in x86-64 mmconfig code
[PATCH] Use string instructions for Core2 copy/clear
[PATCH] x86: - restore i8259A eoi status on resume
[PATCH] i386: Split multi-line printk in oops output.
...
Diffstat (limited to 'arch/x86_64/kernel/time.c')
-rw-r--r-- | arch/x86_64/kernel/time.c | 94 |
1 files changed, 55 insertions, 39 deletions
diff --git a/arch/x86_64/kernel/time.c b/arch/x86_64/kernel/time.c index 7700e6cd2bd9..1c255ee76e7c 100644 --- a/arch/x86_64/kernel/time.c +++ b/arch/x86_64/kernel/time.c | |||
@@ -24,6 +24,8 @@ | |||
24 | #include <linux/device.h> | 24 | #include <linux/device.h> |
25 | #include <linux/sysdev.h> | 25 | #include <linux/sysdev.h> |
26 | #include <linux/bcd.h> | 26 | #include <linux/bcd.h> |
27 | #include <linux/notifier.h> | ||
28 | #include <linux/cpu.h> | ||
27 | #include <linux/kallsyms.h> | 29 | #include <linux/kallsyms.h> |
28 | #include <linux/acpi.h> | 30 | #include <linux/acpi.h> |
29 | #ifdef CONFIG_ACPI | 31 | #ifdef CONFIG_ACPI |
@@ -39,9 +41,7 @@ | |||
39 | #include <asm/sections.h> | 41 | #include <asm/sections.h> |
40 | #include <linux/cpufreq.h> | 42 | #include <linux/cpufreq.h> |
41 | #include <linux/hpet.h> | 43 | #include <linux/hpet.h> |
42 | #ifdef CONFIG_X86_LOCAL_APIC | ||
43 | #include <asm/apic.h> | 44 | #include <asm/apic.h> |
44 | #endif | ||
45 | 45 | ||
46 | #ifdef CONFIG_CPU_FREQ | 46 | #ifdef CONFIG_CPU_FREQ |
47 | static void cpufreq_delayed_get(void); | 47 | static void cpufreq_delayed_get(void); |
@@ -49,7 +49,7 @@ static void cpufreq_delayed_get(void); | |||
49 | extern void i8254_timer_resume(void); | 49 | extern void i8254_timer_resume(void); |
50 | extern int using_apic_timer; | 50 | extern int using_apic_timer; |
51 | 51 | ||
52 | static char *time_init_gtod(void); | 52 | static char *timename = NULL; |
53 | 53 | ||
54 | DEFINE_SPINLOCK(rtc_lock); | 54 | DEFINE_SPINLOCK(rtc_lock); |
55 | EXPORT_SYMBOL(rtc_lock); | 55 | EXPORT_SYMBOL(rtc_lock); |
@@ -187,20 +187,15 @@ unsigned long profile_pc(struct pt_regs *regs) | |||
187 | { | 187 | { |
188 | unsigned long pc = instruction_pointer(regs); | 188 | unsigned long pc = instruction_pointer(regs); |
189 | 189 | ||
190 | /* Assume the lock function has either no stack frame or only a single | 190 | /* Assume the lock function has either no stack frame or a copy |
191 | word. This checks if the address on the stack looks like a kernel | 191 | of eflags from PUSHF |
192 | text address. | 192 | Eflags always has bits 22 and up cleared unlike kernel addresses. */ |
193 | There is a small window for false hits, but in that case the tick | ||
194 | is just accounted to the spinlock function. | ||
195 | Better would be to write these functions in assembler again | ||
196 | and check exactly. */ | ||
197 | if (!user_mode(regs) && in_lock_functions(pc)) { | 193 | if (!user_mode(regs) && in_lock_functions(pc)) { |
198 | char *v = *(char **)regs->rsp; | 194 | unsigned long *sp = (unsigned long *)regs->rsp; |
199 | if ((v >= _stext && v <= _etext) || | 195 | if (sp[0] >> 22) |
200 | (v >= _sinittext && v <= _einittext) || | 196 | return sp[0]; |
201 | (v >= (char *)MODULES_VADDR && v <= (char *)MODULES_END)) | 197 | if (sp[1] >> 22) |
202 | return (unsigned long)v; | 198 | return sp[1]; |
203 | return ((unsigned long *)regs->rsp)[1]; | ||
204 | } | 199 | } |
205 | return pc; | 200 | return pc; |
206 | } | 201 | } |
@@ -281,6 +276,7 @@ static void set_rtc_mmss(unsigned long nowtime) | |||
281 | * Note: This function is required to return accurate | 276 | * Note: This function is required to return accurate |
282 | * time even in the absence of multiple timer ticks. | 277 | * time even in the absence of multiple timer ticks. |
283 | */ | 278 | */ |
279 | static inline unsigned long long cycles_2_ns(unsigned long long cyc); | ||
284 | unsigned long long monotonic_clock(void) | 280 | unsigned long long monotonic_clock(void) |
285 | { | 281 | { |
286 | unsigned long seq; | 282 | unsigned long seq; |
@@ -305,8 +301,7 @@ unsigned long long monotonic_clock(void) | |||
305 | base = monotonic_base; | 301 | base = monotonic_base; |
306 | } while (read_seqretry(&xtime_lock, seq)); | 302 | } while (read_seqretry(&xtime_lock, seq)); |
307 | this_offset = get_cycles_sync(); | 303 | this_offset = get_cycles_sync(); |
308 | /* FIXME: 1000 or 1000000? */ | 304 | offset = cycles_2_ns(this_offset - last_offset); |
309 | offset = (this_offset - last_offset)*1000 / cpu_khz; | ||
310 | } | 305 | } |
311 | return base + offset; | 306 | return base + offset; |
312 | } | 307 | } |
@@ -410,8 +405,7 @@ void main_timer_handler(struct pt_regs *regs) | |||
410 | offset %= USEC_PER_TICK; | 405 | offset %= USEC_PER_TICK; |
411 | } | 406 | } |
412 | 407 | ||
413 | /* FIXME: 1000 or 1000000? */ | 408 | monotonic_base += cycles_2_ns(tsc - vxtime.last_tsc); |
414 | monotonic_base += (tsc - vxtime.last_tsc) * 1000000 / cpu_khz; | ||
415 | 409 | ||
416 | vxtime.last_tsc = tsc - vxtime.quot * delay / vxtime.tsc_quot; | 410 | vxtime.last_tsc = tsc - vxtime.quot * delay / vxtime.tsc_quot; |
417 | 411 | ||
@@ -441,12 +435,8 @@ void main_timer_handler(struct pt_regs *regs) | |||
441 | * have to call the local interrupt handler. | 435 | * have to call the local interrupt handler. |
442 | */ | 436 | */ |
443 | 437 | ||
444 | #ifndef CONFIG_X86_LOCAL_APIC | ||
445 | profile_tick(CPU_PROFILING, regs); | ||
446 | #else | ||
447 | if (!using_apic_timer) | 438 | if (!using_apic_timer) |
448 | smp_local_timer_interrupt(regs); | 439 | smp_local_timer_interrupt(regs); |
449 | #endif | ||
450 | 440 | ||
451 | /* | 441 | /* |
452 | * If we have an externally synchronized Linux clock, then update CMOS clock | 442 | * If we have an externally synchronized Linux clock, then update CMOS clock |
@@ -470,10 +460,8 @@ static irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) | |||
470 | if (apic_runs_main_timer > 1) | 460 | if (apic_runs_main_timer > 1) |
471 | return IRQ_HANDLED; | 461 | return IRQ_HANDLED; |
472 | main_timer_handler(regs); | 462 | main_timer_handler(regs); |
473 | #ifdef CONFIG_X86_LOCAL_APIC | ||
474 | if (using_apic_timer) | 463 | if (using_apic_timer) |
475 | smp_send_timer_broadcast_ipi(); | 464 | smp_send_timer_broadcast_ipi(); |
476 | #endif | ||
477 | return IRQ_HANDLED; | 465 | return IRQ_HANDLED; |
478 | } | 466 | } |
479 | 467 | ||
@@ -893,11 +881,17 @@ static struct irqaction irq0 = { | |||
893 | timer_interrupt, IRQF_DISABLED, CPU_MASK_NONE, "timer", NULL, NULL | 881 | timer_interrupt, IRQF_DISABLED, CPU_MASK_NONE, "timer", NULL, NULL |
894 | }; | 882 | }; |
895 | 883 | ||
896 | void __init time_init(void) | 884 | static int __cpuinit |
885 | time_cpu_notifier(struct notifier_block *nb, unsigned long action, void *hcpu) | ||
897 | { | 886 | { |
898 | char *timename; | 887 | unsigned cpu = (unsigned long) hcpu; |
899 | char *gtod; | 888 | if (action == CPU_ONLINE) |
889 | vsyscall_set_cpu(cpu); | ||
890 | return NOTIFY_DONE; | ||
891 | } | ||
900 | 892 | ||
893 | void __init time_init(void) | ||
894 | { | ||
901 | if (nohpet) | 895 | if (nohpet) |
902 | vxtime.hpet_address = 0; | 896 | vxtime.hpet_address = 0; |
903 | 897 | ||
@@ -931,18 +925,17 @@ void __init time_init(void) | |||
931 | } | 925 | } |
932 | 926 | ||
933 | vxtime.mode = VXTIME_TSC; | 927 | vxtime.mode = VXTIME_TSC; |
934 | gtod = time_init_gtod(); | ||
935 | |||
936 | printk(KERN_INFO "time.c: Using %ld.%06ld MHz WALL %s GTOD %s timer.\n", | ||
937 | vxtime_hz / 1000000, vxtime_hz % 1000000, timename, gtod); | ||
938 | printk(KERN_INFO "time.c: Detected %d.%03d MHz processor.\n", | ||
939 | cpu_khz / 1000, cpu_khz % 1000); | ||
940 | vxtime.quot = (USEC_PER_SEC << US_SCALE) / vxtime_hz; | 928 | vxtime.quot = (USEC_PER_SEC << US_SCALE) / vxtime_hz; |
941 | vxtime.tsc_quot = (USEC_PER_MSEC << US_SCALE) / cpu_khz; | 929 | vxtime.tsc_quot = (USEC_PER_MSEC << US_SCALE) / cpu_khz; |
942 | vxtime.last_tsc = get_cycles_sync(); | 930 | vxtime.last_tsc = get_cycles_sync(); |
931 | set_cyc2ns_scale(cpu_khz); | ||
943 | setup_irq(0, &irq0); | 932 | setup_irq(0, &irq0); |
933 | hotcpu_notifier(time_cpu_notifier, 0); | ||
934 | time_cpu_notifier(NULL, CPU_ONLINE, (void *)(long)smp_processor_id()); | ||
944 | 935 | ||
945 | set_cyc2ns_scale(cpu_khz); | 936 | #ifndef CONFIG_SMP |
937 | time_init_gtod(); | ||
938 | #endif | ||
946 | } | 939 | } |
947 | 940 | ||
948 | /* | 941 | /* |
@@ -973,12 +966,18 @@ __cpuinit int unsynchronized_tsc(void) | |||
973 | /* | 966 | /* |
974 | * Decide what mode gettimeofday should use. | 967 | * Decide what mode gettimeofday should use. |
975 | */ | 968 | */ |
976 | __init static char *time_init_gtod(void) | 969 | void time_init_gtod(void) |
977 | { | 970 | { |
978 | char *timetype; | 971 | char *timetype; |
979 | 972 | ||
980 | if (unsynchronized_tsc()) | 973 | if (unsynchronized_tsc()) |
981 | notsc = 1; | 974 | notsc = 1; |
975 | |||
976 | if (cpu_has(&boot_cpu_data, X86_FEATURE_RDTSCP)) | ||
977 | vgetcpu_mode = VGETCPU_RDTSCP; | ||
978 | else | ||
979 | vgetcpu_mode = VGETCPU_LSL; | ||
980 | |||
982 | if (vxtime.hpet_address && notsc) { | 981 | if (vxtime.hpet_address && notsc) { |
983 | timetype = hpet_use_timer ? "HPET" : "PIT/HPET"; | 982 | timetype = hpet_use_timer ? "HPET" : "PIT/HPET"; |
984 | if (hpet_use_timer) | 983 | if (hpet_use_timer) |
@@ -1001,7 +1000,16 @@ __init static char *time_init_gtod(void) | |||
1001 | timetype = hpet_use_timer ? "HPET/TSC" : "PIT/TSC"; | 1000 | timetype = hpet_use_timer ? "HPET/TSC" : "PIT/TSC"; |
1002 | vxtime.mode = VXTIME_TSC; | 1001 | vxtime.mode = VXTIME_TSC; |
1003 | } | 1002 | } |
1004 | return timetype; | 1003 | |
1004 | printk(KERN_INFO "time.c: Using %ld.%06ld MHz WALL %s GTOD %s timer.\n", | ||
1005 | vxtime_hz / 1000000, vxtime_hz % 1000000, timename, timetype); | ||
1006 | printk(KERN_INFO "time.c: Detected %d.%03d MHz processor.\n", | ||
1007 | cpu_khz / 1000, cpu_khz % 1000); | ||
1008 | vxtime.quot = (USEC_PER_SEC << US_SCALE) / vxtime_hz; | ||
1009 | vxtime.tsc_quot = (USEC_PER_MSEC << US_SCALE) / cpu_khz; | ||
1010 | vxtime.last_tsc = get_cycles_sync(); | ||
1011 | |||
1012 | set_cyc2ns_scale(cpu_khz); | ||
1005 | } | 1013 | } |
1006 | 1014 | ||
1007 | __setup("report_lost_ticks", time_setup); | 1015 | __setup("report_lost_ticks", time_setup); |
@@ -1031,8 +1039,16 @@ static int timer_resume(struct sys_device *dev) | |||
1031 | unsigned long flags; | 1039 | unsigned long flags; |
1032 | unsigned long sec; | 1040 | unsigned long sec; |
1033 | unsigned long ctime = get_cmos_time(); | 1041 | unsigned long ctime = get_cmos_time(); |
1034 | unsigned long sleep_length = (ctime - sleep_start) * HZ; | 1042 | long sleep_length = (ctime - sleep_start) * HZ; |
1035 | 1043 | ||
1044 | if (sleep_length < 0) { | ||
1045 | printk(KERN_WARNING "Time skew detected in timer resume!\n"); | ||
1046 | /* The time after the resume must not be earlier than the time | ||
1047 | * before the suspend or some nasty things will happen | ||
1048 | */ | ||
1049 | sleep_length = 0; | ||
1050 | ctime = sleep_start; | ||
1051 | } | ||
1036 | if (vxtime.hpet_address) | 1052 | if (vxtime.hpet_address) |
1037 | hpet_reenable(); | 1053 | hpet_reenable(); |
1038 | else | 1054 | else |