2007-07-21 19:10:01 +04:00
/*
* Copyright 2006 Andi Kleen , SUSE Labs .
* Subject to the GNU Public License , v .2
*
2011-05-23 17:31:30 +04:00
* Fast user context implementation of clock_gettime , gettimeofday , and time .
2007-07-21 19:10:01 +04:00
*
* The code should have no internal unresolved relocations .
* Check with readelf after changing .
*/
2008-11-12 15:17:38 +03:00
/* Disable profiling for userspace code: */
2008-11-12 23:24:24 +03:00
# define DISABLE_BRANCH_PROFILING
2008-11-12 15:17:38 +03:00
2007-07-21 19:10:01 +04:00
# include <linux/kernel.h>
# include <linux/posix-timers.h>
# include <linux/time.h>
# include <linux/string.h>
# include <asm/vsyscall.h>
2011-07-14 14:47:22 +04:00
# include <asm/fixmap.h>
2007-07-21 19:10:01 +04:00
# include <asm/vgtod.h>
# include <asm/timex.h>
# include <asm/hpet.h>
# include <asm/unistd.h>
# include <asm/io.h>
2012-11-28 05:28:57 +04:00
# include <asm/pvclock.h>
2007-07-21 19:10:01 +04:00
2011-05-23 17:31:24 +04:00
# define gtod (&VVAR(vsyscall_gtod_data))
2007-07-21 19:10:01 +04:00
2011-07-14 14:47:22 +04:00
notrace static cycle_t vread_tsc ( void )
{
cycle_t ret ;
u64 last ;
/*
* Empirically , a fence ( of type that depends on the CPU )
* before rdtsc is enough to ensure that rdtsc is ordered
* with respect to loads . The various CPU manuals are unclear
* as to whether rdtsc can be reordered with later loads ,
* but no one has ever seen it happen .
*/
rdtsc_barrier ( ) ;
ret = ( cycle_t ) vget_cycles ( ) ;
last = VVAR ( vsyscall_gtod_data ) . clock . cycle_last ;
if ( likely ( ret > = last ) )
return ret ;
/*
* GCC likes to generate cmov here , but this branch is extremely
* predictable ( it ' s just a funciton of time and the likely is
* very likely ) and there ' s a data dependence , so force GCC
* to generate a branch instead . I don ' t barrier ( ) because
* we don ' t actually need a barrier , and if this function
* ever gets inlined it will generate worse code .
*/
asm volatile ( " " ) ;
return last ;
}
static notrace cycle_t vread_hpet ( void )
{
2013-02-15 11:58:14 +04:00
return readl ( ( const void __iomem * ) fix_to_virt ( VSYSCALL_HPET ) + HPET_COUNTER ) ;
2011-07-14 14:47:22 +04:00
}
2012-11-28 05:28:57 +04:00
# ifdef CONFIG_PARAVIRT_CLOCK
static notrace const struct pvclock_vsyscall_time_info * get_pvti ( int cpu )
{
const struct pvclock_vsyscall_time_info * pvti_base ;
int idx = cpu / ( PAGE_SIZE / PVTI_SIZE ) ;
int offset = cpu % ( PAGE_SIZE / PVTI_SIZE ) ;
BUG_ON ( PVCLOCK_FIXMAP_BEGIN + idx > PVCLOCK_FIXMAP_END ) ;
pvti_base = ( struct pvclock_vsyscall_time_info * )
__fix_to_virt ( PVCLOCK_FIXMAP_BEGIN + idx ) ;
return & pvti_base [ offset ] ;
}
static notrace cycle_t vread_pvclock ( int * mode )
{
const struct pvclock_vsyscall_time_info * pvti ;
cycle_t ret ;
u64 last ;
u32 version ;
u8 flags ;
unsigned cpu , cpu1 ;
/*
2013-07-11 05:21:57 +04:00
* Note : hypervisor must guarantee that :
* 1. cpu ID number maps 1 : 1 to per - CPU pvclock time info .
* 2. that per - CPU pvclock time info is updated if the
* underlying CPU changes .
* 3. that version is increased whenever underlying CPU
* changes .
*
2012-11-28 05:28:57 +04:00
*/
do {
cpu = __getcpu ( ) & VGETCPU_CPU_MASK ;
/* TODO: We can put vcpu id into higher bits of pvti.version.
* This will save a couple of cycles by getting rid of
* __getcpu ( ) calls ( Gleb ) .
*/
pvti = get_pvti ( cpu ) ;
version = __pvclock_read_cycles ( & pvti - > pvti , & ret , & flags ) ;
/*
* Test we ' re still on the cpu as well as the version .
* We could have been migrated just after the first
* vgetcpu but before fetching the version , so we
* wouldn ' t notice a version change .
*/
cpu1 = __getcpu ( ) & VGETCPU_CPU_MASK ;
} while ( unlikely ( cpu ! = cpu1 | |
( pvti - > pvti . version & 1 ) | |
2013-07-11 05:21:57 +04:00
pvti - > pvti . version ! = version ) ) ;
2012-11-28 05:28:57 +04:00
if ( unlikely ( ! ( flags & PVCLOCK_TSC_STABLE_BIT ) ) )
* mode = VCLOCK_NONE ;
/* refer to tsc.c read_tsc() comment for rationale */
last = VVAR ( vsyscall_gtod_data ) . clock . cycle_last ;
if ( likely ( ret > = last ) )
return ret ;
return last ;
}
# endif
2008-05-12 23:20:41 +04:00
notrace static long vdso_fallback_gettime ( long clock , struct timespec * ts )
2007-07-21 19:10:01 +04:00
{
long ret ;
asm ( " syscall " : " =a " ( ret ) :
" 0 " ( __NR_clock_gettime ) , " D " ( clock ) , " S " ( ts ) : " memory " ) ;
return ret ;
}
2012-03-02 10:11:09 +04:00
notrace static long vdso_fallback_gtod ( struct timeval * tv , struct timezone * tz )
{
long ret ;
asm ( " syscall " : " =a " ( ret ) :
" 0 " ( __NR_gettimeofday ) , " D " ( tv ) , " S " ( tz ) : " memory " ) ;
return ret ;
}
2012-11-28 05:28:57 +04:00
notrace static inline u64 vgetsns ( int * mode )
2007-07-21 19:10:01 +04:00
{
2007-09-11 16:02:09 +04:00
long v ;
2011-07-14 14:47:22 +04:00
cycles_t cycles ;
if ( gtod - > clock . vclock_mode = = VCLOCK_TSC )
cycles = vread_tsc ( ) ;
2012-03-02 10:11:09 +04:00
else if ( gtod - > clock . vclock_mode = = VCLOCK_HPET )
2011-07-14 14:47:22 +04:00
cycles = vread_hpet ( ) ;
2012-11-28 05:28:57 +04:00
# ifdef CONFIG_PARAVIRT_CLOCK
else if ( gtod - > clock . vclock_mode = = VCLOCK_PVCLOCK )
cycles = vread_pvclock ( mode ) ;
# endif
2012-03-02 10:11:09 +04:00
else
return 0 ;
2011-07-14 14:47:22 +04:00
v = ( cycles - gtod - > clock . cycle_last ) & gtod - > clock . mask ;
2012-09-05 00:14:46 +04:00
return v * gtod - > clock . mult ;
2007-07-21 19:10:01 +04:00
}
2012-03-23 08:15:52 +04:00
/* Code size doesn't matter (vdso is 4k anyway) and this is faster. */
notrace static int __always_inline do_realtime ( struct timespec * ts )
2007-07-21 19:10:01 +04:00
{
2012-09-05 00:14:46 +04:00
unsigned long seq ;
u64 ns ;
2012-03-02 10:11:09 +04:00
int mode ;
2012-09-05 00:14:46 +04:00
ts - > tv_nsec = 0 ;
2007-07-21 19:10:01 +04:00
do {
2013-10-08 02:51:59 +04:00
seq = read_seqcount_begin_no_lockdep ( & gtod - > seq ) ;
2012-03-02 10:11:09 +04:00
mode = gtod - > clock . vclock_mode ;
2007-07-21 19:10:01 +04:00
ts - > tv_sec = gtod - > wall_time_sec ;
2012-09-05 00:14:46 +04:00
ns = gtod - > wall_time_snsec ;
2012-11-28 05:28:57 +04:00
ns + = vgetsns ( & mode ) ;
2012-09-05 00:14:46 +04:00
ns > > = gtod - > clock . shift ;
2012-02-28 23:46:04 +04:00
} while ( unlikely ( read_seqcount_retry ( & gtod - > seq , seq ) ) ) ;
2012-03-02 10:11:09 +04:00
2007-07-21 19:10:01 +04:00
timespec_add_ns ( ts , ns ) ;
2012-03-02 10:11:09 +04:00
return mode ;
2007-07-21 19:10:01 +04:00
}
2012-03-23 08:15:52 +04:00
notrace static int do_monotonic ( struct timespec * ts )
2007-07-21 19:10:01 +04:00
{
2012-09-05 00:14:46 +04:00
unsigned long seq ;
u64 ns ;
2012-03-02 10:11:09 +04:00
int mode ;
2012-09-05 00:14:46 +04:00
ts - > tv_nsec = 0 ;
2007-07-21 19:10:01 +04:00
do {
2013-10-08 02:51:59 +04:00
seq = read_seqcount_begin_no_lockdep ( & gtod - > seq ) ;
2012-03-02 10:11:09 +04:00
mode = gtod - > clock . vclock_mode ;
2012-03-23 08:15:51 +04:00
ts - > tv_sec = gtod - > monotonic_time_sec ;
2012-09-05 00:14:46 +04:00
ns = gtod - > monotonic_time_snsec ;
2012-11-28 05:28:57 +04:00
ns + = vgetsns ( & mode ) ;
2012-09-05 00:14:46 +04:00
ns > > = gtod - > clock . shift ;
2012-02-28 23:46:04 +04:00
} while ( unlikely ( read_seqcount_retry ( & gtod - > seq , seq ) ) ) ;
2012-03-23 08:15:51 +04:00
timespec_add_ns ( ts , ns ) ;
2011-05-23 17:31:27 +04:00
2012-03-02 10:11:09 +04:00
return mode ;
2007-07-21 19:10:01 +04:00
}
2012-03-23 08:15:52 +04:00
notrace static int do_realtime_coarse ( struct timespec * ts )
2009-08-20 06:13:34 +04:00
{
unsigned long seq ;
do {
2013-10-08 02:51:59 +04:00
seq = read_seqcount_begin_no_lockdep ( & gtod - > seq ) ;
2009-08-20 06:13:34 +04:00
ts - > tv_sec = gtod - > wall_time_coarse . tv_sec ;
ts - > tv_nsec = gtod - > wall_time_coarse . tv_nsec ;
2012-02-28 23:46:04 +04:00
} while ( unlikely ( read_seqcount_retry ( & gtod - > seq , seq ) ) ) ;
2009-08-20 06:13:34 +04:00
return 0 ;
}
2012-03-23 08:15:52 +04:00
notrace static int do_monotonic_coarse ( struct timespec * ts )
2009-08-20 06:13:34 +04:00
{
2012-03-23 08:15:51 +04:00
unsigned long seq ;
2009-08-20 06:13:34 +04:00
do {
2013-10-08 02:51:59 +04:00
seq = read_seqcount_begin_no_lockdep ( & gtod - > seq ) ;
2012-03-23 08:15:51 +04:00
ts - > tv_sec = gtod - > monotonic_time_coarse . tv_sec ;
ts - > tv_nsec = gtod - > monotonic_time_coarse . tv_nsec ;
2012-02-28 23:46:04 +04:00
} while ( unlikely ( read_seqcount_retry ( & gtod - > seq , seq ) ) ) ;
2011-05-23 17:31:27 +04:00
2009-08-20 06:13:34 +04:00
return 0 ;
}
2008-05-12 23:20:41 +04:00
notrace int __vdso_clock_gettime ( clockid_t clock , struct timespec * ts )
2007-07-21 19:10:01 +04:00
{
2012-03-02 10:11:09 +04:00
int ret = VCLOCK_NONE ;
2011-06-05 21:50:20 +04:00
switch ( clock ) {
case CLOCK_REALTIME :
2012-03-02 10:11:09 +04:00
ret = do_realtime ( ts ) ;
2011-06-05 21:50:20 +04:00
break ;
case CLOCK_MONOTONIC :
2012-03-02 10:11:09 +04:00
ret = do_monotonic ( ts ) ;
2011-06-05 21:50:20 +04:00
break ;
case CLOCK_REALTIME_COARSE :
return do_realtime_coarse ( ts ) ;
case CLOCK_MONOTONIC_COARSE :
return do_monotonic_coarse ( ts ) ;
}
2012-03-02 10:11:09 +04:00
if ( ret = = VCLOCK_NONE )
return vdso_fallback_gettime ( clock , ts ) ;
return 0 ;
2007-07-21 19:10:01 +04:00
}
int clock_gettime ( clockid_t , struct timespec * )
__attribute__ ( ( weak , alias ( " __vdso_clock_gettime " ) ) ) ;
2008-05-12 23:20:41 +04:00
notrace int __vdso_gettimeofday ( struct timeval * tv , struct timezone * tz )
2007-07-21 19:10:01 +04:00
{
2012-03-02 10:11:09 +04:00
long ret = VCLOCK_NONE ;
if ( likely ( tv ! = NULL ) ) {
BUILD_BUG_ON ( offsetof ( struct timeval , tv_usec ) ! =
offsetof ( struct timespec , tv_nsec ) | |
sizeof ( * tv ) ! = sizeof ( struct timespec ) ) ;
ret = do_realtime ( ( struct timespec * ) tv ) ;
tv - > tv_usec / = 1000 ;
2007-07-21 19:10:01 +04:00
}
2012-03-02 10:11:09 +04:00
if ( unlikely ( tz ! = NULL ) ) {
/* Avoid memcpy. Some old compilers fail to inline it */
tz - > tz_minuteswest = gtod - > sys_tz . tz_minuteswest ;
tz - > tz_dsttime = gtod - > sys_tz . tz_dsttime ;
}
if ( ret = = VCLOCK_NONE )
return vdso_fallback_gtod ( tv , tz ) ;
return 0 ;
2007-07-21 19:10:01 +04:00
}
int gettimeofday ( struct timeval * , struct timezone * )
__attribute__ ( ( weak , alias ( " __vdso_gettimeofday " ) ) ) ;
2011-05-23 17:31:30 +04:00
2011-06-05 21:50:20 +04:00
/*
* This will break when the xtime seconds get inaccurate , but that is
* unlikely
*/
2011-05-23 17:31:30 +04:00
notrace time_t __vdso_time ( time_t * t )
{
2011-05-23 17:31:31 +04:00
/* This is atomic on x86_64 so we don't need any locks. */
2011-06-05 21:50:20 +04:00
time_t result = ACCESS_ONCE ( VVAR ( vsyscall_gtod_data ) . wall_time_sec ) ;
2011-05-23 17:31:30 +04:00
if ( t )
* t = result ;
return result ;
}
int time ( time_t * t )
__attribute__ ( ( weak , alias ( " __vdso_time " ) ) ) ;