X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=arch%2Fppc64%2Fkernel%2Ftime.c;h=3d88a7086ad57b01ad46efec312f328bff1f4f78;hb=6a77f38946aaee1cd85eeec6cf4229b204c15071;hp=64844df4a532a99ff477fa2acbcc3aadc4049a6b;hpb=9bf4aaab3e101692164d49b7ca357651eb691cb6;p=linux-2.6.git diff --git a/arch/ppc64/kernel/time.c b/arch/ppc64/kernel/time.c index 64844df4a..3d88a7086 100644 --- a/arch/ppc64/kernel/time.c +++ b/arch/ppc64/kernel/time.c @@ -48,6 +48,8 @@ #include #include #include +#include +#include #include #include @@ -64,8 +66,7 @@ #include #include #include - -void smp_local_timer_interrupt(struct pt_regs *); +#include u64 jiffies_64 __cacheline_aligned_in_smp = INITIAL_JIFFIES; @@ -83,14 +84,14 @@ static unsigned long first_settimeofday = 1; #define XSEC_PER_SEC (1024*1024) unsigned long tb_ticks_per_jiffy; -unsigned long tb_ticks_per_usec; +unsigned long tb_ticks_per_usec = 100; /* sane default */ unsigned long tb_ticks_per_sec; unsigned long next_xtime_sync_tb; unsigned long xtime_sync_interval; unsigned long tb_to_xs; unsigned tb_to_us; unsigned long processor_freq; -spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED; +DEFINE_SPINLOCK(rtc_lock); unsigned long tb_to_ns_scale; unsigned long tb_to_ns_shift; @@ -101,50 +102,12 @@ extern unsigned long wall_jiffies; extern unsigned long lpevent_count; extern int smp_tb_synchronized; +extern struct timezone sys_tz; + void ppc_adjtimex(void); static unsigned adjusting_time = 0; -/* - * The profiling function is SMP safe. (nothing can mess - * around with "current", and the profiling counters are - * updated with atomic operations). This is especially - * useful with a profiling multiplier != 1 - */ -static inline void ppc64_do_profile(struct pt_regs *regs) -{ - unsigned long nip; - extern unsigned long prof_cpu_mask; - - profile_hook(regs); - - if (user_mode(regs)) - return; - - if (!prof_buffer) - return; - - nip = instruction_pointer(regs); - - /* - * Only measure the CPUs specified by /proc/irq/prof_cpu_mask. - * (default is all CPUs.) - */ - if (!((1<>= prof_shift; - /* - * Don't ignore out-of-bounds EIP values silently, - * put them into the last histogram slot, so if - * present, they will show up as a sharp peak. - */ - if (nip > prof_len-1) - nip = prof_len-1; - atomic_inc((atomic_t *)&prof_buffer[nip]); -} - static __inline__ void timer_check_rtc(void) { /* @@ -179,22 +142,73 @@ static __inline__ void timer_check_rtc(void) } } +/* + * This version of gettimeofday has microsecond resolution. + */ +static inline void __do_gettimeofday(struct timeval *tv, unsigned long tb_val) +{ + unsigned long sec, usec, tb_ticks; + unsigned long xsec, tb_xsec; + struct gettimeofday_vars * temp_varp; + unsigned long temp_tb_to_xs, temp_stamp_xsec; + + /* + * These calculations are faster (gets rid of divides) + * if done in units of 1/2^20 rather than microseconds. + * The conversion to microseconds at the end is done + * without a divide (and in fact, without a multiply) + */ + tb_ticks = tb_val - do_gtod.tb_orig_stamp; + temp_varp = do_gtod.varp; + temp_tb_to_xs = temp_varp->tb_to_xs; + temp_stamp_xsec = temp_varp->stamp_xsec; + tb_xsec = mulhdu( tb_ticks, temp_tb_to_xs ); + xsec = temp_stamp_xsec + tb_xsec; + sec = xsec / XSEC_PER_SEC; + xsec -= sec * XSEC_PER_SEC; + usec = (xsec * USEC_PER_SEC)/XSEC_PER_SEC; + + tv->tv_sec = sec; + tv->tv_usec = usec; +} + +void do_gettimeofday(struct timeval *tv) +{ + __do_gettimeofday(tv, get_tb()); +} + +EXPORT_SYMBOL(do_gettimeofday); + /* Synchronize xtime with do_gettimeofday */ -static __inline__ void timer_sync_xtime( unsigned long cur_tb ) +static inline void timer_sync_xtime(unsigned long cur_tb) { struct timeval my_tv; - if ( cur_tb > next_xtime_sync_tb ) { + if (cur_tb > next_xtime_sync_tb) { next_xtime_sync_tb = cur_tb + xtime_sync_interval; - do_gettimeofday( &my_tv ); - if ( xtime.tv_sec <= my_tv.tv_sec ) { + __do_gettimeofday(&my_tv, cur_tb); + + if (xtime.tv_sec <= my_tv.tv_sec) { xtime.tv_sec = my_tv.tv_sec; xtime.tv_nsec = my_tv.tv_usec * 1000; } } } +#ifdef CONFIG_SMP +unsigned long profile_pc(struct pt_regs *regs) +{ + unsigned long pc = instruction_pointer(regs); + + if (in_lock_functions(pc)) + return regs->link; + + return pc; +} +EXPORT_SYMBOL(profile_pc); +#endif + #ifdef CONFIG_PPC_ISERIES /* @@ -233,6 +247,8 @@ static void iSeries_tb_recal(void) do_gtod.tb_ticks_per_sec = tb_ticks_per_sec; tb_to_xs = divres.result_low; do_gtod.varp->tb_to_xs = tb_to_xs; + systemcfg->tb_ticks_per_sec = tb_ticks_per_sec; + systemcfg->tb_to_xs = tb_to_xs; } else { printk( "Titan recalibrate: FAILED (difference > 4 percent)\n" @@ -250,7 +266,7 @@ static void iSeries_tb_recal(void) /* * For iSeries shared processors, we have to let the hypervisor * set the hardware decrementer. We set a virtual decrementer - * in the ItLpPaca and call the hypervisor if the virtual + * in the lppaca and call the hypervisor if the virtual * decrementer is less than the current value in the hardware * decrementer. (almost always the new decrementer value will * be greater than the current hardware decementer so the hypervisor @@ -273,21 +289,30 @@ int timer_interrupt(struct pt_regs * regs) irq_enter(); #ifndef CONFIG_PPC_ISERIES - ppc64_do_profile(regs); + profile_tick(CPU_PROFILING, regs); #endif - lpaca->lppaca.xIntDword.xFields.xDecrInt = 0; + lpaca->lppaca.int_dword.fields.decr_int = 0; while (lpaca->next_jiffy_update_tb <= (cur_tb = get_tb())) { - -#ifdef CONFIG_SMP - smp_local_timer_interrupt(regs); -#endif + /* + * We cannot disable the decrementer, so in the period + * between this cpu's being marked offline in cpu_online_map + * and calling stop-self, it is taking timer interrupts. + * Avoid calling into the scheduler rebalancing code if this + * is the case. + */ + if (!cpu_is_offline(cpu)) + update_process_times(user_mode(regs)); + /* + * No need to check whether cpu is offline here; boot_cpuid + * should have been fixed up by now. + */ if (cpu == boot_cpuid) { write_seqlock(&xtime_lock); tb_last_stamp = lpaca->next_jiffy_update_tb; do_timer(regs); - timer_sync_xtime( cur_tb ); + timer_sync_xtime(lpaca->next_jiffy_update_tb); timer_check_rtc(); write_sequnlock(&xtime_lock); if ( adjusting_time && (time_adjust == 0) ) @@ -326,36 +351,6 @@ unsigned long long sched_clock(void) return mulhdu(get_tb(), tb_to_ns_scale) << tb_to_ns_shift; } -/* - * This version of gettimeofday has microsecond resolution. - */ -void do_gettimeofday(struct timeval *tv) -{ - unsigned long sec, usec, tb_ticks; - unsigned long xsec, tb_xsec; - struct gettimeofday_vars * temp_varp; - unsigned long temp_tb_to_xs, temp_stamp_xsec; - - /* These calculations are faster (gets rid of divides) - * if done in units of 1/2^20 rather than microseconds. - * The conversion to microseconds at the end is done - * without a divide (and in fact, without a multiply) */ - tb_ticks = get_tb() - do_gtod.tb_orig_stamp; - temp_varp = do_gtod.varp; - temp_tb_to_xs = temp_varp->tb_to_xs; - temp_stamp_xsec = temp_varp->stamp_xsec; - tb_xsec = mulhdu( tb_ticks, temp_tb_to_xs ); - xsec = temp_stamp_xsec + tb_xsec; - sec = xsec / XSEC_PER_SEC; - xsec -= sec * XSEC_PER_SEC; - usec = (xsec * USEC_PER_SEC)/XSEC_PER_SEC; - - tv->tv_sec = sec; - tv->tv_usec = usec; -} - -EXPORT_SYMBOL(do_gettimeofday); - int do_settimeofday(struct timespec *tv) { time_t wtm_sec, new_sec = tv->tv_sec; @@ -408,6 +403,7 @@ int do_settimeofday(struct timespec *tv) new_xsec += new_sec * XSEC_PER_SEC; if ( new_xsec > delta_xsec ) { do_gtod.varp->stamp_xsec = new_xsec - delta_xsec; + systemcfg->stamp_xsec = new_xsec - delta_xsec; } else { /* This is only for the case where the user is setting the time @@ -416,8 +412,13 @@ int do_settimeofday(struct timespec *tv) * the time to Jan 5, 1970 */ do_gtod.varp->stamp_xsec = new_xsec; do_gtod.tb_orig_stamp = tb_last_stamp; + systemcfg->stamp_xsec = new_xsec; + systemcfg->tb_orig_stamp = tb_last_stamp; } + systemcfg->tz_minuteswest = sys_tz.tz_minuteswest; + systemcfg->tz_dsttime = sys_tz.tz_dsttime; + write_sequnlock_irqrestore(&xtime_lock, flags); clock_was_set(); return 0; @@ -425,56 +426,6 @@ int do_settimeofday(struct timespec *tv) EXPORT_SYMBOL(do_settimeofday); -/* - * This function is a copy of the architecture independent function - * but which calls do_settimeofday rather than setting the xtime - * fields itself. This way, the fields which are used for - * do_settimeofday get updated too. - */ -long ppc64_sys32_stime(int __user * tptr) -{ - int value; - struct timespec myTimeval; - - if (!capable(CAP_SYS_TIME)) - return -EPERM; - - if (get_user(value, tptr)) - return -EFAULT; - - myTimeval.tv_sec = value; - myTimeval.tv_nsec = 0; - - do_settimeofday(&myTimeval); - - return 0; -} - -/* - * This function is a copy of the architecture independent function - * but which calls do_settimeofday rather than setting the xtime - * fields itself. This way, the fields which are used for - * do_settimeofday get updated too. - */ -long ppc64_sys_stime(long __user * tptr) -{ - long value; - struct timespec myTimeval; - - if (!capable(CAP_SYS_TIME)) - return -EPERM; - - if (get_user(value, tptr)) - return -EFAULT; - - myTimeval.tv_sec = value; - myTimeval.tv_nsec = 0; - - do_settimeofday(&myTimeval); - - return 0; -} - void __init time_init(void) { /* This function is only called on the boot processor */ @@ -520,6 +471,11 @@ void __init time_init(void) do_gtod.tb_ticks_per_sec = tb_ticks_per_sec; do_gtod.varp->tb_to_xs = tb_to_xs; do_gtod.tb_to_us = tb_to_us; + systemcfg->tb_orig_stamp = tb_last_stamp; + systemcfg->tb_update_count = 0; + systemcfg->tb_ticks_per_sec = tb_ticks_per_sec; + systemcfg->stamp_xsec = xtime.tv_sec * XSEC_PER_SEC; + systemcfg->tb_to_xs = tb_to_xs; xtime_sync_interval = tb_ticks_per_sec - (tb_ticks_per_sec/8); next_xtime_sync_tb = tb_last_stamp + xtime_sync_interval; @@ -655,6 +611,22 @@ void ppc_adjtimex(void) do_gtod.varp = temp_varp; do_gtod.var_idx = temp_idx; + /* + * tb_update_count is used to allow the problem state gettimeofday code + * to assure itself that it sees a consistent view of the tb_to_xs and + * stamp_xsec variables. It reads the tb_update_count, then reads + * tb_to_xs and stamp_xsec and then reads tb_update_count again. If + * the two values of tb_update_count match and are even then the + * tb_to_xs and stamp_xsec values are consistent. If not, then it + * loops back and reads them again until this criteria is met. + */ + ++(systemcfg->tb_update_count); + wmb(); + systemcfg->tb_to_xs = new_tb_to_xs; + systemcfg->stamp_xsec = new_stamp_xsec; + wmb(); + ++(systemcfg->tb_update_count); + write_sequnlock_irqrestore( &xtime_lock, flags ); }