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