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/osfmk/ppc/savearea.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 /*
    2  * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
    3  *
    4  * @APPLE_LICENSE_HEADER_START@
    5  * 
    6  * Copyright (c) 1999-2003 Apple Computer, Inc.  All Rights Reserved.
    7  * 
    8  * This file contains Original Code and/or Modifications of Original Code
    9  * as defined in and that are subject to the Apple Public Source License
   10  * Version 2.0 (the 'License'). You may not use this file except in
   11  * compliance with the License. Please obtain a copy of the License at
   12  * http://www.opensource.apple.com/apsl/ and read it before using this
   13  * file.
   14  * 
   15  * The Original Code and all software distributed under the License are
   16  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
   17  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
   18  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
   19  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
   20  * Please see the License for the specific language governing rights and
   21  * limitations under the License.
   22  * 
   23  * @APPLE_LICENSE_HEADER_END@
   24  */
   25 /*
   26  *      This file is used to maintain the exception save areas
   27  *
   28  */
   29 
   30 #include <cpus.h>
   31 #include <debug.h>
   32 #include <mach_kgdb.h>
   33 #include <mach_vm_debug.h>
   34 
   35 #include <kern/thread.h>
   36 #include <mach/vm_attributes.h>
   37 #include <mach/vm_param.h>
   38 #include <vm/vm_kern.h>
   39 #include <vm/vm_map.h>
   40 #include <vm/vm_page.h>
   41 #include <mach/ppc/thread_status.h>
   42 #include <kern/spl.h>
   43 #include <kern/simple_lock.h>
   44 
   45 #include <kern/misc_protos.h>
   46 #include <ppc/misc_protos.h>
   47 #include <ppc/proc_reg.h>
   48 #include <ppc/mem.h>
   49 #include <ppc/pmap.h>
   50 #include <ppc/Firmware.h>
   51 #include <ppc/mappings.h>
   52 #include <ppc/exception.h>
   53 #include <ppc/savearea.h>
   54 #include <ddb/db_output.h>
   55 
   56 
   57 extern struct   Saveanchor saveanchor;                                                  /* Aliged savearea anchor */
   58 struct Saveanchor backpocket;                                                                   /* Emergency saveareas */
   59 unsigned int    debsave0 = 0;                                                                   /* Debug flag */
   60 unsigned int    backchain = 0;                                                                  /* Debug flag */
   61 
   62 /*
   63  *              These routines keep track of exception save areas and keeps the count within specific limits.  If there are
   64  *              too few, more are allocated, too many, and they are released. This savearea is where the PCBs are
   65  *              stored.  They never span a page boundary and are referenced by both virtual and real addresses.
   66  *              Within the interrupt vectors, the real address is used because at that level, no exceptions
   67  *              can be tolerated.  Save areas can be dynamic or permanent.  Permanant saveareas are allocated
   68  *              at boot time and must be in place before any type of exception occurs.  These are never released,
   69  *              and the number is based upon some arbitrary (yet to be determined) amount times the number of
   70  *              processors.  This represents the minimum number required to process a total system failure without
   71  *              destroying valuable and ever-so-handy system debugging information.
   72  *
   73  *              We keep two global free lists (the savearea free pool and the savearea free list) and one local
   74  *              list per processor.
   75  *
   76  *              The local lists are small and require no locked access.  They are chained using physical addresses
   77  *              and no interruptions are allowed when adding to or removing from the list. Also known as the 
   78  *              qfret list. This list is local to a processor and is intended for use only by very low level
   79  *              context handling code. 
   80  *
   81  *              The savearea free list is a medium size list that is globally accessible.  It is updated
   82  *              while holding a simple lock. The length of time that the lock is held is kept short.  The
   83  *              longest period of time is when the list is trimmed. Like the qfret lists, this is chained physically
   84  *              and must be accessed with translation and interruptions disabled. This is where the bulk
   85  *              of the free entries are located.
   86  *
   87  *              The saveareas are allocated from full pages.  A pool element is marked
   88  *              with an allocation map that shows which "slots" are free.  These pages are allocated via the
   89  *              normal kernel memory allocation functions. Queueing is with physical addresses.  The enqueue,
   90  *              dequeue, and search for free blocks is done under free list lock.  
   91  *              only if there are empty slots in it.
   92  *
   93  *              Saveareas that are counted as "in use" once they are removed from the savearea free list.
   94  *              This means that all areas on the local qfret list are considered in use.
   95  *
   96  *              There are two methods of obtaining a savearea.  The save_get function (which is also inlined
   97  *              in the low-level exception handler) attempts to get an area from the local qfret list.  This is
   98  *              done completely without locks.  If qfret is exahusted (or maybe just too low) an area is allocated
   99  *              from the savearea free list. If the free list is empty, we install the back pocket areas and
  100  *              panic.
  101  *
  102  *              The save_alloc function is designed to be called by high level routines, e.g., thread creation,
  103  *              etc.  It will allocate from the free list.  After allocation, it will compare the free count
  104  *              to the target value.  If outside of the range, it will adjust the size either upwards or
  105  *              downwards.
  106  *
  107  *              If we need to shrink the list, it will be trimmed to the target size and unlocked.  The code
  108  *              will walk the chain and return each savearea to its pool page.  If a pool page becomes
  109  *              completely empty, it is dequeued from the free pool list and enqueued (atomic queue
  110  *              function) to be released.
  111  *
  112  *              Once the trim list is finished, the pool release queue is checked to see if there are pages
  113  *              waiting to be released. If so, they are released one at a time.
  114  *
  115  *              If the free list needed to be grown rather than shrunken, we will first attempt to recover
  116  *              a page from the pending release queue (built when we trim the free list).  If we find one,
  117  *              it is allocated, otherwise, a page of kernel memory is allocated.  This loops until there are
  118  *              enough free saveareas.
  119  *              
  120  */
  121 
  122 
  123 
  124 /*
  125  *              Allocate our initial context save areas.  As soon as we do this,
  126  *              we can take an interrupt. We do the saveareas here, 'cause they're guaranteed
  127  *              to be at least page aligned.
  128  *
  129  *              Note: these initial saveareas are all to be allocated from V=R, less than 4GB
  130  *              space.
  131  */
  132 
  133 
  134 void savearea_init(vm_offset_t addr) {
  135 
  136         savearea_comm   *savec;
  137         vm_offset_t     save;
  138         int i;
  139 
  140         
  141         saveanchor.savetarget   = InitialSaveTarget;            /* Initial target value */
  142         saveanchor.saveinuse    = 0;                                            /* Number of areas in use */
  143 
  144         saveanchor.savefree    = 0;                                                     /* Remember the start of the free chain */
  145         saveanchor.savefreecnt = 0;                                                     /* Remember the length */
  146         saveanchor.savepoolfwd = (addr64_t)&saveanchor;         /* Remember pool forward */
  147         saveanchor.savepoolbwd = (addr64_t)&saveanchor;         /* Remember pool backward */
  148 
  149         save =  addr;                                                                           /* Point to the whole block of blocks */        
  150 
  151 /*
  152  *      First we allocate the back pocket in case of emergencies
  153  */
  154 
  155 
  156         for(i=0; i < BackPocketSaveBloks; i++) {                        /* Initialize the back pocket saveareas */
  157 
  158                 savec = (savearea_comm *)save;                                  /* Get the control area for this one */
  159 
  160                 savec->sac_alloc = 0;                                                   /* Mark it allocated */
  161                 savec->sac_vrswap = 0;                                                  /* V=R, so the translation factor is 0 */
  162                 savec->sac_flags = sac_perm;                                    /* Mark it permanent */
  163                 savec->sac_flags |= 0x0000EE00;                                 /* Debug eyecatcher */
  164                 save_queue((uint32_t)savec >> 12);                              /* Add page to savearea lists */
  165                 save += PAGE_SIZE;                                                              /* Jump up to the next one now */
  166         
  167         }
  168 
  169         backpocket = saveanchor;                                                        /* Save this for emergencies */
  170 
  171 
  172 /*
  173  *      We've saved away the back pocket savearea info, so reset it all and
  174  *      now allocate for real
  175  */
  176 
  177 
  178         saveanchor.savefree = 0;                                                        /* Remember the start of the free chain */
  179         saveanchor.savefreecnt = 0;                                                     /* Remember the length */
  180         saveanchor.saveadjust = 0;                                                      /* Set none needed yet */
  181         saveanchor.savepoolfwd = (addr64_t)&saveanchor;         /* Remember pool forward */
  182         saveanchor.savepoolbwd = (addr64_t)&saveanchor;         /* Remember pool backward */
  183 
  184         for(i=0; i < InitialSaveBloks; i++) {                           /* Initialize the saveareas */
  185 
  186                 savec = (savearea_comm *)save;                                  /* Get the control area for this one */
  187 
  188                 savec->sac_alloc = 0;                                                   /* Mark it allocated */
  189                 savec->sac_vrswap = 0;                                                  /* V=R, so the translation factor is 0 */
  190                 savec->sac_flags = sac_perm;                                    /* Mark it permanent */
  191                 savec->sac_flags |= 0x0000EE00;                                 /* Debug eyecatcher */
  192                 save_queue((uint32_t)savec >> 12);                              /* Add page to savearea lists */
  193                 save += PAGE_SIZE;                                                              /* Jump up to the next one now */
  194         
  195         }
  196 
  197 /*
  198  *      We now have a free list that has our initial number of entries  
  199  *      The local qfret lists is empty.  When we call save_get below it will see that
  200  *      the local list is empty and fill it for us.
  201  *
  202  *      It is ok to call save_get here because all initial saveareas are V=R in less
  203  *  than 4GB space, so 32-bit addressing is ok.
  204  *
  205  */
  206 
  207 /*
  208  * This will populate the local list  and get the first one for the system
  209  */     
  210         per_proc_info[0].next_savearea = (vm_offset_t)save_get();
  211 
  212 /*
  213  *      The system is now able to take interruptions
  214  */
  215         return;
  216 }
  217 
  218 
  219 
  220 
  221 /*
  222  *              Obtains a savearea.  If the free list needs size adjustment it happens here.
  223  *              Don't actually allocate the savearea until after the adjustment is done.
  224  */
  225 
  226 struct savearea *save_alloc(void) {                                             /* Reserve a save area */
  227         
  228         
  229         if(saveanchor.saveadjust) save_adjust();                        /* If size need adjustment, do it now */
  230         
  231         return save_get();                                                                      /* Pass the baby... */
  232 }
  233 
  234 
  235 /*
  236  *              This routine releases a save area to the free queue.  If after that, we have more than our maximum target,
  237  *              we start releasing what we can until we hit the normal target. 
  238  */
  239 
  240 
  241 
  242 void save_release(struct savearea *save) {                              /* Release a save area */
  243         
  244         save_ret(save);                                                                         /* Return a savearea to the free list */
  245         
  246         if(saveanchor.saveadjust) save_adjust();                        /* Adjust the savearea free list and pool size if needed */
  247         
  248         return;
  249         
  250 }
  251 
  252 
  253 /*
  254  *              Adjusts the size of the free list.  Can either release or allocate full pages
  255  *              of kernel memory.  This can block.
  256  *
  257  *              Note that we will only run one adjustment and the amount needed may change
  258  *              while we are executing.
  259  *
  260  *              Calling this routine is triggered by saveanchor.saveadjust.  This value is always calculated just before
  261  *              we unlock the saveanchor lock (this keeps it pretty accurate).  If the total of savefreecnt and saveinuse
  262  *              is within the hysteresis range, it is set to 0.  If outside, it is set to the number needed to bring
  263  *              the total to the target value.  Note that there is a minimum size to the free list (FreeListMin) and if
  264  *              savefreecnt falls below that, saveadjust is set to the number needed to bring it to that.
  265  */
  266 
  267 
  268 void save_adjust(void) {
  269         
  270         savearea_comm   *sctl, *sctlnext, *freepage;
  271         kern_return_t ret;
  272         uint64_t vtopmask;
  273         ppnum_t physpage;
  274 
  275         if(saveanchor.saveadjust < 0)                                   {       /* Do we need to adjust down? */
  276                         
  277                 sctl = (savearea_comm *)save_trim_free();               /* Trim list to the need count, return start of trim list */
  278                                 
  279                 while(sctl) {                                                                   /* Release the free pages back to the kernel */
  280                         sctlnext = CAST_DOWN(savearea_comm *, sctl->save_prev); /* Get next in list */  
  281                         kmem_free(kernel_map, (vm_offset_t) sctl, PAGE_SIZE);   /* Release the page */
  282                         sctl = sctlnext;                                                        /* Chain onwards */
  283                 }
  284         }
  285         else {                                                                                          /* We need more... */
  286 
  287                 if(save_recover()) return;                                              /* If we can recover enough from the pool, return */
  288                 
  289                 while(saveanchor.saveadjust > 0) {                              /* Keep going until we have enough */
  290 
  291                         ret = kmem_alloc_wired(kernel_map, (vm_offset_t *)&freepage, PAGE_SIZE);        /* Get a page for free pool */
  292                         if(ret != KERN_SUCCESS) {                                       /* Did we get some memory? */
  293                                 panic("Whoops...  Not a bit of wired memory left for saveareas\n");
  294                         }
  295                         
  296                         physpage = pmap_find_phys(kernel_pmap, (vm_offset_t)freepage);  /* Find physical page */
  297                         if(!physpage) {                                                         /* See if we actually have this mapped*/
  298                                 panic("save_adjust: wired page not mapped - va = %08X\n", freepage);    /* Die */
  299                         }
  300                         
  301                         bzero((void *)freepage, PAGE_SIZE);                     /* Clear it all to zeros */
  302                         freepage->sac_alloc = 0;                                        /* Mark all entries taken */
  303                         freepage->sac_vrswap = ((uint64_t)physpage << 12) ^ (uint64_t)((uintptr_t)freepage);    /* XOR to calculate conversion mask */
  304         
  305                         freepage->sac_flags |= 0x0000EE00;                      /* Set debug eyecatcher */
  306                                                 
  307                         save_queue(physpage);                                           /* Add all saveareas on page to free list */
  308                 }
  309         }
  310 }
  311 
  312 /*
  313  *              Fake up information to make the saveareas look like a zone
  314  */
  315 
  316 save_fake_zone_info(int *count, vm_size_t *cur_size, vm_size_t *max_size, vm_size_t *elem_size,
  317                     vm_size_t *alloc_size, int *collectable, int *exhaustable)
  318 {
  319         *count      = saveanchor.saveinuse;
  320         *cur_size   = (saveanchor.savefreecnt + saveanchor.saveinuse) * (PAGE_SIZE / sac_cnt);
  321         *max_size   = saveanchor.savemaxcount * (PAGE_SIZE / sac_cnt);
  322         *elem_size  = sizeof(savearea);
  323         *alloc_size = PAGE_SIZE;
  324         *collectable = 1;
  325         *exhaustable = 0;
  326 }
  327 
  328 

Cache object: 40035888c8ae4712e098cfaa86bff19b


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