16a7f76e7SChristian König /*
26a7f76e7SChristian König  * Copyright 2016 Advanced Micro Devices, Inc.
36a7f76e7SChristian König  *
46a7f76e7SChristian König  * Permission is hereby granted, free of charge, to any person obtaining a
56a7f76e7SChristian König  * copy of this software and associated documentation files (the "Software"),
66a7f76e7SChristian König  * to deal in the Software without restriction, including without limitation
76a7f76e7SChristian König  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
86a7f76e7SChristian König  * and/or sell copies of the Software, and to permit persons to whom the
96a7f76e7SChristian König  * Software is furnished to do so, subject to the following conditions:
106a7f76e7SChristian König  *
116a7f76e7SChristian König  * The above copyright notice and this permission notice shall be included in
126a7f76e7SChristian König  * all copies or substantial portions of the Software.
136a7f76e7SChristian König  *
146a7f76e7SChristian König  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
156a7f76e7SChristian König  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
166a7f76e7SChristian König  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
176a7f76e7SChristian König  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
186a7f76e7SChristian König  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
196a7f76e7SChristian König  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
206a7f76e7SChristian König  * OTHER DEALINGS IN THE SOFTWARE.
216a7f76e7SChristian König  *
226a7f76e7SChristian König  * Authors: Christian König
236a7f76e7SChristian König  */
246a7f76e7SChristian König 
256a7f76e7SChristian König #include <drm/drmP.h>
266a7f76e7SChristian König #include "amdgpu.h"
276a7f76e7SChristian König 
286a7f76e7SChristian König struct amdgpu_vram_mgr {
296a7f76e7SChristian König 	struct drm_mm mm;
306a7f76e7SChristian König 	spinlock_t lock;
313c848bb3SChristian König 	atomic64_t usage;
323c848bb3SChristian König 	atomic64_t vis_usage;
336a7f76e7SChristian König };
346a7f76e7SChristian König 
356a7f76e7SChristian König /**
3655c374e9SKent Russell  * DOC: mem_info_vram_total
3755c374e9SKent Russell  *
3855c374e9SKent Russell  * The amdgpu driver provides a sysfs API for reporting current total VRAM
3955c374e9SKent Russell  * available on the device
4055c374e9SKent Russell  * The file mem_info_vram_total is used for this and returns the total
4155c374e9SKent Russell  * amount of VRAM in bytes
4255c374e9SKent Russell  */
4355c374e9SKent Russell static ssize_t amdgpu_mem_info_vram_total_show(struct device *dev,
4455c374e9SKent Russell 		struct device_attribute *attr, char *buf)
4555c374e9SKent Russell {
4655c374e9SKent Russell 	struct drm_device *ddev = dev_get_drvdata(dev);
4755c374e9SKent Russell 	struct amdgpu_device *adev = ddev->dev_private;
4855c374e9SKent Russell 
4955c374e9SKent Russell 	return snprintf(buf, PAGE_SIZE, "%llu\n", adev->gmc.real_vram_size);
5055c374e9SKent Russell }
5155c374e9SKent Russell 
5255c374e9SKent Russell /**
5355c374e9SKent Russell  * DOC: mem_info_vis_vram_total
5455c374e9SKent Russell  *
5555c374e9SKent Russell  * The amdgpu driver provides a sysfs API for reporting current total
5655c374e9SKent Russell  * visible VRAM available on the device
5755c374e9SKent Russell  * The file mem_info_vis_vram_total is used for this and returns the total
5855c374e9SKent Russell  * amount of visible VRAM in bytes
5955c374e9SKent Russell  */
6055c374e9SKent Russell static ssize_t amdgpu_mem_info_vis_vram_total_show(struct device *dev,
6155c374e9SKent Russell 		struct device_attribute *attr, char *buf)
6255c374e9SKent Russell {
6355c374e9SKent Russell 	struct drm_device *ddev = dev_get_drvdata(dev);
6455c374e9SKent Russell 	struct amdgpu_device *adev = ddev->dev_private;
6555c374e9SKent Russell 
6655c374e9SKent Russell 	return snprintf(buf, PAGE_SIZE, "%llu\n", adev->gmc.visible_vram_size);
6755c374e9SKent Russell }
6855c374e9SKent Russell 
6955c374e9SKent Russell /**
7055c374e9SKent Russell  * DOC: mem_info_vram_used
7155c374e9SKent Russell  *
7255c374e9SKent Russell  * The amdgpu driver provides a sysfs API for reporting current total VRAM
7355c374e9SKent Russell  * available on the device
7455c374e9SKent Russell  * The file mem_info_vram_used is used for this and returns the total
7555c374e9SKent Russell  * amount of currently used VRAM in bytes
7655c374e9SKent Russell  */
7755c374e9SKent Russell static ssize_t amdgpu_mem_info_vram_used_show(struct device *dev,
7855c374e9SKent Russell 		struct device_attribute *attr, char *buf)
7955c374e9SKent Russell {
8055c374e9SKent Russell 	struct drm_device *ddev = dev_get_drvdata(dev);
8155c374e9SKent Russell 	struct amdgpu_device *adev = ddev->dev_private;
8255c374e9SKent Russell 
8355c374e9SKent Russell 	return snprintf(buf, PAGE_SIZE, "%llu\n",
8455c374e9SKent Russell 		amdgpu_vram_mgr_usage(&adev->mman.bdev.man[TTM_PL_VRAM]));
8555c374e9SKent Russell }
8655c374e9SKent Russell 
8755c374e9SKent Russell /**
8855c374e9SKent Russell  * DOC: mem_info_vis_vram_used
8955c374e9SKent Russell  *
9055c374e9SKent Russell  * The amdgpu driver provides a sysfs API for reporting current total of
9155c374e9SKent Russell  * used visible VRAM
9255c374e9SKent Russell  * The file mem_info_vis_vram_used is used for this and returns the total
9355c374e9SKent Russell  * amount of currently used visible VRAM in bytes
9455c374e9SKent Russell  */
9555c374e9SKent Russell static ssize_t amdgpu_mem_info_vis_vram_used_show(struct device *dev,
9655c374e9SKent Russell 		struct device_attribute *attr, char *buf)
9755c374e9SKent Russell {
9855c374e9SKent Russell 	struct drm_device *ddev = dev_get_drvdata(dev);
9955c374e9SKent Russell 	struct amdgpu_device *adev = ddev->dev_private;
10055c374e9SKent Russell 
10155c374e9SKent Russell 	return snprintf(buf, PAGE_SIZE, "%llu\n",
10255c374e9SKent Russell 		amdgpu_vram_mgr_vis_usage(&adev->mman.bdev.man[TTM_PL_VRAM]));
10355c374e9SKent Russell }
10455c374e9SKent Russell 
10555c374e9SKent Russell static DEVICE_ATTR(mem_info_vram_total, S_IRUGO,
10655c374e9SKent Russell 		   amdgpu_mem_info_vram_total_show, NULL);
10755c374e9SKent Russell static DEVICE_ATTR(mem_info_vis_vram_total, S_IRUGO,
10855c374e9SKent Russell 		   amdgpu_mem_info_vis_vram_total_show,NULL);
10955c374e9SKent Russell static DEVICE_ATTR(mem_info_vram_used, S_IRUGO,
11055c374e9SKent Russell 		   amdgpu_mem_info_vram_used_show, NULL);
11155c374e9SKent Russell static DEVICE_ATTR(mem_info_vis_vram_used, S_IRUGO,
11255c374e9SKent Russell 		   amdgpu_mem_info_vis_vram_used_show, NULL);
11355c374e9SKent Russell 
11455c374e9SKent Russell /**
1156a7f76e7SChristian König  * amdgpu_vram_mgr_init - init VRAM manager and DRM MM
1166a7f76e7SChristian König  *
1176a7f76e7SChristian König  * @man: TTM memory type manager
1186a7f76e7SChristian König  * @p_size: maximum size of VRAM
1196a7f76e7SChristian König  *
1206a7f76e7SChristian König  * Allocate and initialize the VRAM manager.
1216a7f76e7SChristian König  */
1226a7f76e7SChristian König static int amdgpu_vram_mgr_init(struct ttm_mem_type_manager *man,
1236a7f76e7SChristian König 				unsigned long p_size)
1246a7f76e7SChristian König {
12555c374e9SKent Russell 	struct amdgpu_device *adev = amdgpu_ttm_adev(man->bdev);
1266a7f76e7SChristian König 	struct amdgpu_vram_mgr *mgr;
12755c374e9SKent Russell 	int ret;
1286a7f76e7SChristian König 
1296a7f76e7SChristian König 	mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
1306a7f76e7SChristian König 	if (!mgr)
1316a7f76e7SChristian König 		return -ENOMEM;
1326a7f76e7SChristian König 
1336a7f76e7SChristian König 	drm_mm_init(&mgr->mm, 0, p_size);
1346a7f76e7SChristian König 	spin_lock_init(&mgr->lock);
1356a7f76e7SChristian König 	man->priv = mgr;
13655c374e9SKent Russell 
13755c374e9SKent Russell 	/* Add the two VRAM-related sysfs files */
13855c374e9SKent Russell 	ret = device_create_file(adev->dev, &dev_attr_mem_info_vram_total);
13955c374e9SKent Russell 	if (ret) {
14055c374e9SKent Russell 		DRM_ERROR("Failed to create device file mem_info_vram_total\n");
14155c374e9SKent Russell 		return ret;
14255c374e9SKent Russell 	}
14355c374e9SKent Russell 	ret = device_create_file(adev->dev, &dev_attr_mem_info_vis_vram_total);
14455c374e9SKent Russell 	if (ret) {
14555c374e9SKent Russell 		DRM_ERROR("Failed to create device file mem_info_vis_vram_total\n");
14655c374e9SKent Russell 		return ret;
14755c374e9SKent Russell 	}
14855c374e9SKent Russell 	ret = device_create_file(adev->dev, &dev_attr_mem_info_vram_used);
14955c374e9SKent Russell 	if (ret) {
15055c374e9SKent Russell 		DRM_ERROR("Failed to create device file mem_info_vram_used\n");
15155c374e9SKent Russell 		return ret;
15255c374e9SKent Russell 	}
15355c374e9SKent Russell 	ret = device_create_file(adev->dev, &dev_attr_mem_info_vis_vram_used);
15455c374e9SKent Russell 	if (ret) {
15555c374e9SKent Russell 		DRM_ERROR("Failed to create device file mem_info_vis_vram_used\n");
15655c374e9SKent Russell 		return ret;
15755c374e9SKent Russell 	}
15855c374e9SKent Russell 
1596a7f76e7SChristian König 	return 0;
1606a7f76e7SChristian König }
1616a7f76e7SChristian König 
1626a7f76e7SChristian König /**
1636a7f76e7SChristian König  * amdgpu_vram_mgr_fini - free and destroy VRAM manager
1646a7f76e7SChristian König  *
1656a7f76e7SChristian König  * @man: TTM memory type manager
1666a7f76e7SChristian König  *
1676a7f76e7SChristian König  * Destroy and free the VRAM manager, returns -EBUSY if ranges are still
1686a7f76e7SChristian König  * allocated inside it.
1696a7f76e7SChristian König  */
1706a7f76e7SChristian König static int amdgpu_vram_mgr_fini(struct ttm_mem_type_manager *man)
1716a7f76e7SChristian König {
17255c374e9SKent Russell 	struct amdgpu_device *adev = amdgpu_ttm_adev(man->bdev);
1736a7f76e7SChristian König 	struct amdgpu_vram_mgr *mgr = man->priv;
1746a7f76e7SChristian König 
1756a7f76e7SChristian König 	spin_lock(&mgr->lock);
1766a7f76e7SChristian König 	drm_mm_takedown(&mgr->mm);
1776a7f76e7SChristian König 	spin_unlock(&mgr->lock);
1786a7f76e7SChristian König 	kfree(mgr);
1796a7f76e7SChristian König 	man->priv = NULL;
18055c374e9SKent Russell 	device_remove_file(adev->dev, &dev_attr_mem_info_vram_total);
18155c374e9SKent Russell 	device_remove_file(adev->dev, &dev_attr_mem_info_vis_vram_total);
18255c374e9SKent Russell 	device_remove_file(adev->dev, &dev_attr_mem_info_vram_used);
18355c374e9SKent Russell 	device_remove_file(adev->dev, &dev_attr_mem_info_vis_vram_used);
1846a7f76e7SChristian König 	return 0;
1856a7f76e7SChristian König }
1866a7f76e7SChristian König 
1876a7f76e7SChristian König /**
1883c848bb3SChristian König  * amdgpu_vram_mgr_vis_size - Calculate visible node size
1893c848bb3SChristian König  *
1903c848bb3SChristian König  * @adev: amdgpu device structure
1913c848bb3SChristian König  * @node: MM node structure
1923c848bb3SChristian König  *
1933c848bb3SChristian König  * Calculate how many bytes of the MM node are inside visible VRAM
1943c848bb3SChristian König  */
1953c848bb3SChristian König static u64 amdgpu_vram_mgr_vis_size(struct amdgpu_device *adev,
1963c848bb3SChristian König 				    struct drm_mm_node *node)
1973c848bb3SChristian König {
1983c848bb3SChristian König 	uint64_t start = node->start << PAGE_SHIFT;
1993c848bb3SChristian König 	uint64_t end = (node->size + node->start) << PAGE_SHIFT;
2003c848bb3SChristian König 
201770d13b1SChristian König 	if (start >= adev->gmc.visible_vram_size)
2023c848bb3SChristian König 		return 0;
2033c848bb3SChristian König 
204770d13b1SChristian König 	return (end > adev->gmc.visible_vram_size ?
205770d13b1SChristian König 		adev->gmc.visible_vram_size : end) - start;
2063c848bb3SChristian König }
2073c848bb3SChristian König 
2083c848bb3SChristian König /**
209ddc21af4SMichel Dänzer  * amdgpu_vram_mgr_bo_visible_size - CPU visible BO size
2105e9244ffSMichel Dänzer  *
2115e9244ffSMichel Dänzer  * @bo: &amdgpu_bo buffer object (must be in VRAM)
2125e9244ffSMichel Dänzer  *
2135e9244ffSMichel Dänzer  * Returns:
214ddc21af4SMichel Dänzer  * How much of the given &amdgpu_bo buffer object lies in CPU visible VRAM.
2155e9244ffSMichel Dänzer  */
216ddc21af4SMichel Dänzer u64 amdgpu_vram_mgr_bo_visible_size(struct amdgpu_bo *bo)
2175e9244ffSMichel Dänzer {
2187303b39eSMichel Dänzer 	struct amdgpu_device *adev = amdgpu_ttm_adev(bo->tbo.bdev);
2197303b39eSMichel Dänzer 	struct ttm_mem_reg *mem = &bo->tbo.mem;
2207303b39eSMichel Dänzer 	struct drm_mm_node *nodes = mem->mm_node;
2217303b39eSMichel Dänzer 	unsigned pages = mem->num_pages;
222ddc21af4SMichel Dänzer 	u64 usage;
2237303b39eSMichel Dänzer 
2249735bf19SMichel Dänzer 	if (amdgpu_gmc_vram_full_visible(&adev->gmc))
2255e9244ffSMichel Dänzer 		return amdgpu_bo_size(bo);
2265e9244ffSMichel Dänzer 
227ddc21af4SMichel Dänzer 	if (mem->start >= adev->gmc.visible_vram_size >> PAGE_SHIFT)
228ddc21af4SMichel Dänzer 		return 0;
229ddc21af4SMichel Dänzer 
230ddc21af4SMichel Dänzer 	for (usage = 0; nodes && pages; pages -= nodes->size, nodes++)
231ddc21af4SMichel Dänzer 		usage += amdgpu_vram_mgr_vis_size(adev, nodes);
2327303b39eSMichel Dänzer 
2337303b39eSMichel Dänzer 	return usage;
2345e9244ffSMichel Dänzer }
2355e9244ffSMichel Dänzer 
2365e9244ffSMichel Dänzer /**
237433ca054SChristian König  * amdgpu_vram_mgr_virt_start - update virtual start address
238433ca054SChristian König  *
239433ca054SChristian König  * @mem: ttm_mem_reg to update
240433ca054SChristian König  * @node: just allocated node
241433ca054SChristian König  *
242433ca054SChristian König  * Calculate a virtual BO start address to easily check if everything is CPU
243433ca054SChristian König  * accessible.
244433ca054SChristian König  */
245433ca054SChristian König static void amdgpu_vram_mgr_virt_start(struct ttm_mem_reg *mem,
246433ca054SChristian König 				       struct drm_mm_node *node)
247433ca054SChristian König {
248433ca054SChristian König 	unsigned long start;
249433ca054SChristian König 
250433ca054SChristian König 	start = node->start + node->size;
251433ca054SChristian König 	if (start > mem->num_pages)
252433ca054SChristian König 		start -= mem->num_pages;
253433ca054SChristian König 	else
254433ca054SChristian König 		start = 0;
255433ca054SChristian König 	mem->start = max(mem->start, start);
256433ca054SChristian König }
257433ca054SChristian König 
258433ca054SChristian König /**
2596a7f76e7SChristian König  * amdgpu_vram_mgr_new - allocate new ranges
2606a7f76e7SChristian König  *
2616a7f76e7SChristian König  * @man: TTM memory type manager
2626a7f76e7SChristian König  * @tbo: TTM BO we need this range for
2636a7f76e7SChristian König  * @place: placement flags and restrictions
2646a7f76e7SChristian König  * @mem: the resulting mem object
2656a7f76e7SChristian König  *
2666a7f76e7SChristian König  * Allocate VRAM for the given BO.
2676a7f76e7SChristian König  */
2686a7f76e7SChristian König static int amdgpu_vram_mgr_new(struct ttm_mem_type_manager *man,
2696a7f76e7SChristian König 			       struct ttm_buffer_object *tbo,
2706a7f76e7SChristian König 			       const struct ttm_place *place,
2716a7f76e7SChristian König 			       struct ttm_mem_reg *mem)
2726a7f76e7SChristian König {
2733c848bb3SChristian König 	struct amdgpu_device *adev = amdgpu_ttm_adev(man->bdev);
2746a7f76e7SChristian König 	struct amdgpu_vram_mgr *mgr = man->priv;
2756a7f76e7SChristian König 	struct drm_mm *mm = &mgr->mm;
2766a7f76e7SChristian König 	struct drm_mm_node *nodes;
2774e64e553SChris Wilson 	enum drm_mm_insert_mode mode;
2786a7f76e7SChristian König 	unsigned long lpfn, num_nodes, pages_per_node, pages_left;
2793c848bb3SChristian König 	uint64_t usage = 0, vis_usage = 0;
2806a7f76e7SChristian König 	unsigned i;
2816a7f76e7SChristian König 	int r;
2826a7f76e7SChristian König 
2836a7f76e7SChristian König 	lpfn = place->lpfn;
2846a7f76e7SChristian König 	if (!lpfn)
2856a7f76e7SChristian König 		lpfn = man->size;
2866a7f76e7SChristian König 
28789bb5752SChristian König 	if (place->flags & TTM_PL_FLAG_CONTIGUOUS ||
28889bb5752SChristian König 	    amdgpu_vram_page_split == -1) {
2896a7f76e7SChristian König 		pages_per_node = ~0ul;
2906a7f76e7SChristian König 		num_nodes = 1;
2916a7f76e7SChristian König 	} else {
2926a7f76e7SChristian König 		pages_per_node = max((uint32_t)amdgpu_vram_page_split,
2936a7f76e7SChristian König 				     mem->page_alignment);
2946a7f76e7SChristian König 		num_nodes = DIV_ROUND_UP(mem->num_pages, pages_per_node);
2956a7f76e7SChristian König 	}
2966a7f76e7SChristian König 
2976fa39bc1SMichel Dänzer 	nodes = kvmalloc_array(num_nodes, sizeof(*nodes),
2986fa39bc1SMichel Dänzer 			       GFP_KERNEL | __GFP_ZERO);
2996a7f76e7SChristian König 	if (!nodes)
3006a7f76e7SChristian König 		return -ENOMEM;
3016a7f76e7SChristian König 
3024e64e553SChris Wilson 	mode = DRM_MM_INSERT_BEST;
3034e64e553SChris Wilson 	if (place->flags & TTM_PL_FLAG_TOPDOWN)
3044e64e553SChris Wilson 		mode = DRM_MM_INSERT_HIGH;
3056a7f76e7SChristian König 
30689bb5752SChristian König 	mem->start = 0;
3076a7f76e7SChristian König 	pages_left = mem->num_pages;
3086a7f76e7SChristian König 
3096a7f76e7SChristian König 	spin_lock(&mgr->lock);
310433ca054SChristian König 	for (i = 0; pages_left >= pages_per_node; ++i) {
311433ca054SChristian König 		unsigned long pages = rounddown_pow_of_two(pages_left);
312433ca054SChristian König 
313433ca054SChristian König 		r = drm_mm_insert_node_in_range(mm, &nodes[i], pages,
314433ca054SChristian König 						pages_per_node, 0,
315433ca054SChristian König 						place->fpfn, lpfn,
316433ca054SChristian König 						mode);
317433ca054SChristian König 		if (unlikely(r))
318433ca054SChristian König 			break;
319433ca054SChristian König 
320433ca054SChristian König 		usage += nodes[i].size << PAGE_SHIFT;
321433ca054SChristian König 		vis_usage += amdgpu_vram_mgr_vis_size(adev, &nodes[i]);
322433ca054SChristian König 		amdgpu_vram_mgr_virt_start(mem, &nodes[i]);
323433ca054SChristian König 		pages_left -= pages;
324433ca054SChristian König 	}
325433ca054SChristian König 
326433ca054SChristian König 	for (; pages_left; ++i) {
3276a7f76e7SChristian König 		unsigned long pages = min(pages_left, pages_per_node);
3286a7f76e7SChristian König 		uint32_t alignment = mem->page_alignment;
3296a7f76e7SChristian König 
3306a7f76e7SChristian König 		if (pages == pages_per_node)
3316a7f76e7SChristian König 			alignment = pages_per_node;
3326a7f76e7SChristian König 
3334e64e553SChris Wilson 		r = drm_mm_insert_node_in_range(mm, &nodes[i],
3344e64e553SChris Wilson 						pages, alignment, 0,
3356a7f76e7SChristian König 						place->fpfn, lpfn,
3364e64e553SChris Wilson 						mode);
3376a7f76e7SChristian König 		if (unlikely(r))
3386a7f76e7SChristian König 			goto error;
3396a7f76e7SChristian König 
3403c848bb3SChristian König 		usage += nodes[i].size << PAGE_SHIFT;
3413c848bb3SChristian König 		vis_usage += amdgpu_vram_mgr_vis_size(adev, &nodes[i]);
342433ca054SChristian König 		amdgpu_vram_mgr_virt_start(mem, &nodes[i]);
3436a7f76e7SChristian König 		pages_left -= pages;
3446a7f76e7SChristian König 	}
3456a7f76e7SChristian König 	spin_unlock(&mgr->lock);
3466a7f76e7SChristian König 
3473c848bb3SChristian König 	atomic64_add(usage, &mgr->usage);
3483c848bb3SChristian König 	atomic64_add(vis_usage, &mgr->vis_usage);
3493c848bb3SChristian König 
3506a7f76e7SChristian König 	mem->mm_node = nodes;
3516a7f76e7SChristian König 
3526a7f76e7SChristian König 	return 0;
3536a7f76e7SChristian König 
3546a7f76e7SChristian König error:
3556a7f76e7SChristian König 	while (i--)
3566a7f76e7SChristian König 		drm_mm_remove_node(&nodes[i]);
3576a7f76e7SChristian König 	spin_unlock(&mgr->lock);
3586a7f76e7SChristian König 
3596fa39bc1SMichel Dänzer 	kvfree(nodes);
3606a7f76e7SChristian König 	return r == -ENOSPC ? 0 : r;
3616a7f76e7SChristian König }
3626a7f76e7SChristian König 
3636a7f76e7SChristian König /**
3646a7f76e7SChristian König  * amdgpu_vram_mgr_del - free ranges
3656a7f76e7SChristian König  *
3666a7f76e7SChristian König  * @man: TTM memory type manager
3676a7f76e7SChristian König  * @tbo: TTM BO we need this range for
3686a7f76e7SChristian König  * @place: placement flags and restrictions
3696a7f76e7SChristian König  * @mem: TTM memory object
3706a7f76e7SChristian König  *
3716a7f76e7SChristian König  * Free the allocated VRAM again.
3726a7f76e7SChristian König  */
3736a7f76e7SChristian König static void amdgpu_vram_mgr_del(struct ttm_mem_type_manager *man,
3746a7f76e7SChristian König 				struct ttm_mem_reg *mem)
3756a7f76e7SChristian König {
3763c848bb3SChristian König 	struct amdgpu_device *adev = amdgpu_ttm_adev(man->bdev);
3776a7f76e7SChristian König 	struct amdgpu_vram_mgr *mgr = man->priv;
3786a7f76e7SChristian König 	struct drm_mm_node *nodes = mem->mm_node;
3793c848bb3SChristian König 	uint64_t usage = 0, vis_usage = 0;
3806a7f76e7SChristian König 	unsigned pages = mem->num_pages;
3816a7f76e7SChristian König 
3826a7f76e7SChristian König 	if (!mem->mm_node)
3836a7f76e7SChristian König 		return;
3846a7f76e7SChristian König 
3856a7f76e7SChristian König 	spin_lock(&mgr->lock);
3866a7f76e7SChristian König 	while (pages) {
3876a7f76e7SChristian König 		pages -= nodes->size;
3886a7f76e7SChristian König 		drm_mm_remove_node(nodes);
3893c848bb3SChristian König 		usage += nodes->size << PAGE_SHIFT;
3903c848bb3SChristian König 		vis_usage += amdgpu_vram_mgr_vis_size(adev, nodes);
3916a7f76e7SChristian König 		++nodes;
3926a7f76e7SChristian König 	}
3936a7f76e7SChristian König 	spin_unlock(&mgr->lock);
3946a7f76e7SChristian König 
3953c848bb3SChristian König 	atomic64_sub(usage, &mgr->usage);
3963c848bb3SChristian König 	atomic64_sub(vis_usage, &mgr->vis_usage);
3973c848bb3SChristian König 
3986fa39bc1SMichel Dänzer 	kvfree(mem->mm_node);
3996a7f76e7SChristian König 	mem->mm_node = NULL;
4006a7f76e7SChristian König }
4016a7f76e7SChristian König 
4026a7f76e7SChristian König /**
4033c848bb3SChristian König  * amdgpu_vram_mgr_usage - how many bytes are used in this domain
4043c848bb3SChristian König  *
4053c848bb3SChristian König  * @man: TTM memory type manager
4063c848bb3SChristian König  *
4073c848bb3SChristian König  * Returns how many bytes are used in this domain.
4083c848bb3SChristian König  */
4093c848bb3SChristian König uint64_t amdgpu_vram_mgr_usage(struct ttm_mem_type_manager *man)
4103c848bb3SChristian König {
4113c848bb3SChristian König 	struct amdgpu_vram_mgr *mgr = man->priv;
4123c848bb3SChristian König 
4133c848bb3SChristian König 	return atomic64_read(&mgr->usage);
4143c848bb3SChristian König }
4153c848bb3SChristian König 
4163c848bb3SChristian König /**
4173c848bb3SChristian König  * amdgpu_vram_mgr_vis_usage - how many bytes are used in the visible part
4183c848bb3SChristian König  *
4193c848bb3SChristian König  * @man: TTM memory type manager
4203c848bb3SChristian König  *
4213c848bb3SChristian König  * Returns how many bytes are used in the visible part of VRAM
4223c848bb3SChristian König  */
4233c848bb3SChristian König uint64_t amdgpu_vram_mgr_vis_usage(struct ttm_mem_type_manager *man)
4243c848bb3SChristian König {
4253c848bb3SChristian König 	struct amdgpu_vram_mgr *mgr = man->priv;
4263c848bb3SChristian König 
4273c848bb3SChristian König 	return atomic64_read(&mgr->vis_usage);
4283c848bb3SChristian König }
4293c848bb3SChristian König 
4303c848bb3SChristian König /**
4316a7f76e7SChristian König  * amdgpu_vram_mgr_debug - dump VRAM table
4326a7f76e7SChristian König  *
4336a7f76e7SChristian König  * @man: TTM memory type manager
434373533f8SChristian König  * @printer: DRM printer to use
4356a7f76e7SChristian König  *
4366a7f76e7SChristian König  * Dump the table content using printk.
4376a7f76e7SChristian König  */
4386a7f76e7SChristian König static void amdgpu_vram_mgr_debug(struct ttm_mem_type_manager *man,
439373533f8SChristian König 				  struct drm_printer *printer)
4406a7f76e7SChristian König {
4416a7f76e7SChristian König 	struct amdgpu_vram_mgr *mgr = man->priv;
4426a7f76e7SChristian König 
4436a7f76e7SChristian König 	spin_lock(&mgr->lock);
444373533f8SChristian König 	drm_mm_print(&mgr->mm, printer);
4456a7f76e7SChristian König 	spin_unlock(&mgr->lock);
44697cbb284SChristian König 
44797cbb284SChristian König 	drm_printf(printer, "man size:%llu pages, ram usage:%lluMB, vis usage:%lluMB\n",
4483c848bb3SChristian König 		   man->size, amdgpu_vram_mgr_usage(man) >> 20,
4493c848bb3SChristian König 		   amdgpu_vram_mgr_vis_usage(man) >> 20);
4506a7f76e7SChristian König }
4516a7f76e7SChristian König 
4526a7f76e7SChristian König const struct ttm_mem_type_manager_func amdgpu_vram_mgr_func = {
4532a9d6d26SKees Cook 	.init		= amdgpu_vram_mgr_init,
4542a9d6d26SKees Cook 	.takedown	= amdgpu_vram_mgr_fini,
4552a9d6d26SKees Cook 	.get_node	= amdgpu_vram_mgr_new,
4562a9d6d26SKees Cook 	.put_node	= amdgpu_vram_mgr_del,
4572a9d6d26SKees Cook 	.debug		= amdgpu_vram_mgr_debug
4586a7f76e7SChristian König };
459