18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR MIT 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Huge page-table-entry support for IO memory. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2007-2019 Vmware, Inc. All rights reservedd. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci#include "vmwgfx_drv.h" 88c2ecf20Sopenharmony_ci#include <drm/ttm/ttm_module.h> 98c2ecf20Sopenharmony_ci#include <drm/ttm/ttm_bo_driver.h> 108c2ecf20Sopenharmony_ci#include <drm/ttm/ttm_placement.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci/** 138c2ecf20Sopenharmony_ci * struct vmw_thp_manager - Range manager implementing huge page alignment 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * @mm: The underlying range manager. Protected by @lock. 168c2ecf20Sopenharmony_ci * @lock: Manager lock. 178c2ecf20Sopenharmony_ci */ 188c2ecf20Sopenharmony_cistruct vmw_thp_manager { 198c2ecf20Sopenharmony_ci struct ttm_resource_manager manager; 208c2ecf20Sopenharmony_ci struct drm_mm mm; 218c2ecf20Sopenharmony_ci spinlock_t lock; 228c2ecf20Sopenharmony_ci}; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic struct vmw_thp_manager *to_thp_manager(struct ttm_resource_manager *man) 258c2ecf20Sopenharmony_ci{ 268c2ecf20Sopenharmony_ci return container_of(man, struct vmw_thp_manager, manager); 278c2ecf20Sopenharmony_ci} 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic const struct ttm_resource_manager_func vmw_thp_func; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic int vmw_thp_insert_aligned(struct drm_mm *mm, struct drm_mm_node *node, 328c2ecf20Sopenharmony_ci unsigned long align_pages, 338c2ecf20Sopenharmony_ci const struct ttm_place *place, 348c2ecf20Sopenharmony_ci struct ttm_resource *mem, 358c2ecf20Sopenharmony_ci unsigned long lpfn, 368c2ecf20Sopenharmony_ci enum drm_mm_insert_mode mode) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci if (align_pages >= mem->page_alignment && 398c2ecf20Sopenharmony_ci (!mem->page_alignment || align_pages % mem->page_alignment == 0)) { 408c2ecf20Sopenharmony_ci return drm_mm_insert_node_in_range(mm, node, 418c2ecf20Sopenharmony_ci mem->num_pages, 428c2ecf20Sopenharmony_ci align_pages, 0, 438c2ecf20Sopenharmony_ci place->fpfn, lpfn, mode); 448c2ecf20Sopenharmony_ci } 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci return -ENOSPC; 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic int vmw_thp_get_node(struct ttm_resource_manager *man, 508c2ecf20Sopenharmony_ci struct ttm_buffer_object *bo, 518c2ecf20Sopenharmony_ci const struct ttm_place *place, 528c2ecf20Sopenharmony_ci struct ttm_resource *mem) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci struct vmw_thp_manager *rman = to_thp_manager(man); 558c2ecf20Sopenharmony_ci struct drm_mm *mm = &rman->mm; 568c2ecf20Sopenharmony_ci struct drm_mm_node *node; 578c2ecf20Sopenharmony_ci unsigned long align_pages; 588c2ecf20Sopenharmony_ci unsigned long lpfn; 598c2ecf20Sopenharmony_ci enum drm_mm_insert_mode mode = DRM_MM_INSERT_BEST; 608c2ecf20Sopenharmony_ci int ret; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci node = kzalloc(sizeof(*node), GFP_KERNEL); 638c2ecf20Sopenharmony_ci if (!node) 648c2ecf20Sopenharmony_ci return -ENOMEM; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci lpfn = place->lpfn; 678c2ecf20Sopenharmony_ci if (!lpfn) 688c2ecf20Sopenharmony_ci lpfn = man->size; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci mode = DRM_MM_INSERT_BEST; 718c2ecf20Sopenharmony_ci if (place->flags & TTM_PL_FLAG_TOPDOWN) 728c2ecf20Sopenharmony_ci mode = DRM_MM_INSERT_HIGH; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci spin_lock(&rman->lock); 758c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD)) { 768c2ecf20Sopenharmony_ci align_pages = (HPAGE_PUD_SIZE >> PAGE_SHIFT); 778c2ecf20Sopenharmony_ci if (mem->num_pages >= align_pages) { 788c2ecf20Sopenharmony_ci ret = vmw_thp_insert_aligned(mm, node, align_pages, 798c2ecf20Sopenharmony_ci place, mem, lpfn, mode); 808c2ecf20Sopenharmony_ci if (!ret) 818c2ecf20Sopenharmony_ci goto found_unlock; 828c2ecf20Sopenharmony_ci } 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci align_pages = (HPAGE_PMD_SIZE >> PAGE_SHIFT); 868c2ecf20Sopenharmony_ci if (mem->num_pages >= align_pages) { 878c2ecf20Sopenharmony_ci ret = vmw_thp_insert_aligned(mm, node, align_pages, place, mem, 888c2ecf20Sopenharmony_ci lpfn, mode); 898c2ecf20Sopenharmony_ci if (!ret) 908c2ecf20Sopenharmony_ci goto found_unlock; 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci ret = drm_mm_insert_node_in_range(mm, node, mem->num_pages, 948c2ecf20Sopenharmony_ci mem->page_alignment, 0, 958c2ecf20Sopenharmony_ci place->fpfn, lpfn, mode); 968c2ecf20Sopenharmony_cifound_unlock: 978c2ecf20Sopenharmony_ci spin_unlock(&rman->lock); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci if (unlikely(ret)) { 1008c2ecf20Sopenharmony_ci kfree(node); 1018c2ecf20Sopenharmony_ci } else { 1028c2ecf20Sopenharmony_ci mem->mm_node = node; 1038c2ecf20Sopenharmony_ci mem->start = node->start; 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci return ret; 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic void vmw_thp_put_node(struct ttm_resource_manager *man, 1128c2ecf20Sopenharmony_ci struct ttm_resource *mem) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci struct vmw_thp_manager *rman = to_thp_manager(man); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci if (mem->mm_node) { 1178c2ecf20Sopenharmony_ci spin_lock(&rman->lock); 1188c2ecf20Sopenharmony_ci drm_mm_remove_node(mem->mm_node); 1198c2ecf20Sopenharmony_ci spin_unlock(&rman->lock); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci kfree(mem->mm_node); 1228c2ecf20Sopenharmony_ci mem->mm_node = NULL; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ciint vmw_thp_init(struct vmw_private *dev_priv) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci struct vmw_thp_manager *rman; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci rman = kzalloc(sizeof(*rman), GFP_KERNEL); 1318c2ecf20Sopenharmony_ci if (!rman) 1328c2ecf20Sopenharmony_ci return -ENOMEM; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci ttm_resource_manager_init(&rman->manager, 1358c2ecf20Sopenharmony_ci dev_priv->vram_size >> PAGE_SHIFT); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci rman->manager.func = &vmw_thp_func; 1388c2ecf20Sopenharmony_ci drm_mm_init(&rman->mm, 0, rman->manager.size); 1398c2ecf20Sopenharmony_ci spin_lock_init(&rman->lock); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci ttm_set_driver_manager(&dev_priv->bdev, TTM_PL_VRAM, &rman->manager); 1428c2ecf20Sopenharmony_ci ttm_resource_manager_set_used(&rman->manager, true); 1438c2ecf20Sopenharmony_ci return 0; 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_civoid vmw_thp_fini(struct vmw_private *dev_priv) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci struct ttm_resource_manager *man = ttm_manager_type(&dev_priv->bdev, TTM_PL_VRAM); 1498c2ecf20Sopenharmony_ci struct vmw_thp_manager *rman = to_thp_manager(man); 1508c2ecf20Sopenharmony_ci struct drm_mm *mm = &rman->mm; 1518c2ecf20Sopenharmony_ci int ret; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci ttm_resource_manager_set_used(man, false); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci ret = ttm_resource_manager_force_list_clean(&dev_priv->bdev, man); 1568c2ecf20Sopenharmony_ci if (ret) 1578c2ecf20Sopenharmony_ci return; 1588c2ecf20Sopenharmony_ci spin_lock(&rman->lock); 1598c2ecf20Sopenharmony_ci drm_mm_clean(mm); 1608c2ecf20Sopenharmony_ci drm_mm_takedown(mm); 1618c2ecf20Sopenharmony_ci spin_unlock(&rman->lock); 1628c2ecf20Sopenharmony_ci ttm_resource_manager_cleanup(man); 1638c2ecf20Sopenharmony_ci ttm_set_driver_manager(&dev_priv->bdev, TTM_PL_VRAM, NULL); 1648c2ecf20Sopenharmony_ci kfree(rman); 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic void vmw_thp_debug(struct ttm_resource_manager *man, 1688c2ecf20Sopenharmony_ci struct drm_printer *printer) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci struct vmw_thp_manager *rman = to_thp_manager(man); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci spin_lock(&rman->lock); 1738c2ecf20Sopenharmony_ci drm_mm_print(&rman->mm, printer); 1748c2ecf20Sopenharmony_ci spin_unlock(&rman->lock); 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic const struct ttm_resource_manager_func vmw_thp_func = { 1788c2ecf20Sopenharmony_ci .alloc = vmw_thp_get_node, 1798c2ecf20Sopenharmony_ci .free = vmw_thp_put_node, 1808c2ecf20Sopenharmony_ci .debug = vmw_thp_debug 1818c2ecf20Sopenharmony_ci}; 182