make kernel includes standard
[unix-history] / usr / src / sys / kern / sysv_shm.c
CommitLineData
6f843dc9
KM
1/*
2 * Copyright (c) 1988 University of Utah.
3 * Copyright (c) 1990 The Regents of the University of California.
4 * All rights reserved.
5 *
6 * This code is derived from software contributed to Berkeley by
7 * the Systems Programming Group of the University of Utah Computer
8 * Science Department. Originally from University of Wisconsin.
9 *
10 * %sccs.include.redist.c%
11 *
3cf1b235 12 * from: Utah $Hdr: uipc_shm.c 1.11 92/04/23$
6f843dc9 13 *
38a01dbe 14 * @(#)sysv_shm.c 7.20 (Berkeley) %G%
6f843dc9
KM
15 */
16
17/*
18 * System V shared memory routines.
e4f7cdf5
MK
19 * TEMPORARY, until mmap is in place;
20 * needed now for HP-UX compatibility and X server (yech!).
6f843dc9
KM
21 */
22
23#ifdef SYSVSHM
24
38a01dbe
KB
25#include <sys/param.h>
26#include <sys/systm.h>
27#include <sys/kernel.h>
28#include <sys/proc.h>
29#include <sys/shm.h>
30#include <sys/malloc.h>
31#include <sys/mman.h>
32
33#include <vm/vm.h>
34#include <vm/vm_kern.h>
35#include <vm/vm_inherit.h>
36#include <vm/vm_pager.h>
6f843dc9 37
6f843dc9
KM
38int shmat(), shmctl(), shmdt(), shmget();
39int (*shmcalls[])() = { shmat, shmctl, shmdt, shmget };
40int shmtot = 0;
41
9db58063
KM
42/*
43 * Per process internal structure for managing segments.
44 * Each process using shm will have an array of ``shmseg'' of these.
45 */
46struct shmdesc {
47 vm_offset_t shmd_uva;
48 int shmd_id;
49};
50
51/*
52 * Per segment internal structure (shm_handle).
53 */
54struct shmhandle {
55 vm_offset_t shmh_kva;
56 caddr_t shmh_id;
57};
58
59vm_map_t shm_map; /* address space for shared memory segments */
6f843dc9
KM
60
61shminit()
62{
63 register int i;
9db58063 64 vm_offset_t whocares1, whocares2;
6f843dc9 65
9db58063
KM
66 shm_map = kmem_suballoc(kernel_map, &whocares1, &whocares2,
67 shminfo.shmall * NBPG, FALSE);
6f843dc9
KM
68 if (shminfo.shmmni > SHMMMNI)
69 shminfo.shmmni = SHMMMNI;
70 for (i = 0; i < shminfo.shmmni; i++) {
71 shmsegs[i].shm_perm.mode = 0;
72 shmsegs[i].shm_perm.seq = 0;
73 }
74}
75
161c14a2
KM
76/*
77 * Entry point for all SHM calls
78 */
afc12c69
CT
79struct shmsys_args {
80 u_int which;
81};
161c14a2
KM
82shmsys(p, uap, retval)
83 struct proc *p;
afc12c69 84 struct shmsys_args *uap;
161c14a2
KM
85 int *retval;
86{
6f843dc9 87
161c14a2 88 if (uap->which >= sizeof(shmcalls)/sizeof(shmcalls[0]))
d9c2f47f
MK
89 return (EINVAL);
90 return ((*shmcalls[uap->which])(p, &uap[1], retval));
6f843dc9
KM
91}
92
161c14a2
KM
93/*
94 * Get a shared memory segment
95 */
afc12c69
CT
96struct shmget_args {
97 key_t key;
98 int size;
99 int shmflg;
100};
161c14a2
KM
101shmget(p, uap, retval)
102 struct proc *p;
afc12c69 103 register struct shmget_args *uap;
161c14a2
KM
104 int *retval;
105{
6f843dc9 106 register struct shmid_ds *shp;
8429d022 107 register struct ucred *cred = p->p_ucred;
6f843dc9 108 register int i;
161c14a2 109 int error, size, rval = 0;
9db58063 110 register struct shmhandle *shmh;
6f843dc9
KM
111
112 /* look up the specified shm_id */
113 if (uap->key != IPC_PRIVATE) {
114 for (i = 0; i < shminfo.shmmni; i++)
115 if ((shmsegs[i].shm_perm.mode & SHM_ALLOC) &&
116 shmsegs[i].shm_perm.key == uap->key) {
117 rval = i;
118 break;
119 }
120 } else
121 i = shminfo.shmmni;
122
123 /* create a new shared segment if necessary */
124 if (i == shminfo.shmmni) {
161c14a2
KM
125 if ((uap->shmflg & IPC_CREAT) == 0)
126 return (ENOENT);
127 if (uap->size < shminfo.shmmin || uap->size > shminfo.shmmax)
128 return (EINVAL);
6f843dc9
KM
129 for (i = 0; i < shminfo.shmmni; i++)
130 if ((shmsegs[i].shm_perm.mode & SHM_ALLOC) == 0) {
131 rval = i;
132 break;
133 }
161c14a2
KM
134 if (i == shminfo.shmmni)
135 return (ENOSPC);
6f843dc9 136 size = clrnd(btoc(uap->size));
161c14a2
KM
137 if (shmtot + size > shminfo.shmall)
138 return (ENOMEM);
6f843dc9
KM
139 shp = &shmsegs[rval];
140 /*
141 * We need to do a couple of things to ensure consistency
142 * in case we sleep in malloc(). We mark segment as
143 * allocated so that other shmgets() will not allocate it.
144 * We mark it as "destroyed" to insure that shmvalid() is
145 * false making most operations fail (XXX). We set the key,
146 * so that other shmget()s will fail.
147 */
148 shp->shm_perm.mode = SHM_ALLOC | SHM_DEST;
149 shp->shm_perm.key = uap->key;
9db58063
KM
150 shmh = (struct shmhandle *)
151 malloc(sizeof(struct shmhandle), M_SHM, M_WAITOK);
152 shmh->shmh_kva = 0;
153 shmh->shmh_id = (caddr_t)(0xc0000000|rval); /* XXX */
154 error = vm_mmap(shm_map, &shmh->shmh_kva, ctob(size),
155 VM_PROT_ALL, MAP_ANON, shmh->shmh_id, 0);
156 if (error) {
157 free((caddr_t)shmh, M_SHM);
6f843dc9 158 shp->shm_perm.mode = 0;
9db58063 159 return(ENOMEM);
6f843dc9 160 }
9db58063 161 shp->shm_handle = (void *) shmh;
6f843dc9 162 shmtot += size;
161c14a2
KM
163 shp->shm_perm.cuid = shp->shm_perm.uid = cred->cr_uid;
164 shp->shm_perm.cgid = shp->shm_perm.gid = cred->cr_gid;
6f843dc9 165 shp->shm_perm.mode = SHM_ALLOC | (uap->shmflg&0777);
6f843dc9 166 shp->shm_segsz = uap->size;
c9714ae3 167 shp->shm_cpid = p->p_pid;
6f843dc9
KM
168 shp->shm_lpid = shp->shm_nattch = 0;
169 shp->shm_atime = shp->shm_dtime = 0;
170 shp->shm_ctime = time.tv_sec;
171 } else {
172 shp = &shmsegs[rval];
173 /* XXX: probably not the right thing to do */
161c14a2
KM
174 if (shp->shm_perm.mode & SHM_DEST)
175 return (EBUSY);
015c074c 176 if (error = ipcaccess(&shp->shm_perm, uap->shmflg&0777, cred))
161c14a2
KM
177 return (error);
178 if (uap->size && uap->size > shp->shm_segsz)
179 return (EINVAL);
180 if ((uap->shmflg&IPC_CREAT) && (uap->shmflg&IPC_EXCL))
181 return (EEXIST);
6f843dc9 182 }
161c14a2 183 *retval = shp->shm_perm.seq * SHMMMNI + rval;
015c074c 184 return (0);
6f843dc9
KM
185}
186
161c14a2
KM
187/*
188 * Shared memory control
189 */
afc12c69
CT
190struct shmctl_args {
191 int shmid;
192 int cmd;
193 caddr_t buf;
194};
161c14a2
KM
195/* ARGSUSED */
196shmctl(p, uap, retval)
197 struct proc *p;
afc12c69 198 register struct shmctl_args *uap;
161c14a2
KM
199 int *retval;
200{
6f843dc9 201 register struct shmid_ds *shp;
8429d022 202 register struct ucred *cred = p->p_ucred;
6f843dc9 203 struct shmid_ds sbuf;
161c14a2 204 int error;
6f843dc9 205
161c14a2
KM
206 if (error = shmvalid(uap->shmid))
207 return (error);
6f843dc9
KM
208 shp = &shmsegs[uap->shmid % SHMMMNI];
209 switch (uap->cmd) {
210 case IPC_STAT:
015c074c 211 if (error = ipcaccess(&shp->shm_perm, IPC_R, cred))
161c14a2
KM
212 return (error);
213 return (copyout((caddr_t)shp, uap->buf, sizeof(*shp)));
6f843dc9
KM
214
215 case IPC_SET:
161c14a2
KM
216 if (cred->cr_uid && cred->cr_uid != shp->shm_perm.uid &&
217 cred->cr_uid != shp->shm_perm.cuid)
218 return (EPERM);
219 if (error = copyin(uap->buf, (caddr_t)&sbuf, sizeof sbuf))
220 return (error);
221 shp->shm_perm.uid = sbuf.shm_perm.uid;
222 shp->shm_perm.gid = sbuf.shm_perm.gid;
223 shp->shm_perm.mode = (shp->shm_perm.mode & ~0777)
224 | (sbuf.shm_perm.mode & 0777);
225 shp->shm_ctime = time.tv_sec;
6f843dc9
KM
226 break;
227
228 case IPC_RMID:
161c14a2
KM
229 if (cred->cr_uid && cred->cr_uid != shp->shm_perm.uid &&
230 cred->cr_uid != shp->shm_perm.cuid)
231 return (EPERM);
6f843dc9
KM
232 /* set ctime? */
233 shp->shm_perm.key = IPC_PRIVATE;
234 shp->shm_perm.mode |= SHM_DEST;
235 if (shp->shm_nattch <= 0)
236 shmfree(shp);
237 break;
238
6f843dc9 239 default:
161c14a2 240 return (EINVAL);
6f843dc9 241 }
161c14a2 242 return (0);
6f843dc9
KM
243}
244
161c14a2
KM
245/*
246 * Attach to shared memory segment.
247 */
afc12c69
CT
248struct shmat_args {
249 int shmid;
250 caddr_t shmaddr;
251 int shmflg;
252};
161c14a2
KM
253shmat(p, uap, retval)
254 struct proc *p;
afc12c69 255 register struct shmat_args *uap;
161c14a2
KM
256 int *retval;
257{
6f843dc9
KM
258 register struct shmid_ds *shp;
259 register int size;
6f843dc9 260 caddr_t uva;
9db58063
KM
261 int error;
262 int flags;
263 vm_prot_t prot;
264 struct shmdesc *shmd;
6f843dc9 265
9db58063
KM
266 /*
267 * Allocate descriptors now (before validity check)
268 * in case malloc() blocks.
269 */
8429d022 270 shmd = (struct shmdesc *)p->p_vmspace->vm_shm;
9db58063
KM
271 size = shminfo.shmseg * sizeof(struct shmdesc);
272 if (shmd == NULL) {
273 shmd = (struct shmdesc *)malloc(size, M_SHM, M_WAITOK);
274 bzero((caddr_t)shmd, size);
8429d022 275 p->p_vmspace->vm_shm = (caddr_t)shmd;
9db58063 276 }
161c14a2
KM
277 if (error = shmvalid(uap->shmid))
278 return (error);
6f843dc9
KM
279 shp = &shmsegs[uap->shmid % SHMMMNI];
280 if (shp->shm_handle == NULL)
96ea38ce 281 panic("shmat NULL handle");
015c074c 282 if (error = ipcaccess(&shp->shm_perm,
8429d022 283 (uap->shmflg&SHM_RDONLY) ? IPC_R : IPC_R|IPC_W, p->p_ucred))
161c14a2 284 return (error);
6f843dc9
KM
285 uva = uap->shmaddr;
286 if (uva && ((int)uva & (SHMLBA-1))) {
287 if (uap->shmflg & SHM_RND)
288 uva = (caddr_t) ((int)uva & ~(SHMLBA-1));
161c14a2
KM
289 else
290 return (EINVAL);
6f843dc9
KM
291 }
292 /*
293 * Make sure user doesn't use more than their fair share
294 */
9db58063
KM
295 for (size = 0; size < shminfo.shmseg; size++) {
296 if (shmd->shmd_uva == 0)
297 break;
298 shmd++;
299 }
161c14a2
KM
300 if (size >= shminfo.shmseg)
301 return (EMFILE);
6f843dc9 302 size = ctob(clrnd(btoc(shp->shm_segsz)));
9db58063
KM
303 prot = VM_PROT_READ;
304 if ((uap->shmflg & SHM_RDONLY) == 0)
305 prot |= VM_PROT_WRITE;
306 flags = MAP_ANON|MAP_SHARED;
307 if (uva)
308 flags |= MAP_FIXED;
309 else
310 uva = (caddr_t)0x1000000; /* XXX */
451df175
KM
311 error = vm_mmap(&p->p_vmspace->vm_map, (vm_offset_t *)&uva,
312 (vm_size_t)size, prot, flags,
313 ((struct shmhandle *)shp->shm_handle)->shmh_id, 0);
161c14a2 314 if (error)
9db58063
KM
315 return(error);
316 shmd->shmd_uva = (vm_offset_t)uva;
317 shmd->shmd_id = uap->shmid;
6f843dc9
KM
318 /*
319 * Fill in the remaining fields
320 */
c9714ae3 321 shp->shm_lpid = p->p_pid;
6f843dc9
KM
322 shp->shm_atime = time.tv_sec;
323 shp->shm_nattch++;
161c14a2 324 *retval = (int) uva;
015c074c 325 return (0);
6f843dc9
KM
326}
327
161c14a2
KM
328/*
329 * Detach from shared memory segment.
330 */
afc12c69
CT
331struct shmdt_args {
332 caddr_t shmaddr;
333};
161c14a2
KM
334/* ARGSUSED */
335shmdt(p, uap, retval)
336 struct proc *p;
afc12c69 337 struct shmdt_args *uap;
161c14a2
KM
338 int *retval;
339{
9db58063
KM
340 register struct shmdesc *shmd;
341 register int i;
6f843dc9 342
8429d022 343 shmd = (struct shmdesc *)p->p_vmspace->vm_shm;
9db58063
KM
344 for (i = 0; i < shminfo.shmseg; i++, shmd++)
345 if (shmd->shmd_uva &&
346 shmd->shmd_uva == (vm_offset_t)uap->shmaddr)
6f843dc9 347 break;
9db58063
KM
348 if (i == shminfo.shmseg)
349 return(EINVAL);
350 shmufree(p, shmd);
351 shmsegs[shmd->shmd_id % SHMMMNI].shm_lpid = p->p_pid;
6f843dc9
KM
352}
353
8429d022
MK
354shmfork(p1, p2, isvfork)
355 struct proc *p1, *p2;
9db58063 356 int isvfork;
6f843dc9 357{
9db58063
KM
358 register struct shmdesc *shmd;
359 register int size;
6f843dc9 360
9db58063
KM
361 /*
362 * Copy parents descriptive information
363 */
364 size = shminfo.shmseg * sizeof(struct shmdesc);
365 shmd = (struct shmdesc *)malloc(size, M_SHM, M_WAITOK);
8429d022
MK
366 bcopy((caddr_t)p1->p_vmspace->vm_shm, (caddr_t)shmd, size);
367 p2->p_vmspace->vm_shm = (caddr_t)shmd;
9db58063
KM
368 /*
369 * Increment reference counts
370 */
371 for (size = 0; size < shminfo.shmseg; size++, shmd++)
372 if (shmd->shmd_uva)
373 shmsegs[shmd->shmd_id % SHMMMNI].shm_nattch++;
6f843dc9
KM
374}
375
9db58063
KM
376shmexit(p)
377 struct proc *p;
6f843dc9 378{
9db58063
KM
379 register struct shmdesc *shmd;
380 register int i;
c9714ae3 381
8429d022 382 shmd = (struct shmdesc *)p->p_vmspace->vm_shm;
9db58063
KM
383 for (i = 0; i < shminfo.shmseg; i++, shmd++)
384 if (shmd->shmd_uva)
385 shmufree(p, shmd);
8429d022
MK
386 free((caddr_t)p->p_vmspace->vm_shm, M_SHM);
387 p->p_vmspace->vm_shm = NULL;
6f843dc9
KM
388}
389
390shmvalid(id)
391 register int id;
392{
393 register struct shmid_ds *shp;
394
395 if (id < 0 || (id % SHMMMNI) >= shminfo.shmmni)
161c14a2 396 return(EINVAL);
6f843dc9
KM
397 shp = &shmsegs[id % SHMMMNI];
398 if (shp->shm_perm.seq == (id / SHMMMNI) &&
399 (shp->shm_perm.mode & (SHM_ALLOC|SHM_DEST)) == SHM_ALLOC)
161c14a2
KM
400 return(0);
401 return(EINVAL);
6f843dc9
KM
402}
403
404/*
405 * Free user resources associated with a shared memory segment
406 */
9db58063 407shmufree(p, shmd)
c9714ae3 408 struct proc *p;
9db58063 409 struct shmdesc *shmd;
6f843dc9
KM
410{
411 register struct shmid_ds *shp;
412
9db58063 413 shp = &shmsegs[shmd->shmd_id % SHMMMNI];
aa6d6b7e 414 (void) vm_deallocate(&p->p_vmspace->vm_map, shmd->shmd_uva,
9db58063
KM
415 ctob(clrnd(btoc(shp->shm_segsz))));
416 shmd->shmd_id = 0;
417 shmd->shmd_uva = 0;
6f843dc9
KM
418 shp->shm_dtime = time.tv_sec;
419 if (--shp->shm_nattch <= 0 && (shp->shm_perm.mode & SHM_DEST))
420 shmfree(shp);
421}
422
423/*
424 * Deallocate resources associated with a shared memory segment
425 */
426shmfree(shp)
427 register struct shmid_ds *shp;
428{
6f843dc9
KM
429
430 if (shp->shm_handle == NULL)
431 panic("shmfree");
9db58063
KM
432 /*
433 * Lose our lingering object reference by deallocating space
434 * in kernel. Pager will also be deallocated as a side-effect.
435 */
436 vm_deallocate(shm_map,
437 ((struct shmhandle *)shp->shm_handle)->shmh_kva,
9d81fbb9 438 ctob(clrnd(btoc(shp->shm_segsz))));
9db58063 439 free((caddr_t)shp->shm_handle, M_SHM);
6f843dc9
KM
440 shp->shm_handle = NULL;
441 shmtot -= clrnd(btoc(shp->shm_segsz));
442 shp->shm_perm.mode = 0;
443 /*
444 * Increment the sequence number to ensure that outstanding
445 * shmids for this segment will be invalid in the event that
446 * the segment is reallocated. Note that shmids must be
447 * positive as decreed by SVID.
448 */
449 shp->shm_perm.seq++;
450 if ((int)(shp->shm_perm.seq * SHMMMNI) < 0)
451 shp->shm_perm.seq = 0;
452}
453
454/*
455 * XXX This routine would be common to all sysV style IPC
456 * (if the others were implemented).
457 */
161c14a2 458ipcaccess(ipc, mode, cred)
6f843dc9 459 register struct ipc_perm *ipc;
161c14a2
KM
460 int mode;
461 register struct ucred *cred;
6f843dc9
KM
462{
463 register int m;
464
161c14a2 465 if (cred->cr_uid == 0)
6f843dc9
KM
466 return(0);
467 /*
468 * Access check is based on only one of owner, group, public.
469 * If not owner, then check group.
470 * If not a member of the group, then check public access.
471 */
472 mode &= 0700;
473 m = ipc->mode;
161c14a2 474 if (cred->cr_uid != ipc->uid && cred->cr_uid != ipc->cuid) {
6f843dc9 475 m <<= 3;
161c14a2
KM
476 if (!groupmember(ipc->gid, cred) &&
477 !groupmember(ipc->cgid, cred))
6f843dc9
KM
478 m <<= 3;
479 }
480 if ((mode&m) == mode)
161c14a2
KM
481 return (0);
482 return (EACCES);
6f843dc9 483}
6f843dc9 484#endif /* SYSVSHM */