1/* $Id: sysv_shm.c,v 1.12 1995/12/04 02:26:53 jkh Exp $ */
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 <sys/param.h>
35#include <sys/systm.h>
36#include <sys/sysproto.h>
37#include <sys/kernel.h>
38#include <sys/shm.h>
39#include <sys/proc.h>
40#include <sys/malloc.h>
41#include <sys/mman.h>
42#include <sys/stat.h>
43#include <sys/sysent.h>
44
45#include <vm/vm.h>
46#include <vm/vm_param.h>
47#include <vm/vm_prot.h>
48#include <vm/lock.h>
49#include <vm/pmap.h>
50#include <vm/vm_map.h>
51#include <vm/vm_kern.h>
52#include <vm/vm_extern.h>
53
54struct shmat_args;
55extern int shmat __P((struct proc *p, struct shmat_args *uap, int *retval));
56struct shmctl_args;
57extern int shmctl __P((struct proc *p, struct shmctl_args *uap, int *retval));
58struct shmdt_args;
59extern int shmdt __P((struct proc *p, struct shmdt_args *uap, int *retval));
60struct shmget_args;
61extern int shmget __P((struct proc *p, struct shmget_args *uap, int *retval));
62
63static void shminit __P((void *));
64SYSINIT(sysv_shm, SI_SUB_SYSV_SHM, SI_ORDER_FIRST, shminit, NULL)
65
66struct oshmctl_args;
67int oshmctl __P((struct proc *p, struct oshmctl_args *uap, int *retval));
68static int shmget_allocate_segment __P((struct proc *p, struct shmget_args *uap, int mode, int *retval));
69static int shmget_existing __P((struct proc *p, struct shmget_args *uap, int mode, int segnum, int *retval));
70
71/* XXX casting to (sy_call_t *) is bogus, as usual. */
72sy_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
83vm_map_t sysvshm_map;
84int shm_last_free, shm_nused, shm_committed;
85struct shmid_ds *shmsegs;
86
87struct shm_handle {
88 vm_offset_t kva;
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 *, struct shmmap_state *));
100
101static int
102shm_find_segment_by_key(key)
103 key_t key;
104{
105 int i;
106
107 for (i = 0; i < shminfo.shmmni; i++)
108 if ((shmsegs[i].shm_perm.mode & SHMSEG_ALLOCATED) &&
109 shmsegs[i].shm_perm.key == key)
110 return i;
111 return -1;
112}
113
114static struct shmid_ds *
115shm_find_segment_by_shmid(shmid)
116 int shmid;
117{
118 int segnum;
119 struct shmid_ds *shmseg;
120
121 segnum = IPCID_TO_IX(shmid);
122 if (segnum < 0 || segnum >= shminfo.shmmni)
123 return NULL;
124 shmseg = &shmsegs[segnum];
125 if ((shmseg->shm_perm.mode & (SHMSEG_ALLOCATED | SHMSEG_REMOVED))
126 != SHMSEG_ALLOCATED ||
127 shmseg->shm_perm.seq != IPCID_TO_SEQ(shmid))
128 return NULL;
129 return shmseg;
130}
131
132static void
133shm_deallocate_segment(shmseg)
134 struct shmid_ds *shmseg;
135{
136 struct shm_handle *shm_handle;
137 size_t size;
138
139 shm_handle = shmseg->shm_internal;
140 size = (shmseg->shm_segsz + CLOFSET) & ~CLOFSET;
141 (void) vm_map_remove(sysvshm_map, shm_handle->kva, shm_handle->kva + size);
142 free((caddr_t)shm_handle, M_SHM);
143 shmseg->shm_internal = NULL;
144 shm_committed -= btoc(size);
145 shm_nused--;
146 shmseg->shm_perm.mode = SHMSEG_FREE;
147}
148
149static int
150shm_delete_mapping(p, shmmap_s)
151 struct proc *p;
152 struct shmmap_state *shmmap_s;
153{
154 struct shmid_ds *shmseg;
155 int segnum, result;
156 size_t size;
157
158 segnum = IPCID_TO_IX(shmmap_s->shmid);
159 shmseg = &shmsegs[segnum];
160 size = (shmseg->shm_segsz + CLOFSET) & ~CLOFSET;
161 result = vm_map_remove(&p->p_vmspace->vm_map, shmmap_s->va, shmmap_s->va + size);
162 if (result != KERN_SUCCESS)
163 return EINVAL;
164 shmmap_s->shmid = -1;
165 shmseg->shm_dtime = time.tv_sec;
166 if ((--shmseg->shm_nattch <= 0) &&
167 (shmseg->shm_perm.mode & SHMSEG_REMOVED)) {
168 shm_deallocate_segment(shmseg);
169 shm_last_free = segnum;
170 }
171 return 0;
172}
173
174struct shmdt_args {
175 void *shmaddr;
176};
177int
178shmdt(p, uap, retval)
179 struct proc *p;
180 struct shmdt_args *uap;
181 int *retval;
182{
183 struct shmmap_state *shmmap_s;
184 int i;
185
186 shmmap_s = (struct shmmap_state *)p->p_vmspace->vm_shm;
187 if (shmmap_s == NULL)
188 return EINVAL;
189 for (i = 0; i < shminfo.shmseg; i++, shmmap_s++)
190 if (shmmap_s->shmid != -1 &&
191 shmmap_s->va == (vm_offset_t)uap->shmaddr)
192 break;
193 if (i == shminfo.shmseg)
194 return EINVAL;
195 return shm_delete_mapping(p, shmmap_s);
196}
197
198struct shmat_args {
199 int shmid;
200 void *shmaddr;
201 int shmflg;
202};
203int
204shmat(p, uap, retval)
205 struct proc *p;
206 struct shmat_args *uap;
207 int *retval;
208{
209 int error, i, flags;
210 struct ucred *cred = p->p_ucred;
211 struct shmid_ds *shmseg;
212 struct shmmap_state *shmmap_s = NULL;
213 vm_offset_t attach_va;
214 vm_prot_t prot;
215 vm_size_t size;
216
217 shmmap_s = (struct shmmap_state *)p->p_vmspace->vm_shm;
218 if (shmmap_s == NULL) {
219 size = shminfo.shmseg * sizeof(struct shmmap_state);
220 shmmap_s = malloc(size, M_SHM, M_WAITOK);
221 for (i = 0; i < shminfo.shmseg; i++)
222 shmmap_s[i].shmid = -1;
223 p->p_vmspace->vm_shm = (caddr_t)shmmap_s;
224 }
225 shmseg = shm_find_segment_by_shmid(uap->shmid);
226 if (shmseg == NULL)
227 return EINVAL;
228 error = ipcperm(cred, &shmseg->shm_perm,
229 (uap->shmflg & SHM_RDONLY) ? IPC_R : IPC_R|IPC_W);
230 if (error)
231 return error;
232 for (i = 0; i < shminfo.shmseg; i++) {
233 if (shmmap_s->shmid == -1)
234 break;
235 shmmap_s++;
236 }
237 if (i >= shminfo.shmseg)
238 return EMFILE;
239 size = (shmseg->shm_segsz + CLOFSET) & ~CLOFSET;
240 prot = VM_PROT_READ;
241 if ((uap->shmflg & SHM_RDONLY) == 0)
242 prot |= VM_PROT_WRITE;
243 flags = MAP_ANON | MAP_SHARED;
244 if (uap->shmaddr) {
245 flags |= MAP_FIXED;
246 if (uap->shmflg & SHM_RND)
247 attach_va = (vm_offset_t)uap->shmaddr & ~(SHMLBA-1);
248 else if (((vm_offset_t)uap->shmaddr & (SHMLBA-1)) == 0)
249 attach_va = (vm_offset_t)uap->shmaddr;
250 else
251 return EINVAL;
252 } else {
253 /* This is just a hint to vm_mmap() about where to put it. */
254 attach_va = round_page(p->p_vmspace->vm_taddr + MAXTSIZ + MAXDSIZ);
255 }
256 error = vm_mmap(&p->p_vmspace->vm_map, &attach_va, size, prot,
257 VM_PROT_DEFAULT, flags, (caddr_t) uap->shmid, 0);
258 if (error)
259 return error;
260 shmmap_s->va = attach_va;
261 shmmap_s->shmid = uap->shmid;
262 shmseg->shm_lpid = p->p_pid;
263 shmseg->shm_atime = time.tv_sec;
264 shmseg->shm_nattch++;
265 *retval = attach_va;
266 return 0;
267}
268
269struct oshmid_ds {
270 struct ipc_perm shm_perm; /* operation perms */
271 int shm_segsz; /* size of segment (bytes) */
272 ushort shm_cpid; /* pid, creator */
273 ushort shm_lpid; /* pid, last operation */
274 short shm_nattch; /* no. of current attaches */
275 time_t shm_atime; /* last attach time */
276 time_t shm_dtime; /* last detach time */
277 time_t shm_ctime; /* last change time */
278 void *shm_handle; /* internal handle for shm segment */
279};
280
281struct oshmctl_args {
282 int shmid;
283 int cmd;
284 struct oshmid_ds *ubuf;
285};
286
287int
288oshmctl(p, uap, retval)
289 struct proc *p;
290 struct oshmctl_args *uap;
291 int *retval;
292{
293#ifdef COMPAT_43
294 int error;
295 struct ucred *cred = p->p_ucred;
296 struct shmid_ds *shmseg;
297 struct oshmid_ds outbuf;
298
299 shmseg = shm_find_segment_by_shmid(uap->shmid);
300 if (shmseg == NULL)
301 return EINVAL;
302 switch (uap->cmd) {
303 case IPC_STAT:
304 error = ipcperm(cred, &shmseg->shm_perm, IPC_R);
305 if (error)
306 return error;
307 outbuf.shm_perm = shmseg->shm_perm;
308 outbuf.shm_segsz = shmseg->shm_segsz;
309 outbuf.shm_cpid = shmseg->shm_cpid;
310 outbuf.shm_lpid = shmseg->shm_lpid;
311 outbuf.shm_nattch = shmseg->shm_nattch;
312 outbuf.shm_atime = shmseg->shm_atime;
313 outbuf.shm_dtime = shmseg->shm_dtime;
314 outbuf.shm_ctime = shmseg->shm_ctime;
315 outbuf.shm_handle = shmseg->shm_internal;
316 error = copyout((caddr_t)&outbuf, uap->ubuf, sizeof(outbuf));
317 if (error)
318 return error;
319 break;
320 default:
321 /* XXX casting to (sy_call_t *) is bogus, as usual. */
322 return ((sy_call_t *)shmctl)(p, uap, retval);
323 }
324 return 0;
325#else
326 return EINVAL;
327#endif
328}
329
330struct shmctl_args {
331 int shmid;
332 int cmd;
333 struct shmid_ds *ubuf;
334};
335int
336shmctl(p, uap, retval)
337 struct proc *p;
338 struct shmctl_args *uap;
339 int *retval;
340{
341 int error;
342 struct ucred *cred = p->p_ucred;
343 struct shmid_ds inbuf;
344 struct shmid_ds *shmseg;
345
346 shmseg = shm_find_segment_by_shmid(uap->shmid);
347 if (shmseg == NULL)
348 return EINVAL;
349 switch (uap->cmd) {
350 case IPC_STAT:
351 error = ipcperm(cred, &shmseg->shm_perm, IPC_R);
352 if (error)
353 return error;
354 error = copyout((caddr_t)shmseg, uap->ubuf, sizeof(inbuf));
355 if (error)
356 return error;
357 break;
358 case IPC_SET:
359 error = ipcperm(cred, &shmseg->shm_perm, IPC_M);
360 if (error)
361 return error;
362 error = copyin(uap->ubuf, (caddr_t)&inbuf, sizeof(inbuf));
363 if (error)
364 return error;
365 shmseg->shm_perm.uid = inbuf.shm_perm.uid;
366 shmseg->shm_perm.gid = inbuf.shm_perm.gid;
367 shmseg->shm_perm.mode =
368 (shmseg->shm_perm.mode & ~ACCESSPERMS) |
369 (inbuf.shm_perm.mode & ACCESSPERMS);
370 shmseg->shm_ctime = time.tv_sec;
371 break;
372 case IPC_RMID:
373 error = ipcperm(cred, &shmseg->shm_perm, IPC_M);
374 if (error)
375 return error;
376 shmseg->shm_perm.key = IPC_PRIVATE;
377 shmseg->shm_perm.mode |= SHMSEG_REMOVED;
378 if (shmseg->shm_nattch <= 0) {
379 shm_deallocate_segment(shmseg);
380 shm_last_free = IPCID_TO_IX(uap->shmid);
381 }
382 break;
383#if 0
384 case SHM_LOCK:
385 case SHM_UNLOCK:
386#endif
387 default:
388 return EINVAL;
389 }
390 return 0;
391}
392
393struct shmget_args {
394 key_t key;
395 size_t size;
396 int shmflg;
397};
398static int
399shmget_existing(p, uap, mode, segnum, retval)
400 struct proc *p;
401 struct shmget_args *uap;
402 int mode;
403 int segnum;
404 int *retval;
405{
406 struct shmid_ds *shmseg;
407 struct ucred *cred = p->p_ucred;
408 int error;
409
410 shmseg = &shmsegs[segnum];
411 if (shmseg->shm_perm.mode & SHMSEG_REMOVED) {
412 /*
413 * This segment is in the process of being allocated. Wait
414 * until it's done, and look the key up again (in case the
415 * allocation failed or it was freed).
416 */
417 shmseg->shm_perm.mode |= SHMSEG_WANTED;
418 error = tsleep((caddr_t)shmseg, PLOCK | PCATCH, "shmget", 0);
419 if (error)
420 return error;
421 return EAGAIN;
422 }
423 error = ipcperm(cred, &shmseg->shm_perm, mode);
424 if (error)
425 return error;
426 if (uap->size && uap->size > shmseg->shm_segsz)
427 return EINVAL;
428 if (uap->shmflg & (IPC_CREAT | IPC_EXCL) == (IPC_CREAT | IPC_EXCL))
429 return EEXIST;
430 *retval = IXSEQ_TO_IPCID(segnum, shmseg->shm_perm);
431 return 0;
432}
433
434static int
435shmget_allocate_segment(p, uap, mode, retval)
436 struct proc *p;
437 struct shmget_args *uap;
438 int mode;
439 int *retval;
440{
441 int i, segnum, result, shmid, size;
442 struct ucred *cred = p->p_ucred;
443 struct shmid_ds *shmseg;
444 struct shm_handle *shm_handle;
445
446 if (uap->size < shminfo.shmmin || uap->size > shminfo.shmmax)
447 return EINVAL;
448 if (shm_nused >= shminfo.shmmni) /* any shmids left? */
449 return ENOSPC;
450 size = (uap->size + CLOFSET) & ~CLOFSET;
451 if (shm_committed + btoc(size) > shminfo.shmall)
452 return ENOMEM;
453 if (shm_last_free < 0) {
454 for (i = 0; i < shminfo.shmmni; i++)
455 if (shmsegs[i].shm_perm.mode & SHMSEG_FREE)
456 break;
457 if (i == shminfo.shmmni)
458 panic("shmseg free count inconsistent");
459 segnum = i;
460 } else {
461 segnum = shm_last_free;
462 shm_last_free = -1;
463 }
464 shmseg = &shmsegs[segnum];
465 /*
466 * In case we sleep in malloc(), mark the segment present but deleted
467 * so that noone else tries to create the same key.
468 */
469 shmseg->shm_perm.mode = SHMSEG_ALLOCATED | SHMSEG_REMOVED;
470 shmseg->shm_perm.key = uap->key;
471 shmseg->shm_perm.seq = (shmseg->shm_perm.seq + 1) & 0x7fff;
472 shm_handle = (struct shm_handle *)
473 malloc(sizeof(struct shm_handle), M_SHM, M_WAITOK);
474 shmid = IXSEQ_TO_IPCID(segnum, shmseg->shm_perm);
475 result = vm_mmap(sysvshm_map, &shm_handle->kva, size, VM_PROT_ALL,
476 VM_PROT_DEFAULT, MAP_ANON, (caddr_t) shmid, 0);
477 if (result != KERN_SUCCESS) {
478 shmseg->shm_perm.mode = SHMSEG_FREE;
479 shm_last_free = segnum;
480 free((caddr_t)shm_handle, M_SHM);
481 /* Just in case. */
482 wakeup((caddr_t)shmseg);
483 return ENOMEM;
484 }
485 shmseg->shm_internal = shm_handle;
486 shmseg->shm_perm.cuid = shmseg->shm_perm.uid = cred->cr_uid;
487 shmseg->shm_perm.cgid = shmseg->shm_perm.gid = cred->cr_gid;
488 shmseg->shm_perm.mode = (shmseg->shm_perm.mode & SHMSEG_WANTED) |
489 (mode & ACCESSPERMS) | SHMSEG_ALLOCATED;
490 shmseg->shm_segsz = uap->size;
491 shmseg->shm_cpid = p->p_pid;
492 shmseg->shm_lpid = shmseg->shm_nattch = 0;
493 shmseg->shm_atime = shmseg->shm_dtime = 0;
494 shmseg->shm_ctime = time.tv_sec;
495 shm_committed += btoc(size);
496 shm_nused++;
497 if (shmseg->shm_perm.mode & SHMSEG_WANTED) {
498 /*
499 * Somebody else wanted this key while we were asleep. Wake
500 * them up now.
501 */
502 shmseg->shm_perm.mode &= ~SHMSEG_WANTED;
503 wakeup((caddr_t)shmseg);
504 }
505 *retval = shmid;
506 return 0;
507}
508
509int
510shmget(p, uap, retval)
511 struct proc *p;
512 struct shmget_args *uap;
513 int *retval;
514{
515 int segnum, mode, error;
516
517 mode = uap->shmflg & ACCESSPERMS;
518 if (uap->key != IPC_PRIVATE) {
519 again:
520 segnum = shm_find_segment_by_key(uap->key);
521 if (segnum >= 0) {
522 error = shmget_existing(p, uap, mode, segnum, retval);
523 if (error == EAGAIN)
524 goto again;
525 return error;
526 }
527 if ((uap->shmflg & IPC_CREAT) == 0)
528 return ENOENT;
529 }
530 return shmget_allocate_segment(p, uap, mode, retval);
531}
532
533int
534shmsys(p, uap, retval)
535 struct proc *p;
536 /* XXX actually varargs. */
537 struct shmsys_args /* {
538 u_int which;
539 int a2;
540 int a3;
541 int a4;
542 } */ *uap;
543 int *retval;
544{
545
546 if (uap->which >= sizeof(shmcalls)/sizeof(shmcalls[0]))
547 return EINVAL;
548 return ((*shmcalls[uap->which])(p, &uap->a2, retval));
549}
550
551void
552shmfork(p1, p2, isvfork)
553 struct proc *p1, *p2;
554 int isvfork;
555{
556 struct shmmap_state *shmmap_s;
557 size_t size;
558 int i;
559
560 size = shminfo.shmseg * sizeof(struct shmmap_state);
561 shmmap_s = malloc(size, M_SHM, M_WAITOK);
562 bcopy((caddr_t)p1->p_vmspace->vm_shm, (caddr_t)shmmap_s, size);
563 p2->p_vmspace->vm_shm = (caddr_t)shmmap_s;
564 for (i = 0; i < shminfo.shmseg; i++, shmmap_s++)
565 if (shmmap_s->shmid != -1)
566 shmsegs[IPCID_TO_IX(shmmap_s->shmid)].shm_nattch++;
567}
568
569void
570shmexit(p)
571 struct proc *p;
572{
573 struct shmmap_state *shmmap_s;
574 int i;
575
576 shmmap_s = (struct shmmap_state *)p->p_vmspace->vm_shm;
577 for (i = 0; i < shminfo.shmseg; i++, shmmap_s++)
578 if (shmmap_s->shmid != -1)
579 shm_delete_mapping(p, shmmap_s);
580 free((caddr_t)p->p_vmspace->vm_shm, M_SHM);
581 p->p_vmspace->vm_shm = NULL;
582}
583
584void
585shminit(dummy)
586 void *dummy;
587{
588 int i;
589 vm_offset_t garbage1, garbage2;
590
591 /* actually this *should* be pageable. SHM_{LOCK,UNLOCK} */
592 sysvshm_map = kmem_suballoc(kernel_map, &garbage1, &garbage2,
593 shminfo.shmall * NBPG, TRUE);
594 for (i = 0; i < shminfo.shmmni; i++) {
595 shmsegs[i].shm_perm.mode = SHMSEG_FREE;
596 shmsegs[i].shm_perm.seq = 0;
597 }
598 shm_last_free = 0;
599 shm_nused = 0;
600 shm_committed = 0;
601}
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 <sys/param.h>
35#include <sys/systm.h>
36#include <sys/sysproto.h>
37#include <sys/kernel.h>
38#include <sys/shm.h>
39#include <sys/proc.h>
40#include <sys/malloc.h>
41#include <sys/mman.h>
42#include <sys/stat.h>
43#include <sys/sysent.h>
44
45#include <vm/vm.h>
46#include <vm/vm_param.h>
47#include <vm/vm_prot.h>
48#include <vm/lock.h>
49#include <vm/pmap.h>
50#include <vm/vm_map.h>
51#include <vm/vm_kern.h>
52#include <vm/vm_extern.h>
53
54struct shmat_args;
55extern int shmat __P((struct proc *p, struct shmat_args *uap, int *retval));
56struct shmctl_args;
57extern int shmctl __P((struct proc *p, struct shmctl_args *uap, int *retval));
58struct shmdt_args;
59extern int shmdt __P((struct proc *p, struct shmdt_args *uap, int *retval));
60struct shmget_args;
61extern int shmget __P((struct proc *p, struct shmget_args *uap, int *retval));
62
63static void shminit __P((void *));
64SYSINIT(sysv_shm, SI_SUB_SYSV_SHM, SI_ORDER_FIRST, shminit, NULL)
65
66struct oshmctl_args;
67int oshmctl __P((struct proc *p, struct oshmctl_args *uap, int *retval));
68static int shmget_allocate_segment __P((struct proc *p, struct shmget_args *uap, int mode, int *retval));
69static int shmget_existing __P((struct proc *p, struct shmget_args *uap, int mode, int segnum, int *retval));
70
71/* XXX casting to (sy_call_t *) is bogus, as usual. */
72sy_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
83vm_map_t sysvshm_map;
84int shm_last_free, shm_nused, shm_committed;
85struct shmid_ds *shmsegs;
86
87struct shm_handle {
88 vm_offset_t kva;
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 *, struct shmmap_state *));
100
101static int
102shm_find_segment_by_key(key)
103 key_t key;
104{
105 int i;
106
107 for (i = 0; i < shminfo.shmmni; i++)
108 if ((shmsegs[i].shm_perm.mode & SHMSEG_ALLOCATED) &&
109 shmsegs[i].shm_perm.key == key)
110 return i;
111 return -1;
112}
113
114static struct shmid_ds *
115shm_find_segment_by_shmid(shmid)
116 int shmid;
117{
118 int segnum;
119 struct shmid_ds *shmseg;
120
121 segnum = IPCID_TO_IX(shmid);
122 if (segnum < 0 || segnum >= shminfo.shmmni)
123 return NULL;
124 shmseg = &shmsegs[segnum];
125 if ((shmseg->shm_perm.mode & (SHMSEG_ALLOCATED | SHMSEG_REMOVED))
126 != SHMSEG_ALLOCATED ||
127 shmseg->shm_perm.seq != IPCID_TO_SEQ(shmid))
128 return NULL;
129 return shmseg;
130}
131
132static void
133shm_deallocate_segment(shmseg)
134 struct shmid_ds *shmseg;
135{
136 struct shm_handle *shm_handle;
137 size_t size;
138
139 shm_handle = shmseg->shm_internal;
140 size = (shmseg->shm_segsz + CLOFSET) & ~CLOFSET;
141 (void) vm_map_remove(sysvshm_map, shm_handle->kva, shm_handle->kva + size);
142 free((caddr_t)shm_handle, M_SHM);
143 shmseg->shm_internal = NULL;
144 shm_committed -= btoc(size);
145 shm_nused--;
146 shmseg->shm_perm.mode = SHMSEG_FREE;
147}
148
149static int
150shm_delete_mapping(p, shmmap_s)
151 struct proc *p;
152 struct shmmap_state *shmmap_s;
153{
154 struct shmid_ds *shmseg;
155 int segnum, result;
156 size_t size;
157
158 segnum = IPCID_TO_IX(shmmap_s->shmid);
159 shmseg = &shmsegs[segnum];
160 size = (shmseg->shm_segsz + CLOFSET) & ~CLOFSET;
161 result = vm_map_remove(&p->p_vmspace->vm_map, shmmap_s->va, shmmap_s->va + size);
162 if (result != KERN_SUCCESS)
163 return EINVAL;
164 shmmap_s->shmid = -1;
165 shmseg->shm_dtime = time.tv_sec;
166 if ((--shmseg->shm_nattch <= 0) &&
167 (shmseg->shm_perm.mode & SHMSEG_REMOVED)) {
168 shm_deallocate_segment(shmseg);
169 shm_last_free = segnum;
170 }
171 return 0;
172}
173
174struct shmdt_args {
175 void *shmaddr;
176};
177int
178shmdt(p, uap, retval)
179 struct proc *p;
180 struct shmdt_args *uap;
181 int *retval;
182{
183 struct shmmap_state *shmmap_s;
184 int i;
185
186 shmmap_s = (struct shmmap_state *)p->p_vmspace->vm_shm;
187 if (shmmap_s == NULL)
188 return EINVAL;
189 for (i = 0; i < shminfo.shmseg; i++, shmmap_s++)
190 if (shmmap_s->shmid != -1 &&
191 shmmap_s->va == (vm_offset_t)uap->shmaddr)
192 break;
193 if (i == shminfo.shmseg)
194 return EINVAL;
195 return shm_delete_mapping(p, shmmap_s);
196}
197
198struct shmat_args {
199 int shmid;
200 void *shmaddr;
201 int shmflg;
202};
203int
204shmat(p, uap, retval)
205 struct proc *p;
206 struct shmat_args *uap;
207 int *retval;
208{
209 int error, i, flags;
210 struct ucred *cred = p->p_ucred;
211 struct shmid_ds *shmseg;
212 struct shmmap_state *shmmap_s = NULL;
213 vm_offset_t attach_va;
214 vm_prot_t prot;
215 vm_size_t size;
216
217 shmmap_s = (struct shmmap_state *)p->p_vmspace->vm_shm;
218 if (shmmap_s == NULL) {
219 size = shminfo.shmseg * sizeof(struct shmmap_state);
220 shmmap_s = malloc(size, M_SHM, M_WAITOK);
221 for (i = 0; i < shminfo.shmseg; i++)
222 shmmap_s[i].shmid = -1;
223 p->p_vmspace->vm_shm = (caddr_t)shmmap_s;
224 }
225 shmseg = shm_find_segment_by_shmid(uap->shmid);
226 if (shmseg == NULL)
227 return EINVAL;
228 error = ipcperm(cred, &shmseg->shm_perm,
229 (uap->shmflg & SHM_RDONLY) ? IPC_R : IPC_R|IPC_W);
230 if (error)
231 return error;
232 for (i = 0; i < shminfo.shmseg; i++) {
233 if (shmmap_s->shmid == -1)
234 break;
235 shmmap_s++;
236 }
237 if (i >= shminfo.shmseg)
238 return EMFILE;
239 size = (shmseg->shm_segsz + CLOFSET) & ~CLOFSET;
240 prot = VM_PROT_READ;
241 if ((uap->shmflg & SHM_RDONLY) == 0)
242 prot |= VM_PROT_WRITE;
243 flags = MAP_ANON | MAP_SHARED;
244 if (uap->shmaddr) {
245 flags |= MAP_FIXED;
246 if (uap->shmflg & SHM_RND)
247 attach_va = (vm_offset_t)uap->shmaddr & ~(SHMLBA-1);
248 else if (((vm_offset_t)uap->shmaddr & (SHMLBA-1)) == 0)
249 attach_va = (vm_offset_t)uap->shmaddr;
250 else
251 return EINVAL;
252 } else {
253 /* This is just a hint to vm_mmap() about where to put it. */
254 attach_va = round_page(p->p_vmspace->vm_taddr + MAXTSIZ + MAXDSIZ);
255 }
256 error = vm_mmap(&p->p_vmspace->vm_map, &attach_va, size, prot,
257 VM_PROT_DEFAULT, flags, (caddr_t) uap->shmid, 0);
258 if (error)
259 return error;
260 shmmap_s->va = attach_va;
261 shmmap_s->shmid = uap->shmid;
262 shmseg->shm_lpid = p->p_pid;
263 shmseg->shm_atime = time.tv_sec;
264 shmseg->shm_nattch++;
265 *retval = attach_va;
266 return 0;
267}
268
269struct oshmid_ds {
270 struct ipc_perm shm_perm; /* operation perms */
271 int shm_segsz; /* size of segment (bytes) */
272 ushort shm_cpid; /* pid, creator */
273 ushort shm_lpid; /* pid, last operation */
274 short shm_nattch; /* no. of current attaches */
275 time_t shm_atime; /* last attach time */
276 time_t shm_dtime; /* last detach time */
277 time_t shm_ctime; /* last change time */
278 void *shm_handle; /* internal handle for shm segment */
279};
280
281struct oshmctl_args {
282 int shmid;
283 int cmd;
284 struct oshmid_ds *ubuf;
285};
286
287int
288oshmctl(p, uap, retval)
289 struct proc *p;
290 struct oshmctl_args *uap;
291 int *retval;
292{
293#ifdef COMPAT_43
294 int error;
295 struct ucred *cred = p->p_ucred;
296 struct shmid_ds *shmseg;
297 struct oshmid_ds outbuf;
298
299 shmseg = shm_find_segment_by_shmid(uap->shmid);
300 if (shmseg == NULL)
301 return EINVAL;
302 switch (uap->cmd) {
303 case IPC_STAT:
304 error = ipcperm(cred, &shmseg->shm_perm, IPC_R);
305 if (error)
306 return error;
307 outbuf.shm_perm = shmseg->shm_perm;
308 outbuf.shm_segsz = shmseg->shm_segsz;
309 outbuf.shm_cpid = shmseg->shm_cpid;
310 outbuf.shm_lpid = shmseg->shm_lpid;
311 outbuf.shm_nattch = shmseg->shm_nattch;
312 outbuf.shm_atime = shmseg->shm_atime;
313 outbuf.shm_dtime = shmseg->shm_dtime;
314 outbuf.shm_ctime = shmseg->shm_ctime;
315 outbuf.shm_handle = shmseg->shm_internal;
316 error = copyout((caddr_t)&outbuf, uap->ubuf, sizeof(outbuf));
317 if (error)
318 return error;
319 break;
320 default:
321 /* XXX casting to (sy_call_t *) is bogus, as usual. */
322 return ((sy_call_t *)shmctl)(p, uap, retval);
323 }
324 return 0;
325#else
326 return EINVAL;
327#endif
328}
329
330struct shmctl_args {
331 int shmid;
332 int cmd;
333 struct shmid_ds *ubuf;
334};
335int
336shmctl(p, uap, retval)
337 struct proc *p;
338 struct shmctl_args *uap;
339 int *retval;
340{
341 int error;
342 struct ucred *cred = p->p_ucred;
343 struct shmid_ds inbuf;
344 struct shmid_ds *shmseg;
345
346 shmseg = shm_find_segment_by_shmid(uap->shmid);
347 if (shmseg == NULL)
348 return EINVAL;
349 switch (uap->cmd) {
350 case IPC_STAT:
351 error = ipcperm(cred, &shmseg->shm_perm, IPC_R);
352 if (error)
353 return error;
354 error = copyout((caddr_t)shmseg, uap->ubuf, sizeof(inbuf));
355 if (error)
356 return error;
357 break;
358 case IPC_SET:
359 error = ipcperm(cred, &shmseg->shm_perm, IPC_M);
360 if (error)
361 return error;
362 error = copyin(uap->ubuf, (caddr_t)&inbuf, sizeof(inbuf));
363 if (error)
364 return error;
365 shmseg->shm_perm.uid = inbuf.shm_perm.uid;
366 shmseg->shm_perm.gid = inbuf.shm_perm.gid;
367 shmseg->shm_perm.mode =
368 (shmseg->shm_perm.mode & ~ACCESSPERMS) |
369 (inbuf.shm_perm.mode & ACCESSPERMS);
370 shmseg->shm_ctime = time.tv_sec;
371 break;
372 case IPC_RMID:
373 error = ipcperm(cred, &shmseg->shm_perm, IPC_M);
374 if (error)
375 return error;
376 shmseg->shm_perm.key = IPC_PRIVATE;
377 shmseg->shm_perm.mode |= SHMSEG_REMOVED;
378 if (shmseg->shm_nattch <= 0) {
379 shm_deallocate_segment(shmseg);
380 shm_last_free = IPCID_TO_IX(uap->shmid);
381 }
382 break;
383#if 0
384 case SHM_LOCK:
385 case SHM_UNLOCK:
386#endif
387 default:
388 return EINVAL;
389 }
390 return 0;
391}
392
393struct shmget_args {
394 key_t key;
395 size_t size;
396 int shmflg;
397};
398static int
399shmget_existing(p, uap, mode, segnum, retval)
400 struct proc *p;
401 struct shmget_args *uap;
402 int mode;
403 int segnum;
404 int *retval;
405{
406 struct shmid_ds *shmseg;
407 struct ucred *cred = p->p_ucred;
408 int error;
409
410 shmseg = &shmsegs[segnum];
411 if (shmseg->shm_perm.mode & SHMSEG_REMOVED) {
412 /*
413 * This segment is in the process of being allocated. Wait
414 * until it's done, and look the key up again (in case the
415 * allocation failed or it was freed).
416 */
417 shmseg->shm_perm.mode |= SHMSEG_WANTED;
418 error = tsleep((caddr_t)shmseg, PLOCK | PCATCH, "shmget", 0);
419 if (error)
420 return error;
421 return EAGAIN;
422 }
423 error = ipcperm(cred, &shmseg->shm_perm, mode);
424 if (error)
425 return error;
426 if (uap->size && uap->size > shmseg->shm_segsz)
427 return EINVAL;
428 if (uap->shmflg & (IPC_CREAT | IPC_EXCL) == (IPC_CREAT | IPC_EXCL))
429 return EEXIST;
430 *retval = IXSEQ_TO_IPCID(segnum, shmseg->shm_perm);
431 return 0;
432}
433
434static int
435shmget_allocate_segment(p, uap, mode, retval)
436 struct proc *p;
437 struct shmget_args *uap;
438 int mode;
439 int *retval;
440{
441 int i, segnum, result, shmid, size;
442 struct ucred *cred = p->p_ucred;
443 struct shmid_ds *shmseg;
444 struct shm_handle *shm_handle;
445
446 if (uap->size < shminfo.shmmin || uap->size > shminfo.shmmax)
447 return EINVAL;
448 if (shm_nused >= shminfo.shmmni) /* any shmids left? */
449 return ENOSPC;
450 size = (uap->size + CLOFSET) & ~CLOFSET;
451 if (shm_committed + btoc(size) > shminfo.shmall)
452 return ENOMEM;
453 if (shm_last_free < 0) {
454 for (i = 0; i < shminfo.shmmni; i++)
455 if (shmsegs[i].shm_perm.mode & SHMSEG_FREE)
456 break;
457 if (i == shminfo.shmmni)
458 panic("shmseg free count inconsistent");
459 segnum = i;
460 } else {
461 segnum = shm_last_free;
462 shm_last_free = -1;
463 }
464 shmseg = &shmsegs[segnum];
465 /*
466 * In case we sleep in malloc(), mark the segment present but deleted
467 * so that noone else tries to create the same key.
468 */
469 shmseg->shm_perm.mode = SHMSEG_ALLOCATED | SHMSEG_REMOVED;
470 shmseg->shm_perm.key = uap->key;
471 shmseg->shm_perm.seq = (shmseg->shm_perm.seq + 1) & 0x7fff;
472 shm_handle = (struct shm_handle *)
473 malloc(sizeof(struct shm_handle), M_SHM, M_WAITOK);
474 shmid = IXSEQ_TO_IPCID(segnum, shmseg->shm_perm);
475 result = vm_mmap(sysvshm_map, &shm_handle->kva, size, VM_PROT_ALL,
476 VM_PROT_DEFAULT, MAP_ANON, (caddr_t) shmid, 0);
477 if (result != KERN_SUCCESS) {
478 shmseg->shm_perm.mode = SHMSEG_FREE;
479 shm_last_free = segnum;
480 free((caddr_t)shm_handle, M_SHM);
481 /* Just in case. */
482 wakeup((caddr_t)shmseg);
483 return ENOMEM;
484 }
485 shmseg->shm_internal = shm_handle;
486 shmseg->shm_perm.cuid = shmseg->shm_perm.uid = cred->cr_uid;
487 shmseg->shm_perm.cgid = shmseg->shm_perm.gid = cred->cr_gid;
488 shmseg->shm_perm.mode = (shmseg->shm_perm.mode & SHMSEG_WANTED) |
489 (mode & ACCESSPERMS) | SHMSEG_ALLOCATED;
490 shmseg->shm_segsz = uap->size;
491 shmseg->shm_cpid = p->p_pid;
492 shmseg->shm_lpid = shmseg->shm_nattch = 0;
493 shmseg->shm_atime = shmseg->shm_dtime = 0;
494 shmseg->shm_ctime = time.tv_sec;
495 shm_committed += btoc(size);
496 shm_nused++;
497 if (shmseg->shm_perm.mode & SHMSEG_WANTED) {
498 /*
499 * Somebody else wanted this key while we were asleep. Wake
500 * them up now.
501 */
502 shmseg->shm_perm.mode &= ~SHMSEG_WANTED;
503 wakeup((caddr_t)shmseg);
504 }
505 *retval = shmid;
506 return 0;
507}
508
509int
510shmget(p, uap, retval)
511 struct proc *p;
512 struct shmget_args *uap;
513 int *retval;
514{
515 int segnum, mode, error;
516
517 mode = uap->shmflg & ACCESSPERMS;
518 if (uap->key != IPC_PRIVATE) {
519 again:
520 segnum = shm_find_segment_by_key(uap->key);
521 if (segnum >= 0) {
522 error = shmget_existing(p, uap, mode, segnum, retval);
523 if (error == EAGAIN)
524 goto again;
525 return error;
526 }
527 if ((uap->shmflg & IPC_CREAT) == 0)
528 return ENOENT;
529 }
530 return shmget_allocate_segment(p, uap, mode, retval);
531}
532
533int
534shmsys(p, uap, retval)
535 struct proc *p;
536 /* XXX actually varargs. */
537 struct shmsys_args /* {
538 u_int which;
539 int a2;
540 int a3;
541 int a4;
542 } */ *uap;
543 int *retval;
544{
545
546 if (uap->which >= sizeof(shmcalls)/sizeof(shmcalls[0]))
547 return EINVAL;
548 return ((*shmcalls[uap->which])(p, &uap->a2, retval));
549}
550
551void
552shmfork(p1, p2, isvfork)
553 struct proc *p1, *p2;
554 int isvfork;
555{
556 struct shmmap_state *shmmap_s;
557 size_t size;
558 int i;
559
560 size = shminfo.shmseg * sizeof(struct shmmap_state);
561 shmmap_s = malloc(size, M_SHM, M_WAITOK);
562 bcopy((caddr_t)p1->p_vmspace->vm_shm, (caddr_t)shmmap_s, size);
563 p2->p_vmspace->vm_shm = (caddr_t)shmmap_s;
564 for (i = 0; i < shminfo.shmseg; i++, shmmap_s++)
565 if (shmmap_s->shmid != -1)
566 shmsegs[IPCID_TO_IX(shmmap_s->shmid)].shm_nattch++;
567}
568
569void
570shmexit(p)
571 struct proc *p;
572{
573 struct shmmap_state *shmmap_s;
574 int i;
575
576 shmmap_s = (struct shmmap_state *)p->p_vmspace->vm_shm;
577 for (i = 0; i < shminfo.shmseg; i++, shmmap_s++)
578 if (shmmap_s->shmid != -1)
579 shm_delete_mapping(p, shmmap_s);
580 free((caddr_t)p->p_vmspace->vm_shm, M_SHM);
581 p->p_vmspace->vm_shm = NULL;
582}
583
584void
585shminit(dummy)
586 void *dummy;
587{
588 int i;
589 vm_offset_t garbage1, garbage2;
590
591 /* actually this *should* be pageable. SHM_{LOCK,UNLOCK} */
592 sysvshm_map = kmem_suballoc(kernel_map, &garbage1, &garbage2,
593 shminfo.shmall * NBPG, TRUE);
594 for (i = 0; i < shminfo.shmmni; i++) {
595 shmsegs[i].shm_perm.mode = SHMSEG_FREE;
596 shmsegs[i].shm_perm.seq = 0;
597 }
598 shm_last_free = 0;
599 shm_nused = 0;
600 shm_committed = 0;
601}