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_gtt_mgr { |
29 | struct drm_mm mm; |
30 | spinlock_t lock; |
31 | atomic64_t available; |
32 | }; |
33 | |
34 | struct amdgpu_gtt_node { |
35 | struct drm_mm_node node; |
36 | struct ttm_buffer_object *tbo; |
37 | }; |
38 | |
39 | /** |
40 | * amdgpu_gtt_mgr_init - init GTT manager and DRM MM |
41 | * |
42 | * @man: TTM memory type manager |
43 | * @p_size: maximum size of GTT |
44 | * |
45 | * Allocate and initialize the GTT manager. |
46 | */ |
47 | static int amdgpu_gtt_mgr_init(struct ttm_mem_type_manager *man, |
48 | unsigned long p_size) |
49 | { |
50 | struct amdgpu_device *adev = amdgpu_ttm_adev(man->bdev); |
51 | struct amdgpu_gtt_mgr *mgr; |
52 | uint64_t start, size; |
53 | |
54 | mgr = kzalloc(sizeof(*mgr), GFP_KERNEL); |
55 | if (!mgr) |
56 | return -ENOMEM; |
57 | |
58 | start = AMDGPU_GTT_MAX_TRANSFER_SIZE * AMDGPU_GTT_NUM_TRANSFER_WINDOWS; |
59 | size = (adev->gmc.gart_size >> PAGE_SHIFT) - start; |
60 | drm_mm_init(&mgr->mm, start, size); |
61 | spin_lock_init(&mgr->lock); |
62 | atomic64_set(&mgr->available, p_size); |
63 | man->priv = mgr; |
64 | return 0; |
65 | } |
66 | |
67 | /** |
68 | * amdgpu_gtt_mgr_fini - free and destroy GTT manager |
69 | * |
70 | * @man: TTM memory type manager |
71 | * |
72 | * Destroy and free the GTT manager, returns -EBUSY if ranges are still |
73 | * allocated inside it. |
74 | */ |
75 | static int amdgpu_gtt_mgr_fini(struct ttm_mem_type_manager *man) |
76 | { |
77 | struct amdgpu_gtt_mgr *mgr = man->priv; |
78 | spin_lock(&mgr->lock); |
79 | drm_mm_takedown(&mgr->mm); |
80 | spin_unlock(&mgr->lock); |
81 | kfree(mgr); |
82 | man->priv = NULL; |
83 | return 0; |
84 | } |
85 | |
86 | /** |
87 | * amdgpu_gtt_mgr_has_gart_addr - Check if mem has address space |
88 | * |
89 | * @mem: the mem object to check |
90 | * |
91 | * Check if a mem object has already address space allocated. |
92 | */ |
93 | bool amdgpu_gtt_mgr_has_gart_addr(struct ttm_mem_reg *mem) |
94 | { |
95 | struct amdgpu_gtt_node *node = mem->mm_node; |
96 | |
97 | return (node->node.start != AMDGPU_BO_INVALID_OFFSET); |
98 | } |
99 | |
100 | /** |
101 | * amdgpu_gtt_mgr_alloc - allocate new ranges |
102 | * |
103 | * @man: TTM memory type manager |
104 | * @tbo: TTM BO we need this range for |
105 | * @place: placement flags and restrictions |
106 | * @mem: the resulting mem object |
107 | * |
108 | * Allocate the address space for a node. |
109 | */ |
110 | static int amdgpu_gtt_mgr_alloc(struct ttm_mem_type_manager *man, |
111 | struct ttm_buffer_object *tbo, |
112 | const struct ttm_place *place, |
113 | struct ttm_mem_reg *mem) |
114 | { |
115 | struct amdgpu_device *adev = amdgpu_ttm_adev(man->bdev); |
116 | struct amdgpu_gtt_mgr *mgr = man->priv; |
117 | struct amdgpu_gtt_node *node = mem->mm_node; |
118 | enum drm_mm_insert_mode mode; |
119 | unsigned long fpfn, lpfn; |
120 | int r; |
121 | |
122 | if (amdgpu_gtt_mgr_has_gart_addr(mem)) |
123 | return 0; |
124 | |
125 | if (place) |
126 | fpfn = place->fpfn; |
127 | else |
128 | fpfn = 0; |
129 | |
130 | if (place && place->lpfn) |
131 | lpfn = place->lpfn; |
132 | else |
133 | lpfn = adev->gart.num_cpu_pages; |
134 | |
135 | mode = DRM_MM_INSERT_BEST; |
136 | if (place && place->flags & TTM_PL_FLAG_TOPDOWN) |
137 | mode = DRM_MM_INSERT_HIGH; |
138 | |
139 | spin_lock(&mgr->lock); |
140 | r = drm_mm_insert_node_in_range(&mgr->mm, &node->node, mem->num_pages, |
141 | mem->page_alignment, 0, fpfn, lpfn, |
142 | mode); |
143 | spin_unlock(&mgr->lock); |
144 | |
145 | if (!r) |
146 | mem->start = node->node.start; |
147 | |
148 | return r; |
149 | } |
150 | |
151 | /** |
152 | * amdgpu_gtt_mgr_new - allocate a new node |
153 | * |
154 | * @man: TTM memory type manager |
155 | * @tbo: TTM BO we need this range for |
156 | * @place: placement flags and restrictions |
157 | * @mem: the resulting mem object |
158 | * |
159 | * Dummy, allocate the node but no space for it yet. |
160 | */ |
161 | static int amdgpu_gtt_mgr_new(struct ttm_mem_type_manager *man, |
162 | struct ttm_buffer_object *tbo, |
163 | const struct ttm_place *place, |
164 | struct ttm_mem_reg *mem) |
165 | { |
166 | struct amdgpu_gtt_mgr *mgr = man->priv; |
167 | struct amdgpu_gtt_node *node; |
168 | int r; |
169 | |
170 | spin_lock(&mgr->lock); |
171 | if ((&tbo->mem == mem || tbo->mem.mem_type != TTM_PL_TT) && |
172 | atomic64_read(&mgr->available) < mem->num_pages) { |
173 | spin_unlock(&mgr->lock); |
174 | return 0; |
175 | } |
176 | atomic64_sub(mem->num_pages, &mgr->available); |
177 | spin_unlock(&mgr->lock); |
178 | |
179 | node = kzalloc(sizeof(*node), GFP_KERNEL); |
180 | if (!node) { |
181 | r = -ENOMEM; |
182 | goto err_out; |
183 | } |
184 | |
185 | node->node.start = AMDGPU_BO_INVALID_OFFSET; |
186 | node->node.size = mem->num_pages; |
187 | node->tbo = tbo; |
188 | mem->mm_node = node; |
189 | |
190 | if (place->fpfn || place->lpfn || place->flags & TTM_PL_FLAG_TOPDOWN) { |
191 | r = amdgpu_gtt_mgr_alloc(man, tbo, place, mem); |
192 | if (unlikely(r)) { |
193 | kfree(node); |
194 | mem->mm_node = NULL; |
195 | r = 0; |
196 | goto err_out; |
197 | } |
198 | } else { |
199 | mem->start = node->node.start; |
200 | } |
201 | |
202 | return 0; |
203 | err_out: |
204 | atomic64_add(mem->num_pages, &mgr->available); |
205 | |
206 | return r; |
207 | } |
208 | |
209 | /** |
210 | * amdgpu_gtt_mgr_del - free ranges |
211 | * |
212 | * @man: TTM memory type manager |
213 | * @tbo: TTM BO we need this range for |
214 | * @place: placement flags and restrictions |
215 | * @mem: TTM memory object |
216 | * |
217 | * Free the allocated GTT again. |
218 | */ |
219 | static void amdgpu_gtt_mgr_del(struct ttm_mem_type_manager *man, |
220 | struct ttm_mem_reg *mem) |
221 | { |
222 | struct amdgpu_gtt_mgr *mgr = man->priv; |
223 | struct amdgpu_gtt_node *node = mem->mm_node; |
224 | |
225 | if (!node) |
226 | return; |
227 | |
228 | spin_lock(&mgr->lock); |
229 | if (node->node.start != AMDGPU_BO_INVALID_OFFSET) |
230 | drm_mm_remove_node(&node->node); |
231 | spin_unlock(&mgr->lock); |
232 | atomic64_add(mem->num_pages, &mgr->available); |
233 | |
234 | kfree(node); |
235 | mem->mm_node = NULL; |
236 | } |
237 | |
238 | /** |
239 | * amdgpu_gtt_mgr_usage - return usage of GTT domain |
240 | * |
241 | * @man: TTM memory type manager |
242 | * |
243 | * Return how many bytes are used in the GTT domain |
244 | */ |
245 | uint64_t amdgpu_gtt_mgr_usage(struct ttm_mem_type_manager *man) |
246 | { |
247 | struct amdgpu_gtt_mgr *mgr = man->priv; |
248 | s64 result = man->size - atomic64_read(&mgr->available); |
249 | |
250 | return (result > 0 ? result : 0) * PAGE_SIZE; |
251 | } |
252 | |
253 | int amdgpu_gtt_mgr_recover(struct ttm_mem_type_manager *man) |
254 | { |
255 | struct amdgpu_gtt_mgr *mgr = man->priv; |
256 | struct amdgpu_gtt_node *node; |
257 | struct drm_mm_node *mm_node; |
258 | int r = 0; |
259 | |
260 | spin_lock(&mgr->lock); |
261 | drm_mm_for_each_node(mm_node, &mgr->mm) { |
262 | node = container_of(mm_node, struct amdgpu_gtt_node, node); |
263 | r = amdgpu_ttm_recover_gart(node->tbo); |
264 | if (r) |
265 | break; |
266 | } |
267 | spin_unlock(&mgr->lock); |
268 | |
269 | return r; |
270 | } |
271 | |
272 | /** |
273 | * amdgpu_gtt_mgr_debug - dump VRAM table |
274 | * |
275 | * @man: TTM memory type manager |
276 | * @printer: DRM printer to use |
277 | * |
278 | * Dump the table content using printk. |
279 | */ |
280 | static void amdgpu_gtt_mgr_debug(struct ttm_mem_type_manager *man, |
281 | struct drm_printer *printer) |
282 | { |
283 | struct amdgpu_gtt_mgr *mgr = man->priv; |
284 | |
285 | spin_lock(&mgr->lock); |
286 | drm_mm_print(&mgr->mm, printer); |
287 | spin_unlock(&mgr->lock); |
288 | |
289 | drm_printf(printer, "man size:%llu pages, gtt available:%lld pages, usage:%lluMB\n" , |
290 | man->size, (u64)atomic64_read(&mgr->available), |
291 | amdgpu_gtt_mgr_usage(man) >> 20); |
292 | } |
293 | |
294 | const struct ttm_mem_type_manager_func amdgpu_gtt_mgr_func = { |
295 | .init = amdgpu_gtt_mgr_init, |
296 | .takedown = amdgpu_gtt_mgr_fini, |
297 | .get_node = amdgpu_gtt_mgr_new, |
298 | .put_node = amdgpu_gtt_mgr_del, |
299 | .debug = amdgpu_gtt_mgr_debug |
300 | }; |
301 | |