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