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/vm/vm_zone.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) 1997, 1998 John S. Dyson
    3  * 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 immediately at the beginning of the file, without modification,
   10  *      this list of conditions, and the following disclaimer.
   11  * 2. Absolutely no warranty of function or purpose is made by the author
   12  *      John S. Dyson.
   13  *
   14  * $FreeBSD$
   15  */
   16 
   17 #include <sys/param.h>
   18 #include <sys/systm.h>
   19 #include <sys/kernel.h>
   20 #include <sys/lock.h>
   21 #include <sys/malloc.h>
   22 #include <sys/sysctl.h>
   23 #include <sys/vmmeter.h>
   24 
   25 #include <vm/vm.h>
   26 #include <vm/vm_object.h>
   27 #include <vm/vm_page.h>
   28 #include <vm/vm_map.h>
   29 #include <vm/vm_kern.h>
   30 #include <vm/vm_extern.h>
   31 #include <vm/vm_zone.h>
   32 
   33 static MALLOC_DEFINE(M_ZONE, "ZONE", "Zone header");
   34 
   35 #define ZONE_ERROR_INVALID 0
   36 #define ZONE_ERROR_NOTFREE 1
   37 #define ZONE_ERROR_ALREADYFREE 2
   38 
   39 #define ZONE_ROUNDING   32
   40 
   41 #define ZENTRY_FREE     0x12342378
   42 /*
   43  * void *zalloc(vm_zone_t zone) --
   44  *      Returns an item from a specified zone.
   45  *
   46  * void zfree(vm_zone_t zone, void *item) --
   47  *  Frees an item back to a specified zone.
   48  */
   49 static __inline__ void *
   50 _zalloc(vm_zone_t z)
   51 {
   52         void *item;
   53 
   54 #ifdef INVARIANTS
   55         if (z == 0)
   56                 zerror(ZONE_ERROR_INVALID);
   57 #endif
   58 
   59         if (z->zfreecnt <= z->zfreemin) {
   60                 item = _zget(z);
   61                 /*
   62                  * PANICFAIL allows the caller to assume that the zalloc()
   63                  * will always succeed.  If it doesn't, we panic here.
   64                  */
   65                 if (item == NULL && (z->zflags & ZONE_PANICFAIL))
   66                         panic("zalloc(%s) failed", z->zname);
   67                 return(item);
   68         }
   69 
   70         item = z->zitems;
   71         z->zitems = ((void **) item)[0];
   72 #ifdef INVARIANTS
   73         KASSERT(item != NULL, ("zitems unexpectedly NULL"));
   74         if (((void **) item)[1] != (void *) ZENTRY_FREE)
   75                 zerror(ZONE_ERROR_NOTFREE);
   76         ((void **) item)[1] = 0;
   77 #endif
   78 
   79         z->zfreecnt--;
   80         z->znalloc++;
   81         return item;
   82 }
   83 
   84 static __inline__ void
   85 _zfree(vm_zone_t z, void *item)
   86 {
   87         ((void **) item)[0] = z->zitems;
   88 #ifdef INVARIANTS
   89         if (((void **) item)[1] == (void *) ZENTRY_FREE)
   90                 zerror(ZONE_ERROR_ALREADYFREE);
   91         ((void **) item)[1] = (void *) ZENTRY_FREE;
   92 #endif
   93         z->zitems = item;
   94         z->zfreecnt++;
   95 }
   96 
   97 /*
   98  * This file comprises a very simple zone allocator.  This is used
   99  * in lieu of the malloc allocator, where needed or more optimal.
  100  *
  101  * Note that the initial implementation of this had coloring, and
  102  * absolutely no improvement (actually perf degradation) occurred.
  103  *
  104  * Note also that the zones are type stable.  The only restriction is
  105  * that the first two longwords of a data structure can be changed
  106  * between allocations.  Any data that must be stable between allocations
  107  * must reside in areas after the first two longwords.
  108  *
  109  * zinitna, zinit, zbootinit are the initialization routines.
  110  * zalloc, zfree, are the interrupt/lock unsafe allocation/free routines.
  111  * zalloci, zfreei, are the interrupt/lock safe allocation/free routines.
  112  */
  113 
  114 static struct vm_zone *zlist;
  115 static int sysctl_vm_zone(SYSCTL_HANDLER_ARGS);
  116 static int zone_kmem_pages, zone_kern_pages, zone_kmem_kvaspace;
  117 
  118 /*
  119  * Create a zone, but don't allocate the zone structure.  If the
  120  * zone had been previously created by the zone boot code, initialize
  121  * various parts of the zone code.
  122  *
  123  * If waits are not allowed during allocation (e.g. during interrupt
  124  * code), a-priori allocate the kernel virtual space, and allocate
  125  * only pages when needed.
  126  *
  127  * Arguments:
  128  * z            pointer to zone structure.
  129  * obj          pointer to VM object (opt).
  130  * name         name of zone.
  131  * size         size of zone entries.
  132  * nentries     number of zone entries allocated (only ZONE_INTERRUPT.)
  133  * flags        ZONE_INTERRUPT -- items can be allocated at interrupt time.
  134  * zalloc       number of pages allocated when memory is needed.
  135  *
  136  * Note that when using ZONE_INTERRUPT, the size of the zone is limited
  137  * by the nentries argument.  The size of the memory allocatable is
  138  * unlimited if ZONE_INTERRUPT is not set.
  139  *
  140  */
  141 int
  142 zinitna(vm_zone_t z, vm_object_t obj, char *name, int size,
  143         int nentries, int flags, int zalloc)
  144 {
  145         int totsize;
  146 
  147         if ((z->zflags & ZONE_BOOT) == 0) {
  148                 z->zsize = (size + ZONE_ROUNDING - 1) & ~(ZONE_ROUNDING - 1);
  149                 simple_lock_init(&z->zlock);
  150                 z->zfreecnt = 0;
  151                 z->ztotal = 0;
  152                 z->zmax = 0;
  153                 z->zname = name;
  154                 z->znalloc = 0;
  155                 z->zitems = NULL;
  156 
  157                 z->znext = zlist;
  158                 zlist = z;
  159         }
  160 
  161         z->zflags |= flags;
  162 
  163         /*
  164          * If we cannot wait, allocate KVA space up front, and we will fill
  165          * in pages as needed.
  166          */
  167         if (z->zflags & ZONE_INTERRUPT) {
  168 
  169                 totsize = round_page(z->zsize * nentries);
  170                 zone_kmem_kvaspace += totsize;
  171 
  172                 z->zkva = kmem_alloc_pageable(kernel_map, totsize);
  173                 if (z->zkva == 0) {
  174                         zlist = z->znext;
  175                         return 0;
  176                 }
  177 
  178                 z->zpagemax = totsize / PAGE_SIZE;
  179                 if (obj == NULL) {
  180                         z->zobj = vm_object_allocate(OBJT_DEFAULT, z->zpagemax);
  181                 } else {
  182                         z->zobj = obj;
  183                         _vm_object_allocate(OBJT_DEFAULT, z->zpagemax, obj);
  184                 }
  185                 z->zallocflag = VM_ALLOC_INTERRUPT;
  186                 z->zmax += nentries;
  187         } else {
  188                 z->zallocflag = VM_ALLOC_SYSTEM;
  189                 z->zmax = 0;
  190         }
  191 
  192 
  193         if (z->zsize > PAGE_SIZE)
  194                 z->zfreemin = 1;
  195         else
  196                 z->zfreemin = PAGE_SIZE / z->zsize;
  197 
  198         z->zpagecount = 0;
  199         if (zalloc)
  200                 z->zalloc = zalloc;
  201         else
  202                 z->zalloc = 1;
  203 
  204         return 1;
  205 }
  206 
  207 /*
  208  * Subroutine same as zinitna, except zone data structure is allocated
  209  * automatically by malloc.  This routine should normally be used, except
  210  * in certain tricky startup conditions in the VM system -- then
  211  * zbootinit and zinitna can be used.  Zinit is the standard zone
  212  * initialization call.
  213  */
  214 vm_zone_t
  215 zinit(char *name, int size, int nentries, int flags, int zalloc)
  216 {
  217         vm_zone_t z;
  218 
  219         z = (vm_zone_t) malloc(sizeof (struct vm_zone), M_ZONE, M_NOWAIT);
  220         if (z == NULL)
  221                 return NULL;
  222 
  223         z->zflags = 0;
  224         if (zinitna(z, NULL, name, size, nentries, flags, zalloc) == 0) {
  225                 free(z, M_ZONE);
  226                 return NULL;
  227         }
  228 
  229         return z;
  230 }
  231 
  232 /*
  233  * Initialize a zone before the system is fully up.  This routine should
  234  * only be called before full VM startup.
  235  */
  236 void
  237 zbootinit(vm_zone_t z, char *name, int size, void *item, int nitems)
  238 {
  239         int i;
  240 
  241         z->zname = name;
  242         z->zsize = size;
  243         z->zpagemax = 0;
  244         z->zobj = NULL;
  245         z->zflags = ZONE_BOOT;
  246         z->zfreemin = 0;
  247         z->zallocflag = 0;
  248         z->zpagecount = 0;
  249         z->zalloc = 0;
  250         z->znalloc = 0;
  251         simple_lock_init(&z->zlock);
  252 
  253         bzero(item, nitems * z->zsize);
  254         z->zitems = NULL;
  255         for (i = 0; i < nitems; i++) {
  256                 ((void **) item)[0] = z->zitems;
  257 #ifdef INVARIANTS
  258                 ((void **) item)[1] = (void *) ZENTRY_FREE;
  259 #endif
  260                 z->zitems = item;
  261                 (char *) item += z->zsize;
  262         }
  263         z->zfreecnt = nitems;
  264         z->zmax = nitems;
  265         z->ztotal = nitems;
  266 
  267         if (zlist == 0) {
  268                 zlist = z;
  269         } else {
  270                 z->znext = zlist;
  271                 zlist = z;
  272         }
  273 }
  274 
  275 /*
  276  * Zone critical region locks.
  277  */
  278 static __inline int
  279 zlock(vm_zone_t z)
  280 {
  281         int s;
  282 
  283         s = splhigh();
  284         simple_lock(&z->zlock);
  285         return s;
  286 }
  287 
  288 static __inline void
  289 zunlock(vm_zone_t z, int s)
  290 {
  291         simple_unlock(&z->zlock);
  292         splx(s);
  293 }
  294 
  295 /*
  296  * void *zalloc(vm_zone_t zone) --
  297  *      Returns an item from a specified zone.
  298  *
  299  * void zfree(vm_zone_t zone, void *item) --
  300  *  Frees an item back to a specified zone.
  301  *
  302  * void *zalloci(vm_zone_t zone) --
  303  *      Returns an item from a specified zone, interrupt safe.
  304  *
  305  * void zfreei(vm_zone_t zone, void *item) --
  306  *  Frees an item back to a specified zone, interrupt safe.
  307  *
  308  */
  309 
  310 void *
  311 zalloc(vm_zone_t z)
  312 {
  313 #if defined(SMP)
  314         return zalloci(z);
  315 #else
  316         return _zalloc(z);
  317 #endif
  318 }
  319 
  320 void
  321 zfree(vm_zone_t z, void *item)
  322 {
  323 #ifdef SMP
  324         zfreei(z, item);
  325 #else
  326         _zfree(z, item);
  327 #endif
  328 }
  329  
  330 /*
  331  * Zone allocator/deallocator.  These are interrupt / (or potentially SMP)
  332  * safe.  The raw zalloc/zfree routines are not interrupt safe, but are fast.
  333  */
  334 void *
  335 zalloci(vm_zone_t z)
  336 {
  337         int s;
  338         void *item;
  339 
  340         s = zlock(z);
  341         item = _zalloc(z);
  342         zunlock(z, s);
  343         return item;
  344 }
  345 
  346 void
  347 zfreei(vm_zone_t z, void *item)
  348 {
  349         int s;
  350 
  351         s = zlock(z);
  352         _zfree(z, item);
  353         zunlock(z, s);
  354         return;
  355 }
  356 
  357 /*
  358  * Internal zone routine.  Not to be called from external (non vm_zone) code.
  359  */
  360 void *
  361 _zget(vm_zone_t z)
  362 {
  363         int i;
  364         vm_page_t m;
  365         int nitems, nbytes;
  366         void *item;
  367 
  368         if (z == NULL)
  369                 panic("zget: null zone");
  370 
  371         if (z->zflags & ZONE_INTERRUPT) {
  372                 nbytes = z->zpagecount * PAGE_SIZE;
  373                 nbytes -= nbytes % z->zsize;
  374                 item = (char *) z->zkva + nbytes;
  375                 for (i = 0; ((i < z->zalloc) && (z->zpagecount < z->zpagemax));
  376                      i++) {
  377                         vm_offset_t zkva;
  378 
  379                         m = vm_page_alloc(z->zobj, z->zpagecount,
  380                                           z->zallocflag);
  381                         if (m == NULL)
  382                                 break;
  383 
  384                         zkva = z->zkva + z->zpagecount * PAGE_SIZE;
  385                         pmap_kenter(zkva, VM_PAGE_TO_PHYS(m));
  386                         bzero((caddr_t) zkva, PAGE_SIZE);
  387                         z->zpagecount++;
  388                         zone_kmem_pages++;
  389                         cnt.v_wire_count++;
  390                 }
  391                 nitems = ((z->zpagecount * PAGE_SIZE) - nbytes) / z->zsize;
  392         } else {
  393                 nbytes = z->zalloc * PAGE_SIZE;
  394 
  395                 /*
  396                  * Check to see if the kernel map is already locked.  We could allow
  397                  * for recursive locks, but that eliminates a valuable debugging
  398                  * mechanism, and opens up the kernel map for potential corruption
  399                  * by inconsistent data structure manipulation.  We could also use
  400                  * the interrupt allocation mechanism, but that has size limitations.
  401                  * Luckily, we have kmem_map that is a submap of kernel map available
  402                  * for memory allocation, and manipulation of that map doesn't affect
  403                  * the kernel map structures themselves.
  404                  *
  405                  * We can wait, so just do normal map allocation in the appropriate
  406                  * map.
  407                  */
  408                 if (lockstatus(&kernel_map->lock, NULL)) {
  409                         int s;
  410                         s = splvm();
  411 #ifdef SMP
  412                         simple_unlock(&z->zlock);
  413 #endif
  414                         item = (void *) kmem_malloc(kmem_map, nbytes, M_WAITOK);
  415 #ifdef SMP
  416                         simple_lock(&z->zlock);
  417 #endif
  418                         if (item != NULL)
  419                                 zone_kmem_pages += z->zalloc;
  420                         splx(s);
  421                 } else {
  422 #ifdef SMP
  423                         simple_unlock(&z->zlock);
  424 #endif
  425                         item = (void *) kmem_alloc(kernel_map, nbytes);
  426 #ifdef SMP
  427                         simple_lock(&z->zlock);
  428 #endif
  429                         if (item != NULL)
  430                                 zone_kern_pages += z->zalloc;
  431                 }
  432                 if (item != NULL) {
  433                         bzero(item, nbytes);
  434                 } else {
  435                         nbytes = 0;
  436                 }
  437                 nitems = nbytes / z->zsize;
  438         }
  439         z->ztotal += nitems;
  440 
  441         /*
  442          * Save one for immediate allocation
  443          */
  444         if (nitems != 0) {
  445                 nitems -= 1;
  446                 for (i = 0; i < nitems; i++) {
  447                         ((void **) item)[0] = z->zitems;
  448 #ifdef INVARIANTS
  449                         ((void **) item)[1] = (void *) ZENTRY_FREE;
  450 #endif
  451                         z->zitems = item;
  452                         (char *) item += z->zsize;
  453                 }
  454                 z->zfreecnt += nitems;
  455                 z->znalloc++;
  456         } else if (z->zfreecnt > 0) {
  457                 item = z->zitems;
  458                 z->zitems = ((void **) item)[0];
  459 #ifdef INVARIANTS
  460                 if (((void **) item)[1] != (void *) ZENTRY_FREE)
  461                         zerror(ZONE_ERROR_NOTFREE);
  462                 ((void **) item)[1] = 0;
  463 #endif
  464                 z->zfreecnt--;
  465                 z->znalloc++;
  466         } else {
  467                 item = NULL;
  468         }
  469 
  470         return item;
  471 }
  472 
  473 static int
  474 sysctl_vm_zone(SYSCTL_HANDLER_ARGS)
  475 {
  476         int error=0;
  477         vm_zone_t curzone, nextzone;
  478         char tmpbuf[128];
  479         char tmpname[14];
  480 
  481         snprintf(tmpbuf, sizeof(tmpbuf),
  482             "\nITEM            SIZE     LIMIT    USED    FREE  REQUESTS\n");
  483         error = SYSCTL_OUT(req, tmpbuf, strlen(tmpbuf));
  484         if (error)
  485                 return (error);
  486 
  487         for (curzone = zlist; curzone; curzone = nextzone) {
  488                 int i;
  489                 int len;
  490                 int offset;
  491 
  492                 nextzone = curzone->znext;
  493                 len = strlen(curzone->zname);
  494                 if (len >= (sizeof(tmpname) - 1))
  495                         len = (sizeof(tmpname) - 1);
  496                 for(i = 0; i < sizeof(tmpname) - 1; i++)
  497                         tmpname[i] = ' ';
  498                 tmpname[i] = 0;
  499                 memcpy(tmpname, curzone->zname, len);
  500                 tmpname[len] = ':';
  501                 offset = 0;
  502                 if (curzone == zlist) {
  503                         offset = 1;
  504                         tmpbuf[0] = '\n';
  505                 }
  506 
  507                 snprintf(tmpbuf + offset, sizeof(tmpbuf) - offset,
  508                         "%s %6.6u, %8.8u, %6.6u, %6.6u, %8.8u\n",
  509                         tmpname, curzone->zsize, curzone->zmax,
  510                         (curzone->ztotal - curzone->zfreecnt),
  511                         curzone->zfreecnt, curzone->znalloc);
  512 
  513                 len = strlen((char *)tmpbuf);
  514                 if (nextzone == NULL)
  515                         tmpbuf[len - 1] = 0;
  516 
  517                 error = SYSCTL_OUT(req, tmpbuf, len);
  518 
  519                 if (error)
  520                         return (error);
  521         }
  522         return (0);
  523 }
  524 
  525 #ifdef INVARIANT_SUPPORT
  526 void
  527 zerror(int error)
  528 {
  529         char *msg;
  530 
  531         switch (error) {
  532         case ZONE_ERROR_INVALID:
  533                 msg = "zone: invalid zone";
  534                 break;
  535         case ZONE_ERROR_NOTFREE:
  536                 msg = "zone: entry not free";
  537                 break;
  538         case ZONE_ERROR_ALREADYFREE:
  539                 msg = "zone: freeing free entry";
  540                 break;
  541         default:
  542                 msg = "zone: invalid error";
  543                 break;
  544         }
  545         panic(msg);
  546 }
  547 #endif
  548 
  549 SYSCTL_OID(_vm, OID_AUTO, zone, CTLTYPE_STRING|CTLFLAG_RD, \
  550         NULL, 0, sysctl_vm_zone, "A", "Zone Info");
  551 
  552 SYSCTL_INT(_vm, OID_AUTO, zone_kmem_pages,
  553         CTLFLAG_RD, &zone_kmem_pages, 0, "Number of interrupt safe pages allocated by zone");
  554 SYSCTL_INT(_vm, OID_AUTO, zone_kmem_kvaspace,
  555         CTLFLAG_RD, &zone_kmem_kvaspace, 0, "KVA space allocated by zone");
  556 SYSCTL_INT(_vm, OID_AUTO, zone_kern_pages,
  557         CTLFLAG_RD, &zone_kern_pages, 0, "Number of non-interrupt safe pages allocated by zone");

Cache object: e21f58ea437cc07c80c1909a9a0f4732


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