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