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