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