this change
   - makes panic() variadic, doing full printf() formatting -
     no more NO_NUM, and no more separate printf() statements
     needed to print extra info (or something in hex) before panicing
   - unifies panic() - same panic() name and usage for everyone -
     vm, kernel and rest have different names/syntax currently
     in order to implement their own luxuries, but no longer
   - throws out the 1st argument, to make source less noisy.
     the panic() in syslib retrieves the server name from the kernel
     so it should be clear enough who is panicing; e.g.
         panic("sigaction failed: %d", errno);
     looks like:
         at_wini(73130): panic: sigaction failed: 0
         syslib:panic.c: stacktrace: 0x74dc 0x2025 0x100a
   - throws out report() - printf() is more convenient and powerful
   - harmonizes/fixes the use of panic() - there were a few places
     that used printf-style formatting (didn't work) and newlines
     (messes up the formatting) in panic()
   - throws out a few per-server panic() functions
   - cleans up a tie-in of tty with panic()
merging printf() and panic() statements to be done incrementally.
		
	
			
		
			
				
	
	
		
			379 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			379 lines
		
	
	
		
			12 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
 | 
						|
 *   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"
 | 
						|
#include <signal.h>
 | 
						|
#include <sys/time.h>
 | 
						|
#include <string.h>
 | 
						|
#include <minix/com.h>
 | 
						|
#include "mproc.h"
 | 
						|
#include "param.h"
 | 
						|
 | 
						|
#define US 1000000	/* shortcut for microseconds per second */
 | 
						|
 | 
						|
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,
 | 
						|
					 struct itimerval *value)	);
 | 
						|
FORWARD _PROTOTYPE( void cause_sigalrm, (struct timer *tp)		);
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *				ticks_from_timeval			     * 
 | 
						|
 *===========================================================================*/
 | 
						|
PRIVATE 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 = (clock_t) (system_hz * (unsigned long) tv->tv_sec);
 | 
						|
  if ( (unsigned long) ticks / system_hz != (unsigned long) tv->tv_sec) {
 | 
						|
	ticks = LONG_MAX;
 | 
						|
  } else {
 | 
						|
	ticks += (clock_t)
 | 
						|
		((system_hz * (unsigned long) tv->tv_usec + (US-1)) / US);
 | 
						|
  }
 | 
						|
 | 
						|
  if (ticks < 0) ticks = LONG_MAX;
 | 
						|
 | 
						|
  return(ticks);
 | 
						|
}
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *				timeval_from_ticks			     * 
 | 
						|
 *===========================================================================*/
 | 
						|
PRIVATE 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				     * 
 | 
						|
 *===========================================================================*/
 | 
						|
PRIVATE int is_sane_timeval(tv)
 | 
						|
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				     *
 | 
						|
 *===========================================================================*/
 | 
						|
PUBLIC int do_itimer()
 | 
						|
{
 | 
						|
  struct itimerval ovalue, value;	/* old and new interval timers */
 | 
						|
  int setval, getval;			/* set and/or retrieve the values? */
 | 
						|
  int r;
 | 
						|
 | 
						|
  /* Make sure 'which' is one of the defined timers. */
 | 
						|
  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
 | 
						|
   * which of the new_val and old_val parameters are nonzero. At least one of
 | 
						|
   * them must be nonzero.
 | 
						|
   */
 | 
						|
  setval = (m_in.new_val != NULL);
 | 
						|
  getval = (m_in.old_val != NULL);
 | 
						|
 | 
						|
  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, (vir_bytes) m_in.new_val,
 | 
						|
  		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 (m_in.which_timer) {
 | 
						|
  	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, m_in.which_timer,
 | 
						|
  				(setval) ? &value : NULL,
 | 
						|
  				(getval) ? &ovalue : NULL);
 | 
						|
 | 
						|
  		r = OK;
 | 
						|
  		break;
 | 
						|
 | 
						|
	default:
 | 
						|
		panic("invalid timer type: %d", m_in.which_timer);
 | 
						|
  }
 | 
						|
 | 
						|
  /* 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, (vir_bytes) m_in.old_val, (phys_bytes) sizeof(ovalue));
 | 
						|
  }
 | 
						|
 | 
						|
  return(r);
 | 
						|
}
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *				do_alarm				     *
 | 
						|
 *===========================================================================*/
 | 
						|
PUBLIC int do_alarm()
 | 
						|
{
 | 
						|
  struct itimerval value, ovalue;
 | 
						|
  int remaining;		/* previous time left in seconds */
 | 
						|
 | 
						|
  /* retrieve the old timer value, in seconds (rounded up) */
 | 
						|
  get_realtimer(mp, &ovalue);
 | 
						|
  
 | 
						|
  remaining = ovalue.it_value.tv_sec;
 | 
						|
  if (ovalue.it_value.tv_usec > 0) remaining++;
 | 
						|
 | 
						|
  /* set the new timer value */
 | 
						|
  memset(&value, 0, sizeof(value));
 | 
						|
  value.it_value.tv_sec = m_in.seconds;
 | 
						|
 | 
						|
  set_realtimer(mp, &value);
 | 
						|
 | 
						|
  /* and return the old timer value */
 | 
						|
  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("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				     * 
 | 
						|
 *===========================================================================*/
 | 
						|
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("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				     * 
 | 
						|
 *===========================================================================*/
 | 
						|
PRIVATE void get_realtimer(rmp, value)
 | 
						|
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 */
 | 
						|
  int s;
 | 
						|
 | 
						|
  /* First determine remaining time, in ticks, of previous alarm, if set. */
 | 
						|
  if (rmp->mp_flags & ALARM_ON) {
 | 
						|
  	if ( (s = getuptime(&uptime)) != OK)
 | 
						|
  		panic("get_realtimer couldn't get uptime: %d", s);
 | 
						|
  	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				     * 
 | 
						|
 *===========================================================================*/
 | 
						|
PRIVATE void set_realtimer(rmp, value)
 | 
						|
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				     * 
 | 
						|
 *===========================================================================*/
 | 
						|
PUBLIC 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) {
 | 
						|
  	pm_set_timer(&rmp->mp_timer, ticks, cause_sigalrm, rmp->mp_endpoint);
 | 
						|
	rmp->mp_flags |=  ALARM_ON;
 | 
						|
  } else if (rmp->mp_flags & ALARM_ON) {
 | 
						|
  	pm_cancel_timer(&rmp->mp_timer);
 | 
						|
  	rmp->mp_flags &= ~ALARM_ON;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *				cause_sigalrm				     * 
 | 
						|
 *===========================================================================*/
 | 
						|
PRIVATE void cause_sigalrm(tp)
 | 
						|
struct timer *tp;
 | 
						|
{
 | 
						|
  int proc_nr_n;
 | 
						|
  register struct mproc *rmp;
 | 
						|
 | 
						|
  /* get process from timer */
 | 
						|
  if(pm_isokendpt(tmr_arg(tp)->ta_int, &proc_nr_n) != OK) {
 | 
						|
  	printf("PM: ignoring timer for invalid endpoint %d\n",
 | 
						|
  		tmr_arg(tp)->ta_int);
 | 
						|
  	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 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[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 */);
 | 
						|
}
 |