linux/drivers/gpu/drm/ttm/ttm_range_manager.c
<<
>>
Prefs
   1/* SPDX-License-Identifier: GPL-2.0 OR MIT */
   2/**************************************************************************
   3 *
   4 * Copyright (c) 2007-2010 VMware, Inc., Palo Alto, CA., USA
   5 * All Rights Reserved.
   6 *
   7 * Permission is hereby granted, free of charge, to any person obtaining a
   8 * copy of this software and associated documentation files (the
   9 * "Software"), to deal in the Software without restriction, including
  10 * without limitation the rights to use, copy, modify, merge, publish,
  11 * distribute, sub license, and/or sell copies of the Software, and to
  12 * permit persons to whom the Software is furnished to do so, subject to
  13 * the following conditions:
  14 *
  15 * The above copyright notice and this permission notice (including the
  16 * next paragraph) shall be included in all copies or substantial portions
  17 * of the Software.
  18 *
  19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
  22 * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
  23 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  24 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
  25 * USE OR OTHER DEALINGS IN THE SOFTWARE.
  26 *
  27 **************************************************************************/
  28/*
  29 * Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com>
  30 */
  31
  32#include <drm/ttm/ttm_bo_driver.h>
  33#include <drm/ttm/ttm_placement.h>
  34#include <drm/drm_mm.h>
  35#include <linux/slab.h>
  36#include <linux/spinlock.h>
  37#include <linux/module.h>
  38
  39/*
  40 * Currently we use a spinlock for the lock, but a mutex *may* be
  41 * more appropriate to reduce scheduling latency if the range manager
  42 * ends up with very fragmented allocation patterns.
  43 */
  44
  45struct ttm_range_manager {
  46        struct ttm_resource_manager manager;
  47        struct drm_mm mm;
  48        spinlock_t lock;
  49};
  50
  51static inline struct ttm_range_manager *to_range_manager(struct ttm_resource_manager *man)
  52{
  53        return container_of(man, struct ttm_range_manager, manager);
  54}
  55
  56static int ttm_range_man_alloc(struct ttm_resource_manager *man,
  57                               struct ttm_buffer_object *bo,
  58                               const struct ttm_place *place,
  59                               struct ttm_resource *mem)
  60{
  61        struct ttm_range_manager *rman = to_range_manager(man);
  62        struct drm_mm *mm = &rman->mm;
  63        struct drm_mm_node *node;
  64        enum drm_mm_insert_mode mode;
  65        unsigned long lpfn;
  66        int ret;
  67
  68        lpfn = place->lpfn;
  69        if (!lpfn)
  70                lpfn = man->size;
  71
  72        node = kzalloc(sizeof(*node), GFP_KERNEL);
  73        if (!node)
  74                return -ENOMEM;
  75
  76        mode = DRM_MM_INSERT_BEST;
  77        if (place->flags & TTM_PL_FLAG_TOPDOWN)
  78                mode = DRM_MM_INSERT_HIGH;
  79
  80        spin_lock(&rman->lock);
  81        ret = drm_mm_insert_node_in_range(mm, node,
  82                                          mem->num_pages,
  83                                          mem->page_alignment, 0,
  84                                          place->fpfn, lpfn, mode);
  85        spin_unlock(&rman->lock);
  86
  87        if (unlikely(ret)) {
  88                kfree(node);
  89        } else {
  90                mem->mm_node = node;
  91                mem->start = node->start;
  92        }
  93
  94        return ret;
  95}
  96
  97static void ttm_range_man_free(struct ttm_resource_manager *man,
  98                               struct ttm_resource *mem)
  99{
 100        struct ttm_range_manager *rman = to_range_manager(man);
 101
 102        if (mem->mm_node) {
 103                spin_lock(&rman->lock);
 104                drm_mm_remove_node(mem->mm_node);
 105                spin_unlock(&rman->lock);
 106
 107                kfree(mem->mm_node);
 108                mem->mm_node = NULL;
 109        }
 110}
 111
 112static const struct ttm_resource_manager_func ttm_range_manager_func;
 113
 114int ttm_range_man_init(struct ttm_device *bdev,
 115                       unsigned type, bool use_tt,
 116                       unsigned long p_size)
 117{
 118        struct ttm_resource_manager *man;
 119        struct ttm_range_manager *rman;
 120
 121        rman = kzalloc(sizeof(*rman), GFP_KERNEL);
 122        if (!rman)
 123                return -ENOMEM;
 124
 125        man = &rman->manager;
 126        man->use_tt = use_tt;
 127
 128        man->func = &ttm_range_manager_func;
 129
 130        ttm_resource_manager_init(man, p_size);
 131
 132        drm_mm_init(&rman->mm, 0, p_size);
 133        spin_lock_init(&rman->lock);
 134
 135        ttm_set_driver_manager(bdev, type, &rman->manager);
 136        ttm_resource_manager_set_used(man, true);
 137        return 0;
 138}
 139EXPORT_SYMBOL(ttm_range_man_init);
 140
 141int ttm_range_man_fini(struct ttm_device *bdev,
 142                       unsigned type)
 143{
 144        struct ttm_resource_manager *man = ttm_manager_type(bdev, type);
 145        struct ttm_range_manager *rman = to_range_manager(man);
 146        struct drm_mm *mm = &rman->mm;
 147        int ret;
 148
 149        ttm_resource_manager_set_used(man, false);
 150
 151        ret = ttm_resource_manager_evict_all(bdev, man);
 152        if (ret)
 153                return ret;
 154
 155        spin_lock(&rman->lock);
 156        drm_mm_clean(mm);
 157        drm_mm_takedown(mm);
 158        spin_unlock(&rman->lock);
 159
 160        ttm_resource_manager_cleanup(man);
 161        ttm_set_driver_manager(bdev, type, NULL);
 162        kfree(rman);
 163        return 0;
 164}
 165EXPORT_SYMBOL(ttm_range_man_fini);
 166
 167static void ttm_range_man_debug(struct ttm_resource_manager *man,
 168                                struct drm_printer *printer)
 169{
 170        struct ttm_range_manager *rman = to_range_manager(man);
 171
 172        spin_lock(&rman->lock);
 173        drm_mm_print(&rman->mm, printer);
 174        spin_unlock(&rman->lock);
 175}
 176
 177static const struct ttm_resource_manager_func ttm_range_manager_func = {
 178        .alloc = ttm_range_man_alloc,
 179        .free = ttm_range_man_free,
 180        .debug = ttm_range_man_debug
 181};
 182