1 /* $NetBSD: sysv_shm.c,v 1.64 2002/04/03 11:53:01 fvdl 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.64 2002/04/03 11:53:01 fvdl 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/syscallargs.h> 84 85 #include <uvm/uvm_extern.h> 86 87 struct shmid_ds *shm_find_segment_by_shmid __P((int, int)); 88 89 /* 90 * Provides the following externally accessible functions: 91 * 92 * shminit(void); initialization 93 * shmexit(struct vmspace *) cleanup 94 * shmfork(struct vmspace *, struct vmspace *) fork handling 95 * 96 * Structures: 97 * shmsegs (an array of 'struct shmid_ds') 98 * per proc array of 'struct shmmap_state' 99 */ 100 101 #define SHMSEG_FREE 0x0200 102 #define SHMSEG_REMOVED 0x0400 103 #define SHMSEG_ALLOCATED 0x0800 104 #define SHMSEG_WANTED 0x1000 105 106 int shm_last_free, shm_nused, shm_committed; 107 struct shmid_ds *shmsegs; 108 109 struct shm_handle { 110 struct uvm_object *shm_object; 111 }; 112 113 struct shmmap_state { 114 vaddr_t va; 115 int shmid; 116 }; 117 118 static int shm_find_segment_by_key __P((key_t)); 119 static void shm_deallocate_segment __P((struct shmid_ds *)); 120 static void shm_delete_mapping __P((struct vmspace *, struct shmmap_state *)); 121 static int shmget_existing __P((struct proc *, struct sys_shmget_args *, 122 int, int, register_t *)); 123 static int shmget_allocate_segment __P((struct proc *, struct sys_shmget_args *, 124 int, register_t *)); 125 126 static int 127 shm_find_segment_by_key(key) 128 key_t key; 129 { 130 int i; 131 132 for (i = 0; i < shminfo.shmmni; i++) 133 if ((shmsegs[i].shm_perm.mode & SHMSEG_ALLOCATED) && 134 shmsegs[i].shm_perm._key == key) 135 return i; 136 return -1; 137 } 138 139 struct shmid_ds * 140 shm_find_segment_by_shmid(shmid, findremoved) 141 int shmid; 142 int findremoved; 143 { 144 int segnum; 145 struct shmid_ds *shmseg; 146 147 segnum = IPCID_TO_IX(shmid); 148 if (segnum < 0 || segnum >= shminfo.shmmni) 149 return NULL; 150 shmseg = &shmsegs[segnum]; 151 if ((shmseg->shm_perm.mode & SHMSEG_ALLOCATED) == 0) 152 return NULL; 153 if (!findremoved && ((shmseg->shm_perm.mode & SHMSEG_REMOVED) != 0)) 154 return NULL; 155 if (shmseg->shm_perm._seq != IPCID_TO_SEQ(shmid)) 156 return NULL; 157 return shmseg; 158 } 159 160 static void 161 shm_deallocate_segment(shmseg) 162 struct shmid_ds *shmseg; 163 { 164 struct shm_handle *shm_handle; 165 size_t size; 166 167 shm_handle = shmseg->_shm_internal; 168 size = (shmseg->shm_segsz + PGOFSET) & ~PGOFSET; 169 uao_detach(shm_handle->shm_object); 170 free((caddr_t)shm_handle, M_SHM); 171 shmseg->_shm_internal = NULL; 172 shm_committed -= btoc(size); 173 shmseg->shm_perm.mode = SHMSEG_FREE; 174 shm_nused--; 175 } 176 177 static void 178 shm_delete_mapping(vm, shmmap_s) 179 struct vmspace *vm; 180 struct shmmap_state *shmmap_s; 181 { 182 struct shmid_ds *shmseg; 183 int segnum; 184 size_t size; 185 186 segnum = IPCID_TO_IX(shmmap_s->shmid); 187 shmseg = &shmsegs[segnum]; 188 size = (shmseg->shm_segsz + PGOFSET) & ~PGOFSET; 189 uvm_deallocate(&vm->vm_map, shmmap_s->va, size); 190 shmmap_s->shmid = -1; 191 shmseg->shm_dtime = time.tv_sec; 192 if ((--shmseg->shm_nattch <= 0) && 193 (shmseg->shm_perm.mode & SHMSEG_REMOVED)) { 194 shm_deallocate_segment(shmseg); 195 shm_last_free = segnum; 196 } 197 } 198 199 int 200 sys_shmdt(p, v, retval) 201 struct proc *p; 202 void *v; 203 register_t *retval; 204 { 205 struct sys_shmdt_args /* { 206 syscallarg(const void *) shmaddr; 207 } */ *uap = v; 208 struct shmmap_state *shmmap_s; 209 int i; 210 211 shmmap_s = (struct shmmap_state *)p->p_vmspace->vm_shm; 212 if (shmmap_s == NULL) 213 return EINVAL; 214 215 for (i = 0; i < shminfo.shmseg; i++, shmmap_s++) 216 if (shmmap_s->shmid != -1 && 217 shmmap_s->va == (vaddr_t)SCARG(uap, shmaddr)) 218 break; 219 if (i == shminfo.shmseg) 220 return EINVAL; 221 shm_delete_mapping(p->p_vmspace, shmmap_s); 222 return 0; 223 } 224 225 int 226 sys_shmat(p, v, retval) 227 struct proc *p; 228 void *v; 229 register_t *retval; 230 { 231 struct sys_shmat_args /* { 232 syscallarg(int) shmid; 233 syscallarg(const void *) shmaddr; 234 syscallarg(int) shmflg; 235 } */ *uap = v; 236 vaddr_t attach_va; 237 int error; 238 239 error = shmat1(p, SCARG(uap, shmid), SCARG(uap, shmaddr), 240 SCARG(uap, shmflg), &attach_va, 0); 241 if (error != 0) 242 return error; 243 retval[0] = attach_va; 244 return 0; 245 } 246 247 int 248 shmat1(p, shmid, shmaddr, shmflg, attachp, findremoved) 249 struct proc *p; 250 int shmid; 251 const void *shmaddr; 252 int shmflg; 253 vaddr_t *attachp; 254 int findremoved; 255 { 256 int error, i, flags; 257 struct ucred *cred = p->p_ucred; 258 struct shmid_ds *shmseg; 259 struct shmmap_state *shmmap_s = NULL; 260 struct shm_handle *shm_handle; 261 vaddr_t attach_va; 262 vm_prot_t prot; 263 vsize_t size; 264 265 shmmap_s = (struct shmmap_state *)p->p_vmspace->vm_shm; 266 if (shmmap_s == NULL) { 267 size = shminfo.shmseg * sizeof(struct shmmap_state); 268 shmmap_s = malloc(size, M_SHM, M_WAITOK); 269 for (i = 0; i < shminfo.shmseg; i++) 270 shmmap_s[i].shmid = -1; 271 p->p_vmspace->vm_shm = (caddr_t)shmmap_s; 272 } 273 shmseg = shm_find_segment_by_shmid(shmid, findremoved); 274 if (shmseg == NULL) 275 return EINVAL; 276 error = ipcperm(cred, &shmseg->shm_perm, 277 (shmflg & SHM_RDONLY) ? IPC_R : IPC_R|IPC_W); 278 if (error) 279 return error; 280 for (i = 0; i < shminfo.shmseg; i++) { 281 if (shmmap_s->shmid == -1) 282 break; 283 shmmap_s++; 284 } 285 if (i >= shminfo.shmseg) 286 return EMFILE; 287 size = (shmseg->shm_segsz + PGOFSET) & ~PGOFSET; 288 prot = VM_PROT_READ; 289 if ((shmflg & SHM_RDONLY) == 0) 290 prot |= VM_PROT_WRITE; 291 flags = MAP_ANON | MAP_SHARED; 292 if (shmaddr) { 293 flags |= MAP_FIXED; 294 if (shmflg & SHM_RND) 295 attach_va = 296 (vaddr_t)shmaddr & ~(SHMLBA-1); 297 else if (((vaddr_t)shmaddr & (SHMLBA-1)) == 0) 298 attach_va = (vaddr_t)shmaddr; 299 else 300 return EINVAL; 301 } else { 302 /* This is just a hint to vm_mmap() about where to put it. */ 303 attach_va = 304 round_page((vaddr_t)p->p_vmspace->vm_taddr + 305 MAXTSIZ + MAXDSIZ); 306 } 307 shm_handle = shmseg->_shm_internal; 308 uao_reference(shm_handle->shm_object); 309 error = uvm_map(&p->p_vmspace->vm_map, &attach_va, size, 310 shm_handle->shm_object, 0, 0, 311 UVM_MAPFLAG(prot, prot, UVM_INH_SHARE, UVM_ADV_RANDOM, 0)); 312 if (error) { 313 return error; 314 } 315 shmmap_s->va = attach_va; 316 shmmap_s->shmid = shmid; 317 shmseg->shm_lpid = p->p_pid; 318 shmseg->shm_atime = time.tv_sec; 319 shmseg->shm_nattch++; 320 *attachp = attach_va; 321 return 0; 322 } 323 324 int 325 sys___shmctl13(p, v, retval) 326 struct proc *p; 327 void *v; 328 register_t *retval; 329 { 330 struct sys___shmctl13_args /* { 331 syscallarg(int) shmid; 332 syscallarg(int) cmd; 333 syscallarg(struct shmid_ds *) buf; 334 } */ *uap = v; 335 struct shmid_ds shmbuf; 336 int cmd, error; 337 338 cmd = SCARG(uap, cmd); 339 340 if (cmd == IPC_SET) { 341 error = copyin(SCARG(uap, buf), &shmbuf, sizeof(shmbuf)); 342 if (error) 343 return (error); 344 } 345 346 error = shmctl1(p, SCARG(uap, shmid), cmd, 347 (cmd == IPC_SET || cmd == IPC_STAT) ? &shmbuf : NULL); 348 349 if (error == 0 && cmd == IPC_STAT) 350 error = copyout(&shmbuf, SCARG(uap, buf), sizeof(shmbuf)); 351 352 return (error); 353 } 354 355 int 356 shmctl1(p, shmid, cmd, shmbuf) 357 struct proc *p; 358 int shmid; 359 int cmd; 360 struct shmid_ds *shmbuf; 361 { 362 struct ucred *cred = p->p_ucred; 363 struct shmid_ds *shmseg; 364 int error = 0; 365 366 shmseg = shm_find_segment_by_shmid(shmid, 0); 367 if (shmseg == NULL) 368 return EINVAL; 369 switch (cmd) { 370 case IPC_STAT: 371 if ((error = ipcperm(cred, &shmseg->shm_perm, IPC_R)) != 0) 372 return error; 373 memcpy(shmbuf, shmseg, sizeof(struct shmid_ds)); 374 break; 375 case IPC_SET: 376 if ((error = ipcperm(cred, &shmseg->shm_perm, IPC_M)) != 0) 377 return error; 378 shmseg->shm_perm.uid = shmbuf->shm_perm.uid; 379 shmseg->shm_perm.gid = shmbuf->shm_perm.gid; 380 shmseg->shm_perm.mode = 381 (shmseg->shm_perm.mode & ~ACCESSPERMS) | 382 (shmbuf->shm_perm.mode & ACCESSPERMS); 383 shmseg->shm_ctime = time.tv_sec; 384 break; 385 case IPC_RMID: 386 if ((error = ipcperm(cred, &shmseg->shm_perm, IPC_M)) != 0) 387 return error; 388 shmseg->shm_perm._key = IPC_PRIVATE; 389 shmseg->shm_perm.mode |= SHMSEG_REMOVED; 390 if (shmseg->shm_nattch <= 0) { 391 shm_deallocate_segment(shmseg); 392 shm_last_free = IPCID_TO_IX(shmid); 393 } 394 break; 395 case SHM_LOCK: 396 case SHM_UNLOCK: 397 default: 398 return EINVAL; 399 } 400 return 0; 401 } 402 403 static int 404 shmget_existing(p, uap, mode, segnum, retval) 405 struct proc *p; 406 struct sys_shmget_args /* { 407 syscallarg(key_t) key; 408 syscallarg(size_t) size; 409 syscallarg(int) shmflg; 410 } */ *uap; 411 int mode; 412 int segnum; 413 register_t *retval; 414 { 415 struct shmid_ds *shmseg; 416 struct ucred *cred = p->p_ucred; 417 int error; 418 419 shmseg = &shmsegs[segnum]; 420 if (shmseg->shm_perm.mode & SHMSEG_REMOVED) { 421 /* 422 * This segment is in the process of being allocated. Wait 423 * until it's done, and look the key up again (in case the 424 * allocation failed or it was freed). 425 */ 426 shmseg->shm_perm.mode |= SHMSEG_WANTED; 427 error = tsleep((caddr_t)shmseg, PLOCK | PCATCH, "shmget", 0); 428 if (error) 429 return error; 430 return EAGAIN; 431 } 432 if ((error = ipcperm(cred, &shmseg->shm_perm, mode)) != 0) 433 return error; 434 if (SCARG(uap, size) && SCARG(uap, size) > shmseg->shm_segsz) 435 return EINVAL; 436 if ((SCARG(uap, shmflg) & (IPC_CREAT | IPC_EXCL)) == 437 (IPC_CREAT | IPC_EXCL)) 438 return EEXIST; 439 *retval = IXSEQ_TO_IPCID(segnum, shmseg->shm_perm); 440 return 0; 441 } 442 443 static int 444 shmget_allocate_segment(p, uap, mode, retval) 445 struct proc *p; 446 struct sys_shmget_args /* { 447 syscallarg(key_t) key; 448 syscallarg(size_t) size; 449 syscallarg(int) shmflg; 450 } */ *uap; 451 int mode; 452 register_t *retval; 453 { 454 int i, segnum, shmid, size; 455 struct ucred *cred = p->p_ucred; 456 struct shmid_ds *shmseg; 457 struct shm_handle *shm_handle; 458 int error = 0; 459 460 if (SCARG(uap, size) < shminfo.shmmin || 461 SCARG(uap, size) > shminfo.shmmax) 462 return EINVAL; 463 if (shm_nused >= shminfo.shmmni) /* any shmids left? */ 464 return ENOSPC; 465 size = (SCARG(uap, size) + PGOFSET) & ~PGOFSET; 466 if (shm_committed + btoc(size) > shminfo.shmall) 467 return ENOMEM; 468 if (shm_last_free < 0) { 469 for (i = 0; i < shminfo.shmmni; i++) 470 if (shmsegs[i].shm_perm.mode & SHMSEG_FREE) 471 break; 472 if (i == shminfo.shmmni) 473 panic("shmseg free count inconsistent"); 474 segnum = i; 475 } else { 476 segnum = shm_last_free; 477 shm_last_free = -1; 478 } 479 shmseg = &shmsegs[segnum]; 480 /* 481 * In case we sleep in malloc(), mark the segment present but deleted 482 * so that noone else tries to create the same key. 483 */ 484 shmseg->shm_perm.mode = SHMSEG_ALLOCATED | SHMSEG_REMOVED; 485 shmseg->shm_perm._key = SCARG(uap, key); 486 shmseg->shm_perm._seq = (shmseg->shm_perm._seq + 1) & 0x7fff; 487 shm_handle = (struct shm_handle *) 488 malloc(sizeof(struct shm_handle), M_SHM, M_WAITOK); 489 shmid = IXSEQ_TO_IPCID(segnum, shmseg->shm_perm); 490 491 shm_handle->shm_object = uao_create(size, 0); 492 493 shmseg->_shm_internal = shm_handle; 494 shmseg->shm_perm.cuid = shmseg->shm_perm.uid = cred->cr_uid; 495 shmseg->shm_perm.cgid = shmseg->shm_perm.gid = cred->cr_gid; 496 shmseg->shm_perm.mode = (shmseg->shm_perm.mode & SHMSEG_WANTED) | 497 (mode & ACCESSPERMS) | SHMSEG_ALLOCATED; 498 shmseg->shm_segsz = SCARG(uap, size); 499 shmseg->shm_cpid = p->p_pid; 500 shmseg->shm_lpid = shmseg->shm_nattch = 0; 501 shmseg->shm_atime = shmseg->shm_dtime = 0; 502 shmseg->shm_ctime = time.tv_sec; 503 shm_committed += btoc(size); 504 shm_nused++; 505 506 *retval = shmid; 507 if (shmseg->shm_perm.mode & SHMSEG_WANTED) { 508 /* 509 * Somebody else wanted this key while we were asleep. Wake 510 * them up now. 511 */ 512 shmseg->shm_perm.mode &= ~SHMSEG_WANTED; 513 wakeup((caddr_t)shmseg); 514 } 515 return error; 516 } 517 518 int 519 sys_shmget(p, v, retval) 520 struct proc *p; 521 void *v; 522 register_t *retval; 523 { 524 struct sys_shmget_args /* { 525 syscallarg(key_t) key; 526 syscallarg(int) size; 527 syscallarg(int) shmflg; 528 } */ *uap = v; 529 int segnum, mode, error; 530 531 mode = SCARG(uap, shmflg) & ACCESSPERMS; 532 if (SCARG(uap, key) != IPC_PRIVATE) { 533 again: 534 segnum = shm_find_segment_by_key(SCARG(uap, key)); 535 if (segnum >= 0) { 536 error = shmget_existing(p, uap, mode, segnum, retval); 537 if (error == EAGAIN) 538 goto again; 539 return error; 540 } 541 if ((SCARG(uap, shmflg) & IPC_CREAT) == 0) 542 return ENOENT; 543 } 544 return shmget_allocate_segment(p, uap, mode, retval); 545 } 546 547 void 548 shmfork(vm1, vm2) 549 struct vmspace *vm1, *vm2; 550 { 551 struct shmmap_state *shmmap_s; 552 size_t size; 553 int i; 554 555 if (vm1->vm_shm == NULL) { 556 vm2->vm_shm = NULL; 557 return; 558 } 559 560 size = shminfo.shmseg * sizeof(struct shmmap_state); 561 shmmap_s = malloc(size, M_SHM, M_WAITOK); 562 memcpy(shmmap_s, vm1->vm_shm, size); 563 vm2->vm_shm = (caddr_t)shmmap_s; 564 for (i = 0; i < shminfo.shmseg; i++, shmmap_s++) 565 if (shmmap_s->shmid != -1) 566 shmsegs[IPCID_TO_IX(shmmap_s->shmid)].shm_nattch++; 567 } 568 569 void 570 shmexit(vm) 571 struct vmspace *vm; 572 { 573 struct shmmap_state *shmmap_s; 574 int i; 575 576 shmmap_s = (struct shmmap_state *)vm->vm_shm; 577 if (shmmap_s == NULL) 578 return; 579 for (i = 0; i < shminfo.shmseg; i++, shmmap_s++) 580 if (shmmap_s->shmid != -1) 581 shm_delete_mapping(vm, shmmap_s); 582 free(vm->vm_shm, M_SHM); 583 vm->vm_shm = NULL; 584 } 585 586 void 587 shminit() 588 { 589 int i; 590 591 shminfo.shmmax *= PAGE_SIZE; 592 593 for (i = 0; i < shminfo.shmmni; i++) { 594 shmsegs[i].shm_perm.mode = SHMSEG_FREE; 595 shmsegs[i].shm_perm._seq = 0; 596 } 597 shm_last_free = 0; 598 shm_nused = 0; 599 shm_committed = 0; 600 } 601