1 /* $OpenBSD: sysv_sem.c,v 1.59 2020/07/08 21:05:42 deraadt 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 union semun arg; 233 int error = 0, cmd = SCARG(uap, cmd); 234 235 switch (cmd) { 236 case IPC_SET: 237 case IPC_STAT: 238 case GETALL: 239 case SETVAL: 240 case SETALL: 241 error = copyin(SCARG(uap, arg), &arg, sizeof(arg)); 242 break; 243 } 244 if (error == 0) { 245 error = semctl1(p, SCARG(uap, semid), SCARG(uap, semnum), 246 cmd, &arg, retval, copyin, copyout); 247 } 248 return (error); 249 } 250 251 int 252 semctl1(struct proc *p, int semid, int semnum, int cmd, union semun *arg, 253 register_t *retval, int (*ds_copyin)(const void *, void *, size_t), 254 int (*ds_copyout)(const void *, void *, size_t)) 255 { 256 struct ucred *cred = p->p_ucred; 257 int i, ix, error = 0; 258 struct semid_ds sbuf; 259 struct semid_ds *semaptr; 260 unsigned short *semval = NULL; 261 262 DPRINTF(("call to semctl(%d, %d, %d, %p)\n", semid, semnum, cmd, arg)); 263 264 ix = IPCID_TO_IX(semid); 265 if (ix < 0 || ix >= seminfo.semmni) 266 return (EINVAL); 267 268 if ((semaptr = sema[ix]) == NULL || 269 semaptr->sem_perm.seq != IPCID_TO_SEQ(semid)) 270 return (EINVAL); 271 272 switch (cmd) { 273 case IPC_RMID: 274 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_M)) != 0) 275 return (error); 276 semaptr->sem_perm.cuid = cred->cr_uid; 277 semaptr->sem_perm.uid = cred->cr_uid; 278 semtot -= semaptr->sem_nsems; 279 free(semaptr->sem_base, M_SEM, 280 semaptr->sem_nsems * sizeof(struct sem)); 281 pool_put(&sema_pool, semaptr); 282 sema[ix] = NULL; 283 semundo_clear(ix, -1); 284 wakeup(&sema[ix]); 285 break; 286 287 case IPC_SET: 288 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_M))) 289 return (error); 290 if ((error = ds_copyin(arg->buf, &sbuf, sizeof(sbuf))) != 0) 291 return (error); 292 semaptr->sem_perm.uid = sbuf.sem_perm.uid; 293 semaptr->sem_perm.gid = sbuf.sem_perm.gid; 294 semaptr->sem_perm.mode = (semaptr->sem_perm.mode & ~0777) | 295 (sbuf.sem_perm.mode & 0777); 296 semaptr->sem_ctime = gettime(); 297 break; 298 299 case IPC_STAT: 300 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R))) 301 return (error); 302 memcpy(&sbuf, semaptr, sizeof sbuf); 303 sbuf.sem_base = NULL; 304 error = ds_copyout(&sbuf, arg->buf, sizeof(struct semid_ds)); 305 break; 306 307 case GETNCNT: 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].semncnt; 313 break; 314 315 case GETPID: 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].sempid; 321 break; 322 323 case GETVAL: 324 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R))) 325 return (error); 326 if (semnum < 0 || semnum >= semaptr->sem_nsems) 327 return (EINVAL); 328 *retval = semaptr->sem_base[semnum].semval; 329 break; 330 331 case GETALL: 332 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R))) 333 return (error); 334 for (i = 0; i < semaptr->sem_nsems; i++) { 335 error = ds_copyout(&semaptr->sem_base[i].semval, 336 &arg->array[i], sizeof(arg->array[0])); 337 if (error != 0) 338 break; 339 } 340 break; 341 342 case GETZCNT: 343 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R))) 344 return (error); 345 if (semnum < 0 || semnum >= semaptr->sem_nsems) 346 return (EINVAL); 347 *retval = semaptr->sem_base[semnum].semzcnt; 348 break; 349 350 case SETVAL: 351 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_W))) 352 return (error); 353 if (semnum < 0 || semnum >= semaptr->sem_nsems) 354 return (EINVAL); 355 if (arg->val > seminfo.semvmx) 356 return (ERANGE); 357 semaptr->sem_base[semnum].semval = arg->val; 358 semundo_clear(ix, semnum); 359 wakeup(&sema[ix]); 360 break; 361 362 case SETALL: 363 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_W))) 364 return (error); 365 semval = mallocarray(semaptr->sem_nsems, sizeof(arg->array[0]), 366 M_TEMP, M_WAITOK); 367 for (i = 0; i < semaptr->sem_nsems; i++) { 368 error = ds_copyin(&arg->array[i], &semval[i], 369 sizeof(arg->array[0])); 370 if (error != 0) 371 goto error; 372 if (semval[i] > seminfo.semvmx) { 373 error = ERANGE; 374 goto error; 375 } 376 } 377 for (i = 0; i < semaptr->sem_nsems; i++) 378 semaptr->sem_base[i].semval = semval[i]; 379 semundo_clear(ix, -1); 380 wakeup(&sema[ix]); 381 break; 382 383 default: 384 return (EINVAL); 385 } 386 387 error: 388 if (semval) 389 free(semval, M_TEMP, 390 semaptr->sem_nsems * sizeof(arg->array[0])); 391 392 return (error); 393 } 394 395 int 396 sys_semget(struct proc *p, void *v, register_t *retval) 397 { 398 struct sys_semget_args /* { 399 syscallarg(key_t) key; 400 syscallarg(int) nsems; 401 syscallarg(int) semflg; 402 } */ *uap = v; 403 int semid, error; 404 int key = SCARG(uap, key); 405 int nsems = SCARG(uap, nsems); 406 int semflg = SCARG(uap, semflg); 407 struct semid_ds *semaptr, *semaptr_new = NULL; 408 struct ucred *cred = p->p_ucred; 409 410 DPRINTF(("semget(0x%x, %d, 0%o)\n", key, nsems, semflg)); 411 412 /* 413 * Preallocate space for the new semaphore. If we are going 414 * to sleep, we want to sleep now to eliminate any race 415 * condition in allocating a semaphore with a specific key. 416 */ 417 if (key == IPC_PRIVATE || (semflg & IPC_CREAT)) { 418 if (nsems <= 0 || nsems > seminfo.semmsl) { 419 DPRINTF(("nsems out of range (0<%d<=%d)\n", nsems, 420 seminfo.semmsl)); 421 return (EINVAL); 422 } 423 if (nsems > seminfo.semmns - semtot) { 424 DPRINTF(("not enough semaphores left (need %d, got %d)\n", 425 nsems, seminfo.semmns - semtot)); 426 return (ENOSPC); 427 } 428 semaptr_new = pool_get(&sema_pool, PR_WAITOK | PR_ZERO); 429 semaptr_new->sem_base = mallocarray(nsems, sizeof(struct sem), 430 M_SEM, M_WAITOK|M_ZERO); 431 } 432 433 if (key != IPC_PRIVATE) { 434 for (semid = 0, semaptr = NULL; semid < seminfo.semmni; semid++) { 435 if ((semaptr = sema[semid]) != NULL && 436 semaptr->sem_perm.key == key) { 437 DPRINTF(("found public key\n")); 438 if ((error = ipcperm(cred, &semaptr->sem_perm, 439 semflg & 0700))) 440 goto error; 441 if (nsems > 0 && semaptr->sem_nsems < nsems) { 442 DPRINTF(("too small\n")); 443 error = EINVAL; 444 goto error; 445 } 446 if ((semflg & IPC_CREAT) && (semflg & IPC_EXCL)) { 447 DPRINTF(("not exclusive\n")); 448 error = EEXIST; 449 goto error; 450 } 451 if (semaptr_new != NULL) { 452 free(semaptr_new->sem_base, M_SEM, 453 nsems * sizeof(struct sem)); 454 pool_put(&sema_pool, semaptr_new); 455 } 456 goto found; 457 } 458 } 459 } 460 461 DPRINTF(("need to allocate the semid_ds\n")); 462 if (key == IPC_PRIVATE || (semflg & IPC_CREAT)) { 463 for (semid = 0; semid < seminfo.semmni; semid++) { 464 if ((semaptr = sema[semid]) == NULL) 465 break; 466 } 467 if (semid == seminfo.semmni) { 468 DPRINTF(("no more semid_ds's available\n")); 469 error = ENOSPC; 470 goto error; 471 } 472 DPRINTF(("semid %d is available\n", semid)); 473 semaptr_new->sem_perm.key = key; 474 semaptr_new->sem_perm.cuid = cred->cr_uid; 475 semaptr_new->sem_perm.uid = cred->cr_uid; 476 semaptr_new->sem_perm.cgid = cred->cr_gid; 477 semaptr_new->sem_perm.gid = cred->cr_gid; 478 semaptr_new->sem_perm.mode = (semflg & 0777); 479 semaptr_new->sem_perm.seq = semseqs[semid] = 480 (semseqs[semid] + 1) & 0x7fff; 481 semaptr_new->sem_nsems = nsems; 482 semaptr_new->sem_otime = 0; 483 semaptr_new->sem_ctime = gettime(); 484 sema[semid] = semaptr_new; 485 semtot += nsems; 486 } else { 487 DPRINTF(("didn't find it and wasn't asked to create it\n")); 488 return (ENOENT); 489 } 490 491 found: 492 *retval = IXSEQ_TO_IPCID(semid, sema[semid]->sem_perm); 493 return (0); 494 error: 495 if (semaptr_new != NULL) { 496 free(semaptr_new->sem_base, M_SEM, nsems * sizeof(struct sem)); 497 pool_put(&sema_pool, semaptr_new); 498 } 499 return (error); 500 } 501 502 int 503 sys_semop(struct proc *p, void *v, register_t *retval) 504 { 505 struct sys_semop_args /* { 506 syscallarg(int) semid; 507 syscallarg(struct sembuf *) sops; 508 syscallarg(size_t) nsops; 509 } */ *uap = v; 510 #define NSOPS 8 511 struct sembuf sopbuf[NSOPS]; 512 int semid = SCARG(uap, semid); 513 size_t nsops = SCARG(uap, nsops); 514 struct sembuf *sops; 515 struct semid_ds *semaptr; 516 struct sembuf *sopptr = NULL; 517 struct sem *semptr = NULL; 518 struct sem_undo *suptr = NULL; 519 struct ucred *cred = p->p_ucred; 520 size_t i, j; 521 int do_wakeup, do_undos, error; 522 523 DPRINTF(("call to semop(%d, %p, %lu)\n", semid, SCARG(uap, sops), 524 (u_long)nsops)); 525 526 semid = IPCID_TO_IX(semid); /* Convert back to zero origin */ 527 528 if (semid < 0 || semid >= seminfo.semmni) 529 return (EINVAL); 530 531 if ((semaptr = sema[semid]) == NULL || 532 semaptr->sem_perm.seq != IPCID_TO_SEQ(SCARG(uap, semid))) 533 return (EINVAL); 534 535 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_W))) { 536 DPRINTF(("error = %d from ipaccess\n", error)); 537 return (error); 538 } 539 540 if (nsops == 0) { 541 *retval = 0; 542 return (0); 543 } else if (nsops > (size_t)seminfo.semopm) { 544 DPRINTF(("too many sops (max=%d, nsops=%lu)\n", seminfo.semopm, 545 (u_long)nsops)); 546 return (E2BIG); 547 } 548 549 if (nsops <= NSOPS) 550 sops = sopbuf; 551 else 552 sops = mallocarray(nsops, sizeof(struct sembuf), M_SEM, M_WAITOK); 553 error = copyin(SCARG(uap, sops), sops, nsops * sizeof(struct sembuf)); 554 if (error != 0) { 555 DPRINTF(("error = %d from copyin(%p, %p, %u)\n", error, 556 SCARG(uap, sops), &sops, nsops * sizeof(struct sembuf))); 557 goto done2; 558 } 559 560 /* 561 * Loop trying to satisfy the vector of requests. 562 * If we reach a point where we must wait, any requests already 563 * performed are rolled back and we go to sleep until some other 564 * process wakes us up. At this point, we start all over again. 565 * 566 * This ensures that from the perspective of other tasks, a set 567 * of requests is atomic (never partially satisfied). 568 */ 569 do_undos = 0; 570 571 for (;;) { 572 do_wakeup = 0; 573 574 for (i = 0; i < nsops; i++) { 575 sopptr = &sops[i]; 576 577 if (sopptr->sem_num >= semaptr->sem_nsems) { 578 error = EFBIG; 579 goto done2; 580 } 581 582 semptr = &semaptr->sem_base[sopptr->sem_num]; 583 584 DPRINTF(("semop: semaptr=%x, sem_base=%x, semptr=%x, sem[%d]=%d : op=%d, flag=%s\n", 585 semaptr, semaptr->sem_base, semptr, 586 sopptr->sem_num, semptr->semval, sopptr->sem_op, 587 (sopptr->sem_flg & IPC_NOWAIT) ? "nowait" : "wait")); 588 589 if (sopptr->sem_op < 0) { 590 if ((int)(semptr->semval + 591 sopptr->sem_op) < 0) { 592 DPRINTF(("semop: can't do it now\n")); 593 break; 594 } else { 595 semptr->semval += sopptr->sem_op; 596 if (semptr->semval == 0 && 597 semptr->semzcnt > 0) 598 do_wakeup = 1; 599 } 600 if (sopptr->sem_flg & SEM_UNDO) 601 do_undos++; 602 } else if (sopptr->sem_op == 0) { 603 if (semptr->semval > 0) { 604 DPRINTF(("semop: not zero now\n")); 605 break; 606 } 607 } else { 608 if (semptr->semncnt > 0) 609 do_wakeup = 1; 610 semptr->semval += sopptr->sem_op; 611 if (sopptr->sem_flg & SEM_UNDO) 612 do_undos++; 613 } 614 } 615 616 /* 617 * Did we get through the entire vector and can we undo it? 618 */ 619 if (i >= nsops && do_undos <= SEMUME) 620 goto done; 621 622 /* 623 * No ... rollback anything that we've already done 624 */ 625 DPRINTF(("semop: rollback 0 through %d\n", i - 1)); 626 for (j = 0; j < i; j++) 627 semaptr->sem_base[sops[j].sem_num].semval -= 628 sops[j].sem_op; 629 630 /* 631 * Did we have too many SEM_UNDO's 632 */ 633 if (do_undos > SEMUME) { 634 error = ENOSPC; 635 goto done2; 636 } 637 638 /* 639 * If the request that we couldn't satisfy has the 640 * NOWAIT flag set then return with EAGAIN. 641 */ 642 if (sopptr->sem_flg & IPC_NOWAIT) { 643 error = EAGAIN; 644 goto done2; 645 } 646 647 if (sopptr->sem_op == 0) 648 semptr->semzcnt++; 649 else 650 semptr->semncnt++; 651 652 DPRINTF(("semop: good night!\n")); 653 error = tsleep_nsec(&sema[semid], PLOCK | PCATCH, 654 "semwait", INFSLP); 655 DPRINTF(("semop: good morning (error=%d)!\n", error)); 656 657 suptr = NULL; /* sem_undo may have been reallocated */ 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 error = EIDRM; 665 goto done2; 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 * Is it really morning, or was our sleep interrupted? 679 * (Delayed check of tsleep() return code because we 680 * need to decrement sem[nz]cnt either way.) 681 */ 682 if (error != 0) { 683 error = EINTR; 684 goto done2; 685 } 686 DPRINTF(("semop: good morning!\n")); 687 } 688 689 done: 690 /* 691 * Process any SEM_UNDO requests. 692 */ 693 if (do_undos) { 694 for (i = 0; i < nsops; i++) { 695 /* 696 * We only need to deal with SEM_UNDO's for non-zero 697 * op's. 698 */ 699 int adjval; 700 701 if ((sops[i].sem_flg & SEM_UNDO) == 0) 702 continue; 703 adjval = sops[i].sem_op; 704 if (adjval == 0) 705 continue; 706 error = semundo_adjust(p, &suptr, semid, 707 sops[i].sem_num, -adjval); 708 if (error == 0) 709 continue; 710 711 /* 712 * Uh-Oh! We ran out of either sem_undo's or undo's. 713 * Rollback the adjustments to this point and then 714 * rollback the semaphore ups and down so we can return 715 * with an error with all structures restored. We 716 * rollback the undo's in the exact reverse order that 717 * we applied them. This guarantees that we won't run 718 * out of space as we roll things back out. 719 */ 720 for (j = i; j > 0;) { 721 j--; 722 if ((sops[j].sem_flg & SEM_UNDO) == 0) 723 continue; 724 adjval = sops[j].sem_op; 725 if (adjval == 0) 726 continue; 727 if (semundo_adjust(p, &suptr, semid, 728 sops[j].sem_num, adjval) != 0) 729 panic("semop - can't undo undos"); 730 } 731 732 for (j = 0; j < nsops; j++) 733 semaptr->sem_base[sops[j].sem_num].semval -= 734 sops[j].sem_op; 735 736 DPRINTF(("error = %d from semundo_adjust\n", error)); 737 goto done2; 738 } /* loop through the sops */ 739 } /* if (do_undos) */ 740 741 /* We're definitely done - set the sempid's */ 742 for (i = 0; i < nsops; i++) { 743 sopptr = &sops[i]; 744 semptr = &semaptr->sem_base[sopptr->sem_num]; 745 semptr->sempid = p->p_p->ps_pid; 746 } 747 748 semaptr->sem_otime = gettime(); 749 750 /* Do a wakeup if any semaphore was up'd. */ 751 if (do_wakeup) { 752 DPRINTF(("semop: doing wakeup\n")); 753 wakeup(&sema[semid]); 754 DPRINTF(("semop: back from wakeup\n")); 755 } 756 DPRINTF(("semop: done\n")); 757 *retval = 0; 758 done2: 759 if (sops != sopbuf) 760 free(sops, M_SEM, nsops * sizeof(struct sembuf)); 761 return (error); 762 } 763 764 /* 765 * Go through the undo structures for this process and apply the adjustments to 766 * semaphores. 767 */ 768 void 769 semexit(struct process *pr) 770 { 771 struct sem_undo *suptr; 772 struct sem_undo **supptr; 773 774 /* 775 * Go through the chain of undo vectors looking for one associated with 776 * this process. Remember the pointer to the pointer to the element 777 * to dequeue it later. 778 */ 779 supptr = &SLIST_FIRST(&semu_list); 780 SLIST_FOREACH(suptr, &semu_list, un_next) { 781 if (suptr->un_proc == pr) 782 break; 783 supptr = &SLIST_NEXT(suptr, un_next); 784 } 785 786 /* 787 * If there is no undo vector, skip to the end. 788 */ 789 if (suptr == NULL) 790 return; 791 792 /* 793 * We now have an undo vector for this process. 794 */ 795 DPRINTF(("process @%p has undo structure with %d entries\n", pr, 796 suptr->un_cnt)); 797 798 /* 799 * If there are any active undo elements then process them. 800 */ 801 if (suptr->un_cnt > 0) { 802 int ix; 803 804 for (ix = 0; ix < suptr->un_cnt; ix++) { 805 int semid = suptr->un_ent[ix].un_id; 806 int semnum = suptr->un_ent[ix].un_num; 807 int adjval = suptr->un_ent[ix].un_adjval; 808 struct semid_ds *semaptr; 809 810 if ((semaptr = sema[semid]) == NULL) 811 panic("semexit - semid not allocated"); 812 if (semnum >= semaptr->sem_nsems) 813 panic("semexit - semnum out of range"); 814 815 DPRINTF(("semexit: %p id=%d num=%d(adj=%d) ; sem=%d\n", 816 suptr->un_proc, suptr->un_ent[ix].un_id, 817 suptr->un_ent[ix].un_num, 818 suptr->un_ent[ix].un_adjval, 819 semaptr->sem_base[semnum].semval)); 820 821 if (adjval < 0 && 822 semaptr->sem_base[semnum].semval < -adjval) 823 semaptr->sem_base[semnum].semval = 0; 824 else 825 semaptr->sem_base[semnum].semval += adjval; 826 827 wakeup(&sema[semid]); 828 DPRINTF(("semexit: back from wakeup\n")); 829 } 830 } 831 832 /* 833 * Deallocate the undo vector. 834 */ 835 DPRINTF(("removing vector\n")); 836 *supptr = SLIST_NEXT(suptr, un_next); 837 pool_put(&semu_pool, suptr); 838 semutot--; 839 } 840 841 /* 842 * Userland access to struct seminfo. 843 */ 844 int 845 sysctl_sysvsem(int *name, u_int namelen, void *oldp, size_t *oldlenp, 846 void *newp, size_t newlen) 847 { 848 int error, val; 849 struct semid_ds **sema_new; 850 unsigned short *newseqs; 851 852 if (namelen != 2) { 853 switch (name[0]) { 854 case KERN_SEMINFO_SEMMNI: 855 case KERN_SEMINFO_SEMMNS: 856 case KERN_SEMINFO_SEMMNU: 857 case KERN_SEMINFO_SEMMSL: 858 case KERN_SEMINFO_SEMOPM: 859 case KERN_SEMINFO_SEMUME: 860 case KERN_SEMINFO_SEMUSZ: 861 case KERN_SEMINFO_SEMVMX: 862 case KERN_SEMINFO_SEMAEM: 863 break; 864 default: 865 return (ENOTDIR); /* overloaded */ 866 } 867 } 868 869 switch (name[0]) { 870 case KERN_SEMINFO_SEMMNI: 871 val = seminfo.semmni; 872 if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)) || 873 val == seminfo.semmni) 874 return (error); 875 876 if (val < seminfo.semmni || val > 0xffff) 877 return (EINVAL); 878 879 /* Expand semsegs and semseqs arrays */ 880 sema_new = mallocarray(val, sizeof(struct semid_ds *), 881 M_SEM, M_WAITOK|M_ZERO); 882 memcpy(sema_new, sema, 883 seminfo.semmni * sizeof(struct semid_ds *)); 884 newseqs = mallocarray(val, sizeof(unsigned short), M_SEM, 885 M_WAITOK|M_ZERO); 886 memcpy(newseqs, semseqs, 887 seminfo.semmni * sizeof(unsigned short)); 888 free(sema, M_SEM, seminfo.semmni * sizeof(struct semid_ds *)); 889 free(semseqs, M_SEM, seminfo.semmni * sizeof(unsigned short)); 890 sema = sema_new; 891 semseqs = newseqs; 892 seminfo.semmni = val; 893 return (0); 894 case KERN_SEMINFO_SEMMNS: 895 val = seminfo.semmns; 896 if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)) || 897 val == seminfo.semmns) 898 return (error); 899 if (val < seminfo.semmns || val > 0xffff) 900 return (EINVAL); /* can't decrease semmns */ 901 seminfo.semmns = val; 902 return (0); 903 case KERN_SEMINFO_SEMMNU: 904 val = seminfo.semmnu; 905 if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)) || 906 val == seminfo.semmnu) 907 return (error); 908 if (val < seminfo.semmnu) 909 return (EINVAL); /* can't decrease semmnu */ 910 seminfo.semmnu = val; 911 return (0); 912 case KERN_SEMINFO_SEMMSL: 913 val = seminfo.semmsl; 914 if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)) || 915 val == seminfo.semmsl) 916 return (error); 917 if (val < seminfo.semmsl || val > 0xffff) 918 return (EINVAL); /* can't decrease semmsl */ 919 seminfo.semmsl = val; 920 return (0); 921 case KERN_SEMINFO_SEMOPM: 922 val = seminfo.semopm; 923 if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)) || 924 val == seminfo.semopm) 925 return (error); 926 if (val <= 0) 927 return (EINVAL); /* semopm must be >= 1 */ 928 seminfo.semopm = val; 929 return (0); 930 case KERN_SEMINFO_SEMUME: 931 return (sysctl_rdint(oldp, oldlenp, newp, seminfo.semume)); 932 case KERN_SEMINFO_SEMUSZ: 933 return (sysctl_rdint(oldp, oldlenp, newp, seminfo.semusz)); 934 case KERN_SEMINFO_SEMVMX: 935 return (sysctl_rdint(oldp, oldlenp, newp, seminfo.semvmx)); 936 case KERN_SEMINFO_SEMAEM: 937 return (sysctl_rdint(oldp, oldlenp, newp, seminfo.semaem)); 938 default: 939 return (EOPNOTSUPP); 940 } 941 /* NOTREACHED */ 942 } 943