xref: /minix/minix/servers/pm/alarm.c (revision 433d6423)
1 /* This file deals with the alarm clock related system calls, eventually
2  * passing off the work to the functions in timers.c and check_sig() in
3  * signal.c to pass an alarm signal to a process.
4  *
5  * The entry points into this file are:
6  *   do_itimer: perform the ITIMER system call
7  *   set_alarm: tell the timer interface to start or stop a process timer
8  *   check_vtimer: check if one of the virtual timers needs to be restarted
9  */
10 
11 #include "pm.h"
12 #include <signal.h>
13 #include <sys/time.h>
14 #include <minix/com.h>
15 #include <minix/callnr.h>
16 #include "mproc.h"
17 
18 #define US 1000000UL	/* shortcut for microseconds per second */
19 
20 static clock_t ticks_from_timeval(struct timeval *tv);
21 static void timeval_from_ticks(struct timeval *tv, clock_t ticks);
22 static int is_sane_timeval(struct timeval *tv);
23 static void getset_vtimer(struct mproc *mp, int nwhich, struct
24 	itimerval *value, struct itimerval *ovalue);
25 static void get_realtimer(struct mproc *mp, struct itimerval *value);
26 static void set_realtimer(struct mproc *mp, struct itimerval *value);
27 static void cause_sigalrm(minix_timer_t *tp);
28 
29 /*===========================================================================*
30  *				ticks_from_timeval			     *
31  *===========================================================================*/
32 static clock_t ticks_from_timeval(tv)
33 struct timeval *tv;
34 {
35   clock_t ticks;
36 
37   /* Large delays cause a lot of problems.  First, the alarm system call
38    * takes an unsigned seconds count and the library has cast it to an int.
39    * That probably works, but on return the library will convert "negative"
40    * unsigneds to errors.  Presumably no one checks for these errors, so
41    * force this call through.  Second, If unsigned and long have the same
42    * size, converting from seconds to ticks can easily overflow.  Finally,
43    * the kernel has similar overflow bugs adding ticks.
44    *
45    * Fixing this requires a lot of ugly casts to fit the wrong interface
46    * types and to avoid overflow traps.  ALRM_EXP_TIME has the right type
47    * (clock_t) although it is declared as long.  How can variables like
48    * this be declared properly without combinatorial explosion of message
49    * types?
50    */
51 
52   /* In any case, the following conversion must always round up. */
53 
54   ticks = system_hz * (unsigned long) tv->tv_sec;
55   if ( (ticks / system_hz) != (unsigned long)tv->tv_sec) {
56 	ticks = LONG_MAX;
57   } else {
58 	ticks += ((system_hz * (unsigned long)tv->tv_usec + (US-1)) / US);
59   }
60 
61   if (ticks > LONG_MAX) ticks = LONG_MAX;
62 
63   return(ticks);
64 }
65 
66 /*===========================================================================*
67  *				timeval_from_ticks			     *
68  *===========================================================================*/
69 static void timeval_from_ticks(tv, ticks)
70 struct timeval *tv;
71 clock_t ticks;
72 {
73   tv->tv_sec = (long) (ticks / system_hz);
74   tv->tv_usec = (long) ((ticks % system_hz) * US / system_hz);
75 }
76 
77 /*===========================================================================*
78  *				is_sane_timeval				     *
79  *===========================================================================*/
80 static int is_sane_timeval(tv)
81 struct timeval *tv;
82 {
83   /* This imposes a reasonable time value range for setitimer. */
84   return (tv->tv_sec >= 0 && tv->tv_sec <= MAX_SECS &&
85  	  tv->tv_usec >= 0 && tv->tv_usec < US);
86 }
87 
88 /*===========================================================================*
89  *				do_itimer				     *
90  *===========================================================================*/
91 int do_itimer()
92 {
93   struct itimerval ovalue, value;	/* old and new interval timers */
94   int setval, getval;			/* set and/or retrieve the values? */
95   int r, which;
96 
97   /* Make sure 'which' is one of the defined timers. */
98   which = m_in.m_lc_pm_itimer.which;
99   if (which < 0 || which >= NR_ITIMERS) return(EINVAL);
100 
101   /* Determine whether to set and/or return the given timer value, based on
102    * which of the value and ovalue parameters are nonzero. At least one of
103    * them must be nonzero.
104    */
105   setval = (m_in.m_lc_pm_itimer.value != 0);
106   getval = (m_in.m_lc_pm_itimer.ovalue != 0);
107 
108   if (!setval && !getval) return(EINVAL);
109 
110   /* If we're setting a new value, copy the new timer from user space.
111    * Also, make sure its fields have sane values.
112    */
113   if (setval) {
114 	r = sys_datacopy(who_e, m_in.m_lc_pm_itimer.value,
115 		PM_PROC_NR, (vir_bytes)&value, (phys_bytes)sizeof(value));
116   	if (r != OK) return(r);
117 
118   	if (!is_sane_timeval(&value.it_value) ||
119   	    !is_sane_timeval(&value.it_interval))
120   		return(EINVAL);
121   }
122 
123   switch (which) {
124   	case ITIMER_REAL :
125   		if (getval) get_realtimer(mp, &ovalue);
126 
127   		if (setval) set_realtimer(mp, &value);
128 
129   		r = OK;
130   		break;
131 
132   	case ITIMER_VIRTUAL :
133   	case ITIMER_PROF :
134 		getset_vtimer(mp, which, (setval) ? &value : NULL,
135 			(getval) ? &ovalue : NULL);
136 
137   		r = OK;
138   		break;
139 
140 	default:
141 		panic("invalid timer type: %d", which);
142   }
143 
144   /* If requested, copy the old interval timer to user space. */
145   if (r == OK && getval) {
146 	r = sys_datacopy(PM_PROC_NR, (vir_bytes)&ovalue,
147 		who_e, m_in.m_lc_pm_itimer.ovalue,
148 		(phys_bytes)sizeof(ovalue));
149   }
150 
151   return(r);
152 }
153 
154 /*===========================================================================*
155  *				getset_vtimer				     *
156  *===========================================================================*/
157 static void getset_vtimer(rmp, which, value, ovalue)
158 struct mproc *rmp;
159 int which;
160 struct itimerval *value;
161 struct itimerval *ovalue;
162 {
163   clock_t newticks, *nptr;		/* the new timer value, in ticks */
164   clock_t oldticks, *optr;		/* the old ticks value, in ticks */
165   int r, num;
166 
167   /* The default is to provide sys_vtimer with two null pointers, i.e. to do
168    * nothing at all.
169    */
170   optr = nptr = NULL;
171 
172   /* If the old timer value is to be retrieved, have 'optr' point to the
173    * location where the old value is to be stored, and copy the interval.
174    */
175   if (ovalue != NULL) {
176   	optr = &oldticks;
177 
178   	timeval_from_ticks(&ovalue->it_interval, rmp->mp_interval[which]);
179   }
180 
181   /* If a new timer value is to be set, store the new timer value and have
182    * 'nptr' point to it. Also, store the new interval.
183    */
184   if (value != NULL) {
185   	newticks = ticks_from_timeval(&value->it_value);
186   	nptr = &newticks;
187 
188   	/* If no timer is set, the interval must be zero. */
189   	if (newticks <= 0)
190   		rmp->mp_interval[which] = 0;
191 	else
192 		rmp->mp_interval[which] =
193 			ticks_from_timeval(&value->it_interval);
194   }
195 
196   /* Find out which kernel timer number to use. */
197   switch (which) {
198   case ITIMER_VIRTUAL: num = VT_VIRTUAL; break;
199   case ITIMER_PROF:    num = VT_PROF;    break;
200   default:             panic("invalid vtimer type: %d", which);
201   }
202 
203   /* Make the kernel call. If requested, also retrieve and store
204    * the old timer value.
205    */
206   if ((r = sys_vtimer(rmp->mp_endpoint, num, nptr, optr)) != OK)
207   	panic("sys_vtimer failed: %d", r);
208 
209   if (ovalue != NULL) {
210   	/* If the alarm expired already, we should take into account the
211   	 * interval. Return zero only if the interval is zero as well.
212   	 */
213   	if (oldticks <= 0) oldticks = rmp->mp_interval[which];
214 
215 	timeval_from_ticks(&ovalue->it_value, oldticks);
216   }
217 }
218 
219 /*===========================================================================*
220  *				check_vtimer				     *
221  *===========================================================================*/
222 void check_vtimer(proc_nr, sig)
223 int proc_nr;
224 int sig;
225 {
226   register struct mproc *rmp;
227   int which, num;
228 
229   rmp = &mproc[proc_nr];
230 
231   /* Translate back the given signal to a timer type and kernel number. */
232   switch (sig) {
233   case SIGVTALRM: which = ITIMER_VIRTUAL; num = VT_VIRTUAL; break;
234   case SIGPROF:   which = ITIMER_PROF;    num = VT_PROF;    break;
235   default: panic("invalid vtimer signal: %d", sig);
236   }
237 
238   /* If a repetition interval was set for this virtual timer, tell the
239    * kernel to set a new timeout for the virtual timer.
240    */
241   if (rmp->mp_interval[which] > 0)
242   	sys_vtimer(rmp->mp_endpoint, num, &rmp->mp_interval[which], NULL);
243 }
244 
245 /*===========================================================================*
246  *				get_realtimer				     *
247  *===========================================================================*/
248 static void get_realtimer(rmp, value)
249 struct mproc *rmp;
250 struct itimerval *value;
251 {
252   clock_t exptime;	/* time at which alarm will expire */
253   clock_t uptime;	/* current system time */
254   clock_t remaining;	/* time left on alarm */
255   int s;
256 
257   /* First determine remaining time, in ticks, of previous alarm, if set. */
258   if (rmp->mp_flags & ALARM_ON) {
259   	if ( (s = getticks(&uptime)) != OK)
260   		panic("get_realtimer couldn't get uptime: %d", s);
261   	exptime = *tmr_exp_time(&rmp->mp_timer);
262 
263   	remaining = exptime - uptime;
264 
265   	/* If the alarm expired already, we should take into account the
266   	 * interval. Return zero only if the interval is zero as well.
267   	 */
268   	if (remaining <= 0) remaining = rmp->mp_interval[ITIMER_REAL];
269   } else {
270   	remaining = 0;
271   }
272 
273   /* Convert the result to a timeval structure. */
274   timeval_from_ticks(&value->it_value, remaining);
275 
276   /* Similarly convert and store the interval of the timer. */
277   timeval_from_ticks(&value->it_interval, rmp->mp_interval[ITIMER_REAL]);
278 }
279 
280 /*===========================================================================*
281  *				set_realtimer				     *
282  *===========================================================================*/
283 static void set_realtimer(rmp, value)
284 struct mproc *rmp;
285 struct itimerval *value;
286 {
287   clock_t ticks;	/* New amount of ticks to the next alarm. */
288   clock_t interval;	/* New amount of ticks for the alarm's interval. */
289 
290   /* Convert the timeval structures in the 'value' structure to ticks. */
291   ticks = ticks_from_timeval(&value->it_value);
292   interval = ticks_from_timeval(&value->it_interval);
293 
294   /* If no timer is set, the interval must be zero. */
295   if (ticks <= 0) interval = 0;
296 
297   /* Apply these values. */
298   set_alarm(rmp, ticks);
299   rmp->mp_interval[ITIMER_REAL] = interval;
300 }
301 
302 /*===========================================================================*
303  *				set_alarm				     *
304  *===========================================================================*/
305 void set_alarm(rmp, ticks)
306 struct mproc *rmp;		/* process that wants the alarm */
307 clock_t ticks;			/* how many ticks delay before the signal */
308 {
309   if (ticks > 0) {
310   	set_timer(&rmp->mp_timer, ticks, cause_sigalrm, rmp->mp_endpoint);
311 	rmp->mp_flags |=  ALARM_ON;
312   } else if (rmp->mp_flags & ALARM_ON) {
313   	cancel_timer(&rmp->mp_timer);
314   	rmp->mp_flags &= ~ALARM_ON;
315   }
316 }
317 
318 /*===========================================================================*
319  *				cause_sigalrm				     *
320  *===========================================================================*/
321 static void cause_sigalrm(tp)
322 minix_timer_t *tp;
323 {
324   int proc_nr_n;
325   register struct mproc *rmp;
326 
327   /* get process from timer */
328   if(pm_isokendpt(tmr_arg(tp)->ta_int, &proc_nr_n) != OK) {
329   	printf("PM: ignoring timer for invalid endpoint %d\n",
330   		tmr_arg(tp)->ta_int);
331   	return;
332   }
333 
334   rmp = &mproc[proc_nr_n];
335 
336   if ((rmp->mp_flags & (IN_USE | EXITING)) != IN_USE) return;
337   if ((rmp->mp_flags & ALARM_ON) == 0) return;
338 
339   /* If an interval is set, set a new timer; otherwise clear the ALARM_ON flag.
340    * The set_alarm call will be calling set_timer from within this callback
341    * from the expire_timers function. This is safe, but we must not use the
342    * "tp" structure below this point anymore.
343    */
344   if (rmp->mp_interval[ITIMER_REAL] > 0)
345 	set_alarm(rmp, rmp->mp_interval[ITIMER_REAL]);
346   else rmp->mp_flags &= ~ALARM_ON;
347 
348   check_sig(rmp->mp_pid, SIGALRM, FALSE /* ksig */);
349 }
350