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