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