sysv_shm.c revision 77104
1/* $FreeBSD: head/sys/kern/sysv_shm.c 77104 2001-05-23 23:38:05Z dd $ */
2/*	$NetBSD: sysv_shm.c,v 1.23 1994/07/04 23:25:12 glass Exp $	*/
3
4/*
5 * Copyright (c) 1994 Adam Glass and Charles Hannum.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 *    must display the following acknowledgement:
17 *	This product includes software developed by Adam Glass and Charles
18 *	Hannum.
19 * 4. The names of the authors may not be used to endorse or promote products
20 *    derived from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34#include "opt_compat.h"
35#include "opt_rlimit.h"
36#include "opt_sysvipc.h"
37
38#include <sys/param.h>
39#include <sys/systm.h>
40#include <sys/kernel.h>
41#include <sys/lock.h>
42#include <sys/sysctl.h>
43#include <sys/shm.h>
44#include <sys/proc.h>
45#include <sys/malloc.h>
46#include <sys/mman.h>
47#include <sys/mutex.h>
48#include <sys/stat.h>
49#include <sys/syscall.h>
50#include <sys/sysent.h>
51#include <sys/sysproto.h>
52#include <sys/jail.h>
53
54#include <vm/vm.h>
55#include <vm/vm_param.h>
56#include <vm/pmap.h>
57#include <vm/vm_object.h>
58#include <vm/vm_map.h>
59#include <vm/vm_page.h>
60#include <vm/vm_pager.h>
61
62static MALLOC_DEFINE(M_SHM, "shm", "SVID compatible shared memory segments");
63
64struct oshmctl_args;
65static int oshmctl __P((struct proc *p, struct oshmctl_args *uap));
66
67static int shmget_allocate_segment __P((struct proc *p,
68    struct shmget_args *uap, int mode));
69static int shmget_existing __P((struct proc *p, struct shmget_args *uap,
70    int mode, int segnum));
71
72/* XXX casting to (sy_call_t *) is bogus, as usual. */
73static sy_call_t *shmcalls[] = {
74	(sy_call_t *)shmat, (sy_call_t *)oshmctl,
75	(sy_call_t *)shmdt, (sy_call_t *)shmget,
76	(sy_call_t *)shmctl
77};
78
79#define	SHMSEG_FREE     	0x0200
80#define	SHMSEG_REMOVED  	0x0400
81#define	SHMSEG_ALLOCATED	0x0800
82#define	SHMSEG_WANTED		0x1000
83
84static int shm_last_free, shm_nused, shm_committed, shmalloced;
85static struct shmid_ds	*shmsegs;
86
87struct shm_handle {
88	/* vm_offset_t kva; */
89	vm_object_t shm_object;
90};
91
92struct shmmap_state {
93	vm_offset_t va;
94	int shmid;
95};
96
97static void shm_deallocate_segment __P((struct shmid_ds *));
98static int shm_find_segment_by_key __P((key_t));
99static struct shmid_ds *shm_find_segment_by_shmid __P((int));
100static int shm_delete_mapping __P((struct proc *, struct shmmap_state *));
101static void shmrealloc __P((void));
102static void shminit __P((void));
103static int sysvshm_modload __P((struct module *, int, void *));
104static int shmunload __P((void));
105static void shmexit_myhook __P((struct proc *p));
106static void shmfork_myhook __P((struct proc *p1, struct proc *p2));
107
108/*
109 * Tuneable values.
110 */
111#ifndef SHMMAXPGS
112#define	SHMMAXPGS	8192	/* Note: sysv shared memory is swap backed. */
113#endif
114#ifndef SHMMAX
115#define	SHMMAX	(SHMMAXPGS*PAGE_SIZE)
116#endif
117#ifndef SHMMIN
118#define	SHMMIN	1
119#endif
120#ifndef SHMMNI
121#define	SHMMNI	192
122#endif
123#ifndef SHMSEG
124#define	SHMSEG	128
125#endif
126#ifndef SHMALL
127#define	SHMALL	(SHMMAXPGS)
128#endif
129
130struct	shminfo shminfo = {
131	SHMMAX,
132	SHMMIN,
133	SHMMNI,
134	SHMSEG,
135	SHMALL
136};
137
138static int shm_use_phys;
139
140SYSCTL_DECL(_kern_ipc);
141SYSCTL_INT(_kern_ipc, OID_AUTO, shmmax, CTLFLAG_RW, &shminfo.shmmax, 0, "");
142SYSCTL_INT(_kern_ipc, OID_AUTO, shmmin, CTLFLAG_RW, &shminfo.shmmin, 0, "");
143SYSCTL_INT(_kern_ipc, OID_AUTO, shmmni, CTLFLAG_RD, &shminfo.shmmni, 0, "");
144SYSCTL_INT(_kern_ipc, OID_AUTO, shmseg, CTLFLAG_RD, &shminfo.shmseg, 0, "");
145SYSCTL_INT(_kern_ipc, OID_AUTO, shmall, CTLFLAG_RW, &shminfo.shmall, 0, "");
146SYSCTL_INT(_kern_ipc, OID_AUTO, shm_use_phys, CTLFLAG_RW,
147    &shm_use_phys, 0, "");
148
149static int
150shm_find_segment_by_key(key)
151	key_t key;
152{
153	int i;
154
155	for (i = 0; i < shmalloced; i++)
156		if ((shmsegs[i].shm_perm.mode & SHMSEG_ALLOCATED) &&
157		    shmsegs[i].shm_perm.key == key)
158			return i;
159	return -1;
160}
161
162static struct shmid_ds *
163shm_find_segment_by_shmid(shmid)
164	int shmid;
165{
166	int segnum;
167	struct shmid_ds *shmseg;
168
169	segnum = IPCID_TO_IX(shmid);
170	if (segnum < 0 || segnum >= shmalloced)
171		return NULL;
172	shmseg = &shmsegs[segnum];
173	if ((shmseg->shm_perm.mode & (SHMSEG_ALLOCATED | SHMSEG_REMOVED))
174	    != SHMSEG_ALLOCATED ||
175	    shmseg->shm_perm.seq != IPCID_TO_SEQ(shmid))
176		return NULL;
177	return shmseg;
178}
179
180static void
181shm_deallocate_segment(shmseg)
182	struct shmid_ds *shmseg;
183{
184	struct shm_handle *shm_handle;
185	size_t size;
186
187	mtx_assert(&vm_mtx, MA_OWNED);		/* For vm_object_deallocate. */
188	shm_handle = shmseg->shm_internal;
189	vm_object_deallocate(shm_handle->shm_object);
190	free((caddr_t)shm_handle, M_SHM);
191	shmseg->shm_internal = NULL;
192	size = round_page(shmseg->shm_segsz);
193	shm_committed -= btoc(size);
194	shm_nused--;
195	shmseg->shm_perm.mode = SHMSEG_FREE;
196}
197
198static int
199shm_delete_mapping(p, shmmap_s)
200	struct proc *p;
201	struct shmmap_state *shmmap_s;
202{
203	struct shmid_ds *shmseg;
204	int segnum, result;
205	size_t size;
206
207	/* For vm_map_remove and shm_deallocate_segment. */
208	mtx_assert(&vm_mtx, MA_OWNED);
209
210	segnum = IPCID_TO_IX(shmmap_s->shmid);
211	shmseg = &shmsegs[segnum];
212	size = round_page(shmseg->shm_segsz);
213	result = vm_map_remove(&p->p_vmspace->vm_map, shmmap_s->va,
214	    shmmap_s->va + size);
215	if (result != KERN_SUCCESS)
216		return EINVAL;
217	shmmap_s->shmid = -1;
218	shmseg->shm_dtime = time_second;
219	if ((--shmseg->shm_nattch <= 0) &&
220	    (shmseg->shm_perm.mode & SHMSEG_REMOVED)) {
221		shm_deallocate_segment(shmseg);
222		shm_last_free = segnum;
223	}
224	return 0;
225}
226
227#ifndef _SYS_SYSPROTO_H_
228struct shmdt_args {
229	void *shmaddr;
230};
231#endif
232
233int
234shmdt(p, uap)
235	struct proc *p;
236	struct shmdt_args *uap;
237{
238	struct shmmap_state *shmmap_s;
239	int i;
240	int error;
241
242	if (!jail_sysvipc_allowed && jailed(p->p_ucred))
243		return (ENOSYS);
244
245	shmmap_s = (struct shmmap_state *)p->p_vmspace->vm_shm;
246 	if (shmmap_s == NULL)
247 	    return EINVAL;
248	for (i = 0; i < shminfo.shmseg; i++, shmmap_s++)
249		if (shmmap_s->shmid != -1 &&
250		    shmmap_s->va == (vm_offset_t)uap->shmaddr)
251			break;
252	if (i == shminfo.shmseg)
253		return EINVAL;
254	mtx_lock(&vm_mtx);
255	error = shm_delete_mapping(p, shmmap_s);
256	mtx_unlock(&vm_mtx);
257	return error;
258}
259
260#ifndef _SYS_SYSPROTO_H_
261struct shmat_args {
262	int shmid;
263	void *shmaddr;
264	int shmflg;
265};
266#endif
267
268int
269shmat(p, uap)
270	struct proc *p;
271	struct shmat_args *uap;
272{
273	int error, i, flags;
274	struct shmid_ds *shmseg;
275	struct shmmap_state *shmmap_s = NULL;
276	struct shm_handle *shm_handle;
277	vm_offset_t attach_va;
278	vm_prot_t prot;
279	vm_size_t size;
280	int rv;
281
282	if (!jail_sysvipc_allowed && jailed(p->p_ucred))
283		return (ENOSYS);
284
285	shmmap_s = (struct shmmap_state *)p->p_vmspace->vm_shm;
286	if (shmmap_s == NULL) {
287		size = shminfo.shmseg * sizeof(struct shmmap_state);
288		shmmap_s = malloc(size, M_SHM, M_WAITOK);
289		for (i = 0; i < shminfo.shmseg; i++)
290			shmmap_s[i].shmid = -1;
291		p->p_vmspace->vm_shm = (caddr_t)shmmap_s;
292	}
293	shmseg = shm_find_segment_by_shmid(uap->shmid);
294	if (shmseg == NULL)
295		return EINVAL;
296	error = ipcperm(p, &shmseg->shm_perm,
297	    (uap->shmflg & SHM_RDONLY) ? IPC_R : IPC_R|IPC_W);
298	if (error)
299		return error;
300	for (i = 0; i < shminfo.shmseg; i++) {
301		if (shmmap_s->shmid == -1)
302			break;
303		shmmap_s++;
304	}
305	if (i >= shminfo.shmseg)
306		return EMFILE;
307	size = round_page(shmseg->shm_segsz);
308#ifdef VM_PROT_READ_IS_EXEC
309	prot = VM_PROT_READ | VM_PROT_EXECUTE;
310#else
311	prot = VM_PROT_READ;
312#endif
313	if ((uap->shmflg & SHM_RDONLY) == 0)
314		prot |= VM_PROT_WRITE;
315	flags = MAP_ANON | MAP_SHARED;
316	if (uap->shmaddr) {
317		flags |= MAP_FIXED;
318		if (uap->shmflg & SHM_RND)
319			attach_va = (vm_offset_t)uap->shmaddr & ~(SHMLBA-1);
320		else if (((vm_offset_t)uap->shmaddr & (SHMLBA-1)) == 0)
321			attach_va = (vm_offset_t)uap->shmaddr;
322		else
323			return EINVAL;
324	} else {
325		/*
326		 * This is just a hint to vm_map_find() about where to
327		 * put it.
328		 */
329		attach_va = round_page((vm_offset_t)p->p_vmspace->vm_taddr
330		    + MAXTSIZ + MAXDSIZ);
331	}
332
333	shm_handle = shmseg->shm_internal;
334	mtx_lock(&vm_mtx);
335	vm_object_reference(shm_handle->shm_object);
336	rv = vm_map_find(&p->p_vmspace->vm_map, shm_handle->shm_object,
337		0, &attach_va, size, (flags & MAP_FIXED)?0:1, prot, prot, 0);
338	if (rv != KERN_SUCCESS) {
339		mtx_unlock(&vm_mtx);
340		return ENOMEM;
341	}
342	vm_map_inherit(&p->p_vmspace->vm_map,
343		attach_va, attach_va + size, VM_INHERIT_SHARE);
344	mtx_unlock(&vm_mtx);
345
346	shmmap_s->va = attach_va;
347	shmmap_s->shmid = uap->shmid;
348	shmseg->shm_lpid = p->p_pid;
349	shmseg->shm_atime = time_second;
350	shmseg->shm_nattch++;
351	p->p_retval[0] = attach_va;
352	return 0;
353}
354
355struct oshmid_ds {
356	struct	ipc_perm shm_perm;	/* operation perms */
357	int	shm_segsz;		/* size of segment (bytes) */
358	ushort	shm_cpid;		/* pid, creator */
359	ushort	shm_lpid;		/* pid, last operation */
360	short	shm_nattch;		/* no. of current attaches */
361	time_t	shm_atime;		/* last attach time */
362	time_t	shm_dtime;		/* last detach time */
363	time_t	shm_ctime;		/* last change time */
364	void	*shm_handle;		/* internal handle for shm segment */
365};
366
367struct oshmctl_args {
368	int shmid;
369	int cmd;
370	struct oshmid_ds *ubuf;
371};
372
373static int
374oshmctl(p, uap)
375	struct proc *p;
376	struct oshmctl_args *uap;
377{
378#ifdef COMPAT_43
379	int error;
380	struct shmid_ds *shmseg;
381	struct oshmid_ds outbuf;
382
383	if (!jail_sysvipc_allowed && jailed(p->p_ucred))
384		return (ENOSYS);
385
386	shmseg = shm_find_segment_by_shmid(uap->shmid);
387	if (shmseg == NULL)
388		return EINVAL;
389	switch (uap->cmd) {
390	case IPC_STAT:
391		error = ipcperm(p, &shmseg->shm_perm, IPC_R);
392		if (error)
393			return error;
394		outbuf.shm_perm = shmseg->shm_perm;
395		outbuf.shm_segsz = shmseg->shm_segsz;
396		outbuf.shm_cpid = shmseg->shm_cpid;
397		outbuf.shm_lpid = shmseg->shm_lpid;
398		outbuf.shm_nattch = shmseg->shm_nattch;
399		outbuf.shm_atime = shmseg->shm_atime;
400		outbuf.shm_dtime = shmseg->shm_dtime;
401		outbuf.shm_ctime = shmseg->shm_ctime;
402		outbuf.shm_handle = shmseg->shm_internal;
403		error = copyout((caddr_t)&outbuf, uap->ubuf, sizeof(outbuf));
404		if (error)
405			return error;
406		break;
407	default:
408		/* XXX casting to (sy_call_t *) is bogus, as usual. */
409		return ((sy_call_t *)shmctl)(p, uap);
410	}
411	return 0;
412#else
413	return EINVAL;
414#endif
415}
416
417#ifndef _SYS_SYSPROTO_H_
418struct shmctl_args {
419	int shmid;
420	int cmd;
421	struct shmid_ds *buf;
422};
423#endif
424
425int
426shmctl(p, uap)
427	struct proc *p;
428	struct shmctl_args *uap;
429{
430	int error;
431	struct shmid_ds inbuf;
432	struct shmid_ds *shmseg;
433
434	if (!jail_sysvipc_allowed && jailed(p->p_ucred))
435		return (ENOSYS);
436
437	shmseg = shm_find_segment_by_shmid(uap->shmid);
438	if (shmseg == NULL)
439		return EINVAL;
440	switch (uap->cmd) {
441	case IPC_STAT:
442		error = ipcperm(p, &shmseg->shm_perm, IPC_R);
443		if (error)
444			return error;
445		error = copyout((caddr_t)shmseg, uap->buf, sizeof(inbuf));
446		if (error)
447			return error;
448		break;
449	case IPC_SET:
450		error = ipcperm(p, &shmseg->shm_perm, IPC_M);
451		if (error)
452			return error;
453		error = copyin(uap->buf, (caddr_t)&inbuf, sizeof(inbuf));
454		if (error)
455			return error;
456		shmseg->shm_perm.uid = inbuf.shm_perm.uid;
457		shmseg->shm_perm.gid = inbuf.shm_perm.gid;
458		shmseg->shm_perm.mode =
459		    (shmseg->shm_perm.mode & ~ACCESSPERMS) |
460		    (inbuf.shm_perm.mode & ACCESSPERMS);
461		shmseg->shm_ctime = time_second;
462		break;
463	case IPC_RMID:
464		error = ipcperm(p, &shmseg->shm_perm, IPC_M);
465		if (error)
466			return error;
467		shmseg->shm_perm.key = IPC_PRIVATE;
468		shmseg->shm_perm.mode |= SHMSEG_REMOVED;
469		if (shmseg->shm_nattch <= 0) {
470			mtx_lock(&vm_mtx);
471			shm_deallocate_segment(shmseg);
472			mtx_unlock(&vm_mtx);
473			shm_last_free = IPCID_TO_IX(uap->shmid);
474		}
475		break;
476#if 0
477	case SHM_LOCK:
478	case SHM_UNLOCK:
479#endif
480	default:
481		return EINVAL;
482	}
483	return 0;
484}
485
486#ifndef _SYS_SYSPROTO_H_
487struct shmget_args {
488	key_t key;
489	size_t size;
490	int shmflg;
491};
492#endif
493
494static int
495shmget_existing(p, uap, mode, segnum)
496	struct proc *p;
497	struct shmget_args *uap;
498	int mode;
499	int segnum;
500{
501	struct shmid_ds *shmseg;
502	int error;
503
504	shmseg = &shmsegs[segnum];
505	if (shmseg->shm_perm.mode & SHMSEG_REMOVED) {
506		/*
507		 * This segment is in the process of being allocated.  Wait
508		 * until it's done, and look the key up again (in case the
509		 * allocation failed or it was freed).
510		 */
511		shmseg->shm_perm.mode |= SHMSEG_WANTED;
512		error = tsleep((caddr_t)shmseg, PLOCK | PCATCH, "shmget", 0);
513		if (error)
514			return error;
515		return EAGAIN;
516	}
517	if ((uap->shmflg & (IPC_CREAT | IPC_EXCL)) == (IPC_CREAT | IPC_EXCL))
518		return EEXIST;
519	error = ipcperm(p, &shmseg->shm_perm, mode);
520	if (error)
521		return error;
522	if (uap->size && uap->size > shmseg->shm_segsz)
523		return EINVAL;
524	p->p_retval[0] = IXSEQ_TO_IPCID(segnum, shmseg->shm_perm);
525	return 0;
526}
527
528static int
529shmget_allocate_segment(p, uap, mode)
530	struct proc *p;
531	struct shmget_args *uap;
532	int mode;
533{
534	int i, segnum, shmid, size;
535	struct ucred *cred = p->p_ucred;
536	struct shmid_ds *shmseg;
537	struct shm_handle *shm_handle;
538
539	if (uap->size < shminfo.shmmin || uap->size > shminfo.shmmax)
540		return EINVAL;
541	if (shm_nused >= shminfo.shmmni) /* Any shmids left? */
542		return ENOSPC;
543	size = round_page(uap->size);
544	if (shm_committed + btoc(size) > shminfo.shmall)
545		return ENOMEM;
546	if (shm_last_free < 0) {
547		shmrealloc();	/* Maybe expand the shmsegs[] array. */
548		for (i = 0; i < shmalloced; i++)
549			if (shmsegs[i].shm_perm.mode & SHMSEG_FREE)
550				break;
551		if (i == shmalloced)
552			return ENOSPC;
553		segnum = i;
554	} else  {
555		segnum = shm_last_free;
556		shm_last_free = -1;
557	}
558	shmseg = &shmsegs[segnum];
559	/*
560	 * In case we sleep in malloc(), mark the segment present but deleted
561	 * so that noone else tries to create the same key.
562	 */
563	shmseg->shm_perm.mode = SHMSEG_ALLOCATED | SHMSEG_REMOVED;
564	shmseg->shm_perm.key = uap->key;
565	shmseg->shm_perm.seq = (shmseg->shm_perm.seq + 1) & 0x7fff;
566	shm_handle = (struct shm_handle *)
567	    malloc(sizeof(struct shm_handle), M_SHM, M_WAITOK);
568	shmid = IXSEQ_TO_IPCID(segnum, shmseg->shm_perm);
569
570	/*
571	 * We make sure that we have allocated a pager before we need
572	 * to.
573	 */
574	mtx_lock(&vm_mtx);
575	if (shm_use_phys) {
576		shm_handle->shm_object =
577		    vm_pager_allocate(OBJT_PHYS, 0, size, VM_PROT_DEFAULT, 0);
578	} else {
579		shm_handle->shm_object =
580		    vm_pager_allocate(OBJT_SWAP, 0, size, VM_PROT_DEFAULT, 0);
581	}
582	vm_object_clear_flag(shm_handle->shm_object, OBJ_ONEMAPPING);
583	vm_object_set_flag(shm_handle->shm_object, OBJ_NOSPLIT);
584	mtx_unlock(&vm_mtx);
585
586	shmseg->shm_internal = shm_handle;
587	shmseg->shm_perm.cuid = shmseg->shm_perm.uid = cred->cr_uid;
588	shmseg->shm_perm.cgid = shmseg->shm_perm.gid = cred->cr_gid;
589	shmseg->shm_perm.mode = (shmseg->shm_perm.mode & SHMSEG_WANTED) |
590	    (mode & ACCESSPERMS) | SHMSEG_ALLOCATED;
591	shmseg->shm_segsz = uap->size;
592	shmseg->shm_cpid = p->p_pid;
593	shmseg->shm_lpid = shmseg->shm_nattch = 0;
594	shmseg->shm_atime = shmseg->shm_dtime = 0;
595	shmseg->shm_ctime = time_second;
596	shm_committed += btoc(size);
597	shm_nused++;
598	if (shmseg->shm_perm.mode & SHMSEG_WANTED) {
599		/*
600		 * Somebody else wanted this key while we were asleep.  Wake
601		 * them up now.
602		 */
603		shmseg->shm_perm.mode &= ~SHMSEG_WANTED;
604		wakeup((caddr_t)shmseg);
605	}
606	p->p_retval[0] = shmid;
607	return 0;
608}
609
610int
611shmget(p, uap)
612	struct proc *p;
613	struct shmget_args *uap;
614{
615	int segnum, mode, error;
616
617	if (!jail_sysvipc_allowed && jailed(p->p_ucred))
618		return (ENOSYS);
619
620	mode = uap->shmflg & ACCESSPERMS;
621	if (uap->key != IPC_PRIVATE) {
622	again:
623		segnum = shm_find_segment_by_key(uap->key);
624		if (segnum >= 0) {
625			error = shmget_existing(p, uap, mode, segnum);
626			if (error == EAGAIN)
627				goto again;
628			return error;
629		}
630		if ((uap->shmflg & IPC_CREAT) == 0)
631			return ENOENT;
632	}
633	return shmget_allocate_segment(p, uap, mode);
634}
635
636int
637shmsys(p, uap)
638	struct proc *p;
639	/* XXX actually varargs. */
640	struct shmsys_args /* {
641		u_int	which;
642		int	a2;
643		int	a3;
644		int	a4;
645	} */ *uap;
646{
647
648	if (!jail_sysvipc_allowed && jailed(p->p_ucred))
649		return (ENOSYS);
650
651	if (uap->which >= sizeof(shmcalls)/sizeof(shmcalls[0]))
652		return EINVAL;
653	return ((*shmcalls[uap->which])(p, &uap->a2));
654}
655
656static void
657shmfork_myhook(p1, p2)
658	struct proc *p1, *p2;
659{
660	struct shmmap_state *shmmap_s;
661	size_t size;
662	int i;
663
664	size = shminfo.shmseg * sizeof(struct shmmap_state);
665	shmmap_s = malloc(size, M_SHM, M_WAITOK);
666	bcopy((caddr_t)p1->p_vmspace->vm_shm, (caddr_t)shmmap_s, size);
667	p2->p_vmspace->vm_shm = (caddr_t)shmmap_s;
668	for (i = 0; i < shminfo.shmseg; i++, shmmap_s++)
669		if (shmmap_s->shmid != -1)
670			shmsegs[IPCID_TO_IX(shmmap_s->shmid)].shm_nattch++;
671}
672
673static void
674shmexit_myhook(p)
675	struct proc *p;
676{
677	struct shmmap_state *shmmap_s;
678	int i;
679
680	mtx_assert(&vm_mtx, MA_OWNED);	/* For shm_delete_mapping. */
681	shmmap_s = (struct shmmap_state *)p->p_vmspace->vm_shm;
682	for (i = 0; i < shminfo.shmseg; i++, shmmap_s++)
683		if (shmmap_s->shmid != -1)
684			shm_delete_mapping(p, shmmap_s);
685	free((caddr_t)p->p_vmspace->vm_shm, M_SHM);
686	p->p_vmspace->vm_shm = NULL;
687}
688
689static void
690shmrealloc(void)
691{
692	int i;
693	struct shmid_ds *newsegs;
694
695	if (shmalloced >= shminfo.shmmni)
696		return;
697
698	newsegs = malloc(shminfo.shmmni * sizeof(*newsegs), M_SHM, M_WAITOK);
699	if (newsegs == NULL)
700		return;
701	for (i = 0; i < shmalloced; i++)
702		bcopy(&shmsegs[i], &newsegs[i], sizeof(newsegs[0]));
703	for (; i < shminfo.shmmni; i++) {
704		shmsegs[i].shm_perm.mode = SHMSEG_FREE;
705		shmsegs[i].shm_perm.seq = 0;
706	}
707	free(shmsegs, M_SHM);
708	shmsegs = newsegs;
709	shmalloced = shminfo.shmmni;
710}
711
712static void
713shminit()
714{
715	int i;
716
717	shmalloced = shminfo.shmmni;
718	shmsegs = malloc(shmalloced * sizeof(shmsegs[0]), M_SHM, M_WAITOK);
719	if (shmsegs == NULL)
720		panic("cannot allocate initial memory for sysvshm");
721	for (i = 0; i < shmalloced; i++) {
722		shmsegs[i].shm_perm.mode = SHMSEG_FREE;
723		shmsegs[i].shm_perm.seq = 0;
724	}
725	shm_last_free = 0;
726	shm_nused = 0;
727	shm_committed = 0;
728	shmexit_hook = &shmexit_myhook;
729	shmfork_hook = &shmfork_myhook;
730}
731
732static int
733shmunload()
734{
735
736	if (shm_nused > 0)
737		return (EBUSY);
738
739	free(shmsegs, M_SHM);
740	shmexit_hook = NULL;
741	shmfork_hook = NULL;
742	return (0);
743}
744
745static int
746sysvshm_modload(struct module *module, int cmd, void *arg)
747{
748	int error = 0;
749
750	switch (cmd) {
751	case MOD_LOAD:
752		shminit();
753		break;
754	case MOD_UNLOAD:
755		error = shmunload();
756		break;
757	case MOD_SHUTDOWN:
758		break;
759	default:
760		error = EINVAL;
761		break;
762	}
763	return (error);
764}
765
766static moduledata_t sysvshm_mod = {
767	"sysvshm",
768	&sysvshm_modload,
769	NULL
770};
771
772SYSCALL_MODULE_HELPER(shmsys, 4);
773SYSCALL_MODULE_HELPER(shmat, 3);
774SYSCALL_MODULE_HELPER(shmctl, 3);
775SYSCALL_MODULE_HELPER(shmdt, 1);
776SYSCALL_MODULE_HELPER(shmget, 3);
777
778DECLARE_MODULE(sysvshm, sysvshm_mod,
779	SI_SUB_SYSV_SHM, SI_ORDER_FIRST);
780MODULE_VERSION(sysvshm, 1);
781