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