mirror of
				git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
				synced 2025-09-04 20:19:47 +08:00 
			
		
		
		
	 89bb5752c0
			
		
	
	
		89bb5752c0
		
	
	
	
	
		
			
			Implement AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS using TTM_PL_FLAG_CONTIGUOUS instead of a placement limit. That allows us to better handle CPU accessible placements. v2: prevent virtual BO start address from overflowing Signed-off-by: Christian König <christian.koenig@amd.com> Acked-by: Michel Dänzer <michel.daenzer@amd.com> Reviewed-by: Nicolai Hähnle <nicolai.haehnle@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
		
			
				
	
	
		
			229 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			229 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * Copyright 2016 Advanced Micro Devices, Inc.
 | |
|  *
 | |
|  * Permission is hereby granted, free of charge, to any person obtaining a
 | |
|  * copy of this software and associated documentation files (the "Software"),
 | |
|  * to deal in the Software without restriction, including without limitation
 | |
|  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 | |
|  * and/or sell copies of the Software, and to permit persons to whom the
 | |
|  * Software is furnished to do so, subject to the following conditions:
 | |
|  *
 | |
|  * The above copyright notice and this permission notice shall be included in
 | |
|  * all copies or substantial portions of the Software.
 | |
|  *
 | |
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | |
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | |
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 | |
|  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
 | |
|  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 | |
|  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 | |
|  * OTHER DEALINGS IN THE SOFTWARE.
 | |
|  *
 | |
|  * Authors: Christian König
 | |
|  */
 | |
| 
 | |
| #include <drm/drmP.h>
 | |
| #include "amdgpu.h"
 | |
| 
 | |
