2019-08-25 12:49:18 +03:00
// SPDX-License-Identifier: GPL-2.0
2006-01-19 04:42:42 +03:00
/*
2015-11-02 19:16:37 +03:00
* Copyright ( C ) 2015 Anton Ivanov ( aivanov @ { brocade . com , kot - begemot . co . uk } )
* Copyright ( C ) 2015 Thomas Meyer ( thomas @ m3y3r . de )
* Copyright ( C ) 2012 - 2014 Cisco Systems
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
*/
2007-10-16 12:26:54 +04:00
# include <stddef.h>
um: Simplify os_idle_sleep() and sleep longer
There really is no reason to pass the amount of time we should
sleep, especially since it's just hard-coded to one second.
Additionally, one second isn't really all that long, and as we
are expecting to be woken up by a signal, we can sleep longer
and avoid doing some work every second, so replace the current
clock_nanosleep() with just an empty select() that can _only_
be woken up by a signal.
We can also remove the deliver_alarm() since we don't need to
do that when we got e.g. SIGIO that woke us up, and if we got
SIGALRM the signal handler will actually (have) run, so it's
just unnecessary extra work.
Similarly, in time-travel mode, just program the wakeup event
from idle to be S64_MAX, which is basically the most you could
ever simulate to. Of course, you should already have an event
in the list that's earlier and will cause a wakeup, normally
that's the regular timer interrupt, though in suspend it may
(later) also be an RTC event. Since actually getting to this
point would be a bug and you can't ever get out again, panic()
on it in the time control code.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Acked-By: Anton Ivanov <anton.ivanov@cambridgegreys.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
2020-12-02 22:58:04 +03:00
# include <unistd.h>
2007-10-16 12:26:54 +04:00
# 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>
2012-10-08 06:27:32 +04:00
# include <kern_util.h>
# include <os.h>
2015-11-02 19:16:37 +03:00
# include <string.h>
2006-01-19 04:42:42 +03:00
2015-11-02 19:16:37 +03:00
static timer_t event_high_res_timer = 0 ;
2006-09-26 10:33:05 +04:00
2015-11-02 19:16:37 +03:00
static inline long long timespec_to_ns ( const struct timespec * ts )
2007-10-16 12:27:25 +04:00
{
2019-05-06 15:39:38 +03:00
return ( ( long long ) ts - > tv_sec * UM_NSEC_PER_SEC ) + ts - > tv_nsec ;
2015-11-02 19:16:37 +03:00
}
2007-10-16 12:27:25 +04:00
2019-05-06 15:39:38 +03:00
long long os_persistent_clock_emulation ( void )
{
2015-11-02 19:16:37 +03:00
struct timespec realtime_tp ;
2007-10-16 12:27:25 +04:00
2015-11-02 19:16:37 +03:00
clock_gettime ( CLOCK_REALTIME , & realtime_tp ) ;
return timespec_to_ns ( & realtime_tp ) ;
2007-10-16 12:27:25 +04:00
}
2007-10-16 12:27:27 +04:00
/**
2015-11-02 19:16:37 +03:00
* os_timer_create ( ) - create an new posix ( interval ) timer
2007-10-16 12:27:27 +04:00
*/
2019-05-06 15:39:38 +03:00
int os_timer_create ( void )
{
timer_t * t = & event_high_res_timer ;
2015-11-02 19:16:37 +03:00
2019-05-06 15:39:38 +03:00
if ( timer_create ( CLOCK_MONOTONIC , NULL , t ) = = - 1 )
2015-11-02 19:16:37 +03:00
return - 1 ;
2019-05-06 15:39:38 +03:00
2015-11-02 19:16:37 +03:00
return 0 ;
2007-10-16 12:27:27 +04:00
}
2019-05-27 11:34:26 +03:00
int os_timer_set_interval ( unsigned long long nsecs )
2006-01-19 04:42:42 +03:00
{
2015-11-02 19:16:37 +03:00
struct itimerspec its ;
2007-10-16 12:27:22 +04:00
2019-05-27 11:34:26 +03:00
its . it_value . tv_sec = nsecs / UM_NSEC_PER_SEC ;
its . it_value . tv_nsec = nsecs % UM_NSEC_PER_SEC ;
2008-05-13 01:02:00 +04:00
2019-05-27 11:34:26 +03:00
its . it_interval . tv_sec = nsecs / UM_NSEC_PER_SEC ;
its . it_interval . tv_nsec = nsecs % UM_NSEC_PER_SEC ;
2005-04-17 02:20:36 +04:00
2019-05-06 15:39:38 +03:00
if ( timer_settime ( event_high_res_timer , 0 , & its , NULL ) = = - 1 )
2015-11-02 19:16:37 +03:00
return - errno ;
2005-04-17 02:20:36 +04:00
2007-12-01 23:16:29 +03:00
return 0 ;
2007-11-29 03:21:51 +03:00
}
2008-05-13 01:02:00 +04:00
2019-05-27 11:34:26 +03:00
int os_timer_one_shot ( unsigned long long nsecs )
2015-11-02 19:16:37 +03:00
{
2019-05-06 15:39:38 +03:00
struct itimerspec its = {
2019-05-27 11:34:26 +03:00
. it_value . tv_sec = nsecs / UM_NSEC_PER_SEC ,
. it_value . tv_nsec = nsecs % UM_NSEC_PER_SEC ,
2008-06-06 09:46:10 +04:00
2019-05-06 15:39:38 +03:00
. it_interval . tv_sec = 0 ,
. it_interval . tv_nsec = 0 , // we cheat here
} ;
2008-05-13 01:02:00 +04:00
2015-11-02 19:16:37 +03:00
timer_settime ( event_high_res_timer , 0 , & its , NULL ) ;
return 0 ;
2008-05-13 01:02:00 +04:00
}
2015-11-02 19:16:37 +03:00
/**
* os_timer_disable ( ) - disable the posix ( interval ) timer
*/
2019-05-06 15:39:38 +03:00
void os_timer_disable ( void )
2008-05-13 01:02:00 +04:00
{
2015-11-02 19:16:37 +03:00
struct itimerspec its ;
2008-05-13 01:02:00 +04:00
2015-11-02 19:16:37 +03:00
memset ( & its , 0 , sizeof ( struct itimerspec ) ) ;
2019-05-06 15:39:38 +03:00
timer_settime ( event_high_res_timer , 0 , & its , NULL ) ;
2015-11-02 19:16:37 +03:00
}
2007-11-29 03:21:51 +03:00
2015-11-02 19:16:37 +03:00
long long os_nsecs ( void )
{
struct timespec ts ;
2007-11-29 03:21:51 +03:00
2015-11-02 19:16:37 +03:00
clock_gettime ( CLOCK_MONOTONIC , & ts ) ;
return timespec_to_ns ( & ts ) ;
2007-11-29 03:21:51 +03:00
}
2015-11-02 19:16:37 +03:00
/**
um: Simplify os_idle_sleep() and sleep longer
There really is no reason to pass the amount of time we should
sleep, especially since it's just hard-coded to one second.
Additionally, one second isn't really all that long, and as we
are expecting to be woken up by a signal, we can sleep longer
and avoid doing some work every second, so replace the current
clock_nanosleep() with just an empty select() that can _only_
be woken up by a signal.
We can also remove the deliver_alarm() since we don't need to
do that when we got e.g. SIGIO that woke us up, and if we got
SIGALRM the signal handler will actually (have) run, so it's
just unnecessary extra work.
Similarly, in time-travel mode, just program the wakeup event
from idle to be S64_MAX, which is basically the most you could
ever simulate to. Of course, you should already have an event
in the list that's earlier and will cause a wakeup, normally
that's the regular timer interrupt, though in suspend it may
(later) also be an RTC event. Since actually getting to this
point would be a bug and you can't ever get out again, panic()
on it in the time control code.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Acked-By: Anton Ivanov <anton.ivanov@cambridgegreys.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
2020-12-02 22:58:04 +03:00
* os_idle_sleep ( ) - sleep until interrupted
2015-11-02 19:16:37 +03:00
*/
um: Simplify os_idle_sleep() and sleep longer
There really is no reason to pass the amount of time we should
sleep, especially since it's just hard-coded to one second.
Additionally, one second isn't really all that long, and as we
are expecting to be woken up by a signal, we can sleep longer
and avoid doing some work every second, so replace the current
clock_nanosleep() with just an empty select() that can _only_
be woken up by a signal.
We can also remove the deliver_alarm() since we don't need to
do that when we got e.g. SIGIO that woke us up, and if we got
SIGALRM the signal handler will actually (have) run, so it's
just unnecessary extra work.
Similarly, in time-travel mode, just program the wakeup event
from idle to be S64_MAX, which is basically the most you could
ever simulate to. Of course, you should already have an event
in the list that's earlier and will cause a wakeup, normally
that's the regular timer interrupt, though in suspend it may
(later) also be an RTC event. Since actually getting to this
point would be a bug and you can't ever get out again, panic()
on it in the time control code.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Acked-By: Anton Ivanov <anton.ivanov@cambridgegreys.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
2020-12-02 22:58:04 +03:00
void os_idle_sleep ( void )
2006-01-19 04:42:42 +03:00
{
um: fix os_idle_sleep() to not hang
Changing os_idle_sleep() to use pause() (I accidentally described
it as an empty select() in the commit log because I had changed it
from that to pause() in a later revision) exposed a race condition
in the idle code. The following can happen:
timer_settime(0, 0, {it_interval={tv_sec=0, tv_nsec=0}, it_value={tv_sec=0, tv_nsec=624017}}, NULL) = 0
...
<SIGALRM is delivered but we're already on the way to idle>
pause()
and we now hang forever. This was previously possible as well, but
it could never cause UML to hang for more than a second since we
could only sleep for that much, so at most you'd notice a "hiccup"
in the UML. Obviously, any sort of external interrupt also "saves"
it and interrupts pause().
Fix this by properly handling the race, rather than papering over
it again:
- first, block SIGALRM, and obtain the old signal set
- check the timer
- suspend, waiting for any signal out of the old set, if, and only
if, the timer will fire in the future
- restore the old signal mask
This ensures race-free operation: as it's blocked, the signal won't
be delivered while we're looking at the timer even if it were to be
triggered right _after_ we've returned from timer_gettime() with a
non-zero value (telling us the timer will trigger). Thus, despite
getting to sigsuspend() because timer_gettime() told us we're still
waiting, we'll not hang because sigsuspend() will return immediately
due to the pending signal.
Fixes: 49da38a3ef33 ("um: Simplify os_idle_sleep() and sleep longer")
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Acked-By: Anton Ivanov <anton.ivanov@cambridgegreys.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
2021-01-22 23:40:23 +03:00
struct itimerspec its ;
sigset_t set , old ;
/* block SIGALRM while we analyze the timer state */
sigemptyset ( & set ) ;
sigaddset ( & set , SIGALRM ) ;
sigprocmask ( SIG_BLOCK , & set , & old ) ;
/* check the timer, and if it'll fire then wait for it */
timer_gettime ( event_high_res_timer , & its ) ;
if ( its . it_value . tv_sec | | its . it_value . tv_nsec )
sigsuspend ( & old ) ;
/* either way, restore the signal mask */
sigprocmask ( SIG_UNBLOCK , & set , NULL ) ;
2006-01-19 04:42:42 +03:00
}