Support for setitimer(ITIMER_VIRTUAL/ITIMER_PROF). New test (41) for setitimer.

This commit is contained in:
David van Moolenbroek 2009-08-15 21:37:26 +00:00
parent d82e260a90
commit 323f0abdd6
27 changed files with 772 additions and 32 deletions

View file

@ -131,8 +131,8 @@ struct sig sigtab[] = {
#ifdef SIGXFSZ
SIGXFSZ, "XFSZ", NULL,
#endif
#ifdef SIGVTALARM
SIGVTALARM, "VTALARM", "Virtual alarm",
#ifdef SIGVTALRM
SIGVTALRM, "VTALARM", "Virtual alarm",
#endif
#ifdef SIGPROF
SIGPROF, "PROF", "Profiling alarm",

View file

@ -548,6 +548,8 @@ int sig;
case SIGTTIN: return "ttin"; /* 21 */
case SIGTTOU: return "ttou"; /* 22 */
case SIGWINCH: return "winch"; /* 23 */
case SIGVTALRM: return "vtalrm"; /* 24 */
case SIGPROF: return "prof"; /* 25 */
#ifdef __minix_vmd
case SIGFPEMU: return "fpemu"; /* 30 */
#endif

View file

@ -41,6 +41,8 @@ struct signames {
#ifdef SIGWINCH
{ "WINCH", SIGWINCH },
#endif
{ "VTALRM", SIGVTALRM },
{ "PROF", SIGPROF },
{ NULL, 0 }
};

View file

@ -328,7 +328,9 @@
# define SYS_VMCTL (KERNEL_CALL + 43) /* sys_vmctl() */
# define SYS_SYSCTL (KERNEL_CALL + 44) /* sys_sysctl() */
#define NR_SYS_CALLS 45 /* number of system calls */
# define SYS_VTIMER (KERNEL_CALL + 45) /* sys_vtimer() */
#define NR_SYS_CALLS 46 /* number of system calls */
/* Pseudo call for use in kernel/table.c. */
#define SYS_ALL_CALLS (NR_SYS_CALLS)
@ -604,6 +606,14 @@
#define VMCTL_INCSP 16
#define VMCTL_NOPAGEZERO 18
/* Field names for SYS_VTIMER. */
#define VT_WHICH m2_i1 /* which timer to set/retrieve */
# define VT_VIRTUAL 1 /* the ITIMER_VIRTUAL timer */
# define VT_PROF 2 /* the ITIMER_PROF timer */
#define VT_SET m2_i2 /* 1 for setting a timer, 0 retrieval only */
#define VT_VALUE m2_l1 /* new/previous value of the timer */
#define VT_ENDPT m2_l2 /* process to set/retrieve the timer for */
/*===========================================================================*
* Messages for the Reincarnation Server *
*===========================================================================*/

View file

@ -86,10 +86,14 @@ _PROTOTYPE(void *alloc_contig, (size_t len, int flags, phys_bytes *phys));
#define AC_LOWER16M 0x02
#define AC_ALIGN64K 0x04
/* Clock functionality: get system times or (un)schedule an alarm call. */
/* Clock functionality: get system times, (un)schedule an alarm call, or
* retrieve/set a process-virtual timer.
*/
_PROTOTYPE( int sys_times, (endpoint_t proc_nr, clock_t *user_time,
clock_t *sys_time, clock_t *uptime));
_PROTOTYPE(int sys_setalarm, (clock_t exp_time, int abs_time));
_PROTOTYPE( int sys_vtimer, (endpoint_t proc_nr, int which, clock_t *newval,
clock_t *oldval));
/* Shorthands for sys_irqctl() system call. */
#define sys_irqdisable(hook_id) \

View file

@ -44,6 +44,8 @@ typedef unsigned long sigset_t;
#define SIGEMT 16 /* EMT instruction */
#define SIGCHLD 17 /* child process terminated or stopped */
#define SIGWINCH 21 /* window size has changed */
#define SIGVTALRM 24 /* virtual alarm */
#define SIGPROF 25 /* profiler alarm */
/* POSIX requires the following signals to be defined, even if they are
* not supported. Here are the definitions, but they are not supported.
@ -54,7 +56,7 @@ typedef unsigned long sigset_t;
#define SIGTTIN 22 /* background process wants to read */
#define SIGTTOU 23 /* background process wants to write */
#define _NSIG 24 /* highest signal number plus one */
#define _NSIG 26 /* highest signal number plus one */
#ifdef _MINIX
#define SIGIOT SIGABRT /* for people who speak PDP-11 */

View file

@ -96,6 +96,8 @@ PUBLIC void clock_task()
PRIVATE void do_clocktick(m_ptr)
message *m_ptr; /* pointer to request message */
{
register struct proc *bill_copy = bill_ptr;
/* Despite its name, this routine is not called on every clock tick. It
* is called on those clock ticks when a lot of work needs to be done.
*/
@ -120,6 +122,18 @@ message *m_ptr; /* pointer to request message */
}
}
/* Check if a process-virtual timer expired. Check prev_ptr, but also
* bill_ptr - one process's user time is another's system time, and the
* profile timer decreases for both! Do this before the queue operations
* below, which may alter bill_ptr. Note the use a copy of bill_ptr, because
* bill_ptr may have been changed above, and this code can't be put higher
* up because otherwise cause_sig() may dequeue prev_ptr before we do.
*/
vtimer_check(prev_ptr);
if (prev_ptr != bill_copy)
vtimer_check(bill_copy);
/* Check if a clock timer expired and run its watchdog function. */
if (next_timeout <= realtime) {
tmrs_exptimers(&clock_timers, realtime, NULL);
@ -180,11 +194,13 @@ irq_hook_t *hook;
* realtime:
* The current uptime is incremented with all outstanding ticks.
* proc_ptr, bill_ptr:
* These are used for accounting. It does not matter if proc.c
* is changing them, provided they are always valid pointers,
* since at worst the previous process would be billed.
* These are used for accounting and virtual timers. It does not
* matter if proc.c is changing them, provided they are always
* valid pointers, since at worst the previous process would be
* billed.
*/
register unsigned ticks;
register int expired;
if(minix_panicing) return;
@ -208,6 +224,20 @@ irq_hook_t *hook;
bill_ptr->p_ticks_left -= ticks;
}
/* Decrement virtual timers, if applicable. We decrement both the virtual
* and the profile timer of the current process, and if the current process
* is not billable, the timer of the billed process as well.
* If any of the timers expire, do_clocktick() will send out signals.
*/
expired = 0;
if ((proc_ptr->p_misc_flags & VIRT_TIMER) &&
(proc_ptr->p_virt_left -= ticks) <= 0) expired = 1;
if ((proc_ptr->p_misc_flags & PROF_TIMER) &&
(proc_ptr->p_prof_left -= ticks) <= 0) expired = 1;
if (! (priv(proc_ptr)->s_flags & BILLABLE) &&
(bill_ptr->p_misc_flags & PROF_TIMER) &&
(bill_ptr->p_prof_left -= ticks) <= 0) expired = 1;
#if 0
/* Update load average. */
load_update();
@ -216,7 +246,7 @@ irq_hook_t *hook;
/* Check if do_clocktick() must be called. Done for alarms and scheduling.
* Some processes, such as the kernel tasks, cannot be preempted.
*/
if ((next_timeout <= realtime) || (proc_ptr->p_ticks_left <= 0)) {
if ((next_timeout <= realtime) || (proc_ptr->p_ticks_left <= 0) || expired) {
prev_ptr = proc_ptr; /* store running process */
lock_notify(HARDWARE, CLOCK); /* send notification */
}

View file

@ -29,6 +29,7 @@
#define USE_GETINFO 1 /* retrieve a copy of kernel data */
#define USE_TIMES 1 /* get process and system time info */
#define USE_SETALARM 1 /* schedule a synchronous alarm */
#define USE_VTIMER 1 /* set or retrieve a process-virtual timer */
#define USE_DEVIO 1 /* read or write a single I/O port */
#define USE_VDEVIO 1 /* process vector with I/O requests */
#define USE_SDEVIO 1 /* perform I/O request on a buffer */

View file

@ -33,6 +33,9 @@ struct proc {
clock_t p_user_time; /* user time in ticks */
clock_t p_sys_time; /* sys time in ticks */
clock_t p_virt_left; /* number of ticks left on virtual timer */
clock_t p_prof_left; /* number of ticks left on profile timer */
struct proc *p_nextready; /* pointer to next ready process */
struct proc *p_caller_q; /* head of list of procs wishing to send */
struct proc *p_q_link; /* link to next proc wishing to send */
@ -169,6 +172,8 @@ struct proc {
/* Misc flags */
#define REPLY_PENDING 0x01 /* reply to IPC_REQUEST is pending */
#define VIRT_TIMER 0x02 /* process-virtual timer is running */
#define PROF_TIMER 0x04 /* process-virtual profile timer is running */
#define MF_VM 0x08 /* process uses VM */
#define MF_ASYNMSG 0x10 /* Asynchrous message pending */
#define MF_FULLVM 0x20

View file

@ -70,10 +70,12 @@ _PROTOTYPE( void clear_endpoint, (struct proc *rc) );
_PROTOTYPE( phys_bytes umap_bios, (vir_bytes vir_addr, vir_bytes bytes));
_PROTOTYPE( phys_bytes umap_verify_grant, (struct proc *rp, endpoint_t grantee, cp_grant_id_t grant, vir_bytes offset, vir_bytes bytes, int access));
/* system/do_newmap.c */
_PROTOTYPE( int newmap, (struct proc *rp, struct mem_map *map_ptr) );
/* system/do_vtimer.c */
_PROTOTYPE( void vtimer_check, (struct proc *rp) );
/* interrupt.c */
_PROTOTYPE( void intr_handle, (irq_hook_t *hook) );
_PROTOTYPE( void put_irq_handler, (irq_hook_t *hook, int irq,

View file

@ -239,6 +239,7 @@ PRIVATE void initialize(void)
map(SYS_TIMES, do_times); /* get uptime and process times */
map(SYS_SETALARM, do_setalarm); /* schedule a synchronous alarm */
map(SYS_STIME, do_stime); /* set the boottime */
map(SYS_VTIMER, do_vtimer); /* set or retrieve a virtual timer */
/* System control. */
map(SYS_ABORT, do_abort); /* abort MINIX */

View file

@ -173,6 +173,11 @@ _PROTOTYPE( int do_setalarm, (message *m_ptr) );
_PROTOTYPE( int do_stime, (message *m_ptr) );
_PROTOTYPE( int do_vtimer, (message *m_ptr) );
#if ! USE_VTIMER
#define do_vtimer do_unused
#endif
_PROTOTYPE( int do_safecopy, (message *m_ptr) );
_PROTOTYPE( int do_vsafecopy, (message *m_ptr) );
_PROTOTYPE( int do_iopenable, (message *m_ptr) );

View file

@ -32,6 +32,7 @@ OBJECTS = \
$(SYSTEM)(do_times.o) \
$(SYSTEM)(do_setalarm.o) \
$(SYSTEM)(do_stime.o) \
$(SYSTEM)(do_vtimer.o) \
$(SYSTEM)(do_irqctl.o) \
$(SYSTEM)(do_devio.o) \
$(SYSTEM)(do_vdevio.o) \
@ -101,6 +102,9 @@ $(SYSTEM)(do_setalarm.o): do_setalarm.c
$(SYSTEM)(do_stime.o): do_stime.c
$(CC) do_stime.c
$(SYSTEM)(do_vtimer.o): do_vtimer.c
$(CC) do_vtimer.c
$(SYSTEM)(do_irqctl.o): do_irqctl.c
$(CC) do_irqctl.c

View file

@ -57,6 +57,10 @@ register message *m_ptr; /* pointer to request message */
rpc->p_reg.psw &= ~TRACEBIT; /* clear trace bit */
rpc->p_misc_flags &= ~(VIRT_TIMER | PROF_TIMER);
rpc->p_virt_left = 0; /* disable, clear the process-virtual timers */
rpc->p_prof_left = 0;
/* Parent and child have to share the quantum that the forked process had,
* so that queued processes do not have to wait longer because of the fork.
* If the time left is odd, the child gets an extra tick.

116
kernel/system/do_vtimer.c Normal file
View file

@ -0,0 +1,116 @@
/* The kernel call implemented in this file:
* m_type: SYS_VTIMER
*
* The parameters for this kernel call are:
* m2_i1: VT_WHICH (the timer: VT_VIRTUAL or VT_PROF)
* m2_i2: VT_SET (whether to set, or just retrieve)
* m2_l1: VT_VALUE (new/old expiration time, in ticks)
* m2_l2: VT_ENDPT (process to which the timer belongs)
*/
#include "../system.h"
#include <signal.h>
#include <minix/endpoint.h>
#if USE_VTIMER
/*===========================================================================*
* do_vtimer *
*===========================================================================*/
PUBLIC int do_vtimer(m_ptr)
message *m_ptr; /* pointer to request message */
{
/* Set and/or retrieve the value of one of a process' virtual timers. */
struct proc *rrp; /* pointer to requesting process */
struct proc *rp; /* pointer to process the timer belongs to */
register int pt_flag; /* the misc on/off flag for the req.d timer */
register clock_t *pt_left; /* pointer to the process' ticks-left field */
clock_t old_value; /* the previous number of ticks left */
int proc_nr, proc_nr_e;
/* The requesting process must be privileged. */
rrp = proc_addr(who_p);
if (! (priv(rrp)->s_flags & SYS_PROC)) return(EPERM);
if (m_ptr->VT_WHICH != VT_VIRTUAL && m_ptr->VT_WHICH != VT_PROF)
return(EINVAL);
/* The target process must be valid. */
proc_nr_e = (m_ptr->VT_ENDPT == SELF) ? m_ptr->m_source : m_ptr->VT_ENDPT;
if (!isokendpt(proc_nr_e, &proc_nr)) return(EINVAL);
rp = proc_addr(proc_nr);
/* Determine which flag and which field in the proc structure we want to
* retrieve and/or modify. This saves us having to differentiate between
* VT_VIRTUAL and VT_PROF multiple times below.
*/
if (m_ptr->VT_WHICH == VT_VIRTUAL) {
pt_flag = VIRT_TIMER;
pt_left = &rp->p_virt_left;
} else { /* VT_PROF */
pt_flag = PROF_TIMER;
pt_left = &rp->p_prof_left;
}
/* Retrieve the old value. */
if (rp->p_misc_flags & pt_flag) {
old_value = *pt_left;
if (old_value < 0) old_value = 0;
} else {
old_value = 0;
}
/* Set the new value, if requested. This is called from the system task, so
* we can be interrupted by the clock interrupt, but not by the clock task.
* Therefore we only have to protect against interference from clock.c's
* clock_handler(). We can do this without disabling interrupts, by removing
* the timer's flag before changing the ticks-left field; in that case the
* clock interrupt will not touch the latter anymore.
*/
if (m_ptr->VT_SET) {
rp->p_misc_flags &= ~pt_flag; /* disable virtual timer */
if (m_ptr->VT_VALUE > 0) {
*pt_left = m_ptr->VT_VALUE; /* set new timer value */
rp->p_misc_flags |= pt_flag; /* (re)enable virtual timer */
} else {
*pt_left = 0; /* clear timer value */
}
}
m_ptr->VT_VALUE = old_value;
return(OK);
}
#endif /* USE_VTIMER */
/*===========================================================================*
* vtimer_check *
*===========================================================================*/
PUBLIC void vtimer_check(rp)
struct proc *rp; /* pointer to the process */
{
/* This is called from the clock task, so we can be interrupted by the clock
* interrupt, but not by the system task. Therefore we only have to protect
* against interference from the clock handler. We can safely perform the
* following actions without locking as well though, as the clock handler
* never alters p_misc_flags, and only decreases p_virt_left/p_prof_left.
*/
/* Check if the virtual timer expired. If so, send a SIGVTALRM signal. */
if ((rp->p_misc_flags & VIRT_TIMER) && rp->p_virt_left <= 0) {
rp->p_misc_flags &= ~VIRT_TIMER;
rp->p_virt_left = 0;
cause_sig(rp->p_nr, SIGVTALRM);
}
/* Check if the profile timer expired. If so, send a SIGPROF signal. */
if ((rp->p_misc_flags & PROF_TIMER) && rp->p_prof_left <= 0) {
rp->p_misc_flags &= ~PROF_TIMER;
rp->p_prof_left = 0;
cause_sig(rp->p_nr, SIGPROF);
}
}

View file

@ -71,6 +71,7 @@ libsys_FILES=" \
sys_voutb.c \
sys_voutl.c \
sys_voutw.c \
sys_vtimer.c \
taskcall.c \
ds.c \
vm_allocmem.c \

28
lib/syslib/sys_vtimer.c Normal file
View file

@ -0,0 +1,28 @@
#include "syslib.h"
PUBLIC int sys_vtimer(proc, which, newval, oldval)
endpoint_t proc; /* proc to retrieve/set the timer for */
int which; /* timer to retrieve/set */
clock_t *newval; /* if non-NULL, set to this new value */
clock_t *oldval; /* if non-NULL, old value is stored here */
{
message m;
int r;
m.VT_ENDPT = proc;
m.VT_WHICH = which;
if (newval != NULL) {
m.VT_SET = 1;
m.VT_VALUE = *newval;
} else {
m.VT_SET = 0;
}
r = _taskcall(SYSTASK, SYS_VTIMER, &m);
if (oldval != NULL) {
*oldval = m.VT_VALUE;
}
return(r);
}

View file

@ -32,10 +32,14 @@ A timer that is decremented in realtime. When it expires, a
signal is delivered to the process.
.TP
.B ITIMER_VIRTUAL
Not supported on Minix.
A timer that is decremented in process user time. When it expires, a
.BR SIGVTALRM
signal is delivered to the process.
.TP
.B ITIMER_PROF
Not supported on Minix.
A timer that is decremented in process user+system time. When it expires, a
.BR SIGPROF
signal is delivered to the process.
.PP
The specified timer will first expire after the time specified in the 'it_value' field of the itimerval structure. Similarly, upon retrieval the 'it_value' field will contain the time after which the timer will expire.
.PP
@ -58,9 +62,6 @@ Either \fIwhich\fP is not one of the ITIMER_* constants above, or one of the tim
.TP
.B EFAULT
Bad \fIvalue\fP or \fIovalue\fP address.
.TP
.B ENOSYS
The value of \fIwhich\fP is not ITIMER_REAL.
.SH SEE ALSO
.BR alarm (2)
.SH AUTHOR

View file

@ -148,6 +148,8 @@ SIGTSTP 20 ps Interactive stop signal
SIGTTIN 21 ps Background read
SIGTTOU 22 ps Background write
SIGWINCH 23 xvi Window size change
SIGVTALRM 24 xk Virtual alarm clock
SIGPROF 25 xk Profiler alarm clock
.ft R
.fi
.PP

View file

@ -6,6 +6,7 @@
* do_itimer: perform the ITIMER system call
* do_alarm: perform the ALARM 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"
@ -22,6 +23,8 @@ FORWARD _PROTOTYPE( clock_t ticks_from_timeval, (struct timeval *tv) );
FORWARD _PROTOTYPE( void timeval_from_ticks, (struct timeval *tv,
clock_t ticks) );
FORWARD _PROTOTYPE( int is_sane_timeval, (struct timeval *tv) );
FORWARD _PROTOTYPE( void getset_vtimer, (struct mproc *mp, int nwhich,
struct itimerval *value, struct itimerval *ovalue) );
FORWARD _PROTOTYPE( void get_realtimer, (struct mproc *mp,
struct itimerval *value) );
FORWARD _PROTOTYPE( void set_realtimer, (struct mproc *mp,
@ -98,7 +101,7 @@ PUBLIC int do_itimer()
int r;
/* Make sure 'which' is one of the defined timers. */
if (m_in.which_timer < ITIMER_REAL || m_in.which_timer > ITIMER_PROF)
if (m_in.which_timer < 0 || m_in.which_timer >= NR_ITIMERS)
return(EINVAL);
/* Determine whether to set and/or return the given timer value, based on
@ -134,8 +137,11 @@ PUBLIC int do_itimer()
case ITIMER_VIRTUAL :
case ITIMER_PROF :
/* Not implemented. */
r = ENOSYS;
getset_vtimer(mp, m_in.which_timer,
(setval) ? &value : NULL,
(getval) ? &ovalue : NULL);
r = OK;
break;
}
@ -172,6 +178,97 @@ PUBLIC int do_alarm()
return(remaining);
}
/*===========================================================================*
* getset_vtimer *
*===========================================================================*/
PRIVATE void getset_vtimer(rmp, which, value, ovalue)
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(__FILE__, "invalid vtimer type", 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(__FILE__, "sys_vtimer failed", 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 *
*===========================================================================*/
PUBLIC void check_vtimer(proc_nr, sig)
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(__FILE__, "invalid vtimer signal", 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 *
*===========================================================================*/
@ -195,7 +292,7 @@ struct itimerval *value;
/* 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;
if (remaining <= 0) remaining = rmp->mp_interval[ITIMER_REAL];
} else {
remaining = 0;
}
@ -204,7 +301,7 @@ struct itimerval *value;
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);
timeval_from_ticks(&value->it_interval, rmp->mp_interval[ITIMER_REAL]);
}
/*===========================================================================*
@ -226,7 +323,7 @@ struct itimerval *value;
/* Apply these values. */
set_alarm(rmp, ticks);
rmp->mp_interval = interval;
rmp->mp_interval[ITIMER_REAL] = interval;
}
/*===========================================================================*
@ -270,8 +367,8 @@ struct timer *tp;
* The set_alarm call will be calling pm_set_timer from within this callback
* from the pm_expire_timers function. This is safe, but we must not use the
* "tp" structure below this point anymore. */
if (rmp->mp_interval > 0)
set_alarm(rmp, rmp->mp_interval);
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);

View file

@ -12,3 +12,4 @@
#define MAX_SECS (((1<<(sizeof(clock_t)*8-1))-1)/system_hz)
/* max.secs for setitimer() ((2^31-1)/HZ) */
#define NR_ITIMERS 3 /* number of supported interval timers */

View file

@ -41,7 +41,7 @@ PUBLIC int do_fork()
register struct mproc *rmc; /* pointer to child */
pid_t new_pid;
static int next_child;
int n = 0, r, s;
int i, n = 0, r, s;
endpoint_t child_ep;
/* If tables might fill up during FORK, don't even start since recovery half
@ -86,7 +86,8 @@ PUBLIC int do_fork()
rmc->mp_exitstatus = 0;
rmc->mp_sigstatus = 0;
rmc->mp_endpoint = child_ep; /* passed back by VM */
rmc->mp_interval = 0; /* reset interval timer */
for (i = 0; i < NR_ITIMERS; i++)
rmc->mp_interval[i] = 0; /* reset timer intervals */
/* Find a free pid for the child and put it in the table. */
new_pid = get_free_pid();
@ -115,7 +116,7 @@ PUBLIC int do_fork_nb()
int s;
pid_t new_pid;
static int next_child;
int n = 0, r;
int i, n = 0, r;
endpoint_t child_ep;
/* Only system processes are allowed to use fork_nb */
@ -159,6 +160,8 @@ PUBLIC int do_fork_nb()
rmc->mp_child_utime = 0; /* reset administration */
rmc->mp_child_stime = 0; /* reset administration */
rmc->mp_endpoint = child_ep; /* passed back by VM */
for (i = 0; i < NR_ITIMERS; i++)
rmc->mp_interval[i] = 0; /* reset timer intervals */
/* Find a free pid for the child and put it in the table. */
new_pid = get_free_pid();

View file

@ -7,6 +7,8 @@
#include <timers.h>
#include <signal.h>
#include "const.h"
EXTERN struct mproc {
char mp_exitstatus; /* storage for status when process exits */
char mp_sigstatus; /* storage for signal # for killed procs */
@ -39,7 +41,7 @@ EXTERN struct mproc {
* PM_UNPAUSE request is delivered.
*/
struct timer mp_timer; /* watchdog timer for alarm(2), setitimer(2) */
clock_t mp_interval; /* repetition interval for setitimer(2) */
clock_t mp_interval[NR_ITIMERS]; /* setitimer(2) repetition intervals */
unsigned mp_flags; /* flag bits */
vir_bytes mp_procargs; /* ptr to proc's initial stack arguments */

View file

@ -11,6 +11,7 @@ struct memory;
_PROTOTYPE( int do_alarm, (void) );
_PROTOTYPE( int do_itimer, (void) );
_PROTOTYPE( void set_alarm, (struct mproc *rmp, clock_t ticks) );
_PROTOTYPE( void check_vtimer, (int proc_nr, int sig) );
/* break.c */
_PROTOTYPE( int do_brk, (void) );

View file

@ -264,9 +264,10 @@ sigset_t sig_map;
/* Check each bit in turn to see if a signal is to be sent. Unlike
* kill(), the kernel may collect several unrelated signals for a
* process and pass them to PM in one blow. Thus loop on the bit
* map. For SIGINT, SIGWINCH and SIGQUIT, use proc_id 0 to indicate
* a broadcast to the recipient's process group. For SIGKILL, use
* proc_id -1 to indicate a systemwide broadcast.
* map. For SIGVTALRM and SIGPROF, see if we need to restart a
* virtual timer. For SIGINT, SIGWINCH and SIGQUIT, use proc_id 0
* to indicate a broadcast to the recipient's process group. For
* SIGKILL, use proc_id -1 to indicate a systemwide broadcast.
*/
for (i = 1; i <= _NSIG; i++) {
if (!sigismember(&sig_map, i)) continue;
@ -279,6 +280,10 @@ sigset_t sig_map;
case SIGQUIT:
case SIGWINCH:
id = 0; break; /* broadcast to process group */
case SIGVTALRM:
case SIGPROF:
check_vtimer(proc_nr, i);
/* fall-through */
default:
id = proc_id;
break;

View file

@ -7,7 +7,7 @@ OBJ= test1 test2 test3 test4 test5 test6 test7 test8 test9 \
test10 test12 test13 test14 test15 test16 test17 test18 test19 \
test21 test22 test23 test25 test26 test27 test28 test29 \
test30 test31 test32 test34 test35 test36 test37 test38 \
test39 t10a t11a t11b test40 t40a t40b t40c t40d t40e t40f
test39 t10a t11a t11b test40 t40a t40b t40c t40d t40e t40f test41
BIGOBJ= test20 test24
ROOTOBJ= test11 test33
@ -28,7 +28,8 @@ $(ROOTOBJ):
clean:
cd select && make clean
-rm -rf *.o *.s *.bak test? test?? t10a t11a t11b t40a t40b t40c t40d t40e t40f DIR*
-rm -rf *.o *.s *.bak test? test?? t10a t11a t11b \
t40a t40b t40c t40d t40e t40f DIR*
test1: test1.c
test2: test2.c
@ -79,3 +80,4 @@ t40c: t40c.c
t40d: t40d.c
t40e: t40e.c
t40f: t40f.c
test41: test41.c

409
test/test41.c Normal file
View file

@ -0,0 +1,409 @@
/* Tests for getitimer(2)/setitimer(2) - by D.C. van Moolenbroek */
/* Warning: this test deals with (real and virtual) time, and, lacking a proper
* point of reference, its correctness depends on circumstances like CPU speed
* and system load. A succeeding test run says a lot - failure not so much. */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <minix/config.h>
#include <minix/sysinfo.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>
#define ITERATIONS 3
#define MAX_ERROR 4
/* we have to keep in mind the millisecond values are rounded up */
#define UPPERUSEC(us) ((us)+(1000000/system_hz))
#define EQUSEC(l,r) \
((l) <= ((r) + (1000000/system_hz)) && (l) >= ((r) - (1000000/system_hz)))
#define FILLITIMER(it, vs, vu, is, iu) \
(it).it_value.tv_sec = (vs); \
(it).it_value.tv_usec = (vu); \
(it).it_interval.tv_sec = (is); \
(it).it_interval.tv_usec = (iu);
/* these two macros are not fully working for all possible values;
* the tests only use values that the macros can deal with, though. */
#define EQITIMER(it, vs, vu, is, iu) \
((it).it_value.tv_sec == (vs) && EQUSEC((it).it_value.tv_usec,vu) && \
(it).it_interval.tv_sec == (is) && (it).it_interval.tv_usec == (iu))
#define LEITIMER(it, vs, vu, is, iu) \
((it).it_value.tv_sec > 0 && ((it).it_value.tv_sec < (vs) || \
((it).it_value.tv_sec == (vs) && (it).it_value.tv_usec <= \
UPPERUSEC(vu))) && \
(it).it_interval.tv_sec == (is) && EQUSEC((it).it_interval.tv_usec,iu))
_PROTOTYPE(int main, (int argc, char **argv));
_PROTOTYPE(void test, (int m, int t));
_PROTOTYPE(void test_which, (void));
_PROTOTYPE(void test_getset, (void));
_PROTOTYPE(void test_neglarge, (void));
_PROTOTYPE(void test_zero, (void));
_PROTOTYPE(void test_timer, (void));
_PROTOTYPE(void test_alarm, (void));
_PROTOTYPE(void test_fork, (void));
_PROTOTYPE(void test_exec, (void));
_PROTOTYPE(int do_check, (void));
_PROTOTYPE(void got_alarm, (int sig));
_PROTOTYPE(void busy_wait, (int secs));
_PROTOTYPE(void e, (int n));
_PROTOTYPE(void quit, (void));
static char *executable;
static int signals;
static int timer;
static int errct = 0, subtest;
static long system_hz;
static int sigs[] = { SIGALRM, SIGVTALRM, SIGPROF };
static const char *names[] = { "REAL", "VIRTUAL", "PROF" };
int main(argc, argv)
int argc;
char **argv;
{
int i, m = 0xFFFF, n = 0xF;
getsysinfo_up(PM_PROC_NR, SIU_SYSTEMHZ, sizeof(system_hz), &system_hz);
if (strcmp(argv[0], "DO CHECK") == 0) {
timer = atoi(argv[1]);
exit(do_check());
}
printf("Test 41 ");
fflush(stdout);
executable = argv[0];
if (argc >= 2) m = atoi(argv[1]);
if (argc >= 3) n = atoi(argv[2]);
for (i = 0; i < ITERATIONS; i++) {
if (n & 1) test(m, ITIMER_REAL);
if (n & 2) test(m, ITIMER_VIRTUAL);
if (n & 4) test(m, ITIMER_PROF);
}
quit();
return(-1); /* impossible */
}
void test(m, t)
int m;
int t;
{
timer = t;
if (m & 0001) test_which();
if (m & 0002) test_getset();
if (m & 0004) test_neglarge();
if (m & 0010) test_zero();
if (m & 0020) test_timer();
if (m & 0040) test_alarm();
if (m & 0100) test_fork();
if (m & 0200) test_exec();
}
/* test invalid and unsupported 'which' values */
void test_which()
{
struct itimerval it;
subtest = 0;
errno = 0; if (!getitimer(-1, &it) || errno != EINVAL) e(1);
errno = 0; if ( getitimer(timer, &it) ) e(2);
errno = 0; if (!getitimer( 3, &it) || errno != EINVAL) e(3);
}
/* test if we get back what we set */
void test_getset()
{
struct itimerval it, oit;
subtest = 1;
/* no alarm should be set initially */
if (getitimer(timer, &it)) e(1);
if (!EQITIMER(it, 0, 0, 0, 0)) e(2);
if (setitimer(timer, &it, &oit)) e(3);
if (setitimer(timer, &oit, NULL)) e(4);
if (!EQITIMER(oit, 0, 0, 0, 0)) e(5);
FILLITIMER(it, 123, 0, 456, 0);
if (setitimer(timer, &it, NULL)) e(6);
FILLITIMER(it, 987, 0, 654, 0);
if (setitimer(timer, &it, &oit)) e(7);
if (!LEITIMER(oit, 123, 0, 456, 0)) e(8);
if (getitimer(timer, &oit)) e(9);
if (!LEITIMER(oit, 987, 0, 654, 0)) e(10);
FILLITIMER(it, 0, 0, 0, 0);
if (setitimer(timer, &it, &oit)) e(11);
if (!LEITIMER(oit, 987, 0, 654, 0)) e(12);
if (getitimer(timer, &oit)) e(13);
if (!EQITIMER(oit, 0, 0, 0, 0)) e(14);
}
/* test negative/large values */
void test_neglarge()
{
struct itimerval it;
subtest = 2;
FILLITIMER(it, 4, 0, 5, 0);
if (setitimer(timer, &it, NULL)) e(1);
FILLITIMER(it, 1000000000, 0, 0, 0);
if (!setitimer(timer, &it, NULL) || errno != EINVAL) e(2);
FILLITIMER(it, 0, 1000000, 0, 0);
if (!setitimer(timer, &it, NULL) || errno != EINVAL) e(3);
FILLITIMER(it, 0, 0, 0, 1000000);
if (!setitimer(timer, &it, NULL) || errno != EINVAL) e(4);
FILLITIMER(it, -1, 0, 0, 0);
if (!setitimer(timer, &it, NULL) || errno != EINVAL) e(5);
FILLITIMER(it, 0, -1, 0, 0);
if (!setitimer(timer, &it, NULL) || errno != EINVAL) e(6);
FILLITIMER(it, 0, 0, -1, 0);
if (!setitimer(timer, &it, NULL) || errno != EINVAL) e(7);
FILLITIMER(it, 0, 0, 0, -1);
if (!setitimer(timer, &it, NULL) || errno != EINVAL) e(8);
if (getitimer(timer, &it)) e(9);
if (!LEITIMER(it, 4, 0, 5, 0)) e(10);
}
/* setitimer with a zero timer has to set the interval to zero as well */
void test_zero()
{
struct itimerval it;
subtest = 3;
it.it_value.tv_sec = 0;
it.it_value.tv_usec = 0;
it.it_interval.tv_sec = 1;
it.it_interval.tv_usec = 1;
if (setitimer(timer, &it, NULL)) e(1);
if (getitimer(timer, &it)) e(2);
if (!EQITIMER(it, 0, 0, 0, 0)) e(3);
}
/* test actual timer functioning */
void test_timer()
{
struct itimerval it;
subtest = 4;
if (signal(sigs[timer], got_alarm) == SIG_ERR) e(1);
FILLITIMER(it, 0, 1, 0, 1);
if (setitimer(timer, &it, NULL)) e(2);
signals = 0;
busy_wait(1);
FILLITIMER(it, 0, 0, 0, 0);
if (setitimer(timer, &it, NULL)) e(3);
/* we don't know how many signals we'll actually get in practice,
* so these checks more or less cover the extremes of the acceptable */
if (signals < 2) e(4);
if (signals > system_hz * 2) e(5);
/* only for REAL timer can we check against the clock */
if (timer == ITIMER_REAL) {
FILLITIMER(it, 1, 0, 0, 0);
if (setitimer(timer, &it, NULL)) e(6);
signals = 0;
busy_wait(1);
FILLITIMER(it, 0, 0, 0, 0);
if (setitimer(timer, &it, NULL)) e(7);
if (signals != 1) e(8);
}
signals = 0;
busy_wait(1);
if (signals != 0) e(9);
}
/* test itimer/alarm interaction */
void test_alarm(void) {
struct itimerval it;
/* only applicable for ITIMER_REAL */
if (timer != ITIMER_REAL) return;
subtest = 5;
if (signal(SIGALRM, got_alarm) == SIG_ERR) e(1);
FILLITIMER(it, 3, 0, 1, 0);
if (setitimer(timer, &it, NULL)) e(2);
if (alarm(2) != 3) e(3);
if (getitimer(timer, &it)) e(4);
if (!LEITIMER(it, 2, 0, 0, 0)) e(5);
signals = 0;
busy_wait(5);
if (signals != 1) e(6);
if (getitimer(timer, &it)) e(7);
if (!EQITIMER(it, 0, 0, 0, 0)) e(8);
}
/* test that the timer is reset on forking */
void test_fork(void) {
struct itimerval it, oit;
pid_t pid;
int status;
subtest = 6;
FILLITIMER(it, 12, 34, 56, 78);
if (setitimer(timer, &it, NULL)) e(1);
pid = fork();
if (pid < 0) e(2);
if (pid == 0) {
if (getitimer(timer, &it)) exit(5);
if (!EQITIMER(it, 0, 0, 0, 0)) exit(6);
exit(0);
}
if (wait(&status) != pid) e(3);
if (!WIFEXITED(status)) e(4);
if (WEXITSTATUS(status) != 0) e(WEXITSTATUS(status));
FILLITIMER(it, 0, 0, 0, 0);
if (setitimer(timer, &it, &oit)) e(7);
if (!LEITIMER(oit, 12, 34, 56, 78)) e(8);
}
/* test if timer is carried over to exec()'ed process */
void test_exec(void) {
struct itimerval it;
pid_t pid;
int status;
char buf[2];
subtest = 7;
pid = fork();
if (pid < 0) e(1);
if (pid == 0) {
FILLITIMER(it, 3, 0, 1, 0);
if (setitimer(timer, &it, NULL)) exit(2);
sprintf(buf, "%d", timer);
execl(executable, "DO CHECK", buf, NULL);
exit(3);
}
if (wait(&status) != pid) e(4);
if (WIFSIGNALED(status)) {
/* process should have died from corresponding signal */
if (WTERMSIG(status) != sigs[timer]) e(5);
}
else {
if (WIFEXITED(status)) e(WEXITSTATUS(status));
else e(6);
}
}
/* procedure of the exec()'ed process */
int do_check()
{
struct itimerval it;
if (getitimer(timer, &it)) return(81);
if (!LEITIMER(it, 3, 0, 1, 0)) return(82);
busy_wait(60);
return(83);
}
void busy_wait(secs)
int secs;
{
time_t now, exp;
int i;
exp = time(&now) + secs + 1;
while (now < exp) {
for (i = 0; i < 100000; i++);
time(&now);
}
}
void got_alarm(sig)
int sig;
{
if (sig != sigs[timer]) e(1001);
signals++;
}
void e(n)
int n;
{
printf("Timer %s, subtest %d, error %d, errno %d: %s\n",
names[timer], subtest, n, errno, strerror(errno));
if (errct++ > MAX_ERROR) {
printf("Too many errors; test aborted\n");
exit(1);
}
}
void quit()
{
if (errct == 0) {
printf("ok\n");
exit(0);
} else {
printf("%d errors\n", errct);
exit(1);
}
}