sysv_shm.c revision 1.64 1 /* $NetBSD: sysv_shm.c,v 1.64 2002/04/03 11:53:01 fvdl Exp $ */
2
3 /*-
4 * Copyright (c) 1999 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
9 * NASA Ames Research Center.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the NetBSD
22 * Foundation, Inc. and its contributors.
23 * 4. Neither the name of The NetBSD Foundation nor the names of its
24 * contributors may be used to endorse or promote products derived
25 * from this software without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37 * POSSIBILITY OF SUCH DAMAGE.
38 */
39
40 /*
41 * Copyright (c) 1994 Adam Glass and Charles M. Hannum. All rights reserved.
42 *
43 * Redistribution and use in source and binary forms, with or without
44 * modification, are permitted provided that the following conditions
45 * are met:
46 * 1. Redistributions of source code must retain the above copyright
47 * notice, this list of conditions and the following disclaimer.
48 * 2. Redistributions in binary form must reproduce the above copyright
49 * notice, this list of conditions and the following disclaimer in the
50 * documentation and/or other materials provided with the distribution.
51 * 3. All advertising materials mentioning features or use of this software
52 * must display the following acknowledgement:
53 * This product includes software developed by Adam Glass and Charles M.
54 * Hannum.
55 * 4. The names of the authors may not be used to endorse or promote products
56 * derived from this software without specific prior written permission.
57 *
58 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
59 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
60 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
61 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
62 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
63 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
64 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
65 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
66 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
67 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
68 */
69
70 #include <sys/cdefs.h>
71 __KERNEL_RCSID(0, "$NetBSD: sysv_shm.c,v 1.64 2002/04/03 11:53:01 fvdl Exp $");
72
73 #define SYSVSHM
74
75 #include <sys/param.h>
76 #include <sys/kernel.h>
77 #include <sys/shm.h>
78 #include <sys/malloc.h>
79 #include <sys/mman.h>
80 #include <sys/stat.h>
81 #include <sys/sysctl.h>
82 #include <sys/mount.h> /* XXX for <sys/syscallargs.h> */
83 #include <sys/syscallargs.h>
84
85 #include <uvm/uvm_extern.h>
86
87 struct shmid_ds *shm_find_segment_by_shmid __P((int, int));
88
89 /*
90 * Provides the following externally accessible functions:
91 *
92 * shminit(void); initialization
93 * shmexit(struct vmspace *) cleanup
94 * shmfork(struct vmspace *, struct vmspace *) fork handling
95 *
96 * Structures:
97 * shmsegs (an array of 'struct shmid_ds')
98 * per proc array of 'struct shmmap_state'
99 */
100
101 #define SHMSEG_FREE 0x0200
102 #define SHMSEG_REMOVED 0x0400
103 #define SHMSEG_ALLOCATED 0x0800
104 #define SHMSEG_WANTED 0x1000
105
106 int shm_last_free, shm_nused, shm_committed;
107 struct shmid_ds *shmsegs;
108
109 struct shm_handle {
110 struct uvm_object *shm_object;
111 };
112
113 struct shmmap_state {
114 vaddr_t va;
115 int shmid;
116 };
117
118 static int shm_find_segment_by_key __P((key_t));
119 static void shm_deallocate_segment __P((struct shmid_ds *));
120 static void shm_delete_mapping __P((struct vmspace *, struct shmmap_state *));
121 static int shmget_existing __P((struct proc *, struct sys_shmget_args *,
122 int, int, register_t *));
123 static int shmget_allocate_segment __P((struct proc *, struct sys_shmget_args *,
124 int, register_t *));
125
126 static int
127 shm_find_segment_by_key(key)
128 key_t key;
129 {
130 int i;
131
132 for (i = 0; i < shminfo.shmmni; i++)
133 if ((shmsegs[i].shm_perm.mode & SHMSEG_ALLOCATED) &&
134 shmsegs[i].shm_perm._key == key)
135 return i;
136 return -1;
137 }
138
139 struct shmid_ds *
140 shm_find_segment_by_shmid(shmid, findremoved)
141 int shmid;
142 int findremoved;
143 {
144 int segnum;
145 struct shmid_ds *shmseg;
146
147 segnum = IPCID_TO_IX(shmid);
148 if (segnum < 0 || segnum >= shminfo.shmmni)
149 return NULL;
150 shmseg = &shmsegs[segnum];
151 if ((shmseg->shm_perm.mode & SHMSEG_ALLOCATED) == 0)
152 return NULL;
153 if (!findremoved && ((shmseg->shm_perm.mode & SHMSEG_REMOVED) != 0))
154 return NULL;
155 if (shmseg->shm_perm._seq != IPCID_TO_SEQ(shmid))
156 return NULL;
157 return shmseg;
158 }
159
160 static void
161 shm_deallocate_segment(shmseg)
162 struct shmid_ds *shmseg;
163 {
164 struct shm_handle *shm_handle;
165 size_t size;
166
167 shm_handle = shmseg->_shm_internal;
168 size = (shmseg->shm_segsz + PGOFSET) & ~PGOFSET;
169 uao_detach(shm_handle->shm_object);
170 free((caddr_t)shm_handle, M_SHM);
171 shmseg->_shm_internal = NULL;
172 shm_committed -= btoc(size);
173 shmseg->shm_perm.mode = SHMSEG_FREE;
174 shm_nused--;
175 }
176
177 static void
178 shm_delete_mapping(vm, shmmap_s)
179 struct vmspace *vm;
180 struct shmmap_state *shmmap_s;
181 {
182 struct shmid_ds *shmseg;
183 int segnum;
184 size_t size;
185
186 segnum = IPCID_TO_IX(shmmap_s->shmid);
187 shmseg = &shmsegs[segnum];
188 size = (shmseg->shm_segsz + PGOFSET) & ~PGOFSET;
189 uvm_deallocate(&vm->vm_map, shmmap_s->va, size);
190 shmmap_s->shmid = -1;
191 shmseg->shm_dtime = time.tv_sec;
192 if ((--shmseg->shm_nattch <= 0) &&
193 (shmseg->shm_perm.mode & SHMSEG_REMOVED)) {
194 shm_deallocate_segment(shmseg);
195 shm_last_free = segnum;
196 }
197 }
198
199 int
200 sys_shmdt(p, v, retval)
201 struct proc *p;
202 void *v;
203 register_t *retval;
204 {
205 struct sys_shmdt_args /* {
206 syscallarg(const void *) shmaddr;
207 } */ *uap = v;
208 struct shmmap_state *shmmap_s;
209 int i;
210
211 shmmap_s = (struct shmmap_state *)p->p_vmspace->vm_shm;
212 if (shmmap_s == NULL)
213 return EINVAL;
214
215 for (i = 0; i < shminfo.shmseg; i++, shmmap_s++)
216 if (shmmap_s->shmid != -1 &&
217 shmmap_s->va == (vaddr_t)SCARG(uap, shmaddr))
218 break;
219 if (i == shminfo.shmseg)
220 return EINVAL;
221 shm_delete_mapping(p->p_vmspace, shmmap_s);
222 return 0;
223 }
224
225 int
226 sys_shmat(p, v, retval)
227 struct proc *p;
228 void *v;
229 register_t *retval;
230 {
231 struct sys_shmat_args /* {
232 syscallarg(int) shmid;
233 syscallarg(const void *) shmaddr;
234 syscallarg(int) shmflg;
235 } */ *uap = v;
236 vaddr_t attach_va;
237 int error;
238
239 error = shmat1(p, SCARG(uap, shmid), SCARG(uap, shmaddr),
240 SCARG(uap, shmflg), &attach_va, 0);
241 if (error != 0)
242 return error;
243 retval[0] = attach_va;
244 return 0;
245 }
246
247 int
248 shmat1(p, shmid, shmaddr, shmflg, attachp, findremoved)
249 struct proc *p;
250 int shmid;
251 const void *shmaddr;
252 int shmflg;
253 vaddr_t *attachp;
254 int findremoved;
255 {
256 int error, i, flags;
257 struct ucred *cred = p->p_ucred;
258 struct shmid_ds *shmseg;
259 struct shmmap_state *shmmap_s = NULL;
260 struct shm_handle *shm_handle;
261 vaddr_t attach_va;
262 vm_prot_t prot;
263 vsize_t size;
264
265 shmmap_s = (struct shmmap_state *)p->p_vmspace->vm_shm;
266 if (shmmap_s == NULL) {
267 size = shminfo.shmseg * sizeof(struct shmmap_state);
268 shmmap_s = malloc(size, M_SHM, M_WAITOK);
269 for (i = 0; i < shminfo.shmseg; i++)
270 shmmap_s[i].shmid = -1;
271 p->p_vmspace->vm_shm = (caddr_t)shmmap_s;
272 }
273 shmseg = shm_find_segment_by_shmid(shmid, findremoved);
274 if (shmseg == NULL)
275 return EINVAL;
276 error = ipcperm(cred, &shmseg->shm_perm,
277 (shmflg & SHM_RDONLY) ? IPC_R : IPC_R|IPC_W);
278 if (error)
279 return error;
280 for (i = 0; i < shminfo.shmseg; i++) {
281 if (shmmap_s->shmid == -1)
282 break;
283 shmmap_s++;
284 }
285 if (i >= shminfo.shmseg)
286 return EMFILE;
287 size = (shmseg->shm_segsz + PGOFSET) & ~PGOFSET;
288 prot = VM_PROT_READ;
289 if ((shmflg & SHM_RDONLY) == 0)
290 prot |= VM_PROT_WRITE;
291 flags = MAP_ANON | MAP_SHARED;
292 if (shmaddr) {
293 flags |= MAP_FIXED;
294 if (shmflg & SHM_RND)
295 attach_va =
296 (vaddr_t)shmaddr & ~(SHMLBA-1);
297 else if (((vaddr_t)shmaddr & (SHMLBA-1)) == 0)
298 attach_va = (vaddr_t)shmaddr;
299 else
300 return EINVAL;
301 } else {
302 /* This is just a hint to vm_mmap() about where to put it. */
303 attach_va =
304 round_page((vaddr_t)p->p_vmspace->vm_taddr +
305 MAXTSIZ + MAXDSIZ);
306 }
307 shm_handle = shmseg->_shm_internal;
308 uao_reference(shm_handle->shm_object);
309 error = uvm_map(&p->p_vmspace->vm_map, &attach_va, size,
310 shm_handle->shm_object, 0, 0,
311 UVM_MAPFLAG(prot, prot, UVM_INH_SHARE, UVM_ADV_RANDOM, 0));
312 if (error) {
313 return error;
314 }
315 shmmap_s->va = attach_va;
316 shmmap_s->shmid = shmid;
317 shmseg->shm_lpid = p->p_pid;
318 shmseg->shm_atime = time.tv_sec;
319 shmseg->shm_nattch++;
320 *attachp = attach_va;
321 return 0;
322 }
323
324 int
325 sys___shmctl13(p, v, retval)
326 struct proc *p;
327 void *v;
328 register_t *retval;
329 {
330 struct sys___shmctl13_args /* {
331 syscallarg(int) shmid;
332 syscallarg(int) cmd;
333 syscallarg(struct shmid_ds *) buf;
334 } */ *uap = v;
335 struct shmid_ds shmbuf;
336 int cmd, error;
337
338 cmd = SCARG(uap, cmd);
339
340 if (cmd == IPC_SET) {
341 error = copyin(SCARG(uap, buf), &shmbuf, sizeof(shmbuf));
342 if (error)
343 return (error);
344 }
345
346 error = shmctl1(p, SCARG(uap, shmid), cmd,
347 (cmd == IPC_SET || cmd == IPC_STAT) ? &shmbuf : NULL);
348
349 if (error == 0 && cmd == IPC_STAT)
350 error = copyout(&shmbuf, SCARG(uap, buf), sizeof(shmbuf));
351
352 return (error);
353 }
354
355 int
356 shmctl1(p, shmid, cmd, shmbuf)
357 struct proc *p;
358 int shmid;
359 int cmd;
360 struct shmid_ds *shmbuf;
361 {
362 struct ucred *cred = p->p_ucred;
363 struct shmid_ds *shmseg;
364 int error = 0;
365
366 shmseg = shm_find_segment_by_shmid(shmid, 0);
367 if (shmseg == NULL)
368 return EINVAL;
369 switch (cmd) {
370 case IPC_STAT:
371 if ((error = ipcperm(cred, &shmseg->shm_perm, IPC_R)) != 0)
372 return error;
373 memcpy(shmbuf, shmseg, sizeof(struct shmid_ds));
374 break;
375 case IPC_SET:
376 if ((error = ipcperm(cred, &shmseg->shm_perm, IPC_M)) != 0)
377 return error;
378 shmseg->shm_perm.uid = shmbuf->shm_perm.uid;
379 shmseg->shm_perm.gid = shmbuf->shm_perm.gid;
380 shmseg->shm_perm.mode =
381 (shmseg->shm_perm.mode & ~ACCESSPERMS) |
382 (shmbuf->shm_perm.mode & ACCESSPERMS);
383 shmseg->shm_ctime = time.tv_sec;
384 break;
385 case IPC_RMID:
386 if ((error = ipcperm(cred, &shmseg->shm_perm, IPC_M)) != 0)
387 return error;
388 shmseg->shm_perm._key = IPC_PRIVATE;
389 shmseg->shm_perm.mode |= SHMSEG_REMOVED;
390 if (shmseg->shm_nattch <= 0) {
391 shm_deallocate_segment(shmseg);
392 shm_last_free = IPCID_TO_IX(shmid);
393 }
394 break;
395 case SHM_LOCK:
396 case SHM_UNLOCK:
397 default:
398 return EINVAL;
399 }
400 return 0;
401 }
402
403 static int
404 shmget_existing(p, uap, mode, segnum, retval)
405 struct proc *p;
406 struct sys_shmget_args /* {
407 syscallarg(key_t) key;
408 syscallarg(size_t) size;
409 syscallarg(int) shmflg;
410 } */ *uap;
411 int mode;
412 int segnum;
413 register_t *retval;
414 {
415 struct shmid_ds *shmseg;
416 struct ucred *cred = p->p_ucred;
417 int error;
418
419 shmseg = &shmsegs[segnum];
420 if (shmseg->shm_perm.mode & SHMSEG_REMOVED) {
421 /*
422 * This segment is in the process of being allocated. Wait
423 * until it's done, and look the key up again (in case the
424 * allocation failed or it was freed).
425 */
426 shmseg->shm_perm.mode |= SHMSEG_WANTED;
427 error = tsleep((caddr_t)shmseg, PLOCK | PCATCH, "shmget", 0);
428 if (error)
429 return error;
430 return EAGAIN;
431 }
432 if ((error = ipcperm(cred, &shmseg->shm_perm, mode)) != 0)
433 return error;
434 if (SCARG(uap, size) && SCARG(uap, size) > shmseg->shm_segsz)
435 return EINVAL;
436 if ((SCARG(uap, shmflg) & (IPC_CREAT | IPC_EXCL)) ==
437 (IPC_CREAT | IPC_EXCL))
438 return EEXIST;
439 *retval = IXSEQ_TO_IPCID(segnum, shmseg->shm_perm);
440 return 0;
441 }
442
443 static int
444 shmget_allocate_segment(p, uap, mode, retval)
445 struct proc *p;
446 struct sys_shmget_args /* {
447 syscallarg(key_t) key;
448 syscallarg(size_t) size;
449 syscallarg(int) shmflg;
450 } */ *uap;
451 int mode;
452 register_t *retval;
453 {
454 int i, segnum, shmid, size;
455 struct ucred *cred = p->p_ucred;
456 struct shmid_ds *shmseg;
457 struct shm_handle *shm_handle;
458 int error = 0;
459
460 if (SCARG(uap, size) < shminfo.shmmin ||
461 SCARG(uap, size) > shminfo.shmmax)
462 return EINVAL;
463 if (shm_nused >= shminfo.shmmni) /* any shmids left? */
464 return ENOSPC;
465 size = (SCARG(uap, size) + PGOFSET) & ~PGOFSET;
466 if (shm_committed + btoc(size) > shminfo.shmall)
467 return ENOMEM;
468 if (shm_last_free < 0) {
469 for (i = 0; i < shminfo.shmmni; i++)
470 if (shmsegs[i].shm_perm.mode & SHMSEG_FREE)
471 break;
472 if (i == shminfo.shmmni)
473 panic("shmseg free count inconsistent");
474 segnum = i;
475 } else {
476 segnum = shm_last_free;
477 shm_last_free = -1;
478 }
479 shmseg = &shmsegs[segnum];
480 /*
481 * In case we sleep in malloc(), mark the segment present but deleted
482 * so that noone else tries to create the same key.
483 */
484 shmseg->shm_perm.mode = SHMSEG_ALLOCATED | SHMSEG_REMOVED;
485 shmseg->shm_perm._key = SCARG(uap, key);
486 shmseg->shm_perm._seq = (shmseg->shm_perm._seq + 1) & 0x7fff;
487 shm_handle = (struct shm_handle *)
488 malloc(sizeof(struct shm_handle), M_SHM, M_WAITOK);
489 shmid = IXSEQ_TO_IPCID(segnum, shmseg->shm_perm);
490
491 shm_handle->shm_object = uao_create(size, 0);
492
493 shmseg->_shm_internal = shm_handle;
494 shmseg->shm_perm.cuid = shmseg->shm_perm.uid = cred->cr_uid;
495 shmseg->shm_perm.cgid = shmseg->shm_perm.gid = cred->cr_gid;
496 shmseg->shm_perm.mode = (shmseg->shm_perm.mode & SHMSEG_WANTED) |
497 (mode & ACCESSPERMS) | SHMSEG_ALLOCATED;
498 shmseg->shm_segsz = SCARG(uap, size);
499 shmseg->shm_cpid = p->p_pid;
500 shmseg->shm_lpid = shmseg->shm_nattch = 0;
501 shmseg->shm_atime = shmseg->shm_dtime = 0;
502 shmseg->shm_ctime = time.tv_sec;
503 shm_committed += btoc(size);
504 shm_nused++;
505
506 *retval = shmid;
507 if (shmseg->shm_perm.mode & SHMSEG_WANTED) {
508 /*
509 * Somebody else wanted this key while we were asleep. Wake
510 * them up now.
511 */
512 shmseg->shm_perm.mode &= ~SHMSEG_WANTED;
513 wakeup((caddr_t)shmseg);
514 }
515 return error;
516 }
517
518 int
519 sys_shmget(p, v, retval)
520 struct proc *p;
521 void *v;
522 register_t *retval;
523 {
524 struct sys_shmget_args /* {
525 syscallarg(key_t) key;
526 syscallarg(int) size;
527 syscallarg(int) shmflg;
528 } */ *uap = v;
529 int segnum, mode, error;
530
531 mode = SCARG(uap, shmflg) & ACCESSPERMS;
532 if (SCARG(uap, key) != IPC_PRIVATE) {
533 again:
534 segnum = shm_find_segment_by_key(SCARG(uap, key));
535 if (segnum >= 0) {
536 error = shmget_existing(p, uap, mode, segnum, retval);
537 if (error == EAGAIN)
538 goto again;
539 return error;
540 }
541 if ((SCARG(uap, shmflg) & IPC_CREAT) == 0)
542 return ENOENT;
543 }
544 return shmget_allocate_segment(p, uap, mode, retval);
545 }
546
547 void
548 shmfork(vm1, vm2)
549 struct vmspace *vm1, *vm2;
550 {
551 struct shmmap_state *shmmap_s;
552 size_t size;
553 int i;
554
555 if (vm1->vm_shm == NULL) {
556 vm2->vm_shm = NULL;
557 return;
558 }
559
560 size = shminfo.shmseg * sizeof(struct shmmap_state);
561 shmmap_s = malloc(size, M_SHM, M_WAITOK);
562 memcpy(shmmap_s, vm1->vm_shm, size);
563 vm2->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
569 void
570 shmexit(vm)
571 struct vmspace *vm;
572 {
573 struct shmmap_state *shmmap_s;
574 int i;
575
576 shmmap_s = (struct shmmap_state *)vm->vm_shm;
577 if (shmmap_s == NULL)
578 return;
579 for (i = 0; i < shminfo.shmseg; i++, shmmap_s++)
580 if (shmmap_s->shmid != -1)
581 shm_delete_mapping(vm, shmmap_s);
582 free(vm->vm_shm, M_SHM);
583 vm->vm_shm = NULL;
584 }
585
586 void
587 shminit()
588 {
589 int i;
590
591 shminfo.shmmax *= PAGE_SIZE;
592
593 for (i = 0; i < shminfo.shmmni; i++) {
594 shmsegs[i].shm_perm.mode = SHMSEG_FREE;
595 shmsegs[i].shm_perm._seq = 0;
596 }
597 shm_last_free = 0;
598 shm_nused = 0;
599 shm_committed = 0;
600 }
601