1 /* $Id: sysv_shm.c,v 1.17 1996/01/05 16:38:03 wollman 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 "opt_sysvipc.h" 35 36 #include <sys/param.h> 37 #include <sys/systm.h> 38 #include <sys/sysproto.h> 39 #include <sys/kernel.h> 40 #include <sys/shm.h> 41 #include <sys/proc.h> 42 #include <sys/malloc.h> 43 #include <sys/mman.h> 44 #include <sys/stat.h> 45 #include <sys/sysent.h> 46 47 #include <vm/vm.h> 48 #include <vm/vm_param.h> 49 #include <vm/vm_prot.h> 50 #include <vm/lock.h> 51 #include <vm/pmap.h> 52 #include <vm/vm_map.h> 53 #include <vm/vm_kern.h> 54 #include <vm/vm_extern.h> 55 56 #ifndef _SYS_SYSPROTO_H_ 57 struct shmat_args; 58 extern int shmat __P((struct proc *p, struct shmat_args *uap, int *retval)); 59 struct shmctl_args; 60 extern int shmctl __P((struct proc *p, struct shmctl_args *uap, int *retval)); 61 struct shmdt_args; 62 extern int shmdt __P((struct proc *p, struct shmdt_args *uap, int *retval)); 63 struct shmget_args; 64 extern int shmget __P((struct proc *p, struct shmget_args *uap, int *retval)); 65 #endif 66 67 static void shminit __P((void *)); 68 SYSINIT(sysv_shm, SI_SUB_SYSV_SHM, SI_ORDER_FIRST, shminit, NULL) 69 70 struct oshmctl_args; 71 static int oshmctl __P((struct proc *p, struct oshmctl_args *uap, int *retval)); 72 static int shmget_allocate_segment __P((struct proc *p, struct shmget_args *uap, int mode, int *retval)); 73 static int shmget_existing __P((struct proc *p, struct shmget_args *uap, int mode, int segnum, int *retval)); 74 75 /* XXX casting to (sy_call_t *) is bogus, as usual. */ 76 sy_call_t *shmcalls[] = { 77 (sy_call_t *)shmat, (sy_call_t *)oshmctl, 78 (sy_call_t *)shmdt, (sy_call_t *)shmget, 79 (sy_call_t *)shmctl 80 }; 81 82 #define SHMSEG_FREE 0x0200 83 #define SHMSEG_REMOVED 0x0400 84 #define SHMSEG_ALLOCATED 0x0800 85 #define SHMSEG_WANTED 0x1000 86 87 static vm_map_t sysvshm_map; 88 static int shm_last_free, shm_nused, shm_committed; 89 struct shmid_ds *shmsegs; 90 91 struct shm_handle { 92 vm_offset_t kva; 93 }; 94 95 struct shmmap_state { 96 vm_offset_t va; 97 int shmid; 98 }; 99 100 static void shm_deallocate_segment __P((struct shmid_ds *)); 101 static int shm_find_segment_by_key __P((key_t)); 102 static struct shmid_ds *shm_find_segment_by_shmid __P((int)); 103 static int shm_delete_mapping __P((struct proc *, struct shmmap_state *)); 104 105 static int 106 shm_find_segment_by_key(key) 107 key_t key; 108 { 109 int i; 110 111 for (i = 0; i < shminfo.shmmni; i++) 112 if ((shmsegs[i].shm_perm.mode & SHMSEG_ALLOCATED) && 113 shmsegs[i].shm_perm.key == key) 114 return i; 115 return -1; 116 } 117 118 static struct shmid_ds * 119 shm_find_segment_by_shmid(shmid) 120 int shmid; 121 { 122 int segnum; 123 struct shmid_ds *shmseg; 124 125 segnum = IPCID_TO_IX(shmid); 126 if (segnum < 0 || segnum >= shminfo.shmmni) 127 return NULL; 128 shmseg = &shmsegs[segnum]; 129 if ((shmseg->shm_perm.mode & (SHMSEG_ALLOCATED | SHMSEG_REMOVED)) 130 != SHMSEG_ALLOCATED || 131 shmseg->shm_perm.seq != IPCID_TO_SEQ(shmid)) 132 return NULL; 133 return shmseg; 134 } 135 136 static void 137 shm_deallocate_segment(shmseg) 138 struct shmid_ds *shmseg; 139 { 140 struct shm_handle *shm_handle; 141 size_t size; 142 143 shm_handle = shmseg->shm_internal; 144 size = (shmseg->shm_segsz + CLOFSET) & ~CLOFSET; 145 (void) vm_map_remove(sysvshm_map, shm_handle->kva, shm_handle->kva + size); 146 free((caddr_t)shm_handle, M_SHM); 147 shmseg->shm_internal = NULL; 148 shm_committed -= btoc(size); 149 shm_nused--; 150 shmseg->shm_perm.mode = SHMSEG_FREE; 151 } 152 153 static int 154 shm_delete_mapping(p, shmmap_s) 155 struct proc *p; 156 struct shmmap_state *shmmap_s; 157 { 158 struct shmid_ds *shmseg; 159 int segnum, result; 160 size_t size; 161 162 segnum = IPCID_TO_IX(shmmap_s->shmid); 163 shmseg = &shmsegs[segnum]; 164 size = (shmseg->shm_segsz + CLOFSET) & ~CLOFSET; 165 result = vm_map_remove(&p->p_vmspace->vm_map, shmmap_s->va, shmmap_s->va + size); 166 if (result != KERN_SUCCESS) 167 return EINVAL; 168 shmmap_s->shmid = -1; 169 shmseg->shm_dtime = time.tv_sec; 170 if ((--shmseg->shm_nattch <= 0) && 171 (shmseg->shm_perm.mode & SHMSEG_REMOVED)) { 172 shm_deallocate_segment(shmseg); 173 shm_last_free = segnum; 174 } 175 return 0; 176 } 177 178 #ifndef _SYS_SYSPROTO_H_ 179 struct shmdt_args { 180 void *shmaddr; 181 }; 182 #endif 183 184 int 185 shmdt(p, uap, retval) 186 struct proc *p; 187 struct shmdt_args *uap; 188 int *retval; 189 { 190 struct shmmap_state *shmmap_s; 191 int i; 192 193 shmmap_s = (struct shmmap_state *)p->p_vmspace->vm_shm; 194 if (shmmap_s == NULL) 195 return EINVAL; 196 for (i = 0; i < shminfo.shmseg; i++, shmmap_s++) 197 if (shmmap_s->shmid != -1 && 198 shmmap_s->va == (vm_offset_t)uap->shmaddr) 199 break; 200 if (i == shminfo.shmseg) 201 return EINVAL; 202 return shm_delete_mapping(p, shmmap_s); 203 } 204 205 #ifndef _SYS_SYSPROTO_H_ 206 struct shmat_args { 207 int shmid; 208 void *shmaddr; 209 int shmflg; 210 }; 211 #endif 212 213 int 214 shmat(p, uap, retval) 215 struct proc *p; 216 struct shmat_args *uap; 217 int *retval; 218 { 219 int error, i, flags; 220 struct ucred *cred = p->p_ucred; 221 struct shmid_ds *shmseg; 222 struct shmmap_state *shmmap_s = NULL; 223 vm_offset_t attach_va; 224 vm_prot_t prot; 225 vm_size_t size; 226 227 shmmap_s = (struct shmmap_state *)p->p_vmspace->vm_shm; 228 if (shmmap_s == NULL) { 229 size = shminfo.shmseg * sizeof(struct shmmap_state); 230 shmmap_s = malloc(size, M_SHM, M_WAITOK); 231 for (i = 0; i < shminfo.shmseg; i++) 232 shmmap_s[i].shmid = -1; 233 p->p_vmspace->vm_shm = (caddr_t)shmmap_s; 234 } 235 shmseg = shm_find_segment_by_shmid(uap->shmid); 236 if (shmseg == NULL) 237 return EINVAL; 238 error = ipcperm(cred, &shmseg->shm_perm, 239 (uap->shmflg & SHM_RDONLY) ? IPC_R : IPC_R|IPC_W); 240 if (error) 241 return error; 242 for (i = 0; i < shminfo.shmseg; i++) { 243 if (shmmap_s->shmid == -1) 244 break; 245 shmmap_s++; 246 } 247 if (i >= shminfo.shmseg) 248 return EMFILE; 249 size = (shmseg->shm_segsz + CLOFSET) & ~CLOFSET; 250 prot = VM_PROT_READ; 251 if ((uap->shmflg & SHM_RDONLY) == 0) 252 prot |= VM_PROT_WRITE; 253 flags = MAP_ANON | MAP_SHARED; 254 if (uap->shmaddr) { 255 flags |= MAP_FIXED; 256 if (uap->shmflg & SHM_RND) 257 attach_va = (vm_offset_t)uap->shmaddr & ~(SHMLBA-1); 258 else if (((vm_offset_t)uap->shmaddr & (SHMLBA-1)) == 0) 259 attach_va = (vm_offset_t)uap->shmaddr; 260 else 261 return EINVAL; 262 } else { 263 /* This is just a hint to vm_mmap() about where to put it. */ 264 attach_va = round_page(p->p_vmspace->vm_taddr + MAXTSIZ + MAXDSIZ); 265 } 266 error = vm_mmap(&p->p_vmspace->vm_map, &attach_va, size, prot, 267 VM_PROT_DEFAULT, flags, (caddr_t) uap->shmid, 0); 268 if (error) 269 return error; 270 shmmap_s->va = attach_va; 271 shmmap_s->shmid = uap->shmid; 272 shmseg->shm_lpid = p->p_pid; 273 shmseg->shm_atime = time.tv_sec; 274 shmseg->shm_nattch++; 275 *retval = attach_va; 276 return 0; 277 } 278 279 struct oshmid_ds { 280 struct ipc_perm shm_perm; /* operation perms */ 281 int shm_segsz; /* size of segment (bytes) */ 282 ushort shm_cpid; /* pid, creator */ 283 ushort shm_lpid; /* pid, last operation */ 284 short shm_nattch; /* no. of current attaches */ 285 time_t shm_atime; /* last attach time */ 286 time_t shm_dtime; /* last detach time */ 287 time_t shm_ctime; /* last change time */ 288 void *shm_handle; /* internal handle for shm segment */ 289 }; 290 291 struct oshmctl_args { 292 int shmid; 293 int cmd; 294 struct oshmid_ds *ubuf; 295 }; 296 297 static int 298 oshmctl(p, uap, retval) 299 struct proc *p; 300 struct oshmctl_args *uap; 301 int *retval; 302 { 303 #ifdef COMPAT_43 304 int error; 305 struct ucred *cred = p->p_ucred; 306 struct shmid_ds *shmseg; 307 struct oshmid_ds outbuf; 308 309 shmseg = shm_find_segment_by_shmid(uap->shmid); 310 if (shmseg == NULL) 311 return EINVAL; 312 switch (uap->cmd) { 313 case IPC_STAT: 314 error = ipcperm(cred, &shmseg->shm_perm, IPC_R); 315 if (error) 316 return error; 317 outbuf.shm_perm = shmseg->shm_perm; 318 outbuf.shm_segsz = shmseg->shm_segsz; 319 outbuf.shm_cpid = shmseg->shm_cpid; 320 outbuf.shm_lpid = shmseg->shm_lpid; 321 outbuf.shm_nattch = shmseg->shm_nattch; 322 outbuf.shm_atime = shmseg->shm_atime; 323 outbuf.shm_dtime = shmseg->shm_dtime; 324 outbuf.shm_ctime = shmseg->shm_ctime; 325 outbuf.shm_handle = shmseg->shm_internal; 326 error = copyout((caddr_t)&outbuf, uap->ubuf, sizeof(outbuf)); 327 if (error) 328 return error; 329 break; 330 default: 331 /* XXX casting to (sy_call_t *) is bogus, as usual. */ 332 return ((sy_call_t *)shmctl)(p, uap, retval); 333 } 334 return 0; 335 #else 336 return EINVAL; 337 #endif 338 } 339 340 #ifndef _SYS_SYSPROTO_H_ 341 struct shmctl_args { 342 int shmid; 343 int cmd; 344 struct shmid_ds *buf; 345 }; 346 #endif 347 348 int 349 shmctl(p, uap, retval) 350 struct proc *p; 351 struct shmctl_args *uap; 352 int *retval; 353 { 354 int error; 355 struct ucred *cred = p->p_ucred; 356 struct shmid_ds inbuf; 357 struct shmid_ds *shmseg; 358 359 shmseg = shm_find_segment_by_shmid(uap->shmid); 360 if (shmseg == NULL) 361 return EINVAL; 362 switch (uap->cmd) { 363 case IPC_STAT: 364 error = ipcperm(cred, &shmseg->shm_perm, IPC_R); 365 if (error) 366 return error; 367 error = copyout((caddr_t)shmseg, uap->buf, sizeof(inbuf)); 368 if (error) 369 return error; 370 break; 371 case IPC_SET: 372 error = ipcperm(cred, &shmseg->shm_perm, IPC_M); 373 if (error) 374 return error; 375 error = copyin(uap->buf, (caddr_t)&inbuf, sizeof(inbuf)); 376 if (error) 377 return error; 378 shmseg->shm_perm.uid = inbuf.shm_perm.uid; 379 shmseg->shm_perm.gid = inbuf.shm_perm.gid; 380 shmseg->shm_perm.mode = 381 (shmseg->shm_perm.mode & ~ACCESSPERMS) | 382 (inbuf.shm_perm.mode & ACCESSPERMS); 383 shmseg->shm_ctime = time.tv_sec; 384 break; 385 case IPC_RMID: 386 error = ipcperm(cred, &shmseg->shm_perm, IPC_M); 387 if (error) 388 return error; 389 shmseg->shm_perm.key = IPC_PRIVATE; 390 shmseg->shm_perm.mode |= SHMSEG_REMOVED; 391 if (shmseg->shm_nattch <= 0) { 392 shm_deallocate_segment(shmseg); 393 shm_last_free = IPCID_TO_IX(uap->shmid); 394 } 395 break; 396 #if 0 397 case SHM_LOCK: 398 case SHM_UNLOCK: 399 #endif 400 default: 401 return EINVAL; 402 } 403 return 0; 404 } 405 406 #ifndef _SYS_SYSPROTO_H_ 407 struct shmget_args { 408 key_t key; 409 size_t size; 410 int shmflg; 411 }; 412 #endif 413 414 static int 415 shmget_existing(p, uap, mode, segnum, retval) 416 struct proc *p; 417 struct shmget_args *uap; 418 int mode; 419 int segnum; 420 int *retval; 421 { 422 struct shmid_ds *shmseg; 423 struct ucred *cred = p->p_ucred; 424 int error; 425 426 shmseg = &shmsegs[segnum]; 427 if (shmseg->shm_perm.mode & SHMSEG_REMOVED) { 428 /* 429 * This segment is in the process of being allocated. Wait 430 * until it's done, and look the key up again (in case the 431 * allocation failed or it was freed). 432 */ 433 shmseg->shm_perm.mode |= SHMSEG_WANTED; 434 error = tsleep((caddr_t)shmseg, PLOCK | PCATCH, "shmget", 0); 435 if (error) 436 return error; 437 return EAGAIN; 438 } 439 error = ipcperm(cred, &shmseg->shm_perm, mode); 440 if (error) 441 return error; 442 if (uap->size && uap->size > shmseg->shm_segsz) 443 return EINVAL; 444 if ((uap->shmflg & (IPC_CREAT | IPC_EXCL)) == (IPC_CREAT | IPC_EXCL)) 445 return EEXIST; 446 *retval = IXSEQ_TO_IPCID(segnum, shmseg->shm_perm); 447 return 0; 448 } 449 450 static int 451 shmget_allocate_segment(p, uap, mode, retval) 452 struct proc *p; 453 struct shmget_args *uap; 454 int mode; 455 int *retval; 456 { 457 int i, segnum, result, shmid, size; 458 struct ucred *cred = p->p_ucred; 459 struct shmid_ds *shmseg; 460 struct shm_handle *shm_handle; 461 462 if (uap->size < shminfo.shmmin || uap->size > shminfo.shmmax) 463 return EINVAL; 464 if (shm_nused >= shminfo.shmmni) /* any shmids left? */ 465 return ENOSPC; 466 size = (uap->size + CLOFSET) & ~CLOFSET; 467 if (shm_committed + btoc(size) > shminfo.shmall) 468 return ENOMEM; 469 if (shm_last_free < 0) { 470 for (i = 0; i < shminfo.shmmni; i++) 471 if (shmsegs[i].shm_perm.mode & SHMSEG_FREE) 472 break; 473 if (i == shminfo.shmmni) 474 panic("shmseg free count inconsistent"); 475 segnum = i; 476 } else { 477 segnum = shm_last_free; 478 shm_last_free = -1; 479 } 480 shmseg = &shmsegs[segnum]; 481 /* 482 * In case we sleep in malloc(), mark the segment present but deleted 483 * so that noone else tries to create the same key. 484 */ 485 shmseg->shm_perm.mode = SHMSEG_ALLOCATED | SHMSEG_REMOVED; 486 shmseg->shm_perm.key = uap->key; 487 shmseg->shm_perm.seq = (shmseg->shm_perm.seq + 1) & 0x7fff; 488 shm_handle = (struct shm_handle *) 489 malloc(sizeof(struct shm_handle), M_SHM, M_WAITOK); 490 shmid = IXSEQ_TO_IPCID(segnum, shmseg->shm_perm); 491 result = vm_mmap(sysvshm_map, &shm_handle->kva, size, VM_PROT_ALL, 492 VM_PROT_DEFAULT, MAP_ANON, (caddr_t) shmid, 0); 493 if (result != KERN_SUCCESS) { 494 shmseg->shm_perm.mode = SHMSEG_FREE; 495 shm_last_free = segnum; 496 free((caddr_t)shm_handle, M_SHM); 497 /* Just in case. */ 498 wakeup((caddr_t)shmseg); 499 return ENOMEM; 500 } 501 shmseg->shm_internal = shm_handle; 502 shmseg->shm_perm.cuid = shmseg->shm_perm.uid = cred->cr_uid; 503 shmseg->shm_perm.cgid = shmseg->shm_perm.gid = cred->cr_gid; 504 shmseg->shm_perm.mode = (shmseg->shm_perm.mode & SHMSEG_WANTED) | 505 (mode & ACCESSPERMS) | SHMSEG_ALLOCATED; 506 shmseg->shm_segsz = uap->size; 507 shmseg->shm_cpid = p->p_pid; 508 shmseg->shm_lpid = shmseg->shm_nattch = 0; 509 shmseg->shm_atime = shmseg->shm_dtime = 0; 510 shmseg->shm_ctime = time.tv_sec; 511 shm_committed += btoc(size); 512 shm_nused++; 513 if (shmseg->shm_perm.mode & SHMSEG_WANTED) { 514 /* 515 * Somebody else wanted this key while we were asleep. Wake 516 * them up now. 517 */ 518 shmseg->shm_perm.mode &= ~SHMSEG_WANTED; 519 wakeup((caddr_t)shmseg); 520 } 521 *retval = shmid; 522 return 0; 523 } 524 525 int 526 shmget(p, uap, retval) 527 struct proc *p; 528 struct shmget_args *uap; 529 int *retval; 530 { 531 int segnum, mode, error; 532 533 mode = uap->shmflg & ACCESSPERMS; 534 if (uap->key != IPC_PRIVATE) { 535 again: 536 segnum = shm_find_segment_by_key(uap->key); 537 if (segnum >= 0) { 538 error = shmget_existing(p, uap, mode, segnum, retval); 539 if (error == EAGAIN) 540 goto again; 541 return error; 542 } 543 if ((uap->shmflg & IPC_CREAT) == 0) 544 return ENOENT; 545 } 546 return shmget_allocate_segment(p, uap, mode, retval); 547 } 548 549 int 550 shmsys(p, uap, retval) 551 struct proc *p; 552 /* XXX actually varargs. */ 553 struct shmsys_args /* { 554 u_int which; 555 int a2; 556 int a3; 557 int a4; 558 } */ *uap; 559 int *retval; 560 { 561 562 if (uap->which >= sizeof(shmcalls)/sizeof(shmcalls[0])) 563 return EINVAL; 564 return ((*shmcalls[uap->which])(p, &uap->a2, retval)); 565 } 566 567 void 568 shmfork(p1, p2) 569 struct proc *p1, *p2; 570 { 571 struct shmmap_state *shmmap_s; 572 size_t size; 573 int i; 574 575 size = shminfo.shmseg * sizeof(struct shmmap_state); 576 shmmap_s = malloc(size, M_SHM, M_WAITOK); 577 bcopy((caddr_t)p1->p_vmspace->vm_shm, (caddr_t)shmmap_s, size); 578 p2->p_vmspace->vm_shm = (caddr_t)shmmap_s; 579 for (i = 0; i < shminfo.shmseg; i++, shmmap_s++) 580 if (shmmap_s->shmid != -1) 581 shmsegs[IPCID_TO_IX(shmmap_s->shmid)].shm_nattch++; 582 } 583 584 void 585 shmexit(p) 586 struct proc *p; 587 { 588 struct shmmap_state *shmmap_s; 589 int i; 590 591 shmmap_s = (struct shmmap_state *)p->p_vmspace->vm_shm; 592 for (i = 0; i < shminfo.shmseg; i++, shmmap_s++) 593 if (shmmap_s->shmid != -1) 594 shm_delete_mapping(p, shmmap_s); 595 free((caddr_t)p->p_vmspace->vm_shm, M_SHM); 596 p->p_vmspace->vm_shm = NULL; 597 } 598 599 void 600 shminit(dummy) 601 void *dummy; 602 { 603 int i; 604 vm_offset_t garbage1, garbage2; 605 606 /* actually this *should* be pageable. SHM_{LOCK,UNLOCK} */ 607 sysvshm_map = kmem_suballoc(kernel_map, &garbage1, &garbage2, 608 shminfo.shmall * NBPG, TRUE); 609 for (i = 0; i < shminfo.shmmni; i++) { 610 shmsegs[i].shm_perm.mode = SHMSEG_FREE; 611 shmsegs[i].shm_perm.seq = 0; 612 } 613 shm_last_free = 0; 614 shm_nused = 0; 615 shm_committed = 0; 616 } 617