sysv_shm.c revision 110982
1/* $FreeBSD: head/sys/kern/sysv_shm.c 110982 2003-02-16 06:08:55Z alfred $ */ 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_sysvipc.h" 36 37#include <sys/param.h> 38#include <sys/systm.h> 39#include <sys/kernel.h> 40#include <sys/lock.h> 41#include <sys/sysctl.h> 42#include <sys/shm.h> 43#include <sys/proc.h> 44#include <sys/malloc.h> 45#include <sys/mman.h> 46#include <sys/mutex.h> 47#include <sys/stat.h> 48#include <sys/syscall.h> 49#include <sys/sysent.h> 50#include <sys/sysproto.h> 51#include <sys/jail.h> 52 53#include <vm/vm.h> 54#include <vm/vm_param.h> 55#include <vm/pmap.h> 56#include <vm/vm_object.h> 57#include <vm/vm_map.h> 58#include <vm/vm_page.h> 59#include <vm/vm_pager.h> 60 61static MALLOC_DEFINE(M_SHM, "shm", "SVID compatible shared memory segments"); 62 63struct oshmctl_args; 64static int oshmctl(struct thread *td, struct oshmctl_args *uap); 65 66static int shmget_allocate_segment(struct thread *td, 67 struct shmget_args *uap, int mode); 68static int shmget_existing(struct thread *td, struct shmget_args *uap, 69 int mode, int segnum); 70 71/* XXX casting to (sy_call_t *) is bogus, as usual. */ 72static sy_call_t *shmcalls[] = { 73 (sy_call_t *)shmat, (sy_call_t *)oshmctl, 74 (sy_call_t *)shmdt, (sy_call_t *)shmget, 75 (sy_call_t *)shmctl 76}; 77 78#define SHMSEG_FREE 0x0200 79#define SHMSEG_REMOVED 0x0400 80#define SHMSEG_ALLOCATED 0x0800 81#define SHMSEG_WANTED 0x1000 82 83static int shm_last_free, shm_nused, shm_committed, shmalloced; 84static struct shmid_ds *shmsegs; 85 86struct shm_handle { 87 /* vm_offset_t kva; */ 88 vm_object_t shm_object; 89}; 90 91struct shmmap_state { 92 vm_offset_t va; 93 int shmid; 94}; 95 96static void shm_deallocate_segment(struct shmid_ds *); 97static int shm_find_segment_by_key(key_t); 98static struct shmid_ds *shm_find_segment_by_shmid(int); 99static struct shmid_ds *shm_find_segment_by_shmidx(int); 100static int shm_delete_mapping(struct vmspace *vm, struct shmmap_state *); 101static void shmrealloc(void); 102static void shminit(void); 103static int sysvshm_modload(struct module *, int, void *); 104static int shmunload(void); 105static void shmexit_myhook(struct vmspace *vm); 106static void shmfork_myhook(struct proc *p1, struct proc *p2); 107static int sysctl_shmsegs(SYSCTL_HANDLER_ARGS); 108 109/* 110 * Tuneable values. 111 */ 112#ifndef SHMMAXPGS 113#define SHMMAXPGS 8192 /* Note: sysv shared memory is swap backed. */ 114#endif 115#ifndef SHMMAX 116#define SHMMAX (SHMMAXPGS*PAGE_SIZE) 117#endif 118#ifndef SHMMIN 119#define SHMMIN 1 120#endif 121#ifndef SHMMNI 122#define SHMMNI 192 123#endif 124#ifndef SHMSEG 125#define SHMSEG 128 126#endif 127#ifndef SHMALL 128#define SHMALL (SHMMAXPGS) 129#endif 130 131struct shminfo shminfo = { 132 SHMMAX, 133 SHMMIN, 134 SHMMNI, 135 SHMSEG, 136 SHMALL 137}; 138 139static int shm_use_phys; 140 141SYSCTL_DECL(_kern_ipc); 142SYSCTL_INT(_kern_ipc, OID_AUTO, shmmax, CTLFLAG_RW, &shminfo.shmmax, 0, ""); 143SYSCTL_INT(_kern_ipc, OID_AUTO, shmmin, CTLFLAG_RW, &shminfo.shmmin, 0, ""); 144SYSCTL_INT(_kern_ipc, OID_AUTO, shmmni, CTLFLAG_RD, &shminfo.shmmni, 0, ""); 145SYSCTL_INT(_kern_ipc, OID_AUTO, shmseg, CTLFLAG_RD, &shminfo.shmseg, 0, ""); 146SYSCTL_INT(_kern_ipc, OID_AUTO, shmall, CTLFLAG_RW, &shminfo.shmall, 0, ""); 147SYSCTL_INT(_kern_ipc, OID_AUTO, shm_use_phys, CTLFLAG_RW, 148 &shm_use_phys, 0, ""); 149SYSCTL_PROC(_kern_ipc, OID_AUTO, shmsegs, CTLFLAG_RD, 150 NULL, 0, sysctl_shmsegs, "", ""); 151 152static int 153shm_find_segment_by_key(key) 154 key_t key; 155{ 156 int i; 157 158 for (i = 0; i < shmalloced; i++) 159 if ((shmsegs[i].shm_perm.mode & SHMSEG_ALLOCATED) && 160 shmsegs[i].shm_perm.key == key) 161 return (i); 162 return (-1); 163} 164 165static struct shmid_ds * 166shm_find_segment_by_shmid(shmid) 167 int shmid; 168{ 169 int segnum; 170 struct shmid_ds *shmseg; 171 172 segnum = IPCID_TO_IX(shmid); 173 if (segnum < 0 || segnum >= shmalloced) 174 return (NULL); 175 shmseg = &shmsegs[segnum]; 176 if ((shmseg->shm_perm.mode & (SHMSEG_ALLOCATED | SHMSEG_REMOVED)) 177 != SHMSEG_ALLOCATED || 178 shmseg->shm_perm.seq != IPCID_TO_SEQ(shmid)) 179 return (NULL); 180 return (shmseg); 181} 182 183static struct shmid_ds * 184shm_find_segment_by_shmidx(int segnum) 185{ 186 struct shmid_ds *shmseg; 187 188 if (segnum < 0 || segnum >= shmalloced) 189 return (NULL); 190 shmseg = &shmsegs[segnum]; 191 if ((shmseg->shm_perm.mode & (SHMSEG_ALLOCATED | SHMSEG_REMOVED)) 192 != SHMSEG_ALLOCATED ) 193 return (NULL); 194 return (shmseg); 195} 196 197static void 198shm_deallocate_segment(shmseg) 199 struct shmid_ds *shmseg; 200{ 201 struct shm_handle *shm_handle; 202 size_t size; 203 204 GIANT_REQUIRED; 205 206 shm_handle = shmseg->shm_internal; 207 vm_object_deallocate(shm_handle->shm_object); 208 free(shm_handle, M_SHM); 209 shmseg->shm_internal = NULL; 210 size = round_page(shmseg->shm_segsz); 211 shm_committed -= btoc(size); 212 shm_nused--; 213 shmseg->shm_perm.mode = SHMSEG_FREE; 214} 215 216static int 217shm_delete_mapping(struct vmspace *vm, struct shmmap_state *shmmap_s) 218{ 219 struct shmid_ds *shmseg; 220 int segnum, result; 221 size_t size; 222 223 GIANT_REQUIRED; 224 225 segnum = IPCID_TO_IX(shmmap_s->shmid); 226 shmseg = &shmsegs[segnum]; 227 size = round_page(shmseg->shm_segsz); 228 result = vm_map_remove(&vm->vm_map, shmmap_s->va, shmmap_s->va + size); 229 if (result != KERN_SUCCESS) 230 return (EINVAL); 231 shmmap_s->shmid = -1; 232 shmseg->shm_dtime = time_second; 233 if ((--shmseg->shm_nattch <= 0) && 234 (shmseg->shm_perm.mode & SHMSEG_REMOVED)) { 235 shm_deallocate_segment(shmseg); 236 shm_last_free = segnum; 237 } 238 return (0); 239} 240 241#ifndef _SYS_SYSPROTO_H_ 242struct shmdt_args { 243 const void *shmaddr; 244}; 245#endif 246 247/* 248 * MPSAFE 249 */ 250int 251shmdt(td, uap) 252 struct thread *td; 253 struct shmdt_args *uap; 254{ 255 struct proc *p = td->td_proc; 256 struct shmmap_state *shmmap_s; 257 int i; 258 int error = 0; 259 260 if (!jail_sysvipc_allowed && jailed(td->td_ucred)) 261 return (ENOSYS); 262 mtx_lock(&Giant); 263 shmmap_s = p->p_vmspace->vm_shm; 264 if (shmmap_s == NULL) { 265 error = EINVAL; 266 goto done2; 267 } 268 for (i = 0; i < shminfo.shmseg; i++, shmmap_s++) { 269 if (shmmap_s->shmid != -1 && 270 shmmap_s->va == (vm_offset_t)uap->shmaddr) { 271 break; 272 } 273 } 274 if (i == shminfo.shmseg) { 275 error = EINVAL; 276 goto done2; 277 } 278 error = shm_delete_mapping(p->p_vmspace, shmmap_s); 279done2: 280 mtx_unlock(&Giant); 281 return (error); 282} 283 284#ifndef _SYS_SYSPROTO_H_ 285struct shmat_args { 286 int shmid; 287 const void *shmaddr; 288 int shmflg; 289}; 290#endif 291 292/* 293 * MPSAFE 294 */ 295int 296shmat(td, uap) 297 struct thread *td; 298 struct shmat_args *uap; 299{ 300 struct proc *p = td->td_proc; 301 int i, flags; 302 struct shmid_ds *shmseg; 303 struct shmmap_state *shmmap_s = NULL; 304 struct shm_handle *shm_handle; 305 vm_offset_t attach_va; 306 vm_prot_t prot; 307 vm_size_t size; 308 int rv; 309 int error = 0; 310 311 if (!jail_sysvipc_allowed && jailed(td->td_ucred)) 312 return (ENOSYS); 313 mtx_lock(&Giant); 314 shmmap_s = p->p_vmspace->vm_shm; 315 if (shmmap_s == NULL) { 316 size = shminfo.shmseg * sizeof(struct shmmap_state); 317 shmmap_s = malloc(size, M_SHM, 0); 318 for (i = 0; i < shminfo.shmseg; i++) 319 shmmap_s[i].shmid = -1; 320 p->p_vmspace->vm_shm = shmmap_s; 321 } 322 shmseg = shm_find_segment_by_shmid(uap->shmid); 323 if (shmseg == NULL) { 324 error = EINVAL; 325 goto done2; 326 } 327 error = ipcperm(td, &shmseg->shm_perm, 328 (uap->shmflg & SHM_RDONLY) ? IPC_R : IPC_R|IPC_W); 329 if (error) 330 goto done2; 331 for (i = 0; i < shminfo.shmseg; i++) { 332 if (shmmap_s->shmid == -1) 333 break; 334 shmmap_s++; 335 } 336 if (i >= shminfo.shmseg) { 337 error = EMFILE; 338 goto done2; 339 } 340 size = round_page(shmseg->shm_segsz); 341#ifdef VM_PROT_READ_IS_EXEC 342 prot = VM_PROT_READ | VM_PROT_EXECUTE; 343#else 344 prot = VM_PROT_READ; 345#endif 346 if ((uap->shmflg & SHM_RDONLY) == 0) 347 prot |= VM_PROT_WRITE; 348 flags = MAP_ANON | MAP_SHARED; 349 if (uap->shmaddr) { 350 flags |= MAP_FIXED; 351 if (uap->shmflg & SHM_RND) { 352 attach_va = (vm_offset_t)uap->shmaddr & ~(SHMLBA-1); 353 } else if (((vm_offset_t)uap->shmaddr & (SHMLBA-1)) == 0) { 354 attach_va = (vm_offset_t)uap->shmaddr; 355 } else { 356 error = EINVAL; 357 goto done2; 358 } 359 } else { 360 /* 361 * This is just a hint to vm_map_find() about where to 362 * put it. 363 */ 364 attach_va = round_page((vm_offset_t)p->p_vmspace->vm_taddr 365 + maxtsiz + maxdsiz); 366 } 367 368 shm_handle = shmseg->shm_internal; 369 vm_object_reference(shm_handle->shm_object); 370 rv = vm_map_find(&p->p_vmspace->vm_map, shm_handle->shm_object, 371 0, &attach_va, size, (flags & MAP_FIXED)?0:1, prot, prot, 0); 372 if (rv != KERN_SUCCESS) { 373 error = ENOMEM; 374 goto done2; 375 } 376 vm_map_inherit(&p->p_vmspace->vm_map, 377 attach_va, attach_va + size, VM_INHERIT_SHARE); 378 379 shmmap_s->va = attach_va; 380 shmmap_s->shmid = uap->shmid; 381 shmseg->shm_lpid = p->p_pid; 382 shmseg->shm_atime = time_second; 383 shmseg->shm_nattch++; 384 td->td_retval[0] = attach_va; 385done2: 386 mtx_unlock(&Giant); 387 return (error); 388} 389 390struct oshmid_ds { 391 struct ipc_perm shm_perm; /* operation perms */ 392 int shm_segsz; /* size of segment (bytes) */ 393 ushort shm_cpid; /* pid, creator */ 394 ushort shm_lpid; /* pid, last operation */ 395 short shm_nattch; /* no. of current attaches */ 396 time_t shm_atime; /* last attach time */ 397 time_t shm_dtime; /* last detach time */ 398 time_t shm_ctime; /* last change time */ 399 void *shm_handle; /* internal handle for shm segment */ 400}; 401 402struct oshmctl_args { 403 int shmid; 404 int cmd; 405 struct oshmid_ds *ubuf; 406}; 407 408/* 409 * MPSAFE 410 */ 411static int 412oshmctl(td, uap) 413 struct thread *td; 414 struct oshmctl_args *uap; 415{ 416#ifdef COMPAT_43 417 int error = 0; 418 struct shmid_ds *shmseg; 419 struct oshmid_ds outbuf; 420 421 if (!jail_sysvipc_allowed && jailed(td->td_ucred)) 422 return (ENOSYS); 423 mtx_lock(&Giant); 424 shmseg = shm_find_segment_by_shmid(uap->shmid); 425 if (shmseg == NULL) { 426 error = EINVAL; 427 goto done2; 428 } 429 switch (uap->cmd) { 430 case IPC_STAT: 431 error = ipcperm(td, &shmseg->shm_perm, IPC_R); 432 if (error) 433 goto done2; 434 outbuf.shm_perm = shmseg->shm_perm; 435 outbuf.shm_segsz = shmseg->shm_segsz; 436 outbuf.shm_cpid = shmseg->shm_cpid; 437 outbuf.shm_lpid = shmseg->shm_lpid; 438 outbuf.shm_nattch = shmseg->shm_nattch; 439 outbuf.shm_atime = shmseg->shm_atime; 440 outbuf.shm_dtime = shmseg->shm_dtime; 441 outbuf.shm_ctime = shmseg->shm_ctime; 442 outbuf.shm_handle = shmseg->shm_internal; 443 error = copyout(&outbuf, uap->ubuf, sizeof(outbuf)); 444 if (error) 445 goto done2; 446 break; 447 default: 448 /* XXX casting to (sy_call_t *) is bogus, as usual. */ 449 error = ((sy_call_t *)shmctl)(td, uap); 450 break; 451 } 452done2: 453 mtx_unlock(&Giant); 454 return (error); 455#else 456 return (EINVAL); 457#endif 458} 459 460#ifndef _SYS_SYSPROTO_H_ 461struct shmctl_args { 462 int shmid; 463 int cmd; 464 struct shmid_ds *buf; 465}; 466#endif 467 468/* 469 * MPSAFE 470 */ 471int 472shmctl(td, uap) 473 struct thread *td; 474 struct shmctl_args *uap; 475{ 476 int error = 0; 477 struct shmid_ds inbuf; 478 struct shmid_ds *shmseg; 479 480 if (!jail_sysvipc_allowed && jailed(td->td_ucred)) 481 return (ENOSYS); 482 mtx_lock(&Giant); 483 switch (uap->cmd) { 484 case IPC_INFO: 485 error = copyout(&shminfo, uap->buf, sizeof(shminfo)); 486 if (error) 487 goto done2; 488 td->td_retval[0] = shmalloced; 489 goto done2; 490 case SHM_INFO: { 491 struct shm_info shm_info; 492 shm_info.used_ids = shm_nused; 493 shm_info.shm_rss = 0; /*XXX where to get from ? */ 494 shm_info.shm_tot = 0; /*XXX where to get from ? */ 495 shm_info.shm_swp = 0; /*XXX where to get from ? */ 496 shm_info.swap_attempts = 0; /*XXX where to get from ? */ 497 shm_info.swap_successes = 0; /*XXX where to get from ? */ 498 error = copyout(&shm_info, uap->buf, sizeof(shm_info)); 499 if (error) 500 goto done2; 501 td->td_retval[0] = shmalloced; 502 goto done2; 503 } 504 } 505 if( (uap->cmd) == SHM_STAT ) 506 shmseg = shm_find_segment_by_shmidx(uap->shmid); 507 else 508 shmseg = shm_find_segment_by_shmid(uap->shmid); 509 if (shmseg == NULL) { 510 error = EINVAL; 511 goto done2; 512 } 513 switch (uap->cmd) { 514 case SHM_STAT: 515 case IPC_STAT: 516 error = ipcperm(td, &shmseg->shm_perm, IPC_R); 517 if (error) 518 goto done2; 519 error = copyout(shmseg, uap->buf, sizeof(inbuf)); 520 if (error) 521 goto done2; 522 else if( (uap->cmd) == SHM_STAT ) 523 td->td_retval[0] = IXSEQ_TO_IPCID( uap->shmid, shmseg->shm_perm ); 524 break; 525 case IPC_SET: 526 error = ipcperm(td, &shmseg->shm_perm, IPC_M); 527 if (error) 528 goto done2; 529 error = copyin(uap->buf, &inbuf, sizeof(inbuf)); 530 if (error) 531 goto done2; 532 shmseg->shm_perm.uid = inbuf.shm_perm.uid; 533 shmseg->shm_perm.gid = inbuf.shm_perm.gid; 534 shmseg->shm_perm.mode = 535 (shmseg->shm_perm.mode & ~ACCESSPERMS) | 536 (inbuf.shm_perm.mode & ACCESSPERMS); 537 shmseg->shm_ctime = time_second; 538 break; 539 case IPC_RMID: 540 error = ipcperm(td, &shmseg->shm_perm, IPC_M); 541 if (error) 542 goto done2; 543 shmseg->shm_perm.key = IPC_PRIVATE; 544 shmseg->shm_perm.mode |= SHMSEG_REMOVED; 545 if (shmseg->shm_nattch <= 0) { 546 shm_deallocate_segment(shmseg); 547 shm_last_free = IPCID_TO_IX(uap->shmid); 548 } 549 break; 550#if 0 551 case SHM_LOCK: 552 case SHM_UNLOCK: 553#endif 554 default: 555 error = EINVAL; 556 break; 557 } 558done2: 559 mtx_unlock(&Giant); 560 return (error); 561} 562 563#ifndef _SYS_SYSPROTO_H_ 564struct shmget_args { 565 key_t key; 566 size_t size; 567 int shmflg; 568}; 569#endif 570 571static int 572shmget_existing(td, uap, mode, segnum) 573 struct thread *td; 574 struct shmget_args *uap; 575 int mode; 576 int segnum; 577{ 578 struct shmid_ds *shmseg; 579 int error; 580 581 shmseg = &shmsegs[segnum]; 582 if (shmseg->shm_perm.mode & SHMSEG_REMOVED) { 583 /* 584 * This segment is in the process of being allocated. Wait 585 * until it's done, and look the key up again (in case the 586 * allocation failed or it was freed). 587 */ 588 shmseg->shm_perm.mode |= SHMSEG_WANTED; 589 error = tsleep(shmseg, PLOCK | PCATCH, "shmget", 0); 590 if (error) 591 return (error); 592 return (EAGAIN); 593 } 594 if ((uap->shmflg & (IPC_CREAT | IPC_EXCL)) == (IPC_CREAT | IPC_EXCL)) 595 return (EEXIST); 596 error = ipcperm(td, &shmseg->shm_perm, mode); 597 if (error) 598 return (error); 599 if (uap->size && uap->size > shmseg->shm_segsz) 600 return (EINVAL); 601 td->td_retval[0] = IXSEQ_TO_IPCID(segnum, shmseg->shm_perm); 602 return (0); 603} 604 605static int 606shmget_allocate_segment(td, uap, mode) 607 struct thread *td; 608 struct shmget_args *uap; 609 int mode; 610{ 611 int i, segnum, shmid, size; 612 struct ucred *cred = td->td_ucred; 613 struct shmid_ds *shmseg; 614 struct shm_handle *shm_handle; 615 616 GIANT_REQUIRED; 617 618 if (uap->size < shminfo.shmmin || uap->size > shminfo.shmmax) 619 return (EINVAL); 620 if (shm_nused >= shminfo.shmmni) /* Any shmids left? */ 621 return (ENOSPC); 622 size = round_page(uap->size); 623 if (shm_committed + btoc(size) > shminfo.shmall) 624 return (ENOMEM); 625 if (shm_last_free < 0) { 626 shmrealloc(); /* Maybe expand the shmsegs[] array. */ 627 for (i = 0; i < shmalloced; i++) 628 if (shmsegs[i].shm_perm.mode & SHMSEG_FREE) 629 break; 630 if (i == shmalloced) 631 return (ENOSPC); 632 segnum = i; 633 } else { 634 segnum = shm_last_free; 635 shm_last_free = -1; 636 } 637 shmseg = &shmsegs[segnum]; 638 /* 639 * In case we sleep in malloc(), mark the segment present but deleted 640 * so that noone else tries to create the same key. 641 */ 642 shmseg->shm_perm.mode = SHMSEG_ALLOCATED | SHMSEG_REMOVED; 643 shmseg->shm_perm.key = uap->key; 644 shmseg->shm_perm.seq = (shmseg->shm_perm.seq + 1) & 0x7fff; 645 shm_handle = (struct shm_handle *) 646 malloc(sizeof(struct shm_handle), M_SHM, 0); 647 shmid = IXSEQ_TO_IPCID(segnum, shmseg->shm_perm); 648 649 /* 650 * We make sure that we have allocated a pager before we need 651 * to. 652 */ 653 if (shm_use_phys) { 654 shm_handle->shm_object = 655 vm_pager_allocate(OBJT_PHYS, 0, size, VM_PROT_DEFAULT, 0); 656 } else { 657 shm_handle->shm_object = 658 vm_pager_allocate(OBJT_SWAP, 0, size, VM_PROT_DEFAULT, 0); 659 } 660 vm_object_lock(shm_handle->shm_object); 661 vm_object_clear_flag(shm_handle->shm_object, OBJ_ONEMAPPING); 662 vm_object_set_flag(shm_handle->shm_object, OBJ_NOSPLIT); 663 vm_object_unlock(shm_handle->shm_object); 664 665 shmseg->shm_internal = shm_handle; 666 shmseg->shm_perm.cuid = shmseg->shm_perm.uid = cred->cr_uid; 667 shmseg->shm_perm.cgid = shmseg->shm_perm.gid = cred->cr_gid; 668 shmseg->shm_perm.mode = (shmseg->shm_perm.mode & SHMSEG_WANTED) | 669 (mode & ACCESSPERMS) | SHMSEG_ALLOCATED; 670 shmseg->shm_segsz = uap->size; 671 shmseg->shm_cpid = td->td_proc->p_pid; 672 shmseg->shm_lpid = shmseg->shm_nattch = 0; 673 shmseg->shm_atime = shmseg->shm_dtime = 0; 674 shmseg->shm_ctime = time_second; 675 shm_committed += btoc(size); 676 shm_nused++; 677 if (shmseg->shm_perm.mode & SHMSEG_WANTED) { 678 /* 679 * Somebody else wanted this key while we were asleep. Wake 680 * them up now. 681 */ 682 shmseg->shm_perm.mode &= ~SHMSEG_WANTED; 683 wakeup(shmseg); 684 } 685 td->td_retval[0] = shmid; 686 return (0); 687} 688 689/* 690 * MPSAFE 691 */ 692int 693shmget(td, uap) 694 struct thread *td; 695 struct shmget_args *uap; 696{ 697 int segnum, mode; 698 int error; 699 700 if (!jail_sysvipc_allowed && jailed(td->td_ucred)) 701 return (ENOSYS); 702 mtx_lock(&Giant); 703 mode = uap->shmflg & ACCESSPERMS; 704 if (uap->key != IPC_PRIVATE) { 705 again: 706 segnum = shm_find_segment_by_key(uap->key); 707 if (segnum >= 0) { 708 error = shmget_existing(td, uap, mode, segnum); 709 if (error == EAGAIN) 710 goto again; 711 goto done2; 712 } 713 if ((uap->shmflg & IPC_CREAT) == 0) { 714 error = ENOENT; 715 goto done2; 716 } 717 } 718 error = shmget_allocate_segment(td, uap, mode); 719done2: 720 mtx_unlock(&Giant); 721 return (error); 722} 723 724/* 725 * MPSAFE 726 */ 727int 728shmsys(td, uap) 729 struct thread *td; 730 /* XXX actually varargs. */ 731 struct shmsys_args /* { 732 u_int which; 733 int a2; 734 int a3; 735 int a4; 736 } */ *uap; 737{ 738 int error; 739 740 if (!jail_sysvipc_allowed && jailed(td->td_ucred)) 741 return (ENOSYS); 742 if (uap->which >= sizeof(shmcalls)/sizeof(shmcalls[0])) 743 return (EINVAL); 744 mtx_lock(&Giant); 745 error = (*shmcalls[uap->which])(td, &uap->a2); 746 mtx_unlock(&Giant); 747 return (error); 748} 749 750static void 751shmfork_myhook(p1, p2) 752 struct proc *p1, *p2; 753{ 754 struct shmmap_state *shmmap_s; 755 size_t size; 756 int i; 757 758 size = shminfo.shmseg * sizeof(struct shmmap_state); 759 shmmap_s = malloc(size, M_SHM, 0); 760 bcopy(p1->p_vmspace->vm_shm, shmmap_s, size); 761 p2->p_vmspace->vm_shm = shmmap_s; 762 for (i = 0; i < shminfo.shmseg; i++, shmmap_s++) 763 if (shmmap_s->shmid != -1) 764 shmsegs[IPCID_TO_IX(shmmap_s->shmid)].shm_nattch++; 765} 766 767static void 768shmexit_myhook(struct vmspace *vm) 769{ 770 struct shmmap_state *base, *shm; 771 int i; 772 773 GIANT_REQUIRED; 774 775 if ((base = vm->vm_shm) != NULL) { 776 vm->vm_shm = NULL; 777 for (i = 0, shm = base; i < shminfo.shmseg; i++, shm++) { 778 if (shm->shmid != -1) 779 shm_delete_mapping(vm, shm); 780 } 781 free(base, M_SHM); 782 } 783} 784 785static void 786shmrealloc(void) 787{ 788 int i; 789 struct shmid_ds *newsegs; 790 791 if (shmalloced >= shminfo.shmmni) 792 return; 793 794 newsegs = malloc(shminfo.shmmni * sizeof(*newsegs), M_SHM, 0); 795 if (newsegs == NULL) 796 return; 797 for (i = 0; i < shmalloced; i++) 798 bcopy(&shmsegs[i], &newsegs[i], sizeof(newsegs[0])); 799 for (; i < shminfo.shmmni; i++) { 800 shmsegs[i].shm_perm.mode = SHMSEG_FREE; 801 shmsegs[i].shm_perm.seq = 0; 802 } 803 free(shmsegs, M_SHM); 804 shmsegs = newsegs; 805 shmalloced = shminfo.shmmni; 806} 807 808static void 809shminit() 810{ 811 int i; 812 813 TUNABLE_INT_FETCH("kern.ipc.shmmaxpgs", &shminfo.shmall); 814 for (i = PAGE_SIZE; i < 0; i--) { 815 shminfo.shmmax = shminfo.shmall * PAGE_SIZE; 816 if (shminfo.shmmax >= shminfo.shmall) 817 break; 818 } 819 TUNABLE_INT_FETCH("kern.ipc.shmmin", &shminfo.shmmin); 820 TUNABLE_INT_FETCH("kern.ipc.shmmni", &shminfo.shmmni); 821 TUNABLE_INT_FETCH("kern.ipc.shmseg", &shminfo.shmseg); 822 TUNABLE_INT_FETCH("kern.ipc.shm_use_phys", &shm_use_phys); 823 824 shmalloced = shminfo.shmmni; 825 shmsegs = malloc(shmalloced * sizeof(shmsegs[0]), M_SHM, 0); 826 if (shmsegs == NULL) 827 panic("cannot allocate initial memory for sysvshm"); 828 for (i = 0; i < shmalloced; i++) { 829 shmsegs[i].shm_perm.mode = SHMSEG_FREE; 830 shmsegs[i].shm_perm.seq = 0; 831 } 832 shm_last_free = 0; 833 shm_nused = 0; 834 shm_committed = 0; 835 shmexit_hook = &shmexit_myhook; 836 shmfork_hook = &shmfork_myhook; 837} 838 839static int 840shmunload() 841{ 842 843 if (shm_nused > 0) 844 return (EBUSY); 845 846 free(shmsegs, M_SHM); 847 shmexit_hook = NULL; 848 shmfork_hook = NULL; 849 return (0); 850} 851 852static int 853sysctl_shmsegs(SYSCTL_HANDLER_ARGS) 854{ 855 856 return (SYSCTL_OUT(req, shmsegs, shmalloced * sizeof(shmsegs[0]))); 857} 858 859static int 860sysvshm_modload(struct module *module, int cmd, void *arg) 861{ 862 int error = 0; 863 864 switch (cmd) { 865 case MOD_LOAD: 866 shminit(); 867 break; 868 case MOD_UNLOAD: 869 error = shmunload(); 870 break; 871 case MOD_SHUTDOWN: 872 break; 873 default: 874 error = EINVAL; 875 break; 876 } 877 return (error); 878} 879 880static moduledata_t sysvshm_mod = { 881 "sysvshm", 882 &sysvshm_modload, 883 NULL 884}; 885 886SYSCALL_MODULE_HELPER(shmsys); 887SYSCALL_MODULE_HELPER(shmat); 888SYSCALL_MODULE_HELPER(shmctl); 889SYSCALL_MODULE_HELPER(shmdt); 890SYSCALL_MODULE_HELPER(shmget); 891 892DECLARE_MODULE(sysvshm, sysvshm_mod, 893 SI_SUB_SYSV_SHM, SI_ORDER_FIRST); 894MODULE_VERSION(sysvshm, 1); 895