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