
Now that clock_t is an unsigned value, we can also allow the system uptime to wrap. Essentially, instead of using (a <= b) to see if time a occurs no later than time b, we use (b - a <= CLOCK_MAX / 2). The latter value does not exist, so instead we add TMRDIFF_MAX for that purpose. We must therefore also avoid using values like 0 and LONG_MAX as special values for absolute times. This patch extends the libtimers interface so that it no longer uses 0 to indicate "no timeout". Similarly, TMR_NEVER is now used as special value only when otherwise a relative time difference would be used. A minix_timer structure is now considered in use when it has a watchdog function set, rather than when the absolute expiry time is not TMR_NEVER. A few new macros in <minix/timers.h> help with timer comparison and obtaining properties from a minix_timer structure. This patch also eliminates the union of timer arguments, instead using the only union element that is only used (the integer). This prevents potential problems with e.g. live update. The watchdog function prototype is changed to pass in the argument value rather than a pointer to the timer structure, since obtaining the argument value was the only current use of the timer structure anyway. The result is a somewhat friendlier timers API. The VFS select code required a few more invasive changes to restrict the timer value to the new maximum, effectively matching the timer code in PM. As a side effect, select(2) has been changed to reject invalid timeout values. That required a change to the test set, which relied on the previous, erroneous behavior. Finally, while we're rewriting significant chunks of the timer code anyway, also covert it to KNF and add a few more explanatory comments. Change-Id: Id43165c3fbb140b32b90be2cca7f68dd646ea72e
343 lines
11 KiB
C
343 lines
11 KiB
C
/* This file deals with the alarm clock related system calls, eventually
|
|
* passing off the work to the functions in timers.c and check_sig() in
|
|
* signal.c to pass an alarm signal to a process.
|
|
*
|
|
* The entry points into this file are:
|
|
* do_itimer: perform the ITIMER system call
|
|
* set_alarm: tell the timer interface to start or stop a process timer
|
|
* check_vtimer: check if one of the virtual timers needs to be restarted
|
|
*/
|
|
|
|
#include "pm.h"
|
|
#include <signal.h>
|
|
#include <sys/time.h>
|
|
#include <minix/com.h>
|
|
#include <minix/callnr.h>
|
|
#include <assert.h>
|
|
#include "mproc.h"
|
|
|
|
#define US 1000000UL /* shortcut for microseconds per second */
|
|
|
|
static clock_t ticks_from_timeval(struct timeval *tv);
|
|
static void timeval_from_ticks(struct timeval *tv, clock_t ticks);
|
|
static int is_sane_timeval(struct timeval *tv);
|
|
static void getset_vtimer(struct mproc *mp, int nwhich, struct
|
|
itimerval *value, struct itimerval *ovalue);
|
|
static void get_realtimer(struct mproc *mp, struct itimerval *value);
|
|
static void set_realtimer(struct mproc *mp, struct itimerval *value);
|
|
static void cause_sigalrm(int arg);
|
|
|
|
/*===========================================================================*
|
|
* ticks_from_timeval *
|
|
*===========================================================================*/
|
|
static clock_t ticks_from_timeval(tv)
|
|
struct timeval *tv;
|
|
{
|
|
clock_t ticks;
|
|
|
|
/* Large delays cause a lot of problems. First, the alarm system call
|
|
* takes an unsigned seconds count and the library has cast it to an int.
|
|
* That probably works, but on return the library will convert "negative"
|
|
* unsigneds to errors. Presumably no one checks for these errors, so
|
|
* force this call through. Second, If unsigned and long have the same
|
|
* size, converting from seconds to ticks can easily overflow. Finally,
|
|
* the kernel has similar overflow bugs adding ticks.
|
|
*
|
|
* Fixing this requires a lot of ugly casts to fit the wrong interface
|
|
* types and to avoid overflow traps. ALRM_EXP_TIME has the right type
|
|
* (clock_t) although it is declared as long. How can variables like
|
|
* this be declared properly without combinatorial explosion of message
|
|
* types?
|
|
*/
|
|
|
|
/* In any case, the following conversion must always round up. */
|
|
|
|
ticks = system_hz * (unsigned long) tv->tv_sec;
|
|
if ( (ticks / system_hz) != (unsigned long)tv->tv_sec) {
|
|
ticks = LONG_MAX;
|
|
} else {
|
|
ticks += ((system_hz * (unsigned long)tv->tv_usec + (US-1)) / US);
|
|
}
|
|
|
|
if (ticks > LONG_MAX) ticks = LONG_MAX;
|
|
|
|
return(ticks);
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* timeval_from_ticks *
|
|
*===========================================================================*/
|
|
static void timeval_from_ticks(tv, ticks)
|
|
struct timeval *tv;
|
|
clock_t ticks;
|
|
{
|
|
tv->tv_sec = (long) (ticks / system_hz);
|
|
tv->tv_usec = (long) ((ticks % system_hz) * US / system_hz);
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* is_sane_timeval *
|
|
*===========================================================================*/
|
|
static int
|
|
is_sane_timeval(struct timeval *tv)
|
|
{
|
|
/* This imposes a reasonable time value range for setitimer. */
|
|
return (tv->tv_sec >= 0 && tv->tv_sec <= MAX_SECS &&
|
|
tv->tv_usec >= 0 && tv->tv_usec < US);
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* do_itimer *
|
|
*===========================================================================*/
|
|
int
|
|
do_itimer(void)
|
|
{
|
|
struct itimerval ovalue, value; /* old and new interval timers */
|
|
int setval, getval; /* set and/or retrieve the values? */
|
|
int r, which;
|
|
|
|
/* Make sure 'which' is one of the defined timers. */
|
|
which = m_in.m_lc_pm_itimer.which;
|
|
if (which < 0 || which >= NR_ITIMERS) return(EINVAL);
|
|
|
|
/* Determine whether to set and/or return the given timer value, based on
|
|
* which of the value and ovalue parameters are nonzero. At least one of
|
|
* them must be nonzero.
|
|
*/
|
|
setval = (m_in.m_lc_pm_itimer.value != 0);
|
|
getval = (m_in.m_lc_pm_itimer.ovalue != 0);
|
|
|
|
if (!setval && !getval) return(EINVAL);
|
|
|
|
/* If we're setting a new value, copy the new timer from user space.
|
|
* Also, make sure its fields have sane values.
|
|
*/
|
|
if (setval) {
|
|
r = sys_datacopy(who_e, m_in.m_lc_pm_itimer.value,
|
|
PM_PROC_NR, (vir_bytes)&value, (phys_bytes)sizeof(value));
|
|
if (r != OK) return(r);
|
|
|
|
if (!is_sane_timeval(&value.it_value) ||
|
|
!is_sane_timeval(&value.it_interval))
|
|
return(EINVAL);
|
|
}
|
|
|
|
switch (which) {
|
|
case ITIMER_REAL :
|
|
if (getval) get_realtimer(mp, &ovalue);
|
|
|
|
if (setval) set_realtimer(mp, &value);
|
|
|
|
r = OK;
|
|
break;
|
|
|
|
case ITIMER_VIRTUAL :
|
|
case ITIMER_PROF :
|
|
getset_vtimer(mp, which, (setval) ? &value : NULL,
|
|
(getval) ? &ovalue : NULL);
|
|
|
|
r = OK;
|
|
break;
|
|
|
|
default:
|
|
panic("invalid timer type: %d", which);
|
|
}
|
|
|
|
/* If requested, copy the old interval timer to user space. */
|
|
if (r == OK && getval) {
|
|
r = sys_datacopy(PM_PROC_NR, (vir_bytes)&ovalue,
|
|
who_e, m_in.m_lc_pm_itimer.ovalue,
|
|
(phys_bytes)sizeof(ovalue));
|
|
}
|
|
|
|
return(r);
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* getset_vtimer *
|
|
*===========================================================================*/
|
|
static void
|
|
getset_vtimer(struct mproc *rmp, int which, struct itimerval *value, struct itimerval *ovalue)
|
|
{
|
|
clock_t newticks, *nptr; /* the new timer value, in ticks */
|
|
clock_t oldticks, *optr; /* the old ticks value, in ticks */
|
|
int r, num;
|
|
|
|
/* The default is to provide sys_vtimer with two null pointers, i.e. to do
|
|
* nothing at all.
|
|
*/
|
|
optr = nptr = NULL;
|
|
|
|
/* If the old timer value is to be retrieved, have 'optr' point to the
|
|
* location where the old value is to be stored, and copy the interval.
|
|
*/
|
|
if (ovalue != NULL) {
|
|
optr = &oldticks;
|
|
|
|
timeval_from_ticks(&ovalue->it_interval, rmp->mp_interval[which]);
|
|
}
|
|
|
|
/* If a new timer value is to be set, store the new timer value and have
|
|
* 'nptr' point to it. Also, store the new interval.
|
|
*/
|
|
if (value != NULL) {
|
|
newticks = ticks_from_timeval(&value->it_value);
|
|
nptr = &newticks;
|
|
|
|
/* If no timer is set, the interval must be zero. */
|
|
if (newticks <= 0)
|
|
rmp->mp_interval[which] = 0;
|
|
else
|
|
rmp->mp_interval[which] =
|
|
ticks_from_timeval(&value->it_interval);
|
|
}
|
|
|
|
/* Find out which kernel timer number to use. */
|
|
switch (which) {
|
|
case ITIMER_VIRTUAL: num = VT_VIRTUAL; break;
|
|
case ITIMER_PROF: num = VT_PROF; break;
|
|
default: panic("invalid vtimer type: %d", which);
|
|
}
|
|
|
|
/* Make the kernel call. If requested, also retrieve and store
|
|
* the old timer value.
|
|
*/
|
|
if ((r = sys_vtimer(rmp->mp_endpoint, num, nptr, optr)) != OK)
|
|
panic("sys_vtimer failed: %d", r);
|
|
|
|
if (ovalue != NULL) {
|
|
/* If the alarm expired already, we should take into account the
|
|
* interval. Return zero only if the interval is zero as well.
|
|
*/
|
|
if (oldticks <= 0) oldticks = rmp->mp_interval[which];
|
|
|
|
timeval_from_ticks(&ovalue->it_value, oldticks);
|
|
}
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* check_vtimer *
|
|
*===========================================================================*/
|
|
void
|
|
check_vtimer(int proc_nr, int sig)
|
|
{
|
|
register struct mproc *rmp;
|
|
int which, num;
|
|
|
|
rmp = &mproc[proc_nr];
|
|
|
|
/* Translate back the given signal to a timer type and kernel number. */
|
|
switch (sig) {
|
|
case SIGVTALRM: which = ITIMER_VIRTUAL; num = VT_VIRTUAL; break;
|
|
case SIGPROF: which = ITIMER_PROF; num = VT_PROF; break;
|
|
default: panic("invalid vtimer signal: %d", sig);
|
|
}
|
|
|
|
/* If a repetition interval was set for this virtual timer, tell the
|
|
* kernel to set a new timeout for the virtual timer.
|
|
*/
|
|
if (rmp->mp_interval[which] > 0)
|
|
sys_vtimer(rmp->mp_endpoint, num, &rmp->mp_interval[which], NULL);
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* get_realtimer *
|
|
*===========================================================================*/
|
|
static void
|
|
get_realtimer(struct mproc *rmp, struct itimerval *value)
|
|
{
|
|
clock_t exptime; /* time at which alarm will expire */
|
|
clock_t uptime; /* current system time */
|
|
clock_t remaining; /* time left on alarm */
|
|
|
|
/* First determine remaining time, in ticks, of previous alarm, if set. */
|
|
if (rmp->mp_flags & ALARM_ON) {
|
|
uptime = getticks();
|
|
exptime = tmr_exp_time(&rmp->mp_timer);
|
|
|
|
remaining = exptime - uptime;
|
|
|
|
/* If the alarm expired already, we should take into account the
|
|
* interval. Return zero only if the interval is zero as well.
|
|
*/
|
|
if (remaining <= 0) remaining = rmp->mp_interval[ITIMER_REAL];
|
|
} else {
|
|
remaining = 0;
|
|
}
|
|
|
|
/* Convert the result to a timeval structure. */
|
|
timeval_from_ticks(&value->it_value, remaining);
|
|
|
|
/* Similarly convert and store the interval of the timer. */
|
|
timeval_from_ticks(&value->it_interval, rmp->mp_interval[ITIMER_REAL]);
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* set_realtimer *
|
|
*===========================================================================*/
|
|
static void
|
|
set_realtimer(struct mproc *rmp, struct itimerval *value)
|
|
{
|
|
clock_t ticks; /* New amount of ticks to the next alarm. */
|
|
clock_t interval; /* New amount of ticks for the alarm's interval. */
|
|
|
|
/* Convert the timeval structures in the 'value' structure to ticks. */
|
|
ticks = ticks_from_timeval(&value->it_value);
|
|
interval = ticks_from_timeval(&value->it_interval);
|
|
|
|
/* If no timer is set, the interval must be zero. */
|
|
if (ticks <= 0) interval = 0;
|
|
|
|
/* Apply these values. */
|
|
set_alarm(rmp, ticks);
|
|
rmp->mp_interval[ITIMER_REAL] = interval;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* set_alarm *
|
|
*===========================================================================*/
|
|
void set_alarm(rmp, ticks)
|
|
struct mproc *rmp; /* process that wants the alarm */
|
|
clock_t ticks; /* how many ticks delay before the signal */
|
|
{
|
|
if (ticks > 0) {
|
|
assert(ticks <= TMRDIFF_MAX);
|
|
set_timer(&rmp->mp_timer, ticks, cause_sigalrm, rmp->mp_endpoint);
|
|
rmp->mp_flags |= ALARM_ON;
|
|
} else if (rmp->mp_flags & ALARM_ON) {
|
|
cancel_timer(&rmp->mp_timer);
|
|
rmp->mp_flags &= ~ALARM_ON;
|
|
}
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* cause_sigalrm *
|
|
*===========================================================================*/
|
|
static void
|
|
cause_sigalrm(int arg)
|
|
{
|
|
int proc_nr_n;
|
|
register struct mproc *rmp;
|
|
|
|
/* get process from timer */
|
|
if(pm_isokendpt(arg, &proc_nr_n) != OK) {
|
|
printf("PM: ignoring timer for invalid endpoint %d\n", arg);
|
|
return;
|
|
}
|
|
|
|
rmp = &mproc[proc_nr_n];
|
|
|
|
if ((rmp->mp_flags & (IN_USE | EXITING)) != IN_USE) return;
|
|
if ((rmp->mp_flags & ALARM_ON) == 0) return;
|
|
|
|
/* If an interval is set, set a new timer; otherwise clear the ALARM_ON flag.
|
|
* The set_alarm call will be calling set_timer from within this callback
|
|
* from the expire_timers function. This is safe.
|
|
*/
|
|
if (rmp->mp_interval[ITIMER_REAL] > 0)
|
|
set_alarm(rmp, rmp->mp_interval[ITIMER_REAL]);
|
|
else rmp->mp_flags &= ~ALARM_ON;
|
|
|
|
check_sig(rmp->mp_pid, SIGALRM, FALSE /* ksig */);
|
|
}
|