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