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