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