Linux-2.6.12-rc2
[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         p->signal->real_timer.expires = jiffies + interval;
127         add_timer(&p->signal->real_timer);
128 }
129
130 void it_real_fn(unsigned long __data)
131 {
132         struct task_struct * p = (struct task_struct *) __data;
133
134         send_group_sig_info(SIGALRM, SEND_SIG_PRIV, p);
135
136         /*
137          * Now restart the timer if necessary.  We don't need any locking
138          * here because do_setitimer makes sure we have finished running
139          * before it touches anything.
140          */
141         it_real_arm(p, p->signal->it_real_incr);
142 }
143
144 int do_setitimer(int which, struct itimerval *value, struct itimerval *ovalue)
145 {
146         struct task_struct *tsk = current;
147         unsigned long val, interval;
148         cputime_t cval, cinterval, nval, ninterval;
149
150         switch (which) {
151         case ITIMER_REAL:
152                 spin_lock_irq(&tsk->sighand->siglock);
153                 interval = tsk->signal->it_real_incr;
154                 val = it_real_value(tsk->signal);
155                 if (val)
156                         del_timer_sync(&tsk->signal->real_timer);
157                 tsk->signal->it_real_incr =
158                         timeval_to_jiffies(&value->it_interval);
159                 it_real_arm(tsk, timeval_to_jiffies(&value->it_value));
160                 spin_unlock_irq(&tsk->sighand->siglock);
161                 if (ovalue) {
162                         jiffies_to_timeval(val, &ovalue->it_value);
163                         jiffies_to_timeval(interval,
164                                            &ovalue->it_interval);
165                 }
166                 break;
167         case ITIMER_VIRTUAL:
168                 nval = timeval_to_cputime(&value->it_value);
169                 ninterval = timeval_to_cputime(&value->it_interval);
170                 read_lock(&tasklist_lock);
171                 spin_lock_irq(&tsk->sighand->siglock);
172                 cval = tsk->signal->it_virt_expires;
173                 cinterval = tsk->signal->it_virt_incr;
174                 if (!cputime_eq(cval, cputime_zero) ||
175                     !cputime_eq(nval, cputime_zero)) {
176                         if (cputime_gt(nval, cputime_zero))
177                                 nval = cputime_add(nval,
178                                                    jiffies_to_cputime(1));
179                         set_process_cpu_timer(tsk, CPUCLOCK_VIRT,
180                                               &nval, &cval);
181                 }
182                 tsk->signal->it_virt_expires = nval;
183                 tsk->signal->it_virt_incr = ninterval;
184                 spin_unlock_irq(&tsk->sighand->siglock);
185                 read_unlock(&tasklist_lock);
186                 if (ovalue) {
187                         cputime_to_timeval(cval, &ovalue->it_value);
188                         cputime_to_timeval(cinterval, &ovalue->it_interval);
189                 }
190                 break;
191         case ITIMER_PROF:
192                 nval = timeval_to_cputime(&value->it_value);
193                 ninterval = timeval_to_cputime(&value->it_interval);
194                 read_lock(&tasklist_lock);
195                 spin_lock_irq(&tsk->sighand->siglock);
196                 cval = tsk->signal->it_prof_expires;
197                 cinterval = tsk->signal->it_prof_incr;
198                 if (!cputime_eq(cval, cputime_zero) ||
199                     !cputime_eq(nval, cputime_zero)) {
200                         if (cputime_gt(nval, cputime_zero))
201                                 nval = cputime_add(nval,
202                                                    jiffies_to_cputime(1));
203                         set_process_cpu_timer(tsk, CPUCLOCK_PROF,
204                                               &nval, &cval);
205                 }
206                 tsk->signal->it_prof_expires = nval;
207                 tsk->signal->it_prof_incr = ninterval;
208                 spin_unlock_irq(&tsk->sighand->siglock);
209                 read_unlock(&tasklist_lock);
210                 if (ovalue) {
211                         cputime_to_timeval(cval, &ovalue->it_value);
212                         cputime_to_timeval(cinterval, &ovalue->it_interval);
213                 }
214                 break;
215         default:
216                 return -EINVAL;
217         }
218         return 0;
219 }
220
221 asmlinkage long sys_setitimer(int which,
222                               struct itimerval __user *value,
223                               struct itimerval __user *ovalue)
224 {
225         struct itimerval set_buffer, get_buffer;
226         int error;
227
228         if (value) {
229                 if(copy_from_user(&set_buffer, value, sizeof(set_buffer)))
230                         return -EFAULT;
231         } else
232                 memset((char *) &set_buffer, 0, sizeof(set_buffer));
233
234         error = do_setitimer(which, &set_buffer, ovalue ? &get_buffer : NULL);
235         if (error || !ovalue)
236                 return error;
237
238         if (copy_to_user(ovalue, &get_buffer, sizeof(get_buffer)))
239                 return -EFAULT; 
240         return 0;
241 }