| struct amdgpu_vram_mgr {
 | |
| 	struct drm_mm mm;
 | |
| 	spinlock_t lock;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * amdgpu_vram_mgr_init - init VRAM manager and DRM MM
 | |
|  *
 | |
|  * @man: TTM memory type manager
 | |
|  * @p_size: maximum size of VRAM
 | |
|  *
 | |
|  * Allocate and initialize the VRAM manager.
 | |
|  */
 | |
| static int amdgpu_vram_mgr_init(struct ttm_mem_type_manager *man,
 | |
| 				unsigned long p_size)
 | |
| {
 | |
| 	struct amdgpu_vram_mgr *mgr;
 | |
| 
 | |
| 	mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
 | |
| 	if (!mgr)
 | |
| 		return -ENOMEM;
 | |
| 
 | |
| 	drm_mm_init(&mgr->mm, 0, p_size);
 | |
| 	spin_lock_init(&mgr->lock);
 | |
| 	man->priv = mgr;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * amdgpu_vram_mgr_fini - free and destroy VRAM manager
 | |
|  *
 | |
|  * @man: TTM memory type manager
 | |
|  *
 | |
|  * Destroy and free the VRAM manager, returns -EBUSY if ranges are still
 | |
|  * allocated inside it.
 | |
|  */
 | |
| static int amdgpu_vram_mgr_fini(struct ttm_mem_type_manager *man)
 | |
| {
 | |
| 	struct amdgpu_vram_mgr *mgr = man->priv;
 | |
| 
 | |
| 	spin_lock(&mgr->lock);
 | |
| 	if (!drm_mm_clean(&mgr->mm)) {
 | |
| 		spin_unlock(&mgr->lock);
 | |
| 		return -EBUSY;
 | |
| 	}
 | |
| 
 | |
| 	drm_mm_takedown(&mgr->mm);
 | |
| 	spin_unlock(&mgr->lock);
 | |
| 	kfree(mgr);
 | |
| 	man->priv = NULL;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * amdgpu_vram_mgr_new - allocate new ranges
 | |
|  *
 | |
|  * @man: TTM memory type manager
 | |
|  * @tbo: TTM BO we need this range for
 | |
|  * @place: placement flags and restrictions
 | |
|  * @mem: the resulting mem object
 | |
|  *
 | |
|  * Allocate VRAM for the given BO.
 | |
|  */
 | |
| static int 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)
 | |
| {
 | |
| 	struct amdgpu_vram_mgr *mgr = man->priv;
 | |
| 	struct drm_mm *mm = &mgr->mm;
 | |
| 	struct drm_mm_node *nodes;
 | |
| 	enum drm_mm_insert_mode mode;
 | |
| 	unsigned long lpfn, num_nodes, pages_per_node, pages_left;
 | |
| 	unsigned i;
 | |
| 	int r;
 | |
| 
 | |
| 	lpfn = place->lpfn;
 | |
| 	if (!lpfn)
 | |
| 		lpfn = man->size;
 | |
| 
 | |
| 	if (place->flags & TTM_PL_FLAG_CONTIGUOUS ||
 | |
| 	    amdgpu_vram_page_split == -1) {
 | |
| 		pages_per_node = ~0ul;
 | |
| 		num_nodes = 1;
 | |
| 	} else {
 | |
| 		pages_per_node = max((uint32_t)amdgpu_vram_page_split,
 | |
| 				     mem->page_alignment);
 | |
| 		num_nodes = DIV_ROUND_UP(mem->num_pages, pages_per_node);
 | |
| 	}
 | |
| 
 | |
| 	nodes = kcalloc(num_nodes, sizeof(*nodes), GFP_KERNEL);
 | |
| 	if (!nodes)
 | |
| 		return -ENOMEM;
 | |
| 
 | |
| 	mode = DRM_MM_INSERT_BEST;
 | |
| 	if (place->flags & TTM_PL_FLAG_TOPDOWN)
 | |
| 		mode = DRM_MM_INSERT_HIGH;
 | |
| 
 | |
| 	mem->start = 0;
 | |
| 	pages_left = mem->num_pages;
 | |
| 
 | |
| 	spin_lock(&mgr->lock);
 | |
| 	for (i = 0; i < num_nodes; ++i) {
 | |
| 		unsigned long pages = min(pages_left, pages_per_node);
 | |
| 		uint32_t alignment = mem->page_alignment;
 | |
| 		unsigned long start;
 | |
| 
 | |
| 		if (pages == pages_per_node)
 | |
| 			alignment = pages_per_node;
 | |
| 
 | |
| 		r = drm_mm_insert_node_in_range(mm, &nodes[i],
 | |
| 						pages, alignment, 0,
 | |
| 						place->fpfn, lpfn,
 | |
| 						mode);
 | |
| 		if (unlikely(r))
 | |
| 			goto error;
 | |
| 
 | |
| 		/* Calculate a virtual BO start address to easily check if
 | |
| 		 * everything is CPU accessible.
 | |
| 		 */
 | |
| 		start = nodes[i].start + nodes[i].size;
 | |
| 		if (start > mem->num_pages)
 | |
| 			start -= mem->num_pages;
 | |
| 		else
 | |
| 			start = 0;
 | |
| 		mem->start = max(mem->start, start);
 | |
| 		pages_left -= pages;
 | |
| 	}
 | |
| 	spin_unlock(&mgr->lock);
 | |
| 
 | |
| 	mem->mm_node = nodes;
 | |
| 
 | |
| 	return 0;
 | |
| 
 | |
| error:
 | |
| 	while (i--)
 | |
| 		drm_mm_remove_node(&nodes[i]);
 | |
| 	spin_unlock(&mgr->lock);
 | |
| 
 | |
| 	kfree(nodes);
 | |
| 	return r == -ENOSPC ? 0 : r;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * amdgpu_vram_mgr_del - free ranges
 | |
|  *
 | |
|  * @man: TTM memory type manager
 | |
|  * @tbo: TTM BO we need this range for
 | |
|  * @place: placement flags and restrictions
 | |
|  * @mem: TTM memory object
 | |
|  *
 | |
|  * Free the allocated VRAM again.
 | |
|  */
 | |
| static void amdgpu_vram_mgr_del(struct ttm_mem_type_manager *man,
 | |
| 				struct ttm_mem_reg *mem)
 | |
| {
 | |
| 	struct amdgpu_vram_mgr *mgr = man->priv;
 | |
| 	struct drm_mm_node *nodes = mem->mm_node;
 | |
| 	unsigned pages = mem->num_pages;
 | |
| 
 | |
| 	if (!mem->mm_node)
 | |
| 		return;
 | |
| 
 | |
| 	spin_lock(&mgr->lock);
 | |
| 	while (pages) {
 | |
| 		pages -= nodes->size;
 | |
| 		drm_mm_remove_node(nodes);
 | |
| 		++nodes;
 | |
| 	}
 | |
| 	spin_unlock(&mgr->lock);
 | |
| 
 | |
| 	kfree(mem->mm_node);
 | |
| 	mem->mm_node = NULL;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * amdgpu_vram_mgr_debug - dump VRAM table
 | |
|  *
 | |
|  * @man: TTM memory type manager
 | |
|  * @prefix: text prefix
 | |
|  *
 | |
|  * Dump the table content using printk.
 | |
|  */
 | |
| static void amdgpu_vram_mgr_debug(struct ttm_mem_type_manager *man,
 | |
| 				  const char *prefix)
 | |
| {
 | |
| 	struct amdgpu_vram_mgr *mgr = man->priv;
 | |
| 	struct drm_printer p = drm_debug_printer(prefix);
 | |
| 
 | |
| 	spin_lock(&mgr->lock);
 | |
| 	drm_mm_print(&mgr->mm, &p);
 | |
| 	spin_unlock(&mgr->lock);
 | |
| }
 | |
| 
 | |
| const struct ttm_mem_type_manager_func amdgpu_vram_mgr_func = {
 | |
| 	amdgpu_vram_mgr_init,
 | |
| 	amdgpu_vram_mgr_fini,
 | |
| 	amdgpu_vram_mgr_new,
 | |
| 	amdgpu_vram_mgr_del,
 | |
| 	amdgpu_vram_mgr_debug
 | |
| };
 |