xref: /csrg-svn/sys/kern/sysv_shm.c (revision 41490)
1*41490Smckusick /*
2*41490Smckusick  * Copyright (c) 1988 University of Utah.
3*41490Smckusick  * Copyright (c) 1990 The Regents of the University of California.
4*41490Smckusick  * All rights reserved.
5*41490Smckusick  *
6*41490Smckusick  * This code is derived from software contributed to Berkeley by
7*41490Smckusick  * the Systems Programming Group of the University of Utah Computer
8*41490Smckusick  * Science Department. Originally from University of Wisconsin.
9*41490Smckusick  *
10*41490Smckusick  * %sccs.include.redist.c%
11*41490Smckusick  *
12*41490Smckusick  * from: Utah $Hdr: uipc_shm.c 1.9 89/08/14$
13*41490Smckusick  *
14*41490Smckusick  *	@(#)sysv_shm.c	7.1 (Berkeley) 05/08/90
15*41490Smckusick  */
16*41490Smckusick 
17*41490Smckusick /*
18*41490Smckusick  * System V shared memory routines.
19*41490Smckusick  */
20*41490Smckusick 
21*41490Smckusick #ifdef SYSVSHM
22*41490Smckusick 
23*41490Smckusick #include "machine/pte.h"
24*41490Smckusick 
25*41490Smckusick #include "param.h"
26*41490Smckusick #include "systm.h"
27*41490Smckusick #include "user.h"
28*41490Smckusick #include "kernel.h"
29*41490Smckusick #include "proc.h"
30*41490Smckusick #include "vm.h"
31*41490Smckusick #include "shm.h"
32*41490Smckusick #include "mapmem.h"
33*41490Smckusick #include "malloc.h"
34*41490Smckusick 
35*41490Smckusick #ifdef HPUXCOMPAT
36*41490Smckusick #include "../hpux/hpux.h"
37*41490Smckusick #endif
38*41490Smckusick 
39*41490Smckusick int	shmat(), shmctl(), shmdt(), shmget();
40*41490Smckusick int	(*shmcalls[])() = { shmat, shmctl, shmdt, shmget };
41*41490Smckusick int	shmtot = 0;
42*41490Smckusick 
43*41490Smckusick int	shmfork(), shmexit();
44*41490Smckusick struct	mapmemops shmops = { shmfork, (int (*)())0, shmexit, shmexit };
45*41490Smckusick 
46*41490Smckusick shminit()
47*41490Smckusick {
48*41490Smckusick 	register int i;
49*41490Smckusick 
50*41490Smckusick 	if (shminfo.shmmni > SHMMMNI)
51*41490Smckusick 		shminfo.shmmni = SHMMMNI;
52*41490Smckusick 	for (i = 0; i < shminfo.shmmni; i++) {
53*41490Smckusick 		shmsegs[i].shm_perm.mode = 0;
54*41490Smckusick 		shmsegs[i].shm_perm.seq = 0;
55*41490Smckusick 	}
56*41490Smckusick }
57*41490Smckusick 
58*41490Smckusick /* entry point for all SHM calls */
59*41490Smckusick shmsys()
60*41490Smckusick {
61*41490Smckusick 	struct a {
62*41490Smckusick 		int which;
63*41490Smckusick 	} *uap = (struct a *)u.u_ap;
64*41490Smckusick 
65*41490Smckusick 	if (uap->which >= sizeof(shmcalls)/sizeof(shmcalls[0])) {
66*41490Smckusick 		u.u_error = EINVAL;
67*41490Smckusick 		return;
68*41490Smckusick 	}
69*41490Smckusick 	(*shmcalls[uap->which])(u.u_ap+1);
70*41490Smckusick }
71*41490Smckusick 
72*41490Smckusick /* get a shared memory segment */
73*41490Smckusick shmget(ap)
74*41490Smckusick 	int *ap;
75*41490Smckusick {
76*41490Smckusick 	register struct a {
77*41490Smckusick 		key_t key;
78*41490Smckusick 		int size;
79*41490Smckusick 		int shmflg;
80*41490Smckusick 	} *uap = (struct a *)ap;
81*41490Smckusick 	register struct shmid_ds *shp;
82*41490Smckusick 	register int i;
83*41490Smckusick 	int rval = 0, size;
84*41490Smckusick 	caddr_t kva;
85*41490Smckusick 
86*41490Smckusick 	/* look up the specified shm_id */
87*41490Smckusick 	if (uap->key != IPC_PRIVATE) {
88*41490Smckusick 		for (i = 0; i < shminfo.shmmni; i++)
89*41490Smckusick 			if ((shmsegs[i].shm_perm.mode & SHM_ALLOC) &&
90*41490Smckusick 			    shmsegs[i].shm_perm.key == uap->key) {
91*41490Smckusick 				rval = i;
92*41490Smckusick 				break;
93*41490Smckusick 			}
94*41490Smckusick 	} else
95*41490Smckusick 		i = shminfo.shmmni;
96*41490Smckusick 
97*41490Smckusick 	/* create a new shared segment if necessary */
98*41490Smckusick 	if (i == shminfo.shmmni) {
99*41490Smckusick 		if ((uap->shmflg & IPC_CREAT) == 0) {
100*41490Smckusick 			u.u_error = ENOENT;
101*41490Smckusick 			return;
102*41490Smckusick 		}
103*41490Smckusick 		if (uap->size < shminfo.shmmin || uap->size > shminfo.shmmax) {
104*41490Smckusick 			u.u_error = EINVAL;
105*41490Smckusick 			return;
106*41490Smckusick 		}
107*41490Smckusick 		for (i = 0; i < shminfo.shmmni; i++)
108*41490Smckusick 			if ((shmsegs[i].shm_perm.mode & SHM_ALLOC) == 0) {
109*41490Smckusick 				rval = i;
110*41490Smckusick 				break;
111*41490Smckusick 			}
112*41490Smckusick 		if (i == shminfo.shmmni) {
113*41490Smckusick 			u.u_error = ENOSPC;
114*41490Smckusick 			return;
115*41490Smckusick 		}
116*41490Smckusick 		size = clrnd(btoc(uap->size));
117*41490Smckusick 		if (shmtot + size > shminfo.shmall) {
118*41490Smckusick 			u.u_error = ENOMEM;
119*41490Smckusick 			return;
120*41490Smckusick 		}
121*41490Smckusick 		shp = &shmsegs[rval];
122*41490Smckusick 		/*
123*41490Smckusick 		 * We need to do a couple of things to ensure consistency
124*41490Smckusick 		 * in case we sleep in malloc().  We mark segment as
125*41490Smckusick 		 * allocated so that other shmgets() will not allocate it.
126*41490Smckusick 		 * We mark it as "destroyed" to insure that shmvalid() is
127*41490Smckusick 		 * false making most operations fail (XXX).  We set the key,
128*41490Smckusick 		 * so that other shmget()s will fail.
129*41490Smckusick 		 */
130*41490Smckusick 		shp->shm_perm.mode = SHM_ALLOC | SHM_DEST;
131*41490Smckusick 		shp->shm_perm.key = uap->key;
132*41490Smckusick 		kva = (caddr_t) malloc((u_long)ctob(size), M_SHM, M_WAITOK);
133*41490Smckusick 		if (kva == NULL) {
134*41490Smckusick 			shp->shm_perm.mode = 0;
135*41490Smckusick 			u.u_error = ENOMEM;
136*41490Smckusick 			return;
137*41490Smckusick 		}
138*41490Smckusick 		if (!claligned(kva))
139*41490Smckusick 			panic("shmget: non-aligned memory");
140*41490Smckusick 		bzero(kva, (u_int)ctob(size));
141*41490Smckusick 		shmtot += size;
142*41490Smckusick 		shp->shm_perm.cuid = shp->shm_perm.uid = u.u_uid;
143*41490Smckusick 		shp->shm_perm.cgid = shp->shm_perm.gid = u.u_gid;
144*41490Smckusick 		shp->shm_perm.mode = SHM_ALLOC | (uap->shmflg&0777);
145*41490Smckusick 		shp->shm_handle = (void *) kvtopte(kva);
146*41490Smckusick 		shp->shm_segsz = uap->size;
147*41490Smckusick 		shp->shm_cpid = u.u_procp->p_pid;
148*41490Smckusick 		shp->shm_lpid = shp->shm_nattch = 0;
149*41490Smckusick 		shp->shm_atime = shp->shm_dtime = 0;
150*41490Smckusick 		shp->shm_ctime = time.tv_sec;
151*41490Smckusick 	} else {
152*41490Smckusick 		shp = &shmsegs[rval];
153*41490Smckusick 		/* XXX: probably not the right thing to do */
154*41490Smckusick 		if (shp->shm_perm.mode & SHM_DEST) {
155*41490Smckusick 			u.u_error = EBUSY;
156*41490Smckusick 			return;
157*41490Smckusick 		}
158*41490Smckusick 		if (!ipcaccess(&shp->shm_perm, uap->shmflg&0777))
159*41490Smckusick 			return;
160*41490Smckusick 		if (uap->size && uap->size > shp->shm_segsz) {
161*41490Smckusick 			u.u_error = EINVAL;
162*41490Smckusick 			return;
163*41490Smckusick 		}
164*41490Smckusick 		if ((uap->shmflg&IPC_CREAT) && (uap->shmflg&IPC_EXCL)) {
165*41490Smckusick 			u.u_error = EEXIST;
166*41490Smckusick 			return;
167*41490Smckusick 		}
168*41490Smckusick 	}
169*41490Smckusick 	u.u_r.r_val1 = shp->shm_perm.seq * SHMMMNI + rval;
170*41490Smckusick }
171*41490Smckusick 
172*41490Smckusick /* shared memory control */
173*41490Smckusick shmctl(ap)
174*41490Smckusick 	int *ap;
175*41490Smckusick {
176*41490Smckusick 	register struct a {
177*41490Smckusick 		int shmid;
178*41490Smckusick 		int cmd;
179*41490Smckusick 		caddr_t buf;
180*41490Smckusick 	} *uap = (struct a *)ap;
181*41490Smckusick 	register struct shmid_ds *shp;
182*41490Smckusick 	struct shmid_ds sbuf;
183*41490Smckusick 
184*41490Smckusick 	if (!shmvalid(uap->shmid))
185*41490Smckusick 		return;
186*41490Smckusick 	shp = &shmsegs[uap->shmid % SHMMMNI];
187*41490Smckusick 	switch (uap->cmd) {
188*41490Smckusick 	case IPC_STAT:
189*41490Smckusick 		if (ipcaccess(&shp->shm_perm, IPC_R))
190*41490Smckusick 			u.u_error =
191*41490Smckusick 				copyout((caddr_t)shp, uap->buf, sizeof(*shp));
192*41490Smckusick 		break;
193*41490Smckusick 
194*41490Smckusick 	case IPC_SET:
195*41490Smckusick 		if (u.u_uid && u.u_uid != shp->shm_perm.uid &&
196*41490Smckusick 		    u.u_uid != shp->shm_perm.cuid) {
197*41490Smckusick 			u.u_error = EPERM;
198*41490Smckusick 			break;
199*41490Smckusick 		}
200*41490Smckusick 		u.u_error = copyin(uap->buf, (caddr_t)&sbuf, sizeof sbuf);
201*41490Smckusick 		if (!u.u_error) {
202*41490Smckusick 			shp->shm_perm.uid = sbuf.shm_perm.uid;
203*41490Smckusick 			shp->shm_perm.gid = sbuf.shm_perm.gid;
204*41490Smckusick 			shp->shm_perm.mode = (shp->shm_perm.mode & ~0777)
205*41490Smckusick 				| (sbuf.shm_perm.mode & 0777);
206*41490Smckusick 			shp->shm_ctime = time.tv_sec;
207*41490Smckusick 		}
208*41490Smckusick 		break;
209*41490Smckusick 
210*41490Smckusick 	case IPC_RMID:
211*41490Smckusick 		if (u.u_uid && u.u_uid != shp->shm_perm.uid &&
212*41490Smckusick 		    u.u_uid != shp->shm_perm.cuid) {
213*41490Smckusick 			u.u_error = EPERM;
214*41490Smckusick 			break;
215*41490Smckusick 		}
216*41490Smckusick 		/* set ctime? */
217*41490Smckusick 		shp->shm_perm.key = IPC_PRIVATE;
218*41490Smckusick 		shp->shm_perm.mode |= SHM_DEST;
219*41490Smckusick 		if (shp->shm_nattch <= 0)
220*41490Smckusick 			shmfree(shp);
221*41490Smckusick 		break;
222*41490Smckusick 
223*41490Smckusick #ifdef HPUXCOMPAT
224*41490Smckusick 	case SHM_LOCK:
225*41490Smckusick 	case SHM_UNLOCK:
226*41490Smckusick 		/* don't really do anything, but make them think we did */
227*41490Smckusick 		if ((u.u_procp->p_flag & SHPUX) == 0)
228*41490Smckusick 			u.u_error = EINVAL;
229*41490Smckusick 		else if (u.u_uid && u.u_uid != shp->shm_perm.uid &&
230*41490Smckusick 			 u.u_uid != shp->shm_perm.cuid)
231*41490Smckusick 			u.u_error = EPERM;
232*41490Smckusick 		break;
233*41490Smckusick #endif
234*41490Smckusick 
235*41490Smckusick 	default:
236*41490Smckusick 		u.u_error = EINVAL;
237*41490Smckusick 		break;
238*41490Smckusick 	}
239*41490Smckusick }
240*41490Smckusick 
241*41490Smckusick shmat(ap)
242*41490Smckusick 	int *ap;
243*41490Smckusick {
244*41490Smckusick 	struct a {
245*41490Smckusick 		int	shmid;
246*41490Smckusick 		caddr_t	shmaddr;
247*41490Smckusick 		int	shmflg;
248*41490Smckusick 	} *uap = (struct a *)ap;
249*41490Smckusick 	register struct shmid_ds *shp;
250*41490Smckusick 	register int size;
251*41490Smckusick 	struct mapmem *mp;
252*41490Smckusick 	caddr_t uva;
253*41490Smckusick 	int prot, shmmapin();
254*41490Smckusick 
255*41490Smckusick 	if (!shmvalid(uap->shmid))
256*41490Smckusick 		return;
257*41490Smckusick 	shp = &shmsegs[uap->shmid % SHMMMNI];
258*41490Smckusick 	if (shp->shm_handle == NULL)
259*41490Smckusick 		panic("shmat NULL ptbl");
260*41490Smckusick 	if (!ipcaccess(&shp->shm_perm,
261*41490Smckusick 		      (uap->shmflg&SHM_RDONLY) ? IPC_R : IPC_R|IPC_W))
262*41490Smckusick 		return;
263*41490Smckusick 	uva = uap->shmaddr;
264*41490Smckusick 	if (uva && ((int)uva & (SHMLBA-1))) {
265*41490Smckusick 		if (uap->shmflg & SHM_RND)
266*41490Smckusick 			uva = (caddr_t) ((int)uva & ~(SHMLBA-1));
267*41490Smckusick 		else {
268*41490Smckusick 			u.u_error = EINVAL;
269*41490Smckusick 			return;
270*41490Smckusick 		}
271*41490Smckusick 	}
272*41490Smckusick 	/*
273*41490Smckusick 	 * Make sure user doesn't use more than their fair share
274*41490Smckusick 	 */
275*41490Smckusick 	size = 0;
276*41490Smckusick 	for (mp = u.u_mmap; mp; mp = mp->mm_next)
277*41490Smckusick 		if (mp->mm_ops == &shmops)
278*41490Smckusick 			size++;
279*41490Smckusick 	if (size >= shminfo.shmseg) {
280*41490Smckusick 		u.u_error = EMFILE;
281*41490Smckusick 		return;
282*41490Smckusick 	}
283*41490Smckusick 	/*
284*41490Smckusick 	 * Allocate a mapped memory region descriptor and
285*41490Smckusick 	 * attempt to expand the user page table to allow for region
286*41490Smckusick 	 */
287*41490Smckusick 	prot = (uap->shmflg & SHM_RDONLY) ? MM_RO : MM_RW;
288*41490Smckusick #if defined(hp300)
289*41490Smckusick 	prot |= MM_CI;
290*41490Smckusick #endif
291*41490Smckusick 	size = ctob(clrnd(btoc(shp->shm_segsz)));
292*41490Smckusick 	mp = mmalloc(uap->shmid, &uva, (size_t)size, prot, &shmops);
293*41490Smckusick 	if (mp == MMNIL)
294*41490Smckusick 		return;
295*41490Smckusick 	if (!mmmapin(mp, shmmapin)) {
296*41490Smckusick 		mmfree(mp);
297*41490Smckusick 		return;
298*41490Smckusick 	}
299*41490Smckusick 	/*
300*41490Smckusick 	 * Fill in the remaining fields
301*41490Smckusick 	 */
302*41490Smckusick 	shp->shm_lpid = u.u_procp->p_pid;
303*41490Smckusick 	shp->shm_atime = time.tv_sec;
304*41490Smckusick 	shp->shm_nattch++;
305*41490Smckusick 	u.u_r.r_val1 = (int) uva;
306*41490Smckusick }
307*41490Smckusick 
308*41490Smckusick shmdt(ap)
309*41490Smckusick 	int *ap;
310*41490Smckusick {
311*41490Smckusick 	register struct a {
312*41490Smckusick 		caddr_t	shmaddr;
313*41490Smckusick 	} *uap = (struct a *)ap;
314*41490Smckusick 	register struct mapmem *mp;
315*41490Smckusick 
316*41490Smckusick 	for (mp = u.u_mmap; mp; mp = mp->mm_next)
317*41490Smckusick 		if (mp->mm_ops == &shmops && mp->mm_uva == uap->shmaddr)
318*41490Smckusick 			break;
319*41490Smckusick 	if (mp == MMNIL) {
320*41490Smckusick 		u.u_error = EINVAL;
321*41490Smckusick 		return;
322*41490Smckusick 	}
323*41490Smckusick 	shmsegs[mp->mm_id % SHMMMNI].shm_lpid = u.u_procp->p_pid;
324*41490Smckusick 	shmufree(mp);
325*41490Smckusick }
326*41490Smckusick 
327*41490Smckusick shmmapin(mp, off)
328*41490Smckusick 	struct mapmem *mp;
329*41490Smckusick {
330*41490Smckusick 	register struct shmid_ds *shp;
331*41490Smckusick 
332*41490Smckusick 	shp = &shmsegs[mp->mm_id % SHMMMNI];
333*41490Smckusick 	if (off >= ctob(clrnd(btoc(shp->shm_segsz))))
334*41490Smckusick 		return(-1);
335*41490Smckusick 	return(((struct pte *)shp->shm_handle)[btop(off)].pg_pfnum);
336*41490Smckusick }
337*41490Smckusick 
338*41490Smckusick /*
339*41490Smckusick  * Increment attach count on fork
340*41490Smckusick  */
341*41490Smckusick shmfork(mp, ischild)
342*41490Smckusick 	register struct mapmem *mp;
343*41490Smckusick {
344*41490Smckusick 	if (!ischild)
345*41490Smckusick 		shmsegs[mp->mm_id % SHMMMNI].shm_nattch++;
346*41490Smckusick }
347*41490Smckusick 
348*41490Smckusick /*
349*41490Smckusick  * Detach from shared memory segment on exit (or exec)
350*41490Smckusick  */
351*41490Smckusick shmexit(mp)
352*41490Smckusick 	register struct mapmem *mp;
353*41490Smckusick {
354*41490Smckusick 	shmufree(mp);
355*41490Smckusick }
356*41490Smckusick 
357*41490Smckusick shmvalid(id)
358*41490Smckusick 	register int id;
359*41490Smckusick {
360*41490Smckusick 	register struct shmid_ds *shp;
361*41490Smckusick 
362*41490Smckusick 	if (id < 0 || (id % SHMMMNI) >= shminfo.shmmni)
363*41490Smckusick 		return(0);
364*41490Smckusick 	shp = &shmsegs[id % SHMMMNI];
365*41490Smckusick 	if (shp->shm_perm.seq == (id / SHMMMNI) &&
366*41490Smckusick 	    (shp->shm_perm.mode & (SHM_ALLOC|SHM_DEST)) == SHM_ALLOC)
367*41490Smckusick 		return(1);
368*41490Smckusick 	u.u_error = EINVAL;
369*41490Smckusick 	return(0);
370*41490Smckusick }
371*41490Smckusick 
372*41490Smckusick /*
373*41490Smckusick  * Free user resources associated with a shared memory segment
374*41490Smckusick  */
375*41490Smckusick shmufree(mp)
376*41490Smckusick 	struct mapmem *mp;
377*41490Smckusick {
378*41490Smckusick 	register struct shmid_ds *shp;
379*41490Smckusick 
380*41490Smckusick 	shp = &shmsegs[mp->mm_id % SHMMMNI];
381*41490Smckusick 	mmmapout(mp);
382*41490Smckusick 	mmfree(mp);
383*41490Smckusick 	shp->shm_dtime = time.tv_sec;
384*41490Smckusick 	if (--shp->shm_nattch <= 0 && (shp->shm_perm.mode & SHM_DEST))
385*41490Smckusick 		shmfree(shp);
386*41490Smckusick }
387*41490Smckusick 
388*41490Smckusick /*
389*41490Smckusick  * Deallocate resources associated with a shared memory segment
390*41490Smckusick  */
391*41490Smckusick shmfree(shp)
392*41490Smckusick 	register struct shmid_ds *shp;
393*41490Smckusick {
394*41490Smckusick 	caddr_t kva;
395*41490Smckusick 
396*41490Smckusick 	if (shp->shm_handle == NULL)
397*41490Smckusick 		panic("shmfree");
398*41490Smckusick 	kva = (caddr_t) ptetokv(shp->shm_handle);
399*41490Smckusick 	free(kva, M_SHM);
400*41490Smckusick 	shp->shm_handle = NULL;
401*41490Smckusick 	shmtot -= clrnd(btoc(shp->shm_segsz));
402*41490Smckusick 	shp->shm_perm.mode = 0;
403*41490Smckusick 	/*
404*41490Smckusick 	 * Increment the sequence number to ensure that outstanding
405*41490Smckusick 	 * shmids for this segment will be invalid in the event that
406*41490Smckusick 	 * the segment is reallocated.  Note that shmids must be
407*41490Smckusick 	 * positive as decreed by SVID.
408*41490Smckusick 	 */
409*41490Smckusick 	shp->shm_perm.seq++;
410*41490Smckusick 	if ((int)(shp->shm_perm.seq * SHMMMNI) < 0)
411*41490Smckusick 		shp->shm_perm.seq = 0;
412*41490Smckusick }
413*41490Smckusick 
414*41490Smckusick /*
415*41490Smckusick  * XXX This routine would be common to all sysV style IPC
416*41490Smckusick  *     (if the others were implemented).
417*41490Smckusick  */
418*41490Smckusick ipcaccess(ipc, mode)
419*41490Smckusick 	register struct ipc_perm *ipc;
420*41490Smckusick {
421*41490Smckusick 	register int m;
422*41490Smckusick 
423*41490Smckusick 	if (u.u_uid == 0)
424*41490Smckusick 		return(0);
425*41490Smckusick 	/*
426*41490Smckusick 	 * Access check is based on only one of owner, group, public.
427*41490Smckusick 	 * If not owner, then check group.
428*41490Smckusick 	 * If not a member of the group, then check public access.
429*41490Smckusick 	 */
430*41490Smckusick 	mode &= 0700;
431*41490Smckusick 	m = ipc->mode;
432*41490Smckusick 	if (u.u_uid != ipc->uid && u.u_uid != ipc->cuid) {
433*41490Smckusick 		m <<= 3;
434*41490Smckusick 		if (!groupmember(ipc->gid, u.u_cred) &&
435*41490Smckusick 		    !groupmember(ipc->cgid, u.u_cred))
436*41490Smckusick 			m <<= 3;
437*41490Smckusick 	}
438*41490Smckusick 	if ((mode&m) == mode)
439*41490Smckusick 		return (1);
440*41490Smckusick 	u.u_error = EACCES;
441*41490Smckusick 	return (0);
442*41490Smckusick }
443*41490Smckusick 
444*41490Smckusick #endif /* SYSVSHM */
445