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