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