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/uvm/uvm_unix.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 /*      $OpenBSD: uvm_unix.c,v 1.72 2023/01/13 23:02:44 kettenis Exp $  */
    2 /*      $NetBSD: uvm_unix.c,v 1.18 2000/09/13 15:00:25 thorpej Exp $    */
    3 
    4 /*
    5  * Copyright (c) 1997 Charles D. Cranor and Washington University.
    6  * Copyright (c) 1991, 1993 The Regents of the University of California.  
    7  * Copyright (c) 1988 University of Utah.
    8  *
    9  * All rights reserved.
   10  *
   11  * This code is derived from software contributed to Berkeley by
   12  * the Systems Programming Group of the University of Utah Computer
   13  * Science Department.
   14  *
   15  * Redistribution and use in source and binary forms, with or without
   16  * modification, are permitted provided that the following conditions
   17  * are met:
   18  * 1. Redistributions of source code must retain the above copyright
   19  *    notice, this list of conditions and the following disclaimer.
   20  * 2. Redistributions in binary form must reproduce the above copyright
   21  *    notice, this list of conditions and the following disclaimer in the
   22  *    documentation and/or other materials provided with the distribution.
   23  * 3. Neither the name of the University nor the names of its contributors
   24  *    may be used to endorse or promote products derived from this software
   25  *    without specific prior written permission.
   26  *
   27  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
   28  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   30  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   31  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   32  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   33  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   34  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   35  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   36  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   37  * SUCH DAMAGE.
   38  *
   39  * from: Utah $Hdr: vm_unix.c 1.1 89/11/07$
   40  *      @(#)vm_unix.c   8.1 (Berkeley) 6/11/93
   41  * from: Id: uvm_unix.c,v 1.1.2.2 1997/08/25 18:52:30 chuck Exp
   42  */
   43 
   44 /*
   45  * uvm_unix.c: traditional sbrk/grow interface to vm.
   46  */
   47 
   48 #include <sys/param.h>
   49 #include <sys/systm.h>
   50 #include <sys/proc.h>
   51 #include <sys/resourcevar.h>
   52 #include <sys/vnode.h>
   53 
   54 #include <sys/mount.h>
   55 #include <sys/syscallargs.h>
   56 
   57 #include <uvm/uvm.h>
   58 
   59 /*
   60  * sys_obreak: set break
   61  */
   62 
   63 int
   64 sys_obreak(struct proc *p, void *v, register_t *retval)
   65 {
   66         struct sys_obreak_args /* {
   67                 syscallarg(char *) nsize;
   68         } */ *uap = v;
   69         struct vmspace *vm = p->p_vmspace;
   70         vaddr_t new, old, base;
   71         int error;
   72 
   73         base = (vaddr_t)vm->vm_daddr;
   74         new = round_page((vaddr_t)SCARG(uap, nsize));
   75         if (new < base || (new - base) > lim_cur(RLIMIT_DATA))
   76                 return (ENOMEM);
   77 
   78         old = round_page(base + ptoa(vm->vm_dsize));
   79 
   80         if (new == old)
   81                 return (0);
   82 
   83         /* grow or shrink? */
   84         if (new > old) {
   85                 error = uvm_map(&vm->vm_map, &old, new - old, NULL,
   86                     UVM_UNKNOWN_OFFSET, 0,
   87                     UVM_MAPFLAG(PROT_READ | PROT_WRITE,
   88                     PROT_READ | PROT_WRITE | PROT_EXEC, MAP_INHERIT_COPY,
   89                     MADV_NORMAL, UVM_FLAG_FIXED|UVM_FLAG_COPYONW));
   90                 if (error) {
   91                         uprintf("sbrk: grow %ld failed, error = %d\n",
   92                             new - old, error);
   93                         return (ENOMEM);
   94                 }
   95                 vm->vm_dsize += atop(new - old);
   96         } else {
   97                 uvm_unmap(&vm->vm_map, new, old);
   98                 vm->vm_dsize -= atop(old - new);
   99         }
  100 
  101         return (0);
  102 }
  103 
  104 /*
  105  * uvm_grow: enlarge the "stack segment" to include sp.
  106  */
  107 void
  108 uvm_grow(struct proc *p, vaddr_t sp)
  109 {
  110         struct vmspace *vm = p->p_vmspace;
  111         vm_map_t map = &vm->vm_map;
  112         int si;
  113 
  114         /* For user defined stacks (from sendsig). */
  115         if (sp < (vaddr_t)vm->vm_maxsaddr)
  116                 return;
  117 #ifdef MACHINE_STACK_GROWS_UP
  118         if (sp >= (vaddr_t)vm->vm_minsaddr)
  119                 return;
  120 #endif
  121 
  122         vm_map_lock(map);
  123 
  124         /* For common case of already allocated (from trap). */
  125 #ifdef MACHINE_STACK_GROWS_UP
  126         if (sp < (vaddr_t)vm->vm_maxsaddr + ptoa(vm->vm_ssize))
  127 #else
  128         if (sp >= (vaddr_t)vm->vm_minsaddr - ptoa(vm->vm_ssize))
  129 #endif
  130                 goto out;
  131 
  132         /* Really need to check vs limit and increment stack size if ok. */
  133 #ifdef MACHINE_STACK_GROWS_UP
  134         si = atop(sp - (vaddr_t)vm->vm_maxsaddr) - vm->vm_ssize + 1;
  135 #else
  136         si = atop((vaddr_t)vm->vm_minsaddr - sp) - vm->vm_ssize;
  137 #endif
  138         if (vm->vm_ssize + si <= atop(lim_cur(RLIMIT_STACK)))
  139                 vm->vm_ssize += si;
  140 out:
  141         vm_map_unlock(map);
  142 }
  143 
  144 #ifndef SMALL_KERNEL
  145 
  146 #define WALK_CHUNK      32
  147 /*
  148  * Not all the pages in an amap may be present.  When dumping core,
  149  * we don't want to force all the pages to be present: it's a waste
  150  * of time and memory when we already know what they contain (zeros)
  151  * and the ELF format at least can adequately represent them as a
  152  * segment with memory size larger than its file size.
  153  *
  154  * So, we walk the amap with calls to amap_lookups() and scan the
  155  * resulting pointers to find ranges of zero or more present pages
  156  * followed by at least one absent page or the end of the amap.
  157  * When then pass that range to the walk callback with 'start'
  158  * pointing to the start of the present range, 'realend' pointing
  159  * to the first absent page (or the end of the entry), and 'end'
  160  * pointing to the page past the last absent page (or the end of
  161  * the entry).
  162  *
  163  * Note that if the first page of the amap is empty then the callback
  164  * must be invoked with 'start' == 'realend' so it can present that
  165  * first range of absent pages.
  166  */
  167 int
  168 uvm_coredump_walk_amap(struct vm_map_entry *entry, int *nsegmentp,
  169     uvm_coredump_walk_cb *walk, void *cookie)
  170 {
  171         struct vm_anon *anons[WALK_CHUNK];
  172         vaddr_t pos, start, realend, end, entry_end;
  173         vm_prot_t prot;
  174         int nsegment, absent, npages, i, error;
  175 
  176         prot = entry->protection;
  177         nsegment = *nsegmentp;
  178         start = entry->start;
  179         entry_end = MIN(entry->end, VM_MAXUSER_ADDRESS);
  180 
  181         absent = 0;
  182         for (pos = start; pos < entry_end; pos += npages << PAGE_SHIFT) {
  183                 npages = (entry_end - pos) >> PAGE_SHIFT;
  184                 if (npages > WALK_CHUNK)
  185                         npages = WALK_CHUNK;
  186                 amap_lookups(&entry->aref, pos - entry->start, anons, npages);
  187                 for (i = 0; i < npages; i++) {
  188                         if ((anons[i] == NULL) == absent)
  189                                 continue;
  190                         if (!absent) {
  191                                 /* going from present to absent: set realend */
  192                                 realend = pos + (i << PAGE_SHIFT);
  193                                 absent = 1;
  194                                 continue;
  195                         }
  196 
  197                         /* going from absent to present: invoke callback */
  198                         end = pos + (i << PAGE_SHIFT);
  199                         if (start != end) {
  200                                 error = (*walk)(start, realend, end, prot,
  201                                     nsegment, cookie);
  202                                 if (error)
  203                                         return error;
  204                                 nsegment++;
  205                         }
  206                         start = realend = end;
  207                         absent = 0;
  208                 }
  209         }
  210 
  211         if (!absent)
  212                 realend = entry_end;
  213         error = (*walk)(start, realend, entry_end, prot, nsegment, cookie);
  214         *nsegmentp = nsegment + 1;
  215         return error;
  216 }
  217 
  218 /*
  219  * Common logic for whether a map entry should be included in a coredump
  220  */
  221 static inline int
  222 uvm_should_coredump(struct proc *p, struct vm_map_entry *entry)
  223 {
  224         if (!(entry->protection & PROT_WRITE) &&
  225             entry->aref.ar_amap == NULL &&
  226             entry->start != p->p_p->ps_sigcode &&
  227             entry->start != p->p_p->ps_timekeep)
  228                 return 0;
  229 
  230         /*
  231          * Skip ranges marked as unreadable, as uiomove(UIO_USERSPACE)
  232          * will fail on them.  Maybe this really should be a test of
  233          * entry->max_protection, but doing
  234          *      uvm_map_extract(UVM_EXTRACT_FIXPROT)
  235          * on each such page would suck.
  236          */
  237         if (!(entry->protection & PROT_READ) &&
  238             entry->start != p->p_p->ps_sigcode)
  239                 return 0;
  240 
  241         /* Skip ranges excluded from coredumps. */
  242         if (UVM_ET_ISCONCEAL(entry))
  243                 return 0;
  244 
  245         /* Don't dump mmaped devices. */
  246         if (entry->object.uvm_obj != NULL &&
  247             UVM_OBJ_IS_DEVICE(entry->object.uvm_obj))
  248                 return 0;
  249 
  250         if (entry->start >= VM_MAXUSER_ADDRESS)
  251                 return 0;
  252 
  253         return 1;
  254 }
  255 
  256 
  257 /* do nothing callback for uvm_coredump_walk_amap() */
  258 static int
  259 noop(vaddr_t start, vaddr_t realend, vaddr_t end, vm_prot_t prot,
  260     int nsegment, void *cookie)
  261 {
  262         return 0;
  263 }
  264 
  265 /*
  266  * Walk the VA space for a process to identify what to write to
  267  * a coredump.  First the number of contiguous ranges is counted,
  268  * then the 'setup' callback is invoked to prepare for actually
  269  * recording the ranges, then the VA is walked again, invoking
  270  * the 'walk' callback for each range.  The number of ranges walked
  271  * is guaranteed to match the count seen by the 'setup' callback.
  272  */
  273 
  274 int
  275 uvm_coredump_walkmap(struct proc *p, uvm_coredump_setup_cb *setup,
  276     uvm_coredump_walk_cb *walk, void *cookie)
  277 {
  278         struct vmspace *vm = p->p_vmspace;
  279         struct vm_map *map = &vm->vm_map;
  280         struct vm_map_entry *entry;
  281         vaddr_t end;
  282         int refed_amaps = 0;
  283         int nsegment, error;
  284 
  285         /*
  286          * Walk the map once to count the segments.  If an amap is
  287          * referenced more than once than take *another* reference
  288          * and treat the amap as exactly one segment instead of
  289          * checking page presence inside it.  On the second pass
  290          * we'll recognize which amaps we did that for by the ref
  291          * count being >1...and decrement it then.
  292          */
  293         nsegment = 0;
  294         RBT_FOREACH(entry, uvm_map_addr, &map->addr) {
  295                 /* should never happen for a user process */
  296                 if (UVM_ET_ISSUBMAP(entry)) {
  297                         panic("%s: user process with submap?", __func__);
  298                 }
  299 
  300                 if (! uvm_should_coredump(p, entry))
  301                         continue;
  302 
  303                 if (entry->aref.ar_amap != NULL) {
  304                         if (entry->aref.ar_amap->am_ref == 1) {
  305                                 uvm_coredump_walk_amap(entry, &nsegment,
  306                                     &noop, cookie);
  307                                 continue;
  308                         }
  309 
  310                         /*
  311                          * Multiple refs currently, so take another and
  312                          * treat it as a single segment
  313                          */
  314                         entry->aref.ar_amap->am_ref++;
  315                         refed_amaps++;
  316                 }
  317 
  318                 nsegment++;
  319         }
  320 
  321         /*
  322          * Okay, we have a count in nsegment.  Prepare to
  323          * walk it again, then invoke the setup callback. 
  324          */
  325         entry = RBT_MIN(uvm_map_addr, &map->addr);
  326         error = (*setup)(nsegment, cookie);
  327         if (error)
  328                 goto cleanup;
  329 
  330         /*
  331          * Setup went okay, so do the second walk, invoking the walk
  332          * callback on the counted segments and cleaning up references
  333          * as we go.
  334          */
  335         nsegment = 0;
  336         for (; entry != NULL; entry = RBT_NEXT(uvm_map_addr, entry)) {
  337                 if (! uvm_should_coredump(p, entry))
  338                         continue;
  339 
  340                 if (entry->aref.ar_amap != NULL &&
  341                     entry->aref.ar_amap->am_ref == 1) {
  342                         error = uvm_coredump_walk_amap(entry, &nsegment,
  343                             walk, cookie);
  344                         if (error)
  345                                 break;
  346                         continue;
  347                 }
  348 
  349                 end = entry->end;
  350                 if (end > VM_MAXUSER_ADDRESS)
  351                         end = VM_MAXUSER_ADDRESS;
  352 
  353                 error = (*walk)(entry->start, end, end, entry->protection,
  354                     nsegment, cookie);
  355                 if (error)
  356                         break;
  357                 nsegment++;
  358 
  359                 if (entry->aref.ar_amap != NULL &&
  360                     entry->aref.ar_amap->am_ref > 1) {
  361                         /* multiple refs, so we need to drop one */
  362                         entry->aref.ar_amap->am_ref--;
  363                         refed_amaps--;
  364                 }
  365         }
  366 
  367         if (error) {
  368 cleanup:
  369                 /* clean up the extra references from where we left off */
  370                 if (refed_amaps > 0) {
  371                         for (; entry != NULL;
  372                             entry = RBT_NEXT(uvm_map_addr, entry)) {
  373                                 if (entry->aref.ar_amap == NULL ||
  374                                     entry->aref.ar_amap->am_ref == 1)
  375                                         continue;
  376                                 if (! uvm_should_coredump(p, entry))
  377                                         continue;
  378                                 entry->aref.ar_amap->am_ref--;
  379                                 if (refed_amaps-- == 0)
  380                                         break;
  381                         }
  382                 }
  383         }
  384 
  385         return error;
  386 }
  387 
  388 #endif  /* !SMALL_KERNEL */

Cache object: e0ef11ec263cd5b67146368d755ca70b


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