1 /* $OpenBSD: sysv_sem.c,v 1.19 2003/07/21 22:44:50 tedu Exp $ */ 2 /* $NetBSD: sysv_sem.c,v 1.26 1996/02/09 19:00:25 christos Exp $ */ 3 4 /* 5 * Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 * 19 * Sponsored in part by the Defense Advanced Research Projects 20 * Agency (DARPA) and Air Force Research Laboratory, Air Force 21 * Materiel Command, USAF, under agreement number F39502-99-1-0512. 22 */ 23 /* 24 * Implementation of SVID semaphores 25 * 26 * Author: Daniel Boulet 27 * 28 * This software is provided ``AS IS'' without any warranties of any kind. 29 */ 30 31 #include <sys/param.h> 32 #include <sys/systm.h> 33 #include <sys/kernel.h> 34 #include <sys/proc.h> 35 #include <sys/sem.h> 36 #include <sys/sysctl.h> 37 #include <sys/malloc.h> 38 #include <sys/pool.h> 39 40 #include <sys/mount.h> 41 #include <sys/syscallargs.h> 42 43 /* SVID defines EIDRM but BSD does not */ 44 #ifndef EIDRM 45 #define EIDRM EINVAL 46 #endif 47 48 #ifdef SEM_DEBUG 49 #define DPRINTF(x) printf x 50 #else 51 #define DPRINTF(x) 52 #endif 53 54 int semtot = 0; 55 int semutot = 0; 56 struct semid_ds **sema; /* semaphore id list */ 57 struct sem_undo *semu_list; /* list of undo structures */ 58 struct pool sema_pool; /* pool for struct semid_ds */ 59 struct pool semu_pool; /* pool for struct sem_undo (SEMUSZ) */ 60 unsigned short *semseqs; /* array of sem sequence numbers */ 61 62 struct sem_undo *semu_alloc(struct proc *); 63 int semundo_adjust(struct proc *, struct sem_undo **, int, int, int); 64 void semundo_clear(int, int); 65 66 void 67 seminit(void) 68 { 69 70 pool_init(&sema_pool, sizeof(struct semid_ds), 0, 0, 0, "semapl", 71 &pool_allocator_nointr); 72 pool_init(&semu_pool, SEMUSZ, 0, 0, 0, "semupl", 73 &pool_allocator_nointr); 74 sema = malloc(seminfo.semmni * sizeof(struct semid_ds *), 75 M_SEM, M_WAITOK); 76 bzero(sema, seminfo.semmni * sizeof(struct semid_ds *)); 77 semseqs = malloc(seminfo.semmni * sizeof(unsigned short), 78 M_SEM, M_WAITOK); 79 bzero(semseqs, seminfo.semmni * sizeof(unsigned short)); 80 semu_list = NULL; 81 } 82 83 /* 84 * Allocate a new sem_undo structure for a process 85 * (returns ptr to structure or NULL if no more room) 86 */ 87 struct sem_undo * 88 semu_alloc(struct proc *p) 89 { 90 struct sem_undo *suptr, *tmp; 91 92 if (semutot == seminfo.semmnu) 93 return (NULL); /* no space */ 94 95 /* 96 * Allocate a semu w/o waiting if possible. 97 * If we do have to wait, we must check to verify that a semu 98 * with un_proc == p has not been allocated in the meantime. 99 */ 100 semutot++; 101 if ((suptr = pool_get(&semu_pool, 0)) == NULL) { 102 tmp = pool_get(&semu_pool, PR_WAITOK); 103 for (suptr = semu_list; suptr != NULL; suptr = suptr->un_next) { 104 if (suptr->un_proc == p) { 105 pool_put(&semu_pool, tmp); 106 semutot--; 107 return (suptr); 108 } 109 } 110 suptr = tmp; 111 } 112 suptr->un_cnt = 0; 113 suptr->un_proc = p; 114 suptr->un_next = semu_list; 115 semu_list = suptr; 116 return (suptr); 117 } 118 119 /* 120 * Adjust a particular entry for a particular proc 121 */ 122 int 123 semundo_adjust(struct proc *p, struct sem_undo **supptr, int semid, int semnum, 124 int adjval) 125 { 126 struct sem_undo *suptr; 127 struct undo *sunptr; 128 int i; 129 130 /* 131 * Look for and remember the sem_undo if the caller doesn't provide it. 132 */ 133 suptr = *supptr; 134 if (suptr == NULL) { 135 for (suptr = semu_list; suptr != NULL; suptr = suptr->un_next) { 136 if (suptr->un_proc == p) { 137 *supptr = suptr; 138 break; 139 } 140 } 141 if (suptr == NULL) { 142 if (adjval == 0) 143 return (0); 144 suptr = semu_alloc(p); 145 if (suptr == NULL) 146 return (ENOSPC); 147 *supptr = suptr; 148 } 149 } 150 151 /* 152 * Look for the requested entry and adjust it 153 * (delete if adjval becomes 0). 154 */ 155 sunptr = &suptr->un_ent[0]; 156 for (i = 0; i < suptr->un_cnt; i++, sunptr++) { 157 if (sunptr->un_id != semid || sunptr->un_num != semnum) 158 continue; 159 if (adjval == 0) 160 sunptr->un_adjval = 0; 161 else 162 sunptr->un_adjval += adjval; 163 if (sunptr->un_adjval != 0) 164 return (0); 165 166 if (--suptr->un_cnt == 0) { 167 struct sem_undo *suprev; 168 169 if (semu_list == suptr) 170 semu_list = suptr->un_next; 171 else { 172 /* this code path should be rare */ 173 for (suprev = semu_list; suprev != NULL && 174 suprev->un_next != suptr; 175 suprev = suprev->un_next) 176 /* NOTHING */; 177 #ifdef DIAGNOSTIC 178 if (suprev == NULL) 179 panic("semundo_adjust: " 180 "suptr not in semu_list"); 181 #endif 182 suprev->un_next = suptr->un_next; 183 } 184 pool_put(&semu_pool, suptr); 185 semutot--; 186 } else if (i < suptr->un_cnt) 187 suptr->un_ent[i] = 188 suptr->un_ent[suptr->un_cnt]; 189 return (0); 190 } 191 192 /* Didn't find the right entry - create it */ 193 if (adjval == 0) 194 return (0); 195 if (suptr->un_cnt == SEMUME) 196 return (EINVAL); 197 198 sunptr = &suptr->un_ent[suptr->un_cnt]; 199 suptr->un_cnt++; 200 sunptr->un_adjval = adjval; 201 sunptr->un_id = semid; 202 sunptr->un_num = semnum; 203 return (0); 204 } 205 206 void 207 semundo_clear(int semid, int semnum) 208 { 209 struct sem_undo *suptr, *suprev, *tmp; 210 struct undo *sunptr; 211 int i; 212 213 for (suptr = semu_list; suptr != NULL; ) { 214 sunptr = &suptr->un_ent[0]; 215 for (i = 0; i < suptr->un_cnt; i++, sunptr++) { 216 if (sunptr->un_id == semid) { 217 if (semnum == -1 || sunptr->un_num == semnum) { 218 suptr->un_cnt--; 219 if (i < suptr->un_cnt) { 220 suptr->un_ent[i] = 221 suptr->un_ent[suptr->un_cnt]; 222 i--, sunptr--; 223 } 224 } 225 if (semnum != -1) 226 break; 227 } 228 } 229 if (suptr->un_cnt == 0) { 230 tmp = suptr; 231 if (suptr == semu_list) 232 suptr = semu_list = suptr->un_next; 233 else 234 suptr = suprev->un_next = suptr->un_next; 235 pool_put(&semu_pool, tmp); 236 semutot--; 237 } else { 238 suprev = suptr; 239 suptr = suptr->un_next; 240 } 241 } 242 } 243 244 void 245 semid_n2o(struct semid_ds *n, struct osemid_ds *o) 246 { 247 o->sem_base = n->sem_base; 248 o->sem_nsems = n->sem_nsems; 249 o->sem_otime = n->sem_otime; 250 o->sem_pad1 = n->sem_pad1; 251 o->sem_ctime = n->sem_ctime; 252 o->sem_pad2 = n->sem_pad2; 253 bcopy(n->sem_pad3, o->sem_pad3, sizeof o->sem_pad3); 254 ipc_n2o(&n->sem_perm, &o->sem_perm); 255 } 256 257 int 258 sys___semctl(struct proc *p, void *v, register_t *retval) 259 { 260 struct sys___semctl_args /* { 261 syscallarg(int) semid; 262 syscallarg(int) semnum; 263 syscallarg(int) cmd; 264 syscallarg(union semun *) arg; 265 } */ *uap = v; 266 int semid = SCARG(uap, semid); 267 int semnum = SCARG(uap, semnum); 268 int cmd = SCARG(uap, cmd); 269 union semun *arg = SCARG(uap, arg); 270 union semun real_arg; 271 struct ucred *cred = p->p_ucred; 272 int i, rval, error; 273 struct semid_ds sbuf; 274 struct semid_ds *semaptr; 275 276 DPRINTF(("call to semctl(%d, %d, %d, %p)\n", semid, semnum, cmd, arg)); 277 278 semid = IPCID_TO_IX(semid); 279 if (semid < 0 || semid >= seminfo.semmsl) 280 return (EINVAL); 281 282 if ((semaptr = sema[semid]) == NULL || 283 semaptr->sem_perm.seq != IPCID_TO_SEQ(SCARG(uap, semid))) 284 return (EINVAL); 285 286 error = rval = 0; 287 288 switch (cmd) { 289 case IPC_RMID: 290 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_M)) != 0) 291 return (error); 292 semaptr->sem_perm.cuid = cred->cr_uid; 293 semaptr->sem_perm.uid = cred->cr_uid; 294 semtot -= semaptr->sem_nsems; 295 free(semaptr->sem_base, M_SEM); 296 pool_put(&sema_pool, semaptr); 297 sema[semid] = NULL; 298 semundo_clear(semid, -1); 299 wakeup(&sema[semid]); 300 break; 301 302 case IPC_SET: 303 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_M))) 304 return (error); 305 if ((error = copyin(arg, &real_arg, sizeof(real_arg))) != 0) 306 return (error); 307 if ((error = copyin(real_arg.buf, &sbuf, sizeof(sbuf))) != 0) 308 return (error); 309 semaptr->sem_perm.uid = sbuf.sem_perm.uid; 310 semaptr->sem_perm.gid = sbuf.sem_perm.gid; 311 semaptr->sem_perm.mode = (semaptr->sem_perm.mode & ~0777) | 312 (sbuf.sem_perm.mode & 0777); 313 semaptr->sem_ctime = time.tv_sec; 314 break; 315 316 case IPC_STAT: 317 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R))) 318 return (error); 319 if ((error = copyin(arg, &real_arg, sizeof(real_arg))) != 0) 320 return (error); 321 error = copyout(semaptr, real_arg.buf, sizeof(struct semid_ds)); 322 break; 323 324 case GETNCNT: 325 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R))) 326 return (error); 327 if (semnum < 0 || semnum >= semaptr->sem_nsems) 328 return (EINVAL); 329 rval = semaptr->sem_base[semnum].semncnt; 330 break; 331 332 case GETPID: 333 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R))) 334 return (error); 335 if (semnum < 0 || semnum >= semaptr->sem_nsems) 336 return (EINVAL); 337 rval = semaptr->sem_base[semnum].sempid; 338 break; 339 340 case GETVAL: 341 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R))) 342 return (error); 343 if (semnum < 0 || semnum >= semaptr->sem_nsems) 344 return (EINVAL); 345 rval = semaptr->sem_base[semnum].semval; 346 break; 347 348 case GETALL: 349 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R))) 350 return (error); 351 if ((error = copyin(arg, &real_arg, sizeof(real_arg))) != 0) 352 return (error); 353 for (i = 0; i < semaptr->sem_nsems; i++) { 354 error = copyout(&semaptr->sem_base[i].semval, 355 &real_arg.array[i], sizeof(real_arg.array[0])); 356 if (error != 0) 357 break; 358 } 359 break; 360 361 case GETZCNT: 362 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R))) 363 return (error); 364 if (semnum < 0 || semnum >= semaptr->sem_nsems) 365 return (EINVAL); 366 rval = semaptr->sem_base[semnum].semzcnt; 367 break; 368 369 case SETVAL: 370 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_W))) 371 return (error); 372 if (semnum < 0 || semnum >= semaptr->sem_nsems) 373 return (EINVAL); 374 if ((error = copyin(arg, &real_arg, sizeof(real_arg))) != 0) 375 return (error); 376 semaptr->sem_base[semnum].semval = real_arg.val; 377 semundo_clear(semid, semnum); 378 wakeup(&sema[semid]); 379 break; 380 381 case SETALL: 382 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_W))) 383 return (error); 384 if ((error = copyin(arg, &real_arg, sizeof(real_arg))) != 0) 385 return (error); 386 for (i = 0; i < semaptr->sem_nsems; i++) { 387 error = copyin(&real_arg.array[i], 388 &semaptr->sem_base[i].semval, 389 sizeof(real_arg.array[0])); 390 if (error != 0) 391 break; 392 } 393 semundo_clear(semid, -1); 394 wakeup(&sema[semid]); 395 break; 396 397 default: 398 return (EINVAL); 399 } 400 401 if (error == 0) 402 *retval = rval; 403 return (error); 404 } 405 406 int 407 sys_semget(struct proc *p, void *v, register_t *retval) 408 { 409 struct sys_semget_args /* { 410 syscallarg(key_t) key; 411 syscallarg(int) nsems; 412 syscallarg(int) semflg; 413 } */ *uap = v; 414 int semid, error; 415 int key = SCARG(uap, key); 416 int nsems = SCARG(uap, nsems); 417 int semflg = SCARG(uap, semflg); 418 struct semid_ds *semaptr, *semaptr_new = NULL; 419 struct ucred *cred = p->p_ucred; 420 421 DPRINTF(("semget(0x%x, %d, 0%o)\n", key, nsems, semflg)); 422 423 /* 424 * Preallocate space for the new semaphore. If we are going 425 * to sleep, we want to sleep now to elliminate any race 426 * condition in allocating a semaphore with a specific key. 427 */ 428 if (key == IPC_PRIVATE || (semflg & IPC_CREAT)) { 429 semaptr_new = pool_get(&sema_pool, PR_WAITOK); 430 semaptr_new->sem_base = malloc(nsems * sizeof(struct sem), 431 M_SEM, M_WAITOK); 432 bzero(semaptr_new->sem_base, nsems * sizeof(struct sem)); 433 } 434 435 if (key != IPC_PRIVATE) { 436 for (semid = 0, semaptr = NULL; semid < seminfo.semmni; semid++) { 437 if ((semaptr = sema[semid]) != NULL && 438 semaptr->sem_perm.key == key) 439 break; 440 } 441 if (semaptr != NULL) { 442 DPRINTF(("found public key\n")); 443 if ((error = ipcperm(cred, &semaptr->sem_perm, 444 semflg & 0700))) 445 goto error; 446 if (nsems > 0 && semaptr->sem_nsems < nsems) { 447 DPRINTF(("too small\n")); 448 error = EINVAL; 449 goto error; 450 } 451 if ((semflg & IPC_CREAT) && (semflg & IPC_EXCL)) { 452 DPRINTF(("not exclusive\n")); 453 error = EEXIST; 454 goto error; 455 } 456 goto found; 457 } 458 } 459 460 DPRINTF(("need to allocate the semid_ds\n")); 461 if (key == IPC_PRIVATE || (semflg & IPC_CREAT)) { 462 if (nsems <= 0 || nsems > seminfo.semmsl) { 463 DPRINTF(("nsems out of range (0<%d<=%d)\n", nsems, 464 seminfo.semmsl)); 465 error = EINVAL; 466 goto error; 467 } 468 if (nsems > seminfo.semmns - semtot) { 469 DPRINTF(("not enough semaphores left (need %d, got %d)\n", 470 nsems, seminfo.semmns - semtot)); 471 error = ENOSPC; 472 goto error; 473 } 474 for (semid = 0; semid < seminfo.semmni; semid++) { 475 if ((semaptr = sema[semid]) == NULL) 476 break; 477 } 478 if (semid == seminfo.semmni) { 479 DPRINTF(("no more semid_ds's available\n")); 480 error = ENOSPC; 481 goto error; 482 } 483 DPRINTF(("semid %d is available\n", semid)); 484 semaptr_new->sem_perm.key = key; 485 semaptr_new->sem_perm.cuid = cred->cr_uid; 486 semaptr_new->sem_perm.uid = cred->cr_uid; 487 semaptr_new->sem_perm.cgid = cred->cr_gid; 488 semaptr_new->sem_perm.gid = cred->cr_gid; 489 semaptr_new->sem_perm.mode = (semflg & 0777); 490 semaptr_new->sem_perm.seq = semseqs[semid] = 491 (semseqs[semid] + 1) & 0x7fff; 492 semaptr_new->sem_nsems = nsems; 493 semaptr_new->sem_otime = 0; 494 semaptr_new->sem_ctime = time.tv_sec; 495 sema[semid] = semaptr_new; 496 semtot += nsems; 497 } else { 498 DPRINTF(("didn't find it and wasn't asked to create it\n")); 499 return (ENOENT); 500 } 501 502 found: 503 *retval = IXSEQ_TO_IPCID(semid, sema[semid]->sem_perm); 504 return (0); 505 error: 506 if (semaptr_new != NULL) { 507 free(semaptr_new->sem_base, M_SEM); 508 pool_put(&sema_pool, semaptr_new); 509 } 510 return (error); 511 } 512 513 int 514 sys_semop(struct proc *p, void *v, register_t *retval) 515 { 516 struct sys_semop_args /* { 517 syscallarg(int) semid; 518 syscallarg(struct sembuf *) sops; 519 syscallarg(u_int) nsops; 520 } */ *uap = v; 521 int semid = SCARG(uap, semid); 522 u_int nsops = SCARG(uap, nsops); 523 struct sembuf *sops; 524 struct semid_ds *semaptr; 525 struct sembuf *sopptr = NULL; 526 struct sem *semptr = NULL; 527 struct sem_undo *suptr = NULL; 528 struct ucred *cred = p->p_ucred; 529 int i, j, error; 530 int do_wakeup, do_undos; 531 532 DPRINTF(("call to semop(%d, %p, %d)\n", semid, sops, nsops)); 533 534 semid = IPCID_TO_IX(semid); /* Convert back to zero origin */ 535 536 if (semid < 0 || semid >= seminfo.semmsl) 537 return (EINVAL); 538 539 if ((semaptr = sema[semid]) == NULL || 540 semaptr->sem_perm.seq != IPCID_TO_SEQ(SCARG(uap, semid))) 541 return (EINVAL); 542 543 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_W))) { 544 DPRINTF(("error = %d from ipaccess\n", error)); 545 return (error); 546 } 547 548 if (nsops > seminfo.semopm) { 549 DPRINTF(("too many sops (max=%d, nsops=%d)\n", seminfo.semopm, nsops)); 550 return (E2BIG); 551 } 552 553 sops = malloc(nsops * sizeof(struct sembuf), M_SEM, M_WAITOK); 554 error = copyin(SCARG(uap, sops), sops, nsops * sizeof(struct sembuf)); 555 if (error != 0) { 556 DPRINTF(("error = %d from copyin(%p, %p, %d)\n", error, 557 SCARG(uap, sops), &sops, nsops * sizeof(struct sembuf))); 558 free(sops, M_SEM); 559 return (error); 560 } 561 562 /* 563 * Loop trying to satisfy the vector of requests. 564 * If we reach a point where we must wait, any requests already 565 * performed are rolled back and we go to sleep until some other 566 * process wakes us up. At this point, we start all over again. 567 * 568 * This ensures that from the perspective of other tasks, a set 569 * of requests is atomic (never partially satisfied). 570 */ 571 do_undos = 0; 572 573 for (;;) { 574 do_wakeup = 0; 575 576 for (i = 0; i < nsops; i++) { 577 sopptr = &sops[i]; 578 579 if (sopptr->sem_num >= semaptr->sem_nsems) { 580 free(sops, M_SEM); 581 return (EFBIG); 582 } 583 584 semptr = &semaptr->sem_base[sopptr->sem_num]; 585 586 DPRINTF(("semop: semaptr=%x, sem_base=%x, semptr=%x, sem[%d]=%d : op=%d, flag=%s\n", 587 semaptr, semaptr->sem_base, semptr, 588 sopptr->sem_num, semptr->semval, sopptr->sem_op, 589 (sopptr->sem_flg & IPC_NOWAIT) ? "nowait" : "wait")); 590 591 if (sopptr->sem_op < 0) { 592 if ((int)(semptr->semval + 593 sopptr->sem_op) < 0) { 594 DPRINTF(("semop: can't do it now\n")); 595 break; 596 } else { 597 semptr->semval += sopptr->sem_op; 598 if (semptr->semval == 0 && 599 semptr->semzcnt > 0) 600 do_wakeup = 1; 601 } 602 if (sopptr->sem_flg & SEM_UNDO) 603 do_undos = 1; 604 } else if (sopptr->sem_op == 0) { 605 if (semptr->semval > 0) { 606 DPRINTF(("semop: not zero now\n")); 607 break; 608 } 609 } else { 610 if (semptr->semncnt > 0) 611 do_wakeup = 1; 612 semptr->semval += sopptr->sem_op; 613 if (sopptr->sem_flg & SEM_UNDO) 614 do_undos = 1; 615 } 616 } 617 618 /* 619 * Did we get through the entire vector? 620 */ 621 if (i >= nsops) 622 goto done; 623 624 /* 625 * No ... rollback anything that we've already done 626 */ 627 DPRINTF(("semop: rollback 0 through %d\n", i - 1)); 628 for (j = 0; j < i; j++) 629 semaptr->sem_base[sops[j].sem_num].semval -= 630 sops[j].sem_op; 631 632 /* 633 * If the request that we couldn't satisfy has the 634 * NOWAIT flag set then return with EAGAIN. 635 */ 636 if (sopptr->sem_flg & IPC_NOWAIT) { 637 free(sops, M_SEM); 638 return (EAGAIN); 639 } 640 641 if (sopptr->sem_op == 0) 642 semptr->semzcnt++; 643 else 644 semptr->semncnt++; 645 646 DPRINTF(("semop: good night!\n")); 647 error = tsleep(&sema[semid], PLOCK | PCATCH, 648 "semwait", 0); 649 DPRINTF(("semop: good morning (error=%d)!\n", error)); 650 651 suptr = NULL; /* sem_undo may have been reallocated */ 652 653 if (error != 0) { 654 free(sops, M_SEM); 655 return (EINTR); 656 } 657 DPRINTF(("semop: good morning!\n")); 658 659 /* 660 * Make sure that the semaphore still exists 661 */ 662 if (sema[semid] == NULL || 663 semaptr->sem_perm.seq != IPCID_TO_SEQ(SCARG(uap, semid))) { 664 free(sops, M_SEM); 665 return (EIDRM); 666 } 667 668 /* 669 * The semaphore is still alive. Readjust the count of 670 * waiting processes. 671 */ 672 if (sopptr->sem_op == 0) 673 semptr->semzcnt--; 674 else 675 semptr->semncnt--; 676 } 677 678 done: 679 /* 680 * Process any SEM_UNDO requests. 681 */ 682 if (do_undos) { 683 for (i = 0; i < nsops; i++) { 684 /* 685 * We only need to deal with SEM_UNDO's for non-zero 686 * op's. 687 */ 688 int adjval; 689 690 if ((sops[i].sem_flg & SEM_UNDO) == 0) 691 continue; 692 adjval = sops[i].sem_op; 693 if (adjval == 0) 694 continue; 695 error = semundo_adjust(p, &suptr, semid, 696 sops[i].sem_num, -adjval); 697 if (error == 0) 698 continue; 699 700 /* 701 * Uh-Oh! We ran out of either sem_undo's or undo's. 702 * Rollback the adjustments to this point and then 703 * rollback the semaphore ups and down so we can return 704 * with an error with all structures restored. We 705 * rollback the undo's in the exact reverse order that 706 * we applied them. This guarantees that we won't run 707 * out of space as we roll things back out. 708 */ 709 for (j = i - 1; j >= 0; j--) { 710 if ((sops[j].sem_flg & SEM_UNDO) == 0) 711 continue; 712 adjval = sops[j].sem_op; 713 if (adjval == 0) 714 continue; 715 if (semundo_adjust(p, &suptr, semid, 716 sops[j].sem_num, adjval) != 0) 717 panic("semop - can't undo undos"); 718 } 719 720 for (j = 0; j < nsops; j++) 721 semaptr->sem_base[sops[j].sem_num].semval -= 722 sops[j].sem_op; 723 724 DPRINTF(("error = %d from semundo_adjust\n", error)); 725 free(sops, M_SEM); 726 return (error); 727 } /* loop through the sops */ 728 } /* if (do_undos) */ 729 730 /* We're definitely done - set the sempid's */ 731 for (i = 0; i < nsops; i++) { 732 sopptr = &sops[i]; 733 semptr = &semaptr->sem_base[sopptr->sem_num]; 734 semptr->sempid = p->p_pid; 735 } 736 free(sops, M_SEM); 737 738 /* Do a wakeup if any semaphore was up'd. */ 739 if (do_wakeup) { 740 DPRINTF(("semop: doing wakeup\n")); 741 wakeup(&sema[semid]); 742 DPRINTF(("semop: back from wakeup\n")); 743 } 744 DPRINTF(("semop: done\n")); 745 *retval = 0; 746 return (0); 747 } 748 749 /* 750 * Go through the undo structures for this process and apply the adjustments to 751 * semaphores. 752 */ 753 void 754 semexit(struct proc *p) 755 { 756 struct sem_undo *suptr; 757 struct sem_undo **supptr; 758 759 /* 760 * Go through the chain of undo vectors looking for one associated with 761 * this process. 762 */ 763 for (supptr = &semu_list; (suptr = *supptr) != NULL; 764 supptr = &suptr->un_next) { 765 if (suptr->un_proc == p) 766 break; 767 } 768 769 /* 770 * No (i.e. we are in case 1 or 2). 771 * 772 * If there is no undo vector, skip to the end and unlock the 773 * semaphore facility if necessary. 774 */ 775 if (suptr == NULL) 776 return; 777 778 /* 779 * We are now in case 1 or 2, and we have an undo vector for this 780 * process. 781 */ 782 DPRINTF(("proc @%p has undo structure with %d entries\n", p, 783 suptr->un_cnt)); 784 785 /* 786 * If there are any active undo elements then process them. 787 */ 788 if (suptr->un_cnt > 0) { 789 int ix; 790 791 for (ix = 0; ix < suptr->un_cnt; ix++) { 792 int semid = suptr->un_ent[ix].un_id; 793 int semnum = suptr->un_ent[ix].un_num; 794 int adjval = suptr->un_ent[ix].un_adjval; 795 struct semid_ds *semaptr; 796 797 if ((semaptr = sema[semid]) == NULL) 798 panic("semexit - semid not allocated"); 799 if (semnum >= semaptr->sem_nsems) 800 panic("semexit - semnum out of range"); 801 802 DPRINTF(("semexit: %p id=%d num=%d(adj=%d) ; sem=%d\n", 803 suptr->un_proc, suptr->un_ent[ix].un_id, 804 suptr->un_ent[ix].un_num, 805 suptr->un_ent[ix].un_adjval, 806 semaptr->sem_base[semnum].semval)); 807 808 if (adjval < 0 && 809 semaptr->sem_base[semnum].semval < -adjval) 810 semaptr->sem_base[semnum].semval = 0; 811 else 812 semaptr->sem_base[semnum].semval += adjval; 813 814 wakeup(&sema[semid]); 815 DPRINTF(("semexit: back from wakeup\n")); 816 } 817 } 818 819 /* 820 * Deallocate the undo vector. 821 */ 822 DPRINTF(("removing vector\n")); 823 *supptr = suptr->un_next; 824 pool_put(&semu_pool, suptr); 825 semutot--; 826 } 827 828 /* 829 * Userland access to struct seminfo. 830 */ 831 int 832 sysctl_sysvsem(int *name, u_int namelen, void *oldp, size_t *oldlenp, 833 void *newp, size_t newlen) 834 { 835 int error, val; 836 struct semid_ds **sema_new; 837 unsigned short *newseqs; 838 839 if (namelen != 2) { 840 switch (name[0]) { 841 case KERN_SEMINFO_SEMMNI: 842 case KERN_SEMINFO_SEMMNS: 843 case KERN_SEMINFO_SEMMNU: 844 case KERN_SEMINFO_SEMMSL: 845 case KERN_SEMINFO_SEMOPM: 846 case KERN_SEMINFO_SEMUME: 847 case KERN_SEMINFO_SEMUSZ: 848 case KERN_SEMINFO_SEMVMX: 849 case KERN_SEMINFO_SEMAEM: 850 break; 851 default: 852 return (ENOTDIR); /* overloaded */ 853 } 854 } 855 856 switch (name[0]) { 857 case KERN_SEMINFO_SEMMNI: 858 val = seminfo.semmni; 859 if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)) || 860 val == seminfo.semmni) 861 return (error); 862 863 if (val < seminfo.semmni || val > 0xffff) 864 return (EINVAL); 865 866 /* Expand semsegs and semseqs arrays */ 867 sema_new = malloc(val * sizeof(struct semid_ds *), 868 M_SEM, M_WAITOK); 869 bcopy(sema, sema_new, 870 seminfo.semmni * sizeof(struct semid_ds *)); 871 bzero(sema_new + seminfo.semmni, 872 (val - seminfo.semmni) * sizeof(struct semid_ds *)); 873 newseqs = malloc(val * sizeof(unsigned short), M_SEM, M_WAITOK); 874 bcopy(semseqs, newseqs, 875 seminfo.semmni * sizeof(unsigned short)); 876 bzero(newseqs + seminfo.semmni, 877 (val - seminfo.semmni) * sizeof(unsigned short)); 878 free(sema, M_SEM); 879 free(semseqs, M_SEM); 880 sema = sema_new; 881 semseqs = newseqs; 882 seminfo.semmni = val; 883 return (0); 884 case KERN_SEMINFO_SEMMNS: 885 val = seminfo.semmns; 886 if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)) || 887 val == seminfo.semmns) 888 return (error); 889 if (val < seminfo.semmns) 890 return (EINVAL); /* can't decrease semmns */ 891 seminfo.semmns = val; 892 return (0); 893 case KERN_SEMINFO_SEMMNU: 894 val = seminfo.semmnu; 895 if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)) || 896 val == seminfo.semmnu) 897 return (error); 898 if (val < seminfo.semmnu) 899 return (EINVAL); /* can't decrease semmnu */ 900 seminfo.semmnu = val; 901 return (0); 902 case KERN_SEMINFO_SEMMSL: 903 val = seminfo.semmsl; 904 if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)) || 905 val == seminfo.semmsl) 906 return (error); 907 if (val < seminfo.semmsl) 908 return (EINVAL); /* can't decrease semmsl */ 909 seminfo.semmsl = val; 910 return (0); 911 case KERN_SEMINFO_SEMOPM: 912 val = seminfo.semopm; 913 if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)) || 914 val == seminfo.semopm) 915 return (error); 916 if (val <= 0) 917 return (EINVAL); /* semopm must be >= 1 */ 918 seminfo.semopm = val; 919 return (0); 920 case KERN_SEMINFO_SEMUME: 921 return (sysctl_rdint(oldp, oldlenp, newp, seminfo.semume)); 922 case KERN_SEMINFO_SEMUSZ: 923 return (sysctl_rdint(oldp, oldlenp, newp, seminfo.semusz)); 924 case KERN_SEMINFO_SEMVMX: 925 return (sysctl_rdint(oldp, oldlenp, newp, seminfo.semvmx)); 926 case KERN_SEMINFO_SEMAEM: 927 return (sysctl_rdint(oldp, oldlenp, newp, seminfo.semaem)); 928 default: 929 return (EOPNOTSUPP); 930 } 931 /* NOTREACHED */ 932 } 933