1 /* $OpenBSD: sysv_sem.c,v 1.41 2011/02/02 09:33:11 fgsch 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/kernel.h> 34 #include <sys/proc.h> 35 #include <sys/sem.h> 36 #include <sys/sysctl.h> 37 #include <sys/malloc.h> 38 #include <sys/pool.h> 39 40 #include <sys/mount.h> 41 #include <sys/syscallargs.h> 42 43 #ifdef SEM_DEBUG 44 #define DPRINTF(x) printf x 45 #else 46 #define DPRINTF(x) 47 #endif 48 49 int semtot = 0; 50 int semutot = 0; 51 struct semid_ds **sema; /* semaphore id list */ 52 SLIST_HEAD(, sem_undo) semu_list; /* list of undo structures */ 53 struct pool sema_pool; /* pool for struct semid_ds */ 54 struct pool semu_pool; /* pool for struct sem_undo (SEMUSZ) */ 55 unsigned short *semseqs; /* array of sem sequence numbers */ 56 57 struct sem_undo *semu_alloc(struct process *); 58 int semundo_adjust(struct proc *, struct sem_undo **, int, int, int); 59 void semundo_clear(int, int); 60 61 void 62 seminit(void) 63 { 64 65 pool_init(&sema_pool, sizeof(struct semid_ds), 0, 0, 0, "semapl", 66 &pool_allocator_nointr); 67 pool_init(&semu_pool, SEMUSZ, 0, 0, 0, "semupl", 68 &pool_allocator_nointr); 69 sema = malloc(seminfo.semmni * sizeof(struct semid_ds *), 70 M_SEM, M_WAITOK|M_ZERO); 71 semseqs = malloc(seminfo.semmni * sizeof(unsigned short), 72 M_SEM, M_WAITOK|M_ZERO); 73 SLIST_INIT(&semu_list); 74 } 75 76 /* 77 * Allocate a new sem_undo structure for a process 78 * (returns ptr to structure or NULL if no more room) 79 */ 80 struct sem_undo * 81 semu_alloc(struct process *pr) 82 { 83 struct sem_undo *suptr, *sutmp; 84 85 if (semutot == seminfo.semmnu) 86 return (NULL); /* no space */ 87 88 /* 89 * Allocate a semu w/o waiting if possible. 90 * If we do have to wait, we must check to verify that a semu 91 * with un_proc == pr has not been allocated in the meantime. 92 */ 93 semutot++; 94 if ((suptr = pool_get(&semu_pool, PR_NOWAIT)) == NULL) { 95 sutmp = pool_get(&semu_pool, PR_WAITOK); 96 SLIST_FOREACH(suptr, &semu_list, un_next) { 97 if (suptr->un_proc == pr) { 98 pool_put(&semu_pool, sutmp); 99 semutot--; 100 return (suptr); 101 } 102 } 103 suptr = sutmp; 104 } 105 suptr->un_cnt = 0; 106 suptr->un_proc = pr; 107 SLIST_INSERT_HEAD(&semu_list, suptr, un_next); 108 return (suptr); 109 } 110 111 /* 112 * Adjust a particular entry for a particular proc 113 */ 114 int 115 semundo_adjust(struct proc *p, struct sem_undo **supptr, int semid, int semnum, 116 int adjval) 117 { 118 struct process *pr = p->p_p; 119 struct sem_undo *suptr; 120 struct undo *sunptr; 121 int i; 122 123 /* 124 * Look for and remember the sem_undo if the caller doesn't provide it. 125 */ 126 suptr = *supptr; 127 if (suptr == NULL) { 128 SLIST_FOREACH(suptr, &semu_list, un_next) { 129 if (suptr->un_proc == pr) { 130 *supptr = suptr; 131 break; 132 } 133 } 134 if (suptr == NULL) { 135 if (adjval == 0) 136 return (0); 137 suptr = semu_alloc(p->p_p); 138 if (suptr == NULL) 139 return (ENOSPC); 140 *supptr = suptr; 141 } 142 } 143 144 /* 145 * Look for the requested entry and adjust it 146 * (delete if adjval becomes 0). 147 */ 148 sunptr = &suptr->un_ent[0]; 149 for (i = 0; i < suptr->un_cnt; i++, sunptr++) { 150 if (sunptr->un_id != semid || sunptr->un_num != semnum) 151 continue; 152 if (adjval == 0) 153 sunptr->un_adjval = 0; 154 else 155 sunptr->un_adjval += adjval; 156 if (sunptr->un_adjval != 0) 157 return (0); 158 159 if (--suptr->un_cnt == 0) { 160 SLIST_REMOVE(&semu_list, suptr, sem_undo, un_next); 161 pool_put(&semu_pool, suptr); 162 semutot--; 163 } else if (i < suptr->un_cnt) 164 suptr->un_ent[i] = 165 suptr->un_ent[suptr->un_cnt]; 166 return (0); 167 } 168 169 /* Didn't find the right entry - create it */ 170 if (adjval == 0) 171 return (0); 172 if (suptr->un_cnt == SEMUME) 173 return (EINVAL); 174 175 sunptr = &suptr->un_ent[suptr->un_cnt]; 176 suptr->un_cnt++; 177 sunptr->un_adjval = adjval; 178 sunptr->un_id = semid; 179 sunptr->un_num = semnum; 180 return (0); 181 } 182 183 void 184 semundo_clear(int semid, int semnum) 185 { 186 struct sem_undo *suptr = SLIST_FIRST(&semu_list); 187 struct sem_undo *suprev = SLIST_END(&semu_list); 188 struct undo *sunptr; 189 int i; 190 191 while (suptr != SLIST_END(&semu_list)) { 192 sunptr = &suptr->un_ent[0]; 193 for (i = 0; i < suptr->un_cnt; i++, sunptr++) { 194 if (sunptr->un_id == semid) { 195 if (semnum == -1 || sunptr->un_num == semnum) { 196 suptr->un_cnt--; 197 if (i < suptr->un_cnt) { 198 suptr->un_ent[i] = 199 suptr->un_ent[suptr->un_cnt]; 200 i--, sunptr--; 201 } 202 } 203 if (semnum != -1) 204 break; 205 } 206 } 207 if (suptr->un_cnt == 0) { 208 struct sem_undo *sutmp = suptr; 209 210 if (suptr == SLIST_FIRST(&semu_list)) 211 SLIST_REMOVE_HEAD(&semu_list, un_next); 212 else 213 SLIST_REMOVE_NEXT(&semu_list, suprev, un_next); 214 suptr = SLIST_NEXT(suptr, un_next); 215 pool_put(&semu_pool, sutmp); 216 semutot--; 217 } else { 218 suprev = suptr; 219 suptr = SLIST_NEXT(suptr, un_next); 220 } 221 } 222 } 223 224 int 225 sys___semctl(struct proc *p, void *v, register_t *retval) 226 { 227 struct sys___semctl_args /* { 228 syscallarg(int) semid; 229 syscallarg(int) semnum; 230 syscallarg(int) cmd; 231 syscallarg(union semun *) arg; 232 } */ *uap = v; 233 union semun arg; 234 int error = 0, cmd = SCARG(uap, cmd); 235 236 switch (cmd) { 237 case IPC_SET: 238 case IPC_STAT: 239 case GETALL: 240 case SETVAL: 241 case SETALL: 242 error = copyin(SCARG(uap, arg), &arg, sizeof(arg)); 243 break; 244 } 245 if (error == 0) { 246 error = semctl1(p, SCARG(uap, semid), SCARG(uap, semnum), 247 cmd, &arg, retval, copyin, copyout); 248 } 249 return (error); 250 } 251 252 int 253 semctl1(struct proc *p, int semid, int semnum, int cmd, union semun *arg, 254 register_t *retval, int (*ds_copyin)(const void *, void *, size_t), 255 int (*ds_copyout)(const void *, void *, size_t)) 256 { 257 struct ucred *cred = p->p_ucred; 258 int i, ix, error = 0; 259 struct semid_ds sbuf; 260 struct semid_ds *semaptr; 261 unsigned short *semval = NULL; 262 263 DPRINTF(("call to semctl(%d, %d, %d, %p)\n", semid, semnum, cmd, arg)); 264 265 ix = IPCID_TO_IX(semid); 266 if (ix < 0 || ix >= seminfo.semmni) 267 return (EINVAL); 268 269 if ((semaptr = sema[ix]) == NULL || 270 semaptr->sem_perm.seq != IPCID_TO_SEQ(semid)) 271 return (EINVAL); 272 273 switch (cmd) { 274 case IPC_RMID: 275 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_M)) != 0) 276 return (error); 277 semaptr->sem_perm.cuid = cred->cr_uid; 278 semaptr->sem_perm.uid = cred->cr_uid; 279 semtot -= semaptr->sem_nsems; 280 free(semaptr->sem_base, M_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 = malloc(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 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 = malloc(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 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); 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 = malloc(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 = 1; 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 = 1; 609 } 610 } 611 612 /* 613 * Did we get through the entire vector? 614 */ 615 if (i >= nsops) 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 * If the request that we couldn't satisfy has the 628 * NOWAIT flag set then return with EAGAIN. 629 */ 630 if (sopptr->sem_flg & IPC_NOWAIT) { 631 error = EAGAIN; 632 goto done2; 633 } 634 635 if (sopptr->sem_op == 0) 636 semptr->semzcnt++; 637 else 638 semptr->semncnt++; 639 640 DPRINTF(("semop: good night!\n")); 641 error = tsleep(&sema[semid], PLOCK | PCATCH, 642 "semwait", 0); 643 DPRINTF(("semop: good morning (error=%d)!\n", error)); 644 645 suptr = NULL; /* sem_undo may have been reallocated */ 646 647 /* 648 * Make sure that the semaphore still exists 649 */ 650 if (sema[semid] == NULL || 651 semaptr->sem_perm.seq != IPCID_TO_SEQ(SCARG(uap, semid))) { 652 error = EIDRM; 653 goto done2; 654 } 655 656 /* 657 * The semaphore is still alive. Readjust the count of 658 * waiting processes. 659 */ 660 if (sopptr->sem_op == 0) 661 semptr->semzcnt--; 662 else 663 semptr->semncnt--; 664 665 /* 666 * Is it really morning, or was our sleep interrupted? 667 * (Delayed check of tsleep() return code because we 668 * need to decrement sem[nz]cnt either way.) 669 */ 670 if (error != 0) { 671 error = EINTR; 672 goto done2; 673 } 674 DPRINTF(("semop: good morning!\n")); 675 } 676 677 done: 678 /* 679 * Process any SEM_UNDO requests. 680 */ 681 if (do_undos) { 682 for (i = 0; i < nsops; i++) { 683 /* 684 * We only need to deal with SEM_UNDO's for non-zero 685 * op's. 686 */ 687 int adjval; 688 689 if ((sops[i].sem_flg & SEM_UNDO) == 0) 690 continue; 691 adjval = sops[i].sem_op; 692 if (adjval == 0) 693 continue; 694 error = semundo_adjust(p, &suptr, semid, 695 sops[i].sem_num, -adjval); 696 if (error == 0) 697 continue; 698 699 /* 700 * Uh-Oh! We ran out of either sem_undo's or undo's. 701 * Rollback the adjustments to this point and then 702 * rollback the semaphore ups and down so we can return 703 * with an error with all structures restored. We 704 * rollback the undo's in the exact reverse order that 705 * we applied them. This guarantees that we won't run 706 * out of space as we roll things back out. 707 */ 708 if (i != 0) { 709 for (j = i - 1; j >= 0; j--) { 710 if ((sops[j].sem_flg & SEM_UNDO) == 0) 711 continue; 712 adjval = sops[j].sem_op; 713 if (adjval == 0) 714 continue; 715 if (semundo_adjust(p, &suptr, semid, 716 sops[j].sem_num, adjval) != 0) 717 panic("semop - can't undo undos"); 718 } 719 } 720 721 for (j = 0; j < nsops; j++) 722 semaptr->sem_base[sops[j].sem_num].semval -= 723 sops[j].sem_op; 724 725 DPRINTF(("error = %d from semundo_adjust\n", error)); 726 goto done2; 727 } /* loop through the sops */ 728 } /* if (do_undos) */ 729 730 /* We're definitely done - set the sempid's */ 731 for (i = 0; i < nsops; i++) { 732 sopptr = &sops[i]; 733 semptr = &semaptr->sem_base[sopptr->sem_num]; 734 semptr->sempid = p->p_p->ps_mainproc->p_pid; 735 } 736 737 semaptr->sem_otime = time_second; 738 739 /* Do a wakeup if any semaphore was up'd. */ 740 if (do_wakeup) { 741 DPRINTF(("semop: doing wakeup\n")); 742 wakeup(&sema[semid]); 743 DPRINTF(("semop: back from wakeup\n")); 744 } 745 DPRINTF(("semop: done\n")); 746 *retval = 0; 747 done2: 748 if (sops != sopbuf) 749 free(sops, M_SEM); 750 return (error); 751 } 752 753 /* 754 * Go through the undo structures for this process and apply the adjustments to 755 * semaphores. 756 */ 757 void 758 semexit(struct process *pr) 759 { 760 struct sem_undo *suptr; 761 struct sem_undo **supptr; 762 763 /* 764 * Go through the chain of undo vectors looking for one associated with 765 * this process. 766 */ 767 SLIST_FOREACH_PREVPTR(suptr, supptr, &semu_list, un_next) { 768 if (suptr->un_proc == pr) 769 break; 770 } 771 772 /* 773 * If there is no undo vector, skip to the end. 774 */ 775 if (suptr == NULL) 776 return; 777 778 /* 779 * We now have an undo vector for this process. 780 */ 781 DPRINTF(("process @%p has undo structure with %d entries\n", pr, 782 suptr->un_cnt)); 783 784 /* 785 * If there are any active undo elements then process them. 786 */ 787 if (suptr->un_cnt > 0) { 788 int ix; 789 790 for (ix = 0; ix < suptr->un_cnt; ix++) { 791 int semid = suptr->un_ent[ix].un_id; 792 int semnum = suptr->un_ent[ix].un_num; 793 int adjval = suptr->un_ent[ix].un_adjval; 794 struct semid_ds *semaptr; 795 796 if ((semaptr = sema[semid]) == NULL) 797 panic("semexit - semid not allocated"); 798 if (semnum >= semaptr->sem_nsems) 799 panic("semexit - semnum out of range"); 800 801 DPRINTF(("semexit: %p id=%d num=%d(adj=%d) ; sem=%d\n", 802 suptr->un_proc, suptr->un_ent[ix].un_id, 803 suptr->un_ent[ix].un_num, 804 suptr->un_ent[ix].un_adjval, 805 semaptr->sem_base[semnum].semval)); 806 807 if (adjval < 0 && 808 semaptr->sem_base[semnum].semval < -adjval) 809 semaptr->sem_base[semnum].semval = 0; 810 else 811 semaptr->sem_base[semnum].semval += adjval; 812 813 wakeup(&sema[semid]); 814 DPRINTF(("semexit: back from wakeup\n")); 815 } 816 } 817 818 /* 819 * Deallocate the undo vector. 820 */ 821 DPRINTF(("removing vector\n")); 822 *supptr = SLIST_NEXT(suptr, un_next); 823 pool_put(&semu_pool, suptr); 824 semutot--; 825 } 826 827 /* 828 * Userland access to struct seminfo. 829 */ 830 int 831 sysctl_sysvsem(int *name, u_int namelen, void *oldp, size_t *oldlenp, 832 void *newp, size_t newlen) 833 { 834 int error, val; 835 struct semid_ds **sema_new; 836 unsigned short *newseqs; 837 838 if (namelen != 2) { 839 switch (name[0]) { 840 case KERN_SEMINFO_SEMMNI: 841 case KERN_SEMINFO_SEMMNS: 842 case KERN_SEMINFO_SEMMNU: 843 case KERN_SEMINFO_SEMMSL: 844 case KERN_SEMINFO_SEMOPM: 845 case KERN_SEMINFO_SEMUME: 846 case KERN_SEMINFO_SEMUSZ: 847 case KERN_SEMINFO_SEMVMX: 848 case KERN_SEMINFO_SEMAEM: 849 break; 850 default: 851 return (ENOTDIR); /* overloaded */ 852 } 853 } 854 855 switch (name[0]) { 856 case KERN_SEMINFO_SEMMNI: 857 val = seminfo.semmni; 858 if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)) || 859 val == seminfo.semmni) 860 return (error); 861 862 if (val < seminfo.semmni || val > 0xffff) 863 return (EINVAL); 864 865 /* Expand semsegs and semseqs arrays */ 866 sema_new = malloc(val * sizeof(struct semid_ds *), 867 M_SEM, M_WAITOK|M_ZERO); 868 bcopy(sema, sema_new, 869 seminfo.semmni * sizeof(struct semid_ds *)); 870 newseqs = malloc(val * sizeof(unsigned short), M_SEM, 871 M_WAITOK|M_ZERO); 872 bcopy(semseqs, newseqs, 873 seminfo.semmni * sizeof(unsigned short)); 874 free(sema, M_SEM); 875 free(semseqs, M_SEM); 876 sema = sema_new; 877 semseqs = newseqs; 878 seminfo.semmni = val; 879 return (0); 880 case KERN_SEMINFO_SEMMNS: 881 val = seminfo.semmns; 882 if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)) || 883 val == seminfo.semmns) 884 return (error); 885 if (val < seminfo.semmns || val > 0xffff) 886 return (EINVAL); /* can't decrease semmns */ 887 seminfo.semmns = val; 888 return (0); 889 case KERN_SEMINFO_SEMMNU: 890 val = seminfo.semmnu; 891 if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)) || 892 val == seminfo.semmnu) 893 return (error); 894 if (val < seminfo.semmnu) 895 return (EINVAL); /* can't decrease semmnu */ 896 seminfo.semmnu = val; 897 return (0); 898 case KERN_SEMINFO_SEMMSL: 899 val = seminfo.semmsl; 900 if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)) || 901 val == seminfo.semmsl) 902 return (error); 903 if (val < seminfo.semmsl || val > 0xffff) 904 return (EINVAL); /* can't decrease semmsl */ 905 seminfo.semmsl = val; 906 return (0); 907 case KERN_SEMINFO_SEMOPM: 908 val = seminfo.semopm; 909 if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)) || 910 val == seminfo.semopm) 911 return (error); 912 if (val <= 0) 913 return (EINVAL); /* semopm must be >= 1 */ 914 seminfo.semopm = val; 915 return (0); 916 case KERN_SEMINFO_SEMUME: 917 return (sysctl_rdint(oldp, oldlenp, newp, seminfo.semume)); 918 case KERN_SEMINFO_SEMUSZ: 919 return (sysctl_rdint(oldp, oldlenp, newp, seminfo.semusz)); 920 case KERN_SEMINFO_SEMVMX: 921 return (sysctl_rdint(oldp, oldlenp, newp, seminfo.semvmx)); 922 case KERN_SEMINFO_SEMAEM: 923 return (sysctl_rdint(oldp, oldlenp, newp, seminfo.semaem)); 924 default: 925 return (EOPNOTSUPP); 926 } 927 /* NOTREACHED */ 928 } 929