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