include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit...
[safe/jmp/linux-2.6] / arch / ia64 / kernel / time.c
index 71ccdda..47a1927 100644 (file)
 #include <linux/time.h>
 #include <linux/interrupt.h>
 #include <linux/efi.h>
-#include <linux/profile.h>
 #include <linux/timex.h>
+#include <linux/clocksource.h>
+#include <linux/platform_device.h>
 
 #include <asm/machvec.h>
 #include <asm/delay.h>
 #include <asm/hw_irq.h>
+#include <asm/paravirt.h>
 #include <asm/ptrace.h>
 #include <asm/sal.h>
 #include <asm/sections.h>
 #include <asm/system.h>
 
-extern unsigned long wall_jiffies;
+#include "fsyscall_gtod_data.h"
+
+static cycle_t itc_get_cycles(struct clocksource *cs);
+
+struct fsyscall_gtod_data_t fsyscall_gtod_data = {
+       .lock = SEQLOCK_UNLOCKED,
+};
+
+struct itc_jitter_data_t itc_jitter_data;
 
 volatile int time_keeper_id = 0; /* smp_processor_id() of time-keeper */
 
@@ -40,14 +50,121 @@ EXPORT_SYMBOL(last_cli_ip);
 
 #endif
 
-static struct time_interpolator itc_interpolator = {
-       .shift = 16,
-       .mask = 0xffffffffffffffffLL,
-       .source = TIME_SOURCE_CPU
+#ifdef CONFIG_PARAVIRT
+/* We need to define a real function for sched_clock, to override the
+   weak default version */
+unsigned long long sched_clock(void)
+{
+        return paravirt_sched_clock();
+}
+#endif
+
+#ifdef CONFIG_PARAVIRT
+static void
+paravirt_clocksource_resume(struct clocksource *cs)
+{
+       if (pv_time_ops.clocksource_resume)
+               pv_time_ops.clocksource_resume();
+}
+#endif
+
+static struct clocksource clocksource_itc = {
+       .name           = "itc",
+       .rating         = 350,
+       .read           = itc_get_cycles,
+       .mask           = CLOCKSOURCE_MASK(64),
+       .mult           = 0, /*to be calculated*/
+       .shift          = 16,
+       .flags          = CLOCK_SOURCE_IS_CONTINUOUS,
+#ifdef CONFIG_PARAVIRT
+       .resume         = paravirt_clocksource_resume,
+#endif
 };
+static struct clocksource *itc_clocksource;
+
+#ifdef CONFIG_VIRT_CPU_ACCOUNTING
+
+#include <linux/kernel_stat.h>
+
+extern cputime_t cycle_to_cputime(u64 cyc);
+
+/*
+ * Called from the context switch with interrupts disabled, to charge all
+ * accumulated times to the current process, and to prepare accounting on
+ * the next process.
+ */
+void ia64_account_on_switch(struct task_struct *prev, struct task_struct *next)
+{
+       struct thread_info *pi = task_thread_info(prev);
+       struct thread_info *ni = task_thread_info(next);
+       cputime_t delta_stime, delta_utime;
+       __u64 now;
+
+       now = ia64_get_itc();
+
+       delta_stime = cycle_to_cputime(pi->ac_stime + (now - pi->ac_stamp));
+       if (idle_task(smp_processor_id()) != prev)
+               account_system_time(prev, 0, delta_stime, delta_stime);
+       else
+               account_idle_time(delta_stime);
+
+       if (pi->ac_utime) {
+               delta_utime = cycle_to_cputime(pi->ac_utime);
+               account_user_time(prev, delta_utime, delta_utime);
+       }
+
+       pi->ac_stamp = ni->ac_stamp = now;
+       ni->ac_stime = ni->ac_utime = 0;
+}
+
+/*
+ * Account time for a transition between system, hard irq or soft irq state.
+ * Note that this function is called with interrupts enabled.
+ */
+void account_system_vtime(struct task_struct *tsk)
+{
+       struct thread_info *ti = task_thread_info(tsk);
+       unsigned long flags;
+       cputime_t delta_stime;
+       __u64 now;
+
+       local_irq_save(flags);
+
+       now = ia64_get_itc();
+
+       delta_stime = cycle_to_cputime(ti->ac_stime + (now - ti->ac_stamp));
+       if (irq_count() || idle_task(smp_processor_id()) != tsk)
+               account_system_time(tsk, 0, delta_stime, delta_stime);
+       else
+               account_idle_time(delta_stime);
+       ti->ac_stime = 0;
+
+       ti->ac_stamp = now;
+
+       local_irq_restore(flags);
+}
+EXPORT_SYMBOL_GPL(account_system_vtime);
+
+/*
+ * Called from the timer interrupt handler to charge accumulated user time
+ * to the current process.  Must be called with interrupts disabled.
+ */
+void account_process_tick(struct task_struct *p, int user_tick)
+{
+       struct thread_info *ti = task_thread_info(p);
+       cputime_t delta_utime;
+
+       if (ti->ac_utime) {
+               delta_utime = cycle_to_cputime(ti->ac_utime);
+               account_user_time(p, delta_utime, delta_utime);
+               ti->ac_utime = 0;
+       }
+}
+
+#endif /* CONFIG_VIRT_CPU_ACCOUNTING */
 
 static irqreturn_t
-timer_interrupt (int irq, void *dev_id, struct pt_regs *regs)
+timer_interrupt (int irq, void *dev_id)
 {
        unsigned long new_itm;
 
@@ -55,7 +172,7 @@ timer_interrupt (int irq, void *dev_id, struct pt_regs *regs)
                return IRQ_HANDLED;
        }
 
-       platform_timer_interrupt(irq, dev_id, regs);
+       platform_timer_interrupt(irq, dev_id);
 
        new_itm = local_cpu_data->itm_next;
 
@@ -63,10 +180,13 @@ timer_interrupt (int irq, void *dev_id, struct pt_regs *regs)
                printk(KERN_ERR "Oops: timer tick before it's due (itc=%lx,itm=%lx)\n",
                       ia64_get_itc(), new_itm);
 
-       profile_tick(CPU_PROFILING, regs);
+       profile_tick(CPU_PROFILING);
+
+       if (paravirt_do_steal_accounting(&new_itm))
+               goto skip_process_time_accounting;
 
        while (1) {
-               update_process_times(user_mode(regs));
+               update_process_times(user_mode(get_irq_regs()));
 
                new_itm += local_cpu_data->itm_delta;
 
@@ -78,7 +198,7 @@ timer_interrupt (int irq, void *dev_id, struct pt_regs *regs)
                         * xtime_lock.
                         */
                        write_seqlock(&xtime_lock);
-                       do_timer(regs);
+                       do_timer(1);
                        local_cpu_data->itm_next = new_itm;
                        write_sequnlock(&xtime_lock);
                } else
@@ -86,8 +206,16 @@ timer_interrupt (int irq, void *dev_id, struct pt_regs *regs)
 
                if (time_after(new_itm, ia64_get_itc()))
                        break;
+
+               /*
+                * Allow IPIs to interrupt the timer loop.
+                */
+               local_irq_enable();
+               local_irq_disable();
        }
 
