[PATCH] setitimer timer expires too early
[safe/jmp/linux-2.6] / kernel / itimer.c
1 /*
2  * linux/kernel/itimer.c
3  *
4  * Copyright (C) 1992 Darren Senn
5  */
6
7 /* These are all the functions necessary to implement itimers */
8
9 #include <linux/mm.h>
10 #include <linux/smp_lock.h>
11 #include <linux/interrupt.h>
12 #include <linux/syscalls.h>
13 #include <linux/time.h>
14 #include <linux/posix-timers.h>
15
16 #include <asm/uaccess.h>
17
18 static unsigned long it_real_value(struct signal_struct *sig)
19 {
20         unsigned long val = 0;
21         if (timer_pending(&sig->real_timer)) {
22                 val = sig->real_timer.expires - jiffies;
23
24                 /* look out for negative/zero itimer.. */
25                 if ((long) val <= 0)
26                         val = 1;
27         }
28         return val;
29 }
30
31 int do_getitimer(int which, struct itimerval *value)
32 {
33         struct task_struct *tsk = current;
34         unsigned long interval, val;
35         cputime_t cinterval, cval;
36
37         switch (which) {
38         case ITIMER_REAL:
39                 spin_lock_irq(&tsk->sighand->siglock);
40                 interval = tsk->signal->it_real_incr;
41                 val = it_real_value(tsk->signal);
42                 spin_unlock_irq(&tsk->sighand->siglock);
43                 jiffies_to_timeval(val, &value->it_value);
44                 jiffies_to_timeval(interval, &value->it_interval);
45                 break;
46         case ITIMER_VIRTUAL:
47                 read_lock(&tasklist_lock);
48                 spin_lock_irq(&tsk->sighand->siglock);
49                 cval = tsk->signal->it_virt_expires;
50                 cinterval = tsk->signal->it_virt_incr;
51                 if (!cputime_eq(cval, cputime_zero)) {
52                         struct task_struct *t = tsk;
53                         cputime_t utime = tsk->signal->utime;
54                         do {
55                                 utime = cputime_add(utime, t->utime);
56                                 t = next_thread(t);
57                         } while (t != tsk);
58                         if (cputime_le(cval, utime)) { /* about to fire */
59                                 cval = jiffies_to_cputime(1);
60                         } else {
61                                 cval = cputime_sub(cval, utime);
62                         }
63                 }
64                 spin_unlock_irq(&tsk->sighand->siglock);
65                 read_unlock(&tasklist_lock);
66                 cputime_to_timeval(cval, &value->it_value);
67                 cputime_to_timeval(cinterval, &value->it_interval);
68                 break;
69         case ITIMER_PROF:
70                 read_lock(&tasklist_lock);
71                 spin_lock_irq(&tsk->sighand->siglock);
72                 cval = tsk->signal->it_prof_expires;
73                 cinterval = tsk->signal->it_prof_incr;
74                 if (!cputime_eq(cval, cputime_zero)) {
75                         struct task_struct *t = tsk;
76                         cputime_t ptime = cputime_add(tsk->signal->utime,
77                                                       tsk->signal->stime);
78                         do {
79                                 ptime = cputime_add(ptime,
80                                                     cputime_add(t->utime,
81                                                                 t->stime));
82                                 t = next_thread(t);
83                         } while (t != tsk);
84                         if (cputime_le(cval, ptime)) { /* about to fire */
85                                 cval = jiffies_to_cputime(1);
86                         } else {
87                                 cval = cputime_sub(cval, ptime);
88                         }
89                 }
90                 spin_unlock_irq(&tsk->sighand->siglock);
91                 read_unlock(&tasklist_lock);
92                 cputime_to_timeval(cval, &value->it_value);
93                 cputime_to_timeval(cinterval, &value->it_interval);
94                 break;
95         default:
96                 return(-EINVAL);
97         }
98         return 0;
99 }
100
101 asmlinkage long sys_getitimer(int which, struct itimerval __user *value)
102 {
103         int error = -EFAULT;
104         struct itimerval get_buffer;
105
106         if (value) {
107                 error = do_getitimer(which, &get_buffer);
108                 if (!error &&
109                     copy_to_user(value, &get_buffer, sizeof(get_buffer)))
110                         error = -EFAULT;
111         }
112         return error;
113 }
114
115 /*
116  * Called with P->sighand->siglock held and P->signal->real_timer inactive.
117  * If interval is nonzero, arm the timer for interval ticks from now.
118  */
119 static inline void it_real_arm(struct task_struct *p, unsigned long interval)
120 {
121         p->signal->it_real_value = interval; /* XXX unnecessary field?? */
122         if (interval == 0)
123                 return;
124         if (interval > (unsigned long) LONG_MAX)
125                 interval = LONG_MAX;
126         /* the "+ 1" below makes sure that the timer doesn't go off before
127          * the interval requested. This could happen if
128          * time requested % (usecs per jiffy) is more than the usecs left
129          * in the current jiffy */
130         p->signal->real_timer.expires = jiffies + interval + 1;
131         add_timer(&p->signal->real_timer);
132 }
133
134 void it_real_fn(unsigned long __data)
135 {
136         struct task_struct * p = (struct task_struct *) __data;
137
138         send_group_sig_info(SIGALRM, SEND_SIG_PRIV, p);
139
140         /*
141          * Now restart the timer if necessary.  We don't need any locking
142          * here because do_setitimer makes sure we have finished running
143          * before it touches anything.
144          */
145         it_real_arm(p, p->signal->it_real_incr);
146 }
147
148 int do_setitimer(int which, struct itimerval *value, struct itimerval *ovalue)
149 {
150         struct task_struct *tsk = current;
151         unsigned long val, interval;
152         cputime_t cval, cinterval, nval, ninterval;
153
154         switch (which) {
155         case ITIMER_REAL:
156                 spin_lock_irq(&tsk->sighand->siglock);
157                 interval = tsk->signal->it_real_incr;
158                 val = it_real_value(tsk->signal);
159                 if (val)
160                         del_timer_sync(&tsk->signal->real_timer);
161                 tsk->signal->it_real_incr =
162                         timeval_to_jiffies(&value->it_interval);
163                 it_real_arm(tsk, timeval_to_jiffies(&value->it_value));
164                 spin_unlock_irq(&tsk->sighand->siglock);
165                 if (ovalue) {
166                         jiffies_to_timeval(val, &ovalue->it_value);
167                         jiffies_to_timeval(interval,
168                                            &ovalue->it_interval);
169                 }
170                 break;
171         case ITIMER_VIRTUAL:
172                 nval = timeval_to_cputime(&value->it_value);
173                 ninterval = timeval_to_cputime(&value->it_interval);
174                 read_lock(&tasklist_lock);
175                 spin_lock_irq(&tsk->sighand->siglock);
176                 cval = tsk->signal->it_virt_expires;
177                 cinterval = tsk->signal->it_virt_incr;
178                 if (!cputime_eq(cval, cputime_zero) ||
179                     !cputime_eq(nval, cputime_zero)) {
180                         if (cputime_gt(nval, cputime_zero))
181                                 nval = cputime_add(nval,
182                                                    jiffies_to_cputime(1));
183                         set_process_cpu_timer(tsk, CPUCLOCK_VIRT,
184                                               &nval, &cval);
185                 }
186                 tsk->signal->it_virt_expires = nval;
187                 tsk->signal->it_virt_incr = ninterval;
188                 spin_unlock_irq(&tsk->sighand->siglock);
189                 read_unlock(&tasklist_lock);
190                 if (ovalue) {
191                         cputime_to_timeval(cval, &ovalue->it_value);
192                         cputime_to_timeval(cinterval, &ovalue->it_interval);
193                 }
194                 break;
195         case ITIMER_PROF:
196                 nval = timeval_to_cputime(&value->it_value);
197                 ninterval = timeval_to_cputime(&value->it_interval);
198                 read_lock(&tasklist_lock);
199                 spin_lock_irq(&tsk->sighand->siglock);
200                 cval = tsk->signal->it_prof_expires;
201                 cinterval = tsk->signal->it_prof_incr;
202                 if (!cputime_eq(cval, cputime_zero) ||
203                     !cputime_eq(nval, cputime_zero)) {
204                         if (cputime_gt(nval, cputime_zero))
205                                 nval = cputime_add(nval,
206                                                    jiffies_to_cputime(1));
207                         set_process_cpu_timer(tsk, CPUCLOCK_PROF,
208                                               &nval, &cval);
209                 }
210                 tsk->signal->it_prof_expires = nval;
211                 tsk->signal->it_prof_incr = ninterval;
212                 spin_unlock_irq(&tsk->sighand->siglock);
213                 read_unlock(&tasklist_lock);
214                 if (ovalue) {
215                         cputime_to_timeval(cval, &ovalue->it_value);
216                         cputime_to_timeval(cinterval, &ovalue->it_interval);
217                 }
218                 break;
219         default:
220                 return -EINVAL;
221         }
222         return 0;
223 }
224
225 asmlinkage long sys_setitimer(int which,
226                               struct itimerval __user *value,
227                               struct itimerval __user *ovalue)
228 {
229         struct itimerval set_buffer, get_buffer;
230         int error;
231
232         if (value) {
233                 if(copy_from_user(&set_buffer, value, sizeof(set_buffer)))
234                         return -EFAULT;
235         } else
236                 memset((char *) &set_buffer, 0, sizeof(set_buffer));
237
238         error = do_setitimer(which, &set_buffer, ovalue ? &get_buffer : NULL);
239         if (error || !ovalue)
240                 return error;
241
242         if (copy_to_user(ovalue, &get_buffer, sizeof(get_buffer)))
243                 return -EFAULT; 
244         return 0;
245 }