]> git.saurik.com Git - apple/xnu.git/blob - bsd/kern/sysv_sem.c
b499764876b02e230ba9e0addbc9e11cf591bce7
[apple/xnu.git] / bsd / kern / sysv_sem.c
1 /*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22 /*
23 * Implementation of SVID semaphores
24 *
25 * Author: Daniel Boulet
26 *
27 * This software is provided ``AS IS'' without any warranties of any kind.
28 */
29
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/sysproto.h>
33 #include <sys/kernel.h>
34 #include <sys/proc.h>
35 #include <sys/sem.h>
36 #include <sys/sysent.h>
37
38 static void seminit __P((void *));
39 SYSINIT(sysv_sem, SI_SUB_SYSV_SEM, SI_ORDER_FIRST, seminit, NULL)
40
41 #ifndef _SYS_SYSPROTO_H_
42 struct __semctl_args;
43 int __semctl __P((struct proc *p, struct __semctl_args *uap));
44 struct semget_args;
45 int semget __P((struct proc *p, struct semget_args *uap));
46 struct semop_args;
47 int semop __P((struct proc *p, struct semop_args *uap));
48 struct semconfig_args;
49 int semconfig __P((struct proc *p, struct semconfig_args *uap));
50 #endif
51
52 static struct sem_undo *semu_alloc __P((struct proc *p));
53 static int semundo_adjust __P((struct proc *p, struct sem_undo **supptr,
54 int semid, int semnum, int adjval));
55 static void semundo_clear __P((int semid, int semnum));
56
57 /* XXX casting to (sy_call_t *) is bogus, as usual. */
58 static sy_call_t *semcalls[] = {
59 (sy_call_t *)__semctl, (sy_call_t *)semget,
60 (sy_call_t *)semop, (sy_call_t *)semconfig
61 };
62
63 static int semtot = 0;
64 struct semid_ds *sema; /* semaphore id pool */
65 struct sem *sem; /* semaphore pool */
66 static struct sem_undo *semu_list; /* list of active undo structures */
67 int *semu; /* undo structure pool */
68
69 static struct proc *semlock_holder = NULL;
70
71 void
72 seminit(dummy)
73 void *dummy;
74 {
75 register int i;
76
77 if (sema == NULL)
78 panic("sema is NULL");
79 if (semu == NULL)
80 panic("semu is NULL");
81
82 for (i = 0; i < seminfo.semmni; i++) {
83 sema[i].sem_base = 0;
84 sema[i].sem_perm.mode = 0;
85 }
86 for (i = 0; i < seminfo.semmnu; i++) {
87 register struct sem_undo *suptr = SEMU(i);
88 suptr->un_proc = NULL;
89 }
90 semu_list = NULL;
91 }
92
93 /*
94 * Entry point for all SEM calls
95 */
96 int
97 semsys(p, uap)
98 struct proc *p;
99 /* XXX actually varargs. */
100 struct semsys_args /* {
101 u_int which;
102 int a2;
103 int a3;
104 int a4;
105 int a5;
106 } */ *uap;
107 {
108
109 while (semlock_holder != NULL && semlock_holder != p)
110 (void) tsleep((caddr_t)&semlock_holder, (PZERO - 4), "semsys", 0);
111
112 if (uap->which >= sizeof(semcalls)/sizeof(semcalls[0]))
113 return (EINVAL);
114 return ((*semcalls[uap->which])(p, &uap->a2));
115 }
116
117 /*
118 * Lock or unlock the entire semaphore facility.
119 *
120 * This will probably eventually evolve into a general purpose semaphore
121 * facility status enquiry mechanism (I don't like the "read /dev/kmem"
122 * approach currently taken by ipcs and the amount of info that we want
123 * to be able to extract for ipcs is probably beyond what the capability
124 * of the getkerninfo facility.
125 *
126 * At the time that the current version of semconfig was written, ipcs is
127 * the only user of the semconfig facility. It uses it to ensure that the
128 * semaphore facility data structures remain static while it fishes around
129 * in /dev/kmem.
130 */
131
132 #ifndef _SYS_SYSPROTO_H_
133 struct semconfig_args {
134 semconfig_ctl_t flag;
135 };
136 #endif
137
138 int
139 semconfig(p, uap)
140 struct proc *p;
141 struct semconfig_args *uap;
142 {
143 int eval = 0;
144
145 switch (uap->flag) {
146 case SEM_CONFIG_FREEZE:
147 semlock_holder = p;
148 break;
149
150 case SEM_CONFIG_THAW:
151 semlock_holder = NULL;
152 wakeup((caddr_t)&semlock_holder);
153 break;
154
155 default:
156 printf("semconfig: unknown flag parameter value (%d) - ignored\n",
157 uap->flag);
158 eval = EINVAL;
159 break;
160 }
161
162 p->p_retval[0] = 0;
163 return(eval);
164 }
165
166 /*
167 * Allocate a new sem_undo structure for a process
168 * (returns ptr to structure or NULL if no more room)
169 */
170
171 static struct sem_undo *
172 semu_alloc(p)
173 struct proc *p;
174 {
175 register int i;
176 register struct sem_undo *suptr;
177 register struct sem_undo **supptr;
178 int attempt;
179
180 /*
181 * Try twice to allocate something.
182 * (we'll purge any empty structures after the first pass so
183 * two passes are always enough)
184 */
185
186 for (attempt = 0; attempt < 2; attempt++) {
187 /*
188 * Look for a free structure.
189 * Fill it in and return it if we find one.
190 */
191
192 for (i = 0; i < seminfo.semmnu; i++) {
193 suptr = SEMU(i);
194 if (suptr->un_proc == NULL) {
195 suptr->un_next = semu_list;
196 semu_list = suptr;
197 suptr->un_cnt = 0;
198 suptr->un_proc = p;
199 return(suptr);
200 }
201 }
202
203 /*
204 * We didn't find a free one, if this is the first attempt
205 * then try to free some structures.
206 */
207
208 if (attempt == 0) {
209 /* All the structures are in use - try to free some */
210 int did_something = 0;
211
212 supptr = &semu_list;
213 while ((suptr = *supptr) != NULL) {
214 if (suptr->un_cnt == 0) {
215 suptr->un_proc = NULL;
216 *supptr = suptr->un_next;
217 did_something = 1;
218 } else
219 supptr = &(suptr->un_next);
220 }
221
222 /* If we didn't free anything then just give-up */
223 if (!did_something)
224 return(NULL);
225 } else {
226 /*
227 * The second pass failed even though we freed
228 * something after the first pass!
229 * This is IMPOSSIBLE!
230 */
231 panic("semu_alloc - second attempt failed");
232 }
233 }
234 return (NULL);
235 }
236
237 /*
238 * Adjust a particular entry for a particular proc
239 */
240
241 static int
242 semundo_adjust(p, supptr, semid, semnum, adjval)
243 register struct proc *p;
244 struct sem_undo **supptr;
245 int semid, semnum;
246 int adjval;
247 {
248 register struct sem_undo *suptr;
249 register struct undo *sunptr;
250 int i;
251
252 /* Look for and remember the sem_undo if the caller doesn't provide
253 it */
254
255 suptr = *supptr;
256 if (suptr == NULL) {
257 for (suptr = semu_list; suptr != NULL;
258 suptr = suptr->un_next) {
259 if (suptr->un_proc == p) {
260 *supptr = suptr;
261 break;
262 }
263 }
264 if (suptr == NULL) {
265 if (adjval == 0)
266 return(0);
267 suptr = semu_alloc(p);
268 if (suptr == NULL)
269 return(ENOSPC);
270 *supptr = suptr;
271 }
272 }
273
274 /*
275 * Look for the requested entry and adjust it (delete if adjval becomes
276 * 0).
277 */
278 sunptr = &suptr->un_ent[0];
279 for (i = 0; i < suptr->un_cnt; i++, sunptr++) {
280 if (sunptr->un_id != semid || sunptr->un_num != semnum)
281 continue;
282 if (adjval == 0)
283 sunptr->un_adjval = 0;
284 else
285 sunptr->un_adjval += adjval;
286 if (sunptr->un_adjval == 0) {
287 suptr->un_cnt--;
288 if (i < suptr->un_cnt)
289 suptr->un_ent[i] =
290 suptr->un_ent[suptr->un_cnt];
291 }
292 return(0);
293 }
294
295 /* Didn't find the right entry - create it */
296 if (adjval == 0)
297 return(0);
298 if (suptr->un_cnt != seminfo.semume) {
299 sunptr = &suptr->un_ent[suptr->un_cnt];
300 suptr->un_cnt++;
301 sunptr->un_adjval = adjval;
302 sunptr->un_id = semid; sunptr->un_num = semnum;
303 } else
304 return(EINVAL);
305 return(0);
306 }
307
308 static void
309 semundo_clear(semid, semnum)
310 int semid, semnum;
311 {
312 register struct sem_undo *suptr;
313
314 for (suptr = semu_list; suptr != NULL; suptr = suptr->un_next) {
315 register struct undo *sunptr = &suptr->un_ent[0];
316 register int i = 0;
317
318 while (i < suptr->un_cnt) {
319 if (sunptr->un_id == semid) {
320 if (semnum == -1 || sunptr->un_num == semnum) {
321 suptr->un_cnt--;
322 if (i < suptr->un_cnt) {
323 suptr->un_ent[i] =
324 suptr->un_ent[suptr->un_cnt];
325 continue;
326 }
327 }
328 if (semnum != -1)
329 break;
330 }
331 i++, sunptr++;
332 }
333 }
334 }
335
336 /*
337 * Note that the user-mode half of this passes a union, not a pointer
338 */
339 #ifndef _SYS_SYSPROTO_H_
340 struct __semctl_args {
341 int semid;
342 int semnum;
343 int cmd;
344 union semun *arg;
345 };
346 #endif
347
348 int
349 __semctl(p, uap)
350 struct proc *p;
351 register struct __semctl_args *uap;
352 {
353 int semid = uap->semid;
354 int semnum = uap->semnum;
355 int cmd = uap->cmd;
356 union semun *arg = uap->arg;
357 union semun real_arg;
358 struct ucred *cred = p->p_ucred;
359 int i, rval, eval;
360 struct semid_ds sbuf;
361 register struct semid_ds *semaptr;
362
363 #ifdef SEM_DEBUG
364 printf("call to semctl(%d, %d, %d, 0x%x)\n", semid, semnum, cmd, arg);
365 #endif
366
367 semid = IPCID_TO_IX(semid);
368 if (semid < 0 || semid >= seminfo.semmsl)
369 return(EINVAL);
370
371 semaptr = &sema[semid];
372 if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0 ||
373 semaptr->sem_perm.seq != IPCID_TO_SEQ(uap->semid))
374 return(EINVAL);
375
376 eval = 0;
377 rval = 0;
378
379 switch (cmd) {
380 case IPC_RMID:
381 if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_M)))
382 return(eval);
383 semaptr->sem_perm.cuid = cred->cr_uid;
384 semaptr->sem_perm.uid = cred->cr_uid;
385 semtot -= semaptr->sem_nsems;
386 for (i = semaptr->sem_base - sem; i < semtot; i++)
387 sem[i] = sem[i + semaptr->sem_nsems];
388 for (i = 0; i < seminfo.semmni; i++) {
389 if ((sema[i].sem_perm.mode & SEM_ALLOC) &&
390 sema[i].sem_base > semaptr->sem_base)
391 sema[i].sem_base -= semaptr->sem_nsems;
392 }
393 semaptr->sem_perm.mode = 0;
394 semundo_clear(semid, -1);
395 wakeup((caddr_t)semaptr);
396 break;
397
398 case IPC_SET:
399 if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_M)))
400 return(eval);
401 if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
402 return(eval);
403 if ((eval = copyin(real_arg.buf, (caddr_t)&sbuf,
404 sizeof(sbuf))) != 0)
405 return(eval);
406 semaptr->sem_perm.uid = sbuf.sem_perm.uid;
407 semaptr->sem_perm.gid = sbuf.sem_perm.gid;
408 semaptr->sem_perm.mode = (semaptr->sem_perm.mode & ~0777) |
409 (sbuf.sem_perm.mode & 0777);
410 semaptr->sem_ctime = time_second;
411 break;
412
413 case IPC_STAT:
414 if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
415 return(eval);
416 if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
417 return(eval);
418 eval = copyout((caddr_t)semaptr, real_arg.buf,
419 sizeof(struct semid_ds));
420 break;
421
422 case GETNCNT:
423 if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
424 return(eval);
425 if (semnum < 0 || semnum >= semaptr->sem_nsems)
426 return(EINVAL);
427 rval = semaptr->sem_base[semnum].semncnt;
428 break;
429
430 case GETPID:
431 if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
432 return(eval);
433 if (semnum < 0 || semnum >= semaptr->sem_nsems)
434 return(EINVAL);
435 rval = semaptr->sem_base[semnum].sempid;
436 break;
437
438 case GETVAL:
439 if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
440 return(eval);
441 if (semnum < 0 || semnum >= semaptr->sem_nsems)
442 return(EINVAL);
443 rval = semaptr->sem_base[semnum].semval;
444 break;
445
446 case GETALL:
447 if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
448 return(eval);
449 if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
450 return(eval);
451 for (i = 0; i < semaptr->sem_nsems; i++) {
452 eval = copyout((caddr_t)&semaptr->sem_base[i].semval,
453 &real_arg.array[i], sizeof(real_arg.array[0]));
454 if (eval != 0)
455 break;
456 }
457 break;
458
459 case GETZCNT:
460 if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
461 return(eval);
462 if (semnum < 0 || semnum >= semaptr->sem_nsems)
463 return(EINVAL);
464 rval = semaptr->sem_base[semnum].semzcnt;
465 break;
466
467 case SETVAL:
468 if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_W)))
469 return(eval);
470 if (semnum < 0 || semnum >= semaptr->sem_nsems)
471 return(EINVAL);
472 if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
473 return(eval);
474 semaptr->sem_base[semnum].semval = real_arg.val;
475 semundo_clear(semid, semnum);
476 wakeup((caddr_t)semaptr);
477 break;
478
479 case SETALL:
480 if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_W)))
481 return(eval);
482 if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
483 return(eval);
484 for (i = 0; i < semaptr->sem_nsems; i++) {
485 eval = copyin(&real_arg.array[i],
486 (caddr_t)&semaptr->sem_base[i].semval,
487 sizeof(real_arg.array[0]));
488 if (eval != 0)
489 break;
490 }
491 semundo_clear(semid, -1);
492 wakeup((caddr_t)semaptr);
493 break;
494
495 default:
496 return(EINVAL);
497 }
498
499 if (eval == 0)
500 p->p_retval[0] = rval;
501 return(eval);
502 }
503
504 #ifndef _SYS_SYSPROTO_H_
505 struct semget_args {
506 key_t key;
507 int nsems;
508 int semflg;
509 };
510 #endif
511
512 int
513 semget(p, uap)
514 struct proc *p;
515 register struct semget_args *uap;
516 {
517 int semid, eval;
518 int key = uap->key;
519 int nsems = uap->nsems;
520 int semflg = uap->semflg;
521 struct ucred *cred = p->p_ucred;
522
523 #ifdef SEM_DEBUG
524 printf("semget(0x%x, %d, 0%o)\n", key, nsems, semflg);
525 #endif
526
527 if (key != IPC_PRIVATE) {
528 for (semid = 0; semid < seminfo.semmni; semid++) {
529 if ((sema[semid].sem_perm.mode & SEM_ALLOC) &&
530 sema[semid].sem_perm.key == key)
531 break;
532 }
533 if (semid < seminfo.semmni) {
534 #ifdef SEM_DEBUG
535 printf("found public key\n");
536 #endif
537 if ((eval = ipcperm(cred, &sema[semid].sem_perm,
538 semflg & 0700)))
539 return(eval);
540 if (nsems > 0 && sema[semid].sem_nsems < nsems) {
541 #ifdef SEM_DEBUG
542 printf("too small\n");
543 #endif
544 return(EINVAL);
545 }
546 if ((semflg & IPC_CREAT) && (semflg & IPC_EXCL)) {
547 #ifdef SEM_DEBUG
548 printf("not exclusive\n");
549 #endif
550 return(EEXIST);
551 }
552 goto found;
553 }
554 }
555
556 #ifdef SEM_DEBUG
557 printf("need to allocate the semid_ds\n");
558 #endif
559 if (key == IPC_PRIVATE || (semflg & IPC_CREAT)) {
560 if (nsems <= 0 || nsems > seminfo.semmsl) {
561 #ifdef SEM_DEBUG
562 printf("nsems out of range (0<%d<=%d)\n", nsems,
563 seminfo.semmsl);
564 #endif
565 return(EINVAL);
566 }
567 if (nsems > seminfo.semmns - semtot) {
568 #ifdef SEM_DEBUG
569 printf("not enough semaphores left (need %d, got %d)\n",
570 nsems, seminfo.semmns - semtot);
571 #endif
572 return(ENOSPC);
573 }
574 for (semid = 0; semid < seminfo.semmni; semid++) {
575 if ((sema[semid].sem_perm.mode & SEM_ALLOC) == 0)
576 break;
577 }
578 if (semid == seminfo.semmni) {
579 #ifdef SEM_DEBUG
580 printf("no more semid_ds's available\n");
581 #endif
582 return(ENOSPC);
583 }
584 #ifdef SEM_DEBUG
585 printf("semid %d is available\n", semid);
586 #endif
587 sema[semid].sem_perm.key = key;
588 sema[semid].sem_perm.cuid = cred->cr_uid;
589 sema[semid].sem_perm.uid = cred->cr_uid;
590 sema[semid].sem_perm.cgid = cred->cr_gid;
591 sema[semid].sem_perm.gid = cred->cr_gid;
592 sema[semid].sem_perm.mode = (semflg & 0777) | SEM_ALLOC;
593 sema[semid].sem_perm.seq =
594 (sema[semid].sem_perm.seq + 1) & 0x7fff;
595 sema[semid].sem_nsems = nsems;
596 sema[semid].sem_otime = 0;
597 sema[semid].sem_ctime = time_second;
598 sema[semid].sem_base = &sem[semtot];
599 semtot += nsems;
600 bzero(sema[semid].sem_base,
601 sizeof(sema[semid].sem_base[0])*nsems);
602 #ifdef SEM_DEBUG
603 printf("sembase = 0x%x, next = 0x%x\n", sema[semid].sem_base,
604 &sem[semtot]);
605 #endif
606 } else {
607 #ifdef SEM_DEBUG
608 printf("didn't find it and wasn't asked to create it\n");
609 #endif
610 return(ENOENT);
611 }
612
613 found:
614 p->p_retval[0] = IXSEQ_TO_IPCID(semid, sema[semid].sem_perm);
615 return(0);
616 }
617
618 #ifndef _SYS_SYSPROTO_H_
619 struct semop_args {
620 int semid;
621 struct sembuf *sops;
622 int nsops;
623 };
624 #endif
625
626 int
627 semop(p, uap)
628 struct proc *p;
629 register struct semop_args *uap;
630 {
631 int semid = uap->semid;
632 int nsops = uap->nsops;
633 struct sembuf sops[MAX_SOPS];
634 register struct semid_ds *semaptr;
635 register struct sembuf *sopptr;
636 register struct sem *semptr;
637 struct sem_undo *suptr = NULL;
638 struct ucred *cred = p->p_ucred;
639 int i, j, eval;
640 int do_wakeup, do_undos;
641
642 #ifdef SEM_DEBUG
643 printf("call to semop(%d, 0x%x, %d)\n", semid, sops, nsops);
644 #endif
645
646 semid = IPCID_TO_IX(semid); /* Convert back to zero origin */
647
648 if (semid < 0 || semid >= seminfo.semmsl)
649 return(EINVAL);
650
651 semaptr = &sema[semid];
652 if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0)
653 return(EINVAL);
654 if (semaptr->sem_perm.seq != IPCID_TO_SEQ(uap->semid))
655 return(EINVAL);
656
657 if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_W))) {
658 #ifdef SEM_DEBUG
659 printf("eval = %d from ipaccess\n", eval);
660 #endif
661 return(eval);
662 }
663
664 if (nsops > MAX_SOPS) {
665 #ifdef SEM_DEBUG
666 printf("too many sops (max=%d, nsops=%d)\n", MAX_SOPS, nsops);
667 #endif
668 return(E2BIG);
669 }
670
671 if ((eval = copyin(uap->sops, &sops, nsops * sizeof(sops[0]))) != 0) {
672 #ifdef SEM_DEBUG
673 printf("eval = %d from copyin(%08x, %08x, %d)\n", eval,
674 uap->sops, &sops, nsops * sizeof(sops[0]));
675 #endif
676 return(eval);
677 }
678
679 /*
680 * Loop trying to satisfy the vector of requests.
681 * If we reach a point where we must wait, any requests already
682 * performed are rolled back and we go to sleep until some other
683 * process wakes us up. At this point, we start all over again.
684 *
685 * This ensures that from the perspective of other tasks, a set
686 * of requests is atomic (never partially satisfied).
687 */
688 do_undos = 0;
689
690 for (;;) {
691 do_wakeup = 0;
692
693 for (i = 0; i < nsops; i++) {
694 sopptr = &sops[i];
695
696 if (sopptr->sem_num >= semaptr->sem_nsems)
697 return(EFBIG);
698
699 semptr = &semaptr->sem_base[sopptr->sem_num];
700
701 #ifdef SEM_DEBUG
702 printf("semop: semaptr=%x, sem_base=%x, semptr=%x, sem[%d]=%d : op=%d, flag=%s\n",
703 semaptr, semaptr->sem_base, semptr,
704 sopptr->sem_num, semptr->semval, sopptr->sem_op,
705 (sopptr->sem_flg & IPC_NOWAIT) ? "nowait" : "wait");
706 #endif
707
708 if (sopptr->sem_op < 0) {
709 if (semptr->semval + sopptr->sem_op < 0) {
710 #ifdef SEM_DEBUG
711 printf("semop: can't do it now\n");
712 #endif
713 break;
714 } else {
715 semptr->semval += sopptr->sem_op;
716 if (semptr->semval == 0 &&
717 semptr->semzcnt > 0)
718 do_wakeup = 1;
719 }
720 if (sopptr->sem_flg & SEM_UNDO)
721 do_undos = 1;
722 } else if (sopptr->sem_op == 0) {
723 if (semptr->semval > 0) {
724 #ifdef SEM_DEBUG
725 printf("semop: not zero now\n");
726 #endif
727 break;
728 }
729 } else {
730 if (semptr->semncnt > 0)
731 do_wakeup = 1;
732 semptr->semval += sopptr->sem_op;
733 if (sopptr->sem_flg & SEM_UNDO)
734 do_undos = 1;
735 }
736 }
737
738 /*
739 * Did we get through the entire vector?
740 */
741 if (i >= nsops)
742 goto done;
743
744 /*
745 * No ... rollback anything that we've already done
746 */
747 #ifdef SEM_DEBUG
748 printf("semop: rollback 0 through %d\n", i-1);
749 #endif
750 for (j = 0; j < i; j++)
751 semaptr->sem_base[sops[j].sem_num].semval -=
752 sops[j].sem_op;
753
754 /*
755 * If the request that we couldn't satisfy has the
756 * NOWAIT flag set then return with EAGAIN.
757 */
758 if (sopptr->sem_flg & IPC_NOWAIT)
759 return(EAGAIN);
760
761 if (sopptr->sem_op == 0)
762 semptr->semzcnt++;
763 else
764 semptr->semncnt++;
765
766 #ifdef SEM_DEBUG
767 printf("semop: good night!\n");
768 #endif
769 eval = tsleep((caddr_t)semaptr, (PZERO - 4) | PCATCH,
770 "semwait", 0);
771 #ifdef SEM_DEBUG
772 printf("semop: good morning (eval=%d)!\n", eval);
773 #endif
774
775 suptr = NULL; /* sem_undo may have been reallocated */
776
777 if (eval != 0)
778 return(EINTR);
779 #ifdef SEM_DEBUG
780 printf("semop: good morning!\n");
781 #endif
782
783 /*
784 * Make sure that the semaphore still exists
785 */
786 if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0 ||
787 semaptr->sem_perm.seq != IPCID_TO_SEQ(uap->semid)) {
788 /* The man page says to return EIDRM. */
789 /* Unfortunately, BSD doesn't define that code! */
790 #ifdef EIDRM
791 return(EIDRM);
792 #else
793 return(EINVAL);
794 #endif
795 }
796
797 /*
798 * The semaphore is still alive. Readjust the count of
799 * waiting processes.
800 */
801 if (sopptr->sem_op == 0)
802 semptr->semzcnt--;
803 else
804 semptr->semncnt--;
805 }
806
807 done:
808 /*
809 * Process any SEM_UNDO requests.
810 */
811 if (do_undos) {
812 for (i = 0; i < nsops; i++) {
813 /*
814 * We only need to deal with SEM_UNDO's for non-zero
815 * op's.
816 */
817 int adjval;
818
819 if ((sops[i].sem_flg & SEM_UNDO) == 0)
820 continue;
821 adjval = sops[i].sem_op;
822 if (adjval == 0)
823 continue;
824 eval = semundo_adjust(p, &suptr, semid,
825 sops[i].sem_num, -adjval);
826 if (eval == 0)
827 continue;
828
829 /*
830 * Oh-Oh! We ran out of either sem_undo's or undo's.
831 * Rollback the adjustments to this point and then
832 * rollback the semaphore ups and down so we can return
833 * with an error with all structures restored. We
834 * rollback the undo's in the exact reverse order that
835 * we applied them. This guarantees that we won't run
836 * out of space as we roll things back out.
837 */
838 for (j = i - 1; j >= 0; j--) {
839 if ((sops[j].sem_flg & SEM_UNDO) == 0)
840 continue;
841 adjval = sops[j].sem_op;
842 if (adjval == 0)
843 continue;
844 if (semundo_adjust(p, &suptr, semid,
845 sops[j].sem_num, adjval) != 0)
846 panic("semop - can't undo undos");
847 }
848
849 for (j = 0; j < nsops; j++)
850 semaptr->sem_base[sops[j].sem_num].semval -=
851 sops[j].sem_op;
852
853 #ifdef SEM_DEBUG
854 printf("eval = %d from semundo_adjust\n", eval);
855 #endif
856 return(eval);
857 } /* loop through the sops */
858 } /* if (do_undos) */
859
860 /* We're definitely done - set the sempid's */
861 for (i = 0; i < nsops; i++) {
862 sopptr = &sops[i];
863 semptr = &semaptr->sem_base[sopptr->sem_num];
864 semptr->sempid = p->p_pid;
865 }
866
867 /* Do a wakeup if any semaphore was up'd. */
868 if (do_wakeup) {
869 #ifdef SEM_DEBUG
870 printf("semop: doing wakeup\n");
871 #ifdef SEM_WAKEUP
872 sem_wakeup((caddr_t)semaptr);
873 #else
874 wakeup((caddr_t)semaptr);
875 #endif
876 printf("semop: back from wakeup\n");
877 #else
878 wakeup((caddr_t)semaptr);
879 #endif
880 }
881 #ifdef SEM_DEBUG
882 printf("semop: done\n");
883 #endif
884 p->p_retval[0] = 0;
885 return(0);
886 }
887
888 /*
889 * Go through the undo structures for this process and apply the adjustments to
890 * semaphores.
891 */
892 void
893 semexit(p)
894 struct proc *p;
895 {
896 register struct sem_undo *suptr;
897 register struct sem_undo **supptr;
898 int did_something;
899
900 /*
901 * If somebody else is holding the global semaphore facility lock
902 * then sleep until it is released.
903 */
904 while (semlock_holder != NULL && semlock_holder != p) {
905 #ifdef SEM_DEBUG
906 printf("semaphore facility locked - sleeping ...\n");
907 #endif
908 (void) tsleep((caddr_t)&semlock_holder, (PZERO - 4), "semext", 0);
909 }
910
911 did_something = 0;
912
913 /*
914 * Go through the chain of undo vectors looking for one
915 * associated with this process.
916 */
917
918 for (supptr = &semu_list; (suptr = *supptr) != NULL;
919 supptr = &suptr->un_next) {
920 if (suptr->un_proc == p)
921 break;
922 }
923
924 if (suptr == NULL)
925 goto unlock;
926
927 #ifdef SEM_DEBUG
928 printf("proc @%08x has undo structure with %d entries\n", p,
929 suptr->un_cnt);
930 #endif
931
932 /*
933 * If there are any active undo elements then process them.
934 */
935 if (suptr->un_cnt > 0) {
936 int ix;
937
938 for (ix = 0; ix < suptr->un_cnt; ix++) {
939 int semid = suptr->un_ent[ix].un_id;
940 int semnum = suptr->un_ent[ix].un_num;
941 int adjval = suptr->un_ent[ix].un_adjval;
942 struct semid_ds *semaptr;
943
944 semaptr = &sema[semid];
945 if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0)
946 panic("semexit - semid not allocated");
947 if (semnum >= semaptr->sem_nsems)
948 panic("semexit - semnum out of range");
949
950 #ifdef SEM_DEBUG
951 printf("semexit: %08x id=%d num=%d(adj=%d) ; sem=%d\n",
952 suptr->un_proc, suptr->un_ent[ix].un_id,
953 suptr->un_ent[ix].un_num,
954 suptr->un_ent[ix].un_adjval,
955 semaptr->sem_base[semnum].semval);
956 #endif
957
958 if (adjval < 0) {
959 if (semaptr->sem_base[semnum].semval < -adjval)
960 semaptr->sem_base[semnum].semval = 0;
961 else
962 semaptr->sem_base[semnum].semval +=
963 adjval;
964 } else
965 semaptr->sem_base[semnum].semval += adjval;
966
967 #ifdef SEM_WAKEUP
968 sem_wakeup((caddr_t)semaptr);
969 #else
970 wakeup((caddr_t)semaptr);
971 #endif
972 #ifdef SEM_DEBUG
973 printf("semexit: back from wakeup\n");
974 #endif
975 }
976 }
977
978 /*
979 * Deallocate the undo vector.
980 */
981 #ifdef SEM_DEBUG
982 printf("removing vector\n");
983 #endif
984 suptr->un_proc = NULL;
985 *supptr = suptr->un_next;
986
987 unlock:
988 /*
989 * If the exiting process is holding the global semaphore facility
990 * lock then release it.
991 */
992 if (semlock_holder == p) {
993 semlock_holder = NULL;
994 wakeup((caddr_t)&semlock_holder);
995 }
996 }