xref: /dflybsd-src/sys/dev/drm/amd/amdgpu/amdgpu_vram_mgr.c (revision 789731325bde747251c28a37e0a00ed4efb88c46)
1b843c749SSergey Zigachev /*
2b843c749SSergey Zigachev  * Copyright 2016 Advanced Micro Devices, Inc.
3b843c749SSergey Zigachev  *
4b843c749SSergey Zigachev  * Permission is hereby granted, free of charge, to any person obtaining a
5b843c749SSergey Zigachev  * copy of this software and associated documentation files (the "Software"),
6b843c749SSergey Zigachev  * to deal in the Software without restriction, including without limitation
7b843c749SSergey Zigachev  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8b843c749SSergey Zigachev  * and/or sell copies of the Software, and to permit persons to whom the
9b843c749SSergey Zigachev  * Software is furnished to do so, subject to the following conditions:
10b843c749SSergey Zigachev  *
11b843c749SSergey Zigachev  * The above copyright notice and this permission notice shall be included in
12b843c749SSergey Zigachev  * all copies or substantial portions of the Software.
13b843c749SSergey Zigachev  *
14b843c749SSergey Zigachev  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15b843c749SSergey Zigachev  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16b843c749SSergey Zigachev  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17b843c749SSergey Zigachev  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18b843c749SSergey Zigachev  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19b843c749SSergey Zigachev  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20b843c749SSergey Zigachev  * OTHER DEALINGS IN THE SOFTWARE.
21b843c749SSergey Zigachev  *
22b843c749SSergey Zigachev  * Authors: Christian König
23b843c749SSergey Zigachev  */
24b843c749SSergey Zigachev 
25b843c749SSergey Zigachev #include <drm/drmP.h>
26b843c749SSergey Zigachev #include "amdgpu.h"
27b843c749SSergey Zigachev 
28b843c749SSergey Zigachev struct amdgpu_vram_mgr {
29b843c749SSergey Zigachev 	struct drm_mm mm;
30*78973132SSergey Zigachev 	struct spinlock lock;
31b843c749SSergey Zigachev 	atomic64_t usage;
32b843c749SSergey Zigachev 	atomic64_t vis_usage;
33b843c749SSergey Zigachev };
34b843c749SSergey Zigachev 
35b843c749SSergey Zigachev /**
36b843c749SSergey Zigachev  * amdgpu_vram_mgr_init - init VRAM manager and DRM MM
37b843c749SSergey Zigachev  *
38b843c749SSergey Zigachev  * @man: TTM memory type manager
39b843c749SSergey Zigachev  * @p_size: maximum size of VRAM
40b843c749SSergey Zigachev  *
41b843c749SSergey Zigachev  * Allocate and initialize the VRAM manager.
42b843c749SSergey Zigachev  */
amdgpu_vram_mgr_init(struct ttm_mem_type_manager * man,unsigned long p_size)43b843c749SSergey Zigachev static int amdgpu_vram_mgr_init(struct ttm_mem_type_manager *man,
44b843c749SSergey Zigachev 				unsigned long p_size)
45b843c749SSergey Zigachev {
46b843c749SSergey Zigachev 	struct amdgpu_vram_mgr *mgr;
47b843c749SSergey Zigachev 
48b843c749SSergey Zigachev 	mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
49b843c749SSergey Zigachev 	if (!mgr)
50b843c749SSergey Zigachev 		return -ENOMEM;
51b843c749SSergey Zigachev 
52b843c749SSergey Zigachev 	drm_mm_init(&mgr->mm, 0, p_size);
53*78973132SSergey Zigachev 	spin_init(&mgr->lock, "agvrml");
54b843c749SSergey Zigachev 	man->priv = mgr;
55b843c749SSergey Zigachev 	return 0;
56b843c749SSergey Zigachev }
57b843c749SSergey Zigachev 
58b843c749SSergey Zigachev /**
59b843c749SSergey Zigachev  * amdgpu_vram_mgr_fini - free and destroy VRAM manager
60b843c749SSergey Zigachev  *
61b843c749SSergey Zigachev  * @man: TTM memory type manager
62b843c749SSergey Zigachev  *
63b843c749SSergey Zigachev  * Destroy and free the VRAM manager, returns -EBUSY if ranges are still
64b843c749SSergey Zigachev  * allocated inside it.
65b843c749SSergey Zigachev  */
amdgpu_vram_mgr_fini(struct ttm_mem_type_manager * man)66b843c749SSergey Zigachev static int amdgpu_vram_mgr_fini(struct ttm_mem_type_manager *man)
67b843c749SSergey Zigachev {
68b843c749SSergey Zigachev 	struct amdgpu_vram_mgr *mgr = man->priv;
69b843c749SSergey Zigachev 
70b843c749SSergey Zigachev 	spin_lock(&mgr->lock);
71b843c749SSergey Zigachev 	drm_mm_takedown(&mgr->mm);
72b843c749SSergey Zigachev 	spin_unlock(&mgr->lock);
73b843c749SSergey Zigachev 	kfree(mgr);
74b843c749SSergey Zigachev 	man->priv = NULL;
75b843c749SSergey Zigachev 	return 0;
76b843c749SSergey Zigachev }
77b843c749SSergey Zigachev 
78b843c749SSergey Zigachev /**
79b843c749SSergey Zigachev  * amdgpu_vram_mgr_vis_size - Calculate visible node size
80b843c749SSergey Zigachev  *
81b843c749SSergey Zigachev  * @adev: amdgpu device structure
82b843c749SSergey Zigachev  * @node: MM node structure
83b843c749SSergey Zigachev  *
84b843c749SSergey Zigachev  * Calculate how many bytes of the MM node are inside visible VRAM
85b843c749SSergey Zigachev  */
amdgpu_vram_mgr_vis_size(struct amdgpu_device * adev,struct drm_mm_node * node)86b843c749SSergey Zigachev static u64 amdgpu_vram_mgr_vis_size(struct amdgpu_device *adev,
87b843c749SSergey Zigachev 				    struct drm_mm_node *node)
88b843c749SSergey Zigachev {
89b843c749SSergey Zigachev 	uint64_t start = node->start << PAGE_SHIFT;
90b843c749SSergey Zigachev 	uint64_t end = (node->size + node->start) << PAGE_SHIFT;
91b843c749SSergey Zigachev 
92b843c749SSergey Zigachev 	if (start >= adev->gmc.visible_vram_size)
93b843c749SSergey Zigachev 		return 0;
94b843c749SSergey Zigachev 
95b843c749SSergey Zigachev 	return (end > adev->gmc.visible_vram_size ?
96b843c749SSergey Zigachev 		adev->gmc.visible_vram_size : end) - start;
97b843c749SSergey Zigachev }
98b843c749SSergey Zigachev 
99b843c749SSergey Zigachev /**
100b843c749SSergey Zigachev  * amdgpu_vram_mgr_bo_visible_size - CPU visible BO size
101b843c749SSergey Zigachev  *
102b843c749SSergey Zigachev  * @bo: &amdgpu_bo buffer object (must be in VRAM)
103b843c749SSergey Zigachev  *
104b843c749SSergey Zigachev  * Returns:
105b843c749SSergey Zigachev  * How much of the given &amdgpu_bo buffer object lies in CPU visible VRAM.
106b843c749SSergey Zigachev  */
amdgpu_vram_mgr_bo_visible_size(struct amdgpu_bo * bo)107b843c749SSergey Zigachev u64 amdgpu_vram_mgr_bo_visible_size(struct amdgpu_bo *bo)
108b843c749SSergey Zigachev {
109b843c749SSergey Zigachev 	struct amdgpu_device *adev = amdgpu_ttm_adev(bo->tbo.bdev);
110b843c749SSergey Zigachev 	struct ttm_mem_reg *mem = &bo->tbo.mem;
111b843c749SSergey Zigachev 	struct drm_mm_node *nodes = mem->mm_node;
112b843c749SSergey Zigachev 	unsigned pages = mem->num_pages;
113b843c749SSergey Zigachev 	u64 usage;
114b843c749SSergey Zigachev 
115b843c749SSergey Zigachev 	if (amdgpu_gmc_vram_full_visible(&adev->gmc))
116b843c749SSergey Zigachev 		return amdgpu_bo_size(bo);
117b843c749SSergey Zigachev 
118b843c749SSergey Zigachev 	if (mem->start >= adev->gmc.visible_vram_size >> PAGE_SHIFT)
119b843c749SSergey Zigachev 		return 0;
120b843c749SSergey Zigachev 
121b843c749SSergey Zigachev 	for (usage = 0; nodes && pages; pages -= nodes->size, nodes++)
122b843c749SSergey Zigachev 		usage += amdgpu_vram_mgr_vis_size(adev, nodes);
123b843c749SSergey Zigachev 
124b843c749SSergey Zigachev 	return usage;
125b843c749SSergey Zigachev }
126b843c749SSergey Zigachev 
127b843c749SSergey Zigachev /**
128b843c749SSergey Zigachev  * amdgpu_vram_mgr_new - allocate new ranges
129b843c749SSergey Zigachev  *
130b843c749SSergey Zigachev  * @man: TTM memory type manager
131b843c749SSergey Zigachev  * @tbo: TTM BO we need this range for
132b843c749SSergey Zigachev  * @place: placement flags and restrictions
133b843c749SSergey Zigachev  * @mem: the resulting mem object
134b843c749SSergey Zigachev  *
135b843c749SSergey Zigachev  * Allocate VRAM for the given BO.
136b843c749SSergey Zigachev  */
amdgpu_vram_mgr_new(struct ttm_mem_type_manager * man,struct ttm_buffer_object * tbo,const struct ttm_place * place,struct ttm_mem_reg * mem)137b843c749SSergey Zigachev static int amdgpu_vram_mgr_new(struct ttm_mem_type_manager *man,
138b843c749SSergey Zigachev 			       struct ttm_buffer_object *tbo,
139b843c749SSergey Zigachev 			       const struct ttm_place *place,
140b843c749SSergey Zigachev 			       struct ttm_mem_reg *mem)
141b843c749SSergey Zigachev {
142b843c749SSergey Zigachev 	struct amdgpu_device *adev = amdgpu_ttm_adev(man->bdev);
143b843c749SSergey Zigachev 	struct amdgpu_vram_mgr *mgr = man->priv;
144b843c749SSergey Zigachev 	struct drm_mm *mm = &mgr->mm;
145b843c749SSergey Zigachev 	struct drm_mm_node *nodes;
146b843c749SSergey Zigachev 	enum drm_mm_insert_mode mode;
147b843c749SSergey Zigachev 	unsigned long lpfn, num_nodes, pages_per_node, pages_left;
148b843c749SSergey Zigachev 	uint64_t usage = 0, vis_usage = 0;
149b843c749SSergey Zigachev 	unsigned i;
150b843c749SSergey Zigachev 	int r;
151b843c749SSergey Zigachev 
152b843c749SSergey Zigachev 	lpfn = place->lpfn;
153b843c749SSergey Zigachev 	if (!lpfn)
154b843c749SSergey Zigachev 		lpfn = man->size;
155b843c749SSergey Zigachev 
156b843c749SSergey Zigachev 	if (place->flags & TTM_PL_FLAG_CONTIGUOUS ||
157b843c749SSergey Zigachev 	    amdgpu_vram_page_split == -1) {
158b843c749SSergey Zigachev 		pages_per_node = ~0ul;
159b843c749SSergey Zigachev 		num_nodes = 1;
160b843c749SSergey Zigachev 	} else {
161b843c749SSergey Zigachev 		pages_per_node = max((uint32_t)amdgpu_vram_page_split,
162b843c749SSergey Zigachev 				     mem->page_alignment);
163b843c749SSergey Zigachev 		num_nodes = DIV_ROUND_UP(mem->num_pages, pages_per_node);
164b843c749SSergey Zigachev 	}
165b843c749SSergey Zigachev 
166b843c749SSergey Zigachev 	nodes = kvmalloc_array(num_nodes, sizeof(*nodes),
167b843c749SSergey Zigachev 			       GFP_KERNEL | __GFP_ZERO);
168b843c749SSergey Zigachev 	if (!nodes)
169b843c749SSergey Zigachev 		return -ENOMEM;
170b843c749SSergey Zigachev 
171b843c749SSergey Zigachev 	mode = DRM_MM_INSERT_BEST;
172b843c749SSergey Zigachev 	if (place->flags & TTM_PL_FLAG_TOPDOWN)
173b843c749SSergey Zigachev 		mode = DRM_MM_INSERT_HIGH;
174b843c749SSergey Zigachev 
175b843c749SSergey Zigachev 	mem->start = 0;
176b843c749SSergey Zigachev 	pages_left = mem->num_pages;
177b843c749SSergey Zigachev 
178b843c749SSergey Zigachev 	spin_lock(&mgr->lock);
179b843c749SSergey Zigachev 	for (i = 0; i < num_nodes; ++i) {
180b843c749SSergey Zigachev 		unsigned long pages = min(pages_left, pages_per_node);
181b843c749SSergey Zigachev 		uint32_t alignment = mem->page_alignment;
182b843c749SSergey Zigachev 		unsigned long start;
183b843c749SSergey Zigachev 
184b843c749SSergey Zigachev 		if (pages == pages_per_node)
185b843c749SSergey Zigachev 			alignment = pages_per_node;
186b843c749SSergey Zigachev 
187b843c749SSergey Zigachev 		r = drm_mm_insert_node_in_range(mm, &nodes[i],
188b843c749SSergey Zigachev 						pages, alignment, 0,
189b843c749SSergey Zigachev 						place->fpfn, lpfn,
190b843c749SSergey Zigachev 						mode);
191b843c749SSergey Zigachev 		if (unlikely(r))
192b843c749SSergey Zigachev 			goto error;
193b843c749SSergey Zigachev 
194b843c749SSergey Zigachev 		usage += nodes[i].size << PAGE_SHIFT;
195b843c749SSergey Zigachev 		vis_usage += amdgpu_vram_mgr_vis_size(adev, &nodes[i]);
196b843c749SSergey Zigachev 
197b843c749SSergey Zigachev 		/* Calculate a virtual BO start address to easily check if
198b843c749SSergey Zigachev 		 * everything is CPU accessible.
199b843c749SSergey Zigachev 		 */
200b843c749SSergey Zigachev 		start = nodes[i].start + nodes[i].size;
201b843c749SSergey Zigachev 		if (start > mem->num_pages)
202b843c749SSergey Zigachev 			start -= mem->num_pages;
203b843c749SSergey Zigachev 		else
204b843c749SSergey Zigachev 			start = 0;
205b843c749SSergey Zigachev 		mem->start = max(mem->start, start);
206b843c749SSergey Zigachev 		pages_left -= pages;
207b843c749SSergey Zigachev 	}
208b843c749SSergey Zigachev 	spin_unlock(&mgr->lock);
209b843c749SSergey Zigachev 
210b843c749SSergey Zigachev 	atomic64_add(usage, &mgr->usage);
211b843c749SSergey Zigachev 	atomic64_add(vis_usage, &mgr->vis_usage);
212b843c749SSergey Zigachev 
213b843c749SSergey Zigachev 	mem->mm_node = nodes;
214b843c749SSergey Zigachev 
215b843c749SSergey Zigachev 	return 0;
216b843c749SSergey Zigachev 
217b843c749SSergey Zigachev error:
218b843c749SSergey Zigachev 	while (i--)
219b843c749SSergey Zigachev 		drm_mm_remove_node(&nodes[i]);
220b843c749SSergey Zigachev 	spin_unlock(&mgr->lock);
221b843c749SSergey Zigachev 
222b843c749SSergey Zigachev 	kvfree(nodes);
223b843c749SSergey Zigachev 	return r == -ENOSPC ? 0 : r;
224b843c749SSergey Zigachev }
225b843c749SSergey Zigachev 
226b843c749SSergey Zigachev /**
227b843c749SSergey Zigachev  * amdgpu_vram_mgr_del - free ranges
228b843c749SSergey Zigachev  *
229b843c749SSergey Zigachev  * @man: TTM memory type manager
230b843c749SSergey Zigachev  * @tbo: TTM BO we need this range for
231b843c749SSergey Zigachev  * @place: placement flags and restrictions
232b843c749SSergey Zigachev  * @mem: TTM memory object
233b843c749SSergey Zigachev  *
234b843c749SSergey Zigachev  * Free the allocated VRAM again.
235b843c749SSergey Zigachev  */
amdgpu_vram_mgr_del(struct ttm_mem_type_manager * man,struct ttm_mem_reg * mem)236b843c749SSergey Zigachev static void amdgpu_vram_mgr_del(struct ttm_mem_type_manager *man,
237b843c749SSergey Zigachev 				struct ttm_mem_reg *mem)
238b843c749SSergey Zigachev {
239b843c749SSergey Zigachev 	struct amdgpu_device *adev = amdgpu_ttm_adev(man->bdev);
240b843c749SSergey Zigachev 	struct amdgpu_vram_mgr *mgr = man->priv;
241b843c749SSergey Zigachev 	struct drm_mm_node *nodes = mem->mm_node;
242b843c749SSergey Zigachev 	uint64_t usage = 0, vis_usage = 0;
243b843c749SSergey Zigachev 	unsigned pages = mem->num_pages;
244b843c749SSergey Zigachev 
245b843c749SSergey Zigachev 	if (!mem->mm_node)
246b843c749SSergey Zigachev 		return;
247b843c749SSergey Zigachev 
248b843c749SSergey Zigachev 	spin_lock(&mgr->lock);
249b843c749SSergey Zigachev 	while (pages) {
250b843c749SSergey Zigachev 		pages -= nodes->size;
251b843c749SSergey Zigachev 		drm_mm_remove_node(nodes);
252b843c749SSergey Zigachev 		usage += nodes->size << PAGE_SHIFT;
253b843c749SSergey Zigachev 		vis_usage += amdgpu_vram_mgr_vis_size(adev, nodes);
254b843c749SSergey Zigachev 		++nodes;
255b843c749SSergey Zigachev 	}
256b843c749SSergey Zigachev 	spin_unlock(&mgr->lock);
257b843c749SSergey Zigachev 
258b843c749SSergey Zigachev 	atomic64_sub(usage, &mgr->usage);
259b843c749SSergey Zigachev 	atomic64_sub(vis_usage, &mgr->vis_usage);
260b843c749SSergey Zigachev 
261b843c749SSergey Zigachev 	kvfree(mem->mm_node);
262b843c749SSergey Zigachev 	mem->mm_node = NULL;
263b843c749SSergey Zigachev }
264b843c749SSergey Zigachev 
265b843c749SSergey Zigachev /**
266b843c749SSergey Zigachev  * amdgpu_vram_mgr_usage - how many bytes are used in this domain
267b843c749SSergey Zigachev  *
268b843c749SSergey Zigachev  * @man: TTM memory type manager
269b843c749SSergey Zigachev  *
270b843c749SSergey Zigachev  * Returns how many bytes are used in this domain.
271b843c749SSergey Zigachev  */
amdgpu_vram_mgr_usage(struct ttm_mem_type_manager * man)272b843c749SSergey Zigachev uint64_t amdgpu_vram_mgr_usage(struct ttm_mem_type_manager *man)
273b843c749SSergey Zigachev {
274b843c749SSergey Zigachev 	struct amdgpu_vram_mgr *mgr = man->priv;
275b843c749SSergey Zigachev 
276b843c749SSergey Zigachev 	return atomic64_read(&mgr->usage);
277b843c749SSergey Zigachev }
278b843c749SSergey Zigachev 
279b843c749SSergey Zigachev /**
280b843c749SSergey Zigachev  * amdgpu_vram_mgr_vis_usage - how many bytes are used in the visible part
281b843c749SSergey Zigachev  *
282b843c749SSergey Zigachev  * @man: TTM memory type manager
283b843c749SSergey Zigachev  *
284b843c749SSergey Zigachev  * Returns how many bytes are used in the visible part of VRAM
285b843c749SSergey Zigachev  */
amdgpu_vram_mgr_vis_usage(struct ttm_mem_type_manager * man)286b843c749SSergey Zigachev uint64_t amdgpu_vram_mgr_vis_usage(struct ttm_mem_type_manager *man)
287b843c749SSergey Zigachev {
288b843c749SSergey Zigachev 	struct amdgpu_vram_mgr *mgr = man->priv;
289b843c749SSergey Zigachev 
290b843c749SSergey Zigachev 	return atomic64_read(&mgr->vis_usage);
291b843c749SSergey Zigachev }
292b843c749SSergey Zigachev 
293b843c749SSergey Zigachev /**
294b843c749SSergey Zigachev  * amdgpu_vram_mgr_debug - dump VRAM table
295b843c749SSergey Zigachev  *
296b843c749SSergey Zigachev  * @man: TTM memory type manager
297b843c749SSergey Zigachev  * @printer: DRM printer to use
298b843c749SSergey Zigachev  *
299b843c749SSergey Zigachev  * Dump the table content using printk.
300b843c749SSergey Zigachev  */
amdgpu_vram_mgr_debug(struct ttm_mem_type_manager * man,struct drm_printer * printer)301b843c749SSergey Zigachev static void amdgpu_vram_mgr_debug(struct ttm_mem_type_manager *man,
302b843c749SSergey Zigachev 				  struct drm_printer *printer)
303b843c749SSergey Zigachev {
304b843c749SSergey Zigachev 	struct amdgpu_vram_mgr *mgr = man->priv;
305b843c749SSergey Zigachev 
306b843c749SSergey Zigachev 	spin_lock(&mgr->lock);
307b843c749SSergey Zigachev 	drm_mm_print(&mgr->mm, printer);
308b843c749SSergey Zigachev 	spin_unlock(&mgr->lock);
309b843c749SSergey Zigachev 
310*78973132SSergey Zigachev 	drm_printf(printer, "man size:%lu pages, ram usage:%luMB, vis usage:%luMB\n",
311b843c749SSergey Zigachev 		   man->size, amdgpu_vram_mgr_usage(man) >> 20,
312b843c749SSergey Zigachev 		   amdgpu_vram_mgr_vis_usage(man) >> 20);
313b843c749SSergey Zigachev }
314b843c749SSergey Zigachev 
315b843c749SSergey Zigachev const struct ttm_mem_type_manager_func amdgpu_vram_mgr_func = {
316b843c749SSergey Zigachev 	.init		= amdgpu_vram_mgr_init,
317b843c749SSergey Zigachev 	.takedown	= amdgpu_vram_mgr_fini,
318b843c749SSergey Zigachev 	.get_node	= amdgpu_vram_mgr_new,
319b843c749SSergey Zigachev 	.put_node	= amdgpu_vram_mgr_del,
320b843c749SSergey Zigachev 	.debug		= amdgpu_vram_mgr_debug
321b843c749SSergey Zigachev };
322