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.93 2006/11/28 20:35:16 ad 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.93 2006/11/28 20:35:16 ad Exp $");
   72 
   73 #define SYSVSHM
   74 
   75 #include <sys/param.h>
   76 #include <sys/kernel.h>
   77 #include <sys/shm.h>
   78 #include <sys/lock.h>
   79 #include <sys/malloc.h>
   80 #include <sys/mman.h>
   81 #include <sys/stat.h>
   82 #include <sys/sysctl.h>
   83 #include <sys/mount.h>          /* XXX for <sys/syscallargs.h> */
   84 #include <sys/sa.h>
   85 #include <sys/syscallargs.h>
   86 #include <sys/queue.h>
   87 #include <sys/pool.h>
   88 #include <sys/kauth.h>
   89 
   90 #include <uvm/uvm_extern.h>
   91 #include <uvm/uvm_object.h>
   92 
   93 static MALLOC_DEFINE(M_SHM, "shm", "SVID compatible shared memory segments");
   94 
   95 /*
   96  * Provides the following externally accessible functions:
   97  *
   98  * shminit(void);                                initialization
   99  * shmexit(struct vmspace *)                     cleanup
  100  * shmfork(struct vmspace *, struct vmspace *)   fork handling
  101  *
  102  * Structures:
  103  * shmsegs (an array of 'struct shmid_ds')
  104  * per proc array of 'struct shmmap_state'
  105  */
  106 
  107 int shm_nused;
  108 struct  shmid_ds *shmsegs;
  109 
  110 struct shmmap_entry {
  111         SLIST_ENTRY(shmmap_entry) next;
  112         vaddr_t va;
  113         int shmid;
  114 };
  115 
  116 struct lock     shm_lock;
  117 static int      shm_last_free, shm_committed, shm_use_phys;
  118 
  119 static POOL_INIT(shmmap_entry_pool, sizeof(struct shmmap_entry), 0, 0, 0,
  120     "shmmp", &pool_allocator_nointr);
  121 
  122 struct shmmap_state {
  123         unsigned int nitems;
  124         unsigned int nrefs;
  125         SLIST_HEAD(, shmmap_entry) entries;
  126 };
  127 
  128 static int shm_find_segment_by_key(key_t);
  129 static void shm_deallocate_segment(struct shmid_ds *);
  130 static void shm_delete_mapping(struct vmspace *, struct shmmap_state *,
  131                                struct shmmap_entry *);
  132 static int shmget_existing(struct lwp *, struct sys_shmget_args *,
  133                            int, int, register_t *);
  134 static int shmget_allocate_segment(struct lwp *, struct sys_shmget_args *,
  135                                    int, register_t *);
  136 static struct shmmap_state *shmmap_getprivate(struct proc *);
  137 static struct shmmap_entry *shm_find_mapping(struct shmmap_state *, vaddr_t);
  138 static int shmrealloc(int);
  139 
  140 static int
  141 shm_find_segment_by_key(key_t key)
  142 {
  143         int i;
  144 
  145         for (i = 0; i < shminfo.shmmni; i++)
  146                 if ((shmsegs[i].shm_perm.mode & SHMSEG_ALLOCATED) &&
  147                     shmsegs[i].shm_perm._key == key)
  148                         return i;
  149         return -1;
  150 }
  151 
  152 static struct shmid_ds *
  153 shm_find_segment_by_shmid(int shmid)
  154 {
  155         int segnum;
  156         struct shmid_ds *shmseg;
  157 
  158         segnum = IPCID_TO_IX(shmid);
  159         if (segnum < 0 || segnum >= shminfo.shmmni)
  160                 return NULL;
  161         shmseg = &shmsegs[segnum];
  162         if ((shmseg->shm_perm.mode & SHMSEG_ALLOCATED) == 0)
  163                 return NULL;
  164         if ((shmseg->shm_perm.mode & (SHMSEG_REMOVED|SHMSEG_RMLINGER)) == SHMSEG_REMOVED)
  165                 return NULL;
  166         if (shmseg->shm_perm._seq != IPCID_TO_SEQ(shmid))
  167                 return NULL;
  168         return shmseg;
  169 }
  170 
  171 static void
  172 shm_deallocate_segment(struct shmid_ds *shmseg)
  173 {
  174         struct uvm_object *uobj = shmseg->_shm_internal;
  175         size_t size = (shmseg->shm_segsz + PGOFSET) & ~PGOFSET;
  176 
  177 #ifdef SHMDEBUG
  178         printf("shm freeing key 0x%lx seq 0x%x\n",
  179                shmseg->shm_perm._key, shmseg->shm_perm._seq);
  180 #endif
  181 
  182         (*uobj->pgops->pgo_detach)(uobj);
  183         shmseg->_shm_internal = NULL;
  184         shm_committed -= btoc(size);
  185         shmseg->shm_perm.mode = SHMSEG_FREE;
  186         shm_nused--;
  187 }
  188 
  189 static void
  190 shm_delete_mapping(struct vmspace *vm, struct shmmap_state *shmmap_s,
  191     struct shmmap_entry *shmmap_se)
  192 {
  193         struct shmid_ds *shmseg;
  194         int segnum;
  195         size_t size;
  196 
  197         segnum = IPCID_TO_IX(shmmap_se->shmid);
  198 #ifdef DEBUG
  199         if (segnum < 0 || segnum >= shminfo.shmmni)
  200                 panic("shm_delete_mapping: vmspace %p state %p entry %p - "
  201                     "entry segment ID bad (%d)",
  202                     vm, shmmap_s, shmmap_se, segnum);
  203 #endif
  204         shmseg = &shmsegs[segnum];
  205         size = (shmseg->shm_segsz + PGOFSET) & ~PGOFSET;
  206         uvm_deallocate(&vm->vm_map, shmmap_se->va, size);
  207         SLIST_REMOVE(&shmmap_s->entries, shmmap_se, shmmap_entry, next);
  208         shmmap_s->nitems--;
  209         pool_put(&shmmap_entry_pool, shmmap_se);
  210         shmseg->shm_dtime = time_second;
  211         if ((--shmseg->shm_nattch <= 0) &&
  212             (shmseg->shm_perm.mode & SHMSEG_REMOVED)) {
  213                 shm_deallocate_segment(shmseg);
  214                 shm_last_free = segnum;
  215         }
  216 }
  217 
  218 /*
  219  * Get a non-shared shm map for that vmspace.
  220  * 3 cases:
  221  *   - no shm map present: create a fresh one
  222  *   - a shm map with refcount=1, just used by ourselves: fine
  223  *   - a shared shm map: copy to a fresh one and adjust refcounts
  224  */
  225 static struct shmmap_state *
  226 shmmap_getprivate(struct proc *p)
  227 {
  228         struct shmmap_state *oshmmap_s, *shmmap_s;
  229         struct shmmap_entry *oshmmap_se, *shmmap_se;
  230 
  231         oshmmap_s = (struct shmmap_state *)p->p_vmspace->vm_shm;
  232         if (oshmmap_s && oshmmap_s->nrefs == 1)
  233                 return (oshmmap_s);
  234 
  235         shmmap_s = malloc(sizeof(struct shmmap_state), M_SHM, M_WAITOK);
  236         memset(shmmap_s, 0, sizeof(struct shmmap_state));
  237         shmmap_s->nrefs = 1;
  238         SLIST_INIT(&shmmap_s->entries);
  239         p->p_vmspace->vm_shm = (caddr_t)shmmap_s;
  240 
  241         if (!oshmmap_s)
  242                 return (shmmap_s);
  243 
  244 #ifdef SHMDEBUG
  245         printf("shmmap_getprivate: vm %p split (%d entries), was used by %d\n",
  246                p->p_vmspace, oshmmap_s->nitems, oshmmap_s->nrefs);
  247 #endif
  248         SLIST_FOREACH(oshmmap_se, &oshmmap_s->entries, next) {
  249                 shmmap_se = pool_get(&shmmap_entry_pool, PR_WAITOK);
  250                 shmmap_se->va = oshmmap_se->va;
  251                 shmmap_se->shmid = oshmmap_se->shmid;
  252                 SLIST_INSERT_HEAD(&shmmap_s->entries, shmmap_se, next);
  253         }
  254         shmmap_s->nitems = oshmmap_s->nitems;
  255         oshmmap_s->nrefs--;
  256         return (shmmap_s);
  257 }
  258 
  259 static struct shmmap_entry *
  260 shm_find_mapping(struct shmmap_state *map, vaddr_t va)
  261 {
  262         struct shmmap_entry *shmmap_se;
  263 
  264         SLIST_FOREACH(shmmap_se, &map->entries, next) {
  265                 if (shmmap_se->va == va)
  266                         return shmmap_se;
  267         }
  268         return 0;
  269 }
  270 
  271 int
  272 sys_shmdt(struct lwp *l, void *v, register_t *retval)
  273 {
  274         struct sys_shmdt_args /* {
  275                 syscallarg(const void *) shmaddr;
  276         } */ *uap = v;
  277         struct proc *p = l->l_proc;
  278         struct shmmap_state *shmmap_s, *shmmap_s1;
  279         struct shmmap_entry *shmmap_se;
  280 
  281         shmmap_s = (struct shmmap_state *)p->p_vmspace->vm_shm;
  282         if (shmmap_s == NULL)
  283                 return EINVAL;
  284 
  285         shmmap_se = shm_find_mapping(shmmap_s, (vaddr_t)SCARG(uap, shmaddr));
  286         if (!shmmap_se)
  287                 return EINVAL;
  288 
  289         shmmap_s1 = shmmap_getprivate(p);
  290         if (shmmap_s1 != shmmap_s) {
  291                 /* map has been copied, lookup entry in new map */
  292                 shmmap_se = shm_find_mapping(shmmap_s1,
  293                                              (vaddr_t)SCARG(uap, shmaddr));
  294                 KASSERT(shmmap_se != NULL);
  295         }
  296 #ifdef SHMDEBUG
  297         printf("shmdt: vm %p: remove %d @%lx\n",
  298                p->p_vmspace, shmmap_se->shmid, shmmap_se->va);
  299 #endif
  300         shm_delete_mapping(p->p_vmspace, shmmap_s1, shmmap_se);
  301         return 0;
  302 }
  303 
  304 int
  305 sys_shmat(struct lwp *l, void *v, register_t *retval)
  306 {
  307         struct sys_shmat_args /* {
  308                 syscallarg(int) shmid;
  309                 syscallarg(const void *) shmaddr;
  310                 syscallarg(int) shmflg;
  311         } */ *uap = v;
  312         int error, flags;
  313         struct proc *p = l->l_proc;
  314         kauth_cred_t cred = l->l_cred;
  315         struct shmid_ds *shmseg;
  316         struct shmmap_state *shmmap_s;
  317         struct uvm_object *uobj;
  318         vaddr_t attach_va;
  319         vm_prot_t prot;
  320         vsize_t size;
  321         struct shmmap_entry *shmmap_se;
  322 
  323         shmseg = shm_find_segment_by_shmid(SCARG(uap, shmid));
  324         if (shmseg == NULL)
  325                 return EINVAL;
  326         error = ipcperm(cred, &shmseg->shm_perm,
  327                     (SCARG(uap, shmflg) & SHM_RDONLY) ? IPC_R : IPC_R|IPC_W);
  328         if (error)
  329                 return error;
  330 
  331         shmmap_s = (struct shmmap_state *)p->p_vmspace->vm_shm;
  332         if (shmmap_s && shmmap_s->nitems >= shminfo.shmseg)
  333                 return EMFILE;
  334 
  335         size = (shmseg->shm_segsz + PGOFSET) & ~PGOFSET;
  336         prot = VM_PROT_READ;
  337         if ((SCARG(uap, shmflg) & SHM_RDONLY) == 0)
  338                 prot |= VM_PROT_WRITE;
  339         flags = MAP_ANON | MAP_SHARED;
  340         if (SCARG(uap, shmaddr)) {
  341                 flags |= MAP_FIXED;
  342                 if (SCARG(uap, shmflg) & SHM_RND)
  343                         attach_va =
  344                             (vaddr_t)SCARG(uap, shmaddr) & ~(SHMLBA-1);
  345                 else if (((vaddr_t)SCARG(uap, shmaddr) & (SHMLBA-1)) == 0)
  346                         attach_va = (vaddr_t)SCARG(uap, shmaddr);
  347                 else
  348                         return EINVAL;
  349         } else {
  350                 /* This is just a hint to uvm_mmap() about where to put it. */
  351                 attach_va = p->p_emul->e_vm_default_addr(p,
  352                     (vaddr_t)p->p_vmspace->vm_daddr, size);
  353         }
  354         uobj = shmseg->_shm_internal;
  355         (*uobj->pgops->pgo_reference)(uobj);
  356         error = uvm_map(&p->p_vmspace->vm_map, &attach_va, size,
  357             uobj, 0, 0,
  358             UVM_MAPFLAG(prot, prot, UVM_INH_SHARE, UVM_ADV_RANDOM, 0));
  359         if (error)
  360                 goto out;
  361         /* Lock the memory */
  362         if (shm_use_phys || (shmseg->shm_perm.mode & SHMSEG_WIRED)) {
  363                 /* Wire the map */
  364                 error = uvm_map_pageable(&p->p_vmspace->vm_map, attach_va,
  365                     attach_va + size, FALSE, 0);
  366                 if (error) {
  367                         if (error == EFAULT)
  368                                 error = ENOMEM;
  369                         goto out;
  370                 }
  371         }
  372 
  373         shmmap_se = pool_get(&shmmap_entry_pool, PR_WAITOK);
  374         shmmap_se->va = attach_va;
  375         shmmap_se->shmid = SCARG(uap, shmid);
  376         shmmap_s = shmmap_getprivate(p);
  377 #ifdef SHMDEBUG
  378         printf("shmat: vm %p: add %d @%lx\n", p->p_vmspace, shmmap_se->shmid, attach_va);
  379 #endif
  380         SLIST_INSERT_HEAD(&shmmap_s->entries, shmmap_se, next);
  381         shmmap_s->nitems++;
  382         shmseg->shm_lpid = p->p_pid;
  383         shmseg->shm_atime = time_second;
  384         shmseg->shm_nattch++;
  385 
  386         retval[0] = attach_va;
  387         return 0;
  388 out:
  389         (*uobj->pgops->pgo_detach)(uobj);
  390         return error;
  391 }
  392 
  393 int
  394 sys___shmctl13(struct lwp *l, void *v, register_t *retval)
  395 {
  396         struct sys___shmctl13_args /* {
  397                 syscallarg(int) shmid;
  398                 syscallarg(int) cmd;
  399                 syscallarg(struct shmid_ds *) buf;
  400         } */ *uap = v;
  401         struct shmid_ds shmbuf;
  402         int cmd, error;
  403 
  404         cmd = SCARG(uap, cmd);
  405 
  406         if (cmd == IPC_SET) {
  407                 error = copyin(SCARG(uap, buf), &shmbuf, sizeof(shmbuf));
  408                 if (error)
  409                         return (error);
  410         }
  411 
  412         error = shmctl1(l, SCARG(uap, shmid), cmd,
  413             (cmd == IPC_SET || cmd == IPC_STAT) ? &shmbuf : NULL);
  414 
  415         if (error == 0 && cmd == IPC_STAT)
  416                 error = copyout(&shmbuf, SCARG(uap, buf), sizeof(shmbuf));
  417 
  418         return (error);
  419 }
  420 
  421 int
  422 shmctl1(struct lwp *l, int shmid, int cmd, struct shmid_ds *shmbuf)
  423 {
  424         kauth_cred_t cred = l->l_cred;
  425         struct proc *p = l->l_proc;
  426         struct shmid_ds *shmseg;
  427         struct shmmap_entry *shmmap_se;
  428         struct shmmap_state *shmmap_s;
  429         int error = 0;
  430         size_t size;
  431 
  432         shmseg = shm_find_segment_by_shmid(shmid);
  433         if (shmseg == NULL)
  434                 return EINVAL;
  435 
  436         switch (cmd) {
  437         case IPC_STAT:
  438                 if ((error = ipcperm(cred, &shmseg->shm_perm, IPC_R)) != 0)
  439                         return error;
  440                 memcpy(shmbuf, shmseg, sizeof(struct shmid_ds));
  441                 break;
  442         case IPC_SET:
  443                 if ((error = ipcperm(cred, &shmseg->shm_perm, IPC_M)) != 0)
  444                         return error;
  445                 shmseg->shm_perm.uid = shmbuf->shm_perm.uid;
  446                 shmseg->shm_perm.gid = shmbuf->shm_perm.gid;
  447                 shmseg->shm_perm.mode =
  448                     (shmseg->shm_perm.mode & ~ACCESSPERMS) |
  449                     (shmbuf->shm_perm.mode & ACCESSPERMS);
  450                 shmseg->shm_ctime = time_second;
  451                 break;
  452         case IPC_RMID:
  453                 if ((error = ipcperm(cred, &shmseg->shm_perm, IPC_M)) != 0)
  454                         return error;
  455                 shmseg->shm_perm._key = IPC_PRIVATE;
  456                 shmseg->shm_perm.mode |= SHMSEG_REMOVED;
  457                 if (shmseg->shm_nattch <= 0) {
  458                         shm_deallocate_segment(shmseg);
  459                         shm_last_free = IPCID_TO_IX(shmid);
  460                 }
  461                 break;
  462         case SHM_LOCK:
  463         case SHM_UNLOCK:
  464                 if ((error = kauth_authorize_generic(cred,
  465                     KAUTH_GENERIC_ISSUSER, NULL)) != 0)
  466                         return error;
  467                 shmmap_s = shmmap_getprivate(p);
  468                 /* Find our shared memory address by shmid */
  469                 SLIST_FOREACH(shmmap_se, &shmmap_s->entries, next) {
  470                         if (shmmap_se->shmid != shmid)
  471                                 continue;
  472 
  473                         size = (shmseg->shm_segsz + PGOFSET) & ~PGOFSET;
  474 
  475                         if (cmd == SHM_LOCK &&
  476                             !(shmseg->shm_perm.mode & SHMSEG_WIRED)) {
  477                                 /* Wire the entire object */
  478                                 error = uobj_wirepages(shmseg->_shm_internal, 0,
  479                                         round_page(shmseg->shm_segsz));
  480                                 if (error)
  481                                         return EIO;
  482                                 /* Wire the map */
  483                                 error = uvm_map_pageable(&p->p_vmspace->vm_map,
  484                                     shmmap_se->va, shmmap_se->va + size, FALSE,
  485                                     0);
  486                                 if (error) {
  487                                         uobj_unwirepages(shmseg->_shm_internal,
  488                                             0, round_page(shmseg->shm_segsz));
  489                                         if (error == EFAULT)
  490                                                 error = ENOMEM;
  491                                         return error;
  492                                 }
  493                                 /* Tag as wired */
  494                                 shmseg->shm_perm.mode |= SHMSEG_WIRED;
  495 
  496                         } else if (cmd == SHM_UNLOCK &&
  497                             (shmseg->shm_perm.mode & SHMSEG_WIRED)) {
  498                                 /* Unwire the object */
  499                                 uobj_unwirepages(shmseg->_shm_internal, 0,
  500                                     round_page(shmseg->shm_segsz));
  501                                 error = uvm_map_pageable(&p->p_vmspace->vm_map,
  502                                     shmmap_se->va, shmmap_se->va + size, TRUE,
  503                                     0);
  504                                 if (error) {
  505                                         /*
  506                                          * In fact, uvm_map_pageable could fail
  507                                          * only if arguments are invalid,
  508                                          * otherwise it should allways return 0.
  509                                          */
  510                                         return EIO;
  511                                 }
  512                                 /* Tag as unwired */
  513                                 shmseg->shm_perm.mode &= ~SHMSEG_WIRED;
  514                         }
  515                 }
  516                 break;
  517         default:
  518                 return EINVAL;
  519         }
  520         return 0;
  521 }
  522 
  523 static int
  524 shmget_existing(struct lwp *l, struct sys_shmget_args *uap, int mode,
  525     int segnum, register_t *retval)
  526 {
  527         struct shmid_ds *shmseg;
  528         kauth_cred_t cred = l->l_cred;
  529         int error;
  530 
  531         shmseg = &shmsegs[segnum];
  532         if (shmseg->shm_perm.mode & SHMSEG_REMOVED) {
  533                 /*
  534                  * This segment is in the process of being allocated.  Wait
  535                  * until it's done, and look the key up again (in case the
  536                  * allocation failed or it was freed).
  537                  */
  538                 shmseg->shm_perm.mode |= SHMSEG_WANTED;
  539                 error = tsleep((caddr_t)shmseg, PLOCK | PCATCH, "shmget", 0);
  540                 if (error)
  541                         return error;
  542                 return EAGAIN;
  543         }
  544         if ((error = ipcperm(cred, &shmseg->shm_perm, mode)) != 0)
  545                 return error;
  546         if (SCARG(uap, size) && SCARG(uap, size) > shmseg->shm_segsz)
  547                 return EINVAL;
  548         if ((SCARG(uap, shmflg) & (IPC_CREAT | IPC_EXCL)) ==
  549             (IPC_CREAT | IPC_EXCL))
  550                 return EEXIST;
  551         *retval = IXSEQ_TO_IPCID(segnum, shmseg->shm_perm);
  552         return 0;
  553 }
  554 
  555 static int
  556 shmget_allocate_segment(struct lwp *l, struct sys_shmget_args *uap, int mode,
  557     register_t *retval)
  558 {
  559         int i, segnum, shmid, size;
  560         kauth_cred_t cred = l->l_cred;
  561         struct shmid_ds *shmseg;
  562         int error = 0;
  563 
  564         if (SCARG(uap, size) < shminfo.shmmin ||
  565             SCARG(uap, size) > shminfo.shmmax)
  566                 return EINVAL;
  567         if (shm_nused >= shminfo.shmmni) /* any shmids left? */
  568                 return ENOSPC;
  569         size = (SCARG(uap, size) + PGOFSET) & ~PGOFSET;
  570         if (shm_committed + btoc(size) > shminfo.shmall)
  571                 return ENOMEM;
  572         if (shm_last_free < 0) {
  573                 for (i = 0; i < shminfo.shmmni; i++)
  574                         if (shmsegs[i].shm_perm.mode & SHMSEG_FREE)
  575                                 break;
  576                 if (i == shminfo.shmmni)
  577                         panic("shmseg free count inconsistent");
  578                 segnum = i;
  579         } else  {
  580                 segnum = shm_last_free;
  581                 shm_last_free = -1;
  582         }
  583         shmseg = &shmsegs[segnum];
  584         /*
  585          * In case we sleep in malloc(), mark the segment present but deleted
  586          * so that noone else tries to create the same key.
  587          */
  588         shmseg->shm_perm.mode = SHMSEG_ALLOCATED | SHMSEG_REMOVED;
  589         shmseg->shm_perm._key = SCARG(uap, key);
  590         shmseg->shm_perm._seq = (shmseg->shm_perm._seq + 1) & 0x7fff;
  591         shmid = IXSEQ_TO_IPCID(segnum, shmseg->shm_perm);
  592 
  593         shmseg->_shm_internal = uao_create(size, 0);
  594 
  595         shmseg->shm_perm.cuid = shmseg->shm_perm.uid = kauth_cred_geteuid(cred);
  596         shmseg->shm_perm.cgid = shmseg->shm_perm.gid = kauth_cred_getegid(cred);
  597         shmseg->shm_perm.mode = (shmseg->shm_perm.mode & SHMSEG_WANTED) |
  598             (mode & (ACCESSPERMS|SHMSEG_RMLINGER)) | SHMSEG_ALLOCATED;
  599         shmseg->shm_segsz = SCARG(uap, size);
  600         shmseg->shm_cpid = l->l_proc->p_pid;
  601         shmseg->shm_lpid = shmseg->shm_nattch = 0;
  602         shmseg->shm_atime = shmseg->shm_dtime = 0;
  603         shmseg->shm_ctime = time_second;
  604         shm_committed += btoc(size);
  605         shm_nused++;
  606 
  607         *retval = shmid;
  608         if (shmseg->shm_perm.mode & SHMSEG_WANTED) {
  609                 /*
  610                  * Somebody else wanted this key while we were asleep.  Wake
  611                  * them up now.
  612                  */
  613                 shmseg->shm_perm.mode &= ~SHMSEG_WANTED;
  614                 wakeup((caddr_t)shmseg);
  615         }
  616 
  617         /* Lock the memory */
  618         if (shm_use_phys) {
  619                 /* Wire the entire object */
  620                 error = uobj_wirepages(shmseg->_shm_internal, 0,
  621                     round_page(shmseg->shm_segsz));
  622                 if (error) {
  623                         shm_deallocate_segment(shmseg);
  624                 } else {
  625                         /* Tag as wired */
  626                         shmseg->shm_perm.mode |= SHMSEG_WIRED;
  627                 }
  628         }
  629 
  630         return error;
  631 }
  632 
  633 int
  634 sys_shmget(struct lwp *l, void *v, register_t *retval)
  635 {
  636         struct sys_shmget_args /* {
  637                 syscallarg(key_t) key;
  638                 syscallarg(int) size;
  639                 syscallarg(int) shmflg;
  640         } */ *uap = v;
  641         int segnum, mode, error;
  642 
  643         mode = SCARG(uap, shmflg) & ACCESSPERMS;
  644         if (SCARG(uap, shmflg) & _SHM_RMLINGER)
  645                 mode |= SHMSEG_RMLINGER;
  646 
  647 #ifdef SHMDEBUG
  648         printf("shmget: key 0x%lx size 0x%x shmflg 0x%x mode 0x%x\n",
  649                 SCARG(uap, key), SCARG(uap, size), SCARG(uap, shmflg), mode);
  650 #endif
  651 
  652         if (SCARG(uap, key) != IPC_PRIVATE) {
  653 again:
  654                 segnum = shm_find_segment_by_key(SCARG(uap, key));
  655                 if (segnum >= 0) {
  656                         error = shmget_existing(l, uap, mode, segnum, retval);
  657                         if (error == EAGAIN)
  658                                 goto again;
  659                         return error;
  660                 }
  661                 if ((SCARG(uap, shmflg) & IPC_CREAT) == 0)
  662                         return ENOENT;
  663         }
  664         return shmget_allocate_segment(l, uap, mode, retval);
  665 }
  666 
  667 void
  668 shmfork(struct vmspace *vm1, struct vmspace *vm2)
  669 {
  670         struct shmmap_state *shmmap_s;
  671         struct shmmap_entry *shmmap_se;
  672 
  673         vm2->vm_shm = vm1->vm_shm;
  674 
  675         if (vm1->vm_shm == NULL)
  676                 return;
  677 
  678 #ifdef SHMDEBUG
  679         printf("shmfork %p->%p\n", vm1, vm2);
  680 #endif
  681 
  682         shmmap_s = (struct shmmap_state *)vm1->vm_shm;
  683 
  684         SLIST_FOREACH(shmmap_se, &shmmap_s->entries, next)
  685                 shmsegs[IPCID_TO_IX(shmmap_se->shmid)].shm_nattch++;
  686         shmmap_s->nrefs++;
  687 }
  688 
  689 void
  690 shmexit(struct vmspace *vm)
  691 {
  692         struct shmmap_state *shmmap_s;
  693         struct shmmap_entry *shmmap_se;
  694 
  695         shmmap_s = (struct shmmap_state *)vm->vm_shm;
  696         if (shmmap_s == NULL)
  697                 return;
  698 
  699         vm->vm_shm = NULL;
  700 
  701         if (--shmmap_s->nrefs > 0) {
  702 #ifdef SHMDEBUG
  703                 printf("shmexit: vm %p drop ref (%d entries), now used by %d\n",
  704                        vm, shmmap_s->nitems, shmmap_s->nrefs);
  705 #endif
  706                 SLIST_FOREACH(shmmap_se, &shmmap_s->entries, next)
  707                         shmsegs[IPCID_TO_IX(shmmap_se->shmid)].shm_nattch--;
  708                 return;
  709         }
  710 
  711 #ifdef SHMDEBUG
  712         printf("shmexit: vm %p cleanup (%d entries)\n", vm, shmmap_s->nitems);
  713 #endif
  714         while (!SLIST_EMPTY(&shmmap_s->entries)) {
  715                 shmmap_se = SLIST_FIRST(&shmmap_s->entries);
  716                 shm_delete_mapping(vm, shmmap_s, shmmap_se);
  717         }
  718         KASSERT(shmmap_s->nitems == 0);
  719         free(shmmap_s, M_SHM);
  720 }
  721 
  722 static int
  723 shmrealloc(int newshmni)
  724 {
  725         int i, sz;
  726         vaddr_t v;
  727         struct shmid_ds *newshmsegs;
  728 
  729         /* XXX: Would be good to have a upper limit */
  730         if (newshmni < 1)
  731                 return EINVAL;
  732 
  733         /* We can't reallocate lesser memory than we use */
  734         if (shm_nused > newshmni)
  735                 return EPERM;
  736 
  737         /* Allocate new memory area */
  738         sz = newshmni * sizeof(struct shmid_ds);
  739         v = uvm_km_alloc(kernel_map, round_page(sz), 0, UVM_KMF_WIRED);
  740         if (v == 0)
  741                 return ENOMEM;
  742 
  743         newshmsegs = (void *)v;
  744 
  745         /* Copy all memory to the new area */
  746         for (i = 0; i < shm_nused; i++)
  747                 (void)memcpy(&newshmsegs[i], &shmsegs[i],
  748                     sizeof(newshmsegs[0]));
  749 
  750         /* Mark as free all new segments, if there is any */
  751         for (; i < newshmni; i++) {
  752                 newshmsegs[i].shm_perm.mode = SHMSEG_FREE;
  753                 newshmsegs[i].shm_perm._seq = 0;
  754         }
  755 
  756         sz = shminfo.shmmni * sizeof(struct shmid_ds);
  757         uvm_km_free(kernel_map, (vaddr_t)shmsegs, sz, UVM_KMF_WIRED);
  758         shmsegs = newshmsegs;
  759 
  760         return 0;
  761 }
  762 
  763 void
  764 shminit(void)
  765 {
  766         int i, sz;
  767         vaddr_t v;
  768 
  769         lockinit(&shm_lock, PWAIT, "shmlk", 0, 0);
  770 
  771         /* Allocate pageable memory for our structures */
  772         sz = shminfo.shmmni * sizeof(struct shmid_ds);
  773         v = uvm_km_alloc(kernel_map, round_page(sz), 0, UVM_KMF_WIRED);
  774         if (v == 0)
  775                 panic("sysv_shm: cannot allocate memory");
  776         shmsegs = (void *)v;
  777 
  778         shminfo.shmmax *= PAGE_SIZE;
  779 
  780         for (i = 0; i < shminfo.shmmni; i++) {
  781                 shmsegs[i].shm_perm.mode = SHMSEG_FREE;
  782                 shmsegs[i].shm_perm._seq = 0;
  783         }
  784         shm_last_free = 0;
  785         shm_nused = 0;
  786         shm_committed = 0;
  787 }
  788 
  789 static int
  790 sysctl_ipc_shmmni(SYSCTLFN_ARGS)
  791 {
  792         int newsize, error;
  793         struct sysctlnode node;
  794         node = *rnode;
  795         node.sysctl_data = &newsize;
  796 
  797         newsize = shminfo.shmmni;
  798         error = sysctl_lookup(SYSCTLFN_CALL(&node));
  799         if (error || newp == NULL)
  800                 return error;
  801 
  802         lockmgr(&shm_lock, LK_EXCLUSIVE, NULL);
  803         error = shmrealloc(newsize);
  804         if (error == 0)
  805                 shminfo.shmmni = newsize;
  806         lockmgr(&shm_lock, LK_RELEASE, NULL);
  807 
  808         return error;
  809 }
  810 
  811 static int
  812 sysctl_ipc_shmmaxpgs(SYSCTLFN_ARGS)
  813 {
  814         int newsize, error;
  815         struct sysctlnode node;
  816         node = *rnode;
  817         node.sysctl_data = &newsize;
  818         newsize = shminfo.shmall;
  819         error = sysctl_lookup(SYSCTLFN_CALL(&node));
  820         if (error || newp == NULL)
  821                 return error;
  822 
  823         /* XXX: Would be good to have a upper limit */
  824         if (newsize < 1)
  825                 return EINVAL;
  826 
  827         shminfo.shmall = newsize;
  828         shminfo.shmmax = shminfo.shmall * PAGE_SIZE;
  829 
  830         return 0;
  831 }
  832 
  833 SYSCTL_SETUP(sysctl_ipc_shm_setup, "sysctl kern.ipc subtree setup")
  834 {
  835         sysctl_createv(clog, 0, NULL, NULL,
  836                 CTLFLAG_PERMANENT,
  837                 CTLTYPE_NODE, "kern", NULL,
  838                 NULL, 0, NULL, 0,
  839                 CTL_KERN, CTL_EOL);
  840 
  841         sysctl_createv(clog, 0, NULL, NULL,
  842                 CTLFLAG_PERMANENT,
  843                 CTLTYPE_NODE, "ipc",
  844                 SYSCTL_DESCR("SysV IPC options"),
  845                 NULL, 0, NULL, 0,
  846                 CTL_KERN, KERN_SYSVIPC, CTL_EOL);
  847 
  848         sysctl_createv(clog, 0, NULL, NULL,
  849                 CTLFLAG_PERMANENT | CTLFLAG_READONLY,
  850                 CTLTYPE_INT, "shmmax",
  851                 SYSCTL_DESCR("Max shared memory segment size in bytes"),
  852                 NULL, 0, &shminfo.shmmax, 0,
  853                 CTL_KERN, KERN_SYSVIPC, KERN_SYSVIPC_SHMMAX, CTL_EOL);
  854 
  855         sysctl_createv(clog, 0, NULL, NULL,
  856                 CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
  857                 CTLTYPE_INT, "shmmni",
  858                 SYSCTL_DESCR("Max number of shared memory identifiers"),
  859                 sysctl_ipc_shmmni, 0, &shminfo.shmmni, 0,
  860                 CTL_KERN, KERN_SYSVIPC, KERN_SYSVIPC_SHMMNI, CTL_EOL);
  861 
  862         sysctl_createv(clog, 0, NULL, NULL,
  863                 CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
  864                 CTLTYPE_INT, "shmseg",
  865                 SYSCTL_DESCR("Max shared memory segments per process"),
  866                 NULL, 0, &shminfo.shmseg, 0,
  867                 CTL_KERN, KERN_SYSVIPC, KERN_SYSVIPC_SHMSEG, CTL_EOL);
  868 
  869         sysctl_createv(clog, 0, NULL, NULL,
  870                 CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
  871                 CTLTYPE_INT, "shmmaxpgs",
  872                 SYSCTL_DESCR("Max amount of shared memory in pages"),
  873                 sysctl_ipc_shmmaxpgs, 0, &shminfo.shmall, 0,
  874                 CTL_KERN, KERN_SYSVIPC, KERN_SYSVIPC_SHMMAXPGS, CTL_EOL);
  875 
  876         sysctl_createv(clog, 0, NULL, NULL,
  877                 CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
  878                 CTLTYPE_INT, "shm_use_phys",
  879                 SYSCTL_DESCR("Enable/disable locking of shared memory in "
  880                     "physical memory"), NULL, 0, &shm_use_phys, 0,
  881                 CTL_KERN, KERN_SYSVIPC, KERN_SYSVIPC_SHMUSEPHYS, CTL_EOL);
  882 }

Cache object: 8ea7220355b3a3ce0403af5890ccee8c


[ 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.