1 /* $FreeBSD$ */ 2 3 /* 4 * Implementation of SVID semaphores 5 * 6 * Author: Daniel Boulet 7 * 8 * This software is provided ``AS IS'' without any warranties of any kind. 9 */ 10 11 #include "opt_sysvipc.h" 12 13 #include <sys/param.h> 14 #include <sys/systm.h> 15 #include <sys/sysproto.h> 16 #include <sys/kernel.h> 17 #include <sys/proc.h> 18 #include <sys/lock.h> 19 #include <sys/mutex.h> 20 #include <sys/sem.h> 21 #include <sys/syscall.h> 22 #include <sys/sysent.h> 23 #include <sys/sysctl.h> 24 #include <sys/malloc.h> 25 #include <sys/jail.h> 26 27 static MALLOC_DEFINE(M_SEM, "sem", "SVID compatible semaphores"); 28 29 static void seminit __P((void)); 30 static int sysvsem_modload __P((struct module *, int, void *)); 31 static int semunload __P((void)); 32 static void semexit_myhook __P((struct proc *p)); 33 static int sysctl_sema __P((SYSCTL_HANDLER_ARGS)); 34 35 #ifndef _SYS_SYSPROTO_H_ 36 struct __semctl_args; 37 int __semctl __P((struct proc *p, struct __semctl_args *uap)); 38 struct semget_args; 39 int semget __P((struct proc *p, struct semget_args *uap)); 40 struct semop_args; 41 int semop __P((struct proc *p, struct semop_args *uap)); 42 #endif 43 44 static struct sem_undo *semu_alloc __P((struct proc *p)); 45 static int semundo_adjust __P((struct proc *p, struct sem_undo **supptr, 46 int semid, int semnum, int adjval)); 47 static void semundo_clear __P((int semid, int semnum)); 48 49 /* XXX casting to (sy_call_t *) is bogus, as usual. */ 50 static sy_call_t *semcalls[] = { 51 (sy_call_t *)__semctl, (sy_call_t *)semget, 52 (sy_call_t *)semop 53 }; 54 55 static int semtot = 0; 56 static struct semid_ds *sema; /* semaphore id pool */ 57 static struct sem *sem; /* semaphore pool */ 58 static struct sem_undo *semu_list; /* list of active undo structures */ 59 static int *semu; /* undo structure pool */ 60 61 struct sem { 62 u_short semval; /* semaphore value */ 63 pid_t sempid; /* pid of last operation */ 64 u_short semncnt; /* # awaiting semval > cval */ 65 u_short semzcnt; /* # awaiting semval = 0 */ 66 }; 67 68 /* 69 * Undo structure (one per process) 70 */ 71 struct sem_undo { 72 struct sem_undo *un_next; /* ptr to next active undo structure */ 73 struct proc *un_proc; /* owner of this structure */ 74 short un_cnt; /* # of active entries */ 75 struct undo { 76 short un_adjval; /* adjust on exit values */ 77 short un_num; /* semaphore # */ 78 int un_id; /* semid */ 79 } un_ent[1]; /* undo entries */ 80 }; 81 82 /* 83 * Configuration parameters 84 */ 85 #ifndef SEMMNI 86 #define SEMMNI 10 /* # of semaphore identifiers */ 87 #endif 88 #ifndef SEMMNS 89 #define SEMMNS 60 /* # of semaphores in system */ 90 #endif 91 #ifndef SEMUME 92 #define SEMUME 10 /* max # of undo entries per process */ 93 #endif 94 #ifndef SEMMNU 95 #define SEMMNU 30 /* # of undo structures in system */ 96 #endif 97 98 /* shouldn't need tuning */ 99 #ifndef SEMMAP 100 #define SEMMAP 30 /* # of entries in semaphore map */ 101 #endif 102 #ifndef SEMMSL 103 #define SEMMSL SEMMNS /* max # of semaphores per id */ 104 #endif 105 #ifndef SEMOPM 106 #define SEMOPM 100 /* max # of operations per semop call */ 107 #endif 108 109 #define SEMVMX 32767 /* semaphore maximum value */ 110 #define SEMAEM 16384 /* adjust on exit max value */ 111 112 /* 113 * Due to the way semaphore memory is allocated, we have to ensure that 114 * SEMUSZ is properly aligned. 115 */ 116 117 #define SEM_ALIGN(bytes) (((bytes) + (sizeof(long) - 1)) & ~(sizeof(long) - 1)) 118 119 /* actual size of an undo structure */ 120 #define SEMUSZ SEM_ALIGN(offsetof(struct sem_undo, un_ent[SEMUME])) 121 122 /* 123 * Macro to find a particular sem_undo vector 124 */ 125 #define SEMU(ix) ((struct sem_undo *)(((intptr_t)semu)+ix * seminfo.semusz)) 126 127 /* 128 * semaphore info struct 129 */ 130 struct seminfo seminfo = { 131 SEMMAP, /* # of entries in semaphore map */ 132 SEMMNI, /* # of semaphore identifiers */ 133 SEMMNS, /* # of semaphores in system */ 134 SEMMNU, /* # of undo structures in system */ 135 SEMMSL, /* max # of semaphores per id */ 136 SEMOPM, /* max # of operations per semop call */ 137 SEMUME, /* max # of undo entries per process */ 138 SEMUSZ, /* size in bytes of undo structure */ 139 SEMVMX, /* semaphore maximum value */ 140 SEMAEM /* adjust on exit max value */ 141 }; 142 143 SYSCTL_DECL(_kern_ipc); 144 SYSCTL_INT(_kern_ipc, OID_AUTO, semmap, CTLFLAG_RW, &seminfo.semmap, 0, ""); 145 SYSCTL_INT(_kern_ipc, OID_AUTO, semmni, CTLFLAG_RD, &seminfo.semmni, 0, ""); 146 SYSCTL_INT(_kern_ipc, OID_AUTO, semmns, CTLFLAG_RD, &seminfo.semmns, 0, ""); 147 SYSCTL_INT(_kern_ipc, OID_AUTO, semmnu, CTLFLAG_RD, &seminfo.semmnu, 0, ""); 148 SYSCTL_INT(_kern_ipc, OID_AUTO, semmsl, CTLFLAG_RW, &seminfo.semmsl, 0, ""); 149 SYSCTL_INT(_kern_ipc, OID_AUTO, semopm, CTLFLAG_RD, &seminfo.semopm, 0, ""); 150 SYSCTL_INT(_kern_ipc, OID_AUTO, semume, CTLFLAG_RD, &seminfo.semume, 0, ""); 151 SYSCTL_INT(_kern_ipc, OID_AUTO, semusz, CTLFLAG_RD, &seminfo.semusz, 0, ""); 152 SYSCTL_INT(_kern_ipc, OID_AUTO, semvmx, CTLFLAG_RW, &seminfo.semvmx, 0, ""); 153 SYSCTL_INT(_kern_ipc, OID_AUTO, semaem, CTLFLAG_RW, &seminfo.semaem, 0, ""); 154 SYSCTL_PROC(_kern_ipc, OID_AUTO, sema, CTLFLAG_RD, 155 NULL, 0, sysctl_sema, "", ""); 156 157 #if 0 158 RO seminfo.semmap /* SEMMAP unused */ 159 RO seminfo.semmni 160 RO seminfo.semmns 161 RO seminfo.semmnu /* undo entries per system */ 162 RW seminfo.semmsl 163 RO seminfo.semopm /* SEMOPM unused */ 164 RO seminfo.semume 165 RO seminfo.semusz /* param - derived from SEMUME for per-proc sizeof */ 166 RO seminfo.semvmx /* SEMVMX unused - user param */ 167 RO seminfo.semaem /* SEMAEM unused - user param */ 168 #endif 169 170 static void 171 seminit(void) 172 { 173 register int i; 174 175 sem = malloc(sizeof(struct sem) * seminfo.semmns, M_SEM, M_WAITOK); 176 if (sem == NULL) 177 panic("sem is NULL"); 178 sema = malloc(sizeof(struct semid_ds) * seminfo.semmni, M_SEM, M_WAITOK); 179 if (sema == NULL) 180 panic("sema is NULL"); 181 semu = malloc(seminfo.semmnu * seminfo.semusz, M_SEM, M_WAITOK); 182 if (semu == NULL) 183 panic("semu is NULL"); 184 185 for (i = 0; i < seminfo.semmni; i++) { 186 sema[i].sem_base = 0; 187 sema[i].sem_perm.mode = 0; 188 } 189 for (i = 0; i < seminfo.semmnu; i++) { 190 register struct sem_undo *suptr = SEMU(i); 191 suptr->un_proc = NULL; 192 } 193 semu_list = NULL; 194 semexit_hook = &semexit_myhook; 195 } 196 197 static int 198 semunload(void) 199 { 200 201 if (semtot != 0) 202 return (EBUSY); 203 204 free(sem, M_SEM); 205 free(sema, M_SEM); 206 free(semu, M_SEM); 207 semexit_hook = NULL; 208 return (0); 209 } 210 211 static int 212 sysvsem_modload(struct module *module, int cmd, void *arg) 213 { 214 int error = 0; 215 216 switch (cmd) { 217 case MOD_LOAD: 218 seminit(); 219 break; 220 case MOD_UNLOAD: 221 error = semunload(); 222 break; 223 case MOD_SHUTDOWN: 224 break; 225 default: 226 error = EINVAL; 227 break; 228 } 229 return (error); 230 } 231 232 static moduledata_t sysvsem_mod = { 233 "sysvsem", 234 &sysvsem_modload, 235 NULL 236 }; 237 238 SYSCALL_MODULE_HELPER(semsys, 5); 239 SYSCALL_MODULE_HELPER(__semctl, 4); 240 SYSCALL_MODULE_HELPER(semget, 3); 241 SYSCALL_MODULE_HELPER(semop, 3); 242 243 DECLARE_MODULE(sysvsem, sysvsem_mod, 244 SI_SUB_SYSV_SEM, SI_ORDER_FIRST); 245 MODULE_VERSION(sysvsem, 1); 246 247 /* 248 * Entry point for all SEM calls 249 * 250 * MPSAFE 251 */ 252 int 253 semsys(p, uap) 254 struct proc *p; 255 /* XXX actually varargs. */ 256 struct semsys_args /* { 257 u_int which; 258 int a2; 259 int a3; 260 int a4; 261 int a5; 262 } */ *uap; 263 { 264 int error; 265 266 mtx_lock(&Giant); 267 268 if (!jail_sysvipc_allowed && jailed(p->p_ucred)) { 269 error = ENOSYS; 270 goto done2; 271 } 272 273 if (uap->which >= sizeof(semcalls)/sizeof(semcalls[0])) { 274 error = EINVAL; 275 goto done2; 276 } 277 error = (*semcalls[uap->which])(p, &uap->a2); 278 done2: 279 mtx_unlock(&Giant); 280 return (error); 281 } 282 283 /* 284 * Allocate a new sem_undo structure for a process 285 * (returns ptr to structure or NULL if no more room) 286 */ 287 288 static struct sem_undo * 289 semu_alloc(p) 290 struct proc *p; 291 { 292 register int i; 293 register struct sem_undo *suptr; 294 register struct sem_undo **supptr; 295 int attempt; 296 297 /* 298 * Try twice to allocate something. 299 * (we'll purge any empty structures after the first pass so 300 * two passes are always enough) 301 */ 302 303 for (attempt = 0; attempt < 2; attempt++) { 304 /* 305 * Look for a free structure. 306 * Fill it in and return it if we find one. 307 */ 308 309 for (i = 0; i < seminfo.semmnu; i++) { 310 suptr = SEMU(i); 311 if (suptr->un_proc == NULL) { 312 suptr->un_next = semu_list; 313 semu_list = suptr; 314 suptr->un_cnt = 0; 315 suptr->un_proc = p; 316 return(suptr); 317 } 318 } 319 320 /* 321 * We didn't find a free one, if this is the first attempt 322 * then try to free some structures. 323 */ 324 325 if (attempt == 0) { 326 /* All the structures are in use - try to free some */ 327 int did_something = 0; 328 329 supptr = &semu_list; 330 while ((suptr = *supptr) != NULL) { 331 if (suptr->un_cnt == 0) { 332 suptr->un_proc = NULL; 333 *supptr = suptr->un_next; 334 did_something = 1; 335 } else 336 supptr = &(suptr->un_next); 337 } 338 339 /* If we didn't free anything then just give-up */ 340 if (!did_something) 341 return(NULL); 342 } else { 343 /* 344 * The second pass failed even though we freed 345 * something after the first pass! 346 * This is IMPOSSIBLE! 347 */ 348 panic("semu_alloc - second attempt failed"); 349 } 350 } 351 return (NULL); 352 } 353 354 /* 355 * Adjust a particular entry for a particular proc 356 */ 357 358 static int 359 semundo_adjust(p, supptr, semid, semnum, adjval) 360 register struct proc *p; 361 struct sem_undo **supptr; 362 int semid, semnum; 363 int adjval; 364 { 365 register struct sem_undo *suptr; 366 register struct undo *sunptr; 367 int i; 368 369 /* Look for and remember the sem_undo if the caller doesn't provide 370 it */ 371 372 suptr = *supptr; 373 if (suptr == NULL) { 374 for (suptr = semu_list; suptr != NULL; 375 suptr = suptr->un_next) { 376 if (suptr->un_proc == p) { 377 *supptr = suptr; 378 break; 379 } 380 } 381 if (suptr == NULL) { 382 if (adjval == 0) 383 return(0); 384 suptr = semu_alloc(p); 385 if (suptr == NULL) 386 return(ENOSPC); 387 *supptr = suptr; 388 } 389 } 390 391 /* 392 * Look for the requested entry and adjust it (delete if adjval becomes 393 * 0). 394 */ 395 sunptr = &suptr->un_ent[0]; 396 for (i = 0; i < suptr->un_cnt; i++, sunptr++) { 397 if (sunptr->un_id != semid || sunptr->un_num != semnum) 398 continue; 399 if (adjval == 0) 400 sunptr->un_adjval = 0; 401 else 402 sunptr->un_adjval += adjval; 403 if (sunptr->un_adjval == 0) { 404 suptr->un_cnt--; 405 if (i < suptr->un_cnt) 406 suptr->un_ent[i] = 407 suptr->un_ent[suptr->un_cnt]; 408 } 409 return(0); 410 } 411 412 /* Didn't find the right entry - create it */ 413 if (adjval == 0) 414 return(0); 415 if (suptr->un_cnt != seminfo.semume) { 416 sunptr = &suptr->un_ent[suptr->un_cnt]; 417 suptr->un_cnt++; 418 sunptr->un_adjval = adjval; 419 sunptr->un_id = semid; sunptr->un_num = semnum; 420 } else 421 return(EINVAL); 422 return(0); 423 } 424 425 static void 426 semundo_clear(semid, semnum) 427 int semid, semnum; 428 { 429 register struct sem_undo *suptr; 430 431 for (suptr = semu_list; suptr != NULL; suptr = suptr->un_next) { 432 register struct undo *sunptr = &suptr->un_ent[0]; 433 register int i = 0; 434 435 while (i < suptr->un_cnt) { 436 if (sunptr->un_id == semid) { 437 if (semnum == -1 || sunptr->un_num == semnum) { 438 suptr->un_cnt--; 439 if (i < suptr->un_cnt) { 440 suptr->un_ent[i] = 441 suptr->un_ent[suptr->un_cnt]; 442 continue; 443 } 444 } 445 if (semnum != -1) 446 break; 447 } 448 i++, sunptr++; 449 } 450 } 451 } 452 453 /* 454 * Note that the user-mode half of this passes a union, not a pointer 455 */ 456 #ifndef _SYS_SYSPROTO_H_ 457 struct __semctl_args { 458 int semid; 459 int semnum; 460 int cmd; 461 union semun *arg; 462 }; 463 #endif 464 465 /* 466 * MPSAFE 467 */ 468 int 469 __semctl(p, uap) 470 struct proc *p; 471 register struct __semctl_args *uap; 472 { 473 int semid = uap->semid; 474 int semnum = uap->semnum; 475 int cmd = uap->cmd; 476 union semun *arg = uap->arg; 477 union semun real_arg; 478 struct ucred *cred = p->p_ucred; 479 int i, rval, error; 480 struct semid_ds sbuf; 481 register struct semid_ds *semaptr; 482 483 #ifdef SEM_DEBUG 484 printf("call to semctl(%d, %d, %d, 0x%x)\n", semid, semnum, cmd, arg); 485 #endif 486 mtx_lock(&Giant); 487 488 if (!jail_sysvipc_allowed && jailed(p->p_ucred)) { 489 error = ENOSYS; 490 goto done2; 491 } 492 493 semid = IPCID_TO_IX(semid); 494 if (semid < 0 || semid >= seminfo.semmsl) { 495 error = EINVAL; 496 goto done2; 497 } 498 499 semaptr = &sema[semid]; 500 if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0 || 501 semaptr->sem_perm.seq != IPCID_TO_SEQ(uap->semid)) { 502 error = EINVAL; 503 goto done2; 504 } 505 506 error = 0; 507 rval = 0; 508 509 switch (cmd) { 510 case IPC_RMID: 511 if ((error = ipcperm(p, &semaptr->sem_perm, IPC_M))) 512 goto done2; 513 semaptr->sem_perm.cuid = cred->cr_uid; 514 semaptr->sem_perm.uid = cred->cr_uid; 515 semtot -= semaptr->sem_nsems; 516 for (i = semaptr->sem_base - sem; i < semtot; i++) 517 sem[i] = sem[i + semaptr->sem_nsems]; 518 for (i = 0; i < seminfo.semmni; i++) { 519 if ((sema[i].sem_perm.mode & SEM_ALLOC) && 520 sema[i].sem_base > semaptr->sem_base) 521 sema[i].sem_base -= semaptr->sem_nsems; 522 } 523 semaptr->sem_perm.mode = 0; 524 semundo_clear(semid, -1); 525 wakeup((caddr_t)semaptr); 526 break; 527 528 case IPC_SET: 529 if ((error = ipcperm(p, &semaptr->sem_perm, IPC_M))) 530 goto done2; 531 if ((error = copyin(arg, &real_arg, sizeof(real_arg))) != 0) 532 goto done2; 533 if ((error = copyin(real_arg.buf, (caddr_t)&sbuf, 534 sizeof(sbuf))) != 0) { 535 goto done2; 536 } 537 semaptr->sem_perm.uid = sbuf.sem_perm.uid; 538 semaptr->sem_perm.gid = sbuf.sem_perm.gid; 539 semaptr->sem_perm.mode = (semaptr->sem_perm.mode & ~0777) | 540 (sbuf.sem_perm.mode & 0777); 541 semaptr->sem_ctime = time_second; 542 break; 543 544 case IPC_STAT: 545 if ((error = ipcperm(p, &semaptr->sem_perm, IPC_R))) 546 goto done2; 547 if ((error = copyin(arg, &real_arg, sizeof(real_arg))) != 0) 548 goto done2; 549 error = copyout((caddr_t)semaptr, real_arg.buf, 550 sizeof(struct semid_ds)); 551 break; 552 553 case GETNCNT: 554 if ((error = ipcperm(p, &semaptr->sem_perm, IPC_R))) 555 goto done2; 556 if (semnum < 0 || semnum >= semaptr->sem_nsems) { 557 error = EINVAL; 558 goto done2; 559 } 560 rval = semaptr->sem_base[semnum].semncnt; 561 break; 562 563 case GETPID: 564 if ((error = ipcperm(p, &semaptr->sem_perm, IPC_R))) 565 goto done2; 566 if (semnum < 0 || semnum >= semaptr->sem_nsems) { 567 error = EINVAL; 568 goto done2; 569 } 570 rval = semaptr->sem_base[semnum].sempid; 571 break; 572 573 case GETVAL: 574 if ((error = ipcperm(p, &semaptr->sem_perm, IPC_R))) 575 goto done2; 576 if (semnum < 0 || semnum >= semaptr->sem_nsems) { 577 error = EINVAL; 578 goto done2; 579 } 580 rval = semaptr->sem_base[semnum].semval; 581 break; 582 583 case GETALL: 584 if ((error = ipcperm(p, &semaptr->sem_perm, IPC_R))) 585 goto done2; 586 if ((error = copyin(arg, &real_arg, sizeof(real_arg))) != 0) 587 goto done2; 588 for (i = 0; i < semaptr->sem_nsems; i++) { 589 error = copyout((caddr_t)&semaptr->sem_base[i].semval, 590 &real_arg.array[i], sizeof(real_arg.array[0])); 591 if (error != 0) 592 break; 593 } 594 break; 595 596 case GETZCNT: 597 if ((error = ipcperm(p, &semaptr->sem_perm, IPC_R))) 598 goto done2; 599 if (semnum < 0 || semnum >= semaptr->sem_nsems) { 600 error = EINVAL; 601 goto done2; 602 } 603 rval = semaptr->sem_base[semnum].semzcnt; 604 break; 605 606 case SETVAL: 607 if ((error = ipcperm(p, &semaptr->sem_perm, IPC_W))) 608 goto done2; 609 if (semnum < 0 || semnum >= semaptr->sem_nsems) { 610 error = EINVAL; 611 goto done2; 612 } 613 if ((error = copyin(arg, &real_arg, sizeof(real_arg))) != 0) 614 goto done2; 615 semaptr->sem_base[semnum].semval = real_arg.val; 616 semundo_clear(semid, semnum); 617 wakeup((caddr_t)semaptr); 618 break; 619 620 case SETALL: 621 if ((error = ipcperm(p, &semaptr->sem_perm, IPC_W))) 622 goto done2; 623 if ((error = copyin(arg, &real_arg, sizeof(real_arg))) != 0) 624 goto done2; 625 for (i = 0; i < semaptr->sem_nsems; i++) { 626 error = copyin(&real_arg.array[i], 627 (caddr_t)&semaptr->sem_base[i].semval, 628 sizeof(real_arg.array[0])); 629 if (error != 0) 630 break; 631 } 632 semundo_clear(semid, -1); 633 wakeup((caddr_t)semaptr); 634 break; 635 636 default: 637 error = EINVAL; 638 break; 639 } 640 641 if (error == 0) 642 p->p_retval[0] = rval; 643 done2: 644 mtx_unlock(&Giant); 645 return(error); 646 } 647 648 #ifndef _SYS_SYSPROTO_H_ 649 struct semget_args { 650 key_t key; 651 int nsems; 652 int semflg; 653 }; 654 #endif 655 656 /* 657 * MPSAFE 658 */ 659 int 660 semget(p, uap) 661 struct proc *p; 662 register struct semget_args *uap; 663 { 664 int semid, error = 0; 665 int key = uap->key; 666 int nsems = uap->nsems; 667 int semflg = uap->semflg; 668 struct ucred *cred = p->p_ucred; 669 670 #ifdef SEM_DEBUG 671 printf("semget(0x%x, %d, 0%o)\n", key, nsems, semflg); 672 #endif 673 mtx_lock(&Giant); 674 675 if (!jail_sysvipc_allowed && jailed(p->p_ucred)) { 676 error = ENOSYS; 677 goto done2; 678 } 679 680 if (key != IPC_PRIVATE) { 681 for (semid = 0; semid < seminfo.semmni; semid++) { 682 if ((sema[semid].sem_perm.mode & SEM_ALLOC) && 683 sema[semid].sem_perm.key == key) 684 break; 685 } 686 if (semid < seminfo.semmni) { 687 #ifdef SEM_DEBUG 688 printf("found public key\n"); 689 #endif 690 if ((error = ipcperm(p, &sema[semid].sem_perm, 691 semflg & 0700))) { 692 goto done2; 693 } 694 if (nsems > 0 && sema[semid].sem_nsems < nsems) { 695 #ifdef SEM_DEBUG 696 printf("too small\n"); 697 #endif 698 error = EINVAL; 699 goto done2; 700 } 701 if ((semflg & IPC_CREAT) && (semflg & IPC_EXCL)) { 702 #ifdef SEM_DEBUG 703 printf("not exclusive\n"); 704 #endif 705 error = EEXIST; 706 goto done2; 707 } 708 goto found; 709 } 710 } 711 712 #ifdef SEM_DEBUG 713 printf("need to allocate the semid_ds\n"); 714 #endif 715 if (key == IPC_PRIVATE || (semflg & IPC_CREAT)) { 716 if (nsems <= 0 || nsems > seminfo.semmsl) { 717 #ifdef SEM_DEBUG 718 printf("nsems out of range (0<%d<=%d)\n", nsems, 719 seminfo.semmsl); 720 #endif 721 error = EINVAL; 722 goto done2; 723 } 724 if (nsems > seminfo.semmns - semtot) { 725 #ifdef SEM_DEBUG 726 printf("not enough semaphores left (need %d, got %d)\n", 727 nsems, seminfo.semmns - semtot); 728 #endif 729 error = ENOSPC; 730 goto done2; 731 } 732 for (semid = 0; semid < seminfo.semmni; semid++) { 733 if ((sema[semid].sem_perm.mode & SEM_ALLOC) == 0) 734 break; 735 } 736 if (semid == seminfo.semmni) { 737 #ifdef SEM_DEBUG 738 printf("no more semid_ds's available\n"); 739 #endif 740 error = ENOSPC; 741 goto done2; 742 } 743 #ifdef SEM_DEBUG 744 printf("semid %d is available\n", semid); 745 #endif 746 sema[semid].sem_perm.key = key; 747 sema[semid].sem_perm.cuid = cred->cr_uid; 748 sema[semid].sem_perm.uid = cred->cr_uid; 749 sema[semid].sem_perm.cgid = cred->cr_gid; 750 sema[semid].sem_perm.gid = cred->cr_gid; 751 sema[semid].sem_perm.mode = (semflg & 0777) | SEM_ALLOC; 752 sema[semid].sem_perm.seq = 753 (sema[semid].sem_perm.seq + 1) & 0x7fff; 754 sema[semid].sem_nsems = nsems; 755 sema[semid].sem_otime = 0; 756 sema[semid].sem_ctime = time_second; 757 sema[semid].sem_base = &sem[semtot]; 758 semtot += nsems; 759 bzero(sema[semid].sem_base, 760 sizeof(sema[semid].sem_base[0])*nsems); 761 #ifdef SEM_DEBUG 762 printf("sembase = 0x%x, next = 0x%x\n", sema[semid].sem_base, 763 &sem[semtot]); 764 #endif 765 } else { 766 #ifdef SEM_DEBUG 767 printf("didn't find it and wasn't asked to create it\n"); 768 #endif 769 error = ENOENT; 770 goto done2; 771 } 772 773 found: 774 p->p_retval[0] = IXSEQ_TO_IPCID(semid, sema[semid].sem_perm); 775 done2: 776 mtx_unlock(&Giant); 777 return (error); 778 } 779 780 #ifndef _SYS_SYSPROTO_H_ 781 struct semop_args { 782 int semid; 783 struct sembuf *sops; 784 int nsops; 785 }; 786 #endif 787 788 /* 789 * MPSAFE 790 */ 791 int 792 semop(p, uap) 793 struct proc *p; 794 register struct semop_args *uap; 795 { 796 int semid = uap->semid; 797 int nsops = uap->nsops; 798 struct sembuf sops[MAX_SOPS]; 799 register struct semid_ds *semaptr; 800 register struct sembuf *sopptr; 801 register struct sem *semptr; 802 struct sem_undo *suptr = NULL; 803 int i, j, error = 0; 804 int do_wakeup, do_undos; 805 806 #ifdef SEM_DEBUG 807 printf("call to semop(%d, 0x%x, %d)\n", semid, sops, nsops); 808 #endif 809 810 mtx_lock(&Giant); 811 812 if (!jail_sysvipc_allowed && jailed(p->p_ucred)) { 813 error = ENOSYS; 814 goto done2; 815 } 816 817 semid = IPCID_TO_IX(semid); /* Convert back to zero origin */ 818 819 if (semid < 0 || semid >= seminfo.semmsl) { 820 error = EINVAL; 821 goto done2; 822 } 823 824 semaptr = &sema[semid]; 825 if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0) { 826 error = EINVAL; 827 goto done2; 828 } 829 if (semaptr->sem_perm.seq != IPCID_TO_SEQ(uap->semid)) { 830 error = EINVAL; 831 goto done2; 832 } 833 834 if ((error = ipcperm(p, &semaptr->sem_perm, IPC_W))) { 835 #ifdef SEM_DEBUG 836 printf("error = %d from ipaccess\n", error); 837 #endif 838 goto done2; 839 } 840 841 if (nsops > MAX_SOPS) { 842 #ifdef SEM_DEBUG 843 printf("too many sops (max=%d, nsops=%d)\n", MAX_SOPS, nsops); 844 #endif 845 error = E2BIG; 846 goto done2; 847 } 848 849 if ((error = copyin(uap->sops, &sops, nsops * sizeof(sops[0]))) != 0) { 850 #ifdef SEM_DEBUG 851 printf("error = %d from copyin(%08x, %08x, %d)\n", error, 852 uap->sops, &sops, nsops * sizeof(sops[0])); 853 #endif 854 goto done2; 855 } 856 857 /* 858 * Loop trying to satisfy the vector of requests. 859 * If we reach a point where we must wait, any requests already 860 * performed are rolled back and we go to sleep until some other 861 * process wakes us up. At this point, we start all over again. 862 * 863 * This ensures that from the perspective of other tasks, a set 864 * of requests is atomic (never partially satisfied). 865 */ 866 do_undos = 0; 867 868 for (;;) { 869 do_wakeup = 0; 870 871 for (i = 0; i < nsops; i++) { 872 sopptr = &sops[i]; 873 874 if (sopptr->sem_num >= semaptr->sem_nsems) { 875 error = EFBIG; 876 goto done2; 877 } 878 879 semptr = &semaptr->sem_base[sopptr->sem_num]; 880 881 #ifdef SEM_DEBUG 882 printf("semop: semaptr=%x, sem_base=%x, semptr=%x, sem[%d]=%d : op=%d, flag=%s\n", 883 semaptr, semaptr->sem_base, semptr, 884 sopptr->sem_num, semptr->semval, sopptr->sem_op, 885 (sopptr->sem_flg & IPC_NOWAIT) ? "nowait" : "wait"); 886 #endif 887 888 if (sopptr->sem_op < 0) { 889 if (semptr->semval + sopptr->sem_op < 0) { 890 #ifdef SEM_DEBUG 891 printf("semop: can't do it now\n"); 892 #endif 893 break; 894 } else { 895 semptr->semval += sopptr->sem_op; 896 if (semptr->semval == 0 && 897 semptr->semzcnt > 0) 898 do_wakeup = 1; 899 } 900 if (sopptr->sem_flg & SEM_UNDO) 901 do_undos = 1; 902 } else if (sopptr->sem_op == 0) { 903 if (semptr->semval > 0) { 904 #ifdef SEM_DEBUG 905 printf("semop: not zero now\n"); 906 #endif 907 break; 908 } 909 } else { 910 if (semptr->semncnt > 0) 911 do_wakeup = 1; 912 semptr->semval += sopptr->sem_op; 913 if (sopptr->sem_flg & SEM_UNDO) 914 do_undos = 1; 915 } 916 } 917 918 /* 919 * Did we get through the entire vector? 920 */ 921 if (i >= nsops) 922 goto done; 923 924 /* 925 * No ... rollback anything that we've already done 926 */ 927 #ifdef SEM_DEBUG 928 printf("semop: rollback 0 through %d\n", i-1); 929 #endif 930 for (j = 0; j < i; j++) 931 semaptr->sem_base[sops[j].sem_num].semval -= 932 sops[j].sem_op; 933 934 /* 935 * If the request that we couldn't satisfy has the 936 * NOWAIT flag set then return with EAGAIN. 937 */ 938 if (sopptr->sem_flg & IPC_NOWAIT) { 939 error = EAGAIN; 940 goto done2; 941 } 942 943 if (sopptr->sem_op == 0) 944 semptr->semzcnt++; 945 else 946 semptr->semncnt++; 947 948 #ifdef SEM_DEBUG 949 printf("semop: good night!\n"); 950 #endif 951 error = tsleep((caddr_t)semaptr, (PZERO - 4) | PCATCH, 952 "semwait", 0); 953 #ifdef SEM_DEBUG 954 printf("semop: good morning (error=%d)!\n", error); 955 #endif 956 957 suptr = NULL; /* sem_undo may have been reallocated */ 958 959 if (error != 0) { 960 error = EINTR; 961 goto done2; 962 } 963 #ifdef SEM_DEBUG 964 printf("semop: good morning!\n"); 965 #endif 966 967 /* 968 * Make sure that the semaphore still exists 969 */ 970 if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0 || 971 semaptr->sem_perm.seq != IPCID_TO_SEQ(uap->semid)) { 972 error = EIDRM; 973 goto done2; 974 } 975 976 /* 977 * The semaphore is still alive. Readjust the count of 978 * waiting processes. 979 */ 980 if (sopptr->sem_op == 0) 981 semptr->semzcnt--; 982 else 983 semptr->semncnt--; 984 } 985 986 done: 987 /* 988 * Process any SEM_UNDO requests. 989 */ 990 if (do_undos) { 991 for (i = 0; i < nsops; i++) { 992 /* 993 * We only need to deal with SEM_UNDO's for non-zero 994 * op's. 995 */ 996 int adjval; 997 998 if ((sops[i].sem_flg & SEM_UNDO) == 0) 999 continue; 1000 adjval = sops[i].sem_op; 1001 if (adjval == 0) 1002 continue; 1003 error = semundo_adjust(p, &suptr, semid, 1004 sops[i].sem_num, -adjval); 1005 if (error == 0) 1006 continue; 1007 1008 /* 1009 * Oh-Oh! We ran out of either sem_undo's or undo's. 1010 * Rollback the adjustments to this point and then 1011 * rollback the semaphore ups and down so we can return 1012 * with an error with all structures restored. We 1013 * rollback the undo's in the exact reverse order that 1014 * we applied them. This guarantees that we won't run 1015 * out of space as we roll things back out. 1016 */ 1017 for (j = i - 1; j >= 0; j--) { 1018 if ((sops[j].sem_flg & SEM_UNDO) == 0) 1019 continue; 1020 adjval = sops[j].sem_op; 1021 if (adjval == 0) 1022 continue; 1023 if (semundo_adjust(p, &suptr, semid, 1024 sops[j].sem_num, adjval) != 0) 1025 panic("semop - can't undo undos"); 1026 } 1027 1028 for (j = 0; j < nsops; j++) 1029 semaptr->sem_base[sops[j].sem_num].semval -= 1030 sops[j].sem_op; 1031 1032 #ifdef SEM_DEBUG 1033 printf("error = %d from semundo_adjust\n", error); 1034 #endif 1035 goto done2; 1036 } /* loop through the sops */ 1037 } /* if (do_undos) */ 1038 1039 /* We're definitely done - set the sempid's */ 1040 for (i = 0; i < nsops; i++) { 1041 sopptr = &sops[i]; 1042 semptr = &semaptr->sem_base[sopptr->sem_num]; 1043 semptr->sempid = p->p_pid; 1044 } 1045 1046 /* Do a wakeup if any semaphore was up'd. */ 1047 if (do_wakeup) { 1048 #ifdef SEM_DEBUG 1049 printf("semop: doing wakeup\n"); 1050 #endif 1051 wakeup((caddr_t)semaptr); 1052 #ifdef SEM_DEBUG 1053 printf("semop: back from wakeup\n"); 1054 #endif 1055 } 1056 #ifdef SEM_DEBUG 1057 printf("semop: done\n"); 1058 #endif 1059 p->p_retval[0] = 0; 1060 done2: 1061 mtx_unlock(&Giant); 1062 return (error); 1063 } 1064 1065 /* 1066 * Go through the undo structures for this process and apply the adjustments to 1067 * semaphores. 1068 */ 1069 static void 1070 semexit_myhook(p) 1071 struct proc *p; 1072 { 1073 register struct sem_undo *suptr; 1074 register struct sem_undo **supptr; 1075 int did_something; 1076 1077 did_something = 0; 1078 1079 /* 1080 * Go through the chain of undo vectors looking for one 1081 * associated with this process. 1082 */ 1083 1084 for (supptr = &semu_list; (suptr = *supptr) != NULL; 1085 supptr = &suptr->un_next) { 1086 if (suptr->un_proc == p) 1087 break; 1088 } 1089 1090 if (suptr == NULL) 1091 return; 1092 1093 #ifdef SEM_DEBUG 1094 printf("proc @%08x has undo structure with %d entries\n", p, 1095 suptr->un_cnt); 1096 #endif 1097 1098 /* 1099 * If there are any active undo elements then process them. 1100 */ 1101 if (suptr->un_cnt > 0) { 1102 int ix; 1103 1104 for (ix = 0; ix < suptr->un_cnt; ix++) { 1105 int semid = suptr->un_ent[ix].un_id; 1106 int semnum = suptr->un_ent[ix].un_num; 1107 int adjval = suptr->un_ent[ix].un_adjval; 1108 struct semid_ds *semaptr; 1109 1110 semaptr = &sema[semid]; 1111 if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0) 1112 panic("semexit - semid not allocated"); 1113 if (semnum >= semaptr->sem_nsems) 1114 panic("semexit - semnum out of range"); 1115 1116 #ifdef SEM_DEBUG 1117 printf("semexit: %08x id=%d num=%d(adj=%d) ; sem=%d\n", 1118 suptr->un_proc, suptr->un_ent[ix].un_id, 1119 suptr->un_ent[ix].un_num, 1120 suptr->un_ent[ix].un_adjval, 1121 semaptr->sem_base[semnum].semval); 1122 #endif 1123 1124 if (adjval < 0) { 1125 if (semaptr->sem_base[semnum].semval < -adjval) 1126 semaptr->sem_base[semnum].semval = 0; 1127 else 1128 semaptr->sem_base[semnum].semval += 1129 adjval; 1130 } else 1131 semaptr->sem_base[semnum].semval += adjval; 1132 1133 wakeup((caddr_t)semaptr); 1134 #ifdef SEM_DEBUG 1135 printf("semexit: back from wakeup\n"); 1136 #endif 1137 } 1138 } 1139 1140 /* 1141 * Deallocate the undo vector. 1142 */ 1143 #ifdef SEM_DEBUG 1144 printf("removing vector\n"); 1145 #endif 1146 suptr->un_proc = NULL; 1147 *supptr = suptr->un_next; 1148 } 1149 1150 static int 1151 sysctl_sema(SYSCTL_HANDLER_ARGS) 1152 { 1153 1154 return (SYSCTL_OUT(req, sema, 1155 sizeof(struct semid_ds) * seminfo.semmni)); 1156 } 1157