323 lines · c
1/*2 * Copyright 2016 Advanced Micro Devices, Inc.3 *4 * Permission is hereby granted, free of charge, to any person obtaining a5 * copy of this software and associated documentation files (the "Software"),6 * to deal in the Software without restriction, including without limitation7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,8 * and/or sell copies of the Software, and to permit persons to whom the9 * Software is furnished to do so, subject to the following conditions:10 *11 * The above copyright notice and this permission notice shall be included in12 * all copies or substantial portions of the Software.13 *14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR20 * OTHER DEALINGS IN THE SOFTWARE.21 *22 * Authors: Christian König23 */24 25#include <drm/ttm/ttm_range_manager.h>26 27#include "amdgpu.h"28 29static inline struct amdgpu_gtt_mgr *30to_gtt_mgr(struct ttm_resource_manager *man)31{32 return container_of(man, struct amdgpu_gtt_mgr, manager);33}34 35/**36 * DOC: mem_info_gtt_total37 *38 * The amdgpu driver provides a sysfs API for reporting current total size of39 * the GTT.40 * The file mem_info_gtt_total is used for this, and returns the total size of41 * the GTT block, in bytes42 */43static ssize_t amdgpu_mem_info_gtt_total_show(struct device *dev,44 struct device_attribute *attr,45 char *buf)46{47 struct drm_device *ddev = dev_get_drvdata(dev);48 struct amdgpu_device *adev = drm_to_adev(ddev);49 struct ttm_resource_manager *man;50 51 man = ttm_manager_type(&adev->mman.bdev, TTM_PL_TT);52 return sysfs_emit(buf, "%llu\n", man->size);53}54 55/**56 * DOC: mem_info_gtt_used57 *58 * The amdgpu driver provides a sysfs API for reporting current total amount of59 * used GTT.60 * The file mem_info_gtt_used is used for this, and returns the current used61 * size of the GTT block, in bytes62 */63static ssize_t amdgpu_mem_info_gtt_used_show(struct device *dev,64 struct device_attribute *attr,65 char *buf)66{67 struct drm_device *ddev = dev_get_drvdata(dev);68 struct amdgpu_device *adev = drm_to_adev(ddev);69 struct ttm_resource_manager *man = &adev->mman.gtt_mgr.manager;70 71 return sysfs_emit(buf, "%llu\n", ttm_resource_manager_usage(man));72}73 74static DEVICE_ATTR(mem_info_gtt_total, S_IRUGO,75 amdgpu_mem_info_gtt_total_show, NULL);76static DEVICE_ATTR(mem_info_gtt_used, S_IRUGO,77 amdgpu_mem_info_gtt_used_show, NULL);78 79static struct attribute *amdgpu_gtt_mgr_attributes[] = {80 &dev_attr_mem_info_gtt_total.attr,81 &dev_attr_mem_info_gtt_used.attr,82 NULL83};84 85const struct attribute_group amdgpu_gtt_mgr_attr_group = {86 .attrs = amdgpu_gtt_mgr_attributes87};88 89/**90 * amdgpu_gtt_mgr_has_gart_addr - Check if mem has address space91 *92 * @res: the mem object to check93 *94 * Check if a mem object has already address space allocated.95 */96bool amdgpu_gtt_mgr_has_gart_addr(struct ttm_resource *res)97{98 struct ttm_range_mgr_node *node = to_ttm_range_mgr_node(res);99 100 return drm_mm_node_allocated(&node->mm_nodes[0]);101}102 103/**104 * amdgpu_gtt_mgr_new - allocate a new node105 *106 * @man: TTM memory type manager107 * @tbo: TTM BO we need this range for108 * @place: placement flags and restrictions109 * @res: the resulting mem object110 *111 * Dummy, allocate the node but no space for it yet.112 */113static int amdgpu_gtt_mgr_new(struct ttm_resource_manager *man,114 struct ttm_buffer_object *tbo,115 const struct ttm_place *place,116 struct ttm_resource **res)117{118 struct amdgpu_gtt_mgr *mgr = to_gtt_mgr(man);119 uint32_t num_pages = PFN_UP(tbo->base.size);120 struct ttm_range_mgr_node *node;121 int r;122 123 node = kzalloc(struct_size(node, mm_nodes, 1), GFP_KERNEL);124 if (!node)125 return -ENOMEM;126 127 ttm_resource_init(tbo, place, &node->base);128 if (!(place->flags & TTM_PL_FLAG_TEMPORARY) &&129 ttm_resource_manager_usage(man) > man->size) {130 r = -ENOSPC;131 goto err_free;132 }133 134 if (place->lpfn) {135 spin_lock(&mgr->lock);136 r = drm_mm_insert_node_in_range(&mgr->mm, &node->mm_nodes[0],137 num_pages, tbo->page_alignment,138 0, place->fpfn, place->lpfn,139 DRM_MM_INSERT_BEST);140 spin_unlock(&mgr->lock);141 if (unlikely(r))142 goto err_free;143 144 node->base.start = node->mm_nodes[0].start;145 } else {146 node->mm_nodes[0].start = 0;147 node->mm_nodes[0].size = PFN_UP(node->base.size);148 node->base.start = AMDGPU_BO_INVALID_OFFSET;149 }150 151 *res = &node->base;152 return 0;153 154err_free:155 ttm_resource_fini(man, &node->base);156 kfree(node);157 return r;158}159 160/**161 * amdgpu_gtt_mgr_del - free ranges162 *163 * @man: TTM memory type manager164 * @res: TTM memory object165 *166 * Free the allocated GTT again.167 */168static void amdgpu_gtt_mgr_del(struct ttm_resource_manager *man,169 struct ttm_resource *res)170{171 struct ttm_range_mgr_node *node = to_ttm_range_mgr_node(res);172 struct amdgpu_gtt_mgr *mgr = to_gtt_mgr(man);173 174 spin_lock(&mgr->lock);175 if (drm_mm_node_allocated(&node->mm_nodes[0]))176 drm_mm_remove_node(&node->mm_nodes[0]);177 spin_unlock(&mgr->lock);178 179 ttm_resource_fini(man, res);180 kfree(node);181}182 183/**184 * amdgpu_gtt_mgr_recover - re-init gart185 *186 * @mgr: amdgpu_gtt_mgr pointer187 *188 * Re-init the gart for each known BO in the GTT.189 */190void amdgpu_gtt_mgr_recover(struct amdgpu_gtt_mgr *mgr)191{192 struct ttm_range_mgr_node *node;193 struct drm_mm_node *mm_node;194 struct amdgpu_device *adev;195 196 adev = container_of(mgr, typeof(*adev), mman.gtt_mgr);197 spin_lock(&mgr->lock);198 drm_mm_for_each_node(mm_node, &mgr->mm) {199 node = container_of(mm_node, typeof(*node), mm_nodes[0]);200 amdgpu_ttm_recover_gart(node->base.bo);201 }202 spin_unlock(&mgr->lock);203}204 205/**206 * amdgpu_gtt_mgr_intersects - test for intersection207 *208 * @man: Our manager object209 * @res: The resource to test210 * @place: The place for the new allocation211 * @size: The size of the new allocation212 *213 * Simplified intersection test, only interesting if we need GART or not.214 */215static bool amdgpu_gtt_mgr_intersects(struct ttm_resource_manager *man,216 struct ttm_resource *res,217 const struct ttm_place *place,218 size_t size)219{220 return !place->lpfn || amdgpu_gtt_mgr_has_gart_addr(res);221}222 223/**224 * amdgpu_gtt_mgr_compatible - test for compatibility225 *226 * @man: Our manager object227 * @res: The resource to test228 * @place: The place for the new allocation229 * @size: The size of the new allocation230 *231 * Simplified compatibility test.232 */233static bool amdgpu_gtt_mgr_compatible(struct ttm_resource_manager *man,234 struct ttm_resource *res,235 const struct ttm_place *place,236 size_t size)237{238 return !place->lpfn || amdgpu_gtt_mgr_has_gart_addr(res);239}240 241/**242 * amdgpu_gtt_mgr_debug - dump VRAM table243 *244 * @man: TTM memory type manager245 * @printer: DRM printer to use246 *247 * Dump the table content using printk.248 */249static void amdgpu_gtt_mgr_debug(struct ttm_resource_manager *man,250 struct drm_printer *printer)251{252 struct amdgpu_gtt_mgr *mgr = to_gtt_mgr(man);253 254 spin_lock(&mgr->lock);255 drm_mm_print(&mgr->mm, printer);256 spin_unlock(&mgr->lock);257}258 259static const struct ttm_resource_manager_func amdgpu_gtt_mgr_func = {260 .alloc = amdgpu_gtt_mgr_new,261 .free = amdgpu_gtt_mgr_del,262 .intersects = amdgpu_gtt_mgr_intersects,263 .compatible = amdgpu_gtt_mgr_compatible,264 .debug = amdgpu_gtt_mgr_debug265};266 267/**268 * amdgpu_gtt_mgr_init - init GTT manager and DRM MM269 *270 * @adev: amdgpu_device pointer271 * @gtt_size: maximum size of GTT272 *273 * Allocate and initialize the GTT manager.274 */275int amdgpu_gtt_mgr_init(struct amdgpu_device *adev, uint64_t gtt_size)276{277 struct amdgpu_gtt_mgr *mgr = &adev->mman.gtt_mgr;278 struct ttm_resource_manager *man = &mgr->manager;279 uint64_t start, size;280 281 man->use_tt = true;282 man->func = &amdgpu_gtt_mgr_func;283 284 ttm_resource_manager_init(man, &adev->mman.bdev, gtt_size);285 286 start = AMDGPU_GTT_MAX_TRANSFER_SIZE * AMDGPU_GTT_NUM_TRANSFER_WINDOWS;287 size = (adev->gmc.gart_size >> PAGE_SHIFT) - start;288 drm_mm_init(&mgr->mm, start, size);289 spin_lock_init(&mgr->lock);290 291 ttm_set_driver_manager(&adev->mman.bdev, TTM_PL_TT, &mgr->manager);292 ttm_resource_manager_set_used(man, true);293 return 0;294}295 296/**297 * amdgpu_gtt_mgr_fini - free and destroy GTT manager298 *299 * @adev: amdgpu_device pointer300 *301 * Destroy and free the GTT manager, returns -EBUSY if ranges are still302 * allocated inside it.303 */304void amdgpu_gtt_mgr_fini(struct amdgpu_device *adev)305{306 struct amdgpu_gtt_mgr *mgr = &adev->mman.gtt_mgr;307 struct ttm_resource_manager *man = &mgr->manager;308 int ret;309 310 ttm_resource_manager_set_used(man, false);311 312 ret = ttm_resource_manager_evict_all(&adev->mman.bdev, man);313 if (ret)314 return;315 316 spin_lock(&mgr->lock);317 drm_mm_takedown(&mgr->mm);318 spin_unlock(&mgr->lock);319 320 ttm_resource_manager_cleanup(man);321 ttm_set_driver_manager(&adev->mman.bdev, TTM_PL_TT, NULL);322}323