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