sysv_shm.c revision 84783
1/* $FreeBSD: head/sys/kern/sysv_shm.c 84783 2001-10-10 23:06:54Z ps $ */
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_sysvipc.h"
36
37#include <sys/param.h>
38#include <sys/systm.h>
39#include <sys/kernel.h>
40#include <sys/lock.h>
41#include <sys/sysctl.h>
42#include <sys/shm.h>
43#include <sys/proc.h>
44#include <sys/malloc.h>
45#include <sys/mman.h>
46#include <sys/mutex.h>
47#include <sys/stat.h>
48#include <sys/syscall.h>
49#include <sys/sysent.h>
50#include <sys/sysproto.h>
51#include <sys/jail.h>
52
53#include <vm/vm.h>
54#include <vm/vm_param.h>
55#include <vm/pmap.h>
56#include <vm/vm_object.h>
57#include <vm/vm_map.h>
58#include <vm/vm_page.h>
59#include <vm/vm_pager.h>
60
61static MALLOC_DEFINE(M_SHM, "shm", "SVID compatible shared memory segments");
62
63struct oshmctl_args;
64static int oshmctl __P((struct thread *td, struct oshmctl_args *uap));
65
66static int shmget_allocate_segment __P((struct thread *td,
67    struct shmget_args *uap, int mode));
68static int shmget_existing __P((struct thread *td, struct shmget_args *uap,
69    int mode, int segnum));
70
71/* XXX casting to (sy_call_t *) is bogus, as usual. */
72static sy_call_t *shmcalls[] = {
73	(sy_call_t *)shmat, (sy_call_t *)oshmctl,
74	(sy_call_t *)shmdt, (sy_call_t *)shmget,
75	(sy_call_t *)shmctl
76};
77
78#define	SHMSEG_FREE     	0x0200
79#define	SHMSEG_REMOVED  	0x0400
80#define	SHMSEG_ALLOCATED	0x0800
81#define	SHMSEG_WANTED		0x1000
82
83static int shm_last_free, shm_nused, shm_committed, shmalloced;
84static struct shmid_ds	*shmsegs;
85
86struct shm_handle {
87	/* vm_offset_t kva; */
88	vm_object_t shm_object;
89};
90
91struct shmmap_state {
92	vm_offset_t va;
93	int shmid;
94};
95
96static void shm_deallocate_segment __P((struct shmid_ds *));
97static int shm_find_segment_by_key __P((key_t));
98static struct shmid_ds *shm_find_segment_by_shmid __P((int));
99static int shm_delete_mapping __P((struct proc *p, struct shmmap_state *));
100static void shmrealloc __P((void));
101static void shminit __P((void));
102static int sysvshm_modload __P((struct module *, int, void *));
103static int shmunload __P((void));
104static void shmexit_myhook __P((struct proc *p));
105static void shmfork_myhook __P((struct proc *p1, struct proc *p2));
106static int sysctl_shmsegs __P((SYSCTL_HANDLER_ARGS));
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, "");
148SYSCTL_PROC(_kern_ipc, OID_AUTO, shmsegs, CTLFLAG_RD,
149    NULL, 0, sysctl_shmsegs, "", "");
150
151static int
152shm_find_segment_by_key(key)
153	key_t key;
154{
155	int i;
156
157	for (i = 0; i < shmalloced; i++)
158		if ((shmsegs[i].shm_perm.mode & SHMSEG_ALLOCATED) &&
159		    shmsegs[i].shm_perm.key == key)
160			return i;
161	return -1;
162}
163
164static struct shmid_ds *
165shm_find_segment_by_shmid(shmid)
166	int shmid;
167{
168	int segnum;
169	struct shmid_ds *shmseg;
170
171	segnum = IPCID_TO_IX(shmid);
172	if (segnum < 0 || segnum >= shmalloced)
173		return NULL;
174	shmseg = &shmsegs[segnum];
175	if ((shmseg->shm_perm.mode & (SHMSEG_ALLOCATED | SHMSEG_REMOVED))
176	    != SHMSEG_ALLOCATED ||
177	    shmseg->shm_perm.seq != IPCID_TO_SEQ(shmid))
178		return NULL;
179	return shmseg;
180}
181
182static void
183shm_deallocate_segment(shmseg)
184	struct shmid_ds *shmseg;
185{
186	struct shm_handle *shm_handle;
187	size_t size;
188
189	GIANT_REQUIRED;
190
191	shm_handle = shmseg->shm_internal;
192	vm_object_deallocate(shm_handle->shm_object);
193	free((caddr_t)shm_handle, M_SHM);
194	shmseg->shm_internal = NULL;
195	size = round_page(shmseg->shm_segsz);
196	shm_committed -= btoc(size);
197	shm_nused--;
198	shmseg->shm_perm.mode = SHMSEG_FREE;
199}
200
201static int
202shm_delete_mapping(p, shmmap_s)
203	struct proc *p;
204	struct shmmap_state *shmmap_s;
205{
206	struct shmid_ds *shmseg;
207	int segnum, result;
208	size_t size;
209
210	GIANT_REQUIRED;
211
212	segnum = IPCID_TO_IX(shmmap_s->shmid);
213	shmseg = &shmsegs[segnum];
214	size = round_page(shmseg->shm_segsz);
215	result = vm_map_remove(&p->p_vmspace->vm_map, shmmap_s->va,
216	    shmmap_s->va + size);
217	if (result != KERN_SUCCESS)
218		return EINVAL;
219	shmmap_s->shmid = -1;
220	shmseg->shm_dtime = time_second;
221	if ((--shmseg->shm_nattch <= 0) &&
222	    (shmseg->shm_perm.mode & SHMSEG_REMOVED)) {
223		shm_deallocate_segment(shmseg);
224		shm_last_free = segnum;
225	}
226	return 0;
227}
228
229#ifndef _SYS_SYSPROTO_H_
230struct shmdt_args {
231	void *shmaddr;
232};
233#endif
234
235/*
236 * MPSAFE
237 */
238int
239shmdt(td, uap)
240	struct thread *td;
241	struct shmdt_args *uap;
242{
243	struct proc *p = td->td_proc;
244	struct shmmap_state *shmmap_s;
245	int i;
246	int error = 0;
247
248	mtx_lock(&Giant);
249	if (!jail_sysvipc_allowed && jailed(p->p_ucred)) {
250		error = ENOSYS;
251		goto done2;
252	}
253	shmmap_s = (struct shmmap_state *)p->p_vmspace->vm_shm;
254 	if (shmmap_s == NULL) {
255		error = EINVAL;
256		goto done2;
257	}
258	for (i = 0; i < shminfo.shmseg; i++, shmmap_s++) {
259		if (shmmap_s->shmid != -1 &&
260		    shmmap_s->va == (vm_offset_t)uap->shmaddr) {
261			break;
262		}
263	}
264	if (i == shminfo.shmseg) {
265		error = EINVAL;
266		goto done2;
267	}
268	error = shm_delete_mapping(p, shmmap_s);
269done2:
270	mtx_unlock(&Giant);
271	return (error);
272}
273
274#ifndef _SYS_SYSPROTO_H_
275struct shmat_args {
276	int shmid;
277	void *shmaddr;
278	int shmflg;
279};
280#endif
281
282/*
283 * MPSAFE
284 */
285int
286shmat(td, uap)
287	struct thread *td;
288	struct shmat_args *uap;
289{
290	struct proc *p = td->td_proc;
291	int i, flags;
292	struct shmid_ds *shmseg;
293	struct shmmap_state *shmmap_s = NULL;
294	struct shm_handle *shm_handle;
295	vm_offset_t attach_va;
296	vm_prot_t prot;
297	vm_size_t size;
298	int rv;
299	int error = 0;
300
301	mtx_lock(&Giant);
302	if (!jail_sysvipc_allowed && jailed(p->p_ucred)) {
303		error = ENOSYS;
304		goto done2;
305	}
306	shmmap_s = (struct shmmap_state *)p->p_vmspace->vm_shm;
307	if (shmmap_s == NULL) {
308		size = shminfo.shmseg * sizeof(struct shmmap_state);
309		shmmap_s = malloc(size, M_SHM, M_WAITOK);
310		for (i = 0; i < shminfo.shmseg; i++)
311			shmmap_s[i].shmid = -1;
312		p->p_vmspace->vm_shm = (caddr_t)shmmap_s;
313	}
314	shmseg = shm_find_segment_by_shmid(uap->shmid);
315	if (shmseg == NULL) {
316		error = EINVAL;
317		goto done2;
318	}
319	error = ipcperm(td, &shmseg->shm_perm,
320	    (uap->shmflg & SHM_RDONLY) ? IPC_R : IPC_R|IPC_W);
321	if (error)
322		goto done2;
323	for (i = 0; i < shminfo.shmseg; i++) {
324		if (shmmap_s->shmid == -1)
325			break;
326		shmmap_s++;
327	}
328	if (i >= shminfo.shmseg) {
329		error = EMFILE;
330		goto done2;
331	}
332	size = round_page(shmseg->shm_segsz);
333#ifdef VM_PROT_READ_IS_EXEC
334	prot = VM_PROT_READ | VM_PROT_EXECUTE;
335#else
336	prot = VM_PROT_READ;
337#endif
338	if ((uap->shmflg & SHM_RDONLY) == 0)
339		prot |= VM_PROT_WRITE;
340	flags = MAP_ANON | MAP_SHARED;
341	if (uap->shmaddr) {
342		flags |= MAP_FIXED;
343		if (uap->shmflg & SHM_RND) {
344			attach_va = (vm_offset_t)uap->shmaddr & ~(SHMLBA-1);
345		} else if (((vm_offset_t)uap->shmaddr & (SHMLBA-1)) == 0) {
346			attach_va = (vm_offset_t)uap->shmaddr;
347		} else {
348			error = EINVAL;
349			goto done2;
350		}
351	} else {
352		/*
353		 * This is just a hint to vm_map_find() about where to
354		 * put it.
355		 */
356		attach_va = round_page((vm_offset_t)p->p_vmspace->vm_taddr
357		    + maxtsiz + maxdsiz);
358	}
359
360	shm_handle = shmseg->shm_internal;
361	vm_object_reference(shm_handle->shm_object);
362	rv = vm_map_find(&p->p_vmspace->vm_map, shm_handle->shm_object,
363		0, &attach_va, size, (flags & MAP_FIXED)?0:1, prot, prot, 0);
364	if (rv != KERN_SUCCESS) {
365		error = ENOMEM;
366		goto done2;
367	}
368	vm_map_inherit(&p->p_vmspace->vm_map,
369		attach_va, attach_va + size, VM_INHERIT_SHARE);
370
371	shmmap_s->va = attach_va;
372	shmmap_s->shmid = uap->shmid;
373	shmseg->shm_lpid = p->p_pid;
374	shmseg->shm_atime = time_second;
375	shmseg->shm_nattch++;
376	td->td_retval[0] = attach_va;
377done2:
378	mtx_unlock(&Giant);
379	return (error);
380}
381
382struct oshmid_ds {
383	struct	ipc_perm shm_perm;	/* operation perms */
384	int	shm_segsz;		/* size of segment (bytes) */
385	ushort	shm_cpid;		/* pid, creator */
386	ushort	shm_lpid;		/* pid, last operation */
387	short	shm_nattch;		/* no. of current attaches */
388	time_t	shm_atime;		/* last attach time */
389	time_t	shm_dtime;		/* last detach time */
390	time_t	shm_ctime;		/* last change time */
391	void	*shm_handle;		/* internal handle for shm segment */
392};
393
394struct oshmctl_args {
395	int shmid;
396	int cmd;
397	struct oshmid_ds *ubuf;
398};
399
400/*
401 * MPSAFE
402 */
403static int
404oshmctl(td, uap)
405	struct thread *td;
406	struct oshmctl_args *uap;
407{
408#ifdef COMPAT_43
409	int error = 0;
410	struct shmid_ds *shmseg;
411	struct oshmid_ds outbuf;
412
413	mtx_lock(&Giant);
414	if (!jail_sysvipc_allowed && jailed(td->td_proc->p_ucred)) {
415		error = ENOSYS;
416		goto done2;
417	}
418	shmseg = shm_find_segment_by_shmid(uap->shmid);
419	if (shmseg == NULL) {
420		error = EINVAL;
421		goto done2;
422	}
423	switch (uap->cmd) {
424	case IPC_STAT:
425		error = ipcperm(td, &shmseg->shm_perm, IPC_R);
426		if (error)
427			goto done2;
428		outbuf.shm_perm = shmseg->shm_perm;
429		outbuf.shm_segsz = shmseg->shm_segsz;
430		outbuf.shm_cpid = shmseg->shm_cpid;
431		outbuf.shm_lpid = shmseg->shm_lpid;
432		outbuf.shm_nattch = shmseg->shm_nattch;
433		outbuf.shm_atime = shmseg->shm_atime;
434		outbuf.shm_dtime = shmseg->shm_dtime;
435		outbuf.shm_ctime = shmseg->shm_ctime;
436		outbuf.shm_handle = shmseg->shm_internal;
437		error = copyout((caddr_t)&outbuf, uap->ubuf, sizeof(outbuf));
438		if (error)
439			goto done2;
440		break;
441	default:
442		/* XXX casting to (sy_call_t *) is bogus, as usual. */
443		error = ((sy_call_t *)shmctl)(td, uap);
444		break;
445	}
446done2:
447	mtx_unlock(&Giant);
448	return (error);
449#else
450	return EINVAL;
451#endif
452}
453
454#ifndef _SYS_SYSPROTO_H_
455struct shmctl_args {
456	int shmid;
457	int cmd;
458	struct shmid_ds *buf;
459};
460#endif
461
462/*
463 * MPSAFE
464 */
465int
466shmctl(td, uap)
467	struct thread *td;
468	struct shmctl_args *uap;
469{
470	int error = 0;
471	struct shmid_ds inbuf;
472	struct shmid_ds *shmseg;
473
474	mtx_lock(&Giant);
475	if (!jail_sysvipc_allowed && jailed(td->td_proc->p_ucred)) {
476		error = ENOSYS;
477		goto done2;
478	}
479	shmseg = shm_find_segment_by_shmid(uap->shmid);
480	if (shmseg == NULL) {
481		error = EINVAL;
482		goto done2;
483	}
484	switch (uap->cmd) {
485	case IPC_STAT:
486		error = ipcperm(td, &shmseg->shm_perm, IPC_R);
487		if (error)
488			goto done2;
489		error = copyout((caddr_t)shmseg, uap->buf, sizeof(inbuf));
490		if (error)
491			goto done2;
492		break;
493	case IPC_SET:
494		error = ipcperm(td, &shmseg->shm_perm, IPC_M);
495		if (error)
496			goto done2;
497		error = copyin(uap->buf, (caddr_t)&inbuf, sizeof(inbuf));
498		if (error)
499			goto done2;
500		shmseg->shm_perm.uid = inbuf.shm_perm.uid;
501		shmseg->shm_perm.gid = inbuf.shm_perm.gid;
502		shmseg->shm_perm.mode =
503		    (shmseg->shm_perm.mode & ~ACCESSPERMS) |
504		    (inbuf.shm_perm.mode & ACCESSPERMS);
505		shmseg->shm_ctime = time_second;
506		break;
507	case IPC_RMID:
508		error = ipcperm(td, &shmseg->shm_perm, IPC_M);
509		if (error)
510			goto done2;
511		shmseg->shm_perm.key = IPC_PRIVATE;
512		shmseg->shm_perm.mode |= SHMSEG_REMOVED;
513		if (shmseg->shm_nattch <= 0) {
514			shm_deallocate_segment(shmseg);
515			shm_last_free = IPCID_TO_IX(uap->shmid);
516		}
517		break;
518#if 0
519	case SHM_LOCK:
520	case SHM_UNLOCK:
521#endif
522	default:
523		error = EINVAL;
524		break;
525	}
526done2:
527	mtx_unlock(&Giant);
528	return (error);
529}
530
531#ifndef _SYS_SYSPROTO_H_
532struct shmget_args {
533	key_t key;
534	size_t size;
535	int shmflg;
536};
537#endif
538
539static int
540shmget_existing(td, uap, mode, segnum)
541	struct thread *td;
542	struct shmget_args *uap;
543	int mode;
544	int segnum;
545{
546	struct shmid_ds *shmseg;
547	int error;
548
549	shmseg = &shmsegs[segnum];
550	if (shmseg->shm_perm.mode & SHMSEG_REMOVED) {
551		/*
552		 * This segment is in the process of being allocated.  Wait
553		 * until it's done, and look the key up again (in case the
554		 * allocation failed or it was freed).
555		 */
556		shmseg->shm_perm.mode |= SHMSEG_WANTED;
557		error = tsleep((caddr_t)shmseg, PLOCK | PCATCH, "shmget", 0);
558		if (error)
559			return error;
560		return EAGAIN;
561	}
562	if ((uap->shmflg & (IPC_CREAT | IPC_EXCL)) == (IPC_CREAT | IPC_EXCL))
563		return EEXIST;
564	error = ipcperm(td, &shmseg->shm_perm, mode);
565	if (error)
566		return error;
567	if (uap->size && uap->size > shmseg->shm_segsz)
568		return EINVAL;
569	td->td_retval[0] = IXSEQ_TO_IPCID(segnum, shmseg->shm_perm);
570	return 0;
571}
572
573static int
574shmget_allocate_segment(td, uap, mode)
575	struct thread *td;
576	struct shmget_args *uap;
577	int mode;
578{
579	int i, segnum, shmid, size;
580	struct ucred *cred = td->td_proc->p_ucred;
581	struct shmid_ds *shmseg;
582	struct shm_handle *shm_handle;
583
584	GIANT_REQUIRED;
585
586	if (uap->size < shminfo.shmmin || uap->size > shminfo.shmmax)
587		return EINVAL;
588	if (shm_nused >= shminfo.shmmni) /* Any shmids left? */
589		return ENOSPC;
590	size = round_page(uap->size);
591	if (shm_committed + btoc(size) > shminfo.shmall)
592		return ENOMEM;
593	if (shm_last_free < 0) {
594		shmrealloc();	/* Maybe expand the shmsegs[] array. */
595		for (i = 0; i < shmalloced; i++)
596			if (shmsegs[i].shm_perm.mode & SHMSEG_FREE)
597				break;
598		if (i == shmalloced)
599			return ENOSPC;
600		segnum = i;
601	} else  {
602		segnum = shm_last_free;
603		shm_last_free = -1;
604	}
605	shmseg = &shmsegs[segnum];
606	/*
607	 * In case we sleep in malloc(), mark the segment present but deleted
608	 * so that noone else tries to create the same key.
609	 */
610	shmseg->shm_perm.mode = SHMSEG_ALLOCATED | SHMSEG_REMOVED;
611	shmseg->shm_perm.key = uap->key;
612	shmseg->shm_perm.seq = (shmseg->shm_perm.seq + 1) & 0x7fff;
613	shm_handle = (struct shm_handle *)
614	    malloc(sizeof(struct shm_handle), M_SHM, M_WAITOK);
615	shmid = IXSEQ_TO_IPCID(segnum, shmseg->shm_perm);
616
617	/*
618	 * We make sure that we have allocated a pager before we need
619	 * to.
620	 */
621	if (shm_use_phys) {
622		shm_handle->shm_object =
623		    vm_pager_allocate(OBJT_PHYS, 0, size, VM_PROT_DEFAULT, 0);
624	} else {
625		shm_handle->shm_object =
626		    vm_pager_allocate(OBJT_SWAP, 0, size, VM_PROT_DEFAULT, 0);
627	}
628	vm_object_clear_flag(shm_handle->shm_object, OBJ_ONEMAPPING);
629	vm_object_set_flag(shm_handle->shm_object, OBJ_NOSPLIT);
630
631	shmseg->shm_internal = shm_handle;
632	shmseg->shm_perm.cuid = shmseg->shm_perm.uid = cred->cr_uid;
633	shmseg->shm_perm.cgid = shmseg->shm_perm.gid = cred->cr_gid;
634	shmseg->shm_perm.mode = (shmseg->shm_perm.mode & SHMSEG_WANTED) |
635	    (mode & ACCESSPERMS) | SHMSEG_ALLOCATED;
636	shmseg->shm_segsz = uap->size;
637	shmseg->shm_cpid = td->td_proc->p_pid;
638	shmseg->shm_lpid = shmseg->shm_nattch = 0;
639	shmseg->shm_atime = shmseg->shm_dtime = 0;
640	shmseg->shm_ctime = time_second;
641	shm_committed += btoc(size);
642	shm_nused++;
643	if (shmseg->shm_perm.mode & SHMSEG_WANTED) {
644		/*
645		 * Somebody else wanted this key while we were asleep.  Wake
646		 * them up now.
647		 */
648		shmseg->shm_perm.mode &= ~SHMSEG_WANTED;
649		wakeup((caddr_t)shmseg);
650	}
651	td->td_retval[0] = shmid;
652	return 0;
653}
654
655/*
656 * MPSAFE
657 */
658int
659shmget(td, uap)
660	struct thread *td;
661	struct shmget_args *uap;
662{
663	int segnum, mode;
664	int error;
665
666	mtx_lock(&Giant);
667	if (!jail_sysvipc_allowed && jailed(td->td_proc->p_ucred)) {
668		error = ENOSYS;
669		goto done2;
670	}
671	mode = uap->shmflg & ACCESSPERMS;
672	if (uap->key != IPC_PRIVATE) {
673	again:
674		segnum = shm_find_segment_by_key(uap->key);
675		if (segnum >= 0) {
676			error = shmget_existing(td, uap, mode, segnum);
677			if (error == EAGAIN)
678				goto again;
679			goto done2;
680		}
681		if ((uap->shmflg & IPC_CREAT) == 0) {
682			error = ENOENT;
683			goto done2;
684		}
685	}
686	error = shmget_allocate_segment(td, uap, mode);
687done2:
688	mtx_unlock(&Giant);
689	return (error);
690}
691
692/*
693 * MPSAFE
694 */
695int
696shmsys(td, uap)
697	struct thread *td;
698	/* XXX actually varargs. */
699	struct shmsys_args /* {
700		u_int	which;
701		int	a2;
702		int	a3;
703		int	a4;
704	} */ *uap;
705{
706	int error;
707
708	mtx_lock(&Giant);
709	if (!jail_sysvipc_allowed && jailed(td->td_proc->p_ucred)) {
710		error = ENOSYS;
711		goto done2;
712	}
713	if (uap->which >= sizeof(shmcalls)/sizeof(shmcalls[0])) {
714		error = EINVAL;
715		goto done2;
716	}
717	error = (*shmcalls[uap->which])(td, &uap->a2);
718done2:
719	mtx_unlock(&Giant);
720	return (error);
721}
722
723static void
724shmfork_myhook(p1, p2)
725	struct proc *p1, *p2;
726{
727	struct shmmap_state *shmmap_s;
728	size_t size;
729	int i;
730
731	size = shminfo.shmseg * sizeof(struct shmmap_state);
732	shmmap_s = malloc(size, M_SHM, M_WAITOK);
733	bcopy((caddr_t)p1->p_vmspace->vm_shm, (caddr_t)shmmap_s, size);
734	p2->p_vmspace->vm_shm = (caddr_t)shmmap_s;
735	for (i = 0; i < shminfo.shmseg; i++, shmmap_s++)
736		if (shmmap_s->shmid != -1)
737			shmsegs[IPCID_TO_IX(shmmap_s->shmid)].shm_nattch++;
738}
739
740static void
741shmexit_myhook(p)
742	struct proc *p;
743{
744	struct shmmap_state *shmmap_s;
745	int i;
746
747	GIANT_REQUIRED;
748
749	shmmap_s = (struct shmmap_state *)p->p_vmspace->vm_shm;
750	for (i = 0; i < shminfo.shmseg; i++, shmmap_s++)
751		if (shmmap_s->shmid != -1)
752			shm_delete_mapping(p, shmmap_s);
753	free((caddr_t)p->p_vmspace->vm_shm, M_SHM);
754	p->p_vmspace->vm_shm = NULL;
755}
756
757static void
758shmrealloc(void)
759{
760	int i;
761	struct shmid_ds *newsegs;
762
763	if (shmalloced >= shminfo.shmmni)
764		return;
765
766	newsegs = malloc(shminfo.shmmni * sizeof(*newsegs), M_SHM, M_WAITOK);
767	if (newsegs == NULL)
768		return;
769	for (i = 0; i < shmalloced; i++)
770		bcopy(&shmsegs[i], &newsegs[i], sizeof(newsegs[0]));
771	for (; i < shminfo.shmmni; i++) {
772		shmsegs[i].shm_perm.mode = SHMSEG_FREE;
773		shmsegs[i].shm_perm.seq = 0;
774	}
775	free(shmsegs, M_SHM);
776	shmsegs = newsegs;
777	shmalloced = shminfo.shmmni;
778}
779
780static void
781shminit()
782{
783	int i;
784
785	TUNABLE_INT_FETCH("kern.ipc.shmmaxpgs", &shminfo.shmall);
786	shminfo.shmmax = shminfo.shmall * PAGE_SIZE;
787	TUNABLE_INT_FETCH("kern.ipc.shmmin", &shminfo.shmmin);
788	TUNABLE_INT_FETCH("kern.ipc.shmmni", &shminfo.shmmni);
789	TUNABLE_INT_FETCH("kern.ipc.shmseg", &shminfo.shmseg);
790	TUNABLE_INT_FETCH("kern.ipc.shm_use_phys", &shm_use_phys);
791
792	shmalloced = shminfo.shmmni;
793	shmsegs = malloc(shmalloced * sizeof(shmsegs[0]), M_SHM, M_WAITOK);
794	if (shmsegs == NULL)
795		panic("cannot allocate initial memory for sysvshm");
796	for (i = 0; i < shmalloced; i++) {
797		shmsegs[i].shm_perm.mode = SHMSEG_FREE;
798		shmsegs[i].shm_perm.seq = 0;
799	}
800	shm_last_free = 0;
801	shm_nused = 0;
802	shm_committed = 0;
803	shmexit_hook = &shmexit_myhook;
804	shmfork_hook = &shmfork_myhook;
805}
806
807static int
808shmunload()
809{
810
811	if (shm_nused > 0)
812		return (EBUSY);
813
814	free(shmsegs, M_SHM);
815	shmexit_hook = NULL;
816	shmfork_hook = NULL;
817	return (0);
818}
819
820static int
821sysctl_shmsegs(SYSCTL_HANDLER_ARGS)
822{
823
824	return (SYSCTL_OUT(req, shmsegs, shmalloced * sizeof(shmsegs[0])));
825}
826
827static int
828sysvshm_modload(struct module *module, int cmd, void *arg)
829{
830	int error = 0;
831
832	switch (cmd) {
833	case MOD_LOAD:
834		shminit();
835		break;
836	case MOD_UNLOAD:
837		error = shmunload();
838		break;
839	case MOD_SHUTDOWN:
840		break;
841	default:
842		error = EINVAL;
843		break;
844	}
845	return (error);
846}
847
848static moduledata_t sysvshm_mod = {
849	"sysvshm",
850	&sysvshm_modload,
851	NULL
852};
853
854SYSCALL_MODULE_HELPER(shmsys, 4);
855SYSCALL_MODULE_HELPER(shmat, 3);
856SYSCALL_MODULE_HELPER(shmctl, 3);
857SYSCALL_MODULE_HELPER(shmdt, 1);
858SYSCALL_MODULE_HELPER(shmget, 3);
859
860DECLARE_MODULE(sysvshm, sysvshm_mod,
861	SI_SUB_SYSV_SHM, SI_ORDER_FIRST);
862MODULE_VERSION(sysvshm, 1);
863