sysv_shm.c revision 69449
162143Sarchie/* $FreeBSD: head/sys/kern/sysv_shm.c 69449 2000-12-01 08:57:47Z alfred $ */ 262143Sarchie/* $NetBSD: sysv_shm.c,v 1.23 1994/07/04 23:25:12 glass Exp $ */ 362143Sarchie 462143Sarchie/* 562143Sarchie * Copyright (c) 1994 Adam Glass and Charles Hannum. All rights reserved. 662143Sarchie * 762143Sarchie * Redistribution and use in source and binary forms, with or without 862143Sarchie * modification, are permitted provided that the following conditions 962143Sarchie * are met: 1062143Sarchie * 1. Redistributions of source code must retain the above copyright 1162143Sarchie * notice, this list of conditions and the following disclaimer. 1262143Sarchie * 2. Redistributions in binary form must reproduce the above copyright 1362143Sarchie * notice, this list of conditions and the following disclaimer in the 1462143Sarchie * documentation and/or other materials provided with the distribution. 1562143Sarchie * 3. All advertising materials mentioning features or use of this software 1662143Sarchie * must display the following acknowledgement: 1762143Sarchie * This product includes software developed by Adam Glass and Charles 1862143Sarchie * Hannum. 1962143Sarchie * 4. The names of the authors may not be used to endorse or promote products 2062143Sarchie * derived from this software without specific prior written permission. 2162143Sarchie * 2262143Sarchie * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 2362143Sarchie * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 2462143Sarchie * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2562143Sarchie * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, 2662143Sarchie * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2762143Sarchie * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2862143Sarchie * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2962143Sarchie * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3062143Sarchie * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 3162143Sarchie * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3262143Sarchie */ 3362143Sarchie 3462143Sarchie#include "opt_compat.h" 3562143Sarchie#include "opt_rlimit.h" 3662143Sarchie#include "opt_sysvipc.h" 3762143Sarchie 3862143Sarchie#include <sys/param.h> 3962143Sarchie#include <sys/systm.h> 4062143Sarchie#include <sys/sysproto.h> 4162143Sarchie#include <sys/kernel.h> 4262143Sarchie#include <sys/sysctl.h> 4362143Sarchie#include <sys/shm.h> 4462143Sarchie#include <sys/proc.h> 4562143Sarchie#include <sys/malloc.h> 4662143Sarchie#include <sys/mman.h> 4762143Sarchie#include <sys/stat.h> 4862143Sarchie#include <sys/syscall.h> 4962143Sarchie#include <sys/sysent.h> 5062143Sarchie#include <sys/jail.h> 5162143Sarchie 5262143Sarchie#include <vm/vm.h> 5362143Sarchie#include <vm/vm_param.h> 5462143Sarchie#include <sys/lock.h> 5562143Sarchie#include <vm/pmap.h> 5662143Sarchie#include <vm/vm_object.h> 5762143Sarchie#include <vm/vm_map.h> 5862143Sarchie#include <vm/vm_page.h> 5962143Sarchie#include <vm/vm_pager.h> 6062143Sarchie 6162143Sarchiestatic MALLOC_DEFINE(M_SHM, "shm", "SVID compatible shared memory segments"); 6262143Sarchie 6362143Sarchiestruct oshmctl_args; 6462143Sarchiestatic int oshmctl __P((struct proc *p, struct oshmctl_args *uap)); 6562143Sarchie 6662143Sarchiestatic int shmget_allocate_segment __P((struct proc *p, struct shmget_args *uap, int mode)); 6762143Sarchiestatic int shmget_existing __P((struct proc *p, struct shmget_args *uap, int mode, int segnum)); 6862143Sarchie 6962143Sarchie/* XXX casting to (sy_call_t *) is bogus, as usual. */ 7062143Sarchiestatic sy_call_t *shmcalls[] = { 7162143Sarchie (sy_call_t *)shmat, (sy_call_t *)oshmctl, 7262143Sarchie (sy_call_t *)shmdt, (sy_call_t *)shmget, 7362143Sarchie (sy_call_t *)shmctl 7462143Sarchie}; 7562143Sarchie 7662143Sarchie#define SHMSEG_FREE 0x0200 7762143Sarchie#define SHMSEG_REMOVED 0x0400 7862143Sarchie#define SHMSEG_ALLOCATED 0x0800 7962143Sarchie#define SHMSEG_WANTED 0x1000 8062143Sarchie 8162143Sarchiestatic int shm_last_free, shm_nused, shm_committed, shmalloced; 8262143Sarchiestatic struct shmid_ds *shmsegs; 8362143Sarchie 8462143Sarchiestruct shm_handle { 8562143Sarchie /* vm_offset_t kva; */ 8662143Sarchie vm_object_t shm_object; 8762143Sarchie}; 8862143Sarchie 8962143Sarchiestruct shmmap_state { 9062143Sarchie vm_offset_t va; 9162143Sarchie int shmid; 9262143Sarchie}; 9362143Sarchie 9462143Sarchiestatic void shm_deallocate_segment __P((struct shmid_ds *)); 9562143Sarchiestatic int shm_find_segment_by_key __P((key_t)); 9662143Sarchiestatic struct shmid_ds *shm_find_segment_by_shmid __P((int)); 9762143Sarchiestatic int shm_delete_mapping __P((struct proc *, struct shmmap_state *)); 9862143Sarchiestatic void shmrealloc __P((void)); 9962143Sarchiestatic void shminit __P((void)); 10062143Sarchiestatic int sysvshm_modload __P((struct module *, int, void *)); 10162143Sarchiestatic int shmunload __P((void)); 10262143Sarchiestatic void shmexit_myhook __P((struct proc *p)); 10362143Sarchiestatic void shmfork_myhook __P((struct proc *p1, struct proc *p2)); 10462143Sarchie 10562143Sarchie/* 10662143Sarchie * Tuneable values 10762143Sarchie */ 10862143Sarchie#ifndef SHMMAXPGS 10962143Sarchie#define SHMMAXPGS 1024 /* XXX increase this, it's not in kva! */ 11062143Sarchie#endif 11162143Sarchie#ifndef SHMMAX 11262143Sarchie#define SHMMAX (SHMMAXPGS*PAGE_SIZE) 11362143Sarchie#endif 11462143Sarchie#ifndef SHMMIN 11562143Sarchie#define SHMMIN 1 11662143Sarchie#endif 11762143Sarchie#ifndef SHMMNI 11862143Sarchie#define SHMMNI 96 11962143Sarchie#endif 12062143Sarchie#ifndef SHMSEG 12162143Sarchie#define SHMSEG 64 12262143Sarchie#endif 12362143Sarchie#ifndef SHMALL 12462143Sarchie#define SHMALL (SHMMAXPGS) 12562143Sarchie#endif 12662143Sarchie 12762143Sarchiestruct shminfo shminfo = { 12862143Sarchie SHMMAX, 12962143Sarchie SHMMIN, 13062143Sarchie SHMMNI, 13162143Sarchie SHMSEG, 13262143Sarchie SHMALL 13362143Sarchie}; 13462143Sarchie 13562143Sarchiestatic int shm_use_phys; 13662143Sarchie 13762143SarchieSYSCTL_DECL(_kern_ipc); 13862143SarchieSYSCTL_INT(_kern_ipc, OID_AUTO, shmmax, CTLFLAG_RW, &shminfo.shmmax, 0, ""); 13962143SarchieSYSCTL_INT(_kern_ipc, OID_AUTO, shmmin, CTLFLAG_RW, &shminfo.shmmin, 0, ""); 14062143SarchieSYSCTL_INT(_kern_ipc, OID_AUTO, shmmni, CTLFLAG_RD, &shminfo.shmmni, 0, ""); 14162143SarchieSYSCTL_INT(_kern_ipc, OID_AUTO, shmseg, CTLFLAG_RW, &shminfo.shmseg, 0, ""); 14262143SarchieSYSCTL_INT(_kern_ipc, OID_AUTO, shmall, CTLFLAG_RW, &shminfo.shmall, 0, ""); 14362143SarchieSYSCTL_INT(_kern_ipc, OID_AUTO, shm_use_phys, CTLFLAG_RW, &shm_use_phys, 0, ""); 14462143Sarchie 14562143Sarchiestatic int 14662143Sarchieshm_find_segment_by_key(key) 14762143Sarchie key_t key; 14862143Sarchie{ 14962143Sarchie int i; 15062143Sarchie 15162143Sarchie for (i = 0; i < shmalloced; i++) 15262143Sarchie if ((shmsegs[i].shm_perm.mode & SHMSEG_ALLOCATED) && 15362143Sarchie shmsegs[i].shm_perm.key == key) 15462143Sarchie return i; 15562143Sarchie return -1; 15662143Sarchie} 15762143Sarchie 15862143Sarchiestatic struct shmid_ds * 15962143Sarchieshm_find_segment_by_shmid(shmid) 16062143Sarchie int shmid; 16162143Sarchie{ 16262143Sarchie int segnum; 16362143Sarchie struct shmid_ds *shmseg; 16462143Sarchie 16562143Sarchie segnum = IPCID_TO_IX(shmid); 16662143Sarchie if (segnum < 0 || segnum >= shmalloced) 16762143Sarchie return NULL; 16862143Sarchie shmseg = &shmsegs[segnum]; 16962143Sarchie if ((shmseg->shm_perm.mode & (SHMSEG_ALLOCATED | SHMSEG_REMOVED)) 17062143Sarchie != SHMSEG_ALLOCATED || 17162143Sarchie shmseg->shm_perm.seq != IPCID_TO_SEQ(shmid)) 17262143Sarchie return NULL; 17362143Sarchie return shmseg; 17462143Sarchie} 17562143Sarchie 17662143Sarchiestatic void 17762143Sarchieshm_deallocate_segment(shmseg) 17862143Sarchie struct shmid_ds *shmseg; 17962143Sarchie{ 18062143Sarchie struct shm_handle *shm_handle; 18162143Sarchie size_t size; 18262143Sarchie 18362143Sarchie shm_handle = shmseg->shm_internal; 18462143Sarchie vm_object_deallocate(shm_handle->shm_object); 18562143Sarchie free((caddr_t)shm_handle, M_SHM); 18662143Sarchie shmseg->shm_internal = NULL; 18762143Sarchie size = round_page(shmseg->shm_segsz); 18862143Sarchie shm_committed -= btoc(size); 18962143Sarchie shm_nused--; 19062143Sarchie shmseg->shm_perm.mode = SHMSEG_FREE; 19162143Sarchie} 19262143Sarchie 19362143Sarchiestatic int 19462143Sarchieshm_delete_mapping(p, shmmap_s) 19562143Sarchie struct proc *p; 19662143Sarchie struct shmmap_state *shmmap_s; 19762143Sarchie{ 19862143Sarchie struct shmid_ds *shmseg; 19962143Sarchie int segnum, result; 20062143Sarchie size_t size; 20162143Sarchie 20262143Sarchie segnum = IPCID_TO_IX(shmmap_s->shmid); 20362143Sarchie shmseg = &shmsegs[segnum]; 20462143Sarchie size = round_page(shmseg->shm_segsz); 20562143Sarchie result = vm_map_remove(&p->p_vmspace->vm_map, shmmap_s->va, shmmap_s->va + size); 20662143Sarchie if (result != KERN_SUCCESS) 20762143Sarchie return EINVAL; 20862143Sarchie shmmap_s->shmid = -1; 20962143Sarchie shmseg->shm_dtime = time_second; 21062143Sarchie if ((--shmseg->shm_nattch <= 0) && 21162143Sarchie (shmseg->shm_perm.mode & SHMSEG_REMOVED)) { 21262143Sarchie shm_deallocate_segment(shmseg); 21362143Sarchie shm_last_free = segnum; 21462143Sarchie } 21562143Sarchie return 0; 21662143Sarchie} 21762143Sarchie 21862143Sarchie#ifndef _SYS_SYSPROTO_H_ 21962143Sarchiestruct shmdt_args { 22062143Sarchie void *shmaddr; 22162143Sarchie}; 22262143Sarchie#endif 22362143Sarchie 22462143Sarchieint 22562143Sarchieshmdt(p, uap) 22662143Sarchie struct proc *p; 22762143Sarchie struct shmdt_args *uap; 22862143Sarchie{ 22962143Sarchie struct shmmap_state *shmmap_s; 23062143Sarchie int i; 23162143Sarchie 23262143Sarchie if (!jail_sysvipc_allowed && p->p_prison != NULL) 23362143Sarchie return (ENOSYS); 23462143Sarchie 23562143Sarchie shmmap_s = (struct shmmap_state *)p->p_vmspace->vm_shm; 23662143Sarchie if (shmmap_s == NULL) 23762143Sarchie return EINVAL; 23862143Sarchie for (i = 0; i < shminfo.shmseg; i++, shmmap_s++) 23962143Sarchie if (shmmap_s->shmid != -1 && 24062143Sarchie shmmap_s->va == (vm_offset_t)uap->shmaddr) 24162143Sarchie break; 24262143Sarchie if (i == shminfo.shmseg) 24362143Sarchie return EINVAL; 24462143Sarchie return shm_delete_mapping(p, shmmap_s); 24562143Sarchie} 24662143Sarchie 24762143Sarchie#ifndef _SYS_SYSPROTO_H_ 24862143Sarchiestruct shmat_args { 24962143Sarchie int shmid; 25062143Sarchie void *shmaddr; 25162143Sarchie int shmflg; 25262143Sarchie}; 25362143Sarchie#endif 25462143Sarchie 25562143Sarchieint 25662143Sarchieshmat(p, uap) 25762143Sarchie struct proc *p; 25862143Sarchie struct shmat_args *uap; 25962143Sarchie{ 26062143Sarchie int error, i, flags; 26162143Sarchie struct shmid_ds *shmseg; 26262143Sarchie struct shmmap_state *shmmap_s = NULL; 26362143Sarchie struct shm_handle *shm_handle; 26462143Sarchie vm_offset_t attach_va; 26562143Sarchie vm_prot_t prot; 26662143Sarchie vm_size_t size; 26762143Sarchie int rv; 26862143Sarchie 26962143Sarchie if (!jail_sysvipc_allowed && p->p_prison != NULL) 27062143Sarchie return (ENOSYS); 27162143Sarchie 27262143Sarchie shmmap_s = (struct shmmap_state *)p->p_vmspace->vm_shm; 27362143Sarchie if (shmmap_s == NULL) { 27462143Sarchie size = shminfo.shmseg * sizeof(struct shmmap_state); 27562143Sarchie shmmap_s = malloc(size, M_SHM, M_WAITOK); 27662143Sarchie for (i = 0; i < shminfo.shmseg; i++) 27762143Sarchie shmmap_s[i].shmid = -1; 27862143Sarchie p->p_vmspace->vm_shm = (caddr_t)shmmap_s; 27962143Sarchie } 28062143Sarchie shmseg = shm_find_segment_by_shmid(uap->shmid); 28162143Sarchie if (shmseg == NULL) 28262143Sarchie return EINVAL; 28362143Sarchie error = ipcperm(p, &shmseg->shm_perm, 28462143Sarchie (uap->shmflg & SHM_RDONLY) ? IPC_R : IPC_R|IPC_W); 28562143Sarchie if (error) 28662143Sarchie return error; 28762143Sarchie for (i = 0; i < shminfo.shmseg; i++) { 28862143Sarchie if (shmmap_s->shmid == -1) 28962143Sarchie break; 29062143Sarchie shmmap_s++; 29162143Sarchie } 29262143Sarchie if (i >= shminfo.shmseg) 29362143Sarchie return EMFILE; 29462143Sarchie size = round_page(shmseg->shm_segsz); 29562143Sarchie#ifdef VM_PROT_READ_IS_EXEC 29662143Sarchie prot = VM_PROT_READ | VM_PROT_EXECUTE; 29762143Sarchie#else 29862143Sarchie prot = VM_PROT_READ; 29962143Sarchie#endif 30062143Sarchie if ((uap->shmflg & SHM_RDONLY) == 0) 30162143Sarchie prot |= VM_PROT_WRITE; 30262143Sarchie flags = MAP_ANON | MAP_SHARED; 30362143Sarchie if (uap->shmaddr) { 30462143Sarchie flags |= MAP_FIXED; 30562143Sarchie if (uap->shmflg & SHM_RND) 30662143Sarchie attach_va = (vm_offset_t)uap->shmaddr & ~(SHMLBA-1); 30762143Sarchie else if (((vm_offset_t)uap->shmaddr & (SHMLBA-1)) == 0) 30862143Sarchie attach_va = (vm_offset_t)uap->shmaddr; 30962143Sarchie else 31062143Sarchie return EINVAL; 31162143Sarchie } else { 31262143Sarchie /* This is just a hint to vm_map_find() about where to put it. */ 31362143Sarchie attach_va = round_page((vm_offset_t)p->p_vmspace->vm_taddr + MAXTSIZ + MAXDSIZ); 31462143Sarchie } 31562143Sarchie 31662143Sarchie shm_handle = shmseg->shm_internal; 31762143Sarchie vm_object_reference(shm_handle->shm_object); 31862143Sarchie rv = vm_map_find(&p->p_vmspace->vm_map, shm_handle->shm_object, 31962143Sarchie 0, &attach_va, size, (flags & MAP_FIXED)?0:1, prot, prot, 0); 32062143Sarchie if (rv != KERN_SUCCESS) { 32162143Sarchie return ENOMEM; 32262143Sarchie } 32362143Sarchie vm_map_inherit(&p->p_vmspace->vm_map, 32462143Sarchie attach_va, attach_va + size, VM_INHERIT_SHARE); 32562143Sarchie 32662143Sarchie shmmap_s->va = attach_va; 32762143Sarchie shmmap_s->shmid = uap->shmid; 32862143Sarchie shmseg->shm_lpid = p->p_pid; 32962143Sarchie shmseg->shm_atime = time_second; 33062143Sarchie shmseg->shm_nattch++; 33162143Sarchie p->p_retval[0] = attach_va; 33262143Sarchie return 0; 33362143Sarchie} 33462143Sarchie 33562143Sarchiestruct oshmid_ds { 33662143Sarchie struct ipc_perm shm_perm; /* operation perms */ 33762143Sarchie int shm_segsz; /* size of segment (bytes) */ 33862143Sarchie ushort shm_cpid; /* pid, creator */ 33962143Sarchie ushort shm_lpid; /* pid, last operation */ 34062143Sarchie short shm_nattch; /* no. of current attaches */ 34162143Sarchie time_t shm_atime; /* last attach time */ 34262143Sarchie time_t shm_dtime; /* last detach time */ 34362143Sarchie time_t shm_ctime; /* last change time */ 34462143Sarchie void *shm_handle; /* internal handle for shm segment */ 34562143Sarchie}; 34662143Sarchie 34762143Sarchiestruct oshmctl_args { 34862143Sarchie int shmid; 34962143Sarchie int cmd; 35062143Sarchie struct oshmid_ds *ubuf; 35162143Sarchie}; 35262143Sarchie 35362143Sarchiestatic int 35462143Sarchieoshmctl(p, uap) 35562143Sarchie struct proc *p; 35662143Sarchie struct oshmctl_args *uap; 35762143Sarchie{ 35862143Sarchie#ifdef COMPAT_43 35962143Sarchie int error; 36062143Sarchie struct shmid_ds *shmseg; 36162143Sarchie struct oshmid_ds outbuf; 36262143Sarchie 36362143Sarchie if (!jail_sysvipc_allowed && p->p_prison != NULL) 36462143Sarchie return (ENOSYS); 36562143Sarchie 36662143Sarchie shmseg = shm_find_segment_by_shmid(uap->shmid); 36762143Sarchie if (shmseg == NULL) 36862143Sarchie return EINVAL; 36962143Sarchie switch (uap->cmd) { 37062143Sarchie case IPC_STAT: 37162143Sarchie error = ipcperm(p, &shmseg->shm_perm, IPC_R); 37262143Sarchie if (error) 37362143Sarchie return error; 37462143Sarchie outbuf.shm_perm = shmseg->shm_perm; 37562143Sarchie outbuf.shm_segsz = shmseg->shm_segsz; 37662143Sarchie outbuf.shm_cpid = shmseg->shm_cpid; 37762143Sarchie outbuf.shm_lpid = shmseg->shm_lpid; 37862143Sarchie outbuf.shm_nattch = shmseg->shm_nattch; 37962143Sarchie outbuf.shm_atime = shmseg->shm_atime; 38062143Sarchie outbuf.shm_dtime = shmseg->shm_dtime; 38162143Sarchie outbuf.shm_ctime = shmseg->shm_ctime; 38262143Sarchie outbuf.shm_handle = shmseg->shm_internal; 38362143Sarchie error = copyout((caddr_t)&outbuf, uap->ubuf, sizeof(outbuf)); 38462143Sarchie if (error) 38562143Sarchie return error; 38662143Sarchie break; 38762143Sarchie default: 38862143Sarchie /* XXX casting to (sy_call_t *) is bogus, as usual. */ 38962143Sarchie return ((sy_call_t *)shmctl)(p, uap); 39062143Sarchie } 39162143Sarchie return 0; 39262143Sarchie#else 39362143Sarchie return EINVAL; 39462143Sarchie#endif 39562143Sarchie} 39662143Sarchie 39762143Sarchie#ifndef _SYS_SYSPROTO_H_ 39862143Sarchiestruct shmctl_args { 39962143Sarchie int shmid; 40062143Sarchie int cmd; 40162143Sarchie struct shmid_ds *buf; 40262143Sarchie}; 40362143Sarchie#endif 40462143Sarchie 40562143Sarchieint 40662143Sarchieshmctl(p, uap) 40762143Sarchie struct proc *p; 40862143Sarchie struct shmctl_args *uap; 40962143Sarchie{ 41062143Sarchie int error; 41162143Sarchie struct shmid_ds inbuf; 41262143Sarchie struct shmid_ds *shmseg; 41362143Sarchie 41462143Sarchie if (!jail_sysvipc_allowed && p->p_prison != NULL) 41562143Sarchie return (ENOSYS); 41662143Sarchie 41762143Sarchie shmseg = shm_find_segment_by_shmid(uap->shmid); 41862143Sarchie if (shmseg == NULL) 41962143Sarchie return EINVAL; 42062143Sarchie switch (uap->cmd) { 42162143Sarchie case IPC_STAT: 42262143Sarchie error = ipcperm(p, &shmseg->shm_perm, IPC_R); 42362143Sarchie if (error) 42462143Sarchie return error; 42562143Sarchie error = copyout((caddr_t)shmseg, uap->buf, sizeof(inbuf)); 42662143Sarchie if (error) 42762143Sarchie return error; 42862143Sarchie break; 42962143Sarchie case IPC_SET: 43062143Sarchie error = ipcperm(p, &shmseg->shm_perm, IPC_M); 43162143Sarchie if (error) 43262143Sarchie return error; 43362143Sarchie error = copyin(uap->buf, (caddr_t)&inbuf, sizeof(inbuf)); 43462143Sarchie if (error) 43562143Sarchie return error; 43662143Sarchie shmseg->shm_perm.uid = inbuf.shm_perm.uid; 43762143Sarchie shmseg->shm_perm.gid = inbuf.shm_perm.gid; 43862143Sarchie shmseg->shm_perm.mode = 43962143Sarchie (shmseg->shm_perm.mode & ~ACCESSPERMS) | 44062143Sarchie (inbuf.shm_perm.mode & ACCESSPERMS); 44162143Sarchie shmseg->shm_ctime = time_second; 44262143Sarchie break; 44362143Sarchie case IPC_RMID: 44462143Sarchie error = ipcperm(p, &shmseg->shm_perm, IPC_M); 44562143Sarchie if (error) 44662143Sarchie return error; 44762143Sarchie shmseg->shm_perm.key = IPC_PRIVATE; 44862143Sarchie shmseg->shm_perm.mode |= SHMSEG_REMOVED; 44962143Sarchie if (shmseg->shm_nattch <= 0) { 45062143Sarchie shm_deallocate_segment(shmseg); 45162143Sarchie shm_last_free = IPCID_TO_IX(uap->shmid); 45262143Sarchie } 45362143Sarchie break; 45462143Sarchie#if 0 45562143Sarchie case SHM_LOCK: 45662143Sarchie case SHM_UNLOCK: 45762143Sarchie#endif 45862143Sarchie default: 45962143Sarchie return EINVAL; 46062143Sarchie } 46162143Sarchie return 0; 46262143Sarchie} 46362143Sarchie 46462143Sarchie#ifndef _SYS_SYSPROTO_H_ 46562143Sarchiestruct shmget_args { 46662143Sarchie key_t key; 46762143Sarchie size_t size; 46862143Sarchie int shmflg; 46962143Sarchie}; 47062143Sarchie#endif 47162143Sarchie 47262143Sarchiestatic int 47362143Sarchieshmget_existing(p, uap, mode, segnum) 47462143Sarchie struct proc *p; 47562143Sarchie struct shmget_args *uap; 47662143Sarchie int mode; 47762143Sarchie int segnum; 47862143Sarchie{ 47962143Sarchie struct shmid_ds *shmseg; 48062143Sarchie int error; 48162143Sarchie 48262143Sarchie shmseg = &shmsegs[segnum]; 48362143Sarchie if (shmseg->shm_perm.mode & SHMSEG_REMOVED) { 48462143Sarchie /* 48562143Sarchie * This segment is in the process of being allocated. Wait 48662143Sarchie * until it's done, and look the key up again (in case the 48762143Sarchie * allocation failed or it was freed). 48862143Sarchie */ 48962143Sarchie shmseg->shm_perm.mode |= SHMSEG_WANTED; 49062143Sarchie error = tsleep((caddr_t)shmseg, PLOCK | PCATCH, "shmget", 0); 49162143Sarchie if (error) 49262143Sarchie return error; 49362143Sarchie return EAGAIN; 49462143Sarchie } 49562143Sarchie if ((uap->shmflg & (IPC_CREAT | IPC_EXCL)) == (IPC_CREAT | IPC_EXCL)) 49662143Sarchie return EEXIST; 49762143Sarchie error = ipcperm(p, &shmseg->shm_perm, mode); 49862143Sarchie if (error) 49962143Sarchie return error; 50062143Sarchie if (uap->size && uap->size > shmseg->shm_segsz) 50162143Sarchie return EINVAL; 50262143Sarchie p->p_retval[0] = IXSEQ_TO_IPCID(segnum, shmseg->shm_perm); 50362143Sarchie return 0; 50462143Sarchie} 50562143Sarchie 50662143Sarchiestatic int 50762143Sarchieshmget_allocate_segment(p, uap, mode) 50862143Sarchie struct proc *p; 50962143Sarchie struct shmget_args *uap; 51062143Sarchie int mode; 51162143Sarchie{ 51262143Sarchie int i, segnum, shmid, size; 51362143Sarchie struct ucred *cred = p->p_ucred; 51462143Sarchie struct shmid_ds *shmseg; 51562143Sarchie struct shm_handle *shm_handle; 51662143Sarchie 51762143Sarchie if (uap->size < shminfo.shmmin || uap->size > shminfo.shmmax) 51862143Sarchie return EINVAL; 51962143Sarchie if (shm_nused >= shminfo.shmmni) /* any shmids left? */ 52062143Sarchie return ENOSPC; 52162143Sarchie size = round_page(uap->size); 52262143Sarchie if (shm_committed + btoc(size) > shminfo.shmall) 52362143Sarchie return ENOMEM; 52462143Sarchie if (shm_last_free < 0) { 52562143Sarchie shmrealloc(); /* maybe expand the shmsegs[] array */ 52662143Sarchie for (i = 0; i < shmalloced; i++) 52762143Sarchie if (shmsegs[i].shm_perm.mode & SHMSEG_FREE) 52862143Sarchie break; 52962143Sarchie if (i == shmalloced) 53062143Sarchie return ENOSPC; 53162143Sarchie segnum = i; 53262143Sarchie } else { 53362143Sarchie segnum = shm_last_free; 53462143Sarchie shm_last_free = -1; 53562143Sarchie } 53662143Sarchie shmseg = &shmsegs[segnum]; 53762143Sarchie /* 53862143Sarchie * In case we sleep in malloc(), mark the segment present but deleted 53962143Sarchie * so that noone else tries to create the same key. 54062143Sarchie */ 54162143Sarchie shmseg->shm_perm.mode = SHMSEG_ALLOCATED | SHMSEG_REMOVED; 54262143Sarchie shmseg->shm_perm.key = uap->key; 54362143Sarchie shmseg->shm_perm.seq = (shmseg->shm_perm.seq + 1) & 0x7fff; 54462143Sarchie shm_handle = (struct shm_handle *) 54562143Sarchie malloc(sizeof(struct shm_handle), M_SHM, M_WAITOK); 54662143Sarchie shmid = IXSEQ_TO_IPCID(segnum, shmseg->shm_perm); 54762143Sarchie 54862143Sarchie /* 54962143Sarchie * We make sure that we have allocated a pager before we need 55062143Sarchie * to. 55162143Sarchie */ 55262143Sarchie if (shm_use_phys) { 55362143Sarchie shm_handle->shm_object = 55462143Sarchie vm_pager_allocate(OBJT_PHYS, 0, size, VM_PROT_DEFAULT, 0); 55562143Sarchie } else { 55662143Sarchie shm_handle->shm_object = 55762143Sarchie vm_pager_allocate(OBJT_SWAP, 0, size, VM_PROT_DEFAULT, 0); 55862143Sarchie } 55962143Sarchie vm_object_clear_flag(shm_handle->shm_object, OBJ_ONEMAPPING); 56062143Sarchie vm_object_set_flag(shm_handle->shm_object, OBJ_NOSPLIT); 56162143Sarchie 56262143Sarchie shmseg->shm_internal = shm_handle; 56362143Sarchie shmseg->shm_perm.cuid = shmseg->shm_perm.uid = cred->cr_uid; 56462143Sarchie shmseg->shm_perm.cgid = shmseg->shm_perm.gid = cred->cr_gid; 56562143Sarchie shmseg->shm_perm.mode = (shmseg->shm_perm.mode & SHMSEG_WANTED) | 56662143Sarchie (mode & ACCESSPERMS) | SHMSEG_ALLOCATED; 56762143Sarchie shmseg->shm_segsz = uap->size; 56862143Sarchie shmseg->shm_cpid = p->p_pid; 56962143Sarchie shmseg->shm_lpid = shmseg->shm_nattch = 0; 57062143Sarchie shmseg->shm_atime = shmseg->shm_dtime = 0; 57162143Sarchie shmseg->shm_ctime = time_second; 57262143Sarchie shm_committed += btoc(size); 57362143Sarchie shm_nused++; 57462143Sarchie if (shmseg->shm_perm.mode & SHMSEG_WANTED) { 57562143Sarchie /* 57662143Sarchie * Somebody else wanted this key while we were asleep. Wake 57762143Sarchie * them up now. 57862143Sarchie */ 57962143Sarchie shmseg->shm_perm.mode &= ~SHMSEG_WANTED; 58062143Sarchie wakeup((caddr_t)shmseg); 58162143Sarchie } 58262143Sarchie p->p_retval[0] = shmid; 58362143Sarchie return 0; 58462143Sarchie} 58562143Sarchie 58662143Sarchieint 58762143Sarchieshmget(p, uap) 58862143Sarchie struct proc *p; 58962143Sarchie struct shmget_args *uap; 59062143Sarchie{ 59162143Sarchie int segnum, mode, error; 59262143Sarchie 59362143Sarchie if (!jail_sysvipc_allowed && p->p_prison != NULL) 59462143Sarchie return (ENOSYS); 59562143Sarchie 59662143Sarchie mode = uap->shmflg & ACCESSPERMS; 59762143Sarchie if (uap->key != IPC_PRIVATE) { 59862143Sarchie again: 59962143Sarchie segnum = shm_find_segment_by_key(uap->key); 60062143Sarchie if (segnum >= 0) { 60162143Sarchie error = shmget_existing(p, uap, mode, segnum); 60262143Sarchie if (error == EAGAIN) 60362143Sarchie goto again; 60462143Sarchie return error; 60562143Sarchie } 60662143Sarchie if ((uap->shmflg & IPC_CREAT) == 0) 60762143Sarchie return ENOENT; 60862143Sarchie } 60962143Sarchie return shmget_allocate_segment(p, uap, mode); 61062143Sarchie} 61162143Sarchie 61262143Sarchieint 61362143Sarchieshmsys(p, uap) 61462143Sarchie struct proc *p; 61562143Sarchie /* XXX actually varargs. */ 61662143Sarchie struct shmsys_args /* { 61762143Sarchie u_int which; 61862143Sarchie int a2; 61962143Sarchie int a3; 62062143Sarchie int a4; 62162143Sarchie } */ *uap; 62262143Sarchie{ 62362143Sarchie 62462143Sarchie if (!jail_sysvipc_allowed && p->p_prison != NULL) 62562143Sarchie return (ENOSYS); 62662143Sarchie 62762143Sarchie if (uap->which >= sizeof(shmcalls)/sizeof(shmcalls[0])) 62862143Sarchie return EINVAL; 62962143Sarchie return ((*shmcalls[uap->which])(p, &uap->a2)); 63062143Sarchie} 63162143Sarchie 63262143Sarchiestatic void 63362143Sarchieshmfork_myhook(p1, p2) 634 struct proc *p1, *p2; 635{ 636 struct shmmap_state *shmmap_s; 637 size_t size; 638 int i; 639 640 size = shminfo.shmseg * sizeof(struct shmmap_state); 641 shmmap_s = malloc(size, M_SHM, M_WAITOK); 642 bcopy((caddr_t)p1->p_vmspace->vm_shm, (caddr_t)shmmap_s, size); 643 p2->p_vmspace->vm_shm = (caddr_t)shmmap_s; 644 for (i = 0; i < shminfo.shmseg; i++, shmmap_s++) 645 if (shmmap_s->shmid != -1) 646 shmsegs[IPCID_TO_IX(shmmap_s->shmid)].shm_nattch++; 647} 648 649static void 650shmexit_myhook(p) 651 struct proc *p; 652{ 653 struct shmmap_state *shmmap_s; 654 int i; 655 656 shmmap_s = (struct shmmap_state *)p->p_vmspace->vm_shm; 657 for (i = 0; i < shminfo.shmseg; i++, shmmap_s++) 658 if (shmmap_s->shmid != -1) 659 shm_delete_mapping(p, shmmap_s); 660 free((caddr_t)p->p_vmspace->vm_shm, M_SHM); 661 p->p_vmspace->vm_shm = NULL; 662} 663 664static void 665shmrealloc(void) 666{ 667 int i; 668 struct shmid_ds *newsegs; 669 670 if (shmalloced >= shminfo.shmmni) 671 return; 672 673 newsegs = malloc(shminfo.shmmni * sizeof(*newsegs), M_SHM, M_WAITOK); 674 if (newsegs == NULL) 675 return; 676 for (i = 0; i < shmalloced; i++) 677 bcopy(&shmsegs[i], &newsegs[i], sizeof(newsegs[0])); 678 for (; i < shminfo.shmmni; i++) { 679 shmsegs[i].shm_perm.mode = SHMSEG_FREE; 680 shmsegs[i].shm_perm.seq = 0; 681 } 682 free(shmsegs, M_SHM); 683 shmsegs = newsegs; 684 shmalloced = shminfo.shmmni; 685} 686 687static void 688shminit() 689{ 690 int i; 691 692 shmalloced = shminfo.shmmni; 693 shmsegs = malloc(shmalloced * sizeof(shmsegs[0]), M_SHM, M_WAITOK); 694 if (shmsegs == NULL) 695 panic("cannot allocate initial memory for sysvshm"); 696 for (i = 0; i < shmalloced; i++) { 697 shmsegs[i].shm_perm.mode = SHMSEG_FREE; 698 shmsegs[i].shm_perm.seq = 0; 699 } 700 shm_last_free = 0; 701 shm_nused = 0; 702 shm_committed = 0; 703 shmexit_hook = &shmexit_myhook; 704 shmfork_hook = &shmfork_myhook; 705} 706 707static int 708shmunload() 709{ 710 711 if (shm_nused > 0) 712 return (EBUSY); 713 714 free(shmsegs, M_SHM); 715 shmexit_hook = NULL; 716 shmfork_hook = NULL; 717 return (0); 718} 719 720static int 721sysvshm_modload(struct module *module, int cmd, void *arg) 722{ 723 int error = 0; 724 725 switch (cmd) { 726 case MOD_LOAD: 727 shminit(); 728 break; 729 case MOD_UNLOAD: 730 error = shmunload(); 731 break; 732 case MOD_SHUTDOWN: 733 break; 734 default: 735 error = EINVAL; 736 break; 737 } 738 return (error); 739} 740 741static moduledata_t sysvshm_moduledata = { 742 "sysvshm_mod", 743 &sysvshm_modload, 744 NULL 745}; 746 747SYSCALL_MODULE_HELPER(shmsys, 4); 748SYSCALL_MODULE_HELPER(shmat, 3); 749SYSCALL_MODULE_HELPER(shmctl, 3); 750SYSCALL_MODULE_HELPER(shmdt, 1); 751SYSCALL_MODULE_HELPER(shmget, 3); 752 753DECLARE_MODULE(sysvshm_mod, sysvshm_moduledata, 754 SI_SUB_SYSV_SHM, SI_ORDER_FIRST); 755