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