1 /* 2 * Copyright 2016 Advanced Micro Devices, Inc. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in 12 * all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 18 * 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 OR 20 * OTHER DEALINGS IN THE SOFTWARE. 21 * 22 * Authors: Christian König 23 */ 24 25 #include <drm/drmP.h> 26 #include "amdgpu.h" 27 28 struct amdgpu_vram_mgr { 29 struct drm_mm mm; 30 spinlock_t lock; 31 }; 32 33 /** 34 * amdgpu_vram_mgr_init - init VRAM manager and DRM MM 35 * 36 * @man: TTM memory type manager 37 * @p_size: maximum size of VRAM 38 * 39 * Allocate and initialize the VRAM manager. 40 */ 41 static int amdgpu_vram_mgr_init(struct ttm_mem_type_manager *man, 42 unsigned long p_size) 43 { 44 struct amdgpu_vram_mgr *mgr; 45 46 mgr = kzalloc(sizeof(*mgr), GFP_KERNEL); 47 if (!mgr) 48 return -ENOMEM; 49 50 drm_mm_init(&mgr->mm, 0, p_size); 51 spin_lock_init(&mgr->lock); 52 man->priv = mgr; 53 return 0; 54 } 55 56 /** 57 * amdgpu_vram_mgr_fini - free and destroy VRAM manager 58 * 59 * @man: TTM memory type manager 60 * 61 * Destroy and free the VRAM manager, returns -EBUSY if ranges are still 62 * allocated inside it. 63 */ 64 static int amdgpu_vram_mgr_fini(struct ttm_mem_type_manager *man) 65 { 66 struct amdgpu_vram_mgr *mgr = man->priv; 67 68 spin_lock(&mgr->lock); 69 if (!drm_mm_clean(&mgr->mm)) { 70 spin_unlock(&mgr->lock); 71 return -EBUSY; 72 } 73 74 drm_mm_takedown(&mgr->mm); 75 spin_unlock(&mgr->lock); 76 kfree(mgr); 77 man->priv = NULL; 78 return 0; 79 } 80 81 /** 82 * amdgpu_vram_mgr_new - allocate new ranges 83 * 84 * @man: TTM memory type manager 85 * @tbo: TTM BO we need this range for 86 * @place: placement flags and restrictions 87 * @mem: the resulting mem object 88 * 89 * Allocate VRAM for the given BO. 90 */ 91 static int amdgpu_vram_mgr_new(struct ttm_mem_type_manager *man, 92 struct ttm_buffer_object *tbo, 93 const struct ttm_place *place, 94 struct ttm_mem_reg *mem) 95 { 96 struct amdgpu_bo *bo = container_of(tbo, struct amdgpu_bo, tbo); 97 struct amdgpu_vram_mgr *mgr = man->priv; 98 struct drm_mm *mm = &mgr->mm; 99 struct drm_mm_node *nodes; 100 enum drm_mm_search_flags sflags = DRM_MM_SEARCH_DEFAULT; 101 enum drm_mm_allocator_flags aflags = DRM_MM_CREATE_DEFAULT; 102 unsigned long lpfn, num_nodes, pages_per_node, pages_left; 103 unsigned i; 104 int r; 105 106 lpfn = place->lpfn; 107 if (!lpfn) 108 lpfn = man->size; 109 110 if (bo->flags & AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS || 111 amdgpu_vram_page_split == -1) { 112 pages_per_node = ~0ul; 113 num_nodes = 1; 114 } else { 115 pages_per_node = max((uint32_t)amdgpu_vram_page_split, 116 mem->page_alignment); 117 num_nodes = DIV_ROUND_UP(mem->num_pages, pages_per_node); 118 } 119 120 nodes = kcalloc(num_nodes, sizeof(*nodes), GFP_KERNEL); 121 if (!nodes) 122 return -ENOMEM; 123 124 if (place->flags & TTM_PL_FLAG_TOPDOWN) { 125 sflags = DRM_MM_SEARCH_BELOW; 126 aflags = DRM_MM_CREATE_TOP; 127 } 128 129 pages_left = mem->num_pages; 130 131 spin_lock(&mgr->lock); 132 for (i = 0; i < num_nodes; ++i) { 133 unsigned long pages = min(pages_left, pages_per_node); 134 uint32_t alignment = mem->page_alignment; 135 136 if (pages == pages_per_node) 137 alignment = pages_per_node; 138 else 139 sflags |= DRM_MM_SEARCH_BEST; 140 141 r = drm_mm_insert_node_in_range_generic(mm, &nodes[i], pages, 142 alignment, 0, 143 place->fpfn, lpfn, 144 sflags, aflags); 145 if (unlikely(r)) 146 goto error; 147 148 pages_left -= pages; 149 } 150 spin_unlock(&mgr->lock); 151 152 mem->start = num_nodes == 1 ? nodes[0].start : AMDGPU_BO_INVALID_OFFSET; 153 mem->mm_node = nodes; 154 155 return 0; 156 157 error: 158 while (i--) 159 drm_mm_remove_node(&nodes[i]); 160 spin_unlock(&mgr->lock); 161 162 kfree(nodes); 163 return r == -ENOSPC ? 0 : r; 164 } 165 166 /** 167 * amdgpu_vram_mgr_del - free ranges 168 * 169 * @man: TTM memory type manager 170 * @tbo: TTM BO we need this range for 171 * @place: placement flags and restrictions 172 * @mem: TTM memory object 173 * 174 * Free the allocated VRAM again. 175 */ 176 static void amdgpu_vram_mgr_del(struct ttm_mem_type_manager *man, 177 struct ttm_mem_reg *mem) 178 { 179 struct amdgpu_vram_mgr *mgr = man->priv; 180 struct drm_mm_node *nodes = mem->mm_node; 181 unsigned pages = mem->num_pages; 182 183 if (!mem->mm_node) 184 return; 185 186 spin_lock(&mgr->lock); 187 while (pages) { 188 pages -= nodes->size; 189 drm_mm_remove_node(nodes); 190 ++nodes; 191 } 192 spin_unlock(&mgr->lock); 193 194 kfree(mem->mm_node); 195 mem->mm_node = NULL; 196 } 197 198 /** 199 * amdgpu_vram_mgr_debug - dump VRAM table 200 * 201 * @man: TTM memory type manager 202 * @prefix: text prefix 203 * 204 * Dump the table content using printk. 205 */ 206 static void amdgpu_vram_mgr_debug(struct ttm_mem_type_manager *man, 207 const char *prefix) 208 { 209 struct amdgpu_vram_mgr *mgr = man->priv; 210 211 spin_lock(&mgr->lock); 212 drm_mm_debug_table(&mgr->mm, prefix); 213 spin_unlock(&mgr->lock); 214 } 215 216 const struct ttm_mem_type_manager_func amdgpu_vram_mgr_func = { 217 amdgpu_vram_mgr_init, 218 amdgpu_vram_mgr_fini, 219 amdgpu_vram_mgr_new, 220 amdgpu_vram_mgr_del, 221 amdgpu_vram_mgr_debug 222 }; 223