1 /* $NetBSD: sysv_shm.c,v 1.67 2003/02/01 06:23:44 thorpej Exp $ */ 2 3 /*- 4 * Copyright (c) 1999 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, 9 * NASA Ames Research Center. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgement: 21 * This product includes software developed by the NetBSD 22 * Foundation, Inc. and its contributors. 23 * 4. Neither the name of The NetBSD Foundation nor the names of its 24 * contributors may be used to endorse or promote products derived 25 * from this software without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 30 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 * POSSIBILITY OF SUCH DAMAGE. 38 */ 39 40 /* 41 * Copyright (c) 1994 Adam Glass and Charles M. Hannum. All rights reserved. 42 * 43 * Redistribution and use in source and binary forms, with or without 44 * modification, are permitted provided that the following conditions 45 * are met: 46 * 1. Redistributions of source code must retain the above copyright 47 * notice, this list of conditions and the following disclaimer. 48 * 2. Redistributions in binary form must reproduce the above copyright 49 * notice, this list of conditions and the following disclaimer in the 50 * documentation and/or other materials provided with the distribution. 51 * 3. All advertising materials mentioning features or use of this software 52 * must display the following acknowledgement: 53 * This product includes software developed by Adam Glass and Charles M. 54 * Hannum. 55 * 4. The names of the authors may not be used to endorse or promote products 56 * derived from this software without specific prior written permission. 57 * 58 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 59 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 60 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 61 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, 62 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 63 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 64 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 65 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 66 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 67 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 68 */ 69 70 #include <sys/cdefs.h> 71 __KERNEL_RCSID(0, "$NetBSD: sysv_shm.c,v 1.67 2003/02/01 06:23:44 thorpej Exp $"); 72 73 #define SYSVSHM 74 75 #include <sys/param.h> 76 #include <sys/kernel.h> 77 #include <sys/shm.h> 78 #include <sys/malloc.h> 79 #include <sys/mman.h> 80 #include <sys/stat.h> 81 #include <sys/sysctl.h> 82 #include <sys/mount.h> /* XXX for <sys/syscallargs.h> */ 83 #include <sys/sa.h> 84 #include <sys/syscallargs.h> 85 86 #include <uvm/uvm_extern.h> 87 88 struct shmid_ds *shm_find_segment_by_shmid __P((int, int)); 89 90 MALLOC_DEFINE(M_SHM, "shm", "SVID compatible shared memory segments"); 91 92 /* 93 * Provides the following externally accessible functions: 94 * 95 * shminit(void); initialization 96 * shmexit(struct vmspace *) cleanup 97 * shmfork(struct vmspace *, struct vmspace *) fork handling 98 * 99 * Structures: 100 * shmsegs (an array of 'struct shmid_ds') 101 * per proc array of 'struct shmmap_state' 102 */ 103 104 #define SHMSEG_FREE 0x0200 105 #define SHMSEG_REMOVED 0x0400 106 #define SHMSEG_ALLOCATED 0x0800 107 #define SHMSEG_WANTED 0x1000 108 109 int shm_last_free, shm_nused, shm_committed; 110 struct shmid_ds *shmsegs; 111 112 struct shm_handle { 113 struct uvm_object *shm_object; 114 }; 115 116 struct shmmap_state { 117 vaddr_t va; 118 int shmid; 119 }; 120 121 static int shm_find_segment_by_key __P((key_t)); 122 static void shm_deallocate_segment __P((struct shmid_ds *)); 123 static void shm_delete_mapping __P((struct vmspace *, struct shmmap_state *)); 124 static int shmget_existing __P((struct proc *, struct sys_shmget_args *, 125 int, int, register_t *)); 126 static int shmget_allocate_segment __P((struct proc *, struct sys_shmget_args *, 127 int, register_t *)); 128 129 static int 130 shm_find_segment_by_key(key) 131 key_t key; 132 { 133 int i; 134 135 for (i = 0; i < shminfo.shmmni; i++) 136 if ((shmsegs[i].shm_perm.mode & SHMSEG_ALLOCATED) && 137 shmsegs[i].shm_perm._key == key) 138 return i; 139 return -1; 140 } 141 142 struct shmid_ds * 143 shm_find_segment_by_shmid(shmid, findremoved) 144 int shmid; 145 int findremoved; 146 { 147 int segnum; 148 struct shmid_ds *shmseg; 149 150 segnum = IPCID_TO_IX(shmid); 151 if (segnum < 0 || segnum >= shminfo.shmmni) 152 return NULL; 153 shmseg = &shmsegs[segnum]; 154 if ((shmseg->shm_perm.mode & SHMSEG_ALLOCATED) == 0) 155 return NULL; 156 if (!findremoved && ((shmseg->shm_perm.mode & SHMSEG_REMOVED) != 0)) 157 return NULL; 158 if (shmseg->shm_perm._seq != IPCID_TO_SEQ(shmid)) 159 return NULL; 160 return shmseg; 161 } 162 163 static void 164 shm_deallocate_segment(shmseg) 165 struct shmid_ds *shmseg; 166 { 167 struct shm_handle *shm_handle; 168 size_t size; 169 170 shm_handle = shmseg->_shm_internal; 171 size = (shmseg->shm_segsz + PGOFSET) & ~PGOFSET; 172 uao_detach(shm_handle->shm_object); 173 free((caddr_t)shm_handle, M_SHM); 174 shmseg->_shm_internal = NULL; 175 shm_committed -= btoc(size); 176 shmseg->shm_perm.mode = SHMSEG_FREE; 177 shm_nused--; 178 } 179 180 static void 181 shm_delete_mapping(vm, shmmap_s) 182 struct vmspace *vm; 183 struct shmmap_state *shmmap_s; 184 { 185 struct shmid_ds *shmseg; 186 int segnum; 187 size_t size; 188 189 segnum = IPCID_TO_IX(shmmap_s->shmid); 190 shmseg = &shmsegs[segnum]; 191 size = (shmseg->shm_segsz + PGOFSET) & ~PGOFSET; 192 uvm_deallocate(&vm->vm_map, shmmap_s->va, size); 193 shmmap_s->shmid = -1; 194 shmseg->shm_dtime = time.tv_sec; 195 if ((--shmseg->shm_nattch <= 0) && 196 (shmseg->shm_perm.mode & SHMSEG_REMOVED)) { 197 shm_deallocate_segment(shmseg); 198 shm_last_free = segnum; 199 } 200 } 201 202 int 203 sys_shmdt(l, v, retval) 204 struct lwp *l; 205 void *v; 206 register_t *retval; 207 { 208 struct sys_shmdt_args /* { 209 syscallarg(const void *) shmaddr; 210 } */ *uap = v; 211 struct proc *p = l->l_proc; 212 struct shmmap_state *shmmap_s; 213 int i; 214 215 shmmap_s = (struct shmmap_state *)p->p_vmspace->vm_shm; 216 if (shmmap_s == NULL) 217 return EINVAL; 218 219 for (i = 0; i < shminfo.shmseg; i++, shmmap_s++) 220 if (shmmap_s->shmid != -1 && 221 shmmap_s->va == (vaddr_t)SCARG(uap, shmaddr)) 222 break; 223 if (i == shminfo.shmseg) 224 return EINVAL; 225 shm_delete_mapping(p->p_vmspace, shmmap_s); 226 return 0; 227 } 228 229 int 230 sys_shmat(l, v, retval) 231 struct lwp *l; 232 void *v; 233 register_t *retval; 234 { 235 struct sys_shmat_args /* { 236 syscallarg(int) shmid; 237 syscallarg(const void *) shmaddr; 238 syscallarg(int) shmflg; 239 } */ *uap = v; 240 struct proc *p = l->l_proc; 241 vaddr_t attach_va; 242 int error; 243 244 error = shmat1(p, SCARG(uap, shmid), SCARG(uap, shmaddr), 245 SCARG(uap, shmflg), &attach_va, 0); 246 if (error != 0) 247 return error; 248 retval[0] = attach_va; 249 return 0; 250 } 251 252 int 253 shmat1(p, shmid, shmaddr, shmflg, attachp, findremoved) 254 struct proc *p; 255 int shmid; 256 const void *shmaddr; 257 int shmflg; 258 vaddr_t *attachp; 259 int findremoved; 260 { 261 int error, i, flags; 262 struct ucred *cred = p->p_ucred; 263 struct shmid_ds *shmseg; 264 struct shmmap_state *shmmap_s = NULL; 265 struct shm_handle *shm_handle; 266 vaddr_t attach_va; 267 vm_prot_t prot; 268 vsize_t size; 269 270 shmmap_s = (struct shmmap_state *)p->p_vmspace->vm_shm; 271 if (shmmap_s == NULL) { 272 size = shminfo.shmseg * sizeof(struct shmmap_state); 273 shmmap_s = malloc(size, M_SHM, M_WAITOK); 274 for (i = 0; i < shminfo.shmseg; i++) 275 shmmap_s[i].shmid = -1; 276 p->p_vmspace->vm_shm = (caddr_t)shmmap_s; 277 } 278 shmseg = shm_find_segment_by_shmid(shmid, findremoved); 279 if (shmseg == NULL) 280 return EINVAL; 281 error = ipcperm(cred, &shmseg->shm_perm, 282 (shmflg & SHM_RDONLY) ? IPC_R : IPC_R|IPC_W); 283 if (error) 284 return error; 285 for (i = 0; i < shminfo.shmseg; i++) { 286 if (shmmap_s->shmid == -1) 287 break; 288 shmmap_s++; 289 } 290 if (i >= shminfo.shmseg) 291 return EMFILE; 292 size = (shmseg->shm_segsz + PGOFSET) & ~PGOFSET; 293 prot = VM_PROT_READ; 294 if ((shmflg & SHM_RDONLY) == 0) 295 prot |= VM_PROT_WRITE; 296 flags = MAP_ANON | MAP_SHARED; 297 if (shmaddr) { 298 flags |= MAP_FIXED; 299 if (shmflg & SHM_RND) 300 attach_va = 301 (vaddr_t)shmaddr & ~(SHMLBA-1); 302 else if (((vaddr_t)shmaddr & (SHMLBA-1)) == 0) 303 attach_va = (vaddr_t)shmaddr; 304 else 305 return EINVAL; 306 } else { 307 /* This is just a hint to uvm_mmap() about where to put it. */ 308 attach_va = 309 round_page((vaddr_t)p->p_vmspace->vm_taddr + 310 MAXTSIZ + MAXDSIZ); 311 } 312 shm_handle = shmseg->_shm_internal; 313 uao_reference(shm_handle->shm_object); 314 error = uvm_map(&p->p_vmspace->vm_map, &attach_va, size, 315 shm_handle->shm_object, 0, 0, 316 UVM_MAPFLAG(prot, prot, UVM_INH_SHARE, UVM_ADV_RANDOM, 0)); 317 if (error) { 318 return error; 319 } 320 shmmap_s->va = attach_va; 321 shmmap_s->shmid = shmid; 322 shmseg->shm_lpid = p->p_pid; 323 shmseg->shm_atime = time.tv_sec; 324 shmseg->shm_nattch++; 325 *attachp = attach_va; 326 return 0; 327 } 328 329 int 330 sys___shmctl13(l, v, retval) 331 struct lwp *l; 332 void *v; 333 register_t *retval; 334 { 335 struct sys___shmctl13_args /* { 336 syscallarg(int) shmid; 337 syscallarg(int) cmd; 338 syscallarg(struct shmid_ds *) buf; 339 } */ *uap = v; 340 struct proc *p = l->l_proc; 341 struct shmid_ds shmbuf; 342 int cmd, error; 343 344 cmd = SCARG(uap, cmd); 345 346 if (cmd == IPC_SET) { 347 error = copyin(SCARG(uap, buf), &shmbuf, sizeof(shmbuf)); 348 if (error) 349 return (error); 350 } 351 352 error = shmctl1(p, SCARG(uap, shmid), cmd, 353 (cmd == IPC_SET || cmd == IPC_STAT) ? &shmbuf : NULL); 354 355 if (error == 0 && cmd == IPC_STAT) 356 error = copyout(&shmbuf, SCARG(uap, buf), sizeof(shmbuf)); 357 358 return (error); 359 } 360 361 int 362 shmctl1(p, shmid, cmd, shmbuf) 363 struct proc *p; 364 int shmid; 365 int cmd; 366 struct shmid_ds *shmbuf; 367 { 368 struct ucred *cred = p->p_ucred; 369 struct shmid_ds *shmseg; 370 int error = 0; 371 372 shmseg = shm_find_segment_by_shmid(shmid, 0); 373 if (shmseg == NULL) 374 return EINVAL; 375 switch (cmd) { 376 case IPC_STAT: 377 if ((error = ipcperm(cred, &shmseg->shm_perm, IPC_R)) != 0) 378 return error; 379 memcpy(shmbuf, shmseg, sizeof(struct shmid_ds)); 380 break; 381 case IPC_SET: 382 if ((error = ipcperm(cred, &shmseg->shm_perm, IPC_M)) != 0) 383 return error; 384 shmseg->shm_perm.uid = shmbuf->shm_perm.uid; 385 shmseg->shm_perm.gid = shmbuf->shm_perm.gid; 386 shmseg->shm_perm.mode = 387 (shmseg->shm_perm.mode & ~ACCESSPERMS) | 388 (shmbuf->shm_perm.mode & ACCESSPERMS); 389 shmseg->shm_ctime = time.tv_sec; 390 break; 391 case IPC_RMID: 392 if ((error = ipcperm(cred, &shmseg->shm_perm, IPC_M)) != 0) 393 return error; 394 shmseg->shm_perm._key = IPC_PRIVATE; 395 shmseg->shm_perm.mode |= SHMSEG_REMOVED; 396 if (shmseg->shm_nattch <= 0) { 397 shm_deallocate_segment(shmseg); 398 shm_last_free = IPCID_TO_IX(shmid); 399 } 400 break; 401 case SHM_LOCK: 402 case SHM_UNLOCK: 403 default: 404 return EINVAL; 405 } 406 return 0; 407 } 408 409 static int 410 shmget_existing(p, uap, mode, segnum, retval) 411 struct proc *p; 412 struct sys_shmget_args /* { 413 syscallarg(key_t) key; 414 syscallarg(size_t) size; 415 syscallarg(int) shmflg; 416 } */ *uap; 417 int mode; 418 int segnum; 419 register_t *retval; 420 { 421 struct shmid_ds *shmseg; 422 struct ucred *cred = p->p_ucred; 423 int error; 424 425 shmseg = &shmsegs[segnum]; 426 if (shmseg->shm_perm.mode & SHMSEG_REMOVED) { 427 /* 428 * This segment is in the process of being allocated. Wait 429 * until it's done, and look the key up again (in case the 430 * allocation failed or it was freed). 431 */ 432 shmseg->shm_perm.mode |= SHMSEG_WANTED; 433 error = tsleep((caddr_t)shmseg, PLOCK | PCATCH, "shmget", 0); 434 if (error) 435 return error; 436 return EAGAIN; 437 } 438 if ((error = ipcperm(cred, &shmseg->shm_perm, mode)) != 0) 439 return error; 440 if (SCARG(uap, size) && SCARG(uap, size) > shmseg->shm_segsz) 441 return EINVAL; 442 if ((SCARG(uap, shmflg) & (IPC_CREAT | IPC_EXCL)) == 443 (IPC_CREAT | IPC_EXCL)) 444 return EEXIST; 445 *retval = IXSEQ_TO_IPCID(segnum, shmseg->shm_perm); 446 return 0; 447 } 448 449 static int 450 shmget_allocate_segment(p, uap, mode, retval) 451 struct proc *p; 452 struct sys_shmget_args /* { 453 syscallarg(key_t) key; 454 syscallarg(size_t) size; 455 syscallarg(int) shmflg; 456 } */ *uap; 457 int mode; 458 register_t *retval; 459 { 460 int i, segnum, shmid, size; 461 struct ucred *cred = p->p_ucred; 462 struct shmid_ds *shmseg; 463 struct shm_handle *shm_handle; 464 int error = 0; 465 466 if (SCARG(uap, size) < shminfo.shmmin || 467 SCARG(uap, size) > shminfo.shmmax) 468 return EINVAL; 469 if (shm_nused >= shminfo.shmmni) /* any shmids left? */ 470 return ENOSPC; 471 size = (SCARG(uap, size) + PGOFSET) & ~PGOFSET; 472 if (shm_committed + btoc(size) > shminfo.shmall) 473 return ENOMEM; 474 if (shm_last_free < 0) { 475 for (i = 0; i < shminfo.shmmni; i++) 476 if (shmsegs[i].shm_perm.mode & SHMSEG_FREE) 477 break; 478 if (i == shminfo.shmmni) 479 panic("shmseg free count inconsistent"); 480 segnum = i; 481 } else { 482 segnum = shm_last_free; 483 shm_last_free = -1; 484 } 485 shmseg = &shmsegs[segnum]; 486 /* 487 * In case we sleep in malloc(), mark the segment present but deleted 488 * so that noone else tries to create the same key. 489 */ 490 shmseg->shm_perm.mode = SHMSEG_ALLOCATED | SHMSEG_REMOVED; 491 shmseg->shm_perm._key = SCARG(uap, key); 492 shmseg->shm_perm._seq = (shmseg->shm_perm._seq + 1) & 0x7fff; 493 shm_handle = (struct shm_handle *) 494 malloc(sizeof(struct shm_handle), M_SHM, M_WAITOK); 495 shmid = IXSEQ_TO_IPCID(segnum, shmseg->shm_perm); 496 497 shm_handle->shm_object = uao_create(size, 0); 498 499 shmseg->_shm_internal = shm_handle; 500 shmseg->shm_perm.cuid = shmseg->shm_perm.uid = cred->cr_uid; 501 shmseg->shm_perm.cgid = shmseg->shm_perm.gid = cred->cr_gid; 502 shmseg->shm_perm.mode = (shmseg->shm_perm.mode & SHMSEG_WANTED) | 503 (mode & ACCESSPERMS) | SHMSEG_ALLOCATED; 504 shmseg->shm_segsz = SCARG(uap, size); 505 shmseg->shm_cpid = p->p_pid; 506 shmseg->shm_lpid = shmseg->shm_nattch = 0; 507 shmseg->shm_atime = shmseg->shm_dtime = 0; 508 shmseg->shm_ctime = time.tv_sec; 509 shm_committed += btoc(size); 510 shm_nused++; 511 512 *retval = shmid; 513 if (shmseg->shm_perm.mode & SHMSEG_WANTED) { 514 /* 515 * Somebody else wanted this key while we were asleep. Wake 516 * them up now. 517 */ 518 shmseg->shm_perm.mode &= ~SHMSEG_WANTED; 519 wakeup((caddr_t)shmseg); 520 } 521 return error; 522 } 523 524 int 525 sys_shmget(l, v, retval) 526 struct lwp *l; 527 void *v; 528 register_t *retval; 529 { 530 struct sys_shmget_args /* { 531 syscallarg(key_t) key; 532 syscallarg(int) size; 533 syscallarg(int) shmflg; 534 } */ *uap = v; 535 struct proc *p = l->l_proc; 536 int segnum, mode, error; 537 538 mode = SCARG(uap, shmflg) & ACCESSPERMS; 539 if (SCARG(uap, key) != IPC_PRIVATE) { 540 again: 541 segnum = shm_find_segment_by_key(SCARG(uap, key)); 542 if (segnum >= 0) { 543 error = shmget_existing(p, uap, mode, segnum, retval); 544 if (error == EAGAIN) 545 goto again; 546 return error; 547 } 548 if ((SCARG(uap, shmflg) & IPC_CREAT) == 0) 549 return ENOENT; 550 } 551 return shmget_allocate_segment(p, uap, mode, retval); 552 } 553 554 void 555 shmfork(vm1, vm2) 556 struct vmspace *vm1, *vm2; 557 { 558 struct shmmap_state *shmmap_s; 559 size_t size; 560 int i; 561 562 if (vm1->vm_shm == NULL) { 563 vm2->vm_shm = NULL; 564 return; 565 } 566 567 size = shminfo.shmseg * sizeof(struct shmmap_state); 568 shmmap_s = malloc(size, M_SHM, M_WAITOK); 569 memcpy(shmmap_s, vm1->vm_shm, size); 570 vm2->vm_shm = (caddr_t)shmmap_s; 571 for (i = 0; i < shminfo.shmseg; i++, shmmap_s++) 572 if (shmmap_s->shmid != -1) 573 shmsegs[IPCID_TO_IX(shmmap_s->shmid)].shm_nattch++; 574 } 575 576 void 577 shmexit(vm) 578 struct vmspace *vm; 579 { 580 struct shmmap_state *shmmap_s; 581 int i; 582 583 shmmap_s = (struct shmmap_state *)vm->vm_shm; 584 if (shmmap_s == NULL) 585 return; 586 for (i = 0; i < shminfo.shmseg; i++, shmmap_s++) 587 if (shmmap_s->shmid != -1) 588 shm_delete_mapping(vm, shmmap_s); 589 free(vm->vm_shm, M_SHM); 590 vm->vm_shm = NULL; 591 } 592 593 void 594 shminit() 595 { 596 int i; 597 598 shminfo.shmmax *= PAGE_SIZE; 599 600 for (i = 0; i < shminfo.shmmni; i++) { 601 shmsegs[i].shm_perm.mode = SHMSEG_FREE; 602 shmsegs[i].shm_perm._seq = 0; 603 } 604 shm_last_free = 0; 605 shm_nused = 0; 606 shm_committed = 0; 607 } 608