sysv_shm.c revision 130730
1187262Snwhitehorn/* $NetBSD: sysv_shm.c,v 1.23 1994/07/04 23:25:12 glass Exp $ */ 2187262Snwhitehorn/* 3187262Snwhitehorn * Copyright (c) 1994 Adam Glass and Charles Hannum. All rights reserved. 4187262Snwhitehorn * 5187262Snwhitehorn * Redistribution and use in source and binary forms, with or without 6187262Snwhitehorn * modification, are permitted provided that the following conditions 7187262Snwhitehorn * are met: 8187262Snwhitehorn * 1. Redistributions of source code must retain the above copyright 9187262Snwhitehorn * notice, this list of conditions and the following disclaimer. 10187262Snwhitehorn * 2. Redistributions in binary form must reproduce the above copyright 11187262Snwhitehorn * notice, this list of conditions and the following disclaimer in the 12187262Snwhitehorn * documentation and/or other materials provided with the distribution. 13187262Snwhitehorn * 3. All advertising materials mentioning features or use of this software 14187262Snwhitehorn * must display the following acknowledgement: 15187262Snwhitehorn * This product includes software developed by Adam Glass and Charles 16187262Snwhitehorn * Hannum. 17187262Snwhitehorn * 4. The names of the authors may not be used to endorse or promote products 18187262Snwhitehorn * derived from this software without specific prior written permission. 19187262Snwhitehorn * 20187262Snwhitehorn * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 21187262Snwhitehorn * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 22187262Snwhitehorn * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23187262Snwhitehorn * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, 24187262Snwhitehorn * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 25187262Snwhitehorn * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26187262Snwhitehorn * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27187262Snwhitehorn * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28187262Snwhitehorn * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 29187262Snwhitehorn * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30187262Snwhitehorn */ 31187262Snwhitehorn 32187262Snwhitehorn#include <sys/cdefs.h> 33187262Snwhitehorn__FBSDID("$FreeBSD: head/sys/kern/sysv_shm.c 130730 2004-06-19 14:46:13Z tjr $"); 34187262Snwhitehorn 35187262Snwhitehorn#include "opt_compat.h" 36187262Snwhitehorn#include "opt_sysvipc.h" 37187262Snwhitehorn 38187262Snwhitehorn#include <sys/param.h> 39187262Snwhitehorn#include <sys/systm.h> 40187262Snwhitehorn#include <sys/kernel.h> 41187262Snwhitehorn#include <sys/lock.h> 42187262Snwhitehorn#include <sys/sysctl.h> 43187262Snwhitehorn#include <sys/shm.h> 44187262Snwhitehorn#include <sys/proc.h> 45187262Snwhitehorn#include <sys/malloc.h> 46187262Snwhitehorn#include <sys/mman.h> 47187262Snwhitehorn#include <sys/module.h> 48187262Snwhitehorn#include <sys/mutex.h> 49187262Snwhitehorn#include <sys/resourcevar.h> 50187262Snwhitehorn#include <sys/stat.h> 51187262Snwhitehorn#include <sys/syscall.h> 52187262Snwhitehorn#include <sys/syscallsubr.h> 53187262Snwhitehorn#include <sys/sysent.h> 54187262Snwhitehorn#include <sys/sysproto.h> 55187262Snwhitehorn#include <sys/jail.h> 56187262Snwhitehorn 57187262Snwhitehorn#include <vm/vm.h> 58187262Snwhitehorn#include <vm/vm_param.h> 59187262Snwhitehorn#include <vm/pmap.h> 60209302Snwhitehorn#include <vm/vm_object.h> 61187262Snwhitehorn#include <vm/vm_map.h> 62187262Snwhitehorn#include <vm/vm_page.h> 63187262Snwhitehorn#include <vm/vm_pager.h> 64187262Snwhitehorn 65187262Snwhitehornstatic MALLOC_DEFINE(M_SHM, "shm", "SVID compatible shared memory segments"); 66187262Snwhitehorn 67187262Snwhitehornstruct oshmctl_args; 68187262Snwhitehornstatic int oshmctl(struct thread *td, struct oshmctl_args *uap); 69187262Snwhitehorn 70187262Snwhitehornstatic int shmget_allocate_segment(struct thread *td, 71187262Snwhitehorn struct shmget_args *uap, int mode); 72187262Snwhitehornstatic int shmget_existing(struct thread *td, struct shmget_args *uap, 73187262Snwhitehorn int mode, int segnum); 74187262Snwhitehorn 75187262Snwhitehorn/* XXX casting to (sy_call_t *) is bogus, as usual. */ 76187262Snwhitehornstatic sy_call_t *shmcalls[] = { 77187262Snwhitehorn (sy_call_t *)shmat, (sy_call_t *)oshmctl, 78187262Snwhitehorn (sy_call_t *)shmdt, (sy_call_t *)shmget, 79187262Snwhitehorn (sy_call_t *)shmctl 80187262Snwhitehorn}; 81187262Snwhitehorn 82187262Snwhitehorn#define SHMSEG_FREE 0x0200 83187262Snwhitehorn#define SHMSEG_REMOVED 0x0400 84187262Snwhitehorn#define SHMSEG_ALLOCATED 0x0800 85187262Snwhitehorn#define SHMSEG_WANTED 0x1000 86187262Snwhitehorn 87187262Snwhitehornstatic int shm_last_free, shm_nused, shm_committed, shmalloced; 88187262Snwhitehornstatic struct shmid_ds *shmsegs; 89187262Snwhitehorn 90187262Snwhitehornstruct shm_handle { 91187262Snwhitehorn /* vm_offset_t kva; */ 92187262Snwhitehorn vm_object_t shm_object; 93187262Snwhitehorn}; 94187262Snwhitehorn 95187262Snwhitehornstruct shmmap_state { 96187262Snwhitehorn vm_offset_t va; 97187262Snwhitehorn int shmid; 98187262Snwhitehorn}; 99187262Snwhitehorn 100187262Snwhitehornstatic void shm_deallocate_segment(struct shmid_ds *); 101187262Snwhitehornstatic int shm_find_segment_by_key(key_t); 102187262Snwhitehornstatic struct shmid_ds *shm_find_segment_by_shmid(int); 103187262Snwhitehornstatic struct shmid_ds *shm_find_segment_by_shmidx(int); 104187262Snwhitehornstatic int shm_delete_mapping(struct vmspace *vm, struct shmmap_state *); 105187262Snwhitehornstatic void shmrealloc(void); 106187262Snwhitehornstatic void shminit(void); 107187262Snwhitehornstatic int sysvshm_modload(struct module *, int, void *); 108187262Snwhitehornstatic int shmunload(void); 109187262Snwhitehornstatic void shmexit_myhook(struct vmspace *vm); 110187262Snwhitehornstatic void shmfork_myhook(struct proc *p1, struct proc *p2); 111208840Snwhitehornstatic int sysctl_shmsegs(SYSCTL_HANDLER_ARGS); 112187262Snwhitehorn 113187262Snwhitehorn/* 114187262Snwhitehorn * Tuneable values. 115187262Snwhitehorn */ 116187262Snwhitehorn#ifndef SHMMAXPGS 117187262Snwhitehorn#define SHMMAXPGS 8192 /* Note: sysv shared memory is swap backed. */ 118187262Snwhitehorn#endif 119208840Snwhitehorn#ifndef SHMMAX 120187262Snwhitehorn#define SHMMAX (SHMMAXPGS*PAGE_SIZE) 121187262Snwhitehorn#endif 122187262Snwhitehorn#ifndef SHMMIN 123187262Snwhitehorn#define SHMMIN 1 124187262Snwhitehorn#endif 125187262Snwhitehorn#ifndef SHMMNI 126187262Snwhitehorn#define SHMMNI 192 127187262Snwhitehorn#endif 128187262Snwhitehorn#ifndef SHMSEG 129187262Snwhitehorn#define SHMSEG 128 130187262Snwhitehorn#endif 131187262Snwhitehorn#ifndef SHMALL 132187262Snwhitehorn#define SHMALL (SHMMAXPGS) 133187262Snwhitehorn#endif 134187262Snwhitehorn 135187262Snwhitehornstruct shminfo shminfo = { 136187262Snwhitehorn SHMMAX, 137187262Snwhitehorn SHMMIN, 138187262Snwhitehorn SHMMNI, 139187262Snwhitehorn SHMSEG, 140187262Snwhitehorn SHMALL 141187262Snwhitehorn}; 142187262Snwhitehorn 143187262Snwhitehornstatic int shm_use_phys; 144187262Snwhitehornstatic int shm_allow_removed; 145187262Snwhitehorn 146187262SnwhitehornSYSCTL_DECL(_kern_ipc); 147187262SnwhitehornSYSCTL_INT(_kern_ipc, OID_AUTO, shmmax, CTLFLAG_RW, &shminfo.shmmax, 0, ""); 148187262SnwhitehornSYSCTL_INT(_kern_ipc, OID_AUTO, shmmin, CTLFLAG_RW, &shminfo.shmmin, 0, ""); 149187262SnwhitehornSYSCTL_INT(_kern_ipc, OID_AUTO, shmmni, CTLFLAG_RDTUN, &shminfo.shmmni, 0, ""); 150209302SnwhitehornSYSCTL_INT(_kern_ipc, OID_AUTO, shmseg, CTLFLAG_RDTUN, &shminfo.shmseg, 0, ""); 151187262SnwhitehornSYSCTL_INT(_kern_ipc, OID_AUTO, shmall, CTLFLAG_RW, &shminfo.shmall, 0, ""); 152187262SnwhitehornSYSCTL_INT(_kern_ipc, OID_AUTO, shm_use_phys, CTLFLAG_RW, 153187262Snwhitehorn &shm_use_phys, 0, ""); 154187262SnwhitehornSYSCTL_INT(_kern_ipc, OID_AUTO, shm_allow_removed, CTLFLAG_RW, 155187262Snwhitehorn &shm_allow_removed, 0, ""); 156187262SnwhitehornSYSCTL_PROC(_kern_ipc, OID_AUTO, shmsegs, CTLFLAG_RD, 157187262Snwhitehorn NULL, 0, sysctl_shmsegs, "", ""); 158187262Snwhitehorn 159187262Snwhitehornstatic int 160187262Snwhitehornshm_find_segment_by_key(key) 161187262Snwhitehorn key_t key; 162187262Snwhitehorn{ 163187262Snwhitehorn int i; 164187262Snwhitehorn 165187262Snwhitehorn for (i = 0; i < shmalloced; i++) 166187262Snwhitehorn if ((shmsegs[i].shm_perm.mode & SHMSEG_ALLOCATED) && 167187262Snwhitehorn shmsegs[i].shm_perm.key == key) 168187262Snwhitehorn return (i); 169187262Snwhitehorn return (-1); 170187262Snwhitehorn} 171187262Snwhitehorn 172187262Snwhitehornstatic struct shmid_ds * 173187262Snwhitehornshm_find_segment_by_shmid(int shmid) 174187262Snwhitehorn{ 175187262Snwhitehorn int segnum; 176187262Snwhitehorn struct shmid_ds *shmseg; 177187262Snwhitehorn 178187262Snwhitehorn segnum = IPCID_TO_IX(shmid); 179187262Snwhitehorn if (segnum < 0 || segnum >= shmalloced) 180187262Snwhitehorn return (NULL); 181187262Snwhitehorn shmseg = &shmsegs[segnum]; 182187262Snwhitehorn if ((shmseg->shm_perm.mode & SHMSEG_ALLOCATED) == 0 || 183187262Snwhitehorn (!shm_allow_removed && 184187262Snwhitehorn (shmseg->shm_perm.mode & SHMSEG_REMOVED) != 0) || 185187262Snwhitehorn shmseg->shm_perm.seq != IPCID_TO_SEQ(shmid)) 186187262Snwhitehorn return (NULL); 187187262Snwhitehorn return (shmseg); 188187262Snwhitehorn} 189187262Snwhitehorn 190187262Snwhitehornstatic struct shmid_ds * 191187262Snwhitehornshm_find_segment_by_shmidx(int segnum) 192187262Snwhitehorn{ 193187262Snwhitehorn struct shmid_ds *shmseg; 194187262Snwhitehorn 195187262Snwhitehorn if (segnum < 0 || segnum >= shmalloced) 196187262Snwhitehorn return (NULL); 197187262Snwhitehorn shmseg = &shmsegs[segnum]; 198187262Snwhitehorn if ((shmseg->shm_perm.mode & SHMSEG_ALLOCATED) == 0 || 199187262Snwhitehorn (!shm_allow_removed && 200187262Snwhitehorn (shmseg->shm_perm.mode & SHMSEG_REMOVED) != 0)) 201187262Snwhitehorn return (NULL); 202187262Snwhitehorn return (shmseg); 203208840Snwhitehorn} 204208840Snwhitehorn 205208840Snwhitehornstatic void 206208840Snwhitehornshm_deallocate_segment(shmseg) 207208840Snwhitehorn struct shmid_ds *shmseg; 208208840Snwhitehorn{ 209187262Snwhitehorn struct shm_handle *shm_handle; 210187262Snwhitehorn size_t size; 211208840Snwhitehorn 212208840Snwhitehorn GIANT_REQUIRED; 213208840Snwhitehorn 214208840Snwhitehorn shm_handle = shmseg->shm_internal; 215208840Snwhitehorn vm_object_deallocate(shm_handle->shm_object); 216208840Snwhitehorn free(shm_handle, M_SHM); 217208840Snwhitehorn shmseg->shm_internal = NULL; 218208840Snwhitehorn size = round_page(shmseg->shm_segsz); 219208840Snwhitehorn shm_committed -= btoc(size); 220187262Snwhitehorn shm_nused--; 221208840Snwhitehorn shmseg->shm_perm.mode = SHMSEG_FREE; 222187262Snwhitehorn} 223187262Snwhitehorn 224187262Snwhitehornstatic int 225187262Snwhitehornshm_delete_mapping(struct vmspace *vm, struct shmmap_state *shmmap_s) 226187262Snwhitehorn{ 227187262Snwhitehorn struct shmid_ds *shmseg; 228187262Snwhitehorn int segnum, result; 229187262Snwhitehorn size_t size; 230187262Snwhitehorn 231208840Snwhitehorn GIANT_REQUIRED; 232187262Snwhitehorn 233187262Snwhitehorn segnum = IPCID_TO_IX(shmmap_s->shmid); 234187262Snwhitehorn shmseg = &shmsegs[segnum]; 235187262Snwhitehorn size = round_page(shmseg->shm_segsz); 236187262Snwhitehorn result = vm_map_remove(&vm->vm_map, shmmap_s->va, shmmap_s->va + size); 237187262Snwhitehorn if (result != KERN_SUCCESS) 238187262Snwhitehorn return (EINVAL); 239187262Snwhitehorn shmmap_s->shmid = -1; 240209302Snwhitehorn shmseg->shm_dtime = time_second; 241209302Snwhitehorn if ((--shmseg->shm_nattch <= 0) && 242209302Snwhitehorn (shmseg->shm_perm.mode & SHMSEG_REMOVED)) { 243187262Snwhitehorn shm_deallocate_segment(shmseg); 244187262Snwhitehorn shm_last_free = segnum; 245187262Snwhitehorn } 246187262Snwhitehorn return (0); 247187262Snwhitehorn} 248187262Snwhitehorn 249187262Snwhitehorn#ifndef _SYS_SYSPROTO_H_ 250187262Snwhitehornstruct shmdt_args { 251187262Snwhitehorn const void *shmaddr; 252209302Snwhitehorn}; 253212687Sandreast#endif 254187262Snwhitehorn 255187262Snwhitehorn/* 256187262Snwhitehorn * MPSAFE 257187262Snwhitehorn */ 258187262Snwhitehornint 259209302Snwhitehornshmdt(td, uap) 260187262Snwhitehorn struct thread *td; 261187262Snwhitehorn struct shmdt_args *uap; 262187262Snwhitehorn{ 263187262Snwhitehorn struct proc *p = td->td_proc; 264187262Snwhitehorn struct shmmap_state *shmmap_s; 265187262Snwhitehorn int i; 266187262Snwhitehorn int error = 0; 267187262Snwhitehorn 268187262Snwhitehorn if (!jail_sysvipc_allowed && jailed(td->td_ucred)) 269187262Snwhitehorn return (ENOSYS); 270187262Snwhitehorn mtx_lock(&Giant); 271187262Snwhitehorn shmmap_s = p->p_vmspace->vm_shm; 272187262Snwhitehorn if (shmmap_s == NULL) { 273187262Snwhitehorn error = EINVAL; 274187262Snwhitehorn goto done2; 275208840Snwhitehorn } 276208840Snwhitehorn for (i = 0; i < shminfo.shmseg; i++, shmmap_s++) { 277208840Snwhitehorn if (shmmap_s->shmid != -1 && 278208840Snwhitehorn shmmap_s->va == (vm_offset_t)uap->shmaddr) { 279208840Snwhitehorn break; 280208840Snwhitehorn } 281208840Snwhitehorn } 282208840Snwhitehorn if (i == shminfo.shmseg) { 283208840Snwhitehorn error = EINVAL; 284208840Snwhitehorn goto done2; 285208840Snwhitehorn } 286208840Snwhitehorn error = shm_delete_mapping(p->p_vmspace, shmmap_s); 287187262Snwhitehorndone2: 288187262Snwhitehorn mtx_unlock(&Giant); 289187262Snwhitehorn return (error); 290187262Snwhitehorn} 291187262Snwhitehorn 292187262Snwhitehorn#ifndef _SYS_SYSPROTO_H_ 293187262Snwhitehornstruct shmat_args { 294187262Snwhitehorn int shmid; 295187262Snwhitehorn const void *shmaddr; 296187262Snwhitehorn int shmflg; 297187262Snwhitehorn}; 298187262Snwhitehorn#endif 299187262Snwhitehorn 300187262Snwhitehorn/* 301187262Snwhitehorn * MPSAFE 302187262Snwhitehorn */ 303187262Snwhitehornint 304187262Snwhitehornkern_shmat(td, shmid, shmaddr, shmflg) 305187262Snwhitehorn struct thread *td; 306187262Snwhitehorn int shmid; 307187262Snwhitehorn const void *shmaddr; 308187262Snwhitehorn int shmflg; 309187262Snwhitehorn{ 310187262Snwhitehorn struct proc *p = td->td_proc; 311187262Snwhitehorn int i, flags; 312187262Snwhitehorn struct shmid_ds *shmseg; 313187262Snwhitehorn struct shmmap_state *shmmap_s = NULL; 314187262Snwhitehorn struct shm_handle *shm_handle; 315187262Snwhitehorn vm_offset_t attach_va; 316187262Snwhitehorn vm_prot_t prot; 317187262Snwhitehorn vm_size_t size; 318187262Snwhitehorn int rv; 319187262Snwhitehorn int error = 0; 320187262Snwhitehorn 321187262Snwhitehorn if (!jail_sysvipc_allowed && jailed(td->td_ucred)) 322187262Snwhitehorn return (ENOSYS); 323187262Snwhitehorn mtx_lock(&Giant); 324187473Snwhitehorn shmmap_s = p->p_vmspace->vm_shm; 325187473Snwhitehorn if (shmmap_s == NULL) { 326187262Snwhitehorn size = shminfo.shmseg * sizeof(struct shmmap_state); 327187262Snwhitehorn shmmap_s = malloc(size, M_SHM, M_WAITOK); 328187473Snwhitehorn for (i = 0; i < shminfo.shmseg; i++) 329208840Snwhitehorn shmmap_s[i].shmid = -1; 330208840Snwhitehorn p->p_vmspace->vm_shm = shmmap_s; 331187473Snwhitehorn } 332187473Snwhitehorn shmseg = shm_find_segment_by_shmid(shmid); 333187473Snwhitehorn if (shmseg == NULL) { 334187473Snwhitehorn error = EINVAL; 335187473Snwhitehorn goto done2; 336187262Snwhitehorn } 337187262Snwhitehorn error = ipcperm(td, &shmseg->shm_perm, 338187262Snwhitehorn (shmflg & SHM_RDONLY) ? IPC_R : IPC_R|IPC_W); 339187262Snwhitehorn if (error) 340187262Snwhitehorn goto done2; 341187262Snwhitehorn for (i = 0; i < shminfo.shmseg; i++) { 342187262Snwhitehorn if (shmmap_s->shmid == -1) 343187262Snwhitehorn break; 344187262Snwhitehorn shmmap_s++; 345187262Snwhitehorn } 346187262Snwhitehorn if (i >= shminfo.shmseg) { 347187262Snwhitehorn error = EMFILE; 348187262Snwhitehorn goto done2; 349187262Snwhitehorn } 350187262Snwhitehorn size = round_page(shmseg->shm_segsz); 351187262Snwhitehorn#ifdef VM_PROT_READ_IS_EXEC 352187262Snwhitehorn prot = VM_PROT_READ | VM_PROT_EXECUTE; 353187262Snwhitehorn#else 354187262Snwhitehorn prot = VM_PROT_READ; 355187262Snwhitehorn#endif 356187262Snwhitehorn if ((shmflg & SHM_RDONLY) == 0) 357187473Snwhitehorn prot |= VM_PROT_WRITE; 358208840Snwhitehorn flags = MAP_ANON | MAP_SHARED; 359208840Snwhitehorn if (shmaddr) { 360187262Snwhitehorn flags |= MAP_FIXED; 361187262Snwhitehorn if (shmflg & SHM_RND) { 362187262Snwhitehorn attach_va = (vm_offset_t)shmaddr & ~(SHMLBA-1); 363208840Snwhitehorn } else if (((vm_offset_t)shmaddr & (SHMLBA-1)) == 0) { 364187262Snwhitehorn attach_va = (vm_offset_t)shmaddr; 365187262Snwhitehorn } else { 366187262Snwhitehorn error = EINVAL; 367187262Snwhitehorn goto done2; 368187262Snwhitehorn } 369187262Snwhitehorn } else { 370187262Snwhitehorn /* 371187262Snwhitehorn * This is just a hint to vm_map_find() about where to 372187262Snwhitehorn * put it. 373187262Snwhitehorn */ 374187262Snwhitehorn PROC_LOCK(p); 375187262Snwhitehorn attach_va = round_page((vm_offset_t)p->p_vmspace->vm_daddr + 376187262Snwhitehorn lim_max(p, RLIMIT_DATA)); 377208840Snwhitehorn PROC_UNLOCK(p); 378208840Snwhitehorn } 379208840Snwhitehorn 380208840Snwhitehorn shm_handle = shmseg->shm_internal; 381187262Snwhitehorn vm_object_reference(shm_handle->shm_object); 382208840Snwhitehorn rv = vm_map_find(&p->p_vmspace->vm_map, shm_handle->shm_object, 383208840Snwhitehorn 0, &attach_va, size, (flags & MAP_FIXED)?0:1, prot, prot, 0); 384208840Snwhitehorn if (rv != KERN_SUCCESS) { 385208840Snwhitehorn vm_object_deallocate(shm_handle->shm_object); 386208840Snwhitehorn error = ENOMEM; 387208840Snwhitehorn goto done2; 388208840Snwhitehorn } 389208840Snwhitehorn vm_map_inherit(&p->p_vmspace->vm_map, 390208840Snwhitehorn attach_va, attach_va + size, VM_INHERIT_SHARE); 391208840Snwhitehorn 392208840Snwhitehorn shmmap_s->va = attach_va; 393208840Snwhitehorn shmmap_s->shmid = shmid; 394187262Snwhitehorn shmseg->shm_lpid = p->p_pid; 395187262Snwhitehorn shmseg->shm_atime = time_second; 396187262Snwhitehorn shmseg->shm_nattch++; 397187262Snwhitehorn td->td_retval[0] = attach_va; 398187262Snwhitehorndone2: 399187473Snwhitehorn mtx_unlock(&Giant); 400187262Snwhitehorn return (error); 401187262Snwhitehorn} 402187262Snwhitehorn 403187262Snwhitehornint 404187262Snwhitehornshmat(td, uap) 405187262Snwhitehorn struct thread *td; 406208840Snwhitehorn struct shmat_args *uap; 407187262Snwhitehorn{ 408208840Snwhitehorn return kern_shmat(td, uap->shmid, uap->shmaddr, uap->shmflg); 409208840Snwhitehorn} 410208840Snwhitehorn 411208840Snwhitehornstruct oshmid_ds { 412187262Snwhitehorn struct ipc_perm shm_perm; /* operation perms */ 413187262Snwhitehorn int shm_segsz; /* size of segment (bytes) */ 414187262Snwhitehorn u_short shm_cpid; /* pid, creator */ 415187473Snwhitehorn u_short shm_lpid; /* pid, last operation */ 416187473Snwhitehorn short shm_nattch; /* no. of current attaches */ 417187262Snwhitehorn time_t shm_atime; /* last attach time */ 418187262Snwhitehorn time_t shm_dtime; /* last detach time */ 419187473Snwhitehorn time_t shm_ctime; /* last change time */ 420187473Snwhitehorn void *shm_handle; /* internal handle for shm segment */ 421187473Snwhitehorn}; 422187262Snwhitehorn 423187262Snwhitehornstruct oshmctl_args { 424187262Snwhitehorn int shmid; 425187262Snwhitehorn int cmd; 426187262Snwhitehorn struct oshmid_ds *ubuf; 427187262Snwhitehorn}; 428187262Snwhitehorn 429187262Snwhitehorn/* 430187262Snwhitehorn * MPSAFE 431187262Snwhitehorn */ 432187262Snwhitehornstatic int 433187262Snwhitehornoshmctl(td, uap) 434187262Snwhitehorn struct thread *td; 435187262Snwhitehorn struct oshmctl_args *uap; 436187262Snwhitehorn{ 437187262Snwhitehorn#ifdef COMPAT_43 438187262Snwhitehorn int error = 0; 439187262Snwhitehorn struct shmid_ds *shmseg; 440187262Snwhitehorn struct oshmid_ds outbuf; 441187262Snwhitehorn 442187262Snwhitehorn if (!jail_sysvipc_allowed && jailed(td->td_ucred)) 443187262Snwhitehorn return (ENOSYS); 444187262Snwhitehorn mtx_lock(&Giant); 445 shmseg = shm_find_segment_by_shmid(uap->shmid); 446 if (shmseg == NULL) { 447 error = EINVAL; 448 goto done2; 449 } 450 switch (uap->cmd) { 451 case IPC_STAT: 452 error = ipcperm(td, &shmseg->shm_perm, IPC_R); 453 if (error) 454 goto done2; 455 outbuf.shm_perm = shmseg->shm_perm; 456 outbuf.shm_segsz = shmseg->shm_segsz; 457 outbuf.shm_cpid = shmseg->shm_cpid; 458 outbuf.shm_lpid = shmseg->shm_lpid; 459 outbuf.shm_nattch = shmseg->shm_nattch; 460 outbuf.shm_atime = shmseg->shm_atime; 461 outbuf.shm_dtime = shmseg->shm_dtime; 462 outbuf.shm_ctime = shmseg->shm_ctime; 463 outbuf.shm_handle = shmseg->shm_internal; 464 error = copyout(&outbuf, uap->ubuf, sizeof(outbuf)); 465 if (error) 466 goto done2; 467 break; 468 default: 469 /* XXX casting to (sy_call_t *) is bogus, as usual. */ 470 error = ((sy_call_t *)shmctl)(td, uap); 471 break; 472 } 473done2: 474 mtx_unlock(&Giant); 475 return (error); 476#else 477 return (EINVAL); 478#endif 479} 480 481#ifndef _SYS_SYSPROTO_H_ 482struct shmctl_args { 483 int shmid; 484 int cmd; 485 struct shmid_ds *buf; 486}; 487#endif 488 489/* 490 * MPSAFE 491 */ 492int 493kern_shmctl(td, shmid, cmd, buf, bufsz) 494 struct thread *td; 495 int shmid; 496 int cmd; 497 void *buf; 498 size_t *bufsz; 499{ 500 int error = 0; 501 struct shmid_ds *shmseg; 502 503 if (!jail_sysvipc_allowed && jailed(td->td_ucred)) 504 return (ENOSYS); 505 506 mtx_lock(&Giant); 507 switch (cmd) { 508 case IPC_INFO: 509 memcpy(buf, &shminfo, sizeof(shminfo)); 510 if (bufsz) 511 *bufsz = sizeof(shminfo); 512 td->td_retval[0] = shmalloced; 513 goto done2; 514 case SHM_INFO: { 515 struct shm_info shm_info; 516 shm_info.used_ids = shm_nused; 517 shm_info.shm_rss = 0; /*XXX where to get from ? */ 518 shm_info.shm_tot = 0; /*XXX where to get from ? */ 519 shm_info.shm_swp = 0; /*XXX where to get from ? */ 520 shm_info.swap_attempts = 0; /*XXX where to get from ? */ 521 shm_info.swap_successes = 0; /*XXX where to get from ? */ 522 memcpy(buf, &shm_info, sizeof(shm_info)); 523 if (bufsz) 524 *bufsz = sizeof(shm_info); 525 td->td_retval[0] = shmalloced; 526 goto done2; 527 } 528 } 529 if (cmd == SHM_STAT) 530 shmseg = shm_find_segment_by_shmidx(shmid); 531 else 532 shmseg = shm_find_segment_by_shmid(shmid); 533 if (shmseg == NULL) { 534 error = EINVAL; 535 goto done2; 536 } 537 switch (cmd) { 538 case SHM_STAT: 539 case IPC_STAT: 540 error = ipcperm(td, &shmseg->shm_perm, IPC_R); 541 if (error) 542 goto done2; 543 memcpy(buf, shmseg, sizeof(struct shmid_ds)); 544 if (bufsz) 545 *bufsz = sizeof(struct shmid_ds); 546 if (cmd == SHM_STAT) 547 td->td_retval[0] = IXSEQ_TO_IPCID(shmid, shmseg->shm_perm); 548 break; 549 case IPC_SET: { 550 struct shmid_ds *shmid; 551 552 shmid = (struct shmid_ds *)buf; 553 error = ipcperm(td, &shmseg->shm_perm, IPC_M); 554 if (error) 555 goto done2; 556 shmseg->shm_perm.uid = shmid->shm_perm.uid; 557 shmseg->shm_perm.gid = shmid->shm_perm.gid; 558 shmseg->shm_perm.mode = 559 (shmseg->shm_perm.mode & ~ACCESSPERMS) | 560 (shmid->shm_perm.mode & ACCESSPERMS); 561 shmseg->shm_ctime = time_second; 562 break; 563 } 564 case IPC_RMID: 565 error = ipcperm(td, &shmseg->shm_perm, IPC_M); 566 if (error) 567 goto done2; 568 shmseg->shm_perm.key = IPC_PRIVATE; 569 shmseg->shm_perm.mode |= SHMSEG_REMOVED; 570 if (shmseg->shm_nattch <= 0) { 571 shm_deallocate_segment(shmseg); 572 shm_last_free = IPCID_TO_IX(shmid); 573 } 574 break; 575#if 0 576 case SHM_LOCK: 577 case SHM_UNLOCK: 578#endif 579 default: 580 error = EINVAL; 581 break; 582 } 583done2: 584 mtx_unlock(&Giant); 585 return (error); 586} 587 588int 589shmctl(td, uap) 590 struct thread *td; 591 struct shmctl_args *uap; 592{ 593 int error = 0; 594 struct shmid_ds buf; 595 size_t bufsz; 596 597 /* IPC_SET needs to copyin the buffer before calling kern_shmctl */ 598 if (uap->cmd == IPC_SET) { 599 if ((error = copyin(uap->buf, &buf, sizeof(struct shmid_ds)))) 600 goto done; 601 } 602 603 error = kern_shmctl(td, uap->shmid, uap->cmd, (void *)&buf, &bufsz); 604 if (error) 605 goto done; 606 607 /* Cases in which we need to copyout */ 608 switch (uap->cmd) { 609 case IPC_INFO: 610 case SHM_INFO: 611 case SHM_STAT: 612 case IPC_STAT: 613 error = copyout(&buf, uap->buf, bufsz); 614 break; 615 } 616 617done: 618 if (error) { 619 /* Invalidate the return value */ 620 td->td_retval[0] = -1; 621 } 622 return (error); 623} 624 625 626#ifndef _SYS_SYSPROTO_H_ 627struct shmget_args { 628 key_t key; 629 size_t size; 630 int shmflg; 631}; 632#endif 633 634static int 635shmget_existing(td, uap, mode, segnum) 636 struct thread *td; 637 struct shmget_args *uap; 638 int mode; 639 int segnum; 640{ 641 struct shmid_ds *shmseg; 642 int error; 643 644 shmseg = &shmsegs[segnum]; 645 if (shmseg->shm_perm.mode & SHMSEG_REMOVED) { 646 /* 647 * This segment is in the process of being allocated. Wait 648 * until it's done, and look the key up again (in case the 649 * allocation failed or it was freed). 650 */ 651 shmseg->shm_perm.mode |= SHMSEG_WANTED; 652 error = tsleep(shmseg, PLOCK | PCATCH, "shmget", 0); 653 if (error) 654 return (error); 655 return (EAGAIN); 656 } 657 if ((uap->shmflg & (IPC_CREAT | IPC_EXCL)) == (IPC_CREAT | IPC_EXCL)) 658 return (EEXIST); 659 error = ipcperm(td, &shmseg->shm_perm, mode); 660 if (error) 661 return (error); 662 if (uap->size && uap->size > shmseg->shm_segsz) 663 return (EINVAL); 664 td->td_retval[0] = IXSEQ_TO_IPCID(segnum, shmseg->shm_perm); 665 return (0); 666} 667 668static int 669shmget_allocate_segment(td, uap, mode) 670 struct thread *td; 671 struct shmget_args *uap; 672 int mode; 673{ 674 int i, segnum, shmid, size; 675 struct ucred *cred = td->td_ucred; 676 struct shmid_ds *shmseg; 677 struct shm_handle *shm_handle; 678 679 GIANT_REQUIRED; 680 681 if (uap->size < shminfo.shmmin || uap->size > shminfo.shmmax) 682 return (EINVAL); 683 if (shm_nused >= shminfo.shmmni) /* Any shmids left? */ 684 return (ENOSPC); 685 size = round_page(uap->size); 686 if (shm_committed + btoc(size) > shminfo.shmall) 687 return (ENOMEM); 688 if (shm_last_free < 0) { 689 shmrealloc(); /* Maybe expand the shmsegs[] array. */ 690 for (i = 0; i < shmalloced; i++) 691 if (shmsegs[i].shm_perm.mode & SHMSEG_FREE) 692 break; 693 if (i == shmalloced) 694 return (ENOSPC); 695 segnum = i; 696 } else { 697 segnum = shm_last_free; 698 shm_last_free = -1; 699 } 700 shmseg = &shmsegs[segnum]; 701 /* 702 * In case we sleep in malloc(), mark the segment present but deleted 703 * so that noone else tries to create the same key. 704 */ 705 shmseg->shm_perm.mode = SHMSEG_ALLOCATED | SHMSEG_REMOVED; 706 shmseg->shm_perm.key = uap->key; 707 shmseg->shm_perm.seq = (shmseg->shm_perm.seq + 1) & 0x7fff; 708 shm_handle = (struct shm_handle *) 709 malloc(sizeof(struct shm_handle), M_SHM, M_WAITOK); 710 shmid = IXSEQ_TO_IPCID(segnum, shmseg->shm_perm); 711 712 /* 713 * We make sure that we have allocated a pager before we need 714 * to. 715 */ 716 if (shm_use_phys) { 717 shm_handle->shm_object = 718 vm_pager_allocate(OBJT_PHYS, 0, size, VM_PROT_DEFAULT, 0); 719 } else { 720 shm_handle->shm_object = 721 vm_pager_allocate(OBJT_SWAP, 0, size, VM_PROT_DEFAULT, 0); 722 } 723 VM_OBJECT_LOCK(shm_handle->shm_object); 724 vm_object_clear_flag(shm_handle->shm_object, OBJ_ONEMAPPING); 725 vm_object_set_flag(shm_handle->shm_object, OBJ_NOSPLIT); 726 VM_OBJECT_UNLOCK(shm_handle->shm_object); 727 728 shmseg->shm_internal = shm_handle; 729 shmseg->shm_perm.cuid = shmseg->shm_perm.uid = cred->cr_uid; 730 shmseg->shm_perm.cgid = shmseg->shm_perm.gid = cred->cr_gid; 731 shmseg->shm_perm.mode = (shmseg->shm_perm.mode & SHMSEG_WANTED) | 732 (mode & ACCESSPERMS) | SHMSEG_ALLOCATED; 733 shmseg->shm_segsz = uap->size; 734 shmseg->shm_cpid = td->td_proc->p_pid; 735 shmseg->shm_lpid = shmseg->shm_nattch = 0; 736 shmseg->shm_atime = shmseg->shm_dtime = 0; 737 shmseg->shm_ctime = time_second; 738 shm_committed += btoc(size); 739 shm_nused++; 740 if (shmseg->shm_perm.mode & SHMSEG_WANTED) { 741 /* 742 * Somebody else wanted this key while we were asleep. Wake 743 * them up now. 744 */ 745 shmseg->shm_perm.mode &= ~SHMSEG_WANTED; 746 wakeup(shmseg); 747 } 748 td->td_retval[0] = shmid; 749 return (0); 750} 751 752/* 753 * MPSAFE 754 */ 755int 756shmget(td, uap) 757 struct thread *td; 758 struct shmget_args *uap; 759{ 760 int segnum, mode; 761 int error; 762 763 if (!jail_sysvipc_allowed && jailed(td->td_ucred)) 764 return (ENOSYS); 765 mtx_lock(&Giant); 766 mode = uap->shmflg & ACCESSPERMS; 767 if (uap->key != IPC_PRIVATE) { 768 again: 769 segnum = shm_find_segment_by_key(uap->key); 770 if (segnum >= 0) { 771 error = shmget_existing(td, uap, mode, segnum); 772 if (error == EAGAIN) 773 goto again; 774 goto done2; 775 } 776 if ((uap->shmflg & IPC_CREAT) == 0) { 777 error = ENOENT; 778 goto done2; 779 } 780 } 781 error = shmget_allocate_segment(td, uap, mode); 782done2: 783 mtx_unlock(&Giant); 784 return (error); 785} 786 787/* 788 * MPSAFE 789 */ 790int 791shmsys(td, uap) 792 struct thread *td; 793 /* XXX actually varargs. */ 794 struct shmsys_args /* { 795 int which; 796 int a2; 797 int a3; 798 int a4; 799 } */ *uap; 800{ 801 int error; 802 803 if (!jail_sysvipc_allowed && jailed(td->td_ucred)) 804 return (ENOSYS); 805 if (uap->which < 0 || 806 uap->which >= sizeof(shmcalls)/sizeof(shmcalls[0])) 807 return (EINVAL); 808 mtx_lock(&Giant); 809 error = (*shmcalls[uap->which])(td, &uap->a2); 810 mtx_unlock(&Giant); 811 return (error); 812} 813 814static void 815shmfork_myhook(p1, p2) 816 struct proc *p1, *p2; 817{ 818 struct shmmap_state *shmmap_s; 819 size_t size; 820 int i; 821 822 size = shminfo.shmseg * sizeof(struct shmmap_state); 823 shmmap_s = malloc(size, M_SHM, M_WAITOK); 824 bcopy(p1->p_vmspace->vm_shm, shmmap_s, size); 825 p2->p_vmspace->vm_shm = shmmap_s; 826 for (i = 0; i < shminfo.shmseg; i++, shmmap_s++) 827 if (shmmap_s->shmid != -1) 828 shmsegs[IPCID_TO_IX(shmmap_s->shmid)].shm_nattch++; 829} 830 831static void 832shmexit_myhook(struct vmspace *vm) 833{ 834 struct shmmap_state *base, *shm; 835 int i; 836 837 GIANT_REQUIRED; 838 839 if ((base = vm->vm_shm) != NULL) { 840 vm->vm_shm = NULL; 841 for (i = 0, shm = base; i < shminfo.shmseg; i++, shm++) { 842 if (shm->shmid != -1) 843 shm_delete_mapping(vm, shm); 844 } 845 free(base, M_SHM); 846 } 847} 848 849static void 850shmrealloc(void) 851{ 852 int i; 853 struct shmid_ds *newsegs; 854 855 if (shmalloced >= shminfo.shmmni) 856 return; 857 858 newsegs = malloc(shminfo.shmmni * sizeof(*newsegs), M_SHM, M_WAITOK); 859 if (newsegs == NULL) 860 return; 861 for (i = 0; i < shmalloced; i++) 862 bcopy(&shmsegs[i], &newsegs[i], sizeof(newsegs[0])); 863 for (; i < shminfo.shmmni; i++) { 864 shmsegs[i].shm_perm.mode = SHMSEG_FREE; 865 shmsegs[i].shm_perm.seq = 0; 866 } 867 free(shmsegs, M_SHM); 868 shmsegs = newsegs; 869 shmalloced = shminfo.shmmni; 870} 871 872static void 873shminit() 874{ 875 int i; 876 877 TUNABLE_INT_FETCH("kern.ipc.shmmaxpgs", &shminfo.shmall); 878 for (i = PAGE_SIZE; i > 0; i--) { 879 shminfo.shmmax = shminfo.shmall * PAGE_SIZE; 880 if (shminfo.shmmax >= shminfo.shmall) 881 break; 882 } 883 TUNABLE_INT_FETCH("kern.ipc.shmmin", &shminfo.shmmin); 884 TUNABLE_INT_FETCH("kern.ipc.shmmni", &shminfo.shmmni); 885 TUNABLE_INT_FETCH("kern.ipc.shmseg", &shminfo.shmseg); 886 TUNABLE_INT_FETCH("kern.ipc.shm_use_phys", &shm_use_phys); 887 888 shmalloced = shminfo.shmmni; 889 shmsegs = malloc(shmalloced * sizeof(shmsegs[0]), M_SHM, M_WAITOK); 890 if (shmsegs == NULL) 891 panic("cannot allocate initial memory for sysvshm"); 892 for (i = 0; i < shmalloced; i++) { 893 shmsegs[i].shm_perm.mode = SHMSEG_FREE; 894 shmsegs[i].shm_perm.seq = 0; 895 } 896 shm_last_free = 0; 897 shm_nused = 0; 898 shm_committed = 0; 899 shmexit_hook = &shmexit_myhook; 900 shmfork_hook = &shmfork_myhook; 901} 902 903static int 904shmunload() 905{ 906 907 if (shm_nused > 0) 908 return (EBUSY); 909 910 free(shmsegs, M_SHM); 911 shmexit_hook = NULL; 912 shmfork_hook = NULL; 913 return (0); 914} 915 916static int 917sysctl_shmsegs(SYSCTL_HANDLER_ARGS) 918{ 919 920 return (SYSCTL_OUT(req, shmsegs, shmalloced * sizeof(shmsegs[0]))); 921} 922 923static int 924sysvshm_modload(struct module *module, int cmd, void *arg) 925{ 926 int error = 0; 927 928 switch (cmd) { 929 case MOD_LOAD: 930 shminit(); 931 break; 932 case MOD_UNLOAD: 933 error = shmunload(); 934 break; 935 case MOD_SHUTDOWN: 936 break; 937 default: 938 error = EINVAL; 939 break; 940 } 941 return (error); 942} 943 944static moduledata_t sysvshm_mod = { 945 "sysvshm", 946 &sysvshm_modload, 947 NULL 948}; 949 950SYSCALL_MODULE_HELPER(shmsys); 951SYSCALL_MODULE_HELPER(shmat); 952SYSCALL_MODULE_HELPER(shmctl); 953SYSCALL_MODULE_HELPER(shmdt); 954SYSCALL_MODULE_HELPER(shmget); 955 956DECLARE_MODULE(sysvshm, sysvshm_mod, 957 SI_SUB_SYSV_SHM, SI_ORDER_FIRST); 958MODULE_VERSION(sysvshm, 1); 959