]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/kern/sysv_shm.c
Merge ACPICA 20141107 and 20150204.
[FreeBSD/FreeBSD.git] / sys / kern / sysv_shm.c
1 /*      $NetBSD: sysv_shm.c,v 1.23 1994/07/04 23:25:12 glass Exp $      */
2 /*-
3  * Copyright (c) 1994 Adam Glass and Charles Hannum.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by Adam Glass and Charles
16  *      Hannum.
17  * 4. The names of the authors may not be used to endorse or promote products
18  *    derived from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
21  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23  * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 /*-
32  * Copyright (c) 2003-2005 McAfee, Inc.
33  * All rights reserved.
34  *
35  * This software was developed for the FreeBSD Project in part by McAfee
36  * Research, the Security Research Division of McAfee, Inc under DARPA/SPAWAR
37  * contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS research
38  * program.
39  *
40  * Redistribution and use in source and binary forms, with or without
41  * modification, are permitted provided that the following conditions
42  * are met:
43  * 1. Redistributions of source code must retain the above copyright
44  *    notice, this list of conditions and the following disclaimer.
45  * 2. Redistributions in binary form must reproduce the above copyright
46  *    notice, this list of conditions and the following disclaimer in the
47  *    documentation and/or other materials provided with the distribution.
48  *
49  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
50  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
51  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
52  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
53  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
54  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
55  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
56  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
57  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
58  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
59  * SUCH DAMAGE.
60  */
61
62 #include <sys/cdefs.h>
63 __FBSDID("$FreeBSD$");
64
65 #include "opt_compat.h"
66 #include "opt_sysvipc.h"
67
68 #include <sys/param.h>
69 #include <sys/systm.h>
70 #include <sys/kernel.h>
71 #include <sys/limits.h>
72 #include <sys/lock.h>
73 #include <sys/sysctl.h>
74 #include <sys/shm.h>
75 #include <sys/proc.h>
76 #include <sys/malloc.h>
77 #include <sys/mman.h>
78 #include <sys/module.h>
79 #include <sys/mutex.h>
80 #include <sys/racct.h>
81 #include <sys/resourcevar.h>
82 #include <sys/rwlock.h>
83 #include <sys/stat.h>
84 #include <sys/syscall.h>
85 #include <sys/syscallsubr.h>
86 #include <sys/sysent.h>
87 #include <sys/sysproto.h>
88 #include <sys/jail.h>
89
90 #include <security/mac/mac_framework.h>
91
92 #include <vm/vm.h>
93 #include <vm/vm_param.h>
94 #include <vm/pmap.h>
95 #include <vm/vm_object.h>
96 #include <vm/vm_map.h>
97 #include <vm/vm_page.h>
98 #include <vm/vm_pager.h>
99
100 FEATURE(sysv_shm, "System V shared memory segments support");
101
102 static MALLOC_DEFINE(M_SHM, "shm", "SVID compatible shared memory segments");
103
104 static int shmget_allocate_segment(struct thread *td,
105     struct shmget_args *uap, int mode);
106 static int shmget_existing(struct thread *td, struct shmget_args *uap,
107     int mode, int segnum);
108
109 #define SHMSEG_FREE             0x0200
110 #define SHMSEG_REMOVED          0x0400
111 #define SHMSEG_ALLOCATED        0x0800
112 #define SHMSEG_WANTED           0x1000
113
114 static int shm_last_free, shm_nused, shmalloced;
115 vm_size_t shm_committed;
116 static struct shmid_kernel      *shmsegs;
117
118 struct shmmap_state {
119         vm_offset_t va;
120         int shmid;
121 };
122
123 static void shm_deallocate_segment(struct shmid_kernel *);
124 static int shm_find_segment_by_key(key_t);
125 static struct shmid_kernel *shm_find_segment_by_shmid(int);
126 static struct shmid_kernel *shm_find_segment_by_shmidx(int);
127 static int shm_delete_mapping(struct vmspace *vm, struct shmmap_state *);
128 static void shmrealloc(void);
129 static int shminit(void);
130 static int sysvshm_modload(struct module *, int, void *);
131 static int shmunload(void);
132 static void shmexit_myhook(struct vmspace *vm);
133 static void shmfork_myhook(struct proc *p1, struct proc *p2);
134 static int sysctl_shmsegs(SYSCTL_HANDLER_ARGS);
135
136 /*
137  * Tuneable values.
138  */
139 #ifndef SHMMAXPGS
140 #define SHMMAXPGS       131072  /* Note: sysv shared memory is swap backed. */
141 #endif
142 #ifndef SHMMAX
143 #define SHMMAX  (SHMMAXPGS*PAGE_SIZE)
144 #endif
145 #ifndef SHMMIN
146 #define SHMMIN  1
147 #endif
148 #ifndef SHMMNI
149 #define SHMMNI  192
150 #endif
151 #ifndef SHMSEG
152 #define SHMSEG  128
153 #endif
154 #ifndef SHMALL
155 #define SHMALL  (SHMMAXPGS)
156 #endif
157
158 struct  shminfo shminfo = {
159         .shmmax = SHMMAX,
160         .shmmin = SHMMIN,
161         .shmmni = SHMMNI,
162         .shmseg = SHMSEG,
163         .shmall = SHMALL
164 };
165
166 static int shm_use_phys;
167 static int shm_allow_removed;
168
169 SYSCTL_ULONG(_kern_ipc, OID_AUTO, shmmax, CTLFLAG_RWTUN, &shminfo.shmmax, 0,
170     "Maximum shared memory segment size");
171 SYSCTL_ULONG(_kern_ipc, OID_AUTO, shmmin, CTLFLAG_RWTUN, &shminfo.shmmin, 0,
172     "Minimum shared memory segment size");
173 SYSCTL_ULONG(_kern_ipc, OID_AUTO, shmmni, CTLFLAG_RDTUN, &shminfo.shmmni, 0,
174     "Number of shared memory identifiers");
175 SYSCTL_ULONG(_kern_ipc, OID_AUTO, shmseg, CTLFLAG_RDTUN, &shminfo.shmseg, 0,
176     "Number of segments per process");
177 SYSCTL_ULONG(_kern_ipc, OID_AUTO, shmall, CTLFLAG_RWTUN, &shminfo.shmall, 0,
178     "Maximum number of pages available for shared memory");
179 SYSCTL_INT(_kern_ipc, OID_AUTO, shm_use_phys, CTLFLAG_RWTUN,
180     &shm_use_phys, 0, "Enable/Disable locking of shared memory pages in core");
181 SYSCTL_INT(_kern_ipc, OID_AUTO, shm_allow_removed, CTLFLAG_RWTUN,
182     &shm_allow_removed, 0,
183     "Enable/Disable attachment to attached segments marked for removal");
184 SYSCTL_PROC(_kern_ipc, OID_AUTO, shmsegs, CTLTYPE_OPAQUE | CTLFLAG_RD,
185     NULL, 0, sysctl_shmsegs, "",
186     "Current number of shared memory segments allocated");
187
188 static int
189 shm_find_segment_by_key(key)
190         key_t key;
191 {
192         int i;
193
194         for (i = 0; i < shmalloced; i++)
195                 if ((shmsegs[i].u.shm_perm.mode & SHMSEG_ALLOCATED) &&
196                     shmsegs[i].u.shm_perm.key == key)
197                         return (i);
198         return (-1);
199 }
200
201 static struct shmid_kernel *
202 shm_find_segment_by_shmid(int shmid)
203 {
204         int segnum;
205         struct shmid_kernel *shmseg;
206
207         segnum = IPCID_TO_IX(shmid);
208         if (segnum < 0 || segnum >= shmalloced)
209                 return (NULL);
210         shmseg = &shmsegs[segnum];
211         if ((shmseg->u.shm_perm.mode & SHMSEG_ALLOCATED) == 0 ||
212             (!shm_allow_removed &&
213              (shmseg->u.shm_perm.mode & SHMSEG_REMOVED) != 0) ||
214             shmseg->u.shm_perm.seq != IPCID_TO_SEQ(shmid))
215                 return (NULL);
216         return (shmseg);
217 }
218
219 static struct shmid_kernel *
220 shm_find_segment_by_shmidx(int segnum)
221 {
222         struct shmid_kernel *shmseg;
223
224         if (segnum < 0 || segnum >= shmalloced)
225                 return (NULL);
226         shmseg = &shmsegs[segnum];
227         if ((shmseg->u.shm_perm.mode & SHMSEG_ALLOCATED) == 0 ||
228             (!shm_allow_removed &&
229              (shmseg->u.shm_perm.mode & SHMSEG_REMOVED) != 0))
230                 return (NULL);
231         return (shmseg);
232 }
233
234 static void
235 shm_deallocate_segment(shmseg)
236         struct shmid_kernel *shmseg;
237 {
238         vm_size_t size;
239
240         GIANT_REQUIRED;
241
242         vm_object_deallocate(shmseg->object);
243         shmseg->object = NULL;
244         size = round_page(shmseg->u.shm_segsz);
245         shm_committed -= btoc(size);
246         shm_nused--;
247         shmseg->u.shm_perm.mode = SHMSEG_FREE;
248 #ifdef MAC
249         mac_sysvshm_cleanup(shmseg);
250 #endif
251         racct_sub_cred(shmseg->cred, RACCT_NSHM, 1);
252         racct_sub_cred(shmseg->cred, RACCT_SHMSIZE, size);
253         crfree(shmseg->cred);
254         shmseg->cred = NULL;
255 }
256
257 static int
258 shm_delete_mapping(struct vmspace *vm, struct shmmap_state *shmmap_s)
259 {
260         struct shmid_kernel *shmseg;
261         int segnum, result;
262         vm_size_t size;
263
264         GIANT_REQUIRED;
265
266         segnum = IPCID_TO_IX(shmmap_s->shmid);
267         shmseg = &shmsegs[segnum];
268         size = round_page(shmseg->u.shm_segsz);
269         result = vm_map_remove(&vm->vm_map, shmmap_s->va, shmmap_s->va + size);
270         if (result != KERN_SUCCESS)
271                 return (EINVAL);
272         shmmap_s->shmid = -1;
273         shmseg->u.shm_dtime = time_second;
274         if ((--shmseg->u.shm_nattch <= 0) &&
275             (shmseg->u.shm_perm.mode & SHMSEG_REMOVED)) {
276                 shm_deallocate_segment(shmseg);
277                 shm_last_free = segnum;
278         }
279         return (0);
280 }
281
282 #ifndef _SYS_SYSPROTO_H_
283 struct shmdt_args {
284         const void *shmaddr;
285 };
286 #endif
287 int
288 sys_shmdt(td, uap)
289         struct thread *td;
290         struct shmdt_args *uap;
291 {
292         struct proc *p = td->td_proc;
293         struct shmmap_state *shmmap_s;
294 #ifdef MAC
295         struct shmid_kernel *shmsegptr;
296 #endif
297         int i;
298         int error = 0;
299
300         if (!prison_allow(td->td_ucred, PR_ALLOW_SYSVIPC))
301                 return (ENOSYS);
302         mtx_lock(&Giant);
303         shmmap_s = p->p_vmspace->vm_shm;
304         if (shmmap_s == NULL) {
305                 error = EINVAL;
306                 goto done2;
307         }
308         for (i = 0; i < shminfo.shmseg; i++, shmmap_s++) {
309                 if (shmmap_s->shmid != -1 &&
310                     shmmap_s->va == (vm_offset_t)uap->shmaddr) {
311                         break;
312                 }
313         }
314         if (i == shminfo.shmseg) {
315                 error = EINVAL;
316                 goto done2;
317         }
318 #ifdef MAC
319         shmsegptr = &shmsegs[IPCID_TO_IX(shmmap_s->shmid)];
320         error = mac_sysvshm_check_shmdt(td->td_ucred, shmsegptr);
321         if (error != 0)
322                 goto done2;
323 #endif
324         error = shm_delete_mapping(p->p_vmspace, shmmap_s);
325 done2:
326         mtx_unlock(&Giant);
327         return (error);
328 }
329
330 #ifndef _SYS_SYSPROTO_H_
331 struct shmat_args {
332         int shmid;
333         const void *shmaddr;
334         int shmflg;
335 };
336 #endif
337 int
338 kern_shmat(td, shmid, shmaddr, shmflg)
339         struct thread *td;
340         int shmid;
341         const void *shmaddr;
342         int shmflg;
343 {
344         struct proc *p = td->td_proc;
345         int i;
346         struct shmid_kernel *shmseg;
347         struct shmmap_state *shmmap_s = NULL;
348         vm_offset_t attach_va;
349         vm_prot_t prot;
350         vm_size_t size;
351         int rv;
352         int error = 0;
353
354         if (!prison_allow(td->td_ucred, PR_ALLOW_SYSVIPC))
355                 return (ENOSYS);
356         mtx_lock(&Giant);
357         shmmap_s = p->p_vmspace->vm_shm;
358         if (shmmap_s == NULL) {
359                 shmmap_s = malloc(shminfo.shmseg * sizeof(struct shmmap_state),
360                     M_SHM, M_WAITOK);
361
362                 /*
363                  * If malloc() above sleeps, the Giant lock is
364                  * temporarily dropped, which allows another thread to
365                  * allocate shmmap_state and set vm_shm.  Recheck
366                  * vm_shm and free the new shmmap_state if another one
367                  * is already allocated.
368                  */
369                 if (p->p_vmspace->vm_shm != NULL) {
370                         free(shmmap_s, M_SHM);
371                         shmmap_s = p->p_vmspace->vm_shm;
372                 } else {
373                         for (i = 0; i < shminfo.shmseg; i++)
374                                 shmmap_s[i].shmid = -1;
375                         p->p_vmspace->vm_shm = shmmap_s;
376                 }
377         }
378         shmseg = shm_find_segment_by_shmid(shmid);
379         if (shmseg == NULL) {
380                 error = EINVAL;
381                 goto done2;
382         }
383         error = ipcperm(td, &shmseg->u.shm_perm,
384             (shmflg & SHM_RDONLY) ? IPC_R : IPC_R|IPC_W);
385         if (error)
386                 goto done2;
387 #ifdef MAC
388         error = mac_sysvshm_check_shmat(td->td_ucred, shmseg, shmflg);
389         if (error != 0)
390                 goto done2;
391 #endif
392         for (i = 0; i < shminfo.shmseg; i++) {
393                 if (shmmap_s->shmid == -1)
394                         break;
395                 shmmap_s++;
396         }
397         if (i >= shminfo.shmseg) {
398                 error = EMFILE;
399                 goto done2;
400         }
401         size = round_page(shmseg->u.shm_segsz);
402         prot = VM_PROT_READ;
403         if ((shmflg & SHM_RDONLY) == 0)
404                 prot |= VM_PROT_WRITE;
405         if (shmaddr) {
406                 if (shmflg & SHM_RND) {
407                         attach_va = (vm_offset_t)shmaddr & ~(SHMLBA-1);
408                 } else if (((vm_offset_t)shmaddr & (SHMLBA-1)) == 0) {
409                         attach_va = (vm_offset_t)shmaddr;
410                 } else {
411                         error = EINVAL;
412                         goto done2;
413                 }
414         } else {
415                 /*
416                  * This is just a hint to vm_map_find() about where to
417                  * put it.
418                  */
419                 PROC_LOCK(p);
420                 attach_va = round_page((vm_offset_t)p->p_vmspace->vm_daddr +
421                     lim_max(p, RLIMIT_DATA));
422                 PROC_UNLOCK(p);
423         }
424
425         vm_object_reference(shmseg->object);
426         rv = vm_map_find(&p->p_vmspace->vm_map, shmseg->object, 0, &attach_va,
427             size, 0, shmaddr != NULL ? VMFS_NO_SPACE : VMFS_OPTIMAL_SPACE,
428             prot, prot, MAP_INHERIT_SHARE | MAP_PREFAULT_PARTIAL);
429         if (rv != KERN_SUCCESS) {
430                 vm_object_deallocate(shmseg->object);
431                 error = ENOMEM;
432                 goto done2;
433         }
434
435         shmmap_s->va = attach_va;
436         shmmap_s->shmid = shmid;
437         shmseg->u.shm_lpid = p->p_pid;
438         shmseg->u.shm_atime = time_second;
439         shmseg->u.shm_nattch++;
440         td->td_retval[0] = attach_va;
441 done2:
442         mtx_unlock(&Giant);
443         return (error);
444 }
445
446 int
447 sys_shmat(td, uap)
448         struct thread *td;
449         struct shmat_args *uap;
450 {
451         return kern_shmat(td, uap->shmid, uap->shmaddr, uap->shmflg);
452 }
453
454 int
455 kern_shmctl(td, shmid, cmd, buf, bufsz)
456         struct thread *td;
457         int shmid;
458         int cmd;
459         void *buf;
460         size_t *bufsz;
461 {
462         int error = 0;
463         struct shmid_kernel *shmseg;
464
465         if (!prison_allow(td->td_ucred, PR_ALLOW_SYSVIPC))
466                 return (ENOSYS);
467
468         mtx_lock(&Giant);
469         switch (cmd) {
470         /*
471          * It is possible that kern_shmctl is being called from the Linux ABI
472          * layer, in which case, we will need to implement IPC_INFO.  It should
473          * be noted that other shmctl calls will be funneled through here for
474          * Linix binaries as well.
475          *
476          * NB: The Linux ABI layer will convert this data to structure(s) more
477          * consistent with the Linux ABI.
478          */
479         case IPC_INFO:
480                 memcpy(buf, &shminfo, sizeof(shminfo));
481                 if (bufsz)
482                         *bufsz = sizeof(shminfo);
483                 td->td_retval[0] = shmalloced;
484                 goto done2;
485         case SHM_INFO: {
486                 struct shm_info shm_info;
487                 shm_info.used_ids = shm_nused;
488                 shm_info.shm_rss = 0;   /*XXX where to get from ? */
489                 shm_info.shm_tot = 0;   /*XXX where to get from ? */
490                 shm_info.shm_swp = 0;   /*XXX where to get from ? */
491                 shm_info.swap_attempts = 0;     /*XXX where to get from ? */
492                 shm_info.swap_successes = 0;    /*XXX where to get from ? */
493                 memcpy(buf, &shm_info, sizeof(shm_info));
494                 if (bufsz)
495                         *bufsz = sizeof(shm_info);
496                 td->td_retval[0] = shmalloced;
497                 goto done2;
498         }
499         }
500         if (cmd == SHM_STAT)
501                 shmseg = shm_find_segment_by_shmidx(shmid);
502         else
503                 shmseg = shm_find_segment_by_shmid(shmid);
504         if (shmseg == NULL) {
505                 error = EINVAL;
506                 goto done2;
507         }
508 #ifdef MAC
509         error = mac_sysvshm_check_shmctl(td->td_ucred, shmseg, cmd);
510         if (error != 0)
511                 goto done2;
512 #endif
513         switch (cmd) {
514         case SHM_STAT:
515         case IPC_STAT:
516                 error = ipcperm(td, &shmseg->u.shm_perm, IPC_R);
517                 if (error)
518                         goto done2;
519                 memcpy(buf, &shmseg->u, sizeof(struct shmid_ds));
520                 if (bufsz)
521                         *bufsz = sizeof(struct shmid_ds);
522                 if (cmd == SHM_STAT)
523                         td->td_retval[0] = IXSEQ_TO_IPCID(shmid, shmseg->u.shm_perm);
524                 break;
525         case IPC_SET: {
526                 struct shmid_ds *shmid;
527
528                 shmid = (struct shmid_ds *)buf;
529                 error = ipcperm(td, &shmseg->u.shm_perm, IPC_M);
530                 if (error)
531                         goto done2;
532                 shmseg->u.shm_perm.uid = shmid->shm_perm.uid;
533                 shmseg->u.shm_perm.gid = shmid->shm_perm.gid;
534                 shmseg->u.shm_perm.mode =
535                     (shmseg->u.shm_perm.mode & ~ACCESSPERMS) |
536                     (shmid->shm_perm.mode & ACCESSPERMS);
537                 shmseg->u.shm_ctime = time_second;
538                 break;
539         }
540         case IPC_RMID:
541                 error = ipcperm(td, &shmseg->u.shm_perm, IPC_M);
542                 if (error)
543                         goto done2;
544                 shmseg->u.shm_perm.key = IPC_PRIVATE;
545                 shmseg->u.shm_perm.mode |= SHMSEG_REMOVED;
546                 if (shmseg->u.shm_nattch <= 0) {
547                         shm_deallocate_segment(shmseg);
548                         shm_last_free = IPCID_TO_IX(shmid);
549                 }
550                 break;
551 #if 0
552         case SHM_LOCK:
553         case SHM_UNLOCK:
554 #endif
555         default:
556                 error = EINVAL;
557                 break;
558         }
559 done2:
560         mtx_unlock(&Giant);
561         return (error);
562 }
563
564 #ifndef _SYS_SYSPROTO_H_
565 struct shmctl_args {
566         int shmid;
567         int cmd;
568         struct shmid_ds *buf;
569 };
570 #endif
571 int
572 sys_shmctl(td, uap)
573         struct thread *td;
574         struct shmctl_args *uap;
575 {
576         int error = 0;
577         struct shmid_ds buf;
578         size_t bufsz;
579         
580         /*
581          * The only reason IPC_INFO, SHM_INFO, SHM_STAT exists is to support
582          * Linux binaries.  If we see the call come through the FreeBSD ABI,
583          * return an error back to the user since we do not to support this.
584          */
585         if (uap->cmd == IPC_INFO || uap->cmd == SHM_INFO ||
586             uap->cmd == SHM_STAT)
587                 return (EINVAL);
588
589         /* IPC_SET needs to copyin the buffer before calling kern_shmctl */
590         if (uap->cmd == IPC_SET) {
591                 if ((error = copyin(uap->buf, &buf, sizeof(struct shmid_ds))))
592                         goto done;
593         }
594         
595         error = kern_shmctl(td, uap->shmid, uap->cmd, (void *)&buf, &bufsz);
596         if (error)
597                 goto done;
598         
599         /* Cases in which we need to copyout */
600         switch (uap->cmd) {
601         case IPC_STAT:
602                 error = copyout(&buf, uap->buf, bufsz);
603                 break;
604         }
605
606 done:
607         if (error) {
608                 /* Invalidate the return value */
609                 td->td_retval[0] = -1;
610         }
611         return (error);
612 }
613
614
615 static int
616 shmget_existing(td, uap, mode, segnum)
617         struct thread *td;
618         struct shmget_args *uap;
619         int mode;
620         int segnum;
621 {
622         struct shmid_kernel *shmseg;
623         int error;
624
625         shmseg = &shmsegs[segnum];
626         if (shmseg->u.shm_perm.mode & SHMSEG_REMOVED) {
627                 /*
628                  * This segment is in the process of being allocated.  Wait
629                  * until it's done, and look the key up again (in case the
630                  * allocation failed or it was freed).
631                  */
632                 shmseg->u.shm_perm.mode |= SHMSEG_WANTED;
633                 error = tsleep(shmseg, PLOCK | PCATCH, "shmget", 0);
634                 if (error)
635                         return (error);
636                 return (EAGAIN);
637         }
638         if ((uap->shmflg & (IPC_CREAT | IPC_EXCL)) == (IPC_CREAT | IPC_EXCL))
639                 return (EEXIST);
640 #ifdef MAC
641         error = mac_sysvshm_check_shmget(td->td_ucred, shmseg, uap->shmflg);
642         if (error != 0)
643                 return (error);
644 #endif
645         if (uap->size != 0 && uap->size > shmseg->u.shm_segsz)
646                 return (EINVAL);
647         td->td_retval[0] = IXSEQ_TO_IPCID(segnum, shmseg->u.shm_perm);
648         return (0);
649 }
650
651 static int
652 shmget_allocate_segment(td, uap, mode)
653         struct thread *td;
654         struct shmget_args *uap;
655         int mode;
656 {
657         int i, segnum, shmid;
658         size_t size;
659         struct ucred *cred = td->td_ucred;
660         struct shmid_kernel *shmseg;
661         vm_object_t shm_object;
662
663         GIANT_REQUIRED;
664
665         if (uap->size < shminfo.shmmin || uap->size > shminfo.shmmax)
666                 return (EINVAL);
667         if (shm_nused >= shminfo.shmmni) /* Any shmids left? */
668                 return (ENOSPC);
669         size = round_page(uap->size);
670         if (shm_committed + btoc(size) > shminfo.shmall)
671                 return (ENOMEM);
672         if (shm_last_free < 0) {
673                 shmrealloc();   /* Maybe expand the shmsegs[] array. */
674                 for (i = 0; i < shmalloced; i++)
675                         if (shmsegs[i].u.shm_perm.mode & SHMSEG_FREE)
676                                 break;
677                 if (i == shmalloced)
678                         return (ENOSPC);
679                 segnum = i;
680         } else  {
681                 segnum = shm_last_free;
682                 shm_last_free = -1;
683         }
684         shmseg = &shmsegs[segnum];
685 #ifdef RACCT
686         PROC_LOCK(td->td_proc);
687         if (racct_add(td->td_proc, RACCT_NSHM, 1)) {
688                 PROC_UNLOCK(td->td_proc);
689                 return (ENOSPC);
690         }
691         if (racct_add(td->td_proc, RACCT_SHMSIZE, size)) {
692                 racct_sub(td->td_proc, RACCT_NSHM, 1);
693                 PROC_UNLOCK(td->td_proc);
694                 return (ENOMEM);
695         }
696         PROC_UNLOCK(td->td_proc);
697 #endif
698         /*
699          * In case we sleep in malloc(), mark the segment present but deleted
700          * so that noone else tries to create the same key.
701          */
702         shmseg->u.shm_perm.mode = SHMSEG_ALLOCATED | SHMSEG_REMOVED;
703         shmseg->u.shm_perm.key = uap->key;
704         shmseg->u.shm_perm.seq = (shmseg->u.shm_perm.seq + 1) & 0x7fff;
705         shmid = IXSEQ_TO_IPCID(segnum, shmseg->u.shm_perm);
706         
707         /*
708          * We make sure that we have allocated a pager before we need
709          * to.
710          */
711         shm_object = vm_pager_allocate(shm_use_phys ? OBJT_PHYS : OBJT_SWAP,
712             0, size, VM_PROT_DEFAULT, 0, cred);
713         if (shm_object == NULL) {
714 #ifdef RACCT
715                 PROC_LOCK(td->td_proc);
716                 racct_sub(td->td_proc, RACCT_NSHM, 1);
717                 racct_sub(td->td_proc, RACCT_SHMSIZE, size);
718                 PROC_UNLOCK(td->td_proc);
719 #endif
720                 return (ENOMEM);
721         }
722         shm_object->pg_color = 0;
723         VM_OBJECT_WLOCK(shm_object);
724         vm_object_clear_flag(shm_object, OBJ_ONEMAPPING);
725         vm_object_set_flag(shm_object, OBJ_COLORED | OBJ_NOSPLIT);
726         VM_OBJECT_WUNLOCK(shm_object);
727
728         shmseg->object = shm_object;
729         shmseg->u.shm_perm.cuid = shmseg->u.shm_perm.uid = cred->cr_uid;
730         shmseg->u.shm_perm.cgid = shmseg->u.shm_perm.gid = cred->cr_gid;
731         shmseg->u.shm_perm.mode = (shmseg->u.shm_perm.mode & SHMSEG_WANTED) |
732             (mode & ACCESSPERMS) | SHMSEG_ALLOCATED;
733         shmseg->cred = crhold(cred);
734         shmseg->u.shm_segsz = uap->size;
735         shmseg->u.shm_cpid = td->td_proc->p_pid;
736         shmseg->u.shm_lpid = shmseg->u.shm_nattch = 0;
737         shmseg->u.shm_atime = shmseg->u.shm_dtime = 0;
738 #ifdef MAC
739         mac_sysvshm_create(cred, shmseg);
740 #endif
741         shmseg->u.shm_ctime = time_second;
742         shm_committed += btoc(size);
743         shm_nused++;
744         if (shmseg->u.shm_perm.mode & SHMSEG_WANTED) {
745                 /*
746                  * Somebody else wanted this key while we were asleep.  Wake
747                  * them up now.
748                  */
749                 shmseg->u.shm_perm.mode &= ~SHMSEG_WANTED;
750                 wakeup(shmseg);
751         }
752         td->td_retval[0] = shmid;
753         return (0);
754 }
755
756 #ifndef _SYS_SYSPROTO_H_
757 struct shmget_args {
758         key_t key;
759         size_t size;
760         int shmflg;
761 };
762 #endif
763 int
764 sys_shmget(td, uap)
765         struct thread *td;
766         struct shmget_args *uap;
767 {
768         int segnum, mode;
769         int error;
770
771         if (!prison_allow(td->td_ucred, PR_ALLOW_SYSVIPC))
772                 return (ENOSYS);
773         mtx_lock(&Giant);
774         mode = uap->shmflg & ACCESSPERMS;
775         if (uap->key != IPC_PRIVATE) {
776         again:
777                 segnum = shm_find_segment_by_key(uap->key);
778                 if (segnum >= 0) {
779                         error = shmget_existing(td, uap, mode, segnum);
780                         if (error == EAGAIN)
781                                 goto again;
782                         goto done2;
783                 }
784                 if ((uap->shmflg & IPC_CREAT) == 0) {
785                         error = ENOENT;
786                         goto done2;
787                 }
788         }
789         error = shmget_allocate_segment(td, uap, mode);
790 done2:
791         mtx_unlock(&Giant);
792         return (error);
793 }
794
795 static void
796 shmfork_myhook(p1, p2)
797         struct proc *p1, *p2;
798 {
799         struct shmmap_state *shmmap_s;
800         size_t size;
801         int i;
802
803         mtx_lock(&Giant);
804         size = shminfo.shmseg * sizeof(struct shmmap_state);
805         shmmap_s = malloc(size, M_SHM, M_WAITOK);
806         bcopy(p1->p_vmspace->vm_shm, shmmap_s, size);
807         p2->p_vmspace->vm_shm = shmmap_s;
808         for (i = 0; i < shminfo.shmseg; i++, shmmap_s++)
809                 if (shmmap_s->shmid != -1)
810                         shmsegs[IPCID_TO_IX(shmmap_s->shmid)].u.shm_nattch++;
811         mtx_unlock(&Giant);
812 }
813
814 static void
815 shmexit_myhook(struct vmspace *vm)
816 {
817         struct shmmap_state *base, *shm;
818         int i;
819
820         if ((base = vm->vm_shm) != NULL) {
821                 vm->vm_shm = NULL;
822                 mtx_lock(&Giant);
823                 for (i = 0, shm = base; i < shminfo.shmseg; i++, shm++) {
824                         if (shm->shmid != -1)
825                                 shm_delete_mapping(vm, shm);
826                 }
827                 mtx_unlock(&Giant);
828                 free(base, M_SHM);
829         }
830 }
831
832 static void
833 shmrealloc(void)
834 {
835         int i;
836         struct shmid_kernel *newsegs;
837
838         if (shmalloced >= shminfo.shmmni)
839                 return;
840
841         newsegs = malloc(shminfo.shmmni * sizeof(*newsegs), M_SHM, M_WAITOK);
842         for (i = 0; i < shmalloced; i++)
843                 bcopy(&shmsegs[i], &newsegs[i], sizeof(newsegs[0]));
844         for (; i < shminfo.shmmni; i++) {
845                 shmsegs[i].u.shm_perm.mode = SHMSEG_FREE;
846                 shmsegs[i].u.shm_perm.seq = 0;
847 #ifdef MAC
848                 mac_sysvshm_init(&shmsegs[i]);
849 #endif
850         }
851         free(shmsegs, M_SHM);
852         shmsegs = newsegs;
853         shmalloced = shminfo.shmmni;
854 }
855
856 static struct syscall_helper_data shm_syscalls[] = {
857         SYSCALL_INIT_HELPER(shmat),
858         SYSCALL_INIT_HELPER(shmctl),
859         SYSCALL_INIT_HELPER(shmdt),
860         SYSCALL_INIT_HELPER(shmget),
861 #if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
862     defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7)
863         SYSCALL_INIT_HELPER_COMPAT(freebsd7_shmctl),
864 #endif
865 #if defined(__i386__) && (defined(COMPAT_FREEBSD4) || defined(COMPAT_43))
866         SYSCALL_INIT_HELPER(shmsys),
867 #endif
868         SYSCALL_INIT_LAST
869 };
870
871 #ifdef COMPAT_FREEBSD32
872 #include <compat/freebsd32/freebsd32.h>
873 #include <compat/freebsd32/freebsd32_ipc.h>
874 #include <compat/freebsd32/freebsd32_proto.h>
875 #include <compat/freebsd32/freebsd32_signal.h>
876 #include <compat/freebsd32/freebsd32_syscall.h>
877 #include <compat/freebsd32/freebsd32_util.h>
878
879 static struct syscall_helper_data shm32_syscalls[] = {
880         SYSCALL32_INIT_HELPER_COMPAT(shmat),
881         SYSCALL32_INIT_HELPER_COMPAT(shmdt),
882         SYSCALL32_INIT_HELPER_COMPAT(shmget),
883         SYSCALL32_INIT_HELPER(freebsd32_shmsys),
884         SYSCALL32_INIT_HELPER(freebsd32_shmctl),
885 #if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
886     defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7)
887         SYSCALL32_INIT_HELPER(freebsd7_freebsd32_shmctl),
888 #endif
889         SYSCALL_INIT_LAST
890 };
891 #endif
892
893 static int
894 shminit()
895 {
896         int i, error;
897
898 #ifndef BURN_BRIDGES
899         if (TUNABLE_ULONG_FETCH("kern.ipc.shmmaxpgs", &shminfo.shmall) != 0)
900                 printf("kern.ipc.shmmaxpgs is now called kern.ipc.shmall!\n");
901 #endif
902         if (shminfo.shmmax == SHMMAX) {
903                 /* Initialize shmmax dealing with possible overflow. */
904                 for (i = PAGE_SIZE; i != 0; i--) {
905                         shminfo.shmmax = shminfo.shmall * i;
906                         if ((shminfo.shmmax / shminfo.shmall) == (u_long)i)
907                                 break;
908                 }
909         }
910         shmalloced = shminfo.shmmni;
911         shmsegs = malloc(shmalloced * sizeof(shmsegs[0]), M_SHM, M_WAITOK);
912         for (i = 0; i < shmalloced; i++) {
913                 shmsegs[i].u.shm_perm.mode = SHMSEG_FREE;
914                 shmsegs[i].u.shm_perm.seq = 0;
915 #ifdef MAC
916                 mac_sysvshm_init(&shmsegs[i]);
917 #endif
918         }
919         shm_last_free = 0;
920         shm_nused = 0;
921         shm_committed = 0;
922         shmexit_hook = &shmexit_myhook;
923         shmfork_hook = &shmfork_myhook;
924
925         error = syscall_helper_register(shm_syscalls, SY_THR_STATIC_KLD);
926         if (error != 0)
927                 return (error);
928 #ifdef COMPAT_FREEBSD32
929         error = syscall32_helper_register(shm32_syscalls, SY_THR_STATIC_KLD);
930         if (error != 0)
931                 return (error);
932 #endif
933         return (0);
934 }
935
936 static int
937 shmunload()
938 {
939         int i;  
940
941         if (shm_nused > 0)
942                 return (EBUSY);
943
944 #ifdef COMPAT_FREEBSD32
945         syscall32_helper_unregister(shm32_syscalls);
946 #endif
947         syscall_helper_unregister(shm_syscalls);
948
949         for (i = 0; i < shmalloced; i++) {
950 #ifdef MAC
951                 mac_sysvshm_destroy(&shmsegs[i]);
952 #endif
953                 /*
954                  * Objects might be still mapped into the processes
955                  * address spaces.  Actual free would happen on the
956                  * last mapping destruction.
957                  */
958                 if (shmsegs[i].u.shm_perm.mode != SHMSEG_FREE)
959                         vm_object_deallocate(shmsegs[i].object);
960         }
961         free(shmsegs, M_SHM);
962         shmexit_hook = NULL;
963         shmfork_hook = NULL;
964         return (0);
965 }
966
967 static int
968 sysctl_shmsegs(SYSCTL_HANDLER_ARGS)
969 {
970
971         return (SYSCTL_OUT(req, shmsegs, shmalloced * sizeof(shmsegs[0])));
972 }
973
974 #if defined(__i386__) && (defined(COMPAT_FREEBSD4) || defined(COMPAT_43))
975 struct oshmid_ds {
976         struct  ipc_perm_old shm_perm;  /* operation perms */
977         int     shm_segsz;              /* size of segment (bytes) */
978         u_short shm_cpid;               /* pid, creator */
979         u_short shm_lpid;               /* pid, last operation */
980         short   shm_nattch;             /* no. of current attaches */
981         time_t  shm_atime;              /* last attach time */
982         time_t  shm_dtime;              /* last detach time */
983         time_t  shm_ctime;              /* last change time */
984         void    *shm_handle;            /* internal handle for shm segment */
985 };
986
987 struct oshmctl_args {
988         int shmid;
989         int cmd;
990         struct oshmid_ds *ubuf;
991 };
992
993 static int
994 oshmctl(struct thread *td, struct oshmctl_args *uap)
995 {
996 #ifdef COMPAT_43
997         int error = 0;
998         struct shmid_kernel *shmseg;
999         struct oshmid_ds outbuf;
1000
1001         if (!prison_allow(td->td_ucred, PR_ALLOW_SYSVIPC))
1002                 return (ENOSYS);
1003         mtx_lock(&Giant);
1004         shmseg = shm_find_segment_by_shmid(uap->shmid);
1005         if (shmseg == NULL) {
1006                 error = EINVAL;
1007                 goto done2;
1008         }
1009         switch (uap->cmd) {
1010         case IPC_STAT:
1011                 error = ipcperm(td, &shmseg->u.shm_perm, IPC_R);
1012                 if (error)
1013                         goto done2;
1014 #ifdef MAC
1015                 error = mac_sysvshm_check_shmctl(td->td_ucred, shmseg, uap->cmd);
1016                 if (error != 0)
1017                         goto done2;
1018 #endif
1019                 ipcperm_new2old(&shmseg->u.shm_perm, &outbuf.shm_perm);
1020                 outbuf.shm_segsz = shmseg->u.shm_segsz;
1021                 outbuf.shm_cpid = shmseg->u.shm_cpid;
1022                 outbuf.shm_lpid = shmseg->u.shm_lpid;
1023                 outbuf.shm_nattch = shmseg->u.shm_nattch;
1024                 outbuf.shm_atime = shmseg->u.shm_atime;
1025                 outbuf.shm_dtime = shmseg->u.shm_dtime;
1026                 outbuf.shm_ctime = shmseg->u.shm_ctime;
1027                 outbuf.shm_handle = shmseg->object;
1028                 error = copyout(&outbuf, uap->ubuf, sizeof(outbuf));
1029                 if (error)
1030                         goto done2;
1031                 break;
1032         default:
1033                 error = freebsd7_shmctl(td, (struct freebsd7_shmctl_args *)uap);
1034                 break;
1035         }
1036 done2:
1037         mtx_unlock(&Giant);
1038         return (error);
1039 #else
1040         return (EINVAL);
1041 #endif
1042 }
1043
1044 /* XXX casting to (sy_call_t *) is bogus, as usual. */
1045 static sy_call_t *shmcalls[] = {
1046         (sy_call_t *)sys_shmat, (sy_call_t *)oshmctl,
1047         (sy_call_t *)sys_shmdt, (sy_call_t *)sys_shmget,
1048         (sy_call_t *)freebsd7_shmctl
1049 };
1050
1051 int
1052 sys_shmsys(td, uap)
1053         struct thread *td;
1054         /* XXX actually varargs. */
1055         struct shmsys_args /* {
1056                 int     which;
1057                 int     a2;
1058                 int     a3;
1059                 int     a4;
1060         } */ *uap;
1061 {
1062         int error;
1063
1064         if (!prison_allow(td->td_ucred, PR_ALLOW_SYSVIPC))
1065                 return (ENOSYS);
1066         if (uap->which < 0 ||
1067             uap->which >= sizeof(shmcalls)/sizeof(shmcalls[0]))
1068                 return (EINVAL);
1069         mtx_lock(&Giant);
1070         error = (*shmcalls[uap->which])(td, &uap->a2);
1071         mtx_unlock(&Giant);
1072         return (error);
1073 }
1074
1075 #endif  /* i386 && (COMPAT_FREEBSD4 || COMPAT_43) */
1076
1077 #ifdef COMPAT_FREEBSD32
1078
1079 int
1080 freebsd32_shmsys(struct thread *td, struct freebsd32_shmsys_args *uap)
1081 {
1082
1083 #if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
1084     defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7)
1085         switch (uap->which) {
1086         case 0: {       /* shmat */
1087                 struct shmat_args ap;
1088
1089                 ap.shmid = uap->a2;
1090                 ap.shmaddr = PTRIN(uap->a3);
1091                 ap.shmflg = uap->a4;
1092                 return (sysent[SYS_shmat].sy_call(td, &ap));
1093         }
1094         case 2: {       /* shmdt */
1095                 struct shmdt_args ap;
1096
1097                 ap.shmaddr = PTRIN(uap->a2);
1098                 return (sysent[SYS_shmdt].sy_call(td, &ap));
1099         }
1100         case 3: {       /* shmget */
1101                 struct shmget_args ap;
1102
1103                 ap.key = uap->a2;
1104                 ap.size = uap->a3;
1105                 ap.shmflg = uap->a4;
1106                 return (sysent[SYS_shmget].sy_call(td, &ap));
1107         }
1108         case 4: {       /* shmctl */
1109                 struct freebsd7_freebsd32_shmctl_args ap;
1110
1111                 ap.shmid = uap->a2;
1112                 ap.cmd = uap->a3;
1113                 ap.buf = PTRIN(uap->a4);
1114                 return (freebsd7_freebsd32_shmctl(td, &ap));
1115         }
1116         case 1:         /* oshmctl */
1117         default:
1118                 return (EINVAL);
1119         }
1120 #else
1121         return (nosys(td, NULL));
1122 #endif
1123 }
1124
1125 #if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
1126     defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7)
1127 int
1128 freebsd7_freebsd32_shmctl(struct thread *td,
1129     struct freebsd7_freebsd32_shmctl_args *uap)
1130 {
1131         int error = 0;
1132         union {
1133                 struct shmid_ds shmid_ds;
1134                 struct shm_info shm_info;
1135                 struct shminfo shminfo;
1136         } u;
1137         union {
1138                 struct shmid_ds32_old shmid_ds32;
1139                 struct shm_info32 shm_info32;
1140                 struct shminfo32 shminfo32;
1141         } u32;
1142         size_t sz;
1143
1144         if (uap->cmd == IPC_SET) {
1145                 if ((error = copyin(uap->buf, &u32.shmid_ds32,
1146                     sizeof(u32.shmid_ds32))))
1147                         goto done;
1148                 freebsd32_ipcperm_old_in(&u32.shmid_ds32.shm_perm,
1149                     &u.shmid_ds.shm_perm);
1150                 CP(u32.shmid_ds32, u.shmid_ds, shm_segsz);
1151                 CP(u32.shmid_ds32, u.shmid_ds, shm_lpid);
1152                 CP(u32.shmid_ds32, u.shmid_ds, shm_cpid);
1153                 CP(u32.shmid_ds32, u.shmid_ds, shm_nattch);
1154                 CP(u32.shmid_ds32, u.shmid_ds, shm_atime);
1155                 CP(u32.shmid_ds32, u.shmid_ds, shm_dtime);
1156                 CP(u32.shmid_ds32, u.shmid_ds, shm_ctime);
1157         }
1158         
1159         error = kern_shmctl(td, uap->shmid, uap->cmd, (void *)&u, &sz);
1160         if (error)
1161                 goto done;
1162         
1163         /* Cases in which we need to copyout */
1164         switch (uap->cmd) {
1165         case IPC_INFO:
1166                 CP(u.shminfo, u32.shminfo32, shmmax);
1167                 CP(u.shminfo, u32.shminfo32, shmmin);
1168                 CP(u.shminfo, u32.shminfo32, shmmni);
1169                 CP(u.shminfo, u32.shminfo32, shmseg);
1170                 CP(u.shminfo, u32.shminfo32, shmall);
1171                 error = copyout(&u32.shminfo32, uap->buf,
1172                     sizeof(u32.shminfo32));
1173                 break;
1174         case SHM_INFO:
1175                 CP(u.shm_info, u32.shm_info32, used_ids);
1176                 CP(u.shm_info, u32.shm_info32, shm_rss);
1177                 CP(u.shm_info, u32.shm_info32, shm_tot);
1178                 CP(u.shm_info, u32.shm_info32, shm_swp);
1179                 CP(u.shm_info, u32.shm_info32, swap_attempts);
1180                 CP(u.shm_info, u32.shm_info32, swap_successes);
1181                 error = copyout(&u32.shm_info32, uap->buf,
1182                     sizeof(u32.shm_info32));
1183                 break;
1184         case SHM_STAT:
1185         case IPC_STAT:
1186                 freebsd32_ipcperm_old_out(&u.shmid_ds.shm_perm,
1187                     &u32.shmid_ds32.shm_perm);
1188                 if (u.shmid_ds.shm_segsz > INT32_MAX)
1189                         u32.shmid_ds32.shm_segsz = INT32_MAX;
1190                 else
1191                         CP(u.shmid_ds, u32.shmid_ds32, shm_segsz);
1192                 CP(u.shmid_ds, u32.shmid_ds32, shm_lpid);
1193                 CP(u.shmid_ds, u32.shmid_ds32, shm_cpid);
1194                 CP(u.shmid_ds, u32.shmid_ds32, shm_nattch);
1195                 CP(u.shmid_ds, u32.shmid_ds32, shm_atime);
1196                 CP(u.shmid_ds, u32.shmid_ds32, shm_dtime);
1197                 CP(u.shmid_ds, u32.shmid_ds32, shm_ctime);
1198                 u32.shmid_ds32.shm_internal = 0;
1199                 error = copyout(&u32.shmid_ds32, uap->buf,
1200                     sizeof(u32.shmid_ds32));
1201                 break;
1202         }
1203
1204 done:
1205         if (error) {
1206                 /* Invalidate the return value */
1207                 td->td_retval[0] = -1;
1208         }
1209         return (error);
1210 }
1211 #endif
1212
1213 int
1214 freebsd32_shmctl(struct thread *td, struct freebsd32_shmctl_args *uap)
1215 {
1216         int error = 0;
1217         union {
1218                 struct shmid_ds shmid_ds;
1219                 struct shm_info shm_info;
1220                 struct shminfo shminfo;
1221         } u;
1222         union {
1223                 struct shmid_ds32 shmid_ds32;
1224                 struct shm_info32 shm_info32;
1225                 struct shminfo32 shminfo32;
1226         } u32;
1227         size_t sz;
1228         
1229         if (uap->cmd == IPC_SET) {
1230                 if ((error = copyin(uap->buf, &u32.shmid_ds32,
1231                     sizeof(u32.shmid_ds32))))
1232                         goto done;
1233                 freebsd32_ipcperm_in(&u32.shmid_ds32.shm_perm,
1234                     &u.shmid_ds.shm_perm);
1235                 CP(u32.shmid_ds32, u.shmid_ds, shm_segsz);
1236                 CP(u32.shmid_ds32, u.shmid_ds, shm_lpid);
1237                 CP(u32.shmid_ds32, u.shmid_ds, shm_cpid);
1238                 CP(u32.shmid_ds32, u.shmid_ds, shm_nattch);
1239                 CP(u32.shmid_ds32, u.shmid_ds, shm_atime);
1240                 CP(u32.shmid_ds32, u.shmid_ds, shm_dtime);
1241                 CP(u32.shmid_ds32, u.shmid_ds, shm_ctime);
1242         }
1243         
1244         error = kern_shmctl(td, uap->shmid, uap->cmd, (void *)&u, &sz);
1245         if (error)
1246                 goto done;
1247         
1248         /* Cases in which we need to copyout */
1249         switch (uap->cmd) {
1250         case IPC_INFO:
1251                 CP(u.shminfo, u32.shminfo32, shmmax);
1252                 CP(u.shminfo, u32.shminfo32, shmmin);
1253                 CP(u.shminfo, u32.shminfo32, shmmni);
1254                 CP(u.shminfo, u32.shminfo32, shmseg);
1255                 CP(u.shminfo, u32.shminfo32, shmall);
1256                 error = copyout(&u32.shminfo32, uap->buf,
1257                     sizeof(u32.shminfo32));
1258                 break;
1259         case SHM_INFO:
1260                 CP(u.shm_info, u32.shm_info32, used_ids);
1261                 CP(u.shm_info, u32.shm_info32, shm_rss);
1262                 CP(u.shm_info, u32.shm_info32, shm_tot);
1263                 CP(u.shm_info, u32.shm_info32, shm_swp);
1264                 CP(u.shm_info, u32.shm_info32, swap_attempts);
1265                 CP(u.shm_info, u32.shm_info32, swap_successes);
1266                 error = copyout(&u32.shm_info32, uap->buf,
1267                     sizeof(u32.shm_info32));
1268                 break;
1269         case SHM_STAT:
1270         case IPC_STAT:
1271                 freebsd32_ipcperm_out(&u.shmid_ds.shm_perm,
1272                     &u32.shmid_ds32.shm_perm);
1273                 if (u.shmid_ds.shm_segsz > INT32_MAX)
1274                         u32.shmid_ds32.shm_segsz = INT32_MAX;
1275                 else
1276                         CP(u.shmid_ds, u32.shmid_ds32, shm_segsz);
1277                 CP(u.shmid_ds, u32.shmid_ds32, shm_lpid);
1278                 CP(u.shmid_ds, u32.shmid_ds32, shm_cpid);
1279                 CP(u.shmid_ds, u32.shmid_ds32, shm_nattch);
1280                 CP(u.shmid_ds, u32.shmid_ds32, shm_atime);
1281                 CP(u.shmid_ds, u32.shmid_ds32, shm_dtime);
1282                 CP(u.shmid_ds, u32.shmid_ds32, shm_ctime);
1283                 error = copyout(&u32.shmid_ds32, uap->buf,
1284                     sizeof(u32.shmid_ds32));
1285                 break;
1286         }
1287
1288 done:
1289         if (error) {
1290                 /* Invalidate the return value */
1291                 td->td_retval[0] = -1;
1292         }
1293         return (error);
1294 }
1295 #endif
1296
1297 #if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
1298     defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7)
1299
1300 #ifndef CP
1301 #define CP(src, dst, fld)       do { (dst).fld = (src).fld; } while (0)
1302 #endif
1303
1304 #ifndef _SYS_SYSPROTO_H_
1305 struct freebsd7_shmctl_args {
1306         int shmid;
1307         int cmd;
1308         struct shmid_ds_old *buf;
1309 };
1310 #endif
1311 int
1312 freebsd7_shmctl(td, uap)
1313         struct thread *td;
1314         struct freebsd7_shmctl_args *uap;
1315 {
1316         int error = 0;
1317         struct shmid_ds_old old;
1318         struct shmid_ds buf;
1319         size_t bufsz;
1320         
1321         /*
1322          * The only reason IPC_INFO, SHM_INFO, SHM_STAT exists is to support
1323          * Linux binaries.  If we see the call come through the FreeBSD ABI,
1324          * return an error back to the user since we do not to support this.
1325          */
1326         if (uap->cmd == IPC_INFO || uap->cmd == SHM_INFO ||
1327             uap->cmd == SHM_STAT)
1328                 return (EINVAL);
1329
1330         /* IPC_SET needs to copyin the buffer before calling kern_shmctl */
1331         if (uap->cmd == IPC_SET) {
1332                 if ((error = copyin(uap->buf, &old, sizeof(old))))
1333                         goto done;
1334                 ipcperm_old2new(&old.shm_perm, &buf.shm_perm);
1335                 CP(old, buf, shm_segsz);
1336                 CP(old, buf, shm_lpid);
1337                 CP(old, buf, shm_cpid);
1338                 CP(old, buf, shm_nattch);
1339                 CP(old, buf, shm_atime);
1340                 CP(old, buf, shm_dtime);
1341                 CP(old, buf, shm_ctime);
1342         }
1343         
1344         error = kern_shmctl(td, uap->shmid, uap->cmd, (void *)&buf, &bufsz);
1345         if (error)
1346                 goto done;
1347
1348         /* Cases in which we need to copyout */
1349         switch (uap->cmd) {
1350         case IPC_STAT:
1351                 ipcperm_new2old(&buf.shm_perm, &old.shm_perm);
1352                 if (buf.shm_segsz > INT_MAX)
1353                         old.shm_segsz = INT_MAX;
1354                 else
1355                         CP(buf, old, shm_segsz);
1356                 CP(buf, old, shm_lpid);
1357                 CP(buf, old, shm_cpid);
1358                 if (buf.shm_nattch > SHRT_MAX)
1359                         old.shm_nattch = SHRT_MAX;
1360                 else
1361                         CP(buf, old, shm_nattch);
1362                 CP(buf, old, shm_atime);
1363                 CP(buf, old, shm_dtime);
1364                 CP(buf, old, shm_ctime);
1365                 old.shm_internal = NULL;
1366                 error = copyout(&old, uap->buf, sizeof(old));
1367                 break;
1368         }
1369
1370 done:
1371         if (error) {
1372                 /* Invalidate the return value */
1373                 td->td_retval[0] = -1;
1374         }
1375         return (error);
1376 }
1377
1378 #endif  /* COMPAT_FREEBSD4 || COMPAT_FREEBSD5 || COMPAT_FREEBSD6 ||
1379            COMPAT_FREEBSD7 */
1380
1381 static int
1382 sysvshm_modload(struct module *module, int cmd, void *arg)
1383 {
1384         int error = 0;
1385
1386         switch (cmd) {
1387         case MOD_LOAD:
1388                 error = shminit();
1389                 if (error != 0)
1390                         shmunload();
1391                 break;
1392         case MOD_UNLOAD:
1393                 error = shmunload();
1394                 break;
1395         case MOD_SHUTDOWN:
1396                 break;
1397         default:
1398                 error = EINVAL;
1399                 break;
1400         }
1401         return (error);
1402 }
1403
1404 static moduledata_t sysvshm_mod = {
1405         "sysvshm",
1406         &sysvshm_modload,
1407         NULL
1408 };
1409
1410 DECLARE_MODULE(sysvshm, sysvshm_mod, SI_SUB_SYSV_SHM, SI_ORDER_FIRST);
1411 MODULE_VERSION(sysvshm, 1);