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