sysv_shm.c revision 69449
162143Sarchie/* $FreeBSD: head/sys/kern/sysv_shm.c 69449 2000-12-01 08:57:47Z alfred $ */
262143Sarchie/*	$NetBSD: sysv_shm.c,v 1.23 1994/07/04 23:25:12 glass Exp $	*/
362143Sarchie
462143Sarchie/*
562143Sarchie * Copyright (c) 1994 Adam Glass and Charles Hannum.  All rights reserved.
662143Sarchie *
762143Sarchie * Redistribution and use in source and binary forms, with or without
862143Sarchie * modification, are permitted provided that the following conditions
962143Sarchie * are met:
1062143Sarchie * 1. Redistributions of source code must retain the above copyright
1162143Sarchie *    notice, this list of conditions and the following disclaimer.
1262143Sarchie * 2. Redistributions in binary form must reproduce the above copyright
1362143Sarchie *    notice, this list of conditions and the following disclaimer in the
1462143Sarchie *    documentation and/or other materials provided with the distribution.
1562143Sarchie * 3. All advertising materials mentioning features or use of this software
1662143Sarchie *    must display the following acknowledgement:
1762143Sarchie *	This product includes software developed by Adam Glass and Charles
1862143Sarchie *	Hannum.
1962143Sarchie * 4. The names of the authors may not be used to endorse or promote products
2062143Sarchie *    derived from this software without specific prior written permission.
2162143Sarchie *
2262143Sarchie * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
2362143Sarchie * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2462143Sarchie * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2562143Sarchie * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
2662143Sarchie * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2762143Sarchie * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2862143Sarchie * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2962143Sarchie * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3062143Sarchie * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3162143Sarchie * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3262143Sarchie */
3362143Sarchie
3462143Sarchie#include "opt_compat.h"
3562143Sarchie#include "opt_rlimit.h"
3662143Sarchie#include "opt_sysvipc.h"
3762143Sarchie
3862143Sarchie#include <sys/param.h>
3962143Sarchie#include <sys/systm.h>
4062143Sarchie#include <sys/sysproto.h>
4162143Sarchie#include <sys/kernel.h>
4262143Sarchie#include <sys/sysctl.h>
4362143Sarchie#include <sys/shm.h>
4462143Sarchie#include <sys/proc.h>
4562143Sarchie#include <sys/malloc.h>
4662143Sarchie#include <sys/mman.h>
4762143Sarchie#include <sys/stat.h>
4862143Sarchie#include <sys/syscall.h>
4962143Sarchie#include <sys/sysent.h>
5062143Sarchie#include <sys/jail.h>
5162143Sarchie
5262143Sarchie#include <vm/vm.h>
5362143Sarchie#include <vm/vm_param.h>
5462143Sarchie#include <sys/lock.h>
5562143Sarchie#include <vm/pmap.h>
5662143Sarchie#include <vm/vm_object.h>
5762143Sarchie#include <vm/vm_map.h>
5862143Sarchie#include <vm/vm_page.h>
5962143Sarchie#include <vm/vm_pager.h>
6062143Sarchie
6162143Sarchiestatic MALLOC_DEFINE(M_SHM, "shm", "SVID compatible shared memory segments");
6262143Sarchie
6362143Sarchiestruct oshmctl_args;
6462143Sarchiestatic int oshmctl __P((struct proc *p, struct oshmctl_args *uap));
6562143Sarchie
6662143Sarchiestatic int shmget_allocate_segment __P((struct proc *p, struct shmget_args *uap, int mode));
6762143Sarchiestatic int shmget_existing __P((struct proc *p, struct shmget_args *uap, int mode, int segnum));
6862143Sarchie
6962143Sarchie/* XXX casting to (sy_call_t *) is bogus, as usual. */
7062143Sarchiestatic sy_call_t *shmcalls[] = {
7162143Sarchie	(sy_call_t *)shmat, (sy_call_t *)oshmctl,
7262143Sarchie	(sy_call_t *)shmdt, (sy_call_t *)shmget,
7362143Sarchie	(sy_call_t *)shmctl
7462143Sarchie};
7562143Sarchie
7662143Sarchie#define	SHMSEG_FREE     	0x0200
7762143Sarchie#define	SHMSEG_REMOVED  	0x0400
7862143Sarchie#define	SHMSEG_ALLOCATED	0x0800
7962143Sarchie#define	SHMSEG_WANTED		0x1000
8062143Sarchie
8162143Sarchiestatic int shm_last_free, shm_nused, shm_committed, shmalloced;
8262143Sarchiestatic struct shmid_ds	*shmsegs;
8362143Sarchie
8462143Sarchiestruct shm_handle {
8562143Sarchie	/* vm_offset_t kva; */
8662143Sarchie	vm_object_t shm_object;
8762143Sarchie};
8862143Sarchie
8962143Sarchiestruct shmmap_state {
9062143Sarchie	vm_offset_t va;
9162143Sarchie	int shmid;
9262143Sarchie};
9362143Sarchie
9462143Sarchiestatic void shm_deallocate_segment __P((struct shmid_ds *));
9562143Sarchiestatic int shm_find_segment_by_key __P((key_t));
9662143Sarchiestatic struct shmid_ds *shm_find_segment_by_shmid __P((int));
9762143Sarchiestatic int shm_delete_mapping __P((struct proc *, struct shmmap_state *));
9862143Sarchiestatic void shmrealloc __P((void));
9962143Sarchiestatic void shminit __P((void));
10062143Sarchiestatic int sysvshm_modload __P((struct module *, int, void *));
10162143Sarchiestatic int shmunload __P((void));
10262143Sarchiestatic void shmexit_myhook __P((struct proc *p));
10362143Sarchiestatic void shmfork_myhook __P((struct proc *p1, struct proc *p2));
10462143Sarchie
10562143Sarchie/*
10662143Sarchie * Tuneable values
10762143Sarchie */
10862143Sarchie#ifndef SHMMAXPGS
10962143Sarchie#define	SHMMAXPGS	1024	/* XXX increase this, it's not in kva! */
11062143Sarchie#endif
11162143Sarchie#ifndef SHMMAX
11262143Sarchie#define	SHMMAX	(SHMMAXPGS*PAGE_SIZE)
11362143Sarchie#endif
11462143Sarchie#ifndef SHMMIN
11562143Sarchie#define	SHMMIN	1
11662143Sarchie#endif
11762143Sarchie#ifndef SHMMNI
11862143Sarchie#define	SHMMNI	96
11962143Sarchie#endif
12062143Sarchie#ifndef SHMSEG
12162143Sarchie#define	SHMSEG	64
12262143Sarchie#endif
12362143Sarchie#ifndef SHMALL
12462143Sarchie#define	SHMALL	(SHMMAXPGS)
12562143Sarchie#endif
12662143Sarchie
12762143Sarchiestruct	shminfo shminfo = {
12862143Sarchie	SHMMAX,
12962143Sarchie	SHMMIN,
13062143Sarchie	SHMMNI,
13162143Sarchie	SHMSEG,
13262143Sarchie	SHMALL
13362143Sarchie};
13462143Sarchie
13562143Sarchiestatic int shm_use_phys;
13662143Sarchie
13762143SarchieSYSCTL_DECL(_kern_ipc);
13862143SarchieSYSCTL_INT(_kern_ipc, OID_AUTO, shmmax, CTLFLAG_RW, &shminfo.shmmax, 0, "");
13962143SarchieSYSCTL_INT(_kern_ipc, OID_AUTO, shmmin, CTLFLAG_RW, &shminfo.shmmin, 0, "");
14062143SarchieSYSCTL_INT(_kern_ipc, OID_AUTO, shmmni, CTLFLAG_RD, &shminfo.shmmni, 0, "");
14162143SarchieSYSCTL_INT(_kern_ipc, OID_AUTO, shmseg, CTLFLAG_RW, &shminfo.shmseg, 0, "");
14262143SarchieSYSCTL_INT(_kern_ipc, OID_AUTO, shmall, CTLFLAG_RW, &shminfo.shmall, 0, "");
14362143SarchieSYSCTL_INT(_kern_ipc, OID_AUTO, shm_use_phys, CTLFLAG_RW, &shm_use_phys, 0, "");
14462143Sarchie
14562143Sarchiestatic int
14662143Sarchieshm_find_segment_by_key(key)
14762143Sarchie	key_t key;
14862143Sarchie{
14962143Sarchie	int i;
15062143Sarchie
15162143Sarchie	for (i = 0; i < shmalloced; i++)
15262143Sarchie		if ((shmsegs[i].shm_perm.mode & SHMSEG_ALLOCATED) &&
15362143Sarchie		    shmsegs[i].shm_perm.key == key)
15462143Sarchie			return i;
15562143Sarchie	return -1;
15662143Sarchie}
15762143Sarchie
15862143Sarchiestatic struct shmid_ds *
15962143Sarchieshm_find_segment_by_shmid(shmid)
16062143Sarchie	int shmid;
16162143Sarchie{
16262143Sarchie	int segnum;
16362143Sarchie	struct shmid_ds *shmseg;
16462143Sarchie
16562143Sarchie	segnum = IPCID_TO_IX(shmid);
16662143Sarchie	if (segnum < 0 || segnum >= shmalloced)
16762143Sarchie		return NULL;
16862143Sarchie	shmseg = &shmsegs[segnum];
16962143Sarchie	if ((shmseg->shm_perm.mode & (SHMSEG_ALLOCATED | SHMSEG_REMOVED))
17062143Sarchie	    != SHMSEG_ALLOCATED ||
17162143Sarchie	    shmseg->shm_perm.seq != IPCID_TO_SEQ(shmid))
17262143Sarchie		return NULL;
17362143Sarchie	return shmseg;
17462143Sarchie}
17562143Sarchie
17662143Sarchiestatic void
17762143Sarchieshm_deallocate_segment(shmseg)
17862143Sarchie	struct shmid_ds *shmseg;
17962143Sarchie{
18062143Sarchie	struct shm_handle *shm_handle;
18162143Sarchie	size_t size;
18262143Sarchie
18362143Sarchie	shm_handle = shmseg->shm_internal;
18462143Sarchie	vm_object_deallocate(shm_handle->shm_object);
18562143Sarchie	free((caddr_t)shm_handle, M_SHM);
18662143Sarchie	shmseg->shm_internal = NULL;
18762143Sarchie	size = round_page(shmseg->shm_segsz);
18862143Sarchie	shm_committed -= btoc(size);
18962143Sarchie	shm_nused--;
19062143Sarchie	shmseg->shm_perm.mode = SHMSEG_FREE;
19162143Sarchie}
19262143Sarchie
19362143Sarchiestatic int
19462143Sarchieshm_delete_mapping(p, shmmap_s)
19562143Sarchie	struct proc *p;
19662143Sarchie	struct shmmap_state *shmmap_s;
19762143Sarchie{
19862143Sarchie	struct shmid_ds *shmseg;
19962143Sarchie	int segnum, result;
20062143Sarchie	size_t size;
20162143Sarchie
20262143Sarchie	segnum = IPCID_TO_IX(shmmap_s->shmid);
20362143Sarchie	shmseg = &shmsegs[segnum];
20462143Sarchie	size = round_page(shmseg->shm_segsz);
20562143Sarchie	result = vm_map_remove(&p->p_vmspace->vm_map, shmmap_s->va, shmmap_s->va + size);
20662143Sarchie	if (result != KERN_SUCCESS)
20762143Sarchie		return EINVAL;
20862143Sarchie	shmmap_s->shmid = -1;
20962143Sarchie	shmseg->shm_dtime = time_second;
21062143Sarchie	if ((--shmseg->shm_nattch <= 0) &&
21162143Sarchie	    (shmseg->shm_perm.mode & SHMSEG_REMOVED)) {
21262143Sarchie		shm_deallocate_segment(shmseg);
21362143Sarchie		shm_last_free = segnum;
21462143Sarchie	}
21562143Sarchie	return 0;
21662143Sarchie}
21762143Sarchie
21862143Sarchie#ifndef _SYS_SYSPROTO_H_
21962143Sarchiestruct shmdt_args {
22062143Sarchie	void *shmaddr;
22162143Sarchie};
22262143Sarchie#endif
22362143Sarchie
22462143Sarchieint
22562143Sarchieshmdt(p, uap)
22662143Sarchie	struct proc *p;
22762143Sarchie	struct shmdt_args *uap;
22862143Sarchie{
22962143Sarchie	struct shmmap_state *shmmap_s;
23062143Sarchie	int i;
23162143Sarchie
23262143Sarchie	if (!jail_sysvipc_allowed && p->p_prison != NULL)
23362143Sarchie		return (ENOSYS);
23462143Sarchie
23562143Sarchie	shmmap_s = (struct shmmap_state *)p->p_vmspace->vm_shm;
23662143Sarchie 	if (shmmap_s == NULL)
23762143Sarchie 	    return EINVAL;
23862143Sarchie	for (i = 0; i < shminfo.shmseg; i++, shmmap_s++)
23962143Sarchie		if (shmmap_s->shmid != -1 &&
24062143Sarchie		    shmmap_s->va == (vm_offset_t)uap->shmaddr)
24162143Sarchie			break;
24262143Sarchie	if (i == shminfo.shmseg)
24362143Sarchie		return EINVAL;
24462143Sarchie	return shm_delete_mapping(p, shmmap_s);
24562143Sarchie}
24662143Sarchie
24762143Sarchie#ifndef _SYS_SYSPROTO_H_
24862143Sarchiestruct shmat_args {
24962143Sarchie	int shmid;
25062143Sarchie	void *shmaddr;
25162143Sarchie	int shmflg;
25262143Sarchie};
25362143Sarchie#endif
25462143Sarchie
25562143Sarchieint
25662143Sarchieshmat(p, uap)
25762143Sarchie	struct proc *p;
25862143Sarchie	struct shmat_args *uap;
25962143Sarchie{
26062143Sarchie	int error, i, flags;
26162143Sarchie	struct shmid_ds *shmseg;
26262143Sarchie	struct shmmap_state *shmmap_s = NULL;
26362143Sarchie	struct shm_handle *shm_handle;
26462143Sarchie	vm_offset_t attach_va;
26562143Sarchie	vm_prot_t prot;
26662143Sarchie	vm_size_t size;
26762143Sarchie	int rv;
26862143Sarchie
26962143Sarchie	if (!jail_sysvipc_allowed && p->p_prison != NULL)
27062143Sarchie		return (ENOSYS);
27162143Sarchie
27262143Sarchie	shmmap_s = (struct shmmap_state *)p->p_vmspace->vm_shm;
27362143Sarchie	if (shmmap_s == NULL) {
27462143Sarchie		size = shminfo.shmseg * sizeof(struct shmmap_state);
27562143Sarchie		shmmap_s = malloc(size, M_SHM, M_WAITOK);
27662143Sarchie		for (i = 0; i < shminfo.shmseg; i++)
27762143Sarchie			shmmap_s[i].shmid = -1;
27862143Sarchie		p->p_vmspace->vm_shm = (caddr_t)shmmap_s;
27962143Sarchie	}
28062143Sarchie	shmseg = shm_find_segment_by_shmid(uap->shmid);
28162143Sarchie	if (shmseg == NULL)
28262143Sarchie		return EINVAL;
28362143Sarchie	error = ipcperm(p, &shmseg->shm_perm,
28462143Sarchie	    (uap->shmflg & SHM_RDONLY) ? IPC_R : IPC_R|IPC_W);
28562143Sarchie	if (error)
28662143Sarchie		return error;
28762143Sarchie	for (i = 0; i < shminfo.shmseg; i++) {
28862143Sarchie		if (shmmap_s->shmid == -1)
28962143Sarchie			break;
29062143Sarchie		shmmap_s++;
29162143Sarchie	}
29262143Sarchie	if (i >= shminfo.shmseg)
29362143Sarchie		return EMFILE;
29462143Sarchie	size = round_page(shmseg->shm_segsz);
29562143Sarchie#ifdef VM_PROT_READ_IS_EXEC
29662143Sarchie	prot = VM_PROT_READ | VM_PROT_EXECUTE;
29762143Sarchie#else
29862143Sarchie	prot = VM_PROT_READ;
29962143Sarchie#endif
30062143Sarchie	if ((uap->shmflg & SHM_RDONLY) == 0)
30162143Sarchie		prot |= VM_PROT_WRITE;
30262143Sarchie	flags = MAP_ANON | MAP_SHARED;
30362143Sarchie	if (uap->shmaddr) {
30462143Sarchie		flags |= MAP_FIXED;
30562143Sarchie		if (uap->shmflg & SHM_RND)
30662143Sarchie			attach_va = (vm_offset_t)uap->shmaddr & ~(SHMLBA-1);
30762143Sarchie		else if (((vm_offset_t)uap->shmaddr & (SHMLBA-1)) == 0)
30862143Sarchie			attach_va = (vm_offset_t)uap->shmaddr;
30962143Sarchie		else
31062143Sarchie			return EINVAL;
31162143Sarchie	} else {
31262143Sarchie		/* This is just a hint to vm_map_find() about where to put it. */
31362143Sarchie		attach_va = round_page((vm_offset_t)p->p_vmspace->vm_taddr + MAXTSIZ + MAXDSIZ);
31462143Sarchie	}
31562143Sarchie
31662143Sarchie	shm_handle = shmseg->shm_internal;
31762143Sarchie	vm_object_reference(shm_handle->shm_object);
31862143Sarchie	rv = vm_map_find(&p->p_vmspace->vm_map, shm_handle->shm_object,
31962143Sarchie		0, &attach_va, size, (flags & MAP_FIXED)?0:1, prot, prot, 0);
32062143Sarchie	if (rv != KERN_SUCCESS) {
32162143Sarchie		return ENOMEM;
32262143Sarchie	}
32362143Sarchie	vm_map_inherit(&p->p_vmspace->vm_map,
32462143Sarchie		attach_va, attach_va + size, VM_INHERIT_SHARE);
32562143Sarchie
32662143Sarchie	shmmap_s->va = attach_va;
32762143Sarchie	shmmap_s->shmid = uap->shmid;
32862143Sarchie	shmseg->shm_lpid = p->p_pid;
32962143Sarchie	shmseg->shm_atime = time_second;
33062143Sarchie	shmseg->shm_nattch++;
33162143Sarchie	p->p_retval[0] = attach_va;
33262143Sarchie	return 0;
33362143Sarchie}
33462143Sarchie
33562143Sarchiestruct oshmid_ds {
33662143Sarchie	struct	ipc_perm shm_perm;	/* operation perms */
33762143Sarchie	int	shm_segsz;		/* size of segment (bytes) */
33862143Sarchie	ushort	shm_cpid;		/* pid, creator */
33962143Sarchie	ushort	shm_lpid;		/* pid, last operation */
34062143Sarchie	short	shm_nattch;		/* no. of current attaches */
34162143Sarchie	time_t	shm_atime;		/* last attach time */
34262143Sarchie	time_t	shm_dtime;		/* last detach time */
34362143Sarchie	time_t	shm_ctime;		/* last change time */
34462143Sarchie	void	*shm_handle;		/* internal handle for shm segment */
34562143Sarchie};
34662143Sarchie
34762143Sarchiestruct oshmctl_args {
34862143Sarchie	int shmid;
34962143Sarchie	int cmd;
35062143Sarchie	struct oshmid_ds *ubuf;
35162143Sarchie};
35262143Sarchie
35362143Sarchiestatic int
35462143Sarchieoshmctl(p, uap)
35562143Sarchie	struct proc *p;
35662143Sarchie	struct oshmctl_args *uap;
35762143Sarchie{
35862143Sarchie#ifdef COMPAT_43
35962143Sarchie	int error;
36062143Sarchie	struct shmid_ds *shmseg;
36162143Sarchie	struct oshmid_ds outbuf;
36262143Sarchie
36362143Sarchie	if (!jail_sysvipc_allowed && p->p_prison != NULL)
36462143Sarchie		return (ENOSYS);
36562143Sarchie
36662143Sarchie	shmseg = shm_find_segment_by_shmid(uap->shmid);
36762143Sarchie	if (shmseg == NULL)
36862143Sarchie		return EINVAL;
36962143Sarchie	switch (uap->cmd) {
37062143Sarchie	case IPC_STAT:
37162143Sarchie		error = ipcperm(p, &shmseg->shm_perm, IPC_R);
37262143Sarchie		if (error)
37362143Sarchie			return error;
37462143Sarchie		outbuf.shm_perm = shmseg->shm_perm;
37562143Sarchie		outbuf.shm_segsz = shmseg->shm_segsz;
37662143Sarchie		outbuf.shm_cpid = shmseg->shm_cpid;
37762143Sarchie		outbuf.shm_lpid = shmseg->shm_lpid;
37862143Sarchie		outbuf.shm_nattch = shmseg->shm_nattch;
37962143Sarchie		outbuf.shm_atime = shmseg->shm_atime;
38062143Sarchie		outbuf.shm_dtime = shmseg->shm_dtime;
38162143Sarchie		outbuf.shm_ctime = shmseg->shm_ctime;
38262143Sarchie		outbuf.shm_handle = shmseg->shm_internal;
38362143Sarchie		error = copyout((caddr_t)&outbuf, uap->ubuf, sizeof(outbuf));
38462143Sarchie		if (error)
38562143Sarchie			return error;
38662143Sarchie		break;
38762143Sarchie	default:
38862143Sarchie		/* XXX casting to (sy_call_t *) is bogus, as usual. */
38962143Sarchie		return ((sy_call_t *)shmctl)(p, uap);
39062143Sarchie	}
39162143Sarchie	return 0;
39262143Sarchie#else
39362143Sarchie	return EINVAL;
39462143Sarchie#endif
39562143Sarchie}
39662143Sarchie
39762143Sarchie#ifndef _SYS_SYSPROTO_H_
39862143Sarchiestruct shmctl_args {
39962143Sarchie	int shmid;
40062143Sarchie	int cmd;
40162143Sarchie	struct shmid_ds *buf;
40262143Sarchie};
40362143Sarchie#endif
40462143Sarchie
40562143Sarchieint
40662143Sarchieshmctl(p, uap)
40762143Sarchie	struct proc *p;
40862143Sarchie	struct shmctl_args *uap;
40962143Sarchie{
41062143Sarchie	int error;
41162143Sarchie	struct shmid_ds inbuf;
41262143Sarchie	struct shmid_ds *shmseg;
41362143Sarchie
41462143Sarchie	if (!jail_sysvipc_allowed && p->p_prison != NULL)
41562143Sarchie		return (ENOSYS);
41662143Sarchie
41762143Sarchie	shmseg = shm_find_segment_by_shmid(uap->shmid);
41862143Sarchie	if (shmseg == NULL)
41962143Sarchie		return EINVAL;
42062143Sarchie	switch (uap->cmd) {
42162143Sarchie	case IPC_STAT:
42262143Sarchie		error = ipcperm(p, &shmseg->shm_perm, IPC_R);
42362143Sarchie		if (error)
42462143Sarchie			return error;
42562143Sarchie		error = copyout((caddr_t)shmseg, uap->buf, sizeof(inbuf));
42662143Sarchie		if (error)
42762143Sarchie			return error;
42862143Sarchie		break;
42962143Sarchie	case IPC_SET:
43062143Sarchie		error = ipcperm(p, &shmseg->shm_perm, IPC_M);
43162143Sarchie		if (error)
43262143Sarchie			return error;
43362143Sarchie		error = copyin(uap->buf, (caddr_t)&inbuf, sizeof(inbuf));
43462143Sarchie		if (error)
43562143Sarchie			return error;
43662143Sarchie		shmseg->shm_perm.uid = inbuf.shm_perm.uid;
43762143Sarchie		shmseg->shm_perm.gid = inbuf.shm_perm.gid;
43862143Sarchie		shmseg->shm_perm.mode =
43962143Sarchie		    (shmseg->shm_perm.mode & ~ACCESSPERMS) |
44062143Sarchie		    (inbuf.shm_perm.mode & ACCESSPERMS);
44162143Sarchie		shmseg->shm_ctime = time_second;
44262143Sarchie		break;
44362143Sarchie	case IPC_RMID:
44462143Sarchie		error = ipcperm(p, &shmseg->shm_perm, IPC_M);
44562143Sarchie		if (error)
44662143Sarchie			return error;
44762143Sarchie		shmseg->shm_perm.key = IPC_PRIVATE;
44862143Sarchie		shmseg->shm_perm.mode |= SHMSEG_REMOVED;
44962143Sarchie		if (shmseg->shm_nattch <= 0) {
45062143Sarchie			shm_deallocate_segment(shmseg);
45162143Sarchie			shm_last_free = IPCID_TO_IX(uap->shmid);
45262143Sarchie		}
45362143Sarchie		break;
45462143Sarchie#if 0
45562143Sarchie	case SHM_LOCK:
45662143Sarchie	case SHM_UNLOCK:
45762143Sarchie#endif
45862143Sarchie	default:
45962143Sarchie		return EINVAL;
46062143Sarchie	}
46162143Sarchie	return 0;
46262143Sarchie}
46362143Sarchie
46462143Sarchie#ifndef _SYS_SYSPROTO_H_
46562143Sarchiestruct shmget_args {
46662143Sarchie	key_t key;
46762143Sarchie	size_t size;
46862143Sarchie	int shmflg;
46962143Sarchie};
47062143Sarchie#endif
47162143Sarchie
47262143Sarchiestatic int
47362143Sarchieshmget_existing(p, uap, mode, segnum)
47462143Sarchie	struct proc *p;
47562143Sarchie	struct shmget_args *uap;
47662143Sarchie	int mode;
47762143Sarchie	int segnum;
47862143Sarchie{
47962143Sarchie	struct shmid_ds *shmseg;
48062143Sarchie	int error;
48162143Sarchie
48262143Sarchie	shmseg = &shmsegs[segnum];
48362143Sarchie	if (shmseg->shm_perm.mode & SHMSEG_REMOVED) {
48462143Sarchie		/*
48562143Sarchie		 * This segment is in the process of being allocated.  Wait
48662143Sarchie		 * until it's done, and look the key up again (in case the
48762143Sarchie		 * allocation failed or it was freed).
48862143Sarchie		 */
48962143Sarchie		shmseg->shm_perm.mode |= SHMSEG_WANTED;
49062143Sarchie		error = tsleep((caddr_t)shmseg, PLOCK | PCATCH, "shmget", 0);
49162143Sarchie		if (error)
49262143Sarchie			return error;
49362143Sarchie		return EAGAIN;
49462143Sarchie	}
49562143Sarchie	if ((uap->shmflg & (IPC_CREAT | IPC_EXCL)) == (IPC_CREAT | IPC_EXCL))
49662143Sarchie		return EEXIST;
49762143Sarchie	error = ipcperm(p, &shmseg->shm_perm, mode);
49862143Sarchie	if (error)
49962143Sarchie		return error;
50062143Sarchie	if (uap->size && uap->size > shmseg->shm_segsz)
50162143Sarchie		return EINVAL;
50262143Sarchie	p->p_retval[0] = IXSEQ_TO_IPCID(segnum, shmseg->shm_perm);
50362143Sarchie	return 0;
50462143Sarchie}
50562143Sarchie
50662143Sarchiestatic int
50762143Sarchieshmget_allocate_segment(p, uap, mode)
50862143Sarchie	struct proc *p;
50962143Sarchie	struct shmget_args *uap;
51062143Sarchie	int mode;
51162143Sarchie{
51262143Sarchie	int i, segnum, shmid, size;
51362143Sarchie	struct ucred *cred = p->p_ucred;
51462143Sarchie	struct shmid_ds *shmseg;
51562143Sarchie	struct shm_handle *shm_handle;
51662143Sarchie
51762143Sarchie	if (uap->size < shminfo.shmmin || uap->size > shminfo.shmmax)
51862143Sarchie		return EINVAL;
51962143Sarchie	if (shm_nused >= shminfo.shmmni) /* any shmids left? */
52062143Sarchie		return ENOSPC;
52162143Sarchie	size = round_page(uap->size);
52262143Sarchie	if (shm_committed + btoc(size) > shminfo.shmall)
52362143Sarchie		return ENOMEM;
52462143Sarchie	if (shm_last_free < 0) {
52562143Sarchie		shmrealloc();	/* maybe expand the shmsegs[] array */
52662143Sarchie		for (i = 0; i < shmalloced; i++)
52762143Sarchie			if (shmsegs[i].shm_perm.mode & SHMSEG_FREE)
52862143Sarchie				break;
52962143Sarchie		if (i == shmalloced)
53062143Sarchie			return ENOSPC;
53162143Sarchie		segnum = i;
53262143Sarchie	} else  {
53362143Sarchie		segnum = shm_last_free;
53462143Sarchie		shm_last_free = -1;
53562143Sarchie	}
53662143Sarchie	shmseg = &shmsegs[segnum];
53762143Sarchie	/*
53862143Sarchie	 * In case we sleep in malloc(), mark the segment present but deleted
53962143Sarchie	 * so that noone else tries to create the same key.
54062143Sarchie	 */
54162143Sarchie	shmseg->shm_perm.mode = SHMSEG_ALLOCATED | SHMSEG_REMOVED;
54262143Sarchie	shmseg->shm_perm.key = uap->key;
54362143Sarchie	shmseg->shm_perm.seq = (shmseg->shm_perm.seq + 1) & 0x7fff;
54462143Sarchie	shm_handle = (struct shm_handle *)
54562143Sarchie	    malloc(sizeof(struct shm_handle), M_SHM, M_WAITOK);
54662143Sarchie	shmid = IXSEQ_TO_IPCID(segnum, shmseg->shm_perm);
54762143Sarchie
54862143Sarchie	/*
54962143Sarchie	 * We make sure that we have allocated a pager before we need
55062143Sarchie	 * to.
55162143Sarchie	 */
55262143Sarchie	if (shm_use_phys) {
55362143Sarchie		shm_handle->shm_object =
55462143Sarchie		    vm_pager_allocate(OBJT_PHYS, 0, size, VM_PROT_DEFAULT, 0);
55562143Sarchie	} else {
55662143Sarchie		shm_handle->shm_object =
55762143Sarchie		    vm_pager_allocate(OBJT_SWAP, 0, size, VM_PROT_DEFAULT, 0);
55862143Sarchie	}
55962143Sarchie	vm_object_clear_flag(shm_handle->shm_object, OBJ_ONEMAPPING);
56062143Sarchie	vm_object_set_flag(shm_handle->shm_object, OBJ_NOSPLIT);
56162143Sarchie
56262143Sarchie	shmseg->shm_internal = shm_handle;
56362143Sarchie	shmseg->shm_perm.cuid = shmseg->shm_perm.uid = cred->cr_uid;
56462143Sarchie	shmseg->shm_perm.cgid = shmseg->shm_perm.gid = cred->cr_gid;
56562143Sarchie	shmseg->shm_perm.mode = (shmseg->shm_perm.mode & SHMSEG_WANTED) |
56662143Sarchie	    (mode & ACCESSPERMS) | SHMSEG_ALLOCATED;
56762143Sarchie	shmseg->shm_segsz = uap->size;
56862143Sarchie	shmseg->shm_cpid = p->p_pid;
56962143Sarchie	shmseg->shm_lpid = shmseg->shm_nattch = 0;
57062143Sarchie	shmseg->shm_atime = shmseg->shm_dtime = 0;
57162143Sarchie	shmseg->shm_ctime = time_second;
57262143Sarchie	shm_committed += btoc(size);
57362143Sarchie	shm_nused++;
57462143Sarchie	if (shmseg->shm_perm.mode & SHMSEG_WANTED) {
57562143Sarchie		/*
57662143Sarchie		 * Somebody else wanted this key while we were asleep.  Wake
57762143Sarchie		 * them up now.
57862143Sarchie		 */
57962143Sarchie		shmseg->shm_perm.mode &= ~SHMSEG_WANTED;
58062143Sarchie		wakeup((caddr_t)shmseg);
58162143Sarchie	}
58262143Sarchie	p->p_retval[0] = shmid;
58362143Sarchie	return 0;
58462143Sarchie}
58562143Sarchie
58662143Sarchieint
58762143Sarchieshmget(p, uap)
58862143Sarchie	struct proc *p;
58962143Sarchie	struct shmget_args *uap;
59062143Sarchie{
59162143Sarchie	int segnum, mode, error;
59262143Sarchie
59362143Sarchie	if (!jail_sysvipc_allowed && p->p_prison != NULL)
59462143Sarchie		return (ENOSYS);
59562143Sarchie
59662143Sarchie	mode = uap->shmflg & ACCESSPERMS;
59762143Sarchie	if (uap->key != IPC_PRIVATE) {
59862143Sarchie	again:
59962143Sarchie		segnum = shm_find_segment_by_key(uap->key);
60062143Sarchie		if (segnum >= 0) {
60162143Sarchie			error = shmget_existing(p, uap, mode, segnum);
60262143Sarchie			if (error == EAGAIN)
60362143Sarchie				goto again;
60462143Sarchie			return error;
60562143Sarchie		}
60662143Sarchie		if ((uap->shmflg & IPC_CREAT) == 0)
60762143Sarchie			return ENOENT;
60862143Sarchie	}
60962143Sarchie	return shmget_allocate_segment(p, uap, mode);
61062143Sarchie}
61162143Sarchie
61262143Sarchieint
61362143Sarchieshmsys(p, uap)
61462143Sarchie	struct proc *p;
61562143Sarchie	/* XXX actually varargs. */
61662143Sarchie	struct shmsys_args /* {
61762143Sarchie		u_int	which;
61862143Sarchie		int	a2;
61962143Sarchie		int	a3;
62062143Sarchie		int	a4;
62162143Sarchie	} */ *uap;
62262143Sarchie{
62362143Sarchie
62462143Sarchie	if (!jail_sysvipc_allowed && p->p_prison != NULL)
62562143Sarchie		return (ENOSYS);
62662143Sarchie
62762143Sarchie	if (uap->which >= sizeof(shmcalls)/sizeof(shmcalls[0]))
62862143Sarchie		return EINVAL;
62962143Sarchie	return ((*shmcalls[uap->which])(p, &uap->a2));
63062143Sarchie}
63162143Sarchie
63262143Sarchiestatic void
63362143Sarchieshmfork_myhook(p1, p2)
634	struct proc *p1, *p2;
635{
636	struct shmmap_state *shmmap_s;
637	size_t size;
638	int i;
639
640	size = shminfo.shmseg * sizeof(struct shmmap_state);
641	shmmap_s = malloc(size, M_SHM, M_WAITOK);
642	bcopy((caddr_t)p1->p_vmspace->vm_shm, (caddr_t)shmmap_s, size);
643	p2->p_vmspace->vm_shm = (caddr_t)shmmap_s;
644	for (i = 0; i < shminfo.shmseg; i++, shmmap_s++)
645		if (shmmap_s->shmid != -1)
646			shmsegs[IPCID_TO_IX(shmmap_s->shmid)].shm_nattch++;
647}
648
649static void
650shmexit_myhook(p)
651	struct proc *p;
652{
653	struct shmmap_state *shmmap_s;
654	int i;
655
656	shmmap_s = (struct shmmap_state *)p->p_vmspace->vm_shm;
657	for (i = 0; i < shminfo.shmseg; i++, shmmap_s++)
658		if (shmmap_s->shmid != -1)
659			shm_delete_mapping(p, shmmap_s);
660	free((caddr_t)p->p_vmspace->vm_shm, M_SHM);
661	p->p_vmspace->vm_shm = NULL;
662}
663
664static void
665shmrealloc(void)
666{
667	int i;
668	struct shmid_ds *newsegs;
669
670	if (shmalloced >= shminfo.shmmni)
671		return;
672
673	newsegs = malloc(shminfo.shmmni * sizeof(*newsegs), M_SHM, M_WAITOK);
674	if (newsegs == NULL)
675		return;
676	for (i = 0; i < shmalloced; i++)
677		bcopy(&shmsegs[i], &newsegs[i], sizeof(newsegs[0]));
678	for (; i < shminfo.shmmni; i++) {
679		shmsegs[i].shm_perm.mode = SHMSEG_FREE;
680		shmsegs[i].shm_perm.seq = 0;
681	}
682	free(shmsegs, M_SHM);
683	shmsegs = newsegs;
684	shmalloced = shminfo.shmmni;
685}
686
687static void
688shminit()
689{
690	int i;
691
692	shmalloced = shminfo.shmmni;
693	shmsegs = malloc(shmalloced * sizeof(shmsegs[0]), M_SHM, M_WAITOK);
694	if (shmsegs == NULL)
695		panic("cannot allocate initial memory for sysvshm");
696	for (i = 0; i < shmalloced; i++) {
697		shmsegs[i].shm_perm.mode = SHMSEG_FREE;
698		shmsegs[i].shm_perm.seq = 0;
699	}
700	shm_last_free = 0;
701	shm_nused = 0;
702	shm_committed = 0;
703	shmexit_hook = &shmexit_myhook;
704	shmfork_hook = &shmfork_myhook;
705}
706
707static int
708shmunload()
709{
710
711	if (shm_nused > 0)
712		return (EBUSY);
713
714	free(shmsegs, M_SHM);
715	shmexit_hook = NULL;
716	shmfork_hook = NULL;
717	return (0);
718}
719
720static int
721sysvshm_modload(struct module *module, int cmd, void *arg)
722{
723	int error = 0;
724
725	switch (cmd) {
726	case MOD_LOAD:
727		shminit();
728		break;
729	case MOD_UNLOAD:
730		error = shmunload();
731		break;
732	case MOD_SHUTDOWN:
733		break;
734	default:
735		error = EINVAL;
736		break;
737	}
738	return (error);
739}
740
741static moduledata_t sysvshm_moduledata = {
742	"sysvshm_mod",
743	&sysvshm_modload,
744	NULL
745};
746
747SYSCALL_MODULE_HELPER(shmsys, 4);
748SYSCALL_MODULE_HELPER(shmat, 3);
749SYSCALL_MODULE_HELPER(shmctl, 3);
750SYSCALL_MODULE_HELPER(shmdt, 1);
751SYSCALL_MODULE_HELPER(shmget, 3);
752
753DECLARE_MODULE(sysvshm_mod, sysvshm_moduledata,
754	SI_SUB_SYSV_SHM, SI_ORDER_FIRST);
755