sysv_shm.c revision 48039
138494Sobrien/* $Id: sysv_shm.c,v 1.41 1999/04/27 12:21:09 phk Exp $ */ 2174294Sobrien/* $NetBSD: sysv_shm.c,v 1.23 1994/07/04 23:25:12 glass Exp $ */ 338494Sobrien 438494Sobrien/* 538494Sobrien * Copyright (c) 1994 Adam Glass and Charles Hannum. All rights reserved. 638494Sobrien * 738494Sobrien * Redistribution and use in source and binary forms, with or without 838494Sobrien * modification, are permitted provided that the following conditions 938494Sobrien * are met: 1038494Sobrien * 1. Redistributions of source code must retain the above copyright 1138494Sobrien * notice, this list of conditions and the following disclaimer. 1238494Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1338494Sobrien * notice, this list of conditions and the following disclaimer in the 1438494Sobrien * documentation and/or other materials provided with the distribution. 1538494Sobrien * 3. All advertising materials mentioning features or use of this software 1638494Sobrien * must display the following acknowledgement: 1738494Sobrien * This product includes software developed by Adam Glass and Charles 1838494Sobrien * Hannum. 1938494Sobrien * 4. The names of the authors may not be used to endorse or promote products 2042629Sobrien * derived from this software without specific prior written permission. 2138494Sobrien * 2238494Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 2338494Sobrien * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 2438494Sobrien * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2538494Sobrien * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, 2638494Sobrien * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2738494Sobrien * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2838494Sobrien * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2938494Sobrien * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3038494Sobrien * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 3138494Sobrien * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3238494Sobrien */ 3338494Sobrien 3438494Sobrien#include "opt_compat.h" 3538494Sobrien#include "opt_rlimit.h" 3638494Sobrien 3738494Sobrien#include <sys/param.h> 3838494Sobrien#include <sys/systm.h> 3938494Sobrien#include <sys/sysproto.h> 40174294Sobrien#include <sys/kernel.h> 4138494Sobrien#include <sys/shm.h> 4238494Sobrien#include <sys/proc.h> 4338494Sobrien#include <sys/malloc.h> 4438494Sobrien#include <sys/mman.h> 4538494Sobrien#include <sys/stat.h> 4638494Sobrien#include <sys/sysent.h> 4738494Sobrien 4838494Sobrien#include <vm/vm.h> 4938494Sobrien#include <vm/vm_param.h> 5038494Sobrien#include <vm/vm_prot.h> 5138494Sobrien#include <sys/lock.h> 5238494Sobrien#include <vm/pmap.h> 5338494Sobrien#include <vm/vm_object.h> 5438494Sobrien#include <vm/vm_map.h> 5582794Sobrien#include <vm/vm_page.h> 5638494Sobrien#include <vm/vm_pager.h> 5738494Sobrien#include <vm/vm_inherit.h> 5838494Sobrien 5938494Sobrien#ifndef _SYS_SYSPROTO_H_ 6038494Sobrienstruct shmat_args; 6138494Sobrienextern int shmat __P((struct proc *p, struct shmat_args *uap)); 6238494Sobrienstruct shmctl_args; 6338494Sobrienextern int shmctl __P((struct proc *p, struct shmctl_args *uap)); 6482794Sobrienstruct shmdt_args; 6538494Sobrienextern int shmdt __P((struct proc *p, struct shmdt_args *uap)); 6682794Sobrienstruct shmget_args; 6738494Sobrienextern int shmget __P((struct proc *p, struct shmget_args *uap)); 6838494Sobrien#endif 6938494Sobrien 7038494Sobrienstatic MALLOC_DEFINE(M_SHM, "shm", "SVID compatible shared memory segments"); 7182794Sobrien 7238494Sobrienstatic void shminit __P((void *)); 7382794SobrienSYSINIT(sysv_shm, SI_SUB_SYSV_SHM, SI_ORDER_FIRST, shminit, NULL) 7482794Sobrien 7538494Sobrienstruct oshmctl_args; 7682794Sobrienstatic int oshmctl __P((struct proc *p, struct oshmctl_args *uap)); 7782794Sobrienstatic int shmget_allocate_segment __P((struct proc *p, struct shmget_args *uap, int mode)); 7838494Sobrienstatic int shmget_existing __P((struct proc *p, struct shmget_args *uap, int mode, int segnum)); 7982794Sobrien 8082794Sobrien/* XXX casting to (sy_call_t *) is bogus, as usual. */ 8138494Sobrienstatic sy_call_t *shmcalls[] = { 8282794Sobrien (sy_call_t *)shmat, (sy_call_t *)oshmctl, 8382794Sobrien (sy_call_t *)shmdt, (sy_call_t *)shmget, 8438494Sobrien (sy_call_t *)shmctl 8582794Sobrien}; 8682794Sobrien 8738494Sobrien#define SHMSEG_FREE 0x0200 8882794Sobrien#define SHMSEG_REMOVED 0x0400 8938494Sobrien#define SHMSEG_ALLOCATED 0x0800 9038494Sobrien#define SHMSEG_WANTED 0x1000 9138494Sobrien 9238494Sobrienstatic int shm_last_free, shm_nused, shm_committed; 9338494Sobrienstruct shmid_ds *shmsegs; 9438494Sobrien 9538494Sobrienstruct shm_handle { 9638494Sobrien /* vm_offset_t kva; */ 9738494Sobrien vm_object_t shm_object; 9838494Sobrien}; 9938494Sobrien 10038494Sobrienstruct shmmap_state { 10138494Sobrien vm_offset_t va; 10238494Sobrien int shmid; 10338494Sobrien}; 10438494Sobrien 10538494Sobrienstatic void shm_deallocate_segment __P((struct shmid_ds *)); 10638494Sobrienstatic int shm_find_segment_by_key __P((key_t)); 10738494Sobrienstatic struct shmid_ds *shm_find_segment_by_shmid __P((int)); 10838494Sobrienstatic int shm_delete_mapping __P((struct proc *, struct shmmap_state *)); 10938494Sobrien 11038494Sobrienstatic int 11138494Sobrienshm_find_segment_by_key(key) 11238494Sobrien key_t key; 11338494Sobrien{ 11438494Sobrien int i; 11538494Sobrien 11638494Sobrien for (i = 0; i < shminfo.shmmni; i++) 11738494Sobrien if ((shmsegs[i].shm_perm.mode & SHMSEG_ALLOCATED) && 11838494Sobrien shmsegs[i].shm_perm.key == key) 11938494Sobrien return i; 12038494Sobrien return -1; 12138494Sobrien} 12238494Sobrien 12338494Sobrienstatic struct shmid_ds * 12438494Sobrienshm_find_segment_by_shmid(shmid) 125174294Sobrien int shmid; 12638494Sobrien{ 12738494Sobrien int segnum; 12838494Sobrien struct shmid_ds *shmseg; 129119679Smbr 13038494Sobrien segnum = IPCID_TO_IX(shmid); 13182794Sobrien if (segnum < 0 || segnum >= shminfo.shmmni) 13238494Sobrien return NULL; 13382794Sobrien shmseg = &shmsegs[segnum]; 13482794Sobrien if ((shmseg->shm_perm.mode & (SHMSEG_ALLOCATED | SHMSEG_REMOVED)) 13538494Sobrien != SHMSEG_ALLOCATED || 13682794Sobrien shmseg->shm_perm.seq != IPCID_TO_SEQ(shmid)) 13782794Sobrien return NULL; 13838494Sobrien return shmseg; 13982794Sobrien} 14082794Sobrien 14138494Sobrienstatic void 14282794Sobrienshm_deallocate_segment(shmseg) 14338494Sobrien struct shmid_ds *shmseg; 14438494Sobrien{ 14538494Sobrien struct shm_handle *shm_handle; 14638494Sobrien size_t size; 14738494Sobrien 148174294Sobrien shm_handle = shmseg->shm_internal; 14938494Sobrien vm_object_deallocate(shm_handle->shm_object); 15038494Sobrien free((caddr_t)shm_handle, M_SHM); 151174294Sobrien shmseg->shm_internal = NULL; 15238494Sobrien size = round_page(shmseg->shm_segsz); 15338494Sobrien shm_committed -= btoc(size); 15438494Sobrien shm_nused--; 155174294Sobrien shmseg->shm_perm.mode = SHMSEG_FREE; 15638494Sobrien} 157174294Sobrien 158174294Sobrienstatic int 159174294Sobrienshm_delete_mapping(p, shmmap_s) 160174294Sobrien struct proc *p; 161174294Sobrien struct shmmap_state *shmmap_s; 16238494Sobrien{ 16338494Sobrien struct shmid_ds *shmseg; 16438494Sobrien int segnum, result; 16538494Sobrien size_t size; 16638494Sobrien 16738494Sobrien segnum = IPCID_TO_IX(shmmap_s->shmid); 168174294Sobrien shmseg = &shmsegs[segnum]; 16938494Sobrien size = round_page(shmseg->shm_segsz); 170174294Sobrien result = vm_map_remove(&p->p_vmspace->vm_map, shmmap_s->va, shmmap_s->va + size); 171174294Sobrien if (result != KERN_SUCCESS) 172174294Sobrien return EINVAL; 173174294Sobrien shmmap_s->shmid = -1; 174174294Sobrien shmseg->shm_dtime = time_second; 175174294Sobrien if ((--shmseg->shm_nattch <= 0) && 17638494Sobrien (shmseg->shm_perm.mode & SHMSEG_REMOVED)) { 17738494Sobrien shm_deallocate_segment(shmseg); 17838494Sobrien shm_last_free = segnum; 17938494Sobrien } 18038494Sobrien return 0; 181174294Sobrien} 18238494Sobrien 183174294Sobrien#ifndef _SYS_SYSPROTO_H_ 18438494Sobrienstruct shmdt_args { 18538494Sobrien void *shmaddr; 18638494Sobrien}; 187174294Sobrien#endif 188174294Sobrien 189174294Sobrienint 190174294Sobrienshmdt(p, uap) 19138494Sobrien struct proc *p; 192174294Sobrien struct shmdt_args *uap; 19338494Sobrien{ 19438494Sobrien struct shmmap_state *shmmap_s; 19538494Sobrien int i; 196174294Sobrien 19738494Sobrien shmmap_s = (struct shmmap_state *)p->p_vmspace->vm_shm; 19838494Sobrien if (shmmap_s == NULL) 19938494Sobrien return EINVAL; 200174294Sobrien for (i = 0; i < shminfo.shmseg; i++, shmmap_s++) 20138494Sobrien if (shmmap_s->shmid != -1 && 20238494Sobrien shmmap_s->va == (vm_offset_t)uap->shmaddr) 20338494Sobrien break; 204174294Sobrien if (i == shminfo.shmseg) 20538494Sobrien return EINVAL; 20638494Sobrien return shm_delete_mapping(p, shmmap_s); 20738494Sobrien} 208174294Sobrien 20938494Sobrien#ifndef _SYS_SYSPROTO_H_ 21038494Sobrienstruct shmat_args { 21138494Sobrien int shmid; 212174294Sobrien void *shmaddr; 21338494Sobrien int shmflg; 21438494Sobrien}; 21538494Sobrien#endif 216174294Sobrien 21738494Sobrienint 21838494Sobrienshmat(p, uap) 21938494Sobrien struct proc *p; 220174294Sobrien struct shmat_args *uap; 22138494Sobrien{ 22238494Sobrien int error, i, flags; 22338494Sobrien struct shmid_ds *shmseg; 224174294Sobrien struct shmmap_state *shmmap_s = NULL; 22538494Sobrien struct shm_handle *shm_handle; 22638494Sobrien vm_offset_t attach_va; 22738494Sobrien vm_prot_t prot; 228174294Sobrien vm_size_t size; 22938494Sobrien int rv; 23038494Sobrien 23138494Sobrien shmmap_s = (struct shmmap_state *)p->p_vmspace->vm_shm; 232174294Sobrien if (shmmap_s == NULL) { 23338494Sobrien size = shminfo.shmseg * sizeof(struct shmmap_state); 23438494Sobrien shmmap_s = malloc(size, M_SHM, M_WAITOK); 23538494Sobrien for (i = 0; i < shminfo.shmseg; i++) 236174294Sobrien shmmap_s[i].shmid = -1; 23738494Sobrien p->p_vmspace->vm_shm = (caddr_t)shmmap_s; 23838494Sobrien } 23938494Sobrien shmseg = shm_find_segment_by_shmid(uap->shmid); 240174294Sobrien if (shmseg == NULL) 24138494Sobrien return EINVAL; 24238494Sobrien error = ipcperm(p, &shmseg->shm_perm, 24338494Sobrien (uap->shmflg & SHM_RDONLY) ? IPC_R : IPC_R|IPC_W); 244174294Sobrien if (error) 24538494Sobrien return error; 24638494Sobrien for (i = 0; i < shminfo.shmseg; i++) { 24738494Sobrien if (shmmap_s->shmid == -1) 248174294Sobrien break; 24938494Sobrien shmmap_s++; 25038494Sobrien } 25138494Sobrien if (i >= shminfo.shmseg) 252174294Sobrien return EMFILE; 25338494Sobrien size = round_page(shmseg->shm_segsz); 25438494Sobrien prot = VM_PROT_READ; 25538494Sobrien if ((uap->shmflg & SHM_RDONLY) == 0) 25638494Sobrien prot |= VM_PROT_WRITE; 25738494Sobrien flags = MAP_ANON | MAP_SHARED; 25838494Sobrien if (uap->shmaddr) { 25938494Sobrien flags |= MAP_FIXED; 260174294Sobrien if (uap->shmflg & SHM_RND) 26138494Sobrien attach_va = (vm_offset_t)uap->shmaddr & ~(SHMLBA-1); 26238494Sobrien else if (((vm_offset_t)uap->shmaddr & (SHMLBA-1)) == 0) 26338494Sobrien attach_va = (vm_offset_t)uap->shmaddr; 26438494Sobrien else 26538494Sobrien return EINVAL; 26638494Sobrien } else { 26738494Sobrien /* This is just a hint to vm_map_find() about where to put it. */ 26838494Sobrien attach_va = round_page((vm_offset_t)p->p_vmspace->vm_taddr + MAXTSIZ + MAXDSIZ); 26938494Sobrien } 27038494Sobrien 27138494Sobrien shm_handle = shmseg->shm_internal; 27238494Sobrien vm_object_reference(shm_handle->shm_object); 27338494Sobrien rv = vm_map_find(&p->p_vmspace->vm_map, shm_handle->shm_object, 27438494Sobrien 0, &attach_va, size, (flags & MAP_FIXED)?0:1, prot, prot, 0); 27538494Sobrien if (rv != KERN_SUCCESS) { 27638494Sobrien return ENOMEM; 27738494Sobrien } 27838494Sobrien vm_map_inherit(&p->p_vmspace->vm_map, 27938494Sobrien attach_va, attach_va + size, VM_INHERIT_SHARE); 28038494Sobrien 28138494Sobrien shmmap_s->va = attach_va; 28238494Sobrien shmmap_s->shmid = uap->shmid; 28338494Sobrien shmseg->shm_lpid = p->p_pid; 28438494Sobrien shmseg->shm_atime = time_second; 28538494Sobrien shmseg->shm_nattch++; 28638494Sobrien p->p_retval[0] = attach_va; 28738494Sobrien return 0; 28838494Sobrien} 28938494Sobrien 29038494Sobrienstruct oshmid_ds { 29138494Sobrien struct ipc_perm shm_perm; /* operation perms */ 292174294Sobrien int shm_segsz; /* size of segment (bytes) */ 29338494Sobrien ushort shm_cpid; /* pid, creator */ 29438494Sobrien ushort shm_lpid; /* pid, last operation */ 295174294Sobrien short shm_nattch; /* no. of current attaches */ 296174294Sobrien time_t shm_atime; /* last attach time */ 29738494Sobrien time_t shm_dtime; /* last detach time */ 29838494Sobrien time_t shm_ctime; /* last change time */ 29938494Sobrien void *shm_handle; /* internal handle for shm segment */ 30038494Sobrien}; 30138494Sobrien 30238494Sobrienstruct oshmctl_args { 30338494Sobrien int shmid; 30438494Sobrien int cmd; 30538494Sobrien struct oshmid_ds *ubuf; 30638494Sobrien}; 30738494Sobrien 30838494Sobrienstatic int 30938494Sobrienoshmctl(p, uap) 31042629Sobrien struct proc *p; 31138494Sobrien struct oshmctl_args *uap; 31238494Sobrien{ 31338494Sobrien#ifdef COMPAT_43 31438494Sobrien int error; 31582794Sobrien struct shmid_ds *shmseg; 31638494Sobrien struct oshmid_ds outbuf; 31738494Sobrien 318174294Sobrien shmseg = shm_find_segment_by_shmid(uap->shmid); 31938494Sobrien if (shmseg == NULL) 32038494Sobrien return EINVAL; 32138494Sobrien switch (uap->cmd) { 32242629Sobrien case IPC_STAT: 32338494Sobrien error = ipcperm(p, &shmseg->shm_perm, IPC_R); 32438494Sobrien if (error) 32538494Sobrien return error; 32638494Sobrien outbuf.shm_perm = shmseg->shm_perm; 32738494Sobrien outbuf.shm_segsz = shmseg->shm_segsz; 32838494Sobrien outbuf.shm_cpid = shmseg->shm_cpid; 32938494Sobrien outbuf.shm_lpid = shmseg->shm_lpid; 330174294Sobrien outbuf.shm_nattch = shmseg->shm_nattch; 33138494Sobrien outbuf.shm_atime = shmseg->shm_atime; 33282794Sobrien outbuf.shm_dtime = shmseg->shm_dtime; 33382794Sobrien outbuf.shm_ctime = shmseg->shm_ctime; 33438494Sobrien outbuf.shm_handle = shmseg->shm_internal; 33538494Sobrien error = copyout((caddr_t)&outbuf, uap->ubuf, sizeof(outbuf)); 33638494Sobrien if (error) 337174294Sobrien return error; 33838494Sobrien break; 33938494Sobrien default: 340174294Sobrien /* XXX casting to (sy_call_t *) is bogus, as usual. */ 341174294Sobrien return ((sy_call_t *)shmctl)(p, uap); 342174294Sobrien } 34338494Sobrien return 0; 344174294Sobrien#else 34538494Sobrien return EINVAL; 34638494Sobrien#endif 34738494Sobrien} 34838494Sobrien 349174294Sobrien#ifndef _SYS_SYSPROTO_H_ 350174294Sobrienstruct shmctl_args { 351174294Sobrien int shmid; 35238494Sobrien int cmd; 353174294Sobrien struct shmid_ds *buf; 35438494Sobrien}; 35538494Sobrien#endif 35638494Sobrien 35738494Sobrienint 35838494Sobrienshmctl(p, uap) 35938494Sobrien struct proc *p; 36038494Sobrien struct shmctl_args *uap; 36138494Sobrien{ 362174294Sobrien int error; 36338494Sobrien struct shmid_ds inbuf; 36438494Sobrien struct shmid_ds *shmseg; 36538494Sobrien 366174294Sobrien shmseg = shm_find_segment_by_shmid(uap->shmid); 367174294Sobrien if (shmseg == NULL) 368174294Sobrien return EINVAL; 369174294Sobrien switch (uap->cmd) { 370174294Sobrien case IPC_STAT: 37138494Sobrien error = ipcperm(p, &shmseg->shm_perm, IPC_R); 372174294Sobrien if (error) 373174294Sobrien return error; 374174294Sobrien error = copyout((caddr_t)shmseg, uap->buf, sizeof(inbuf)); 375174294Sobrien if (error) 376174294Sobrien return error; 377174294Sobrien break; 378174294Sobrien case IPC_SET: 37938494Sobrien error = ipcperm(p, &shmseg->shm_perm, IPC_M); 38038494Sobrien if (error) 38138494Sobrien return error; 38238494Sobrien error = copyin(uap->buf, (caddr_t)&inbuf, sizeof(inbuf)); 38338494Sobrien if (error) 38438494Sobrien return error; 38538494Sobrien shmseg->shm_perm.uid = inbuf.shm_perm.uid; 38638494Sobrien shmseg->shm_perm.gid = inbuf.shm_perm.gid; 38738494Sobrien shmseg->shm_perm.mode = 38838494Sobrien (shmseg->shm_perm.mode & ~ACCESSPERMS) | 38938494Sobrien (inbuf.shm_perm.mode & ACCESSPERMS); 39038494Sobrien shmseg->shm_ctime = time_second; 391174294Sobrien break; 39238494Sobrien case IPC_RMID: 39338494Sobrien error = ipcperm(p, &shmseg->shm_perm, IPC_M); 39438494Sobrien if (error) 39538494Sobrien return error; 39638494Sobrien shmseg->shm_perm.key = IPC_PRIVATE; 39738494Sobrien shmseg->shm_perm.mode |= SHMSEG_REMOVED; 39838494Sobrien if (shmseg->shm_nattch <= 0) { 39938494Sobrien shm_deallocate_segment(shmseg); 40038494Sobrien shm_last_free = IPCID_TO_IX(uap->shmid); 40138494Sobrien } 40238494Sobrien break; 40338494Sobrien#if 0 40438494Sobrien case SHM_LOCK: 40538494Sobrien case SHM_UNLOCK: 40638494Sobrien#endif 40738494Sobrien default: 40838494Sobrien return EINVAL; 40938494Sobrien } 41038494Sobrien return 0; 41138494Sobrien} 41238494Sobrien 41338494Sobrien#ifndef _SYS_SYSPROTO_H_ 41438494Sobrienstruct shmget_args { 41538494Sobrien key_t key; 41638494Sobrien size_t size; 41751292Sobrien int shmflg; 41838494Sobrien}; 41938494Sobrien#endif 42051292Sobrien 42151292Sobrienstatic int 42251292Sobrienshmget_existing(p, uap, mode, segnum) 42351292Sobrien struct proc *p; 42482794Sobrien struct shmget_args *uap; 42551292Sobrien int mode; 42651292Sobrien int segnum; 42751292Sobrien{ 42851292Sobrien struct shmid_ds *shmseg; 42951292Sobrien int error; 43051292Sobrien 43151292Sobrien shmseg = &shmsegs[segnum]; 43251292Sobrien if (shmseg->shm_perm.mode & SHMSEG_REMOVED) { 43382794Sobrien /* 43451292Sobrien * This segment is in the process of being allocated. Wait 43551292Sobrien * until it's done, and look the key up again (in case the 43651292Sobrien * allocation failed or it was freed). 43751292Sobrien */ 43882794Sobrien shmseg->shm_perm.mode |= SHMSEG_WANTED; 43951292Sobrien error = tsleep((caddr_t)shmseg, PLOCK | PCATCH, "shmget", 0); 44051292Sobrien if (error) 44151292Sobrien return error; 44251292Sobrien return EAGAIN; 44338494Sobrien } 44438494Sobrien if ((uap->shmflg & (IPC_CREAT | IPC_EXCL)) == (IPC_CREAT | IPC_EXCL)) 44538494Sobrien return EEXIST; 446174294Sobrien error = ipcperm(p, &shmseg->shm_perm, mode); 447174294Sobrien if (error) 448174294Sobrien return error; 449174294Sobrien if (uap->size && uap->size > shmseg->shm_segsz) 450174294Sobrien return EINVAL; 451174294Sobrien p->p_retval[0] = IXSEQ_TO_IPCID(segnum, shmseg->shm_perm); 452174294Sobrien return 0; 453174294Sobrien} 454174294Sobrien 455174294Sobrienstatic int 456174294Sobrienshmget_allocate_segment(p, uap, mode) 457174294Sobrien struct proc *p; 458174294Sobrien struct shmget_args *uap; 459174294Sobrien int mode; 460174294Sobrien{ 46138494Sobrien int i, segnum, shmid, size; 46238494Sobrien struct ucred *cred = p->p_ucred; 46338494Sobrien struct shmid_ds *shmseg; 46438494Sobrien struct shm_handle *shm_handle; 46538494Sobrien 466174294Sobrien if (uap->size < shminfo.shmmin || uap->size > shminfo.shmmax) 467174294Sobrien return EINVAL; 46838494Sobrien if (shm_nused >= shminfo.shmmni) /* any shmids left? */ 46938494Sobrien return ENOSPC; 47038494Sobrien size = round_page(uap->size); 47138494Sobrien if (shm_committed + btoc(size) > shminfo.shmall) 47238494Sobrien return ENOMEM; 473174294Sobrien if (shm_last_free < 0) { 47438494Sobrien for (i = 0; i < shminfo.shmmni; i++) 47538494Sobrien if (shmsegs[i].shm_perm.mode & SHMSEG_FREE) 476 break; 477 if (i == shminfo.shmmni) 478 panic("shmseg free count inconsistent"); 479 segnum = i; 480 } else { 481 segnum = shm_last_free; 482 shm_last_free = -1; 483 } 484 shmseg = &shmsegs[segnum]; 485 /* 486 * In case we sleep in malloc(), mark the segment present but deleted 487 * so that noone else tries to create the same key. 488 */ 489 shmseg->shm_perm.mode = SHMSEG_ALLOCATED | SHMSEG_REMOVED; 490 shmseg->shm_perm.key = uap->key; 491 shmseg->shm_perm.seq = (shmseg->shm_perm.seq + 1) & 0x7fff; 492 shm_handle = (struct shm_handle *) 493 malloc(sizeof(struct shm_handle), M_SHM, M_WAITOK); 494 shmid = IXSEQ_TO_IPCID(segnum, shmseg->shm_perm); 495 496 /* 497 * We make sure that we have allocated a pager before we need 498 * to. 499 */ 500 shm_handle->shm_object = 501 vm_pager_allocate(OBJT_SWAP, 0, size, VM_PROT_DEFAULT, 0); 502 vm_object_clear_flag(shm_handle->shm_object, OBJ_ONEMAPPING); 503 vm_object_set_flag(shm_handle->shm_object, OBJ_NOSPLIT); 504 505 shmseg->shm_internal = shm_handle; 506 shmseg->shm_perm.cuid = shmseg->shm_perm.uid = cred->cr_uid; 507 shmseg->shm_perm.cgid = shmseg->shm_perm.gid = cred->cr_gid; 508 shmseg->shm_perm.mode = (shmseg->shm_perm.mode & SHMSEG_WANTED) | 509 (mode & ACCESSPERMS) | SHMSEG_ALLOCATED; 510 shmseg->shm_segsz = uap->size; 511 shmseg->shm_cpid = p->p_pid; 512 shmseg->shm_lpid = shmseg->shm_nattch = 0; 513 shmseg->shm_atime = shmseg->shm_dtime = 0; 514 shmseg->shm_ctime = time_second; 515 shm_committed += btoc(size); 516 shm_nused++; 517 if (shmseg->shm_perm.mode & SHMSEG_WANTED) { 518 /* 519 * Somebody else wanted this key while we were asleep. Wake 520 * them up now. 521 */ 522 shmseg->shm_perm.mode &= ~SHMSEG_WANTED; 523 wakeup((caddr_t)shmseg); 524 } 525 p->p_retval[0] = shmid; 526 return 0; 527} 528 529int 530shmget(p, uap) 531 struct proc *p; 532 struct shmget_args *uap; 533{ 534 int segnum, mode, error; 535 536 mode = uap->shmflg & ACCESSPERMS; 537 if (uap->key != IPC_PRIVATE) { 538 again: 539 segnum = shm_find_segment_by_key(uap->key); 540 if (segnum >= 0) { 541 error = shmget_existing(p, uap, mode, segnum); 542 if (error == EAGAIN) 543 goto again; 544 return error; 545 } 546 if ((uap->shmflg & IPC_CREAT) == 0) 547 return ENOENT; 548 } 549 return shmget_allocate_segment(p, uap, mode); 550} 551 552int 553shmsys(p, uap) 554 struct proc *p; 555 /* XXX actually varargs. */ 556 struct shmsys_args /* { 557 u_int which; 558 int a2; 559 int a3; 560 int a4; 561 } */ *uap; 562{ 563 564 if (uap->which >= sizeof(shmcalls)/sizeof(shmcalls[0])) 565 return EINVAL; 566 return ((*shmcalls[uap->which])(p, &uap->a2)); 567} 568 569void 570shmfork(p1, p2) 571 struct proc *p1, *p2; 572{ 573 struct shmmap_state *shmmap_s; 574 size_t size; 575 int i; 576 577 size = shminfo.shmseg * sizeof(struct shmmap_state); 578 shmmap_s = malloc(size, M_SHM, M_WAITOK); 579 bcopy((caddr_t)p1->p_vmspace->vm_shm, (caddr_t)shmmap_s, size); 580 p2->p_vmspace->vm_shm = (caddr_t)shmmap_s; 581 for (i = 0; i < shminfo.shmseg; i++, shmmap_s++) 582 if (shmmap_s->shmid != -1) 583 shmsegs[IPCID_TO_IX(shmmap_s->shmid)].shm_nattch++; 584} 585 586void 587shmexit(p) 588 struct proc *p; 589{ 590 struct shmmap_state *shmmap_s; 591 int i; 592 593 shmmap_s = (struct shmmap_state *)p->p_vmspace->vm_shm; 594 for (i = 0; i < shminfo.shmseg; i++, shmmap_s++) 595 if (shmmap_s->shmid != -1) 596 shm_delete_mapping(p, shmmap_s); 597 free((caddr_t)p->p_vmspace->vm_shm, M_SHM); 598 p->p_vmspace->vm_shm = NULL; 599} 600 601void 602shminit(dummy) 603 void *dummy; 604{ 605 int i; 606 for (i = 0; i < shminfo.shmmni; i++) { 607 shmsegs[i].shm_perm.mode = SHMSEG_FREE; 608 shmsegs[i].shm_perm.seq = 0; 609 } 610 shm_last_free = 0; 611 shm_nused = 0; 612 shm_committed = 0; 613} 614