LCOV - code coverage report
Current view: top level - drivers/gpu/drm/i915 - i915_buddy.c (source / functions) Hit Total Coverage
Test: combined.info Lines: 5 201 2.5 %
Date: 2022-04-01 14:58:12 Functions: 1 11 9.1 %
Branches: 1 740 0.1 %

           Branch data     Line data    Source code
       1                 :            : // SPDX-License-Identifier: MIT
       2                 :            : /*
       3                 :            :  * Copyright © 2019 Intel Corporation
       4                 :            :  */
       5                 :            : 
       6                 :            : #include <linux/kmemleak.h>
       7                 :            : #include <linux/slab.h>
       8                 :            : 
       9                 :            : #include "i915_buddy.h"
      10                 :            : 
      11                 :            : #include "i915_gem.h"
      12                 :            : #include "i915_globals.h"
      13                 :            : #include "i915_utils.h"
      14                 :            : 
      15                 :            : static struct i915_global_block {
      16                 :            :         struct i915_global base;
      17                 :            :         struct kmem_cache *slab_blocks;
      18                 :            : } global;
      19                 :            : 
      20                 :          0 : static void i915_global_buddy_shrink(void)
      21                 :            : {
      22                 :          0 :         kmem_cache_shrink(global.slab_blocks);
      23                 :          0 : }
      24                 :            : 
      25                 :          0 : static void i915_global_buddy_exit(void)
      26                 :            : {
      27                 :          0 :         kmem_cache_destroy(global.slab_blocks);
      28                 :          0 : }
      29                 :            : 
      30                 :            : static struct i915_global_block global = { {
      31                 :            :         .shrink = i915_global_buddy_shrink,
      32                 :            :         .exit = i915_global_buddy_exit,
      33                 :            : } };
      34                 :            : 
      35                 :          3 : int __init i915_global_buddy_init(void)
      36                 :            : {
      37                 :          3 :         global.slab_blocks = KMEM_CACHE(i915_buddy_block, SLAB_HWCACHE_ALIGN);
      38         [ +  - ]:          3 :         if (!global.slab_blocks)
      39                 :            :                 return -ENOMEM;
      40                 :            : 
      41                 :          3 :         i915_global_register(&global.base);
      42                 :          3 :         return 0;
      43                 :            : }
      44                 :            : 
      45                 :          0 : static struct i915_buddy_block *i915_block_alloc(struct i915_buddy_block *parent,
      46                 :            :                                                  unsigned int order,
      47                 :            :                                                  u64 offset)
      48                 :            : {
      49                 :          0 :         struct i915_buddy_block *block;
      50                 :            : 
      51                 :          0 :         block = kmem_cache_zalloc(global.slab_blocks, GFP_KERNEL);
      52   [ #  #  #  #  :          0 :         if (!block)
                   #  # ]
      53                 :            :                 return NULL;
      54                 :            : 
      55                 :          0 :         block->header = offset;
      56                 :          0 :         block->header |= order;
      57                 :          0 :         block->parent = parent;
      58                 :            : 
      59                 :          0 :         return block;
      60                 :            : }
      61                 :            : 
      62                 :          0 : static void i915_block_free(struct i915_buddy_block *block)
      63                 :            : {
      64                 :          0 :         kmem_cache_free(global.slab_blocks, block);
      65                 :          0 : }
      66                 :            : 
      67                 :          0 : static void mark_allocated(struct i915_buddy_block *block)
      68                 :            : {
      69                 :          0 :         block->header &= ~I915_BUDDY_HEADER_STATE;
      70                 :          0 :         block->header |= I915_BUDDY_ALLOCATED;
      71                 :            : 
      72                 :          0 :         list_del(&block->link);
      73                 :            : }
      74                 :            : 
      75                 :          0 : static void mark_free(struct i915_buddy_mm *mm,
      76                 :            :                       struct i915_buddy_block *block)
      77                 :            : {
      78                 :          0 :         block->header &= ~I915_BUDDY_HEADER_STATE;
      79                 :          0 :         block->header |= I915_BUDDY_FREE;
      80                 :            : 
      81                 :          0 :         list_add(&block->link,
      82                 :          0 :                  &mm->free_list[i915_buddy_block_order(block)]);
      83                 :            : }
      84                 :            : 
      85                 :          0 : static void mark_split(struct i915_buddy_block *block)
      86                 :            : {
      87                 :          0 :         block->header &= ~I915_BUDDY_HEADER_STATE;
      88                 :          0 :         block->header |= I915_BUDDY_SPLIT;
      89                 :            : 
      90                 :          0 :         list_del(&block->link);
      91                 :            : }
      92                 :            : 
      93                 :          0 : int i915_buddy_init(struct i915_buddy_mm *mm, u64 size, u64 chunk_size)
      94                 :            : {
      95                 :          0 :         unsigned int i;
      96                 :          0 :         u64 offset;
      97                 :            : 
      98         [ #  # ]:          0 :         if (size < chunk_size)
      99                 :            :                 return -EINVAL;
     100                 :            : 
     101         [ #  # ]:          0 :         if (chunk_size < PAGE_SIZE)
     102                 :            :                 return -EINVAL;
     103                 :            : 
     104   [ #  #  #  # ]:          0 :         if (!is_power_of_2(chunk_size))
     105                 :            :                 return -EINVAL;
     106                 :            : 
     107                 :          0 :         size = round_down(size, chunk_size);
     108                 :            : 
     109                 :          0 :         mm->size = size;
     110                 :          0 :         mm->chunk_size = chunk_size;
     111   [ #  #  #  #  :          0 :         mm->max_order = ilog2(size) - ilog2(chunk_size);
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
                   #  # ]
     112                 :            : 
     113                 :          0 :         GEM_BUG_ON(mm->max_order > I915_BUDDY_MAX_ORDER);
     114                 :            : 
     115                 :          0 :         mm->free_list = kmalloc_array(mm->max_order + 1,
     116                 :            :                                       sizeof(struct list_head),
     117                 :            :                                       GFP_KERNEL);
     118         [ #  # ]:          0 :         if (!mm->free_list)
     119                 :            :                 return -ENOMEM;
     120                 :            : 
     121         [ #  # ]:          0 :         for (i = 0; i <= mm->max_order; ++i)
     122                 :          0 :                 INIT_LIST_HEAD(&mm->free_list[i]);
     123                 :            : 
     124         [ #  # ]:          0 :         mm->n_roots = hweight64(size);
     125                 :            : 
     126                 :          0 :         mm->roots = kmalloc_array(mm->n_roots,
     127                 :            :                                   sizeof(struct i915_buddy_block *),
     128                 :            :                                   GFP_KERNEL);
     129         [ #  # ]:          0 :         if (!mm->roots)
     130                 :          0 :                 goto out_free_list;
     131                 :            : 
     132                 :            :         offset = 0;
     133                 :            :         i = 0;
     134                 :            : 
     135                 :            :         /*
     136                 :            :          * Split into power-of-two blocks, in case we are given a size that is
     137                 :            :          * not itself a power-of-two.
     138                 :            :          */
     139                 :          0 :         do {
     140                 :          0 :                 struct i915_buddy_block *root;
     141                 :          0 :                 unsigned int order;
     142                 :          0 :                 u64 root_size;
     143                 :            : 
     144   [ #  #  #  #  :          0 :                 root_size = rounddown_pow_of_two(size);
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
             #  #  #  # ]
     145   [ #  #  #  #  :          0 :                 order = ilog2(root_size) - ilog2(chunk_size);
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
                   #  # ]
     146                 :            : 
     147                 :          0 :                 root = i915_block_alloc(NULL, order, offset);
     148                 :          0 :                 if (!root)
     149                 :          0 :                         goto out_free_roots;
     150                 :            : 
     151         [ #  # ]:          0 :                 mark_free(mm, root);
     152                 :            : 
     153                 :          0 :                 GEM_BUG_ON(i > mm->max_order);
     154                 :          0 :                 GEM_BUG_ON(i915_buddy_block_size(mm, root) < chunk_size);
     155                 :            : 
     156                 :          0 :                 mm->roots[i] = root;
     157                 :            : 
     158                 :          0 :                 offset += root_size;
     159                 :          0 :                 size -= root_size;
     160                 :          0 :                 i++;
     161         [ #  # ]:          0 :         } while (size);
     162                 :            : 
     163                 :            :         return 0;
     164                 :            : 
     165                 :            : out_free_roots:
     166         [ #  # ]:          0 :         while (i--)
     167                 :          0 :                 i915_block_free(mm->roots[i]);
     168                 :          0 :         kfree(mm->roots);
     169                 :          0 : out_free_list:
     170                 :          0 :         kfree(mm->free_list);
     171                 :          0 :         return -ENOMEM;
     172                 :            : }
     173                 :            : 
     174                 :          0 : void i915_buddy_fini(struct i915_buddy_mm *mm)
     175                 :            : {
     176                 :          0 :         int i;
     177                 :            : 
     178         [ #  # ]:          0 :         for (i = 0; i < mm->n_roots; ++i) {
     179                 :          0 :                 GEM_WARN_ON(!i915_buddy_block_is_free(mm->roots[i]));
     180                 :          0 :                 i915_block_free(mm->roots[i]);
     181                 :            :         }
     182                 :            : 
     183                 :          0 :         kfree(mm->roots);
     184                 :          0 :         kfree(mm->free_list);
     185                 :          0 : }
     186                 :            : 
     187                 :          0 : static int split_block(struct i915_buddy_mm *mm,
     188                 :            :                        struct i915_buddy_block *block)
     189                 :            : {
     190                 :          0 :         unsigned int block_order = i915_buddy_block_order(block) - 1;
     191                 :          0 :         u64 offset = i915_buddy_block_offset(block);
     192                 :            : 
     193                 :          0 :         GEM_BUG_ON(!i915_buddy_block_is_free(block));
     194                 :          0 :         GEM_BUG_ON(!i915_buddy_block_order(block));
     195                 :            : 
     196                 :          0 :         block->left = i915_block_alloc(block, block_order, offset);
     197         [ #  # ]:          0 :         if (!block->left)
     198                 :            :                 return -ENOMEM;
     199                 :            : 
     200                 :          0 :         block->right = i915_block_alloc(block, block_order,
     201                 :          0 :                                         offset + (mm->chunk_size << block_order));
     202         [ #  # ]:          0 :         if (!block->right) {
     203                 :          0 :                 i915_block_free(block->left);
     204                 :          0 :                 return -ENOMEM;
     205                 :            :         }
     206                 :            : 
     207                 :          0 :         mark_free(mm, block->left);
     208                 :          0 :         mark_free(mm, block->right);
     209                 :            : 
     210                 :          0 :         mark_split(block);
     211                 :            : 
     212                 :          0 :         return 0;
     213                 :            : }
     214                 :            : 
     215                 :            : static struct i915_buddy_block *
     216                 :          0 : get_buddy(struct i915_buddy_block *block)
     217                 :            : {
     218                 :          0 :         struct i915_buddy_block *parent;
     219                 :            : 
     220                 :          0 :         parent = block->parent;
     221                 :          0 :         if (!parent)
     222                 :            :                 return NULL;
     223                 :            : 
     224         [ #  # ]:          0 :         if (parent->left == block)
     225                 :          0 :                 return parent->right;
     226                 :            : 
     227                 :            :         return parent->left;
     228                 :            : }
     229                 :            : 
     230                 :          0 : static void __i915_buddy_free(struct i915_buddy_mm *mm,
     231                 :            :                               struct i915_buddy_block *block)
     232                 :            : {
     233                 :          0 :         struct i915_buddy_block *parent;
     234                 :            : 
     235         [ #  # ]:          0 :         while ((parent = block->parent)) {
     236                 :          0 :                 struct i915_buddy_block *buddy;
     237                 :            : 
     238         [ #  # ]:          0 :                 buddy = get_buddy(block);
     239                 :            : 
     240         [ #  # ]:          0 :                 if (!i915_buddy_block_is_free(buddy))
     241                 :            :                         break;
     242                 :            : 
     243                 :          0 :                 list_del(&buddy->link);
     244                 :            : 
     245                 :          0 :                 i915_block_free(block);
     246                 :          0 :                 i915_block_free(buddy);
     247                 :            : 
     248                 :          0 :                 block = parent;
     249                 :            :         }
     250                 :            : 
     251                 :          0 :         mark_free(mm, block);
     252                 :          0 : }
     253                 :            : 
     254                 :          0 : void i915_buddy_free(struct i915_buddy_mm *mm,
     255                 :            :                      struct i915_buddy_block *block)
     256                 :            : {
     257                 :          0 :         GEM_BUG_ON(!i915_buddy_block_is_allocated(block));
     258                 :          0 :         __i915_buddy_free(mm, block);
     259                 :          0 : }
     260                 :            : 
     261                 :          0 : void i915_buddy_free_list(struct i915_buddy_mm *mm, struct list_head *objects)
     262                 :            : {
     263                 :          0 :         struct i915_buddy_block *block, *on;
     264                 :            : 
     265         [ #  # ]:          0 :         list_for_each_entry_safe(block, on, objects, link) {
     266                 :          0 :                 i915_buddy_free(mm, block);
     267                 :          0 :                 cond_resched();
     268                 :            :         }
     269                 :          0 :         INIT_LIST_HEAD(objects);
     270                 :          0 : }
     271                 :            : 
     272                 :            : /*
     273                 :            :  * Allocate power-of-two block. The order value here translates to:
     274                 :            :  *
     275                 :            :  *   0 = 2^0 * mm->chunk_size
     276                 :            :  *   1 = 2^1 * mm->chunk_size
     277                 :            :  *   2 = 2^2 * mm->chunk_size
     278                 :            :  *   ...
     279                 :            :  */
     280                 :            : struct i915_buddy_block *
     281                 :          0 : i915_buddy_alloc(struct i915_buddy_mm *mm, unsigned int order)
     282                 :            : {
     283                 :          0 :         struct i915_buddy_block *block = NULL;
     284                 :          0 :         unsigned int i;
     285                 :          0 :         int err;
     286                 :            : 
     287         [ #  # ]:          0 :         for (i = order; i <= mm->max_order; ++i) {
     288         [ #  # ]:          0 :                 block = list_first_entry_or_null(&mm->free_list[i],
     289                 :            :                                                  struct i915_buddy_block,
     290                 :            :                                                  link);
     291         [ #  # ]:          0 :                 if (block)
     292                 :            :                         break;
     293                 :            :         }
     294                 :            : 
     295         [ #  # ]:          0 :         if (!block)
     296                 :            :                 return ERR_PTR(-ENOSPC);
     297                 :            : 
     298                 :            :         GEM_BUG_ON(!i915_buddy_block_is_free(block));
     299                 :            : 
     300         [ #  # ]:          0 :         while (i != order) {
     301                 :          0 :                 err = split_block(mm, block);
     302         [ #  # ]:          0 :                 if (unlikely(err))
     303                 :          0 :                         goto out_free;
     304                 :            : 
     305                 :            :                 /* Go low */
     306                 :          0 :                 block = block->left;
     307                 :          0 :                 i--;
     308                 :            :         }
     309                 :            : 
     310                 :          0 :         mark_allocated(block);
     311                 :          0 :         kmemleak_update_trace(block);
     312                 :          0 :         return block;
     313                 :            : 
     314                 :            : out_free:
     315                 :          0 :         __i915_buddy_free(mm, block);
     316                 :          0 :         return ERR_PTR(err);
     317                 :            : }
     318                 :            : 
     319                 :          0 : static inline bool overlaps(u64 s1, u64 e1, u64 s2, u64 e2)
     320                 :            : {
     321                 :          0 :         return s1 <= e2 && e1 >= s2;
     322                 :            : }
     323                 :            : 
     324                 :          0 : static inline bool contains(u64 s1, u64 e1, u64 s2, u64 e2)
     325                 :            : {
     326                 :          0 :         return s1 <= s2 && e1 >= e2;
     327                 :            : }
     328                 :            : 
     329                 :            : /*
     330                 :            :  * Allocate range. Note that it's safe to chain together multiple alloc_ranges
     331                 :            :  * with the same blocks list.
     332                 :            :  *
     333                 :            :  * Intended for pre-allocating portions of the address space, for example to
     334                 :            :  * reserve a block for the initial framebuffer or similar, hence the expectation
     335                 :            :  * here is that i915_buddy_alloc() is still the main vehicle for
     336                 :            :  * allocations, so if that's not the case then the drm_mm range allocator is
     337                 :            :  * probably a much better fit, and so you should probably go use that instead.
     338                 :            :  */
     339                 :          0 : int i915_buddy_alloc_range(struct i915_buddy_mm *mm,
     340                 :            :                            struct list_head *blocks,
     341                 :            :                            u64 start, u64 size)
     342                 :            : {
     343                 :          0 :         struct i915_buddy_block *block;
     344                 :          0 :         struct i915_buddy_block *buddy;
     345                 :          0 :         LIST_HEAD(allocated);
     346                 :          0 :         LIST_HEAD(dfs);
     347                 :          0 :         u64 end;
     348                 :          0 :         int err;
     349                 :          0 :         int i;
     350                 :            : 
     351         [ #  # ]:          0 :         if (size < mm->chunk_size)
     352                 :            :                 return -EINVAL;
     353                 :            : 
     354         [ #  # ]:          0 :         if (!IS_ALIGNED(size | start, mm->chunk_size))
     355                 :            :                 return -EINVAL;
     356                 :            : 
     357   [ #  #  #  # ]:          0 :         if (range_overflows(start, size, mm->size))
     358                 :            :                 return -EINVAL;
     359                 :            : 
     360         [ #  # ]:          0 :         for (i = 0; i < mm->n_roots; ++i)
     361                 :          0 :                 list_add_tail(&mm->roots[i]->tmp_link, &dfs);
     362                 :            : 
     363                 :          0 :         end = start + size - 1;
     364                 :            : 
     365                 :          0 :         do {
     366                 :          0 :                 u64 block_start;
     367                 :          0 :                 u64 block_end;
     368                 :            : 
     369         [ #  # ]:          0 :                 block = list_first_entry_or_null(&dfs,
     370                 :            :                                                  struct i915_buddy_block,
     371                 :            :                                                  tmp_link);
     372         [ #  # ]:          0 :                 if (!block)
     373                 :            :                         break;
     374                 :            : 
     375         [ #  # ]:          0 :                 list_del(&block->tmp_link);
     376                 :            : 
     377         [ #  # ]:          0 :                 block_start = i915_buddy_block_offset(block);
     378         [ #  # ]:          0 :                 block_end = block_start + i915_buddy_block_size(mm, block) - 1;
     379                 :            : 
     380         [ #  # ]:          0 :                 if (!overlaps(start, end, block_start, block_end))
     381                 :          0 :                         continue;
     382                 :            : 
     383         [ #  # ]:          0 :                 if (i915_buddy_block_is_allocated(block)) {
     384                 :          0 :                         err = -ENOSPC;
     385                 :          0 :                         goto err_free;
     386                 :            :                 }
     387                 :            : 
     388         [ #  # ]:          0 :                 if (contains(start, end, block_start, block_end)) {
     389         [ #  # ]:          0 :                         if (!i915_buddy_block_is_free(block)) {
     390                 :          0 :                                 err = -ENOSPC;
     391                 :          0 :                                 goto err_free;
     392                 :            :                         }
     393                 :            : 
     394                 :          0 :                         mark_allocated(block);
     395                 :          0 :                         list_add_tail(&block->link, &allocated);
     396                 :          0 :                         continue;
     397                 :            :                 }
     398                 :            : 
     399         [ #  # ]:          0 :                 if (!i915_buddy_block_is_split(block)) {
     400                 :          0 :                         err = split_block(mm, block);
     401         [ #  # ]:          0 :                         if (unlikely(err))
     402                 :          0 :                                 goto err_undo;
     403                 :            :                 }
     404                 :            : 
     405                 :          0 :                 list_add(&block->right->tmp_link, &dfs);
     406                 :          0 :                 list_add(&block->left->tmp_link, &dfs);
     407                 :            :         } while (1);
     408                 :            : 
     409         [ #  # ]:          0 :         list_splice_tail(&allocated, blocks);
     410                 :            :         return 0;
     411                 :            : 
     412                 :            : err_undo:
     413                 :            :         /*
     414                 :            :          * We really don't want to leave around a bunch of split blocks, since
     415                 :            :          * bigger is better, so make sure we merge everything back before we
     416                 :            :          * free the allocated blocks.
     417                 :            :          */
     418         [ #  # ]:          0 :         buddy = get_buddy(block);
     419   [ #  #  #  # ]:          0 :         if (buddy &&
     420         [ #  # ]:          0 :             (i915_buddy_block_is_free(block) &&
     421                 :            :              i915_buddy_block_is_free(buddy)))
     422                 :          0 :                 __i915_buddy_free(mm, block);
     423                 :            : 
     424                 :          0 : err_free:
     425                 :          0 :         i915_buddy_free_list(mm, &allocated);
     426                 :          0 :         return err;
     427                 :            : }
     428                 :            : 
     429                 :            : #if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
     430                 :            : #include "selftests/i915_buddy.c"
     431                 :            : #endif

Generated by: LCOV version 1.14