2007-07-21 19:10:01 +04:00
/*
* Copyright 2006 Andi Kleen , SUSE Labs .
* Subject to the GNU Public License , v .2
*
* Fast user context implementation of clock_gettime and gettimeofday .
*
* The code should have no internal unresolved relocations .
* Check with readelf after changing .
* Also alternative ( ) doesn ' t work .
*/
# include <linux/kernel.h>
# include <linux/posix-timers.h>
# include <linux/time.h>
# include <linux/string.h>
# include <asm/vsyscall.h>
# include <asm/vgtod.h>
# include <asm/timex.h>
# include <asm/hpet.h>
# include <asm/unistd.h>
# include <asm/io.h>
# include "vextern.h"
# define gtod vdso_vsyscall_gtod_data
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 ;
}
2008-05-12 23:20:41 +04:00
notrace static inline long vgetns ( void )
2007-07-21 19:10:01 +04:00
{
2007-09-11 16:02:09 +04:00
long v ;
2007-07-21 19:10:01 +04:00
cycles_t ( * vread ) ( void ) ;
vread = gtod - > clock . vread ;
2007-09-11 16:02:09 +04:00
v = ( vread ( ) - gtod - > clock . cycle_last ) & gtod - > clock . mask ;
return ( v * gtod - > clock . mult ) > > gtod - > clock . shift ;
2007-07-21 19:10:01 +04:00
}
2008-05-12 23:20:41 +04:00
notrace static noinline int do_realtime ( struct timespec * ts )
2007-07-21 19:10:01 +04:00
{
unsigned long seq , ns ;
do {
seq = read_seqbegin ( & gtod - > lock ) ;
ts - > tv_sec = gtod - > wall_time_sec ;
ts - > tv_nsec = gtod - > wall_time_nsec ;
ns = vgetns ( ) ;
} while ( unlikely ( read_seqretry ( & gtod - > lock , seq ) ) ) ;
timespec_add_ns ( ts , ns ) ;
return 0 ;
}
/* Copy of the version in kernel/time.c which we cannot directly access */
2008-05-12 23:20:41 +04:00
notrace static void
vset_normalized_timespec ( struct timespec * ts , long sec , long nsec )
2007-07-21 19:10:01 +04:00
{
while ( nsec > = NSEC_PER_SEC ) {
nsec - = NSEC_PER_SEC ;
+ + sec ;
}
while ( nsec < 0 ) {
nsec + = NSEC_PER_SEC ;
- - sec ;
}
ts - > tv_sec = sec ;
ts - > tv_nsec = nsec ;
}
2008-05-12 23:20:41 +04:00
notrace static noinline int do_monotonic ( struct timespec * ts )
2007-07-21 19:10:01 +04:00
{
unsigned long seq , ns , secs ;
do {
seq = read_seqbegin ( & gtod - > lock ) ;
secs = gtod - > wall_time_sec ;
ns = gtod - > wall_time_nsec + vgetns ( ) ;
secs + = gtod - > wall_to_monotonic . tv_sec ;
ns + = gtod - > wall_to_monotonic . tv_nsec ;
} while ( unlikely ( read_seqretry ( & gtod - > lock , seq ) ) ) ;
vset_normalized_timespec ( ts , secs , ns ) ;
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
{
if ( likely ( gtod - > sysctl_enabled & & gtod - > clock . vread ) )
switch ( clock ) {
case CLOCK_REALTIME :
return do_realtime ( ts ) ;
case CLOCK_MONOTONIC :
return do_monotonic ( ts ) ;
}
return vdso_fallback_gettime ( clock , ts ) ;
}
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
{
long ret ;
if ( likely ( gtod - > sysctl_enabled & & gtod - > clock . vread ) ) {
BUILD_BUG_ON ( offsetof ( struct timeval , tv_usec ) ! =
offsetof ( struct timespec , tv_nsec ) | |
sizeof ( * tv ) ! = sizeof ( struct timespec ) ) ;
do_realtime ( ( struct timespec * ) tv ) ;
tv - > tv_usec / = 1000 ;
if ( unlikely ( tz ! = NULL ) ) {
2008-05-15 03:10:42 +04:00
/* 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 ;
2007-07-21 19:10:01 +04:00
}
return 0 ;
}
asm ( " syscall " : " =a " ( ret ) :
" 0 " ( __NR_gettimeofday ) , " D " ( tv ) , " S " ( tz ) : " memory " ) ;
return ret ;
}
int gettimeofday ( struct timeval * , struct timezone * )
__attribute__ ( ( weak , alias ( " __vdso_gettimeofday " ) ) ) ;