From 153a59a6f47ff2c919cc8834d8da9c213329407b Mon Sep 17 00:00:00 2001 From: NAKAMURA Gou Date: Thu, 15 Oct 2015 15:16:10 +0900 Subject: [PATCH] gettimeofday: avoid per-cpu data in calculation Because it is difficult to safely update per-cpu data of other cpus in settimeofday(). --- kernel/include/cls.h | 5 ----- kernel/include/syscall.h | 6 ++++++ kernel/init.c | 27 +++++++++++------------ kernel/syscall.c | 46 ++++++++++++++++++++-------------------- 4 files changed, 43 insertions(+), 41 deletions(-) diff --git a/kernel/include/cls.h b/kernel/include/cls.h index eb1070db..8fcd25a6 100644 --- a/kernel/include/cls.h +++ b/kernel/include/cls.h @@ -73,11 +73,6 @@ struct cpu_local_var { int in_interrupt; int no_preempt; int timer_enabled; - - unsigned long tv_sec; - unsigned long tv_nsec; - unsigned long last_tsc; - } __attribute__((aligned(64))); diff --git a/kernel/include/syscall.h b/kernel/include/syscall.h index 786d57c8..fa57d91b 100644 --- a/kernel/include/syscall.h +++ b/kernel/include/syscall.h @@ -16,6 +16,7 @@ #include #include #include +#include #define NUM_SYSCALLS 255 @@ -286,4 +287,9 @@ struct procfs_file { }; extern void terminate(int, int); + +/* kernel/syscall.c */ +extern struct timespec origin_ts; /* realtime when tsc=0 */ +extern unsigned long clocks_per_sec; + #endif diff --git a/kernel/init.c b/kernel/init.c index 2f5ba98d..68502b1d 100644 --- a/kernel/init.c +++ b/kernel/init.c @@ -201,11 +201,21 @@ static void pc_test(void) } extern void ihk_mc_get_boot_time(unsigned long *tv_sec, unsigned long *tv_nsec); +extern unsigned long ihk_mc_get_ns_per_tsc(void); + static void time_init(void) { - ihk_mc_get_boot_time(&cpu_local_var(tv_sec), - &cpu_local_var(tv_nsec)); - cpu_local_var(last_tsc) = 0; + unsigned long tv_sec, tv_nsec; + unsigned long ns_per_kclock; + + ihk_mc_get_boot_time(&tv_sec, &tv_nsec); + ns_per_kclock = ihk_mc_get_ns_per_tsc(); + + origin_ts.tv_sec = tv_sec; + origin_ts.tv_nsec = tv_nsec; + clocks_per_sec = (1000L * NS_PER_SEC) / ns_per_kclock; + + return; } static void rest_init(void) @@ -233,11 +243,9 @@ static void rest_init(void) int host_ikc_inited = 0; extern int num_processors; extern void zero_tsc(void); -extern void update_cpu_local_time(void); static void post_init(void) { - int i; cpu_enable_interrupt(); while (!host_ikc_inited) { @@ -253,16 +261,9 @@ static void post_init(void) ihk_mc_spinlock_init(&syscall_lock); } - /* Update time elapsed so far during boot, distribute the current - * date to all cores and zero TSC. + /* Zero TSC. * All AP cores are wait spinning for ap_start() and they will zero * their TSC immediatly. */ - update_cpu_local_time(); - cpu_local_var(last_tsc) = 0; - for (i = 0; i < num_processors; ++i) { - get_cpu_local_var(i)->tv_sec = cpu_local_var(tv_sec); - get_cpu_local_var(i)->tv_nsec = cpu_local_var(tv_nsec); - } zero_tsc(); ap_start(); diff --git a/kernel/syscall.c b/kernel/syscall.c index 723aa4f9..482f118f 100644 --- a/kernel/syscall.c +++ b/kernel/syscall.c @@ -94,6 +94,9 @@ static char *syscall_name[] MCKERNEL_UNUSED = { #undef SYSCALL_DELEGATED }; +struct timespec origin_ts; +unsigned long clocks_per_sec; + void check_signal(unsigned long, void *, int); void do_signal(long rc, void *regs, struct thread *thread, struct sig_pending *pending, int num); extern unsigned long do_kill(struct thread *thread, int pid, int tid, int sig, struct siginfo *info, int ptracecont); @@ -4786,30 +4789,26 @@ SYSCALL_DECLARE(get_cpu_id) return ihk_mc_get_processor_id(); } -void __update_time_from_tsc_delta(unsigned long *tv_sec, - unsigned long *tv_nsec, - unsigned long tsc_delta) +static void calculate_time_from_tsc(struct timespec *ts) { - unsigned long ns_delta = tsc_delta * ihk_mc_get_ns_per_tsc() / 1000; + unsigned long current_tsc; + time_t sec_delta; + long ns_delta; - *tv_sec += (ns_delta / NS_PER_SEC); - *tv_nsec += (ns_delta % NS_PER_SEC); - if (*tv_nsec > NS_PER_SEC) { - *tv_nsec -= NS_PER_SEC; - ++*tv_sec; + current_tsc = rdtsc(); + sec_delta = current_tsc / clocks_per_sec; + ns_delta = NS_PER_SEC * (current_tsc % clocks_per_sec) + / clocks_per_sec; + /* calc. of ns_delta overflows if clocks_per_sec exceeds 18.44 GHz */ + + ts->tv_sec = origin_ts.tv_sec + sec_delta; + ts->tv_nsec = origin_ts.tv_nsec + ns_delta; + if (ts->tv_nsec >= NS_PER_SEC) { + ts->tv_nsec -= NS_PER_SEC; + ++ts->tv_sec; } -} -void update_cpu_local_time(void) -{ - unsigned long tsc = rdtsc(); - - __update_time_from_tsc_delta( - &cpu_local_var(tv_sec), - &cpu_local_var(tv_nsec), - tsc - cpu_local_var(last_tsc)); - - cpu_local_var(last_tsc) = tsc; + return; } @@ -4820,6 +4819,7 @@ SYSCALL_DECLARE(gettimeofday) struct timezone *tz = (struct timezone *)ihk_mc_syscall_arg1(ctx); struct timeval atv; int error; + struct timespec ats; if (!tv && !tz) { /* nothing to do */ @@ -4828,10 +4828,10 @@ SYSCALL_DECLARE(gettimeofday) /* Do it locally if supported */ if (!tz && gettime_local_support) { - update_cpu_local_time(); + calculate_time_from_tsc(&ats); - atv.tv_sec = cpu_local_var(tv_sec); - atv.tv_usec = cpu_local_var(tv_nsec) / 1000; + atv.tv_sec = ats.tv_sec; + atv.tv_usec = ats.tv_nsec / 1000; error = copy_to_user(tv, &atv, sizeof(atv));