aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86_64/kernel/time.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@g5.osdl.org>2006-09-26 16:07:55 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2006-09-26 16:07:55 -0400
commitb278240839e20fa9384ea430df463b367b90e04e (patch)
treef99f0c8cdd4cc7f177cd75440e6bd181cded7fb3 /arch/x86_64/kernel/time.c
parentdd77a4ee0f3981693d4229aa1d57cea9e526ff47 (diff)
parent3f75f42d7733e73aca5c78326489efd4189e0111 (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.c94
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
47static void cpufreq_delayed_get(void); 47static void cpufreq_delayed_get(void);
@@ -49,7 +49,7 @@ static void cpufreq_delayed_get(void);
49extern void i8254_timer_resume(void); 49extern void i8254_timer_resume(void);
50extern int using_apic_timer; 50extern int using_apic_timer;
51 51
52static char *time_init_gtod(void); 52static char *timename = NULL;
53 53
54DEFINE_SPINLOCK(rtc_lock); 54DEFINE_SPINLOCK(rtc_lock);
55EXPORT_SYMBOL(rtc_lock); 55EXPORT_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 */
279static inline unsigned long long cycles_2_ns(unsigned long long cyc);
284unsigned long long monotonic_clock(void) 280unsigned 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
896void __init time_init(void) 884static int __cpuinit
885time_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
893void __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) 969void 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