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