/*
       * linux/kernel/itimer.c
       *
       * Copyright (C) 1992 Darren Senn
       */
      
      /* These are all the functions necessary to implement itimers */
      
      #include <linux/mm.h>
      #include <linux/smp_lock.h>
      #include <linux/interrupt.h>
      
      #include <asm/uaccess.h>
      
      /*
       * change timeval to jiffies, trying to avoid the 
       * most obvious overflows..
       *
       * The tv_*sec values are signed, but nothing seems to 
       * indicate whether we really should use them as signed values
       * when doing itimers. POSIX doesn't mention this (but if
       * alarm() uses itimers without checking, we have to use unsigned
       * arithmetic).
       */
  25  static unsigned long tvtojiffies(struct timeval *value)
      {
      	unsigned long sec = (unsigned) value->tv_sec;
      	unsigned long usec = (unsigned) value->tv_usec;
      
  30  	if (sec > (ULONG_MAX / HZ))
  31  		return ULONG_MAX;
      	usec += 1000000 / HZ - 1;
      	usec /= 1000000 / HZ;
  34  	return HZ*sec+usec;
      }
      
  37  static void jiffiestotv(unsigned long jiffies, struct timeval *value)
      {
      	value->tv_usec = (jiffies % HZ) * (1000000 / HZ);
      	value->tv_sec = jiffies / HZ;
      }
      
  43  int do_getitimer(int which, struct itimerval *value)
      {
      	register unsigned long val, interval;
      
  47  	switch (which) {
  48  	case ITIMER_REAL:
      		interval = current->it_real_incr;
      		val = 0;
      		/* 
      		 * FIXME! This needs to be atomic, in case the kernel timer happens!
      		 */
  54  		if (timer_pending(¤t->real_timer)) {
      			val = current->real_timer.expires - jiffies;
      
      			/* look out for negative/zero itimer.. */
  58  			if ((long) val <= 0)
      				val = 1;
      		}
  61  		break;
  62  	case ITIMER_VIRTUAL:
      		val = current->it_virt_value;
      		interval = current->it_virt_incr;
  65  		break;
  66  	case ITIMER_PROF:
      		val = current->it_prof_value;
      		interval = current->it_prof_incr;
  69  		break;
  70  	default:
  71  		return(-EINVAL);
      	}
      	jiffiestotv(val, &value->it_value);
      	jiffiestotv(interval, &value->it_interval);
  75  	return 0;
      }
      
      /* SMP: Only we modify our itimer values. */
  79  asmlinkage long sys_getitimer(int which, struct itimerval *value)
      {
      	int error = -EFAULT;
      	struct itimerval get_buffer;
      
  84  	if (value) {
      		error = do_getitimer(which, &get_buffer);
      		if (!error &&
  87  		    copy_to_user(value, &get_buffer, sizeof(get_buffer)))
      			error = -EFAULT;
      	}
  90  	return error;
      }
      
  93  void it_real_fn(unsigned long __data)
      {
      	struct task_struct * p = (struct task_struct *) __data;
      	unsigned long interval;
      
      	send_sig(SIGALRM, p, 1);
      	interval = p->it_real_incr;
 100  	if (interval) {
 101  		if (interval > (unsigned long) LONG_MAX)
      			interval = LONG_MAX;
      		p->real_timer.expires = jiffies + interval;
      		add_timer(&p->real_timer);
      	}
      }
      
 108  int do_setitimer(int which, struct itimerval *value, struct itimerval *ovalue)
      {
      	register unsigned long i, j;
      	int k;
      
      	i = tvtojiffies(&value->it_interval);
      	j = tvtojiffies(&value->it_value);
 115  	if (ovalue && (k = do_getitimer(which, ovalue)) < 0)
 116  		return k;
 117  	switch (which) {
 118  		case ITIMER_REAL:
      			del_timer_sync(¤t->real_timer);
      			current->it_real_value = j;
      			current->it_real_incr = i;
 122  			if (!j)
 123  				break;
 124  			if (j > (unsigned long) LONG_MAX)
      				j = LONG_MAX;
      			i = j + jiffies;
      			current->real_timer.expires = i;
      			add_timer(¤t->real_timer);
 129  			break;
 130  		case ITIMER_VIRTUAL:
 131  			if (j)
      				j++;
      			current->it_virt_value = j;
      			current->it_virt_incr = i;
 135  			break;
 136  		case ITIMER_PROF:
 137  			if (j)
      				j++;
      			current->it_prof_value = j;
      			current->it_prof_incr = i;
 141  			break;
 142  		default:
 143  			return -EINVAL;
      	}
 145  	return 0;
      }
      
      /* SMP: Again, only we play with our itimers, and signals are SMP safe
       *      now so that is not an issue at all anymore.
       */
 151  asmlinkage long sys_setitimer(int which, struct itimerval *value,
      			      struct itimerval *ovalue)
      {
      	struct itimerval set_buffer, get_buffer;
      	int error;
      
 157  	if (value) {
 158  		if(copy_from_user(&set_buffer, value, sizeof(set_buffer)))
 159  			return -EFAULT;
 160  	} else
      		memset((char *) &set_buffer, 0, sizeof(set_buffer));
      
      	error = do_setitimer(which, &set_buffer, ovalue ? &get_buffer : 0);
 164  	if (error || !ovalue)
 165  		return error;
      
 167  	if (copy_to_user(ovalue, &get_buffer, sizeof(get_buffer)))
 168  		return -EFAULT; 
 169  	return 0;
      }