+skip_process_time_accounting:
+
        do {
                /*
                 * If we're too close to the next clock tick for
@@ -207,8 +335,6 @@ ia64_init_itm (void)
                                        + itc_freq/2)/itc_freq;
 
        if (!(sal_platform_features & IA64_SAL_PLATFORM_FEATURE_ITC_DRIFT)) {
-               itc_interpolator.frequency = local_cpu_data->itc_freq;
-               itc_interpolator.drift = itc_drift;
 #ifdef CONFIG_SMP
                /* On IA64 in an SMP configuration ITCs are never accurately synchronized.
                 * Jitter compensation requires a cmpxchg which may limit
@@ -220,25 +346,89 @@ ia64_init_itm (void)
                 * even going backward) if the ITC offsets between the individual CPUs
                 * are too large.
                 */
-               if (!nojitter) itc_interpolator.jitter = 1;
+               if (!nojitter)
+                       itc_jitter_data.itc_jitter = 1;
 #endif
-               register_time_interpolator(&itc_interpolator);
-       }
+       } else
+               /*
+                * ITC is drifty and we have not synchronized the ITCs in smpboot.c.
+                * ITC values may fluctuate significantly between processors.
+                * Clock should not be used for hrtimers. Mark itc as only
+                * useful for boot and testing.
+                *
+                * Note that jitter compensation is off! There is no point of
+                * synchronizing ITCs since they may be large differentials
+                * that change over time.
+                *
+                * The only way to fix this would be to repeatedly sync the
+                * ITCs. Until that time we have to avoid ITC.
+                */
+               clocksource_itc.rating = 50;
+
+       paravirt_init_missing_ticks_accounting(smp_processor_id());
+
+       /* avoid softlock up message when cpu is unplug and plugged again. */
+       touch_softlockup_watchdog();
 
        /* Setup the CPU local timer tick */
        ia64_cpu_local_tick();
