1 /* $Id: sysv_shm.c,v 1.9 1995/09/09 18:10:09 davidg Exp $ */ 2 /* $NetBSD: sysv_shm.c,v 1.23 1994/07/04 23:25:12 glass Exp $ */ 3 4 /* 5 * Copyright (c) 1994 Adam Glass and Charles Hannum. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by Adam Glass and Charles 18 * Hannum. 19 * 4. The names of the authors may not be used to endorse or promote products 20 * derived from this software without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 31 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 #include <sys/param.h> 35 #include <sys/systm.h> 36 #include <sys/sysproto.h> 37 #include <sys/kernel.h> 38 #include <sys/shm.h> 39 #include <sys/proc.h> 40 #include <sys/malloc.h> 41 #include <sys/mman.h> 42 #include <sys/stat.h> 43 #include <sys/sysent.h> 44 45 #include <vm/vm.h> 46 #include <vm/vm_map.h> 47 #include <vm/vm_kern.h> 48 49 struct shmat_args; 50 extern int shmat __P((struct proc *p, struct shmat_args *uap, int *retval)); 51 struct shmctl_args; 52 extern int shmctl __P((struct proc *p, struct shmctl_args *uap, int *retval)); 53 struct shmdt_args; 54 extern int shmdt __P((struct proc *p, struct shmdt_args *uap, int *retval)); 55 struct shmget_args; 56 extern int shmget __P((struct proc *p, struct shmget_args *uap, int *retval)); 57 58 static void shminit __P((void *)); 59 SYSINIT(sysv_shm, SI_SUB_SYSV_SHM, SI_ORDER_FIRST, shminit, NULL) 60 61 struct oshmctl_args; 62 int oshmctl __P((struct proc *p, struct oshmctl_args *uap, int *retval)); 63 static int shmget_allocate_segment __P((struct proc *p, struct shmget_args *uap, int mode, int *retval)); 64 static int shmget_existing __P((struct proc *p, struct shmget_args *uap, int mode, int segnum, int *retval)); 65 66 /* XXX casting to (sy_call_t *) is bogus, as usual. */ 67 sy_call_t *shmcalls[] = { 68 (sy_call_t *)shmat, (sy_call_t *)oshmctl, 69 (sy_call_t *)shmdt, (sy_call_t *)shmget, 70 (sy_call_t *)shmctl 71 }; 72 73 #define SHMSEG_FREE 0x0200 74 #define SHMSEG_REMOVED 0x0400 75 #define SHMSEG_ALLOCATED 0x0800 76 #define SHMSEG_WANTED 0x1000 77 78 vm_map_t sysvshm_map; 79 int shm_last_free, shm_nused, shm_committed; 80 struct shmid_ds *shmsegs; 81 82 struct shm_handle { 83 vm_offset_t kva; 84 }; 85 86 struct shmmap_state { 87 vm_offset_t va; 88 int shmid; 89 }; 90 91 static void shm_deallocate_segment __P((struct shmid_ds *)); 92 static int shm_find_segment_by_key __P((key_t)); 93 static struct shmid_ds *shm_find_segment_by_shmid __P((int)); 94 static int shm_delete_mapping __P((struct proc *, struct shmmap_state *)); 95 96 static int 97 shm_find_segment_by_key(key) 98 key_t key; 99 { 100 int i; 101 102 for (i = 0; i < shminfo.shmmni; i++) 103 if ((shmsegs[i].shm_perm.mode & SHMSEG_ALLOCATED) && 104 shmsegs[i].shm_perm.key == key) 105 return i; 106 return -1; 107 } 108 109 static struct shmid_ds * 110 shm_find_segment_by_shmid(shmid) 111 int shmid; 112 { 113 int segnum; 114 struct shmid_ds *shmseg; 115 116 segnum = IPCID_TO_IX(shmid); 117 if (segnum < 0 || segnum >= shminfo.shmmni) 118 return NULL; 119 shmseg = &shmsegs[segnum]; 120 if ((shmseg->shm_perm.mode & (SHMSEG_ALLOCATED | SHMSEG_REMOVED)) 121 != SHMSEG_ALLOCATED || 122 shmseg->shm_perm.seq != IPCID_TO_SEQ(shmid)) 123 return NULL; 124 return shmseg; 125 } 126 127 static void 128 shm_deallocate_segment(shmseg) 129 struct shmid_ds *shmseg; 130 { 131 struct shm_handle *shm_handle; 132 size_t size; 133 134 shm_handle = shmseg->shm_internal; 135 size = (shmseg->shm_segsz + CLOFSET) & ~CLOFSET; 136 (void) vm_map_remove(sysvshm_map, shm_handle->kva, shm_handle->kva + size); 137 free((caddr_t)shm_handle, M_SHM); 138 shmseg->shm_internal = NULL; 139 shm_committed -= btoc(size); 140 shm_nused--; 141 shmseg->shm_perm.mode = SHMSEG_FREE; 142 } 143 144 static int 145 shm_delete_mapping(p, shmmap_s) 146 struct proc *p; 147 struct shmmap_state *shmmap_s; 148 { 149 struct shmid_ds *shmseg; 150 int segnum, result; 151 size_t size; 152 153 segnum = IPCID_TO_IX(shmmap_s->shmid); 154 shmseg = &shmsegs[segnum]; 155 size = (shmseg->shm_segsz + CLOFSET) & ~CLOFSET; 156 result = vm_map_remove(&p->p_vmspace->vm_map, shmmap_s->va, shmmap_s->va + size); 157 if (result != KERN_SUCCESS) 158 return EINVAL; 159 shmmap_s->shmid = -1; 160 shmseg->shm_dtime = time.tv_sec; 161 if ((--shmseg->shm_nattch <= 0) && 162 (shmseg->shm_perm.mode & SHMSEG_REMOVED)) { 163 shm_deallocate_segment(shmseg); 164 shm_last_free = segnum; 165 } 166 return 0; 167 } 168 169 struct shmdt_args { 170 void *shmaddr; 171 }; 172 int 173 shmdt(p, uap, retval) 174 struct proc *p; 175 struct shmdt_args *uap; 176 int *retval; 177 { 178 struct shmmap_state *shmmap_s; 179 int i; 180 181 shmmap_s = (struct shmmap_state *)p->p_vmspace->vm_shm; 182 for (i = 0; i < shminfo.shmseg; i++, shmmap_s++) 183 if (shmmap_s->shmid != -1 && 184 shmmap_s->va == (vm_offset_t)uap->shmaddr) 185 break; 186 if (i == shminfo.shmseg) 187 return EINVAL; 188 return shm_delete_mapping(p, shmmap_s); 189 } 190 191 struct shmat_args { 192 int shmid; 193 void *shmaddr; 194 int shmflg; 195 }; 196 int 197 shmat(p, uap, retval) 198 struct proc *p; 199 struct shmat_args *uap; 200 int *retval; 201 { 202 int error, i, flags; 203 struct ucred *cred = p->p_ucred; 204 struct shmid_ds *shmseg; 205 struct shmmap_state *shmmap_s = NULL; 206 vm_offset_t attach_va; 207 vm_prot_t prot; 208 vm_size_t size; 209 210 shmmap_s = (struct shmmap_state *)p->p_vmspace->vm_shm; 211 if (shmmap_s == NULL) { 212 size = shminfo.shmseg * sizeof(struct shmmap_state); 213 shmmap_s = malloc(size, M_SHM, M_WAITOK); 214 for (i = 0; i < shminfo.shmseg; i++) 215 shmmap_s[i].shmid = -1; 216 p->p_vmspace->vm_shm = (caddr_t)shmmap_s; 217 } 218 shmseg = shm_find_segment_by_shmid(uap->shmid); 219 if (shmseg == NULL) 220 return EINVAL; 221 error = ipcperm(cred, &shmseg->shm_perm, 222 (uap->shmflg & SHM_RDONLY) ? IPC_R : IPC_R|IPC_W); 223 if (error) 224 return error; 225 for (i = 0; i < shminfo.shmseg; i++) { 226 if (shmmap_s->shmid == -1) 227 break; 228 shmmap_s++; 229 } 230 if (i >= shminfo.shmseg) 231 return EMFILE; 232 size = (shmseg->shm_segsz + CLOFSET) & ~CLOFSET; 233 prot = VM_PROT_READ; 234 if ((uap->shmflg & SHM_RDONLY) == 0) 235 prot |= VM_PROT_WRITE; 236 flags = MAP_ANON | MAP_SHARED; 237 if (uap->shmaddr) { 238 flags |= MAP_FIXED; 239 if (uap->shmflg & SHM_RND) 240 attach_va = (vm_offset_t)uap->shmaddr & ~(SHMLBA-1); 241 else if (((vm_offset_t)uap->shmaddr & (SHMLBA-1)) == 0) 242 attach_va = (vm_offset_t)uap->shmaddr; 243 else 244 return EINVAL; 245 } else { 246 /* This is just a hint to vm_mmap() about where to put it. */ 247 attach_va = round_page(p->p_vmspace->vm_daddr + MAXDSIZ); 248 } 249 error = vm_mmap(&p->p_vmspace->vm_map, &attach_va, size, prot, 250 VM_PROT_DEFAULT, flags, (caddr_t) uap->shmid, 0); 251 if (error) 252 return error; 253 shmmap_s->va = attach_va; 254 shmmap_s->shmid = uap->shmid; 255 shmseg->shm_lpid = p->p_pid; 256 shmseg->shm_atime = time.tv_sec; 257 shmseg->shm_nattch++; 258 *retval = attach_va; 259 return 0; 260 } 261 262 struct oshmid_ds { 263 struct ipc_perm shm_perm; /* operation perms */ 264 int shm_segsz; /* size of segment (bytes) */ 265 ushort shm_cpid; /* pid, creator */ 266 ushort shm_lpid; /* pid, last operation */ 267 short shm_nattch; /* no. of current attaches */ 268 time_t shm_atime; /* last attach time */ 269 time_t shm_dtime; /* last detach time */ 270 time_t shm_ctime; /* last change time */ 271 void *shm_handle; /* internal handle for shm segment */ 272 }; 273 274 struct oshmctl_args { 275 int shmid; 276 int cmd; 277 struct oshmid_ds *ubuf; 278 }; 279 280 int 281 oshmctl(p, uap, retval) 282 struct proc *p; 283 struct oshmctl_args *uap; 284 int *retval; 285 { 286 #ifdef COMPAT_43 287 int error; 288 struct ucred *cred = p->p_ucred; 289 struct shmid_ds *shmseg; 290 struct oshmid_ds outbuf; 291 292 shmseg = shm_find_segment_by_shmid(uap->shmid); 293 if (shmseg == NULL) 294 return EINVAL; 295 switch (uap->cmd) { 296 case IPC_STAT: 297 error = ipcperm(cred, &shmseg->shm_perm, IPC_R); 298 if (error) 299 return error; 300 outbuf.shm_perm = shmseg->shm_perm; 301 outbuf.shm_segsz = shmseg->shm_segsz; 302 outbuf.shm_cpid = shmseg->shm_cpid; 303 outbuf.shm_lpid = shmseg->shm_lpid; 304 outbuf.shm_nattch = shmseg->shm_nattch; 305 outbuf.shm_atime = shmseg->shm_atime; 306 outbuf.shm_dtime = shmseg->shm_dtime; 307 outbuf.shm_ctime = shmseg->shm_ctime; 308 outbuf.shm_handle = shmseg->shm_internal; 309 error = copyout((caddr_t)&outbuf, uap->ubuf, sizeof(outbuf)); 310 if (error) 311 return error; 312 break; 313 default: 314 /* XXX casting to (sy_call_t *) is bogus, as usual. */ 315 return ((sy_call_t *)shmctl)(p, uap, retval); 316 } 317 return 0; 318 #else 319 return EINVAL; 320 #endif 321 } 322 323 struct shmctl_args { 324 int shmid; 325 int cmd; 326 struct shmid_ds *ubuf; 327 }; 328 int 329 shmctl(p, uap, retval) 330 struct proc *p; 331 struct shmctl_args *uap; 332 int *retval; 333 { 334 int error; 335 struct ucred *cred = p->p_ucred; 336 struct shmid_ds inbuf; 337 struct shmid_ds *shmseg; 338 339 shmseg = shm_find_segment_by_shmid(uap->shmid); 340 if (shmseg == NULL) 341 return EINVAL; 342 switch (uap->cmd) { 343 case IPC_STAT: 344 error = ipcperm(cred, &shmseg->shm_perm, IPC_R); 345 if (error) 346 return error; 347 error = copyout((caddr_t)shmseg, uap->ubuf, sizeof(inbuf)); 348 if (error) 349 return error; 350 break; 351 case IPC_SET: 352 error = ipcperm(cred, &shmseg->shm_perm, IPC_M); 353 if (error) 354 return error; 355 error = copyin(uap->ubuf, (caddr_t)&inbuf, sizeof(inbuf)); 356 if (error) 357 return error; 358 shmseg->shm_perm.uid = inbuf.shm_perm.uid; 359 shmseg->shm_perm.gid = inbuf.shm_perm.gid; 360 shmseg->shm_perm.mode = 361 (shmseg->shm_perm.mode & ~ACCESSPERMS) | 362 (inbuf.shm_perm.mode & ACCESSPERMS); 363 shmseg->shm_ctime = time.tv_sec; 364 break; 365 case IPC_RMID: 366 error = ipcperm(cred, &shmseg->shm_perm, IPC_M); 367 if (error) 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(uap->shmid); 374 } 375 break; 376 #if 0 377 case SHM_LOCK: 378 case SHM_UNLOCK: 379 #endif 380 default: 381 return EINVAL; 382 } 383 return 0; 384 } 385 386 struct shmget_args { 387 key_t key; 388 size_t size; 389 int shmflg; 390 }; 391 static int 392 shmget_existing(p, uap, mode, segnum, retval) 393 struct proc *p; 394 struct shmget_args *uap; 395 int mode; 396 int segnum; 397 int *retval; 398 { 399 struct shmid_ds *shmseg; 400 struct ucred *cred = p->p_ucred; 401 int error; 402 403 shmseg = &shmsegs[segnum]; 404 if (shmseg->shm_perm.mode & SHMSEG_REMOVED) { 405 /* 406 * This segment is in the process of being allocated. Wait 407 * until it's done, and look the key up again (in case the 408 * allocation failed or it was freed). 409 */ 410 shmseg->shm_perm.mode |= SHMSEG_WANTED; 411 error = tsleep((caddr_t)shmseg, PLOCK | PCATCH, "shmget", 0); 412 if (error) 413 return error; 414 return EAGAIN; 415 } 416 error = ipcperm(cred, &shmseg->shm_perm, mode); 417 if (error) 418 return error; 419 if (uap->size && uap->size > shmseg->shm_segsz) 420 return EINVAL; 421 if (uap->shmflg & (IPC_CREAT | IPC_EXCL) == (IPC_CREAT | IPC_EXCL)) 422 return EEXIST; 423 *retval = IXSEQ_TO_IPCID(segnum, shmseg->shm_perm); 424 return 0; 425 } 426 427 static int 428 shmget_allocate_segment(p, uap, mode, retval) 429 struct proc *p; 430 struct shmget_args *uap; 431 int mode; 432 int *retval; 433 { 434 int i, segnum, result, shmid, size; 435 struct ucred *cred = p->p_ucred; 436 struct shmid_ds *shmseg; 437 struct shm_handle *shm_handle; 438 439 if (uap->size < shminfo.shmmin || uap->size > shminfo.shmmax) 440 return EINVAL; 441 if (shm_nused >= shminfo.shmmni) /* any shmids left? */ 442 return ENOSPC; 443 size = (uap->size + CLOFSET) & ~CLOFSET; 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 = 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 result = vm_mmap(sysvshm_map, &shm_handle->kva, size, VM_PROT_ALL, 469 VM_PROT_DEFAULT, MAP_ANON, (caddr_t) shmid, 0); 470 if (result != KERN_SUCCESS) { 471 shmseg->shm_perm.mode = SHMSEG_FREE; 472 shm_last_free = segnum; 473 free((caddr_t)shm_handle, M_SHM); 474 /* Just in case. */ 475 wakeup((caddr_t)shmseg); 476 return ENOMEM; 477 } 478 shmseg->shm_internal = shm_handle; 479 shmseg->shm_perm.cuid = shmseg->shm_perm.uid = cred->cr_uid; 480 shmseg->shm_perm.cgid = shmseg->shm_perm.gid = cred->cr_gid; 481 shmseg->shm_perm.mode = (shmseg->shm_perm.mode & SHMSEG_WANTED) | 482 (mode & ACCESSPERMS) | SHMSEG_ALLOCATED; 483 shmseg->shm_segsz = uap->size; 484 shmseg->shm_cpid = p->p_pid; 485 shmseg->shm_lpid = shmseg->shm_nattch = 0; 486 shmseg->shm_atime = shmseg->shm_dtime = 0; 487 shmseg->shm_ctime = time.tv_sec; 488 shm_committed += btoc(size); 489 shm_nused++; 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 *retval = shmid; 499 return 0; 500 } 501 502 int 503 shmget(p, uap, retval) 504 struct proc *p; 505 struct shmget_args *uap; 506 int *retval; 507 { 508 int segnum, mode, error; 509 510 mode = uap->shmflg & ACCESSPERMS; 511 if (uap->key != IPC_PRIVATE) { 512 again: 513 segnum = shm_find_segment_by_key(uap->key); 514 if (segnum >= 0) { 515 error = shmget_existing(p, uap, mode, segnum, retval); 516 if (error == EAGAIN) 517 goto again; 518 return error; 519 } 520 if ((uap->shmflg & IPC_CREAT) == 0) 521 return ENOENT; 522 } 523 return shmget_allocate_segment(p, uap, mode, retval); 524 } 525 526 int 527 shmsys(p, uap, retval) 528 struct proc *p; 529 /* XXX actually varargs. */ 530 struct shmsys_args /* { 531 u_int which; 532 int a2; 533 int a3; 534 int a4; 535 } */ *uap; 536 int *retval; 537 { 538 539 if (uap->which >= sizeof(shmcalls)/sizeof(shmcalls[0])) 540 return EINVAL; 541 return ((*shmcalls[uap->which])(p, &uap->a2, retval)); 542 } 543 544 void 545 shmfork(p1, p2, isvfork) 546 struct proc *p1, *p2; 547 int isvfork; 548 { 549 struct shmmap_state *shmmap_s; 550 size_t size; 551 int i; 552 553 size = shminfo.shmseg * sizeof(struct shmmap_state); 554 shmmap_s = malloc(size, M_SHM, M_WAITOK); 555 bcopy((caddr_t)p1->p_vmspace->vm_shm, (caddr_t)shmmap_s, size); 556 p2->p_vmspace->vm_shm = (caddr_t)shmmap_s; 557 for (i = 0; i < shminfo.shmseg; i++, shmmap_s++) 558 if (shmmap_s->shmid != -1) 559 shmsegs[IPCID_TO_IX(shmmap_s->shmid)].shm_nattch++; 560 } 561 562 void 563 shmexit(p) 564 struct proc *p; 565 { 566 struct shmmap_state *shmmap_s; 567 int i; 568 569 shmmap_s = (struct shmmap_state *)p->p_vmspace->vm_shm; 570 for (i = 0; i < shminfo.shmseg; i++, shmmap_s++) 571 if (shmmap_s->shmid != -1) 572 shm_delete_mapping(p, shmmap_s); 573 free((caddr_t)p->p_vmspace->vm_shm, M_SHM); 574 p->p_vmspace->vm_shm = NULL; 575 } 576 577 void 578 shminit(dummy) 579 void *dummy; 580 { 581 int i; 582 vm_offset_t garbage1, garbage2; 583 584 /* actually this *should* be pageable. SHM_{LOCK,UNLOCK} */ 585 sysvshm_map = kmem_suballoc(kernel_map, &garbage1, &garbage2, 586 shminfo.shmall * NBPG, TRUE); 587 for (i = 0; i < shminfo.shmmni; i++) { 588 shmsegs[i].shm_perm.mode = SHMSEG_FREE; 589 shmsegs[i].shm_perm.seq = 0; 590 } 591 shm_last_free = 0; 592 shm_nused = 0; 593 shm_committed = 0; 594 } 595