#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",
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
#ifdef SIGWINCH
{ "WINCH", SIGWINCH },
#endif
+ { "VTALRM", SIGVTALRM },
+ { "PROF", SIGPROF },
{ NULL, 0 }
};
# 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)
#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 *
*===========================================================================*/
#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) \
#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.
#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 */
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.
*/
}
}
+ /* 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);
* 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;
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();
/* 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 */
}
#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 */
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 */
/* 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
_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,
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 */
_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) );
$(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) \
$(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
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.
--- /dev/null
+/* 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);
+ }
+}
sys_voutb.c \
sys_voutl.c \
sys_voutw.c \
+ sys_vtimer.c \
taskcall.c \
ds.c \
vm_allocmem.c \
--- /dev/null
+#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);
+}
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
.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
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
* 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"
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,
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
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;
}
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 *
*===========================================================================*/
/* 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;
}
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]);
}
/*===========================================================================*
/* Apply these values. */
set_alarm(rmp, ticks);
- rmp->mp_interval = interval;
+ rmp->mp_interval[ITIMER_REAL] = interval;
}
/*===========================================================================*
* 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);
#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 */
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
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();
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 */
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();
#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 */
* 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 */
_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) );
/* 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;
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;
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
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
t40d: t40d.c
t40e: t40e.c
t40f: t40f.c
+test41: test41.c
--- /dev/null
+/* 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);
+ }
+}