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