The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/kern/sysv_shm.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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 #include <sys/cdefs.h>
   33 __FBSDID("$FreeBSD: releng/5.4/sys/kern/sysv_shm.c 145335 2005-04-20 19:11:07Z cvs2svn $");
   34 
   35 #include "opt_compat.h"
   36 #include "opt_sysvipc.h"
   37 
   38 #include <sys/param.h>
   39 #include <sys/systm.h>
   40 #include <sys/kernel.h>
   41 #include <sys/lock.h>
   42 #include <sys/sysctl.h>
   43 #include <sys/shm.h>
   44 #include <sys/proc.h>
   45 #include <sys/malloc.h>
   46 #include <sys/mman.h>
   47 #include <sys/module.h>
   48 #include <sys/mutex.h>
   49 #include <sys/resourcevar.h>
   50 #include <sys/stat.h>
   51 #include <sys/syscall.h>
   52 #include <sys/syscallsubr.h>
   53 #include <sys/sysent.h>
   54 #include <sys/sysproto.h>
   55 #include <sys/jail.h>
   56 
   57 #include <vm/vm.h>
   58 #include <vm/vm_param.h>
   59 #include <vm/pmap.h>
   60 #include <vm/vm_object.h>
   61 #include <vm/vm_map.h>
   62 #include <vm/vm_page.h>
   63 #include <vm/vm_pager.h>
   64 
   65 static MALLOC_DEFINE(M_SHM, "shm", "SVID compatible shared memory segments");
   66 
   67 struct oshmctl_args;
   68 static int oshmctl(struct thread *td, struct oshmctl_args *uap);
   69 
   70 static int shmget_allocate_segment(struct thread *td,
   71     struct shmget_args *uap, int mode);
   72 static int shmget_existing(struct thread *td, struct shmget_args *uap,
   73     int mode, int segnum);
   74 
   75 /* XXX casting to (sy_call_t *) is bogus, as usual. */
   76 static sy_call_t *shmcalls[] = {
   77         (sy_call_t *)shmat, (sy_call_t *)oshmctl,
   78         (sy_call_t *)shmdt, (sy_call_t *)shmget,
   79         (sy_call_t *)shmctl
   80 };
   81 
   82 #define SHMSEG_FREE             0x0200
   83 #define SHMSEG_REMOVED          0x0400
   84 #define SHMSEG_ALLOCATED        0x0800
   85 #define SHMSEG_WANTED           0x1000
   86 
   87 static int shm_last_free, shm_nused, shm_committed, shmalloced;
   88 static struct shmid_ds  *shmsegs;
   89 
   90 struct shmmap_state {
   91         vm_offset_t va;
   92         int shmid;
   93 };
   94 
   95 static void shm_deallocate_segment(struct shmid_ds *);
   96 static int shm_find_segment_by_key(key_t);
   97 static struct shmid_ds *shm_find_segment_by_shmid(int);
   98 static struct shmid_ds *shm_find_segment_by_shmidx(int);
   99 static int shm_delete_mapping(struct vmspace *vm, struct shmmap_state *);
  100 static void shmrealloc(void);
  101 static void shminit(void);
  102 static int sysvshm_modload(struct module *, int, void *);
  103 static int shmunload(void);
  104 static void shmexit_myhook(struct vmspace *vm);
  105 static void shmfork_myhook(struct proc *p1, struct proc *p2);
  106 static int sysctl_shmsegs(SYSCTL_HANDLER_ARGS);
  107 
  108 /*
  109  * Tuneable values.
  110  */
  111 #ifndef SHMMAXPGS
  112 #define SHMMAXPGS       8192    /* Note: sysv shared memory is swap backed. */
  113 #endif
  114 #ifndef SHMMAX
  115 #define SHMMAX  (SHMMAXPGS*PAGE_SIZE)
  116 #endif
  117 #ifndef SHMMIN
  118 #define SHMMIN  1
  119 #endif
  120 #ifndef SHMMNI
  121 #define SHMMNI  192
  122 #endif
  123 #ifndef SHMSEG
  124 #define SHMSEG  128
  125 #endif
  126 #ifndef SHMALL
  127 #define SHMALL  (SHMMAXPGS)
  128 #endif
  129 
  130 struct  shminfo shminfo = {
  131         SHMMAX,
  132         SHMMIN,
  133         SHMMNI,
  134         SHMSEG,
  135         SHMALL
  136 };
  137 
  138 static int shm_use_phys;
  139 static int shm_allow_removed;
  140 
  141 SYSCTL_DECL(_kern_ipc);
  142 SYSCTL_INT(_kern_ipc, OID_AUTO, shmmax, CTLFLAG_RW, &shminfo.shmmax, 0,
  143     "Maximum shared memory segment size");
  144 SYSCTL_INT(_kern_ipc, OID_AUTO, shmmin, CTLFLAG_RW, &shminfo.shmmin, 0,
  145     "Minimum shared memory segment size");
  146 SYSCTL_INT(_kern_ipc, OID_AUTO, shmmni, CTLFLAG_RDTUN, &shminfo.shmmni, 0,
  147     "Number of shared memory identifiers");
  148 SYSCTL_INT(_kern_ipc, OID_AUTO, shmseg, CTLFLAG_RDTUN, &shminfo.shmseg, 0,
  149     "Number of segments per process");
  150 SYSCTL_INT(_kern_ipc, OID_AUTO, shmall, CTLFLAG_RW, &shminfo.shmall, 0,
  151     "Maximum number of pages available for shared memory");
  152 SYSCTL_INT(_kern_ipc, OID_AUTO, shm_use_phys, CTLFLAG_RW,
  153     &shm_use_phys, 0, "Enable/Disable locking of shared memory pages in core");
  154 SYSCTL_INT(_kern_ipc, OID_AUTO, shm_allow_removed, CTLFLAG_RW,
  155     &shm_allow_removed, 0,
  156     "Enable/Disable attachment to attached segments marked for removal");
  157 SYSCTL_PROC(_kern_ipc, OID_AUTO, shmsegs, CTLFLAG_RD,
  158     NULL, 0, sysctl_shmsegs, "",
  159     "Current number of shared memory segments allocated");
  160 
  161 static int
  162 shm_find_segment_by_key(key)
  163         key_t key;
  164 {
  165         int i;
  166 
  167         for (i = 0; i < shmalloced; i++)
  168                 if ((shmsegs[i].shm_perm.mode & SHMSEG_ALLOCATED) &&
  169                     shmsegs[i].shm_perm.key == key)
  170                         return (i);
  171         return (-1);
  172 }
  173 
  174 static struct shmid_ds *
  175 shm_find_segment_by_shmid(int shmid)
  176 {
  177         int segnum;
  178         struct shmid_ds *shmseg;
  179 
  180         segnum = IPCID_TO_IX(shmid);
  181         if (segnum < 0 || segnum >= shmalloced)
  182                 return (NULL);
  183         shmseg = &shmsegs[segnum];
  184         if ((shmseg->shm_perm.mode & SHMSEG_ALLOCATED) == 0 ||
  185             (!shm_allow_removed &&
  186              (shmseg->shm_perm.mode & SHMSEG_REMOVED) != 0) ||
  187             shmseg->shm_perm.seq != IPCID_TO_SEQ(shmid))
  188                 return (NULL);
  189         return (shmseg);
  190 }
  191 
  192 static struct shmid_ds *
  193 shm_find_segment_by_shmidx(int segnum)
  194 {
  195         struct shmid_ds *shmseg;
  196 
  197         if (segnum < 0 || segnum >= shmalloced)
  198                 return (NULL);
  199         shmseg = &shmsegs[segnum];
  200         if ((shmseg->shm_perm.mode & SHMSEG_ALLOCATED) == 0 ||
  201             (!shm_allow_removed &&
  202              (shmseg->shm_perm.mode & SHMSEG_REMOVED) != 0))
  203                 return (NULL);
  204         return (shmseg);
  205 }
  206 
  207 static void
  208 shm_deallocate_segment(shmseg)
  209         struct shmid_ds *shmseg;
  210 {
  211         size_t size;
  212 
  213         GIANT_REQUIRED;
  214 
  215         vm_object_deallocate(shmseg->shm_internal);
  216         shmseg->shm_internal = NULL;
  217         size = round_page(shmseg->shm_segsz);
  218         shm_committed -= btoc(size);
  219         shm_nused--;
  220         shmseg->shm_perm.mode = SHMSEG_FREE;
  221 }
  222 
  223 static int
  224 shm_delete_mapping(struct vmspace *vm, struct shmmap_state *shmmap_s)
  225 {
  226         struct shmid_ds *shmseg;
  227         int segnum, result;
  228         size_t size;
  229 
  230         GIANT_REQUIRED;
  231 
  232         segnum = IPCID_TO_IX(shmmap_s->shmid);
  233         shmseg = &shmsegs[segnum];
  234         size = round_page(shmseg->shm_segsz);
  235         result = vm_map_remove(&vm->vm_map, shmmap_s->va, shmmap_s->va + size);
  236         if (result != KERN_SUCCESS)
  237                 return (EINVAL);
  238         shmmap_s->shmid = -1;
  239         shmseg->shm_dtime = time_second;
  240         if ((--shmseg->shm_nattch <= 0) &&
  241             (shmseg->shm_perm.mode & SHMSEG_REMOVED)) {
  242                 shm_deallocate_segment(shmseg);
  243                 shm_last_free = segnum;
  244         }
  245         return (0);
  246 }
  247 
  248 #ifndef _SYS_SYSPROTO_H_
  249 struct shmdt_args {
  250         const void *shmaddr;
  251 };
  252 #endif
  253 
  254 /*
  255  * MPSAFE
  256  */
  257 int
  258 shmdt(td, uap)
  259         struct thread *td;
  260         struct shmdt_args *uap;
  261 {
  262         struct proc *p = td->td_proc;
  263         struct shmmap_state *shmmap_s;
  264         int i;
  265         int error = 0;
  266 
  267         if (!jail_sysvipc_allowed && jailed(td->td_ucred))
  268                 return (ENOSYS);
  269         mtx_lock(&Giant);
  270         shmmap_s = p->p_vmspace->vm_shm;
  271         if (shmmap_s == NULL) {
  272                 error = EINVAL;
  273                 goto done2;
  274         }
  275         for (i = 0; i < shminfo.shmseg; i++, shmmap_s++) {
  276                 if (shmmap_s->shmid != -1 &&
  277                     shmmap_s->va == (vm_offset_t)uap->shmaddr) {
  278                         break;
  279                 }
  280         }
  281         if (i == shminfo.shmseg) {
  282                 error = EINVAL;
  283                 goto done2;
  284         }
  285         error = shm_delete_mapping(p->p_vmspace, shmmap_s);
  286 done2:
  287         mtx_unlock(&Giant);
  288         return (error);
  289 }
  290 
  291 #ifndef _SYS_SYSPROTO_H_
  292 struct shmat_args {
  293         int shmid;
  294         const void *shmaddr;
  295         int shmflg;
  296 };
  297 #endif
  298 
  299 /*
  300  * MPSAFE
  301  */
  302 int
  303 kern_shmat(td, shmid, shmaddr, shmflg)
  304         struct thread *td;
  305         int shmid;
  306         const void *shmaddr;
  307         int shmflg;
  308 {
  309         struct proc *p = td->td_proc;
  310         int i, flags;
  311         struct shmid_ds *shmseg;
  312         struct shmmap_state *shmmap_s = NULL;
  313         vm_offset_t attach_va;
  314         vm_prot_t prot;
  315         vm_size_t size;
  316         int rv;
  317         int error = 0;
  318 
  319         if (!jail_sysvipc_allowed && jailed(td->td_ucred))
  320                 return (ENOSYS);
  321         mtx_lock(&Giant);
  322         shmmap_s = p->p_vmspace->vm_shm;
  323         if (shmmap_s == NULL) {
  324                 size = shminfo.shmseg * sizeof(struct shmmap_state);
  325                 shmmap_s = malloc(size, M_SHM, M_WAITOK);
  326                 for (i = 0; i < shminfo.shmseg; i++)
  327                         shmmap_s[i].shmid = -1;
  328                 p->p_vmspace->vm_shm = shmmap_s;
  329         }
  330         shmseg = shm_find_segment_by_shmid(shmid);
  331         if (shmseg == NULL) {
  332                 error = EINVAL;
  333                 goto done2;
  334         }
  335         error = ipcperm(td, &shmseg->shm_perm,
  336             (shmflg & SHM_RDONLY) ? IPC_R : IPC_R|IPC_W);
  337         if (error)
  338                 goto done2;
  339         for (i = 0; i < shminfo.shmseg; i++) {
  340                 if (shmmap_s->shmid == -1)
  341                         break;
  342                 shmmap_s++;
  343         }
  344         if (i >= shminfo.shmseg) {
  345                 error = EMFILE;
  346                 goto done2;
  347         }
  348         size = round_page(shmseg->shm_segsz);
  349 #ifdef VM_PROT_READ_IS_EXEC
  350         prot = VM_PROT_READ | VM_PROT_EXECUTE;
  351 #else
  352         prot = VM_PROT_READ;
  353 #endif
  354         if ((shmflg & SHM_RDONLY) == 0)
  355                 prot |= VM_PROT_WRITE;
  356         flags = MAP_ANON | MAP_SHARED;
  357         if (shmaddr) {
  358                 flags |= MAP_FIXED;
  359                 if (shmflg & SHM_RND) {
  360                         attach_va = (vm_offset_t)shmaddr & ~(SHMLBA-1);
  361                 } else if (((vm_offset_t)shmaddr & (SHMLBA-1)) == 0) {
  362                         attach_va = (vm_offset_t)shmaddr;
  363                 } else {
  364                         error = EINVAL;
  365                         goto done2;
  366                 }
  367         } else {
  368                 /*
  369                  * This is just a hint to vm_map_find() about where to
  370                  * put it.
  371                  */
  372                 PROC_LOCK(p);
  373                 attach_va = round_page((vm_offset_t)p->p_vmspace->vm_daddr +
  374                     lim_max(p, RLIMIT_DATA));
  375                 PROC_UNLOCK(p);
  376         }
  377 
  378         vm_object_reference(shmseg->shm_internal);
  379         rv = vm_map_find(&p->p_vmspace->vm_map, shmseg->shm_internal,
  380                 0, &attach_va, size, (flags & MAP_FIXED)?0:1, prot, prot, 0);
  381         if (rv != KERN_SUCCESS) {
  382                 vm_object_deallocate(shmseg->shm_internal);
  383                 error = ENOMEM;
  384                 goto done2;
  385         }
  386         vm_map_inherit(&p->p_vmspace->vm_map,
  387                 attach_va, attach_va + size, VM_INHERIT_SHARE);
  388 
  389         shmmap_s->va = attach_va;
  390         shmmap_s->shmid = shmid;
  391         shmseg->shm_lpid = p->p_pid;
  392         shmseg->shm_atime = time_second;
  393         shmseg->shm_nattch++;
  394         td->td_retval[0] = attach_va;
  395 done2:
  396         mtx_unlock(&Giant);
  397         return (error);
  398 }
  399 
  400 int
  401 shmat(td, uap)
  402         struct thread *td;
  403         struct shmat_args *uap;
  404 {
  405         return kern_shmat(td, uap->shmid, uap->shmaddr, uap->shmflg);
  406 }
  407 
  408 struct oshmid_ds {
  409         struct  ipc_perm shm_perm;      /* operation perms */
  410         int     shm_segsz;              /* size of segment (bytes) */
  411         u_short shm_cpid;               /* pid, creator */
  412         u_short shm_lpid;               /* pid, last operation */
  413         short   shm_nattch;             /* no. of current attaches */
  414         time_t  shm_atime;              /* last attach time */
  415         time_t  shm_dtime;              /* last detach time */
  416         time_t  shm_ctime;              /* last change time */
  417         void    *shm_handle;            /* internal handle for shm segment */
  418 };
  419 
  420 struct oshmctl_args {
  421         int shmid;
  422         int cmd;
  423         struct oshmid_ds *ubuf;
  424 };
  425 
  426 /*
  427  * MPSAFE
  428  */
  429 static int
  430 oshmctl(td, uap)
  431         struct thread *td;
  432         struct oshmctl_args *uap;
  433 {
  434 #ifdef COMPAT_43
  435         int error = 0;
  436         struct shmid_ds *shmseg;
  437         struct oshmid_ds outbuf;
  438 
  439         if (!jail_sysvipc_allowed && jailed(td->td_ucred))
  440                 return (ENOSYS);
  441         mtx_lock(&Giant);
  442         shmseg = shm_find_segment_by_shmid(uap->shmid);
  443         if (shmseg == NULL) {
  444                 error = EINVAL;
  445                 goto done2;
  446         }
  447         switch (uap->cmd) {
  448         case IPC_STAT:
  449                 error = ipcperm(td, &shmseg->shm_perm, IPC_R);
  450                 if (error)
  451                         goto done2;
  452                 outbuf.shm_perm = shmseg->shm_perm;
  453                 outbuf.shm_segsz = shmseg->shm_segsz;
  454                 outbuf.shm_cpid = shmseg->shm_cpid;
  455                 outbuf.shm_lpid = shmseg->shm_lpid;
  456                 outbuf.shm_nattch = shmseg->shm_nattch;
  457                 outbuf.shm_atime = shmseg->shm_atime;
  458                 outbuf.shm_dtime = shmseg->shm_dtime;
  459                 outbuf.shm_ctime = shmseg->shm_ctime;
  460                 outbuf.shm_handle = shmseg->shm_internal;
  461                 error = copyout(&outbuf, uap->ubuf, sizeof(outbuf));
  462                 if (error)
  463                         goto done2;
  464                 break;
  465         default:
  466                 error = shmctl(td, (struct shmctl_args *)uap);
  467                 break;
  468         }
  469 done2:
  470         mtx_unlock(&Giant);
  471         return (error);
  472 #else
  473         return (EINVAL);
  474 #endif
  475 }
  476 
  477 #ifndef _SYS_SYSPROTO_H_
  478 struct shmctl_args {
  479         int shmid;
  480         int cmd;
  481         struct shmid_ds *buf;
  482 };
  483 #endif
  484 
  485 /*
  486  * MPSAFE
  487  */
  488 int
  489 kern_shmctl(td, shmid, cmd, buf, bufsz)
  490         struct thread *td;
  491         int shmid;
  492         int cmd;
  493         void *buf;
  494         size_t *bufsz;
  495 {
  496         int error = 0;
  497         struct shmid_ds *shmseg;
  498 
  499         if (!jail_sysvipc_allowed && jailed(td->td_ucred))
  500                 return (ENOSYS);
  501 
  502         mtx_lock(&Giant);
  503         switch (cmd) {
  504         case IPC_INFO:
  505                 memcpy(buf, &shminfo, sizeof(shminfo));
  506                 if (bufsz)
  507                         *bufsz = sizeof(shminfo);
  508                 td->td_retval[0] = shmalloced;
  509                 goto done2;
  510         case SHM_INFO: {
  511                 struct shm_info shm_info;
  512                 shm_info.used_ids = shm_nused;
  513                 shm_info.shm_rss = 0;   /*XXX where to get from ? */
  514                 shm_info.shm_tot = 0;   /*XXX where to get from ? */
  515                 shm_info.shm_swp = 0;   /*XXX where to get from ? */
  516                 shm_info.swap_attempts = 0;     /*XXX where to get from ? */
  517                 shm_info.swap_successes = 0;    /*XXX where to get from ? */
  518                 memcpy(buf, &shm_info, sizeof(shm_info));
  519                 if (bufsz)
  520                         *bufsz = sizeof(shm_info);
  521                 td->td_retval[0] = shmalloced;
  522                 goto done2;
  523         }
  524         }
  525         if (cmd == SHM_STAT)
  526                 shmseg = shm_find_segment_by_shmidx(shmid);
  527         else
  528                 shmseg = shm_find_segment_by_shmid(shmid);
  529         if (shmseg == NULL) {
  530                 error = EINVAL;
  531                 goto done2;
  532         }
  533         switch (cmd) {
  534         case SHM_STAT:
  535         case IPC_STAT:
  536                 error = ipcperm(td, &shmseg->shm_perm, IPC_R);
  537                 if (error)
  538                         goto done2;
  539                 memcpy(buf, shmseg, sizeof(struct shmid_ds));
  540                 if (bufsz)
  541                         *bufsz = sizeof(struct shmid_ds);
  542                 if (cmd == SHM_STAT)
  543                         td->td_retval[0] = IXSEQ_TO_IPCID(shmid, shmseg->shm_perm);
  544                 break;
  545         case IPC_SET: {
  546                 struct shmid_ds *shmid;
  547 
  548                 shmid = (struct shmid_ds *)buf;
  549                 error = ipcperm(td, &shmseg->shm_perm, IPC_M);
  550                 if (error)
  551                         goto done2;
  552                 shmseg->shm_perm.uid = shmid->shm_perm.uid;
  553                 shmseg->shm_perm.gid = shmid->shm_perm.gid;
  554                 shmseg->shm_perm.mode =
  555                     (shmseg->shm_perm.mode & ~ACCESSPERMS) |
  556                     (shmid->shm_perm.mode & ACCESSPERMS);
  557                 shmseg->shm_ctime = time_second;
  558                 break;
  559         }
  560         case IPC_RMID:
  561                 error = ipcperm(td, &shmseg->shm_perm, IPC_M);
  562                 if (error)
  563                         goto done2;
  564                 shmseg->shm_perm.key = IPC_PRIVATE;
  565                 shmseg->shm_perm.mode |= SHMSEG_REMOVED;
  566                 if (shmseg->shm_nattch <= 0) {
  567                         shm_deallocate_segment(shmseg);
  568                         shm_last_free = IPCID_TO_IX(shmid);
  569                 }
  570                 break;
  571 #if 0
  572         case SHM_LOCK:
  573         case SHM_UNLOCK:
  574 #endif
  575         default:
  576                 error = EINVAL;
  577                 break;
  578         }
  579 done2:
  580         mtx_unlock(&Giant);
  581         return (error);
  582 }
  583 
  584 int
  585 shmctl(td, uap)
  586         struct thread *td;
  587         struct shmctl_args *uap;
  588 {
  589         int error = 0;
  590         struct shmid_ds buf;
  591         size_t bufsz;
  592         
  593         /* IPC_SET needs to copyin the buffer before calling kern_shmctl */
  594         if (uap->cmd == IPC_SET) {
  595                 if ((error = copyin(uap->buf, &buf, sizeof(struct shmid_ds))))
  596                         goto done;
  597         }
  598         
  599         error = kern_shmctl(td, uap->shmid, uap->cmd, (void *)&buf, &bufsz);
  600         if (error)
  601                 goto done;
  602         
  603         /* Cases in which we need to copyout */
  604         switch (uap->cmd) {
  605         case IPC_INFO:
  606         case SHM_INFO:
  607         case SHM_STAT:
  608         case IPC_STAT:
  609                 error = copyout(&buf, uap->buf, bufsz);
  610                 break;
  611         }
  612 
  613 done:
  614         if (error) {
  615                 /* Invalidate the return value */
  616                 td->td_retval[0] = -1;
  617         }
  618         return (error);
  619 }
  620 
  621 
  622 #ifndef _SYS_SYSPROTO_H_
  623 struct shmget_args {
  624         key_t key;
  625         size_t size;
  626         int shmflg;
  627 };
  628 #endif
  629 
  630 static int
  631 shmget_existing(td, uap, mode, segnum)
  632         struct thread *td;
  633         struct shmget_args *uap;
  634         int mode;
  635         int segnum;
  636 {
  637         struct shmid_ds *shmseg;
  638         int error;
  639 
  640         shmseg = &shmsegs[segnum];
  641         if (shmseg->shm_perm.mode & SHMSEG_REMOVED) {
  642                 /*
  643                  * This segment is in the process of being allocated.  Wait
  644                  * until it's done, and look the key up again (in case the
  645                  * allocation failed or it was freed).
  646                  */
  647                 shmseg->shm_perm.mode |= SHMSEG_WANTED;
  648                 error = tsleep(shmseg, PLOCK | PCATCH, "shmget", 0);
  649                 if (error)
  650                         return (error);
  651                 return (EAGAIN);
  652         }
  653         if ((uap->shmflg & (IPC_CREAT | IPC_EXCL)) == (IPC_CREAT | IPC_EXCL))
  654                 return (EEXIST);
  655         error = ipcperm(td, &shmseg->shm_perm, mode);
  656         if (error)
  657                 return (error);
  658         if (uap->size && uap->size > shmseg->shm_segsz)
  659                 return (EINVAL);
  660         td->td_retval[0] = IXSEQ_TO_IPCID(segnum, shmseg->shm_perm);
  661         return (0);
  662 }
  663 
  664 static int
  665 shmget_allocate_segment(td, uap, mode)
  666         struct thread *td;
  667         struct shmget_args *uap;
  668         int mode;
  669 {
  670         int i, segnum, shmid, size;
  671         struct ucred *cred = td->td_ucred;
  672         struct shmid_ds *shmseg;
  673         vm_object_t shm_object;
  674 
  675         GIANT_REQUIRED;
  676 
  677         if (uap->size < shminfo.shmmin || uap->size > shminfo.shmmax)
  678                 return (EINVAL);
  679         if (shm_nused >= shminfo.shmmni) /* Any shmids left? */
  680                 return (ENOSPC);
  681         size = round_page(uap->size);
  682         if (shm_committed + btoc(size) > shminfo.shmall)
  683                 return (ENOMEM);
  684         if (shm_last_free < 0) {
  685                 shmrealloc();   /* Maybe expand the shmsegs[] array. */
  686                 for (i = 0; i < shmalloced; i++)
  687                         if (shmsegs[i].shm_perm.mode & SHMSEG_FREE)
  688                                 break;
  689                 if (i == shmalloced)
  690                         return (ENOSPC);
  691                 segnum = i;
  692         } else  {
  693                 segnum = shm_last_free;
  694                 shm_last_free = -1;
  695         }
  696         shmseg = &shmsegs[segnum];
  697         /*
  698          * In case we sleep in malloc(), mark the segment present but deleted
  699          * so that noone else tries to create the same key.
  700          */
  701         shmseg->shm_perm.mode = SHMSEG_ALLOCATED | SHMSEG_REMOVED;
  702         shmseg->shm_perm.key = uap->key;
  703         shmseg->shm_perm.seq = (shmseg->shm_perm.seq + 1) & 0x7fff;
  704         shmid = IXSEQ_TO_IPCID(segnum, shmseg->shm_perm);
  705         
  706         /*
  707          * We make sure that we have allocated a pager before we need
  708          * to.
  709          */
  710         if (shm_use_phys) {
  711                 shm_object =
  712                     vm_pager_allocate(OBJT_PHYS, 0, size, VM_PROT_DEFAULT, 0);
  713         } else {
  714                 shm_object =
  715                     vm_pager_allocate(OBJT_SWAP, 0, size, VM_PROT_DEFAULT, 0);
  716         }
  717         VM_OBJECT_LOCK(shm_object);
  718         vm_object_clear_flag(shm_object, OBJ_ONEMAPPING);
  719         vm_object_set_flag(shm_object, OBJ_NOSPLIT);
  720         VM_OBJECT_UNLOCK(shm_object);
  721 
  722         shmseg->shm_internal = shm_object;
  723         shmseg->shm_perm.cuid = shmseg->shm_perm.uid = cred->cr_uid;
  724         shmseg->shm_perm.cgid = shmseg->shm_perm.gid = cred->cr_gid;
  725         shmseg->shm_perm.mode = (shmseg->shm_perm.mode & SHMSEG_WANTED) |
  726             (mode & ACCESSPERMS) | SHMSEG_ALLOCATED;
  727         shmseg->shm_segsz = uap->size;
  728         shmseg->shm_cpid = td->td_proc->p_pid;
  729         shmseg->shm_lpid = shmseg->shm_nattch = 0;
  730         shmseg->shm_atime = shmseg->shm_dtime = 0;
  731         shmseg->shm_ctime = time_second;
  732         shm_committed += btoc(size);
  733         shm_nused++;
  734         if (shmseg->shm_perm.mode & SHMSEG_WANTED) {
  735                 /*
  736                  * Somebody else wanted this key while we were asleep.  Wake
  737                  * them up now.
  738                  */
  739                 shmseg->shm_perm.mode &= ~SHMSEG_WANTED;
  740                 wakeup(shmseg);
  741         }
  742         td->td_retval[0] = shmid;
  743         return (0);
  744 }
  745 
  746 /*
  747  * MPSAFE
  748  */
  749 int
  750 shmget(td, uap)
  751         struct thread *td;
  752         struct shmget_args *uap;
  753 {
  754         int segnum, mode;
  755         int error;
  756 
  757         if (!jail_sysvipc_allowed && jailed(td->td_ucred))
  758                 return (ENOSYS);
  759         mtx_lock(&Giant);
  760         mode = uap->shmflg & ACCESSPERMS;
  761         if (uap->key != IPC_PRIVATE) {
  762         again:
  763                 segnum = shm_find_segment_by_key(uap->key);
  764                 if (segnum >= 0) {
  765                         error = shmget_existing(td, uap, mode, segnum);
  766                         if (error == EAGAIN)
  767                                 goto again;
  768                         goto done2;
  769                 }
  770                 if ((uap->shmflg & IPC_CREAT) == 0) {
  771                         error = ENOENT;
  772                         goto done2;
  773                 }
  774         }
  775         error = shmget_allocate_segment(td, uap, mode);
  776 done2:
  777         mtx_unlock(&Giant);
  778         return (error);
  779 }
  780 
  781 /*
  782  * MPSAFE
  783  */
  784 int
  785 shmsys(td, uap)
  786         struct thread *td;
  787         /* XXX actually varargs. */
  788         struct shmsys_args /* {
  789                 int     which;
  790                 int     a2;
  791                 int     a3;
  792                 int     a4;
  793         } */ *uap;
  794 {
  795         int error;
  796 
  797         if (!jail_sysvipc_allowed && jailed(td->td_ucred))
  798                 return (ENOSYS);
  799         if (uap->which < 0 ||
  800             uap->which >= sizeof(shmcalls)/sizeof(shmcalls[0]))
  801                 return (EINVAL);
  802         mtx_lock(&Giant);
  803         error = (*shmcalls[uap->which])(td, &uap->a2);
  804         mtx_unlock(&Giant);
  805         return (error);
  806 }
  807 
  808 static void
  809 shmfork_myhook(p1, p2)
  810         struct proc *p1, *p2;
  811 {
  812         struct shmmap_state *shmmap_s;
  813         size_t size;
  814         int i;
  815 
  816         mtx_lock(&Giant);
  817         size = shminfo.shmseg * sizeof(struct shmmap_state);
  818         shmmap_s = malloc(size, M_SHM, M_WAITOK);
  819         bcopy(p1->p_vmspace->vm_shm, shmmap_s, size);
  820         p2->p_vmspace->vm_shm = shmmap_s;
  821         for (i = 0; i < shminfo.shmseg; i++, shmmap_s++)
  822                 if (shmmap_s->shmid != -1)
  823                         shmsegs[IPCID_TO_IX(shmmap_s->shmid)].shm_nattch++;
  824         mtx_unlock(&Giant);
  825 }
  826 
  827 static void
  828 shmexit_myhook(struct vmspace *vm)
  829 {
  830         struct shmmap_state *base, *shm;
  831         int i;
  832 
  833         if ((base = vm->vm_shm) != NULL) {
  834                 vm->vm_shm = NULL;
  835                 mtx_lock(&Giant);
  836                 for (i = 0, shm = base; i < shminfo.shmseg; i++, shm++) {
  837                         if (shm->shmid != -1)
  838                                 shm_delete_mapping(vm, shm);
  839                 }
  840                 mtx_unlock(&Giant);
  841                 free(base, M_SHM);
  842         }
  843 }
  844 
  845 static void
  846 shmrealloc(void)
  847 {
  848         int i;
  849         struct shmid_ds *newsegs;
  850 
  851         if (shmalloced >= shminfo.shmmni)
  852                 return;
  853 
  854         newsegs = malloc(shminfo.shmmni * sizeof(*newsegs), M_SHM, M_WAITOK);
  855         if (newsegs == NULL)
  856                 return;
  857         for (i = 0; i < shmalloced; i++)
  858                 bcopy(&shmsegs[i], &newsegs[i], sizeof(newsegs[0]));
  859         for (; i < shminfo.shmmni; i++) {
  860                 shmsegs[i].shm_perm.mode = SHMSEG_FREE;
  861                 shmsegs[i].shm_perm.seq = 0;
  862         }
  863         free(shmsegs, M_SHM);
  864         shmsegs = newsegs;
  865         shmalloced = shminfo.shmmni;
  866 }
  867 
  868 static void
  869 shminit()
  870 {
  871         int i;
  872 
  873         TUNABLE_INT_FETCH("kern.ipc.shmmaxpgs", &shminfo.shmall);
  874         for (i = PAGE_SIZE; i > 0; i--) {
  875                 shminfo.shmmax = shminfo.shmall * PAGE_SIZE;
  876                 if (shminfo.shmmax >= shminfo.shmall)
  877                         break;
  878         }
  879         TUNABLE_INT_FETCH("kern.ipc.shmmin", &shminfo.shmmin);
  880         TUNABLE_INT_FETCH("kern.ipc.shmmni", &shminfo.shmmni);
  881         TUNABLE_INT_FETCH("kern.ipc.shmseg", &shminfo.shmseg);
  882         TUNABLE_INT_FETCH("kern.ipc.shm_use_phys", &shm_use_phys);
  883 
  884         shmalloced = shminfo.shmmni;
  885         shmsegs = malloc(shmalloced * sizeof(shmsegs[0]), M_SHM, M_WAITOK);
  886         if (shmsegs == NULL)
  887                 panic("cannot allocate initial memory for sysvshm");
  888         for (i = 0; i < shmalloced; i++) {
  889                 shmsegs[i].shm_perm.mode = SHMSEG_FREE;
  890                 shmsegs[i].shm_perm.seq = 0;
  891         }
  892         shm_last_free = 0;
  893         shm_nused = 0;
  894         shm_committed = 0;
  895         shmexit_hook = &shmexit_myhook;
  896         shmfork_hook = &shmfork_myhook;
  897 }
  898 
  899 static int
  900 shmunload()
  901 {
  902 
  903         if (shm_nused > 0)
  904                 return (EBUSY);
  905 
  906         free(shmsegs, M_SHM);
  907         shmexit_hook = NULL;
  908         shmfork_hook = NULL;
  909         return (0);
  910 }
  911 
  912 static int
  913 sysctl_shmsegs(SYSCTL_HANDLER_ARGS)
  914 {
  915 
  916         return (SYSCTL_OUT(req, shmsegs, shmalloced * sizeof(shmsegs[0])));
  917 }
  918 
  919 static int
  920 sysvshm_modload(struct module *module, int cmd, void *arg)
  921 {
  922         int error = 0;
  923 
  924         switch (cmd) {
  925         case MOD_LOAD:
  926                 shminit();
  927                 break;
  928         case MOD_UNLOAD:
  929                 error = shmunload();
  930                 break;
  931         case MOD_SHUTDOWN:
  932                 break;
  933         default:
  934                 error = EINVAL;
  935                 break;
  936         }
  937         return (error);
  938 }
  939 
  940 static moduledata_t sysvshm_mod = {
  941         "sysvshm",
  942         &sysvshm_modload,
  943         NULL
  944 };
  945 
  946 SYSCALL_MODULE_HELPER(shmsys);
  947 SYSCALL_MODULE_HELPER(shmat);
  948 SYSCALL_MODULE_HELPER(shmctl);
  949 SYSCALL_MODULE_HELPER(shmdt);
  950 SYSCALL_MODULE_HELPER(shmget);
  951 
  952 DECLARE_MODULE(sysvshm, sysvshm_mod,
  953         SI_SUB_SYSV_SHM, SI_ORDER_FIRST);
  954 MODULE_VERSION(sysvshm, 1);

Cache object: cfede77c63a20161d8496a0d8fcc474a


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.