xref: /openbmc/linux/drivers/gpu/drm/ttm/ttm_device.c (revision c8045b4a33a511ff1feaeb806e819572b90b6625)
18af8a109SChristian König /* SPDX-License-Identifier: GPL-2.0 OR MIT */
28af8a109SChristian König 
38af8a109SChristian König /*
48af8a109SChristian König  * Copyright (c) 2006-2009 VMware, Inc., Palo Alto, CA., USA
58af8a109SChristian König  * Copyright 2020 Advanced Micro Devices, Inc.
68af8a109SChristian König  *
78af8a109SChristian König  * Permission is hereby granted, free of charge, to any person obtaining a
88af8a109SChristian König  * copy of this software and associated documentation files (the "Software"),
98af8a109SChristian König  * to deal in the Software without restriction, including without limitation
108af8a109SChristian König  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
118af8a109SChristian König  * and/or sell copies of the Software, and to permit persons to whom the
128af8a109SChristian König  * Software is furnished to do so, subject to the following conditions:
138af8a109SChristian König  *
148af8a109SChristian König  * The above copyright notice and this permission notice shall be included in
158af8a109SChristian König  * all copies or substantial portions of the Software.
168af8a109SChristian König  *
178af8a109SChristian König  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
188af8a109SChristian König  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
198af8a109SChristian König  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
208af8a109SChristian König  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
218af8a109SChristian König  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
228af8a109SChristian König  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
238af8a109SChristian König  * OTHER DEALINGS IN THE SOFTWARE.
248af8a109SChristian König  *
258af8a109SChristian König  * Authors: Christian König
268af8a109SChristian König  */
278af8a109SChristian König 
288af8a109SChristian König #define pr_fmt(fmt) "[TTM DEVICE] " fmt
298af8a109SChristian König 
30f07069daSChristian König #include <linux/mm.h>
31f07069daSChristian König 
32a3185f91SChristian König #include <drm/ttm/ttm_bo.h>
338af8a109SChristian König #include <drm/ttm/ttm_device.h>
34f07069daSChristian König #include <drm/ttm/ttm_tt.h>
358af8a109SChristian König #include <drm/ttm/ttm_placement.h>
368af8a109SChristian König 
378af8a109SChristian König #include "ttm_module.h"
388af8a109SChristian König 
39178bdba8SLee Jones /*
408af8a109SChristian König  * ttm_global_mutex - protecting the global state
418af8a109SChristian König  */
42d4e68236SChristian König static DEFINE_MUTEX(ttm_global_mutex);
43d4e68236SChristian König static unsigned ttm_glob_use_count;
448af8a109SChristian König struct ttm_global ttm_glob;
458af8a109SChristian König EXPORT_SYMBOL(ttm_glob);
468af8a109SChristian König 
4769de4421SJason Ekstrand struct dentry *ttm_debugfs_root;
4869de4421SJason Ekstrand 
ttm_global_release(void)498af8a109SChristian König static void ttm_global_release(void)
508af8a109SChristian König {
518af8a109SChristian König 	struct ttm_global *glob = &ttm_glob;
528af8a109SChristian König 
538af8a109SChristian König 	mutex_lock(&ttm_global_mutex);
548af8a109SChristian König 	if (--ttm_glob_use_count > 0)
558af8a109SChristian König 		goto out;
568af8a109SChristian König 
57f07069daSChristian König 	ttm_pool_mgr_fini();
5869de4421SJason Ekstrand 	debugfs_remove(ttm_debugfs_root);
59f07069daSChristian König 
608af8a109SChristian König 	__free_page(glob->dummy_read_page);
618af8a109SChristian König 	memset(glob, 0, sizeof(*glob));
628af8a109SChristian König out:
638af8a109SChristian König 	mutex_unlock(&ttm_global_mutex);
648af8a109SChristian König }
658af8a109SChristian König 
ttm_global_init(void)668af8a109SChristian König static int ttm_global_init(void)
678af8a109SChristian König {
688af8a109SChristian König 	struct ttm_global *glob = &ttm_glob;
69680dcedeSChristian König 	unsigned long num_pages, num_dma32;
70f07069daSChristian König 	struct sysinfo si;
718af8a109SChristian König 	int ret = 0;
728af8a109SChristian König 
738af8a109SChristian König 	mutex_lock(&ttm_global_mutex);
748af8a109SChristian König 	if (++ttm_glob_use_count > 1)
758af8a109SChristian König 		goto out;
768af8a109SChristian König 
77f07069daSChristian König 	si_meminfo(&si);
78f07069daSChristian König 
7969de4421SJason Ekstrand 	ttm_debugfs_root = debugfs_create_dir("ttm", NULL);
8069de4421SJason Ekstrand 	if (IS_ERR(ttm_debugfs_root)) {
8169de4421SJason Ekstrand 		ttm_debugfs_root = NULL;
8269de4421SJason Ekstrand 	}
8369de4421SJason Ekstrand 
84f07069daSChristian König 	/* Limit the number of pages in the pool to about 50% of the total
85f07069daSChristian König 	 * system memory.
86f07069daSChristian König 	 */
87f07069daSChristian König 	num_pages = ((u64)si.totalram * si.mem_unit) >> PAGE_SHIFT;
88680dcedeSChristian König 	num_pages /= 2;
89680dcedeSChristian König 
90680dcedeSChristian König 	/* But for DMA32 we limit ourself to only use 2GiB maximum. */
91680dcedeSChristian König 	num_dma32 = (u64)(si.totalram - si.totalhigh) * si.mem_unit
92680dcedeSChristian König 		>> PAGE_SHIFT;
93680dcedeSChristian König 	num_dma32 = min(num_dma32, 2UL << (30 - PAGE_SHIFT));
94680dcedeSChristian König 
95680dcedeSChristian König 	ttm_pool_mgr_init(num_pages);
96680dcedeSChristian König 	ttm_tt_mgr_init(num_pages, num_dma32);
978af8a109SChristian König 
988af8a109SChristian König 	glob->dummy_read_page = alloc_page(__GFP_ZERO | GFP_DMA32);
998af8a109SChristian König 
1008af8a109SChristian König 	if (unlikely(glob->dummy_read_page == NULL)) {
1018af8a109SChristian König 		ret = -ENOMEM;
1028af8a109SChristian König 		goto out;
1038af8a109SChristian König 	}
1048af8a109SChristian König 
1058af8a109SChristian König 	INIT_LIST_HEAD(&glob->device_list);
1068af8a109SChristian König 	atomic_set(&glob->bo_count, 0);
1078af8a109SChristian König 
1088af8a109SChristian König 	debugfs_create_atomic_t("buffer_objects", 0444, ttm_debugfs_root,
1098af8a109SChristian König 				&glob->bo_count);
1108af8a109SChristian König out:
11169de4421SJason Ekstrand 	if (ret && ttm_debugfs_root)
11269de4421SJason Ekstrand 		debugfs_remove(ttm_debugfs_root);
113235c3610SJason Ekstrand 	if (ret)
114235c3610SJason Ekstrand 		--ttm_glob_use_count;
1158af8a109SChristian König 	mutex_unlock(&ttm_global_mutex);
1168af8a109SChristian König 	return ret;
1178af8a109SChristian König }
1188af8a109SChristian König 
119178bdba8SLee Jones /*
120ebd59851SChristian König  * A buffer object shrink method that tries to swap out the first
121ebd59851SChristian König  * buffer object on the global::swap_lru list.
122ebd59851SChristian König  */
ttm_global_swapout(struct ttm_operation_ctx * ctx,gfp_t gfp_flags)123ebd59851SChristian König int ttm_global_swapout(struct ttm_operation_ctx *ctx, gfp_t gfp_flags)
124ebd59851SChristian König {
125ebd59851SChristian König 	struct ttm_global *glob = &ttm_glob;
126f9e2a03eSChristian König 	struct ttm_device *bdev;
12713ea9aa1SShiwu Zhang 	int ret = 0;
128f9e2a03eSChristian König 
129f9e2a03eSChristian König 	mutex_lock(&ttm_global_mutex);
130f9e2a03eSChristian König 	list_for_each_entry(bdev, &glob->device_list, device_list) {
131f9e2a03eSChristian König 		ret = ttm_device_swapout(bdev, ctx, gfp_flags);
132f9e2a03eSChristian König 		if (ret > 0) {
133f9e2a03eSChristian König 			list_move_tail(&bdev->device_list, &glob->device_list);
134f9e2a03eSChristian König 			break;
135f9e2a03eSChristian König 		}
136f9e2a03eSChristian König 	}
137f9e2a03eSChristian König 	mutex_unlock(&ttm_global_mutex);
138f9e2a03eSChristian König 	return ret;
139f9e2a03eSChristian König }
140f9e2a03eSChristian König 
ttm_device_swapout(struct ttm_device * bdev,struct ttm_operation_ctx * ctx,gfp_t gfp_flags)141f9e2a03eSChristian König int ttm_device_swapout(struct ttm_device *bdev, struct ttm_operation_ctx *ctx,
142f9e2a03eSChristian König 		       gfp_t gfp_flags)
143f9e2a03eSChristian König {
1445d05b988SChristian König 	struct ttm_resource_cursor cursor;
145f9e2a03eSChristian König 	struct ttm_resource_manager *man;
1466a9b0289SChristian König 	struct ttm_resource *res;
1475d05b988SChristian König 	unsigned i;
148ebd59851SChristian König 	int ret;
149ebd59851SChristian König 
150a1f091f8SChristian König 	spin_lock(&bdev->lru_lock);
151f9e2a03eSChristian König 	for (i = TTM_PL_SYSTEM; i < TTM_NUM_MEM_TYPES; ++i) {
152f9e2a03eSChristian König 		man = ttm_manager_type(bdev, i);
153f9e2a03eSChristian König 		if (!man || !man->use_tt)
154f9e2a03eSChristian König 			continue;
155ebd59851SChristian König 
1565d05b988SChristian König 		ttm_resource_manager_for_each_res(man, &cursor, res) {
1575d05b988SChristian König 			struct ttm_buffer_object *bo = res->bo;
15881b0d0e4SChristian König 			uint32_t num_pages;
159f9e2a03eSChristian König 
1609a9a8fe2SThomas Hellström 			if (!bo || bo->resource != res)
16181b0d0e4SChristian König 				continue;
16281b0d0e4SChristian König 
16381b0d0e4SChristian König 			num_pages = PFN_UP(bo->base.size);
164ebd59851SChristian König 			ret = ttm_bo_swapout(bo, ctx, gfp_flags);
165ebd59851SChristian König 			/* ttm_bo_swapout has dropped the lru_lock */
166ebd59851SChristian König 			if (!ret)
167ebd59851SChristian König 				return num_pages;
168ebd59851SChristian König 			if (ret != -EBUSY)
169ebd59851SChristian König 				return ret;
170ebd59851SChristian König 		}
171ebd59851SChristian König 	}
172a1f091f8SChristian König 	spin_unlock(&bdev->lru_lock);
173ebd59851SChristian König 	return 0;
174ebd59851SChristian König }
175f9e2a03eSChristian König EXPORT_SYMBOL(ttm_device_swapout);
176ebd59851SChristian König 
1778af8a109SChristian König /**
1788af8a109SChristian König  * ttm_device_init
1798af8a109SChristian König  *
1808af8a109SChristian König  * @bdev: A pointer to a struct ttm_device to initialize.
1818af8a109SChristian König  * @funcs: Function table for the device.
1828af8a109SChristian König  * @dev: The core kernel device pointer for DMA mappings and allocations.
1838af8a109SChristian König  * @mapping: The address space to use for this bo.
1848af8a109SChristian König  * @vma_manager: A pointer to a vma manager.
1858af8a109SChristian König  * @use_dma_alloc: If coherent DMA allocation API should be used.
1868af8a109SChristian König  * @use_dma32: If we should use GFP_DMA32 for device memory allocations.
1878af8a109SChristian König  *
1888af8a109SChristian König  * Initializes a struct ttm_device:
1898af8a109SChristian König  * Returns:
1908af8a109SChristian König  * !0: Failure.
1918af8a109SChristian König  */
ttm_device_init(struct ttm_device * bdev,const struct ttm_device_funcs * funcs,struct device * dev,struct address_space * mapping,struct drm_vma_offset_manager * vma_manager,bool use_dma_alloc,bool use_dma32)1921ad79759SJani Nikula int ttm_device_init(struct ttm_device *bdev, const struct ttm_device_funcs *funcs,
1938af8a109SChristian König 		    struct device *dev, struct address_space *mapping,
1948af8a109SChristian König 		    struct drm_vma_offset_manager *vma_manager,
1958af8a109SChristian König 		    bool use_dma_alloc, bool use_dma32)
1968af8a109SChristian König {
1978af8a109SChristian König 	struct ttm_global *glob = &ttm_glob;
1988af8a109SChristian König 	int ret;
1998af8a109SChristian König 
2008af8a109SChristian König 	if (WARN_ON(vma_manager == NULL))
2018af8a109SChristian König 		return -EINVAL;
2028af8a109SChristian König 
2038af8a109SChristian König 	ret = ttm_global_init();
2048af8a109SChristian König 	if (ret)
2058af8a109SChristian König 		return ret;
2068af8a109SChristian König 
2079bff18d1SChristian König 	bdev->wq = alloc_workqueue("ttm", WQ_MEM_RECLAIM | WQ_HIGHPRI, 16);
2089bff18d1SChristian König 	if (!bdev->wq) {
2099bff18d1SChristian König 		ttm_global_release();
2109bff18d1SChristian König 		return -ENOMEM;
2119bff18d1SChristian König 	}
2129bff18d1SChristian König 
2138af8a109SChristian König 	bdev->funcs = funcs;
2148af8a109SChristian König 
215b072b9cdSChristian König 	ttm_sys_man_init(bdev);
2164482d3c9SRajneesh Bhardwaj 	ttm_pool_init(&bdev->pool, dev, NUMA_NO_NODE, use_dma_alloc, use_dma32);
2178af8a109SChristian König 
2188af8a109SChristian König 	bdev->vma_manager = vma_manager;
219a1f091f8SChristian König 	spin_lock_init(&bdev->lru_lock);
22032eadf52SAndrey Grodzovsky 	INIT_LIST_HEAD(&bdev->pinned);
2218af8a109SChristian König 	bdev->dev_mapping = mapping;
2228af8a109SChristian König 	mutex_lock(&ttm_global_mutex);
2238af8a109SChristian König 	list_add_tail(&bdev->device_list, &glob->device_list);
2248af8a109SChristian König 	mutex_unlock(&ttm_global_mutex);
2258af8a109SChristian König 
2268af8a109SChristian König 	return 0;
2278af8a109SChristian König }
2288af8a109SChristian König EXPORT_SYMBOL(ttm_device_init);
2298af8a109SChristian König 
ttm_device_fini(struct ttm_device * bdev)2308af8a109SChristian König void ttm_device_fini(struct ttm_device *bdev)
2318af8a109SChristian König {
2328af8a109SChristian König 	struct ttm_resource_manager *man;
2338af8a109SChristian König 	unsigned i;
2348af8a109SChristian König 
2358af8a109SChristian König 	mutex_lock(&ttm_global_mutex);
2368af8a109SChristian König 	list_del(&bdev->device_list);
2378af8a109SChristian König 	mutex_unlock(&ttm_global_mutex);
2388af8a109SChristian König 
2399bff18d1SChristian König 	drain_workqueue(bdev->wq);
2409bff18d1SChristian König 	destroy_workqueue(bdev->wq);
2418af8a109SChristian König 
242*3b401e30SKarolina Stolarek 	man = ttm_manager_type(bdev, TTM_PL_SYSTEM);
243*3b401e30SKarolina Stolarek 	ttm_resource_manager_set_used(man, false);
244*3b401e30SKarolina Stolarek 	ttm_set_driver_manager(bdev, TTM_PL_SYSTEM, NULL);
245*3b401e30SKarolina Stolarek 
246a1f091f8SChristian König 	spin_lock(&bdev->lru_lock);
2478af8a109SChristian König 	for (i = 0; i < TTM_MAX_BO_PRIORITY; ++i)
2488af8a109SChristian König 		if (list_empty(&man->lru[0]))
2498af8a109SChristian König 			pr_debug("Swap list %d was clean\n", i);
250a1f091f8SChristian König 	spin_unlock(&bdev->lru_lock);
2518af8a109SChristian König 
2528af8a109SChristian König 	ttm_pool_fini(&bdev->pool);
2538af8a109SChristian König 	ttm_global_release();
2548af8a109SChristian König }
2558af8a109SChristian König EXPORT_SYMBOL(ttm_device_fini);
256c97f082cSAndrey Grodzovsky 
ttm_device_clear_lru_dma_mappings(struct ttm_device * bdev,struct list_head * list)2576a9b0289SChristian König static void ttm_device_clear_lru_dma_mappings(struct ttm_device *bdev,
2586a9b0289SChristian König 					      struct list_head *list)
259c97f082cSAndrey Grodzovsky {
2606a9b0289SChristian König 	struct ttm_resource *res;
261c97f082cSAndrey Grodzovsky 
262c97f082cSAndrey Grodzovsky 	spin_lock(&bdev->lru_lock);
2636a9b0289SChristian König 	while ((res = list_first_entry_or_null(list, typeof(*res), lru))) {
2646a9b0289SChristian König 		struct ttm_buffer_object *bo = res->bo;
2656a9b0289SChristian König 
266c97f082cSAndrey Grodzovsky 		/* Take ref against racing releases once lru_lock is unlocked */
2676a9b0289SChristian König 		if (!ttm_bo_get_unless_zero(bo))
2686a9b0289SChristian König 			continue;
2696a9b0289SChristian König 
2706a9b0289SChristian König 		list_del_init(&res->lru);
271c97f082cSAndrey Grodzovsky 		spin_unlock(&bdev->lru_lock);
272c97f082cSAndrey Grodzovsky 
273c97f082cSAndrey Grodzovsky 		if (bo->ttm)
274c97f082cSAndrey Grodzovsky 			ttm_tt_unpopulate(bo->bdev, bo->ttm);
275c97f082cSAndrey Grodzovsky 
276c97f082cSAndrey Grodzovsky 		ttm_bo_put(bo);
277c97f082cSAndrey Grodzovsky 		spin_lock(&bdev->lru_lock);
278c97f082cSAndrey Grodzovsky 	}
2796a9b0289SChristian König 	spin_unlock(&bdev->lru_lock);
280c97f082cSAndrey Grodzovsky }
281c97f082cSAndrey Grodzovsky 
ttm_device_clear_dma_mappings(struct ttm_device * bdev)2826a9b0289SChristian König void ttm_device_clear_dma_mappings(struct ttm_device *bdev)
2836a9b0289SChristian König {
2846a9b0289SChristian König 	struct ttm_resource_manager *man;
2856a9b0289SChristian König 	unsigned int i, j;
2866a9b0289SChristian König 
2876a9b0289SChristian König 	ttm_device_clear_lru_dma_mappings(bdev, &bdev->pinned);
2886a9b0289SChristian König 
289c97f082cSAndrey Grodzovsky 	for (i = TTM_PL_SYSTEM; i < TTM_NUM_MEM_TYPES; ++i) {
290c97f082cSAndrey Grodzovsky 		man = ttm_manager_type(bdev, i);
291c97f082cSAndrey Grodzovsky 		if (!man || !man->use_tt)
292c97f082cSAndrey Grodzovsky 			continue;
293c97f082cSAndrey Grodzovsky 
2946a9b0289SChristian König 		for (j = 0; j < TTM_MAX_BO_PRIORITY; ++j)
2956a9b0289SChristian König 			ttm_device_clear_lru_dma_mappings(bdev, &man->lru[j]);
296c97f082cSAndrey Grodzovsky 	}
297c97f082cSAndrey Grodzovsky }
298c97f082cSAndrey Grodzovsky EXPORT_SYMBOL(ttm_device_clear_dma_mappings);
299