Stefano Stabellini
2010-Feb-09 20:10 UTC
[Xen-devel] [PATCH] [PVOPS] [UPDATE] dom0 sync xen wallclock
Hi Jeremy, this patch removes clock_was_set and adds an atomic notification chain instead so that not only hrtimers but other parts of the kernel can be notified of a time change as well. In fact xen/time.c needs to be notified so that can update xen wallclock time to keep it in sync; this is necessary otherwise other PV guests will get a wrong wallclock time at boot. This updated version contains more clock_was_set substitutions for architectures other than x86. Please let me know if my approach is suitable for upstream, I am willing to do any change needed and to send the patch upstream myself if you give me some directions. Cheers, Stefano Signed-off-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com> --- diff --git a/arch/alpha/kernel/time.c b/arch/alpha/kernel/time.c index b04e2cb..7615696 100644 --- a/arch/alpha/kernel/time.c +++ b/arch/alpha/kernel/time.c @@ -497,7 +497,7 @@ do_settimeofday(struct timespec *tv) ntp_clear(); write_sequnlock_irq(&xtime_lock); - clock_was_set(); + atomic_notifier_call_chain(&clockset_notifier_list, 0, NULL); return 0; } diff --git a/arch/arm/kernel/time.c b/arch/arm/kernel/time.c index 4cdc4a0..2bb7e6f 100644 --- a/arch/arm/kernel/time.c +++ b/arch/arm/kernel/time.c @@ -287,7 +287,7 @@ int do_settimeofday(struct timespec *tv) ntp_clear(); write_sequnlock_irq(&xtime_lock); - clock_was_set(); + atomic_notifier_call_chain(&clockset_notifier_list, 0, NULL); return 0; } diff --git a/arch/blackfin/kernel/time.c b/arch/blackfin/kernel/time.c index adb54aa..521263f 100644 --- a/arch/blackfin/kernel/time.c +++ b/arch/blackfin/kernel/time.c @@ -236,7 +236,7 @@ int do_settimeofday(struct timespec *tv) ntp_clear(); write_sequnlock_irq(&xtime_lock); - clock_was_set(); + atomic_notifier_call_chain(&clockset_notifier_list, 0, NULL); return 0; } diff --git a/arch/cris/kernel/time.c b/arch/cris/kernel/time.c index 074fe7d..ff9fad8 100644 --- a/arch/cris/kernel/time.c +++ b/arch/cris/kernel/time.c @@ -104,7 +104,7 @@ int do_settimeofday(struct timespec *tv) ntp_clear(); write_sequnlock_irq(&xtime_lock); - clock_was_set(); + atomic_notifier_call_chain(&clockset_notifier_list, 0, NULL); return 0; } diff --git a/arch/m32r/kernel/time.c b/arch/m32r/kernel/time.c index cada3ba..750826c 100644 --- a/arch/m32r/kernel/time.c +++ b/arch/m32r/kernel/time.c @@ -158,7 +158,7 @@ int do_settimeofday(struct timespec *tv) ntp_clear(); write_sequnlock_irq(&xtime_lock); - clock_was_set(); + atomic_notifier_call_chain(&clockset_notifier_list, 0, NULL); return 0; } diff --git a/arch/m68k/kernel/time.c b/arch/m68k/kernel/time.c index 54d9807..1d9dc12 100644 --- a/arch/m68k/kernel/time.c +++ b/arch/m68k/kernel/time.c @@ -155,7 +155,7 @@ int do_settimeofday(struct timespec *tv) ntp_clear(); write_sequnlock_irq(&xtime_lock); - clock_was_set(); + atomic_notifier_call_chain(&clockset_notifier_list, 0, NULL); return 0; } diff --git a/arch/sparc/kernel/time_32.c b/arch/sparc/kernel/time_32.c index 614ac7b..b987855 100644 --- a/arch/sparc/kernel/time_32.c +++ b/arch/sparc/kernel/time_32.c @@ -284,7 +284,7 @@ int do_settimeofday(struct timespec *tv) write_seqlock_irq(&xtime_lock); ret = bus_do_settimeofday(tv); write_sequnlock_irq(&xtime_lock); - clock_was_set(); + atomic_notifier_call_chain(&clockset_notifier_list, 0, NULL); return ret; } diff --git a/arch/x86/xen/time.c b/arch/x86/xen/time.c index 0b56fd4..162a0a0 100644 --- a/arch/x86/xen/time.c +++ b/arch/x86/xen/time.c @@ -13,6 +13,7 @@ #include <linux/clockchips.h> #include <linux/kernel_stat.h> #include <linux/math64.h> +#include <linux/notifier.h> #include <asm/pvclock.h> #include <asm/xen/hypervisor.h> @@ -526,6 +527,42 @@ static int __init xen_setup_vsyscall_timeinfo(int cpu) } #endif /* CONFIG_PARAVIRT_CLOCK_VSYSCALL */ +static void sync_xen_wallclock(unsigned long dummy); +static DEFINE_TIMER(sync_xen_wallclock_timer, sync_xen_wallclock, 0, 0); +static void sync_xen_wallclock(unsigned long dummy) +{ + struct xen_platform_op op; + struct timespec ts; + + write_seqlock_irq(&xtime_lock); + + set_normalized_timespec(&ts, xtime.tv_sec, xtime.tv_nsec); + + op.cmd = XENPF_settime; + op.u.settime.secs = ts.tv_sec; + op.u.settime.nsecs = ts.tv_nsec; + op.u.settime.system_time = xen_clocksource_read(); + WARN_ON(HYPERVISOR_dom0_op(&op)); + + write_sequnlock_irq(&xtime_lock); + + /* Once per minute. */ + mod_timer(&sync_xen_wallclock_timer, jiffies + 60*HZ); +} + +int xen_update_persistent_clock(struct notifier_block *this, + unsigned long event, void *ptr) +{ + if (!xen_initial_domain()) + return -1; + mod_timer(&sync_xen_wallclock_timer, jiffies + 1); + return 0; +} + +static struct notifier_block xen_clock_was_set = { + .notifier_call = xen_update_persistent_clock, +}; + __init void xen_time_init(void) { int cpu = smp_processor_id(); @@ -549,6 +586,8 @@ __init void xen_time_init(void) xen_setup_runstate_info(cpu); xen_setup_timer(cpu); xen_setup_cpu_clockevents(); + if (xen_initial_domain()) + atomic_notifier_chain_register(&clockset_notifier_list, &xen_clock_was_set); #ifdef CONFIG_PARAVIRT_CLOCK_VSYSCALL if (xen_setup_vsyscall_timeinfo(cpu) == 0) diff --git a/drivers/xen/manage.c b/drivers/xen/manage.c index 5d42d55..422e8ef 100644 --- a/drivers/xen/manage.c +++ b/drivers/xen/manage.c @@ -130,7 +130,7 @@ out_resume: dpm_resume_end(PMSG_RESUME); /* Make sure timer events get retriggered on all CPUs */ - clock_was_set(); + atomic_notifier_call_chain(&clockset_notifier_list, 0, NULL); out_thaw: #ifdef CONFIG_PREEMPT diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h index 4759917..c46a4e5 100644 --- a/include/linux/hrtimer.h +++ b/include/linux/hrtimer.h @@ -247,7 +247,6 @@ static inline ktime_t hrtimer_expires_remaining(const struct hrtimer *timer) #ifdef CONFIG_HIGH_RES_TIMERS struct clock_event_device; -extern void clock_was_set(void); extern void hres_timers_resume(void); extern void hrtimer_interrupt(struct clock_event_device *dev); @@ -282,12 +281,6 @@ extern void hrtimer_peek_ahead_timers(void); # define MONOTONIC_RES_NSEC LOW_RES_NSEC # define KTIME_MONOTONIC_RES KTIME_LOW_RES -/* - * clock_was_set() is a NOP for non- high-resolution systems. The - * time-sorted order guarantees that a timer does not expire early and - * is expired in the next softirq when the clock was advanced. - */ -static inline void clock_was_set(void) { } static inline void hrtimer_peek_ahead_timers(void) { } static inline void hres_timers_resume(void) { } diff --git a/include/linux/time.h b/include/linux/time.h index ea16c1a..26367c2 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -7,6 +7,7 @@ # include <linux/cache.h> # include <linux/seqlock.h> # include <linux/math64.h> +# include <linux/notifier.h> #endif #ifndef _STRUCT_TIMESPEC @@ -131,6 +132,7 @@ static inline u32 arch_gettimeoffset(void) { return 0; } extern void do_gettimeofday(struct timeval *tv); extern int do_settimeofday(struct timespec *tv); extern int do_sys_settimeofday(struct timespec *tv, struct timezone *tz); +extern struct atomic_notifier_head clockset_notifier_list; #define do_posix_clock_monotonic_gettime(ts) ktime_get_ts(ts) extern long do_utimes(int dfd, char __user *filename, struct timespec *times, int flags); struct itimerval; diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index 49da79a..18edb2e 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c @@ -665,12 +665,17 @@ static void retrigger_next_event(void *arg) * resolution timer interrupts. On UP we just disable interrupts and * call the high resolution interrupt code. */ -void clock_was_set(void) +static int clock_was_set(struct notifier_block *this, unsigned long event, + void *ptr) { /* Retrigger the CPU local events everywhere */ on_each_cpu(retrigger_next_event, NULL, 1); } +static struct notifier_block hrt_clock_was_set = { + .notifier_call = clock_was_set, +}; + /* * During resume we might have to reprogram the high resolution timer * interrupt (on the local CPU): @@ -1726,6 +1731,7 @@ void __init hrtimers_init(void) (void *)(long)smp_processor_id()); register_cpu_notifier(&hrtimers_nb); #ifdef CONFIG_HIGH_RES_TIMERS + atomic_notifier_chain_register(&clockset_notifier_list, &hrt_clock_was_set); open_softirq(HRTIMER_SOFTIRQ, run_hrtimer_softirq); #endif } diff --git a/kernel/time.c b/kernel/time.c index 2951194..667b959 100644 --- a/kernel/time.c +++ b/kernel/time.c @@ -138,7 +138,7 @@ static inline void warp_clock(void) xtime.tv_sec += sys_tz.tz_minuteswest * 60; update_xtime_cache(0); write_sequnlock_irq(&xtime_lock); - clock_was_set(); + atomic_notifier_call_chain(&clockset_notifier_list, 0, NULL); } /* diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index e8c77d9..284539f 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -18,7 +18,11 @@ #include <linux/jiffies.h> #include <linux/time.h> #include <linux/tick.h> +#include <linux/notifier.h> +ATOMIC_NOTIFIER_HEAD(clockset_notifier_list); + +EXPORT_SYMBOL(clockset_notifier_list); /* * This read-write spinlock protects us from races in SMP while @@ -174,8 +178,7 @@ int do_settimeofday(struct timespec *tv) write_sequnlock_irqrestore(&xtime_lock, flags); - /* signal hrtimers about time change */ - clock_was_set(); + atomic_notifier_call_chain(&clockset_notifier_list, 0, NULL); return 0; } _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel