2006-01-19 04:42:42 +03:00
/*
2007-10-16 12:26:54 +04:00
* Copyright ( C ) 2000 - 2007 Jeff Dike ( jdike { addtoit , linux . intel } . com )
2006-01-19 04:42:42 +03:00
* Licensed under the GPL
*/
2007-10-16 12:26:54 +04:00
# include <stddef.h>
# include <errno.h>
# include <signal.h>
2006-01-19 04:42:42 +03:00
# include <time.h>
2005-04-17 02:20:36 +04:00
# include <sys/time.h>
2006-01-19 04:42:42 +03:00
# include "kern_constants.h"
# include "os.h"
2007-10-16 12:26:54 +04:00
# include "user.h"
2006-01-19 04:42:42 +03:00
2007-10-16 12:27:22 +04:00
int set_interval ( void )
2006-01-19 04:42:42 +03:00
{
2007-10-16 12:27:28 +04:00
int usec = UM_USEC_PER_SEC / UM_HZ ;
2006-01-19 04:42:42 +03:00
struct itimerval interval = ( ( struct itimerval ) { { 0 , usec } ,
{ 0 , usec } } ) ;
2007-10-16 12:27:22 +04:00
if ( setitimer ( ITIMER_VIRTUAL , & interval , NULL ) = = - 1 )
2006-09-26 10:33:05 +04:00
return - errno ;
return 0 ;
2006-01-19 04:42:42 +03:00
}
2007-10-16 12:27:25 +04:00
int timer_one_shot ( int ticks )
{
2007-10-16 12:27:28 +04:00
unsigned long usec = ticks * UM_USEC_PER_SEC / UM_HZ ;
unsigned long sec = usec / UM_USEC_PER_SEC ;
2007-10-16 12:27:25 +04:00
struct itimerval interval ;
2007-10-16 12:27:28 +04:00
usec % = UM_USEC_PER_SEC ;
2007-10-16 12:27:25 +04:00
interval = ( ( struct itimerval ) { { 0 , 0 } , { sec , usec } } ) ;
if ( setitimer ( ITIMER_VIRTUAL , & interval , NULL ) = = - 1 )
return - errno ;
return 0 ;
}
2007-10-16 12:27:27 +04:00
/**
* timeval_to_ns - Convert timeval to nanoseconds
* @ ts : pointer to the timeval variable to be converted
*
* Returns the scalar nanosecond representation of the timeval
* parameter .
*
* Ripped from linux / time . h because it ' s a kernel header , and thus
* unusable from here .
*/
static inline long long timeval_to_ns ( const struct timeval * tv )
{
return ( ( long long ) tv - > tv_sec * UM_NSEC_PER_SEC ) +
tv - > tv_usec * UM_NSEC_PER_USEC ;
}
long long disable_timer ( void )
2006-01-19 04:42:42 +03:00
{
2007-10-16 12:27:26 +04:00
struct itimerval time = ( ( struct itimerval ) { { 0 , 0 } , { 0 , 0 } } ) ;
2007-10-16 12:26:54 +04:00
2007-11-29 03:21:51 +03:00
if ( setitimer ( ITIMER_VIRTUAL , & time , & time ) < 0 )
2007-10-16 12:26:54 +04:00
printk ( UM_KERN_ERR " disable_timer - setitimer failed, "
" errno = %d \n " , errno ) ;
2007-10-16 12:27:22 +04:00
2007-10-16 12:27:27 +04:00
return timeval_to_ns ( & time . it_value ) ;
2006-01-19 04:42:42 +03:00
}
2005-04-17 02:20:36 +04:00
2007-10-16 12:27:27 +04:00
long long os_nsecs ( void )
2005-04-17 02:20:36 +04:00
{
struct timeval tv ;
gettimeofday ( & tv , NULL ) ;
2007-10-16 12:27:25 +04:00
return timeval_to_ns ( & tv ) ;
2005-04-17 02:20:36 +04:00
}
2007-11-29 03:21:51 +03:00
# ifdef UML_CONFIG_NO_HZ
static int after_sleep_interval ( struct timespec * ts )
{
}
# else
static inline long long timespec_to_us ( const struct timespec * ts )
{
return ( ( long long ) ts - > tv_sec * UM_USEC_PER_SEC ) +
ts - > tv_nsec / UM_NSEC_PER_USEC ;
}
static int after_sleep_interval ( struct timespec * ts )
{
int usec = UM_USEC_PER_SEC / UM_HZ ;
long long start_usecs = timespec_to_us ( ts ) ;
struct timeval tv ;
struct itimerval interval ;
/*
* It seems that rounding can increase the value returned from
* setitimer to larger than the one passed in . Over time ,
* this will cause the remaining time to be greater than the
* tick interval . If this happens , then just reduce the first
* tick to the interval value .
*/
if ( start_usecs > usec )
start_usecs = usec ;
tv = ( ( struct timeval ) { . tv_sec = start_usecs / UM_USEC_PER_SEC ,
. tv_usec = start_usecs % UM_USEC_PER_SEC } ) ;
interval = ( ( struct itimerval ) { { 0 , usec } , tv } ) ;
if ( setitimer ( ITIMER_VIRTUAL , & interval , NULL ) = = - 1 )
return - errno ;
return 0 ;
}
# endif
2007-10-16 12:27:26 +04:00
extern void alarm_handler ( int sig , struct sigcontext * sc ) ;
void idle_sleep ( unsigned long long nsecs )
2006-01-19 04:42:42 +03:00
{
2007-11-29 03:21:51 +03:00
struct timespec ts ;
/*
* nsecs can come in as zero , in which case , this starts a
* busy loop . To prevent this , reset nsecs to the tick
* interval if it is zero .
*/
if ( nsecs = = 0 )
nsecs = UM_NSEC_PER_SEC / UM_HZ ;
ts = ( ( struct timespec ) { . tv_sec = nsecs / UM_NSEC_PER_SEC ,
. tv_nsec = nsecs % UM_NSEC_PER_SEC } ) ;
2006-01-19 04:42:42 +03:00
2007-10-16 12:27:26 +04:00
if ( nanosleep ( & ts , & ts ) = = 0 )
alarm_handler ( SIGVTALRM , NULL ) ;
2007-11-29 03:21:51 +03:00
after_sleep_interval ( & ts ) ;
2006-01-19 04:42:42 +03:00
}