Home | History | Annotate | Download | only in core
      1 /*******************************************************************************
      2 * Copyright (C) 2018 Cadence Design Systems, Inc.
      3 *
      4 * Permission is hereby granted, free of charge, to any person obtaining
      5 * a copy of this software and associated documentation files (the
      6 * "Software"), to use this Software with Cadence processor cores only and
      7 * not with any other processors and platforms, subject to
      8 * the following conditions:
      9 *
     10 * The above copyright notice and this permission notice shall be included
     11 * in all copies or substantial portions of the Software.
     12 *
     13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     14 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     15 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
     16 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
     17 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
     18 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
     19 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     20 
     21 ******************************************************************************/
     22 
     23 /*******************************************************************************
     24  * xf-mem.c
     25  *
     26  * Dynamic memory allocator implementation (based on rb-tree index)
     27  *
     28  ******************************************************************************/
     29 
     30 #define MODULE_TAG                      MM
     31 
     32 /*******************************************************************************
     33  * Includes
     34  ******************************************************************************/
     35 
     36 #include "xf.h"
     37 
     38 /*******************************************************************************
     39  * Tracing configuration
     40  ******************************************************************************/
     41 
     42 TRACE_TAG(INIT, 1);
     43 
     44 /*******************************************************************************
     45  * Internal helpers
     46  ******************************************************************************/
     47 
     48 /* ...initialize block */
     49 static inline xf_mm_block_t * xf_mm_block_init(void *addr, u32 size)
     50 {
     51     xf_mm_block_t  *b = (xf_mm_block_t *)addr;
     52 
     53     /* ...use 31 available bits of node color to keep aligned size */
     54     return b->l_node.color = size, b;
     55 }
     56 
     57 /* ...check if the length of the block is less than given */
     58 static inline int xf_mm_block_length_less(xf_mm_block_t *b, u32 size)
     59 {
     60     /* ...we don't really care about LSB of color */
     61     return (b->l_node.color < size);
     62 }
     63 
     64 /* ...return exact block length */
     65 static inline u32 xf_mm_block_length(xf_mm_block_t *b)
     66 {
     67     /* ...wipe out least-significant bit from node color */
     68     return (b->l_node.color & ~1);
     69 }
     70 
     71 /* ...increase block length */
     72 static inline u32 xf_mm_block_length_add(xf_mm_block_t *b, u32 size)
     73 {
     74     /* ...return exact block length after increase */
     75     return ((b->l_node.color += size) & ~1);
     76 }
     77 
     78 /* ...decrease block length */
     79 static inline u32 xf_mm_block_length_sub(xf_mm_block_t *b, u32 size)
     80 {
     81     /* ...return exact block length after decrease */
     82     return ((b->l_node.color -= size) & ~1);
     83 }
     84 
     85 /*******************************************************************************
     86  * Internal functions
     87  ******************************************************************************/
     88 
     89 /* ...find best-match node given requested size */
     90 static inline  xf_mm_block_t * xf_mm_find_by_size(xf_mm_pool_t *pool, u32 size)
     91 {
     92     rb_tree_t *tree = &pool->l_map;
     93     rb_idx_t   p_idx, t_idx;
     94 
     95     /* ...find first block having length greater than requested */
     96     for (p_idx = rb_root(tree); p_idx != rb_null(tree); p_idx = rb_right(tree, p_idx))
     97     {
     98         xf_mm_block_t  *b = container_of(p_idx, xf_mm_block_t, l_node);
     99 
    100         /* ...break upon finding first matching candidate */
    101         if (!xf_mm_block_length_less(b, size))
    102             break;
    103     }
    104 
    105     /* ...bail out if haven't found a block with sufficient size */
    106     if (p_idx == rb_null(tree))
    107         return NULL;
    108 
    109     /* ...try to find better match in left subtree */
    110     for (t_idx = rb_left(tree, p_idx); t_idx != rb_null(tree); )
    111     {
    112         xf_mm_block_t  *b = container_of(t_idx, xf_mm_block_t, l_node);
    113 
    114         /* ...check the size of the block */
    115         if (!xf_mm_block_length_less(b, size))
    116         {
    117             /* ...update best match */
    118             p_idx = t_idx;
    119 
    120             /* ...and check if we have anything better in left sbtree */
    121             t_idx = rb_left(tree, t_idx);
    122         }
    123         else
    124         {
    125             /* ...move towards higher block sizes in that subtree */
    126             t_idx = rb_right(tree, t_idx);
    127         }
    128     }
    129 
    130     /* ...p_idx is our best choice */
    131     return container_of(p_idx, xf_mm_block_t, l_node);
    132 }
    133 
    134 /* ...find the neighbours of the block basing on its address */
    135 static void xf_mm_find_by_addr(xf_mm_pool_t *pool, void *addr, xf_mm_block_t **n)
    136 {
    137     rb_tree_t  *tree = &pool->a_map;
    138     rb_idx_t    p_idx, l_idx, r_idx;
    139 
    140     /* ...it is not possible to have exact match in this map */
    141     for (p_idx = rb_root(tree), l_idx = r_idx = NULL; p_idx != rb_null(tree); )
    142     {
    143         /* ...only "is less than" comparison is valid (as "a_node" pointer is biased) */
    144         if ((u32)p_idx < (u32)addr)
    145         {
    146             /* ...update lower neighbour */
    147             l_idx = p_idx;
    148 
    149             /* ...and move towards higher addresses */
    150             p_idx = rb_right(tree, p_idx);
    151         }
    152         else
    153         {
    154             /* ...update higher neighbour */
    155             r_idx = p_idx;
    156 
    157             /* ...and move towards lower addresses */
    158             p_idx = rb_left(tree, p_idx);
    159         }
    160     }
    161 
    162     /* ...translate nodes into blocks */
    163     n[0] = (l_idx ? container_of(l_idx, xf_mm_block_t, a_node) : NULL);
    164     n[1] = (r_idx ? container_of(r_idx, xf_mm_block_t, a_node) : NULL);
    165 }
    166 
    167 /* ...insert the block into L-map */
    168 static void xf_mm_insert_size(xf_mm_pool_t *pool, xf_mm_block_t *b, u32 size)
    169 {
    170     rb_tree_t  *tree = &pool->l_map;
    171     rb_idx_t    p_idx, t_idx;
    172 
    173     /* ...find the parent node for the next block */
    174     for (p_idx = rb_root(tree); p_idx != rb_null(tree); p_idx = t_idx)
    175     {
    176         /* ...check for the size */
    177         if (xf_mm_block_length_less(container_of(p_idx, xf_mm_block_t, l_node), size))
    178         {
    179             /* ...move towards higher addresses */
    180             if ((t_idx = rb_right(tree, p_idx)) == rb_null(tree))
    181             {
    182                 /* ...node becomes a right child of parent p */
    183                 rb_set_right(tree, p_idx, &b->l_node);
    184                 break;
    185             }
    186         }
    187         else
    188         {
    189             /* ...move towards lower addresses (ok if exact size match is found) */
    190             if ((t_idx = rb_left(tree, p_idx)) == rb_null(tree))
    191             {
    192                 /* ...node becomes a left child of parent p */
    193                 rb_set_left(tree, p_idx, &b->l_node);
    194                 break;
    195             }
    196         }
    197     }
    198 
    199     /* ...insert node into tree */
    200     rb_insert(tree, &b->l_node, p_idx);
    201 }
    202 
    203 /* ...insert the block into A-map */
    204 static void xf_mm_insert_addr(xf_mm_pool_t *pool, xf_mm_block_t *b)
    205 {
    206     rb_tree_t  *tree = &pool->a_map;
    207     rb_idx_t    p_idx, t_idx;
    208 
    209     /* ...find the parent node for the next block */
    210     for (p_idx = rb_root(tree); p_idx != rb_null(tree); p_idx = t_idx)
    211     {
    212         /* ...check for the address (only "is less than" comparison is valid) */
    213         if ((u32)p_idx < (u32)b)
    214         {
    215             /* ...move towards higher addresses */
    216             if ((t_idx = rb_right(tree, p_idx)) == rb_null(tree))
    217             {
    218                 /* ...node becomes a right child of parent p */
    219                 rb_set_right(tree, p_idx, &b->a_node);
    220                 break;
    221             }
    222         }
    223         else
    224         {
    225             /* ...move towards lower addresses (by design there cannot be exact match) */
    226             if ((t_idx = rb_left(tree, p_idx)) == rb_null(tree))
    227             {
    228                 /* ...node becomes a left child of parent p */
    229                 rb_set_left(tree, p_idx, &b->a_node);
    230                 break;
    231             }
    232         }
    233     }
    234 
    235     /* ...insert node into tree */
    236     rb_insert(tree, &b->a_node, p_idx);
    237 }
    238 
    239 /*******************************************************************************
    240  * Entry points
    241  ******************************************************************************/
    242 
    243 /* ...block allocation */
    244 void * xf_mm_alloc(xf_mm_pool_t *pool, u32 size)
    245 {
    246     xf_mm_block_t  *b;
    247 
    248     /* ...find best-fit free block */
    249     XF_CHK_ERR(b = xf_mm_find_by_size(pool, size), NULL);
    250 
    251     /* ...remove the block from the L-map */
    252     rb_delete(&pool->l_map, &b->l_node);
    253 
    254     /* ...check if the size is exactly the same as requested */
    255     if ((size = xf_mm_block_length_sub(b, size)) == 0)
    256     {
    257         /* ...the block needs to be removed from the A-map as well */
    258         rb_delete(&pool->a_map, &b->a_node);
    259 
    260         /* ...entire block goes to user */
    261         return (void *) b;
    262     }
    263     else
    264     {
    265         /* ...insert the block into L-map */
    266         xf_mm_insert_size(pool, b, size);
    267 
    268         /* ...A-map remains intact; tail of the block goes to user */
    269         return (void *) b + size;
    270     }
    271 }
    272 
    273 /* ...block deallocation */
    274 void xf_mm_free(xf_mm_pool_t *pool, void *addr, u32 size)
    275 {
    276     xf_mm_block_t  *b = xf_mm_block_init(addr, size);
    277     xf_mm_block_t  *n[2];
    278 
    279     /* ...find block neighbours in A-map */
    280     xf_mm_find_by_addr(pool, addr, n);
    281 
    282     /* ...check if we can merge block to left neighbour */
    283     if (n[0])
    284     {
    285         if ((void *)n[0] + xf_mm_block_length(n[0]) == addr)
    286         {
    287             /* ...merge free block with left neighbour; delete it from L-map */
    288             rb_delete(&pool->l_map, &n[0]->l_node);
    289 
    290             /* ...adjust block length (block remains in A-map) */
    291             addr = (void *)(b = n[0]), size = xf_mm_block_length_add(b, size);
    292         }
    293         else
    294         {
    295             /* ...mark there is no left-merge */
    296             n[0] = NULL;
    297         }
    298     }
    299 
    300     /* ...check if we can merge block to right neighbour */
    301     if (n[1])
    302     {
    303         if ((void *)n[1] == addr + size)
    304         {
    305             /* ...merge free block with right neighbour; delete it from L-map */
    306             rb_delete(&pool->l_map, &n[1]->l_node);
    307 
    308             /* ...adjust block length */
    309             size = xf_mm_block_length_add(b, xf_mm_block_length(n[1]));
    310 
    311             /* ...check if left merge took place as well */
    312             if (n[0])
    313             {
    314                 /* ...left neighbour covers now all three blocks; drop record from A-map */
    315                 rb_delete(&pool->a_map, &n[1]->a_node);
    316             }
    317             else
    318             {
    319                 /* ...fixup tree pointers (equivalent to remove/reinsert the same key) */
    320                 rb_replace(&pool->a_map, &n[1]->a_node, &b->a_node);
    321             }
    322         }
    323         else
    324         {
    325             n[1] = NULL;
    326         }
    327     }
    328 
    329     /* ...if any merge has occured, A-map is updated */
    330     if (n[0] == NULL && n[1] == NULL)
    331     {
    332         /* ...add new block into A-map */
    333         xf_mm_insert_addr(pool, b);
    334     }
    335 
    336     /* ...add (new or adjusted) block into L-map */
    337     xf_mm_insert_size(pool, b, size);
    338 }
    339 
    340 /* ...initialize memory allocator */
    341 int xf_mm_init(xf_mm_pool_t *pool, void *addr, u32 size)
    342 {
    343     /* ...check pool alignment validity */
    344     XF_CHK_ERR(((u32)addr & (sizeof(xf_mm_block_t) - 1)) == 0, -EINVAL);
    345 
    346     /* ...check pool size validity */
    347     XF_CHK_ERR(((size) & (sizeof(xf_mm_block_t) - 1)) == 0, -EINVAL);
    348 
    349     /* ...set pool parameters (need that stuff at all? - tbd) */
    350     pool->addr = addr, pool->size = size;
    351 
    352     /* ...initialize rb-trees */
    353     rb_init(&pool->l_map), rb_init(&pool->a_map);
    354 
    355     /* ..."free" the entire block */
    356     xf_mm_free(pool, addr, size);
    357 
    358     TRACE(INIT, _b("memory allocator initialized: [%p..%p)"), addr, addr + size);
    359 
    360     return 0;
    361 }
    362