sysv_shm.c revision 11626
111626Sbde/* $Id: sysv_shm.c,v 1.9 1995/09/09 18:10:09 davidg Exp $ */ 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 342729Sdfr#include <sys/param.h> 3511626Sbde#include <sys/systm.h> 3611626Sbde#include <sys/sysproto.h> 372729Sdfr#include <sys/kernel.h> 382729Sdfr#include <sys/shm.h> 392729Sdfr#include <sys/proc.h> 402729Sdfr#include <sys/malloc.h> 412729Sdfr#include <sys/mman.h> 422729Sdfr#include <sys/stat.h> 4311626Sbde#include <sys/sysent.h> 442729Sdfr 452729Sdfr#include <vm/vm.h> 462729Sdfr#include <vm/vm_map.h> 472729Sdfr#include <vm/vm_kern.h> 482729Sdfr 4911626Sbdestruct shmat_args; 5011626Sbdeextern int shmat __P((struct proc *p, struct shmat_args *uap, int *retval)); 5111626Sbdestruct shmctl_args; 5211626Sbdeextern int shmctl __P((struct proc *p, struct shmctl_args *uap, int *retval)); 5311626Sbdestruct shmdt_args; 5411626Sbdeextern int shmdt __P((struct proc *p, struct shmdt_args *uap, int *retval)); 5511626Sbdestruct shmget_args; 5611626Sbdeextern int shmget __P((struct proc *p, struct shmget_args *uap, int *retval)); 5711626Sbde 5810653Sdgstatic void shminit __P((void *)); 5910358SjulianSYSINIT(sysv_shm, SI_SUB_SYSV_SHM, SI_ORDER_FIRST, shminit, NULL) 6010358Sjulian 6111626Sbdestruct oshmctl_args; 6211626Sbdeint oshmctl __P((struct proc *p, struct oshmctl_args *uap, int *retval)); 6311626Sbdestatic int shmget_allocate_segment __P((struct proc *p, struct shmget_args *uap, int mode, int *retval)); 6411626Sbdestatic int shmget_existing __P((struct proc *p, struct shmget_args *uap, int mode, int segnum, int *retval)); 658876Srgrimes 6611626Sbde/* XXX casting to (sy_call_t *) is bogus, as usual. */ 6711626Sbdesy_call_t *shmcalls[] = { 6811626Sbde (sy_call_t *)shmat, (sy_call_t *)oshmctl, 6911626Sbde (sy_call_t *)shmdt, (sy_call_t *)shmget, 7011626Sbde (sy_call_t *)shmctl 7111626Sbde}; 7211626Sbde 732729Sdfr#define SHMSEG_FREE 0x0200 742729Sdfr#define SHMSEG_REMOVED 0x0400 752729Sdfr#define SHMSEG_ALLOCATED 0x0800 762729Sdfr#define SHMSEG_WANTED 0x1000 772729Sdfr 782729Sdfrvm_map_t sysvshm_map; 792729Sdfrint shm_last_free, shm_nused, shm_committed; 809759Sbdestruct shmid_ds *shmsegs; 812729Sdfr 822729Sdfrstruct shm_handle { 832729Sdfr vm_offset_t kva; 842729Sdfr}; 852729Sdfr 862729Sdfrstruct shmmap_state { 872729Sdfr vm_offset_t va; 882729Sdfr int shmid; 892729Sdfr}; 902729Sdfr 912729Sdfrstatic void shm_deallocate_segment __P((struct shmid_ds *)); 922729Sdfrstatic int shm_find_segment_by_key __P((key_t)); 932729Sdfrstatic struct shmid_ds *shm_find_segment_by_shmid __P((int)); 942729Sdfrstatic int shm_delete_mapping __P((struct proc *, struct shmmap_state *)); 952729Sdfr 962729Sdfrstatic int 972729Sdfrshm_find_segment_by_key(key) 982729Sdfr key_t key; 992729Sdfr{ 1002729Sdfr int i; 1012729Sdfr 1022729Sdfr for (i = 0; i < shminfo.shmmni; i++) 1032729Sdfr if ((shmsegs[i].shm_perm.mode & SHMSEG_ALLOCATED) && 1042729Sdfr shmsegs[i].shm_perm.key == key) 1052729Sdfr return i; 1062729Sdfr return -1; 1072729Sdfr} 1082729Sdfr 1092729Sdfrstatic struct shmid_ds * 1102729Sdfrshm_find_segment_by_shmid(shmid) 1112729Sdfr int shmid; 1122729Sdfr{ 1132729Sdfr int segnum; 1142729Sdfr struct shmid_ds *shmseg; 1152729Sdfr 1162729Sdfr segnum = IPCID_TO_IX(shmid); 1172729Sdfr if (segnum < 0 || segnum >= shminfo.shmmni) 1182729Sdfr return NULL; 1192729Sdfr shmseg = &shmsegs[segnum]; 1202729Sdfr if ((shmseg->shm_perm.mode & (SHMSEG_ALLOCATED | SHMSEG_REMOVED)) 1212729Sdfr != SHMSEG_ALLOCATED || 1222729Sdfr shmseg->shm_perm.seq != IPCID_TO_SEQ(shmid)) 1232729Sdfr return NULL; 1242729Sdfr return shmseg; 1252729Sdfr} 1262729Sdfr 1272729Sdfrstatic void 1282729Sdfrshm_deallocate_segment(shmseg) 1292729Sdfr struct shmid_ds *shmseg; 1302729Sdfr{ 1312729Sdfr struct shm_handle *shm_handle; 1322729Sdfr size_t size; 1332729Sdfr 1342729Sdfr shm_handle = shmseg->shm_internal; 1352729Sdfr size = (shmseg->shm_segsz + CLOFSET) & ~CLOFSET; 1366579Sdg (void) vm_map_remove(sysvshm_map, shm_handle->kva, shm_handle->kva + size); 1372729Sdfr free((caddr_t)shm_handle, M_SHM); 1382729Sdfr shmseg->shm_internal = NULL; 1392729Sdfr shm_committed -= btoc(size); 1402729Sdfr shm_nused--; 1412729Sdfr shmseg->shm_perm.mode = SHMSEG_FREE; 1422729Sdfr} 1432729Sdfr 1442729Sdfrstatic int 1452729Sdfrshm_delete_mapping(p, shmmap_s) 1462729Sdfr struct proc *p; 1472729Sdfr struct shmmap_state *shmmap_s; 1482729Sdfr{ 1492729Sdfr struct shmid_ds *shmseg; 1502729Sdfr int segnum, result; 1512729Sdfr size_t size; 1528876Srgrimes 1532729Sdfr segnum = IPCID_TO_IX(shmmap_s->shmid); 1542729Sdfr shmseg = &shmsegs[segnum]; 1552729Sdfr size = (shmseg->shm_segsz + CLOFSET) & ~CLOFSET; 1566579Sdg result = vm_map_remove(&p->p_vmspace->vm_map, shmmap_s->va, shmmap_s->va + size); 1572729Sdfr if (result != KERN_SUCCESS) 1582729Sdfr return EINVAL; 1592729Sdfr shmmap_s->shmid = -1; 1602729Sdfr shmseg->shm_dtime = time.tv_sec; 1612729Sdfr if ((--shmseg->shm_nattch <= 0) && 1622729Sdfr (shmseg->shm_perm.mode & SHMSEG_REMOVED)) { 1632729Sdfr shm_deallocate_segment(shmseg); 1642729Sdfr shm_last_free = segnum; 1652729Sdfr } 1662729Sdfr return 0; 1672729Sdfr} 1682729Sdfr 1692729Sdfrstruct shmdt_args { 1702729Sdfr void *shmaddr; 1712729Sdfr}; 1722729Sdfrint 1732729Sdfrshmdt(p, uap, retval) 1742729Sdfr struct proc *p; 1752729Sdfr struct shmdt_args *uap; 1762729Sdfr int *retval; 1772729Sdfr{ 1782729Sdfr struct shmmap_state *shmmap_s; 1792729Sdfr int i; 1802729Sdfr 1812729Sdfr shmmap_s = (struct shmmap_state *)p->p_vmspace->vm_shm; 1822729Sdfr for (i = 0; i < shminfo.shmseg; i++, shmmap_s++) 1832729Sdfr if (shmmap_s->shmid != -1 && 1842729Sdfr shmmap_s->va == (vm_offset_t)uap->shmaddr) 1852729Sdfr break; 1862729Sdfr if (i == shminfo.shmseg) 1872729Sdfr return EINVAL; 1882729Sdfr return shm_delete_mapping(p, shmmap_s); 1892729Sdfr} 1902729Sdfr 1912729Sdfrstruct shmat_args { 1922729Sdfr int shmid; 1932729Sdfr void *shmaddr; 1942729Sdfr int shmflg; 1952729Sdfr}; 1962729Sdfrint 1972729Sdfrshmat(p, uap, retval) 1982729Sdfr struct proc *p; 1992729Sdfr struct shmat_args *uap; 2002729Sdfr int *retval; 2012729Sdfr{ 2022729Sdfr int error, i, flags; 2032729Sdfr struct ucred *cred = p->p_ucred; 2042729Sdfr struct shmid_ds *shmseg; 2052729Sdfr struct shmmap_state *shmmap_s = NULL; 2062729Sdfr vm_offset_t attach_va; 2072729Sdfr vm_prot_t prot; 2082729Sdfr vm_size_t size; 2092729Sdfr 2102729Sdfr shmmap_s = (struct shmmap_state *)p->p_vmspace->vm_shm; 2112729Sdfr if (shmmap_s == NULL) { 2122729Sdfr size = shminfo.shmseg * sizeof(struct shmmap_state); 2132729Sdfr shmmap_s = malloc(size, M_SHM, M_WAITOK); 2142729Sdfr for (i = 0; i < shminfo.shmseg; i++) 2152729Sdfr shmmap_s[i].shmid = -1; 2162729Sdfr p->p_vmspace->vm_shm = (caddr_t)shmmap_s; 2172729Sdfr } 2182729Sdfr shmseg = shm_find_segment_by_shmid(uap->shmid); 2192729Sdfr if (shmseg == NULL) 2202729Sdfr return EINVAL; 2213308Sphk error = ipcperm(cred, &shmseg->shm_perm, 2223308Sphk (uap->shmflg & SHM_RDONLY) ? IPC_R : IPC_R|IPC_W); 2233308Sphk if (error) 2242729Sdfr return error; 2252729Sdfr for (i = 0; i < shminfo.shmseg; i++) { 2262729Sdfr if (shmmap_s->shmid == -1) 2272729Sdfr break; 2282729Sdfr shmmap_s++; 2292729Sdfr } 2302729Sdfr if (i >= shminfo.shmseg) 2312729Sdfr return EMFILE; 2322729Sdfr size = (shmseg->shm_segsz + CLOFSET) & ~CLOFSET; 2332729Sdfr prot = VM_PROT_READ; 2342729Sdfr if ((uap->shmflg & SHM_RDONLY) == 0) 2352729Sdfr prot |= VM_PROT_WRITE; 2362729Sdfr flags = MAP_ANON | MAP_SHARED; 2372729Sdfr if (uap->shmaddr) { 2382729Sdfr flags |= MAP_FIXED; 2398876Srgrimes if (uap->shmflg & SHM_RND) 2402729Sdfr attach_va = (vm_offset_t)uap->shmaddr & ~(SHMLBA-1); 2412729Sdfr else if (((vm_offset_t)uap->shmaddr & (SHMLBA-1)) == 0) 2422729Sdfr attach_va = (vm_offset_t)uap->shmaddr; 2432729Sdfr else 2442729Sdfr return EINVAL; 2452729Sdfr } else { 2462729Sdfr /* This is just a hint to vm_mmap() about where to put it. */ 2472729Sdfr attach_va = round_page(p->p_vmspace->vm_daddr + MAXDSIZ); 2482729Sdfr } 2492729Sdfr error = vm_mmap(&p->p_vmspace->vm_map, &attach_va, size, prot, 2502729Sdfr VM_PROT_DEFAULT, flags, (caddr_t) uap->shmid, 0); 2512729Sdfr if (error) 2522729Sdfr return error; 2532729Sdfr shmmap_s->va = attach_va; 2542729Sdfr shmmap_s->shmid = uap->shmid; 2552729Sdfr shmseg->shm_lpid = p->p_pid; 2562729Sdfr shmseg->shm_atime = time.tv_sec; 2572729Sdfr shmseg->shm_nattch++; 2582729Sdfr *retval = attach_va; 2592729Sdfr return 0; 2602729Sdfr} 2612729Sdfr 2622828Sdfrstruct oshmid_ds { 2632828Sdfr struct ipc_perm shm_perm; /* operation perms */ 2642828Sdfr int shm_segsz; /* size of segment (bytes) */ 2652828Sdfr ushort shm_cpid; /* pid, creator */ 2662828Sdfr ushort shm_lpid; /* pid, last operation */ 2672828Sdfr short shm_nattch; /* no. of current attaches */ 2682828Sdfr time_t shm_atime; /* last attach time */ 2692828Sdfr time_t shm_dtime; /* last detach time */ 2702828Sdfr time_t shm_ctime; /* last change time */ 2712828Sdfr void *shm_handle; /* internal handle for shm segment */ 2722828Sdfr}; 2732828Sdfr 2742828Sdfrstruct oshmctl_args { 2752828Sdfr int shmid; 2762828Sdfr int cmd; 2772828Sdfr struct oshmid_ds *ubuf; 2782828Sdfr}; 2792828Sdfr 2802828Sdfrint 2812828Sdfroshmctl(p, uap, retval) 2822828Sdfr struct proc *p; 2832828Sdfr struct oshmctl_args *uap; 2842828Sdfr int *retval; 2852828Sdfr{ 2862828Sdfr#ifdef COMPAT_43 2873308Sphk int error; 2882828Sdfr struct ucred *cred = p->p_ucred; 2892828Sdfr struct shmid_ds *shmseg; 2902828Sdfr struct oshmid_ds outbuf; 2912828Sdfr 2922828Sdfr shmseg = shm_find_segment_by_shmid(uap->shmid); 2932828Sdfr if (shmseg == NULL) 2942828Sdfr return EINVAL; 2952828Sdfr switch (uap->cmd) { 2962828Sdfr case IPC_STAT: 2973308Sphk error = ipcperm(cred, &shmseg->shm_perm, IPC_R); 2983308Sphk if (error) 2992828Sdfr return error; 3002828Sdfr outbuf.shm_perm = shmseg->shm_perm; 3012828Sdfr outbuf.shm_segsz = shmseg->shm_segsz; 3022828Sdfr outbuf.shm_cpid = shmseg->shm_cpid; 3032828Sdfr outbuf.shm_lpid = shmseg->shm_lpid; 3042828Sdfr outbuf.shm_nattch = shmseg->shm_nattch; 3052828Sdfr outbuf.shm_atime = shmseg->shm_atime; 3062828Sdfr outbuf.shm_dtime = shmseg->shm_dtime; 3072828Sdfr outbuf.shm_ctime = shmseg->shm_ctime; 3082828Sdfr outbuf.shm_handle = shmseg->shm_internal; 3093308Sphk error = copyout((caddr_t)&outbuf, uap->ubuf, sizeof(outbuf)); 3103308Sphk if (error) 3112828Sdfr return error; 3122828Sdfr break; 3132828Sdfr default: 31411626Sbde /* XXX casting to (sy_call_t *) is bogus, as usual. */ 31511626Sbde return ((sy_call_t *)shmctl)(p, uap, retval); 3162828Sdfr } 3172828Sdfr return 0; 3182828Sdfr#else 3192828Sdfr return EINVAL; 3202828Sdfr#endif 3218876Srgrimes} 3222828Sdfr 3232729Sdfrstruct shmctl_args { 3242729Sdfr int shmid; 3252729Sdfr int cmd; 3262828Sdfr struct shmid_ds *ubuf; 3272729Sdfr}; 3282729Sdfrint 3292729Sdfrshmctl(p, uap, retval) 3302729Sdfr struct proc *p; 3312729Sdfr struct shmctl_args *uap; 3322729Sdfr int *retval; 3332729Sdfr{ 3343308Sphk int error; 3352729Sdfr struct ucred *cred = p->p_ucred; 3362729Sdfr struct shmid_ds inbuf; 3372729Sdfr struct shmid_ds *shmseg; 3382729Sdfr 3392729Sdfr shmseg = shm_find_segment_by_shmid(uap->shmid); 3402729Sdfr if (shmseg == NULL) 3412729Sdfr return EINVAL; 3422729Sdfr switch (uap->cmd) { 3432729Sdfr case IPC_STAT: 3443308Sphk error = ipcperm(cred, &shmseg->shm_perm, IPC_R); 3453308Sphk if (error) 3462729Sdfr return error; 3473308Sphk error = copyout((caddr_t)shmseg, uap->ubuf, sizeof(inbuf)); 3483308Sphk if (error) 3492729Sdfr return error; 3502729Sdfr break; 3512729Sdfr case IPC_SET: 3523308Sphk error = ipcperm(cred, &shmseg->shm_perm, IPC_M); 3533308Sphk if (error) 3542729Sdfr return error; 3553308Sphk error = copyin(uap->ubuf, (caddr_t)&inbuf, sizeof(inbuf)); 3563308Sphk if (error) 3572729Sdfr return error; 3582729Sdfr shmseg->shm_perm.uid = inbuf.shm_perm.uid; 3592729Sdfr shmseg->shm_perm.gid = inbuf.shm_perm.gid; 3602729Sdfr shmseg->shm_perm.mode = 3612729Sdfr (shmseg->shm_perm.mode & ~ACCESSPERMS) | 3622729Sdfr (inbuf.shm_perm.mode & ACCESSPERMS); 3632729Sdfr shmseg->shm_ctime = time.tv_sec; 3642729Sdfr break; 3652729Sdfr case IPC_RMID: 3663308Sphk error = ipcperm(cred, &shmseg->shm_perm, IPC_M); 3673308Sphk if (error) 3682729Sdfr return error; 3692729Sdfr shmseg->shm_perm.key = IPC_PRIVATE; 3702729Sdfr shmseg->shm_perm.mode |= SHMSEG_REMOVED; 3712729Sdfr if (shmseg->shm_nattch <= 0) { 3722729Sdfr shm_deallocate_segment(shmseg); 3732729Sdfr shm_last_free = IPCID_TO_IX(uap->shmid); 3742729Sdfr } 3752729Sdfr break; 3762729Sdfr#if 0 3772729Sdfr case SHM_LOCK: 3782729Sdfr case SHM_UNLOCK: 3792729Sdfr#endif 3802729Sdfr default: 3812729Sdfr return EINVAL; 3822729Sdfr } 3832729Sdfr return 0; 3842729Sdfr} 3852729Sdfr 3862729Sdfrstruct shmget_args { 3872729Sdfr key_t key; 3882729Sdfr size_t size; 3892729Sdfr int shmflg; 3902729Sdfr}; 3912729Sdfrstatic int 3922729Sdfrshmget_existing(p, uap, mode, segnum, retval) 3932729Sdfr struct proc *p; 3942729Sdfr struct shmget_args *uap; 3952729Sdfr int mode; 3962729Sdfr int segnum; 3972729Sdfr int *retval; 3982729Sdfr{ 3992729Sdfr struct shmid_ds *shmseg; 4002729Sdfr struct ucred *cred = p->p_ucred; 4012729Sdfr int error; 4022729Sdfr 4032729Sdfr shmseg = &shmsegs[segnum]; 4042729Sdfr if (shmseg->shm_perm.mode & SHMSEG_REMOVED) { 4052729Sdfr /* 4062729Sdfr * This segment is in the process of being allocated. Wait 4072729Sdfr * until it's done, and look the key up again (in case the 4082729Sdfr * allocation failed or it was freed). 4092729Sdfr */ 4102729Sdfr shmseg->shm_perm.mode |= SHMSEG_WANTED; 4113308Sphk error = tsleep((caddr_t)shmseg, PLOCK | PCATCH, "shmget", 0); 4123308Sphk if (error) 4132729Sdfr return error; 4142729Sdfr return EAGAIN; 4152729Sdfr } 4163308Sphk error = ipcperm(cred, &shmseg->shm_perm, mode); 4173308Sphk if (error) 4182729Sdfr return error; 4192729Sdfr if (uap->size && uap->size > shmseg->shm_segsz) 4202729Sdfr return EINVAL; 4212729Sdfr if (uap->shmflg & (IPC_CREAT | IPC_EXCL) == (IPC_CREAT | IPC_EXCL)) 4222729Sdfr return EEXIST; 4232729Sdfr *retval = IXSEQ_TO_IPCID(segnum, shmseg->shm_perm); 4242729Sdfr return 0; 4252729Sdfr} 4262729Sdfr 4272729Sdfrstatic int 4282729Sdfrshmget_allocate_segment(p, uap, mode, retval) 4292729Sdfr struct proc *p; 4302729Sdfr struct shmget_args *uap; 4312729Sdfr int mode; 4322729Sdfr int *retval; 4332729Sdfr{ 4342729Sdfr int i, segnum, result, shmid, size; 4352729Sdfr struct ucred *cred = p->p_ucred; 4362729Sdfr struct shmid_ds *shmseg; 4372729Sdfr struct shm_handle *shm_handle; 4388876Srgrimes 4392729Sdfr if (uap->size < shminfo.shmmin || uap->size > shminfo.shmmax) 4402729Sdfr return EINVAL; 4412729Sdfr if (shm_nused >= shminfo.shmmni) /* any shmids left? */ 4422729Sdfr return ENOSPC; 4432729Sdfr size = (uap->size + CLOFSET) & ~CLOFSET; 4442729Sdfr if (shm_committed + btoc(size) > shminfo.shmall) 4452729Sdfr return ENOMEM; 4462729Sdfr if (shm_last_free < 0) { 4472729Sdfr for (i = 0; i < shminfo.shmmni; i++) 4482729Sdfr if (shmsegs[i].shm_perm.mode & SHMSEG_FREE) 4492729Sdfr break; 4502729Sdfr if (i == shminfo.shmmni) 4512729Sdfr panic("shmseg free count inconsistent"); 4522729Sdfr segnum = i; 4532729Sdfr } else { 4542729Sdfr segnum = shm_last_free; 4552729Sdfr shm_last_free = -1; 4562729Sdfr } 4572729Sdfr shmseg = &shmsegs[segnum]; 4582729Sdfr /* 4592729Sdfr * In case we sleep in malloc(), mark the segment present but deleted 4602729Sdfr * so that noone else tries to create the same key. 4612729Sdfr */ 4622729Sdfr shmseg->shm_perm.mode = SHMSEG_ALLOCATED | SHMSEG_REMOVED; 4632729Sdfr shmseg->shm_perm.key = uap->key; 4642729Sdfr shmseg->shm_perm.seq = (shmseg->shm_perm.seq + 1) & 0x7fff; 4652729Sdfr shm_handle = (struct shm_handle *) 4662729Sdfr malloc(sizeof(struct shm_handle), M_SHM, M_WAITOK); 4672729Sdfr shmid = IXSEQ_TO_IPCID(segnum, shmseg->shm_perm); 4682729Sdfr result = vm_mmap(sysvshm_map, &shm_handle->kva, size, VM_PROT_ALL, 4692729Sdfr VM_PROT_DEFAULT, MAP_ANON, (caddr_t) shmid, 0); 4702729Sdfr if (result != KERN_SUCCESS) { 4712729Sdfr shmseg->shm_perm.mode = SHMSEG_FREE; 4722729Sdfr shm_last_free = segnum; 4732729Sdfr free((caddr_t)shm_handle, M_SHM); 4742729Sdfr /* Just in case. */ 4752729Sdfr wakeup((caddr_t)shmseg); 4762729Sdfr return ENOMEM; 4772729Sdfr } 4782729Sdfr shmseg->shm_internal = shm_handle; 4792729Sdfr shmseg->shm_perm.cuid = shmseg->shm_perm.uid = cred->cr_uid; 4802729Sdfr shmseg->shm_perm.cgid = shmseg->shm_perm.gid = cred->cr_gid; 4812729Sdfr shmseg->shm_perm.mode = (shmseg->shm_perm.mode & SHMSEG_WANTED) | 4822729Sdfr (mode & ACCESSPERMS) | SHMSEG_ALLOCATED; 4832729Sdfr shmseg->shm_segsz = uap->size; 4842729Sdfr shmseg->shm_cpid = p->p_pid; 4852729Sdfr shmseg->shm_lpid = shmseg->shm_nattch = 0; 4862729Sdfr shmseg->shm_atime = shmseg->shm_dtime = 0; 4872729Sdfr shmseg->shm_ctime = time.tv_sec; 4882729Sdfr shm_committed += btoc(size); 4892729Sdfr shm_nused++; 4902729Sdfr if (shmseg->shm_perm.mode & SHMSEG_WANTED) { 4912729Sdfr /* 4922729Sdfr * Somebody else wanted this key while we were asleep. Wake 4932729Sdfr * them up now. 4942729Sdfr */ 4952729Sdfr shmseg->shm_perm.mode &= ~SHMSEG_WANTED; 4962729Sdfr wakeup((caddr_t)shmseg); 4972729Sdfr } 4982729Sdfr *retval = shmid; 4992729Sdfr return 0; 5002729Sdfr} 5012729Sdfr 5022729Sdfrint 5032729Sdfrshmget(p, uap, retval) 5042729Sdfr struct proc *p; 5052729Sdfr struct shmget_args *uap; 5062729Sdfr int *retval; 5072729Sdfr{ 5082729Sdfr int segnum, mode, error; 5092729Sdfr 5102729Sdfr mode = uap->shmflg & ACCESSPERMS; 5112729Sdfr if (uap->key != IPC_PRIVATE) { 5122729Sdfr again: 5132729Sdfr segnum = shm_find_segment_by_key(uap->key); 5142729Sdfr if (segnum >= 0) { 5152729Sdfr error = shmget_existing(p, uap, mode, segnum, retval); 5162729Sdfr if (error == EAGAIN) 5172729Sdfr goto again; 5182729Sdfr return error; 5192729Sdfr } 5208876Srgrimes if ((uap->shmflg & IPC_CREAT) == 0) 5212729Sdfr return ENOENT; 5222729Sdfr } 5232729Sdfr return shmget_allocate_segment(p, uap, mode, retval); 5242729Sdfr} 5252729Sdfr 5262729Sdfrint 5272729Sdfrshmsys(p, uap, retval) 5282729Sdfr struct proc *p; 52911626Sbde /* XXX actually varargs. */ 53011626Sbde struct shmsys_args /* { 53111626Sbde u_int which; 53211626Sbde int a2; 53311626Sbde int a3; 53411626Sbde int a4; 53511626Sbde } */ *uap; 5362729Sdfr int *retval; 5372729Sdfr{ 5382729Sdfr 5392729Sdfr if (uap->which >= sizeof(shmcalls)/sizeof(shmcalls[0])) 5402729Sdfr return EINVAL; 54111626Sbde return ((*shmcalls[uap->which])(p, &uap->a2, retval)); 5422729Sdfr} 5432729Sdfr 5442729Sdfrvoid 5452729Sdfrshmfork(p1, p2, isvfork) 5462729Sdfr struct proc *p1, *p2; 5472729Sdfr int isvfork; 5482729Sdfr{ 5492729Sdfr struct shmmap_state *shmmap_s; 5502729Sdfr size_t size; 5512729Sdfr int i; 5522729Sdfr 5532729Sdfr size = shminfo.shmseg * sizeof(struct shmmap_state); 5542729Sdfr shmmap_s = malloc(size, M_SHM, M_WAITOK); 5552729Sdfr bcopy((caddr_t)p1->p_vmspace->vm_shm, (caddr_t)shmmap_s, size); 5562729Sdfr p2->p_vmspace->vm_shm = (caddr_t)shmmap_s; 5572729Sdfr for (i = 0; i < shminfo.shmseg; i++, shmmap_s++) 5582729Sdfr if (shmmap_s->shmid != -1) 5592729Sdfr shmsegs[IPCID_TO_IX(shmmap_s->shmid)].shm_nattch++; 5602729Sdfr} 5612729Sdfr 5622729Sdfrvoid 5632729Sdfrshmexit(p) 5642729Sdfr struct proc *p; 5652729Sdfr{ 5662729Sdfr struct shmmap_state *shmmap_s; 5672729Sdfr int i; 5682729Sdfr 5692729Sdfr shmmap_s = (struct shmmap_state *)p->p_vmspace->vm_shm; 5702729Sdfr for (i = 0; i < shminfo.shmseg; i++, shmmap_s++) 5712729Sdfr if (shmmap_s->shmid != -1) 5722729Sdfr shm_delete_mapping(p, shmmap_s); 5732729Sdfr free((caddr_t)p->p_vmspace->vm_shm, M_SHM); 5742729Sdfr p->p_vmspace->vm_shm = NULL; 5752729Sdfr} 5762729Sdfr 5772729Sdfrvoid 57811626Sbdeshminit(dummy) 57911626Sbde void *dummy; 5802729Sdfr{ 5812729Sdfr int i; 5822729Sdfr vm_offset_t garbage1, garbage2; 5832729Sdfr 5842729Sdfr /* actually this *should* be pageable. SHM_{LOCK,UNLOCK} */ 5852729Sdfr sysvshm_map = kmem_suballoc(kernel_map, &garbage1, &garbage2, 5862729Sdfr shminfo.shmall * NBPG, TRUE); 5872729Sdfr for (i = 0; i < shminfo.shmmni; i++) { 5882729Sdfr shmsegs[i].shm_perm.mode = SHMSEG_FREE; 5892729Sdfr shmsegs[i].shm_perm.seq = 0; 5902729Sdfr } 5912729Sdfr shm_last_free = 0; 5922729Sdfr shm_nused = 0; 5932729Sdfr shm_committed = 0; 5942729Sdfr} 595