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