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