1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34#include <linux/errno.h>
35#include <linux/string.h>
36#include <linux/sched.h>
37#include <linux/sem.h>
38#include <linux/ipc.h>
39#include <linux/stat.h>
40#include <linux/malloc.h>
41#include <linux/smp.h>
42#include <linux/smp_lock.h>
43#include <linux/init.h>
44
45#include <asm/uaccess.h>
46
47extern int ipcperms (struct ipc_perm *ipcp, short semflg);
48static int newary (key_t, int, int);
49static int findkey (key_t key);
50static void freeary (int id);
51
52static struct semid_ds *semary[SEMMNI];
53static int used_sems = 0, used_semids = 0;
54static struct wait_queue *sem_lock = NULL;
55static int max_semid = 0;
56
57static unsigned short sem_seq = 0;
58
59__initfunc(void sem_init (void))
60{
61 int i;
62
63 sem_lock = NULL;
64 used_sems = used_semids = max_semid = sem_seq = 0;
65 for (i = 0; i < SEMMNI; i++)
66 semary[i] = (struct semid_ds *) IPC_UNUSED;
67 return;
68}
69
70static int findkey (key_t key)
71{
72 int id;
73 struct semid_ds *sma;
74
75 for (id = 0; id <= max_semid; id++) {
76 while ((sma = semary[id]) == IPC_NOID)
77 interruptible_sleep_on (&sem_lock);
78 if (sma == IPC_UNUSED)
79 continue;
80 if (key == sma->sem_perm.key)
81 return id;
82 }
83 return -1;
84}
85
86static int newary (key_t key, int nsems, int semflg)
87{
88 int id;
89 struct semid_ds *sma;
90 struct ipc_perm *ipcp;
91 int size;
92
93 if (!nsems)
94 return -EINVAL;
95 if (used_sems + nsems > SEMMNS)
96 return -ENOSPC;
97 for (id = 0; id < SEMMNI; id++)
98 if (semary[id] == IPC_UNUSED) {
99 semary[id] = (struct semid_ds *) IPC_NOID;
100 goto found;
101 }
102 return -ENOSPC;
103found:
104 size = sizeof (*sma) + nsems * sizeof (struct sem);
105 used_sems += nsems;
106 sma = (struct semid_ds *) kmalloc (size, GFP_KERNEL);
107 if (!sma) {
108 semary[id] = (struct semid_ds *) IPC_UNUSED;
109 used_sems -= nsems;
110 wake_up (&sem_lock);
111 return -ENOMEM;
112 }
113 memset (sma, 0, size);
114 sma->sem_base = (struct sem *) &sma[1];
115 ipcp = &sma->sem_perm;
116 ipcp->mode = (semflg & S_IRWXUGO);
117 ipcp->key = key;
118 ipcp->cuid = ipcp->uid = current->euid;
119 ipcp->gid = ipcp->cgid = current->egid;
120 sma->sem_perm.seq = sem_seq;
121
122 sma->sem_pending_last = &sma->sem_pending;
123
124 sma->sem_nsems = nsems;
125 sma->sem_ctime = CURRENT_TIME;
126 if (id > max_semid)
127 max_semid = id;
128 used_semids++;
129 semary[id] = sma;
130 wake_up (&sem_lock);
131 return (unsigned int) sma->sem_perm.seq * SEMMNI + id;
132}
133
134asmlinkage int sys_semget (key_t key, int nsems, int semflg)
135{
136 int id, err = -EINVAL;
137 struct semid_ds *sma;
138
139 lock_kernel();
140 if (nsems < 0 || nsems > SEMMSL)
141 goto out;
142 if (key == IPC_PRIVATE) {
143 err = newary(key, nsems, semflg);
144 } else if ((id = findkey (key)) == -1) {
145 if (!(semflg & IPC_CREAT))
146 err = -ENOENT;
147 else
148 err = newary(key, nsems, semflg);
149 } else if (semflg & IPC_CREAT && semflg & IPC_EXCL) {
150 err = -EEXIST;
151 } else {
152 sma = semary[id];
153 if (nsems > sma->sem_nsems)
154 err = -EINVAL;
155 else if (ipcperms(&sma->sem_perm, semflg))
156 err = -EACCES;
157 else
158 err = (int) sma->sem_perm.seq * SEMMNI + id;
159 }
160out:
161 unlock_kernel();
162 return err;
163}
164
165
166
167
168static inline void insert_into_queue (struct semid_ds * sma, struct sem_queue * q)
169{
170 *(q->prev = sma->sem_pending_last) = q;
171 *(sma->sem_pending_last = &q->next) = NULL;
172}
173static inline void remove_from_queue (struct semid_ds * sma, struct sem_queue * q)
174{
175 *(q->prev) = q->next;
176 if (q->next)
177 q->next->prev = q->prev;
178 else
179 sma->sem_pending_last = q->prev;
180 q->prev = NULL;
181}
182
183
184
185
186static int try_semop (struct semid_ds * sma, struct sembuf * sops, int nsops)
187{
188 int result = 0;
189 int i = 0;
190
191 while (i < nsops) {
192 struct sembuf * sop = &sops[i];
193 struct sem * curr = &sma->sem_base[sop->sem_num];
194 if (sop->sem_op + curr->semval > SEMVMX) {
195 result = -ERANGE;
196 break;
197 }
198 if (!sop->sem_op && curr->semval) {
199 if (sop->sem_flg & IPC_NOWAIT)
200 result = -EAGAIN;
201 else
202 result = 1;
203 break;
204 }
205 i++;
206 curr->semval += sop->sem_op;
207 if (curr->semval < 0) {
208 if (sop->sem_flg & IPC_NOWAIT)
209 result = -EAGAIN;
210 else
211 result = 1;
212 break;
213 }
214 }
215 while (--i >= 0) {
216 struct sembuf * sop = &sops[i];
217 struct sem * curr = &sma->sem_base[sop->sem_num];
218 curr->semval -= sop->sem_op;
219 }
220 return result;
221}
222
223
224
225static int do_semop (struct semid_ds * sma, struct sembuf * sops, int nsops,
226 struct sem_undo * un, int pid)
227{
228 int i;
229
230 for (i = 0; i < nsops; i++) {
231 struct sembuf * sop = &sops[i];
232 struct sem * curr = &sma->sem_base[sop->sem_num];
233 if (sop->sem_op + curr->semval > SEMVMX) {
234 printk("do_semop: race\n");
235 break;
236 }
237 if (!sop->sem_op) {
238 if (curr->semval) {
239 printk("do_semop: race\n");
240 break;
241 }
242 } else {
243 curr->semval += sop->sem_op;
244 if (curr->semval < 0) {
245 printk("do_semop: race\n");
246 break;
247 }
248 if (sop->sem_flg & SEM_UNDO)
249 un->semadj[sop->sem_num] -= sop->sem_op;
250 }
251 curr->sempid = pid;
252 }
253 sma->sem_otime = CURRENT_TIME;
254
255
256
257
258
259 return 0;
260}
261
262
263
264
265
266static void update_queue (struct semid_ds * sma)
267{
268 int wokeup, error;
269 struct sem_queue * q;
270
271 do {
272 wokeup = 0;
273 for (q = sma->sem_pending; q; q = q->next) {
274 error = try_semop(sma, q->sops, q->nsops);
275
276 if (error > 0)
277 continue;
278
279 if (!error)
280 error = do_semop(sma, q->sops, q->nsops, q->undo, q->pid);
281 q->status = error;
282
283 remove_from_queue(sma,q);
284
285 wake_up_interruptible(&q->sleeper);
286 wokeup++;
287 }
288 } while (wokeup);
289}
290
291
292
293
294
295
296
297
298
299
300static int count_semncnt (struct semid_ds * sma, ushort semnum)
301{
302 int semncnt;
303 struct sem_queue * q;
304
305 semncnt = 0;
306 for (q = sma->sem_pending; q; q = q->next) {
307 struct sembuf * sops = q->sops;
308 int nsops = q->nsops;
309 int i;
310 for (i = 0; i < nsops; i++)
311 if (sops[i].sem_num == semnum
312 && (sops[i].sem_op < 0)
313 && !(sops[i].sem_flg & IPC_NOWAIT))
314 semncnt++;
315 }
316 return semncnt;
317}
318static int count_semzcnt (struct semid_ds * sma, ushort semnum)
319{
320 int semzcnt;
321 struct sem_queue * q;
322
323 semzcnt = 0;
324 for (q = sma->sem_pending; q; q = q->next) {
325 struct sembuf * sops = q->sops;
326 int nsops = q->nsops;
327 int i;
328 for (i = 0; i < nsops; i++)
329 if (sops[i].sem_num == semnum
330 && (sops[i].sem_op == 0)
331 && !(sops[i].sem_flg & IPC_NOWAIT))
332 semzcnt++;
333 }
334 return semzcnt;
335}
336
337
338static void freeary (int id)
339{
340 struct semid_ds *sma = semary[id];
341 struct sem_undo *un;
342 struct sem_queue *q;
343
344
345 sma->sem_perm.seq++;
346 sem_seq = (sem_seq+1) % ((unsigned)(1<<31)/SEMMNI);
347 used_sems -= sma->sem_nsems;
348 if (id == max_semid)
349 while (max_semid && (semary[--max_semid] == IPC_UNUSED));
350 semary[id] = (struct semid_ds *) IPC_UNUSED;
351 used_semids--;
352
353
354
355
356 for (un = sma->undo; un; un = un->id_next)
357 un->semid = -1;
358
359
360 for (q = sma->sem_pending; q; q = q->next) {
361 q->status = -EIDRM;
362 q->prev = NULL;
363 wake_up_interruptible(&q->sleeper);
364 }
365
366 kfree(sma);
367}
368
369asmlinkage int sys_semctl (int semid, int semnum, int cmd, union semun arg)
370{
371 struct semid_ds *buf = NULL;
372 struct semid_ds tbuf;
373 int i, id, val = 0;
374 struct semid_ds *sma;
375 struct ipc_perm *ipcp;
376 struct sem *curr = NULL;
377 struct sem_undo *un;
378 unsigned int nsems;
379 ushort *array = NULL;
380 ushort sem_io[SEMMSL];
381 int err = -EINVAL;
382
383 lock_kernel();
384 if (semid < 0 || semnum < 0 || cmd < 0)
385 goto out;
386
387 switch (cmd) {
388 case IPC_INFO:
389 case SEM_INFO:
390 {
391 struct seminfo seminfo, *tmp = arg.__buf;
392 seminfo.semmni = SEMMNI;
393 seminfo.semmns = SEMMNS;
394 seminfo.semmsl = SEMMSL;
395 seminfo.semopm = SEMOPM;
396 seminfo.semvmx = SEMVMX;
397 seminfo.semmnu = SEMMNU;
398 seminfo.semmap = SEMMAP;
399 seminfo.semume = SEMUME;
400 seminfo.semusz = SEMUSZ;
401 seminfo.semaem = SEMAEM;
402 if (cmd == SEM_INFO) {
403 seminfo.semusz = used_semids;
404 seminfo.semaem = used_sems;
405 }
406 err = verify_area(VERIFY_WRITE, tmp, sizeof(struct seminfo));
407 if (err)
408 goto out;
409 copy_to_user (tmp, &seminfo, sizeof(struct seminfo));
410 err = max_semid;
411 goto out;
412 }
413
414 case SEM_STAT:
415 buf = arg.buf;
416 err = verify_area (VERIFY_WRITE, buf, sizeof (*buf));
417 if (err)
418 goto out;
419 err = -EINVAL;
420 if (semid > max_semid)
421 goto out;
422 sma = semary[semid];
423 if (sma == IPC_UNUSED || sma == IPC_NOID)
424 goto out;
425 err = -EACCES;
426 if (ipcperms (&sma->sem_perm, S_IRUGO))
427 goto out;
428 id = (unsigned int) sma->sem_perm.seq * SEMMNI + semid;
429 tbuf.sem_perm = sma->sem_perm;
430 tbuf.sem_otime = sma->sem_otime;
431 tbuf.sem_ctime = sma->sem_ctime;
432 tbuf.sem_nsems = sma->sem_nsems;
433 copy_to_user (buf, &tbuf, sizeof(*buf));
434 err = id;
435 goto out;
436 }
437
438 id = (unsigned int) semid % SEMMNI;
439 sma = semary [id];
440 err = -EINVAL;
441 if (sma == IPC_UNUSED || sma == IPC_NOID)
442 goto out;
443 ipcp = &sma->sem_perm;
444 nsems = sma->sem_nsems;
445 err = -EIDRM;
446 if (sma->sem_perm.seq != (unsigned int) semid / SEMMNI)
447 goto out;
448
449 switch (cmd) {
450 case GETVAL:
451 case GETPID:
452 case GETNCNT:
453 case GETZCNT:
454 case SETVAL:
455 err = -EINVAL;
456 if (semnum >= nsems)
457 goto out;
458 curr = &sma->sem_base[semnum];
459 break;
460 }
461
462 switch (cmd) {
463 case GETVAL:
464 case GETPID:
465 case GETNCNT:
466 case GETZCNT:
467 case GETALL:
468 err = -EACCES;
469 if (ipcperms (ipcp, S_IRUGO))
470 goto out;
471 switch (cmd) {
472 case GETVAL : return curr->semval;
473 case GETPID : return curr->sempid;
474 case GETNCNT: return count_semncnt(sma,semnum);
475 case GETZCNT: return count_semzcnt(sma,semnum);
476 case GETALL:
477 array = arg.array;
478 err = verify_area (VERIFY_WRITE, array, nsems*sizeof(ushort));
479 if (err)
480 goto out;
481 }
482 break;
483 case SETVAL:
484 val = arg.val;
485 err = -ERANGE;
486 if (val > SEMVMX || val < 0)
487 goto out;
488 break;
489 case IPC_RMID:
490 if (suser() || current->euid == ipcp->cuid || current->euid == ipcp->uid) {
491 freeary (id);
492 err = 0;
493 goto out;
494 }
495 err = -EPERM;
496 goto out;
497 case SETALL:
498 array = arg.array;
499 if ((err = verify_area (VERIFY_READ, array, nsems*sizeof(ushort))))
500 goto out;
501 copy_from_user (sem_io, array, nsems*sizeof(ushort));
502 for (i = 0; i < nsems; i++)
503 if (sem_io[i] > SEMVMX) {
504 err = -ERANGE;
505 goto out;
506 }
507 break;
508 case IPC_STAT:
509 buf = arg.buf;
510 if ((err = verify_area (VERIFY_WRITE, buf, sizeof(*buf))))
511 goto out;
512 break;
513 case IPC_SET:
514 buf = arg.buf;
515 if ((err = verify_area (VERIFY_READ, buf, sizeof (*buf))))
516 goto out;
517 copy_from_user (&tbuf, buf, sizeof (*buf));
518 break;
519 }
520
521 err = -EIDRM;
522 if (semary[id] == IPC_UNUSED || semary[id] == IPC_NOID)
523 goto out;
524 if (sma->sem_perm.seq != (unsigned int) semid / SEMMNI)
525 goto out;
526
527 switch (cmd) {
528 case GETALL:
529 err = -EACCES;
530 if (ipcperms (ipcp, S_IRUGO))
531 goto out;
532 for (i = 0; i < sma->sem_nsems; i++)
533 sem_io[i] = sma->sem_base[i].semval;
534 copy_to_user (array, sem_io, nsems*sizeof(ushort));
535 break;
536 case SETVAL:
537 err = -EACCES;
538 if (ipcperms (ipcp, S_IWUGO))
539 goto out;
540 for (un = sma->undo; un; un = un->id_next)
541 un->semadj[semnum] = 0;
542 curr->semval = val;
543 sma->sem_ctime = CURRENT_TIME;
544
545 update_queue(sma);
546 break;
547 case IPC_SET:
548 if (suser() || current->euid == ipcp->cuid || current->euid == ipcp->uid) {
549 ipcp->uid = tbuf.sem_perm.uid;
550 ipcp->gid = tbuf.sem_perm.gid;
551 ipcp->mode = (ipcp->mode & ~S_IRWXUGO)
552 | (tbuf.sem_perm.mode & S_IRWXUGO);
553 sma->sem_ctime = CURRENT_TIME;
554 err = 0;
555 goto out;
556 }
557 err = -EPERM;
558 goto out;
559 case IPC_STAT:
560 err = -EACCES;
561 if (ipcperms (ipcp, S_IRUGO))
562 goto out;
563 tbuf.sem_perm = sma->sem_perm;
564 tbuf.sem_otime = sma->sem_otime;
565 tbuf.sem_ctime = sma->sem_ctime;
566 tbuf.sem_nsems = sma->sem_nsems;
567 copy_to_user (buf, &tbuf, sizeof(*buf));
568 break;
569 case SETALL:
570 err = -EACCES;
571 if (ipcperms (ipcp, S_IWUGO))
572 goto out;
573 for (i = 0; i < nsems; i++)
574 sma->sem_base[i].semval = sem_io[i];
575 for (un = sma->undo; un; un = un->id_next)
576 for (i = 0; i < nsems; i++)
577 un->semadj[i] = 0;
578 sma->sem_ctime = CURRENT_TIME;
579
580 update_queue(sma);
581 break;
582 default:
583 err = -EINVAL;
584 goto out;
585 }
586 err = 0;
587out:
588 unlock_kernel();
589 return err;
590}
591
592asmlinkage int sys_semop (int semid, struct sembuf *tsops, unsigned nsops)
593{
594 int i, id, size, error = -EINVAL;
595 struct semid_ds *sma;
596 struct sembuf sops[SEMOPM], *sop;
597 struct sem_undo *un;
598 int undos = 0, alter = 0;
599
600 lock_kernel();
601 if (nsops < 1 || semid < 0)
602 goto out;
603 error = -E2BIG;
604 if (nsops > SEMOPM)
605 goto out;
606 error = -EFAULT;
607 if (!tsops)
608 goto out;
609 if ((i = verify_area (VERIFY_READ, tsops, nsops * sizeof(*tsops))))
610 goto out;
611 copy_from_user (sops, tsops, nsops * sizeof(*tsops));
612 id = (unsigned int) semid % SEMMNI;
613 error = -EINVAL;
614 if ((sma = semary[id]) == IPC_UNUSED || sma == IPC_NOID)
615 goto out;
616 error = -EIDRM;
617 if (sma->sem_perm.seq != (unsigned int) semid / SEMMNI)
618 goto out;
619 for (i = 0; i < nsops; i++) {
620 sop = &sops[i];
621 error = -EFBIG;
622 if (sop->sem_num >= sma->sem_nsems)
623 goto out;
624 if (sop->sem_flg & SEM_UNDO)
625 undos++;
626 if (sop->sem_op)
627 alter++;
628 }
629 error = -EACCES;
630 if (ipcperms(&sma->sem_perm, alter ? S_IWUGO : S_IRUGO))
631 goto out;
632 error = try_semop(sma, sops, nsops);
633 if (error < 0)
634 goto out;
635 if (undos) {
636
637
638
639 for (un = current->semundo; un; un = un->proc_next)
640 if (un->semid == semid)
641 break;
642 if (!un) {
643 size = sizeof(struct sem_undo) + sizeof(short)*sma->sem_nsems;
644 un = (struct sem_undo *) kmalloc(size, GFP_ATOMIC);
645 if (!un) {
646 error = -ENOMEM;
647 goto out;
648 }
649 memset(un, 0, size);
650 un->semadj = (short *) &un[1];
651 un->semid = semid;
652 un->proc_next = current->semundo;
653 current->semundo = un;
654 un->id_next = sma->undo;
655 sma->undo = un;
656 }
657 } else
658 un = NULL;
659 if (error == 0) {
660
661 error = do_semop(sma, sops, nsops, un, current->pid);
662
663 update_queue(sma);
664 goto out;
665 } else {
666
667
668
669 struct sem_queue queue;
670
671 queue.sma = sma;
672 queue.sops = sops;
673 queue.nsops = nsops;
674 queue.undo = un;
675 queue.pid = current->pid;
676 queue.status = 0;
677 insert_into_queue(sma,&queue);
678 queue.sleeper = NULL;
679 current->semsleeping = &queue;
680 interruptible_sleep_on(&queue.sleeper);
681 current->semsleeping = NULL;
682
683
684
685 if (!queue.prev) {
686
687 error = queue.status;
688 } else {
689 remove_from_queue(sma,&queue);
690 error = -EINTR;
691 }
692 }
693out:
694 unlock_kernel();
695 return error;
696}
697
698
699
700
701
702
703
704
705
706
707
708
709
710void sem_exit (void)
711{
712 struct sem_queue *q;
713 struct sem_undo *u, *un = NULL, **up, **unp;
714 struct semid_ds *sma;
715 int nsems, i;
716
717
718
719
720 if ((q = current->semsleeping)) {
721 if (q->prev)
722 remove_from_queue(q->sma,q);
723 current->semsleeping = NULL;
724 }
725
726 for (up = ¤t->semundo; (u = *up); *up = u->proc_next, kfree(u)) {
727 if (u->semid == -1)
728 continue;
729 sma = semary[(unsigned int) u->semid % SEMMNI];
730 if (sma == IPC_UNUSED || sma == IPC_NOID)
731 continue;
732 if (sma->sem_perm.seq != (unsigned int) u->semid / SEMMNI)
733 continue;
734
735 for (unp = &sma->undo; (un = *unp); unp = &un->id_next) {
736 if (u == un)
737 goto found;
738 }
739 printk ("sem_exit undo list error id=%d\n", u->semid);
740 break;
741found:
742 *unp = un->id_next;
743
744 nsems = sma->sem_nsems;
745 for (i = 0; i < nsems; i++) {
746 struct sem * sem = &sma->sem_base[i];
747 sem->semval += u->semadj[i];
748 if (sem->semval < 0)
749 sem->semval = 0;
750 sem->sempid = current->pid;
751 }
752 sma->sem_otime = CURRENT_TIME;
753
754 update_queue(sma);
755 }
756 current->semundo = NULL;
757}
758