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