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