sysv_shm.c revision 69449
150477Speter/* $FreeBSD: head/sys/kern/sysv_shm.c 69449 2000-12-01 08:57:47Z alfred $ */ 22729Sdfr/* $NetBSD: sysv_shm.c,v 1.23 1994/07/04 23:25:12 glass Exp $ */ 32729Sdfr 42729Sdfr/* 52729Sdfr * Copyright (c) 1994 Adam Glass and Charles Hannum. All rights reserved. 62729Sdfr * 72729Sdfr * Redistribution and use in source and binary forms, with or without 82729Sdfr * modification, are permitted provided that the following conditions 92729Sdfr * are met: 102729Sdfr * 1. Redistributions of source code must retain the above copyright 112729Sdfr * notice, this list of conditions and the following disclaimer. 122729Sdfr * 2. Redistributions in binary form must reproduce the above copyright 132729Sdfr * notice, this list of conditions and the following disclaimer in the 142729Sdfr * documentation and/or other materials provided with the distribution. 152729Sdfr * 3. All advertising materials mentioning features or use of this software 162729Sdfr * must display the following acknowledgement: 172729Sdfr * This product includes software developed by Adam Glass and Charles 182729Sdfr * Hannum. 192729Sdfr * 4. The names of the authors may not be used to endorse or promote products 202729Sdfr * derived from this software without specific prior written permission. 212729Sdfr * 222729Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 232729Sdfr * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 242729Sdfr * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 252729Sdfr * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, 262729Sdfr * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 272729Sdfr * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 282729Sdfr * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 292729Sdfr * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 302729Sdfr * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 312729Sdfr * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 322729Sdfr */ 332729Sdfr 3431778Seivind#include "opt_compat.h" 3520821Sjoerg#include "opt_rlimit.h" 3658820Speter#include "opt_sysvipc.h" 3713255Swollman 382729Sdfr#include <sys/param.h> 3911626Sbde#include <sys/systm.h> 4011626Sbde#include <sys/sysproto.h> 412729Sdfr#include <sys/kernel.h> 4258820Speter#include <sys/sysctl.h> 432729Sdfr#include <sys/shm.h> 442729Sdfr#include <sys/proc.h> 452729Sdfr#include <sys/malloc.h> 462729Sdfr#include <sys/mman.h> 472729Sdfr#include <sys/stat.h> 4869449Salfred#include <sys/syscall.h> 4911626Sbde#include <sys/sysent.h> 5068024Srwatson#include <sys/jail.h> 512729Sdfr 522729Sdfr#include <vm/vm.h> 5312662Sdg#include <vm/vm_param.h> 5422521Sdyson#include <sys/lock.h> 5512662Sdg#include <vm/pmap.h> 5618098Sdyson#include <vm/vm_object.h> 572729Sdfr#include <vm/vm_map.h> 5842957Sdillon#include <vm/vm_page.h> 5918199Sdyson#include <vm/vm_pager.h> 602729Sdfr 6130354Sphkstatic MALLOC_DEFINE(M_SHM, "shm", "SVID compatible shared memory segments"); 6230309Sphk 6311626Sbdestruct oshmctl_args; 6430994Sphkstatic int oshmctl __P((struct proc *p, struct oshmctl_args *uap)); 6558820Speter 6630994Sphkstatic int shmget_allocate_segment __P((struct proc *p, struct shmget_args *uap, int mode)); 6730994Sphkstatic int shmget_existing __P((struct proc *p, struct shmget_args *uap, int mode, int segnum)); 688876Srgrimes 6911626Sbde/* XXX casting to (sy_call_t *) is bogus, as usual. */ 7033181Seivindstatic sy_call_t *shmcalls[] = { 7111626Sbde (sy_call_t *)shmat, (sy_call_t *)oshmctl, 7211626Sbde (sy_call_t *)shmdt, (sy_call_t *)shmget, 7311626Sbde (sy_call_t *)shmctl 7411626Sbde}; 7511626Sbde 762729Sdfr#define SHMSEG_FREE 0x0200 772729Sdfr#define SHMSEG_REMOVED 0x0400 782729Sdfr#define SHMSEG_ALLOCATED 0x0800 792729Sdfr#define SHMSEG_WANTED 0x1000 802729Sdfr 8158820Speterstatic int shm_last_free, shm_nused, shm_committed, shmalloced; 8258820Speterstatic struct shmid_ds *shmsegs; 832729Sdfr 842729Sdfrstruct shm_handle { 8518098Sdyson /* vm_offset_t kva; */ 8618098Sdyson vm_object_t shm_object; 872729Sdfr}; 882729Sdfr 892729Sdfrstruct shmmap_state { 902729Sdfr vm_offset_t va; 912729Sdfr int shmid; 922729Sdfr}; 932729Sdfr 942729Sdfrstatic void shm_deallocate_segment __P((struct shmid_ds *)); 952729Sdfrstatic int shm_find_segment_by_key __P((key_t)); 962729Sdfrstatic struct shmid_ds *shm_find_segment_by_shmid __P((int)); 972729Sdfrstatic int shm_delete_mapping __P((struct proc *, struct shmmap_state *)); 9858820Speterstatic void shmrealloc __P((void)); 9969449Salfredstatic void shminit __P((void)); 10069449Salfredstatic int sysvshm_modload __P((struct module *, int, void *)); 10169449Salfredstatic int shmunload __P((void)); 10269449Salfredstatic void shmexit_myhook __P((struct proc *p)); 10369449Salfredstatic void shmfork_myhook __P((struct proc *p1, struct proc *p2)); 1042729Sdfr 10558820Speter/* 10658820Speter * Tuneable values 10758820Speter */ 10858820Speter#ifndef SHMMAXPGS 10958820Speter#define SHMMAXPGS 1024 /* XXX increase this, it's not in kva! */ 11058820Speter#endif 11158820Speter#ifndef SHMMAX 11258820Speter#define SHMMAX (SHMMAXPGS*PAGE_SIZE) 11358820Speter#endif 11458820Speter#ifndef SHMMIN 11558820Speter#define SHMMIN 1 11658820Speter#endif 11758820Speter#ifndef SHMMNI 11858820Speter#define SHMMNI 96 11958820Speter#endif 12058820Speter#ifndef SHMSEG 12158820Speter#define SHMSEG 64 12258820Speter#endif 12358820Speter#ifndef SHMALL 12458820Speter#define SHMALL (SHMMAXPGS) 12558820Speter#endif 12658820Speter 12758820Speterstruct shminfo shminfo = { 12858820Speter SHMMAX, 12958820Speter SHMMIN, 13058820Speter SHMMNI, 13158820Speter SHMSEG, 13258820Speter SHMALL 13358820Speter}; 13458820Speter 13561081Sdillonstatic int shm_use_phys; 13661081Sdillon 13758820SpeterSYSCTL_DECL(_kern_ipc); 13858820SpeterSYSCTL_INT(_kern_ipc, OID_AUTO, shmmax, CTLFLAG_RW, &shminfo.shmmax, 0, ""); 13958820SpeterSYSCTL_INT(_kern_ipc, OID_AUTO, shmmin, CTLFLAG_RW, &shminfo.shmmin, 0, ""); 14058820SpeterSYSCTL_INT(_kern_ipc, OID_AUTO, shmmni, CTLFLAG_RD, &shminfo.shmmni, 0, ""); 14158820SpeterSYSCTL_INT(_kern_ipc, OID_AUTO, shmseg, CTLFLAG_RW, &shminfo.shmseg, 0, ""); 14258820SpeterSYSCTL_INT(_kern_ipc, OID_AUTO, shmall, CTLFLAG_RW, &shminfo.shmall, 0, ""); 14361081SdillonSYSCTL_INT(_kern_ipc, OID_AUTO, shm_use_phys, CTLFLAG_RW, &shm_use_phys, 0, ""); 14458820Speter 1452729Sdfrstatic int 1462729Sdfrshm_find_segment_by_key(key) 1472729Sdfr key_t key; 1482729Sdfr{ 1492729Sdfr int i; 1502729Sdfr 15158820Speter for (i = 0; i < shmalloced; i++) 1522729Sdfr if ((shmsegs[i].shm_perm.mode & SHMSEG_ALLOCATED) && 1532729Sdfr shmsegs[i].shm_perm.key == key) 1542729Sdfr return i; 1552729Sdfr return -1; 1562729Sdfr} 1572729Sdfr 1582729Sdfrstatic struct shmid_ds * 1592729Sdfrshm_find_segment_by_shmid(shmid) 1602729Sdfr int shmid; 1612729Sdfr{ 1622729Sdfr int segnum; 1632729Sdfr struct shmid_ds *shmseg; 1642729Sdfr 1652729Sdfr segnum = IPCID_TO_IX(shmid); 16658820Speter if (segnum < 0 || segnum >= shmalloced) 1672729Sdfr return NULL; 1682729Sdfr shmseg = &shmsegs[segnum]; 1692729Sdfr if ((shmseg->shm_perm.mode & (SHMSEG_ALLOCATED | SHMSEG_REMOVED)) 1702729Sdfr != SHMSEG_ALLOCATED || 1712729Sdfr shmseg->shm_perm.seq != IPCID_TO_SEQ(shmid)) 1722729Sdfr return NULL; 1732729Sdfr return shmseg; 1742729Sdfr} 1752729Sdfr 1762729Sdfrstatic void 1772729Sdfrshm_deallocate_segment(shmseg) 1782729Sdfr struct shmid_ds *shmseg; 1792729Sdfr{ 1802729Sdfr struct shm_handle *shm_handle; 1812729Sdfr size_t size; 1822729Sdfr 1832729Sdfr shm_handle = shmseg->shm_internal; 18418098Sdyson vm_object_deallocate(shm_handle->shm_object); 1852729Sdfr free((caddr_t)shm_handle, M_SHM); 1862729Sdfr shmseg->shm_internal = NULL; 18718098Sdyson size = round_page(shmseg->shm_segsz); 1882729Sdfr shm_committed -= btoc(size); 1892729Sdfr shm_nused--; 1902729Sdfr shmseg->shm_perm.mode = SHMSEG_FREE; 1912729Sdfr} 1922729Sdfr 1932729Sdfrstatic int 1942729Sdfrshm_delete_mapping(p, shmmap_s) 1952729Sdfr struct proc *p; 1962729Sdfr struct shmmap_state *shmmap_s; 1972729Sdfr{ 1982729Sdfr struct shmid_ds *shmseg; 1992729Sdfr int segnum, result; 2002729Sdfr size_t size; 2018876Srgrimes 2022729Sdfr segnum = IPCID_TO_IX(shmmap_s->shmid); 2032729Sdfr shmseg = &shmsegs[segnum]; 20415583Sphk size = round_page(shmseg->shm_segsz); 2056579Sdg result = vm_map_remove(&p->p_vmspace->vm_map, shmmap_s->va, shmmap_s->va + size); 2062729Sdfr if (result != KERN_SUCCESS) 2072729Sdfr return EINVAL; 2082729Sdfr shmmap_s->shmid = -1; 20934961Sphk shmseg->shm_dtime = time_second; 2102729Sdfr if ((--shmseg->shm_nattch <= 0) && 2112729Sdfr (shmseg->shm_perm.mode & SHMSEG_REMOVED)) { 2122729Sdfr shm_deallocate_segment(shmseg); 2132729Sdfr shm_last_free = segnum; 2142729Sdfr } 2152729Sdfr return 0; 2162729Sdfr} 2172729Sdfr 21812866Speter#ifndef _SYS_SYSPROTO_H_ 2192729Sdfrstruct shmdt_args { 2202729Sdfr void *shmaddr; 2212729Sdfr}; 22212866Speter#endif 22312866Speter 2242729Sdfrint 22530994Sphkshmdt(p, uap) 2262729Sdfr struct proc *p; 2272729Sdfr struct shmdt_args *uap; 2282729Sdfr{ 2292729Sdfr struct shmmap_state *shmmap_s; 2302729Sdfr int i; 2312729Sdfr 23268024Srwatson if (!jail_sysvipc_allowed && p->p_prison != NULL) 23368024Srwatson return (ENOSYS); 23468024Srwatson 2352729Sdfr shmmap_s = (struct shmmap_state *)p->p_vmspace->vm_shm; 23612613Sjkh if (shmmap_s == NULL) 23712613Sjkh return EINVAL; 2382729Sdfr for (i = 0; i < shminfo.shmseg; i++, shmmap_s++) 2392729Sdfr if (shmmap_s->shmid != -1 && 2402729Sdfr shmmap_s->va == (vm_offset_t)uap->shmaddr) 2412729Sdfr break; 2422729Sdfr if (i == shminfo.shmseg) 2432729Sdfr return EINVAL; 2442729Sdfr return shm_delete_mapping(p, shmmap_s); 2452729Sdfr} 2462729Sdfr 24712866Speter#ifndef _SYS_SYSPROTO_H_ 2482729Sdfrstruct shmat_args { 2492729Sdfr int shmid; 2502729Sdfr void *shmaddr; 2512729Sdfr int shmflg; 2522729Sdfr}; 25312866Speter#endif 25412866Speter 2552729Sdfrint 25630994Sphkshmat(p, uap) 2572729Sdfr struct proc *p; 2582729Sdfr struct shmat_args *uap; 2592729Sdfr{ 2602729Sdfr int error, i, flags; 2612729Sdfr struct shmid_ds *shmseg; 2622729Sdfr struct shmmap_state *shmmap_s = NULL; 26318098Sdyson struct shm_handle *shm_handle; 2642729Sdfr vm_offset_t attach_va; 2652729Sdfr vm_prot_t prot; 2662729Sdfr vm_size_t size; 26718098Sdyson int rv; 2682729Sdfr 26968024Srwatson if (!jail_sysvipc_allowed && p->p_prison != NULL) 27068024Srwatson return (ENOSYS); 27168024Srwatson 2722729Sdfr shmmap_s = (struct shmmap_state *)p->p_vmspace->vm_shm; 2732729Sdfr if (shmmap_s == NULL) { 2742729Sdfr size = shminfo.shmseg * sizeof(struct shmmap_state); 2752729Sdfr shmmap_s = malloc(size, M_SHM, M_WAITOK); 2762729Sdfr for (i = 0; i < shminfo.shmseg; i++) 2772729Sdfr shmmap_s[i].shmid = -1; 2782729Sdfr p->p_vmspace->vm_shm = (caddr_t)shmmap_s; 2792729Sdfr } 2802729Sdfr shmseg = shm_find_segment_by_shmid(uap->shmid); 2812729Sdfr if (shmseg == NULL) 2822729Sdfr return EINVAL; 28346116Sphk error = ipcperm(p, &shmseg->shm_perm, 2843308Sphk (uap->shmflg & SHM_RDONLY) ? IPC_R : IPC_R|IPC_W); 2853308Sphk if (error) 2862729Sdfr return error; 2872729Sdfr for (i = 0; i < shminfo.shmseg; i++) { 2882729Sdfr if (shmmap_s->shmid == -1) 2892729Sdfr break; 2902729Sdfr shmmap_s++; 2912729Sdfr } 2922729Sdfr if (i >= shminfo.shmseg) 2932729Sdfr return EMFILE; 29415583Sphk size = round_page(shmseg->shm_segsz); 29557884Salc#ifdef VM_PROT_READ_IS_EXEC 29657884Salc prot = VM_PROT_READ | VM_PROT_EXECUTE; 29757884Salc#else 2982729Sdfr prot = VM_PROT_READ; 29957884Salc#endif 3002729Sdfr if ((uap->shmflg & SHM_RDONLY) == 0) 3012729Sdfr prot |= VM_PROT_WRITE; 3022729Sdfr flags = MAP_ANON | MAP_SHARED; 3032729Sdfr if (uap->shmaddr) { 3042729Sdfr flags |= MAP_FIXED; 3058876Srgrimes if (uap->shmflg & SHM_RND) 3062729Sdfr attach_va = (vm_offset_t)uap->shmaddr & ~(SHMLBA-1); 3072729Sdfr else if (((vm_offset_t)uap->shmaddr & (SHMLBA-1)) == 0) 3082729Sdfr attach_va = (vm_offset_t)uap->shmaddr; 3092729Sdfr else 3102729Sdfr return EINVAL; 3112729Sdfr } else { 31218098Sdyson /* This is just a hint to vm_map_find() about where to put it. */ 31340286Sdg attach_va = round_page((vm_offset_t)p->p_vmspace->vm_taddr + MAXTSIZ + MAXDSIZ); 3142729Sdfr } 31518098Sdyson 31618098Sdyson shm_handle = shmseg->shm_internal; 31718098Sdyson vm_object_reference(shm_handle->shm_object); 31818098Sdyson rv = vm_map_find(&p->p_vmspace->vm_map, shm_handle->shm_object, 31918098Sdyson 0, &attach_va, size, (flags & MAP_FIXED)?0:1, prot, prot, 0); 32018098Sdyson if (rv != KERN_SUCCESS) { 32118098Sdyson return ENOMEM; 32218098Sdyson } 32318231Sdyson vm_map_inherit(&p->p_vmspace->vm_map, 32418231Sdyson attach_va, attach_va + size, VM_INHERIT_SHARE); 32518231Sdyson 3262729Sdfr shmmap_s->va = attach_va; 3272729Sdfr shmmap_s->shmid = uap->shmid; 3282729Sdfr shmseg->shm_lpid = p->p_pid; 32934961Sphk shmseg->shm_atime = time_second; 3302729Sdfr shmseg->shm_nattch++; 33130994Sphk p->p_retval[0] = attach_va; 3322729Sdfr return 0; 3332729Sdfr} 3342729Sdfr 3352828Sdfrstruct oshmid_ds { 3362828Sdfr struct ipc_perm shm_perm; /* operation perms */ 3372828Sdfr int shm_segsz; /* size of segment (bytes) */ 3382828Sdfr ushort shm_cpid; /* pid, creator */ 3392828Sdfr ushort shm_lpid; /* pid, last operation */ 3402828Sdfr short shm_nattch; /* no. of current attaches */ 3412828Sdfr time_t shm_atime; /* last attach time */ 3422828Sdfr time_t shm_dtime; /* last detach time */ 3432828Sdfr time_t shm_ctime; /* last change time */ 3442828Sdfr void *shm_handle; /* internal handle for shm segment */ 3452828Sdfr}; 3462828Sdfr 3472828Sdfrstruct oshmctl_args { 3482828Sdfr int shmid; 3492828Sdfr int cmd; 3502828Sdfr struct oshmid_ds *ubuf; 3512828Sdfr}; 3522828Sdfr 35312819Sphkstatic int 35430994Sphkoshmctl(p, uap) 3552828Sdfr struct proc *p; 3562828Sdfr struct oshmctl_args *uap; 3572828Sdfr{ 3582828Sdfr#ifdef COMPAT_43 3593308Sphk int error; 3602828Sdfr struct shmid_ds *shmseg; 3612828Sdfr struct oshmid_ds outbuf; 3622828Sdfr 36368024Srwatson if (!jail_sysvipc_allowed && p->p_prison != NULL) 36468024Srwatson return (ENOSYS); 36568024Srwatson 3662828Sdfr shmseg = shm_find_segment_by_shmid(uap->shmid); 3672828Sdfr if (shmseg == NULL) 3682828Sdfr return EINVAL; 3692828Sdfr switch (uap->cmd) { 3702828Sdfr case IPC_STAT: 37146116Sphk error = ipcperm(p, &shmseg->shm_perm, IPC_R); 3723308Sphk if (error) 3732828Sdfr return error; 3742828Sdfr outbuf.shm_perm = shmseg->shm_perm; 3752828Sdfr outbuf.shm_segsz = shmseg->shm_segsz; 3762828Sdfr outbuf.shm_cpid = shmseg->shm_cpid; 3772828Sdfr outbuf.shm_lpid = shmseg->shm_lpid; 3782828Sdfr outbuf.shm_nattch = shmseg->shm_nattch; 3792828Sdfr outbuf.shm_atime = shmseg->shm_atime; 3802828Sdfr outbuf.shm_dtime = shmseg->shm_dtime; 3812828Sdfr outbuf.shm_ctime = shmseg->shm_ctime; 3822828Sdfr outbuf.shm_handle = shmseg->shm_internal; 3833308Sphk error = copyout((caddr_t)&outbuf, uap->ubuf, sizeof(outbuf)); 3843308Sphk if (error) 3852828Sdfr return error; 3862828Sdfr break; 3872828Sdfr default: 38811626Sbde /* XXX casting to (sy_call_t *) is bogus, as usual. */ 38930994Sphk return ((sy_call_t *)shmctl)(p, uap); 3902828Sdfr } 3912828Sdfr return 0; 3922828Sdfr#else 3932828Sdfr return EINVAL; 3942828Sdfr#endif 3958876Srgrimes} 3962828Sdfr 39712866Speter#ifndef _SYS_SYSPROTO_H_ 3982729Sdfrstruct shmctl_args { 3992729Sdfr int shmid; 4002729Sdfr int cmd; 40112866Speter struct shmid_ds *buf; 4022729Sdfr}; 40312866Speter#endif 40412866Speter 4052729Sdfrint 40630994Sphkshmctl(p, uap) 4072729Sdfr struct proc *p; 4082729Sdfr struct shmctl_args *uap; 4092729Sdfr{ 4103308Sphk int error; 4112729Sdfr struct shmid_ds inbuf; 4122729Sdfr struct shmid_ds *shmseg; 4132729Sdfr 41468024Srwatson if (!jail_sysvipc_allowed && p->p_prison != NULL) 41568024Srwatson return (ENOSYS); 41668024Srwatson 4172729Sdfr shmseg = shm_find_segment_by_shmid(uap->shmid); 4182729Sdfr if (shmseg == NULL) 4192729Sdfr return EINVAL; 4202729Sdfr switch (uap->cmd) { 4212729Sdfr case IPC_STAT: 42246116Sphk error = ipcperm(p, &shmseg->shm_perm, IPC_R); 4233308Sphk if (error) 4242729Sdfr return error; 42512866Speter error = copyout((caddr_t)shmseg, uap->buf, sizeof(inbuf)); 4263308Sphk if (error) 4272729Sdfr return error; 4282729Sdfr break; 4292729Sdfr case IPC_SET: 43046116Sphk error = ipcperm(p, &shmseg->shm_perm, IPC_M); 4313308Sphk if (error) 4322729Sdfr return error; 43312866Speter error = copyin(uap->buf, (caddr_t)&inbuf, sizeof(inbuf)); 4343308Sphk if (error) 4352729Sdfr return error; 4362729Sdfr shmseg->shm_perm.uid = inbuf.shm_perm.uid; 4372729Sdfr shmseg->shm_perm.gid = inbuf.shm_perm.gid; 4382729Sdfr shmseg->shm_perm.mode = 4392729Sdfr (shmseg->shm_perm.mode & ~ACCESSPERMS) | 4402729Sdfr (inbuf.shm_perm.mode & ACCESSPERMS); 44134961Sphk shmseg->shm_ctime = time_second; 4422729Sdfr break; 4432729Sdfr case IPC_RMID: 44446116Sphk error = ipcperm(p, &shmseg->shm_perm, IPC_M); 4453308Sphk if (error) 4462729Sdfr return error; 4472729Sdfr shmseg->shm_perm.key = IPC_PRIVATE; 4482729Sdfr shmseg->shm_perm.mode |= SHMSEG_REMOVED; 4492729Sdfr if (shmseg->shm_nattch <= 0) { 4502729Sdfr shm_deallocate_segment(shmseg); 4512729Sdfr shm_last_free = IPCID_TO_IX(uap->shmid); 4522729Sdfr } 4532729Sdfr break; 4542729Sdfr#if 0 4552729Sdfr case SHM_LOCK: 4562729Sdfr case SHM_UNLOCK: 4572729Sdfr#endif 4582729Sdfr default: 4592729Sdfr return EINVAL; 4602729Sdfr } 4612729Sdfr return 0; 4622729Sdfr} 4632729Sdfr 46412866Speter#ifndef _SYS_SYSPROTO_H_ 4652729Sdfrstruct shmget_args { 4662729Sdfr key_t key; 4672729Sdfr size_t size; 4682729Sdfr int shmflg; 4692729Sdfr}; 47012866Speter#endif 47112866Speter 4722729Sdfrstatic int 47330994Sphkshmget_existing(p, uap, mode, segnum) 4742729Sdfr struct proc *p; 4752729Sdfr struct shmget_args *uap; 4762729Sdfr int mode; 4772729Sdfr int segnum; 4782729Sdfr{ 4792729Sdfr struct shmid_ds *shmseg; 4802729Sdfr int error; 4812729Sdfr 4822729Sdfr shmseg = &shmsegs[segnum]; 4832729Sdfr if (shmseg->shm_perm.mode & SHMSEG_REMOVED) { 4842729Sdfr /* 4852729Sdfr * This segment is in the process of being allocated. Wait 4862729Sdfr * until it's done, and look the key up again (in case the 4872729Sdfr * allocation failed or it was freed). 4882729Sdfr */ 4892729Sdfr shmseg->shm_perm.mode |= SHMSEG_WANTED; 4903308Sphk error = tsleep((caddr_t)shmseg, PLOCK | PCATCH, "shmget", 0); 4913308Sphk if (error) 4922729Sdfr return error; 4932729Sdfr return EAGAIN; 4942729Sdfr } 49548039Salc if ((uap->shmflg & (IPC_CREAT | IPC_EXCL)) == (IPC_CREAT | IPC_EXCL)) 49648039Salc return EEXIST; 49746116Sphk error = ipcperm(p, &shmseg->shm_perm, mode); 4983308Sphk if (error) 4992729Sdfr return error; 5002729Sdfr if (uap->size && uap->size > shmseg->shm_segsz) 5012729Sdfr return EINVAL; 50230994Sphk p->p_retval[0] = IXSEQ_TO_IPCID(segnum, shmseg->shm_perm); 5032729Sdfr return 0; 5042729Sdfr} 5052729Sdfr 5062729Sdfrstatic int 50730994Sphkshmget_allocate_segment(p, uap, mode) 5082729Sdfr struct proc *p; 5092729Sdfr struct shmget_args *uap; 5102729Sdfr int mode; 5112729Sdfr{ 51218098Sdyson int i, segnum, shmid, size; 5132729Sdfr struct ucred *cred = p->p_ucred; 5142729Sdfr struct shmid_ds *shmseg; 5152729Sdfr struct shm_handle *shm_handle; 5168876Srgrimes 5172729Sdfr if (uap->size < shminfo.shmmin || uap->size > shminfo.shmmax) 5182729Sdfr return EINVAL; 5192729Sdfr if (shm_nused >= shminfo.shmmni) /* any shmids left? */ 5202729Sdfr return ENOSPC; 52115636Sjoerg size = round_page(uap->size); 5222729Sdfr if (shm_committed + btoc(size) > shminfo.shmall) 5232729Sdfr return ENOMEM; 5242729Sdfr if (shm_last_free < 0) { 52558820Speter shmrealloc(); /* maybe expand the shmsegs[] array */ 52658820Speter for (i = 0; i < shmalloced; i++) 5272729Sdfr if (shmsegs[i].shm_perm.mode & SHMSEG_FREE) 5282729Sdfr break; 52958820Speter if (i == shmalloced) 53058820Speter return ENOSPC; 5312729Sdfr segnum = i; 5322729Sdfr } else { 5332729Sdfr segnum = shm_last_free; 5342729Sdfr shm_last_free = -1; 5352729Sdfr } 5362729Sdfr shmseg = &shmsegs[segnum]; 5372729Sdfr /* 5382729Sdfr * In case we sleep in malloc(), mark the segment present but deleted 5392729Sdfr * so that noone else tries to create the same key. 5402729Sdfr */ 5412729Sdfr shmseg->shm_perm.mode = SHMSEG_ALLOCATED | SHMSEG_REMOVED; 5422729Sdfr shmseg->shm_perm.key = uap->key; 5432729Sdfr shmseg->shm_perm.seq = (shmseg->shm_perm.seq + 1) & 0x7fff; 5442729Sdfr shm_handle = (struct shm_handle *) 5452729Sdfr malloc(sizeof(struct shm_handle), M_SHM, M_WAITOK); 5462729Sdfr shmid = IXSEQ_TO_IPCID(segnum, shmseg->shm_perm); 54718098Sdyson 54818199Sdyson /* 54918199Sdyson * We make sure that we have allocated a pager before we need 55018199Sdyson * to. 55118199Sdyson */ 55261081Sdillon if (shm_use_phys) { 55361081Sdillon shm_handle->shm_object = 55461081Sdillon vm_pager_allocate(OBJT_PHYS, 0, size, VM_PROT_DEFAULT, 0); 55561081Sdillon } else { 55661081Sdillon shm_handle->shm_object = 55761081Sdillon vm_pager_allocate(OBJT_SWAP, 0, size, VM_PROT_DEFAULT, 0); 55861081Sdillon } 55938517Sdfr vm_object_clear_flag(shm_handle->shm_object, OBJ_ONEMAPPING); 56038517Sdfr vm_object_set_flag(shm_handle->shm_object, OBJ_NOSPLIT); 56135669Sdyson 5622729Sdfr shmseg->shm_internal = shm_handle; 5632729Sdfr shmseg->shm_perm.cuid = shmseg->shm_perm.uid = cred->cr_uid; 5642729Sdfr shmseg->shm_perm.cgid = shmseg->shm_perm.gid = cred->cr_gid; 5652729Sdfr shmseg->shm_perm.mode = (shmseg->shm_perm.mode & SHMSEG_WANTED) | 5662729Sdfr (mode & ACCESSPERMS) | SHMSEG_ALLOCATED; 5672729Sdfr shmseg->shm_segsz = uap->size; 5682729Sdfr shmseg->shm_cpid = p->p_pid; 5692729Sdfr shmseg->shm_lpid = shmseg->shm_nattch = 0; 5702729Sdfr shmseg->shm_atime = shmseg->shm_dtime = 0; 57134961Sphk shmseg->shm_ctime = time_second; 5722729Sdfr shm_committed += btoc(size); 5732729Sdfr shm_nused++; 5742729Sdfr if (shmseg->shm_perm.mode & SHMSEG_WANTED) { 5752729Sdfr /* 5762729Sdfr * Somebody else wanted this key while we were asleep. Wake 5772729Sdfr * them up now. 5782729Sdfr */ 5792729Sdfr shmseg->shm_perm.mode &= ~SHMSEG_WANTED; 5802729Sdfr wakeup((caddr_t)shmseg); 5812729Sdfr } 58230994Sphk p->p_retval[0] = shmid; 5832729Sdfr return 0; 5842729Sdfr} 5852729Sdfr 5862729Sdfrint 58730994Sphkshmget(p, uap) 5882729Sdfr struct proc *p; 5892729Sdfr struct shmget_args *uap; 5902729Sdfr{ 5912729Sdfr int segnum, mode, error; 5922729Sdfr 59368024Srwatson if (!jail_sysvipc_allowed && p->p_prison != NULL) 59468024Srwatson return (ENOSYS); 59568024Srwatson 5962729Sdfr mode = uap->shmflg & ACCESSPERMS; 5972729Sdfr if (uap->key != IPC_PRIVATE) { 5982729Sdfr again: 5992729Sdfr segnum = shm_find_segment_by_key(uap->key); 6002729Sdfr if (segnum >= 0) { 60130994Sphk error = shmget_existing(p, uap, mode, segnum); 6022729Sdfr if (error == EAGAIN) 6032729Sdfr goto again; 6042729Sdfr return error; 6052729Sdfr } 6068876Srgrimes if ((uap->shmflg & IPC_CREAT) == 0) 6072729Sdfr return ENOENT; 6082729Sdfr } 60930994Sphk return shmget_allocate_segment(p, uap, mode); 6102729Sdfr} 6112729Sdfr 6122729Sdfrint 61330994Sphkshmsys(p, uap) 6142729Sdfr struct proc *p; 61511626Sbde /* XXX actually varargs. */ 61611626Sbde struct shmsys_args /* { 61711626Sbde u_int which; 61811626Sbde int a2; 61911626Sbde int a3; 62011626Sbde int a4; 62111626Sbde } */ *uap; 6222729Sdfr{ 6232729Sdfr 62468024Srwatson if (!jail_sysvipc_allowed && p->p_prison != NULL) 62568024Srwatson return (ENOSYS); 62668024Srwatson 6272729Sdfr if (uap->which >= sizeof(shmcalls)/sizeof(shmcalls[0])) 6282729Sdfr return EINVAL; 62930994Sphk return ((*shmcalls[uap->which])(p, &uap->a2)); 6302729Sdfr} 6312729Sdfr 63269449Salfredstatic void 63369449Salfredshmfork_myhook(p1, p2) 6342729Sdfr struct proc *p1, *p2; 6352729Sdfr{ 6362729Sdfr struct shmmap_state *shmmap_s; 6372729Sdfr size_t size; 6382729Sdfr int i; 6392729Sdfr 6402729Sdfr size = shminfo.shmseg * sizeof(struct shmmap_state); 6412729Sdfr shmmap_s = malloc(size, M_SHM, M_WAITOK); 6422729Sdfr bcopy((caddr_t)p1->p_vmspace->vm_shm, (caddr_t)shmmap_s, size); 6432729Sdfr p2->p_vmspace->vm_shm = (caddr_t)shmmap_s; 6442729Sdfr for (i = 0; i < shminfo.shmseg; i++, shmmap_s++) 6452729Sdfr if (shmmap_s->shmid != -1) 6462729Sdfr shmsegs[IPCID_TO_IX(shmmap_s->shmid)].shm_nattch++; 6472729Sdfr} 6482729Sdfr 64969449Salfredstatic void 65069449Salfredshmexit_myhook(p) 6512729Sdfr struct proc *p; 6522729Sdfr{ 6532729Sdfr struct shmmap_state *shmmap_s; 6542729Sdfr int i; 6552729Sdfr 6562729Sdfr shmmap_s = (struct shmmap_state *)p->p_vmspace->vm_shm; 6572729Sdfr for (i = 0; i < shminfo.shmseg; i++, shmmap_s++) 6582729Sdfr if (shmmap_s->shmid != -1) 6592729Sdfr shm_delete_mapping(p, shmmap_s); 6602729Sdfr free((caddr_t)p->p_vmspace->vm_shm, M_SHM); 6612729Sdfr p->p_vmspace->vm_shm = NULL; 6622729Sdfr} 6632729Sdfr 66458820Speterstatic void 66558820Spetershmrealloc(void) 66658820Speter{ 66758820Speter int i; 66858820Speter struct shmid_ds *newsegs; 66958820Speter 67058820Speter if (shmalloced >= shminfo.shmmni) 67158820Speter return; 67258820Speter 67358820Speter newsegs = malloc(shminfo.shmmni * sizeof(*newsegs), M_SHM, M_WAITOK); 67458820Speter if (newsegs == NULL) 67558820Speter return; 67658820Speter for (i = 0; i < shmalloced; i++) 67758820Speter bcopy(&shmsegs[i], &newsegs[i], sizeof(newsegs[0])); 67858820Speter for (; i < shminfo.shmmni; i++) { 67958820Speter shmsegs[i].shm_perm.mode = SHMSEG_FREE; 68058820Speter shmsegs[i].shm_perm.seq = 0; 68158820Speter } 68258820Speter free(shmsegs, M_SHM); 68358820Speter shmsegs = newsegs; 68458820Speter shmalloced = shminfo.shmmni; 68558820Speter} 68658820Speter 68758820Speterstatic void 68869449Salfredshminit() 6892729Sdfr{ 6902729Sdfr int i; 69158820Speter 69258820Speter shmalloced = shminfo.shmmni; 69358820Speter shmsegs = malloc(shmalloced * sizeof(shmsegs[0]), M_SHM, M_WAITOK); 69458820Speter if (shmsegs == NULL) 69558820Speter panic("cannot allocate initial memory for sysvshm"); 69658820Speter for (i = 0; i < shmalloced; i++) { 6972729Sdfr shmsegs[i].shm_perm.mode = SHMSEG_FREE; 6982729Sdfr shmsegs[i].shm_perm.seq = 0; 6992729Sdfr } 7002729Sdfr shm_last_free = 0; 7012729Sdfr shm_nused = 0; 7022729Sdfr shm_committed = 0; 70369449Salfred shmexit_hook = &shmexit_myhook; 70469449Salfred shmfork_hook = &shmfork_myhook; 7052729Sdfr} 70669449Salfred 70769449Salfredstatic int 70869449Salfredshmunload() 70969449Salfred{ 71069449Salfred 71169449Salfred if (shm_nused > 0) 71269449Salfred return (EBUSY); 71369449Salfred 71469449Salfred free(shmsegs, M_SHM); 71569449Salfred shmexit_hook = NULL; 71669449Salfred shmfork_hook = NULL; 71769449Salfred return (0); 71869449Salfred} 71969449Salfred 72069449Salfredstatic int 72169449Salfredsysvshm_modload(struct module *module, int cmd, void *arg) 72269449Salfred{ 72369449Salfred int error = 0; 72469449Salfred 72569449Salfred switch (cmd) { 72669449Salfred case MOD_LOAD: 72769449Salfred shminit(); 72869449Salfred break; 72969449Salfred case MOD_UNLOAD: 73069449Salfred error = shmunload(); 73169449Salfred break; 73269449Salfred case MOD_SHUTDOWN: 73369449Salfred break; 73469449Salfred default: 73569449Salfred error = EINVAL; 73669449Salfred break; 73769449Salfred } 73869449Salfred return (error); 73969449Salfred} 74069449Salfred 74169449Salfredstatic moduledata_t sysvshm_moduledata = { 74269449Salfred "sysvshm_mod", 74369449Salfred &sysvshm_modload, 74469449Salfred NULL 74569449Salfred}; 74669449Salfred 74769449SalfredSYSCALL_MODULE_HELPER(shmsys, 4); 74869449SalfredSYSCALL_MODULE_HELPER(shmat, 3); 74969449SalfredSYSCALL_MODULE_HELPER(shmctl, 3); 75069449SalfredSYSCALL_MODULE_HELPER(shmdt, 1); 75169449SalfredSYSCALL_MODULE_HELPER(shmget, 3); 75269449Salfred 75369449SalfredDECLARE_MODULE(sysvshm_mod, sysvshm_moduledata, 75469449Salfred SI_SUB_SYSV_SHM, SI_ORDER_FIRST); 755