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