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