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