+
+       if (!itc_clocksource) {
+               /* Sort out mult/shift values: */
+               clocksource_itc.mult =
+                       clocksource_hz2mult(local_cpu_data->itc_freq,
+                                               clocksource_itc.shift);
+               clocksource_register(&clocksource_itc);
+               itc_clocksource = &clocksource_itc;
+       }
+}
+
+static cycle_t itc_get_cycles(struct clocksource *cs)
+{
+       unsigned long lcycle, now, ret;
+
+       if (!itc_jitter_data.itc_jitter)
+               return get_cycles();
+
+       lcycle = itc_jitter_data.itc_lastcycle;
+       now = get_cycles();
+       if (lcycle && time_after(lcycle, now))
+               return lcycle;
+
+       /*
+        * Keep track of the last timer value returned.
+        * In an SMP environment, you could lose out in contention of
+        * cmpxchg. If so, your cmpxchg returns new value which the
+        * winner of contention updated to. Use the new value instead.
+        */
+       ret = cmpxchg(&itc_jitter_data.itc_lastcycle, lcycle, now);
+       if (unlikely(ret != lcycle))
+               return ret;
+
+       return now;
 }
 
+
 static struct irqaction timer_irqaction = {
        .handler =      timer_interrupt,
-       .flags =        SA_INTERRUPT,
+       .flags =        IRQF_DISABLED | IRQF_IRQPOLL,
        .name =         "timer"
 };
 
-void __devinit ia64_disable_timer(void)
+static struct platform_device rtc_efi_dev = {
+       .name = "rtc-efi",
+       .id = -1,
+};
+
+static int __init rtc_init(void)
 {
-       ia64_set_itv(1 << 16);
+       if (platform_device_register(&rtc_efi_dev) < 0)
+               printk(KERN_ERR "unable to register rtc device...\n");
+
+       /* not necessarily an error */
+       return 0;
 }
+module_init(rtc_init);
 
 void __init
 time_init (void)
@@ -278,29 +468,38 @@ udelay (unsigned long usecs)
 }
 EXPORT_SYMBOL(udelay);
 
-static unsigned long long ia64_itc_printk_clock(void)
+/* IA64 doesn't cache the timezone */
+void update_vsyscall_tz(void)
 {
-       if (ia64_get_kr(IA64_KR_PER_CPU_DATA))
-               return sched_clock();
-       return 0;
 }
 
-static unsigned long long ia64_default_printk_clock(void)
+void update_vsyscall(struct timespec *wall, struct clocksource *c, u32 mult)
 {
-       return (unsigned long long)(jiffies_64 - INITIAL_JIFFIES) *
-               (1000000000/HZ);
-}
-
-unsigned long long (*ia64_printk_clock)(void) = &ia64_default_printk_clock;
+        unsigned long flags;
+
+        write_seqlock_irqsave(&fsyscall_gtod_data.lock, flags);
+
+        /* copy fsyscall clock data */
+        fsyscall_gtod_data.clk_mask = c->mask;
+        fsyscall_gtod_data.clk_mult = mult;
+        fsyscall_gtod_data.clk_shift = c->shift;
+        fsyscall_gtod_data.clk_fsys_mmio = c->fsys_mmio;
+        fsyscall_gtod_data.clk_cycle_last = c->cycle_last;
+
+       /* copy kernel time structures */
+        fsyscall_gtod_data.wall_time.tv_sec = wall->tv_sec;
+        fsyscall_gtod_data.wall_time.tv_nsec = wall->tv_nsec;
+        fsyscall_gtod_data.monotonic_time.tv_sec = wall_to_monotonic.tv_sec
+                                                       + wall->tv_sec;
+        fsyscall_gtod_data.monotonic_time.tv_nsec = wall_to_monotonic.tv_nsec
+                                                       + wall->tv_nsec;
+
+       /* normalize */
+       while (fsyscall_gtod_data.monotonic_time.tv_nsec >= NSEC_PER_SEC) {
+               fsyscall_gtod_data.monotonic_time.tv_nsec -= NSEC_PER_SEC;
+               fsyscall_gtod_data.monotonic_time.tv_sec++;
+       }
 
-unsigned long long printk_clock(void)
-{
-       return ia64_printk_clock();
+        write_sequnlock_irqrestore(&fsyscall_gtod_data.lock, flags);
 }
 
-void __init
-ia64_setup_printk_clock(void)
-{
-       if (!(sal_platform_features & IA64_SAL_PLATFORM_FEATURE_ITC_DRIFT))
-               ia64_printk_clock = ia64_itc_printk_clock;
-}