linux/drivers/gpu/drm/i915/i915_ttm_buddy_manager.c
<<
>>
Prefs
   1// SPDX-License-Identifier: MIT
   2/*
   3 * Copyright © 2021 Intel Corporation
   4 */
   5
   6#include <linux/slab.h>
   7
   8#include <drm/ttm/ttm_bo_driver.h>
   9#include <drm/ttm/ttm_placement.h>
  10
  11#include "i915_ttm_buddy_manager.h"
  12
  13#include "i915_buddy.h"
  14#include "i915_gem.h"
  15
  16struct i915_ttm_buddy_manager {
  17        struct ttm_resource_manager manager;
  18        struct i915_buddy_mm mm;
  19        struct list_head reserved;
  20        struct mutex lock;
  21        u64 default_page_size;
  22};
  23
  24static struct i915_ttm_buddy_manager *
  25to_buddy_manager(struct ttm_resource_manager *man)
  26{
  27        return container_of(man, struct i915_ttm_buddy_manager, manager);
  28}
  29
  30static int i915_ttm_buddy_man_alloc(struct ttm_resource_manager *man,
  31                                    struct ttm_buffer_object *bo,
  32                                    const struct ttm_place *place,
  33                                    struct ttm_resource **res)
  34{
  35        struct i915_ttm_buddy_manager *bman = to_buddy_manager(man);
  36        struct i915_ttm_buddy_resource *bman_res;
  37        struct i915_buddy_mm *mm = &bman->mm;
  38        unsigned long n_pages;
  39        unsigned int min_order;
  40        u64 min_page_size;
  41        u64 size;
  42        int err;
  43
  44        GEM_BUG_ON(place->fpfn || place->lpfn);
  45
  46        bman_res = kzalloc(sizeof(*bman_res), GFP_KERNEL);
  47        if (!bman_res)
  48                return -ENOMEM;
  49
  50        ttm_resource_init(bo, place, &bman_res->base);
  51        INIT_LIST_HEAD(&bman_res->blocks);
  52        bman_res->mm = mm;
  53
  54        GEM_BUG_ON(!bman_res->base.num_pages);
  55        size = bman_res->base.num_pages << PAGE_SHIFT;
  56
  57        min_page_size = bman->default_page_size;
  58        if (bo->page_alignment)
  59                min_page_size = bo->page_alignment << PAGE_SHIFT;
  60
  61        GEM_BUG_ON(min_page_size < mm->chunk_size);
  62        min_order = ilog2(min_page_size) - ilog2(mm->chunk_size);
  63        if (place->flags & TTM_PL_FLAG_CONTIGUOUS) {
  64                size = roundup_pow_of_two(size);
  65                min_order = ilog2(size) - ilog2(mm->chunk_size);
  66        }
  67
  68        if (size > mm->size) {
  69                err = -E2BIG;
  70                goto err_free_res;
  71        }
  72
  73        n_pages = size >> ilog2(mm->chunk_size);
  74
  75        do {
  76                struct i915_buddy_block *block;
  77                unsigned int order;
  78
  79                order = fls(n_pages) - 1;
  80                GEM_BUG_ON(order > mm->max_order);
  81                GEM_BUG_ON(order < min_order);
  82
  83                do {
  84                        mutex_lock(&bman->lock);
  85                        block = i915_buddy_alloc(mm, order);
  86                        mutex_unlock(&bman->lock);
  87                        if (!IS_ERR(block))
  88                                break;
  89
  90                        if (order-- == min_order) {
  91                                err = -ENOSPC;
  92                                goto err_free_blocks;
  93                        }
  94                } while (1);
  95
  96                n_pages -= BIT(order);
  97
  98                list_add_tail(&block->link, &bman_res->blocks);
  99
 100                if (!n_pages)
 101                        break;
 102        } while (1);
 103
 104        *res = &bman_res->base;
 105        return 0;
 106
 107err_free_blocks:
 108        mutex_lock(&bman->lock);
 109        i915_buddy_free_list(mm, &bman_res->blocks);
 110        mutex_unlock(&bman->lock);
 111err_free_res:
 112        kfree(bman_res);
 113        return err;
 114}
 115
 116static void i915_ttm_buddy_man_free(struct ttm_resource_manager *man,
 117                                    struct ttm_resource *res)
 118{
 119        struct i915_ttm_buddy_resource *bman_res = to_ttm_buddy_resource(res);
 120        struct i915_ttm_buddy_manager *bman = to_buddy_manager(man);
 121
 122        mutex_lock(&bman->lock);
 123        i915_buddy_free_list(&bman->mm, &bman_res->blocks);
 124        mutex_unlock(&bman->lock);
 125
 126        kfree(bman_res);
 127}
 128
 129static const struct ttm_resource_manager_func i915_ttm_buddy_manager_func = {
 130        .alloc = i915_ttm_buddy_man_alloc,
 131        .free = i915_ttm_buddy_man_free,
 132};
 133
 134
 135/**
 136 * i915_ttm_buddy_man_init - Setup buddy allocator based ttm manager
 137 * @bdev: The ttm device
 138 * @type: Memory type we want to manage
 139 * @use_tt: Set use_tt for the manager
 140 * @size: The size in bytes to manage
 141 * @default_page_size: The default minimum page size in bytes for allocations,
 142 * this must be at least as large as @chunk_size, and can be overridden by
 143 * setting the BO page_alignment, to be larger or smaller as needed.
 144 * @chunk_size: The minimum page size in bytes for our allocations i.e
 145 * order-zero
 146 *
 147 * Note that the starting address is assumed to be zero here, since this
 148 * simplifies keeping the property where allocated blocks having natural
 149 * power-of-two alignment. So long as the real starting address is some large
 150 * power-of-two, or naturally start from zero, then this should be fine.  Also
 151 * the &i915_ttm_buddy_man_reserve interface can be used to preserve alignment
 152 * if say there is some unusable range from the start of the region. We can
 153 * revisit this in the future and make the interface accept an actual starting
 154 * offset and let it take care of the rest.
 155 *
 156 * Note that if the @size is not aligned to the @chunk_size then we perform the
 157 * required rounding to get the usable size. The final size in pages can be
 158 * taken from &ttm_resource_manager.size.
 159 *
 160 * Return: 0 on success, negative error code on failure.
 161 */
 162int i915_ttm_buddy_man_init(struct ttm_device *bdev,
 163                            unsigned int type, bool use_tt,
 164                            u64 size, u64 default_page_size,
 165                            u64 chunk_size)
 166{
 167        struct ttm_resource_manager *man;
 168        struct i915_ttm_buddy_manager *bman;
 169        int err;
 170
 171        bman = kzalloc(sizeof(*bman), GFP_KERNEL);
 172        if (!bman)
 173                return -ENOMEM;
 174
 175        err = i915_buddy_init(&bman->mm, size, chunk_size);
 176        if (err)
 177                goto err_free_bman;
 178
 179        mutex_init(&bman->lock);
 180        INIT_LIST_HEAD(&bman->reserved);
 181        GEM_BUG_ON(default_page_size < chunk_size);
 182        bman->default_page_size = default_page_size;
 183
 184        man = &bman->manager;
 185        man->use_tt = use_tt;
 186        man->func = &i915_ttm_buddy_manager_func;
 187        ttm_resource_manager_init(man, bman->mm.size >> PAGE_SHIFT);
 188
 189        ttm_resource_manager_set_used(man, true);
 190        ttm_set_driver_manager(bdev, type, man);
 191
 192        return 0;
 193
 194err_free_bman:
 195        kfree(bman);
 196        return err;
 197}
 198
 199/**
 200 * i915_ttm_buddy_man_fini - Destroy the buddy allocator ttm manager
 201 * @bdev: The ttm device
 202 * @type: Memory type we want to manage
 203 *
 204 * Note that if we reserved anything with &i915_ttm_buddy_man_reserve, this will
 205 * also be freed for us here.
 206 *
 207 * Return: 0 on success, negative error code on failure.
 208 */
 209int i915_ttm_buddy_man_fini(struct ttm_device *bdev, unsigned int type)
 210{
 211        struct ttm_resource_manager *man = ttm_manager_type(bdev, type);
 212        struct i915_ttm_buddy_manager *bman = to_buddy_manager(man);
 213        struct i915_buddy_mm *mm = &bman->mm;
 214        int ret;
 215
 216        ttm_resource_manager_set_used(man, false);
 217
 218        ret = ttm_resource_manager_evict_all(bdev, man);
 219        if (ret)
 220                return ret;
 221
 222        ttm_set_driver_manager(bdev, type, NULL);
 223
 224        mutex_lock(&bman->lock);
 225        i915_buddy_free_list(mm, &bman->reserved);
 226        i915_buddy_fini(mm);
 227        mutex_unlock(&bman->lock);
 228
 229        ttm_resource_manager_cleanup(man);
 230        kfree(bman);
 231
 232        return 0;
 233}
 234
 235/**
 236 * i915_ttm_buddy_man_reserve - Reserve address range
 237 * @man: The buddy allocator ttm manager
 238 * @start: The offset in bytes, where the region start is assumed to be zero
 239 * @size: The size in bytes
 240 *
 241 * Note that the starting address for the region is always assumed to be zero.
 242 *
 243 * Return: 0 on success, negative error code on failure.
 244 */
 245int i915_ttm_buddy_man_reserve(struct ttm_resource_manager *man,
 246                               u64 start, u64 size)
 247{
 248        struct i915_ttm_buddy_manager *bman = to_buddy_manager(man);
 249        struct i915_buddy_mm *mm = &bman->mm;
 250        int ret;
 251
 252        mutex_lock(&bman->lock);
 253        ret = i915_buddy_alloc_range(mm, &bman->reserved, start, size);
 254        mutex_unlock(&bman->lock);
 255
 256        return ret;
 257}
 258
 259