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