Subversion Repositories HelenOS

Rev

Rev 3185 | Rev 3188 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Copyright (c) 2001-2005 Jakub Jermar
  3.  * Copyright (c) 2005 Sergey Bondari
  4.  * All rights reserved.
  5.  *
  6.  * Redistribution and use in source and binary forms, with or without
  7.  * modification, are permitted provided that the following conditions
  8.  * are met:
  9.  *
  10.  * - Redistributions of source code must retain the above copyright
  11.  *   notice, this list of conditions and the following disclaimer.
  12.  * - Redistributions in binary form must reproduce the above copyright
  13.  *   notice, this list of conditions and the following disclaimer in the
  14.  *   documentation and/or other materials provided with the distribution.
  15.  * - The name of the author may not be used to endorse or promote products
  16.  *   derived from this software without specific prior written permission.
  17.  *
  18.  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  19.  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  20.  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  21.  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  22.  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  23.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  24.  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  25.  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  26.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  27.  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28.  */
  29.  
  30. /** @addtogroup genericmm
  31.  * @{
  32.  */
  33.  
  34. /**
  35.  * @file
  36.  * @brief   Physical frame allocator.
  37.  *
  38.  * This file contains the physical frame allocator and memory zone management.
  39.  * The frame allocator is built on top of the buddy allocator.
  40.  *
  41.  * @see buddy.c
  42.  */
  43.  
  44. /*
  45.  * Locking order
  46.  *
  47.  * In order to access particular zone, the process must first lock
  48.  * the zones.lock, then lock the zone and then unlock the zones.lock.
  49.  * This insures, that we can fiddle with the zones in runtime without
  50.  * affecting the processes.
  51.  *
  52.  */
  53.  
  54. #include <arch/types.h>
  55. #include <mm/frame.h>
  56. #include <mm/as.h>
  57. #include <panic.h>
  58. #include <debug.h>
  59. #include <adt/list.h>
  60. #include <synch/spinlock.h>
  61. #include <synch/mutex.h>
  62. #include <synch/condvar.h>
  63. #include <arch/asm.h>
  64. #include <arch.h>
  65. #include <print.h>
  66. #include <align.h>
  67. #include <mm/slab.h>
  68. #include <bitops.h>
  69. #include <macros.h>
  70. #include <config.h>
  71.  
  72. typedef struct {
  73.     count_t refcount;   /**< tracking of shared frames  */
  74.     uint8_t buddy_order;    /**< buddy system block order */
  75.     link_t buddy_link;  /**< link to the next free block inside one
  76.                      order */
  77.     void *parent;           /**< If allocated by slab, this points there */
  78. } frame_t;
  79.  
  80. typedef struct {
  81.     SPINLOCK_DECLARE(lock); /**< this lock protects everything below */
  82.     pfn_t base;     /**< frame_no of the first frame in the frames
  83.                      array */
  84.     count_t count;          /**< Size of zone */
  85.  
  86.     frame_t *frames;    /**< array of frame_t structures in this
  87.                      zone */
  88.     count_t free_count; /**< number of free frame_t structures */
  89.     count_t busy_count; /**< number of busy frame_t structures */
  90.    
  91.     buddy_system_t *buddy_system; /**< buddy system for the zone */
  92.     int flags;
  93. } zone_t;
  94.  
  95. /*
  96.  * The zoneinfo.lock must be locked when accessing zoneinfo structure.
  97.  * Some of the attributes in zone_t structures are 'read-only'
  98.  */
  99.  
  100. typedef struct {
  101.     SPINLOCK_DECLARE(lock);
  102.     unsigned int count;
  103.     zone_t *info[ZONES_MAX];
  104. } zones_t;
  105.  
  106. static zones_t zones;
  107.  
  108. /*
  109.  * Synchronization primitives used to sleep when there is no memory
  110.  * available.
  111.  */
  112. mutex_t zones_mtx;
  113. condvar_t zones_cv;
  114. int new_freed_mem = false;
  115.  
  116. /********************/
  117. /* Helper functions */
  118. /********************/
  119.  
  120. static inline index_t frame_index(zone_t *zone, frame_t *frame)
  121. {
  122.     return (index_t) (frame - zone->frames);
  123. }
  124.  
  125. static inline index_t frame_index_abs(zone_t *zone, frame_t *frame)
  126. {
  127.     return (index_t) (frame - zone->frames) + zone->base;
  128. }
  129.  
  130. static inline int frame_index_valid(zone_t *zone, index_t index)
  131. {
  132.     return (index < zone->count);
  133. }
  134.  
  135. /** Compute pfn_t from frame_t pointer & zone pointer */
  136. static index_t make_frame_index(zone_t *zone, frame_t *frame)
  137. {
  138.     return (frame - zone->frames);
  139. }
  140.  
  141. /** Initialize frame structure.
  142.  *
  143.  * @param framei    Frame structure to be initialized.
  144.  */
  145. static void frame_initialize(frame_t *frame)
  146. {
  147.     frame->refcount = 1;
  148.     frame->buddy_order = 0;
  149. }
  150.  
  151. /**********************/
  152. /* Zoneinfo functions */
  153. /**********************/
  154.  
  155. /** Insert-sort zone into zones list.
  156.  *
  157.  * @param newzone   New zone to be inserted into zone list.
  158.  * @return      Zone number on success, -1 on error.
  159.  */
  160. static int zones_add_zone(zone_t *newzone)
  161. {
  162.     unsigned int i, j;
  163.     ipl_t ipl;
  164.     zone_t *z;
  165.  
  166.     ipl = interrupts_disable();
  167.     spinlock_lock(&zones.lock);
  168.    
  169.     /* Try to merge */
  170.     if (zones.count + 1 == ZONES_MAX) {
  171.         printf("Maximum zone count %u exceeded!\n", ZONES_MAX);
  172.         spinlock_unlock(&zones.lock);
  173.         interrupts_restore(ipl);
  174.         return -1;
  175.     }
  176.    
  177.     for (i = 0; i < zones.count; i++) {
  178.         /* Check for overflow */
  179.         z = zones.info[i];
  180.         if (overlaps(newzone->base, newzone->count, z->base,
  181.             z->count)) {
  182.             printf("Zones overlap!\n");
  183.             return -1;
  184.         }
  185.         if (newzone->base < z->base)
  186.             break;
  187.     }
  188.    
  189.     /* Move other zones up */
  190.     for (j = i; j < zones.count; j++)
  191.         zones.info[j + 1] = zones.info[j];
  192.    
  193.     zones.info[i] = newzone;
  194.     zones.count++;
  195.    
  196.     spinlock_unlock(&zones.lock);
  197.     interrupts_restore(ipl);
  198.  
  199.     return i;
  200. }
  201.  
  202. /**
  203.  * Try to find a zone where can we find the frame.
  204.  *
  205.  * Assume interrupts are disabled.
  206.  *
  207.  * @param frame     Frame number contained in zone.
  208.  * @param pzone     If not null, it is used as zone hint. Zone index is
  209.  *          filled into the variable on success.
  210.  * @return      Pointer to locked zone containing frame.
  211.  */
  212. static zone_t *find_zone_and_lock(pfn_t frame, unsigned int *pzone)
  213. {
  214.     unsigned int i;
  215.     unsigned int hint = pzone ? *pzone : 0;
  216.     zone_t *z;
  217.    
  218.     spinlock_lock(&zones.lock);
  219.  
  220.     if (hint >= zones.count)
  221.         hint = 0;
  222.    
  223.     i = hint;
  224.     do {
  225.         z = zones.info[i];
  226.         spinlock_lock(&z->lock);
  227.         if (z->base <= frame && z->base + z->count > frame) {
  228.             /* Unlock the global lock */
  229.             spinlock_unlock(&zones.lock);
  230.             if (pzone)
  231.                 *pzone = i;
  232.             return z;
  233.         }
  234.         spinlock_unlock(&z->lock);
  235.  
  236.         i++;
  237.         if (i >= zones.count)
  238.             i = 0;
  239.     } while(i != hint);
  240.  
  241.     spinlock_unlock(&zones.lock);
  242.     return NULL;
  243. }
  244.  
  245. /** @return True if zone can allocate specified order */
  246. static int zone_can_alloc(zone_t *z, uint8_t order)
  247. {
  248.     return buddy_system_can_alloc(z->buddy_system, order);
  249. }
  250.  
  251. /** Find and lock zone that can allocate order frames.
  252.  *
  253.  * Assume interrupts are disabled.
  254.  *
  255.  * @param order     Size (2^order) of free space we are trying to find.
  256.  * @param pzone     Pointer to preferred zone or NULL, on return contains
  257.  *          zone number.
  258.  */
  259. static zone_t *find_free_zone_and_lock(uint8_t order, unsigned int *pzone)
  260. {
  261.     unsigned int i;
  262.     zone_t *z;
  263.     unsigned int hint = pzone ? *pzone : 0;
  264.    
  265.     spinlock_lock(&zones.lock);
  266.     if (hint >= zones.count)
  267.         hint = 0;
  268.     i = hint;
  269.     do {
  270.         z = zones.info[i];
  271.        
  272.         spinlock_lock(&z->lock);
  273.  
  274.         /* Check if the zone has 2^order frames area available  */
  275.         if (zone_can_alloc(z, order)) {
  276.             spinlock_unlock(&zones.lock);
  277.             if (pzone)
  278.                 *pzone = i;
  279.             return z;
  280.         }
  281.         spinlock_unlock(&z->lock);
  282.         if (++i >= zones.count)
  283.             i = 0;
  284.     } while(i != hint);
  285.     spinlock_unlock(&zones.lock);
  286.     return NULL;
  287. }
  288.  
  289. /**************************/
  290. /* Buddy system functions */
  291. /**************************/
  292.  
  293. /** Buddy system find_block implementation.
  294.  *
  295.  * Find block that is parent of current list.
  296.  * That means go to lower addresses, until such block is found
  297.  *
  298.  * @param order     Order of parent must be different then this
  299.  *          parameter!!
  300.  */
  301. static link_t *zone_buddy_find_block(buddy_system_t *b, link_t *child,
  302.     uint8_t order)
  303. {
  304.     frame_t *frame;
  305.     zone_t *zone;
  306.     index_t index;
  307.    
  308.     frame = list_get_instance(child, frame_t, buddy_link);
  309.     zone = (zone_t *) b->data;
  310.  
  311.     index = frame_index(zone, frame);
  312.     do {
  313.         if (zone->frames[index].buddy_order != order) {
  314.             return &zone->frames[index].buddy_link;
  315.         }
  316.     } while(index-- > 0);
  317.     return NULL;
  318. }
  319.  
  320. static void zone_buddy_print_id(buddy_system_t *b, link_t *block)
  321. {
  322.     frame_t *frame;
  323.     zone_t *zone;
  324.     index_t index;
  325.  
  326.     frame = list_get_instance(block, frame_t, buddy_link);
  327.     zone = (zone_t *) b->data;
  328.     index = frame_index(zone, frame);
  329.     printf("%" PRIi, index);
  330. }                    
  331.  
  332. /** Buddy system find_buddy implementation.
  333.  *
  334.  * @param b     Buddy system.
  335.  * @param block     Block for which buddy should be found.
  336.  *
  337.  * @return      Buddy for given block if found.
  338.  */
  339. static link_t *zone_buddy_find_buddy(buddy_system_t *b, link_t *block)
  340. {
  341.     frame_t *frame;
  342.     zone_t *zone;
  343.     index_t index;
  344.     bool is_left, is_right;
  345.  
  346.     frame = list_get_instance(block, frame_t, buddy_link);
  347.     zone = (zone_t *) b->data;
  348.     ASSERT(IS_BUDDY_ORDER_OK(frame_index_abs(zone, frame),
  349.         frame->buddy_order));
  350.    
  351.     is_left = IS_BUDDY_LEFT_BLOCK_ABS(zone, frame);
  352.     is_right = IS_BUDDY_RIGHT_BLOCK_ABS(zone, frame);
  353.  
  354.     ASSERT(is_left ^ is_right);
  355.     if (is_left) {
  356.         index = (frame_index(zone, frame)) +
  357.             (1 << frame->buddy_order);
  358.     } else {    /* if (is_right) */
  359.         index = (frame_index(zone, frame)) -
  360.             (1 << frame->buddy_order);
  361.     }
  362.    
  363.     if (frame_index_valid(zone, index)) {
  364.         if (zone->frames[index].buddy_order == frame->buddy_order &&
  365.             zone->frames[index].refcount == 0) {
  366.             return &zone->frames[index].buddy_link;
  367.         }
  368.     }
  369.  
  370.     return NULL;   
  371. }
  372.  
  373. /** Buddy system bisect implementation.
  374.  *
  375.  * @param b     Buddy system.
  376.  * @param block     Block to bisect.
  377.  *
  378.  * @return      Right block.
  379.  */
  380. static link_t *zone_buddy_bisect(buddy_system_t *b, link_t *block)
  381. {
  382.     frame_t *frame_l, *frame_r;
  383.  
  384.     frame_l = list_get_instance(block, frame_t, buddy_link);
  385.     frame_r = (frame_l + (1 << (frame_l->buddy_order - 1)));
  386.    
  387.     return &frame_r->buddy_link;
  388. }
  389.  
  390. /** Buddy system coalesce implementation.
  391.  *
  392.  * @param b     Buddy system.
  393.  * @param block_1   First block.
  394.  * @param block_2   First block's buddy.
  395.  *
  396.  * @return      Coalesced block (actually block that represents lower
  397.  *          address).
  398.  */
  399. static link_t *zone_buddy_coalesce(buddy_system_t *b, link_t *block_1,
  400.     link_t *block_2)
  401. {
  402.     frame_t *frame1, *frame2;
  403.    
  404.     frame1 = list_get_instance(block_1, frame_t, buddy_link);
  405.     frame2 = list_get_instance(block_2, frame_t, buddy_link);
  406.    
  407.     return frame1 < frame2 ? block_1 : block_2;
  408. }
  409.  
  410. /** Buddy system set_order implementation.
  411.  *
  412.  * @param b     Buddy system.
  413.  * @param block     Buddy system block.
  414.  * @param order     Order to set.
  415.  */
  416. static void zone_buddy_set_order(buddy_system_t *b, link_t *block,
  417.     uint8_t order)
  418. {
  419.     frame_t *frame;
  420.     frame = list_get_instance(block, frame_t, buddy_link);
  421.     frame->buddy_order = order;
  422. }
  423.  
  424. /** Buddy system get_order implementation.
  425.  *
  426.  * @param b     Buddy system.
  427.  * @param block     Buddy system block.
  428.  *
  429.  * @return      Order of block.
  430.  */
  431. static uint8_t zone_buddy_get_order(buddy_system_t *b, link_t *block)
  432. {
  433.     frame_t *frame;
  434.     frame = list_get_instance(block, frame_t, buddy_link);
  435.     return frame->buddy_order;
  436. }
  437.  
  438. /** Buddy system mark_busy implementation.
  439.  *
  440.  * @param b     Buddy system.
  441.  * @param block     Buddy system block.
  442.  */
  443. static void zone_buddy_mark_busy(buddy_system_t *b, link_t * block)
  444. {
  445.     frame_t * frame;
  446.  
  447.     frame = list_get_instance(block, frame_t, buddy_link);
  448.     frame->refcount = 1;
  449. }
  450.  
  451. /** Buddy system mark_available implementation.
  452.  *
  453.  * @param b     Buddy system.
  454.  * @param block     Buddy system block.
  455.  */
  456. static void zone_buddy_mark_available(buddy_system_t *b, link_t *block)
  457. {
  458.     frame_t *frame;
  459.     frame = list_get_instance(block, frame_t, buddy_link);
  460.     frame->refcount = 0;
  461. }
  462.  
  463. static buddy_system_operations_t zone_buddy_system_operations = {
  464.     .find_buddy = zone_buddy_find_buddy,
  465.     .bisect = zone_buddy_bisect,
  466.     .coalesce = zone_buddy_coalesce,
  467.     .set_order = zone_buddy_set_order,
  468.     .get_order = zone_buddy_get_order,
  469.     .mark_busy = zone_buddy_mark_busy,
  470.     .mark_available = zone_buddy_mark_available,
  471.     .find_block = zone_buddy_find_block,
  472.     .print_id = zone_buddy_print_id
  473. };
  474.  
  475. /******************/
  476. /* Zone functions */
  477. /******************/
  478.  
  479. /** Allocate frame in particular zone.
  480.  *
  481.  * Assume zone is locked.
  482.  * Panics if allocation is impossible.
  483.  *
  484.  * @param zone      Zone to allocate from.
  485.  * @param order     Allocate exactly 2^order frames.
  486.  *
  487.  * @return      Frame index in zone.
  488.  *
  489.  */
  490. static pfn_t zone_frame_alloc(zone_t *zone, uint8_t order)
  491. {
  492.     pfn_t v;
  493.     link_t *tmp;
  494.     frame_t *frame;
  495.  
  496.     /* Allocate frames from zone buddy system */
  497.     tmp = buddy_system_alloc(zone->buddy_system, order);
  498.    
  499.     ASSERT(tmp);
  500.    
  501.     /* Update zone information. */
  502.     zone->free_count -= (1 << order);
  503.     zone->busy_count += (1 << order);
  504.  
  505.     /* Frame will be actually a first frame of the block. */
  506.     frame = list_get_instance(tmp, frame_t, buddy_link);
  507.    
  508.     /* get frame address */
  509.     v = make_frame_index(zone, frame);
  510.     return v;
  511. }
  512.  
  513. /** Free frame from zone.
  514.  *
  515.  * Assume zone is locked.
  516.  *
  517.  * @param zone      Pointer to zone from which the frame is to be freed.
  518.  * @param frame_idx Frame index relative to zone.
  519.  */
  520. static void zone_frame_free(zone_t *zone, index_t frame_idx)
  521. {
  522.     frame_t *frame;
  523.     uint8_t order;
  524.  
  525.     frame = &zone->frames[frame_idx];
  526.    
  527.     /* remember frame order */
  528.     order = frame->buddy_order;
  529.  
  530.     ASSERT(frame->refcount);
  531.  
  532.     if (!--frame->refcount) {
  533.         buddy_system_free(zone->buddy_system, &frame->buddy_link);
  534.    
  535.         /* Update zone information. */
  536.         zone->free_count += (1 << order);
  537.         zone->busy_count -= (1 << order);
  538.     }
  539. }
  540.  
  541. /** Return frame from zone. */
  542. static frame_t * zone_get_frame(zone_t *zone, index_t frame_idx)
  543. {
  544.     ASSERT(frame_idx < zone->count);
  545.     return &zone->frames[frame_idx];
  546. }
  547.  
  548. /** Mark frame in zone unavailable to allocation. */
  549. static void zone_mark_unavailable(zone_t *zone, index_t frame_idx)
  550. {
  551.     frame_t *frame;
  552.     link_t *link;
  553.  
  554.     frame = zone_get_frame(zone, frame_idx);
  555.     if (frame->refcount)
  556.         return;
  557.     link = buddy_system_alloc_block(zone->buddy_system,
  558.         &frame->buddy_link);
  559.     ASSERT(link);
  560.     zone->free_count--;
  561. }
  562.  
  563. /** Join two zones.
  564.  *
  565.  * Expect zone_t *z to point to space at least zone_conf_size large.
  566.  *
  567.  * Assume z1 & z2 are locked.
  568.  *
  569.  * @param z     Target zone structure pointer.
  570.  * @param z1        Zone to merge.
  571.  * @param z2        Zone to merge.
  572.  */
  573. static void _zone_merge(zone_t *z, zone_t *z1, zone_t *z2)
  574. {
  575.     uint8_t max_order;
  576.     unsigned int i;
  577.     int z2idx;
  578.     pfn_t frame_idx;
  579.     frame_t *frame;
  580.  
  581.     ASSERT(!overlaps(z1->base, z1->count, z2->base, z2->count));
  582.     ASSERT(z1->base < z2->base);
  583.  
  584.     spinlock_initialize(&z->lock, "zone_lock");
  585.     z->base = z1->base;
  586.     z->count = z2->base + z2->count - z1->base;
  587.     z->flags = z1->flags & z2->flags;
  588.  
  589.     z->free_count = z1->free_count + z2->free_count;
  590.     z->busy_count = z1->busy_count + z2->busy_count;
  591.    
  592.     max_order = fnzb(z->count);
  593.  
  594.     z->buddy_system = (buddy_system_t *) &z[1];
  595.     buddy_system_create(z->buddy_system, max_order,
  596.         &zone_buddy_system_operations, (void *) z);
  597.  
  598.     z->frames = (frame_t *)((uint8_t *) z->buddy_system +
  599.         buddy_conf_size(max_order));
  600.     for (i = 0; i < z->count; i++) {
  601.         /* This marks all frames busy */
  602.         frame_initialize(&z->frames[i]);
  603.     }
  604.     /* Copy frames from both zones to preserve full frame orders,
  605.      * parents etc. Set all free frames with refcount=0 to 1, because
  606.      * we add all free frames to buddy allocator later again, clear
  607.      * order to 0. Don't set busy frames with refcount=0, as they
  608.      * will not be reallocated during merge and it would make later
  609.      * problems with allocation/free.
  610.      */
  611.     for (i = 0; i < z1->count; i++)
  612.         z->frames[i] = z1->frames[i];
  613.     for (i = 0; i < z2->count; i++) {
  614.         z2idx = i + (z2->base - z1->base);
  615.         z->frames[z2idx] = z2->frames[i];
  616.     }
  617.     i = 0;
  618.     while (i < z->count) {
  619.         if (z->frames[i].refcount) {
  620.             /* skip busy frames */
  621.             i += 1 << z->frames[i].buddy_order;
  622.         } else { /* Free frames, set refcount=1 */
  623.             /* All free frames have refcount=0, we need not
  624.              * to check the order */
  625.             z->frames[i].refcount = 1;
  626.             z->frames[i].buddy_order = 0;
  627.             i++;
  628.         }
  629.     }
  630.     /* Add free blocks from the 2 original zones */
  631.     while (zone_can_alloc(z1, 0)) {
  632.         frame_idx = zone_frame_alloc(z1, 0);
  633.         frame = &z->frames[frame_idx];
  634.         frame->refcount = 0;
  635.         buddy_system_free(z->buddy_system, &frame->buddy_link);
  636.     }
  637.     while (zone_can_alloc(z2, 0)) {
  638.         frame_idx = zone_frame_alloc(z2, 0);
  639.         frame = &z->frames[frame_idx + (z2->base - z1->base)];
  640.         frame->refcount = 0;
  641.         buddy_system_free(z->buddy_system, &frame->buddy_link);
  642.     }
  643. }
  644.  
  645. /** Return old configuration frames into the zone.
  646.  *
  647.  * We have several cases
  648.  * - the conf. data is outside of zone -> exit, shall we call frame_free??
  649.  * - the conf. data was created by zone_create or
  650.  *   updated with reduce_region -> free every frame
  651.  *
  652.  * @param newzone   The actual zone where freeing should occur.
  653.  * @param oldzone   Pointer to old zone configuration data that should
  654.  *          be freed from new zone.
  655.  */
  656. static void return_config_frames(zone_t *newzone, zone_t *oldzone)
  657. {
  658.     pfn_t pfn;
  659.     frame_t *frame;
  660.     count_t cframes;
  661.     unsigned int i;
  662.  
  663.     pfn = ADDR2PFN((uintptr_t)KA2PA(oldzone));
  664.     cframes = SIZE2FRAMES(zone_conf_size(oldzone->count));
  665.    
  666.     if (pfn < newzone->base || pfn >= newzone->base + newzone->count)
  667.         return;
  668.  
  669.     frame = &newzone->frames[pfn - newzone->base];
  670.     ASSERT(!frame->buddy_order);
  671.  
  672.     for (i = 0; i < cframes; i++) {
  673.         newzone->busy_count++;
  674.         zone_frame_free(newzone, pfn+i-newzone->base);
  675.     }
  676. }
  677.  
  678. /** Reduce allocated block to count of order 0 frames.
  679.  *
  680.  * The allocated block need 2^order frames of space. Reduce all frames
  681.  * in block to order 0 and free the unneeded frames. This means, that
  682.  * when freeing the previously allocated block starting with frame_idx,
  683.  * you have to free every frame.
  684.  *
  685.  * @param zone
  686.  * @param frame_idx     Index to block.
  687.  * @param count         Allocated space in block.
  688.  */
  689. static void zone_reduce_region(zone_t *zone, pfn_t frame_idx, count_t count)
  690. {
  691.     count_t i;
  692.     uint8_t order;
  693.     frame_t *frame;
  694.    
  695.     ASSERT(frame_idx + count < zone->count);
  696.  
  697.     order = zone->frames[frame_idx].buddy_order;
  698.     ASSERT((count_t) (1 << order) >= count);
  699.  
  700.     /* Reduce all blocks to order 0 */
  701.     for (i = 0; i < (count_t) (1 << order); i++) {
  702.         frame = &zone->frames[i + frame_idx];
  703.         frame->buddy_order = 0;
  704.         if (!frame->refcount)
  705.             frame->refcount = 1;
  706.         ASSERT(frame->refcount == 1);
  707.     }
  708.     /* Free unneeded frames */
  709.     for (i = count; i < (count_t) (1 << order); i++) {
  710.         zone_frame_free(zone, i + frame_idx);
  711.     }
  712. }
  713.  
  714. /** Merge zones z1 and z2.
  715.  *
  716.  * - the zones must be 2 zones with no zone existing in between,
  717.  *   which means that z2 = z1+1
  718.  *
  719.  * - When you create a new zone, the frame allocator configuration does
  720.  *   not to be 2^order size. Once the allocator is running it is no longer
  721.  *   possible, merged configuration data occupies more space :-/
  722.  */
  723. void zone_merge(unsigned int z1, unsigned int z2)
  724. {
  725.     ipl_t ipl;
  726.     zone_t *zone1, *zone2, *newzone;
  727.     unsigned int cframes;
  728.     uint8_t order;
  729.     unsigned int i;
  730.     pfn_t pfn;
  731.  
  732.     ipl = interrupts_disable();
  733.     spinlock_lock(&zones.lock);
  734.  
  735.     if ((z1 >= zones.count) || (z2 >= zones.count))
  736.         goto errout;
  737.     /* We can join only 2 zones with none existing inbetween */
  738.     if (z2-z1 != 1)
  739.         goto errout;
  740.  
  741.     zone1 = zones.info[z1];
  742.     zone2 = zones.info[z2];
  743.     spinlock_lock(&zone1->lock);
  744.     spinlock_lock(&zone2->lock);
  745.  
  746.     cframes = SIZE2FRAMES(zone_conf_size(zone2->base + zone2->count -
  747.         zone1->base));
  748.     if (cframes == 1)
  749.         order = 0;
  750.     else
  751.         order = fnzb(cframes - 1) + 1;
  752.  
  753.     /* Allocate zonedata inside one of the zones */
  754.     if (zone_can_alloc(zone1, order))
  755.         pfn = zone1->base + zone_frame_alloc(zone1, order);
  756.     else if (zone_can_alloc(zone2, order))
  757.         pfn = zone2->base + zone_frame_alloc(zone2, order);
  758.     else
  759.         goto errout2;
  760.  
  761.     newzone = (zone_t *) PA2KA(PFN2ADDR(pfn));
  762.  
  763.     _zone_merge(newzone, zone1, zone2);
  764.  
  765.     /* Free unneeded config frames */
  766.     zone_reduce_region(newzone, pfn - newzone->base,  cframes);
  767.     /* Subtract zone information from busy frames */
  768.     newzone->busy_count -= cframes;
  769.  
  770.     /* Replace existing zones in zoneinfo list */
  771.     zones.info[z1] = newzone;
  772.     for (i = z2 + 1; i < zones.count; i++)
  773.         zones.info[i - 1] = zones.info[i];
  774.     zones.count--;
  775.  
  776.     /* Free old zone information */
  777.     return_config_frames(newzone, zone1);
  778.     return_config_frames(newzone, zone2);
  779. errout2:
  780.     /* Nobody is allowed to enter to zone, so we are safe
  781.      * to touch the spinlocks last time */
  782.     spinlock_unlock(&zone1->lock);
  783.     spinlock_unlock(&zone2->lock);
  784. errout:
  785.     spinlock_unlock(&zones.lock);
  786.     interrupts_restore(ipl);
  787. }
  788.  
  789. /** Merge all zones into one big zone.
  790.  *
  791.  * It is reasonable to do this on systems whose bios reports parts in chunks,
  792.  * so that we could have 1 zone (it's faster).
  793.  */
  794. void zone_merge_all(void)
  795. {
  796.     int count = zones.count;
  797.  
  798.     while (zones.count > 1 && --count) {
  799.         zone_merge(0,1);
  800.         break;
  801.     }
  802. }
  803.  
  804. /** Create new frame zone.
  805.  *
  806.  * @param start     Physical address of the first frame within the zone.
  807.  * @param count     Count of frames in zone.
  808.  * @param z     Address of configuration information of zone.
  809.  * @param flags     Zone flags.
  810.  *
  811.  * @return      Initialized zone.
  812.  */
  813. static void zone_construct(pfn_t start, count_t count, zone_t *z, int flags)
  814. {
  815.     unsigned int i;
  816.     uint8_t max_order;
  817.  
  818.     spinlock_initialize(&z->lock, "zone_lock");
  819.     z->base = start;
  820.     z->count = count;
  821.     z->flags = flags;
  822.     z->free_count = count;
  823.     z->busy_count = 0;
  824.  
  825.     /*
  826.      * Compute order for buddy system, initialize
  827.      */
  828.     max_order = fnzb(count);
  829.     z->buddy_system = (buddy_system_t *)&z[1];
  830.    
  831.     buddy_system_create(z->buddy_system, max_order,
  832.         &zone_buddy_system_operations, (void *) z);
  833.    
  834.     /* Allocate frames _after_ the conframe */
  835.     /* Check sizes */
  836.     z->frames = (frame_t *)((uint8_t *) z->buddy_system +
  837.         buddy_conf_size(max_order));
  838.     for (i = 0; i < count; i++) {
  839.         frame_initialize(&z->frames[i]);
  840.     }
  841.    
  842.     /* Stuffing frames */
  843.     for (i = 0; i < count; i++) {
  844.         z->frames[i].refcount = 0;
  845.         buddy_system_free(z->buddy_system, &z->frames[i].buddy_link);
  846.     }
  847. }
  848.  
  849. /** Compute configuration data size for zone.
  850.  *
  851.  * @param count     Size of zone in frames.
  852.  * @return      Size of zone configuration info (in bytes).
  853.  */
  854. uintptr_t zone_conf_size(count_t count)
  855. {
  856.     int size = sizeof(zone_t) + count * sizeof(frame_t);
  857.     int max_order;
  858.  
  859.     max_order = fnzb(count);
  860.     size += buddy_conf_size(max_order);
  861.     return size;
  862. }
  863.  
  864. /** Create and add zone to system.
  865.  *
  866.  * @param start     First frame number (absolute).
  867.  * @param count     Size of zone in frames.
  868.  * @param confframe Where configuration frames are supposed to be.
  869.  *          Automatically checks, that we will not disturb the
  870.  *          kernel and possibly init.  If confframe is given
  871.  *          _outside_ this zone, it is expected, that the area is
  872.  *          already marked BUSY and big enough to contain
  873.  *          zone_conf_size() amount of data.  If the confframe is
  874.  *          inside the area, the zone free frame information is
  875.  *          modified not to include it.
  876.  *
  877.  * @return      Zone number or -1 on error.
  878.  */
  879. int zone_create(pfn_t start, count_t count, pfn_t confframe, int flags)
  880. {
  881.     zone_t *z;
  882.     uintptr_t addr;
  883.     count_t confcount;
  884.     unsigned int i;
  885.     int znum;
  886.  
  887.     /* Theoretically we could have here 0, practically make sure
  888.      * nobody tries to do that. If some platform requires, remove
  889.      * the assert
  890.      */
  891.     ASSERT(confframe);
  892.     /* If conframe is supposed to be inside our zone, then make sure
  893.      * it does not span kernel & init
  894.      */
  895.     confcount = SIZE2FRAMES(zone_conf_size(count));
  896.     if (confframe >= start && confframe < start+count) {
  897.         for (;confframe < start + count; confframe++) {
  898.             addr = PFN2ADDR(confframe);
  899.             if (overlaps(addr, PFN2ADDR(confcount),
  900.                 KA2PA(config.base), config.kernel_size))
  901.                 continue;
  902.            
  903.             if (overlaps(addr, PFN2ADDR(confcount),
  904.                 KA2PA(config.stack_base), config.stack_size))
  905.                 continue;
  906.            
  907.             bool overlap = false;
  908.             count_t i;
  909.             for (i = 0; i < init.cnt; i++)
  910.                 if (overlaps(addr, PFN2ADDR(confcount),
  911.                     KA2PA(init.tasks[i].addr),
  912.                     init.tasks[i].size)) {
  913.                     overlap = true;
  914.                     break;
  915.                 }
  916.             if (overlap)
  917.                 continue;
  918.            
  919.             break;
  920.         }
  921.         if (confframe >= start + count)
  922.             panic("Cannot find configuration data for zone.");
  923.     }
  924.  
  925.     z = (zone_t *) PA2KA(PFN2ADDR(confframe));
  926.     zone_construct(start, count, z, flags);
  927.     znum = zones_add_zone(z);
  928.     if (znum == -1)
  929.         return -1;
  930.  
  931.     /* If confdata in zone, mark as unavailable */
  932.     if (confframe >= start && confframe < start + count)
  933.         for (i = confframe; i < confframe + confcount; i++) {
  934.             zone_mark_unavailable(z, i - z->base);
  935.         }
  936.     return znum;
  937. }
  938.  
  939. /***************************************/
  940. /* Frame functions */
  941.  
  942. /** Set parent of frame. */
  943. void frame_set_parent(pfn_t pfn, void *data, unsigned int hint)
  944. {
  945.     zone_t *zone = find_zone_and_lock(pfn, &hint);
  946.  
  947.     ASSERT(zone);
  948.  
  949.     zone_get_frame(zone, pfn-zone->base)->parent = data;
  950.     spinlock_unlock(&zone->lock);
  951. }
  952.  
  953. void *frame_get_parent(pfn_t pfn, unsigned int hint)
  954. {
  955.     zone_t *zone = find_zone_and_lock(pfn, &hint);
  956.     void *res;
  957.  
  958.     ASSERT(zone);
  959.     res = zone_get_frame(zone, pfn - zone->base)->parent;
  960.    
  961.     spinlock_unlock(&zone->lock);
  962.     return res;
  963. }
  964.  
  965. /** Allocate power-of-two frames of physical memory.
  966.  *
  967.  * @param order     Allocate exactly 2^order frames.
  968.  * @param flags     Flags for host zone selection and address processing.
  969.  * @param pzone     Preferred zone.
  970.  *
  971.  * @return      Physical address of the allocated frame.
  972.  *
  973.  */
  974. void *frame_alloc_generic(uint8_t order, int flags, unsigned int *pzone)
  975. {
  976.     ipl_t ipl;
  977.     int freed;
  978.     pfn_t v;
  979.     zone_t *zone;
  980.    
  981. loop:
  982.     ipl = interrupts_disable();
  983.    
  984.     /*
  985.      * First, find suitable frame zone.
  986.      */
  987.     zone = find_free_zone_and_lock(order, pzone);
  988.    
  989.     /* If no memory, reclaim some slab memory,
  990.        if it does not help, reclaim all */
  991.     if (!zone && !(flags & FRAME_NO_RECLAIM)) {
  992.         freed = slab_reclaim(0);
  993.         if (freed)
  994.             zone = find_free_zone_and_lock(order, pzone);
  995.         if (!zone) {
  996.             freed = slab_reclaim(SLAB_RECLAIM_ALL);
  997.             if (freed)
  998.                 zone = find_free_zone_and_lock(order, pzone);
  999.         }
  1000.     }
  1001.     if (!zone) {
  1002.         /*
  1003.          * Sleep until some frames are available again.
  1004.          */
  1005.         if (flags & FRAME_ATOMIC) {
  1006.             interrupts_restore(ipl);
  1007.             return 0;
  1008.         }
  1009.        
  1010. #ifdef CONFIG_DEBUG
  1011.         printf("Thread %" PRIu64 " falling asleep, low memory.\n", THREAD->tid);
  1012. #endif
  1013.  
  1014.         mutex_lock(&zones_mtx);
  1015.         if (!new_freed_mem)
  1016.             condvar_wait(&zones_cv, &zones_mtx);
  1017.         new_freed_mem = false;
  1018.         mutex_unlock(&zones_mtx);
  1019.  
  1020. #ifdef CONFIG_DEBUG
  1021.         printf("Thread %" PRIu64 " woken up, memory available.\n", THREAD->tid);
  1022. #endif
  1023.  
  1024.         interrupts_restore(ipl);
  1025.         goto loop;
  1026.     }
  1027.    
  1028.     v = zone_frame_alloc(zone, order);
  1029.     v += zone->base;
  1030.  
  1031.     spinlock_unlock(&zone->lock);
  1032.     interrupts_restore(ipl);
  1033.  
  1034.     if (flags & FRAME_KA)
  1035.         return (void *)PA2KA(PFN2ADDR(v));
  1036.     return (void *)PFN2ADDR(v);
  1037. }
  1038.  
  1039. /** Free a frame.
  1040.  *
  1041.  * Find respective frame structure for supplied physical frame address.
  1042.  * Decrement frame reference count.
  1043.  * If it drops to zero, move the frame structure to free list.
  1044.  *
  1045.  * @param frame     Physical Address of of the frame to be freed.
  1046.  */
  1047. void frame_free(uintptr_t frame)
  1048. {
  1049.     ipl_t ipl;
  1050.     zone_t *zone;
  1051.     pfn_t pfn = ADDR2PFN(frame);
  1052.  
  1053.     ipl = interrupts_disable();
  1054.  
  1055.     /*
  1056.      * First, find host frame zone for addr.
  1057.      */
  1058.     zone = find_zone_and_lock(pfn, NULL);
  1059.     ASSERT(zone);
  1060.    
  1061.     zone_frame_free(zone, pfn - zone->base);
  1062.    
  1063.     spinlock_unlock(&zone->lock);
  1064.    
  1065.     /*
  1066.      * Signal that some memory has been freed.
  1067.      */
  1068.     mutex_lock(&zones_mtx);
  1069.     new_freed_mem = true;
  1070.     condvar_broadcast(&zones_cv);
  1071.     mutex_unlock(&zones_mtx);
  1072.  
  1073.     interrupts_restore(ipl);
  1074. }
  1075.  
  1076. /** Add reference to frame.
  1077.  *
  1078.  * Find respective frame structure for supplied PFN and
  1079.  * increment frame reference count.
  1080.  *
  1081.  * @param pfn       Frame number of the frame to be freed.
  1082.  */
  1083. void frame_reference_add(pfn_t pfn)
  1084. {
  1085.     ipl_t ipl;
  1086.     zone_t *zone;
  1087.     frame_t *frame;
  1088.  
  1089.     ipl = interrupts_disable();
  1090.    
  1091.     /*
  1092.      * First, find host frame zone for addr.
  1093.      */
  1094.     zone = find_zone_and_lock(pfn, NULL);
  1095.     ASSERT(zone);
  1096.    
  1097.     frame = &zone->frames[pfn - zone->base];
  1098.     frame->refcount++;
  1099.    
  1100.     spinlock_unlock(&zone->lock);
  1101.     interrupts_restore(ipl);
  1102. }
  1103.  
  1104. /** Mark given range unavailable in frame zones. */
  1105. void frame_mark_unavailable(pfn_t start, count_t count)
  1106. {
  1107.     unsigned int i;
  1108.     zone_t *zone;
  1109.     unsigned int prefzone = 0;
  1110.    
  1111.     for (i = 0; i < count; i++) {
  1112.         zone = find_zone_and_lock(start + i, &prefzone);
  1113.         if (!zone) /* PFN not found */
  1114.             continue;
  1115.         zone_mark_unavailable(zone, start + i - zone->base);
  1116.  
  1117.         spinlock_unlock(&zone->lock);
  1118.     }
  1119. }
  1120.  
  1121. /** Initialize physical memory management. */
  1122. void frame_init(void)
  1123. {
  1124.     if (config.cpu_active == 1) {
  1125.         zones.count = 0;
  1126.         spinlock_initialize(&zones.lock, "zones.lock");
  1127.     }
  1128.     /* Tell the architecture to create some memory */
  1129.     frame_arch_init();
  1130.     if (config.cpu_active == 1) {
  1131.         frame_mark_unavailable(ADDR2PFN(KA2PA(config.base)),
  1132.             SIZE2FRAMES(config.kernel_size));
  1133.         frame_mark_unavailable(ADDR2PFN(KA2PA(config.stack_base)),
  1134.             SIZE2FRAMES(config.stack_size));
  1135.        
  1136.         count_t i;
  1137.         for (i = 0; i < init.cnt; i++) {
  1138.             pfn_t pfn = ADDR2PFN(KA2PA(init.tasks[i].addr));
  1139.             frame_mark_unavailable(pfn,
  1140.                 SIZE2FRAMES(init.tasks[i].size));
  1141.         }
  1142.  
  1143.         if (ballocs.size)
  1144.             frame_mark_unavailable(ADDR2PFN(KA2PA(ballocs.base)),
  1145.                 SIZE2FRAMES(ballocs.size));
  1146.  
  1147.         /* Black list first frame, as allocating NULL would
  1148.          * fail in some places */
  1149.         frame_mark_unavailable(0, 1);
  1150.     }
  1151.  
  1152.     mutex_initialize(&zones_mtx, MUTEX_ACTIVE); /* mimic spinlock */
  1153.     condvar_initialize(&zones_cv);
  1154. }
  1155.  
  1156.  
  1157. /** Return total size of all zones. */
  1158. uint64_t zone_total_size(void)
  1159. {
  1160.     zone_t *zone = NULL;
  1161.     unsigned int i;
  1162.     ipl_t ipl;
  1163.     uint64_t total = 0;
  1164.  
  1165.     ipl = interrupts_disable();
  1166.     spinlock_lock(&zones.lock);
  1167.    
  1168.     for (i = 0; i < zones.count; i++) {
  1169.         zone = zones.info[i];
  1170.         spinlock_lock(&zone->lock);
  1171.         total += (uint64_t) FRAMES2SIZE(zone->count);
  1172.         spinlock_unlock(&zone->lock);
  1173.     }
  1174.    
  1175.     spinlock_unlock(&zones.lock);
  1176.     interrupts_restore(ipl);
  1177.    
  1178.     return total;
  1179. }
  1180.  
  1181. /** Prints list of zones. */
  1182. void zone_print_list(void)
  1183. {
  1184.     zone_t *zone = NULL;
  1185.     unsigned int i;
  1186.     ipl_t ipl;
  1187.  
  1188.     ipl = interrupts_disable();
  1189.     spinlock_lock(&zones.lock);
  1190.  
  1191. #ifdef __32_BITS__ 
  1192.     printf("#  base address free frames  busy frames\n");
  1193.     printf("-- ------------ ------------ ------------\n");
  1194. #endif
  1195.  
  1196. #ifdef __64_BITS__
  1197.     printf("#  base address         free frames  busy frames\n");
  1198.     printf("-- -------------------- ------------ ------------\n");
  1199. #endif
  1200.    
  1201.     for (i = 0; i < zones.count; i++) {
  1202.         zone = zones.info[i];
  1203.         spinlock_lock(&zone->lock);
  1204.  
  1205. #ifdef __32_BITS__
  1206.         printf("%-2u   %10p %12" PRIc " %12" PRIc "\n",
  1207.             i, PFN2ADDR(zone->base), zone->free_count,
  1208.             zone->busy_count);
  1209. #endif
  1210.  
  1211. #ifdef __64_BITS__
  1212.         printf("%-2u   %18p %12" PRIc " %12" PRIc "\n", i,
  1213.             PFN2ADDR(zone->base), zone->free_count, zone->busy_count);
  1214. #endif
  1215.        
  1216.         spinlock_unlock(&zone->lock);
  1217.     }
  1218.    
  1219.     spinlock_unlock(&zones.lock);
  1220.     interrupts_restore(ipl);
  1221. }
  1222.  
  1223. /** Prints zone details.
  1224.  *
  1225.  * @param num       Zone base address or zone number.
  1226.  */
  1227. void zone_print_one(unsigned int num)
  1228. {
  1229.     zone_t *zone = NULL;
  1230.     ipl_t ipl;
  1231.     unsigned int i;
  1232.  
  1233.     ipl = interrupts_disable();
  1234.     spinlock_lock(&zones.lock);
  1235.  
  1236.     for (i = 0; i < zones.count; i++) {
  1237.         if ((i == num) || (PFN2ADDR(zones.info[i]->base) == num)) {
  1238.             zone = zones.info[i];
  1239.             break;
  1240.         }
  1241.     }
  1242.     if (!zone) {
  1243.         printf("Zone not found.\n");
  1244.         goto out;
  1245.     }
  1246.    
  1247.     spinlock_lock(&zone->lock);
  1248.     printf("Memory zone information\n");
  1249.     printf("Zone base address: %p\n", PFN2ADDR(zone->base));
  1250.     printf("Zone size: %" PRIc " frames (%" PRIs " KB)\n", zone->count,
  1251.         SIZE2KB(FRAMES2SIZE(zone->count)));
  1252.     printf("Allocated space: %" PRIc " frames (%" PRIs " KB)\n",
  1253.         zone->busy_count, SIZE2KB(FRAMES2SIZE(zone->busy_count)));
  1254.     printf("Available space: %" PRIc " frames (%" PRIs " KB)\n",
  1255.         zone->free_count, SIZE2KB(FRAMES2SIZE(zone->free_count)));
  1256.     buddy_system_structure_print(zone->buddy_system, FRAME_SIZE);
  1257.     spinlock_unlock(&zone->lock);
  1258.    
  1259. out:
  1260.     spinlock_unlock(&zones.lock);
  1261.     interrupts_restore(ipl);
  1262. }
  1263.  
  1264. /** @}
  1265.  */
  1266.  
  1267.