make oldconfig will rebuild these...
[linux-2.4.21-pre4.git] / ipc / sem.c
1 /*
2  * linux/ipc/sem.c
3  * Copyright (C) 1992 Krishna Balasubramanian
4  * Copyright (C) 1995 Eric Schenk, Bruno Haible
5  *
6  * IMPLEMENTATION NOTES ON CODE REWRITE (Eric Schenk, January 1995):
7  * This code underwent a massive rewrite in order to solve some problems
8  * with the original code. In particular the original code failed to
9  * wake up processes that were waiting for semval to go to 0 if the
10  * value went to 0 and was then incremented rapidly enough. In solving
11  * this problem I have also modified the implementation so that it
12  * processes pending operations in a FIFO manner, thus give a guarantee
13  * that processes waiting for a lock on the semaphore won't starve
14  * unless another locking process fails to unlock.
15  * In addition the following two changes in behavior have been introduced:
16  * - The original implementation of semop returned the value
17  *   last semaphore element examined on success. This does not
18  *   match the manual page specifications, and effectively
19  *   allows the user to read the semaphore even if they do not
20  *   have read permissions. The implementation now returns 0
21  *   on success as stated in the manual page.
22  * - There is some confusion over whether the set of undo adjustments
23  *   to be performed at exit should be done in an atomic manner.
24  *   That is, if we are attempting to decrement the semval should we queue
25  *   up and wait until we can do so legally?
26  *   The original implementation attempted to do this.
27  *   The current implementation does not do so. This is because I don't
28  *   think it is the right thing (TM) to do, and because I couldn't
29  *   see a clean way to get the old behavior with the new design.
30  *   The POSIX standard and SVID should be consulted to determine
31  *   what behavior is mandated.
32  *
33  * Further notes on refinement (Christoph Rohland, December 1998):
34  * - The POSIX standard says, that the undo adjustments simply should
35  *   redo. So the current implementation is o.K.
36  * - The previous code had two flaws:
37  *   1) It actively gave the semaphore to the next waiting process
38  *      sleeping on the semaphore. Since this process did not have the
39  *      cpu this led to many unnecessary context switches and bad
40  *      performance. Now we only check which process should be able to
41  *      get the semaphore and if this process wants to reduce some
42  *      semaphore value we simply wake it up without doing the
43  *      operation. So it has to try to get it later. Thus e.g. the
44  *      running process may reacquire the semaphore during the current
45  *      time slice. If it only waits for zero or increases the semaphore,
46  *      we do the operation in advance and wake it up.
47  *   2) It did not wake up all zero waiting processes. We try to do
48  *      better but only get the semops right which only wait for zero or
49  *      increase. If there are decrement operations in the operations
50  *      array we do the same as before.
51  *
52  * /proc/sysvipc/sem support (c) 1999 Dragos Acostachioaie <dragos@iname.com>
53  *
54  * SMP-threaded, sysctl's added
55  * (c) 1999 Manfred Spraul <manfreds@colorfullife.com>
56  * Enforced range limit on SEM_UNDO
57  * (c) 2001 Red Hat Inc <alan@redhat.com>
58  */
59
60 #include <linux/config.h>
61 #include <linux/slab.h>
62 #include <linux/spinlock.h>
63 #include <linux/init.h>
64 #include <linux/proc_fs.h>
65 #include <asm/uaccess.h>
66 #include "util.h"
67
68
69 #define sem_lock(id)    ((struct sem_array*)ipc_lock(&sem_ids,id))
70 #define sem_unlock(id)  ipc_unlock(&sem_ids,id)
71 #define sem_rmid(id)    ((struct sem_array*)ipc_rmid(&sem_ids,id))
72 #define sem_checkid(sma, semid) \
73         ipc_checkid(&sem_ids,&sma->sem_perm,semid)
74 #define sem_buildid(id, seq) \
75         ipc_buildid(&sem_ids, id, seq)
76 static struct ipc_ids sem_ids;
77
78 static int newary (key_t, int, int);
79 static void freeary (int id);
80 #ifdef CONFIG_PROC_FS
81 static int sysvipc_sem_read_proc(char *buffer, char **start, off_t offset, int length, int *eof, void *data);
82 #endif
83
84 #define SEMMSL_FAST     256 /* 512 bytes on stack */
85 #define SEMOPM_FAST     64  /* ~ 372 bytes on stack */
86
87 /*
88  * linked list protection:
89  *      sem_undo.id_next,
90  *      sem_array.sem_pending{,last},
91  *      sem_array.sem_undo: sem_lock() for read/write
92  *      sem_undo.proc_next: only "current" is allowed to read/write that field.
93  *      
94  */
95
96 int sem_ctls[4] = {SEMMSL, SEMMNS, SEMOPM, SEMMNI};
97 #define sc_semmsl       (sem_ctls[0])
98 #define sc_semmns       (sem_ctls[1])
99 #define sc_semopm       (sem_ctls[2])
100 #define sc_semmni       (sem_ctls[3])
101
102 static int used_sems;
103
104 void __init sem_init (void)
105 {
106         used_sems = 0;
107         ipc_init_ids(&sem_ids,sc_semmni);
108
109 #ifdef CONFIG_PROC_FS
110         create_proc_read_entry("sysvipc/sem", 0, 0, sysvipc_sem_read_proc, NULL);
111 #endif
112 }
113
114 static int newary (key_t key, int nsems, int semflg)
115 {
116         int id;
117         struct sem_array *sma;
118         int size;
119
120         if (!nsems)
121                 return -EINVAL;
122         if (used_sems + nsems > sc_semmns)
123                 return -ENOSPC;
124
125         size = sizeof (*sma) + nsems * sizeof (struct sem);
126         sma = (struct sem_array *) ipc_alloc(size);
127         if (!sma) {
128                 return -ENOMEM;
129         }
130         memset (sma, 0, size);
131         id = ipc_addid(&sem_ids, &sma->sem_perm, sc_semmni);
132         if(id == -1) {
133                 ipc_free(sma, size);
134                 return -ENOSPC;
135         }
136         used_sems += nsems;
137
138         sma->sem_perm.mode = (semflg & S_IRWXUGO);
139         sma->sem_perm.key = key;
140
141         sma->sem_base = (struct sem *) &sma[1];
142         /* sma->sem_pending = NULL; */
143         sma->sem_pending_last = &sma->sem_pending;
144         /* sma->undo = NULL; */
145         sma->sem_nsems = nsems;
146         sma->sem_ctime = CURRENT_TIME;
147         sem_unlock(id);
148
149         return sem_buildid(id, sma->sem_perm.seq);
150 }
151
152 asmlinkage long sys_semget (key_t key, int nsems, int semflg)
153 {
154         int id, err = -EINVAL;
155         struct sem_array *sma;
156
157         if (nsems < 0 || nsems > sc_semmsl)
158                 return -EINVAL;
159         down(&sem_ids.sem);
160         
161         if (key == IPC_PRIVATE) {
162                 err = newary(key, nsems, semflg);
163         } else if ((id = ipc_findkey(&sem_ids, key)) == -1) {  /* key not used */
164                 if (!(semflg & IPC_CREAT))
165                         err = -ENOENT;
166                 else
167                         err = newary(key, nsems, semflg);
168         } else if (semflg & IPC_CREAT && semflg & IPC_EXCL) {
169                 err = -EEXIST;
170         } else {
171                 sma = sem_lock(id);
172                 if(sma==NULL)
173                         BUG();
174                 if (nsems > sma->sem_nsems)
175                         err = -EINVAL;
176                 else if (ipcperms(&sma->sem_perm, semflg))
177                         err = -EACCES;
178                 else
179                         err = sem_buildid(id, sma->sem_perm.seq);
180                 sem_unlock(id);
181         }
182
183         up(&sem_ids.sem);
184         return err;
185 }
186
187 /* doesn't acquire the sem_lock on error! */
188 static int sem_revalidate(int semid, struct sem_array* sma, int nsems, short flg)
189 {
190         struct sem_array* smanew;
191
192         smanew = sem_lock(semid);
193         if(smanew==NULL)
194                 return -EIDRM;
195         if(smanew != sma || sem_checkid(sma,semid) || sma->sem_nsems != nsems) {
196                 sem_unlock(semid);
197                 return -EIDRM;
198         }
199
200         if (ipcperms(&sma->sem_perm, flg)) {
201                 sem_unlock(semid);
202                 return -EACCES;
203         }
204         return 0;
205 }
206 /* Manage the doubly linked list sma->sem_pending as a FIFO:
207  * insert new queue elements at the tail sma->sem_pending_last.
208  */
209 static inline void append_to_queue (struct sem_array * sma,
210                                     struct sem_queue * q)
211 {
212         *(q->prev = sma->sem_pending_last) = q;
213         *(sma->sem_pending_last = &q->next) = NULL;
214 }
215
216 static inline void prepend_to_queue (struct sem_array * sma,
217                                      struct sem_queue * q)
218 {
219         q->next = sma->sem_pending;
220         *(q->prev = &sma->sem_pending) = q;
221         if (q->next)
222                 q->next->prev = &q->next;
223         else /* sma->sem_pending_last == &sma->sem_pending */
224                 sma->sem_pending_last = &q->next;
225 }
226
227 static inline void remove_from_queue (struct sem_array * sma,
228                                       struct sem_queue * q)
229 {
230         *(q->prev) = q->next;
231         if (q->next)
232                 q->next->prev = q->prev;
233         else /* sma->sem_pending_last == &q->next */
234                 sma->sem_pending_last = q->prev;
235         q->prev = NULL; /* mark as removed */
236 }
237
238 /*
239  * Determine whether a sequence of semaphore operations would succeed
240  * all at once. Return 0 if yes, 1 if need to sleep, else return error code.
241  */
242
243 static int try_atomic_semop (struct sem_array * sma, struct sembuf * sops,
244                              int nsops, struct sem_undo *un, int pid,
245                              int do_undo)
246 {
247         int result, sem_op;
248         struct sembuf *sop;
249         struct sem * curr;
250
251         for (sop = sops; sop < sops + nsops; sop++) {
252                 curr = sma->sem_base + sop->sem_num;
253                 sem_op = sop->sem_op;
254
255                 if (!sem_op && curr->semval)
256                         goto would_block;
257
258                 curr->sempid = (curr->sempid << 16) | pid;
259                 curr->semval += sem_op;
260                 if (sop->sem_flg & SEM_UNDO)
261                 {
262                         int undo = un->semadj[sop->sem_num] - sem_op;
263                         /*
264                          *      Exceeding the undo range is an error.
265                          */
266                         if (undo < (-SEMAEM - 1) || undo > SEMAEM)
267                         {
268                                 /* Don't undo the undo */
269                                 sop->sem_flg &= ~SEM_UNDO;
270                                 goto out_of_range;
271                         }
272                         un->semadj[sop->sem_num] = undo;
273                 }
274                 if (curr->semval < 0)
275                         goto would_block;
276                 if (curr->semval > SEMVMX)
277                         goto out_of_range;
278         }
279
280         if (do_undo)
281         {
282                 sop--;
283                 result = 0;
284                 goto undo;
285         }
286
287         sma->sem_otime = CURRENT_TIME;
288         return 0;
289
290 out_of_range:
291         result = -ERANGE;
292         goto undo;
293
294 would_block:
295         if (sop->sem_flg & IPC_NOWAIT)
296                 result = -EAGAIN;
297         else
298                 result = 1;
299
300 undo:
301         while (sop >= sops) {
302                 curr = sma->sem_base + sop->sem_num;
303                 curr->semval -= sop->sem_op;
304                 curr->sempid >>= 16;
305
306                 if (sop->sem_flg & SEM_UNDO)
307                         un->semadj[sop->sem_num] += sop->sem_op;
308                 sop--;
309         }
310
311         return result;
312 }
313
314 /* Go through the pending queue for the indicated semaphore
315  * looking for tasks that can be completed.
316  */
317 static void update_queue (struct sem_array * sma)
318 {
319         int error;
320         struct sem_queue * q;
321
322         for (q = sma->sem_pending; q; q = q->next) {
323                         
324                 if (q->status == 1)
325                         continue;       /* this one was woken up before */
326
327                 error = try_atomic_semop(sma, q->sops, q->nsops,
328                                          q->undo, q->pid, q->alter);
329
330                 /* Does q->sleeper still need to sleep? */
331                 if (error <= 0) {
332                                 /* Found one, wake it up */
333                         wake_up_process(q->sleeper);
334                         if (error == 0 && q->alter) {
335                                 /* if q-> alter let it self try */
336                                 q->status = 1;
337                                 return;
338                         }
339                         q->status = error;
340                         remove_from_queue(sma,q);
341                 }
342         }
343 }
344
345 /* The following counts are associated to each semaphore:
346  *   semncnt        number of tasks waiting on semval being nonzero
347  *   semzcnt        number of tasks waiting on semval being zero
348  * This model assumes that a task waits on exactly one semaphore.
349  * Since semaphore operations are to be performed atomically, tasks actually
350  * wait on a whole sequence of semaphores simultaneously.
351  * The counts we return here are a rough approximation, but still
352  * warrant that semncnt+semzcnt>0 if the task is on the pending queue.
353  */
354 static int count_semncnt (struct sem_array * sma, ushort semnum)
355 {
356         int semncnt;
357         struct sem_queue * q;
358
359         semncnt = 0;
360         for (q = sma->sem_pending; q; q = q->next) {
361                 struct sembuf * sops = q->sops;
362                 int nsops = q->nsops;
363                 int i;
364                 for (i = 0; i < nsops; i++)
365                         if (sops[i].sem_num == semnum
366                             && (sops[i].sem_op < 0)
367                             && !(sops[i].sem_flg & IPC_NOWAIT))
368                                 semncnt++;
369         }
370         return semncnt;
371 }
372 static int count_semzcnt (struct sem_array * sma, ushort semnum)
373 {
374         int semzcnt;
375         struct sem_queue * q;
376
377         semzcnt = 0;
378         for (q = sma->sem_pending; q; q = q->next) {
379                 struct sembuf * sops = q->sops;
380                 int nsops = q->nsops;
381                 int i;
382                 for (i = 0; i < nsops; i++)
383                         if (sops[i].sem_num == semnum
384                             && (sops[i].sem_op == 0)
385                             && !(sops[i].sem_flg & IPC_NOWAIT))
386                                 semzcnt++;
387         }
388         return semzcnt;
389 }
390
391 /* Free a semaphore set. */
392 static void freeary (int id)
393 {
394         struct sem_array *sma;
395         struct sem_undo *un;
396         struct sem_queue *q;
397         int size;
398
399         sma = sem_rmid(id);
400
401         /* Invalidate the existing undo structures for this semaphore set.
402          * (They will be freed without any further action in sem_exit()
403          * or during the next semop.)
404          */
405         for (un = sma->undo; un; un = un->id_next)
406                 un->semid = -1;
407
408         /* Wake up all pending processes and let them fail with EIDRM. */
409         for (q = sma->sem_pending; q; q = q->next) {
410                 q->status = -EIDRM;
411                 q->prev = NULL;
412                 wake_up_process(q->sleeper); /* doesn't sleep */
413         }
414         sem_unlock(id);
415
416         used_sems -= sma->sem_nsems;
417         size = sizeof (*sma) + sma->sem_nsems * sizeof (struct sem);
418         ipc_free(sma, size);
419 }
420
421 static unsigned long copy_semid_to_user(void *buf, struct semid64_ds *in, int version)
422 {
423         switch(version) {
424         case IPC_64:
425                 return copy_to_user(buf, in, sizeof(*in));
426         case IPC_OLD:
427             {
428                 struct semid_ds out;
429
430                 ipc64_perm_to_ipc_perm(&in->sem_perm, &out.sem_perm);
431
432                 out.sem_otime   = in->sem_otime;
433                 out.sem_ctime   = in->sem_ctime;
434                 out.sem_nsems   = in->sem_nsems;
435
436                 return copy_to_user(buf, &out, sizeof(out));
437             }
438         default:
439                 return -EINVAL;
440         }
441 }
442
443 static int semctl_nolock(int semid, int semnum, int cmd, int version, union semun arg)
444 {
445         int err = -EINVAL;
446
447         switch(cmd) {
448         case IPC_INFO:
449         case SEM_INFO:
450         {
451                 struct seminfo seminfo;
452                 int max_id;
453
454                 memset(&seminfo,0,sizeof(seminfo));
455                 seminfo.semmni = sc_semmni;
456                 seminfo.semmns = sc_semmns;
457                 seminfo.semmsl = sc_semmsl;
458                 seminfo.semopm = sc_semopm;
459                 seminfo.semvmx = SEMVMX;
460                 seminfo.semmnu = SEMMNU;
461                 seminfo.semmap = SEMMAP;
462                 seminfo.semume = SEMUME;
463                 down(&sem_ids.sem);
464                 if (cmd == SEM_INFO) {
465                         seminfo.semusz = sem_ids.in_use;
466                         seminfo.semaem = used_sems;
467                 } else {
468                         seminfo.semusz = SEMUSZ;
469                         seminfo.semaem = SEMAEM;
470                 }
471                 max_id = sem_ids.max_id;
472                 up(&sem_ids.sem);
473                 if (copy_to_user (arg.__buf, &seminfo, sizeof(struct seminfo))) 
474                         return -EFAULT;
475                 return (max_id < 0) ? 0: max_id;
476         }
477         case SEM_STAT:
478         {
479                 struct sem_array *sma;
480                 struct semid64_ds tbuf;
481                 int id;
482
483                 if(semid >= sem_ids.size)
484                         return -EINVAL;
485
486                 memset(&tbuf,0,sizeof(tbuf));
487
488                 sma = sem_lock(semid);
489                 if(sma == NULL)
490                         return -EINVAL;
491
492                 err = -EACCES;
493                 if (ipcperms (&sma->sem_perm, S_IRUGO))
494                         goto out_unlock;
495                 id = sem_buildid(semid, sma->sem_perm.seq);
496
497                 kernel_to_ipc64_perm(&sma->sem_perm, &tbuf.sem_perm);
498                 tbuf.sem_otime  = sma->sem_otime;
499                 tbuf.sem_ctime  = sma->sem_ctime;
500                 tbuf.sem_nsems  = sma->sem_nsems;
501                 sem_unlock(semid);
502                 if (copy_semid_to_user (arg.buf, &tbuf, version))
503                         return -EFAULT;
504                 return id;
505         }
506         default:
507                 return -EINVAL;
508         }
509         return err;
510 out_unlock:
511         sem_unlock(semid);
512         return err;
513 }
514
515 static int semctl_main(int semid, int semnum, int cmd, int version, union semun arg)
516 {
517         struct sem_array *sma;
518         struct sem* curr;
519         int err;
520         ushort fast_sem_io[SEMMSL_FAST];
521         ushort* sem_io = fast_sem_io;
522         int nsems;
523
524         sma = sem_lock(semid);
525         if(sma==NULL)
526                 return -EINVAL;
527
528         nsems = sma->sem_nsems;
529
530         err=-EIDRM;
531         if (sem_checkid(sma,semid))
532                 goto out_unlock;
533
534         err = -EACCES;
535         if (ipcperms (&sma->sem_perm, (cmd==SETVAL||cmd==SETALL)?S_IWUGO:S_IRUGO))
536                 goto out_unlock;
537
538         switch (cmd) {
539         case GETALL:
540         {
541                 ushort *array = arg.array;
542                 int i;
543
544                 if(nsems > SEMMSL_FAST) {
545                         sem_unlock(semid);                      
546                         sem_io = ipc_alloc(sizeof(ushort)*nsems);
547                         if(sem_io == NULL)
548                                 return -ENOMEM;
549                         err = sem_revalidate(semid, sma, nsems, S_IRUGO);
550                         if(err)
551                                 goto out_free;
552                 }
553
554                 for (i = 0; i < sma->sem_nsems; i++)
555                         sem_io[i] = sma->sem_base[i].semval;
556                 sem_unlock(semid);
557                 err = 0;
558                 if(copy_to_user(array, sem_io, nsems*sizeof(ushort)))
559                         err = -EFAULT;
560                 goto out_free;
561         }
562         case SETALL:
563         {
564                 int i;
565                 struct sem_undo *un;
566
567                 sem_unlock(semid);
568
569                 if(nsems > SEMMSL_FAST) {
570                         sem_io = ipc_alloc(sizeof(ushort)*nsems);
571                         if(sem_io == NULL)
572                                 return -ENOMEM;
573                 }
574
575                 if (copy_from_user (sem_io, arg.array, nsems*sizeof(ushort))) {
576                         err = -EFAULT;
577                         goto out_free;
578                 }
579
580                 for (i = 0; i < nsems; i++) {
581                         if (sem_io[i] > SEMVMX) {
582                                 err = -ERANGE;
583                                 goto out_free;
584                         }
585                 }
586                 err = sem_revalidate(semid, sma, nsems, S_IWUGO);
587                 if(err)
588                         goto out_free;
589
590                 for (i = 0; i < nsems; i++)
591                         sma->sem_base[i].semval = sem_io[i];
592                 for (un = sma->undo; un; un = un->id_next)
593                         for (i = 0; i < nsems; i++)
594                                 un->semadj[i] = 0;
595                 sma->sem_ctime = CURRENT_TIME;
596                 /* maybe some queued-up processes were waiting for this */
597                 update_queue(sma);
598                 err = 0;
599                 goto out_unlock;
600         }
601         case IPC_STAT:
602         {
603                 struct semid64_ds tbuf;
604                 memset(&tbuf,0,sizeof(tbuf));
605                 kernel_to_ipc64_perm(&sma->sem_perm, &tbuf.sem_perm);
606                 tbuf.sem_otime  = sma->sem_otime;
607                 tbuf.sem_ctime  = sma->sem_ctime;
608                 tbuf.sem_nsems  = sma->sem_nsems;
609                 sem_unlock(semid);
610                 if (copy_semid_to_user (arg.buf, &tbuf, version))
611                         return -EFAULT;
612                 return 0;
613         }
614         /* GETVAL, GETPID, GETNCTN, GETZCNT, SETVAL: fall-through */
615         }
616         err = -EINVAL;
617         if(semnum < 0 || semnum >= nsems)
618                 goto out_unlock;
619
620         curr = &sma->sem_base[semnum];
621
622         switch (cmd) {
623         case GETVAL:
624                 err = curr->semval;
625                 goto out_unlock;
626         case GETPID:
627                 err = curr->sempid & 0xffff;
628                 goto out_unlock;
629         case GETNCNT:
630                 err = count_semncnt(sma,semnum);
631                 goto out_unlock;
632         case GETZCNT:
633                 err = count_semzcnt(sma,semnum);
634                 goto out_unlock;
635         case SETVAL:
636         {
637                 int val = arg.val;
638                 struct sem_undo *un;
639                 err = -ERANGE;
640                 if (val > SEMVMX || val < 0)
641                         goto out_unlock;
642
643                 for (un = sma->undo; un; un = un->id_next)
644                         un->semadj[semnum] = 0;
645                 curr->semval = val;
646                 curr->sempid = current->pid;
647                 sma->sem_ctime = CURRENT_TIME;
648                 /* maybe some queued-up processes were waiting for this */
649                 update_queue(sma);
650                 err = 0;
651                 goto out_unlock;
652         }
653         }
654 out_unlock:
655         sem_unlock(semid);
656 out_free:
657         if(sem_io != fast_sem_io)
658                 ipc_free(sem_io, sizeof(ushort)*nsems);
659         return err;
660 }
661
662 struct sem_setbuf {
663         uid_t   uid;
664         gid_t   gid;
665         mode_t  mode;
666 };
667
668 static inline unsigned long copy_semid_from_user(struct sem_setbuf *out, void *buf, int version)
669 {
670         switch(version) {
671         case IPC_64:
672             {
673                 struct semid64_ds tbuf;
674
675                 if(copy_from_user(&tbuf, buf, sizeof(tbuf)))
676                         return -EFAULT;
677
678                 out->uid        = tbuf.sem_perm.uid;
679                 out->gid        = tbuf.sem_perm.gid;
680                 out->mode       = tbuf.sem_perm.mode;
681
682                 return 0;
683             }
684         case IPC_OLD:
685             {
686                 struct semid_ds tbuf_old;
687
688                 if(copy_from_user(&tbuf_old, buf, sizeof(tbuf_old)))
689                         return -EFAULT;
690
691                 out->uid        = tbuf_old.sem_perm.uid;
692                 out->gid        = tbuf_old.sem_perm.gid;
693                 out->mode       = tbuf_old.sem_perm.mode;
694
695                 return 0;
696             }
697         default:
698                 return -EINVAL;
699         }
700 }
701
702 static int semctl_down(int semid, int semnum, int cmd, int version, union semun arg)
703 {
704         struct sem_array *sma;
705         int err;
706         struct sem_setbuf setbuf;
707         struct kern_ipc_perm *ipcp;
708
709         if(cmd == IPC_SET) {
710                 if(copy_semid_from_user (&setbuf, arg.buf, version))
711                         return -EFAULT;
712         }
713         sma = sem_lock(semid);
714         if(sma==NULL)
715                 return -EINVAL;
716
717         if (sem_checkid(sma,semid)) {
718                 err=-EIDRM;
719                 goto out_unlock;
720         }       
721         ipcp = &sma->sem_perm;
722         
723         if (current->euid != ipcp->cuid && 
724             current->euid != ipcp->uid && !capable(CAP_SYS_ADMIN)) {
725                 err=-EPERM;
726                 goto out_unlock;
727         }
728
729         switch(cmd){
730         case IPC_RMID:
731                 freeary(semid);
732                 err = 0;
733                 break;
734         case IPC_SET:
735                 ipcp->uid = setbuf.uid;
736                 ipcp->gid = setbuf.gid;
737                 ipcp->mode = (ipcp->mode & ~S_IRWXUGO)
738                                 | (setbuf.mode & S_IRWXUGO);
739                 sma->sem_ctime = CURRENT_TIME;
740                 sem_unlock(semid);
741                 err = 0;
742                 break;
743         default:
744                 sem_unlock(semid);
745                 err = -EINVAL;
746                 break;
747         }
748         return err;
749
750 out_unlock:
751         sem_unlock(semid);
752         return err;
753 }
754
755 asmlinkage long sys_semctl (int semid, int semnum, int cmd, union semun arg)
756 {
757         int err = -EINVAL;
758         int version;
759
760         if (semid < 0)
761                 return -EINVAL;
762
763         version = ipc_parse_version(&cmd);
764
765         switch(cmd) {
766         case IPC_INFO:
767         case SEM_INFO:
768         case SEM_STAT:
769                 err = semctl_nolock(semid,semnum,cmd,version,arg);
770                 return err;
771         case GETALL:
772         case GETVAL:
773         case GETPID:
774         case GETNCNT:
775         case GETZCNT:
776         case IPC_STAT:
777         case SETVAL:
778         case SETALL:
779                 err = semctl_main(semid,semnum,cmd,version,arg);
780                 return err;
781         case IPC_RMID:
782         case IPC_SET:
783                 down(&sem_ids.sem);
784                 err = semctl_down(semid,semnum,cmd,version,arg);
785                 up(&sem_ids.sem);
786                 return err;
787         default:
788                 return -EINVAL;
789         }
790 }
791
792 static struct sem_undo* freeundos(struct sem_array *sma, struct sem_undo* un)
793 {
794         struct sem_undo* u;
795         struct sem_undo** up;
796
797         for(up = &current->semundo;(u=*up);up=&u->proc_next) {
798                 if(un==u) {
799                         un=u->proc_next;
800                         *up=un;
801                         kfree(u);
802                         return un;
803                 }
804         }
805         printk ("freeundos undo list error id=%d\n", un->semid);
806         return un->proc_next;
807 }
808
809 /* returns without sem_lock on error! */
810 static int alloc_undo(struct sem_array *sma, struct sem_undo** unp, int semid, int alter)
811 {
812         int size, nsems, error;
813         struct sem_undo *un;
814
815         nsems = sma->sem_nsems;
816         size = sizeof(struct sem_undo) + sizeof(short)*nsems;
817         sem_unlock(semid);
818
819         un = (struct sem_undo *) kmalloc(size, GFP_KERNEL);
820         if (!un)
821                 return -ENOMEM;
822
823         memset(un, 0, size);
824         error = sem_revalidate(semid, sma, nsems, alter ? S_IWUGO : S_IRUGO);
825         if(error) {
826                 kfree(un);
827                 return error;
828         }
829
830         un->semadj = (short *) &un[1];
831         un->semid = semid;
832         un->proc_next = current->semundo;
833         current->semundo = un;
834         un->id_next = sma->undo;
835         sma->undo = un;
836         *unp = un;
837         return 0;
838 }
839
840 asmlinkage long sys_semop (int semid, struct sembuf *tsops, unsigned nsops)
841 {
842         int error = -EINVAL;
843         struct sem_array *sma;
844         struct sembuf fast_sops[SEMOPM_FAST];
845         struct sembuf* sops = fast_sops, *sop;
846         struct sem_undo *un;
847         int undos = 0, decrease = 0, alter = 0;
848         struct sem_queue queue;
849
850         if (nsops < 1 || semid < 0)
851                 return -EINVAL;
852         if (nsops > sc_semopm)
853                 return -E2BIG;
854         if(nsops > SEMOPM_FAST) {
855                 sops = kmalloc(sizeof(*sops)*nsops,GFP_KERNEL);
856                 if(sops==NULL)
857                         return -ENOMEM;
858         }
859         if (copy_from_user (sops, tsops, nsops * sizeof(*tsops))) {
860                 error=-EFAULT;
861                 goto out_free;
862         }
863         sma = sem_lock(semid);
864         error=-EINVAL;
865         if(sma==NULL)
866                 goto out_free;
867         error = -EIDRM;
868         if (sem_checkid(sma,semid))
869                 goto out_unlock_free;
870         error = -EFBIG;
871         for (sop = sops; sop < sops + nsops; sop++) {
872                 if (sop->sem_num >= sma->sem_nsems)
873                         goto out_unlock_free;
874                 if (sop->sem_flg & SEM_UNDO)
875                         undos++;
876                 if (sop->sem_op < 0)
877                         decrease = 1;
878                 if (sop->sem_op > 0)
879                         alter = 1;
880         }
881         alter |= decrease;
882
883         error = -EACCES;
884         if (ipcperms(&sma->sem_perm, alter ? S_IWUGO : S_IRUGO))
885                 goto out_unlock_free;
886         if (undos) {
887                 /* Make sure we have an undo structure
888                  * for this process and this semaphore set.
889                  */
890                 un=current->semundo;
891                 while(un != NULL) {
892                         if(un->semid==semid)
893                                 break;
894                         if(un->semid==-1)
895                                 un=freeundos(sma,un);
896                          else
897                                 un=un->proc_next;
898                 }
899                 if (!un) {
900                         error = alloc_undo(sma,&un,semid,alter);
901                         if(error)
902                                 goto out_free;
903                 }
904         } else
905                 un = NULL;
906
907         error = try_atomic_semop (sma, sops, nsops, un, current->pid, 0);
908         if (error <= 0)
909                 goto update;
910
911         /* We need to sleep on this operation, so we put the current
912          * task into the pending queue and go to sleep.
913          */
914                 
915         queue.sma = sma;
916         queue.sops = sops;
917         queue.nsops = nsops;
918         queue.undo = un;
919         queue.pid = current->pid;
920         queue.alter = decrease;
921         queue.id = semid;
922         if (alter)
923                 append_to_queue(sma ,&queue);
924         else
925                 prepend_to_queue(sma ,&queue);
926         current->semsleeping = &queue;
927
928         for (;;) {
929                 struct sem_array* tmp;
930                 queue.status = -EINTR;
931                 queue.sleeper = current;
932                 current->state = TASK_INTERRUPTIBLE;
933                 sem_unlock(semid);
934
935                 schedule();
936
937                 tmp = sem_lock(semid);
938                 if(tmp==NULL) {
939                         if(queue.prev != NULL)
940                                 BUG();
941                         current->semsleeping = NULL;
942                         error = -EIDRM;
943                         goto out_free;
944                 }
945                 /*
946                  * If queue.status == 1 we where woken up and
947                  * have to retry else we simply return.
948                  * If an interrupt occurred we have to clean up the
949                  * queue
950                  *
951                  */
952                 if (queue.status == 1)
953                 {
954                         error = try_atomic_semop (sma, sops, nsops, un,
955                                                   current->pid,0);
956                         if (error <= 0) 
957                                 break;
958                 } else {
959                         error = queue.status;
960                         if (queue.prev) /* got Interrupt */
961                                 break;
962                         /* Everything done by update_queue */
963                         current->semsleeping = NULL;
964                         goto out_unlock_free;
965                 }
966         }
967         current->semsleeping = NULL;
968         remove_from_queue(sma,&queue);
969 update:
970         if (alter)
971                 update_queue (sma);
972 out_unlock_free:
973         sem_unlock(semid);
974 out_free:
975         if(sops != fast_sops)
976                 kfree(sops);
977         return error;
978 }
979
980 /*
981  * add semadj values to semaphores, free undo structures.
982  * undo structures are not freed when semaphore arrays are destroyed
983  * so some of them may be out of date.
984  * IMPLEMENTATION NOTE: There is some confusion over whether the
985  * set of adjustments that needs to be done should be done in an atomic
986  * manner or not. That is, if we are attempting to decrement the semval
987  * should we queue up and wait until we can do so legally?
988  * The original implementation attempted to do this (queue and wait).
989  * The current implementation does not do so. The POSIX standard
990  * and SVID should be consulted to determine what behavior is mandated.
991  */
992 void sem_exit (void)
993 {
994         struct sem_queue *q;
995         struct sem_undo *u, *un = NULL, **up, **unp;
996         struct sem_array *sma;
997         int nsems, i;
998
999         /* If the current process was sleeping for a semaphore,
1000          * remove it from the queue.
1001          */
1002         if ((q = current->semsleeping)) {
1003                 int semid = q->id;
1004                 sma = sem_lock(semid);
1005                 current->semsleeping = NULL;
1006
1007                 if (q->prev) {
1008                         if(sma==NULL)
1009                                 BUG();
1010                         remove_from_queue(q->sma,q);
1011                 }
1012                 if(sma!=NULL)
1013                         sem_unlock(semid);
1014         }
1015
1016         for (up = &current->semundo; (u = *up); *up = u->proc_next, kfree(u)) {
1017                 int semid = u->semid;
1018                 if(semid == -1)
1019                         continue;
1020                 sma = sem_lock(semid);
1021                 if (sma == NULL)
1022                         continue;
1023
1024                 if (u->semid == -1)
1025                         goto next_entry;
1026
1027                 if (sem_checkid(sma,u->semid))
1028                         goto next_entry;
1029
1030                 /* remove u from the sma->undo list */
1031                 for (unp = &sma->undo; (un = *unp); unp = &un->id_next) {
1032                         if (u == un)
1033                                 goto found;
1034                 }
1035                 printk ("sem_exit undo list error id=%d\n", u->semid);
1036                 goto next_entry;
1037 found:
1038                 *unp = un->id_next;
1039                 /* perform adjustments registered in u */
1040                 nsems = sma->sem_nsems;
1041                 for (i = 0; i < nsems; i++) {
1042                         struct sem * sem = &sma->sem_base[i];
1043                         sem->semval += u->semadj[i];
1044                         if (sem->semval < 0)
1045                                 sem->semval = 0; /* shouldn't happen */
1046                         sem->sempid = current->pid;
1047                 }
1048                 sma->sem_otime = CURRENT_TIME;
1049                 /* maybe some queued-up processes were waiting for this */
1050                 update_queue(sma);
1051 next_entry:
1052                 sem_unlock(semid);
1053         }
1054         current->semundo = NULL;
1055 }
1056
1057 #ifdef CONFIG_PROC_FS
1058 static int sysvipc_sem_read_proc(char *buffer, char **start, off_t offset, int length, int *eof, void *data)
1059 {
1060         off_t pos = 0;
1061         off_t begin = 0;
1062         int i, len = 0;
1063
1064         len += sprintf(buffer, "       key      semid perms      nsems   uid   gid  cuid  cgid      otime      ctime\n");
1065         down(&sem_ids.sem);
1066
1067         for(i = 0; i <= sem_ids.max_id; i++) {
1068                 struct sem_array *sma;
1069                 sma = sem_lock(i);
1070                 if(sma) {
1071                         len += sprintf(buffer + len, "%10d %10d  %4o %10lu %5u %5u %5u %5u %10lu %10lu\n",
1072                                 sma->sem_perm.key,
1073                                 sem_buildid(i,sma->sem_perm.seq),
1074                                 sma->sem_perm.mode,
1075                                 sma->sem_nsems,
1076                                 sma->sem_perm.uid,
1077                                 sma->sem_perm.gid,
1078                                 sma->sem_perm.cuid,
1079                                 sma->sem_perm.cgid,
1080                                 sma->sem_otime,
1081                                 sma->sem_ctime);
1082                         sem_unlock(i);
1083
1084                         pos += len;
1085                         if(pos < offset) {
1086                                 len = 0;
1087                                 begin = pos;
1088                         }
1089                         if(pos > offset + length)
1090                                 goto done;
1091                 }
1092         }
1093         *eof = 1;
1094 done:
1095         up(&sem_ids.sem);
1096         *start = buffer + (offset - begin);
1097         len -= (offset - begin);
1098         if(len > length)
1099                 len = length;
1100         if(len < 0)
1101                 len = 0;
1102         return len;
1103 }
1104 #endif