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